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);