Skip to content

CEF window transparency on Linux/Wayland #301

@AgentU-asaf

Description

@AgentU-asaf

Summary

Window transparency does not work on Linux/Wayland with the CEF host. The goal is semi-transparent windows (controlled by window:opacity setting, default 0.85) so the desktop is visible through the app. Electron achieves this on the same system; CEF does not.

Root Cause

The transparency pipeline has four layers. Three are solved; one remains:

1. Widget opacity — SOLVED (binary patch)

CEF hardcodes Widget::InitParams::opacity = kOpaque in libcef.so. Three binary patches fix this:

  • NOP at file offset 0x2ec460a: prevents CefNativeWidgetDelegate::Init from setting kOpaque before Widget::Init
  • Byte at 0x9d92a8d and 0x9d92b65: Widget::Init fallback paths changed from kOpaque(1) → kTranslucent(2)

2. Wayland surface opaque region — SOLVED (LD_PRELOAD shim)

scripts/wayland-alpha-shim.c intercepts wl_surface_set_opaque_region() and passes NULL, telling the compositor the surface has per-pixel alpha.

3. Wayland buffer format — ALREADY CORRECT

WaylandShmBuffer::Initialize passes with_alpha=true to CreateShmBuffer. The SHM buffer format is WL_SHM_FORMAT_ARGB8888. No fix needed.

4. Renderer compositor has_transparent_background — THE BLOCKER

The cc compositor's LayerTreeHost defaults to background_color = SK_PMColor4fWHITE. This makes has_transparent_background = false in every CompositorFrame. The viz Display::DrawAndSwap reads this flag and clamps all pixel alpha to 1.0 when false.

How Electron fixes this: Electron calls RenderWidgetHostViewBase::SetBackgroundColor(SK_ColorTRANSPARENT), which triggers:

  1. RenderViewHostImpl::SetBackgroundOpaque(false)
  2. Mojo IPC → renderer process
  3. WebFrameWidgetImpl::SetBackgroundOpaque(false)SetBaseBackgroundColorOverrideTransparent(true)
  4. has_transparent_background = true on LayerTreeHost
  5. All subsequent CompositorFrames preserve per-pixel alpha

Why CEF doesn't do this: CEF's Chrome-style browser calls ContentsWebView::SetBackgroundVisible(false) during creation. ContentsWebView::UpdateBackgroundColor should call RWHV::SetBackgroundColor(0) when RenderViewReady fires, but this either never fires in CEF's path, fires too late, or the call is a no-op because the default color is already 0.

Evidence

  • CDP Page.captureScreenshot with Emulation.setDefaultBackgroundColorOverride({color:{r:0,g:0,b:0,a:0}}) produces pixels with correct alpha: srgba(34,34,34,0.70). This proves the renderer CAN produce transparent pixels when told to.
  • Electron 41 on the same system (GNOME/Wayland/Mutter) has fully working transparency.
  • gnome-screenshot always shows rgb(100,100,100) in the CEF window area — exactly rgba(34,34,34,0.7) composited over white (34*0.7 + 255*0.3 ≈ 100).
  • The Emulation.setDefaultBackgroundColorOverride CDP method only affects DevTools-captured screenshots, NOT the actual rendered output to the Wayland surface.

What was tried (binary patches)

Patch Effect
Widget opacity (3 sites) kOpaque → kTranslucent — working
CefContext::GetBackgroundColor alpha removal Removed or $0xff000000 — stops alpha clamping to 0xFF
WebViewImpl::BaseBackgroundColor → kTransparent Changed Blink fallback — no visible effect
BaseBackgroundColor always return kTransparent Forced unconditional jmp — no visible effect
RGBA buffer format (Skia + Presenter) Forced kPremul and needs_alpha=true — no visible effect
DRM format XRGB→ARGB Changed fourcc — inert (SHM path used, not DRM)
Display::DrawAndSwap force has_transparent_background Forced r14b=1 in browser process — no effect (renderer-side flag is what matters)
Trampoline SetPageBaseBackgroundColor(0) Code cave injection after tab creation — no visible effect
CommitState background → transparent xorps zero — broke rendering (all white, NOTREACHED errors)
LayerTreeHost constructor alpha NOP Crashed with bad_array_new_length
SetBackgroundOpaque always transparent Only triggers when called — never called
DirectRenderer::DrawFrame jne NOP Crashed GPU process
Rust CDP setDefaultBackgroundColorOverride in on_load_end Only affects DevTools screenshots

Proposed fix

CEF PR #4086 (unmerged) fixes this by setting params.opacity = kTranslucent when CefSettings.background_color has alpha=0, AND properly propagating transparency through SetBackgroundOpaque(false). This is the correct upstream fix.

Options:

  1. Build CEF from source with PR #4086 applied (~4-6 hours build, ~100GB disk)
  2. Upstream the fix — contribute to getting PR #4086 merged
  3. Find the binary patch that forces RWHV::SetBackgroundColor(0) to be called early enough — difficult because the default color is already 0, causing the early-return check to skip the SetBackgroundOpaque call

Environment

  • Ubuntu 24.04, GNOME on Wayland, Mutter compositor
  • CEF 146 (Chromium 146), cef = "146.4.1+146.0.9"
  • CEF Views framework, Ozone/Wayland backend
  • GPU process crashes (VA-API init failure) → software rendering via WaylandCanvasSurface + WaylandShmBuffer

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions