diff --git a/Makefile.in b/Makefile.in index 9f05c9bca9..f6b3ff20b1 100644 --- a/Makefile.in +++ b/Makefile.in @@ -4062,7 +4062,8 @@ COND_USE_GUI_1_ALL_GUI_HEADERS = \ wx/webview.h \ wx/webviewarchivehandler.h \ wx/webviewfshandler.h \ - wx/webview_chromium.h + wx/webview_chromium.h \ + wx/webview_chromium_impl.h @COND_USE_GUI_1@ALL_GUI_HEADERS = $(COND_USE_GUI_1_ALL_GUI_HEADERS) COND_MONOLITHIC_1_SHARED_1___monodll___depname = \ $(LIBDIRNAME)/$(DLLPREFIX)$(WXDLLNAMEPREFIXGUI)u$(WXDEBUGFLAG)$(WX_LIB_FLAVOUR)$(WXCOMPILER)$(VENDORTAG)$(WXDLLVERSIONTAG)$(dll___targetsuf3) diff --git a/build/bakefiles/files.bkl b/build/bakefiles/files.bkl index 35165a0455..600f877471 100644 --- a/build/bakefiles/files.bkl +++ b/build/bakefiles/files.bkl @@ -2708,6 +2708,7 @@ IMPORTANT: please read docs/tech/tn0016.txt before modifying this file! wx/webviewarchivehandler.h wx/webviewfshandler.h wx/webview_chromium.h + wx/webview_chromium_impl.h $(WEBVIEW_HDR_PLATFORM) diff --git a/build/cmake/files.cmake b/build/cmake/files.cmake index 540208158f..4f153ceea8 100644 --- a/build/cmake/files.cmake +++ b/build/cmake/files.cmake @@ -2533,6 +2533,7 @@ set(WEBVIEW_CMN_HDR wx/webviewarchivehandler.h wx/webviewfshandler.h wx/webview_chromium.h + wx/webview_chromium_impl.h ) set(WEBVIEW_OSX_SHARED_HDR diff --git a/build/files b/build/files index 1a6102efe7..77dd21df01 100644 --- a/build/files +++ b/build/files @@ -2486,6 +2486,7 @@ WEBVIEW_CMN_HDR = wx/webviewarchivehandler.h wx/webviewfshandler.h wx/webview_chromium.h + wx/webview_chromium_impl.h WEBVIEW_OSX_SHARED_HDR = wx/osx/webviewhistoryitem_webkit.h diff --git a/build/msw/wx_webview.vcxproj b/build/msw/wx_webview.vcxproj index d350da412e..e8ff5db009 100644 --- a/build/msw/wx_webview.vcxproj +++ b/build/msw/wx_webview.vcxproj @@ -507,6 +507,7 @@ + diff --git a/build/msw/wx_webview.vcxproj.filters b/build/msw/wx_webview.vcxproj.filters index d92aa9bd94..9cd2b021bd 100644 --- a/build/msw/wx_webview.vcxproj.filters +++ b/build/msw/wx_webview.vcxproj.filters @@ -61,6 +61,9 @@ Common Headers + + Common Headers + Common Headers diff --git a/include/wx/webview_chromium.h b/include/wx/webview_chromium.h index 1a587f76e7..4074ad8562 100644 --- a/include/wx/webview_chromium.h +++ b/include/wx/webview_chromium.h @@ -18,6 +18,8 @@ class WXDLLIMPEXP_FWD_BASE wxFileName; extern WXDLLIMPEXP_DATA_WEBVIEW(const char) wxWebViewBackendChromium[]; +class CefClient; + // Private namespace containing classes used only in the implementation. namespace wxCEF { @@ -170,6 +172,10 @@ private: friend class wxCEF::ClientHandler; wxCEF::ClientHandler* m_clientHandler = nullptr; + // Actual client used by CEF: this can be either m_clientHandler itself or + // a custom client provided by the application. + CefClient* m_actualClient = nullptr; + friend class wxWebViewChromiumModule; static bool ms_cefInitialized; @@ -198,6 +204,19 @@ public: // Logging level must be one of cef_log_severity_t values (0 means default). int m_logLevel = 0; + + // Function to create the custom CefClient to use if non-null. + // + // The CefClient subclass must delegate all not otherwise implemented + // functions to the provided client (and should always delegate the + // lifetime-related callbacks). + // + // It is recommended, although not required, to derive the custom client + // from wxDelegatingCefClient defined in wx/webview_chromium_impl.h. + CefClient* (*m_clientCreate)(CefClient* client, void* data) = nullptr; + + // Data to pass to m_clientCreate if it is used. + void* m_clientCreateData = nullptr; }; diff --git a/include/wx/webview_chromium_impl.h b/include/wx/webview_chromium_impl.h new file mode 100644 index 0000000000..d12e19c4f0 --- /dev/null +++ b/include/wx/webview_chromium_impl.h @@ -0,0 +1,97 @@ +/////////////////////////////////////////////////////////////////////////////// +// Name: wx/webview_chromium_impl.h +// Purpose: Helpers for implementing custom CefClient for wxWebViewChromium +// Author: Vadim Zeitlin +// Created: 2024-02-17 +// Copyright: (c) 2024 Vadim Zeitlin +// Licence: wxWindows licence +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _WX_WEBVIEW_CHROMIUM_IMPL_H_ +#define _WX_WEBVIEW_CHROMIUM_IMPL_H_ + +// Note that this header includes CEF headers and so the appropriate include +// path should be set up when using it. + +#ifdef __VISUALC__ +#pragma warning(push) +#pragma warning(disable:4100) +#endif + +wxGCC_WARNING_SUPPRESS(unused-parameter) + +#include "include/cef_client.h" + +wxGCC_WARNING_RESTORE(unused-parameter) + +#ifdef __VISUALC__ +#pragma warning(pop) +#endif + +// ---------------------------------------------------------------------------- +// Convenient base class for custom CefClient implementations. +// ---------------------------------------------------------------------------- + +class wxDelegatingCefClient : public CefClient +{ +public: + // Forward all CefClient methods to the original client. + virtual CefRefPtr GetAudioHandler() override + { return m_clientOrig->GetAudioHandler(); } + virtual CefRefPtr GetCommandHandler() override + { return m_clientOrig->GetCommandHandler(); } + virtual CefRefPtr GetContextMenuHandler() override + { return m_clientOrig->GetContextMenuHandler(); } + virtual CefRefPtr GetDialogHandler() override + { return m_clientOrig->GetDialogHandler(); } + virtual CefRefPtr GetDisplayHandler() override + { return m_clientOrig->GetDisplayHandler(); } + virtual CefRefPtr GetDownloadHandler() override + { return m_clientOrig->GetDownloadHandler(); } + virtual CefRefPtr GetDragHandler() override + { return m_clientOrig->GetDragHandler(); } + virtual CefRefPtr GetFindHandler() override + { return m_clientOrig->GetFindHandler(); } + virtual CefRefPtr GetFocusHandler() override + { return m_clientOrig->GetFocusHandler(); } + virtual CefRefPtr GetFrameHandler() override + { return m_clientOrig->GetFrameHandler(); } + virtual CefRefPtr GetPermissionHandler() override + { return m_clientOrig->GetPermissionHandler(); } + virtual CefRefPtr GetJSDialogHandler() override + { return m_clientOrig->GetJSDialogHandler(); } + virtual CefRefPtr GetKeyboardHandler() override + { return m_clientOrig->GetKeyboardHandler(); } + virtual CefRefPtr GetLifeSpanHandler() override + { return m_clientOrig->GetLifeSpanHandler(); } + virtual CefRefPtr GetLoadHandler() override + { return m_clientOrig->GetLoadHandler(); } + virtual CefRefPtr GetPrintHandler() override + { return m_clientOrig->GetPrintHandler(); } + virtual CefRefPtr GetRenderHandler() override + { return m_clientOrig->GetRenderHandler(); } + virtual CefRefPtr GetRequestHandler() override + { return m_clientOrig->GetRequestHandler(); } + virtual bool OnProcessMessageReceived(CefRefPtr browser, + CefRefPtr frame, + CefProcessId source_process, + CefRefPtr message) override + { + return m_clientOrig->OnProcessMessageReceived(browser, frame, + source_process, message); + } + +protected: + // Objects of this class shouldn't be created, only derived classes should + // be used, hence the constructor is protected. + explicit wxDelegatingCefClient(CefClient* clientOrig) + : m_clientOrig{clientOrig} + { + } + + CefRefPtr const m_clientOrig; + + IMPLEMENT_REFCOUNTING(wxDelegatingCefClient); +}; + +#endif // _WX_WEBVIEW_CHROMIUM_IMPL_H_ diff --git a/interface/wx/webview_chromium.h b/interface/wx/webview_chromium.h index 8a5f137602..528aec53e6 100644 --- a/interface/wx/webview_chromium.h +++ b/interface/wx/webview_chromium.h @@ -386,6 +386,36 @@ public: Default value 0 means to use default "INFO" log level. */ int m_logLevel = 0; + + /** + Function to create the custom CefClient to use if non-null. + + CEF uses an object of CefClient class to customize handling of many + operations, by allowing to return custom objects from its callbacks, + and for processing IPC messages received from the other processes used + by CEF. By defining this function pointer, the application can use its + own CefClient subclass to customize many aspects of CEF behaviour + beyond what is possible using the standard wxWebView API. + + Please note that the returned object must delegate all not otherwise + implemented functions to the provided @a client (and should always + delegate the lifetime-related callbacks). You can ensure that this is + the case by deriving your custom CefClient subclass from + wxDelegatingCefClient, but you still need to do it manually if not + using this class. + */ + CefClient* (*m_clientCreate)(CefClient* client, void* data) = nullptr; + + /** + Data to pass to m_clientCreate if it is used. + + This is just an arbitrary pointer, which is passed as argument to + m_clientCreate function if it is non-null. + + This pointer itself may be null if it is not necessary to pass any + extra data to the client creation function. + */ + void* m_clientCreateData = nullptr; }; /** @@ -416,6 +446,13 @@ public: ); @endcode + @note Using wxEvent dispatching adds a significant overhead to handling of + CEF IPC messages, so if performance is important (i.e. many such messages + are expected), it is recommended to configure wxWebViewChromium to use a + custom `CefClient` as described in wxWebViewConfigurationChromium + documentation and handle the messages directly in the overridden + `OnProcessMessageReceived()` of the custom client class. + @since 3.3.0 @library{wxwebview} @category{webview} diff --git a/interface/wx/webview_chromium_impl.h b/interface/wx/webview_chromium_impl.h new file mode 100644 index 0000000000..a5a0963d48 --- /dev/null +++ b/interface/wx/webview_chromium_impl.h @@ -0,0 +1,64 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: webview_chromium_impl.h +// Purpose: Documentation for wxWebViewChromium implementation helpers +// Author: Vadim Zeitlin +// Created: 2024-02-17 +// Copyright: (c) 2024 Vadim Zeitlin +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +/** + Helper class to simplify creating custom CefClient subclasses. + + Please note that the application including this header must set up the + compiler options to include the CEF directory as one of the include paths, + as this header -- unlike wx/webview_chromium.h -- includes CEF headers. + + Here is a simple example of using this class to customize processing the + messages received from the other CEF processes: + + @code + #include + + struct CustomClient : wxDelegatingCefClient + { + using wxDelegatingCefClient::wxDelegatingCefClient; + + static CefClient* Create(CefClient* client, void* WXUNUSED(data) + { + return new CustomClient(client); + } + + bool OnProcessMessageReceived(CefRefPtr browser, + CefRefPtr frame, + CefProcessId source_process, + CefRefPtr message) override + { + // Handle the message here. + + return true; // or false if not handled + } + }; + + void MyFunction() + { + wxWebViewConfiguration config = wxWebView::NewConfiguration(wxWebViewBackendChromium); + + auto configChrome = + static_cast(config.GetNativeConfiguration()); + configChrome->m_clientCreate = &CustomClient::Create; + + auto webview = new wxWebViewChromium(config); + if ( !webview->Create(this, wxID_ANY, url) ) + { + // Handle error. + } + } + @endcode + + @since 3.3.0 + @category{webview} + */ +class wxDelegatingCefClient : CefClient +{ +}; diff --git a/src/common/webview_chromium.cpp b/src/common/webview_chromium.cpp index e958d605cc..6c886fd668 100644 --- a/src/common/webview_chromium.cpp +++ b/src/common/webview_chromium.cpp @@ -829,9 +829,22 @@ bool wxWebViewChromium::DoCreateBrowser(const wxString& url) } } + // Check if we need to create a custom client handler. + const auto configChrome = + static_cast(m_implData->m_config.GetNativeConfiguration()); + if ( auto create = configChrome->m_clientCreate ) + { + m_actualClient = create(m_clientHandler, configChrome->m_clientCreateData); + if ( m_actualClient ) + m_actualClient->AddRef(); + } + + if ( !m_actualClient ) + m_actualClient = m_clientHandler; + if ( !CefBrowserHost::CreateBrowser( info, - CefRefPtr{m_clientHandler}, + CefRefPtr{m_actualClient}, url.ToStdString(), browsersettings, nullptr, // No extra info @@ -856,7 +869,10 @@ wxWebViewChromium::~wxWebViewChromium() constexpr bool forceClose = true; m_clientHandler->GetHost()->CloseBrowser(forceClose); - m_clientHandler->Release(); + + // We need to destroy the client handler used by the browser to allow + // it to close. + m_actualClient->Release(); // We need to wait until the browser is really closed, which happens // asynchronously, as otherwise we could exit the program and call @@ -905,6 +921,11 @@ wxWebViewChromium::~wxWebViewChromium() // really destroying the object before CefShutdown() is called. wxUnusedVar(handle); #endif + + // If we hadn't release our own client handler above, we need to do it + // now (notice that it's safe to do it, as it can't be used any more). + if ( m_clientHandler != m_actualClient ) + m_clientHandler->Release(); } delete m_implData;