Title: [218083] trunk
Revision
218083
Author
[email protected]
Date
2017-06-11 20:46:47 -0700 (Sun, 11 Jun 2017)

Log Message

didMoveToNewDocument doesn't get called on an Attr inside a shadow tree
https://bugs.webkit.org/show_bug.cgi?id=173133

Reviewed by Antti Koivisto.

Source/WebCore:

The bug was caused by moveShadowTreeToNewDocument never calling didMoveToNewDocument on Attr nodes.
Fixed the bug by using the same traversal code as moveTreeToNewScope in moveShadowTreeToNewDocument
by extracting the traversal code as a templatized traverseSubtreeToUpdateTreeScope.

Also removed the code to increment the DOM tree version in moveTreeToNewScope. This code was there
to invalidate the HTML collection caches which used to clear the cache whenever the DOM tree version
changed before r122621 (five years ago! by me). Since we now eagerly invalidate each node list and
HTML collection's caches via NodeListsNodeData::adoptTreeScope and NodeListsNodeData::adoptDocument.

Test: fast/dom/adopt-attr-with-shadow-tree.html

* dom/Node.cpp:
(WebCore::moveNodeToNewDocument): Assert that the node had been adopted to a new document.
(WebCore::traverseSubtreeToUpdateTreeScope): Extracted from moveTreeToNewScope.
(WebCore::moveShadowTreeToNewDocument): Use traverseSubtreeToUpdateTreeScope to adopt each node
to the new document.
(WebCore::Node::moveTreeToNewScope): See above.
* testing/Internals.cpp:
(WebCore::Internals::referencingNodeCount): Added. Used in the newly added regression test.
* testing/Internals.h:
* testing/Internals.idl:

LayoutTests:

Added a regression test for moving a shadow tree with an Attr node across a document.
The test hits an assertion in a debug build and fails in a release build without the fix.

* fast/dom/adopt-attr-with-shadow-tree-expected.txt: Added.
* fast/dom/adopt-attr-with-shadow-tree.html: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (218082 => 218083)


--- trunk/LayoutTests/ChangeLog	2017-06-12 02:40:43 UTC (rev 218082)
+++ trunk/LayoutTests/ChangeLog	2017-06-12 03:46:47 UTC (rev 218083)
@@ -1,3 +1,16 @@
+2017-06-09  Ryosuke Niwa  <[email protected]>
+
+        didMoveToNewDocument doesn't get called on an Attr inside a shadow tree
+        https://bugs.webkit.org/show_bug.cgi?id=173133
+
+        Reviewed by Antti Koivisto.
+
+        Added a regression test for moving a shadow tree with an Attr node across a document.
+        The test hits an assertion in a debug build and fails in a release build without the fix.
+
+        * fast/dom/adopt-attr-with-shadow-tree-expected.txt: Added.
+        * fast/dom/adopt-attr-with-shadow-tree.html: Added.
+
 2017-06-11  Keith Miller  <[email protected]>
 
         TypedArray constructor with string shouldn't throw

Added: trunk/LayoutTests/fast/dom/adopt-attr-with-shadow-tree-expected.txt (0 => 218083)


--- trunk/LayoutTests/fast/dom/adopt-attr-with-shadow-tree-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/dom/adopt-attr-with-shadow-tree-expected.txt	2017-06-12 03:46:47 UTC (rev 218083)
@@ -0,0 +1,10 @@
+This tests adopting a shadow tree with an Attr node.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS referenceCountInDestination is referenceCountInSource
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/fast/dom/adopt-attr-with-shadow-tree.html (0 => 218083)


--- trunk/LayoutTests/fast/dom/adopt-attr-with-shadow-tree.html	                        (rev 0)
+++ trunk/LayoutTests/fast/dom/adopt-attr-with-shadow-tree.html	2017-06-12 03:46:47 UTC (rev 218083)
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<html>
+<body>
+<script src=""
+<script>
+
+description('This tests adopting a shadow tree with an Attr node.');
+
+var referenceCountInSource;
+var referenceCountInDestination;
+function runTest() {
+    let startCount = internals.referencingNodeCount(document);
+
+    const outerHost = document.createElement('shadow-host');
+    document.body.appendChild(outerHost);
+    const outerRoot = outerHost.attachShadow({mode: 'closed'});
+    outerRoot.innerHTML = '<span title="foo"></span>';
+
+    const innerHost = outerRoot.firstChild;
+    const innerRoot = innerHost.attachShadow({mode: 'closed'});
+    innerRoot.innerHTML = '<div lang="en"></div>';
+
+    const outerAttr = innerHost.attributes[0];
+    const outerAttrNodeList = outerAttr.childNodes;
+    const innerAttr = innerRoot.firstChild.attributes[0];
+    const innerAttrNodeList = innerAttr.childNodes;
+
+    referenceCountInSource = internals.referencingNodeCount(document) - startCount;
+
+    const iframe = document.createElement('iframe');
+    document.body.appendChild(iframe);
+
+    startCount = internals.referencingNodeCount(iframe.contentDocument);
+    iframe.contentDocument.body.appendChild(outerHost);
+    referenceCountInDestination = internals.referencingNodeCount(iframe.contentDocument) - startCount;
+
+    iframe.remove();
+}
+
+if (!window.GCController || !window.internals)
+    testFailed('This test requires testRunner, internals, and GCController objects.');
+else {
+    runTest();
+    GCController.collect();
+    shouldBe('referenceCountInDestination', 'referenceCountInSource');
+}
+
+</script>
+</body>
+</html>

Modified: trunk/Source/WebCore/ChangeLog (218082 => 218083)


--- trunk/Source/WebCore/ChangeLog	2017-06-12 02:40:43 UTC (rev 218082)
+++ trunk/Source/WebCore/ChangeLog	2017-06-12 03:46:47 UTC (rev 218083)
@@ -1,3 +1,32 @@
+2017-06-09  Ryosuke Niwa  <[email protected]>
+
+        didMoveToNewDocument doesn't get called on an Attr inside a shadow tree
+        https://bugs.webkit.org/show_bug.cgi?id=173133
+
+        Reviewed by Antti Koivisto.
+
+        The bug was caused by moveShadowTreeToNewDocument never calling didMoveToNewDocument on Attr nodes.
+        Fixed the bug by using the same traversal code as moveTreeToNewScope in moveShadowTreeToNewDocument
+        by extracting the traversal code as a templatized traverseSubtreeToUpdateTreeScope.
+
+        Also removed the code to increment the DOM tree version in moveTreeToNewScope. This code was there
+        to invalidate the HTML collection caches which used to clear the cache whenever the DOM tree version
+        changed before r122621 (five years ago! by me). Since we now eagerly invalidate each node list and
+        HTML collection's caches via NodeListsNodeData::adoptTreeScope and NodeListsNodeData::adoptDocument.
+
+        Test: fast/dom/adopt-attr-with-shadow-tree.html
+
+        * dom/Node.cpp:
+        (WebCore::moveNodeToNewDocument): Assert that the node had been adopted to a new document.
+        (WebCore::traverseSubtreeToUpdateTreeScope): Extracted from moveTreeToNewScope.
+        (WebCore::moveShadowTreeToNewDocument): Use traverseSubtreeToUpdateTreeScope to adopt each node
+        to the new document.
+        (WebCore::Node::moveTreeToNewScope): See above.
+        * testing/Internals.cpp:
+        (WebCore::Internals::referencingNodeCount): Added. Used in the newly added regression test.
+        * testing/Internals.h:
+        * testing/Internals.idl:
+
 2017-06-11  Dan Bernstein  <[email protected]>
 
         [Mac] Unaligned pointers in static CMBufferCallbacks structs defined in WebCoreDecompressionSession.mm

Modified: trunk/Source/WebCore/dom/Node.cpp (218082 => 218083)


--- trunk/Source/WebCore/dom/Node.cpp	2017-06-12 02:40:43 UTC (rev 218082)
+++ trunk/Source/WebCore/dom/Node.cpp	2017-06-12 03:46:47 UTC (rev 218083)
@@ -1975,65 +1975,73 @@
     ASSERT(!node.isConnected() || &oldDocument != &newDocument);
     DidMoveToNewDocumentAssertionScope scope(node, oldDocument, newDocument);
     node.didMoveToNewDocument(oldDocument, newDocument);
+    ASSERT_WITH_SECURITY_IMPLICATION(&node.document() == &newDocument);
 }
 
-static void moveShadowTreeToNewDocument(ShadowRoot& shadowRoot, Document& oldDocument, Document& newDocument)
+template <typename MoveNodeFunction, typename MoveShadowRootFunction>
+static void traverseSubtreeToUpdateTreeScope(Node& root, MoveNodeFunction moveNode, MoveShadowRootFunction moveShadowRoot)
 {
-    for (Node* node = &shadowRoot; node; node = NodeTraversal::next(*node, &shadowRoot)) {
-        moveNodeToNewDocument(*node, oldDocument, newDocument);
-        if (auto* shadow = node->shadowRoot())
-            moveShadowTreeToNewDocument(*shadow, oldDocument, newDocument);
+    for (Node* node = &root; node; node = NodeTraversal::next(*node, &root)) {
+        moveNode(*node);
+
+        if (!is<Element>(*node))
+            continue;
+        Element& element = downcast<Element>(*node);
+
+        if (element.hasSyntheticAttrChildNodes()) {
+            for (auto& attr : element.attrNodeList())
+                moveNode(*attr);
+        }
+
+        if (auto* shadow = element.shadowRoot())
+            moveShadowRoot(*shadow);
     }
 }
 
+static void moveShadowTreeToNewDocument(ShadowRoot& shadowRoot, Document& oldDocument, Document& newDocument)
+{
+    traverseSubtreeToUpdateTreeScope(shadowRoot, [&oldDocument, &newDocument](Node& node) {
+        moveNodeToNewDocument(node, oldDocument, newDocument);
+    }, [&oldDocument, &newDocument](ShadowRoot& innerShadowRoot) {
+        ASSERT_WITH_SECURITY_IMPLICATION(&innerShadowRoot.document() == &oldDocument);
+        moveShadowTreeToNewDocument(innerShadowRoot, oldDocument, newDocument);
+    });
+}
+
 void Node::moveTreeToNewScope(Node& root, TreeScope& oldScope, TreeScope& newScope)
 {
     ASSERT(&oldScope != &newScope);
     ASSERT_WITH_SECURITY_IMPLICATION(&root.treeScope() == &oldScope);
 
-    // If an element is moved from a document and then eventually back again the collection cache for
-    // that element may contain stale data as changes made to it will have updated the DOMTreeVersion
-    // of the document it was moved to. By increasing the DOMTreeVersion of the donating document here
-    // we ensure that the collection cache will be invalidated as needed when the element is moved back.
     Document& oldDocument = oldScope.documentScope();
     Document& newDocument = newScope.documentScope();
-    bool shouldUpdateDocumentScope = &oldDocument != &newDocument;
-    if (shouldUpdateDocumentScope) {
+    if (&oldDocument != &newDocument) {
         oldDocument.incrementReferencingNodeCount();
-        oldDocument.incDOMTreeVersion();
-    }
-
-    for (Node* node = &root; node; node = NodeTraversal::next(*node, &root)) {
-        ASSERT(!node->isTreeScope());
-        ASSERT(&node->treeScope() == &oldScope);
-        node->setTreeScope(newScope);
-
-        if (shouldUpdateDocumentScope)
-            moveNodeToNewDocument(*node, oldDocument, newDocument);
-        else if (node->hasRareData()) {
-            if (auto* nodeLists = node->rareData()->nodeLists())
+        traverseSubtreeToUpdateTreeScope(root, [&](Node& node) {
+            ASSERT(!node.isTreeScope());
+            ASSERT_WITH_SECURITY_IMPLICATION(&node.treeScope() == &oldScope);
+            ASSERT_WITH_SECURITY_IMPLICATION(&node.document() == &oldDocument);
+            node.setTreeScope(newScope);
+            moveNodeToNewDocument(node, oldDocument, newDocument);
+        }, [&](ShadowRoot& shadowRoot) {
+            ASSERT_WITH_SECURITY_IMPLICATION(&shadowRoot.document() == &oldDocument);
+            shadowRoot.setParentTreeScope(newScope);
+            moveShadowTreeToNewDocument(shadowRoot, oldDocument, newDocument);
+        });
+        oldDocument.decrementReferencingNodeCount();
+    } else {
+        traverseSubtreeToUpdateTreeScope(root, [&](Node& node) {
+            ASSERT(!node.isTreeScope());
+            ASSERT_WITH_SECURITY_IMPLICATION(&node.treeScope() == &oldScope);
+            node.setTreeScope(newScope);
+            if (!node.hasRareData())
+                return;
+            if (auto* nodeLists = node.rareData()->nodeLists())
                 nodeLists->adoptTreeScope();
-        }
-
-        if (!is<Element>(*node))
-            continue;
-        Element& element = downcast<Element>(*node);
-
-        if (element.hasSyntheticAttrChildNodes()) {
-            for (auto& attr : element.attrNodeList())
-                moveTreeToNewScope(*attr, oldScope, newScope);
-        }
-
-        if (auto* shadow = element.shadowRoot()) {
-            ASSERT_WITH_SECURITY_IMPLICATION(&shadow->document() == &oldDocument);
-            shadow->setParentTreeScope(newScope);
-            if (shouldUpdateDocumentScope)
-                moveShadowTreeToNewDocument(*shadow, oldDocument, newDocument);
-        }
+        }, [&newScope](ShadowRoot& shadowRoot) {
+            shadowRoot.setParentTreeScope(newScope);
+        });
     }
-
-    if (shouldUpdateDocumentScope)
-        oldDocument.decrementReferencingNodeCount();
 }
 
 void Node::didMoveToNewDocument(Document& oldDocument, Document& newDocument)

Modified: trunk/Source/WebCore/testing/Internals.cpp (218082 => 218083)


--- trunk/Source/WebCore/testing/Internals.cpp	2017-06-12 02:40:43 UTC (rev 218082)
+++ trunk/Source/WebCore/testing/Internals.cpp	2017-06-12 03:46:47 UTC (rev 218083)
@@ -2195,6 +2195,11 @@
     return Document::allDocuments().size();
 }
 
+unsigned Internals::referencingNodeCount(const Document& document) const
+{
+    return document.referencingNodeCount();
+}
+
 RefPtr<DOMWindow> Internals::openDummyInspectorFrontend(const String& url)
 {
     auto* inspectedPage = contextDocument()->frame()->page();

Modified: trunk/Source/WebCore/testing/Internals.h (218082 => 218083)


--- trunk/Source/WebCore/testing/Internals.h	2017-06-12 02:40:43 UTC (rev 218082)
+++ trunk/Source/WebCore/testing/Internals.h	2017-06-12 03:46:47 UTC (rev 218083)
@@ -325,6 +325,7 @@
 
     unsigned numberOfLiveNodes() const;
     unsigned numberOfLiveDocuments() const;
+    unsigned referencingNodeCount(const Document&) const;
 
     RefPtr<DOMWindow> openDummyInspectorFrontend(const String& url);
     void closeDummyInspectorFrontend();

Modified: trunk/Source/WebCore/testing/Internals.idl (218082 => 218083)


--- trunk/Source/WebCore/testing/Internals.idl	2017-06-12 02:40:43 UTC (rev 218082)
+++ trunk/Source/WebCore/testing/Internals.idl	2017-06-12 03:46:47 UTC (rev 218083)
@@ -305,6 +305,7 @@
 
     unsigned long numberOfLiveNodes();
     unsigned long numberOfLiveDocuments();
+    unsigned long referencingNodeCount(Document document);
     DOMWindow? openDummyInspectorFrontend(DOMString url);
     void closeDummyInspectorFrontend();
     [MayThrowException] void setInspectorIsUnderTest(boolean isUnderTest);
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to