diff --git a/include/wx/msw/private/webview_edge.h b/include/wx/msw/private/webview_edge.h index ec497f0e77..49b91f7c44 100644 --- a/include/wx/msw/private/webview_edge.h +++ b/include/wx/msw/private/webview_edge.h @@ -53,6 +53,7 @@ public: wxCOMPtr m_webViewEnvironment; wxCOMPtr m_webView; wxCOMPtr m_webViewController; + wxCOMPtr m_webViewEnvironmentOptions; bool m_initialized; bool m_isBusy; diff --git a/include/wx/msw/webview_edge.h b/include/wx/msw/webview_edge.h index d84b74d927..a52f0a989e 100644 --- a/include/wx/msw/webview_edge.h +++ b/include/wx/msw/webview_edge.h @@ -98,6 +98,7 @@ public: virtual void RegisterHandler(wxSharedPtr handler) wxOVERRIDE; virtual void* GetNativeBackend() const wxOVERRIDE; + virtual void* GetNativeConfiguration() const wxOVERRIDE; static void MSWSetBrowserExecutableDir(const wxString& path); diff --git a/include/wx/osx/webview_webkit.h b/include/wx/osx/webview_webkit.h index 420b8f6452..7ea1f0d688 100644 --- a/include/wx/osx/webview_webkit.h +++ b/include/wx/osx/webview_webkit.h @@ -32,7 +32,7 @@ class WXDLLIMPEXP_WEBVIEW wxWebViewWebKit : public wxWebView public: wxDECLARE_DYNAMIC_CLASS(wxWebViewWebKit); - wxWebViewWebKit() {} + wxWebViewWebKit() { Init(); } wxWebViewWebKit(wxWindow *parent, wxWindowID winID = wxID_ANY, const wxString& strURL = wxASCII_STR(wxWebViewDefaultURLStr), @@ -40,6 +40,7 @@ public: const wxSize& size = wxDefaultSize, long style = 0, const wxString& name = wxASCII_STR(wxWebViewNameStr)) { + Init(); Create(parent, winID, strURL, pos, size, style, name); } bool Create(wxWindow *parent, @@ -105,6 +106,7 @@ public: virtual void RegisterHandler(wxSharedPtr handler) wxOVERRIDE; virtual void* GetNativeBackend() const wxOVERRIDE { return m_webView; } + virtual void* GetNativeConfiguration() const wxOVERRIDE { return m_webViewConfiguration; } protected: virtual void DoSetPage(const wxString& html, const wxString& baseUrl) wxOVERRIDE; @@ -112,12 +114,15 @@ protected: wxDECLARE_EVENT_TABLE(); private: + WX_NSObject m_webViewConfiguration; OSXWebViewPtr m_webView; wxStringToWebHandlerMap m_handlers; wxString m_customUserAgent; WX_NSObject m_navigationDelegate; WX_NSObject m_UIDelegate; + + void Init(); }; class WXDLLIMPEXP_WEBVIEW wxWebViewFactoryWebKit : public wxWebViewFactory diff --git a/include/wx/private/jsscriptwrapper.h b/include/wx/private/jsscriptwrapper.h index a69b286b48..6e9bb1844b 100644 --- a/include/wx/private/jsscriptwrapper.h +++ b/include/wx/private/jsscriptwrapper.h @@ -33,38 +33,42 @@ public: }; wxJSScriptWrapper(const wxString& js, OutputType outputType) - : m_escapedCode(js), m_outputType(outputType) + : m_outputType(outputType) { // Adds one escape level. const char *charsNeededToBeEscaped = "\\\"\n\r\v\t\b\f"; - for ( - size_t pos = m_escapedCode.find_first_of(charsNeededToBeEscaped, 0); - pos != wxString::npos; - pos = m_escapedCode.find_first_of(charsNeededToBeEscaped, pos) - ) { - switch (m_escapedCode[pos].GetValue()) + m_escapedCode.reserve(js.size()); + for (wxString::const_iterator it = js.begin(); it != js.end(); ++it) + { + if (wxStrchr(charsNeededToBeEscaped, *it)) { - case 0x0A: // '\n' - m_escapedCode[pos] = 'n'; - break; - case 0x0D: // '\r' - m_escapedCode[pos] = 'r'; - break; - case 0x0B: // '\v' - m_escapedCode[pos] = 'v'; - break; - case 0x09: // '\t' - m_escapedCode[pos] = 't'; - break; - case 0x08: // '\b' - m_escapedCode[pos] = 'b'; - break; - case 0x0C: // '\f' - m_escapedCode[pos] = 'f'; - break; + m_escapedCode += '\\'; + switch ((wxChar) *it) + { + case 0x0A: // '\n' + m_escapedCode += 'n'; + break; + case 0x0D: // '\r' + m_escapedCode += 'r'; + break; + case 0x0B: // '\v' + m_escapedCode += 'v'; + break; + case 0x09: // '\t' + m_escapedCode += 't'; + break; + case 0x08: // '\b' + m_escapedCode += 'b'; + break; + case 0x0C: // '\f' + m_escapedCode += 'f'; + break; + default: + m_escapedCode += *it; + } } - m_escapedCode.insert(pos, '\\'); - pos += 2; + else + m_escapedCode += *it; } } diff --git a/include/wx/webview.h b/include/wx/webview.h index e1af2b67ac..5ea5f03f74 100644 --- a/include/wx/webview.h +++ b/include/wx/webview.h @@ -259,6 +259,7 @@ public: //Get the pointer to the underlying native engine. virtual void* GetNativeBackend() const = 0; + virtual void* GetNativeConfiguration() const { return NULL; } //Find function virtual long Find(const wxString& text, int flags = wxWEBVIEW_FIND_DEFAULT); diff --git a/interface/wx/webview.h b/interface/wx/webview.h index e9f58cf055..7b2fb837bc 100644 --- a/interface/wx/webview.h +++ b/interface/wx/webview.h @@ -623,6 +623,48 @@ public: */ virtual void* GetNativeBackend() const = 0; + /** + Return the pointer to the native configuration used during creation of + this control. + + When using two-step creation this method can be used to customize + configuration options not available via GetNativeBackend() + after using Create(). + + The return value needs to be down-casted to the appropriate type + depending on the platform: under macOS, it's a + WKWebViewConfiguration + pointer, under Windows with Edge it's a pointer to + ICoreWebView2EnvironmentOptions. + With other backends/platforms it's not implemented. + + The following pseudo code shows how to use this method with two-step + creation to set no user action requirement to play video in a + web view: + @code + #if defined(__WXMSW__) + #include "webview2.h" + #elif defined(__WXOSX__) + #import "WebKit/WebKit.h" + #endif + + wxWebView* webView = wxWebView::New(); + #if defined(__WXMSW__) + ICoreWebView2EnvironmentOptions* webViewOptions = + (ICoreWebView2EnvironmentOptions*) webView->GetNativeConfiguration(); + webViewOptions->put_AdditionalBrowserArguments("--autoplay-policy=no-user-gesture-required"); + #elif defined(__WXOSX__) + WKWebViewConfiguration* webViewConfiguration = + (WKWebViewConfiguration*) webView->GetNativeConfiguration(); + webViewConfiguration.mediaTypesRequiringUserActionForPlayback = WKAudiovisualMediaTypeNone; + #endif + webView->Create(this, wxID_ANY, "https://www.wxwidgets.org"); + @endcode + + @since 3.3.0 + */ + virtual void* GetNativeConfiguration() const; + /** Get the HTML source code of the currently displayed document. @return The HTML source code, or an empty string if no page is currently @@ -711,6 +753,27 @@ public: */ virtual void Stop() = 0; + /** + 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 Scripting */ @@ -953,28 +1016,6 @@ 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/src/msw/webview_edge.cpp b/src/msw/webview_edge.cpp index 5302691d2c..0ed09366e8 100644 --- a/src/msw/webview_edge.cpp +++ b/src/msw/webview_edge.cpp @@ -69,7 +69,9 @@ wxString wxWebViewEdgeImpl::ms_browserExecutableDir; wxWebViewEdgeImpl::wxWebViewEdgeImpl(wxWebViewEdge* webview): m_ctrl(webview) { - +#ifdef __VISUALC__ + m_webViewEnvironmentOptions = Make().Get(); +#endif } wxWebViewEdgeImpl::~wxWebViewEdgeImpl() @@ -99,23 +101,15 @@ bool wxWebViewEdgeImpl::Create() m_historyPosition = -1; wxString userDataPath = wxStandardPaths::Get().GetUserLocalDataDir(); -#ifdef __VISUALC__ - auto options = - Make(); - if (!m_customUserAgent.empty()) - options->put_AdditionalBrowserArguments( + if (m_webViewEnvironmentOptions && !m_customUserAgent.empty()) + m_webViewEnvironmentOptions->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 + m_webViewEnvironmentOptions, Callback(this, &wxWebViewEdgeImpl::OnEnvironmentCreated).Get()); if (FAILED(hr)) @@ -844,6 +838,11 @@ void* wxWebViewEdge::GetNativeBackend() const return m_impl->m_webView; } +void* wxWebViewEdge::GetNativeConfiguration() const +{ + return m_impl->m_webViewEnvironmentOptions; +} + void wxWebViewEdge::MSWSetBrowserExecutableDir(const wxString & path) { wxWebViewEdgeImpl::ms_browserExecutableDir = path; diff --git a/src/osx/webview_webkit.mm b/src/osx/webview_webkit.mm index 64b67381ab..d5f78341c7 100644 --- a/src/osx/webview_webkit.mm +++ b/src/osx/webview_webkit.mm @@ -111,6 +111,11 @@ wxVersionInfo wxWebViewFactoryWebKit::GetVersionInfo() // creation/destruction // ---------------------------------------------------------------------------- +void wxWebViewWebKit::Init() +{ + m_webViewConfiguration = [[WKWebViewConfiguration alloc] init]; +} + bool wxWebViewWebKit::Create(wxWindow *parent, wxWindowID winID, const wxString& strURL, @@ -122,7 +127,7 @@ bool wxWebViewWebKit::Create(wxWindow *parent, wxControl::Create(parent, winID, pos, size, style, wxDefaultValidator, name); NSRect r = wxOSXGetFrameForControl( this, pos , size ) ; - WKWebViewConfiguration* webViewConfig = [[WKWebViewConfiguration alloc] init]; + WKWebViewConfiguration* webViewConfig = (WKWebViewConfiguration*) m_webViewConfiguration; if (!m_handlers.empty()) { @@ -146,6 +151,10 @@ bool wxWebViewWebKit::Create(wxWindow *parent, MacPostControlCreate(pos, size); + // WKWebView configuration is only used during creation + [m_webViewConfiguration release]; + m_webViewConfiguration = nil; + if (!m_customUserAgent.empty()) SetUserAgent(m_customUserAgent);