diff --git a/cjs/mixin/parent-node.js b/cjs/mixin/parent-node.js
index 47590b42..57b5408a 100644
--- a/cjs/mixin/parent-node.js
+++ b/cjs/mixin/parent-node.js
@@ -29,6 +29,13 @@ const {nextElementSibling} = require('./non-document-type-child-node.js');
const isNode = node => node instanceof Node;
+const validate = (parentNode, node, before) => {
+ if (before && before !== parentNode[END] && before.parentNode !== parentNode)
+ throw new Error('node is not a child');
+ if (node === parentNode || node.contains(parentNode))
+ throw new Error('unable to append a node to itself');
+};
+
const insert = (parentNode, child, nodes) => {
const {ownerDocument} = parentNode;
for (const node of nodes)
@@ -217,8 +224,7 @@ class ParentNode extends Node {
insertBefore(node, before = null) {
if (node === before)
return node;
- if (node === this)
- throw new Error('unable to append a node to itself');
+ validate(this, node, before);
const next = before || this[END];
switch (node.nodeType) {
case ELEMENT_NODE:
@@ -286,6 +292,7 @@ class ParentNode extends Node {
}
replaceChild(node, replaced) {
+ validate(this, node, replaced);
const next = getEnd(replaced)[NEXT];
replaced.remove();
this.insertBefore(node, next);
diff --git a/esm/mixin/parent-node.js b/esm/mixin/parent-node.js
index b54c4a0e..baedd0e5 100644
--- a/esm/mixin/parent-node.js
+++ b/esm/mixin/parent-node.js
@@ -28,6 +28,13 @@ import {nextElementSibling} from './non-document-type-child-node.js';
const isNode = node => node instanceof Node;
+const validate = (parentNode, node, before) => {
+ if (before && before !== parentNode[END] && before.parentNode !== parentNode)
+ throw new Error('node is not a child');
+ if (node === parentNode || node.contains(parentNode))
+ throw new Error('unable to append a node to itself');
+};
+
const insert = (parentNode, child, nodes) => {
const {ownerDocument} = parentNode;
for (const node of nodes)
@@ -216,8 +223,7 @@ export class ParentNode extends Node {
insertBefore(node, before = null) {
if (node === before)
return node;
- if (node === this)
- throw new Error('unable to append a node to itself');
+ validate(this, node, before);
const next = before || this[END];
switch (node.nodeType) {
case ELEMENT_NODE:
@@ -285,6 +291,7 @@ export class ParentNode extends Node {
}
replaceChild(node, replaced) {
+ validate(this, node, replaced);
const next = getEnd(replaced)[NEXT];
replaced.remove();
this.insertBefore(node, next);
diff --git a/test/interface/node.js b/test/interface/node.js
index 52a99dec..57b1d172 100644
--- a/test/interface/node.js
+++ b/test/interface/node.js
@@ -41,3 +41,30 @@ assert(NodeFilter.SHOW_ELEMENT, 1, 'NodeFilter.SHOW_ELEMENT');
assert(NodeFilter.SHOW_TEXT, 4, 'NodeFilter.SHOW_TEXT');
assert(NodeFilter.SHOW_CDATA_SECTION, 8, 'NodeFilter.SHOW_CDATA_SECTION');
assert(NodeFilter.SHOW_COMMENT, 128, 'NodeFilter.SHOW_COMMENT');
+
+const {document: parentDocument} = parseHTML(
+ ''
+);
+const [main, section, aside] = parentDocument.querySelectorAll('main,section,aside');
+const throws = callback => {
+ try {
+ callback();
+ return false;
+ }
+ catch {
+ return true;
+ }
+};
+
+assert(throws(() => {
+ main.insertBefore(parentDocument.createElement('i'), aside.firstChild);
+}), true, 'insertBefore rejects a foreign reference node');
+assert(main.toString(), '', 'insertBefore keeps the receiver intact');
+assert(aside.toString(), '', 'insertBefore keeps the foreign parent intact');
+
+assert(throws(() => main.appendChild(main)), true, 'appendChild rejects itself');
+assert(throws(() => section.appendChild(main)), true, 'appendChild rejects an ancestor');
+assert(throws(() => {
+ main.replaceChild(parentDocument.createElement('i'), aside.firstChild);
+}), true, 'replaceChild rejects a foreign child');
+assert(aside.toString(), '', 'replaceChild keeps the foreign parent intact');
diff --git a/worker.js b/worker.js
index 2671c7f5..2cd0c2af 100644
--- a/worker.js
+++ b/worker.js
@@ -6775,6 +6775,13 @@ let Text$1 = class Text extends CharacterData$1 {
const isNode = node => node instanceof Node$1;
+const validate = (parentNode, node, before) => {
+ if (before && before !== parentNode[END] && before.parentNode !== parentNode)
+ throw new Error('node is not a child');
+ if (node === parentNode || node.contains(parentNode))
+ throw new Error('unable to append a node to itself');
+};
+
const insert = (parentNode, child, nodes) => {
const {ownerDocument} = parentNode;
for (const node of nodes)
@@ -6963,8 +6970,7 @@ class ParentNode extends Node$1 {
insertBefore(node, before = null) {
if (node === before)
return node;
- if (node === this)
- throw new Error('unable to append a node to itself');
+ validate(this, node, before);
const next = before || this[END];
switch (node.nodeType) {
case ELEMENT_NODE:
@@ -7032,6 +7038,7 @@ class ParentNode extends Node$1 {
}
replaceChild(node, replaced) {
+ validate(this, node, replaced);
const next = getEnd(replaced)[NEXT];
replaced.remove();
this.insertBefore(node, next);