Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,10 @@ group("cef") {
if (!is_linux || ozone_platform_x11) {
deps += [ ":cefclient" ]
}

if (is_linux && ozone_platform_wayland) {
deps += [ ":cefembed_wayland" ]
}
}


Expand Down Expand Up @@ -415,6 +419,10 @@ source_set("libcef_static_unittested") {
"libcef/browser/devtools/devtools_util.h",
"libcef/browser/geometry_util.h",
"libcef/browser/geometry_util.cc",

# CefStructBase copies string members through these, so anything testing a
# struct's copy semantics needs them linked in.
"libcef/common/string_types_impl.cc",
]

deps = [
Expand Down Expand Up @@ -442,6 +450,10 @@ test("libcef_static_unittests") {
"libcef/browser/geometry_util_unittest.cc",
]

if (is_linux) {
sources += [ "libcef/browser/native/window_info_linux_unittest.cc" ]
}

deps = [
":libcef_static_unittested",
"//base/test:run_all_unittests",
Expand Down Expand Up @@ -1067,6 +1079,9 @@ source_set("libcef_static") {
sources += includes_linux + [
"libcef/browser/native/browser_platform_delegate_native_linux.cc",
"libcef/browser/native/browser_platform_delegate_native_linux.h",
"libcef/browser/native/ozone_util_linux.cc",
"libcef/browser/native/ozone_util_linux.h",
"libcef/browser/native/wayland_util_linux.cc",
"libcef/browser/osr/browser_platform_delegate_osr_linux.cc",
"libcef/browser/osr/browser_platform_delegate_osr_linux.h",
"libcef/browser/printing/print_dialog_linux.cc",
Expand Down Expand Up @@ -2712,6 +2727,40 @@ if (is_mac) {
# cefsimple targets.
#

#
# cefembed_wayland target.
#

# Minimal Wayland client that embeds a browser in its own window. cefclient's
# Linux windowing path is GTK over X11 and hands CEF an XID, so this is the
# only sample that exercises the three-argument CefWindowInfo::SetAsChild().
if (is_linux && ozone_platform_wayland) {
executable("cefembed_wayland") {
# Necessary because the libcef target is testonly.
testonly = true

sources = [
"tests/cefembed_wayland/main.cc",
]

deps = [
":libcef",
":libcef_dll_wrapper",
"//third_party/wayland:wayland_client",
"//third_party/wayland-protocols:cursor_shape_protocol",

# Not used directly. cursor-shape-v1 references zwp_tablet_tool_v2 in
# its own interface table, so dropping this fails the link with
# "undefined symbol: zwp_tablet_tool_v2_interface".
"//third_party/wayland-protocols:tablet_protocol",
"//third_party/wayland-protocols:xdg_shell_protocol",
]

# Disable clang modules for this target (see issue #3611).
use_libcxx_modules = false
}
}

executable("cefsimple") {
# Necessary because the libcef target is testonly.
testonly = true
Expand Down
240 changes: 240 additions & 0 deletions docs/wayland_embedding.md

Large diffs are not rendered by default.

27 changes: 27 additions & 0 deletions include/cef_browser.h
Original file line number Diff line number Diff line change
Expand Up @@ -1089,6 +1089,33 @@ class CefBrowserHost : public virtual CefBaseRefCounted {
/*--cef(added=experimental)--*/
virtual void SetAxViewportCollapse(bool enabled) = 0;
#endif

#if CEF_API_ADDED(CEF_EXPERIMENTAL)
///
/// Set the position and size of a windowed browser, relative to the parent
/// window that was passed to CefWindowInfo. Only used on Linux.
///
/// On X11 the browser observes the parent window and resizes itself, so this
/// is not needed. On Wayland there is no such signal: a wl_subsurface does
/// not receive configure events, which are delivered to the client's
/// xdg_toplevel instead. The client application must therefore forward its
/// own layout changes here.
///
/// |bounds| is in density-independent pixels, not device pixels. The values
/// reach wl_subsurface.set_position, which is surface-local to the client's
/// surface and therefore in the same logical units the compositor uses for
/// that surface. A client that keeps its layout in device pixels must divide
/// by its scale factor before calling this; the two are identical at scale 1,
/// so getting it wrong is invisible until the window meets a HiDPI or
/// fractional-scale output, where the browser lands offset by the scale.
///
/// Note that wl_subsurface.set_position is part of the parent surface's
/// double-buffered state, so the move only becomes visible when the client
/// next commits its own surface.
///
/*--cef(added=experimental)--*/
virtual void SetWindowBounds(const CefRect& bounds) = 0;
#endif
};

#endif // CEF_INCLUDE_CEF_BROWSER_H_
49 changes: 49 additions & 0 deletions include/internal/cef_linux.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@
#define CefCursorHandle cef_cursor_handle_t
#define CefEventHandle cef_event_handle_t
#define CefWindowHandle cef_window_handle_t
#if CEF_API_ADDED(CEF_EXPERIMENTAL)
#define CefWaylandDisplayHandle cef_wayland_display_handle_t
#define CefXdgSurfaceHandle cef_xdg_surface_handle_t
#endif

///
/// Class representing CefExecuteProcess arguments.
Expand Down Expand Up @@ -71,6 +75,21 @@ struct CefWindowInfoTraits {
target->external_begin_frame_enabled = src->external_begin_frame_enabled;
target->window = src->window;
target->runtime_style = src->runtime_style;
#if CEF_API_ADDED(CEF_EXPERIMENTAL)
// |src| comes from the client and may have been compiled against an older,
// smaller struct. Reading the member without this check is a read past the
// end of that allocation.
//
// The else matters as much as the check. CefStructBase::Set() calls
// Clear() first to "clear newer members that won't be set", but clear()
// only frees string members; a stale pointer here would otherwise survive
// being assigned over from an older client's struct.
if (CEF_MEMBER_EXISTS(src, parent_xdg_surface)) {
target->parent_xdg_surface = src->parent_xdg_surface;
} else {
target->parent_xdg_surface = nullptr;
}
#endif
}
};

Expand All @@ -91,6 +110,36 @@ class CefWindowInfo : public CefStructBase<CefWindowInfoTraits> {
this->bounds = bounds;
}

#if CEF_API_ADDED(CEF_EXPERIMENTAL)
///
/// Create the browser as a child window, additionally naming the client's
/// xdg_surface when the active Ozone platform is Wayland.
///
/// |parent| is the same opaque native parent handle the two-argument
/// overload takes: an X11 Window under Ozone/X11, a struct wl_surface* cast
/// to CefWindowHandle under Ozone/Wayland. A Wayland surface must belong to
/// the connection previously passed to cef_set_wayland_display(), which must
/// be called before CefInitialize, because wl_surface objects cannot be
/// shared across connections. See
/// https://github.com/chromiumembedded/cef/issues/2804.
///
/// |parent_xdg| is the xdg_surface that |parent| belongs to. It is what the
/// browser's menus, <select> dropdowns and tooltips are anchored on, since a
/// wl_subsurface cannot be an xdg_popup parent. Passing NULL still leaves the
/// browser able to render and take input, with popups falling back to a
/// degraded wl_subsurface form; see cef_window_info_t::parent_xdg_surface.
/// It is ignored under X11, where the two-argument overload is all that is
/// needed.
///
void SetAsChild(CefWindowHandle parent,
CefXdgSurfaceHandle parent_xdg,
const CefRect& bounds) {
parent_window = parent;
parent_xdg_surface = parent_xdg;
this->bounds = bounds;
}
#endif

///
/// Create the browser using windowless (off-screen) rendering. No window
/// will be created for the browser and all rendering will occur via the
Expand Down
98 changes: 98 additions & 0 deletions include/internal/cef_types_linux.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@

#if defined(OS_LINUX)

#include "include/cef_api_hash.h"
#include "include/internal/cef_export.h"
#include "include/internal/cef_string.h"
#include "include/internal/cef_types_color.h"
Expand All @@ -48,6 +49,11 @@
#define kNullEventHandle NULL
#define kNullWindowHandle 0

#if CEF_API_ADDED(CEF_EXPERIMENTAL)
#define kNullWaylandDisplayHandle NULL
#define kNullXdgSurfaceHandle NULL
#endif

#ifdef __cplusplus
extern "C" {
#endif
Expand All @@ -66,6 +72,19 @@ typedef void* cef_event_handle_t;

typedef unsigned long cef_window_handle_t;

#if CEF_API_ADDED(CEF_EXPERIMENTAL)

///
/// Handle type for a Wayland connection (struct wl_display*).
///
typedef void* cef_wayland_display_handle_t;

///
/// Handle type for an xdg_surface (struct xdg_surface*).
///
typedef void* cef_xdg_surface_handle_t;
#endif

///
/// Return the singleton X11 display shared with Chromium. The display is not
/// thread-safe and must only be accessed on the browser process UI thread.
Expand All @@ -74,6 +93,39 @@ typedef unsigned long cef_window_handle_t;
CEF_EXPORT XDisplay* cef_get_xdisplay(void);
#endif

#if CEF_API_ADDED(CEF_EXPERIMENTAL)
///
/// Give CEF the Wayland connection (struct wl_display*) owned by the client
/// application, so that browser surfaces can be created as subsurfaces of the
/// client's surfaces. A wl_surface is a protocol object scoped to one
/// connection and wl_subcompositor_get_subsurface() requires both surfaces to
/// belong to the same one, so embedding is only possible if CEF joins the
/// client's connection instead of opening its own.
///
/// Must be called before CefInitialize. Chromium creates its Wayland connection
/// while initializing Ozone, which happens during CefInitialize and long before
/// any browser exists, so this is necessarily a process-global, one-time
/// decision rather than a per-browser one.
///
/// The client retains ownership and must keep the connection alive until after
/// CefShutdown. CEF dispatches its own protocol traffic on a dedicated
/// wl_event_queue and never calls wl_display_disconnect() on an adopted
/// connection, so the client may keep running its own event loop on the
/// default queue.
///
/// Has no effect if the active Ozone platform is not Wayland.
///
CEF_EXPORT void cef_set_wayland_display(cef_wayland_display_handle_t display);

///
/// Return the Wayland connection used by Chromium, or NULL if the active Ozone
/// platform is not Wayland. If the client provided a connection via
/// cef_set_wayland_display() then that same connection is returned. Must only
/// be accessed on the browser process UI thread.
///
CEF_EXPORT cef_wayland_display_handle_t cef_get_wayland_display(void);
#endif

///
/// Structure representing CefExecuteProcess arguments.
///
Expand Down Expand Up @@ -109,6 +161,19 @@ typedef struct _cef_window_info_t {
///
/// Pointer for the parent window.
///
/// When the active Ozone platform is Wayland this is a struct wl_surface*
/// belonging to the client application, cast to cef_window_handle_t; the
/// browser surface becomes a wl_subsurface of it. Under X11 it is an X11
/// Window, as before. The field has always been an opaque native parent
/// handle whose meaning depends on the platform -- it is an NSView* on
/// macOS and an HWND on Windows -- and Wayland is no different; the
/// unsigned long spelling here is an artifact of X11.
///
/// A Wayland surface must belong to the connection previously passed to
/// cef_set_wayland_display(). Note that CEF cannot tell a mistaken X11
/// Window from a valid pointer, so passing the wrong kind of handle for the
/// active platform is undefined rather than diagnosed.
///
cef_window_handle_t parent_window;

///
Expand Down Expand Up @@ -141,6 +206,15 @@ typedef struct _cef_window_info_t {
///
/// Pointer for the new browser window. Only used with windowed rendering.
///
/// Under Ozone/X11 this is the X11 Window of the browser, usable from any
/// client on the connection. Under Ozone/Wayland it is not a protocol object
/// at all: it is the gfx::AcceleratedWidget Ozone uses internally, an id
/// meaningful only inside this process. A wl_surface cannot be handed back
/// this way because it is scoped to the connection that created it, so an
/// embedder that needs to address the browser's surface should keep the
/// parent surface it passed in and position the browser through
/// CefBrowserHost::SetWindowBounds() instead.
///
cef_window_handle_t window;

///
Expand All @@ -149,6 +223,30 @@ typedef struct _cef_window_info_t {
/// documentation for details.
///
cef_runtime_style_t runtime_style;

#if CEF_API_ADDED(CEF_EXPERIMENTAL)
///
/// The client application's xdg_surface (struct xdg_surface*), the one
/// |parent_window| belongs to when it names a wl_surface.
///
/// Required for the browser to open menus, <select> dropdowns, tooltips and
/// autocomplete. A wl_subsurface has no xdg_surface role, and
/// xdg_surface.get_popup demands an xdg_surface, so popups from an embedded
/// browser have to be anchored on the client's instead. Their positions are
/// then computed in the client's surface coordinates.
///
/// May be NULL for hosts whose toolkit does not expose the xdg_surface. The
/// browser then renders and takes input as usual, and popups fall back to a
/// wl_subsurface of the browser. That fallback is degraded and deliberately
/// so: a subsurface cannot extend past the host window, so a dropdown near an
/// edge is clipped rather than repositioned, and it holds no grab, so
/// clicking outside does not dismiss it. Provide the xdg_surface wherever the
/// toolkit allows it.
///
/// Ignored unless the active Ozone platform is Wayland.
///
cef_xdg_surface_handle_t parent_xdg_surface;
#endif
} cef_window_info_t;

///
Expand Down
19 changes: 19 additions & 0 deletions libcef/browser/browser_host_base.cc
Original file line number Diff line number Diff line change
Expand Up @@ -895,6 +895,25 @@ bool CefBrowserHostBase::IsAudioMuted() {
return false;
}

#if CEF_API_ADDED(CEF_EXPERIMENTAL)
void CefBrowserHostBase::SetWindowBounds(const CefRect& bounds) {
if (!CEF_CURRENTLY_ON_UIT()) {
CEF_POST_TASK(CEF_UIT, base::BindOnce(&CefBrowserHostBase::SetWindowBounds,
this, bounds));
return;
}

#if BUILDFLAG(IS_WIN) || (BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_MAC))
// SetHostBounds() only exists on the platforms where a windowed browser can
// be repositioned inside a parent window; see CefBrowserPlatformDelegate.
if (platform_delegate_) {
platform_delegate_->SetHostBounds(
gfx::Rect(bounds.x, bounds.y, bounds.width, bounds.height));
}
#endif
}
#endif

void CefBrowserHostBase::NotifyMoveOrResizeStarted() {
#if BUILDFLAG(IS_WIN) || (BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_MAC))
if (!CEF_CURRENTLY_ON_UIT()) {
Expand Down
3 changes: 3 additions & 0 deletions libcef/browser/browser_host_base.h
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,9 @@ class CefBrowserHostBase : public CefBrowserHost,
void SetAudioMuted(bool mute) override;
bool IsAudioMuted() override;
void NotifyMoveOrResizeStarted() override;
#if CEF_API_ADDED(CEF_EXPERIMENTAL)
void SetWindowBounds(const CefRect& bounds) override;
#endif
void NotifyScreenInfoChanged() override;
bool IsFullscreen() override;
void ExitFullscreen(bool will_cause_resize) override;
Expand Down
2 changes: 2 additions & 0 deletions libcef/browser/browser_platform_delegate.cc
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,8 @@ void CefBrowserPlatformDelegate::SendCaptureLostEvent() {
void CefBrowserPlatformDelegate::NotifyMoveOrResizeStarted() {}

void CefBrowserPlatformDelegate::SizeTo(int width, int height) {}

void CefBrowserPlatformDelegate::SetHostBounds(const gfx::Rect& bounds) {}
#endif

gfx::Point CefBrowserPlatformDelegate::GetScreenPoint(
Expand Down
5 changes: 5 additions & 0 deletions libcef/browser/browser_platform_delegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,11 @@ class CefBrowserPlatformDelegate {
// Resize the host window to the given dimensions. Only used with windowed
// rendering on Windows and Linux.
virtual void SizeTo(int width, int height);

// Move and resize a windowed browser within its parent window. Only
// meaningful on platforms where the browser cannot observe the parent window
// itself; see CefBrowserHost::SetWindowBounds.
virtual void SetHostBounds(const gfx::Rect& bounds);
#endif

// Convert from view DIP coordinates to screen coordinates. If
Expand Down
Loading