CEF pitfalls
Things CEF, Chromium and the cef crate do that cost time to find out. Add to this page when you hit one, next to the area it belongs to.
Views and windows
- A preferred size with a 0 width or height counts as unset. The view then takes its large default and squeezes the page out of the layout. Always return a non-empty size (
window::bar_size). - Dropping the last reference to a
BrowserViewcloses its browser synchronously, which re-enters our handlers. The shell's state is a UI-threadRefCell, so never drop CEF objects, or call CEF methods that fire callbacks, while it's borrowed (shell::with). - The
cefcrate answers 0 forcan_resize,can_maximizeandcan_minimizeunless the delegate implements them, where CEF's own default is 1. Windows then ask for a fixed size and tiling window managers float them. Both window delegates return 1. - A Chrome-style
BrowserViewdoesn't exist until it's added to a window, so callingset_focusableon it first crashes. A Chrome-style window takes one Chrome-style view, which must be added before any Alloy view (the first view sets the window's profile). Popups must match their opener's style. Seewindow::create_callandtabs::add_call_view. - Overlay views are opaque and only as big as their bounds. Rounded corners show square ones behind them, and there's no dimming the page behind a box.
- A hidden view pauses
requestAnimationFrame. A page that measures itself before it's shown must do it synchronously (ui/float.html). - A window doesn't finish closing while any browser in it is open, overlay views included. Floats and panels are closed in the window's
can_close(float::close_window,panel::close_window), or:quithangs.
Keys and input
- The command line is a Rust-owned buffer; keys are consumed in
OnPreKeyEvent, so UI pages never need keyboard focus. - Chromium ignores input to a page while it shows a JavaScript dialog, so prompt keys go through the status bar's browser, which gets focus for the duration.
- A new tab can miss focus requested before its browser existed;
on_after_createdfocuses it again. send_key_eventto a hidden tab is dropped (keys go to the window's focused view), and DevTools'Input.dispatchKeyEventreports success without reaching the page in windowed browsers. The first key a page ever gets starts something that drops keys for about 400 ms (client::send_when_ready).- Under Xvfb, a key sent just as a page finishes loading can be lost below Views. The e2e harness waits two animation frames after each load.
- Middle-clicks and Ctrl+clicks come through
OnOpenURLFromTab, not popups, and Alloy loads them in the same tab if it isn't handled. - Ctrl+wheel over a bar zooms every
riptide://uipage (Chromium saves zoom per host). The bar pages refuse it, andon_load_endresets a saved zoom for non-tab roles.
Pages and navigation
- Loading a
data:error page fromOnLoadErroradds a history entry, sobackloops into the error. Draw into Chromium's own error document fromOnLoadEndinstead. BrowserHost::Findwithfind_next = falsenever activates or scrolls to a match. Every call passestrue; a repeated search first callsStopFinding.- CEF passes a browser's original
extra_infotoon_browser_createdagain on reload, which would undo a:greasemonkey-reload. Script lists carry a generation number. Page.addScriptToEvaluateOnNewDocumentsilently does nothing withoutPage.enablefirst (adblock::before_navigation).Notificationis defined after the context is created, so the renderer's stand-in takes its place on the first microtask,DOMContentLoadedandload.- A page starting to load clears every status message, so startup messages wait for the first load (
shell::show_message_after_load). - A stored default for Chromium's
PROTOCOL_HANDLERScontent setting fails aCHECKwhen a private window's profile inherits it.
Profiles, preferences and services
- Chromium names its profile folder
Defaultwhatevercache_pathsays, socache_pathpoints there. - Services start within 100 ms, so privacy preferences are written into
Local StateandDefault/Preferencesbefore CEF starts.SetPreferencefromon_context_initializedis too late, and through thecefcrate it fails silently unless the error out-string is non-empty (an emptyCefStringis passed as NULL). - Feature names in
libcef.sostrings carry akprefix that Chromium strips (kAimEnabled→AimEnabled). - Chrome's login prompt swallows HTTP auth unless
--disable-chrome-login-promptis set (cef#3603). GetAuthCredentialsruns on the IO thread; the prompt is posted to the UI thread.- Chromium saves permission answers per site, but camera and microphone requests go through a separate API that isn't saved; riptide keeps its own per-site answers. Chromium also holds non-media permission prompts from hidden tabs until they show.
The cef crate
CefStringList::clonecopies the opaque C struct, so iterating a clone is always empty; read lists through the C API.- An empty
CefStringis passed to C as NULL.
Extensions
- Manifest V2 is gone in Chromium 154: MV2 extensions silently don't load.
- Alloy tabs are invisible to
chrome.tabs.query, though messages reach them (sender.tabis set). Popups and keyboard commands that act on "the active tab" can't find riptide's tabs. - With a
declarativeNetRequestextension, a page opened during startup may never start loading;extensions::after_startupreloads such tabs. - Letting Chromium finish a
.crxdownload hands it to its own installer, which deletes the file. riptide cancels the download and fetches the URL itself.
Crashes
chrome://crashwith the sandbox on leaves the tab loading instead of crashing it; the test channel'sCrashTababorts the renderer instead.- Crashpad reads
crash_reporter.cfgnext to the executable, and keeps dumps with no size or age limit on Linux (rt_storage::crash_reports::dumpsprunes them). - A panic that reaches CEF's C callers panics again ("cannot unwind"); only the first panic on a thread is reported.
Building and releasing
- CEF's
libcef.socarries debug info (1.4 GB); stripping it gives 260 MB. - Inside an AppImage,
chrome-sandboxcan't be setuid, so the sandbox needs user namespaces there. - A tag pushed with a workflow's own token doesn't trigger other workflows, and GitHub Actions can't bypass a ruleset on a personal repository; see Releasing.
- mdBook's smart punctuation turns
--forceinto an en dash, so it's off.
Testing
- Unix socket paths must fit in about 108 bytes, so the e2e harness uses a short
XDG_RUNTIME_DIR. - CEF's zygote leaves the process group; the harness also stops the helpers that carry its
--user-data-dir. cargo test -p rt-e2edrives the builttarget/debug/riptideand doesn't rebuild it: runcargo buildfirst, or./task e2e.