Adopt full subtrees during cross-document insertion - #682
olavoasantos wants to merge 1 commit into
Conversation
978a2ed to
61bd1af
Compare
ff2de3a to
9788a1c
Compare
|
The full-subtree insertion fix looks right at Document.ts:124-132 collects the adoption snapshot, calls public Minimal polyfill reproduction: const source = new Window();
const destination = new Window();
source.customElements.define(
'x-moving',
class extends source.HTMLElement {
disconnectedCallback() {
this.appendChild(source.document.createElement('span'));
}
},
);
const root = source.document.createElement('x-moving');
source.document.body.appendChild(root);
destination.document.adoptNode(root);
root.ownerDocument === destination.document; // true
root.lastChild.ownerDocument === destination.document; // false on headThe equivalent native case and the actual base finish with both nodes owned by destination. Head leaves the newly appended child source-owned underneath a destination-owned root because the child was absent from the snapshot. The inverse also corrupts ownership: if Both new failures also reproduce from the source Window's Please retain the iterative snapshot and preflight, but commit detachment and adoption before either removal hooks or lifecycle callbacks run, matching the insertion path's committed-state-before-effects ordering. Deferring only custom-element reactions misses hook reentrancy; resnapshotting after callbacks retains the wrong callback-visible context. The DOM operation is [CEReactions] and completes its adoption steps before the callback boundary. Focused regression coverage:
Validation: 247 polyfill tests and 59 focused core tests pass; eight of nine added tests fail the actual base. The locked-TypeScript polyfill check, formatting, and reported CI pass. Isolated Chrome/native comparisons match all 40 ordinary operation/connectivity combinations (49/58 overall). Eight targeted hook scenarios expose four new explicit-adoption routing failures while confirming two implicit-insertion routing fixes. Existing inert-template-document and other adoptNode contract gaps are separate, not requests to expand this correction. No production/candidate fix, complete locked-repository/full-build validation, real multi-receiver integration, live Shell/admin validation, or approval performed. |
9788a1c to
0dffbfb
Compare
0dffbfb to
cf11b70
Compare
|
Agreed. |
cf11b70 to
08b54c9
Compare
08b54c9 to
fc245ba
Compare
Problem
Cross-document insertion updated
ownerDocumentonly on the inserted root. Descendants and attached attributes retained the source document, and initializedHTMLTemplateElement.contentwas outside the ordinary child tree, so later mutations could resolve hooks through the wrongWindow.Impact
Important. After inserting a subtree into another document, a descendant attribute, text, or child mutation could be dispatched through the source window rather than the destination. The local tree would appear inserted while its Remote DOM effects were sent to the wrong receiver.
Reproduction
Before this change,
root.ownerDocumentchanged butchildand its attached attribute still belonged tosource, so the final mutation could use the source window’s hook set. It now adopts the root, descendants, and attached attributes intodestinationbefore later mutations run.Change
Build an iterative adoption snapshot before insertion commits, covering the subtree, attached attributes, and already initialized template-content subtrees. Apply that snapshot to the destination document during insertion, and share the same adoption routine with
Document.adoptNode(). The snapshot deduplicates malformed template host/content cycles and preflights traversal so an error leaves links, ownership, connectivity, and hooks unchanged.Tests
Regression coverage uses two windows to verify nested elements, text, and attached attributes receive the destination owner document and route later attribute, text, and insertion effects only through destination hooks. It also covers initialized and untouched template content,
Document.adoptNode(), document-fragment roots, malformed template cycles, and failed preflight preservation. The 6,000-deep initialized-template adoption case is iterative and has an explicit 20-second test budget.Stack
correct-child-replace-withThe 6,000-deep adoption regression test has an explicit 20-second CI budget; the full coverage run passes.
Validation
Fresh GitHub CI on restacked head
9788a1cpasses: