Rename Node.treeRoot to rootNode and turn it on by default
authorrniwa@webkit.org <rniwa@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 9 Mar 2016 22:29:33 +0000 (22:29 +0000)
committerrniwa@webkit.org <rniwa@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 9 Mar 2016 22:29:33 +0000 (22:29 +0000)
https://bugs.webkit.org/show_bug.cgi?id=155226

Reviewed by Antonio Gomes.
Source/WebCore:

Node.prototype.treeRoot has been merged into DOM spec from Shadow DOM spec and renamed to rootNode:
https://dom.spec.whatwg.org/#dom-node-rootnode

Rename the method and expose it unconditionally on Node.prototype.

Tests: fast/dom/Node/rootNode.html
       fast/shadow-dom/Node-interface-rootNode.html

* dom/ContainerNode.h:
(WebCore::Node::highestAncestor): Deleted. There is no need for this function to be inlined.
* dom/Document.h: Now that both TreeScope and Node defines rootNode, we need to pick either.
Here, we pick TreeScope's definition since Document is by definition always in a document so there is
no need to even check inTreeScope().
* dom/Node.cpp:
(WebCore::Node::rootNode): Moved here. Also added a fast path for when "this" node is in a document
or a shadow root since TreeScope stores its root node as a member variable (m_rootNode).
* dom/Node.h:
* dom/Node.idl: Renamed the method and removed Conditional=SHADOW_DOM.
* dom/ShadowRoot.h: Similar to the change in Document.h. See above.
* editing/Editor.cpp:
(WebCore::correctSpellcheckingPreservingTextCheckingParagraph): Use rootNode instead of free function
defined in htmlediting.cpp, which was removed in this patch.
* editing/htmlediting.cpp:
(WebCore::highestAncestor): Deleted.
* editing/htmlediting.h:
* html/FormAssociatedElement.cpp:
(WebCore::computeRootNode): Added.
(WebCore::FormAssociatedElement::removedFrom): We can't use Node::rootNode here because this function
is called in the middle of removing a subtree, and some associated form element's inDocument flag may
not have been updated yet. So use computeRootNode to manually find the highest ancestor.
(WebCore::FormAssociatedElement::formRemovedFromTree): Ditto.
* xml/XPathPath.cpp:
(WebCore::XPath::LocationPath::evaluate):

LayoutTests:

Split Node-interface-treeRoot.html into two pieces, the one that doesn't invoke shadow DOM and the other that tests
shadow DOM related cases. I intend to upstream these tests to W3C at some point so keep them in testharness.js form.

* fast/dom/Node/rootNode-expected.txt: Added.
* fast/dom/Node/rootNode.html: Copied from LayoutTests/fast/shadow-dom/Node-interface-treeRoot.html.
* fast/shadow-dom/Node-interface-rootNode-expected.txt: Renamed from Node-interface-treeRoot-expected.txt.
* fast/shadow-dom/Node-interface-rootNode.html: Renamed from LayoutTests/fast/shadow-dom/Node-interface-treeRoot.html.
* js/dom/dom-static-property-for-in-iteration-expected.txt:
* platform/efl/js/dom/dom-static-property-for-in-iteration-expected.txt:
* platform/gtk/js/dom/dom-static-property-for-in-iteration-expected.txt:

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

21 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/dom/Node/rootNode-expected.txt [new file with mode: 0644]
LayoutTests/fast/dom/Node/rootNode.html [new file with mode: 0644]
LayoutTests/fast/shadow-dom/Node-interface-rootNode-expected.txt [moved from LayoutTests/fast/shadow-dom/Node-interface-treeRoot-expected.txt with 52% similarity]
LayoutTests/fast/shadow-dom/Node-interface-rootNode.html [new file with mode: 0644]
LayoutTests/fast/shadow-dom/Node-interface-treeRoot.html [deleted file]
LayoutTests/js/dom/dom-static-property-for-in-iteration-expected.txt
LayoutTests/platform/efl/js/dom/dom-static-property-for-in-iteration-expected.txt
LayoutTests/platform/gtk/js/dom/dom-static-property-for-in-iteration-expected.txt
Source/WebCore/ChangeLog
Source/WebCore/dom/ContainerNode.h
Source/WebCore/dom/Document.h
Source/WebCore/dom/Node.cpp
Source/WebCore/dom/Node.h
Source/WebCore/dom/Node.idl
Source/WebCore/dom/ShadowRoot.h
Source/WebCore/editing/Editor.cpp
Source/WebCore/editing/htmlediting.cpp
Source/WebCore/editing/htmlediting.h
Source/WebCore/html/FormAssociatedElement.cpp
Source/WebCore/xml/XPathPath.cpp

index bfdc375..16c0a54 100644 (file)
@@ -1,3 +1,21 @@
+2016-03-09  Ryosuke Niwa  <rniwa@webkit.org>
+
+        Rename Node.treeRoot to rootNode and turn it on by default
+        https://bugs.webkit.org/show_bug.cgi?id=155226
+
+        Reviewed by Antonio Gomes.
+        
+        Split Node-interface-treeRoot.html into two pieces, the one that doesn't invoke shadow DOM and the other that tests
+        shadow DOM related cases. I intend to upstream these tests to W3C at some point so keep them in testharness.js form.
+
+        * fast/dom/Node/rootNode-expected.txt: Added.
+        * fast/dom/Node/rootNode.html: Copied from LayoutTests/fast/shadow-dom/Node-interface-treeRoot.html.
+        * fast/shadow-dom/Node-interface-rootNode-expected.txt: Renamed from Node-interface-treeRoot-expected.txt.
+        * fast/shadow-dom/Node-interface-rootNode.html: Renamed from LayoutTests/fast/shadow-dom/Node-interface-treeRoot.html.
+        * js/dom/dom-static-property-for-in-iteration-expected.txt:
+        * platform/efl/js/dom/dom-static-property-for-in-iteration-expected.txt:
+        * platform/gtk/js/dom/dom-static-property-for-in-iteration-expected.txt:
+
 2016-03-09  Michael Saboff  <msaboff@apple.com>
 
         [ES6] Implement RegExp sticky flag and related functionality
diff --git a/LayoutTests/fast/dom/Node/rootNode-expected.txt b/LayoutTests/fast/dom/Node/rootNode-expected.txt
new file mode 100644 (file)
index 0000000..ee0805e
--- /dev/null
@@ -0,0 +1,7 @@
+
+PASS rootNode attribute must be defined on Node interface 
+PASS rootNode attribute must return the context object when it does not have any parent 
+PASS rootNode attribute must return the parent node of the context object when the context object has a single ancestor not in a document 
+PASS rootNode attribute must return the document when a node is in document 
+PASS rootNode attribute must return a document fragment when a node is in the fragment 
+
diff --git a/LayoutTests/fast/dom/Node/rootNode.html b/LayoutTests/fast/dom/Node/rootNode.html
new file mode 100644 (file)
index 0000000..9702c0e
--- /dev/null
@@ -0,0 +1,94 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>DOM: 4.4. Interface Node</title>
+<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
+<meta name="assert" content="Node interface must have rootNode attribute">
+<link rel="help" href="https://dom.spec.whatwg.org/#dom-node-rootnode">
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<link rel='stylesheet' href='../../../resources/testharness.css'>
+</head>
+<body>
+<div id="log"></div>
+<script>
+
+test(function () {
+    assert_true('rootNode' in Node.prototype, 'rootNode must be defined on Node.prototype');
+    assert_true('rootNode' in document.createElement('div'), 'rootNode must be defined on a div element');
+    assert_true('rootNode' in document.createTextNode(''), 'rootNode must be defined on a text node');
+    assert_true('rootNode' in document.createComment(''), 'rootNode must be defined on a comment node');
+    assert_true('rootNode' in document.createProcessingInstruction('target', 'data'), 'rootNode must be defined on a processing instruction node');
+    assert_true('rootNode' in document, 'rootNode must be defined on a document node');
+}, 'rootNode attribute must be defined on Node interface');
+
+test(function () {
+    var element = document.createElement('div');
+    assert_equals(element.rootNode, element, 'rootNode on an element without a parent must return the element itself');
+
+    var text = document.createTextNode('');
+    assert_equals(text.rootNode, text, 'rootNode on a text node without a parent must return the text node itself');
+
+    var processingInstruction = document.createProcessingInstruction('target', 'data');
+    assert_equals(processingInstruction.rootNode, processingInstruction, 'rootNode on a processing instruction node without a parent must return the processing instruction node itself');
+
+    assert_equals(document.rootNode, document, 'rootNode on a document node must return the document itself');
+
+}, 'rootNode attribute must return the context object when it does not have any parent');
+
+test(function () {
+    var parent = document.createElement('div');
+
+    var element = document.createElement('div');
+    parent.appendChild(element);
+    assert_equals(element.rootNode, parent, 'rootNode on an element with a single ancestor must return the parent node');
+
+    var text = document.createTextNode('');
+    parent.appendChild(text);
+    assert_equals(text.rootNode, parent, 'rootNode on a text node with a single ancestor must return the parent node');
+
+    var processingInstruction = document.createProcessingInstruction('target', 'data');
+    parent.appendChild(processingInstruction)
+    assert_equals(processingInstruction.rootNode, parent, 'rootNode on a processing instruction node with a single ancestor must return the parent node');
+
+}, 'rootNode attribute must return the parent node of the context object when the context object has a single ancestor not in a document');
+
+test(function () {
+    var parent = document.createElement('div');
+    document.body.appendChild(parent);
+
+    var element = document.createElement('div');
+    parent.appendChild(element);
+    assert_equals(element.rootNode, document, 'rootNode on an element inside a document must return the document');
+
+    var text = document.createTextNode('');
+    parent.appendChild(text);
+    assert_equals(text.rootNode, document, 'rootNode on a text node inside a document must return the document');
+
+    var processingInstruction = document.createProcessingInstruction('target', 'data');
+    parent.appendChild(processingInstruction)
+    assert_equals(processingInstruction.rootNode, document, 'rootNode on a processing instruction node inside a document must return the document');
+}, 'rootNode attribute must return the document when a node is in document');
+
+test(function () {
+    var fragment = document.createDocumentFragment();
+    var parent = document.createElement('div');
+    fragment.appendChild(parent);
+
+    var element = document.createElement('div');
+    parent.appendChild(element);
+    assert_equals(element.rootNode, fragment, 'rootNode on an element inside a document fragment must return the fragment');
+
+    var text = document.createTextNode('');
+    parent.appendChild(text);
+    assert_equals(text.rootNode, fragment, 'rootNode on a text node inside a document fragment must return the fragment');
+
+    var processingInstruction = document.createProcessingInstruction('target', 'data');
+    parent.appendChild(processingInstruction)
+    assert_equals(processingInstruction.rootNode, fragment,
+        'rootNode on a processing instruction node inside a document fragment must return the fragment');
+}, 'rootNode attribute must return a document fragment when a node is in the fragment');
+
+</script>
+</body>
+</html>
@@ -1,12 +1,12 @@
 
-PASS treeRoot attribute must be defined on Node interface 
-PASS treeRoot attribute must return the context object when it does not have any parent 
-PASS treeRoot attribute must return the parent node of the context object when the context object has a single ancestor not in a document 
-PASS treeRoot attribute must return the document when a node is in document and not in a shadow tree 
-PASS treeRoot attribute must return the open shadow root of the context object when the shadow host is in a document 
-PASS treeRoot attribute must return the closed shadow root of the context object when the shadow host is in a document 
-PASS treeRoot attribute must return the root node of the context object when the context object is inside a open shadow root whose shadow host is in another open shadow root 
-PASS treeRoot attribute must return the root node of the context object when the context object is inside a closed shadow root whose shadow host is in another open shadow root 
-PASS treeRoot attribute must return the root node of the context object when the context object is inside a open shadow root whose shadow host is in another closed shadow root 
-PASS treeRoot attribute must return the root node of the context object when the context object is inside a closed shadow root whose shadow host is in another closed shadow root 
+PASS rootNode attribute must be defined on ShadowRoot interface 
+PASS rootNode attribute must return the context object when it does not have any parent 
+PASS rootNode attribute must return the parent node of the context object when the context object has a single ancestor not in a document 
+PASS rootNode attribute must return the document when a node is in document and not in a shadow tree 
+PASS rootNode attribute must return the open shadow root of the context object when the shadow host is in a document 
+PASS rootNode attribute must return the closed shadow root of the context object when the shadow host is in a document 
+PASS rootNode attribute must return the root node of the context object when the context object is inside a open shadow root whose shadow host is in another open shadow root 
+PASS rootNode attribute must return the root node of the context object when the context object is inside a closed shadow root whose shadow host is in another open shadow root 
+PASS rootNode attribute must return the root node of the context object when the context object is inside a open shadow root whose shadow host is in another closed shadow root 
+PASS rootNode attribute must return the root node of the context object when the context object is inside a closed shadow root whose shadow host is in another closed shadow root 
 
diff --git a/LayoutTests/fast/shadow-dom/Node-interface-rootNode.html b/LayoutTests/fast/shadow-dom/Node-interface-rootNode.html
new file mode 100644 (file)
index 0000000..e77634f
--- /dev/null
@@ -0,0 +1,121 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Shadow DOM: Extensions to Node interface</title>
+<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
+<meta name="assert" content="Node interface must have rootNode attribute">
+<link rel="help" href="http://w3c.github.io/webcomponents/spec/shadow/#extensions-to-node-interface">
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<link rel='stylesheet' href='../../resources/testharness.css'>
+</head>
+<body>
+<div id="log"></div>
+<script>
+
+test(function () {
+    assert_true('rootNode' in document.createElement('div').attachShadow({mode: 'closed'}), 'rootNode must be defined on a closed shadow root element');
+    assert_true('rootNode' in document.createElement('div').attachShadow({mode: 'open'}), 'rootNode must be defined on a open shadow root element');
+}, 'rootNode attribute must be defined on ShadowRoot interface');
+
+test(function () {
+    var closedShadowRoot = document.createElement('div').attachShadow({mode: 'closed'});
+    assert_equals(closedShadowRoot.rootNode, closedShadowRoot, 'rootNode on a closed shadow root must return the shadow root itself');
+
+    var openShadowRoot = document.createElement('div').attachShadow({mode: 'open'});
+    assert_equals(openShadowRoot.rootNode, openShadowRoot, 'rootNode on a open shadow root must return the shadow root itself');
+}, 'rootNode attribute must return the context object when it does not have any parent');
+
+test(function () {
+    var parent = document.createElement('div');
+
+    var hostWithClosedShadowRoot = document.createElement('div');
+    parent.appendChild(hostWithClosedShadowRoot);
+    var closedShadowRoot = hostWithClosedShadowRoot.attachShadow({mode: 'closed'});
+    assert_equals(closedShadowRoot.rootNode, closedShadowRoot, 'rootNode on a closed shadow root with a single ancestor on its host must return the shadow root itself');
+
+    var hostWithOpenShadowRoot = document.createElement('div');
+    parent.appendChild(hostWithOpenShadowRoot);
+    var openShadowRoot = hostWithOpenShadowRoot.attachShadow({mode: 'open'});
+    assert_equals(openShadowRoot.rootNode, openShadowRoot, 'rootNode on a open shadow root with a single ancestor on its host must return the shadow root itself');
+}, 'rootNode attribute must return the parent node of the context object when the context object has a single ancestor not in a document');
+
+test(function () {
+    var parent = document.createElement('div');
+    document.body.appendChild(parent);
+
+    var element = document.createElement('div');
+    parent.appendChild(element);
+    assert_equals(element.rootNode, document, 'rootNode on an element inside a document must return the document');
+
+    var text = document.createTextNode('');
+    parent.appendChild(text);
+    assert_equals(text.rootNode, document, 'rootNode on a text node inside a document must return the document');
+
+    var processingInstruction = document.createProcessingInstruction('target', 'data');
+    parent.appendChild(processingInstruction)
+    assert_equals(processingInstruction.rootNode, document, 'rootNode on a processing instruction node inside a document must return the document');
+}, 'rootNode attribute must return the document when a node is in document and not in a shadow tree');
+
+function testrootNodeOnNodeInsideShadowTree(mode) {
+    test(function () {
+        var host = document.createElement('div');
+        document.body.appendChild(host);
+
+        var shadowRoot = host.attachShadow({mode: mode});
+        var parent = document.createElement('p');
+        shadowRoot.appendChild(parent);
+
+        var element = document.createElement('span');
+        parent.appendChild(element);
+        assert_equals(element.rootNode, shadowRoot, 'rootNode on an element inside a shadow tree must return the shadow root');
+
+        var text = document.createTextNode('');
+        parent.appendChild(text);
+        assert_equals(text.rootNode, shadowRoot, 'rootNode on a text node inside a shadow tree must return the shadow root');
+
+        var processingInstruction = document.createProcessingInstruction('target', 'data');
+        parent.appendChild(processingInstruction);
+        assert_equals(processingInstruction.rootNode, shadowRoot, 'rootNode on a processing instruction node inside a shadow tree must return the shadow root');
+    }, 'rootNode attribute must return the ' + mode + ' shadow root of the context object when the shadow host is in a document');
+}
+
+testrootNodeOnNodeInsideShadowTree('open');
+testrootNodeOnNodeInsideShadowTree('closed');
+
+function testrootNodeOnNodeInsideNestedShadowTree(outerMode, innerMode) {
+    test(function () {
+        var outerHost = document.createElement('div');
+        document.body.appendChild(outerHost);
+        var outerShadowRoot = outerHost.attachShadow({mode: outerMode});
+
+        var innerHost = document.createElement('section');
+        outerShadowRoot.appendChild(innerHost);
+        var innerShadowRoot = innerHost.attachShadow({mode: innerMode});
+
+        var parent = document.createElement('p');
+        innerShadowRoot.appendChild(parent);
+
+        var element = document.createElement('span');
+        parent.appendChild(element);
+        assert_equals(element.rootNode, innerShadowRoot, 'rootNode on an element inside a shadow tree must return its root node');
+
+        var text = document.createTextNode('');
+        parent.appendChild(text);
+        assert_equals(text.rootNode, innerShadowRoot, 'rootNode on a text node inside a shadow tree must return its root node');
+
+        var processingInstruction = document.createProcessingInstruction('target', 'data');
+        parent.appendChild(processingInstruction);
+        assert_equals(processingInstruction.rootNode, innerShadowRoot, 'rootNode on a processing instruction node inside a shadow tree must return its root node');
+    }, 'rootNode attribute must return the root node of the context object when the context object is inside a ' + innerMode
+        + ' shadow root whose shadow host is in another ' + outerMode + ' shadow root');
+}
+
+testrootNodeOnNodeInsideNestedShadowTree('open', 'open');
+testrootNodeOnNodeInsideNestedShadowTree('open', 'closed');
+testrootNodeOnNodeInsideNestedShadowTree('closed', 'open');
+testrootNodeOnNodeInsideNestedShadowTree('closed', 'closed');
+
+</script>
+</body>
+</html>
diff --git a/LayoutTests/fast/shadow-dom/Node-interface-treeRoot.html b/LayoutTests/fast/shadow-dom/Node-interface-treeRoot.html
deleted file mode 100644 (file)
index 1466561..0000000
+++ /dev/null
@@ -1,150 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-<title>Shadow DOM: Extensions to Node interface</title>
-<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
-<meta name="assert" content="Node interface must have treeRoot attribute">
-<link rel="help" href="http://w3c.github.io/webcomponents/spec/shadow/#extensions-to-node-interface">
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<link rel='stylesheet' href='../../resources/testharness.css'>
-</head>
-<body>
-<div id="log"></div>
-<script>
-
-test(function () {
-    assert_true('treeRoot' in Node.prototype, 'treeRoot must be defined on Node.prototype');
-    assert_true('treeRoot' in document.createElement('div'), 'treeRoot must be defined on a div element');
-    assert_true('treeRoot' in document.createTextNode(''), 'assignedSlot must be defined on a text node');
-    assert_true('treeRoot' in document.createComment(''), 'assignedSlot must be defined on a comment node');
-    assert_true('treeRoot' in document.createProcessingInstruction('target', 'data'), 'assignedSlot must be defined on a processing instruction node');
-    assert_true('treeRoot' in document, 'assignedSlot must be defined on a document node');
-    assert_true('treeRoot' in document.createElement('div').attachShadow({mode: 'closed'}), 'treeRoot must be defined on a closed shadow root element');
-    assert_true('treeRoot' in document.createElement('div').attachShadow({mode: 'open'}), 'treeRoot must be defined on a open shadow root element');
-}, 'treeRoot attribute must be defined on Node interface');
-
-test(function () {
-    var element = document.createElement('div');
-    assert_equals(element.treeRoot, element, 'treeRoot on an element without a parent must return the element itself');
-
-    var text = document.createTextNode('');
-    assert_equals(text.treeRoot, text, 'treeRoot on a text node without a parent must return the text node itself');
-
-    var processingInstruction = document.createProcessingInstruction('target', 'data');
-    assert_equals(processingInstruction.treeRoot, processingInstruction, 'treeRoot on a processing instruction node without a parent must return the processing instruction node itself');
-
-    assert_equals(document.treeRoot, document, 'treeRoot on a document node must return the document itself');
-
-    var closedShadowRoot = document.createElement('div').attachShadow({mode: 'closed'});
-    assert_equals(closedShadowRoot.treeRoot, closedShadowRoot, 'treeRoot on a closed shadow root must return the shadow root itself');
-
-    var openShadowRoot = document.createElement('div').attachShadow({mode: 'open'});
-    assert_equals(openShadowRoot.treeRoot, openShadowRoot, 'treeRoot on a open shadow root must return the shadow root itself');
-}, 'treeRoot attribute must return the context object when it does not have any parent');
-
-test(function () {
-    var parent = document.createElement('div');
-
-    var element = document.createElement('div');
-    parent.appendChild(element);
-    assert_equals(element.treeRoot, parent, 'treeRoot on an element with a single ancestor must return the parent node');
-
-    var text = document.createTextNode('');
-    parent.appendChild(text);
-    assert_equals(text.treeRoot, parent, 'treeRoot on a text node with a single ancestor must return the parent node');
-
-    var processingInstruction = document.createProcessingInstruction('target', 'data');
-    parent.appendChild(processingInstruction)
-    assert_equals(processingInstruction.treeRoot, parent, 'treeRoot on a processing instruction node with a single ancestor must return the parent node');
-
-    var hostWithClosedShadowRoot = document.createElement('div');
-    parent.appendChild(hostWithClosedShadowRoot);
-    var closedShadowRoot = hostWithClosedShadowRoot.attachShadow({mode: 'closed'});
-    assert_equals(closedShadowRoot.treeRoot, closedShadowRoot, 'treeRoot on a closed shadow root with a single ancestor on its host must return the shadow root itself');
-
-    var hostWithOpenShadowRoot = document.createElement('div');
-    parent.appendChild(hostWithOpenShadowRoot);
-    var openShadowRoot = hostWithOpenShadowRoot.attachShadow({mode: 'open'});
-    assert_equals(openShadowRoot.treeRoot, openShadowRoot, 'treeRoot on a open shadow root with a single ancestor on its host must return the shadow root itself');
-}, 'treeRoot attribute must return the parent node of the context object when the context object has a single ancestor not in a document');
-
-test(function () {
-    var parent = document.createElement('div');
-    document.body.appendChild(parent);
-
-    var element = document.createElement('div');
-    parent.appendChild(element);
-    assert_equals(element.treeRoot, document, 'treeRoot on an element inside a document must return the document');
-
-    var text = document.createTextNode('');
-    parent.appendChild(text);
-    assert_equals(text.treeRoot, document, 'treeRoot on a text node inside a document must return the document');
-
-    var processingInstruction = document.createProcessingInstruction('target', 'data');
-    parent.appendChild(processingInstruction)
-    assert_equals(processingInstruction.treeRoot, document, 'treeRoot on a processing instruction node inside a document must return the document');
-}, 'treeRoot attribute must return the document when a node is in document and not in a shadow tree');
-
-function testTreeRootOnNodeInsideShadowTree(mode) {
-    test(function () {
-        var host = document.createElement('div');
-        document.body.appendChild(host);
-
-        var shadowRoot = host.attachShadow({mode: mode});
-        var parent = document.createElement('p');
-        shadowRoot.appendChild(parent);
-
-        var element = document.createElement('span');
-        parent.appendChild(element);
-        assert_equals(element.treeRoot, shadowRoot, 'treeRoot on an element inside a shadow tree must return the shadow root');
-
-        var text = document.createTextNode('');
-        parent.appendChild(text);
-        assert_equals(text.treeRoot, shadowRoot, 'treeRoot on a text node inside a shadow tree must return the shadow root');
-
-        var processingInstruction = document.createProcessingInstruction('target', 'data');
-        parent.appendChild(processingInstruction);
-        assert_equals(processingInstruction.treeRoot, shadowRoot, 'treeRoot on a processing instruction node inside a shadow tree must return the shadow root');
-    }, 'treeRoot attribute must return the ' + mode + ' shadow root of the context object when the shadow host is in a document');
-}
-
-testTreeRootOnNodeInsideShadowTree('open');
-testTreeRootOnNodeInsideShadowTree('closed');
-
-function testTreeRootOnNodeInsideNestedShadowTree(outerMode, innerMode) {
-    test(function () {
-        var outerHost = document.createElement('div');
-        document.body.appendChild(outerHost);
-        var outerShadowRoot = outerHost.attachShadow({mode: outerMode});
-
-        var innerHost = document.createElement('section');
-        outerShadowRoot.appendChild(innerHost);
-        var innerShadowRoot = innerHost.attachShadow({mode: innerMode});
-
-        var parent = document.createElement('p');
-        innerShadowRoot.appendChild(parent);
-
-        var element = document.createElement('span');
-        parent.appendChild(element);
-        assert_equals(element.treeRoot, innerShadowRoot, 'treeRoot on an element inside a shadow tree must return its root node');
-
-        var text = document.createTextNode('');
-        parent.appendChild(text);
-        assert_equals(text.treeRoot, innerShadowRoot, 'treeRoot on a text node inside a shadow tree must return its root node');
-
-        var processingInstruction = document.createProcessingInstruction('target', 'data');
-        parent.appendChild(processingInstruction);
-        assert_equals(processingInstruction.treeRoot, innerShadowRoot, 'treeRoot on a processing instruction node inside a shadow tree must return its root node');
-    }, 'treeRoot attribute must return the root node of the context object when the context object is inside a ' + innerMode
-        + ' shadow root whose shadow host is in another ' + outerMode + ' shadow root');
-}
-
-testTreeRootOnNodeInsideNestedShadowTree('open', 'open');
-testTreeRootOnNodeInsideNestedShadowTree('open', 'closed');
-testTreeRootOnNodeInsideNestedShadowTree('closed', 'open');
-testTreeRootOnNodeInsideNestedShadowTree('closed', 'closed');
-
-</script>
-</body>
-</html>
index 77b0eec..e488cf9 100644 (file)
@@ -142,7 +142,7 @@ PASS a["lastChild"] is [object Text]
 PASS a["previousSibling"] is [object Text]
 PASS a["nextSibling"] is [object Text]
 PASS a["ownerDocument"] is [object HTMLDocument]
-PASS a["treeRoot"] is [object HTMLDocument]
+PASS a["rootNode"] is [object HTMLDocument]
 PASS a["namespaceURI"] is http://www.w3.org/1999/xhtml
 PASS a["prefix"] is null
 PASS a["localName"] is a
index 3c17cde..19dbf92 100644 (file)
@@ -77,6 +77,7 @@ PASS a["prefix"] is null
 PASS a["childNodes"] is [object NodeList]
 PASS a["nextSibling"] is [object Text]
 PASS a["ownerDocument"] is [object HTMLDocument]
+PASS a["rootNode"] is [object HTMLDocument]
 PASS a["namespaceURI"] is http://www.w3.org/1999/xhtml
 PASS a["localName"] is a
 PASS a["parentElement"] is [object HTMLBodyElement]
index dc679e4..0affa88 100644 (file)
@@ -81,6 +81,7 @@ PASS a["lastChild"] is [object Text]
 PASS a["previousSibling"] is [object Text]
 PASS a["nextSibling"] is [object Text]
 PASS a["ownerDocument"] is [object HTMLDocument]
+PASS a["rootNode"] is [object HTMLDocument]
 PASS a["namespaceURI"] is http://www.w3.org/1999/xhtml
 PASS a["prefix"] is null
 PASS a["localName"] is a
index 585390d..0f8aaee 100644 (file)
@@ -1,3 +1,44 @@
+2016-03-09  Ryosuke Niwa  <rniwa@webkit.org>
+
+        Rename Node.treeRoot to rootNode and turn it on by default
+        https://bugs.webkit.org/show_bug.cgi?id=155226
+
+        Reviewed by Antonio Gomes.
+
+        Node.prototype.treeRoot has been merged into DOM spec from Shadow DOM spec and renamed to rootNode:
+        https://dom.spec.whatwg.org/#dom-node-rootnode
+
+        Rename the method and expose it unconditionally on Node.prototype.
+
+        Tests: fast/dom/Node/rootNode.html
+               fast/shadow-dom/Node-interface-rootNode.html
+
+        * dom/ContainerNode.h:
+        (WebCore::Node::highestAncestor): Deleted. There is no need for this function to be inlined.
+        * dom/Document.h: Now that both TreeScope and Node defines rootNode, we need to pick either.
+        Here, we pick TreeScope's definition since Document is by definition always in a document so there is
+        no need to even check inTreeScope().
+        * dom/Node.cpp:
+        (WebCore::Node::rootNode): Moved here. Also added a fast path for when "this" node is in a document
+        or a shadow root since TreeScope stores its root node as a member variable (m_rootNode).
+        * dom/Node.h:
+        * dom/Node.idl: Renamed the method and removed Conditional=SHADOW_DOM.
+        * dom/ShadowRoot.h: Similar to the change in Document.h. See above.
+        * editing/Editor.cpp:
+        (WebCore::correctSpellcheckingPreservingTextCheckingParagraph): Use rootNode instead of free function
+        defined in htmlediting.cpp, which was removed in this patch.
+        * editing/htmlediting.cpp:
+        (WebCore::highestAncestor): Deleted.
+        * editing/htmlediting.h:
+        * html/FormAssociatedElement.cpp:
+        (WebCore::computeRootNode): Added.
+        (WebCore::FormAssociatedElement::removedFrom): We can't use Node::rootNode here because this function
+        is called in the middle of removing a subtree, and some associated form element's inDocument flag may
+        not have been updated yet. So use computeRootNode to manually find the highest ancestor.
+        (WebCore::FormAssociatedElement::formRemovedFromTree): Ditto.
+        * xml/XPathPath.cpp:
+        (WebCore::XPath::LocationPath::evaluate):
+
 2016-03-09  Konstantin Tokarev  <annulen@yandex.ru>
 
         [cmake] Fixed All-in-One build.
index 5e5feb2..1ce4f80 100644 (file)
@@ -212,15 +212,6 @@ inline Node* Node::lastChild() const
     return downcast<ContainerNode>(*this).lastChild();
 }
 
-inline Node* Node::highestAncestor() const
-{
-    Node* node = const_cast<Node*>(this);
-    Node* highest = node;
-    for (; node; node = node->parentNode())
-        highest = node;
-    return highest;
-}
-
 inline bool Node::isTreeScope() const
 {
     return &treeScope().rootNode() == this;
index f49162b..59156db 100644 (file)
@@ -340,6 +340,7 @@ public:
 
     using ContainerNode::ref;
     using ContainerNode::deref;
+    using TreeScope::rootNode;
 
     bool canContainRangeEndPoint() const final { return true; }
 
index ca421d5..0c7cc07 100644 (file)
@@ -1110,6 +1110,18 @@ Element* Node::parentOrShadowHostElement() const
     return downcast<Element>(parent);
 }
 
+Node* Node::rootNode() const
+{
+    if (isInTreeScope())
+        return &treeScope().rootNode();
+
+    Node* node = const_cast<Node*>(this);
+    Node* highest = node;
+    for (; node; node = node->parentNode())
+        highest = node;
+    return highest;
+}
+
 Node::InsertionNotificationRequest Node::insertedInto(ContainerNode& insertionPoint)
 {
     ASSERT(insertionPoint.inDocument() || isContainerNode());
index 58e4569..e435160 100644 (file)
@@ -276,7 +276,7 @@ public:
     ContainerNode* parentOrShadowHostNode() const;
     Element* parentOrShadowHostElement() const;
     void setParentNode(ContainerNode*);
-    Node* highestAncestor() const;
+    Node* rootNode() const;
 
     // Use when it's guaranteed to that shadowHost is null.
     ContainerNode* parentNodeGuaranteedHostFree() const;
index 2888be7..2485d72 100644 (file)
@@ -60,7 +60,7 @@
     readonly attribute Node             nextSibling;
     readonly attribute Document         ownerDocument;
 
-    [Conditional=SHADOW_DOM, ImplementedAs=highestAncestor] readonly attribute Node treeRoot;
+    readonly attribute Node rootNode;
 
     [ObjCLegacyUnnamedParameters, Custom, RaisesException] Node insertBefore([CustomReturn] Node newChild,
                                                             Node refChild);
index 6cbea37..692887d 100644 (file)
@@ -62,6 +62,8 @@ public:
 
     virtual ~ShadowRoot();
 
+    using TreeScope::rootNode;
+
     StyleResolver& styleResolver();
     AuthorStyleSheets& authorStyleSheets();
     
index b988514..339736a 100644 (file)
@@ -2427,7 +2427,7 @@ static bool isAutomaticTextReplacementType(TextCheckingType type)
 
 static void correctSpellcheckingPreservingTextCheckingParagraph(TextCheckingParagraph& paragraph, PassRefPtr<Range> rangeToReplace, const String& replacement, int resultLocation, int resultLength)
 {
-    ContainerNode* scope = downcast<ContainerNode>(highestAncestor(&paragraph.paragraphRange()->startContainer()));
+    ContainerNode* scope = downcast<ContainerNode>(paragraph.paragraphRange()->startContainer().rootNode());
 
     size_t paragraphLocation;
     size_t paragraphLength;
index 2d7ad1e..a24e502 100644 (file)
@@ -795,15 +795,6 @@ bool canMergeLists(Element* firstList, Element* secondList)
     // Make sure there is no visible content between this li and the previous list
 }
 
-Node* highestAncestor(Node* node)
-{
-    ASSERT(node);
-    Node* parent = node;
-    while ((node = node->parentNode()))
-        parent = node;
-    return parent;
-}
-
 static Node* previousNodeConsideringAtomicNodes(const Node* node)
 {
     if (node->previousSibling()) {
index 9fe5f47..e408468 100644 (file)
@@ -53,7 +53,6 @@ class VisibleSelection;
 
 // Functions returning Node
 
-Node* highestAncestor(Node*);
 Node* highestEditableRoot(const Position&, EditableType = ContentIsEditable);
 
 Node* highestEnclosingNodeOfType(const Position&, bool (*nodeIsOfType)(const Node*),
index da3de8d..18eebd1 100644 (file)
@@ -82,6 +82,17 @@ void FormAssociatedElement::insertedInto(ContainerNode& insertionPoint)
         resetFormAttributeTargetObserver();
 }
 
+// Compute the highest ancestor instead of calling Node::rootNode in removedFrom / formRemovedFromTree
+// since inDocument flag on some form associated elements may not have been updated yet.
+static Node* computeRootNode(Node& node)
+{
+    Node* current = &node;
+    Node* parent = current;
+    while ((current = current->parentNode()))
+        parent = current;
+    return parent;
+}
+
 void FormAssociatedElement::removedFrom(ContainerNode& insertionPoint)
 {
     HTMLElement& element = asHTMLElement();
@@ -89,7 +100,7 @@ void FormAssociatedElement::removedFrom(ContainerNode& insertionPoint)
         m_formAttributeTargetObserver = nullptr;
     // If the form and element are both in the same tree, preserve the connection to the form.
     // Otherwise, null out our form and remove ourselves from the form's list of elements.
-    if (m_form && element.highestAncestor() != m_form->highestAncestor())
+    if (m_form && computeRootNode(element) != computeRootNode(*m_form))
         setForm(nullptr);
 }
 
@@ -117,7 +128,7 @@ HTMLFormElement* FormAssociatedElement::findAssociatedForm(const HTMLElement* el
 void FormAssociatedElement::formRemovedFromTree(const Node* formRoot)
 {
     ASSERT(m_form);
-    if (asHTMLElement().highestAncestor() != formRoot)
+    if (computeRootNode(asHTMLElement()) != formRoot)
         setForm(nullptr);
 }
 
index a72fc31..d44ec62 100644 (file)
@@ -88,12 +88,8 @@ Value LocationPath::evaluate() const
     // This is for compatibility with Firefox, and also seems like a more
     // logical treatment of where you would expect the "root" to be.
     Node* context = evaluationContext.node.get();
-    if (m_isAbsolute && !context->isDocumentNode())  {
-        if (context->inDocument())
-            context = context->ownerDocument();
-        else
-            context = context->highestAncestor();
-    }
+    if (m_isAbsolute && !context->isDocumentNode())
+        context = context->rootNode();
 
     NodeSet nodes;
     nodes.append(context);