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.
This commit is contained in:
Marshall Greenblatt
2022-04-08 16:48:56 -04:00
parent 5f4bccd672
commit 3000bc8748
43 changed files with 702 additions and 245 deletions

View File

@@ -45,6 +45,10 @@ class CefBrowserPlatformDelegateChrome
void set_chrome_browser(Browser* browser);
CefBrowserPlatformDelegateNative* native_delegate() const {
return native_delegate_.get();
}
protected:
std::unique_ptr<CefBrowserPlatformDelegateNative> native_delegate_;

View File

@@ -341,6 +341,7 @@ std::unique_ptr<BrowserDelegate> BrowserDelegate::Create(
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

View File

@@ -143,6 +143,9 @@ void ChromeBrowserHostImpl::OnSetFocus(cef_focus_source_t source) {
if (contents_delegate_->OnSetFocus(source))
return;
if (platform_delegate_)
platform_delegate_->SetFocus(true);
if (browser_) {
const int tab_index = GetCurrentTabIndex();
if (tab_index != TabStripModel::kNoTab) {
@@ -163,13 +166,13 @@ bool ChromeBrowserHostImpl::TryCloseBrowser() {
return true;
}
void ChromeBrowserHostImpl::SetFocus(bool focus) {
if (focus) {
OnSetFocus(FOCUS_SOURCE_SYSTEM);
}
}
CefWindowHandle ChromeBrowserHostImpl::GetWindowHandle() {
if (CEF_CURRENTLY_ON_UIT()) {
// Always return the most up-to-date window handle for a views-hosted
// browser since it may change if the view is re-parented.
if (platform_delegate_)
return platform_delegate_->GetHostWindowHandle();
}
NOTIMPLEMENTED();
return kNullWindowHandle;
}
@@ -282,10 +285,6 @@ void ChromeBrowserHostImpl::SendCaptureLostEvent() {
NOTIMPLEMENTED();
}
void ChromeBrowserHostImpl::NotifyMoveOrResizeStarted() {
NOTIMPLEMENTED();
}
int ChromeBrowserHostImpl::GetWindowlessFrameRate() {
return 0;
}
@@ -513,7 +512,20 @@ void ChromeBrowserHostImpl::Attach(content::WebContents* web_contents,
// Notify that the browser has been created. These must be delivered in the
// expected order.
// 1. Notify the browser's LifeSpanHandler. This must always be the first
if (opener && opener->platform_delegate_) {
// 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
// create the Widget for the new popup browser. Without that Widget
// 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);
}
// 2. Notify the browser's LifeSpanHandler. This must always be the first
// notification for the browser.
{
// The WebContents won't be added to the Browser's TabStripModel until later
@@ -522,17 +534,9 @@ void ChromeBrowserHostImpl::Attach(content::WebContents* web_contents,
OnAfterCreated();
}
// 2. Notify the platform delegate. With Views this will result in a call to
// 3. Notify the platform delegate. With Views this will result in a call to
// CefBrowserViewDelegate::OnBrowserCreated().
platform_delegate_->NotifyBrowserCreated();
if (opener && opener->platform_delegate_) {
// 3. Notify the opener browser's platform delegate. With Views this will
// result in a call to CefBrowserViewDelegate::OnPopupBrowserViewCreated().
opener->platform_delegate_->PopupBrowserCreated(
this,
/*is_devtools_popup=*/false);
}
}
void ChromeBrowserHostImpl::SetBrowser(Browser* browser) {

View File

@@ -62,7 +62,6 @@ class ChromeBrowserHostImpl : public CefBrowserHostBase {
// CefBrowserHost methods:
void CloseBrowser(bool force_close) override;
bool TryCloseBrowser() override;
void SetFocus(bool focus) override;
CefWindowHandle GetWindowHandle() override;
CefWindowHandle GetOpenerWindowHandle() override;
double GetZoomLevel() override;
@@ -96,7 +95,6 @@ class ChromeBrowserHostImpl : public CefBrowserHostBase {
void SendExternalBeginFrame() override;
void SendTouchEvent(const CefTouchEvent& event) override;
void SendCaptureLostEvent() override;
void NotifyMoveOrResizeStarted() override;
int GetWindowlessFrameRate() override;
void SetWindowlessFrameRate(int frame_rate) override;
void ImeSetComposition(const CefString& text,

View File

@@ -0,0 +1,38 @@
// Copyright 2022 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 "libcef/browser/chrome/views/browser_platform_delegate_chrome_child_window.h"
#include "include/views/cef_browser_view.h"
#include "libcef/browser/chrome/views/chrome_child_window.h"
CefBrowserPlatformDelegateChromeChildWindow::
CefBrowserPlatformDelegateChromeChildWindow(
std::unique_ptr<CefBrowserPlatformDelegateNative> native_delegate,
CefRefPtr<CefBrowserViewImpl> browser_view)
: CefBrowserPlatformDelegateChromeViews(std::move(native_delegate),
browser_view) {}
void CefBrowserPlatformDelegateChromeChildWindow::RenderViewCreated(
content::RenderViewHost* render_view_host) {
CefBrowserPlatformDelegateChromeViews::RenderViewCreated(render_view_host);
// Calls SetHasExternalParent(true). This setting is required for proper
// focus handling on Windows and Linux.
native_delegate_->RenderViewCreated(render_view_host);
}
void CefBrowserPlatformDelegateChromeChildWindow::CloseHostWindow() {
native_delegate_->CloseHostWindow();
}
void CefBrowserPlatformDelegateChromeChildWindow::SetFocus(bool focus) {
native_delegate_->SetFocus(focus);
}
#if BUILDFLAG(IS_WIN) || (BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_MAC))
void CefBrowserPlatformDelegateChromeChildWindow::NotifyMoveOrResizeStarted() {
native_delegate_->NotifyMoveOrResizeStarted();
}
#endif

View File

@@ -0,0 +1,28 @@
// Copyright 2022 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_LIBCEF_BROWSER_CHROME_VIEWS_BROWSER_PLATFORM_DELEGATE_CHROME_CHILD_WINDOW_H_
#define CEF_LIBCEF_BROWSER_CHROME_VIEWS_BROWSER_PLATFORM_DELEGATE_CHROME_CHILD_WINDOW_H_
#include "libcef/browser/chrome/views/browser_platform_delegate_chrome_views.h"
// Implementation of Chrome-based browser functionality.
class CefBrowserPlatformDelegateChromeChildWindow
: public CefBrowserPlatformDelegateChromeViews {
public:
CefBrowserPlatformDelegateChromeChildWindow(
std::unique_ptr<CefBrowserPlatformDelegateNative> native_delegate,
CefRefPtr<CefBrowserViewImpl> browser_view);
// CefBrowserPlatformDelegate overrides.
void RenderViewCreated(content::RenderViewHost* render_view_host) override;
void CloseHostWindow() override;
void SetFocus(bool focus) override;
#if BUILDFLAG(IS_WIN) || (BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_MAC))
void NotifyMoveOrResizeStarted() override;
#endif
};
#endif // CEF_LIBCEF_BROWSER_CHROME_VIEWS_BROWSER_PLATFORM_DELEGATE_CHROME_CHILD_WINDOW_H_

View File

@@ -95,6 +95,11 @@ void CefBrowserPlatformDelegateChromeViews::CloseHostWindow() {
widget->Close();
}
CefWindowHandle CefBrowserPlatformDelegateChromeViews::GetHostWindowHandle()
const {
return view_util::GetWindowHandle(GetWindowWidget());
}
views::Widget* CefBrowserPlatformDelegateChromeViews::GetWindowWidget() const {
if (browser_view_->root_view())
return browser_view_->root_view()->GetWidget();

View File

@@ -24,6 +24,7 @@ class CefBrowserPlatformDelegateChromeViews
void NotifyBrowserDestroyed() override;
void BrowserDestroyed(CefBrowserHostBase* browser) override;
void CloseHostWindow() override;
CefWindowHandle GetHostWindowHandle() const override;
views::Widget* GetWindowWidget() const override;
CefRefPtr<CefBrowserView> GetBrowserView() const override;
void PopupWebContentsCreated(
@@ -36,6 +37,8 @@ class CefBrowserPlatformDelegateChromeViews
bool is_devtools) override;
bool IsViewsHosted() const override;
CefRefPtr<CefBrowserViewImpl> browser_view() const { return browser_view_; }
private:
void SetBrowserView(CefRefPtr<CefBrowserViewImpl> browser_view);

View File

@@ -0,0 +1,199 @@
// Copyright 2022 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 "libcef/browser/chrome/views/chrome_child_window.h"
#include "libcef/browser/chrome/views/browser_platform_delegate_chrome_views.h"
#include "libcef/browser/views/browser_view_impl.h"
#include "libcef/browser/views/window_impl.h"
#if BUILDFLAG(IS_WIN)
#include "libcef/browser/native/browser_platform_delegate_native_win.h"
#include "ui/views/win/hwnd_util.h"
#endif
namespace {
class ChildWindowDelegate : public CefWindowDelegate {
public:
ChildWindowDelegate(const ChildWindowDelegate&) = delete;
ChildWindowDelegate& operator=(const ChildWindowDelegate&) = delete;
static void Create(CefRefPtr<CefBrowserView> browser_view,
const CefWindowInfo& window_info,
gfx::AcceleratedWidget parent_handle) {
DCHECK(parent_handle != gfx::kNullAcceleratedWidget);
// Create the Window. It will show itself after creation.
CefWindowImpl::Create(new ChildWindowDelegate(browser_view, window_info),
parent_handle);
}
void OnWindowCreated(CefRefPtr<CefWindow> window) override {
DCHECK(!window_);
window_ = window;
// Add the browser view. It will now have an associated Widget.
window_->AddChildView(browser_view_);
ShowWindow();
}
void OnWindowDestroyed(CefRefPtr<CefWindow> window) override {
browser_view_ = nullptr;
window_ = nullptr;
}
CefRect GetInitialBounds(CefRefPtr<CefWindow> window) override {
CefRect initial_bounds(window_info_.bounds);
if (initial_bounds.IsEmpty())
return CefRect(0, 0, 800, 600);
return initial_bounds;
}
void ShowWindow() {
#if BUILDFLAG(IS_WIN)
auto widget = static_cast<CefWindowImpl*>(window_.get())->widget();
DCHECK(widget);
const HWND widget_hwnd = HWNDForWidget(widget);
DCHECK(widget_hwnd);
// The native delegate needs state to perform some actions.
auto browser =
static_cast<CefBrowserHostBase*>(browser_view_->GetBrowser().get());
auto platform_delegate = browser->platform_delegate();
DCHECK(platform_delegate->IsViewsHosted());
auto chrome_delegate =
static_cast<CefBrowserPlatformDelegateChromeViews*>(platform_delegate);
auto native_delegate = static_cast<CefBrowserPlatformDelegateNativeWin*>(
chrome_delegate->native_delegate());
native_delegate->set_widget(widget, widget_hwnd);
if (window_info_.ex_style & WS_EX_NOACTIVATE) {
const DWORD widget_ex_styles = GetWindowLongPtr(widget_hwnd, GWL_EXSTYLE);
// Add the WS_EX_NOACTIVATE style on the DesktopWindowTreeHostWin HWND
// so that HWNDMessageHandler::Show() called via Widget::Show() does not
// activate the window.
SetWindowLongPtr(widget_hwnd, GWL_EXSTYLE,
widget_ex_styles | WS_EX_NOACTIVATE);
window_->Show();
// Remove the WS_EX_NOACTIVATE style so that future mouse clicks inside
// the browser correctly activate and focus the window.
SetWindowLongPtr(widget_hwnd, GWL_EXSTYLE, widget_ex_styles);
return;
}
#endif // BUILDFLAG(IS_WIN)
window_->Show();
// Give keyboard focus to the browser view.
browser_view_->RequestFocus();
}
private:
ChildWindowDelegate(CefRefPtr<CefBrowserView> browser_view,
const CefWindowInfo& window_info)
: browser_view_(browser_view), window_info_(window_info) {}
CefRefPtr<CefBrowserView> browser_view_;
const CefWindowInfo window_info_;
CefRefPtr<CefWindow> window_;
IMPLEMENT_REFCOUNTING(ChildWindowDelegate);
};
class ChildBrowserViewDelegate : public CefBrowserViewDelegate {
public:
ChildBrowserViewDelegate() = default;
ChildBrowserViewDelegate(const ChildBrowserViewDelegate&) = delete;
ChildBrowserViewDelegate& operator=(const ChildBrowserViewDelegate&) = delete;
CefRefPtr<CefBrowserViewDelegate> GetDelegateForPopupBrowserView(
CefRefPtr<CefBrowserView> browser_view,
const CefBrowserSettings& settings,
CefRefPtr<CefClient> client,
bool is_devtools) override {
return new ChildBrowserViewDelegate();
}
bool OnPopupBrowserViewCreated(CefRefPtr<CefBrowserView> browser_view,
CefRefPtr<CefBrowserView> popup_browser_view,
bool is_devtools) override {
DCHECK(!is_devtools);
auto new_browser = static_cast<CefBrowserHostBase*>(
popup_browser_view->GetBrowser().get());
auto new_platform_delegate = new_browser->platform_delegate();
DCHECK(new_platform_delegate->IsViewsHosted());
auto new_platform_delegate_impl =
static_cast<CefBrowserPlatformDelegateChromeViews*>(
new_platform_delegate);
const auto& window_info =
new_platform_delegate_impl->native_delegate()->window_info();
const auto parent_handle =
chrome_child_window::GetParentHandle(window_info);
if (parent_handle != gfx::kNullAcceleratedWidget) {
ChildWindowDelegate::Create(popup_browser_view, window_info,
parent_handle);
return true;
}
// Use the default implementation that creates a new Views-hosted top-level
// window.
return false;
}
private:
IMPLEMENT_REFCOUNTING(ChildBrowserViewDelegate);
};
} // namespace
namespace chrome_child_window {
bool HasParentHandle(const CefWindowInfo& window_info) {
return GetParentHandle(window_info) != gfx::kNullAcceleratedWidget;
}
gfx::AcceleratedWidget GetParentHandle(const CefWindowInfo& window_info) {
#if !BUILDFLAG(IS_MAC)
return window_info.parent_window;
#else
return gfx::kNullAcceleratedWidget;
#endif
}
CefRefPtr<CefBrowserHostBase> MaybeCreateChildBrowser(
const CefBrowserCreateParams& create_params) {
// If the BrowserView already exists it means that we're dealing with a popup
// and we'll instead create the Window in OnPopupBrowserViewCreated.
if (create_params.browser_view)
return nullptr;
if (!create_params.window_info)
return nullptr;
const auto parent_handle = GetParentHandle(*create_params.window_info);
if (parent_handle == gfx::kNullAcceleratedWidget)
return nullptr;
// Create the BrowserView.
auto browser_view = CefBrowserViewImpl::Create(
*create_params.window_info, create_params.client, create_params.url,
create_params.settings, create_params.extra_info,
create_params.request_context, new ChildBrowserViewDelegate());
ChildWindowDelegate::Create(browser_view, *create_params.window_info,
parent_handle);
return static_cast<CefBrowserHostBase*>(browser_view->GetBrowser().get());
}
} // namespace chrome_child_window

View File

@@ -0,0 +1,24 @@
// Copyright 2022 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_LIBCEF_BROWSER_CHROME_VIEWS_CHROME_CHILD_WINDOW_H_
#define CEF_LIBCEF_BROWSER_CHROME_VIEWS_CHROME_CHILD_WINDOW_H_
#pragma once
#include "libcef/browser/browser_host_base.h"
#include "ui/gfx/native_widget_types.h"
namespace chrome_child_window {
bool HasParentHandle(const CefWindowInfo& window_info);
gfx::AcceleratedWidget GetParentHandle(const CefWindowInfo& window_info);
// Called from CefBrowserHostBase::Create.
CefRefPtr<CefBrowserHostBase> MaybeCreateChildBrowser(
const CefBrowserCreateParams& create_params);
} // namespace chrome_child_window
#endif // CEF_LIBCEF_BROWSER_CHROME_VIEWS_CHROME_CHILD_WINDOW_H_