From 97e469c25542b05059bfe159ab1e66d7711f28ea Mon Sep 17 00:00:00 2001 From: Tobias Taschner Date: Tue, 16 Mar 2021 14:18:52 +0100 Subject: [PATCH 1/5] Add access to user agent for wxWebView Allow setting a custom user agent for a webview. Also allow access to the current user agent. --- include/wx/webview.h | 2 ++ interface/wx/webview.h | 22 ++++++++++++++++++++++ samples/webview/webview.cpp | 29 ++++++++++++++++++++++++++--- src/common/webview.cpp | 7 +++++++ 4 files changed, 57 insertions(+), 3 deletions(-) diff --git a/include/wx/webview.h b/include/wx/webview.h index c300f72195..43a3bf39c1 100644 --- a/include/wx/webview.h +++ b/include/wx/webview.h @@ -187,6 +187,8 @@ public: virtual void Print() = 0; virtual void RegisterHandler(wxSharedPtr handler) = 0; virtual void Reload(wxWebViewReloadFlags flags = wxWEBVIEW_RELOAD_DEFAULT) = 0; + virtual bool SetUserAgent(const wxString& userAgent) { wxUnusedVar(userAgent); return false; } + virtual wxString GetUserAgent() const; // Script virtual bool RunScript(const wxString& javascript, wxString* output = NULL) const = 0; diff --git a/interface/wx/webview.h b/interface/wx/webview.h index d23493ae2d..1b59eb6e57 100644 --- a/interface/wx/webview.h +++ b/interface/wx/webview.h @@ -889,6 +889,28 @@ public: */ virtual bool IsAccessToDevToolsEnabled() const; + /** + Specify a custom user agent string for the web view. + Returns @true the user agent could be set. + + If your first request should already use the custom user agent + please use two step creation and call SetUserAgent() before Create(). + + @note This is not implemented for IE. For Edge SetUserAgent() + MUST be called before Create(). + + @since 3.1.5 + */ + virtual bool SetUserAgent(const wxString& userAgent); + + /** + Returns the current user agent string for the web view. + + @since 3.1.5 + */ + virtual wxString GetUserAgent() const; + + /** @name History */ diff --git a/samples/webview/webview.cpp b/samples/webview/webview.cpp index 3bd20ac462..c8d585a2de 100644 --- a/samples/webview/webview.cpp +++ b/samples/webview/webview.cpp @@ -161,6 +161,7 @@ public: void OnRunScriptMessage(wxCommandEvent& evt); void OnRunScriptCustom(wxCommandEvent& evt); void OnAddUserScript(wxCommandEvent& evt); + void OnSetCustomUserAgent(wxCommandEvent& evt); void OnClearSelection(wxCommandEvent& evt); void OnDeleteSelection(wxCommandEvent& evt); void OnSelectAll(wxCommandEvent& evt); @@ -393,9 +394,6 @@ WebFrame::WebFrame(const wxString& url) : #endif // Create the webview m_browser = wxWebView::New(); - // Log backend information - wxLogMessage("Backend: %s Version: %s", m_browser->GetClassInfo()->GetClassName(), - wxWebView::GetBackendVersionInfo().ToString()); #ifdef __WXMAC__ // With WKWebView handlers need to be registered before creation m_browser->RegisterHandler(wxSharedPtr(new wxWebViewArchiveHandler("wxfs"))); @@ -404,6 +402,11 @@ WebFrame::WebFrame(const wxString& url) : m_browser->Create(this, wxID_ANY, url, wxDefaultPosition, wxDefaultSize); topsizer->Add(m_browser, wxSizerFlags().Expand().Proportion(1)); + // Log backend information + wxLogMessage("Backend: %s Version: %s", m_browser->GetClassInfo()->GetClassName(), + wxWebView::GetBackendVersionInfo().ToString()); + wxLogMessage("User Agent: %s", m_browser->GetUserAgent()); + #ifndef __WXMAC__ //We register the wxfs:// protocol for testing purposes m_browser->RegisterHandler(wxSharedPtr(new wxWebViewArchiveHandler("wxfs"))); @@ -493,6 +496,7 @@ WebFrame::WebFrame(const wxString& url) : 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")); + wxMenuItem* setCustomUserAgent = m_tools_menu->Append(wxID_ANY, _("Set custom user agent")); //Selection menu wxMenu* selection = new wxMenu(); @@ -593,6 +597,7 @@ WebFrame::WebFrame(const wxString& url) : 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::OnSetCustomUserAgent, this, setCustomUserAgent->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()); @@ -1230,6 +1235,24 @@ void WebFrame::OnAddUserScript(wxCommandEvent & WXUNUSED(evt)) wxLogError("Could not add user script"); } +void WebFrame::OnSetCustomUserAgent(wxCommandEvent& WXUNUSED(evt)) +{ + wxString customUserAgent = "Mozilla/5.0 (iPhone; CPU iPhone OS 13_1_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.1 Mobile/15E148 Safari/604.1"; + wxTextEntryDialog dialog + ( + this, + "Enter the custom user agent string you would like to use.", + wxGetTextFromUserPromptStr, + customUserAgent, + wxOK | wxCANCEL | wxCENTRE + ); + if (dialog.ShowModal() != wxID_OK) + return; + + if (!m_browser->SetUserAgent(customUserAgent)) + wxLogError("Could not set custom user agent"); +} + void WebFrame::OnClearSelection(wxCommandEvent& WXUNUSED(evt)) { m_browser->ClearSelection(); diff --git a/src/common/webview.cpp b/src/common/webview.cpp index cc845a2b5e..dacd00653a 100644 --- a/src/common/webview.cpp +++ b/src/common/webview.cpp @@ -217,6 +217,13 @@ long wxWebView::Find(const wxString& text, int flags) return 1; } +wxString wxWebView::GetUserAgent() const +{ + wxString userAgent; + RunScript("navigator.userAgent", &userAgent); + return userAgent; +} + // static wxWebView* wxWebView::New(const wxString& backend) { From 18f5b01cc684d6af73c7783d28074d809701a67a Mon Sep 17 00:00:00 2001 From: Tobias Taschner Date: Tue, 16 Mar 2021 14:21:09 +0100 Subject: [PATCH 2/5] Implement user agent access for macOS --- include/wx/osx/webview_webkit.h | 2 ++ src/osx/webview_webkit.mm | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/include/wx/osx/webview_webkit.h b/include/wx/osx/webview_webkit.h index 2166966cc5..443d9b22d1 100644 --- a/include/wx/osx/webview_webkit.h +++ b/include/wx/osx/webview_webkit.h @@ -73,6 +73,7 @@ public: virtual bool IsAccessToDevToolsEnabled() const wxOVERRIDE; virtual void EnableAccessToDevTools(bool enable = true) wxOVERRIDE; + virtual bool SetUserAgent(const wxString& userAgent) wxOVERRIDE; //History functions virtual void ClearHistory() wxOVERRIDE; @@ -111,6 +112,7 @@ protected: private: OSXWebViewPtr m_webView; wxStringToWebHandlerMap m_handlers; + wxString m_customUserAgent; WX_NSObject m_navigationDelegate; WX_NSObject m_UIDelegate; diff --git a/src/osx/webview_webkit.mm b/src/osx/webview_webkit.mm index 3f304310bf..0513df3735 100644 --- a/src/osx/webview_webkit.mm +++ b/src/osx/webview_webkit.mm @@ -151,6 +151,9 @@ bool wxWebViewWebKit::Create(wxWindow *parent, MacPostControlCreate(pos, size); + if (!m_customUserAgent.empty()) + SetUserAgent(m_customUserAgent); + [m_webView setHidden:false]; @@ -328,6 +331,21 @@ void wxWebViewWebKit::EnableAccessToDevTools(bool enable) [prefs performSelector:devToolsSelector withObject:(id)enable]; } +bool wxWebViewWebKit::SetUserAgent(const wxString& userAgent) +{ + if (WX_IS_MACOS_AVAILABLE(10, 11)) + { + if (m_webView) + m_webView.customUserAgent = wxCFStringRef(userAgent).AsNSString(); + else + m_customUserAgent = userAgent; + + return true; + } + else + return false; +} + void wxWebViewWebKit::SetZoomType(wxWebViewZoomType zoomType) { // there is only one supported zoom type at the moment so this setter From 7f7191066bc2d2b75e5b4e0c336d12589ac920d0 Mon Sep 17 00:00:00 2001 From: Tobias Taschner Date: Tue, 16 Mar 2021 15:37:37 +0100 Subject: [PATCH 3/5] Implement user agent access for GTK --- include/wx/gtk/webview_webkit.h | 2 ++ src/gtk/webview_webkit2.cpp | 15 +++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/include/wx/gtk/webview_webkit.h b/include/wx/gtk/webview_webkit.h index 0b9a1939f8..75a22e5343 100644 --- a/include/wx/gtk/webview_webkit.h +++ b/include/wx/gtk/webview_webkit.h @@ -81,6 +81,7 @@ public: #if wxUSE_WEBVIEW_WEBKIT2 virtual void EnableAccessToDevTools(bool enable = true) wxOVERRIDE; virtual bool IsAccessToDevToolsEnabled() const wxOVERRIDE; + virtual bool SetUserAgent(const wxString& userAgent) wxOVERRIDE; #endif void SetZoomType(wxWebViewZoomType) wxOVERRIDE; @@ -176,6 +177,7 @@ private: #endif WebKitWebView *m_web_view; + wxString m_customUserAgent; int m_historyLimit; wxVector > m_handlerList; diff --git a/src/gtk/webview_webkit2.cpp b/src/gtk/webview_webkit2.cpp index 4ae910e5af..2bddbe8de8 100644 --- a/src/gtk/webview_webkit2.cpp +++ b/src/gtk/webview_webkit2.cpp @@ -651,6 +651,9 @@ bool wxWebViewWebKit::Create(wxWindow *parent, GTKCreateScrolledWindowWith(GTK_WIDGET(m_web_view)); g_object_ref(m_widget); + if (!m_customUserAgent.empty()) + SetUserAgent(m_customUserAgent); + g_signal_connect(m_web_view, "decide-policy", G_CALLBACK(wxgtk_webview_webkit_decide_policy), this); @@ -756,6 +759,18 @@ bool wxWebViewWebKit::IsAccessToDevToolsEnabled() const return webkit_settings_get_enable_developer_extras(settings); } +bool wxWebViewWebKit::SetUserAgent(const wxString& userAgent) +{ + if (m_web_view) + { + WebKitSettings* settings = webkit_web_view_get_settings(m_web_view); + webkit_settings_set_user_agent(settings, userAgent.utf8_str()); + } + else + m_customUserAgent = userAgent; + return true; +} + void wxWebViewWebKit::Stop() { webkit_web_view_stop_loading(m_web_view); From 3d568c7105252e93ddd27f6e8bacf4c857d2ce8d Mon Sep 17 00:00:00 2001 From: Tobias Taschner Date: Tue, 16 Mar 2021 22:09:55 +0100 Subject: [PATCH 4/5] Create wxWebViewEdge implementation in constructor This helps with various potential error when using two step creation --- include/wx/msw/webview_edge.h | 7 ++----- src/msw/webview_edge.cpp | 21 ++++++++++++++++++++- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/include/wx/msw/webview_edge.h b/include/wx/msw/webview_edge.h index 6f233ec598..bb58d97b50 100644 --- a/include/wx/msw/webview_edge.h +++ b/include/wx/msw/webview_edge.h @@ -23,7 +23,7 @@ class WXDLLIMPEXP_WEBVIEW wxWebViewEdge : public wxWebView { public: - wxWebViewEdge() {} + wxWebViewEdge(); wxWebViewEdge(wxWindow* parent, wxWindowID id, @@ -31,10 +31,7 @@ public: const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = 0, - const wxString& name = wxWebViewNameStr) - { - Create(parent, id, url, pos, size, style, name); - } + const wxString& name = wxWebViewNameStr); ~wxWebViewEdge(); diff --git a/src/msw/webview_edge.cpp b/src/msw/webview_edge.cpp index 0da43c229d..6ab2b9d1a8 100644 --- a/src/msw/webview_edge.cpp +++ b/src/msw/webview_edge.cpp @@ -476,6 +476,24 @@ ICoreWebView2Settings* wxWebViewEdgeImpl::GetSettings() return settings; } +wxWebViewEdge::wxWebViewEdge(): + m_impl(new wxWebViewEdgeImpl(this)) +{ + +} + +wxWebViewEdge::wxWebViewEdge(wxWindow* parent, + wxWindowID id, + const wxString& url, + const wxPoint& pos, + const wxSize& size, + long style, + const wxString& name): + m_impl(new wxWebViewEdgeImpl(this)) +{ + Create(parent, id, url, pos, size, style, name); +} + wxWebViewEdge::~wxWebViewEdge() { wxWindow* topLevelParent = wxGetTopLevelParent(this); @@ -501,7 +519,6 @@ bool wxWebViewEdge::Create(wxWindow* parent, return false; } - m_impl = new wxWebViewEdgeImpl(this); if (!m_impl->Create()) return false; Bind(wxEVT_SIZE, &wxWebViewEdge::OnSize, this); @@ -774,6 +791,8 @@ void wxWebViewEdge::MSWSetBrowserExecutableDir(const wxString & path) bool wxWebViewEdge::RunScriptSync(const wxString& javascript, wxString* output) const { bool scriptExecuted = false; + if (!m_impl->m_webView) + return false; // Start script execution HRESULT executionResult = m_impl->m_webView->ExecuteScript(javascript.wc_str(), Callback( From 2487a48b6606922b7e9a2226ee47b1613ccc75c1 Mon Sep 17 00:00:00 2001 From: Tobias Taschner Date: Tue, 16 Mar 2021 22:16:33 +0100 Subject: [PATCH 5/5] Implement user agent access for Edge --- include/wx/msw/private/webview_edge.h | 1 + include/wx/msw/webview_edge.h | 2 ++ src/msw/webview_edge.cpp | 27 +++++++++++++++++++++++++++ 3 files changed, 30 insertions(+) diff --git a/include/wx/msw/private/webview_edge.h b/include/wx/msw/private/webview_edge.h index f9bdb76917..e771848fdc 100644 --- a/include/wx/msw/private/webview_edge.h +++ b/include/wx/msw/private/webview_edge.h @@ -58,6 +58,7 @@ public: wxVector m_pendingUserScripts; wxVector m_userScriptIds; wxString m_scriptMsgHandlerName; + wxString m_customUserAgent; // WebView Events tokens EventRegistrationToken m_navigationStartingToken = { }; diff --git a/include/wx/msw/webview_edge.h b/include/wx/msw/webview_edge.h index bb58d97b50..c273697a29 100644 --- a/include/wx/msw/webview_edge.h +++ b/include/wx/msw/webview_edge.h @@ -86,6 +86,8 @@ public: virtual void EnableAccessToDevTools(bool enable = true) wxOVERRIDE; virtual bool IsAccessToDevToolsEnabled() const wxOVERRIDE; + virtual bool SetUserAgent(const wxString& userAgent) 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; diff --git a/src/msw/webview_edge.cpp b/src/msw/webview_edge.cpp index 6ab2b9d1a8..3abb6382eb 100644 --- a/src/msw/webview_edge.cpp +++ b/src/msw/webview_edge.cpp @@ -29,6 +29,7 @@ #ifdef __VISUALC__ #include using namespace Microsoft::WRL; +#include "WebView2EnvironmentOptions.h" #else #include #endif // !__VISUALC__ @@ -91,11 +92,23 @@ bool wxWebViewEdgeImpl::Create() m_historyPosition = -1; wxString userDataPath = wxStandardPaths::Get().GetUserLocalDataDir(); +#ifdef __VISUALC__ + auto options = + Make(); + + if (!m_customUserAgent.empty()) + options->put_AdditionalBrowserArguments( + wxString::Format("--user-agent=\"%s\"", m_customUserAgent).wc_str()); +#endif HRESULT hr = wxCreateCoreWebView2EnvironmentWithOptions( ms_browserExecutableDir.wc_str(), userDataPath.wc_str(), +#ifdef __VISUALC__ + options.Get(), +#else nullptr, +#endif Callback(this, &wxWebViewEdgeImpl::OnEnvironmentCreated).Get()); if (FAILED(hr)) @@ -778,6 +791,20 @@ bool wxWebViewEdge::IsAccessToDevToolsEnabled() const return true; } +bool wxWebViewEdge::SetUserAgent(const wxString& userAgent) +{ + m_impl->m_customUserAgent = userAgent; + // Can currently only be set before Create() + wxCHECK_MSG(!m_impl->m_webViewController, false, "Can't be called after Create()"); + if (m_impl->m_webViewController) + return false; + else + return true; + + // TODO: As of Edge SDK 1.0.790 an experimental API to set the user agent + // is available. Reimplement using m_impl->GetSettings() when it's stable. +} + void* wxWebViewEdge::GetNativeBackend() const { return m_impl->m_webView;