cef/libcef/browser/native/browser_platform_delegate_native_linux.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

317 lines
10 KiB
C++

// Copyright 2015 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/native/browser_platform_delegate_native_linux.h"
#include "libcef/browser/browser_host_base.h"
#include "libcef/browser/context.h"
#include "libcef/browser/native/menu_runner_linux.h"
#include "libcef/browser/native/window_delegate_view.h"
#include "libcef/browser/thread_util.h"
#include "base/no_destructor.h"
#include "content/browser/renderer_host/render_widget_host_impl.h"
#include "content/public/browser/native_web_keyboard_event.h"
#include "content/public/browser/render_view_host.h"
#include "third_party/blink/public/mojom/renderer_preferences.mojom.h"
#include "ui/events/keycodes/dom/dom_key.h"
#include "ui/events/keycodes/dom/keycode_converter.h"
#include "ui/events/keycodes/keyboard_code_conversion_x.h"
#include "ui/events/keycodes/keyboard_code_conversion_xkb.h"
#include "ui/events/keycodes/keysym_to_unicode.h"
#include "ui/gfx/font_render_params.h"
#include "ui/views/widget/widget.h"
#if BUILDFLAG(OZONE_PLATFORM_X11)
#include "libcef/browser/native/window_x11.h"
#include "ui/views/widget/desktop_aura/desktop_window_tree_host_linux.h"
#endif
CefBrowserPlatformDelegateNativeLinux::CefBrowserPlatformDelegateNativeLinux(
const CefWindowInfo& window_info,
SkColor background_color)
: CefBrowserPlatformDelegateNativeAura(window_info, background_color),
host_window_created_(false),
window_widget_(nullptr) {}
void CefBrowserPlatformDelegateNativeLinux::BrowserDestroyed(
CefBrowserHostBase* browser) {
CefBrowserPlatformDelegateNativeAura::BrowserDestroyed(browser);
if (host_window_created_) {
// Release the reference added in CreateHostWindow().
browser->Release();
}
}
bool CefBrowserPlatformDelegateNativeLinux::CreateHostWindow() {
DCHECK(!window_widget_);
if (window_info_.bounds.width == 0)
window_info_.bounds.width = 800;
if (window_info_.bounds.height == 0)
window_info_.bounds.height = 600;
gfx::Rect rect(window_info_.bounds.x, window_info_.bounds.y,
window_info_.bounds.width, window_info_.bounds.height);
#if BUILDFLAG(OZONE_PLATFORM_X11)
DCHECK(!window_x11_);
x11::Window parent_window = x11::Window::None;
if (window_info_.parent_window != kNullWindowHandle) {
parent_window = static_cast<x11::Window>(window_info_.parent_window);
}
// Create a new window object. It will delete itself when the associated X11
// window is destroyed.
window_x11_ =
new CefWindowX11(browser_, parent_window, rect,
CefString(&window_info_.window_name).ToString());
DCHECK_NE(window_x11_->xwindow(), x11::Window::None);
window_info_.window =
static_cast<cef_window_handle_t>(window_x11_->xwindow());
host_window_created_ = true;
// Add a reference that will be released in BrowserDestroyed().
browser_->AddRef();
CefWindowDelegateView* delegate_view = new CefWindowDelegateView(
GetBackgroundColor(), window_x11_->TopLevelAlwaysOnTop(),
GetBoundsChangedCallback());
delegate_view->Init(static_cast<gfx::AcceleratedWidget>(window_info_.window),
web_contents_, gfx::Rect(gfx::Point(), rect.size()));
window_widget_ = delegate_view->GetWidget();
window_widget_->Show();
window_x11_->Show();
#endif // BUILDFLAG(OZONE_PLATFORM_X11)
// As an additional requirement on Linux, we must set the colors for the
// render widgets in webkit.
auto prefs = web_contents_->GetMutableRendererPrefs();
prefs->focus_ring_color = SkColorSetARGB(255, 229, 151, 0);
prefs->active_selection_bg_color = SkColorSetRGB(30, 144, 255);
prefs->active_selection_fg_color = SK_ColorWHITE;
prefs->inactive_selection_bg_color = SkColorSetRGB(200, 200, 200);
prefs->inactive_selection_fg_color = SkColorSetRGB(50, 50, 50);
// Set font-related attributes.
static const gfx::FontRenderParams params(
gfx::GetFontRenderParams(gfx::FontRenderParamsQuery(), nullptr));
prefs->should_antialias_text = params.antialiasing;
prefs->use_subpixel_positioning = params.subpixel_positioning;
prefs->hinting = params.hinting;
prefs->use_autohinter = params.autohinter;
prefs->use_bitmaps = params.use_bitmaps;
prefs->subpixel_rendering = params.subpixel_rendering;
web_contents_->SyncRendererPrefs();
return true;
}
void CefBrowserPlatformDelegateNativeLinux::CloseHostWindow() {
#if BUILDFLAG(OZONE_PLATFORM_X11)
if (window_x11_)
window_x11_->Close();
#endif
}
CefWindowHandle CefBrowserPlatformDelegateNativeLinux::GetHostWindowHandle()
const {
if (windowless_handler_)
return windowless_handler_->GetParentWindowHandle();
return window_info_.window;
}
views::Widget* CefBrowserPlatformDelegateNativeLinux::GetWindowWidget() const {
return window_widget_;
}
void CefBrowserPlatformDelegateNativeLinux::SetFocus(bool setFocus) {
if (!setFocus)
return;
if (web_contents_) {
// Give logical focus to the RenderWidgetHostViewAura in the views
// hierarchy. This does not change the native keyboard focus.
web_contents_->Focus();
}
#if BUILDFLAG(OZONE_PLATFORM_X11)
if (window_x11_) {
// Give native focus to the DesktopNativeWidgetAura for the root window.
// Needs to be done via the ::Window so that keyboard focus is assigned
// correctly.
window_x11_->Focus();
}
#endif // BUILDFLAG(OZONE_PLATFORM_X11)
}
void CefBrowserPlatformDelegateNativeLinux::NotifyMoveOrResizeStarted() {
// Call the parent method to dismiss any existing popups.
CefBrowserPlatformDelegateNativeAura::NotifyMoveOrResizeStarted();
if (!web_contents_)
return;
#if BUILDFLAG(OZONE_PLATFORM_X11)
if (!window_x11_)
return;
views::DesktopWindowTreeHostLinux* tree_host = window_x11_->GetHost();
if (!tree_host)
return;
// Explicitly set the screen bounds so that WindowTreeHost::*Screen()
// methods return the correct results.
const gfx::Rect& bounds = window_x11_->GetBoundsInScreen();
tree_host->set_screen_bounds(bounds);
// Send updated screen rectangle information to the renderer process so that
// popups are displayed in the correct location.
content::RenderWidgetHostImpl::From(
web_contents_->GetRenderViewHost()->GetWidget())
->SendScreenRects();
#endif // BUILDFLAG(OZONE_PLATFORM_X11)
}
void CefBrowserPlatformDelegateNativeLinux::SizeTo(int width, int height) {
#if BUILDFLAG(OZONE_PLATFORM_X11)
if (window_x11_) {
window_x11_->SetBounds(
gfx::Rect(window_x11_->bounds().origin(), gfx::Size(width, height)));
}
#endif // BUILDFLAG(OZONE_PLATFORM_X11)
}
gfx::Point CefBrowserPlatformDelegateNativeLinux::GetScreenPoint(
const gfx::Point& view) const {
if (windowless_handler_)
return windowless_handler_->GetParentScreenPoint(view);
#if BUILDFLAG(OZONE_PLATFORM_X11)
if (!window_x11_)
return view;
// We can't use aura::Window::GetBoundsInScreen on Linux because it will
// return bounds from DesktopWindowTreeHostLinux which in our case is relative
// to the parent window instead of the root window (screen).
const gfx::Rect& bounds_in_screen = window_x11_->GetBoundsInScreen();
return gfx::Point(bounds_in_screen.x() + view.x(),
bounds_in_screen.y() + view.y());
#else // !BUILDFLAG(OZONE_PLATFORM_X11)
return gfx::Point();
#endif
}
void CefBrowserPlatformDelegateNativeLinux::ViewText(const std::string& text) {
char buff[] = "/tmp/CEFSourceXXXXXX";
int fd = mkstemp(buff);
if (fd == -1)
return;
FILE* srcOutput = fdopen(fd, "w+");
if (!srcOutput)
return;
if (fputs(text.c_str(), srcOutput) < 0) {
fclose(srcOutput);
return;
}
fclose(srcOutput);
std::string newName(buff);
newName.append(".txt");
if (rename(buff, newName.c_str()) != 0)
return;
std::string openCommand("xdg-open ");
openCommand += newName;
[[maybe_unused]] int result = system(openCommand.c_str());
}
bool CefBrowserPlatformDelegateNativeLinux::HandleKeyboardEvent(
const content::NativeWebKeyboardEvent& event) {
// TODO(cef): Is something required here to handle shortcut keys?
return false;
}
// static
void CefBrowserPlatformDelegate::HandleExternalProtocol(const GURL& url) {}
CefEventHandle CefBrowserPlatformDelegateNativeLinux::GetEventHandle(
const content::NativeWebKeyboardEvent& event) const {
// TODO(cef): We need to return an XEvent* from this method, but
// |event.os_event->native_event()| now returns a ui::Event* instead.
// See https://crbug.com/965991.
return nullptr;
}
std::unique_ptr<CefMenuRunner>
CefBrowserPlatformDelegateNativeLinux::CreateMenuRunner() {
return base::WrapUnique(new CefMenuRunnerLinux);
}
gfx::Point CefBrowserPlatformDelegateNativeLinux::GetDialogPosition(
const gfx::Size& size) {
const gfx::Size& max_size = GetMaximumDialogSize();
return gfx::Point((max_size.width() - size.width()) / 2,
(max_size.height() - size.height()) / 2);
}
gfx::Size CefBrowserPlatformDelegateNativeLinux::GetMaximumDialogSize() {
return GetWindowWidget()->GetWindowBoundsInScreen().size();
}
ui::KeyEvent CefBrowserPlatformDelegateNativeLinux::TranslateUiKeyEvent(
const CefKeyEvent& key_event) const {
int flags = TranslateUiEventModifiers(key_event.modifiers);
ui::KeyboardCode key_code =
static_cast<ui::KeyboardCode>(key_event.windows_key_code);
ui::DomCode dom_code =
ui::KeycodeConverter::NativeKeycodeToDomCode(key_event.native_key_code);
int keysym = ui::XKeysymForWindowsKeyCode(
key_code, !!(key_event.modifiers & EVENTFLAG_SHIFT_DOWN));
char16_t character = ui::GetUnicodeCharacterFromXKeySym(keysym);
base::TimeTicks time_stamp = GetEventTimeStamp();
if (key_event.type == KEYEVENT_CHAR) {
return ui::KeyEvent(character, key_code, dom_code, flags, time_stamp);
}
ui::EventType type = ui::ET_UNKNOWN;
switch (key_event.type) {
case KEYEVENT_RAWKEYDOWN:
case KEYEVENT_KEYDOWN:
type = ui::ET_KEY_PRESSED;
break;
case KEYEVENT_KEYUP:
type = ui::ET_KEY_RELEASED;
break;
default:
NOTREACHED();
}
ui::DomKey dom_key = ui::XKeySymToDomKey(keysym, character);
return ui::KeyEvent(type, key_code, dom_code, flags, dom_key, time_stamp);
}
content::NativeWebKeyboardEvent
CefBrowserPlatformDelegateNativeLinux::TranslateWebKeyEvent(
const CefKeyEvent& key_event) const {
ui::KeyEvent ui_event = TranslateUiKeyEvent(key_event);
if (key_event.type == KEYEVENT_CHAR) {
return content::NativeWebKeyboardEvent(ui_event, key_event.character);
}
return content::NativeWebKeyboardEvent(ui_event);
}