diff --git a/cef_paths2.gypi b/cef_paths2.gypi index 0cf1e4475..c38f13d8c 100644 --- a/cef_paths2.gypi +++ b/cef_paths2.gypi @@ -288,6 +288,8 @@ 'tests/cefclient/browser/urlrequest_test.h', 'tests/cefclient/browser/views_menu_bar.cc', 'tests/cefclient/browser/views_menu_bar.h', + 'tests/cefclient/browser/views_overlay_browser.cc', + 'tests/cefclient/browser/views_overlay_browser.h', 'tests/cefclient/browser/views_overlay_controls.cc', 'tests/cefclient/browser/views_overlay_controls.h', 'tests/cefclient/browser/views_style.cc', diff --git a/libcef/browser/views/browser_view_impl.cc b/libcef/browser/views/browser_view_impl.cc index 72927bc88..e545a3d68 100644 --- a/libcef/browser/views/browser_view_impl.cc +++ b/libcef/browser/views/browser_view_impl.cc @@ -280,8 +280,8 @@ void CefBrowserViewImpl::AddedToWidget() { CefWidget* cef_widget = CefWidget::GetForWidget(widget); DCHECK(cef_widget); - if (!browser_) { - if (cef_widget->IsAlloyStyle() && !is_alloy_style_) { + if (!browser_ && !is_alloy_style_) { + if (cef_widget->IsAlloyStyle()) { LOG(ERROR) << "Cannot add Chrome style BrowserView to Alloy style Window"; return; } diff --git a/tests/cefclient/browser/default_client_handler.cc b/tests/cefclient/browser/default_client_handler.cc index 21cc38c00..3c649c1a1 100644 --- a/tests/cefclient/browser/default_client_handler.cc +++ b/tests/cefclient/browser/default_client_handler.cc @@ -4,8 +4,16 @@ #include "tests/cefclient/browser/default_client_handler.h" +#include "tests/cefclient/browser/main_context.h" +#include "tests/cefclient/browser/root_window_manager.h" + namespace client { +DefaultClientHandler::DefaultClientHandler(std::optional use_alloy_style) + : use_alloy_style_( + use_alloy_style.value_or(MainContext::Get()->UseAlloyStyleGlobal())) { +} + // static CefRefPtr DefaultClientHandler::GetForClient( CefRefPtr client) { @@ -16,4 +24,39 @@ CefRefPtr DefaultClientHandler::GetForClient( return nullptr; } +bool DefaultClientHandler::OnBeforePopup( + CefRefPtr browser, + CefRefPtr frame, + const CefString& target_url, + const CefString& target_frame_name, + CefLifeSpanHandler::WindowOpenDisposition target_disposition, + bool user_gesture, + const CefPopupFeatures& popupFeatures, + CefWindowInfo& windowInfo, + CefRefPtr& client, + CefBrowserSettings& settings, + CefRefPtr& extra_info, + bool* no_javascript_access) { + CEF_REQUIRE_UI_THREAD(); + + if (target_disposition == CEF_WOD_NEW_PICTURE_IN_PICTURE) { + // Use default handling for document picture-in-picture popups. + client = nullptr; + return false; + } + + // Used to configure default values. + RootWindowConfig config(/*command_line=*/nullptr); + + // Potentially create a new RootWindow for the popup browser that will be + // created asynchronously. + MainContext::Get()->GetRootWindowManager()->CreateRootWindowAsPopup( + config.use_views, use_alloy_style_, config.with_controls, + /*is_osr=*/false, /*is_devtools=*/false, popupFeatures, windowInfo, + client, settings); + + // Allow popup creation. + return false; +} + } // namespace client diff --git a/tests/cefclient/browser/default_client_handler.h b/tests/cefclient/browser/default_client_handler.h index 4b88b9395..e404ba115 100644 --- a/tests/cefclient/browser/default_client_handler.h +++ b/tests/cefclient/browser/default_client_handler.h @@ -6,6 +6,8 @@ #define CEF_TESTS_CEFCLIENT_BROWSER_DEFAULT_CLIENT_HANDLER_H_ #pragma once +#include + #include "tests/cefclient/browser/base_client_handler.h" namespace client { @@ -14,18 +16,37 @@ namespace client { // style only. class DefaultClientHandler : public BaseClientHandler { public: - DefaultClientHandler() = default; + // If |use_alloy_style| is nullopt the global default will be used. + explicit DefaultClientHandler( + std::optional use_alloy_style = std::nullopt); // Returns the DefaultClientHandler for |client|, or nullptr if |client| is // not a DefaultClientHandler. static CefRefPtr GetForClient( CefRefPtr client); + protected: + bool OnBeforePopup( + CefRefPtr browser, + CefRefPtr frame, + const CefString& target_url, + const CefString& target_frame_name, + CefLifeSpanHandler::WindowOpenDisposition target_disposition, + bool user_gesture, + const CefPopupFeatures& popupFeatures, + CefWindowInfo& windowInfo, + CefRefPtr& client, + CefBrowserSettings& settings, + CefRefPtr& extra_info, + bool* no_javascript_access) override; + private: // Used to determine the object type. virtual const void* GetTypeKey() const override { return &kTypeKey; } static constexpr int kTypeKey = 0; + const bool use_alloy_style_; + IMPLEMENT_REFCOUNTING(DefaultClientHandler); DISALLOW_COPY_AND_ASSIGN(DefaultClientHandler); }; diff --git a/tests/cefclient/browser/root_window_manager.cc b/tests/cefclient/browser/root_window_manager.cc index f7f61c3b7..bbdecd08e 100644 --- a/tests/cefclient/browser/root_window_manager.cc +++ b/tests/cefclient/browser/root_window_manager.cc @@ -134,8 +134,11 @@ scoped_refptr RootWindowManager::CreateRootWindowAsPopup( if (MainContext::Get()->UseDefaultPopup() || (is_devtools && !use_views)) { // Use default window creation for the popup. A new |client| instance is - // still required by cefclient architecture. - client = new DefaultClientHandler(); + // required by cefclient architecture if the type is not already + // DefaultClientHandler. + if (!DefaultClientHandler::GetForClient(client)) { + client = new DefaultClientHandler(use_alloy_style); + } return nullptr; } diff --git a/tests/cefclient/browser/views_overlay_browser.cc b/tests/cefclient/browser/views_overlay_browser.cc new file mode 100644 index 000000000..3e38a3953 --- /dev/null +++ b/tests/cefclient/browser/views_overlay_browser.cc @@ -0,0 +1,102 @@ +// Copyright (c) 2024 The Chromium Embedded Framework Authors. All rights +// reserved. Use of this source code is governed by a BSD-style license that +// can be found in the LICENSE file. + +#include "tests/cefclient/browser/views_overlay_browser.h" + +#include "include/views/cef_window.h" +#include "tests/cefclient/browser/views_window.h" + +namespace client { + +ViewsOverlayBrowser::ViewsOverlayBrowser(ViewsWindow* owner_window) + : owner_window_(owner_window) {} + +void ViewsOverlayBrowser::Initialize( + CefRefPtr window, + CefRefPtr client, + const std::string& url, + const CefBrowserSettings& settings, + CefRefPtr request_context) { + CHECK(!window_); + window_ = window; + CHECK(window_); + + browser_view_ = CefBrowserView::CreateBrowserView( + client, url, settings, nullptr, request_context, this); + CHECK(browser_view_); + + controller_ = window_->AddOverlayView(browser_view_, CEF_DOCKING_MODE_CUSTOM, + /*can_activate=*/true); + CHECK(controller_); +} + +void ViewsOverlayBrowser::Destroy() { + window_ = nullptr; + browser_view_ = nullptr; + controller_->Destroy(); + controller_ = nullptr; +} + +void ViewsOverlayBrowser::UpdateBounds(CefInsets insets) { + if (!controller_) { + return; + } + + // Update location bar size, position and visibility. + const auto window_bounds = window_->GetBounds(); + + // Client coordinates with insets. + CefRect bounds; + bounds.x = insets.left; + bounds.width = window_bounds.width - insets.left - insets.right; + bounds.y = insets.top; + bounds.height = window_bounds.height - insets.top - insets.bottom; + + const auto min_size = browser_view_->GetMinimumSize(); + if (bounds.width < min_size.width || bounds.height < min_size.height) { + // Not enough space. + controller_->SetVisible(false); + } else { + controller_->SetSize(CefSize(bounds.width, bounds.height)); + controller_->SetBounds(bounds); + controller_->SetVisible(true); + } +} + +void ViewsOverlayBrowser::UpdateDraggableRegions( + std::vector& window_regions) { + if (controller_ && controller_->IsVisible()) { + window_regions.emplace_back(controller_->GetBounds(), + /*draggable=*/false); + } +} + +CefSize ViewsOverlayBrowser::GetMinimumSize(CefRefPtr view) { + return CefSize(200, 200); +} + +CefRefPtr +ViewsOverlayBrowser::GetDelegateForPopupBrowserView( + CefRefPtr browser_view, + const CefBrowserSettings& settings, + CefRefPtr client, + bool is_devtools) { + return owner_window_->GetDelegateForPopupBrowserView(browser_view, settings, + client, is_devtools); +} + +bool ViewsOverlayBrowser::OnPopupBrowserViewCreated( + CefRefPtr browser_view, + CefRefPtr popup_browser_view, + bool is_devtools) { + return owner_window_->OnPopupBrowserViewCreated( + browser_view, popup_browser_view, is_devtools); +} + +cef_runtime_style_t ViewsOverlayBrowser::GetBrowserRuntimeStyle() { + // Overlay browser view must always be Alloy style. + return CEF_RUNTIME_STYLE_ALLOY; +} + +} // namespace client diff --git a/tests/cefclient/browser/views_overlay_browser.h b/tests/cefclient/browser/views_overlay_browser.h new file mode 100644 index 000000000..f55d8e48f --- /dev/null +++ b/tests/cefclient/browser/views_overlay_browser.h @@ -0,0 +1,63 @@ +// Copyright (c) 2024 The Chromium Embedded Framework Authors. All rights +// reserved. Use of this source code is governed by a BSD-style license that +// can be found in the LICENSE file. + +#ifndef CEF_TESTS_CEFCLIENT_BROWSER_VIEWS_OVERLAY_BROWSER_H_ +#define CEF_TESTS_CEFCLIENT_BROWSER_VIEWS_OVERLAY_BROWSER_H_ +#pragma once + +#include "include/views/cef_browser_view.h" +#include "include/views/cef_browser_view_delegate.h" +#include "include/views/cef_overlay_controller.h" + +namespace client { + +class ViewsWindow; + +// Implements a browser view that receives absolute positioning on top of the +// main browser view. All methods must be called on the browser process UI +// thread. +class ViewsOverlayBrowser : public CefBrowserViewDelegate { + public: + explicit ViewsOverlayBrowser(ViewsWindow* owner_window); + + void Initialize(CefRefPtr window, + CefRefPtr client, + const std::string& url, + const CefBrowserSettings& settings, + CefRefPtr request_context); + void Destroy(); + + // Update browser bounds. + void UpdateBounds(CefInsets insets); + + // Exclude all regions obscured by overlays. + void UpdateDraggableRegions(std::vector& window_regions); + + private: + // CefViewDelegate methods: + CefSize GetMinimumSize(CefRefPtr view) override; + + // CefBrowserViewDelegate methods: + CefRefPtr GetDelegateForPopupBrowserView( + CefRefPtr browser_view, + const CefBrowserSettings& settings, + CefRefPtr client, + bool is_devtools) override; + bool OnPopupBrowserViewCreated(CefRefPtr browser_view, + CefRefPtr popup_browser_view, + bool is_devtools) override; + cef_runtime_style_t GetBrowserRuntimeStyle() override; + + ViewsWindow* const owner_window_; + CefRefPtr window_; + CefRefPtr browser_view_; + CefRefPtr controller_; + + IMPLEMENT_REFCOUNTING(ViewsOverlayBrowser); + DISALLOW_COPY_AND_ASSIGN(ViewsOverlayBrowser); +}; + +} // namespace client + +#endif // CEF_TESTS_CEFCLIENT_BROWSER_VIEWS_OVERLAY_BROWSER_H_ diff --git a/tests/cefclient/browser/views_window.cc b/tests/cefclient/browser/views_window.cc index 0ed0f59ea..43668716a 100644 --- a/tests/cefclient/browser/views_window.cc +++ b/tests/cefclient/browser/views_window.cc @@ -12,6 +12,7 @@ #include "include/cef_i18n_util.h" #include "include/views/cef_box_layout.h" #include "include/wrapper/cef_helpers.h" +#include "tests/cefclient/browser/default_client_handler.h" #include "tests/cefclient/browser/main_context.h" #include "tests/cefclient/browser/resource.h" #include "tests/cefclient/browser/views_style.h" @@ -146,6 +147,14 @@ CefRefPtr ViewsWindow::Create( CefRefPtr views_window = new ViewsWindow(type, delegate, nullptr, command_line); + // Only create an overlay browser for a primary window. + if (command_line->HasSwitch(switches::kShowOverlayBrowser)) { + views_window->with_overlay_browser_ = true; + views_window->initial_url_ = url; + views_window->settings_ = settings; + views_window->request_context_ = request_context; + } + const auto expected_browser_runtime_style = views_window->use_alloy_style_ ? CEF_RUNTIME_STYLE_ALLOY : CEF_RUNTIME_STYLE_CHROME; @@ -359,6 +368,11 @@ void ViewsWindow::SetDraggableRegions( overlay_controls_->UpdateDraggableRegions(window_regions); } + if (overlay_browser_) { + // Exclude all regions obscured by overlays. + overlay_browser_->UpdateDraggableRegions(window_regions); + } + window_->SetDraggableRegions(window_regions); } @@ -1001,6 +1015,19 @@ void ViewsWindow::OnWindowChanged(CefRefPtr view, bool added) { CreateLocationBar(), chrome_toolbar_type_ != CEF_CTT_NONE); } + + if (with_overlay_browser_) { + overlay_browser_ = new ViewsOverlayBrowser(this); + + // Use default behavior for the overlay browser. A new |client| instance + // is still required by cefclient architecture. + CefRefPtr client = + new DefaultClientHandler(/*use_alloy_style=*/true); + + overlay_browser_->Initialize(window_, client, initial_url_, settings_, + request_context_); + request_context_ = nullptr; + } } else { // Remove any controls that may include the Chrome toolbar before removing // the BrowserView. @@ -1015,6 +1042,10 @@ void ViewsWindow::OnWindowChanged(CefRefPtr view, bool added) { location_bar_ = nullptr; } } + if (overlay_browser_) { + overlay_browser_->Destroy(); + overlay_browser_ = nullptr; + } } } @@ -1028,6 +1059,12 @@ void ViewsWindow::OnLayoutChanged(CefRefPtr view, if (overlay_controls_) { overlay_controls_->UpdateControls(); } + + if (overlay_browser_) { + // TODO: Consider modifying insets based on toolbar visibility. + CefInsets window_insets(200, 200, 200, 200); + overlay_browser_->UpdateBounds(window_insets); + } } void ViewsWindow::OnThemeChanged(CefRefPtr view) { diff --git a/tests/cefclient/browser/views_window.h b/tests/cefclient/browser/views_window.h index 953a5579b..1b81eb3fb 100644 --- a/tests/cefclient/browser/views_window.h +++ b/tests/cefclient/browser/views_window.h @@ -27,6 +27,7 @@ #include "tests/cefclient/browser/image_cache.h" #include "tests/cefclient/browser/root_window.h" #include "tests/cefclient/browser/views_menu_bar.h" +#include "tests/cefclient/browser/views_overlay_browser.h" #include "tests/cefclient/browser/views_overlay_controls.h" namespace client { @@ -280,6 +281,13 @@ class ViewsWindow : public CefBrowserViewDelegate, CefRefPtr overlay_controls_; + // Overlay browser view state. + bool with_overlay_browser_ = false; + std::string initial_url_; + CefBrowserSettings settings_; + CefRefPtr request_context_; + CefRefPtr overlay_browser_; + std::optional default_titlebar_height_; std::optional override_titlebar_height_; diff --git a/tests/shared/common/client_switches.cc b/tests/shared/common/client_switches.cc index eb08b3cf2..9c43013d5 100644 --- a/tests/shared/common/client_switches.cc +++ b/tests/shared/common/client_switches.cc @@ -58,5 +58,6 @@ const char kHideWindowOnClose[] = "hide-window-on-close"; const char kAcceptsFirstMouse[] = "accepts-first-mouse"; const char kUseAlloyStyle[] = "use-alloy-style"; const char kUseChromeStyleWindow[] = "use-chrome-style-window"; +const char kShowOverlayBrowser[] = "show-overlay-browser"; } // namespace client::switches diff --git a/tests/shared/common/client_switches.h b/tests/shared/common/client_switches.h index 7f2d7480d..c1e111cdd 100644 --- a/tests/shared/common/client_switches.h +++ b/tests/shared/common/client_switches.h @@ -52,6 +52,7 @@ extern const char kHideWindowOnClose[]; extern const char kAcceptsFirstMouse[]; extern const char kUseAlloyStyle[]; extern const char kUseChromeStyleWindow[]; +extern const char kShowOverlayBrowser[]; } // namespace client::switches