Files
cef/libcef/browser/chrome/chrome_browser_delegate.cc
Marshall Greenblatt 3000bc8748 chrome: win/linux: Add support for browser with native parent (see issue #3294)
This change adds Chrome runtime support on Windows and Linux for creating a
browser parented to a native window supplied by the client application.
Expected API usage and window behavior is similar to what already exists with
the Alloy runtime. The parent window handle should be specified by using
CefWindowInfo::SetAsChild in combination with the CefBrowserHost::CreateBrowser
and CefLifeSpanHandler::OnBeforePopup callbacks.

The previously existing behavior of creating a fully-featured Chrome browser
window when empty CefWindowInfo is used with CreateBrowser remains unchanged
and Views is still the preferred API for creating top-level Chrome windows
with custom styling (e.g. title bar only, frameless, etc).

The cefclient Popup Window test with a native parent window continues to crash
on Linux with both the Alloy and Chrome runtimes (see issue #3165).

Also adds Chrome runtime support for CefDisplayHandler::OnCursorChange.

To test:
- Run `cefclient --enable-chrome-runtime [--use-views]` for the default (and
  previously existing) Views-based behavior.
- Run `cefclient --enable-chrome-runtime --use-native` for the new native
  parent window behavior.
- Run `cefclient --enable-chrome-runtime --use-native --no-activate` and the
  window will not be activated (take input focus) on launch (Windows only).
- Run `cefclient --enable-chrome-runtime [--use-views|--use-native]
  --mouse-cursor-change-disabled` and the mouse cursor will not change on
  mouseover of DOM elements.
2022-04-12 11:49:26 -04:00

354 lines
12 KiB
C++

// Copyright 2020 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 <memory>
#include "libcef/browser/chrome/chrome_browser_delegate.h"
#include "libcef/browser/browser_contents_delegate.h"
#include "libcef/browser/browser_host_base.h"
#include "libcef/browser/browser_info_manager.h"
#include "libcef/browser/browser_platform_delegate.h"
#include "libcef/browser/chrome/chrome_browser_host_impl.h"
#include "libcef/browser/request_context_impl.h"
#include "libcef/common/app_manager.h"
#include "libcef/common/frame_util.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_tabstrip.h"
#include "content/public/browser/global_routing_id.h"
#include "content/public/browser/keyboard_event_processing_result.h"
#include "content/public/browser/native_web_keyboard_event.h"
using content::KeyboardEventProcessingResult;
ChromeBrowserDelegate::ChromeBrowserDelegate(
Browser* browser,
const CefBrowserCreateParams& create_params)
: browser_(browser), create_params_(create_params) {
DCHECK(browser_);
}
ChromeBrowserDelegate::~ChromeBrowserDelegate() = default;
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.
new_contents->SetDelegate(browser_);
SetAsDelegate(new_contents, /*set_delegate=*/true);
}
void ChromeBrowserDelegate::SetAsDelegate(content::WebContents* web_contents,
bool set_delegate) {
DCHECK(web_contents);
auto browser_host =
ChromeBrowserHostImpl::GetBrowserForContents(web_contents);
// |set_delegate=false| only makes sense if we already have a browser host.
DCHECK(browser_host || set_delegate);
if (browser_host) {
// We already have a browser host, so just change the associated Browser.
browser_host->SetBrowser(set_delegate ? browser_ : nullptr);
return;
}
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);
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);
}
bool ChromeBrowserDelegate::ShowStatusBubble(bool show_by_default) {
if (!show_status_bubble_.has_value()) {
show_status_bubble_ = show_by_default;
if (auto browser = ChromeBrowserHostImpl::GetBrowserForBrowser(browser_)) {
const auto& state = browser->settings().chrome_status_bubble;
if (show_by_default && state == STATE_DISABLED) {
show_status_bubble_ = false;
} else if (!show_by_default && state == STATE_ENABLED) {
show_status_bubble_ = true;
}
}
}
return *show_status_bubble_;
}
bool ChromeBrowserDelegate::HandleCommand(int command_id,
WindowOpenDisposition disposition) {
if (auto browser = ChromeBrowserHostImpl::GetBrowserForBrowser(browser_)) {
if (auto client = browser->GetClient()) {
if (auto handler = client->GetCommandHandler()) {
return handler->OnChromeCommand(
browser.get(), command_id,
static_cast<cef_window_open_disposition_t>(disposition));
}
}
}
return false;
}
void ChromeBrowserDelegate::WebContentsCreated(
content::WebContents* source_contents,
int opener_render_process_id,
int opener_render_frame_id,
const std::string& frame_name,
const GURL& target_url,
content::WebContents* new_contents) {
CefBrowserSettings settings;
CefRefPtr<CefClient> client;
std::unique_ptr<CefBrowserPlatformDelegate> platform_delegate;
CefRefPtr<CefDictionaryValue> extra_info;
CefBrowserInfoManager::GetInstance()->WebContentsCreated(
target_url,
frame_util::MakeGlobalId(opener_render_process_id,
opener_render_frame_id),
settings, client, platform_delegate, extra_info);
auto opener = ChromeBrowserHostImpl::GetBrowserForContents(source_contents);
if (!opener) {
LOG(ERROR) << "No opener found for chrome popup browser";
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);
}
void ChromeBrowserDelegate::AddNewContents(
content::WebContents* source_contents,
std::unique_ptr<content::WebContents> new_contents,
const GURL& target_url,
WindowOpenDisposition disposition,
const gfx::Rect& initial_rect,
bool user_gesture,
bool* was_blocked) {
auto new_browser =
ChromeBrowserHostImpl::GetBrowserForContents(new_contents.get());
if (new_browser) {
// Create a new Browser and give it ownership of the WebContents.
new_browser->AddNewContents(std::move(new_contents));
return;
}
// Fall back to default behavior from Browser::AddNewContents.
chrome::AddWebContents(browser_, source_contents, std::move(new_contents),
target_url, disposition, initial_rect);
}
content::WebContents* ChromeBrowserDelegate::OpenURLFromTab(
content::WebContents* source,
const content::OpenURLParams& params) {
// |source| may be nullptr when opening a link from chrome UI such as the
// Reading List sidebar. In that case we default to using the Browser's
// currently active WebContents.
if (!source) {
source = browser_->tab_strip_model()->GetActiveWebContents();
DCHECK(source);
}
// Return nullptr to cancel the navigation. Otherwise, proceed with default
// chrome handling.
if (auto delegate = GetDelegateForWebContents(source)) {
return delegate->OpenURLFromTab(source, params);
}
return nullptr;
}
void ChromeBrowserDelegate::LoadingStateChanged(content::WebContents* source,
bool should_show_loading_ui) {
if (auto delegate = GetDelegateForWebContents(source)) {
delegate->LoadingStateChanged(source, should_show_loading_ui);
}
}
void ChromeBrowserDelegate::UpdateTargetURL(content::WebContents* source,
const GURL& url) {
if (auto delegate = GetDelegateForWebContents(source)) {
delegate->UpdateTargetURL(source, url);
}
}
bool ChromeBrowserDelegate::DidAddMessageToConsole(
content::WebContents* source,
blink::mojom::ConsoleMessageLevel log_level,
const std::u16string& message,
int32_t line_no,
const std::u16string& source_id) {
if (auto delegate = GetDelegateForWebContents(source)) {
return delegate->DidAddMessageToConsole(source, log_level, message, line_no,
source_id);
}
return false;
}
void ChromeBrowserDelegate::DidNavigatePrimaryMainFramePostCommit(
content::WebContents* web_contents) {
if (auto delegate = GetDelegateForWebContents(web_contents)) {
delegate->DidNavigatePrimaryMainFramePostCommit(web_contents);
}
}
void ChromeBrowserDelegate::EnterFullscreenModeForTab(
content::RenderFrameHost* requesting_frame,
const blink::mojom::FullscreenOptions& options) {
auto web_contents =
content::WebContents::FromRenderFrameHost(requesting_frame);
if (!web_contents)
return;
if (auto delegate = GetDelegateForWebContents(web_contents)) {
delegate->EnterFullscreenModeForTab(requesting_frame, options);
}
}
void ChromeBrowserDelegate::ExitFullscreenModeForTab(
content::WebContents* web_contents) {
if (auto delegate = GetDelegateForWebContents(web_contents)) {
delegate->ExitFullscreenModeForTab(web_contents);
}
}
void ChromeBrowserDelegate::CanDownload(
const GURL& url,
const std::string& request_method,
base::OnceCallback<void(bool)> callback) {
auto source = browser_->tab_strip_model()->GetActiveWebContents();
DCHECK(source);
if (auto delegate = GetDelegateForWebContents(source)) {
delegate->CanDownload(url, request_method, std::move(callback));
return;
}
std::move(callback).Run(true);
}
KeyboardEventProcessingResult ChromeBrowserDelegate::PreHandleKeyboardEvent(
content::WebContents* source,
const content::NativeWebKeyboardEvent& event) {
if (auto delegate = GetDelegateForWebContents(source)) {
return delegate->PreHandleKeyboardEvent(source, event);
}
return KeyboardEventProcessingResult::NOT_HANDLED;
}
bool ChromeBrowserDelegate::HandleKeyboardEvent(
content::WebContents* source,
const content::NativeWebKeyboardEvent& event) {
if (auto delegate = GetDelegateForWebContents(source)) {
return delegate->HandleKeyboardEvent(source, event);
}
return false;
}
void ChromeBrowserDelegate::CreateBrowser(
content::WebContents* web_contents,
CefBrowserSettings settings,
CefRefPtr<CefClient> client,
std::unique_ptr<CefBrowserPlatformDelegate> platform_delegate,
scoped_refptr<CefBrowserInfo> browser_info,
CefRefPtr<ChromeBrowserHostImpl> opener,
CefRefPtr<CefRequestContextImpl> request_context_impl) {
CEF_REQUIRE_UIT();
DCHECK(web_contents);
DCHECK(platform_delegate);
DCHECK(browser_info);
DCHECK(request_context_impl);
// If |opener| is non-nullptr it must be a popup window.
DCHECK(!opener.get() || browser_info->is_popup());
if (!client) {
if (auto app = CefAppManager::Get()->GetApplication()) {
if (auto bph = app->GetBrowserProcessHandler()) {
client = bph->GetDefaultClient();
}
}
}
if (!client) {
LOG(WARNING) << "Creating a chrome browser without a client";
}
// Check if chrome and CEF are using the same browser context.
// TODO(chrome-runtime): Verify if/when this might occur.
auto chrome_browser_context =
CefBrowserContext::FromBrowserContext(browser_->create_params().profile);
if (chrome_browser_context != request_context_impl->GetBrowserContext()) {
LOG(WARNING) << "Creating a chrome browser with mismatched context";
}
// Remains alive until the associated WebContents is destroyed.
CefRefPtr<ChromeBrowserHostImpl> browser_host =
new ChromeBrowserHostImpl(settings, client, std::move(platform_delegate),
browser_info, request_context_impl);
browser_host->Attach(web_contents, opener);
// The Chrome browser for a popup won't be created until AddNewContents().
if (!opener) {
browser_host->SetBrowser(browser_);
}
}
CefBrowserContentsDelegate* ChromeBrowserDelegate::GetDelegateForWebContents(
content::WebContents* web_contents) {
auto browser_host =
ChromeBrowserHostImpl::GetBrowserForContents(web_contents);
if (browser_host)
return browser_host->contents_delegate();
return nullptr;
}
namespace cef {
// static
std::unique_ptr<BrowserDelegate> BrowserDelegate::Create(
Browser* browser,
scoped_refptr<CreateParams> cef_params) {
CefBrowserCreateParams create_params;
// Parameters from ChromeBrowserHostImpl::Create, or nullptr if the Browser
// was created from somewhere else.
auto params = static_cast<ChromeBrowserHostImpl::DelegateCreateParams*>(
cef_params.get());
if (params) {
create_params = params->create_params_;
// Clear these values so they're not persisted to additional Browsers.
params->create_params_.window_info.reset();
#if defined(TOOLKIT_VIEWS)
params->create_params_.browser_view = nullptr;
#endif
}
return std::make_unique<ChromeBrowserDelegate>(browser, create_params);
}
} // namespace cef