Enable -Wexit-time-destructors for all in-tree library targets and
update process-lifetime library statics so they no longer register
exit-time destructors. Long-lived caches, lookup tables, singleton
registries, and generated constants now use NeverDestroyed or leaked
references where the data is intended to live until process exit.
Update LibWeb, LibLine, and the binding generators so regenerated
sources follow the same rule instead of reintroducing destructed
statics.
Compositor recovery recreated contexts and replayed viewport sizes,
but it left the restarted helper with the default display metadata. A
recovered context could fall back to a 60 Hz timer instead of the
window's current display id and refresh rate until the frontend later
observed a screen change.
Store the display id in the shared view state, alongside the existing
maximum frame rate, and replay both values while restoring view state
after reconnect.
Page-presenting compositor contexts still carried a separate tagged ID
namespace and Browser-side page/context maps even though page IDs are
now allocated globally by the UI process. That made context
registration keep two ways to describe the same relationship and
forced compositor IPC to pass an extra presentation bit.
Use each page ID directly as its page-presenting compositor context ID
and allocate non-page compositor contexts from the same Browser-owned
counter. The registration path now sends only the optional page ID, and
Compositor infers client presentation from that page ID.
Page IDs were allocated independently by each WebContent PageHost,
starting at zero for every process. That made them unsuitable as a
global identity for page-presenting compositor contexts, because the
same numeric page ID could exist in multiple WebContent processes.
Move page ID allocation to WebView::Application and initialize each
WebContent process with its browser-assigned first page ID over IPC.
New view requests now get a browser-assigned page ID before
WebContent creates the PageClient, and PageHost no longer owns a
local page counter.
This gives page-presenting compositor contexts a stable global page
identity, which will allow their context IDs to be derived from page
IDs and simplify compositor context allocation in a follow-up.
When an HTTPS response carries a Strict-Transport-Security header, the
received policy is now respected. Subsequent HTTP requests to a known
HSTS host are upgraded to HTTPS before the fetch algorithm makes
further decisions such as CORS and mixed content.
Fixes tpexpress.co.uk, where an XHR redirects HTTPS -> HTTP -> HTTPS,
relying on a HSTS policy received on the document response to avoid the
CORS failure.
LibWebView owns the active page connection and bridges the DevTools
delegate to WebContent. Track picker state there, translate UI hover,
preview, pick, and cancel requests into hit-test IPC, and forward the
resulting node ids back through the DevTools callback.
Keeping the request bookkeeping in LibWebView also lets the platform UI
frontends share the same picker behavior.
Picking needs hit testing in the page process, where layout and event
targeting state live. Expose a small page-level query and WebContent IPC
entry point that returns the node id at a viewport position.
This lets DevTools ask WebContent what the picker is pointing at without
duplicating hit-test logic outside LibWeb.
Commit 218d82cb65 added support for
pasting text with the middle mouse button. But primary pasting is
actually meant to interact with the "selection" clipboard, not the
text clipboard.
Add IPC structures and requests for inspecting grid layout data from
WebContent.
WebView forwards the async replies to LibDevTools and exposes the grid
highlighter hooks used by the protocol layer.
Parse Firefox grid highlighter options in WebContent and store them with
the active grid highlight.
This lays the groundwork to allow shared workers, nested workers (i.e.
workers owned by workers) and service workers to function independently
of WebContent.
Track whether WebContent still needs a beforeunload check and let the
frontends immediately remove a tab or window when no prompt can be
shown. WebContent still receives the close request so pagehide, unload,
and cleanup steps can run.
When the visible view is removed immediately, keep detached ownership of
the WebContent page until it reports that the top-level traversable
closed. If no acknowledgement arrives, release detached ownership and
ask ProcessManager to shut down the unused WebContent process.
Remove the command-line option that allowed Browser to start
WebContent with the in-process compositor thread instead of the
Compositor helper process. The compositor process is now the default
path, so the opt-out flag and the matching WebContent selector only kept
the old thread mode reachable.
With that option gone, simplify Browser and WebContent startup to wire
each WebContent process to the Compositor process directly. The old
local Browser-to-WebContent compositor IPC setup and its fallback input
and ready-to-paint branches are no longer needed.
Notify the UI process with the solid canvas background color recorded
for the top-level document. This is the Canvas system color with the
effective document background composited over it, matching the color
used before normal painting.
Store that color on the view and use it when AppKit, Qt, and Gtk need
to fill areas exposed while an older bitmap is still on screen during a
window resize.
The browser previously treated the out-of-process Compositor as fatal.
Restart the shared Compositor from the browser process, reconnect
process-backed WebContent clients, recreate compositor contexts, restore
viewport state, and ask WebContent to repaint and republish canvas and
media resources. WebContent now marks its compositor connection lost,
returns conservative values for synchronous compositor queries while
reconnecting, and drops outgoing updates until the replacement transport
arrives.
Synchronous input queries through the compositor control connection now
use fallible IPC. If the Compositor exits after the open check or before
the sync reply arrives, scroll and mouse handling report that the
Compositor did not handle the event and let the normal WebContent
fallback run.
Mouse events queued while the Compositor is unavailable now fall back to
direct WebContent dispatch. This keeps input completion in step with the
pending-event queue.
Recovery is capped at three automatic restarts. If the restart limit is
exceeded, if restart, reconnect, or context recreation fails, or if the
replacement Compositor exits during active recovery, the browser crashes
instead of switching process-backed views to a fallback path.
We are moving toward an architecture where the browser owns a single
process that holds the GPU context, so Skia resources can be shared
across every renderer and WebContent processes can be sandboxed away
from direct GPU access. A dedicated Compositor helper process is the
first step. The earlier commits laid the foundation; this one turns the
helper on as a selectable backend, gated behind
--enable-compositor-process so the existing in-process path stays the
shipping default.
Default topology -- one compositor per WebContent, in-process:
+---------+
| Browser |
+---------+
/ | \
v v v
+---+ +---+ +---+ each WebContent runs its own
|WC | |WC | |WC | CompositorThread on a dedicated
|+C+| |+C+| |+C+| thread, with its own GPU context.
+---+ +---+ +---+
Opt-in topology -- one single-threaded Compositor, shared by all WCs:
+---------+ control +-------------------+
| Browser |<----------->| Compositor |
+---------+ | (single thread, |
/ | \ | single GPU ctx, |
v v v data | shared by all |
[WC] [WC] [WC] ---------->| connected WCs) |
+-------------------+
Three IPC channels carry the work in the opt-in topology. The
existing Browser<->WebContent channel gains context allocation.
A new Browser<->Compositor channel carries context lifetime,
viewport, UI input, and presentation acks plus backing-store and
frame upcalls. A new WebContent<->Compositor channel carries
display lists, scroll state, video, compositor surfaces, async
scrolling, presentation, and screenshots, with upcalls for
delegated input and compositor loss.
The helper process is a Browser-owned singleton, so Browser has to be
the source of truth for which contexts exist: it is the only party that
can register them with the helper, route backing-store and presentation
upcalls back to the right WebContent and page, and reap them when a
WebContent crashes. Move id allocation into Browser and have WebContent
ask for ids over IPC, so the runtime switch in the next commit only has
to select the backend. Behavior is preserved because Browser reproduces
the previous deterministic-vs-fresh allocation logic and the in-process
compositor still owns rendering.
The compositor is moving into a dedicated helper process. That requires
a process to launch, channels for Browser and WebContent to talk to it
over, and client proxies on each side. Land all of that as an inert
scaffold first, gated behind --enable-compositor-process, so the default
rendering path is unchanged and later commits can fill in the protocol,
the service-side state, and the runtime switch against a stable target.
LibWeb still exposed the concrete CompositorThread to Page,
Navigable, and EventHandler, so compositor IPC would have leaked the
thread implementation into callers. The old thread APIs also bundled
page presentation callbacks and main-thread wakeups into the same
object, which made it awkward for WebContent to put an actor boundary
in between.
Introduce CompositorHost and context handles as the caller-facing API,
and move shared compositor protocol values out of CompositorThread. Add
WebContentCompositor IPC endpoints and route PageHost through a paired
in-process transport. The actor owns CompositorThread with explicit
main-thread and UI presentation clients, while screenshot completion is
serialized on the WebContent event loop using request IDs.
The intention for introducing IPC here is to prepare for moving the
compositor thread into a separate process.
Load the browser-generated crash page as a synthetic response for the
URL that was active when WebContent exited. This keeps the session
history entry, response URL, and created Document aligned with the same
navigation URL, so reload targets the original page without creating a
local document for an HTTP(S) history entry.
Suppress history metadata updates from the generated page and declare
an inert rel=icon. Fallback favicon loading now follows the HTML
condition that no link with the icon keyword exists, which avoids the
credentialed /favicon.ico request from the crashed origin.
CompositorConnectionToServer kept a raw reference to its client. Queued
compositor IPC can outlive that client, so a late did_paint or
backing-store notification could dereference freed memory while looking
up the target page.
Keep a weak pointer to the client instead. Drop compositor notifications
that arrive after the client has gone away. The Linux arm64 sanitizer
job caught this as a heap-use-after-free in view_for_page_id().
Route mouse events over the compositor IPC path while async scrolling is
enabled, and let the compositor capture viewport scrollbar drags before
the main-thread event path sees them. Dragging mutates the compositor
scroll tree and schedules an async-scroll present, so the viewport thumb
and content can move without a main-thread round trip.
Keep normal main-thread event handling for misses and preserve the wheel
bypass behavior for page events.
The synchronous wheel path treats wheel deltas as CSS-pixel scroll
distances, while the async compositor scroll tree mutates scroll state
stored in device pixels. Passing the same unscaled delta into the
compositor made async scrolling advance too little whenever
device pixels per CSSPixel was greater than 1, so scrolling felt slower
than with async scrolling disabled.
Convert wheel deltas before crossing the compositor boundary and make
the compositor IPC carry only the device-pixel position and delta it
needs. This keeps AsyncScrollTree device-pixel native and makes async
viewport scrolling match the synchronous path across high-DPI displays
and page zoom levels.
Trace the compositor state transitions that matter once presentation and
async scrolling share one path: page registration, backing-store swaps,
did_paint delivery, ready_to_paint acknowledgements, wheel admission,
and deferred presents waiting for reusable bitmaps.
Keep the logs under COMPOSITOR_DEBUG and use the [Compositor] prefix
consistently. The trace is about ownership and backpressure instead of
dumping every wheel event detail, so stalls can be diagnosed without
reintroducing noisy async-scrolling-only logging.
Send the experimental UI-process wheel bypass to WebContent over the
Compositor IPC connection instead of adding a separate async scrolling
transport. This keeps bypass admission on the channel that owns bitmaps
and ready_to_paint acknowledgements.
If the compositor rejects the wheel, LibWebView falls back to regular
input delivery. If it accepts, mark the queued DOM event as having
already performed its default action so dispatch preserves ordering
without scrolling twice.
Introduce a dedicated Compositor IPC channel between the UI process and
WebContent. Use it for backing-store setup, presented bitmap delivery,
and bitmap-specific ready_to_paint acknowledgements.
This makes CompositorThread the single owner of frame presentation
bookkeeping before async scrolling starts producing frames without the
main thread.
Remove old paint and backing-store messages from WebContentClient and
PageClient so the UI process no longer observes two presentation
protocols.
Some navigations report the same URL more than once through
WebContentClient::did_change_url(). Forwarding those duplicate updates
into the frontend turned an internal no-op into visible UI churn,
including location bar resets and AppKit focus stealing.
Treat repeated URL notifications as a no-op inside LibWebView so
frontends only react to real URL changes.
Record visits as soon as a page produces useful metadata such as a
title or favicon so pages that never finish loading still become
autocomplete candidates.
Store favicons in the history schema from the start instead of
introducing an upgrade path inside this series, and cover persisted
metadata behavior in TestHistoryStore.
Teach LibWebView autocomplete to query HistoryStore before falling back
to remote engines and move the wiring out of the AppKit frontend.
Refine matching so scheme and www. boilerplate do not dominate results,
short title and substring queries stay quiet, and history tracing can
explain what the ranking code is doing.
Route BroadcastChannel messages over IPC so matching channels can
receive them across WebContent and WebWorker processes, rather than only
within a single process.
Each channel now serializes its payload, sends it upward over IPC, and
receiving processes deliver it locally after matching by storage key and
channel name.
Generalize the backing store sharing abstraction into SharedImage, which
represents shared GPU memory independently of Skia and can be used to
share memory between different processes or different GPU contexts.
Now that LibIPC uses Mach ports for transport on macOS, IOSurface port
rights can be sent as regular IPC message attachments instead of through
a separate ad-hoc Mach message side-channel. Introduce
Web::SharedBackingStore that wraps either a MachPort (macOS) or
ShareableBitmap (other platforms) with IPC encode/decode support,
unifying backing store allocation into the existing
did_allocate_backing_stores IPC message.
Previously, `create_paired()` returned two full Transport objects, and
callers would immediately call `from_transport()` on the remote side to
extract its underlying fd. This wasted resources: the remote
Transport's IO thread, wakeup pipes, and send queue were initialized
only to be torn down without ever sending or receiving a message.
Now `create_paired()` returns `{Transport, TransportHandle}` — the
remote side is born as a lightweight handle containing just the raw fd,
skipping all unnecessary initialization.
Also replace `release_underlying_transport_for_transfer()` (which
returned a raw int fd) with `release_for_transfer()` (which returns a
TransportHandle directly), hiding the socket implementation detail
from callers including MessagePort.
Replace clone_from_transport() (which dup()s the FD) with
from_transport() (which releases the FD) in the WebWorkerClient
call site. The UI process never uses the WebWorkerClient connection
after spawning — it only passes the transport to WebContent — so
releasing instead of cloning is safe and simpler.
This removes clone_from_transport() from TransportHandle, and
clone_for_transfer() from TransportSocket/TransportSocketWindows,
as they no longer have any callers.
Instead of passing RequestServer and ImageDecoder socket FDs as
command-line arguments to WebWorker, send them over the main IPC channel
after launch. The worker-agent handoff now carries all three transport
handles (worker, RequestServer, ImageDecoder) so the connection path
matches WebContent.
Add IPC::TransportHandle as an abstraction for passing IPC
transports through .ipc messages. This replaces IPC::File at
all sites where a transport (not a generic file) is being
transferred between processes.
TransportHandle provides from_transport(),
clone_from_transport(), and create_transport() methods that
encapsulate the fd-to-socket-to-transport conversion in one
place. This is preparatory work for Mach port support on
macOS -- when that lands, only TransportHandle's internals
need to change while all .ipc definitions and call sites
remain untouched.
These IPC methods should be expanded in the future to allow WebContent
to specify what UI elements should be kept/removed, for example, the
navigation UI.
This patch introduces a cookie cache in the WebContent process to reduce
blocking IPC calls when JS accesses document.cookie. The UI process now
maintains a cookie version counter per-domain in shared memory. When JS
reads document.cookie, we check whether we have a valid cached cookie by
comparing the current shared version to the last used version. If they
match, the cached cookie is returned without IPC.
This optimization is based on Chromium's shared versioning, in which it
was observed that 87% of document.cookie accesses were redundant. See:
https://blog.chromium.org/2024/06/introducing-shared-memory-versioning-to.html
Note that this cache only supports document.cookie, not HTTP Cookie
headers. HTTP cookies are attached to requests with varying URLs and
paths. The cookies that match the document URL might not match the
request URL, which we wouldn't know from WebContent. So attaching the
cached document cookie would be incorrect.
On https://twinings.co.uk, we see approximately 600 document.cookie
requests while the page loads. This patch reduces the time spent in
the document.cookie getter from ~45ms to 2-3ms.
These can get very large, exceeding the new IPC message size limits.
Instead of serializing them into messages (which was silly anyway)
we now send them as Core::AnonymousBuffer which uses shared memory.