From 01ee3942f82459498d73e2064269c31385f8c8bf Mon Sep 17 00:00:00 2001 From: Tobias Taschner Date: Tue, 7 Mar 2023 21:08:08 +0100 Subject: [PATCH] Add wxWebView child window handling Add new class wxWebViewWindowInfo: This new class returned by wxWebViewEvent::GetTargetWindowInfo() allows to get information about the window that is about to be opened and to set the window that will be used to display the content. --- include/wx/msw/private/webview_edge.h | 5 + include/wx/msw/webview_edge.h | 2 + include/wx/osx/webview_webkit.h | 9 +- include/wx/webview.h | 22 ++++ interface/wx/webview.h | 102 ++++++++++++++- samples/webview/webview.cpp | 85 ++++++++---- src/msw/webview_edge.cpp | 161 +++++++++++++++++++++-- src/osx/webview_webkit.mm | 178 ++++++++++++++++++++------ 8 files changed, 483 insertions(+), 81 deletions(-) diff --git a/include/wx/msw/private/webview_edge.h b/include/wx/msw/private/webview_edge.h index 202e6e7caf..b6a72a2339 100644 --- a/include/wx/msw/private/webview_edge.h +++ b/include/wx/msw/private/webview_edge.h @@ -45,6 +45,8 @@ __CRT_UUID_DECL(ICoreWebView2Settings3, 0xfdb5ab74, 0xaf33, 0x4854, 0x84,0xf0,0x WX_DECLARE_STRING_HASH_MAP(wxSharedPtr, wxStringToWebHandlerMap); +class wxWebViewEdgeParentWindowInfo; + class wxWebViewEdgeImpl { public: @@ -53,12 +55,15 @@ public: bool Create(); + wxWebViewEdge* CreateChildWebView(std::shared_ptr parentWindowInfo); + wxWebViewEdge* m_ctrl; wxCOMPtr m_webViewEnvironment; wxCOMPtr m_webView; wxCOMPtr m_webViewController; wxCOMPtr m_webViewEnvironmentOptions; + std::shared_ptr m_parentWindowInfo; bool m_initialized; bool m_isBusy; diff --git a/include/wx/msw/webview_edge.h b/include/wx/msw/webview_edge.h index 804332f810..310c48abcb 100644 --- a/include/wx/msw/webview_edge.h +++ b/include/wx/msw/webview_edge.h @@ -120,6 +120,8 @@ private: void OnTopLevelParentIconized(wxIconizeEvent& event); wxDECLARE_DYNAMIC_CLASS(wxWebViewEdge); + + friend class wxWebViewEdgeImpl; }; class WXDLLIMPEXP_WEBVIEW wxWebViewFactoryEdge : public wxWebViewFactory diff --git a/include/wx/osx/webview_webkit.h b/include/wx/osx/webview_webkit.h index 32580e03c4..2ed189fa77 100644 --- a/include/wx/osx/webview_webkit.h +++ b/include/wx/osx/webview_webkit.h @@ -27,12 +27,18 @@ WX_DECLARE_STRING_HASH_MAP(wxSharedPtr, wxStringToWebHandlerMap); +class wxWebViewWindowInfoWebKit; + class WXDLLIMPEXP_WEBVIEW wxWebViewWebKit : public wxWebView { public: wxDECLARE_DYNAMIC_CLASS(wxWebViewWebKit); - wxWebViewWebKit() { Init(); } + wxWebViewWebKit(wxWebViewWindowInfoWebKit* parentWindowInfo = nullptr) + { + m_parentWindowInfo = parentWindowInfo; + Init(); + } wxWebViewWebKit(wxWindow *parent, wxWindowID winID = wxID_ANY, const wxString& strURL = wxASCII_STR(wxWebViewDefaultURLStr), @@ -118,6 +124,7 @@ private: OSXWebViewPtr m_webView; wxStringToWebHandlerMap m_handlers; wxString m_customUserAgent; + wxWebViewWindowInfoWebKit* m_parentWindowInfo = nullptr; WX_NSObject m_navigationDelegate; WX_NSObject m_UIDelegate; diff --git a/include/wx/webview.h b/include/wx/webview.h index d77db04301..59877deab7 100644 --- a/include/wx/webview.h +++ b/include/wx/webview.h @@ -323,6 +323,27 @@ private: wxDECLARE_ABSTRACT_CLASS(wxWebView); }; +class WXDLLIMPEXP_WEBVIEW wxWebViewWindowInfo +{ +public: + virtual ~wxWebViewWindowInfo() = default; + + virtual wxPoint GetPosition() const = 0; + + virtual wxSize GetSize() const = 0; + + virtual bool ShouldDisplayMenuBar() const = 0; + + virtual bool ShouldDisplayStatusBar() const = 0; + + virtual bool ShouldDisplayToolBar() const = 0; + + virtual bool ShouldDisplayScrollBars() const = 0; + + virtual wxWebView* CreateChildWebView() = 0; +}; + + class WXDLLIMPEXP_WEBVIEW wxWebViewEvent : public wxNotifyEvent { public: @@ -343,6 +364,7 @@ public: wxWebViewNavigationActionFlags GetNavigationAction() const { return m_actionFlags; } const wxString& GetMessageHandler() const { return m_messageHandler; } + wxWebViewWindowInfo* GetTargetWindowInfo() const { return (wxWebViewWindowInfo*)m_clientData; } virtual wxEvent* Clone() const override { return new wxWebViewEvent(*this); } private: diff --git a/interface/wx/webview.h b/interface/wx/webview.h index ad17091885..bd9997046f 100644 --- a/interface/wx/webview.h +++ b/interface/wx/webview.h @@ -161,6 +161,91 @@ enum wxWebViewIE_EmulationLevel wxWEBVIEWIE_EMU_IE11_FORCE = 11001 }; +/** + A class describing the window information for a new child window. + This class is available via wxWebViewEvent::GetTargetWindowInfo() + while handling @c wxEVT_WEBVIEW_NEWWINDOW. + + If the new window should be created use CreateChildWebView() while + processing the event. This will ensure that the new web view is + accessible from java script within the originating wxWebView. + + + @since 3.3.0 +*/ +class WXDLLIMPEXP_WEBVIEW wxWebViewWindowInfo +{ +public: + /** + Returns the position of the new window if specified by + a @c window.open() call. + */ + virtual wxPoint GetPosition() const = 0; + + /** + Returns the size of the new window if specified by + a @c window.open() call. + */ + virtual wxSize GetSize() const = 0; + + /** + Returns @true if the target window is expected to display + a menu bar as specified by a @c window.open() call. + */ + virtual bool ShouldDisplayMenuBar() const = 0; + + /** + Returns @true if the target window is expected to display + a status bar as specified by a @c window.open() call. + */ + virtual bool ShouldDisplayStatusBar() const = 0; + + /** + Returns @true if the target window is expected to display + a tool bar as specified by a @c window.open() call. + */ + virtual bool ShouldDisplayToolBar() const = 0; + + /** + Returns @true if the target window is expected to display + scroll bars as specified by a @c window.open() call. + */ + virtual bool ShouldDisplayScrollBars() const = 0; + + /** + Create a new child web view for the target window. + + This @b must be created in the event handler for @c wxEVT_WEBVIEW_NEWWINDOW + and wxWebView::Create() @b must be called directly. + + The requested URL will be loaded automatically in the new child web view. + + Sample C++ code handling the event: + @code + // Bind handler + m_webView->Bind(wxEVT_WEBVIEW_NEWWINDOW, [](wxWebViewEvent& evt) { + // Get target window info + wxWebViewWindowInfo* info = evt.GetTargetWindowInfo(); + // Create a top level window for the child web view + wxWindow* win = new wxWindow(this, wxID_ANY, info->GetPosition(), info->GetSize()); + wxSizer* sizer = new wxBoxSizer(wxVERTICAL); + // Create the child web view + wxWebView* childWebView = info->CreateChildWebView(); + // Place the child web view into the window + childWebView->Create(win, wxID_ANY); + sizer->Add(childWebView, 1, wxEXPAND); + }); + @endcode + + Sample javascript opening a new window: + @code + window.open("http://www.wxwidgets.org", "newWindow", "width=400,height=400"); + @endcode + + */ + virtual wxWebView* CreateChildWebView() = 0; +}; + /** @class wxWebViewHandlerRequest @@ -707,7 +792,8 @@ public: @event{EVT_WEBVIEW_NEWWINDOW(id, func)} Process a @c wxEVT_WEBVIEW_NEWWINDOW event, generated when a new window is created. You must handle this event if you want anything to - happen, for example to load the page in a new window or tab. + happen, for example to load the page in a new window or tab. For usage + details see wxWebViewWindowInfo::CreateChildWebView(). @event{wxEVT_WEBVIEW_WINDOW_CLOSE_REQUESTED(id, func)} Process a @c wxEVT_WEBVIEW_WINDOW_CLOSE_REQUESTED event, generated when a window is requested to be closed. @@ -1603,7 +1689,8 @@ public: @event{EVT_WEBVIEW_NEWWINDOW(id, func)} Process a @c wxEVT_WEBVIEW_NEWWINDOW event, generated when a new window is created. You must handle this event if you want anything to - happen, for example to load the page in a new window or tab. + happen, for example to load the page in a new window or tab. For usage + details see wxWebViewWindowInfo::CreateChildWebView(). @event{wxEVT_WEBVIEW_WINDOW_CLOSE_REQUESTED(id, func)} Process a @c wxEVT_WEBVIEW_WINDOW_CLOSE_REQUESTED event, generated when a window is requested to be closed. @@ -1669,6 +1756,17 @@ public: */ const wxString& GetMessageHandler() const; + /** + Get information about the target window. Only valid for events of type + @c wxEVT_WEBVIEW_NEWWINDOW + + @note This is only available with the macOS and the Edge backend. + + @see wxWebViewWindowInfo + @since 3.3.0 + */ + wxWebViewWindowInfo* GetTargetWindowInfo() const; + /** Returns true the script execution failed. Only valid for events of type @c wxEVT_WEBVIEW_SCRIPT_RESULT diff --git a/samples/webview/webview.cpp b/samples/webview/webview.cpp index 9bd93a7c4b..d1605b5c7c 100644 --- a/samples/webview/webview.cpp +++ b/samples/webview/webview.cpp @@ -98,7 +98,7 @@ private: class WebFrame : public wxFrame { public: - WebFrame(const wxString& url); + WebFrame(const wxString& url, bool isMain = true, wxWebViewWindowInfo* newWindowInfo = nullptr); virtual ~WebFrame(); void UpdateState(); @@ -177,6 +177,7 @@ public: private: wxTextCtrl* m_url; wxWebView* m_browser; + bool m_isMainFrame; wxToolBar* m_toolbar; wxToolBarToolBase* m_toolbar_back; @@ -333,9 +334,11 @@ bool WebApp::OnInit() return true; } -WebFrame::WebFrame(const wxString& url) : +WebFrame::WebFrame(const wxString& url, bool isMain, wxWebViewWindowInfo* newWindowInfo) : wxFrame(nullptr, wxID_ANY, "wxWebView Sample") { + m_isMainFrame = isMain; + // set the frame icon SetIcon(wxICON(sample)); SetTitle("wxWebView Sample"); @@ -350,7 +353,7 @@ WebFrame::WebFrame(const wxString& url) : m_toolbar_forward = m_toolbar->AddTool(wxID_ANY, _("Forward"), wxArtProvider::GetBitmapBundle(wxART_GO_FORWARD, wxART_TOOLBAR)); m_toolbar_stop = m_toolbar->AddTool(wxID_ANY, _("Stop"), wxArtProvider::GetBitmapBundle(wxART_STOP, wxART_TOOLBAR)); m_toolbar_reload = m_toolbar->AddTool(wxID_ANY, _("Reload"), wxArtProvider::GetBitmapBundle(wxART_REFRESH, wxART_TOOLBAR)); - m_url = new wxTextCtrl(m_toolbar, wxID_ANY, "", wxDefaultPosition, FromDIP(wxSize(400, -1)), wxTE_PROCESS_ENTER ); + m_url = new wxTextCtrl(m_toolbar, wxID_ANY, "", wxDefaultPosition, FromDIP(wxSize(400, -1)), wxTE_PROCESS_ENTER); m_toolbar->AddControl(m_url, _("URL")); m_toolbar_tools = m_toolbar->AddTool(wxID_ANY, _("Menu"), wxArtProvider::GetBitmapBundle(wxART_WX_LOGO, wxART_TOOLBAR)); @@ -401,7 +404,8 @@ WebFrame::WebFrame(const wxString& url) : topsizer->Add(m_info, wxSizerFlags().Expand()); // Create a log window - new wxLogWindow(this, _("Logging"), true, false); + if (m_isMainFrame) + new wxLogWindow(this, _("Logging"), true, false); #if wxUSE_WEBVIEW_EDGE // Check if a fixed version of edge is present in @@ -416,30 +420,50 @@ WebFrame::WebFrame(const wxString& url) : } #endif // Create the webview - m_browser = wxWebView::New(); + m_browser = (newWindowInfo) ? newWindowInfo->CreateChildWebView() : wxWebView::New(); #ifdef __WXMAC__ - // With WKWebView handlers need to be registered before creation - m_browser->RegisterHandler(wxSharedPtr(new wxWebViewArchiveHandler("wxfs"))); - m_browser->RegisterHandler(wxSharedPtr(new wxWebViewFSHandler("memory"))); - m_browser->RegisterHandler(wxSharedPtr(new AdvancedWebViewHandler())); + if (m_isMainFrame) + { + // With WKWebView handlers need to be registered before creation + m_browser->RegisterHandler(wxSharedPtr(new wxWebViewArchiveHandler("wxfs"))); + m_browser->RegisterHandler(wxSharedPtr(new wxWebViewFSHandler("memory"))); + m_browser->RegisterHandler(wxSharedPtr(new AdvancedWebViewHandler())); + } #endif 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()); + if (newWindowInfo) + { + if (newWindowInfo->GetSize().IsFullySpecified()) + SetSize(FromDIP(newWindowInfo->GetSize())); + if (newWindowInfo->GetPosition().IsFullySpecified()) + Move(FromDIP(newWindowInfo->GetPosition())); + if (!newWindowInfo->ShouldDisplayToolBar()) + m_toolbar->Hide(); + if (!newWindowInfo->ShouldDisplayMenuBar()) + SetMenuBar(nullptr); + } + + if (m_isMainFrame) + { + // 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"))); - //And the memory: file system - m_browser->RegisterHandler(wxSharedPtr(new wxWebViewFSHandler("memory"))); - m_browser->RegisterHandler(wxSharedPtr(new AdvancedWebViewHandler())); + //We register the wxfs:// protocol for testing purposes + m_browser->RegisterHandler(wxSharedPtr(new wxWebViewArchiveHandler("wxfs"))); + //And the memory: file system + m_browser->RegisterHandler(wxSharedPtr(new wxWebViewFSHandler("memory"))); + m_browser->RegisterHandler(wxSharedPtr(new AdvancedWebViewHandler())); #endif - if (!m_browser->AddScriptMessageHandler("wx")) - wxLogError("Could not add script message handler"); + if (!m_browser->AddScriptMessageHandler("wx")) + wxLogError("Could not add script message handler"); + } + else + wxLogMessage("Created new window"); SetSizer(topsizer); @@ -949,11 +973,20 @@ void WebFrame::OnNewWindow(wxWebViewEvent& evt) } wxLogMessage("%s", "New window; url='" + evt.GetURL() + "'" + flag); + wxWebViewWindowInfo* info = evt.GetTargetWindowInfo(); + if (info) + { + wxLogMessage(" New window info: Position=%d,%d Size=%d,%d", + info->GetPosition().x, info->GetPosition().y, + info->GetSize().GetWidth(), info->GetSize().GetHeight()); + } - //If we handle new window events then just load them in this window as we - //are a single window browser - if(m_tools_handle_new_window->IsChecked()) - m_browser->LoadURL(evt.GetURL()); + //If we handle new window events then create a new frame + if (m_tools_handle_new_window->IsChecked()) + { + WebFrame* newFrame = new WebFrame(evt.GetURL(), false, info); + newFrame->Show(); + } UpdateState(); } @@ -983,9 +1016,11 @@ void WebFrame::OnScriptResult(wxWebViewEvent& evt) wxLogMessage("Async script result received; value = %s", evt.GetString()); } -void WebFrame::OnWindowCloseRequested(wxWebViewEvent& evt) +void WebFrame::OnWindowCloseRequested(wxWebViewEvent& WXUNUSED(evt)) { wxLogMessage("Window close requested"); + if (!m_isMainFrame) + Close(); } void WebFrame::OnSetPage(wxCommandEvent& WXUNUSED(evt)) diff --git a/src/msw/webview_edge.cpp b/src/msw/webview_edge.cpp index 18147f7230..067b1b8536 100644 --- a/src/msw/webview_edge.cpp +++ b/src/msw/webview_edge.cpp @@ -252,6 +252,114 @@ public: wxCOMPtr m_args; }; +// wxWebViewNewWindowInfoEdge + +class wxWebViewNewWindowInfoEdge : public wxWebViewWindowInfo +{ +public: + wxWebViewNewWindowInfoEdge(wxWebViewEdgeImpl* impl, ICoreWebView2NewWindowRequestedEventArgs* args): + m_impl(impl), + m_args(args) + { + m_args->get_WindowFeatures(&m_windowFeatures); + } + + virtual wxPoint GetPosition() const override + { + wxPoint result(-1, -1); + BOOL hasPosition; + if (SUCCEEDED(m_windowFeatures->get_HasPosition(&hasPosition)) && hasPosition) + { + UINT32 x, y; + if (SUCCEEDED(m_windowFeatures->get_Left(&x)) && + SUCCEEDED(m_windowFeatures->get_Top(&y))) + result = wxPoint(x, y); + } + return result; + } + + virtual wxSize GetSize() const override + { + wxSize result(-1, -1); + BOOL hasSize; + if (SUCCEEDED(m_windowFeatures->get_HasSize(&hasSize)) && hasSize) + { + UINT32 width, height; + if (SUCCEEDED(m_windowFeatures->get_Width(&width)) && + SUCCEEDED(m_windowFeatures->get_Height(&height))) + result = wxSize(width, height); + } + return result; + } + + virtual bool ShouldDisplayMenuBar() const override + { + BOOL result; + if (SUCCEEDED(m_windowFeatures->get_ShouldDisplayMenuBar(&result))) + return result; + else + return true; + } + + virtual bool ShouldDisplayStatusBar() const override + { + BOOL result; + if (SUCCEEDED(m_windowFeatures->get_ShouldDisplayStatus(&result))) + return result; + else + return true; + } + virtual bool ShouldDisplayToolBar() const override + { + BOOL result; + if (SUCCEEDED(m_windowFeatures->get_ShouldDisplayToolbar(&result))) + return result; + else + return true; + } + + virtual bool ShouldDisplayScrollBars() const override + { + BOOL result; + if (SUCCEEDED(m_windowFeatures->get_ShouldDisplayScrollBars(&result))) + return result; + else + return true; + } + + virtual wxWebView* CreateChildWebView() override + { + return m_impl->CreateChildWebView( + std::make_shared(m_impl, m_args)); + } + +private: + wxWebViewEdgeImpl* m_impl; + wxCOMPtr m_args; + wxCOMPtr m_windowFeatures; +}; + +class wxWebViewEdgeParentWindowInfo +{ +public: + wxWebViewEdgeParentWindowInfo(wxWebViewEdgeImpl* impl, + ICoreWebView2NewWindowRequestedEventArgs* args): + m_impl(impl), + m_args(args) + { + HRESULT hr = m_args->GetDeferral(&m_deferral); + if (FAILED(hr)) + wxLogApiError("GetDeferral", hr); + } + + virtual ~wxWebViewEdgeParentWindowInfo() = default; + + wxWebViewEdgeImpl* m_impl; + wxCOMPtr m_args; + wxCOMPtr m_deferral; +}; + + #define wxWEBVIEW_EDGE_EVENT_HANDLER_METHOD \ m_inEventCallback = true; \ wxON_BLOCK_EXIT_SET(m_inEventCallback, false); @@ -299,19 +407,34 @@ bool wxWebViewEdgeImpl::Create() wxString userDataPath = wxStandardPaths::Get().GetUserLocalDataDir(); - HRESULT hr = wxCreateCoreWebView2EnvironmentWithOptions( - ms_browserExecutableDir.wc_str(), - userDataPath.wc_str(), - m_webViewEnvironmentOptions, - Callback(this, - &wxWebViewEdgeImpl::OnEnvironmentCreated).Get()); - if (FAILED(hr)) + if (m_parentWindowInfo) { - wxLogApiError("CreateWebView2EnvironmentWithOptions", hr); - return false; + OnEnvironmentCreated(S_OK, m_parentWindowInfo->m_impl->m_webViewEnvironment); + return true; } else - return true; + { + HRESULT hr = wxCreateCoreWebView2EnvironmentWithOptions( + ms_browserExecutableDir.wc_str(), + userDataPath.wc_str(), + m_webViewEnvironmentOptions, + Callback(this, + &wxWebViewEdgeImpl::OnEnvironmentCreated).Get()); + if (FAILED(hr)) + { + wxLogApiError("CreateWebView2EnvironmentWithOptions", hr); + return false; + } + else + return true; + } +} + +wxWebViewEdge* wxWebViewEdgeImpl::CreateChildWebView(std::shared_ptr parentWindowInfo) +{ + wxWebViewEdge* childWebView = new wxWebViewEdge(); + childWebView->m_impl->m_parentWindowInfo = parentWindowInfo; + return childWebView; } HRESULT wxWebViewEdgeImpl::OnEnvironmentCreated( @@ -494,6 +617,7 @@ HRESULT wxWebViewEdgeImpl::OnNavigationCompleted(ICoreWebView2* WXUNUSED(sender) HRESULT wxWebViewEdgeImpl::OnNewWindowRequested(ICoreWebView2* WXUNUSED(sender), ICoreWebView2NewWindowRequestedEventArgs* args) { + wxWEBVIEW_EDGE_EVENT_HANDLER_METHOD wxCoTaskMemPtr uri; wxString evtURL; if (SUCCEEDED(args->get_Uri(&uri))) @@ -504,8 +628,10 @@ HRESULT wxWebViewEdgeImpl::OnNewWindowRequested(ICoreWebView2* WXUNUSED(sender), if (SUCCEEDED(args->get_IsUserInitiated(&isUserInitiated)) && isUserInitiated) navFlags = wxWEBVIEW_NAV_ACTION_USER; - wxWebViewEvent evt(wxEVT_WEBVIEW_NEWWINDOW, m_ctrl->GetId(), evtURL, wxString(), navFlags); - m_ctrl->GetEventHandler()->AddPendingEvent(evt); + wxWebViewNewWindowInfoEdge windowInfo(this, args); + wxWebViewEvent evt(wxEVT_WEBVIEW_NEWWINDOW, m_ctrl->GetId(), evtURL, wxString(), navFlags, ""); + evt.SetClientData(&windowInfo); + m_ctrl->HandleWindowEvent(evt); args->put_Handled(true); return S_OK; } @@ -734,6 +860,17 @@ HRESULT wxWebViewEdgeImpl::OnWebViewCreated(HRESULT result, ICoreWebView2Control m_pendingUserScripts.clear(); } + if (m_parentWindowInfo) + { + if (FAILED(m_parentWindowInfo->m_args->put_NewWindow(baseWebView))) + wxLogApiError("WebView2::WebViewCreated (put_NewWindow)", hr); + if (FAILED(m_parentWindowInfo->m_deferral->Complete())) + wxLogApiError("WebView2::WebViewCreated (Complete)", hr); + m_parentWindowInfo.reset(); + + return S_OK; + } + if (!m_pendingURL.empty()) { m_ctrl->LoadURL(m_pendingURL); diff --git a/src/osx/webview_webkit.mm b/src/osx/webview_webkit.mm index 4c26dc9037..43d0ffcfcf 100644 --- a/src/osx/webview_webkit.mm +++ b/src/osx/webview_webkit.mm @@ -110,6 +110,86 @@ wxVersionInfo wxWebViewFactoryWebKit::GetVersionInfo() return wxVersionInfo("WKWebView", verMaj, verMin, verMicro); } +//----------------------------------------------------------------------------- +// wxWebViewWindowInfoWebKit +//----------------------------------------------------------------------------- + +class wxWebViewWindowInfoWebKit: public wxWebViewWindowInfo +{ +public: + wxWebViewWindowInfoWebKit(WKWebViewConfiguration* configuration, + WKNavigationAction* navigationAction, + WKWindowFeatures* windowFeatures): + m_configuration(configuration), + m_navigationAction(navigationAction), + m_windowFeatures(windowFeatures) + { + + } + + virtual wxPoint GetPosition() const override + { + wxPoint pos(-1, -1); + if (m_windowFeatures.y) + pos.y = m_windowFeatures.y.intValue; + if (m_windowFeatures.x) + pos.x = m_windowFeatures.x.intValue; + + return pos; + } + + virtual wxSize GetSize() const override + { + wxSize size(-1, -1); + if (m_windowFeatures.height) + size.y = m_windowFeatures.height.intValue; + if (m_windowFeatures.width) + size.x = m_windowFeatures.width.intValue; + return size; + } + + virtual bool ShouldDisplayMenuBar() const override + { + if (m_windowFeatures.menuBarVisibility) + return m_windowFeatures.menuBarVisibility.boolValue; + else + return true; + } + + virtual bool ShouldDisplayStatusBar() const override + { + if (m_windowFeatures.statusBarVisibility) + return m_windowFeatures.statusBarVisibility.boolValue; + else + return true; + } + + virtual bool ShouldDisplayToolBar() const override + { + if (m_windowFeatures.toolbarsVisibility) + return m_windowFeatures.toolbarsVisibility.boolValue; + else + return true; + } + + virtual bool ShouldDisplayScrollBars() const override + { + return true; + } + + virtual wxWebView* CreateChildWebView() override + { + m_childWebView = new wxWebViewWebKit(this); + return m_childWebView; + } + + wxWebView* m_childWebView = nullptr; + WKWebViewConfiguration* m_configuration; + WKNavigationAction* m_navigationAction; + WKWindowFeatures* m_windowFeatures; +}; + + // ---------------------------------------------------------------------------- // creation/destruction // ---------------------------------------------------------------------------- @@ -130,9 +210,10 @@ bool wxWebViewWebKit::Create(wxWindow *parent, wxControl::Create(parent, winID, pos, size, style, wxDefaultValidator, name); NSRect r = wxOSXGetFrameForControl( this, pos , size ) ; - WKWebViewConfiguration* webViewConfig = (WKWebViewConfiguration*) m_webViewConfiguration; + WKWebViewConfiguration* webViewConfig = (m_parentWindowInfo) ? + m_parentWindowInfo->m_configuration : (WKWebViewConfiguration*) m_webViewConfiguration; - if (!m_handlers.empty()) + if (!m_handlers.empty() && !m_parentWindowInfo) { #if __MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_13 if ( WX_IS_MACOS_AVAILABLE(10, 13) ) @@ -154,9 +235,12 @@ bool wxWebViewWebKit::Create(wxWindow *parent, MacPostControlCreate(pos, size); - // WKWebView configuration is only used during creation - [m_webViewConfiguration release]; - m_webViewConfiguration = nil; + if (!m_parentWindowInfo) + { + // WKWebView configuration is only used during creation + [m_webViewConfiguration release]; + m_webViewConfiguration = nil; + } if (!m_customUserAgent.empty()) SetUserAgent(m_customUserAgent); @@ -178,45 +262,51 @@ bool wxWebViewWebKit::Create(wxWindow *parent, [m_webView setUIDelegate:uiDelegate]; - // Implement javascript fullscreen interface with user script and message handler - AddUserScript("\ - document.__wxToggleFullscreen = function (elem) { \ - if (!document.__wxStylesAdded) { \ - function createClass(name,rules) { \ - var style= document.createElement('style'); style.type = 'text/css'; \ - document.getElementsByTagName('head')[0].appendChild(style); \ - style.sheet.addRule(name, rules); \ + if (!m_parentWindowInfo) + { + // Implement javascript fullscreen interface with user script and message handler + AddUserScript("\ + document.__wxToggleFullscreen = function (elem) { \ + if (!document.__wxStylesAdded) { \ + function createClass(name,rules) { \ + var style= document.createElement('style'); style.type = 'text/css'; \ + document.getElementsByTagName('head')[0].appendChild(style); \ + style.sheet.addRule(name, rules); \ + } \ + createClass(\"body.wxfullscreen\", \"padding: 0; margin: 0; height: 100%;\"); \ + createClass(\".wxfullscreen\", \"position: fixed; overflow: hidden; z-index: 1000; left: 0; top: 0; bottom: 0; right: 0;\"); \ + createClass(\".wxfullscreenelem\", \"width: 100% !important; height: 100% !important; padding-top: 0 !important;\"); \ + document.__wxStylesAdded = true; \ } \ - createClass(\"body.wxfullscreen\", \"padding: 0; margin: 0; height: 100%;\"); \ - createClass(\".wxfullscreen\", \"position: fixed; overflow: hidden; z-index: 1000; left: 0; top: 0; bottom: 0; right: 0;\"); \ - createClass(\".wxfullscreenelem\", \"width: 100% !important; height: 100% !important; padding-top: 0 !important;\"); \ - document.__wxStylesAdded = true; \ - } \ - if (elem) { \ - elem.classList.add(\"wxfullscreen\"); \ - elem.classList.add(\"wxfullscreenelem\"); \ - document.body.classList.add(\"wxfullscreen\"); \ - } else if (document.webkitFullscreenElement) { \ - document.webkitFullscreenElement.classList.remove(\"wxfullscreen\"); \ - document.webkitFullscreenElement.classList.remove(\"wxfullscreenelem\"); \ - document.body.classList.remove(\"wxfullscreen\"); \ - } \ - document.webkitFullscreenElement = elem; \ - window.webkit.messageHandlers.__wxfullscreen.postMessage((elem) ? 1: 0); \ - document.dispatchEvent(new Event('webkitfullscreenchange')); \ - if (document.onwebkitfullscreenchange) document.onwebkitfullscreenchange(); \ - }; \ - Element.prototype.webkitRequestFullscreen = function() {document.__wxToggleFullscreen(this);}; \ - document.webkitExitFullscreen = function() {document.__wxToggleFullscreen(undefined);}; \ - document.onwebkitfullscreenchange = null; \ - document.webkitFullscreenEnabled = true; \ - "); - [m_webView.configuration.userContentController addScriptMessageHandler: - [[WebViewScriptMessageHandler alloc] initWithWxWindow:this] name:@"__wxfullscreen"]; + if (elem) { \ + elem.classList.add(\"wxfullscreen\"); \ + elem.classList.add(\"wxfullscreenelem\"); \ + document.body.classList.add(\"wxfullscreen\"); \ + } else if (document.webkitFullscreenElement) { \ + document.webkitFullscreenElement.classList.remove(\"wxfullscreen\"); \ + document.webkitFullscreenElement.classList.remove(\"wxfullscreenelem\"); \ + document.body.classList.remove(\"wxfullscreen\"); \ + } \ + document.webkitFullscreenElement = elem; \ + window.webkit.messageHandlers.__wxfullscreen.postMessage((elem) ? 1: 0); \ + document.dispatchEvent(new Event('webkitfullscreenchange')); \ + if (document.onwebkitfullscreenchange) document.onwebkitfullscreenchange(); \ + }; \ + Element.prototype.webkitRequestFullscreen = function() {document.__wxToggleFullscreen(this);}; \ + document.webkitExitFullscreen = function() {document.__wxToggleFullscreen(undefined);}; \ + document.onwebkitfullscreenchange = null; \ + document.webkitFullscreenEnabled = true; \ + "); + [m_webView.configuration.userContentController addScriptMessageHandler: + [[WebViewScriptMessageHandler alloc] initWithWxWindow:this] name:@"__wxfullscreen"]; + } m_UIDelegate = uiDelegate; - LoadURL(strURL); + if (m_parentWindowInfo) + [m_webView loadRequest:m_parentWindowInfo->m_navigationAction.request]; + else + LoadURL(strURL); return true; } @@ -1013,15 +1103,21 @@ WX_API_AVAILABLE_MACOS(10, 13) wxWEBVIEW_NAV_ACTION_USER : wxWEBVIEW_NAV_ACTION_OTHER; + wxWebViewWindowInfoWebKit windowInfo(configuration, navigationAction, windowFeatures); + wxWebViewEvent event(wxEVT_WEBVIEW_NEWWINDOW, webKitWindow->GetId(), wxCFStringRef::AsString( navigationAction.request.URL.absoluteString ), "", navFlags); + event.SetClientData(&windowInfo); if (webKitWindow && webKitWindow->GetEventHandler()) webKitWindow->GetEventHandler()->ProcessEvent(event); - return nil; + if (windowInfo.m_childWebView) + return (WKWebView*) windowInfo.m_childWebView->GetNativeBackend(); + else + return nil; } - (void)webViewDidClose:(WKWebView *)webView