webview2: Add support for javascript injection

Allow javascript injection for various custom use cases.
For example, scrollbars and scrolling can be disabled via

gst-launch-1.0 webview2src location=https://gstreamer.freedesktop.org \
    javascript="document.querySelector('body').style.overflow='hidden'" ! ...

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/6487>
This commit is contained in:
Seungha Yang 2024-03-31 01:21:03 +09:00 committed by GStreamer Marge Bot
parent 85d422f7c6
commit f7bdf91ad7
3 changed files with 80 additions and 12 deletions

View file

@ -119,7 +119,9 @@ GstGetActivationFactory (InterfaceType ** factory)
class GstWebView2Item : public RuntimeClass<RuntimeClassFlags<ClassicCom>,
FtmBase, IFrameArrivedHandler,
ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler,
ICoreWebView2CreateCoreWebView2CompositionControllerCompletedHandler>
ICoreWebView2CreateCoreWebView2CompositionControllerCompletedHandler,
ICoreWebView2NavigationCompletedEventHandler,
ICoreWebView2ExecuteScriptCompletedHandler>
{
public:
static LRESULT CALLBACK
@ -238,6 +240,9 @@ public:
hr = ctrl_->get_CoreWebView2 (&webview_);
CHECK_HR_AND_RETURN (hr, get_CoreWebView2);
hr = webview_->add_NavigationCompleted (this, &navigation_compl_token_);
CHECK_HR_AND_RETURN (hr, get_CoreWebView2);
ComPtr<ICoreWebView2_8> webview8;
hr = webview_.As (&webview8);
if (SUCCEEDED (hr))
@ -251,6 +256,26 @@ public:
return S_OK;
}
IFACEMETHOD(Invoke) (HRESULT hr, LPCWSTR result_json)
{
GST_DEBUG_OBJECT (obj_, "Executing script result 0x%x", (guint) hr);
return S_OK;
}
IFACEMETHOD(Invoke) (ICoreWebView2 * sender,
ICoreWebView2NavigationCompletedEventArgs * arg)
{
GST_DEBUG_OBJECT (obj_, "Navigation completed");
if (!script_.empty ()) {
GST_DEBUG_OBJECT (obj_, "Executing script");
sender->ExecuteScript (script_.c_str (), this);
}
return S_OK;
}
HRESULT
startCapture ()
{
@ -367,11 +392,16 @@ public:
comp_ = nullptr;
}
HRESULT Navigate (LPCWSTR location)
HRESULT Navigate (LPCWSTR location, LPCWSTR script)
{
if (!webview_ || !location)
return E_FAIL;
if (script)
script_ = script;
else
script_.clear ();
return webview_->Navigate (location);
}
@ -503,6 +533,8 @@ private:
ComPtr<ICoreWebView2Controller3> ctrl_;
ComPtr<ICoreWebView2CompositionController > comp_ctrl_;
ComPtr<ICoreWebView2 > webview_;
EventRegistrationToken navigation_compl_token_ = { };
std::wstring script_;
ComPtr<IGraphicsCaptureItem> item_;
SizeInt32 frame_size_ = { };
@ -576,18 +608,23 @@ class UpdateLocationHandler : public RuntimeClass<RuntimeClassFlags<ClassicCom>,
{
public:
STDMETHODIMP
RuntimeClassInitialize (GstWebView2Item * item, const std::string & location)
RuntimeClassInitialize (GstWebView2Item * item, const std::string & location,
const std::string & script)
{
item_ = item;
location_wide_ = g_utf8_to_utf16 (location.c_str (),
-1, nullptr, nullptr, nullptr);
if (!script.empty ()) {
script_wide_ = g_utf8_to_utf16 (script.c_str (),
-1, nullptr, nullptr, nullptr);
}
return S_OK;
}
IFACEMETHOD(Invoke) (void)
{
item_->Navigate ((LPCWSTR) location_wide_);
item_->Navigate ((LPCWSTR) location_wide_, (LPCWSTR) script_wide_);
item_ = nullptr;
return S_OK;
@ -596,11 +633,13 @@ public:
~UpdateLocationHandler ()
{
g_free (location_wide_);
g_free (script_wide_);
}
private:
ComPtr<GstWebView2Item> item_;
gunichar2 *location_wide_ = nullptr;
gunichar2 *script_wide_ = nullptr;
};
class AsyncWaiter : public RuntimeClass<RuntimeClassFlags<ClassicCom>,
@ -932,7 +971,7 @@ gst_webview2_object_new (GstD3D11Device * device)
gboolean
gst_webview2_object_set_location (GstWebView2Object * object,
const std::string & location)
const std::string & location, const std::string & script)
{
auto priv = object->priv;
std::lock_guard < std::mutex > lk (priv->lock);
@ -941,7 +980,7 @@ gst_webview2_object_set_location (GstWebView2Object * object,
ComPtr < UpdateLocationHandler > handler;
auto hr = MakeAndInitialize < UpdateLocationHandler > (&handler,
priv->item.Get (), location);
priv->item.Get (), location, script);
if (FAILED (hr))
return FALSE;

View file

@ -33,10 +33,12 @@ G_DECLARE_FINAL_TYPE (GstWebView2Object, gst_webview2_object,
GstWebView2Object * gst_webview2_object_new (GstD3D11Device * device);
gboolean gst_webview2_object_set_location (GstWebView2Object * client,
const std::string & location);
const std::string & location,
const std::string & script);
gboolean gst_webview_object_update_size (GstWebView2Object * client,
guint width, guint height);
guint width,
guint height);
void gst_webview2_object_send_event (GstWebView2Object * client,
GstEvent * event);

View file

@ -57,6 +57,7 @@ enum
PROP_ADAPTER,
PROP_LOCATION,
PROP_PROCESSING_DEADLINE,
PROP_JAVASCRIPT,
};
#define DEFAULT_LOCATION "about:blank"
@ -80,6 +81,7 @@ struct GstWebView2SrcPrivate
gint adapter_index = DEFAULT_ADAPTER;
std::string location = DEFAULT_LOCATION;
GstClockTime processing_deadline = DEFAULT_PROCESSING_DEADLINE;
std::string script;
};
/* *INDENT-ON* */
@ -138,7 +140,7 @@ gst_webview2_src_class_init (GstWebView2SrcClass * klass)
G_PARAM_STATIC_STRINGS)));
g_object_class_install_property (object_class, PROP_LOCATION,
g_param_spec_string ("location", "location",
g_param_spec_string ("location", "Location",
"The URL to display",
nullptr, (GParamFlags) (G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS | GST_PARAM_MUTABLE_PLAYING)));
@ -149,6 +151,12 @@ gst_webview2_src_class_init (GstWebView2SrcClass * klass)
DEFAULT_PROCESSING_DEADLINE, (GParamFlags) (G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS | GST_PARAM_MUTABLE_PLAYING)));
g_object_class_install_property (object_class, PROP_JAVASCRIPT,
g_param_spec_string ("javascript", "Javascript",
"Javascript to run on nevigation completed",
nullptr, (GParamFlags) (G_PARAM_READWRITE |
G_PARAM_STATIC_STRINGS | GST_PARAM_MUTABLE_PLAYING)));
gst_element_class_set_static_metadata (element_class,
"WebView2 Source", "Source/Video",
"Creates a video stream rendered by WebView2",
@ -208,8 +216,10 @@ gst_webview2_src_set_location (GstWebView2Src * self, const gchar * location)
else
priv->location = DEFAULT_LOCATION;
if (priv->object)
gst_webview2_object_set_location (priv->object, priv->location);
if (priv->object) {
gst_webview2_object_set_location (priv->object,
priv->location, priv->script);
}
}
static void
@ -242,6 +252,20 @@ gst_webview2_src_set_property (GObject * object, guint prop_id,
}
break;
}
case PROP_JAVASCRIPT:
{
auto script = g_value_get_string (value);
if (script)
priv->script = script;
else
priv->script.clear ();
if (priv->object) {
gst_webview2_object_set_location (priv->object, priv->location,
priv->script);
}
break;
}
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -266,6 +290,9 @@ gst_win32_video_src_get_property (GObject * object, guint prop_id,
case PROP_PROCESSING_DEADLINE:
g_value_set_uint64 (value, priv->processing_deadline);
break;
case PROP_JAVASCRIPT:
g_value_set_string (value, priv->script.c_str ());
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@ -311,7 +338,7 @@ gst_webview2_src_start (GstBaseSrc * src)
return FALSE;
}
gst_webview2_object_set_location (priv->object, priv->location);
gst_webview2_object_set_location (priv->object, priv->location, priv->script);
priv->last_frame_no = -1;