Commit graph

310 commits

Author SHA1 Message Date
Aliaksandr Kalenik
d17b7fe70d LibWeb: Track structural invalidation dependencies by direction
Split the structural-change selector metadata into directional bits for
first/last-child and forward/backward positional selectors.

This gives sibling invalidation enough information to distinguish which
side of a mutation can affect an element, instead of treating all
structural selectors as bidirectional.
2026-03-07 00:34:00 +01:00
Aliaksandr Kalenik
8f7bb7dd2e LibWeb: Skip layout update for disconnected elements querying metrics
Introduce Document::update_layout_if_needed_for_node() which only calls
update_layout() when the node is connected. Use it at all call sites
that query layout metrics (offsets, client dimensions, image size, SVG
bounding box, etc.) so disconnected elements no longer trigger an
unnecessary layout.
2026-03-05 14:17:20 +01:00
Aliaksandr Kalenik
d49809cba3 LibWeb: Remove paint-only properties resolution phase
With per-paintable display list command caching now in place, the
separate paint-only properties resolution phase is no longer needed.
Resolution now happens inline during painting and its cost is amortized
since it only runs on cache miss.

Move all property resolution to point of consumption:
- is_visible() and visible_for_hit_testing() compute on the fly
- Filter resolution moved to assign_accumulated_visual_contexts()
- Border radii, outlines computed on access
- Box shadows, backdrop filter resolved inline during painting
- Background resolution moved into paint_background()
- Mask resolution moved to StackingContext::paint()
- Text fragment and SVG stroke properties resolved during painting
2026-03-04 19:35:45 +01:00
Aliaksandr Kalenik
eae94a8a46 LibWeb: Route repaint requests through paintables, not Document
Rename Document::set_needs_display() to set_needs_repaint() and make it
private. External callers must now go through Node/Paintable which
route the request to the document internally.

Fix one existing misuse in AnimationEffect that was calling
document-level set_needs_display() instead of routing through the
target element's paintable.

This is preparation for per-paintable display list command caching:
repaint requests must go through specific paintables so their cached
command lists can be invalidated.
2026-03-04 19:35:45 +01:00
Timothy Flynn
aabf7ae8fb LibWeb: Skip fullscreen transient activation requests for WebDriver
A transient activation is rarely going to exist when invoked from
WebDriver.
2026-03-02 15:49:13 -05:00
Timothy Flynn
2282636f98 LibWeb: Clean up the element fullscreen APIs a bit
* Don't publicly expose methods that are only used internally.

* Modernize comment style (wrap at 120 chars, use NB for our notes).

* Change the stringifier for RequestFullscreenError to return a UTF-16
  string. We were allocating a String, then transcoding it to UTF-16.

* Give helper methods clearer names, e.g.:

  fullscreen_has_error_check -> is_element_allowed_to_enter_fullscreen
2026-03-01 15:41:43 -06:00
Jelle Raaijmakers
499ec26f97 LibWeb: Remove CSS pixel offsets from ScrollState
This was arguably put in a worse place by #8162; we mostly need the
device pixel offsets from the scroll state so keep track of those and
convert back to CSS pixels when necessary (i.e. scrollbar data).
2026-02-27 08:29:29 +01:00
Andreas Kling
a146225331 LibWeb: Use unsafe layout/paintable accessors where appropriate
Add unsafe_layout_node(), unsafe_paintable(), and unsafe_paintable_box()
accessors that skip layout-staleness verification. These are for use in
contexts where accessing layout/paintable data is legitimate despite
layout not being up to date: tree construction, style recalculation,
painting, animation interpolation, DOM mutation, and invalidation
propagation.

Also add wrapper APIs on Node to centralize common patterns:
- set_needs_display() wraps if (unsafe_paintable()) ...set_needs_display
- set_needs_paint_only_properties_update() wraps similar
- set_needs_layout_update() wraps if (unsafe_layout_node()) ...

And add Document::layout_is_up_to_date() which checks whether layout
tree update flags are all clear.
2026-02-26 21:09:08 +01:00
Jelle Raaijmakers
90a211bf47 LibWeb: Use device-pixel coordinates in display list and AVC
Stop converting between CSS and device pixels as part of rendering - the
display list should be as simple as possible, so convert to DevicePixels
once when constructing the display list.
2026-02-26 07:43:00 +01:00
Simon Farre
4396e5b8d0 LibWeb: Unfullscreen when removing an element or unloading a document 2026-02-23 18:44:26 +00:00
Simon Farre
bc17805b2b LibWeb: Implement requestFullscreen algorithm
The required functionality to exit fullscreen will be in a followup
commit.
2026-02-23 18:44:26 +00:00
Simon Farre
db076bab92 LibWeb: Add Fullscreen event handlers to Document and Element
This also adds a stub to the Permissions Policy checks.
2026-02-23 18:44:26 +00:00
Zaggy1024
bacf689c88 LibWeb: Update the layout tree when setting shadow roots 2026-02-23 07:27:31 +01:00
Andreas Kling
3a2de5374c LibWeb: Don't require layout to determine clientLeft and clientTop
These properties only return the computed border width, which is a
style-level value that doesn't depend on layout geometry. Replace
the full update_layout() call with update_style_if_needed_for_element()
and resolve border widths directly from computed properties.

This avoids potentially expensive synchronous layout when only
CSS computed values are needed.

Very profitable on https://x.com/ where we avoid lots of layout work.
2026-02-22 13:24:05 +01:00
Andreas Kling
675a7d4c0c LibWeb: Remove redundant update_style() in is_potentially_scrollable()
update_layout() already calls update_style() as its first step, so
the follow-up update_style() call was always a no-op.
2026-02-22 13:24:05 +01:00
Aliaksandr Kalenik
7cc973fa77 LibWeb: Lazify ElementByIdMap resolution and cache first element
Eliminate O(n) tree-order sorted insert on every add() by simply
appending elements and deferring order resolution to lookup time. Cache
the first-in-tree-order element in get() so repeated getElementById()
calls avoid repeated subtree traversal.

Improves performance on YouTube where add() was hot in profiles while
scrolling through comments.
2026-02-21 13:56:00 +01:00
Callum Law
b8f2989ccb LibWeb: Reduce recompilation from editing CascadedProperties.h
This reduces the recompilation of editing `Properties.json` from ~1429
to ~158
2026-02-19 11:27:06 +00:00
Andreas Kling
2b6e6e4ea2 LibWeb: Fix crash in style inheritance for pseudo-element slots
Elements in internal shadow trees that represent CSS pseudo-elements
(e.g. the DetailsContent slot in <details>) store their cascaded
properties on the host element's pseudo-element data, not on the
element itself. This meant that when slotted elements walked the
inheritance chain and encountered such a slot, they would dereference
null cascaded properties and crash.

Fix this by copying the cascaded properties onto the slot element
itself after computing its style, keeping both cascaded and computed
properties accessible in the same place.
2026-02-14 14:36:21 -05:00
Callum Law
32da7edf5e LibWeb: Compute font properties the same as other properties
Previously we computed font properties separately from other properties
for two reasons:
  1) These font properties were computed using a different length
     resolution context than the rest of the properties.
  2) These properties were required to be computed before creating the
     length resolution context for the rest of the properties.

The first issue was solved in the previous commit by introducing a
generic method to get the computation context for a property, and
the second is solved in this commit by computing properties in the
required order.

This simplifies the code a bit and opens up some opportunities for
optimization.
2026-02-13 21:54:06 +01:00
Andreas Kling
9e8e568b43 LibWeb: Use structural sharing for CSS custom properties
Replace per-element OrderedHashMap storage for custom properties with
a RefCounted chain (CustomPropertyData) that enables structural
sharing. Each chain node stores only the properties declared directly
on its element, with a parent pointer to the inherited chain.

Elements that don't override any custom properties share the parent's
data directly (just a RefPtr copy). During cascade, only entries that
actually differ from the parent are stored in own_values - the rest
are inherited through the chain. During var() resolution, resolved
values are compared against the parent's and matching entries are
dropped, enabling further sharing.

The chain uses a depth limit (max 32) with flattening, plus
absorption of small parent nodes (threshold 8) to keep lookups fast.

This reduces custom property memory from ~79 MB to ~5.7 MB on
cloudflare.com.
2026-02-13 14:57:15 +01:00
Andreas Kling
fb11732526 LibWeb: Fix style inheritance for slotted elements
Two issues prevented slotted elements from correctly inheriting
styles from their assigned slot:

1. Element::element_to_inherit_style_from() was skipping the slot
   element and returning the shadow host instead. This meant slotted
   elements inherited from the host, completely ignoring any styles
   on the slot itself.

2. When a slot element's style changed during the style tree walk,
   its assigned (slotted) nodes were never marked for recomputation.
   The tree walk follows the DOM tree, but slotted elements are DOM
   children of the shadow host, not the slot, so they were missed.

Fix (1) by returning the slot directly as the inheritance parent.
Fix (2) by marking assigned nodes dirty in update_style_recursively
when a slot's style changes.
2026-02-13 10:22:30 +01:00
Aliaksandr Kalenik
fde2015846 LibWeb: Reduce recompilation impact of DOM/Element.h
Remove unused/redundant includes from Element.h:
- AK/IterationDecision.h (redundant)
- ARIA/AttributeNames.h (redundant via ARIAMixin.h)
- CSS/CascadedProperties.h (redundant via PseudoElement.h)
- CSS/StylePropertyMapReadOnly.h (pointer types only)
- HTML/LazyLoadingElement.h (unused in header)

Extract IntersectionObserverRegistration struct from
IntersectionObserver.h into its own lightweight header.
This breaks the heavy transitive include chain through
IntersectionObserverEntry.h and Geometry/DOMRect.h that
was pulled into every file including Element.h.

Indirect recompilation impact reductions:
- IntersectionObserver.h: ~1387 -> ~27 files
- LazyLoadingElement.h: ~1387 -> ~1002 files
2026-02-11 20:02:28 +01:00
Aliaksandr Kalenik
30e4779acb AK+LibWeb: Reduce recompilation impact of DOM/Node.h
Remove includes from Node.h that are only needed for forward
declarations (AccessibilityTreeNode.h, XMLSerializer.h,
JsonObjectSerializer.h). Extract StyleInvalidationReason and
FragmentSerializationMode enums into standalone lightweight
headers so downstream headers (CSSStyleSheet.h, CSSStyleProperties.h,
HTMLParser.h) can include just the enum they need instead of all of
Node.h. Replace Node.h with forward declarations in headers that only
use Node by pointer/reference.

This breaks the circular dependency between Node.h and
AccessibilityTreeNode.h, reducing AccessibilityTreeNode.h's
recompilation footprint from ~1399 to ~25 files.
2026-02-11 20:02:28 +01:00
Aliaksandr Kalenik
901cc28272 LibWeb: Reduce recompilation impact of DOM/Document.h
Remove 11 heavy includes from Document.h that were only needed for
pointer/reference types (already forward-declared in Forward.h), and
extract the nested ViewportClient interface to a standalone header.

This reduces Document.h's recompilation cascade from ~1228 files to
~717 files (42% reduction). Headers like BrowsingContext.h that were
previously transitively included see even larger improvements (from
~1228 down to ~73 dependents).
2026-02-11 20:02:28 +01:00
Praise-Garfield
ebd312689e LibWeb: Support :placeholder-shown pseudo-class for textarea elements
Previously only input elements were matched. Add placeholder_value()
to HTMLTextAreaElement mirroring the HTMLInputElement API and update
both selector matching code paths to handle textarea.
2026-02-11 16:11:11 +01:00
Jelle Raaijmakers
87ada9e887 LibWeb: Do not mark ScrollHandled as [[nodiscard]]
There's only one place where we don't `(void)` the result of these
methods, so let's not be too pedantic about it.
2026-02-11 11:04:53 +01:00
Callum Law
379db7a42c LibWeb: Support animation-timeline scroll() value 2026-02-11 10:49:34 +01:00
Callum Law
2af57d6cb6 LibWeb: Store CSS defined animations as CSSAnimation
In a later commit we will be calling `CSSAnimation` specific methods on
these and this saves us casting to a `CSSAnimation` every time
2026-02-11 10:49:34 +01:00
Aliaksandr Kalenik
40429292fe LibWeb: Forward-declare RequiredInvalidationAfterStyleChange in Element
Replace the direct #include of StyleInvalidation.h in Element.h with a
forward declaration in Forward.h. Element.h only uses the type in
function declarations, so the complete type is not needed.

This reduces the recompilation impact of modifying StyleInvalidation.h
from ~1380 files to ~4 files, since Element.h is transitively included
by nearly every HTML and SVG element header.
2026-02-11 06:52:11 +01:00
Jelle Raaijmakers
7714471aae LibWeb: Recompute style for ::selection pseudo element 2026-02-06 10:47:50 +00:00
Callum Law
665feb57ae LibWeb: Use computed values in Element::is_potentially_scrollable
`Layout::NodeWithStyle::computed_values()` actually holds used values
which may not be the same as computed values e.g. if they have been
modified by `Document::propagate_overflow_to_viewport()`
2026-02-05 16:45:34 +01:00
Sam Atkins
78fcc7ed72 LibWeb/DOM: Add FIXME for flat tree descendant check 2026-02-05 11:21:08 +01:00
Sam Atkins
bd753eafb6 LibWeb/DOM: Walk flat tree in Element::check_visibility() 2026-02-05 11:21:08 +01:00
Andreas Kling
5f434a442a LibWeb: Use targeted style invalidation when adding a new stylesheet
Instead of doing a full document style invalidation when a stylesheet is
dynamically added, we now analyze the new sheet's selectors to determine
which elements could potentially be affected, and only invalidate those.

This works by building an InvalidationSet from the rightmost compound
selector (the "subject") of each rule in the new stylesheet, extracting
class, ID, tag name, attribute, and pseudo-class features. We then walk
the DOM tree and only mark elements matching those features as needing a
style update.

If any selector has a rightmost compound that is purely universal (no
identifying features), or uses a pseudo-class not supported by the
invalidation set matching logic, we fall back to full invalidation.
2026-02-02 21:08:30 +01:00
Aliaksandr Kalenik
0fcd8c0bfa LibWeb: Fix pseudo-element scroll offset leaking to generating element
In Element::set_scroll_offset(), when setting a pseudo-element's
scroll offset, the code was also incorrectly setting the generating
element's own m_scroll_offset. Added an else branch so only the
pseudo-element's offset is set.

Also adds a ref test for scrollable pseudo-elements to prevent
regression. The test scrolls a ::before pseudo-element via wheel
event and verifies the content scrolls correctly.
2026-01-27 20:32:12 +01:00
Luke Wilde
babfd70ca7 LibGC: Enforce that a Cell type must declare the allocator to use
This ensures that we are explicitly declaring the allocator to use when
allocating a cell(-inheriting) type, instead of silently falling back
to size-based allocation.

Since this is done in allocate_cell, this will only be detected for
types that are actively being allocated. However, since that means
they're _not_ being allocated, that means it's safe to not declare
an allocator to use for those. For example, the base TypedArray<T>,
which is never directly allocated and only the defined specializations
are ever allocated.
2026-01-20 12:00:11 +01:00
Aliaksandr Kalenik
23a74ddc2a LibWeb: Use AccumulatedVisualContext in Element::get_client_rects()
The previous implementation had a bug: it composed all ancestor
transforms but applied them around only the innermost element's
transform origin. The correct behavior is to apply each transform
around its own origin.

AccumulatedVisualContext already tracks all visual transformations
(transforms, scroll offsets, perspective) correctly for hit testing.
This change adds a new transform_rect_to_viewport() method that performs
the forward transformation (element coordinates to viewport
coordinates), which is the inverse direction of
transform_point_for_hit_test().

This fixes getBoundingClientRect() returning incorrect coordinates for
elements inside transformed ancestors with non-default
transform-origins.
2026-01-19 08:53:30 +01:00
Aliaksandr Kalenik
0ab35eee12 LibWeb: Fix spurious relayout triggers in font list comparison
Previously, compute_required_invalidation() compared font lists using
cached_computed_font_list(), which returns the lazily-cached value.
Since newly computed styles haven't had their font list computed yet,
this compared a non-null cached value (old style) against null (new
style), causing unnecessary relayout even when fonts hadn't changed.

Fix by using computed_font_list() to ensure both styles have their
font lists computed before comparison.
2026-01-19 04:01:37 +01:00
Sam Atkins
692760b109 LibWeb/CSS: Update links to css-scoping and css-shadow-parts specs
These have been merged together into a new "CSS Shadow Module" spec. No
behaviour changes.

Corresponds to:
80d140567a
2026-01-13 16:18:11 +01:00
Psychpsyo
fe2bc2bfe7 LibWeb: Improve scrollingElement handling
This change is currently entirely undetectable because of what the
added FIXME talks about. Currently, the HTML element's overflow is
always set to visible in both axes, so it getting set to "clip" in
the imported test ends up not mattering at all.
2026-01-13 11:47:13 +00:00
Tim Ledbetter
a34c51a35e LibWeb: Only use base element target if no target attribute is present
This aligns step 2 of `get_an_elements_target()` with the specification.
2026-01-13 10:05:40 +01:00
Tim Ledbetter
94fa08dcfe LibWeb/SVG: Implement activation behavior for SVGAElement
This makes navigation work when clicking on SVG `<a>` elements.
2026-01-13 10:05:40 +01:00
Tim Ledbetter
ba7b0c60f0 LibWeb: Move hyperlink navigation methods to DOM::Element
This allows us to use these methods from `SVGAElement` without
inheriting  `HTMLHyperlinkElementUtils`, which we can't do for
`SVGAElement` due to a naming conflict with the `href()` method in
`SVGURIReferenceMixin`.
2026-01-13 10:05:40 +01:00
Sam Atkins
d84a0d411c LibWeb/HTML: Return Promises from Element scroll methods
This re-applies 3a7fcde341 which was
reverted in cacadc8806. The issues it had
were bugs in the previous commit and have been resolved.
2026-01-08 14:50:09 +00:00
Sam Atkins
0a57e1e8ac LibWeb: Clarify some scrolling-related code
A lot of our scrolling code is quite old, and doesn't match the spec,
but does use some similar names. This is quite confusing. In particular
`perform_scroll_of_viewport()` is not the same as the spec algorithm.
That algorithm is actually almost implemented in
`scroll_viewport_by_delta()`.

To clarify things, this commit makes a few changes:
- Rename perform_scroll_of_viewport() to
  perform_scroll_of_viewport_scrolling_box(). This is a better match
  for how we use this method, even if it's not actually a match for the
  algorithm. (:yakbait:)
- Move `scroll_viewport_by_delta()`'s code into a new
  `perform_a_scroll_of_the_viewport()` method, and make it take a
  position like it should. `scroll_viewport_by_delta()` now calls it
  with a calculated position.

I've avoided reusing the original `perform_scroll_of_viewport()` name to
avoid accidents.
2026-01-08 14:50:09 +00:00
Tim Ledbetter
cacadc8806 Revert "LibWeb/HTML: Return Promises from Element scroll methods"
This reverts commit 3a7fcde341.
2025-12-26 19:33:51 +01:00
Callum Law
f235625670 LibWeb: Don't disassociate animations from timeline when target orphaned
An animation with an orphaned owning element should continue to be
ticked by the timeline.

Reverts c8b574e and instead avoids leaking animations by not visiting
`Animation`s from `AnimationTimeline`s.

Fixes a timeout in the imported test
2025-12-23 14:54:22 +01:00
Sam Atkins
3a7fcde341 LibWeb/HTML: Return Promises from Element scroll methods
Corresponds to part of:
c548a9a1d4
2025-12-23 14:24:28 +01:00
Sam Atkins
bf57b18b9a LibWeb/DOM: Expose Element's parts list
The existing part_list() method used by the bindings lazily creates a
DOMTokenList, which we don't want to do just to check if an Element has
any parts defined.
2025-12-15 14:12:39 +00:00
Sam Atkins
30a5f84a07 LibWeb/DOM: Use "attribute" validation context for attribute change
Corresponds to:
91f461145c
2025-12-11 22:54:39 +00:00