Keep regular rule cache invalidation from discarding the parsed user
style sheet. User style and content blocker source changes now use an
explicit invalidation path that clears the parsed sheet before the rule
cache is rebuilt.
This avoids reparsing user CSS for unrelated style changes, which was
very annoying when browsing with cosmetic CSS from content blockers.
Compositor-backed canvas contexts keep their transports tied to a single
Compositor connection. When that connection dies, the 2D backing
storage and WebGL GL objects disappear with it, but WebContent does not
surface the loss to canvas contexts or create fresh host contexts after
reconnect.
Track compositor loss through the WebContent connection, mark WebGL
contexts lost, dispatch the standard context events, and rebuild the
remote proxy when the page opts into restoration. For 2D canvas, queue
the canvas context loss steps, discard the dead backing storage, and
create new storage before firing contextrestored.
Canvas rendering is a major remaining path where WebContent directly
owns GPU-facing drawing state. Back 2D and WebGL canvas contexts with
remote Compositor transports, so WebContent talks to canvas surfaces
through IPC while the Compositor owns the rasterization resources.
This is a large step toward GPU sandboxing because canvas GPU work now
lives behind the Compositor boundary. It also gives OffscreenCanvas the
process-independent canvas plumbing that HTMLCanvasElement now uses,
making worker-owned canvases possible without another WebContent-local
rendering path.
Use the LibWebView history mirror to preserve traversable session
history across WebContent process swaps. WebContent reports snapshots to
the UI process, and new renderers can be seeded from the mirror.
Browser back and forward now resolve through the UI-owned used history
steps. WebContent still runs the spec traversal path when the current
renderer has enough matching state to do so.
Handle canceled and no-op UI navigations without leaving speculative
history entries or pending WebDriver waits behind. Preserve traversal
precheck state across synchronous IPC shutdown, and avoid overwriting a
restored target entry's persisted scroll state before the document has
adopted that entry.
Track whether a keydown should perform text insertion separately from
the delivered code point. Native frontends and text-oriented test paths
can now mark events that came from text input, while shortcut-style key
events keep Alt-modified default insertion suppressed.
This lets macOS Option-generated text such as Option+A and Option+Space
insert into editable controls without making plain Alt shortcuts insert
their base character. Add coverage for Alt text, Ctrl+Alt text,
separator text, Ctrl-only shortcuts, and Alt shortcuts.
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.
Only use the loading-time top-level canvas fallback when the root
color-scheme value is still normal. An explicit light scheme on the
root element already gives the canvas a light used scheme, so replacing
it with the preferred dark scheme makes the viewport disagree with the
computed system colors until readiness advances.
Add internals coverage for the document canvas scheme and a text test
that exercises the loading state with a dark preferred color scheme.
Trackpad pinches applied directly as visual viewport zoom, so pages
could not observe the gesture or cancel the browser default. Canvas
apps such as maps expect the ctrl-wheel path instead.
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.
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.
Do not invalidate shadow-root rule caches when the document user style
sheet changes. Shadow trees still need style invalidation because
document user rules can match their descendants, but their local author
rule caches do not include the document user sheet.
Since user and content-blocker sheets now live only in the document
scope, shadow-DOM state changes also have to consult the document user
selector insights for :has() and pseudo-class invalidation.
Add content blocker coverage for user-style refreshes, shadow :has()
state changes, and shadow pseudo-class invalidation.
Build the user stylesheet only for document style scopes, since user
rules are already considered relevant across shadow boundaries during
rule matching. This avoids regenerating and reparsing the same cosmetic
content blocker stylesheet for every shadow root in the document.
Keep the generated cosmetic stylesheet cached on the Document and clear
it whenever user style is invalidated, so content blocker changes still
produce fresh CSS for the next style update.
Split cosmetic blocker rules out from network patterns. Expose matching
rules as user CSS through StyleScope.
Invalidate affected user style caches when blocker state changes.
Generated cosmetic CSS now respects disabled content blocking.
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.
Previously, if the find in page query was updated and no matches were
found we would keep the old selection instead of clearing it.
`window.find` behaves differently from the find in page UI and
preserves the old behavior.
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.
Move compositor-thread ownership to WebContent's PageHost so every Page
object in one WebContent process registers its navigables on the same
compositor thread. This covers auxiliary pages created by window.open(),
while worker and SVG helper pages continue to skip compositor thread
creation.
Keep page presentation keyed by page id. Each presenting context records
the page id it presents for, and static compositor entry points route
ready-to-paint, async scrolling, and viewport scrollbar events to that
page's presenting context on the shared thread.
Async scrolling tests used requestAnimationFrame() as a proxy for the
compositor thread to return pending scroll updates to the main thread.
That waited for a rendering opportunity, so tests could observe stale
DOM scroll offsets when the compositor update had not been adopted yet.
Make internals.wheel() return a promise that resolves after a tracked
async scroll operation has been applied by Navigable. Tracking is opt-in
from the internals test API, so regular page wheel input and compositor
IPC keep the boolean async-scroll path without allocating operation IDs.
Tracked test scrolls are the only operations that record completions.
Update async scrolling and wheel propagation tests to await the wheel
promise directly instead of relying on animation frame timing in tests.
Wheel deltas were truncated to int at the platform input boundary,
which dropped the sub-pixel tail of trackpad momentum scrolls. Each
NSEvent's scrollingDeltaY arrived as a CGFloat, got cast to int, and
flowed through IPC, EventHandler, and PaintableBox::scroll_by as int,
losing fractional information that never came back.
Widen Web::MouseEvent::wheel_delta_{x,y} to double and propagate
through Page, EventHandler, Paintable, PaintableBox, and the AppKit,
Qt, and GTK input paths.
Bump a page-level wheel listener generation whenever non-passive wheel
listeners are added or removed. Store that generation in async scrolling
snapshots, and mark compositor wheel admission stale when listener state
changes before a current snapshot arrives.
This keeps the UI-process compositor wheel bypass from trusting an old
no-listener snapshot after script has installed a cancelable wheel
listener. Add async-scrolling coverage for the listener generation bump
and the fresh routing state after a blocking wheel listener is added.
Use the snapshot from the previous commit to let CompositorThread apply
experimental viewport wheel deltas when async scrolling is enabled. The
event handler first performs synchronous admission on the main thread,
then enqueues a compositor scroll command instead of mutating live
document scroll state directly.
Rasterize accepted scrolls through the same compositor presentation
path added earlier. The compositor stores the newest async viewport
offset so the next main-thread display-list recording can adopt it
before repainting, preventing older paints from snapping the visible
position backward.
Keep DOM wheel dispatch on the main thread. When the compositor already
performed the default action, dispatch the wheel as non-cancelable and
suppress a second default scroll. Non-viewport targets, nested
scrollers, and pages with blocking wheel listeners stay synchronous.
Previously we were inconsistent by generating code for enum definitions
but not generating code for dictionaries. With future changes to the
IDL generator to expose helpers to convert to and from IDL values
this produced circular depdendencies. To solve this problem, also
generate the dictionary definitions in bindings headers.
Previously a fixed-rate paint refresh timer kept queueing rendering
update tasks at the maximum configured frame rate, regardless of whether
anything had actually changed. This wasted CPU on idle pages, which
spend most of their time in a steady state where no style, layout, or
paint work is needed.
Replace the repeating timer with a single-shot frame timer driven by
PageClient::request_frame(). A rendering update is now scheduled only
when something requires one. The configured maximum frame rate is
preserved as a ceiling on how closely consecutive frames can follow each
other.
Instead of immediately firing fullscreenchange, defer that until
WebContent's client has confirmed that it is in fullscreen for the
content. The fullscreenchange is fired by the viewport change, so in
cases where the fullscreen transition is instantaneous (i.e. the
fullscreen state is entered at the exact moment the viewport expands),
the resize event should precede the fullscreenchange event, as the spec
requires.
This fixes the WPT element-request-fullscreen-timing.html test, which
was previously succeeding by accident because we were immediately
fullscreenchange upon requestFullscreen() being called, instead of
following spec and doing the viewport (window) resize in parallel. The
WPT test was actually initially intended to assert that the
fullscreenchange event follows the resize event, but the WPT runner
didn't actually have a different resolution for normal vs fullscreen
viewports, so the resize event doesn't actually fire in their setup. In
our headless mode, the default viewport is 800x600, and the fullscreen
viewport is 1920x1080, so we do fire a resize event when entering
fullscreen. Therefore, that imported test is reverted to assert that
the resize precedes the fullscreenchange.
This moves normal/double/triple click checking into WebContent, the
client only has to send a click count in order to activate a double
or triple click in the content. This means that the AppKit UI will no
longer fire multiple double clicks when clicking in place more than 3
times. This matches the behavior of other browsers on macOS.
We will now also fire the click event regardless of whether a dblclick
event will follow, as the spec requires.
present() now snapshots the PaintingSurface into an ImmutableBitmap
and publishes it to the ExternalContentSource, so the rendering thread
never touches the live GPU surface — eliminating the data race
described in the ExternalContentSource commit (problem 1).
Canvas elements are registered with Page and presented once per frame
from the event loop, rather than on every individual draw call in
CRC2D::did_draw(). A dirty flag on HTMLCanvasElement ensures the
snapshot is only taken when content has actually changed, and makes
the present() call in CanvasPaintable::paint() a no-op when the
surface has already been snapshotted for the current frame.
When triple clicking on text, we should select the entire paragraph, or
entire line in <input>s and <textarea>s. If the mouse button is held
down and the user starts dragging, the selection expands with additional
paragraphs or lines.
This expands on the work of Kai Wildberger (PR #7681) but was adjusted
for the work that happened previously to support double click + drag
moves and includes triple click support for our Qt UI.
Co-authored-by: Kai Wildberger <kiawildberger@gmail.com>
Add ElementResizeAction to Page (maybe there's a better place). It's
just a mousemove delegate that updates styles on the target element.
Add ChromeMetrics for zoom-invariant chrome like scrollbar thumb
thickness, resize gripper size, paddings, etc. It's not user-stylable
but separates basic concerns in a way that a visually gifted
designer unlike myself can adjust to taste.
These values are pre-divided by zoom factor so that PaintableBox can
continue using device_pixels_per_css_pixel calls as normal.
The adjusted metrics are computed on demand from Page multiple times
per paint cycle, which is not ideal but avoids lifetime management and
atomics. Maybe someone with more surety about the painting flow control
can improve this, but it won't be a huge win. If profiling shows
this slowing paints, then Ladybird is in good shape.
Update PaintableBox to draw the resize gripper and deconflict
the scrollbars. Set apropriate cursors for scrollbars and gripper in
mousemove. We override EventHandler's cursor handling because nothing
should ever come between a man and his resize gripper.
Chrome metrics use the CSSPixels class. This is good because it's
broadly compatible but bad because they're actually different units
when zoom is not 1.0. If that's a problem, we could make a new type
or just use double.
Before this change, we've been maintaining various StyleComputer caches
at the document level.
This made sense for old-school documents without shadow trees, since
all the style information was document-wide anyway. However, documents
with many shadow trees ended up suffering since any time you mutated
a style sheet inside a shadow tree, *all* style caches for the entire
document would get invalidated.
This was particularly expensive on Reddit, which has tons of shadow
trees with their own style elements. Every time we'd create one of their
custom elements, we'd invalidate the document-level "rule cache" and
have to rebuild it, taking about ~60ms each time (ouch).
This commit introduces a new object called StyleScope.
Every Document and ShadowRoot has its own StyleScope. Rule caches etc
are moved from StyleComputer to StyleScope.
Rule cache invalidation now happens at StyleScope level. As an example,
rule cache rebuilds now take ~1ms on Reddit instead of ~60ms.
This is largely a mechanical change, moving things around, but there's
one key detail to be aware of: due to the :host selector, which works
across the shadow DOM boundary and reaches from inside a shadow tree out
into the light tree, there are various places where we have to check
both the shadow tree's StyleScope *and* the document-level StyleScope
in order to get all rules that may apply.
Prevents observably calling Trusted Types, which can run arbitrary JS,
cause crashes due to use of MUST and allow arbitrary JS to modify
internal elements.
With this commit, all PlaybackManager can do is autoplay a file from
start to finish, with no pausing or seeking functionality.
All audio playback functionality has been removed from HTMLMediaElement
and HTMLAudioElement in anticipation of PlaybackManager taking that
over, for both audio-only and audio/video.
Adds pinch event handling that adjusts the VisualViewport scale and
offset. VisualViewport's (offset, scale) is then used to construct a
transformation matrix which is applied before display list execution.
The faux position we created here is adjusted by the device pixel ratio
later on, which would invoke integer overflow on screens with a DPR
greater than 1.
Instead of creating special data for a mouse move event, let's just add
an explicit leave event handler.
We currently have a single IPC to set clipboard data. We will also need
an IPC to retrieve that data from the UI. This defines system clipboard
data in LibWeb to handle this transfer, and adds the IPC to provide it.
This removes a couple of places where we were constructing strings or
vectors just to transfer data over IPC. And passes some values by const&
to remove clangd noise.
We set the page's focused navigable upon mouse-down events from the UI.
However, we neglected to ever clear that focused navigable upon events
such as subsequent page navigations. This left the page with a stale
reference to a no-longer-active navigable. The effect was that any key
events from the UI would not be sent to the new page until either the
reference was collected by GC, or another mouse-down event occurred.
In the test added here, without this fix, the text sent to the input
element would not be received, and the change event would not fire.