mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-06-11 09:35:59 -04:00
Merge pull request #2366 from lightpanda-io/custom_element_callbacks_again
Protect DOM mutations against custom element callbacks.
This commit is contained in:
@@ -65,3 +65,32 @@
|
||||
assertChildren(['a'], d3);
|
||||
testing.expectEqual(null, b.parentNode);
|
||||
</script>
|
||||
|
||||
<div id=d4></div>
|
||||
<div id=d4_stash></div>
|
||||
<script id=appendChild_disconnect_callback_reparents>
|
||||
// Moving a connected child into a disconnected target makes
|
||||
// will_be_reconnected=false, so disconnectedCallback fires synchronously
|
||||
// inside removeNode. The callback re-parents the child; appendChild
|
||||
// respects that placement instead of overriding it.
|
||||
const d4 = $('#d4');
|
||||
const stash = $('#d4_stash');
|
||||
|
||||
class ReparentOnDisconnect extends HTMLElement {
|
||||
disconnectedCallback() {
|
||||
if (this.parentNode === null) {
|
||||
stash.appendChild(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
customElements.define('reparent-on-disconnect', ReparentOnDisconnect);
|
||||
|
||||
const rpd = document.createElement('reparent-on-disconnect');
|
||||
rpd.id = 'rpd';
|
||||
d4.appendChild(rpd);
|
||||
|
||||
const detached = document.createElement('div');
|
||||
detached.appendChild(rpd);
|
||||
|
||||
testing.expectEqual(stash, rpd.parentNode);
|
||||
</script>
|
||||
|
||||
@@ -39,3 +39,34 @@
|
||||
assertChildren([], d1);
|
||||
assertChildren([c1, c2], d2);
|
||||
</script>
|
||||
|
||||
<div id=d3></div>
|
||||
<div id=d3_stash></div>
|
||||
<script id=insertBefore_disconnect_callback_reparents>
|
||||
// Same disconnectedCallback re-parenting pattern as in append_child.html,
|
||||
// exercised through insertBefore. insertBefore respects the callback's
|
||||
// placement instead of overriding it.
|
||||
const d3 = $('#d3');
|
||||
const stash = $('#d3_stash');
|
||||
|
||||
class IBReparentOnDisconnect extends HTMLElement {
|
||||
disconnectedCallback() {
|
||||
if (this.parentNode === null) {
|
||||
stash.appendChild(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
customElements.define('ib-reparent-on-disconnect', IBReparentOnDisconnect);
|
||||
|
||||
const moving = document.createElement('ib-reparent-on-disconnect');
|
||||
moving.id = 'ib_moving';
|
||||
d3.appendChild(moving);
|
||||
|
||||
const detached = document.createElement('div');
|
||||
const ref = document.createElement('span');
|
||||
detached.appendChild(ref);
|
||||
|
||||
detached.insertBefore(moving, ref);
|
||||
|
||||
testing.expectEqual(stash, moving.parentNode);
|
||||
</script>
|
||||
|
||||
@@ -253,6 +253,11 @@ pub fn appendChild(self: *Node, child: *Node, frame: *Frame) !*Node {
|
||||
try frame.adoptNodeTree(child, child_owner.?, parent_owner);
|
||||
}
|
||||
|
||||
// A custom element callback can re-parent the node. If it does, we're done
|
||||
if (child._parent != null) {
|
||||
return child;
|
||||
}
|
||||
|
||||
try frame.appendNode(self, child, .{
|
||||
.child_already_connected = child_connected,
|
||||
.adopting_to_new_document = adopting_to_new_document,
|
||||
@@ -624,6 +629,22 @@ pub fn insertBefore(self: *Node, new_node: *Node, ref_node_: ?*Node, frame: *Fra
|
||||
try frame.adoptNodeTree(new_node, child_owner.?, parent_owner);
|
||||
}
|
||||
|
||||
// See Node.appendChild: a callback above (disconnectedCallback or
|
||||
// adoptedCallback) can re-parent new_node. Let that placement stand.
|
||||
if (new_node._parent != null) {
|
||||
return new_node;
|
||||
}
|
||||
|
||||
// The same callback could also have detached ref_node from self. Fall
|
||||
// back to append so new_node still lands in self.
|
||||
if (ref_node._parent != self) {
|
||||
try frame.appendNode(self, new_node, .{
|
||||
.child_already_connected = child_already_connected,
|
||||
.adopting_to_new_document = adopting_to_new_document,
|
||||
});
|
||||
return new_node;
|
||||
}
|
||||
|
||||
try frame.insertNodeRelative(
|
||||
self,
|
||||
new_node,
|
||||
|
||||
Reference in New Issue
Block a user