Skip to content

Adopt node before removing child in replace#1488

Merged
annevk merged 2 commits into
mainfrom
replace-adopt-before-remove
Jul 9, 2026
Merged

Adopt node before removing child in replace#1488
annevk merged 2 commits into
mainfrom
replace-adopt-before-remove

Conversation

@annevk

@annevk annevk commented Jul 8, 2026

Copy link
Copy Markdown
Member

replace removes child (with suppressObservers set) before inserting node. Because insert now adopts node as one of its steps, when node is already in the tree its removal from the old location — and the mutation record that removal queues — happened after child had already been silently removed. That gave the record a stale previousSibling and made the two records appear non-atomic.

Adopt node up front, before child is removed, so node's removal is observed while child is still present. This restores the record ordering and previousSibling that predates #754, which had removed replace's explicit adoption step without accounting for this interaction. All engines kept the pre-#754 behavior, so this aligns the standard with reality.

The related replace all algorithm intentionally keeps adopting inside insert: its only caller that can receive an already-present node is replaceChildren(), which postdates #754 and folds such a node into its single record.

Fixes #814.

(See WHATWG Working Mode: Changes for more details.)


Preview | Diff

annevk added 2 commits July 8, 2026 18:06
replace removes child (with suppressObservers set) before inserting
node. Because insert now adopts node as one of its steps, when node is
already in the tree its removal from the old location — and the mutation
record that removal queues — happened *after* child had already been
silently removed. That gave the record a stale previousSibling and made
the two records appear non-atomic.

Adopt node up front, before child is removed, so node's removal is
observed while child is still present. This restores the record ordering
and previousSibling that predates #754, which had removed replace's
explicit adoption step without accounting for this interaction. All
engines kept the pre-#754 behavior, so this aligns the standard with
reality.

The related replace all algorithm intentionally keeps adopting inside
insert: its only caller that can receive an already-present node is
replaceChildren(), which postdates #754 and folds such a node into its
single record.

Tests: dom/nodes/MutationObserver-childList.html (existing n52/n53
coverage) and web-platform-tests/wpt#61155.

Fixes #814.
@annevk
annevk requested review from domfarolino and noamr July 8, 2026 17:01

@domfarolino domfarolino left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!

@annevk
annevk merged commit eb0dfd5 into main Jul 9, 2026
2 checks passed
@annevk
annevk deleted the replace-adopt-before-remove branch July 9, 2026 05:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

replaceChild() has odd mutation observer behavior after #754

3 participants