Commit Graph

236 Commits

Author SHA1 Message Date
Andreas Kling
95eb41092c LibWeb: Move structural mutation invalidation into a helper
Node.cpp still contained selector-specific policy for sibling and
same-parent-move structural invalidation. Move that logic into
CSS::Invalidation::StructuralMutationInvalidator so DOM mutation code
can delegate structural selector dependency handling.

This is a behavior-preserving extraction. It keeps the existing
previous-sibling walk guard, sibling-distance checks, shadow-root
marking, and ancestor child-needs-style propagation.
2026-04-29 15:47:23 +02:00
Andreas Kling
e4e3c46837 LibWeb: Move :has() mutation scheduling into a helper
Node.cpp still contained the policy for deciding when a DOM mutation
should schedule pending :has() invalidation work. Move that into
CSS::Invalidation::HasMutationInvalidator, next to the mutation feature
collector it depends on.

This keeps DOM mutation code focused on reporting that a mutation
happened, while CSS invalidation code owns the selector-specific checks
for :has() metadata and sibling-combinator sensitivity.
2026-04-29 15:47:23 +02:00
Andreas Kling
ea64c5e147 LibWeb: Move :has() mutation checks into a helper
Node.cpp currently knows too much about selector invalidation metadata
when deciding whether subtree mutations can affect :has() selectors.
Pull that logic into CSS/Invalidation/HasMutationFeatureCollector so DOM
mutation code can ask a focused helper instead of inspecting
StyleInvalidationData directly.

This is a behavior-preserving extraction. It keeps the existing
conservative fallbacks for featureless subtree-sensitive selectors and
still uses the existing element property matching helper for
pseudo-class metadata.
2026-04-29 15:47:23 +02:00
Andreas Kling
e9ed125bbe LibWeb: Don't fan sibling structural invalidation into descendants
When an insert or remove flips a sibling's match for :first-child,
:last-child, :nth-child, :nth-of-type, or a + / ~ combinator, only the
sibling's own match changes. Descendants only need rechecking when a
selector ties them to the sibling's position.

invalidate_structurally_affected_siblings used to mark the affected
sibling's entire subtree, so a sibling with N descendants turned a
match-state flip into N+1 element recomputes. Reuse the per-element
flags introduced for the same-parent-move optimization: when neither
affected_by_structural_pseudo_class_in_non_subject_position nor
affected_by_sibling_combinator_in_non_subject_position is set, mark
only the sibling root, and let the existing inherited-update path
reach descendants if the sibling's own style changes.

Rebaseline the impacted tests. styleInvalidations gains one per
affected sibling because set_needs_style_update increments the counter
(entire-subtree marks did not), but elementStyleRecomputations falls
accordingly.
2026-04-29 04:53:14 +02:00
Andreas Kling
ce5d0bdfc7 LibWeb: Narrow :has descendant invalidation fanout
When a :has() mutation is known to come from a specific subtree, use
that subtree as the mutation root while walking observed ancestors.

Before dirtying an anchor and its non-subject descendants, check whether
any cached :has() rule for that anchor can observe the changed subtree.
This keeps unrelated descendant mutations from invalidating every rule
that merely contains :has().
2026-04-28 15:34:49 +02:00
Andreas Kling
356a369aa6 LibWeb: Avoid descendant recomputes for same-parent moves
Moving a node within the same parent changes sibling and positional
relationships, but it does not make every descendant of the moved node
need a fresh computed style. Handle this as a structural mutation at
the old and new sibling edges and dirty only the moved root and the
affected ancestors, instead of marking the entire moved subtree.

Factor the existing previous- and next-sibling structural invalidation
out of Node::invalidate_style() into invalidate_structurally_affected_-
siblings(), and pull the ancestor child-needs-style-update walk into
mark_ancestors_as_having_child_needing_style_update(). The new
invalidate_style_after_same_parent_move() reuses both helpers.

Whether the moved root itself needs its own style recomputed depends
on whether any selector matched against it (or against a descendant)
relied on its position in the sibling list. Track that via two new
sticky bits on Element, set during selector matching:

  - m_affected_by_structural_pseudo_class_in_non_subject_position
  - m_affected_by_sibling_combinator_in_non_subject_position

Both are write-once (sticky) because matching descendants can set them
while we're not currently re-matching this element's own selectors;
keeping them set is conservative and avoids stale descendant style.

When neither bit is set and the element only carries subject-position
positional/sibling/has() involvement, we just dirty the root and skip
its descendants.

Rebaseline same-parent-move-root-only and the structural-feature filter
counters to reflect the new path. Matching behavior is unchanged.
2026-04-28 15:34:49 +02:00
Andreas Kling
9fae2bcff9 LibWeb: Avoid unrelated structural :has invalidation
Track the simple selector features that appear inside :has() arguments
on each StyleScope, then consult that metadata before scheduling an
ancestor walk for a structural mutation. If the mutated subtree has no
tag, id, class, attribute, or pseudo-class feature that any cached
:has() argument cares about, skip the walk entirely.

Stay conservative for featureless-sensitive arguments such as :has(*),
:has(:not(...)), :has(:empty), and child-index pseudos: an unfeatured
node can still start or stop matching there. Track that case via a new
has_selectors_sensitive_to_featureless_subtree_changes flag on
StyleInvalidationData and fall back to the old conservative walk.

Stay conservative for pseudo-classes the subtree filter cannot probe
(:focus, :hover, validation pseudos). Move :default out of the set of
trackable feature pseudo-classes for the same reason; it now triggers
the conservative walk where it previously recorded metadata.

Tag and attribute names are stored lowercased, so for non-HTML elements
(SVG, MathML) treat lowercased matches as scheduling hints only; the
actual :has() match still goes through case-sensitive selector matching.

Test counter expectations are rebaselined to reflect the skipped walks
and reduced recomputations. Matching behavior is unchanged.
2026-04-28 15:34:49 +02:00
Tim Ledbetter
6bb037aec7 LibWeb: Skip backward sibling invalidation when no child needs it
We now track when a parent has a child affected by a backward structural
pseudo-class. These are selectors whose match result for an element can
depend on siblings after that element, such as `:last-child`,
`:only-child`, `:last-of-type`, `:only-of-type`, `:nth-last-child`, and
`:nth-last-of-type`.

When inserting or removing a node, previous siblings only need style
invalidation if one of them was matched against such a selector. Use the
parent-level flag to skip the previous-sibling walk when no child under
that parent can be affected.

This saves a lot of invalidation work on sites that insert a lot of
nodes into the DOM via JS.
2026-04-26 16:14:43 +02:00
Tim Ledbetter
08dd93a8e0 LibWeb: Count previous-sibling visits during style invalidation
Add a `previousSiblingInvalidationWalkVisits` counter that increments
once per element examined during the previous-sibling walk in
`invalidate_style` on `NodeInsertBefore` and `NodeRemove`. This can be
expensive and the next commit introduces an optimization that prevents
this work being done unnecessarily
2026-04-26 16:14:43 +02:00
Andreas Kling
89e5deb7b9 LibWeb: Centralize style scope lookup for invalidation
Several invalidation paths need to consider not only a node's own root
scope, but also shadow scopes that can observe the node through :host(),
::slotted(), or ::part() selectors. Each caller open-coded that
traversal, which made the dir/lang and dir=auto fixes carry the same
shadow-boundary logic in multiple places.

Add Node helpers for resolving a node's style scope and for visiting
every style scope that may observe that node. Use them from the
property, child-list, dir/lang, and dir=auto invalidation paths, and
share the same style-scope lookup with DOM::AbstractElement and
Layout::NodeWithStyle.
2026-04-26 10:40:58 +02:00
Andreas Kling
3cbe6bc89a LibWeb: Schedule attribute :has() walks on outer scopes too
The property-based Node::invalidate_style only added inner shadow
scopes (the element's own shadow and enclosing hosts' shadows). When
the mutated element lived inside a shadow tree, mutations could not
reach ::part(...:has(...)) rules in the outer document or outer
shadow root because those scopes were never collected.

Walk parent_or_shadow_host and add the document scope (or each
crossed shadow root scope) when the ancestor's root differs from
this element's root, alongside the inner shadow scopes already
collected.
2026-04-26 10:40:58 +02:00
Andreas Kling
60113a3be2 LibWeb: Schedule attribute :has() walks on enclosing shadow scopes
The property-based Node::invalidate_style only collected the element's
own shadow scope (when the element itself was a shadow host), not the
shadow scopes of enclosing hosts. So a class or attribute change on a
light-DOM descendant of a shadow host could not flip
:host(...:has(.descendant)) rules in that host's shadow root.

Walk parent_or_shadow_host from the mutated element and add every
enclosing shadow root's StyleScope to the set we check :has()
metadata against, schedule walks on, and run invalidation plans
through.
2026-04-26 10:40:58 +02:00
Andreas Kling
bc4ff9038a LibWeb: Schedule :has() walks on enclosing shadow scopes for inserts
Light-DOM mutations under a shadow host can flip
:host(...:has(...)) rules that live entirely in the host's shadow
root. Node::invalidate_style only scheduled the :has() walk on the
root scope of the mutated node, so those shadow-side rules never
re-evaluated when their light-DOM input changed.

Walk up parent_or_shadow_host from the mutation parent and schedule
the :has() walk on every enclosing shadow root's StyleScope that has
:has() rules.
2026-04-26 10:40:58 +02:00
Andreas Kling
b8573eba6f LibWeb: Use document scope for invalidation in user-agent shadow trees
SVG <use> shadow trees opt into the host document's stylesheets via
ShadowRoot::uses_document_style_sheets. Style computation already
honors this in AbstractElement::style_scope(), but
Node::invalidate_style picked the shadow root's own StyleScope based
solely on root().is_shadow_root(), so mutations inside such trees
never consulted document :has() metadata or invalidation plans.

Match the existing style-computation scope selection so invalidation
runs against the document StyleScope when the root is a shadow root
that uses document stylesheets.
2026-04-26 10:40:58 +02:00
Andreas Kling
5268b5a5b7 LibWeb: Run :has() invalidation when character data is inserted
Node::invalidate_style was bailing out for character data nodes before
scheduling :has() ancestor invalidation. Inserting or removing a text
node changes the parent's :empty state and can flip ancestor
:has(:empty) selectors, but with the early return in place those
ancestors were never marked for re-evaluation.

Move the early return below the :has() walk so character data still
participates in ancestor :has() invalidation. The text node itself
still has no style of its own, so the rest of the per-node work is
skipped as before.
2026-04-26 10:40:58 +02:00
Andreas Kling
73aafc2ade LibWeb: Expose full-style-invalidation counter through Internals
Full-subtree style invalidations are cheap to count where they happen
(Node::invalidate_style on a document node) but previously invisible to
tests. Surface the counter as fullStyleInvalidations alongside the
existing style invalidation counters so text tests can assert that a
given mutation path stayed surgical instead of degrading to a broad
document restyle.
2026-04-23 16:45:22 +02:00
Andreas Kling
e29281893a LibWeb: Preserve pending style-update flags across document adoption
Adopting a node into another document preserves the node's dirty style
flags, but the destination ancestor chain never sees them propagate. If
a style update is already pending in the new document, it can skip the
adopted subtree entirely.

Snapshot the subtree and child dirty bits before set_document() updates
m_document, then walk the new ancestor chain and re-mark
child_needs_style_update so the pending restyle still descends into the
adopted subtree.
2026-04-23 16:45:22 +02:00
Sam Atkins
6d02296eb5 LibWeb: Pass better information to node moving/removing steps
Corresponds to:
73de9e5e1b
097be9feaa
2026-04-22 14:05:49 +01:00
Shannon Booth
fd44da6829 LibWeb/Bindings: Emit one bindings header and cpp per IDL
Previously, the LibWeb bindings generator would output multiple per
interface files like Prototype/Constructor/Namespace/GlobalMixin
depending on the contents of that IDL file.

This complicates the build system as it means that it does not know
what files will be generated without knowledge of the contents of that
IDL file.

Instead, for each IDL file only generate a single Bindings/<IDLFile>.h
and Bindings/<IDLFile>.cpp.
2026-04-21 07:36:13 +02:00
Andreas Kling
85ff13870f LibWeb: Stop :has() invalidation walk when out of :has() scope
A DOM mutation under a document that uses any :has() rule currently
walks every ancestor up to the root, invoking invalidate_style_if_
affected_by_has() on each. Most of those ancestors have nothing to
do with :has(), so the work scales linearly with DOM depth.

Introduce an in_has_scope flag on Element, set while evaluating :has()
arguments for invalidation metadata. StyleScope's upward invalidation
walk now terminates at the first element that is neither in :has()
scope nor a :has() anchor, so it only traverses the region where some
:has() rule might actually care about the change.

Keep the existing fast :has() matching paths for normal selector
matching, but bypass them while collecting per-element metadata so the
scope markers still get populated. Node insertion also schedules the
parent for the :has() walk so newly inserted nodes still reach the real
anchor.

The css-has-invalidation suite adds focused coverage for these shapes
and updates the expected counters to reflect the shorter walks.
2026-04-20 13:20:41 +02:00
Andreas Kling
e1d62eaf85 LibWeb: Bucket :has() invalidation metadata by feature
Record per-feature :has() invalidation metadata instead of only tracking
whether some selector somewhere mentions a class, id, attribute, tag,
or pseudo-class. The new buckets preserve the relative selector and a
coarse scope classification for each :has() argument, which gives the
next invalidation step enough information to route mutations more
precisely.

Keep this commit behavior-preserving for mutation handling by only
switching the lookup path over to the new metadata buckets. Expose a
test-only counter for the number of candidate :has() metadata entries a
mutation matched, and add coverage showing that one feature can map to
one or multiple :has() buckets without forcing a document-wide yes/no
answer.
2026-04-20 13:20:41 +02:00
Andreas Kling
a72fae8d36 LibWeb: Add test-only counters for :has() invalidation work
Introduce a small set of counters on Document that track the work done
while processing :has() invalidation: how often the upward walk runs,
how many elements it visits, how often matches_has_pseudo_class() is
invoked, how well the per-pass result cache performs, and how many
elements transition from clean to needs-style-update.

Expose the counters through internals so tests can assert precise bounds
on the invalidation work triggered by a mutation, which regular
reference tests cannot express.

Add a css-has-invalidation test suite that covers subject-position,
non-subject-position, sibling-combinator, and no-:has() cases. The
baseline tests share a helper script so later coverage can reuse the
same counter-printing path.

The counters are test-only observation; they do not affect style
computation itself.
2026-04-20 13:20:41 +02:00
Andreas Kling
e330d5b9ab LibWeb: Make Node::is_connected() O(1) via a cached flag
Previously this walked up the parent chain on every call, which shows
up as a 2.5% item in the profile while watching YouTube videos.

Cache an m_is_connected bit on Node instead, maintained by the DOM
insertion and removal steps.
2026-04-16 08:26:34 +02:00
Callum Law
2313460ab6 LibWeb: Make null content on ComputedValues more explicit
In a later commit we can use the null state for an optimization.
2026-04-15 11:07:38 +01:00
Andreas Kling
0f4575e7d0 LibWeb: Clear stale layout state for inactive documents
IntersectionObserver can keep elements from a navigated iframe's old
document alive until a later rendering update. Once that document tears
down its layout tree, descendant nodes and pseudo-elements can still
retain stale layout and paintable pointers, and destruction can bypass
the usual inactive-document teardown entirely.

Clear per-node layout and paintable pointers across the inactive
document subtree before tearing down the layout tree, and do the same
from destroy() for documents that never go through
did_stop_being_active_document_in_navigable().

Add a crash test that observes an iframe target, navigates the iframe,
and waits for rendering updates without touching stale layout state.

Fixes #8670
2026-04-11 16:03:26 +02:00
Shannon Booth
bb0f244667 LibWeb: Remove ShadowRealm HTML integration 2026-04-05 13:57:58 +02:00
Aliaksandr Kalenik
4985dabf3d LibWeb: Replace cached navigable with Navigable-maintained back-pointer
Now that Navigable directly owns its active document (m_active_document)
we can have Navigable maintain a back-pointer on Document instead of
using the old cache-with-validation pattern that fell back to a linear
scan of all navigables via navigable_with_active_document().
2026-04-01 11:51:43 +02:00
Sam Atkins
ed6a5f25a0 LibWeb: Implement scoped custom element registries 2026-03-27 19:49:55 +00:00
Sam Atkins
ecbd846272 LibWeb/DOM: Implement DocumentOrShadowRoot.customElementRegistry
Step towards scoped custom element registries.
2026-03-27 19:49:55 +00:00
Andreas Kling
d7bf9d3898 LibRegex: Remove the legacy C++ ECMA-262 engine
Delete the old C++ ECMA-262 parser, optimizer, and matcher now that all
in-tree users compile and execute through `ECMAScriptRegex`.

Stop building the legacy engine, remove its source files and the
POSIX-only fuzzers that depended on it, and update the remaining
LibRegex tests to target the Rust-backed facade instead of the deleted
implementation. Clean up the last includes, comments, and helper paths
that only existed to support the old backend.

After this commit LibRegex has a single ECMAScript engine in-tree,
eliminating duplicated maintenance and unifying future regex work.
2026-03-27 17:32:19 +01:00
Shannon Booth
cc6536b527 LibWeb/HTML: Always provide ChildrenChangedMetadata to children changed 2026-03-19 09:46:54 +01:00
Zaggy1024
1236565afd LibWeb: Include shadow host when checking for node event listeners
Fixes a regression in 57c9bb5 where the media controls would not show
when moving the cursor over the video overlay element.
2026-03-12 03:11:02 +01:00
Jelle Raaijmakers
57c9bb5caa LibWeb: Skip event dispatching work if there are no relevant listeners
We were going through path building, retargeting, capturing/bubbling
phases etc. for each event that we were dispatching. We now optimize for
the case where there are no listeners for the event type on the target
node nor its ancestors by skipping all that work.

This decimates methods that depend on event dispatching like
`Document::set_hovered_node()` when there are no relevant listeners,
which reduced its runtime by 99% (42.5ms -> 0.5ms) on my machine for
sites like https://wordsalad.online/.
2026-03-10 16:57:36 +01:00
Tim Ledbetter
fd4c5b53eb LibWeb: Invalidate paint cache for all layout node paintables on repaint 2026-03-10 15:54:44 +01:00
Jelle Raaijmakers
f61528238e LibWeb: Replace is<T> + as<T> with as_if<T>
Doing so results in a single fast_if<T> or dynamic_cast<T> call instead
of two. No functional changes.
2026-03-10 15:17:51 +01:00
Aliaksandr Kalenik
9df1372452 LibWeb: Implement sibling invalidation sets
Replace flat InvalidationSet with recursive InvalidationPlan trees
that preserve selector combinator structure. Previously, selectors
with sibling combinators (+ and ~) fell back to whole-subtree
invalidation. Now the StyleInvalidator walks the DOM following
combinator-specific rules, so ".a + .b" only invalidates the
adjacent sibling matching ".b" rather than the entire subtree.

Plans are compiled at stylesheet parse time by walking selector
compounds right-to-left. For ".a .b + .c":
```
  [.c]: plan = { invalidate_self }
        register: "c" → plan

  [.b]: wrap("+", righthand)
        plan = { sibling_rules: [match ".c", adjacent, {self}] }
        register: "b" → plan

  [.a]: wrap(" ", righthand)
        plan = { descendant_rules: [match ".b", <sibling plan>] }
        register: "a" → plan
```

Changing class "a" produces a plan that walks descendants for ".b",
checks ".b"'s adjacent sibling for ".c", and invalidates only that
element.
2026-03-09 18:35:46 +01:00
Aliaksandr Kalenik
8170926d4f LibWeb: Tighten structural invalidation for sibling changes
Use the directional structural metadata when invalidating siblings for
NodeInsertBefore and NodeRemove.

This keeps move invalidation scoped to the moved node's local
neighborhood instead of invalidating the entire old and new parents, and
makes structural invalidation honor bounded direct-sibling distances for
`+` combinators.
2026-03-07 00:34:00 +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
Jelle Raaijmakers
d9b1c2df9f LibWeb: Skip unnecessary style invalidation for :has() selectors
There are a couple of style invalidation reasons that could not possibly
change the outcome of a `:has()` selector. This prevents the style
invalidation for `:has()` caused by font loads on youtube.com from
showing up in profiles.
2026-03-04 18:37:57 +01:00
Andreas Kling
5fe1a70c8e LibWeb: Add VERIFY checks to layout_node(), paintable(), paintable_box()
Add VERIFY(document().layout_is_up_to_date()) assertions to the safe
layout_node() and paintable() accessors on DOM::Node. These catch cases
where code accesses layout or paint data without first ensuring layout
is up to date.

Code that legitimately needs to access layout/paintable data when layout
is stale (e.g. during tree construction, style recalculation, painting,
or invalidation propagation) should use the unsafe_layout_node(),
unsafe_paintable(), or unsafe_paintable_box() accessors instead.
2026-02-26 21:09:08 +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
Andreas Kling
5fd608b7cd LibWeb: Cache editability flag on DOM nodes for O(1) lookups
Add a cached m_in_editable_subtree flag to Node, updated on tree
mutations and contenteditable/designMode changes.

This replaces the recursive parent walk in is_editable() and
is_editing_host() with an O(1) flag check. The flag is recomputed
in inserted(), moved_from(), and cleared in removed_from(). Subtree
walks recompute the flag when contenteditable attributes change or
design mode is toggled.

This was 4% of CPU time while playing a YouTube video.
2026-02-21 03:51: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
Sam Atkins
ce13ab733d LibWeb/DOM: Walk the flat tree to propagate layout tree updates
This fixes an issue where we wouldn't propagate the layout update from a
slotted node to its slot.
2026-02-05 11:21:08 +01:00
Sam Atkins
bccf388110 LibWeb/DOM: Implement "flat tree parent" getter for DOM Node
Various parts of the spec ask us to walk the flat tree. In most cases
that's the same as getting its parent or shadow host, but `<slot>` in
particular breaks this rule, as a slotted element's flat tree parent is
the slot.
2026-02-05 11:21:08 +01:00
Tim Ledbetter
18b8ba1fd3 LibWeb: Avoid subtree iteration when assigning slottables
This change introduces SlotRegistry to track slot elements per shadow
root. This allows us to iterate slots directly when assigning
slottables for a tree instead of walking an entire subtree.
2026-01-31 11:44:20 +01:00
Jonathan Gamble
116ccdd878 LibWeb: Add find_in_shadow_including_ancestry method to Node
The search walks self and ancestors until the predicate returns true.
2026-01-09 18:09:09 +01:00
Sam Atkins
bcc8e6ca60 LibWeb/DOM: Clarify attribute-list comparison
Corresponds to:
7b84f10e47
2026-01-09 14:35:21 +00:00