From c2c1a4037f5c3835e027468e7b3a66768d1e5e15 Mon Sep 17 00:00:00 2001 From: Anne van Kesteren Date: Sun, 19 Jul 2026 17:38:18 +0200 Subject: [PATCH 1/5] Drop a node the adoption agency cannot reinsert MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The adoption agency algorithm's "Insert lastNode ... at the adjusted insertion location" step moves an already-inserted node. Script running during parsing can reparent that node first, so the move would either form a cycle — including one that crosses a shadow root or a template's contents, i.e. where lastNode is a host-including inclusive ancestor of the target — or target a Document that already has an element child. The step used a low-level insertion with no validity check, so such a move built a cyclic or otherwise invalid tree. Tests: https://github.com/web-platform-tests/wpt/pull/61398 --- source | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/source b/source index 8c38ccd201c..cd06eecb8a4 100644 --- a/source +++ b/source @@ -3324,8 +3324,9 @@ a.setAttribute('href', 'https://example.com/'); // change the content attribute descendant, shadow-including ancestor, shadow-including descendant, - shadow-including inclusive descendant, and - shadow-including inclusive ancestor concepts + shadow-including inclusive descendant, + shadow-including inclusive ancestor, and + host-including inclusive ancestor concepts
  • The first child, last child, next sibling, @@ -147342,8 +147343,32 @@ document.body.appendChild(text);
  • Let insertionLocation be commonAncestor, after its last child, if any.

  • -
  • Insert whatever lastNode ended up being in the previous step at the - adjusted insertion location given insertionLocation.

  • +
  • Let adjustedInsertionLocation be the adjusted insertion location + given insertionLocation.

  • + +
  • +

    If either of the following is true:

    + + + +

    then remove lastNode.

    + +

    Otherwise:

    + +
      +
    1. Assert: ensure pre-insert validity given + lastNode, the node in which adjustedInsertionLocation finds itself, + null, and « » does not throw.

    2. + +
    3. Insert lastNode at adjustedInsertionLocation.

    4. +
    +
  • Create an element for the token for which formattingElement was created, in the HTML namespace, with furthestBlock as the intended parent.

  • From 8044ee3c2ca389aee6921d2f8dd4459228c5af3c Mon Sep 17 00:00:00 2001 From: Anne van Kesteren Date: Tue, 18 Aug 2026 10:55:46 +0200 Subject: [PATCH 2/5] fixup! Drop a node the adoption agency cannot reinsert x --- source | 38 ++++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/source b/source index cd06eecb8a4..2bd747dbf7b 100644 --- a/source +++ b/source @@ -147346,25 +147346,43 @@ document.body.appendChild(text);
  • Let adjustedInsertionLocation be the adjusted insertion location given insertionLocation.

  • +
  • Let target be the node in which adjustedInsertionLocation finds + itself.

  • + +
  • Let refNode be the node adjustedInsertionLocation is immediately + before, or null if adjustedInsertionLocation is after target's last + child.

  • + +
  • +

    If lastNode's parent is non-null, then + remove lastNode.

    + +

    This can run script, e.g., because it destroys an iframe + element's navigable. Such script can change the tree, which is why the conditions + below are checked afterwards rather than before.

    +
  • +
  • -

    If either of the following is true:

    +

    If any of the following are true:

      -
    • lastNode is a host-including inclusive ancestor of the node in - which adjustedInsertionLocation finds itself; or
    • +
    • lastNode's parent is non-null;
    • -
    • the node in which adjustedInsertionLocation finds itself is a - Document node that already has an element child,
    • -
    +
  • lastNode is a host-including inclusive ancestor of + target;
  • -

    then remove lastNode.

    +
  • target is a Document node that already has an element child; + or
  • -

    Otherwise:

    +
  • refNode is non-null and its parent is + not target,
  • + + +

    then lastNode is dropped on the floor. Otherwise:

    1. Assert: ensure pre-insert validity given - lastNode, the node in which adjustedInsertionLocation finds itself, - null, and « » does not throw.

    2. + lastNode, target, refNode, and « » does not throw.

    3. Insert lastNode at adjustedInsertionLocation.

    From 8469a4553c8813aaeb151dd701d61df7bd84d1cf Mon Sep 17 00:00:00 2001 From: Anne van Kesteren Date: Tue, 18 Aug 2026 11:15:10 +0200 Subject: [PATCH 3/5] fixup! Drop a node the adoption agency cannot reinsert x --- source | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/source b/source index 2bd747dbf7b..3f92236de66 100644 --- a/source +++ b/source @@ -147353,14 +147353,14 @@ document.body.appendChild(text); before, or null if adjustedInsertionLocation is after target's last child.

    -
  • -

    If lastNode's parent is non-null, then - remove lastNode.

    - -

    This can run script, e.g., because it destroys an iframe - element's navigable. Such script can change the tree, which is why the conditions - below are checked afterwards rather than before.

    -
  • +
  • If lastNode's parent is non-null, then + remove lastNode.

  • + +
  • If any of the following are true:

    From 0a8f796d7926ac5ca55d28870ce4d98111c1840c Mon Sep 17 00:00:00 2001 From: Anne van Kesteren Date: Tue, 18 Aug 2026 11:15:54 +0200 Subject: [PATCH 4/5] fixup! Drop a node the adoption agency cannot reinsert x --- source | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/source b/source index 3f92236de66..6b47bca27b6 100644 --- a/source +++ b/source @@ -147378,7 +147378,11 @@ document.body.appendChild(text); not target,
  • -

    then lastNode is dropped on the floor. Otherwise:

    +

    then lastNode is dropped on the floor.

    + + +
  • +

    Otherwise:

    1. Assert: ensure pre-insert validity given From 16b0955cee58e0441a190669a67069e9ad746b86 Mon Sep 17 00:00:00 2001 From: Anne van Kesteren Date: Tue, 18 Aug 2026 11:17:04 +0200 Subject: [PATCH 5/5] Update source --- source | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/source b/source index 6b47bca27b6..72cdb4312c0 100644 --- a/source +++ b/source @@ -147353,14 +147353,13 @@ document.body.appendChild(text); before, or null if adjustedInsertionLocation is after target's last child.

    2. -
    3. If lastNode's parent is non-null, then - remove lastNode.

    4. - - +
    5. If lastNode's parent is non-null, then remove lastNode.

    6. +
    7. If any of the following are true: