Commit graph

79522 commits

Author SHA1 Message Date
Andreas Kling
8d85a17527 LibWeb: Extract ancestor hashes from :is() and :where()
Look through single-argument :is() and :where() pseudo-classes when
collecting ancestor-filter hashes. Reuse hashes from the rightmost
compound of the argument selector, including nested wrappers, because
that compound matches the same ancestor element.

Keep selector lists with multiple alternatives conservative, since no
single hash is mandatory for every branch.
2026-06-19 01:40:49 +02:00
Andreas Kling
bc201e3257 LibWeb: Bucket more :is() and :where() rules
Look through single-argument :is() and :where() selectors when
selecting a rule-cache bucket. Use mandatory ids, classes, tag names
or attributes from the rightmost compound of the argument selector,
including nested :is() and :where() wrappers.

Leave multi-argument lists in the broad bucket, since a single key
would not be mandatory for every alternative.
2026-06-19 01:40:49 +02:00
Andreas Kling
b7a290cbe2 LibWeb: Keep pseudo-element rules in pseudo buckets
Keep pseudo-element style rules out of the normal element rule buckets
while preserving the same id, class, tag, attribute and root buckets
inside each known pseudo-element type. This avoids collecting pseudo
rules for normal element style, without broadening pseudo style
collection to every rule targeting the queried pseudo-element.

Update the has invalidator to walk both the normal rule buckets and
the pseudo-element bucket maps when deciding whether pending :has()
mutations may affect style.
2026-06-19 01:40:49 +02:00
Aliaksandr Kalenik
a08734ecbc LibGfx+LibWeb+Compositor+WebContent: Commit 2D canvas flushes atomically
2D canvas contexts started publishing partial frames after canvas
rasterization moved into the Compositor. WebContent still splits large
recorded command lists after 64 commands, but every split batch was sent
through the same compositor path as the end-of-frame flush. The
compositor replayed each batch into the DrawCanvas source surface,
so a pending present could sample a canvas after clear and before the
rest of the next frame had been drawn. Canvas-heavy pages such as
slither.com then flickered between partial and complete frames.

Carry an explicit commit bit with 2D canvas command updates. Non-commit
batches now update a hidden working canvas in the Compositor, while the
display-list-visible surface keeps the last committed canvas contents.
The end-of-frame canvas preparation sends the commit boundary, including
the empty-commit case needed when the auto-flush consumed all recorded
commands before prepare_for_compositing() runs.
2026-06-19 00:01:26 +02:00
Sam Atkins
cb88229c3f LibDevTools: Handle fragmented protocol packets
Firefox can split a DevTools protocol packet across multiple TCP
reads. The previous reader only checked that some data was available,
then tried to synchronously read the whole length-prefixed packet from
the readiness callback. Once the socket was nonblocking this could make
startup flaky when Firefox opened the inspector.

Buffer incoming bytes instead, and only dispatch messages once a full
length-prefixed JSON payload has arrived. Add a protocol test that sends
a request in two fragments through the real DevTools server.
2026-06-18 22:53:30 +02:00
Andreas Kling
2ca74acc3d LibWeb: Store small invalidation sets inline
Most invalidation sets carry at most one selector feature. Storing
every set in a HashTable pays allocation and hashing costs while
constructing style invalidation data, even in the common case.

Store the first property inline and upgrade to HashTable only when a
second distinct property is inserted. Keep hash and equality checks
direct over the backing so those hot paths do not route through the
public callback iterator.
2026-06-18 22:53:13 +02:00
sideshowbarker
13b609133d CI: Add wasm-tools to the build image
wasm-tools is needed at build time to generate the WebAssembly spec
tests (INCLUDE_WASM_SPEC_TESTS), and the setup action downloads it from
the bytecodealliance releases CDN for every build job. That CDN
intermittently returns 504, which fails otherwise-green CI runs.

Bake the pinned wasm-tools 1.243.0 into the ladybird-ci image so the
Linux container jobs, which are the bulk of the build matrix, get it
from the image instead of fetching it per run.

A follow-up will drop the per-job download for the container platforms
once this image is published. The macOS and Windows jobs run on native
runners that do not use this image, so they keep downloading the same
pinned version.
2026-06-18 22:44:40 +02:00
sideshowbarker
1484686c70 CI: Add caching and retries to the wasm-tools download
Cache the wasm-tools binary rather than doing (trying) a re-fetch of it
on every build job. And when we ever do need to re-download it (after a
cache miss), then do it with some sensible retry-with-backoff-and-wait.

Otherwise, a single network hiccup when trying to download wasm-tools
was failing multiple CI jobs at once — before their builds even started.
2026-06-18 22:44:40 +02:00
Jelle Raaijmakers
d819152b4e LibGfx+LibWeb: Preserve text color for selections
Selecting text without custom ::selection styling changed the
foreground color of the selected content. This was especially visible
for links, where the text changed color but the underline did not.

The default selection style supplied both a selection background and a
foreground color from the palette or HighlightText system color. That
made ordinary selections behave as if the page had explicitly styled
::selection color.

Only provide a default selection background, so selected content keeps
its own foreground color unless CSS overrides it. Remove the now-unused
SelectionText palette role.
2026-06-18 22:43:26 +02:00
Tim Ledbetter
91d3d135eb LibWeb: Paint emoji glyph text-shadows in the shadow color 2026-06-18 22:42:29 +02:00
Andreas Kling
86e0e281a0 LibWeb: Avoid unnecessary pseudo-element style recomputation
Track the synthetic pseudo-elements that matched while computing an
originating element's normal style. Store the transient match set as a
bitfield, then copy those bits into ComputedProperties. Use them during
style invalidation to skip pseudo style recomputation when neither the
old nor new originating style matched pseudo rules and no pseudo style
already exists.

This shaves roughly 500 ms off loading the Ladybird GitHub repository.

Materialize synthetic pseudo styles on demand for CSSOM reads so
getComputedStyle(element, "::before") still computes skipped styles when
script asks for them. Add coverage for a universal pseudo selector, and
update style invalidation counter expectations for the reduced work.
2026-06-18 19:50:34 +02:00
Tim Ledbetter
340ef361d8 LibWeb: Support anchor() in calc() trees
Previously, `anchor()`  was only resolved when it appeared bare in an
inset property. We now allow it to appear anywhere inside a `calc()`
tree.
2026-06-18 17:36:56 +01:00
Tim Ledbetter
b62a099e91 LibWeb: Serialize a non-math function at the root of a calc() tree
Previously, math function serialization assumed the calculation tree
root was a numeric value or a calc-operator node, and otherwise emitted
the root's name followed by its comma-separated children.
A non-math function node such as `sibling-index()` or `anchor()` has no
children, so a `calc()` whose entire contents was such a function
serialized to an empty "calc()". We now serialize the function directly
instead.
2026-06-18 17:36:56 +01:00
Tim Ledbetter
e4a5957bc6 LibWeb: Only allow anchor() values in inset properties 2026-06-18 17:36:56 +01:00
Tim Ledbetter
c1100a7c73 LibWeb: Simplify property validity check in parse_anchor_size() 2026-06-18 17:36:56 +01:00
Tim Ledbetter
d9fac87e8a LibWeb: Use correct spec link for anchor() 2026-06-18 17:36:56 +01:00
Andreas Kling
849d528220 LibWeb: Build style invalidation data lazily
Split StyleCache's rule matching data from its invalidation metadata.
Allow callers to build either payload independently. Style invalidation
queries no longer force a full rule cache rebuild, and rule matching
no longer builds invalidation metadata as a side effect.

Previously, callers often treated an absent rule cache as proof that no
style invalidation metadata existed. That made some invalidation paths
do nothing until something else had populated the rule cache first.
Build invalidation data before reading it instead, so these paths use
the metadata whenever stylesheet rules require it.

Rebaseline style invalidation counter expectations for the new lazy
build points. Flush setup style work in the structural :has() feature
filter test before measuring each mutation, so the recomputation
counters describe the mutation itself instead of leftover setup work.
2026-06-18 15:45:12 +02:00
Jelle Raaijmakers
d8a55dad1c LibWeb: Target :active and :open invalidation
Changing :active or :open used the broad style invalidation path, so
large subtrees were recomputed even when only the target element and
selector-matched relatives could be affected.

Reuse the :has() feature collector to keep conservative fallback for
observable :has() cases, then use pseudo-class property invalidation
for the common targeted path.
2026-06-18 13:42:51 +02:00
sideshowbarker
4bbc5e0950 LibWeb: Fix crash from SVG resource boxes outliving removed element
Problem: Crash when removing a <mask>, <clipPath>, or <pattern> element
that’s referenced via url(#id), and then GC’ing it.

Cause: <mask>, <clipPath>, and <pattern> are laid out as resource boxes
attached to the referencing element's layout subtree, rather than their
own. So those survive cleanup of their DOM ancestor. Once a removed
<mask>/<clipPath>/<pattern> is collected, its resource box’s weak DOM
node pointer is null. And dereferencing that trips an assert — both
while painting and while tearing down the layout tree.

Fix: When a <mask>, <clipPath>, or <pattern> is removed, request a full
layout-tree update while the element’s still alive. That drops the stale
resource boxes (whose url(#id) references no longer resolve) before the
node gets collected.

Fixes https://github.com/LadybirdBrowser/ladybird/issues/10127
2026-06-18 13:35:32 +02:00
Andreas Kling
66f313fe48 LibWeb: Preserve parsed user style sheets
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.
2026-06-18 13:27:39 +02:00
Andreas Kling
4daec8f6e0 LibWeb: Use Event storage for MouseEvent relatedTarget
Store MouseEvent's relatedTarget in the inherited Event field instead of
keeping a second slot on MouseEvent.

Event dispatch retargets and updates the inherited field while building
the event path. The second slot left JS listeners observing stale or
null relatedTarget values during mouse and pointer boundary events.

Add coverage for boundary events between sibling elements.
2026-06-18 12:40:37 +02:00
Callum Law
8ebdd6d41e LibWeb: Schedule rendering update on AVC only style change
b583fd skipped display list invalidation for AVC only style changes
which also meant that we no longer marked the navigable as needing a
repaint or requested the next frame.

This commit updates `set_needs_accumulated_visual_contexts_update` to do
so.

This makes the animation when hovering icons on
https://chrede88.github.io/L1nkr paint intermediate frames not just the
first and last.
2026-06-18 12:26:22 +02:00
Callum Law
64f866cae2 LibWeb: Support number/percentage interpolation for scale
Previously we didn't support interpolating component values of `scale`
from a number to a percentage (or vice versa). This also caused
interpolation from `none` to a percentage value to fail since the
fallback value is number based.

This makes the transition run when hovering buttons on
https://chrede88.github.io/L1nkr/ rather than being discrete.
2026-06-18 12:26:22 +02:00
Callum Law
2c6aa4d272 LibWeb: Apply null transform properties on style recomputation
When applying style in `Layout::Node::apply_style` we previously ignored
null `rotate`, `translate`, and `scale` values which left the old values
in place in the case of nonnull -> null changes.

Fixes #10125
2026-06-18 12:26:22 +02:00
Sam Atkins
147d4595c6 LibWebView: Restore RFC cookie storage behavior
In 11b053b154 I accidentally changed the
behaviour of CookieJar::set_cookie() to not match what the RFC
requires, particularly when dealing with too-long paths. This commit
restores the original behaviour, now that the validation required by
DevTools happens elsewhere, before set_cookie() is called.
2026-06-18 11:37:36 +02:00
Sam Atkins
953251351d LibHTTP: Validate direct cookie conversion
DevTools edits cookies as concrete fields rather than Set-Cookie
strings. Validate direct Cookie objects while converting them back to
ParsedCookie so invalid edits can be reported without making the RFC
storage algorithm stricter.
2026-06-18 11:37:36 +02:00
Sam Atkins
680dc78dac LibHTTP: Reuse cookie domain attribute parsing
The Cookie to ParsedCookie conversion stripped a leading dot and
lowercased the cookie domain itself. Use the existing Domain attribute
parser instead, so edited cookies follow the same ASCII validation and
normalization as Set-Cookie parsing.
2026-06-18 11:37:36 +02:00
Sam Atkins
b1547d467e LibHTTP+LibWebView: Share Cookie conversion
Nothing about `parsed_cookie_from_devtools_cookie()` is specific to
DevTools, so move it to ParsedCookie.h as `parse_cookie()` instead.
2026-06-18 11:37:36 +02:00
sideshowbarker
9f7a328d9b LibWeb: Reject canvas toDataURL()/toBlob() when not origin-clean
Problem: Drawing a cross-origin image onto a 2D canvas clears its
origin-clean flag, but toDataURL() and toBlob() ignored that flag and
serialized the bitmap regardless. So, a page could read back the
cross-origin pixels it shouldn't (per spec) be allowed to access.

Cause: The origin-clean checks in to_data_url() and to_blob() were left
as FIXMEs. Only getImageData() enforced the flag.

Fix: Throw a SecurityError exception from both serialization entry
points when the canvas isn't origin-clean — matching getImageData() and
the spec. The same check also implements the previously-stubbed
origin-clean step in the WebDriver canvas-encoding algorithm.

Fixes: https://github.com/LadybirdBrowser/ladybird/issues/10009
2026-06-18 10:52:42 +02:00
Jelle Raaijmakers
d642f7d85a LibWeb: Do not dump number of children for StackingContexts
The number of children a SC has is not that useful in dumps, when all
its children are dumped anyway.
2026-06-18 10:50:08 +02:00
Callum Law
8ebdaeab69 LibWeb: Transfer animation ownership to AnimatedBitmapDecodedImageData
Previously animation ownership was a messy split between
`AnimatedBitmapDecodedImageData` and the consumers (i.e.
`ImageStyleValueResource`, `HTMLImageElement`, and `SVGImageElement`)
with `AnimatedBitmapDecodedImageData` owning the frames and a current
frame index, and the consumers owning the rest of the state (e.g. loop
count, timers to drive the animation forward, their own current index).

This had a couple of main issues:
 - While `AnimatedDecodedImageData` partially synchronized animations by
   dropping unexpected advancement notifications, this didn't apply to
   other animation state which meant, for instance, that a later started
   consumer could drive the animation of an earlier one past the max
   loop count (albeit without invalidating the earlier consumer).
 - Multiple consumers didn't share frame timings, meaning animations
   could be up to a full frame out of sync visually.
 - Animations were paused depending on whether there were any consumers,
   this is different to the behavior in other browsers (where they
   continue regardless of whether there are any consumers).
 - It was an overgeneralization of how animations need to work - only
   `AnimatedBitmapDecodedImageData` works with an indexed frame model,
   with animated SVGs (although not yet implemented) relying on their
   internal event loop to be driven forward.

Given the above the new approach implemented in this commit is:
 - The API for `DecodedImageData` is animation system agnostic, only
   exposing `default_frame`, `current_frame`, and `restart_animation`
   methods not reliant on providing a specific frame index.
 - `AnimatedBitmapDecodedImageData` owns its own timer, loop count,
   etc. The animation starts when the first consumer registers and ends
   when the document is hidden or becomes inactive (or completes in the
   case of finite animations).
 - Consumers are invalidated by `AnimatedBitmapDecodedImageData` when
   required.

Tests have been added for:
 - Animations being paused when the document becomes inactive and
   restarted when it becomes active again.
 - Frame timings being synchronized across consumers.
 - Restarts triggered by `HTMLImageElement` applying to all consumers.
 - Processing ending once a non-infinite animation plays to completion.

The tests to ensure animations are cancelled when consumers are removed
(e.g. `animated-background-image-timer-stops-when-hidden.html`) have
been updated to assert the inverse since animation state is now per
resource not per consumer.
2026-06-18 10:44:25 +02:00
Callum Law
27381e9b00 LibWeb: Remove index based frame getters for ImageStyleValue
`ImageStyleValueResource::frame` was unused and `ImageStyleValue::frame`
was only ever used to get the current frame so it can just be inlined.
2026-06-18 10:44:25 +02:00
Callum Law
3852b3f5a8 LibWeb: Rename AnimatedDecodedImageData
To `AnimatedBitmapDecodedImageData`. This better reflects what it is and
frees up `AnimatedDecodedImageData` to be used as an abstract class in a
later commit.
2026-06-18 10:44:25 +02:00
Callum Law
a7881ca3eb LibWeb: Register DecodedImageData consumers as clients
In a future commit, ownership of animation will be transferred from
these clients to `AnimatedDecodedImageData` and we will need a way to
invalidate them for new frames.

This also revealed some `ImageProvider`s which don't yet support
animated images (e.g. `<input type="file">`, `<object>`, etc) but that
is left as a FIXME for now.
2026-06-18 10:44:25 +02:00
Callum Law
74e04ed258 LibWeb: Add DecodedImageData::default_frame helper
The index based `frame(size_t, ...)` getter is going to be removed in a
future commit.
2026-06-18 10:44:25 +02:00
Callum Law
4428eba205 LibWeb: Simplify ImageStyleValueResource
`ImageStyleValueResource` now holds a reference to a
`HTML::SharedResourceRequest` for it's entire lifetime.

We also `VERIFY` that we have created a resource (by registering a
client) before calling `ImageStyleValue::image_data` rather than
silently failing.
2026-06-18 10:44:25 +02:00
Callum Law
2335941e43 LibWeb: Register ImageObservers for all mask-images
As of 114c8a7 we paint all mask layers, not just the first, so we should
likewise observe all `mask-image` values, not just the first.
2026-06-18 10:44:25 +02:00
Callum Law
11ab66c752 LibWeb: Notify ImageStyleValue::Clients on animation
Previously image animations driven by `ImageStyleValueResource`
invalidated clients using `on_animate`, this was only implemented by the
`background` presentational attribute of `HTMLBodyElement`.

It now uses `notify_clients_did_update` which is implemented by all
clients.
2026-06-18 10:44:25 +02:00
Callum Law
ce7a963344 LibWeb: Guard ImageRequest::fetch_image against redundant fetches
All other callers of `SharedResourceRequest::fetch_resource` guard based
on `needs_fetching` so let's do that here as well (and add a `VERIFY` so
that future callers don't make the same mistake).
2026-06-18 10:44:25 +02:00
Callum Law
373327a207 LibWeb: Remove AnimatedDecodedImageData::m_highest_requested_frame
This was only ever set, not read.
2026-06-18 10:44:25 +02:00
Callum Law
d98c5d1b03 LibWeb: Inline image scaling mode computation
This allows us to remove the `frame_rect` accessor.

This also fixes a bug where we computed the scaling mode based on the
clipping rect rather than the draw rect for `ImagePaintable`
2026-06-18 10:44:25 +02:00
Callum Law
d3c0cfdc71 LibWeb: Respect SVG intrinsic sizing during object fitting
Previously we used the `frame_rect` size, this is the same as the
intrinsic size for bitmap images but is `OptionalNone` for SVG which
caused us to always fall back to the `image_rect` and thus not apply
any scaling for SVGs regardless of whether they had intrinsic sizing.
2026-06-18 10:44:25 +02:00
Aliaksandr Kalenik
f6f68ece44 Services: Deny GPU operations in WebContent
WebContent no longer creates a GPU-backed Skia context, so its Linux
sandbox does not need the broad GPU device syscall allowance. Remove
allow_gpu_device_operations() from the renderer policy while keeping the
narrower file descriptor ioctl allowances used by IPC transport setup.
2026-06-18 10:25:44 +02:00
Aliaksandr Kalenik
724ca7cc24 LibSandbox: Allow TCGETS2 fd queries
WebContent is about to stop using the broad GPU device sandbox
allowance. That also removes the accidental permission for every ioctl,
which exposes ordinary terminal attribute queries made while printing
diagnostics.

The media decode error path can ask stderr for TCGETS2 while reporting a
corrupt video frame. Allow that narrow fd metadata query alongside
TCGETS, FIONBIO, and FIONREAD so renderer processes can keep the GPU
ioctl denial without crashing the media decode-error coverage.
2026-06-18 10:25:44 +02:00
Aliaksandr Kalenik
23885e7b4d LibWeb+WebContent+WebWorker: Drop display list player type selection
WebContent no longer chooses between CPU and GPU display list players,
and the remaining callers always use Skia raster playback. Remove the
PageClient virtual and now-single-value enum, then play SVG image and
cursor display lists directly.
2026-06-18 10:25:44 +02:00
Aliaksandr Kalenik
40d446d558 LibWebView+WebContent: Stop creating GPU Skia backend in WebContent
Canvas and display list rasterization now run in the Compositor
process, so WebContent no longer needs its own Skia GPU backend. Drop
the WebContent --force-cpu-painting option and stop forwarding it when
launching the renderer. The flag remains available for Compositor.
2026-06-18 10:25:44 +02:00
sideshowbarker
33d969682b Tests: Wait for session history convergence in UI history assertions
Problem: TestWebDriverSessionHistory failed intermittently on the slower
sanitizer CI runners. The UI history already matched what’s expected,
but webContentHistoryMatchesUI was still false — because the snapshot
was caught mid process-swap. The failing assertion varied from run to
run, making it almost certainly a sampling race.

Cause: A cross-site history navigation swaps the WebContent process and
re-seeds its history, and the UI-process mirror converges to that
asynchronously — after the script-visible navigation has finished. But
expect_ui_session_history sampled the mirror once, and asserted right
away — so it could read the state before convergence.

Fix: When a converged state is expected, wait for it instead of sampling
once: Poll until the UI history matches and WebContent matches the UI.
2026-06-18 10:23:49 +02:00
Jelle Raaijmakers
9ed75e4f50 LibWeb/CSS: Retain length precision in PercentageOr<T>
Instead of operating within the (saturating) CSSPixels constraints,
calculate the expected value using a floating point calculation first
and then create the CSSPixels value.
2026-06-18 09:59:41 +02:00
sideshowbarker
a38407d457 LibWeb: Unregister a “use” element from its document when finalized
Problem: Discarding a document that contains an SVG “use” element could
abort the process with a !is_in_list() verification failure in the
IntrusiveListNode destructor. That surfaced intermittently in our style-
invalidation stress tests, depending on GC sweep order.

Cause: A “use” element connected to a document registers itself in the
document’s list of “use” elements and unregisters during its removal
steps. A GC’ed “use” element is swept without running those removal
steps — so it stays linked. When it’s destroyed before its document,
its list node is still linked — and the destructor aborts.

Fix: Override finalize() to unregister the “use” element before
destruction. The collector finalizes every dying cell before destroying
any of them. So, the node is always unlinked in time — the same approach
DocumentObserver and NavigationObserver already use.
2026-06-18 09:39:47 +02:00
Aliaksandr Kalenik
aa1926159e Services: Move GPU runtime access to Compositor
WebGL rasterization now happens in the Compositor process, but the GPU
runtime Landlock rules still lived only in WebContent. ANGLE initializes
native EGL when a page creates a WebGL context, which happens after the
Compositor sandbox is installed, so loading libEGL.so.1 and the
Mesa/GLVND driver stack was denied.

Grant the Compositor access to native GL/Vulkan driver and configuration
paths, DRI devices, /sys, LD_LIBRARY_PATH entries, the Mesa shader
cache, and executable mappings required by the driver stack.

Remove the corresponding late filesystem access from WebContent.
WebContent still initializes its Skia GPU backend before installing its
sandbox, so it does not need to open the GPU runtime afterward; keep the
GPU device seccomp allowance there because Skia continues to issue
operations on already-opened GPU fds for display-list painting.
2026-06-18 08:22:09 +02:00