From 53ef570f571de4b5e33c70ee7e8d4423bd66aca5 Mon Sep 17 00:00:00 2001 From: Marshall Greenblatt Date: Tue, 14 Nov 2023 17:16:43 +0000 Subject: [PATCH] chrome: Support Chrome and DevTools command execution (fixes #3282, fixes #3600) Add new CefBrowserHost::[Can]ExecuteChromeCommand methods for executing arbitrary Chrome commands. Add support for existing CefBrowserHost::ShowDevTools, CloseDevTools and HasDevTools methods. DevTools windows now support the same Views callbacks as normal popup windows with the new CefLifeSpanHandler::OnBeforeDevToolsPopup callback as the DevTools-specific equivalent of OnBeforePopup. Always create DevTools as an undocked window to support use of ShowDevTools with default Chrome browser windows. To test: Run `ceftests --enable-chrome-runtime [--use-views] --gtest_filter=V8Test.OnUncaughtExceptionDevTools` OR: 1. Run `cefclient --enable-chrome-runtime [--use-native]` 2. Select "Show DevTools", "Close DevTools" or "Inspect" from the right-click menu. 3. Notice that the DevTools window is Views-hosted (or native-hosted) and works as expected. Add --use-default-popup to get a default styled popup in step 3. --- include/capi/cef_browser_capi.h | 21 +- include/capi/cef_life_span_handler_capi.h | 30 ++- include/cef_api_hash.h | 8 +- include/cef_browser.h | 18 ++ include/cef_life_span_handler.h | 27 +++ include/internal/cef_types.h | 26 ++- .../browser/alloy/alloy_browser_host_impl.cc | 56 ++--- .../browser/alloy/alloy_browser_host_impl.h | 11 +- .../alloy/browser_platform_delegate_alloy.cc | 15 +- libcef/browser/browser_host_base.cc | 16 ++ libcef/browser/browser_host_base.h | 25 ++ libcef/browser/browser_info.cc | 10 + libcef/browser/browser_info_manager.cc | 24 +- libcef/browser/browser_info_manager.h | 3 + libcef/browser/chrome/browser_delegate.h | 9 + .../browser/chrome/chrome_browser_delegate.cc | 219 ++++++++++++++++-- .../browser/chrome/chrome_browser_delegate.h | 26 ++- .../chrome/chrome_browser_host_impl.cc | 157 ++++++++++--- .../browser/chrome/chrome_browser_host_impl.h | 29 ++- .../chrome/views/chrome_browser_frame.cc | 23 ++ .../chrome/views/chrome_browser_frame.h | 1 + .../chrome/views/chrome_child_window.cc | 2 - .../net_service/browser_urlrequest_impl.cc | 10 +- libcef/browser/request_context_impl.cc | 26 +++ libcef/browser/request_context_impl.h | 9 + libcef/common/value_base.h | 7 + libcef_dll/cpptoc/browser_host_cpptoc.cc | 43 +++- libcef_dll/cpptoc/life_span_handler_cpptoc.cc | 124 +++++++++- libcef_dll/ctocpp/browser_host_ctocpp.cc | 37 ++- libcef_dll/ctocpp/browser_host_ctocpp.h | 5 +- libcef_dll/ctocpp/life_span_handler_ctocpp.cc | 72 +++++- libcef_dll/ctocpp/life_span_handler_ctocpp.h | 8 +- patch/patch.cfg | 4 + patch/patches/chrome_browser_browser.patch | 66 +++++- patch/patches/chrome_runtime_views.patch | 22 +- patch/patches/views_widget.patch | 11 +- tests/cefclient/browser/client_handler.cc | 73 +++--- tests/cefclient/browser/client_handler.h | 11 +- tests/cefclient/browser/main_context.h | 4 + tests/cefclient/browser/main_context_impl.cc | 9 +- tests/cefclient/browser/main_context_impl.h | 2 + tests/cefclient/browser/root_window.h | 3 + tests/cefclient/browser/root_window_views.cc | 4 +- tests/cefclient/browser/views_window.cc | 31 ++- tests/cefclient/browser/views_window.h | 2 + tests/ceftests/navigation_unittest.cc | 2 +- tests/ceftests/request_context_unittest.cc | 4 +- tests/ceftests/test_handler.cc | 47 +++- tests/ceftests/test_handler.h | 16 +- tests/ceftests/v8_unittest.cc | 5 +- 50 files changed, 1173 insertions(+), 240 deletions(-) diff --git a/include/capi/cef_browser_capi.h b/include/capi/cef_browser_capi.h index 3a8a64a1c..cf64c83db 100644 --- a/include/capi/cef_browser_capi.h +++ b/include/capi/cef_browser_capi.h @@ -33,7 +33,7 @@ // by hand. See the translator.README.txt file in the tools directory for // more information. // -// $hash=13ba2d807f2c1ac3adfc65f2bdb269baecba57ec$ +// $hash=eed525e9abcbf8e8b959067e0056ca470c5210c7$ // #ifndef CEF_INCLUDE_CAPI_CEF_BROWSER_CAPI_H_ @@ -955,6 +955,25 @@ typedef struct _cef_browser_host_t { /// void(CEF_CALLBACK* exit_fullscreen)(struct _cef_browser_host_t* self, int will_cause_resize); + + /// + /// Returns true (1) if a Chrome command is supported and enabled. Values for + /// |command_id| can be found in the cef_command_ids.h file. This function can + /// only be called on the UI thread. Only used with the Chrome runtime. + /// + int(CEF_CALLBACK* can_execute_chrome_command)( + struct _cef_browser_host_t* self, + int command_id); + + /// + /// Execute a Chrome command. Values for |command_id| can be found in the + /// cef_command_ids.h file. |disposition| provides information about the + /// intended command target. Only used with the Chrome runtime. + /// + void(CEF_CALLBACK* execute_chrome_command)( + struct _cef_browser_host_t* self, + int command_id, + cef_window_open_disposition_t disposition); } cef_browser_host_t; /// diff --git a/include/capi/cef_life_span_handler_capi.h b/include/capi/cef_life_span_handler_capi.h index f02ea6a4a..a12b0864f 100644 --- a/include/capi/cef_life_span_handler_capi.h +++ b/include/capi/cef_life_span_handler_capi.h @@ -33,7 +33,7 @@ // by hand. See the translator.README.txt file in the tools directory for // more information. // -// $hash=1c807597b96889f44a1e5199e860e8db4948b473$ +// $hash=32a0c21a71aa7137fa9660b942f597705bc8b05e$ // #ifndef CEF_INCLUDE_CAPI_CEF_LIFE_SPAN_HANDLER_CAPI_H_ @@ -100,6 +100,34 @@ typedef struct _cef_life_span_handler_t { struct _cef_dictionary_value_t** extra_info, int* no_javascript_access); + /// + /// Called on the UI thread before a new DevTools popup browser is created. + /// The |browser| value represents the source of the popup request. Optionally + /// modify |windowInfo|, |client|, |settings| and |extra_info| values. The + /// |client|, |settings| and |extra_info| values will default to the source + /// browser's values. Any modifications to |windowInfo| will be ignored if the + /// parent browser is Views-hosted (wrapped in a cef_browser_view_t). + /// + /// The |extra_info| parameter provides an opportunity to specify extra + /// information specific to the created popup browser that will be passed to + /// cef_render_process_handler_t::on_browser_created() in the render process. + /// The existing |extra_info| object, if any, will be read-only but may be + /// replaced with a new object. + /// + /// Views-hosted source browsers will create Views-hosted DevTools popups + /// unless |use_default_window| is set to to true (1). DevTools popups can be + /// blocked by returning true (1) from cef_command_handler_t::OnChromeCommand + /// for IDC_DEV_TOOLS. Only used with the Chrome runtime. + /// + void(CEF_CALLBACK* on_before_dev_tools_popup)( + struct _cef_life_span_handler_t* self, + struct _cef_browser_t* browser, + struct _cef_window_info_t* windowInfo, + struct _cef_client_t** client, + struct _cef_browser_settings_t* settings, + struct _cef_dictionary_value_t** extra_info, + int* use_default_window); + /// /// Called after a new browser is created. It is now safe to begin performing /// actions with |browser|. cef_frame_handler_t callbacks related to initial diff --git a/include/cef_api_hash.h b/include/cef_api_hash.h index 8b9c86893..a774bdc23 100644 --- a/include/cef_api_hash.h +++ b/include/cef_api_hash.h @@ -42,13 +42,13 @@ // way that may cause binary incompatibility with other builds. The universal // hash value will change if any platform is affected whereas the platform hash // values will change only if that particular platform is affected. -#define CEF_API_HASH_UNIVERSAL "c0c754c1ca4f72f6ca6a80861b38b34a61ed5116" +#define CEF_API_HASH_UNIVERSAL "ce9401699c6753553cba867b1f5c329f759d2c67" #if defined(OS_WIN) -#define CEF_API_HASH_PLATFORM "b8db902c4604f9447ece1184f0d4f674503d0655" +#define CEF_API_HASH_PLATFORM "e094f42b7a60d2c8c9bcb3db51907a3b42f51d04" #elif defined(OS_MAC) -#define CEF_API_HASH_PLATFORM "a01475fc7c5d8bdc91e29add1aae56c7445a4d4b" +#define CEF_API_HASH_PLATFORM "6ce44bd7182aa7e9544f5ca33c310f2a096ab638" #elif defined(OS_LINUX) -#define CEF_API_HASH_PLATFORM "75cbf2876ee57cc093f9ab7905e19034754a4b8a" +#define CEF_API_HASH_PLATFORM "8e9886cd490aefc89283d65f5f7d104a51e2d289" #endif #ifdef __cplusplus diff --git a/include/cef_browser.h b/include/cef_browser.h index 20e0a7f9e..7ba498580 100644 --- a/include/cef_browser.h +++ b/include/cef_browser.h @@ -988,6 +988,24 @@ class CefBrowserHost : public virtual CefBaseRefCounted { /// /*--cef()--*/ virtual void ExitFullscreen(bool will_cause_resize) = 0; + + /// + /// Returns true if a Chrome command is supported and enabled. Values for + /// |command_id| can be found in the cef_command_ids.h file. This method can + /// only be called on the UI thread. Only used with the Chrome runtime. + /// + /*--cef()--*/ + virtual bool CanExecuteChromeCommand(int command_id) = 0; + + /// + /// Execute a Chrome command. Values for |command_id| can be found in the + /// cef_command_ids.h file. |disposition| provides information about the + /// intended command target. Only used with the Chrome runtime. + /// + /*--cef()--*/ + virtual void ExecuteChromeCommand( + int command_id, + cef_window_open_disposition_t disposition) = 0; }; #endif // CEF_INCLUDE_CEF_BROWSER_H_ diff --git a/include/cef_life_span_handler.h b/include/cef_life_span_handler.h index d779b075c..067a3f9d6 100644 --- a/include/cef_life_span_handler.h +++ b/include/cef_life_span_handler.h @@ -94,6 +94,33 @@ class CefLifeSpanHandler : public virtual CefBaseRefCounted { return false; } + /// + /// Called on the UI thread before a new DevTools popup browser is created. + /// The |browser| value represents the source of the popup request. Optionally + /// modify |windowInfo|, |client|, |settings| and |extra_info| values. The + /// |client|, |settings| and |extra_info| values will default to the source + /// browser's values. Any modifications to |windowInfo| will be ignored if the + /// parent browser is Views-hosted (wrapped in a CefBrowserView). + /// + /// The |extra_info| parameter provides an opportunity to specify extra + /// information specific to the created popup browser that will be passed to + /// CefRenderProcessHandler::OnBrowserCreated() in the render process. The + /// existing |extra_info| object, if any, will be read-only but may be + /// replaced with a new object. + /// + /// Views-hosted source browsers will create Views-hosted DevTools popups + /// unless |use_default_window| is set to to true. DevTools popups can be + /// blocked by returning true from CefCommandHandler::OnChromeCommand for + /// IDC_DEV_TOOLS. Only used with the Chrome runtime. + /// + /*--cef()--*/ + virtual void OnBeforeDevToolsPopup(CefRefPtr browser, + CefWindowInfo& windowInfo, + CefRefPtr& client, + CefBrowserSettings& settings, + CefRefPtr& extra_info, + bool* use_default_window) {} + /// /// Called after a new browser is created. It is now safe to begin performing /// actions with |browser|. CefFrameHandler callbacks related to initial main diff --git a/include/internal/cef_types.h b/include/internal/cef_types.h index 4456000bb..ea3fe8fd7 100644 --- a/include/internal/cef_types.h +++ b/include/internal/cef_types.h @@ -1004,52 +1004,52 @@ typedef enum { /// renumbered. /// typedef enum { - WOD_UNKNOWN, + CEF_WOD_UNKNOWN, /// /// Current tab. This is the default in most cases. /// - WOD_CURRENT_TAB, + CEF_WOD_CURRENT_TAB, /// /// Indicates that only one tab with the url should exist in the same window. /// - WOD_SINGLETON_TAB, + CEF_WOD_SINGLETON_TAB, /// /// Shift key + Middle mouse button or meta/ctrl key while clicking. /// - WOD_NEW_FOREGROUND_TAB, + CEF_WOD_NEW_FOREGROUND_TAB, /// /// Middle mouse button or meta/ctrl key while clicking. /// - WOD_NEW_BACKGROUND_TAB, + CEF_WOD_NEW_BACKGROUND_TAB, /// /// New popup window. /// - WOD_NEW_POPUP, + CEF_WOD_NEW_POPUP, /// /// Shift key while clicking. /// - WOD_NEW_WINDOW, + CEF_WOD_NEW_WINDOW, /// /// Alt key while clicking. /// - WOD_SAVE_TO_DISK, + CEF_WOD_SAVE_TO_DISK, /// /// New off-the-record (incognito) window. /// - WOD_OFF_THE_RECORD, + CEF_WOD_OFF_THE_RECORD, /// /// Special case error condition from the renderer. /// - WOD_IGNORE_ACTION, + CEF_WOD_IGNORE_ACTION, /// /// Activates an existing tab containing the url, rather than navigating. @@ -1059,12 +1059,14 @@ typedef enum { /// no session history; and behaves like CURRENT_TAB instead of /// NEW_FOREGROUND_TAB when no existing tab is found. /// - WOD_SWITCH_TO_TAB, + CEF_WOD_SWITCH_TO_TAB, /// /// Creates a new document picture-in-picture window showing a child WebView. /// - WOD_NEW_PICTURE_IN_PICTURE, + CEF_WOD_NEW_PICTURE_IN_PICTURE, + + CEF_WOD_MAX_VALUE = CEF_WOD_NEW_PICTURE_IN_PICTURE, } cef_window_open_disposition_t; /// diff --git a/libcef/browser/alloy/alloy_browser_host_impl.cc b/libcef/browser/alloy/alloy_browser_host_impl.cc index 9195c30c3..703b040c8 100644 --- a/libcef/browser/alloy/alloy_browser_host_impl.cc +++ b/libcef/browser/alloy/alloy_browser_host_impl.cc @@ -57,33 +57,6 @@ using content::KeyboardEventProcessingResult; namespace { -class ShowDevToolsHelper { - public: - ShowDevToolsHelper(CefRefPtr browser, - const CefWindowInfo& windowInfo, - CefRefPtr client, - const CefBrowserSettings& settings, - const CefPoint& inspect_element_at) - : browser_(browser), - window_info_(windowInfo), - client_(client), - settings_(settings), - inspect_element_at_(inspect_element_at) {} - - CefRefPtr browser_; - CefWindowInfo window_info_; - CefRefPtr client_; - CefBrowserSettings settings_; - CefPoint inspect_element_at_; -}; - -void ShowDevToolsWithHelper(ShowDevToolsHelper* helper) { - helper->browser_->ShowDevTools(helper->window_info_, helper->client_, - helper->settings_, - helper->inspect_element_at_); - delete helper; -} - static constexpr base::TimeDelta kRecentlyAudibleTimeout = base::Seconds(2); } // namespace @@ -358,22 +331,15 @@ void AlloyBrowserHostImpl::StopFinding(bool clearSelection) { } } -void AlloyBrowserHostImpl::ShowDevTools(const CefWindowInfo& windowInfo, - CefRefPtr client, - const CefBrowserSettings& settings, - const CefPoint& inspect_element_at) { - if (!CEF_CURRENTLY_ON_UIT()) { - ShowDevToolsHelper* helper = new ShowDevToolsHelper( - this, windowInfo, client, settings, inspect_element_at); - CEF_POST_TASK(CEF_UIT, base::BindOnce(ShowDevToolsWithHelper, helper)); - return; - } - +void AlloyBrowserHostImpl::ShowDevToolsOnUIThread( + std::unique_ptr params) { + CEF_REQUIRE_UIT(); if (!EnsureDevToolsManager()) { return; } - devtools_manager_->ShowDevTools(windowInfo, client, settings, - inspect_element_at); + devtools_manager_->ShowDevTools(params->window_info_, params->client_, + params->settings_, + params->inspect_element_at_); } void AlloyBrowserHostImpl::CloseDevTools() { @@ -438,6 +404,16 @@ bool AlloyBrowserHostImpl::IsBackgroundHost() { return is_background_host_; } +bool AlloyBrowserHostImpl::CanExecuteChromeCommand(int command_id) { + return false; +} + +void AlloyBrowserHostImpl::ExecuteChromeCommand( + int command_id, + cef_window_open_disposition_t disposition) { + NOTIMPLEMENTED(); +} + bool AlloyBrowserHostImpl::IsWindowRenderingDisabled() { return IsWindowless(); } diff --git a/libcef/browser/alloy/alloy_browser_host_impl.h b/libcef/browser/alloy/alloy_browser_host_impl.h index ac2f9f46d..e2298ba37 100644 --- a/libcef/browser/alloy/alloy_browser_host_impl.h +++ b/libcef/browser/alloy/alloy_browser_host_impl.h @@ -85,10 +85,6 @@ class AlloyBrowserHostImpl : public CefBrowserHostBase, bool matchCase, bool findNext) override; void StopFinding(bool clearSelection) override; - void ShowDevTools(const CefWindowInfo& windowInfo, - CefRefPtr client, - const CefBrowserSettings& settings, - const CefPoint& inspect_element_at) override; void CloseDevTools() override; bool HasDevTools() override; bool IsWindowRenderingDisabled() override; @@ -127,6 +123,9 @@ class AlloyBrowserHostImpl : public CefBrowserHostBase, const CefSize& max_size) override; CefRefPtr GetExtension() override; bool IsBackgroundHost() override; + bool CanExecuteChromeCommand(int command_id) override; + void ExecuteChromeCommand(int command_id, + cef_window_open_disposition_t disposition) override; // Returns true if windowless rendering is enabled. bool IsWindowless() const override; @@ -285,6 +284,10 @@ class AlloyBrowserHostImpl : public CefBrowserHostBase, override; void WebContentsDestroyed() override; + protected: + void ShowDevToolsOnUIThread( + std::unique_ptr params) override; + private: friend class CefBrowserPlatformDelegateAlloy; diff --git a/libcef/browser/alloy/browser_platform_delegate_alloy.cc b/libcef/browser/alloy/browser_platform_delegate_alloy.cc index 57a96d673..49849bdbc 100644 --- a/libcef/browser/alloy/browser_platform_delegate_alloy.cc +++ b/libcef/browser/alloy/browser_platform_delegate_alloy.cc @@ -39,20 +39,15 @@ content::WebContents* CefBrowserPlatformDelegateAlloy::CreateWebContents( REQUIRE_ALLOY_RUNTIME(); DCHECK(primary_); - // Get or create the request context and browser context. - CefRefPtr request_context_impl = - CefRequestContextImpl::GetOrCreateForRequestContext( - create_params.request_context); - CHECK(request_context_impl); - auto cef_browser_context = request_context_impl->GetBrowserContext(); - CHECK(cef_browser_context); - auto browser_context = cef_browser_context->AsBrowserContext(); - if (!create_params.request_context) { // Using the global request context. - create_params.request_context = request_context_impl.get(); + create_params.request_context = CefRequestContext::GetGlobalContext(); } + auto* browser_context = + CefRequestContextImpl::GetBrowserContext(create_params.request_context); + CHECK(browser_context); + scoped_refptr site_instance; if (extensions::ExtensionsEnabled() && !create_params.url.empty()) { GURL gurl = url_util::MakeGURL(create_params.url, /*fixup=*/true); diff --git a/libcef/browser/browser_host_base.cc b/libcef/browser/browser_host_base.cc index 414d4e087..3f310c4d1 100644 --- a/libcef/browser/browser_host_base.cc +++ b/libcef/browser/browser_host_base.cc @@ -505,6 +505,22 @@ void CefBrowserHostBase::PrintToPDF(const CefString& path, print_util::PrintToPDF(web_contents, path, settings, callback); } +void CefBrowserHostBase::ShowDevTools(const CefWindowInfo& windowInfo, + CefRefPtr client, + const CefBrowserSettings& settings, + const CefPoint& inspect_element_at) { + auto params = std::make_unique( + windowInfo, client, settings, inspect_element_at); + + if (!CEF_CURRENTLY_ON_UIT()) { + CEF_POST_TASK(CEF_UIT, + base::BindOnce(&CefBrowserHostBase::ShowDevToolsOnUIThread, + this, std::move(params))); + } else { + ShowDevToolsOnUIThread(std::move(params)); + } +} + bool CefBrowserHostBase::SendDevToolsMessage(const void* message, size_t message_size) { if (!message || message_size == 0) { diff --git a/libcef/browser/browser_host_base.h b/libcef/browser/browser_host_base.h index 3d7d9c44e..f1724c5d0 100644 --- a/libcef/browser/browser_host_base.h +++ b/libcef/browser/browser_host_base.h @@ -94,6 +94,23 @@ struct CefBrowserCreateParams { extensions::mojom::ViewType::kInvalid; }; +// Parameters passed to ShowDevToolsOnUIThread(). +struct CefShowDevToolsParams { + CefShowDevToolsParams(const CefWindowInfo& windowInfo, + CefRefPtr client, + const CefBrowserSettings& settings, + const CefPoint& inspect_element_at) + : window_info_(windowInfo), + client_(client), + settings_(settings), + inspect_element_at_(inspect_element_at) {} + + CefWindowInfo window_info_; + CefRefPtr client_; + CefBrowserSettings settings_; + CefPoint inspect_element_at_; +}; + // Base class for CefBrowserHost implementations. Includes functionality that is // shared by the alloy and chrome runtimes. All methods are thread-safe unless // otherwise indicated. @@ -191,6 +208,10 @@ class CefBrowserHostBase : public CefBrowserHost, void PrintToPDF(const CefString& path, const CefPdfPrintSettings& settings, CefRefPtr callback) override; + void ShowDevTools(const CefWindowInfo& windowInfo, + CefRefPtr client, + const CefBrowserSettings& settings, + const CefPoint& inspect_element_at) override; void ReplaceMisspelling(const CefString& word) override; void AddWordToDictionary(const CefString& word) override; void SendKeyEvent(const CefKeyEvent& event) override; @@ -346,6 +367,10 @@ class CefBrowserHostBase : public CefBrowserHost, // Called from LoadMainFrameURL to perform the actual navigation. virtual bool Navigate(const content::OpenURLParams& params); + // Called from ShowDevTools to perform the actual show. + virtual void ShowDevToolsOnUIThread( + std::unique_ptr params) = 0; + // Create the CefFileDialogManager if it doesn't already exist. bool EnsureFileDialogManager(); diff --git a/libcef/browser/browser_info.cc b/libcef/browser/browser_info.cc index 1b6f5ffb3..d77d3f2a4 100644 --- a/libcef/browser/browser_info.cc +++ b/libcef/browser/browser_info.cc @@ -33,6 +33,16 @@ CefBrowserInfo::CefBrowserInfo(int browser_id, is_windowless_(is_windowless), extra_info_(extra_info) { DCHECK_GT(browser_id, 0); + + if (extra_info_ && !extra_info_->IsReadOnly()) { + // |extra_info_| should always be read-only to avoid accidental future + // modification. Take a copy instead of modifying the passed-in object for + // backwards compatibility. + extra_info_ = extra_info_->Copy(/*exclude_empty_children=*/false); + auto extra_info_impl = + static_cast(extra_info_.get()); + extra_info_impl->MarkReadOnly(); + } } CefBrowserInfo::~CefBrowserInfo() { diff --git a/libcef/browser/browser_info_manager.cc b/libcef/browser/browser_info_manager.cc index 7f0c9df4f..39feaaf72 100644 --- a/libcef/browser/browser_info_manager.cc +++ b/libcef/browser/browser_info_manager.cc @@ -193,15 +193,8 @@ bool CefBrowserInfoManager::CanCreateWindow( pending_popup->use_default_browser_creation = true; } - // In most cases, Views-hosted browsers should create Views-hosted popups - // and native browsers should use default popup handling. With the Chrome - // runtime, we should additionally use default handling (a) when using an - // external parent and (b) when using default Browser creation. - create_params.popup_with_views_hosted_opener = - browser->HasView() && - !browser->platform_delegate()->HasExternalParent() && - !pending_popup->use_default_browser_creation; - + create_params.popup_with_views_hosted_opener = ShouldCreateViewsHostedPopup( + browser, pending_popup->use_default_browser_creation); create_params.settings = pending_popup->settings; create_params.client = pending_popup->client; create_params.extra_info = pending_popup->extra_info; @@ -420,6 +413,19 @@ bool CefBrowserInfoManager::MaybeAllowNavigation( return true; } +// static +bool CefBrowserInfoManager::ShouldCreateViewsHostedPopup( + CefRefPtr opener, + bool use_default_browser_creation) { + // In most cases, Views-hosted browsers should create Views-hosted popups + // and native browsers should use default popup handling. With the Chrome + // runtime, we should additionally use default handling (a) when using an + // external parent and (b) when using default Browser creation. + return opener->HasView() && + !opener->platform_delegate()->HasExternalParent() && + !use_default_browser_creation; +} + CefBrowserInfoManager::BrowserInfoList CefBrowserInfoManager::GetBrowserInfoList() { base::AutoLock lock_scope(browser_info_lock_); diff --git a/libcef/browser/browser_info_manager.h b/libcef/browser/browser_info_manager.h index bbe1f6a57..1ccc4def3 100644 --- a/libcef/browser/browser_info_manager.h +++ b/libcef/browser/browser_info_manager.h @@ -143,6 +143,9 @@ class CefBrowserInfoManager : public content::RenderProcessHostObserver { const content::OpenURLParams& params, CefRefPtr& browser) const; + static bool ShouldCreateViewsHostedPopup(CefRefPtr opener, + bool use_default_browser_creation); + private: // RenderProcessHostObserver methods: void RenderProcessHostDestroyed(content::RenderProcessHost* host) override; diff --git a/libcef/browser/chrome/browser_delegate.h b/libcef/browser/chrome/browser_delegate.h index 12f5fd07b..5e9b53dda 100644 --- a/libcef/browser/chrome/browser_delegate.h +++ b/libcef/browser/chrome/browser_delegate.h @@ -16,6 +16,7 @@ #include "ui/base/window_open_disposition.h" class Browser; +class Profile; namespace cef { @@ -40,6 +41,14 @@ class BrowserDelegate : public content::WebContentsDelegate { ~BrowserDelegate() override {} + // Optionally override Browser creation in + // DevToolsWindow::CreateDevToolsBrowser. The returned Browser, if any, will + // take ownership of |devtools_contents|. + virtual Browser* CreateDevToolsBrowser( + Profile* profile, + Browser* opener, + std::unique_ptr& devtools_contents) = 0; + // Optionally override chrome::AddWebContents behavior. This is most often // called via Browser::AddNewContents for new popup browsers and provides an // opportunity for CEF to create a new Browser instead of proceeding with diff --git a/libcef/browser/chrome/chrome_browser_delegate.cc b/libcef/browser/chrome/chrome_browser_delegate.cc index 1432b5215..083073fb1 100644 --- a/libcef/browser/chrome/chrome_browser_delegate.cc +++ b/libcef/browser/chrome/chrome_browser_delegate.cc @@ -12,6 +12,7 @@ #include "libcef/browser/browser_platform_delegate.h" #include "libcef/browser/chrome/chrome_browser_host_impl.h" #include "libcef/browser/chrome/views/chrome_browser_view.h" +#include "libcef/browser/chrome/views/chrome_child_window.h" #include "libcef/browser/media_access_query.h" #include "libcef/browser/request_context_impl.h" #include "libcef/browser/views/browser_view_impl.h" @@ -38,7 +39,8 @@ ChromeBrowserDelegate::ChromeBrowserDelegate( DCHECK(browser_); if (opener) { - DCHECK(browser->is_type_picture_in_picture()); + DCHECK(browser->is_type_picture_in_picture() || + browser->is_type_devtools()); auto opener_host = ChromeBrowserHostImpl::GetBrowserForBrowser(opener); DCHECK(opener_host); if (opener_host) { @@ -49,6 +51,134 @@ ChromeBrowserDelegate::ChromeBrowserDelegate( ChromeBrowserDelegate::~ChromeBrowserDelegate() = default; +Browser* ChromeBrowserDelegate::CreateDevToolsBrowser( + Profile* profile, + Browser* opener, + std::unique_ptr& devtools_contents) { + // |opener| is the same value that will be passed to the ChromeBrowserDelegate + // constructor for the new popup Browser. It may be nullptr in certain rare + // situations (e.g. if DevTools is launched for a WebContents that is not a + // Browser Tab). In that case, the popup browser host will instead be created + // via SetAsDelegate. + auto opener_browser_host = + opener ? ChromeBrowserHostImpl::GetBrowserForBrowser(opener) : nullptr; + if (!opener_browser_host) { + return nullptr; + } + + // We expect openers and popups to have the same Profile. + CHECK_EQ( + CefRequestContextImpl::GetProfile(opener_browser_host->request_context()), + profile); + + // + // 1. Get configuration settings from the user and create the new platform + // delegate. Logical equivalent of CefBrowserInfoManager::CanCreateWindow() + // for normal popups. + // + + auto opener_client = opener_browser_host->GetClient(); + auto life_span_handler = + opener_client ? opener_client->GetLifeSpanHandler() : nullptr; + + CefBrowserCreateParams create_params; + CefWindowInfo window_info; + + // If |client| is empty, or if the user clears |client| in + // OnBeforeDevToolsPopup, we'll use the result of GetDefaultClient() later on + // in CreateBrowserHost(). + if (pending_show_devtools_params_) { + // Start with the params passed to CefBrowserHost::ShowDevTools(). + create_params.client = pending_show_devtools_params_->client_; + create_params.settings = pending_show_devtools_params_->settings_; + window_info = pending_show_devtools_params_->window_info_; + + // Pending params are only used a single time. + pending_show_devtools_params_.reset(); + } else { + // Start with the same client and settings as the opener. + create_params.client = opener_client; + create_params.settings = opener_browser_host->settings(); + +#if BUILDFLAG(IS_WIN) + window_info.SetAsPopup(nullptr, CefString()); +#endif + } + + // Start with the same extra info as the opener, for consistency with + // current Alloy runtime behavior (see CefDevToolsFrontend::Show). This + // value, if non-empty, will be read-only. + create_params.extra_info = opener_browser_host->browser_info()->extra_info(); + DCHECK(!create_params.extra_info || create_params.extra_info->IsReadOnly()); + + // Use default (non-Views-hosted) window if OnBeforeDevToolsPopup is + // unhandled. + bool use_default_window = !life_span_handler; + + if (life_span_handler) { + life_span_handler->OnBeforeDevToolsPopup( + opener_browser_host.get(), window_info, create_params.client, + create_params.settings, create_params.extra_info, &use_default_window); + } + + if (opener_browser_host->platform_delegate()->HasExternalParent()) { + // A parent window handle for DevTools creation is only supported if the + // opener also has an external parent. + create_params.MaybeSetWindowInfo(window_info); + } else if (chrome_child_window::HasParentHandle(window_info)) { + LOG(ERROR) << "Parent window handle not supported for this DevTools window"; + } + + create_params.popup_with_views_hosted_opener = + CefBrowserInfoManager::ShouldCreateViewsHostedPopup(opener_browser_host, + use_default_window); + + auto platform_delegate = CefBrowserPlatformDelegate::Create(create_params); + CHECK(platform_delegate); + + // + // 2. Create the new browser host. Logical equivalent of WebContentsCreated() + // for normal popups. + // + + // Create a new browser host that remains alive until the associated + // WebContents is destroyed. Associate that browser host with the WebContents + // and execute initial client callbacks. Deliver required information to the + // renderer process. + auto browser_host = CreateBrowserHostForPopup( + devtools_contents.get(), create_params.settings, create_params.client, + create_params.extra_info, std::move(platform_delegate), + /*is_devtools_popup=*/true, opener_browser_host); + + // + // 3. Create the new Browser. Logical equivalent of AddWebContents() for + // normal popups. + // + + // Use Browser creation params specific to DevTools popups. + auto chrome_params = Browser::CreateParams::CreateForDevTools(profile); + + // Pass |opener| to the ChromeBrowserDelegate constructor for the new popup + // Browser. + chrome_params.opener = opener; + + // Create a new Browser and give it ownership of the new WebContents. + // Results in a call to SetAsDelegate to associate the Browser with the + // browser host. + browser_host->AddNewContents(std::move(devtools_contents), + std::move(chrome_params)); + + // Give the opener browser a reference to the new DevTools browser. Do this + // last because don't want the client to attempt access to the DevTools + // browser via opener browser methods (e.g. ShowDevTools, CloseDevTools, etc) + // while creation is still in progress. + opener_browser_host->SetDevToolsBrowserHost(browser_host->GetWeakPtr()); + + auto browser = browser_host->browser(); + CHECK(browser); + return browser; +} + std::unique_ptr ChromeBrowserDelegate::AddWebContents( std::unique_ptr new_contents) { if (CefBrowserInfoManager::GetInstance()->AddWebContents( @@ -58,7 +188,9 @@ std::unique_ptr ChromeBrowserDelegate::AddWebContents( ChromeBrowserHostImpl::GetBrowserForContents(new_contents.get()); if (new_browser) { // Create a new Browser and give it ownership of the new WebContents. - new_browser->AddNewContents(std::move(new_contents)); + // Results in a call to SetAsDelegate to associate the Browser with the + // browser host. + new_browser->AddNewContents(std::move(new_contents), std::nullopt); } else { LOG(ERROR) << "No host found for chrome popup browser"; } @@ -92,19 +224,26 @@ void ChromeBrowserDelegate::SetAsDelegate(content::WebContents* web_contents, return; } + const bool is_devtools_popup = browser_->is_type_devtools(); + + // We should never reach here for DevTools popups that have an opener, as + // CreateDevToolsBrowser should have already created the browser host. + DCHECK(!is_devtools_popup || !opener_host_); + auto platform_delegate = CefBrowserPlatformDelegate::Create(create_params_); CHECK(platform_delegate); auto browser_info = CefBrowserInfoManager::GetInstance()->CreateBrowserInfo( - /*is_popup=*/false, /*is_windowless=*/false, create_params_.extra_info); + is_devtools_popup, /*is_windowless=*/false, create_params_.extra_info); auto request_context_impl = CefRequestContextImpl::GetOrCreateForRequestContext( create_params_.request_context); - CreateBrowser(web_contents, create_params_.settings, create_params_.client, - std::move(platform_delegate), browser_info, /*opener=*/nullptr, - request_context_impl); + CreateBrowserHost(web_contents, create_params_.settings, + create_params_.client, std::move(platform_delegate), + browser_info, is_devtools_popup, /*opener=*/nullptr, + request_context_impl); } bool ChromeBrowserDelegate::ShowStatusBubble(bool show_by_default) { @@ -125,6 +264,11 @@ bool ChromeBrowserDelegate::ShowStatusBubble(bool show_by_default) { bool ChromeBrowserDelegate::HandleCommand(int command_id, WindowOpenDisposition disposition) { + // Verify that our enum matches Chromium's values. + static_assert(static_cast(CEF_WOD_MAX_VALUE) == + static_cast(WindowOpenDisposition::MAX_VALUE), + "enum mismatch"); + if (auto browser = ChromeBrowserHostImpl::GetBrowserForBrowser(browser_)) { if (auto client = browser->GetClient()) { if (auto handler = client->GetCommandHandler()) { @@ -301,19 +445,13 @@ void ChromeBrowserDelegate::WebContentsCreated( return; } - auto browser_info = - CefBrowserInfoManager::GetInstance()->CreatePopupBrowserInfo( - new_contents, /*is_windowless=*/false, extra_info); - CHECK(browser_info->is_popup()); - - // Popups must share the same RequestContext as the parent. - auto request_context_impl = opener->request_context(); - CHECK(request_context_impl); - - // We don't officially own |new_contents| until AddNewContents() is called. - // However, we need to install observers/delegates here. - CreateBrowser(new_contents, settings, client, std::move(platform_delegate), - browser_info, opener, request_context_impl); + // Create a new browser host that remains alive until the associated + // WebContents is destroyed. Associate that browser host with the WebContents + // and execute initial client callbacks. Deliver required information to the + // renderer process. + CreateBrowserHostForPopup(new_contents, settings, client, extra_info, + std::move(platform_delegate), + /*is_devtools_popup=*/false, opener); } content::WebContents* ChromeBrowserDelegate::OpenURLFromTab( @@ -423,12 +561,19 @@ bool ChromeBrowserDelegate::HandleKeyboardEvent( return false; } -void ChromeBrowserDelegate::CreateBrowser( +void ChromeBrowserDelegate::SetPendingShowDevToolsParams( + std::unique_ptr params) { + DCHECK(!pending_show_devtools_params_); + pending_show_devtools_params_ = std::move(params); +} + +CefRefPtr ChromeBrowserDelegate::CreateBrowserHost( content::WebContents* web_contents, - CefBrowserSettings settings, + const CefBrowserSettings& settings, CefRefPtr client, std::unique_ptr platform_delegate, scoped_refptr browser_info, + bool is_devtools_popup, CefRefPtr opener, CefRefPtr request_context_impl) { CEF_REQUIRE_UIT(); @@ -464,12 +609,40 @@ void ChromeBrowserDelegate::CreateBrowser( CefRefPtr browser_host = new ChromeBrowserHostImpl(settings, client, std::move(platform_delegate), browser_info, request_context_impl); - browser_host->Attach(web_contents, opener); + browser_host->Attach(web_contents, is_devtools_popup, opener); - // The Chrome browser for a popup won't be created until AddNewContents(). + // The Chrome browser for a normal popup won't be created until + // AddNewContents(). if (!opener) { browser_host->SetBrowser(browser_); } + + return browser_host; +} + +CefRefPtr +ChromeBrowserDelegate::CreateBrowserHostForPopup( + content::WebContents* web_contents, + const CefBrowserSettings& settings, + CefRefPtr client, + CefRefPtr extra_info, + std::unique_ptr platform_delegate, + bool is_devtools_popup, + CefRefPtr opener) { + auto browser_info = + CefBrowserInfoManager::GetInstance()->CreatePopupBrowserInfo( + web_contents, /*is_windowless=*/false, extra_info); + CHECK(browser_info->is_popup()); + + // Popups must share the same RequestContext as the parent. + auto request_context_impl = opener->request_context(); + CHECK(request_context_impl); + + // We don't officially own |web_contents| until AddNewContents() is called. + // However, we need to install observers/delegates here. + return CreateBrowserHost(web_contents, settings, client, + std::move(platform_delegate), browser_info, + is_devtools_popup, opener, request_context_impl); } CefBrowserContentsDelegate* ChromeBrowserDelegate::GetDelegateForWebContents( diff --git a/libcef/browser/chrome/chrome_browser_delegate.h b/libcef/browser/chrome/chrome_browser_delegate.h index 9a70d3687..c25a95c5b 100644 --- a/libcef/browser/chrome/chrome_browser_delegate.h +++ b/libcef/browser/chrome/chrome_browser_delegate.h @@ -39,6 +39,9 @@ class ChromeBrowserHostImpl; // but the Browser object will change when the tab is dragged between windows. class ChromeBrowserDelegate : public cef::BrowserDelegate { public: + // The |create_params| and |opener| values are specified via the + // Browser::CreateParams passed to Browser::Create. |opener| will only be + // specified for certain special Browser types. ChromeBrowserDelegate(Browser* browser, const CefBrowserCreateParams& create_params, const Browser* opener); @@ -49,6 +52,10 @@ class ChromeBrowserDelegate : public cef::BrowserDelegate { ~ChromeBrowserDelegate() override; // cef::BrowserDelegate methods: + Browser* CreateDevToolsBrowser( + Profile* profile, + Browser* opener, + std::unique_ptr& devtools_contents) override; std::unique_ptr AddWebContents( std::unique_ptr new_contents) override; void OnWebContentsCreated(content::WebContents* new_contents) override; @@ -104,18 +111,31 @@ class ChromeBrowserDelegate : public cef::BrowserDelegate { content::WebContents* source, const content::NativeWebKeyboardEvent& event) override; + void SetPendingShowDevToolsParams( + std::unique_ptr params); + Browser* browser() const { return browser_; } private: - void CreateBrowser( + CefRefPtr CreateBrowserHost( content::WebContents* web_contents, - CefBrowserSettings settings, + const CefBrowserSettings& settings, CefRefPtr client, std::unique_ptr platform_delegate, scoped_refptr browser_info, + bool is_devtools_popup, CefRefPtr opener, CefRefPtr request_context_impl); + CefRefPtr CreateBrowserHostForPopup( + content::WebContents* web_contents, + const CefBrowserSettings& settings, + CefRefPtr client, + CefRefPtr extra_info, + std::unique_ptr platform_delegate, + bool is_devtools_popup, + CefRefPtr opener); + CefBrowserContentsDelegate* GetDelegateForWebContents( content::WebContents* web_contents); @@ -130,6 +150,8 @@ class ChromeBrowserDelegate : public cef::BrowserDelegate { absl::optional show_status_bubble_; absl::optional draggable_region_; mutable absl::optional frameless_pip_; + + std::unique_ptr pending_show_devtools_params_; }; #endif // CEF_LIBCEF_BROWSER_CHROME_CHROME_BROWSER_DELEGATE_H_ diff --git a/libcef/browser/chrome/chrome_browser_host_impl.cc b/libcef/browser/chrome/chrome_browser_host_impl.cc index d05d5a862..93dc9882a 100644 --- a/libcef/browser/chrome/chrome_browser_host_impl.cc +++ b/libcef/browser/chrome/chrome_browser_host_impl.cc @@ -6,6 +6,7 @@ #include "libcef/browser/browser_platform_delegate.h" #include "libcef/browser/chrome/browser_platform_delegate_chrome.h" +#include "libcef/browser/chrome/chrome_browser_delegate.h" #include "libcef/browser/thread_util.h" #include "libcef/browser/views/browser_view_impl.h" #include "libcef/common/net/url_util.h" @@ -13,8 +14,8 @@ #include "base/logging.h" #include "base/notreached.h" +#include "chrome/browser/devtools/devtools_window.h" #include "chrome/browser/profiles/profile.h" -#include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_commands.h" #include "chrome/browser/ui/browser_navigator.h" #include "chrome/browser/ui/browser_tabstrip.h" @@ -28,7 +29,7 @@ // static CefRefPtr ChromeBrowserHostImpl::Create( const CefBrowserCreateParams& params) { - auto browser = CreateBrowser(params); + auto browser = CreateBrowser(params, std::nullopt); GURL url = url_util::MakeGURL(params.url, /*fixup=*/true); if (url.is_empty()) { @@ -99,7 +100,8 @@ CefRefPtr ChromeBrowserHostImpl::GetBrowserForBrowser( ChromeBrowserHostImpl::~ChromeBrowserHostImpl() = default; void ChromeBrowserHostImpl::AddNewContents( - std::unique_ptr contents) { + std::unique_ptr contents, + std::optional browser_create_params) { DCHECK(contents); DCHECK(!browser_); @@ -111,7 +113,7 @@ void ChromeBrowserHostImpl::AddNewContents( params.browser_view = GetBrowserView(); // Create the new Browser representation. - auto browser = CreateBrowser(params); + auto browser = CreateBrowser(params, std::move(browser_create_params)); // Add the WebContents to the Browser. browser->tab_strip_model()->AddWebContents( @@ -199,20 +201,66 @@ void ChromeBrowserHostImpl::StopFinding(bool clearSelection) { NOTIMPLEMENTED(); } -void ChromeBrowserHostImpl::ShowDevTools(const CefWindowInfo& windowInfo, - CefRefPtr client, - const CefBrowserSettings& settings, - const CefPoint& inspect_element_at) { - NOTIMPLEMENTED(); +void ChromeBrowserHostImpl::ShowDevToolsOnUIThread( + std::unique_ptr params) { + CEF_REQUIRE_UIT(); + + if (!browser_) { + return; + } + + auto* web_contents = GetWebContents(); + if (!web_contents) { + return; + } + + auto* profile = CefRequestContextImpl::GetProfile(request_context()); + if (!DevToolsWindow::AllowDevToolsFor(profile, web_contents)) { + LOG(WARNING) << "DevTools is not allowed for this browser"; + return; + } + + auto inspect_element_at = params->inspect_element_at_; + + if (!devtools_browser_host_) { + // Configure parameters for ChromeBrowserDelegate::CreateDevToolsBrowser + // which will be called indirectly to create the DevTools window. + auto chrome_browser_delegate = + static_cast(browser_->cef_delegate()); + chrome_browser_delegate->SetPendingShowDevToolsParams(std::move(params)); + } + + // Focus the existing DevTools window or create a new one. + if (!inspect_element_at.IsEmpty()) { + DevToolsWindow::InspectElement(web_contents->GetPrimaryMainFrame(), + inspect_element_at.x, inspect_element_at.y); + } else { + DevToolsWindow::OpenDevToolsWindow(web_contents, profile); + } + + // The DevTools browser host should now exist. + DCHECK(devtools_browser_host_); } void ChromeBrowserHostImpl::CloseDevTools() { - NOTIMPLEMENTED(); + if (!CEF_CURRENTLY_ON_UIT()) { + CEF_POST_TASK(CEF_UIT, + base::BindOnce(&ChromeBrowserHostImpl::CloseDevTools, this)); + return; + } + + if (devtools_browser_host_) { + devtools_browser_host_->TryCloseBrowser(); + } } bool ChromeBrowserHostImpl::HasDevTools() { - NOTIMPLEMENTED(); - return false; + if (!CEF_CURRENTLY_ON_UIT()) { + DCHECK(false) << "called on invalid thread"; + return false; + } + + return !!devtools_browser_host_; } bool ChromeBrowserHostImpl::IsWindowRenderingDisabled() { @@ -332,6 +380,36 @@ bool ChromeBrowserHostImpl::IsBackgroundHost() { return false; } +bool ChromeBrowserHostImpl::CanExecuteChromeCommand(int command_id) { + // Verify that this method is being called on the UI thread. + if (!CEF_CURRENTLY_ON_UIT()) { + DCHECK(false) << "called on invalid thread"; + return false; + } + + if (browser_) { + return chrome::SupportsCommand(browser_, command_id) && + chrome::IsCommandEnabled(browser_, command_id); + } + return false; +} + +void ChromeBrowserHostImpl::ExecuteChromeCommand( + int command_id, + cef_window_open_disposition_t disposition) { + if (!CEF_CURRENTLY_ON_UIT()) { + CEF_POST_TASK(CEF_UIT, + base::BindOnce(&ChromeBrowserHostImpl::ExecuteChromeCommand, + this, command_id, disposition)); + return; + } + + if (browser_) { + chrome::ExecuteCommandWithDisposition( + browser_, command_id, static_cast(disposition)); + } +} + ChromeBrowserView* ChromeBrowserHostImpl::chrome_browser_view() const { if (browser_ && is_views_hosted_) { return static_cast(browser_->window()); @@ -389,19 +467,16 @@ ChromeBrowserHostImpl::ChromeBrowserHostImpl( // static Browser* ChromeBrowserHostImpl::CreateBrowser( - const CefBrowserCreateParams& params) { - // Get or create the request context and profile. - CefRefPtr request_context_impl = - CefRequestContextImpl::GetOrCreateForRequestContext( - params.request_context); - CHECK(request_context_impl); - auto cef_browser_context = request_context_impl->GetBrowserContext(); - CHECK(cef_browser_context); - auto profile = cef_browser_context->AsProfile(); - CHECK(profile); - - Browser::CreateParams chrome_params = - Browser::CreateParams(profile, /*user_gesture=*/false); + const CefBrowserCreateParams& params, + std::optional browser_create_params) { + Browser::CreateParams chrome_params = [¶ms, &browser_create_params]() { + if (!browser_create_params.has_value()) { + auto* profile = CefRequestContextImpl::GetProfile(params.request_context); + return Browser::CreateParams(profile, /*user_gesture=*/false); + } else { + return std::move(*browser_create_params); + } + }(); // Pass |params| to cef::BrowserDelegate::Create from the Browser constructor. chrome_params.cef_params = base::MakeRefCounted(params); @@ -412,10 +487,12 @@ Browser* ChromeBrowserHostImpl::CreateBrowser( // documentation. ChromeBrowserView* chrome_browser_view = nullptr; if (params.browser_view) { - // Don't show most controls. - chrome_params.type = Browser::TYPE_POPUP; - // Don't show title bar or address. - chrome_params.trusted_source = true; + if (chrome_params.type == Browser::TYPE_NORMAL) { + // Don't show most controls. + chrome_params.type = Browser::TYPE_POPUP; + // Don't show title bar or address. + chrome_params.trusted_source = true; + } auto view_impl = static_cast(params.browser_view.get()); @@ -456,6 +533,7 @@ Browser* ChromeBrowserHostImpl::CreateBrowser( } void ChromeBrowserHostImpl::Attach(content::WebContents* web_contents, + bool is_devtools_popup, CefRefPtr opener) { DCHECK(web_contents); @@ -464,7 +542,7 @@ void ChromeBrowserHostImpl::Attach(content::WebContents* web_contents, // new browser's platform delegate. opener->platform_delegate_->PopupWebContentsCreated( settings_, client_, web_contents, platform_delegate_.get(), - /*is_devtools_popup=*/false); + is_devtools_popup); } platform_delegate_->WebContentsCreated(web_contents, @@ -480,7 +558,7 @@ void ChromeBrowserHostImpl::Attach(content::WebContents* web_contents, // Notify that the browser has been created. These must be delivered in the // expected order. - if (opener && opener->platform_delegate_) { + if (opener) { // 1. Notify the opener browser's platform delegate. With Views this will // result in a call to CefBrowserViewDelegate::OnPopupBrowserViewCreated(). // We want to call this method first because the implementation will often @@ -488,9 +566,7 @@ void ChromeBrowserHostImpl::Attach(content::WebContents* web_contents, // CefBrowserHost::GetWindowHandle() will return kNullWindowHandle in // OnAfterCreated(), which breaks client expectations (e.g. clients expect // everything about the browser to be valid at that time). - opener->platform_delegate_->PopupBrowserCreated( - this, - /*is_devtools_popup=*/false); + opener->platform_delegate_->PopupBrowserCreated(this, is_devtools_popup); } // 2. Notify the browser's LifeSpanHandler. This must always be the first @@ -509,14 +585,27 @@ void ChromeBrowserHostImpl::Attach(content::WebContents* web_contents, void ChromeBrowserHostImpl::SetBrowser(Browser* browser) { CEF_REQUIRE_UIT(); + if (browser == browser_) { + return; + } + browser_ = browser; static_cast(platform_delegate_.get()) ->set_chrome_browser(browser); if (browser_) { host_window_handle_ = platform_delegate_->GetHostWindowHandle(); + } else { + host_window_handle_ = kNullWindowHandle; } } +void ChromeBrowserHostImpl::SetDevToolsBrowserHost( + base::WeakPtr devtools_browser_host) { + CEF_REQUIRE_UIT(); + DCHECK(!devtools_browser_host_); + devtools_browser_host_ = devtools_browser_host; +} + void ChromeBrowserHostImpl::WindowDestroyed() { CEF_REQUIRE_UIT(); if (auto view = chrome_browser_view()) { diff --git a/libcef/browser/chrome/chrome_browser_host_impl.h b/libcef/browser/chrome/chrome_browser_host_impl.h index d6c27c96a..fb162fa1a 100644 --- a/libcef/browser/chrome/chrome_browser_host_impl.h +++ b/libcef/browser/chrome/chrome_browser_host_impl.h @@ -12,8 +12,8 @@ #include "libcef/browser/chrome/browser_delegate.h" #include "base/memory/weak_ptr.h" +#include "chrome/browser/ui/browser.h" -class Browser; class ChromeBrowserDelegate; class ChromeBrowserView; @@ -72,10 +72,6 @@ class ChromeBrowserHostImpl : public CefBrowserHostBase { bool matchCase, bool findNext) override; void StopFinding(bool clearSelection) override; - void ShowDevTools(const CefWindowInfo& windowInfo, - CefRefPtr client, - const CefBrowserSettings& settings, - const CefPoint& inspect_element_at) override; void CloseDevTools() override; bool HasDevTools() override; bool IsWindowRenderingDisabled() override; @@ -114,6 +110,9 @@ class ChromeBrowserHostImpl : public CefBrowserHostBase { const CefSize& max_size) override; CefRefPtr GetExtension() override; bool IsBackgroundHost() override; + bool CanExecuteChromeCommand(int command_id) override; + void ExecuteChromeCommand(int command_id, + cef_window_open_disposition_t disposition) override; Browser* browser() const { return browser_; } @@ -126,6 +125,8 @@ class ChromeBrowserHostImpl : public CefBrowserHostBase { protected: bool Navigate(const content::OpenURLParams& params) override; + void ShowDevToolsOnUIThread( + std::unique_ptr params) override; private: friend class ChromeBrowserDelegate; @@ -138,22 +139,32 @@ class ChromeBrowserHostImpl : public CefBrowserHostBase { CefRefPtr request_context); // Create a new Browser without initializing the WebContents. - static Browser* CreateBrowser(const CefBrowserCreateParams& params); + // |browser_create_params| may be empty for default Browser creation behavior. + static Browser* CreateBrowser( + const CefBrowserCreateParams& params, + std::optional browser_create_params); // Called from ChromeBrowserDelegate::CreateBrowser when this object is first // created. Must be called on the UI thread. void Attach(content::WebContents* web_contents, + bool is_devtools_popup, CefRefPtr opener); // Called from ChromeBrowserDelegate::AddNewContents to take ownership of a - // popup WebContents. - void AddNewContents(std::unique_ptr contents); + // popup WebContents. |browser_create_params| may be empty for default Browser + // creation behavior. + void AddNewContents( + std::unique_ptr contents, + std::optional browser_create_params); // Called when this object changes Browser ownership (e.g. initially created, // dragging between windows, etc). The old Browser, if any, will be cleared // before the new Browser is added. Must be called on the UI thread. void SetBrowser(Browser* browser); + void SetDevToolsBrowserHost( + base::WeakPtr devtools_browser_host); + // CefBrowserHostBase methods: void WindowDestroyed() override; bool WillBeDestroyed() const override; @@ -168,6 +179,8 @@ class ChromeBrowserHostImpl : public CefBrowserHostBase { Browser* browser_ = nullptr; CefWindowHandle host_window_handle_ = kNullWindowHandle; + base::WeakPtr devtools_browser_host_; + base::WeakPtrFactory weak_ptr_factory_{this}; }; diff --git a/libcef/browser/chrome/views/chrome_browser_frame.cc b/libcef/browser/chrome/views/chrome_browser_frame.cc index c6eae43f5..99608541d 100644 --- a/libcef/browser/chrome/views/chrome_browser_frame.cc +++ b/libcef/browser/chrome/views/chrome_browser_frame.cc @@ -4,6 +4,8 @@ #include "libcef/browser/chrome/views/chrome_browser_frame.h" +#include "libcef/browser/chrome/chrome_browser_host_impl.h" + #include "chrome/browser/themes/theme_service.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_commands.h" @@ -57,3 +59,24 @@ ChromeBrowserFrame::CreateNonClientFrameView() { // Bypass the BrowserFrame implementation. return views::Widget::CreateNonClientFrameView(); } + +void ChromeBrowserFrame::Activate() { + if (browser_view_ && browser_view_->browser() && + browser_view_->browser()->is_type_devtools()) { + if (auto browser_host = ChromeBrowserHostImpl::GetBrowserForBrowser( + browser_view_->browser())) { + if (browser_host->platform_delegate()->HasExternalParent()) { + // Handle activation of DevTools with external parent via the platform + // delegate. On Windows the default platform implementation + // (HWNDMessageHandler::Activate) will call SetForegroundWindow but that + // doesn't seem to work for DevTools windows when activated via the + // right-click context menu. + browser_host->SetFocus(true); + return; + } + } + } + + // Proceed with default handling. + BrowserFrame::Activate(); +} diff --git a/libcef/browser/chrome/views/chrome_browser_frame.h b/libcef/browser/chrome/views/chrome_browser_frame.h index e8ffa3bd7..ffd823e22 100644 --- a/libcef/browser/chrome/views/chrome_browser_frame.h +++ b/libcef/browser/chrome/views/chrome_browser_frame.h @@ -103,6 +103,7 @@ class ChromeBrowserFrame : public BrowserFrame { views::internal::RootView* CreateRootView() override; std::unique_ptr CreateNonClientFrameView() override; + void Activate() override; BrowserView* browser_view() const { return browser_view_; } diff --git a/libcef/browser/chrome/views/chrome_child_window.cc b/libcef/browser/chrome/views/chrome_child_window.cc index 9d1368ac6..e37561c45 100644 --- a/libcef/browser/chrome/views/chrome_child_window.cc +++ b/libcef/browser/chrome/views/chrome_child_window.cc @@ -126,8 +126,6 @@ class ChildBrowserViewDelegate : public CefBrowserViewDelegate { bool OnPopupBrowserViewCreated(CefRefPtr browser_view, CefRefPtr popup_browser_view, bool is_devtools) override { - DCHECK(!is_devtools); - auto new_browser = static_cast( popup_browser_view->GetBrowser().get()); auto new_platform_delegate = new_browser->platform_delegate(); diff --git a/libcef/browser/net_service/browser_urlrequest_impl.cc b/libcef/browser/net_service/browser_urlrequest_impl.cc index 7433191a3..90f9e2683 100644 --- a/libcef/browser/net_service/browser_urlrequest_impl.cc +++ b/libcef/browser/net_service/browser_urlrequest_impl.cc @@ -202,14 +202,8 @@ class CefBrowserURLRequest::Context scoped_refptr task_runner) { CEF_REQUIRE_UIT(); - // Get or create the request context and browser context. - CefRefPtr request_context_impl = - CefRequestContextImpl::GetOrCreateForRequestContext(request_context); - CHECK(request_context_impl); - CefBrowserContext* cef_browser_context = - request_context_impl->GetBrowserContext(); - CHECK(cef_browser_context); - auto browser_context = cef_browser_context->AsBrowserContext(); + auto* browser_context = + CefRequestContextImpl::GetBrowserContext(request_context); CHECK(browser_context); scoped_refptr loader_factory_getter; diff --git a/libcef/browser/request_context_impl.cc b/libcef/browser/request_context_impl.cc index 244eec670..6689f1e6f 100644 --- a/libcef/browser/request_context_impl.cc +++ b/libcef/browser/request_context_impl.cc @@ -89,6 +89,17 @@ class ResolveHostHelper : public network::ResolveHostClientBase { mojo::Receiver receiver_{this}; }; +CefBrowserContext* GetCefBrowserContext( + CefRefPtr request_context) { + CEF_REQUIRE_UIT(); + CefRefPtr request_context_impl = + CefRequestContextImpl::GetOrCreateForRequestContext(request_context); + CHECK(request_context_impl); + auto* cef_browser_context = request_context_impl->GetBrowserContext(); + CHECK(cef_browser_context); + return cef_browser_context; +} + } // namespace // CefBrowserContext @@ -184,6 +195,21 @@ CefRequestContextImpl::GetOrCreateForRequestContext( return CefRequestContextImpl::GetOrCreateRequestContext(config); } +content::BrowserContext* CefRequestContextImpl::GetBrowserContext( + CefRefPtr request_context) { + auto* browser_context = + GetCefBrowserContext(request_context)->AsBrowserContext(); + CHECK(browser_context); + return browser_context; +} + +Profile* CefRequestContextImpl::GetProfile( + CefRefPtr request_context) { + auto* profile = GetCefBrowserContext(request_context)->AsProfile(); + CHECK(profile); + return profile; +} + bool CefRequestContextImpl::VerifyBrowserContext() const { if (!CEF_CURRENTLY_ON_UIT()) { DCHECK(false) << "called on invalid thread"; diff --git a/libcef/browser/request_context_impl.h b/libcef/browser/request_context_impl.h index d2668a9aa..a035fa984 100644 --- a/libcef/browser/request_context_impl.h +++ b/libcef/browser/request_context_impl.h @@ -38,6 +38,15 @@ class CefRequestContextImpl : public CefRequestContext { static CefRefPtr GetOrCreateForRequestContext( CefRefPtr request_context); + // Returns the BrowserContext for the specified |request_context|. Will return + // the global BrowserContext if |request_context| is NULL. + static content::BrowserContext* GetBrowserContext( + CefRefPtr request_context); + + // Returns the Profile for the specified |request_context|. Will return the + // global Profile if |request_context| is NULL. + static Profile* GetProfile(CefRefPtr request_context); + // Verify that the browser context can be directly accessed (e.g. on the UI // thread and initialized). bool VerifyBrowserContext() const; diff --git a/libcef/common/value_base.h b/libcef/common/value_base.h index 6db69963e..c860563d3 100644 --- a/libcef/common/value_base.h +++ b/libcef/common/value_base.h @@ -296,6 +296,13 @@ class CefValueBase : public CefType, public CefValueController::Object { // True if access to the underlying value is read-only. inline bool read_only() const { return read_only_; } + // Convert a writable value to read-only. The reverse could be surprising and + // is therefore not supported. + void MarkReadOnly() { + DCHECK(!read_only_); + read_only_ = true; + } + // True if the underlying value has been detached. inline bool detached() const { return !controller_.get(); } diff --git a/libcef_dll/cpptoc/browser_host_cpptoc.cc b/libcef_dll/cpptoc/browser_host_cpptoc.cc index a96e08d53..2f30f8cf0 100644 --- a/libcef_dll/cpptoc/browser_host_cpptoc.cc +++ b/libcef_dll/cpptoc/browser_host_cpptoc.cc @@ -9,7 +9,7 @@ // implementations. See the translator.README.txt file in the tools directory // for more information. // -// $hash=5358aa617ebb6d7d074e2d346599fbd6777f1770$ +// $hash=a5c3b05b23c536eba7ce2e7242c3840e93729b29$ // #include "libcef_dll/cpptoc/browser_host_cpptoc.h" @@ -1468,6 +1468,44 @@ void CEF_CALLBACK browser_host_exit_fullscreen(struct _cef_browser_host_t* self, : false); } +int CEF_CALLBACK +browser_host_can_execute_chrome_command(struct _cef_browser_host_t* self, + int command_id) { + shutdown_checker::AssertNotShutdown(); + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + DCHECK(self); + if (!self) { + return 0; + } + + // Execute + bool _retval = + CefBrowserHostCppToC::Get(self)->CanExecuteChromeCommand(command_id); + + // Return type: bool + return _retval; +} + +void CEF_CALLBACK +browser_host_execute_chrome_command(struct _cef_browser_host_t* self, + int command_id, + cef_window_open_disposition_t disposition) { + shutdown_checker::AssertNotShutdown(); + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + DCHECK(self); + if (!self) { + return; + } + + // Execute + CefBrowserHostCppToC::Get(self)->ExecuteChromeCommand(command_id, + disposition); +} + } // namespace // CONSTRUCTOR - Do not edit by hand. @@ -1547,6 +1585,9 @@ CefBrowserHostCppToC::CefBrowserHostCppToC() { GetStruct()->is_audio_muted = browser_host_is_audio_muted; GetStruct()->is_fullscreen = browser_host_is_fullscreen; GetStruct()->exit_fullscreen = browser_host_exit_fullscreen; + GetStruct()->can_execute_chrome_command = + browser_host_can_execute_chrome_command; + GetStruct()->execute_chrome_command = browser_host_execute_chrome_command; } // DESTRUCTOR - Do not edit by hand. diff --git a/libcef_dll/cpptoc/life_span_handler_cpptoc.cc b/libcef_dll/cpptoc/life_span_handler_cpptoc.cc index a85ef8e70..bc361ce75 100644 --- a/libcef_dll/cpptoc/life_span_handler_cpptoc.cc +++ b/libcef_dll/cpptoc/life_span_handler_cpptoc.cc @@ -9,7 +9,7 @@ // implementations. See the translator.README.txt file in the tools directory // for more information. // -// $hash=e96fe0660b55afa20c3bb5fdebc85635ccc53d09$ +// $hash=4990c06888649a2cb06ba7028f16f9cd762f3ad0$ // #include "libcef_dll/cpptoc/life_span_handler_cpptoc.h" @@ -169,6 +169,126 @@ int CEF_CALLBACK life_span_handler_on_before_popup( return _retval; } +void CEF_CALLBACK life_span_handler_on_before_dev_tools_popup( + struct _cef_life_span_handler_t* self, + cef_browser_t* browser, + cef_window_info_t* windowInfo, + cef_client_t** client, + struct _cef_browser_settings_t* settings, + struct _cef_dictionary_value_t** extra_info, + int* use_default_window) { + shutdown_checker::AssertNotShutdown(); + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + DCHECK(self); + if (!self) { + return; + } + // Verify param: browser; type: refptr_diff + DCHECK(browser); + if (!browser) { + return; + } + // Verify param: windowInfo; type: struct_byref + DCHECK(windowInfo); + if (!windowInfo) { + return; + } + if (!template_util::has_valid_size(windowInfo)) { + DCHECK(false) << "invalid windowInfo->[base.]size"; + return; + } + // Verify param: client; type: refptr_same_byref + DCHECK(client); + if (!client) { + return; + } + // Verify param: settings; type: struct_byref + DCHECK(settings); + if (!settings) { + return; + } + if (!template_util::has_valid_size(settings)) { + DCHECK(false) << "invalid settings->[base.]size"; + return; + } + // Verify param: extra_info; type: refptr_diff_byref + DCHECK(extra_info); + if (!extra_info) { + return; + } + // Verify param: use_default_window; type: bool_byaddr + DCHECK(use_default_window); + if (!use_default_window) { + return; + } + + // Translate param: windowInfo; type: struct_byref + CefWindowInfo windowInfoObj; + if (windowInfo) { + windowInfoObj.AttachTo(*windowInfo); + } + // Translate param: client; type: refptr_same_byref + CefRefPtr clientPtr; + if (client && *client) { + clientPtr = CefClientCppToC::Unwrap(*client); + } + CefClient* clientOrig = clientPtr.get(); + // Translate param: settings; type: struct_byref + CefBrowserSettings settingsObj; + if (settings) { + settingsObj.AttachTo(*settings); + } + // Translate param: extra_info; type: refptr_diff_byref + CefRefPtr extra_infoPtr; + if (extra_info && *extra_info) { + extra_infoPtr = CefDictionaryValueCToCpp::Wrap(*extra_info); + } + CefDictionaryValue* extra_infoOrig = extra_infoPtr.get(); + // Translate param: use_default_window; type: bool_byaddr + bool use_default_windowBool = + (use_default_window && *use_default_window) ? true : false; + + // Execute + CefLifeSpanHandlerCppToC::Get(self)->OnBeforeDevToolsPopup( + CefBrowserCToCpp::Wrap(browser), windowInfoObj, clientPtr, settingsObj, + extra_infoPtr, &use_default_windowBool); + + // Restore param: windowInfo; type: struct_byref + if (windowInfo) { + windowInfoObj.DetachTo(*windowInfo); + } + // Restore param: client; type: refptr_same_byref + if (client) { + if (clientPtr.get()) { + if (clientPtr.get() != clientOrig) { + *client = CefClientCppToC::Wrap(clientPtr); + } + } else { + *client = nullptr; + } + } + // Restore param: settings; type: struct_byref + if (settings) { + settingsObj.DetachTo(*settings); + } + // Restore param: extra_info; type: refptr_diff_byref + if (extra_info) { + if (extra_infoPtr.get()) { + if (extra_infoPtr.get() != extra_infoOrig) { + *extra_info = CefDictionaryValueCToCpp::Unwrap(extra_infoPtr); + } + } else { + *extra_info = nullptr; + } + } + // Restore param: use_default_window; type: bool_byaddr + if (use_default_window) { + *use_default_window = use_default_windowBool ? true : false; + } +} + void CEF_CALLBACK life_span_handler_on_after_created(struct _cef_life_span_handler_t* self, cef_browser_t* browser) { @@ -244,6 +364,8 @@ life_span_handler_on_before_close(struct _cef_life_span_handler_t* self, CefLifeSpanHandlerCppToC::CefLifeSpanHandlerCppToC() { GetStruct()->on_before_popup = life_span_handler_on_before_popup; + GetStruct()->on_before_dev_tools_popup = + life_span_handler_on_before_dev_tools_popup; GetStruct()->on_after_created = life_span_handler_on_after_created; GetStruct()->do_close = life_span_handler_do_close; GetStruct()->on_before_close = life_span_handler_on_before_close; diff --git a/libcef_dll/ctocpp/browser_host_ctocpp.cc b/libcef_dll/ctocpp/browser_host_ctocpp.cc index ee13bbb8e..a6eea8e9f 100644 --- a/libcef_dll/ctocpp/browser_host_ctocpp.cc +++ b/libcef_dll/ctocpp/browser_host_ctocpp.cc @@ -9,7 +9,7 @@ // implementations. See the translator.README.txt file in the tools directory // for more information. // -// $hash=4d51bbece0dd5773f9c97163008d6b2f4bf1ccbf$ +// $hash=b06e3bbc86769bbc2485d4ab9530b5c39dc73243$ // #include "libcef_dll/ctocpp/browser_host_ctocpp.h" @@ -1261,6 +1261,41 @@ void CefBrowserHostCToCpp::ExitFullscreen(bool will_cause_resize) { _struct->exit_fullscreen(_struct, will_cause_resize); } +NO_SANITIZE("cfi-icall") +bool CefBrowserHostCToCpp::CanExecuteChromeCommand(int command_id) { + shutdown_checker::AssertNotShutdown(); + + cef_browser_host_t* _struct = GetStruct(); + if (CEF_MEMBER_MISSING(_struct, can_execute_chrome_command)) { + return false; + } + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Execute + int _retval = _struct->can_execute_chrome_command(_struct, command_id); + + // Return type: bool + return _retval ? true : false; +} + +NO_SANITIZE("cfi-icall") +void CefBrowserHostCToCpp::ExecuteChromeCommand( + int command_id, + cef_window_open_disposition_t disposition) { + shutdown_checker::AssertNotShutdown(); + + cef_browser_host_t* _struct = GetStruct(); + if (CEF_MEMBER_MISSING(_struct, execute_chrome_command)) { + return; + } + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Execute + _struct->execute_chrome_command(_struct, command_id, disposition); +} + // CONSTRUCTOR - Do not edit by hand. CefBrowserHostCToCpp::CefBrowserHostCToCpp() {} diff --git a/libcef_dll/ctocpp/browser_host_ctocpp.h b/libcef_dll/ctocpp/browser_host_ctocpp.h index 305ef1fe5..2cc121268 100644 --- a/libcef_dll/ctocpp/browser_host_ctocpp.h +++ b/libcef_dll/ctocpp/browser_host_ctocpp.h @@ -9,7 +9,7 @@ // implementations. See the translator.README.txt file in the tools directory // for more information. // -// $hash=5c1df6572bffebd983970394be27397837db0b25$ +// $hash=e07873b7e67c06fb54dafa370eca83d2c698ffb9$ // #ifndef CEF_LIBCEF_DLL_CTOCPP_BROWSER_HOST_CTOCPP_H_ @@ -136,6 +136,9 @@ class CefBrowserHostCToCpp : public CefCToCppRefCounted browser, + CefWindowInfo& windowInfo, + CefRefPtr& client, + CefBrowserSettings& settings, + CefRefPtr& extra_info, + bool* use_default_window) { + shutdown_checker::AssertNotShutdown(); + + cef_life_span_handler_t* _struct = GetStruct(); + if (CEF_MEMBER_MISSING(_struct, on_before_dev_tools_popup)) { + return; + } + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Verify param: browser; type: refptr_diff + DCHECK(browser.get()); + if (!browser.get()) { + return; + } + // Verify param: use_default_window; type: bool_byaddr + DCHECK(use_default_window); + if (!use_default_window) { + return; + } + + // Translate param: client; type: refptr_same_byref + cef_client_t* clientStruct = NULL; + if (client.get()) { + clientStruct = CefClientCToCpp::Unwrap(client); + } + cef_client_t* clientOrig = clientStruct; + // Translate param: extra_info; type: refptr_diff_byref + cef_dictionary_value_t* extra_infoStruct = NULL; + if (extra_info.get()) { + extra_infoStruct = CefDictionaryValueCppToC::Wrap(extra_info); + } + cef_dictionary_value_t* extra_infoOrig = extra_infoStruct; + // Translate param: use_default_window; type: bool_byaddr + int use_default_windowInt = use_default_window ? *use_default_window : 0; + + // Execute + _struct->on_before_dev_tools_popup(_struct, CefBrowserCppToC::Wrap(browser), + &windowInfo, &clientStruct, &settings, + &extra_infoStruct, &use_default_windowInt); + + // Restore param:client; type: refptr_same_byref + if (clientStruct) { + if (clientStruct != clientOrig) { + client = CefClientCToCpp::Wrap(clientStruct); + } + } else { + client = nullptr; + } + // Restore param:extra_info; type: refptr_diff_byref + if (extra_infoStruct) { + if (extra_infoStruct != extra_infoOrig) { + extra_info = CefDictionaryValueCppToC::Unwrap(extra_infoStruct); + } + } else { + extra_info = nullptr; + } + // Restore param:use_default_window; type: bool_byaddr + if (use_default_window) { + *use_default_window = use_default_windowInt ? true : false; + } +} + NO_SANITIZE("cfi-icall") void CefLifeSpanHandlerCToCpp::OnAfterCreated(CefRefPtr browser) { shutdown_checker::AssertNotShutdown(); diff --git a/libcef_dll/ctocpp/life_span_handler_ctocpp.h b/libcef_dll/ctocpp/life_span_handler_ctocpp.h index 542c53280..dc0995af6 100644 --- a/libcef_dll/ctocpp/life_span_handler_ctocpp.h +++ b/libcef_dll/ctocpp/life_span_handler_ctocpp.h @@ -9,7 +9,7 @@ // implementations. See the translator.README.txt file in the tools directory // for more information. // -// $hash=229117f1ecb1cc27b5ab5eebd79e64c30d73a855$ +// $hash=53f00e60e361c79c69a8f19474a234d5011454f5$ // #ifndef CEF_LIBCEF_DLL_CTOCPP_LIFE_SPAN_HANDLER_CTOCPP_H_ @@ -49,6 +49,12 @@ class CefLifeSpanHandlerCToCpp CefBrowserSettings& settings, CefRefPtr& extra_info, bool* no_javascript_access) override; + void OnBeforeDevToolsPopup(CefRefPtr browser, + CefWindowInfo& windowInfo, + CefRefPtr& client, + CefBrowserSettings& settings, + CefRefPtr& extra_info, + bool* use_default_window) override; void OnAfterCreated(CefRefPtr browser) override; bool DoClose(CefRefPtr browser) override; void OnBeforeClose(CefRefPtr browser) override; diff --git a/patch/patch.cfg b/patch/patch.cfg index c62d1b2ef..e451e8ae4 100644 --- a/patch/patch.cfg +++ b/patch/patch.cfg @@ -117,6 +117,10 @@ patches = [ # Windows: Fix incorrect DIPToScreenRect usage in DesktopWindowTreeHostWin # when |has_external_parent_| is true. # https://github.com/chromiumembedded/cef/issues/3359 + # + # chrome: Allow override of Widget::Activate() to support activation of + # DevTools windows with external parent. + # https://github.com/chromiumembedded/cef/issues/3282 'name': 'views_widget', }, { diff --git a/patch/patches/chrome_browser_browser.patch b/patch/patches/chrome_browser_browser.patch index e5bbf23a4..b72e25f20 100644 --- a/patch/patches/chrome_browser_browser.patch +++ b/patch/patches/chrome_browser_browser.patch @@ -12,6 +12,68 @@ index 2480282a19d12..dbd1fbf8a15b5 100644 return false; } +diff --git chrome/browser/devtools/devtools_window.cc chrome/browser/devtools/devtools_window.cc +index 4d99a769d6069..0ebd577182dc6 100644 +--- chrome/browser/devtools/devtools_window.cc ++++ chrome/browser/devtools/devtools_window.cc +@@ -31,6 +31,7 @@ + #include "chrome/browser/profiles/profile.h" + #include "chrome/browser/task_manager/web_contents_tags.h" + #include "chrome/browser/ui/browser.h" ++#include "chrome/browser/ui/browser_finder.h" + #include "chrome/browser/ui/browser_list.h" + #include "chrome/browser/ui/browser_tabstrip.h" + #include "chrome/browser/ui/browser_window.h" +@@ -1162,6 +1163,13 @@ DevToolsWindow* DevToolsWindow::Create( + !browser->is_type_normal()) { + can_dock = false; + } ++ ++#if BUILDFLAG(ENABLE_CEF) ++ if (can_dock && browser && browser->cef_delegate()) { ++ // Don't dock DevTools for CEF-managed browsers. ++ can_dock = false; ++ } ++#endif + } + + // Create WebContents with devtools. +@@ -1738,12 +1746,29 @@ void DevToolsWindow::CreateDevToolsBrowser() { + Browser::CreationStatus::kOk) { + return; + } +- browser_ = +- Browser::Create(Browser::CreateParams::CreateForDevTools(profile_)); +- browser_->tab_strip_model()->AddWebContents( +- OwnedMainWebContents::TakeWebContents( +- std::move(owned_main_web_contents_)), +- -1, ui::PAGE_TRANSITION_AUTO_TOPLEVEL, AddTabTypes::ADD_ACTIVE); ++ ++ auto opener = chrome::FindBrowserWithWebContents(GetInspectedWebContents()); ++ auto devtools_contents = OwnedMainWebContents::TakeWebContents( ++ std::move(owned_main_web_contents_)); ++ ++#if BUILDFLAG(ENABLE_CEF) ++ if (opener && opener->cef_delegate()) { ++ // If a Browser is created, it will take ownership of |devtools_contents|. ++ browser_ = opener->cef_delegate()->CreateDevToolsBrowser( ++ profile_, opener, devtools_contents); ++ } ++#endif ++ ++ if (!browser_) { ++ auto create_params = Browser::CreateParams::CreateForDevTools(profile_); ++ create_params.opener = opener; ++ ++ browser_ = Browser::Create(std::move(create_params)); ++ browser_->tab_strip_model()->AddWebContents( ++ std::move(devtools_contents), ++ -1, ui::PAGE_TRANSITION_AUTO_TOPLEVEL, AddTabTypes::ADD_ACTIVE); ++ } ++ + OverrideAndSyncDevToolsRendererPrefs(); + } + diff --git chrome/browser/ui/BUILD.gn chrome/browser/ui/BUILD.gn index eca90c39e0d44..2157b6d86ab02 100644 --- chrome/browser/ui/BUILD.gn @@ -333,7 +395,7 @@ index 9ba2025634365..b6ceaa7f0b531 100644 case TYPE_NORMAL: return NormalBrowserSupportsWindowFeature(feature, check_can_support); diff --git chrome/browser/ui/browser.h chrome/browser/ui/browser.h -index db37e861d2546..c47dfc31131f7 100644 +index db37e861d2546..ea85b9966267d 100644 --- chrome/browser/ui/browser.h +++ chrome/browser/ui/browser.h @@ -22,6 +22,7 @@ @@ -364,7 +426,7 @@ index db37e861d2546..c47dfc31131f7 100644 + scoped_refptr cef_params; + + // Specify the Browser that is opening this popup. -+ // Currently only used with TYPE_PICTURE_IN_PICTURE. ++ // Currently only used with TYPE_PICTURE_IN_PICTURE and TYPE_DEVTOOLS. + raw_ptr opener = nullptr; +#endif + diff --git a/patch/patches/chrome_runtime_views.patch b/patch/patches/chrome_runtime_views.patch index 36b9390df..0b2eb4afe 100644 --- a/patch/patches/chrome_runtime_views.patch +++ b/patch/patches/chrome_runtime_views.patch @@ -231,7 +231,7 @@ index 59024587ef6b7..0c30aa71768cf 100644 void FindBarHost::RegisterAccelerators() { diff --git chrome/browser/ui/views/frame/browser_frame.cc chrome/browser/ui/views/frame/browser_frame.cc -index bb4b3821bcfda..9734fe4aaffcf 100644 +index bb4b3821bcfda..4014f61a7057f 100644 --- chrome/browser/ui/views/frame/browser_frame.cc +++ chrome/browser/ui/views/frame/browser_frame.cc @@ -114,15 +114,23 @@ ui::ColorProviderKey::SchemeVariant GetSchemeVariant( @@ -260,7 +260,7 @@ index bb4b3821bcfda..9734fe4aaffcf 100644 } BrowserFrame::~BrowserFrame() {} -@@ -228,6 +236,12 @@ void BrowserFrame::LayoutWebAppWindowTitle( +@@ -228,10 +236,20 @@ void BrowserFrame::LayoutWebAppWindowTitle( } int BrowserFrame::GetTopInset() const { @@ -273,7 +273,15 @@ index bb4b3821bcfda..9734fe4aaffcf 100644 return browser_frame_view_->GetTopInset(false); } -@@ -240,6 +254,8 @@ BrowserNonClientFrameView* BrowserFrame::GetFrameView() const { + void BrowserFrame::UpdateThrobber(bool running) { ++ if (!browser_frame_view_) { ++ // Not supported with CEF Views-hosted DevTools windows. ++ return; ++ } + browser_frame_view_->UpdateThrobber(running); + } + +@@ -240,6 +258,8 @@ BrowserNonClientFrameView* BrowserFrame::GetFrameView() const { } bool BrowserFrame::UseCustomFrame() const { @@ -282,7 +290,7 @@ index bb4b3821bcfda..9734fe4aaffcf 100644 return native_browser_frame_->UseCustomFrame(); } -@@ -253,20 +269,30 @@ bool BrowserFrame::ShouldDrawFrameHeader() const { +@@ -253,20 +273,30 @@ bool BrowserFrame::ShouldDrawFrameHeader() const { void BrowserFrame::GetWindowPlacement(gfx::Rect* bounds, ui::WindowShowState* show_state) const { @@ -313,7 +321,7 @@ index bb4b3821bcfda..9734fe4aaffcf 100644 browser_frame_view_->OnBrowserViewInitViewsComplete(); } -@@ -367,6 +393,8 @@ ui::ColorProviderKey::ThemeInitializerSupplier* BrowserFrame::GetCustomTheme() +@@ -367,6 +397,8 @@ ui::ColorProviderKey::ThemeInitializerSupplier* BrowserFrame::GetCustomTheme() } void BrowserFrame::OnNativeWidgetWorkspaceChanged() { @@ -322,7 +330,7 @@ index bb4b3821bcfda..9734fe4aaffcf 100644 chrome::SaveWindowWorkspace(browser_view_->browser(), GetWorkspace()); chrome::SaveWindowVisibleOnAllWorkspaces(browser_view_->browser(), IsVisibleOnAllWorkspaces()); -@@ -478,6 +506,8 @@ void BrowserFrame::OnNativeThemeUpdated(ui::NativeTheme* observed_theme) { +@@ -478,6 +510,8 @@ void BrowserFrame::OnNativeThemeUpdated(ui::NativeTheme* observed_theme) { ui::ColorProviderKey BrowserFrame::GetColorProviderKey() const { auto key = Widget::GetColorProviderKey(); @@ -331,7 +339,7 @@ index bb4b3821bcfda..9734fe4aaffcf 100644 key.app_controller = browser_view_->browser()->app_controller(); -@@ -632,5 +662,8 @@ bool BrowserFrame::RegenerateFrameOnThemeChange( +@@ -632,5 +666,8 @@ bool BrowserFrame::RegenerateFrameOnThemeChange( } bool BrowserFrame::IsIncognitoBrowser() const { diff --git a/patch/patches/views_widget.patch b/patch/patches/views_widget.patch index 9f722d1a1..79d8a9973 100644 --- a/patch/patches/views_widget.patch +++ b/patch/patches/views_widget.patch @@ -414,7 +414,7 @@ index 2f552f72074e3..3f057242d198c 100644 } diff --git ui/views/widget/widget.h ui/views/widget/widget.h -index 419b75d0608b2..d772dc17d165c 100644 +index 419b75d0608b2..57bb1fca770ed 100644 --- ui/views/widget/widget.h +++ ui/views/widget/widget.h @@ -351,6 +351,8 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate, @@ -426,6 +426,15 @@ index 419b75d0608b2..d772dc17d165c 100644 // Specifies the initial bounds of the Widget. Default is empty, which means // the NativeWidget may specify a default size. If the parent is specified, // |bounds| is in the parent's coordinate system. If the parent is not +@@ -742,7 +744,7 @@ class VIEWS_EXPORT Widget : public internal::NativeWidgetDelegate, + void ShowInactive(); + + // Activates the widget, assuming it already exists and is visible. +- void Activate(); ++ virtual void Activate(); + + // Deactivates the widget, making the next window in the Z order the active + // window. diff --git ui/views/widget/widget_delegate.h ui/views/widget/widget_delegate.h index 6d87be86ae8b3..02fe7a11958d0 100644 --- ui/views/widget/widget_delegate.h diff --git a/tests/cefclient/browser/client_handler.cc b/tests/cefclient/browser/client_handler.cc index f2b1bf313..dc2433619 100644 --- a/tests/cefclient/browser/client_handler.cc +++ b/tests/cefclient/browser/client_handler.cc @@ -583,7 +583,7 @@ bool ClientHandler::OnChromeCommand(CefRefPtr browser, } else if (!with_controls_) { // If controls are hidden, block all commands that don't target the current // tab or aren't specifically allowed. - block = disposition != WOD_CURRENT_TAB || !allowed; + block = disposition != CEF_WOD_CURRENT_TAB || !allowed; } if (block) { @@ -644,11 +644,12 @@ void ClientHandler::OnBeforeContextMenu(CefRefPtr browser, model->AddSeparator(); } + // Add DevTools items to all context menus. + model->AddItem(CLIENT_ID_SHOW_DEVTOOLS, "&Show DevTools"); + model->AddItem(CLIENT_ID_CLOSE_DEVTOOLS, "Close DevTools"); + if (!use_chrome_runtime) { - // TODO(chrome-runtime): Add support for this. - // Add DevTools items to all context menus. - model->AddItem(CLIENT_ID_SHOW_DEVTOOLS, "&Show DevTools"); - model->AddItem(CLIENT_ID_CLOSE_DEVTOOLS, "Close DevTools"); + // Chrome runtime already gives us an "Inspect" menu item. model->AddSeparator(); model->AddItem(CLIENT_ID_INSPECT_ELEMENT, "Inspect Element"); } @@ -938,15 +939,36 @@ bool ClientHandler::OnBeforePopup( bool* no_javascript_access) { CEF_REQUIRE_UI_THREAD(); - if (target_disposition == WOD_NEW_PICTURE_IN_PICTURE) { + if (target_disposition == CEF_WOD_NEW_PICTURE_IN_PICTURE) { // Use default handling for document picture-in-picture popups. client = nullptr; return false; } - // Return true to cancel the popup window. - return !CreatePopupWindow(browser, false, popupFeatures, windowInfo, client, - settings); + // Potentially create a new RootWindow for the popup browser that will be + // created asynchronously. + CreatePopupWindow(browser, /*is_devtools=*/false, popupFeatures, windowInfo, + client, settings); + + // Allow popup creation. + return false; +} + +void ClientHandler::OnBeforeDevToolsPopup( + CefRefPtr browser, + CefWindowInfo& windowInfo, + CefRefPtr& client, + CefBrowserSettings& settings, + CefRefPtr& extra_info, + bool* use_default_window) { + CEF_REQUIRE_UI_THREAD(); + + // Potentially create a new RootWindow for the DevTools popup browser that + // will be created immediately after this method returns. + if (!CreatePopupWindow(browser, /*is_devtools=*/true, CefPopupFeatures(), + windowInfo, client, settings)) { + *use_default_window = true; + } } void ClientHandler::OnAfterCreated(CefRefPtr browser) { @@ -1081,8 +1103,8 @@ bool ClientHandler::OnOpenURLFromTab( const CefString& target_url, CefRequestHandler::WindowOpenDisposition target_disposition, bool user_gesture) { - if (target_disposition == WOD_NEW_BACKGROUND_TAB || - target_disposition == WOD_NEW_FOREGROUND_TAB) { + if (target_disposition == CEF_WOD_NEW_BACKGROUND_TAB || + target_disposition == CEF_WOD_NEW_FOREGROUND_TAB) { // Handle middle-click and ctrl + left-click by opening the URL in a new // browser window. auto config = std::make_unique(); @@ -1303,25 +1325,21 @@ void ClientHandler::ShowDevTools(CefRefPtr browser, CefRefPtr client; CefBrowserSettings settings; - MainContext::Get()->PopulateBrowserSettings(&settings); - CefRefPtr host = browser->GetHost(); // Test if the DevTools browser already exists. - bool has_devtools = host->HasDevTools(); - if (!has_devtools) { - // Create a new RootWindow for the DevTools browser that will be created - // by ShowDevTools(). - has_devtools = CreatePopupWindow(browser, true, CefPopupFeatures(), - windowInfo, client, settings); + if (!MainContext::Get()->UseChromeRuntime() && !host->HasDevTools()) { + // Potentially create a new RootWindow for the DevTools browser that will be + // created by ShowDevTools(). For Chrome runtime this occurs in + // OnBeforeDevToolsPopup instead. + CreatePopupWindow(browser, /*is_devtools=*/true, CefPopupFeatures(), + windowInfo, client, settings); } - if (has_devtools) { - // Create the DevTools browser if it doesn't already exist. - // Otherwise, focus the existing DevTools browser and inspect the element - // at |inspect_element_at| if non-empty. - host->ShowDevTools(windowInfo, client, settings, inspect_element_at); - } + // Create the DevTools browser if it doesn't already exist. + // Otherwise, focus the existing DevTools browser and inspect the element + // at |inspect_element_at| if non-empty. + host->ShowDevTools(windowInfo, client, settings, inspect_element_at); } void ClientHandler::CloseDevTools(CefRefPtr browser) { @@ -1407,11 +1425,10 @@ bool ClientHandler::CreatePopupWindow(CefRefPtr browser, // The popup browser will be parented to a new native window. // Don't show URL bar and navigation buttons on DevTools windows. - MainContext::Get()->GetRootWindowManager()->CreateRootWindowAsPopup( + // May return nullptr if UseDefaultPopup() returns true. + return !!MainContext::Get()->GetRootWindowManager()->CreateRootWindowAsPopup( with_controls_ && !is_devtools, is_osr_, popupFeatures, windowInfo, client, settings); - - return true; } void ClientHandler::NotifyBrowserCreated(CefRefPtr browser) { diff --git a/tests/cefclient/browser/client_handler.h b/tests/cefclient/browser/client_handler.h index b1458c6ba..f1f9fe1b9 100644 --- a/tests/cefclient/browser/client_handler.h +++ b/tests/cefclient/browser/client_handler.h @@ -224,6 +224,12 @@ class ClientHandler : public CefClient, CefBrowserSettings& settings, CefRefPtr& extra_info, bool* no_javascript_access) override; + void OnBeforeDevToolsPopup(CefRefPtr browser, + CefWindowInfo& windowInfo, + CefRefPtr& client, + CefBrowserSettings& settings, + CefRefPtr& extra_info, + bool* use_default_window) override; void OnAfterCreated(CefRefPtr browser) override; bool DoClose(CefRefPtr browser) override; void OnBeforeClose(CefRefPtr browser) override; @@ -348,9 +354,8 @@ class ClientHandler : public CefClient, friend class ClientDownloadImageCallback; // Create a new popup window using the specified information. |is_devtools| - // will be true if the window will be used for DevTools. Return true to - // proceed with popup browser creation or false to cancel the popup browser. - // May be called on any thead. + // will be true if the window will be used for DevTools. Returns true if a + // RootWindow was created for the popup. bool CreatePopupWindow(CefRefPtr browser, bool is_devtools, const CefPopupFeatures& popupFeatures, diff --git a/tests/cefclient/browser/main_context.h b/tests/cefclient/browser/main_context.h index 67d3e3574..c1bfacad7 100644 --- a/tests/cefclient/browser/main_context.h +++ b/tests/cefclient/browser/main_context.h @@ -42,6 +42,10 @@ class MainContext { // Returns true if the Chrome runtime will be used. virtual bool UseChromeRuntime() = 0; + // Returns true if a native parent window is being used with the Chrome + // runtime. + virtual bool UseChromeRuntimeNative() = 0; + // Returns true if the Views framework will be used. virtual bool UseViews() = 0; diff --git a/tests/cefclient/browser/main_context_impl.cc b/tests/cefclient/browser/main_context_impl.cc index 4aed76ec4..1e85ed95f 100644 --- a/tests/cefclient/browser/main_context_impl.cc +++ b/tests/cefclient/browser/main_context_impl.cc @@ -111,8 +111,9 @@ MainContextImpl::MainContextImpl(CefRefPtr command_line, } #if defined(OS_WIN) || defined(OS_LINUX) - if (use_chrome_runtime_ && !use_views_ && - !command_line->HasSwitch(switches::kUseNative)) { + use_chrome_runtime_native_ = + use_chrome_runtime_ && command_line->HasSwitch(switches::kUseNative); + if (use_chrome_runtime_ && !use_views_ && !use_chrome_runtime_native_) { LOG(WARNING) << "Chrome runtime defaults to the Views framework."; use_views_ = true; } @@ -174,6 +175,10 @@ bool MainContextImpl::UseChromeRuntime() { return use_chrome_runtime_; } +bool MainContextImpl::UseChromeRuntimeNative() { + return use_chrome_runtime_native_; +} + bool MainContextImpl::UseViews() { return use_views_; } diff --git a/tests/cefclient/browser/main_context_impl.h b/tests/cefclient/browser/main_context_impl.h index d07bc088a..876804eb8 100644 --- a/tests/cefclient/browser/main_context_impl.h +++ b/tests/cefclient/browser/main_context_impl.h @@ -29,6 +29,7 @@ class MainContextImpl : public MainContext { std::string GetMainURL() override; cef_color_t GetBackgroundColor() override; bool UseChromeRuntime() override; + bool UseChromeRuntimeNative() override; bool UseViews() override; bool UseWindowlessRendering() override; bool TouchEventsEnabled() override; @@ -74,6 +75,7 @@ class MainContextImpl : public MainContext { bool use_windowless_rendering_; int windowless_frame_rate_ = 0; bool use_chrome_runtime_; + bool use_chrome_runtime_native_ = false; bool use_views_; std::unique_ptr root_window_manager_; diff --git a/tests/cefclient/browser/root_window.h b/tests/cefclient/browser/root_window.h index 53d6f8c87..688360263 100644 --- a/tests/cefclient/browser/root_window.h +++ b/tests/cefclient/browser/root_window.h @@ -28,6 +28,9 @@ enum class WindowType { // The window is a modal dialog. DIALOG, + + // The window is a DevTools popup. + DEVTOOLS, }; // Used to configure how a RootWindow is created. diff --git a/tests/cefclient/browser/root_window_views.cc b/tests/cefclient/browser/root_window_views.cc index 9171e0a65..f5f0ff223 100644 --- a/tests/cefclient/browser/root_window_views.cc +++ b/tests/cefclient/browser/root_window_views.cc @@ -249,7 +249,7 @@ void RootWindowViews::OnViewsWindowClosing(CefRefPtr window) { CEF_REQUIRE_UI_THREAD(); DCHECK(window_); - if (config_->window_type != WindowType::NORMAL) { + if (!window_->SupportsWindowRestore()) { return; } @@ -516,7 +516,7 @@ void RootWindowViews::InitOnUIThread( // Initial state was specified via the config object. initial_bounds_ = config_->bounds; initial_show_state_ = config_->show_state; - } else if (config_->window_type == WindowType::NORMAL) { + } else if (ViewsWindow::SupportsWindowRestore(config_->window_type)) { // Initial state may be specified via the command-line or global // preferences. std::optional bounds; diff --git a/tests/cefclient/browser/views_window.cc b/tests/cefclient/browser/views_window.cc index da59962b1..65abb6dba 100644 --- a/tests/cefclient/browser/views_window.cc +++ b/tests/cefclient/browser/views_window.cc @@ -399,10 +399,21 @@ void ViewsWindow::OnExtensionsChanged(const ExtensionSet& extensions) { base::BindOnce(&ViewsWindow::OnExtensionIconsLoaded, this, extensions)); } +// static +bool ViewsWindow::SupportsWindowRestore(WindowType type) { + // Only support window restore with normal windows. + return type == WindowType::NORMAL; +} + +bool ViewsWindow::SupportsWindowRestore() const { + return SupportsWindowRestore(type_); +} + bool ViewsWindow::GetWindowRestorePreferences( cef_show_state_t& show_state, std::optional& dip_bounds) { CEF_REQUIRE_UI_THREAD(); + DCHECK(SupportsWindowRestore()); if (!window_) { return false; } @@ -460,7 +471,9 @@ CefRefPtr ViewsWindow::GetDelegateForPopupBrowserView( DCHECK(popup_delegate != delegate_); // Create a new ViewsWindow for the popup BrowserView. - return new ViewsWindow(WindowType::NORMAL, popup_delegate, nullptr); + return new ViewsWindow( + is_devtools ? WindowType::DEVTOOLS : WindowType::NORMAL, popup_delegate, + nullptr); } bool ViewsWindow::OnPopupBrowserViewCreated( @@ -676,12 +689,12 @@ void ViewsWindow::OnWindowCreated(CefRefPtr window) { delegate_->OnViewsWindowCreated(this); - if (type_ == WindowType::NORMAL) { + if (type_ == WindowType::NORMAL || type_ == WindowType::DEVTOOLS) { const CefRect bounds = delegate_->GetInitialBounds(); if (bounds.IsEmpty()) { // Size the Window and center it at the default size. window_->CenterWindow(CefSize(kDefaultWidth, kDefaultHeight)); - } else { + } else if (SupportsWindowRestore()) { // Remember the bounds from the previous application run in case the user // does not move or resize the window during this application run. last_visible_bounds_ = bounds; @@ -758,7 +771,7 @@ void ViewsWindow::OnWindowActivationChanged(CefRefPtr window, void ViewsWindow::OnWindowBoundsChanged(CefRefPtr window, const CefRect& new_bounds) { - if (type_ == WindowType::NORMAL && !window->IsMinimized() && + if (SupportsWindowRestore() && !window->IsMinimized() && !window->IsMaximized() && !window->IsFullscreen()) { // Track the last visible bounds for window restore purposes. last_visible_bounds_ = new_bounds; @@ -845,20 +858,18 @@ bool ViewsWindow::GetTitlebarHeight(CefRefPtr window, bool ViewsWindow::CanResize(CefRefPtr window) { CEF_REQUIRE_UI_THREAD(); - // Only allow resize of normal windows. - return type_ == WindowType::NORMAL; + // Only allow resize of normal and DevTools windows. + return type_ == WindowType::NORMAL || type_ == WindowType::DEVTOOLS; } bool ViewsWindow::CanMaximize(CefRefPtr window) { CEF_REQUIRE_UI_THREAD(); - // Only allow maximize of normal windows. - return type_ == WindowType::NORMAL; + return CanResize(window); } bool ViewsWindow::CanMinimize(CefRefPtr window) { CEF_REQUIRE_UI_THREAD(); - // Only allow minimize of normal windows. - return type_ == WindowType::NORMAL; + return CanResize(window); } bool ViewsWindow::OnAccelerator(CefRefPtr window, int command_id) { diff --git a/tests/cefclient/browser/views_window.h b/tests/cefclient/browser/views_window.h index 4855c8a55..628a091d2 100644 --- a/tests/cefclient/browser/views_window.h +++ b/tests/cefclient/browser/views_window.h @@ -127,6 +127,8 @@ class ViewsWindow : public CefBrowserViewDelegate, void OnBeforeContextMenu(CefRefPtr model); void OnExtensionsChanged(const ExtensionSet& extensions); + static bool SupportsWindowRestore(WindowType type); + bool SupportsWindowRestore() const; bool GetWindowRestorePreferences(cef_show_state_t& show_state, std::optional& dip_bounds); void SetTitlebarHeight(const std::optional& height); diff --git a/tests/ceftests/navigation_unittest.cc b/tests/ceftests/navigation_unittest.cc index 0ea5849a2..b1693fc19 100644 --- a/tests/ceftests/navigation_unittest.cc +++ b/tests/ceftests/navigation_unittest.cc @@ -1980,7 +1980,7 @@ class LoadNavTestHandler : public TestHandler { EXPECT_TRUE(user_gesture); } - EXPECT_EQ(WOD_NEW_BACKGROUND_TAB, target_disposition); + EXPECT_EQ(CEF_WOD_NEW_BACKGROUND_TAB, target_disposition); // OnOpenURLFromTab should be called before OnBeforeBrowse for the file URL. EXPECT_FALSE(got_before_browse_); diff --git a/tests/ceftests/request_context_unittest.cc b/tests/ceftests/request_context_unittest.cc index a8882b0ca..d368d02f7 100644 --- a/tests/ceftests/request_context_unittest.cc +++ b/tests/ceftests/request_context_unittest.cc @@ -298,7 +298,7 @@ class PopupTestHandler : public TestHandler { const std::string& url = target_url; EXPECT_STREQ(url.c_str(), popup_url_.c_str()); - EXPECT_EQ(WOD_NEW_FOREGROUND_TAB, target_disposition); + EXPECT_EQ(CEF_WOD_NEW_FOREGROUND_TAB, target_disposition); if (mode_ == MODE_WINDOW_OPEN) { EXPECT_FALSE(user_gesture); @@ -528,7 +528,7 @@ class PopupNavTestHandler : public TestHandler { EXPECT_STREQ(kPopupNavPageUrl, frame->GetURL().ToString().c_str()); EXPECT_STREQ(kPopupNavPopupUrl, target_url.ToString().c_str()); EXPECT_STREQ(kPopupNavPopupName, target_frame_name.ToString().c_str()); - EXPECT_EQ(WOD_NEW_FOREGROUND_TAB, target_disposition); + EXPECT_EQ(CEF_WOD_NEW_FOREGROUND_TAB, target_disposition); EXPECT_FALSE(user_gesture); EXPECT_FALSE(*no_javascript_access); diff --git a/tests/ceftests/test_handler.cc b/tests/ceftests/test_handler.cc index 3c6062cfd..ac8ee1251 100644 --- a/tests/ceftests/test_handler.cc +++ b/tests/ceftests/test_handler.cc @@ -23,7 +23,7 @@ namespace { -bool UseViews() { +bool UseViewsGlobal() { static bool use_views = []() { return CefCommandLine::GetGlobalCommandLine()->HasSwitch( client::switches::kUseViews); @@ -237,7 +237,8 @@ void TestHandler::UIThreadHelper::TaskHelper(base::OnceClosure task) { std::atomic TestHandler::test_handler_count_{0U}; TestHandler::TestHandler(CompletionState* completion_state) - : debug_string_prefix_(MakeDebugStringPrefix()) { + : debug_string_prefix_(MakeDebugStringPrefix()), + use_views_(UseViewsGlobal()) { test_handler_count_++; if (completion_state) { @@ -281,7 +282,8 @@ void TestHandler::OnAfterCreated(CefRefPtr browser) { } browser_map_.insert(std::make_pair(browser_id, browser)); - OnCreated(browser_id, NT_BROWSER); + const bool views_hosted = !!CefBrowserView::GetForBrowser(browser); + OnCreated(browser_id, NT_BROWSER, views_hosted); } void TestHandler::OnBeforeClose(CefRefPtr browser) { @@ -302,19 +304,31 @@ void TestHandler::OnBeforeClose(CefRefPtr browser) { } void TestHandler::OnWindowCreated(int browser_id) { - CHECK(UseViews()); + CHECK(use_views_); EXPECT_UI_THREAD(); - OnCreated(browser_id, NT_WINDOW); + OnCreated(browser_id, NT_WINDOW, /*views_hosted=*/true); } void TestHandler::OnWindowDestroyed(int browser_id) { - CHECK(UseViews()); + CHECK(use_views_); EXPECT_UI_THREAD(); OnClosed(browser_id, NT_WINDOW); } -void TestHandler::OnCreated(int browser_id, NotifyType type) { +void TestHandler::OnCreated(int browser_id, + NotifyType type, + bool views_hosted) { + CHECK(use_views_ || !views_hosted); + + const bool has_value = browser_status_map_.contains(browser_id); + auto& browser_status = browser_status_map_[browser_id]; + if (has_value) { + CHECK_EQ(browser_status.views_hosted, views_hosted); + } else { + browser_status.views_hosted = views_hosted; + } + EXPECT_FALSE(browser_status.got_created[type]) << "Duplicate call to OnCreated(" << browser_id << ", " << (type == NT_BROWSER ? "BROWSER" : "WINDOW") << ")"; @@ -324,7 +338,7 @@ void TestHandler::OnCreated(int browser_id, NotifyType type) { bool creation_complete = false; // When using Views, wait for both Browser and Window notifications. - if (UseViews()) { + if (browser_status.views_hosted) { creation_complete = browser_status.got_created[NT_BROWSER] && browser_status.got_created[NT_WINDOW]; } else { @@ -347,7 +361,7 @@ void TestHandler::OnClosed(int browser_id, NotifyType type) { browser_status.got_closed[type].yes(); // When using Views, wait for both Browser and Window notifications. - if (UseViews()) { + if (browser_status.views_hosted) { close_complete = browser_status.got_closed[NT_BROWSER] && browser_status.got_closed[NT_WINDOW]; } else { @@ -517,8 +531,7 @@ void TestHandler::OnTestTimeout(int timeout_ms, bool treat_as_error) { void TestHandler::CreateBrowser(const CefString& url, CefRefPtr request_context, CefRefPtr extra_info) { - if (UseViews() && !CefCurrentlyOn(TID_UI)) { - // Views classes must be accessed on the UI thread. + if (!CefCurrentlyOn(TID_UI)) { CefPostTask(TID_UI, base::BindOnce(&TestHandler::CreateBrowser, this, url, request_context, extra_info)); return; @@ -527,7 +540,7 @@ void TestHandler::CreateBrowser(const CefString& url, CefWindowInfo windowInfo; CefBrowserSettings settings; - if (UseViews()) { + if (use_views_) { // Create the BrowserView. CefRefPtr browser_view = CefBrowserView::CreateBrowserView( this, url, settings, extra_info, request_context, @@ -615,6 +628,16 @@ void TestHandler::SetTestTimeout(int timeout_ms, bool treat_as_error) { *timeout); } +void TestHandler::SetUseViews(bool use_views) { + if (!CefCurrentlyOn(TID_UI)) { + CefPostTask(TID_UI, + base::BindOnce(&TestHandler::SetUseViews, this, use_views)); + return; + } + + use_views_ = use_views; +} + void TestHandler::SetSignalTestCompletionCount(size_t count) { #if VERBOSE_DEBUGGING LOG(INFO) << debug_string_prefix_ diff --git a/tests/ceftests/test_handler.h b/tests/ceftests/test_handler.h index dbdeb1c50..ef6910af4 100644 --- a/tests/ceftests/test_handler.h +++ b/tests/ceftests/test_handler.h @@ -262,7 +262,7 @@ class TestHandler : public CefClient, // number of times. void SignalTestCompletion(); - // Reuturns true if SignalTestCompletion() has been called the necessary + // Returns true if SignalTestCompletion() has been called the necessary // number of times (may be 0), in which case TestComplete() will be called // automatically when all browsers have closed. Must be called on the // UI thread. @@ -275,6 +275,11 @@ class TestHandler : public CefClient, // Call OnTestTimeout() after the specified amount of time. void SetTestTimeout(int timeout_ms = 5000, bool treat_as_error = true); + // Call prior to CreateBrowser() to configure whether browsers will be created + // as Views-hosted. Defaults to false unless the `--use-views` command-line + // flag is specified. + void SetUseViews(bool use_views); + // Returns the single UIThreadHelper instance, creating it if necessary. Must // be called on the UI thread. UIThreadHelper* GetUIThreadHelper(); @@ -290,7 +295,7 @@ class TestHandler : public CefClient, NT_BROWSER, NT_WINDOW, }; - void OnCreated(int browser_id, NotifyType type); + void OnCreated(int browser_id, NotifyType type, bool views_hosted); void OnClosed(int browser_id, NotifyType type); std::string MakeDebugStringPrefix() const; @@ -300,10 +305,17 @@ class TestHandler : public CefClient, CompletionState* completion_state_; bool completion_state_owned_; + bool use_views_; + // Map of browser ID to browser object. Only accessed on the UI thread. BrowserMap browser_map_; struct NotifyStatus { + // True if this particular browser window is Views-hosted (see SetUseViews). + // Some tests create popup or DevTools windows with default handling (e.g. + // not Views-hosted). + bool views_hosted; + // Keyed by NotifyType. TrackCallback got_created[2]; TrackCallback got_closed[2]; diff --git a/tests/ceftests/v8_unittest.cc b/tests/ceftests/v8_unittest.cc index afe55f7d1..7ed16a1cf 100644 --- a/tests/ceftests/v8_unittest.cc +++ b/tests/ceftests/v8_unittest.cc @@ -11,10 +11,8 @@ #include "tests/ceftests/test_handler.h" #include "tests/ceftests/test_util.h" #include "tests/gtest/include/gtest/gtest.h" -#include "tests/shared/browser/client_app_browser.h" #include "tests/shared/renderer/client_app_renderer.h" -using client::ClientAppBrowser; using client::ClientAppRenderer; // How to add a new test: @@ -3280,8 +3278,7 @@ class V8TestHandler : public TestHandler { CefBrowserSettings settings; #if defined(OS_WIN) - windowInfo.SetAsPopup(browser->GetHost()->GetWindowHandle(), - "DevTools"); + windowInfo.SetAsPopup(nullptr, "DevTools"); #endif browser->GetHost()->ShowDevTools(windowInfo, this, settings,