Implement ParentNode.prototype.replaceChildren
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 1 Jun 2020 16:57:58 +0000 (16:57 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 1 Jun 2020 16:57:58 +0000 (16:57 +0000)
https://bugs.webkit.org/show_bug.cgi?id=198578

Patch by Tetsuharu Ohzeki <tetsuharu.ohzeki@gmail.com> on 2020-06-01
Reviewed by Darin Adler.

LayoutTests/imported/w3c:

* web-platform-tests/dom/idlharness.window-expected.txt:
* web-platform-tests/dom/nodes/ParentNode-replaceChildren-expected.txt:

Source/WebCore:

Ideally, we can use `ContainerNode::replaceAllChildren` to implement
this simply but the current of it does not have a path to support
`DocumentFragment`.

Hence, we call related methods from `ParentNode.prototype.replaceChildren` directly.

* dom/ContainerNode.cpp:
(WebCore::ContainerNode::replaceChildren):
* dom/ContainerNode.h:
* dom/ParentNode.idl:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@262381 268f45cc-cd09-0410-ab3c-d52691b4dbfc

LayoutTests/imported/w3c/ChangeLog
LayoutTests/imported/w3c/web-platform-tests/dom/idlharness.window-expected.txt
LayoutTests/imported/w3c/web-platform-tests/dom/nodes/ParentNode-replaceChildren-expected.txt
Source/WebCore/ChangeLog
Source/WebCore/dom/ContainerNode.cpp
Source/WebCore/dom/ContainerNode.h
Source/WebCore/dom/ParentNode.idl

index 5c37f77..7b11415 100644 (file)
@@ -1,3 +1,13 @@
+2020-06-01  Tetsuharu Ohzeki  <tetsuharu.ohzeki@gmail.com>
+
+        Implement ParentNode.prototype.replaceChildren
+        https://bugs.webkit.org/show_bug.cgi?id=198578
+
+        Reviewed by Darin Adler.
+
+        * web-platform-tests/dom/idlharness.window-expected.txt:
+        * web-platform-tests/dom/nodes/ParentNode-replaceChildren-expected.txt:
+
 2020-05-31  Rob Buis  <rbuis@igalia.com>
 
         Implement named item condition for images
index ce8bf0b..ae66e4b 100644 (file)
@@ -367,7 +367,7 @@ PASS Document interface: attribute lastElementChild
 PASS Document interface: attribute childElementCount 
 PASS Document interface: operation prepend((Node or DOMString)...) 
 PASS Document interface: operation append((Node or DOMString)...) 
-FAIL Document interface: operation replaceChildren((Node or DOMString)...) assert_own_property: interface prototype object missing non-static operation expected property "replaceChildren" missing
+PASS Document interface: operation replaceChildren((Node or DOMString)...) 
 PASS Document interface: operation querySelector(DOMString) 
 PASS Document interface: operation querySelectorAll(DOMString) 
 PASS Document interface: operation createExpression(DOMString, optional XPathNSResolver?) 
@@ -429,8 +429,8 @@ PASS Document interface: new Document() must inherit property "prepend((Node or
 PASS Document interface: calling prepend((Node or DOMString)...) on new Document() with too few arguments must throw TypeError 
 PASS Document interface: new Document() must inherit property "append((Node or DOMString)...)" with the proper type 
 PASS Document interface: calling append((Node or DOMString)...) on new Document() with too few arguments must throw TypeError 
-FAIL Document interface: new Document() must inherit property "replaceChildren((Node or DOMString)...)" with the proper type assert_inherits: property "replaceChildren" not found in prototype chain
-FAIL Document interface: calling replaceChildren((Node or DOMString)...) on new Document() with too few arguments must throw TypeError assert_inherits: property "replaceChildren" not found in prototype chain
+PASS Document interface: new Document() must inherit property "replaceChildren((Node or DOMString)...)" with the proper type 
+PASS Document interface: calling replaceChildren((Node or DOMString)...) on new Document() with too few arguments must throw TypeError 
 PASS Document interface: new Document() must inherit property "querySelector(DOMString)" with the proper type 
 PASS Document interface: calling querySelector(DOMString) on new Document() with too few arguments must throw TypeError 
 PASS Document interface: new Document() must inherit property "querySelectorAll(DOMString)" with the proper type 
@@ -569,8 +569,8 @@ PASS Document interface: xmlDoc must inherit property "prepend((Node or DOMStrin
 PASS Document interface: calling prepend((Node or DOMString)...) on xmlDoc with too few arguments must throw TypeError 
 PASS Document interface: xmlDoc must inherit property "append((Node or DOMString)...)" with the proper type 
 PASS Document interface: calling append((Node or DOMString)...) on xmlDoc with too few arguments must throw TypeError 
-FAIL Document interface: xmlDoc must inherit property "replaceChildren((Node or DOMString)...)" with the proper type assert_inherits: property "replaceChildren" not found in prototype chain
-FAIL Document interface: calling replaceChildren((Node or DOMString)...) on xmlDoc with too few arguments must throw TypeError assert_inherits: property "replaceChildren" not found in prototype chain
+PASS Document interface: xmlDoc must inherit property "replaceChildren((Node or DOMString)...)" with the proper type 
+PASS Document interface: calling replaceChildren((Node or DOMString)...) on xmlDoc with too few arguments must throw TypeError 
 PASS Document interface: xmlDoc must inherit property "querySelector(DOMString)" with the proper type 
 PASS Document interface: calling querySelector(DOMString) on xmlDoc with too few arguments must throw TypeError 
 PASS Document interface: xmlDoc must inherit property "querySelectorAll(DOMString)" with the proper type 
@@ -770,7 +770,7 @@ PASS DocumentFragment interface: attribute lastElementChild
 PASS DocumentFragment interface: attribute childElementCount 
 PASS DocumentFragment interface: operation prepend((Node or DOMString)...) 
 PASS DocumentFragment interface: operation append((Node or DOMString)...) 
-FAIL DocumentFragment interface: operation replaceChildren((Node or DOMString)...) assert_own_property: interface prototype object missing non-static operation expected property "replaceChildren" missing
+PASS DocumentFragment interface: operation replaceChildren((Node or DOMString)...) 
 PASS DocumentFragment interface: operation querySelector(DOMString) 
 PASS DocumentFragment interface: operation querySelectorAll(DOMString) 
 PASS DocumentFragment must be primary interface of document.createDocumentFragment() 
@@ -785,8 +785,8 @@ PASS DocumentFragment interface: document.createDocumentFragment() must inherit
 PASS DocumentFragment interface: calling prepend((Node or DOMString)...) on document.createDocumentFragment() with too few arguments must throw TypeError 
 PASS DocumentFragment interface: document.createDocumentFragment() must inherit property "append((Node or DOMString)...)" with the proper type 
 PASS DocumentFragment interface: calling append((Node or DOMString)...) on document.createDocumentFragment() with too few arguments must throw TypeError 
-FAIL DocumentFragment interface: document.createDocumentFragment() must inherit property "replaceChildren((Node or DOMString)...)" with the proper type assert_inherits: property "replaceChildren" not found in prototype chain
-FAIL DocumentFragment interface: calling replaceChildren((Node or DOMString)...) on document.createDocumentFragment() with too few arguments must throw TypeError assert_inherits: property "replaceChildren" not found in prototype chain
+PASS DocumentFragment interface: document.createDocumentFragment() must inherit property "replaceChildren((Node or DOMString)...)" with the proper type 
+PASS DocumentFragment interface: calling replaceChildren((Node or DOMString)...) on document.createDocumentFragment() with too few arguments must throw TypeError 
 PASS DocumentFragment interface: document.createDocumentFragment() must inherit property "querySelector(DOMString)" with the proper type 
 PASS DocumentFragment interface: calling querySelector(DOMString) on document.createDocumentFragment() with too few arguments must throw TypeError 
 PASS DocumentFragment interface: document.createDocumentFragment() must inherit property "querySelectorAll(DOMString)" with the proper type 
@@ -913,7 +913,7 @@ PASS Element interface: attribute lastElementChild
 PASS Element interface: attribute childElementCount 
 PASS Element interface: operation prepend((Node or DOMString)...) 
 PASS Element interface: operation append((Node or DOMString)...) 
-FAIL Element interface: operation replaceChildren((Node or DOMString)...) assert_own_property: interface prototype object missing non-static operation expected property "replaceChildren" missing
+PASS Element interface: operation replaceChildren((Node or DOMString)...) 
 PASS Element interface: operation querySelector(DOMString) 
 PASS Element interface: operation querySelectorAll(DOMString) 
 PASS Element interface: attribute previousElementSibling 
@@ -991,8 +991,8 @@ PASS Element interface: element must inherit property "prepend((Node or DOMStrin
 PASS Element interface: calling prepend((Node or DOMString)...) on element with too few arguments must throw TypeError 
 PASS Element interface: element must inherit property "append((Node or DOMString)...)" with the proper type 
 PASS Element interface: calling append((Node or DOMString)...) on element with too few arguments must throw TypeError 
-FAIL Element interface: element must inherit property "replaceChildren((Node or DOMString)...)" with the proper type assert_inherits: property "replaceChildren" not found in prototype chain
-FAIL Element interface: calling replaceChildren((Node or DOMString)...) on element with too few arguments must throw TypeError assert_inherits: property "replaceChildren" not found in prototype chain
+PASS Element interface: element must inherit property "replaceChildren((Node or DOMString)...)" with the proper type 
+PASS Element interface: calling replaceChildren((Node or DOMString)...) on element with too few arguments must throw TypeError 
 PASS Element interface: element must inherit property "querySelector(DOMString)" with the proper type 
 PASS Element interface: calling querySelector(DOMString) on element with too few arguments must throw TypeError 
 PASS Element interface: element must inherit property "querySelectorAll(DOMString)" with the proper type 
index 3663048..62b8ab8 100644 (file)
@@ -1,27 +1,27 @@
 
-FAIL If node is a host-including inclusive ancestor of parent, then throw a HierarchyRequestError DOMException. assert_throws_dom: function "() => insert(doc.body, doc.body)" threw object "TypeError: undefined is not an object (evaluating 'parent[methodName].length')" that is not a DOMException HierarchyRequestError: property "code" is equal to undefined, expected 3
-FAIL If node is not a DocumentFragment, DocumentType, Element, Text, ProcessingInstruction, or Comment node, then throw a HierarchyRequestError DOMException. assert_throws_dom: function "() => insert(doc, doc2)" threw object "TypeError: undefined is not an object (evaluating 'parent[methodName].length')" that is not a DOMException HierarchyRequestError: property "code" is equal to undefined, expected 3
-FAIL If node is a Text node and parent is a document, then throw a HierarchyRequestError DOMException. assert_throws_dom: function "() => insert(doc, doc.createTextNode("text"))" threw object "TypeError: undefined is not an object (evaluating 'parent[methodName].length')" that is not a DOMException HierarchyRequestError: property "code" is equal to undefined, expected 3
-FAIL If node is a doctype and parent is not a document, then throw a HierarchyRequestError DOMException. assert_throws_dom: function "() => insert(doc.createElement("a"), doctype)" threw object "TypeError: undefined is not an object (evaluating 'parent[methodName].length')" that is not a DOMException HierarchyRequestError: property "code" is equal to undefined, expected 3
-FAIL If node is a DocumentFragment with multiple elements and parent is a document, then throw a HierarchyRequestError DOMException. assert_throws_dom: function "() => insert(doc, df)" threw object "TypeError: undefined is not an object (evaluating 'parent[methodName].length')" that is not a DOMException HierarchyRequestError: property "code" is equal to undefined, expected 3
-FAIL If node is a DocumentFragment with an element and parent is a document with another element, then throw a HierarchyRequestError DOMException. assert_throws_dom: function "() => insert(doc, df)" threw object "TypeError: undefined is not an object (evaluating 'parent[methodName].length')" that is not a DOMException HierarchyRequestError: property "code" is equal to undefined, expected 3
-FAIL If node is an Element and parent is a document with another element, then throw a HierarchyRequestError DOMException. assert_throws_dom: function "() => insert(doc, el)" threw object "TypeError: undefined is not an object (evaluating 'parent[methodName].length')" that is not a DOMException HierarchyRequestError: property "code" is equal to undefined, expected 3
-FAIL If node is a doctype and parent is a document with another doctype, then throw a HierarchyRequestError DOMException. assert_throws_dom: function "() => insert(doc, doctype)" threw object "TypeError: undefined is not an object (evaluating 'parent[methodName].length')" that is not a DOMException HierarchyRequestError: property "code" is equal to undefined, expected 3
-FAIL If node is a doctype and parent is a document with an element, then throw a HierarchyRequestError DOMException. assert_throws_dom: function "() => insert(doc, doctype)" threw object "TypeError: undefined is not an object (evaluating 'parent[methodName].length')" that is not a DOMException HierarchyRequestError: property "code" is equal to undefined, expected 3
-FAIL Element.replaceChildren() without any argument, on a parent having no child. parent.replaceChildren is not a function. (In 'parent.replaceChildren()', 'parent.replaceChildren' is undefined)
-FAIL Element.replaceChildren() with null as an argument, on a parent having no child. parent.replaceChildren is not a function. (In 'parent.replaceChildren(null)', 'parent.replaceChildren' is undefined)
-FAIL Element.replaceChildren() with undefined as an argument, on a parent having no child. parent.replaceChildren is not a function. (In 'parent.replaceChildren(undefined)', 'parent.replaceChildren' is undefined)
-FAIL Element.replaceChildren() with only text as an argument, on a parent having no child. parent.replaceChildren is not a function. (In 'parent.replaceChildren('text')', 'parent.replaceChildren' is undefined)
-FAIL Element.replaceChildren() with only one element as an argument, on a parent having no child. parent.replaceChildren is not a function. (In 'parent.replaceChildren(x)', 'parent.replaceChildren' is undefined)
-FAIL Element.replaceChildren() with null as an argument, on a parent having a child. parent.replaceChildren is not a function. (In 'parent.replaceChildren(null)', 'parent.replaceChildren' is undefined)
-FAIL Element.replaceChildren() with one element and text as argument, on a parent having a child. parent.replaceChildren is not a function. (In 'parent.replaceChildren(x, 'text')', 'parent.replaceChildren' is undefined)
-FAIL Element.replaceChildren() should move nodes in the right order parent.replaceChildren is not a function. (In 'parent.replaceChildren(...previousParent.children)', 'parent.replaceChildren' is undefined)
-FAIL DocumentFragment.replaceChildren() without any argument, on a parent having no child. parent.replaceChildren is not a function. (In 'parent.replaceChildren()', 'parent.replaceChildren' is undefined)
-FAIL DocumentFragment.replaceChildren() with null as an argument, on a parent having no child. parent.replaceChildren is not a function. (In 'parent.replaceChildren(null)', 'parent.replaceChildren' is undefined)
-FAIL DocumentFragment.replaceChildren() with undefined as an argument, on a parent having no child. parent.replaceChildren is not a function. (In 'parent.replaceChildren(undefined)', 'parent.replaceChildren' is undefined)
-FAIL DocumentFragment.replaceChildren() with only text as an argument, on a parent having no child. parent.replaceChildren is not a function. (In 'parent.replaceChildren('text')', 'parent.replaceChildren' is undefined)
-FAIL DocumentFragment.replaceChildren() with only one element as an argument, on a parent having no child. parent.replaceChildren is not a function. (In 'parent.replaceChildren(x)', 'parent.replaceChildren' is undefined)
-FAIL DocumentFragment.replaceChildren() with null as an argument, on a parent having a child. parent.replaceChildren is not a function. (In 'parent.replaceChildren(null)', 'parent.replaceChildren' is undefined)
-FAIL DocumentFragment.replaceChildren() with one element and text as argument, on a parent having a child. parent.replaceChildren is not a function. (In 'parent.replaceChildren(x, 'text')', 'parent.replaceChildren' is undefined)
-FAIL DocumentFragment.replaceChildren() should move nodes in the right order parent.replaceChildren is not a function. (In 'parent.replaceChildren(...previousParent.children)', 'parent.replaceChildren' is undefined)
+PASS If node is a host-including inclusive ancestor of parent, then throw a HierarchyRequestError DOMException. 
+PASS If node is not a DocumentFragment, DocumentType, Element, Text, ProcessingInstruction, or Comment node, then throw a HierarchyRequestError DOMException. 
+PASS If node is a Text node and parent is a document, then throw a HierarchyRequestError DOMException. 
+PASS If node is a doctype and parent is not a document, then throw a HierarchyRequestError DOMException. 
+PASS If node is a DocumentFragment with multiple elements and parent is a document, then throw a HierarchyRequestError DOMException. 
+PASS If node is a DocumentFragment with an element and parent is a document with another element, then throw a HierarchyRequestError DOMException. 
+PASS If node is an Element and parent is a document with another element, then throw a HierarchyRequestError DOMException. 
+PASS If node is a doctype and parent is a document with another doctype, then throw a HierarchyRequestError DOMException. 
+PASS If node is a doctype and parent is a document with an element, then throw a HierarchyRequestError DOMException. 
+PASS Element.replaceChildren() without any argument, on a parent having no child. 
+PASS Element.replaceChildren() with null as an argument, on a parent having no child. 
+PASS Element.replaceChildren() with undefined as an argument, on a parent having no child. 
+PASS Element.replaceChildren() with only text as an argument, on a parent having no child. 
+PASS Element.replaceChildren() with only one element as an argument, on a parent having no child. 
+PASS Element.replaceChildren() with null as an argument, on a parent having a child. 
+PASS Element.replaceChildren() with one element and text as argument, on a parent having a child. 
+PASS Element.replaceChildren() should move nodes in the right order 
+PASS DocumentFragment.replaceChildren() without any argument, on a parent having no child. 
+PASS DocumentFragment.replaceChildren() with null as an argument, on a parent having no child. 
+PASS DocumentFragment.replaceChildren() with undefined as an argument, on a parent having no child. 
+PASS DocumentFragment.replaceChildren() with only text as an argument, on a parent having no child. 
+PASS DocumentFragment.replaceChildren() with only one element as an argument, on a parent having no child. 
+PASS DocumentFragment.replaceChildren() with null as an argument, on a parent having a child. 
+PASS DocumentFragment.replaceChildren() with one element and text as argument, on a parent having a child. 
+PASS DocumentFragment.replaceChildren() should move nodes in the right order 
 
index 3ddd1e6..e8a794d 100644 (file)
@@ -1,3 +1,21 @@
+2020-06-01  Tetsuharu Ohzeki  <tetsuharu.ohzeki@gmail.com>
+
+        Implement ParentNode.prototype.replaceChildren
+        https://bugs.webkit.org/show_bug.cgi?id=198578
+
+        Reviewed by Darin Adler.
+
+        Ideally, we can use `ContainerNode::replaceAllChildren` to implement
+        this simply but the current of it does not have a path to support
+        `DocumentFragment`.
+
+        Hence, we call related methods from `ParentNode.prototype.replaceChildren` directly.
+
+        * dom/ContainerNode.cpp:
+        (WebCore::ContainerNode::replaceChildren):
+        * dom/ContainerNode.h:
+        * dom/ParentNode.idl:
+
 2020-05-25  Sergio Villar Senin  <svillar@igalia.com>
 
         [css-flexbox] Tables as flex items should obey the flex container sizing
index 8d4ef96..020788f 100644 (file)
@@ -952,6 +952,38 @@ ExceptionOr<void> ContainerNode::prepend(Vector<NodeOrString>&& vector)
     return insertBefore(*node, firstChild());
 }
 
+// https://dom.spec.whatwg.org/#dom-parentnode-replacechildren
+ExceptionOr<void> ContainerNode::replaceChildren(Vector<NodeOrString>&& vector)
+{
+    // step 1
+    auto result = convertNodesOrStringsIntoNode(WTFMove(vector));
+    if (result.hasException())
+        return result.releaseException();
+
+    RefPtr<Node> node = result.releaseReturnValue();
+    if (!node)
+        return { };
+
+    // step 2
+    auto validityCheckResult = ensurePreInsertionValidity(*node, nullptr);
+    if (validityCheckResult.hasException())
+        return validityCheckResult.releaseException();
+
+    // step 3
+    Ref<ContainerNode> protectedThis(*this);
+    ChildListMutationScope mutation(*this);
+    removeAllChildrenWithScriptAssertion(ChildChangeSource::API, DeferChildrenChanged::Yes);
+
+    auto insertResult = appendChildWithoutPreInsertionValidityCheck(*node);
+    if (insertResult.hasException())
+        return insertResult.releaseException();
+
+    rebuildSVGExtensionsElementsIfNecessary();
+    dispatchSubtreeModifiedEvent();
+
+    return { };
+}
+
 HTMLCollection* ContainerNode::cachedHTMLCollection(CollectionType type)
 {
     return hasRareData() && rareData()->nodeLists() ? rareData()->nodeLists()->cachedCollection<HTMLCollection>(type) : nullptr;
index 2b29060..92de3ff 100644 (file)
@@ -128,6 +128,8 @@ public:
     ExceptionOr<void> append(Vector<NodeOrString>&&);
     ExceptionOr<void> prepend(Vector<NodeOrString>&&);
 
+    ExceptionOr<void> replaceChildren(Vector<NodeOrString>&&);
+
     ExceptionOr<void> ensurePreInsertionValidity(Node& newChild, Node* refChild);
 
 protected:
index 6d79dc5..cb70f8a 100644 (file)
@@ -35,6 +35,7 @@
 
     [CEReactions, Unscopable, MayThrowException] void prepend((Node or DOMString)... nodes);
     [CEReactions, Unscopable, MayThrowException] void append((Node or DOMString)... nodes);
+    [CEReactions, Unscopable, MayThrowException] void replaceChildren((Node or DOMString)... nodes);
 
     [MayThrowException] Element? querySelector(DOMString selectors);
     [MayThrowException, NewObject] NodeList querySelectorAll(DOMString selectors);