Bug 1626015 - Implement ParentNode#ReplaceChildren. r=emilio
authorAlex Vincent <ajvincent@gmail.com>
Thu, 28 May 2020 00:58:24 +0000
changeset 532651 a3fcf899da1a3f92ee5ecd65ef5c038b8eb0729f
parent 532650 72ff319a6e561d31ae78f50e3a977777c3a0cb82
child 532652 6e7282ed160be0587e8c874af9d92d4fb3b23207
push id37457
push usernerli@mozilla.com
push dateThu, 28 May 2020 15:51:15 +0000
treeherdermozilla-central@272e3c98d002 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersemilio
bugs1626015
milestone78.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1626015 - Implement ParentNode#ReplaceChildren. r=emilio Differential Revision: https://phabricator.services.mozilla.com/D75891
dom/base/nsINode.cpp
dom/base/nsINode.h
dom/webidl/ParentNode.webidl
testing/web-platform/meta/dom/idlharness.window.js.ini
testing/web-platform/meta/dom/nodes/ParentNode-replaceChildren.html.ini
testing/web-platform/tests/dom/nodes/ParentNode-replaceChildren.html
--- a/dom/base/nsINode.cpp
+++ b/dom/base/nsINode.cpp
@@ -2020,16 +2020,45 @@ void nsINode::Append(const Sequence<Owni
   nsCOMPtr<nsINode> node = ConvertNodesOrStringsIntoNode(aNodes, doc, aRv);
   if (aRv.Failed()) {
     return;
   }
 
   AppendChild(*node, aRv);
 }
 
+// https://dom.spec.whatwg.org/#dom-parentnode-replacechildren
+void nsINode::ReplaceChildren(const Sequence<OwningNodeOrString>& aNodes,
+                              ErrorResult& aRv) {
+  nsCOMPtr<Document> doc = OwnerDoc();
+  nsCOMPtr<nsINode> node = ConvertNodesOrStringsIntoNode(aNodes, doc, aRv);
+  if (aRv.Failed()) {
+    return;
+  }
+
+  EnsurePreInsertionValidity(*node, nullptr, aRv);
+  if (aRv.Failed()) {
+    return;
+  }
+
+  // Needed when used in combination with contenteditable (maybe)
+  mozAutoDocUpdate updateBatch(doc, true);
+
+  nsAutoMutationBatch mb(this, true, false);
+
+  // Replace all with node within this.
+  while (mFirstChild) {
+    RemoveChildNode(mFirstChild, true);
+  }
+  mb.RemovalDone();
+
+  AppendChild(*node, aRv);
+  mb.NodesAdded();
+}
+
 void nsINode::RemoveChildNode(nsIContent* aKid, bool aNotify) {
   // NOTE: This function must not trigger any calls to
   // Document::GetRootElement() calls until *after* it has removed aKid from
   // aChildArray. Any calls before then could potentially restore a stale
   // value for our cached root element, per note in
   // Document::RemoveChildNode().
   MOZ_ASSERT(aKid && aKid->GetParentNode() == this, "Bogus aKid");
   MOZ_ASSERT(!IsAttr());
--- a/dom/base/nsINode.h
+++ b/dom/base/nsINode.h
@@ -2028,16 +2028,18 @@ class nsINode : public mozilla::dom::Eve
   already_AddRefed<nsIHTMLCollection> GetElementsByAttributeNS(
       const nsAString& aNamespaceURI, const nsAString& aAttribute,
       const nsAString& aValue, ErrorResult& aRv);
 
   MOZ_CAN_RUN_SCRIPT void Prepend(const Sequence<OwningNodeOrString>& aNodes,
                                   ErrorResult& aRv);
   MOZ_CAN_RUN_SCRIPT void Append(const Sequence<OwningNodeOrString>& aNodes,
                                  ErrorResult& aRv);
+  MOZ_CAN_RUN_SCRIPT void ReplaceChildren(
+      const Sequence<OwningNodeOrString>& aNodes, ErrorResult& aRv);
 
   void GetBoxQuads(const BoxQuadOptions& aOptions,
                    nsTArray<RefPtr<DOMQuad>>& aResult, CallerType aCallerType,
                    ErrorResult& aRv);
 
   void GetBoxQuadsFromWindowOrigin(const BoxQuadOptions& aOptions,
                                    nsTArray<RefPtr<DOMQuad>>& aResult,
                                    ErrorResult& aRv);
--- a/dom/webidl/ParentNode.webidl
+++ b/dom/webidl/ParentNode.webidl
@@ -23,9 +23,11 @@ interface mixin ParentNode {
   [ChromeOnly, Throws]
   HTMLCollection getElementsByAttributeNS(DOMString? namespaceURI, DOMString name,
                                           [TreatNullAs=EmptyString] DOMString value);
 
   [CEReactions, Throws, Unscopable]
   void prepend((Node or DOMString)... nodes);
   [CEReactions, Throws, Unscopable]
   void append((Node or DOMString)... nodes);
+  [CEReactions, Throws, Unscopable]
+  void replaceChildren((Node or DOMString)... nodes);
 };
--- a/testing/web-platform/meta/dom/idlharness.window.js.ini
+++ b/testing/web-platform/meta/dom/idlharness.window.js.ini
@@ -1,42 +1,8 @@
 [idlharness.window.html?include=Node]
 
 [idlharness.window.html?exclude=Node]
   [Document interface: existence and properties of interface prototype object's @@unscopables property]
     expected: FAIL
 
   [ShadowRoot interface: attribute onslotchange]
     expected: FAIL
-
-  [Element interface: element must inherit property "replaceChildren((Node or DOMString)...)" with the proper type]
-    expected: FAIL
-
-  [Document interface: calling replaceChildren((Node or DOMString)...) on xmlDoc with too few arguments must throw TypeError]
-    expected: FAIL
-
-  [Document interface: calling replaceChildren((Node or DOMString)...) on new Document() with too few arguments must throw TypeError]
-    expected: FAIL
-
-  [DocumentFragment interface: document.createDocumentFragment() must inherit property "replaceChildren((Node or DOMString)...)" with the proper type]
-    expected: FAIL
-
-  [DocumentFragment interface: operation replaceChildren((Node or DOMString)...)]
-    expected: FAIL
-
-  [Element interface: operation replaceChildren((Node or DOMString)...)]
-    expected: FAIL
-
-  [Document interface: operation replaceChildren((Node or DOMString)...)]
-    expected: FAIL
-
-  [Element interface: calling replaceChildren((Node or DOMString)...) on element with too few arguments must throw TypeError]
-    expected: FAIL
-
-  [Document interface: new Document() must inherit property "replaceChildren((Node or DOMString)...)" with the proper type]
-    expected: FAIL
-
-  [DocumentFragment interface: calling replaceChildren((Node or DOMString)...) on document.createDocumentFragment() with too few arguments must throw TypeError]
-    expected: FAIL
-
-  [Document interface: xmlDoc must inherit property "replaceChildren((Node or DOMString)...)" with the proper type]
-    expected: FAIL
-
deleted file mode 100644
--- a/testing/web-platform/meta/dom/nodes/ParentNode-replaceChildren.html.ini
+++ /dev/null
@@ -1,76 +0,0 @@
-[ParentNode-replaceChildren.html]
-  [DocumentFragment.replaceChildren() with null as an argument, on a parent having a child.]
-    expected: FAIL
-
-  [DocumentFragment.replaceChildren() with only text as an argument, on a parent having no child.]
-    expected: FAIL
-
-  [Element.replaceChildren() with null as an argument, on a parent having no child.]
-    expected: FAIL
-
-  [Element.replaceChildren() without any argument, on a parent having no child.]
-    expected: FAIL
-
-  [If node is a host-including inclusive ancestor of parent, then throw a HierarchyRequestError DOMException.]
-    expected: FAIL
-
-  [If node is an Element and parent is a document with another element, then throw a HierarchyRequestError DOMException.]
-    expected: FAIL
-
-  [DocumentFragment.replaceChildren() with null as an argument, on a parent having no child.]
-    expected: FAIL
-
-  [DocumentFragment.replaceChildren() with one element and text as argument, on a parent having a child.]
-    expected: FAIL
-
-  [Element.replaceChildren() with null as an argument, on a parent having a child.]
-    expected: FAIL
-
-  [DocumentFragment.replaceChildren() with only one element as an argument, on a parent having no child.]
-    expected: FAIL
-
-  [Element.replaceChildren() should move nodes in the right order]
-    expected: FAIL
-
-  [If node is a Text node and parent is a document, then throw a HierarchyRequestError DOMException.]
-    expected: FAIL
-
-  [If node is a DocumentFragment with an element and parent is a document with another element, then throw a HierarchyRequestError DOMException.]
-    expected: FAIL
-
-  [DocumentFragment.replaceChildren() with undefined as an argument, on a parent having no child.]
-    expected: FAIL
-
-  [If node is a doctype and parent is not a document, then throw a HierarchyRequestError DOMException.]
-    expected: FAIL
-
-  [If node is a doctype and parent is a document with another doctype, then throw a HierarchyRequestError DOMException.]
-    expected: FAIL
-
-  [If node is a DocumentFragment with multiple elements and parent is a document, then throw a HierarchyRequestError DOMException.]
-    expected: FAIL
-
-  [Element.replaceChildren() with undefined as an argument, on a parent having no child.]
-    expected: FAIL
-
-  [Element.replaceChildren() with only one element as an argument, on a parent having no child.]
-    expected: FAIL
-
-  [Element.replaceChildren() with one element and text as argument, on a parent having a child.]
-    expected: FAIL
-
-  [DocumentFragment.replaceChildren() should move nodes in the right order]
-    expected: FAIL
-
-  [If node is a doctype and parent is a document with an element, then throw a HierarchyRequestError DOMException.]
-    expected: FAIL
-
-  [Element.replaceChildren() with only text as an argument, on a parent having no child.]
-    expected: FAIL
-
-  [DocumentFragment.replaceChildren() without any argument, on a parent having no child.]
-    expected: FAIL
-
-  [If node is not a DocumentFragment, DocumentType, Element, Text, ProcessingInstruction, or Comment node, then throw a HierarchyRequestError DOMException.]
-    expected: FAIL
-
--- a/testing/web-platform/tests/dom/nodes/ParentNode-replaceChildren.html
+++ b/testing/web-platform/tests/dom/nodes/ParentNode-replaceChildren.html
@@ -89,24 +89,24 @@
           }
           phase = 1;
         });
       });
       previousObserver.observe(previousParent, { childList: true });
 
       const observer = new MutationObserver(mutations => {
         t.step(() => {
-          assert_equals(phase, 1);
-          assert_equals(mutations.length, 1);
+          assert_equals(phase, 1, "phase");
+          assert_equals(mutations.length, 1, "mutations.length");
           const mutation = mutations[0];
-          assert_equals(mutation.type, "childList");
-          assert_equals(mutation.addedNodes.length, 2);
-          assert_array_equals([...mutation.addedNodes], insertions);
-          assert_equals(mutation.removedNodes.length, 2);
-          assert_array_equals([...mutation.removedNodes], children);
+          assert_equals(mutation.type, "childList", "mutation.type");
+          assert_equals(mutation.addedNodes.length, 2, "added nodes length");
+          assert_array_equals([...mutation.addedNodes], insertions, "added nodes");
+          assert_equals(mutation.removedNodes.length, 2, "removed nodes length");
+          assert_array_equals([...mutation.removedNodes], children, "removed nodes");
         });
         t.done();
       });
       observer.observe(parent, { childList: true });
 
       parent.replaceChildren(...previousParent.children);
     }, `${nodeName}.replaceChildren() should move nodes in the right order`);
   }