chrome: Add support for Alloy style browsers and windows (see #3681)

Split the Alloy runtime into bootstrap and style components. Support
creation of Alloy style browsers and windows with the Chrome runtime.
Chrome runtime (`--enable-chrome-runtime`) + Alloy style
(`--use-alloy-style`) supports Views (`--use-views`), native parent
(`--use-native`) and windowless rendering
(`--off-screen-rendering-enabled`).

Print preview is supported in all cases except with windowless rendering
on all platforms and native parent on MacOS. It is disabled by default
with Alloy style for legacy compatibility. Where supported it can be
enabled or disabled globally using `--[enable|disable]-print-preview` or
configured on a per-RequestContext basis using the
`printing.print_preview_disabled` preference. It also behaves as
expected when triggered via the PDF viewer print button.

Chrome runtime + Alloy style behavior differs from Alloy runtime in the
following significant ways:

- Supports Chrome error pages by default.
- DevTools popups are Chrome style only (cannot be windowless).
- The Alloy extension API will not supported.

Chrome runtime + Alloy style passes all expected Alloy ceftests except
the following:

- `DisplayTest.AutoResize` (Alloy extension API not supported)
- `DownloadTest.*` (Download API not yet supported)
- `ExtensionTest.*` (Alloy extension API not supported)

This change also adds Chrome runtime support for
CefContextMenuHandler::RunContextMenu (see #3293).

This change also explicitly blocks (and doesn't retry) FrameAttached
requests from PDF viewer and print preview excluded frames (see #3664).

Known issues specific to Chrome runtime + Alloy style:
- DevTools popup with windowless rendering doesn't load successfully.
  Use windowed rendering or remote debugging as a workaround.
- Chrome style Window with Alloy style BrowserView (`--use-alloy-style
  --use-chrome-style-window`) does not show Chrome theme changes.

To test:
- Run `ceftests --enable-chrome-runtime --use-alloy-style
       [--use-chrome-style-window] [--use-views|--use-native]
       --gtest_filter=...`
- Run `cefclient --enable-chrome-runtime --use-alloy-style
       [--use-chrome-style-window]
       [--use-views|--use-native|--off-screen-rendering-enabled]`
- Run `cefsimple --enable-chrome-runtime --use-alloy-style [--use-views]`
This commit is contained in:
Marshall Greenblatt
2024-04-17 12:01:26 -04:00
parent 62c93f01f4
commit dca0435d2f
216 changed files with 3388 additions and 1565 deletions

View File

@@ -12,6 +12,7 @@
#include "libcef/browser/browser_platform_delegate.h"
#include "libcef/browser/chrome/chrome_browser_context.h"
#include "libcef/browser/chrome/chrome_browser_host_impl.h"
#include "libcef/browser/chrome/chrome_devtools_window_runner.h"
#include "libcef/browser/chrome/views/chrome_browser_view.h"
#include "libcef/browser/chrome/views/chrome_child_window.h"
#include "libcef/browser/hang_monitor.h"
@@ -53,23 +54,35 @@ ChromeBrowserDelegate::ChromeBrowserDelegate(
ChromeBrowserDelegate::~ChromeBrowserDelegate() = default;
// static
Browser* ChromeBrowserDelegate::CreateDevToolsBrowser(
Profile* profile,
Browser* opener,
content::WebContents* inspected_web_contents,
std::unique_ptr<content::WebContents>& 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
// constructor for the new popup Browser. It may be nullptr in certain
// 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 =
// Browser Tab).
CefRefPtr<CefBrowserHostBase> opener_browser_host =
opener ? ChromeBrowserHostImpl::GetBrowserForBrowser(opener) : nullptr;
if (!opener_browser_host) {
return nullptr;
// |inspected_web_contents| may be an Alloy style browser.
opener_browser_host =
CefBrowserHostBase::GetBrowserForContents(inspected_web_contents);
if (!opener_browser_host) {
// The popup browser host will instead be created via SetAsDelegate.
return nullptr;
}
}
// We expect openers and popups to have the same Profile.
CHECK_EQ(opener->profile(), profile);
CHECK_EQ(
CefRequestContextImpl::GetProfile(opener_browser_host->request_context()),
profile);
if (opener) {
CHECK_EQ(opener->profile(), profile);
}
//
// 1. Get configuration settings from the user and create the new platform
@@ -84,25 +97,26 @@ Browser* ChromeBrowserDelegate::CreateDevToolsBrowser(
CefBrowserCreateParams create_params;
CefWindowInfo window_info;
auto* devtools_window_runner = static_cast<ChromeDevToolsWindowRunner*>(
opener_browser_host->GetDevToolsWindowRunner());
auto pending_show_devtools_params =
devtools_window_runner->TakePendingParams();
// 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_) {
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();
create_params.client = pending_show_devtools_params->client_;
create_params.settings = pending_show_devtools_params->settings_;
window_info = pending_show_devtools_params->window_info_;
} 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
CefBrowserCreateParams::InitWindowInfo(&window_info,
opener_browser_host.get());
}
// Start with the same extra info as the opener, for consistency with
@@ -121,10 +135,14 @@ Browser* ChromeBrowserDelegate::CreateDevToolsBrowser(
create_params.settings, create_params.extra_info, &use_default_window);
}
if (opener_browser_host->platform_delegate()->HasExternalParent()) {
if (window_info.windowless_rendering_enabled) {
LOG(ERROR)
<< "Windowless rendering is not supported for this DevTools window";
} else 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);
create_params.MaybeSetWindowInfo(window_info, /*allow_alloy_style=*/false,
/*allow_chrome_style=*/true);
} else if (chrome_child_window::HasParentHandle(window_info)) {
LOG(ERROR) << "Parent window handle not supported for this DevTools window";
}
@@ -132,20 +150,24 @@ Browser* ChromeBrowserDelegate::CreateDevToolsBrowser(
create_params.popup_with_views_hosted_opener =
CefBrowserInfoManager::ShouldCreateViewsHostedPopup(opener_browser_host,
use_default_window);
create_params.popup_with_alloy_style_opener = false;
auto platform_delegate = CefBrowserPlatformDelegate::Create(create_params);
CHECK(platform_delegate);
// Expect runtime style to match.
CHECK(platform_delegate->IsChromeStyle());
//
// 2. Create the new browser host. Logical equivalent of WebContentsCreated()
// for normal popups.
// 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(
// 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 = ChromeBrowserDelegate::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);
@@ -169,10 +191,10 @@ Browser* ChromeBrowserDelegate::CreateDevToolsBrowser(
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());
// last because we 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.
devtools_window_runner->SetDevToolsBrowserHost(browser_host->GetWeakPtr());
auto browser = browser_host->browser();
CHECK(browser);
@@ -203,7 +225,8 @@ std::unique_ptr<content::WebContents> ChromeBrowserDelegate::AddWebContents(
void ChromeBrowserDelegate::OnWebContentsCreated(
content::WebContents* new_contents) {
// Necessary to receive LoadingStateChanged calls during initial navigation.
// This will be called again in Browser::SetAsDelegate, which should be fine.
// This will be called again in Browser::SetAsDelegate, which should be
// fine.
new_contents->SetDelegate(browser_);
SetAsDelegate(new_contents, /*set_delegate=*/true);
@@ -233,14 +256,18 @@ void ChromeBrowserDelegate::SetAsDelegate(content::WebContents* web_contents,
auto platform_delegate = CefBrowserPlatformDelegate::Create(create_params_);
CHECK(platform_delegate);
// Expect runtime style to match.
CHECK(platform_delegate->IsChromeStyle());
auto browser_info = CefBrowserInfoManager::GetInstance()->CreateBrowserInfo(
is_devtools_popup, /*is_windowless=*/false, create_params_.extra_info);
is_devtools_popup, /*is_windowless=*/false,
platform_delegate->IsPrintPreviewSupported(), create_params_.extra_info);
auto request_context_impl =
CefRequestContextImpl::GetOrCreateForRequestContext(
create_params_.request_context);
CreateBrowserHost(web_contents, create_params_.settings,
CreateBrowserHost(browser_, web_contents, create_params_.settings,
create_params_.client, std::move(platform_delegate),
browser_info, is_devtools_popup, /*opener=*/nullptr,
request_context_impl);
@@ -477,9 +504,9 @@ void ChromeBrowserDelegate::WebContentsCreated(
}
// 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.
// 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);
@@ -592,20 +619,16 @@ bool ChromeBrowserDelegate::HandleKeyboardEvent(
return false;
}
void ChromeBrowserDelegate::SetPendingShowDevToolsParams(
std::unique_ptr<CefShowDevToolsParams> params) {
DCHECK(!pending_show_devtools_params_);
pending_show_devtools_params_ = std::move(params);
}
// static
CefRefPtr<ChromeBrowserHostImpl> ChromeBrowserDelegate::CreateBrowserHost(
Browser* browser,
content::WebContents* web_contents,
const CefBrowserSettings& settings,
CefRefPtr<CefClient> client,
std::unique_ptr<CefBrowserPlatformDelegate> platform_delegate,
scoped_refptr<CefBrowserInfo> browser_info,
bool is_devtools_popup,
CefRefPtr<ChromeBrowserHostImpl> opener,
CefRefPtr<CefBrowserHostBase> opener,
CefRefPtr<CefRequestContextImpl> request_context_impl) {
CEF_REQUIRE_UIT();
DCHECK(web_contents);
@@ -615,6 +638,7 @@ CefRefPtr<ChromeBrowserHostImpl> ChromeBrowserDelegate::CreateBrowserHost(
// If |opener| is non-nullptr it must be a popup window.
DCHECK(!opener.get() || browser_info->is_popup());
DCHECK(browser || opener);
if (!client) {
if (auto app = CefAppManager::Get()->GetApplication()) {
@@ -628,15 +652,20 @@ CefRefPtr<ChromeBrowserHostImpl> ChromeBrowserDelegate::CreateBrowserHost(
LOG(WARNING) << "Creating a chrome browser without a client";
}
// Get or create a ChromeBrowserContext for the browser Profile. Creation may
// be necessary when selecting a new or incognito Profile for the first time
// via the Chrome UI.
auto chrome_browser_context =
ChromeBrowserContext::GetOrCreateForProfile(browser_->profile());
Profile* profile =
browser ? browser->profile()
: Profile::FromBrowserContext(web_contents->GetBrowserContext());
// If the provided CefRequestContext matches the ChromeBrowserContext then use
// the provided one, as it will have the preferred CefRequestContextHandler.
// Otherwise, get or create a CefRequestContext that matches.
// Get or create a ChromeBrowserContext for the browser Profile. Creation
// may be necessary when selecting a new or incognito Profile for the first
// time via the Chrome UI.
auto* chrome_browser_context =
ChromeBrowserContext::GetOrCreateForProfile(profile);
// If the provided CefRequestContext matches the ChromeBrowserContext then
// use the provided one, as it will have the preferred
// CefRequestContextHandler. Otherwise, get or create a CefRequestContext
// that matches.
if (chrome_browser_context != request_context_impl->GetBrowserContext()) {
CefRefPtr<CefRequestContextHandler> handler;
if (auto app = CefAppManager::Get()->GetApplication()) {
@@ -658,12 +687,13 @@ CefRefPtr<ChromeBrowserHostImpl> ChromeBrowserDelegate::CreateBrowserHost(
// The Chrome browser for a normal popup won't be created until
// AddNewContents().
if (!opener) {
browser_host->SetBrowser(browser_);
browser_host->SetBrowser(browser);
}
return browser_host;
}
// static
CefRefPtr<ChromeBrowserHostImpl>
ChromeBrowserDelegate::CreateBrowserHostForPopup(
content::WebContents* web_contents,
@@ -672,10 +702,13 @@ ChromeBrowserDelegate::CreateBrowserHostForPopup(
CefRefPtr<CefDictionaryValue> extra_info,
std::unique_ptr<CefBrowserPlatformDelegate> platform_delegate,
bool is_devtools_popup,
CefRefPtr<ChromeBrowserHostImpl> opener) {
CefRefPtr<CefBrowserHostBase> opener) {
DCHECK(opener);
auto browser_info =
CefBrowserInfoManager::GetInstance()->CreatePopupBrowserInfo(
web_contents, /*is_windowless=*/false, extra_info);
web_contents, /*is_windowless=*/false,
platform_delegate->IsPrintPreviewSupported(), extra_info);
CHECK(browser_info->is_popup());
// Popups must share the same RequestContext as the parent.
@@ -684,7 +717,7 @@ ChromeBrowserDelegate::CreateBrowserHostForPopup(
// 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,
return CreateBrowserHost(/*browser=*/nullptr, web_contents, settings, client,
std::move(platform_delegate), browser_info,
is_devtools_popup, opener, request_context_impl);
}
@@ -750,4 +783,14 @@ std::unique_ptr<BrowserDelegate> BrowserDelegate::Create(
opener);
}
// static
Browser* BrowserDelegate::CreateDevToolsBrowser(
Profile* profile,
Browser* opener,
content::WebContents* inspected_web_contents,
std::unique_ptr<content::WebContents>& devtools_contents) {
return ChromeBrowserDelegate::CreateDevToolsBrowser(
profile, opener, inspected_web_contents, devtools_contents);
}
} // namespace cef