Bug 1269155 - Revise Node.rootNode to Node.getRootNode. r=smaug
authorJohn Dai <jdai@mozilla.com>
Mon, 16 Jan 2017 01:46:00 +0800
changeset 329651 14a12ac05a4b9dddeb6919540ab05af08e730912
parent 329650 87a0e0ccdc3417c33414bb91c3199e9b99dfa025
child 329652 4ac5466b4c416faabe9f4a3498e82d682203447d
push id31220
push userryanvm@gmail.com
push dateTue, 17 Jan 2017 16:30:26 +0000
treeherdermozilla-central@6a23526fe516 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1269155
milestone53.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 1269155 - Revise Node.rootNode to Node.getRootNode. r=smaug
dom/base/nsINode.cpp
dom/base/nsINode.h
dom/tests/mochitest/webcomponents/mochitest.ini
dom/tests/mochitest/webcomponents/test_bug1269155.html
dom/webidl/Node.webidl
modules/libpref/init/all.js
testing/web-platform/meta/dom/historical.html.ini
testing/web-platform/meta/dom/interfaces.html.ini
testing/web-platform/meta/dom/nodes/rootNode.html.ini
--- a/dom/base/nsINode.cpp
+++ b/dom/base/nsINode.cpp
@@ -100,16 +100,17 @@
 #include "WrapperFactory.h"
 #include "DocumentType.h"
 #include <algorithm>
 #include "nsGlobalWindow.h"
 #include "nsDOMMutationObserver.h"
 #include "GeometryUtils.h"
 #include "nsIAnimationObserver.h"
 #include "nsChildContentList.h"
+#include "mozilla/dom/NodeBinding.h"
 
 #ifdef ACCESSIBILITY
 #include "mozilla/dom/AccessibleNode.h"
 #endif
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
@@ -240,16 +241,40 @@ nsINode::GetTextEditorRootContent(nsIEdi
     nsIContent* rootContent = GetEditorRootContent(editor);
     if (aEditor)
       editor.swap(*aEditor);
     return rootContent;
   }
   return nullptr;
 }
 
+nsINode* nsINode::GetRootNode(const GetRootNodeOptions& aOptions)
+{
+  if (aOptions.mComposed) {
+    if (IsInComposedDoc() && GetComposedDoc()) {
+      return OwnerDoc();
+    }
+
+    nsINode* node = this;
+    ShadowRoot* shadowRootParent = nullptr;
+    while(node) {
+      node = node->SubtreeRoot();
+      shadowRootParent = ShadowRoot::FromNode(node);
+      if (!shadowRootParent) {
+         break;
+      }
+      node = shadowRootParent->GetHost();
+    }
+
+    return node;
+  }
+
+  return SubtreeRoot();
+}
+
 nsINode*
 nsINode::SubtreeRoot() const
 {
   // There are four cases of interest here.  nsINodes that are really:
   // 1. nsIDocument nodes - Are always in the document.
   // 2.a nsIContent nodes not in a shadow tree - Are either in the document,
   //     or mSubtreeRoot is updated in BindToTree/UnbindFromTree.
   // 2.b nsIContent nodes in a shadow tree - Are never in the document,
--- a/dom/base/nsINode.h
+++ b/dom/base/nsINode.h
@@ -77,16 +77,17 @@ class DOMRectReadOnly;
 class Element;
 class EventHandlerNonNull;
 template<typename T> class Optional;
 class OwningNodeOrString;
 template<typename> class Sequence;
 class Text;
 class TextOrElementOrDocument;
 struct DOMPointInit;
+struct GetRootNodeOptions;
 } // namespace dom
 } // namespace mozilla
 
 #define NODE_FLAG_BIT(n_) \
   (nsWrapperCache::FlagsType(1U) << (WRAPPER_CACHE_FLAGS_BITS_USED + (n_)))
 
 enum {
   // This bit will be set if the node has a listener manager.
@@ -943,20 +944,21 @@ public:
 
   /**
    * Get the root of the subtree this node belongs to.  This never returns
    * null.  It may return 'this' (e.g. for document nodes, and nodes that
    * are the roots of disconnected subtrees).
    */
   nsINode* SubtreeRoot() const;
 
-  nsINode* RootNode() const
-  {
-    return SubtreeRoot();
-  }
+  /*
+   * Get context object's shadow-including root if options's composed is true,
+   * and context object's root otherwise.
+   */
+  nsINode* GetRootNode(const mozilla::dom::GetRootNodeOptions& aOptions);
 
   /**
    * See nsIDOMEventTarget
    */
   NS_DECL_NSIDOMEVENTTARGET
 
   virtual mozilla::EventListenerManager*
     GetExistingListenerManager() const override;
--- a/dom/tests/mochitest/webcomponents/mochitest.ini
+++ b/dom/tests/mochitest/webcomponents/mochitest.ini
@@ -47,8 +47,9 @@ support-files =
 [test_shadowroot_host.html]
 [test_shadowroot_style.html]
 [test_shadowroot_style_multiple_shadow.html]
 [test_shadowroot_style_order.html]
 [test_shadowroot_youngershadowroot.html]
 [test_style_fallback_content.html]
 [test_unresolved_pseudo_class.html]
 [test_link_prefetch.html]
+[test_bug1269155.html]
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/webcomponents/test_bug1269155.html
@@ -0,0 +1,89 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1269155
+-->
+<head>
+  <title>Test for Bug 1269155</title>
+  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank"
+	href="https://bugzilla.mozilla.org/show_bug.cgi?id=1269155">Mozilla Bug 1269155</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 1269155 **/
+var host = document.querySelector('#content');
+ var root = host.createShadowRoot();
+
+ var header1 = document.createElement('h1');
+ header1.textContent = 'Shadow Header1';
+
+ var paragraph1 = document.createElement('p');
+ paragraph1.textContent = 'shadow text paragraph1';
+
+ root.appendChild(header1);
+ root.appendChild(paragraph1);
+
+var root2 = paragraph1.createShadowRoot();
+var header2 = document.createElement('h2');
+header2.textContent = 'Shadow Header2';
+
+var paragraph2 = document.createElement('p');
+paragraph2.textContent = 'shadow text paragraph2';
+root2.appendChild(header2);
+root2.appendChild(paragraph2);
+
+
+var frag = document.createDocumentFragment();
+var paragraph3 = document.createElement('p');
+paragraph3.textContent = 'fragment paragraph3';
+frag.appendChild(paragraph3);
+
+var root3 = paragraph3.createShadowRoot();
+var header4 = document.createElement('h2');
+header4.textContent = 'Shadow Header3';
+
+var paragraph4 = document.createElement('p');
+paragraph4.textContent = 'shadow text paragraph4';
+
+root3.appendChild(header4);
+root3.appendChild(paragraph4);
+
+//shadow dom without compose
+is(root.getRootNode(), root, "root.getRootNode() should be root.");
+is(root2.getRootNode(), root2, "root2.getRootNode() should be root.");
+is(root3.getRootNode(), root3, "root3.getRootNode() should be root.");
+is(header1.getRootNode(), root, "header1.getRootNode() should be root.");
+is(header2.getRootNode(), root2, "header1.getRootNode() should be root2.");
+is(header4.getRootNode(), root3, "header1.getRootNode() should be root3.");
+//shadow dom with compose
+is(root.getRootNode({ composed: true }), document, "root.getRootNode() with composed flag should be document.");
+is(root2.getRootNode({ composed: true }), document, "root2.getRootNode() with composed flag should be document.");
+is(root3.getRootNode({ composed: true }), frag, "root3.getRootNode() with composed flag should be frag.");
+is(header1.getRootNode({ composed: true }) , document, "header1.getRootNode() with composed flag should be document.");
+is(header2.getRootNode({ composed: true }) , document, "header2.getRootNode() with composed flag should be document.");
+is(header4.getRootNode({ composed: true }) , frag, "head4.getRootNode() with composed flag should be frag.");
+//dom without compose
+is(host.getRootNode(), document, "host.getRootNode() should be document.");
+is(header1.getRootNode(), root, "header1.getRootNode() should be root.");
+is(paragraph1.getRootNode(), root, "paragraph1.getRootNode() should be root.");
+is(frag.getRootNode(), frag, "frag.getRootNode() should be frag.");
+//dom with compose
+is(host.getRootNode({ composed: true }) , document, "host.getRootNode() with composed flag should be document.");
+is(header1.getRootNode({ composed: true }) , document, "header1.getRootNode() with composed flag should be document.");
+is(paragraph1.getRootNode({ composed: true }) , document, "paragraph1.getRootNode() with composed flag should be document.");
+is(frag.getRootNode({ composed: true }) , frag, "frag.getRootNode() with composed flag should be frag.");
+
+</script>
+</pre>
+</body>
+</html>
+
--- a/dom/webidl/Node.webidl
+++ b/dom/webidl/Node.webidl
@@ -33,18 +33,18 @@ interface Node : EventTarget {
 
   [Pure, Throws]
   readonly attribute DOMString? baseURI;
 
   [Pure, BinaryName=getComposedDoc]
   readonly attribute boolean isConnected;
   [Pure]
   readonly attribute Document? ownerDocument;
-  [Pure, Pref="dom.node.rootNode.enabled"]
-  readonly attribute Node rootNode;
+  [Pure]
+  Node getRootNode(optional GetRootNodeOptions options);
   [Pure]
   readonly attribute Node? parentNode;
   [Pure]
   readonly attribute Element? parentElement;
   [Pure]
   boolean hasChildNodes();
   [SameObject]
   readonly attribute NodeList childNodes;
@@ -108,8 +108,12 @@ interface Node : EventTarget {
   [ChromeOnly]
   sequence<MutationObserver> getBoundMutationObservers();
 
 #ifdef ACCESSIBILITY
   [Pref="accessibility.AOM.enabled"]
   readonly attribute AccessibleNode? accessibleNode;
 #endif
 };
+
+dictionary GetRootNodeOptions {
+  boolean composed = false;
+};
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -5493,23 +5493,16 @@ pref("plugins.navigator_hide_disabled_fl
 // Disable browser frames by default
 pref("dom.mozBrowserFramesEnabled", false);
 
 // Is support for 'color-adjust' CSS property enabled?
 pref("layout.css.color-adjust.enabled", true);
 
 pref("dom.audiochannel.audioCompeting", false);
 
-// Disable Node.rootNode in release builds.
-#ifdef RELEASE_OR_BETA
-pref("dom.node.rootNode.enabled", false);
-#else
-pref("dom.node.rootNode.enabled", true);
-#endif
-
 // Default media volume
 pref("media.default_volume", "1.0");
 
 // Once bug 1276272 is resolved, we will trun this preference to default ON in
 // non-release channels.
 #ifdef RELEASE_OR_BETA
 pref("media.seekToNextFrame.enabled", false);
 #else
--- a/testing/web-platform/meta/dom/historical.html.ini
+++ b/testing/web-platform/meta/dom/historical.html.ini
@@ -6,13 +6,8 @@
 
   [Historical DOM features must be removed: DOMError]
     expected: FAIL
     bug: 1120178
 
   [Historical DOM features must be removed: createCDATASection]
     expected: FAIL
     bug: 660660
-
-  [Node member must be nuked: rootNode]
-    disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1303802
-    bug: 1269155
-
--- a/testing/web-platform/meta/dom/interfaces.html.ini
+++ b/testing/web-platform/meta/dom/interfaces.html.ini
@@ -1,11 +1,10 @@
 [interfaces.html]
   type: testharness
-  prefs: [dom.node.rootNode.enabled:true]
   [Document interface: attribute origin]
     expected: FAIL
     bug: 931884
 
   [Document interface: xmlDoc must inherit property "origin" with the proper type (3)]
     expected: FAIL
     bug: 931884
 
@@ -42,65 +41,8 @@
     expected: FAIL
 
   [Text interface: attribute assignedSlot]
     expected: FAIL
 
   [Text interface: document.createTextNode("abc") must inherit property "assignedSlot" with the proper type (2)]
     expected: FAIL
 
-  [Node interface: operation getRootNode(GetRootNodeOptions)]
-    expected: FAIL
-
-  [Node interface: new Document() must inherit property "getRootNode" with the proper type (17)]
-    expected: FAIL
-
-  [Node interface: calling getRootNode(GetRootNodeOptions) on new Document() with too few arguments must throw TypeError]
-    expected: FAIL
-
-  [Node interface: xmlDoc must inherit property "getRootNode" with the proper type (17)]
-    expected: FAIL
-
-  [Node interface: calling getRootNode(GetRootNodeOptions) on xmlDoc with too few arguments must throw TypeError]
-    expected: FAIL
-
-  [Node interface: document.doctype must inherit property "getRootNode" with the proper type (17)]
-    expected: FAIL
-
-  [Node interface: calling getRootNode(GetRootNodeOptions) on document.doctype with too few arguments must throw TypeError]
-    expected: FAIL
-
-  [Node interface: document.createDocumentFragment() must inherit property "getRootNode" with the proper type (17)]
-    expected: FAIL
-
-  [Node interface: calling getRootNode(GetRootNodeOptions) on document.createDocumentFragment() with too few arguments must throw TypeError]
-    expected: FAIL
-
-  [Node interface: element must inherit property "getRootNode" with the proper type (17)]
-    expected: FAIL
-
-  [Node interface: calling getRootNode(GetRootNodeOptions) on element with too few arguments must throw TypeError]
-    expected: FAIL
-
-  [Node interface: document.querySelector("[id\]").attributes[0\] must inherit property "getRootNode" with the proper type (17)]
-    expected: FAIL
-
-  [Node interface: calling getRootNode(GetRootNodeOptions) on document.querySelector("[id\]").attributes[0\] with too few arguments must throw TypeError]
-    expected: FAIL
-
-  [Node interface: document.createTextNode("abc") must inherit property "getRootNode" with the proper type (17)]
-    expected: FAIL
-
-  [Node interface: calling getRootNode(GetRootNodeOptions) on document.createTextNode("abc") with too few arguments must throw TypeError]
-    expected: FAIL
-
-  [Node interface: xmlDoc.createProcessingInstruction("abc", "def") must inherit property "getRootNode" with the proper type (17)]
-    expected: FAIL
-
-  [Node interface: calling getRootNode(GetRootNodeOptions) on xmlDoc.createProcessingInstruction("abc", "def") with too few arguments must throw TypeError]
-    expected: FAIL
-
-  [Node interface: document.createComment("abc") must inherit property "getRootNode" with the proper type (17)]
-    expected: FAIL
-
-  [Node interface: calling getRootNode(GetRootNodeOptions) on document.createComment("abc") with too few arguments must throw TypeError]
-    expected: FAIL
-
deleted file mode 100644
--- a/testing/web-platform/meta/dom/nodes/rootNode.html.ini
+++ /dev/null
@@ -1,15 +0,0 @@
-[rootNode.html]
-  type: testharness
-  prefs: [dom.node.rootNode.enabled:true]
-  [getRootNode() must return the context object when it does not have any parent]
-    expected: FAIL
-
-  [getRootNode() must return the parent node of the context object when the context object has a single ancestor not in a document]
-    expected: FAIL
-
-  [getRootNode() must return the document when a node is in document]
-    expected: FAIL
-
-  [getRootNode() must return a document fragment when a node is in the fragment]
-    expected: FAIL
-