cefclient: views: Add overlay BrowserView demo (see #3790)

Adds new `--show-overlay-browser` command-line flag that creates an
overlay containing an Alloy-style BrowserView.  The main BrowserView
may be Chrome- (default) or Alloy-style (add `--use-alloy-style`).
This overlay will only be added to standalone (non-popup) windows.

Popups triggered via the overlay will receive standard Alloy-style
handling in cefclient (e.g. Views-hosted window). Add
`--use-default-popup` for default popup handling.
This commit is contained in:
Marshall Greenblatt 2024-10-07 13:50:16 -04:00
parent e23bf31bf4
commit 1bc1ca15d5
11 changed files with 286 additions and 5 deletions

View File

@ -288,6 +288,8 @@
'tests/cefclient/browser/urlrequest_test.h', 'tests/cefclient/browser/urlrequest_test.h',
'tests/cefclient/browser/views_menu_bar.cc', 'tests/cefclient/browser/views_menu_bar.cc',
'tests/cefclient/browser/views_menu_bar.h', '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.cc',
'tests/cefclient/browser/views_overlay_controls.h', 'tests/cefclient/browser/views_overlay_controls.h',
'tests/cefclient/browser/views_style.cc', 'tests/cefclient/browser/views_style.cc',

View File

@ -280,8 +280,8 @@ void CefBrowserViewImpl::AddedToWidget() {
CefWidget* cef_widget = CefWidget::GetForWidget(widget); CefWidget* cef_widget = CefWidget::GetForWidget(widget);
DCHECK(cef_widget); DCHECK(cef_widget);
if (!browser_) { if (!browser_ && !is_alloy_style_) {
if (cef_widget->IsAlloyStyle() && !is_alloy_style_) { if (cef_widget->IsAlloyStyle()) {
LOG(ERROR) << "Cannot add Chrome style BrowserView to Alloy style Window"; LOG(ERROR) << "Cannot add Chrome style BrowserView to Alloy style Window";
return; return;
} }

View File

@ -4,8 +4,16 @@
#include "tests/cefclient/browser/default_client_handler.h" #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 { namespace client {
DefaultClientHandler::DefaultClientHandler(std::optional<bool> use_alloy_style)
: use_alloy_style_(
use_alloy_style.value_or(MainContext::Get()->UseAlloyStyleGlobal())) {
}
// static // static
CefRefPtr<DefaultClientHandler> DefaultClientHandler::GetForClient( CefRefPtr<DefaultClientHandler> DefaultClientHandler::GetForClient(
CefRefPtr<CefClient> client) { CefRefPtr<CefClient> client) {
@ -16,4 +24,39 @@ CefRefPtr<DefaultClientHandler> DefaultClientHandler::GetForClient(
return nullptr; return nullptr;
} }
bool DefaultClientHandler::OnBeforePopup(
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
const CefString& target_url,
const CefString& target_frame_name,
CefLifeSpanHandler::WindowOpenDisposition target_disposition,
bool user_gesture,
const CefPopupFeatures& popupFeatures,
CefWindowInfo& windowInfo,
CefRefPtr<CefClient>& client,
CefBrowserSettings& settings,
CefRefPtr<CefDictionaryValue>& 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 } // namespace client

View File

@ -6,6 +6,8 @@
#define CEF_TESTS_CEFCLIENT_BROWSER_DEFAULT_CLIENT_HANDLER_H_ #define CEF_TESTS_CEFCLIENT_BROWSER_DEFAULT_CLIENT_HANDLER_H_
#pragma once #pragma once
#include <optional>
#include "tests/cefclient/browser/base_client_handler.h" #include "tests/cefclient/browser/base_client_handler.h"
namespace client { namespace client {
@ -14,18 +16,37 @@ namespace client {
// style only. // style only.
class DefaultClientHandler : public BaseClientHandler { class DefaultClientHandler : public BaseClientHandler {
public: public:
DefaultClientHandler() = default; // If |use_alloy_style| is nullopt the global default will be used.
explicit DefaultClientHandler(
std::optional<bool> use_alloy_style = std::nullopt);
// Returns the DefaultClientHandler for |client|, or nullptr if |client| is // Returns the DefaultClientHandler for |client|, or nullptr if |client| is
// not a DefaultClientHandler. // not a DefaultClientHandler.
static CefRefPtr<DefaultClientHandler> GetForClient( static CefRefPtr<DefaultClientHandler> GetForClient(
CefRefPtr<CefClient> client); CefRefPtr<CefClient> client);
protected:
bool OnBeforePopup(
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
const CefString& target_url,
const CefString& target_frame_name,
CefLifeSpanHandler::WindowOpenDisposition target_disposition,
bool user_gesture,
const CefPopupFeatures& popupFeatures,
CefWindowInfo& windowInfo,
CefRefPtr<CefClient>& client,
CefBrowserSettings& settings,
CefRefPtr<CefDictionaryValue>& extra_info,
bool* no_javascript_access) override;
private: private:
// Used to determine the object type. // Used to determine the object type.
virtual const void* GetTypeKey() const override { return &kTypeKey; } virtual const void* GetTypeKey() const override { return &kTypeKey; }
static constexpr int kTypeKey = 0; static constexpr int kTypeKey = 0;
const bool use_alloy_style_;
IMPLEMENT_REFCOUNTING(DefaultClientHandler); IMPLEMENT_REFCOUNTING(DefaultClientHandler);
DISALLOW_COPY_AND_ASSIGN(DefaultClientHandler); DISALLOW_COPY_AND_ASSIGN(DefaultClientHandler);
}; };

View File

@ -134,8 +134,11 @@ scoped_refptr<RootWindow> RootWindowManager::CreateRootWindowAsPopup(
if (MainContext::Get()->UseDefaultPopup() || (is_devtools && !use_views)) { if (MainContext::Get()->UseDefaultPopup() || (is_devtools && !use_views)) {
// Use default window creation for the popup. A new |client| instance is // Use default window creation for the popup. A new |client| instance is
// still required by cefclient architecture. // required by cefclient architecture if the type is not already
client = new DefaultClientHandler(); // DefaultClientHandler.
if (!DefaultClientHandler::GetForClient(client)) {
client = new DefaultClientHandler(use_alloy_style);
}
return nullptr; return nullptr;
} }

View File

@ -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<CefWindow> window,
CefRefPtr<CefClient> client,
const std::string& url,
const CefBrowserSettings& settings,
CefRefPtr<CefRequestContext> 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<CefDraggableRegion>& window_regions) {
if (controller_ && controller_->IsVisible()) {
window_regions.emplace_back(controller_->GetBounds(),
/*draggable=*/false);
}
}
CefSize ViewsOverlayBrowser::GetMinimumSize(CefRefPtr<CefView> view) {
return CefSize(200, 200);
}
CefRefPtr<CefBrowserViewDelegate>
ViewsOverlayBrowser::GetDelegateForPopupBrowserView(
CefRefPtr<CefBrowserView> browser_view,
const CefBrowserSettings& settings,
CefRefPtr<CefClient> client,
bool is_devtools) {
return owner_window_->GetDelegateForPopupBrowserView(browser_view, settings,
client, is_devtools);
}
bool ViewsOverlayBrowser::OnPopupBrowserViewCreated(
CefRefPtr<CefBrowserView> browser_view,
CefRefPtr<CefBrowserView> 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

View File

@ -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<CefWindow> window,
CefRefPtr<CefClient> client,
const std::string& url,
const CefBrowserSettings& settings,
CefRefPtr<CefRequestContext> request_context);
void Destroy();
// Update browser bounds.
void UpdateBounds(CefInsets insets);
// Exclude all regions obscured by overlays.
void UpdateDraggableRegions(std::vector<CefDraggableRegion>& window_regions);
private:
// CefViewDelegate methods:
CefSize GetMinimumSize(CefRefPtr<CefView> view) override;
// CefBrowserViewDelegate methods:
CefRefPtr<CefBrowserViewDelegate> GetDelegateForPopupBrowserView(
CefRefPtr<CefBrowserView> browser_view,
const CefBrowserSettings& settings,
CefRefPtr<CefClient> client,
bool is_devtools) override;
bool OnPopupBrowserViewCreated(CefRefPtr<CefBrowserView> browser_view,
CefRefPtr<CefBrowserView> popup_browser_view,
bool is_devtools) override;
cef_runtime_style_t GetBrowserRuntimeStyle() override;
ViewsWindow* const owner_window_;
CefRefPtr<CefWindow> window_;
CefRefPtr<CefBrowserView> browser_view_;
CefRefPtr<CefOverlayController> controller_;
IMPLEMENT_REFCOUNTING(ViewsOverlayBrowser);
DISALLOW_COPY_AND_ASSIGN(ViewsOverlayBrowser);
};
} // namespace client
#endif // CEF_TESTS_CEFCLIENT_BROWSER_VIEWS_OVERLAY_BROWSER_H_

View File

@ -12,6 +12,7 @@
#include "include/cef_i18n_util.h" #include "include/cef_i18n_util.h"
#include "include/views/cef_box_layout.h" #include "include/views/cef_box_layout.h"
#include "include/wrapper/cef_helpers.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/main_context.h"
#include "tests/cefclient/browser/resource.h" #include "tests/cefclient/browser/resource.h"
#include "tests/cefclient/browser/views_style.h" #include "tests/cefclient/browser/views_style.h"
@ -146,6 +147,14 @@ CefRefPtr<ViewsWindow> ViewsWindow::Create(
CefRefPtr<ViewsWindow> views_window = CefRefPtr<ViewsWindow> views_window =
new ViewsWindow(type, delegate, nullptr, command_line); 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_ const auto expected_browser_runtime_style = views_window->use_alloy_style_
? CEF_RUNTIME_STYLE_ALLOY ? CEF_RUNTIME_STYLE_ALLOY
: CEF_RUNTIME_STYLE_CHROME; : CEF_RUNTIME_STYLE_CHROME;
@ -359,6 +368,11 @@ void ViewsWindow::SetDraggableRegions(
overlay_controls_->UpdateDraggableRegions(window_regions); overlay_controls_->UpdateDraggableRegions(window_regions);
} }
if (overlay_browser_) {
// Exclude all regions obscured by overlays.
overlay_browser_->UpdateDraggableRegions(window_regions);
}
window_->SetDraggableRegions(window_regions); window_->SetDraggableRegions(window_regions);
} }
@ -1001,6 +1015,19 @@ void ViewsWindow::OnWindowChanged(CefRefPtr<CefView> view, bool added) {
CreateLocationBar(), CreateLocationBar(),
chrome_toolbar_type_ != CEF_CTT_NONE); 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<CefClient> client =
new DefaultClientHandler(/*use_alloy_style=*/true);
overlay_browser_->Initialize(window_, client, initial_url_, settings_,
request_context_);
request_context_ = nullptr;
}
} else { } else {
// Remove any controls that may include the Chrome toolbar before removing // Remove any controls that may include the Chrome toolbar before removing
// the BrowserView. // the BrowserView.
@ -1015,6 +1042,10 @@ void ViewsWindow::OnWindowChanged(CefRefPtr<CefView> view, bool added) {
location_bar_ = nullptr; location_bar_ = nullptr;
} }
} }
if (overlay_browser_) {
overlay_browser_->Destroy();
overlay_browser_ = nullptr;
}
} }
} }
@ -1028,6 +1059,12 @@ void ViewsWindow::OnLayoutChanged(CefRefPtr<CefView> view,
if (overlay_controls_) { if (overlay_controls_) {
overlay_controls_->UpdateControls(); 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<CefView> view) { void ViewsWindow::OnThemeChanged(CefRefPtr<CefView> view) {

View File

@ -27,6 +27,7 @@
#include "tests/cefclient/browser/image_cache.h" #include "tests/cefclient/browser/image_cache.h"
#include "tests/cefclient/browser/root_window.h" #include "tests/cefclient/browser/root_window.h"
#include "tests/cefclient/browser/views_menu_bar.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" #include "tests/cefclient/browser/views_overlay_controls.h"
namespace client { namespace client {
@ -280,6 +281,13 @@ class ViewsWindow : public CefBrowserViewDelegate,
CefRefPtr<ViewsOverlayControls> overlay_controls_; CefRefPtr<ViewsOverlayControls> overlay_controls_;
// Overlay browser view state.
bool with_overlay_browser_ = false;
std::string initial_url_;
CefBrowserSettings settings_;
CefRefPtr<CefRequestContext> request_context_;
CefRefPtr<ViewsOverlayBrowser> overlay_browser_;
std::optional<float> default_titlebar_height_; std::optional<float> default_titlebar_height_;
std::optional<float> override_titlebar_height_; std::optional<float> override_titlebar_height_;

View File

@ -58,5 +58,6 @@ const char kHideWindowOnClose[] = "hide-window-on-close";
const char kAcceptsFirstMouse[] = "accepts-first-mouse"; const char kAcceptsFirstMouse[] = "accepts-first-mouse";
const char kUseAlloyStyle[] = "use-alloy-style"; const char kUseAlloyStyle[] = "use-alloy-style";
const char kUseChromeStyleWindow[] = "use-chrome-style-window"; const char kUseChromeStyleWindow[] = "use-chrome-style-window";
const char kShowOverlayBrowser[] = "show-overlay-browser";
} // namespace client::switches } // namespace client::switches

View File

@ -52,6 +52,7 @@ extern const char kHideWindowOnClose[];
extern const char kAcceptsFirstMouse[]; extern const char kAcceptsFirstMouse[];
extern const char kUseAlloyStyle[]; extern const char kUseAlloyStyle[];
extern const char kUseChromeStyleWindow[]; extern const char kUseChromeStyleWindow[];
extern const char kShowOverlayBrowser[];
} // namespace client::switches } // namespace client::switches