Merge branch 'webview_script_message' of https://github.com/TcT2k/wxWidgets

Add WebView script message and user scripts.

See https://github.com/wxWidgets/wxWidgets/pull/2237
This commit is contained in:
Vadim Zeitlin
2021-03-05 18:44:46 +01:00
11 changed files with 550 additions and 54 deletions

View File

@@ -78,6 +78,10 @@ public:
virtual wxString GetPageText() const wxOVERRIDE;
virtual void Print() wxOVERRIDE;
virtual bool IsBusy() const wxOVERRIDE;
#if wxUSE_WEBVIEW_WEBKIT2
virtual void EnableAccessToDevTools(bool enable = true) wxOVERRIDE;
virtual bool IsAccessToDevToolsEnabled() const wxOVERRIDE;
#endif
void SetZoomType(wxWebViewZoomType) wxOVERRIDE;
wxWebViewZoomType GetZoomType() const wxOVERRIDE;
@@ -115,6 +119,13 @@ public:
virtual void ClearSelection() wxOVERRIDE;
virtual bool RunScript(const wxString& javascript, wxString* output = NULL) const wxOVERRIDE;
#if wxUSE_WEBVIEW_WEBKIT2
virtual bool AddScriptMessageHandler(const wxString& name) wxOVERRIDE;
virtual bool RemoveScriptMessageHandler(const wxString& name) wxOVERRIDE;
virtual bool AddUserScript(const wxString& javascript,
wxWebViewUserScriptInjectionTime injectionTime = wxWEBVIEW_INJECT_AT_DOCUMENT_START) wxOVERRIDE;
virtual void RemoveAllUserScripts() wxOVERRIDE;
#endif
//Virtual Filesystem Support
virtual void RegisterHandler(wxSharedPtr<wxWebViewHandler> handler) wxOVERRIDE;

View File

@@ -55,6 +55,9 @@ public:
wxString m_pendingURL;
int m_pendingContextMenuEnabled;
int m_pendingAccessToDevToolsEnabled;
wxVector<wxString> m_pendingUserScripts;
wxVector<wxString> m_userScriptIds;
wxString m_scriptMsgHandlerName;
// WebView Events tokens
EventRegistrationToken m_navigationStartingToken = { };
@@ -64,6 +67,7 @@ public:
EventRegistrationToken m_documentTitleChangedToken = { };
EventRegistrationToken m_contentLoadingToken = { };
EventRegistrationToken m_containsFullScreenElementChangedToken = { };
EventRegistrationToken m_webMessageReceivedToken = { };
// WebView Event handlers
HRESULT OnNavigationStarting(ICoreWebView2* sender, ICoreWebView2NavigationStartingEventArgs* args);
@@ -73,6 +77,8 @@ public:
HRESULT OnDocumentTitleChanged(ICoreWebView2* sender, IUnknown* args);
HRESULT OnContentLoading(ICoreWebView2* sender, ICoreWebView2ContentLoadingEventArgs* args);
HRESULT OnContainsFullScreenElementChanged(ICoreWebView2* sender, IUnknown* args);
HRESULT OnWebMessageReceived(ICoreWebView2* sender, ICoreWebView2WebMessageReceivedEventArgs* args);
HRESULT OnAddScriptToExecuteOnDocumentedCreatedCompleted(HRESULT errorCode, LPCWSTR id);
HRESULT OnEnvironmentCreated(HRESULT result, ICoreWebView2Environment* environment);
HRESULT OnWebViewCreated(HRESULT result, ICoreWebView2Controller* webViewController);
@@ -86,6 +92,8 @@ public:
ICoreWebView2Settings* GetSettings();
void UpdateWebMessageHandler();
static wxDynamicLibrary ms_loaderDll;
static wxString ms_browserExecutableDir;
static wxString ms_version;

View File

@@ -90,6 +90,11 @@ public:
virtual bool IsAccessToDevToolsEnabled() const wxOVERRIDE;
virtual bool RunScript(const wxString& javascript, wxString* output = NULL) const wxOVERRIDE;
virtual bool AddScriptMessageHandler(const wxString& name) wxOVERRIDE;
virtual bool RemoveScriptMessageHandler(const wxString& name) wxOVERRIDE;
virtual bool AddUserScript(const wxString& javascript,
wxWebViewUserScriptInjectionTime injectionTime = wxWEBVIEW_INJECT_AT_DOCUMENT_START) wxOVERRIDE;
virtual void RemoveAllUserScripts() wxOVERRIDE;
virtual void RegisterHandler(wxSharedPtr<wxWebViewHandler> handler) wxOVERRIDE;

View File

@@ -89,6 +89,11 @@ public:
virtual bool IsEditable() const wxOVERRIDE;
bool RunScript(const wxString& javascript, wxString* output = NULL) const wxOVERRIDE;
virtual bool AddScriptMessageHandler(const wxString& name) wxOVERRIDE;
virtual bool RemoveScriptMessageHandler(const wxString& name) wxOVERRIDE;
virtual bool AddUserScript(const wxString& javascript,
wxWebViewUserScriptInjectionTime injectionTime = wxWEBVIEW_INJECT_AT_DOCUMENT_START) wxOVERRIDE;
virtual void RemoveAllUserScripts() wxOVERRIDE;
//Virtual Filesystem Support
virtual void RegisterHandler(wxSharedPtr<wxWebViewHandler> handler) wxOVERRIDE;

View File

@@ -86,6 +86,12 @@ enum wxWebViewNavigationActionFlags
wxWEBVIEW_NAV_ACTION_OTHER
};
enum wxWebViewUserScriptInjectionTime
{
wxWEBVIEW_INJECT_AT_DOCUMENT_START,
wxWEBVIEW_INJECT_AT_DOCUMENT_END
};
//Base class for custom scheme handlers
class WXDLLIMPEXP_WEBVIEW wxWebViewHandler
{
@@ -181,7 +187,18 @@ public:
virtual void Print() = 0;
virtual void RegisterHandler(wxSharedPtr<wxWebViewHandler> handler) = 0;
virtual void Reload(wxWebViewReloadFlags flags = wxWEBVIEW_RELOAD_DEFAULT) = 0;
// Script
virtual bool RunScript(const wxString& javascript, wxString* output = NULL) const = 0;
virtual bool AddScriptMessageHandler(const wxString& name)
{ wxUnusedVar(name); return false; }
virtual bool RemoveScriptMessageHandler(const wxString& name)
{ wxUnusedVar(name); return false; }
virtual bool AddUserScript(const wxString& javascript,
wxWebViewUserScriptInjectionTime injectionTime = wxWEBVIEW_INJECT_AT_DOCUMENT_START)
{ wxUnusedVar(javascript); wxUnusedVar(injectionTime); return false; }
virtual void RemoveAllUserScripts() {}
virtual void SetEditable(bool enable = true) = 0;
void SetPage(const wxString& html, const wxString& baseUrl)
{
@@ -269,9 +286,10 @@ public:
wxWebViewEvent() {}
wxWebViewEvent(wxEventType type, int id, const wxString& url,
const wxString target,
wxWebViewNavigationActionFlags flags = wxWEBVIEW_NAV_ACTION_NONE)
wxWebViewNavigationActionFlags flags = wxWEBVIEW_NAV_ACTION_NONE,
const wxString& messageHandler = wxString())
: wxNotifyEvent(type, id), m_url(url), m_target(target),
m_actionFlags(flags)
m_actionFlags(flags), m_messageHandler(messageHandler)
{}
@@ -279,12 +297,14 @@ public:
const wxString& GetTarget() const { return m_target; }
wxWebViewNavigationActionFlags GetNavigationAction() const { return m_actionFlags; }
const wxString& GetMessageHandler() const { return m_messageHandler; }
virtual wxEvent* Clone() const wxOVERRIDE { return new wxWebViewEvent(*this); }
private:
wxString m_url;
wxString m_target;
wxWebViewNavigationActionFlags m_actionFlags;
wxString m_messageHandler;
wxDECLARE_DYNAMIC_CLASS_NO_ASSIGN(wxWebViewEvent);
};
@@ -296,6 +316,7 @@ wxDECLARE_EXPORTED_EVENT( WXDLLIMPEXP_WEBVIEW, wxEVT_WEBVIEW_ERROR, wxWebViewEve
wxDECLARE_EXPORTED_EVENT( WXDLLIMPEXP_WEBVIEW, wxEVT_WEBVIEW_NEWWINDOW, wxWebViewEvent );
wxDECLARE_EXPORTED_EVENT( WXDLLIMPEXP_WEBVIEW, wxEVT_WEBVIEW_TITLE_CHANGED, wxWebViewEvent );
wxDECLARE_EXPORTED_EVENT( WXDLLIMPEXP_WEBVIEW, wxEVT_WEBVIEW_FULLSCREEN_CHANGED, wxWebViewEvent);
wxDECLARE_EXPORTED_EVENT( WXDLLIMPEXP_WEBVIEW, wxEVT_WEBVIEW_SCRIPT_MESSAGE_RECEIVED, wxWebViewEvent);
typedef void (wxEvtHandler::*wxWebViewEventFunction)
(wxWebViewEvent&);

View File

@@ -107,6 +107,19 @@ enum wxWebViewNavigationActionFlags
wxWEBVIEW_NAV_ACTION_OTHER
};
/**
Specifies at which place of documents an user script will be inserted.
@since 3.1.5
*/
enum wxWebViewUserScriptInjectionTime
{
/** Insert the code of the user script at the beginning of loaded documents. */
wxWEBVIEW_INJECT_AT_DOCUMENT_START,
/** Insert the code of the user script at the end of the loaded documents. */
wxWEBVIEW_INJECT_AT_DOCUMENT_END
};
/**
Internet Explorer emulation modes for wxWebViewIE.
@@ -374,6 +387,12 @@ public:
systems on macOS 10.13+. In order to use handlers two-step creation has to be used
and RegisterHandler() has to be called before Create().
Starting with macOS 10.11 and iOS 9 an application cannot create unsecure
connections (this includes HTTP and unverified HTTPS). You have to include
additional fields in your Info.plist to enable such connections.
For further details see the documentation on NSAppTransportSecurity
<a target=_new href="https://developer.apple.com/documentation/bundleresources/information_property_list/nsapptransportsecurity">here</a>
@section async Asynchronous Notifications
Many of the methods in wxWebView are asynchronous, i.e. they return
@@ -433,6 +452,10 @@ public:
the page wants to enter or leave fullscreen. Use GetInt to get the status.
Currently only implemented for the edge and WebKit2GTK+ backend
and is only available in wxWidgets 3.1.5 or later.
@event{EVT_WEBVIEW_SCRIPT_MESSAGE_RECEIVED(id, func)}
Process a @c wxEVT_WEBVIEW_SCRIPT_MESSAGE_RECEIVED event
only available in wxWidgets 3.1.5 or later. For usage details see
AddScriptMessageHandler().
@endEventTable
@since 2.9.3
@@ -609,6 +632,44 @@ public:
*/
virtual void Reload(wxWebViewReloadFlags flags = wxWEBVIEW_RELOAD_DEFAULT) = 0;
/**
Set the editable property of the web control. Enabling allows the user
to edit the page even if the @c contenteditable attribute is not set.
The exact capabilities vary with the backend being used.
@note This is not implemented on macOS.
*/
virtual void SetEditable(bool enable = true) = 0;
/**
Set the displayed page source to the contents of the given string.
@param html The string that contains the HTML data to display.
@param baseUrl URL assigned to the HTML data, to be used to resolve
relative paths, for instance.
@note When using @c wxWEBVIEW_BACKEND_IE you must wait for the current
page to finish loading before calling SetPage(). The baseURL
parameter is not used in this backend and the edge backend.
*/
virtual void SetPage(const wxString& html, const wxString& baseUrl) = 0;
/**
Set the displayed page source to the contents of the given stream.
@param html The stream to read HTML data from.
@param baseUrl URL assigned to the HTML data, to be used to resolve
relative paths, for instance.
*/
virtual void SetPage(wxInputStream& html, wxString baseUrl);
/**
Stop the current page loading process, if any.
May trigger an error event of type @c wxWEBVIEW_NAV_ERR_USER_CANCELLED.
TODO: make @c wxWEBVIEW_NAV_ERR_USER_CANCELLED errors uniform across ports.
*/
virtual void Stop() = 0;
/**
@name Scripting
*/
/**
Runs the given JavaScript code.
@@ -670,39 +731,78 @@ public:
virtual bool RunScript(const wxString& javascript, wxString* output = NULL) const = 0;
/**
Set the editable property of the web control. Enabling allows the user
to edit the page even if the @c contenteditable attribute is not set.
The exact capabilities vary with the backend being used.
Add a script message handler with the given name.
@note This is not implemented on macOS.
To use the script message handler from javascript use
@c window.<name>.postMessage(<messageBody>) where <name> corresponds the the value
of the name parameter. The <messageBody> will be available to the application
via a @c wxEVT_WEBVIEW_SCRIPT_MESSAGE_RECEIVED event.
Sample C++ code receiving a script message:
@code
// Install message handler with the name wx_msg
m_webView->AddScriptMessageHandler('wx_msg');
// Bind handler
m_webView->Bind(wxEVT_WEBVIEW_SCRIPT_MESSAGE_RECEIVED, [](wxWebViewEvent& evt) {
wxLogMessage("Script message received; value = %s, handler = %s", evt.GetString(), evt.GetMessageHandler());
});
@endcode
Sample javascript sending a script message:
@code
// Send sample message body
window.wx_msg.postMessage('This is a message body');
@endcode
@param name Name of the message handler that can be used from javascript
@return @true if the handler could be added, @false if it could not be added.
@see RemoveScriptMessageHandler()
@note The Edge (Chromium) backend only supports a single message handler and
the IE backend does not support script message handlers.
@since 3.1.5
*/
virtual void SetEditable(bool enable = true) = 0;
virtual bool AddScriptMessageHandler(const wxString& name);
/**
Set the displayed page source to the contents of the given string.
@param html The string that contains the HTML data to display.
@param baseUrl URL assigned to the HTML data, to be used to resolve
relative paths, for instance.
@note When using @c wxWEBVIEW_BACKEND_IE you must wait for the current
page to finish loading before calling SetPage(). The baseURL
parameter is not used in this backend and the edge backend.
Remove a script message handler with the given name that was previously added via
AddScriptMessageHandler().
@return @true if the handler could be removed, @false if it could not be removed.
@see AddScriptMessageHandler()
@since 3.1.5
*/
virtual void SetPage(const wxString& html, const wxString& baseUrl) = 0;
virtual bool RemoveScriptMessageHandler(const wxString& name);
/**
Set the displayed page source to the contents of the given stream.
@param html The stream to read HTML data from.
@param baseUrl URL assigned to the HTML data, to be used to resolve
relative paths, for instance.
Injects the specified script into the webpages content.
@param javascript The javascript code to add.
@param injectionTime Specifies when the script will be executed.
@return Returns true if the script was added successfully.
@note Please note that this is unsupported by the IE backend and
the Edge (Chromium) backend does only support wxWEBVIEW_INJECT_AT_DOCUMENT_START.
@see RemoveAllUserScripts()
@since 3.1.5
*/
virtual void SetPage(wxInputStream& html, wxString baseUrl);
virtual bool AddUserScript(const wxString& javascript,
wxWebViewUserScriptInjectionTime injectionTime = wxWEBVIEW_INJECT_AT_DOCUMENT_START);
/**
Stop the current page loading process, if any.
May trigger an error event of type @c wxWEBVIEW_NAV_ERR_USER_CANCELLED.
TODO: make @c wxWEBVIEW_NAV_ERR_USER_CANCELLED errors uniform across ports.
Removes all user scripts from the web view.
@see AddUserScript()
@since 3.1.5
*/
virtual void Stop() = 0;
virtual void RemoveAllUserScripts();
/**
@name Clipboard
@@ -772,8 +872,8 @@ public:
/**
Enable or disable access to dev tools for the user.
This is currently only implemented for the Edge (Chromium) backend
where the dev tools are enabled by default.
This is currently only implemented for the Edge (Chromium) backend and
the WebKit2GTK+ backend. Dev tools are disabled by default.
@since 3.1.4
*/
@@ -1094,6 +1194,10 @@ public:
@event{EVT_WEBVIEW_TITLE_CHANGED(id, func)}
Process a @c wxEVT_WEBVIEW_TITLE_CHANGED event, generated when
the page title changes. Use GetString to get the title.
@event{EVT_WEBVIEW_SCRIPT_MESSAGE_RECEIVED(id, func)}
Process a @c wxEVT_WEBVIEW_SCRIPT_MESSAGE_RECEIVED event
only available in wxWidgets 3.1.5 or later. For usage details see
wxWebView::AddScriptMessageHandler().
@endEventTable
@since 2.9.3
@@ -1108,7 +1212,8 @@ public:
wxWebViewEvent();
wxWebViewEvent(wxEventType type, int id, const wxString href,
const wxString target,
wxWebViewNavigationActionFlags flags = wxWEBVIEW_NAV_ACTION_NONE);
wxWebViewNavigationActionFlags flags = wxWEBVIEW_NAV_ACTION_NONE,
const wxString& messageHandler = wxString());
/**
Get the name of the target frame which the url of this event
@@ -1129,6 +1234,14 @@ public:
@since 3.1.2
*/
wxWebViewNavigationActionFlags GetNavigationAction() const;
/**
Get the name of the script handler. Only valid for events of type
@c wxEVT_WEBVIEW_SCRIPT_MESSAGE_RECEIVED
@since 3.1.5
*/
const wxString& GetMessageHandler() const;
};

View File

@@ -121,6 +121,7 @@ public:
void OnNewWindow(wxWebViewEvent& evt);
void OnTitleChanged(wxWebViewEvent& evt);
void OnFullScreenChanged(wxWebViewEvent& evt);
void OnScriptMessage(wxWebViewEvent& evt);
void OnSetPage(wxCommandEvent& evt);
void OnViewSourceRequest(wxCommandEvent& evt);
void OnViewTextRequest(wxCommandEvent& evt);
@@ -157,7 +158,9 @@ public:
void OnRunScriptDateWithEmulationLevel(wxCommandEvent& evt);
void OnRunScriptArrayWithEmulationLevel(wxCommandEvent& evt);
#endif
void OnRunScriptMessage(wxCommandEvent& evt);
void OnRunScriptCustom(wxCommandEvent& evt);
void OnAddUserScript(wxCommandEvent& evt);
void OnClearSelection(wxCommandEvent& evt);
void OnDeleteSelection(wxCommandEvent& evt);
void OnSelectAll(wxCommandEvent& evt);
@@ -227,6 +230,7 @@ private:
wxMenuItem* m_script_date_el;
wxMenuItem* m_script_array_el;
#endif
wxMenuItem* m_script_message;
wxMenuItem* m_script_custom;
wxMenuItem* m_selection_clear;
wxMenuItem* m_selection_delete;
@@ -406,6 +410,8 @@ WebFrame::WebFrame(const wxString& url) :
//And the memory: file system
m_browser->RegisterHandler(wxSharedPtr<wxWebViewHandler>(new wxWebViewFSHandler("memory")));
#endif
if (!m_browser->AddScriptMessageHandler("wx"))
wxLogError("Could not add script message handler");
SetSizer(topsizer);
@@ -483,8 +489,10 @@ WebFrame::WebFrame(const wxString& url) :
m_script_array_el = script_menu->Append(wxID_ANY, "Return array changing emulation level");
}
#endif
m_script_message = script_menu->Append(wxID_ANY, "Send script message");
m_script_custom = script_menu->Append(wxID_ANY, "Custom script");
m_tools_menu->AppendSubMenu(script_menu, _("Run Script"));
wxMenuItem* addUserScript = m_tools_menu->Append(wxID_ANY, _("Add user script"));
//Selection menu
wxMenu* selection = new wxMenu();
@@ -538,6 +546,7 @@ WebFrame::WebFrame(const wxString& url) :
Bind(wxEVT_WEBVIEW_NEWWINDOW, &WebFrame::OnNewWindow, this, m_browser->GetId());
Bind(wxEVT_WEBVIEW_TITLE_CHANGED, &WebFrame::OnTitleChanged, this, m_browser->GetId());
Bind(wxEVT_WEBVIEW_FULLSCREEN_CHANGED, &WebFrame::OnFullScreenChanged, this, m_browser->GetId());
Bind(wxEVT_WEBVIEW_SCRIPT_MESSAGE_RECEIVED, &WebFrame::OnScriptMessage, this, m_browser->GetId());
// Connect the menu events
Bind(wxEVT_MENU, &WebFrame::OnSetPage, this, setPage->GetId());
@@ -581,7 +590,9 @@ WebFrame::WebFrame(const wxString& url) :
Bind(wxEVT_MENU, &WebFrame::OnRunScriptArrayWithEmulationLevel, this, m_script_array_el->GetId());
}
#endif
Bind(wxEVT_MENU, &WebFrame::OnRunScriptMessage, this, m_script_message->GetId());
Bind(wxEVT_MENU, &WebFrame::OnRunScriptCustom, this, m_script_custom->GetId());
Bind(wxEVT_MENU, &WebFrame::OnAddUserScript, this, addUserScript->GetId());
Bind(wxEVT_MENU, &WebFrame::OnClearSelection, this, m_selection_clear->GetId());
Bind(wxEVT_MENU, &WebFrame::OnDeleteSelection, this, m_selection_delete->GetId());
Bind(wxEVT_MENU, &WebFrame::OnSelectAll, this, selectall->GetId());
@@ -905,6 +916,11 @@ void WebFrame::OnFullScreenChanged(wxWebViewEvent & evt)
ShowFullScreen(evt.GetInt() != 0);
}
void WebFrame::OnScriptMessage(wxWebViewEvent& evt)
{
wxLogMessage("Script message received; value = %s, handler = %s", evt.GetString(), evt.GetMessageHandler());
}
void WebFrame::OnSetPage(wxCommandEvent& WXUNUSED(evt))
{
m_browser->SetPage
@@ -1175,6 +1191,11 @@ void WebFrame::OnRunScriptArrayWithEmulationLevel(wxCommandEvent& WXUNUSED(evt))
}
#endif
void WebFrame::OnRunScriptMessage(wxCommandEvent& WXUNUSED(evt))
{
RunScript("window.wx.postMessage('This is a web message');");
}
void WebFrame::OnRunScriptCustom(wxCommandEvent& WXUNUSED(evt))
{
wxTextEntryDialog dialog
@@ -1191,6 +1212,24 @@ void WebFrame::OnRunScriptCustom(wxCommandEvent& WXUNUSED(evt))
RunScript(dialog.GetValue());
}
void WebFrame::OnAddUserScript(wxCommandEvent & WXUNUSED(evt))
{
wxString userScript = "window.wx_test_var = 'wxWidgets webview sample';";
wxTextEntryDialog dialog
(
this,
"Enter the JavaScript code to run as the initialization script that runs before any script in the HTML document.",
wxGetTextFromUserPromptStr,
userScript,
wxOK | wxCANCEL | wxCENTRE | wxTE_MULTILINE
);
if (dialog.ShowModal() != wxID_OK)
return;
if (!m_browser->AddUserScript(dialog.GetValue()))
wxLogError("Could not add user script");
}
void WebFrame::OnClearSelection(wxCommandEvent& WXUNUSED(evt))
{
m_browser->ClearSelection();

View File

@@ -49,6 +49,7 @@ wxDEFINE_EVENT( wxEVT_WEBVIEW_ERROR, wxWebViewEvent );
wxDEFINE_EVENT( wxEVT_WEBVIEW_NEWWINDOW, wxWebViewEvent );
wxDEFINE_EVENT( wxEVT_WEBVIEW_TITLE_CHANGED, wxWebViewEvent );
wxDEFINE_EVENT( wxEVT_WEBVIEW_FULLSCREEN_CHANGED, wxWebViewEvent);
wxDEFINE_EVENT( wxEVT_WEBVIEW_SCRIPT_MESSAGE_RECEIVED, wxWebViewEvent);
wxStringWebViewFactoryMap wxWebView::m_factoryMap;

View File

@@ -31,6 +31,37 @@
#include <JavaScriptCore/JSValueRef.h>
#include <JavaScriptCore/JSStringRef.h>
// Helper function to get string from Webkit JS result
bool wxGetStringFromJSResult(WebKitJavascriptResult* js_result, wxString* output)
{
JSGlobalContextRef context = webkit_javascript_result_get_global_context(js_result);
JSValueRef value = webkit_javascript_result_get_value(js_result);
JSValueRef exception = NULL;
wxJSStringRef js_value
(
JSValueIsObject(context, value)
? JSValueCreateJSONString(context, value, 0, &exception)
: JSValueToStringCopy(context, value, &exception)
);
if ( exception )
{
if ( output )
{
wxJSStringRef ex_value(JSValueToStringCopy(context, exception, NULL));
*output = ex_value.ToWxString();
}
return false;
}
if ( output != NULL )
*output = js_value.ToWxString();
return true;
}
// ----------------------------------------------------------------------------
// GTK callbacks
// ----------------------------------------------------------------------------
@@ -290,6 +321,21 @@ wxgtk_webview_webkit_leave_fullscreen(WebKitWebView *WXUNUSED(web_view),
return FALSE;
}
static void
wxgtk_webview_webkit_script_message_received(WebKitUserContentManager *WXUNUSED(content_manager),
WebKitJavascriptResult *js_result,
wxWebViewWebKit *webKitCtrl)
{
wxWebViewEvent event(wxEVT_WEBVIEW_SCRIPT_MESSAGE_RECEIVED,
webKitCtrl->GetId(),
webKitCtrl->GetCurrentURL(),
"");
wxString msgStr;
if (wxGetStringFromJSResult(js_result, &msgStr))
event.SetString(msgStr);
webKitCtrl->HandleWindowEvent(event);
}
static gboolean
wxgtk_webview_webkit_decide_policy(WebKitWebView *web_view,
WebKitPolicyDecision *decision,
@@ -698,6 +744,18 @@ float wxWebViewWebKit::GetWebkitZoom() const
return webkit_web_view_get_zoom_level(m_web_view);
}
void wxWebViewWebKit::EnableAccessToDevTools(bool enable)
{
WebKitSettings* settings = webkit_web_view_get_settings(m_web_view);
webkit_settings_set_enable_developer_extras(settings, enable);
}
bool wxWebViewWebKit::IsAccessToDevToolsEnabled() const
{
WebKitSettings* settings = webkit_web_view_get_settings(m_web_view);
return webkit_settings_get_enable_developer_extras(settings);
}
void wxWebViewWebKit::Stop()
{
webkit_web_view_stop_loading(m_web_view);
@@ -1209,32 +1267,7 @@ bool wxWebViewWebKit::RunScriptSync(const wxString& javascript, wxString* output
return false;
}
JSGlobalContextRef context = webkit_javascript_result_get_global_context(js_result);
JSValueRef value = webkit_javascript_result_get_value(js_result);
JSValueRef exception = NULL;
wxJSStringRef js_value
(
JSValueIsObject(context, value)
? JSValueCreateJSONString(context, value, 0, &exception)
: JSValueToStringCopy(context, value, &exception)
);
if ( exception )
{
if ( output )
{
wxJSStringRef ex_value(JSValueToStringCopy(context, exception, NULL));
*output = ex_value.ToWxString();
}
return false;
}
if ( output != NULL )
*output = js_value.ToWxString();
return true;
return wxGetStringFromJSResult(js_result, output);
}
bool wxWebViewWebKit::RunScript(const wxString& javascript, wxString* output) const
@@ -1266,6 +1299,57 @@ bool wxWebViewWebKit::RunScript(const wxString& javascript, wxString* output) co
return true;
}
bool wxWebViewWebKit::AddScriptMessageHandler(const wxString& name)
{
if (!m_web_view)
return false;
WebKitUserContentManager *ucm = webkit_web_view_get_user_content_manager(m_web_view);
g_signal_connect(ucm, wxString::Format("script-message-received::%s", name).utf8_str(),
G_CALLBACK(wxgtk_webview_webkit_script_message_received), this);
bool res = webkit_user_content_manager_register_script_message_handler(ucm, name.utf8_str());
if (res)
{
// Make webkit message handler available under common name
wxString js = wxString::Format("window.%s = window.webkit.messageHandlers.%s;",
name, name);
AddUserScript(js);
RunScript(js);
}
return res;
}
bool wxWebViewWebKit::RemoveScriptMessageHandler(const wxString& name)
{
WebKitUserContentManager *ucm = webkit_web_view_get_user_content_manager(m_web_view);
webkit_user_content_manager_unregister_script_message_handler(ucm, name.utf8_str());
return true;
}
bool wxWebViewWebKit::AddUserScript(const wxString& javascript,
wxWebViewUserScriptInjectionTime injectionTime)
{
WebKitUserScript* userScript = webkit_user_script_new(
javascript.utf8_str(),
WEBKIT_USER_CONTENT_INJECT_ALL_FRAMES,
(injectionTime == wxWEBVIEW_INJECT_AT_DOCUMENT_START) ?
WEBKIT_USER_SCRIPT_INJECT_AT_DOCUMENT_START : WEBKIT_USER_SCRIPT_INJECT_AT_DOCUMENT_END,
NULL, NULL
);
WebKitUserContentManager *ucm = webkit_web_view_get_user_content_manager(m_web_view);
webkit_user_content_manager_add_script(ucm, userScript);
webkit_user_script_unref(userScript);
return true;
}
void wxWebViewWebKit::RemoveAllUserScripts()
{
WebKitUserContentManager *ucm = webkit_web_view_get_user_content_manager(m_web_view);
webkit_user_content_manager_remove_all_scripts(ucm);
}
void wxWebViewWebKit::RegisterHandler(wxSharedPtr<wxWebViewHandler> handler)
{
m_handlerList.push_back(handler);

View File

@@ -75,6 +75,7 @@ wxWebViewEdgeImpl::~wxWebViewEdgeImpl()
m_webView->remove_DocumentTitleChanged(m_documentTitleChangedToken);
m_webView->remove_ContentLoading(m_contentLoadingToken);
m_webView->remove_ContainsFullScreenElementChanged(m_containsFullScreenElementChangedToken);
m_webView->remove_WebMessageReceived(m_webMessageReceivedToken);
}
}
@@ -83,7 +84,7 @@ bool wxWebViewEdgeImpl::Create()
m_initialized = false;
m_isBusy = false;
m_pendingContextMenuEnabled = -1;
m_pendingAccessToDevToolsEnabled = -1;
m_pendingAccessToDevToolsEnabled = 0;
m_historyLoadingFromList = false;
m_historyEnabled = true;
@@ -320,6 +321,37 @@ HRESULT wxWebViewEdgeImpl::OnContainsFullScreenElementChanged(ICoreWebView2* WXU
return S_OK;
}
HRESULT
wxWebViewEdgeImpl::OnWebMessageReceived(ICoreWebView2* WXUNUSED(sender),
ICoreWebView2WebMessageReceivedEventArgs* args)
{
wxCoTaskMemPtr<wchar_t> msgContent;
HRESULT hr = args->get_WebMessageAsJson(&msgContent);
if (FAILED(hr))
{
wxLogApiError("get_WebMessageAsJson", hr);
return hr;
}
wxWebViewEvent event(wxEVT_WEBVIEW_SCRIPT_MESSAGE_RECEIVED, m_ctrl->GetId(),
m_ctrl->GetCurrentURL(), wxString(),
wxWEBVIEW_NAV_ACTION_NONE, m_scriptMsgHandlerName);
event.SetEventObject(m_ctrl);
// Try to decode JSON string or return original
// result if it's not a valid JSON string
wxString msgStr;
wxString msgJson(msgContent);
if (!wxJSON::DecodeString(msgJson, &msgStr))
msgStr = msgJson;
event.SetString(msgStr);
m_ctrl->HandleWindowEvent(event);
return S_OK;
}
HRESULT wxWebViewEdgeImpl::OnWebViewCreated(HRESULT result, ICoreWebView2Controller* webViewController)
{
if (FAILED(result))
@@ -369,6 +401,10 @@ HRESULT wxWebViewEdgeImpl::OnWebViewCreated(HRESULT result, ICoreWebView2Control
Callback<ICoreWebView2ContainsFullScreenElementChangedEventHandler>(
this, &wxWebViewEdgeImpl::OnContainsFullScreenElementChanged).Get(),
&m_containsFullScreenElementChangedToken);
m_webView->add_WebMessageReceived(
Callback<ICoreWebView2WebMessageReceivedEventHandler>(
this, &wxWebViewEdgeImpl::OnWebMessageReceived).Get(),
&m_webMessageReceivedToken);
if (m_pendingContextMenuEnabled != -1)
{
@@ -384,7 +420,18 @@ HRESULT wxWebViewEdgeImpl::OnWebViewCreated(HRESULT result, ICoreWebView2Control
wxCOMPtr<ICoreWebView2Settings> settings(GetSettings());
if (settings)
{
settings->put_IsStatusBarEnabled(false);
}
UpdateWebMessageHandler();
if (!m_pendingUserScripts.empty())
{
for (wxVector<wxString>::iterator it = m_pendingUserScripts.begin();
it != m_pendingUserScripts.end(); ++it)
m_ctrl->AddUserScript(*it);
m_pendingUserScripts.clear();
}
if (!m_pendingURL.empty())
{
@@ -395,6 +442,24 @@ HRESULT wxWebViewEdgeImpl::OnWebViewCreated(HRESULT result, ICoreWebView2Control
return S_OK;
}
void wxWebViewEdgeImpl::UpdateWebMessageHandler()
{
wxCOMPtr<ICoreWebView2Settings> settings(GetSettings());
if (!settings)
return;
settings->put_IsWebMessageEnabled(!m_scriptMsgHandlerName.empty());
if (!m_scriptMsgHandlerName.empty())
{
// Make edge message handler available under common name
wxString js = wxString::Format("window.%s = window.chrome.webview;",
m_scriptMsgHandlerName);
m_ctrl->AddUserScript(js);
m_webView->ExecuteScript(js.wc_str(), NULL);
}
}
ICoreWebView2Settings* wxWebViewEdgeImpl::GetSettings()
{
if (!m_webView)
@@ -774,6 +839,65 @@ bool wxWebViewEdge::RunScript(const wxString& javascript, wxString* output) cons
return true;
}
bool wxWebViewEdge::AddScriptMessageHandler(const wxString& name)
{
// Edge only supports a single message handler
if (!m_impl->m_scriptMsgHandlerName.empty())
return false;
m_impl->m_scriptMsgHandlerName = name;
m_impl->UpdateWebMessageHandler();
return true;
}
bool wxWebViewEdge::RemoveScriptMessageHandler(const wxString& WXUNUSED(name))
{
m_impl->m_scriptMsgHandlerName.clear();
m_impl->UpdateWebMessageHandler();
return true;
}
HRESULT wxWebViewEdgeImpl::OnAddScriptToExecuteOnDocumentedCreatedCompleted(HRESULT errorCode, LPCWSTR id)
{
if (SUCCEEDED(errorCode))
m_userScriptIds.push_back(id);
return S_OK;
}
bool wxWebViewEdge::AddUserScript(const wxString& javascript,
wxWebViewUserScriptInjectionTime injectionTime)
{
// Currently only AT_DOCUMENT_START is supported
if (injectionTime != wxWEBVIEW_INJECT_AT_DOCUMENT_START)
return false;
if (m_impl->m_webView)
{
HRESULT hr = m_impl->m_webView->AddScriptToExecuteOnDocumentCreated(javascript.wc_str(),
Callback<ICoreWebView2AddScriptToExecuteOnDocumentCreatedCompletedHandler>(m_impl,
&wxWebViewEdgeImpl::OnAddScriptToExecuteOnDocumentedCreatedCompleted).Get());
if (FAILED(hr))
return false;
}
else
m_impl->m_pendingUserScripts.push_back(javascript);
return true;
}
void wxWebViewEdge::RemoveAllUserScripts()
{
m_impl->m_pendingUserScripts.clear();
for (auto& scriptId : m_impl->m_userScriptIds)
{
HRESULT hr = m_impl->m_webView->RemoveScriptToExecuteOnDocumentCreated(scriptId.wc_str());
if (FAILED(hr))
wxLogApiError("RemoveScriptToExecuteOnDocumentCreated", hr);
}
m_impl->m_userScriptIds.clear();
}
void wxWebViewEdge::RegisterHandler(wxSharedPtr<wxWebViewHandler> WXUNUSED(handler))
{
// TODO: could maybe be implemented via IWebView2WebView5::add_WebResourceRequested

View File

@@ -87,6 +87,15 @@ wxEND_EVENT_TABLE()
@end
#endif // macOS 10.13+
@interface WebViewScriptMessageHandler: NSObject<WKScriptMessageHandler>
{
wxWebViewWebKit* webKitWindow;
}
- (id)initWithWxWindow: (wxWebViewWebKit*)inWindow;
@end
//-----------------------------------------------------------------------------
// wxWebViewFactoryWebKit
//-----------------------------------------------------------------------------
@@ -385,6 +394,41 @@ bool wxWebViewWebKit::RunScript(const wxString& javascript, wxString* output) co
return true;
}
bool wxWebViewWebKit::AddScriptMessageHandler(const wxString& name)
{
[m_webView.configuration.userContentController addScriptMessageHandler:
[[WebViewScriptMessageHandler alloc] initWithWxWindow:this] name:wxCFStringRef(name).AsNSString()];
// Make webkit message handler available under common name
wxString js = wxString::Format("window.%s = window.webkit.messageHandlers.%s;",
name, name);
AddUserScript(js);
RunScript(js);
return true;
}
bool wxWebViewWebKit::RemoveScriptMessageHandler(const wxString& name)
{
[m_webView.configuration.userContentController removeScriptMessageHandlerForName:wxCFStringRef(name).AsNSString()];
return true;
}
bool wxWebViewWebKit::AddUserScript(const wxString& javascript,
wxWebViewUserScriptInjectionTime injectionTime)
{
WKUserScript* userScript =
[[WKUserScript alloc] initWithSource:wxCFStringRef(javascript).AsNSString()
injectionTime:(injectionTime == wxWEBVIEW_INJECT_AT_DOCUMENT_START) ?
WKUserScriptInjectionTimeAtDocumentStart : WKUserScriptInjectionTimeAtDocumentEnd
forMainFrameOnly:NO];
[m_webView.configuration.userContentController addUserScript:userScript];
return true;
}
void wxWebViewWebKit::RemoveAllUserScripts()
{
[m_webView.configuration.userContentController removeAllUserScripts];
}
void wxWebViewWebKit::LoadURL(const wxString& url)
{
[m_webView loadRequest:[NSURLRequest requestWithURL:
@@ -891,4 +935,45 @@ WX_API_AVAILABLE_MACOS(10, 12)
@end
@implementation WebViewScriptMessageHandler
- (id)initWithWxWindow: (wxWebViewWebKit*)inWindow
{
if (self = [super init])
{
webKitWindow = inWindow; // non retained
}
return self;
}
- (void)userContentController:(nonnull WKUserContentController *)userContentController
didReceiveScriptMessage:(nonnull WKScriptMessage *)message
{
wxWebViewEvent event(wxEVT_WEBVIEW_SCRIPT_MESSAGE_RECEIVED,
webKitWindow->GetId(),
webKitWindow->GetCurrentURL(),
"",
wxWEBVIEW_NAV_ACTION_NONE,
wxCFStringRef::AsString(message.name));
if ([message.body isKindOfClass:NSString.class])
event.SetString(wxCFStringRef::AsString(message.body));
else if ([message.body isKindOfClass:NSNumber.class])
event.SetString(wxCFStringRef::AsString(((NSNumber*)message.body).stringValue));
else if ([message.body isKindOfClass:NSDate.class])
event.SetString(wxCFStringRef::AsString(((NSDate*)message.body).description));
else if ([message.body isKindOfClass:NSNull.class])
event.SetString("null");
else if ([message.body isKindOfClass:NSDictionary.class] || [message.body isKindOfClass:NSArray.class])
{
NSError* error = nil;
NSData* jsonData = [NSJSONSerialization dataWithJSONObject:message.body options:0 error:&error];
NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
event.SetString(wxCFStringRef::AsString(jsonString));
}
webKitWindow->ProcessWindowEvent(event);
}
@end
#endif //wxUSE_WEBVIEW && wxUSE_WEBVIEW_WEBKIT