Skip to content

Fine-grained child insert/remove invalidation based on ElementSelectorFlags - #802

Open
nicoburns wants to merge 10 commits into
mainfrom
devin/1787705871-fine-grained-child-invalidation
Open

Fine-grained child insert/remove invalidation based on ElementSelectorFlags#802
nicoburns wants to merge 10 commits into
mainfrom
devin/1787705871-fine-grained-child-invalidation

Conversation

@nicoburns

@nicoburns nicoburns commented Aug 26, 2026

Copy link
Copy Markdown
Member

Summary

Replaces the blanket parent RestyleHint::restyle_subtree() on every child insertion/removal (and the TODO: make this fine grained / conditional based on ElementSelectorFlags comments) with flag-driven invalidation mirroring Gecko's RestyleManager::RestyleForInsertOrChange / ContentAppended / ContentWillBeRemoved, and fixes two invalidation gaps the new tests exposed.

New BaseDocument::restyle_for_child_insert_or_remove(parent_id, change_idx) (called after the child list is updated, with the insertion index or the removed child's old index):

  • no relevant flags on the container → no restyles at all
  • HAS_EMPTY_SELECTORrestyle_for_empty_change: restyle the container's subtree, plus the container's later siblings when the grandparent has HAS_SLOW_SELECTOR_LATER_SIBLINGS (:empty + E / :empty ~ E), mirroring Gecko's RestyleForEmptyChange
  • HAS_SLOW_SELECTOR (:nth-last-child family) → single restyle_subtree() on the container, like Gecko's RestyleWholeContainer
  • HAS_SLOW_SELECTOR_LATER_SIBLINGS (:nth-child family) → restyle only element children at/after change_idx; a change at index 0 escalates to a whole-container restyle to avoid quadratic behavior when clearing a container from the start (Gecko's ContentWillBeRemoved optimization)
  • HAS_EDGE_CHILD_SELECTOR (:first/last/only-child) → restyle the first/last element children and the nearest element children on either side of the change (covers the previously-last child on append / previously-first on prepend, like Gecko's MaybeRestyleForEdgeChildChange)

Repeated mutations within one style flush are deduped: if the container already carries a pending restyle_subtree() hint, the helper early-outs (the equivalent of Gecko's mRestyledAsWholeContainer set — Blitz can use the hint itself since it lives on the node and is cleared by the traversal).

All DocumentMutator child-list mutation paths route through it (add_children_to_parent, remove_node, remove_and_drop_node_with, remove_and_drop_all_children), which also fixes a pre-existing gap where remove_node inserted damage but never posted a restyle hint, so removals could leave stale sibling styles. The blanket parent restyle in set_attribute is removed entirely: attribute changes can't shift sibling indices, and stylo's snapshot invalidation covers attribute-dependent selectors (matching Gecko, which does no container-level work for attribute changes).

Two further fixes found while verifying against Gecko:

  • Nodes moved within the document (old and new parent both in-document) now get restyle_subtree() themselves — previously masked by the blanket parent restyle, a moved node kept styles matched against its old ancestors (e.g. .red span).
  • :empty text handling: set_node_text / append_text_to_node now trigger the empty-change restyle when a child text node transitions empty↔non-empty (Gecko's CharacterDataChanged path), and is_empty now ignores zero-length text nodes and comments per spec instead of treating any child as significant.

Known conservative divergence from Gecko (correctness-preserving): the :empty path restyles unconditionally when the flag is set rather than checking was/is-empty first.

Includes a new child_invalidation.rs test suite (7 tests) covering :nth-child, :nth-last-child, :last-child, :empty (structural, sibling-combinator, and text-content cases) and moved-node restyling.

Extracted from #800 so it can land first; the :nth-child(An+B of S) work will be rebased on top.

WPT results

No changes in test results compared to main.

Generated by the WPT workflow.

Link to Devin session: https://dioxus.staging.devinenterprise.com/sessions/65287500bfb04b2589b9fc6a08bc2d14
Open in Devin Desktop: https://dioxus.staging.devinenterprise.com/desktop/session/65287500bfb04b2589b9fc6a08bc2d14?variant=devin-insiders
Requested by: @nicoburns

@staging-devin-ai-integration

Copy link
Copy Markdown
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR that start with 'DevinAI' or '@devin'.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant