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

View file

@ -33,10 +33,12 @@ G_DECLARE_FINAL_TYPE (GstWebView2Object, gst_webview2_object,
GstWebView2Object * gst_webview2_object_new (GstD3D11Device * device); GstWebView2Object * gst_webview2_object_new (GstD3D11Device * device);
gboolean gst_webview2_object_set_location (GstWebView2Object * client, 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, gboolean gst_webview_object_update_size (GstWebView2Object * client,
guint width, guint height); guint width,
guint height);
void gst_webview2_object_send_event (GstWebView2Object * client, void gst_webview2_object_send_event (GstWebView2Object * client,
GstEvent * event); GstEvent * event);

View file

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