Title: [186408] releases/WebKitGTK/webkit-2.8
Revision
186408
Author
[email protected]
Date
2015-07-07 00:07:02 -0700 (Tue, 07 Jul 2015)

Log Message

Merge r185435 - ASSERT_WITH_SECURITY_IMPLICATION in WebCore::DocumentOrderedMap::getElementById
https://bugs.webkit.org/show_bug.cgi?id=145857
<rdar://problem/16798440>

Reviewed by Darin Adler.

Source/WebCore:

Make sure Node::insertedInto() gets called on the inserted node and its
descendants after its insertion into the tree but *before*
ContainerNode::childrenChanged() is called on the parent node. This is
needed so that the descendants know they've been inserted into the tree
(and their InDocumentFlag flag gets set) before the parent node does
anything with them in childrenChanged().

In the case of <rdar://problem/16798440>, executing HTMLScriptElement's
childrenChanged() after appending a child to a script element was causing
the script to be executed. The script would call getElementBy() which
would traverse the DOM tree and find a matching Element in the newly
inserted subtree. However, the matching Element's InDocumentFlag flag was
not set yet because the element's insertedInto() method has not been called
yet at this point. This would cause us to hit an assertion as
DocumentOrderedMap::getElementById() is only supposed to return elements
that are in a Document.

This patch is based on Blink r178976 by <[email protected]>:
https://src.chromium.org/viewvc/blink?view=rev&revision=178976

Tests: fast/dom/script-getElementById-during-insertion.html
       fast/dom/script-remove-child-id-map.html

* dom/ContainerNode.cpp:
(WebCore::ContainerNode::notifyChildInserted):
(WebCore::ContainerNode::notifyChildRemoved):
(WebCore::ContainerNode::removeChildren):
(WebCore::ContainerNode::parserInsertBefore): Deleted.
(WebCore::ContainerNode::removeChild): Deleted.
(WebCore::ContainerNode::parserRemoveChild): Deleted.
(WebCore::ContainerNode::parserAppendChild): Deleted.
(WebCore::ContainerNode::childrenChanged): Deleted.
(WebCore::ContainerNode::setAttributeEventListener): Deleted.
(WebCore::ContainerNode::querySelector): Deleted.
* dom/ContainerNodeAlgorithms.cpp:
(WebCore::ChildNodeInsertionNotifier::notifyDescendantInsertedIntoDocument):
(WebCore::ChildNodeInsertionNotifier::notifyDescendantInsertedIntoTree):
* dom/ContainerNodeAlgorithms.h:
(WebCore::ChildNodeInsertionNotifier::notifyNodeInsertedIntoDocument):
(WebCore::ChildNodeInsertionNotifier::notifyNodeInsertedIntoTree):
(WebCore::ChildNodeInsertionNotifier::notify):
(WebCore::ChildNodeRemovalNotifier::notifyNodeRemovedFromDocument): Deleted.
* dom/Element.cpp:
(WebCore::Element::addShadowRoot):

LayoutTests:

Add layout tests covering different crashes caused by the same bug.

* fast/dom/script-getElementById-during-insertion-expected.txt: Added.
* fast/dom/script-getElementById-during-insertion.html: Added.

Reduction test case for <rdar://problem/16798440>.

* fast/dom/script-remove-child-id-map-expected.txt: Added.
* fast/dom/script-remove-child-id-map.html: Added.

Test imported from Blink r178976.

Modified Paths

Added Paths

Diff

Modified: releases/WebKitGTK/webkit-2.8/LayoutTests/ChangeLog (186407 => 186408)


--- releases/WebKitGTK/webkit-2.8/LayoutTests/ChangeLog	2015-07-07 07:04:52 UTC (rev 186407)
+++ releases/WebKitGTK/webkit-2.8/LayoutTests/ChangeLog	2015-07-07 07:07:02 UTC (rev 186408)
@@ -1,3 +1,23 @@
+2015-06-10  Chris Dumez  <[email protected]>
+
+        ASSERT_WITH_SECURITY_IMPLICATION in WebCore::DocumentOrderedMap::getElementById
+        https://bugs.webkit.org/show_bug.cgi?id=145857
+        <rdar://problem/16798440>
+
+        Reviewed by Darin Adler.
+
+        Add layout tests covering different crashes caused by the same bug.
+
+        * fast/dom/script-getElementById-during-insertion-expected.txt: Added.
+        * fast/dom/script-getElementById-during-insertion.html: Added.
+
+        Reduction test case for <rdar://problem/16798440>.
+
+        * fast/dom/script-remove-child-id-map-expected.txt: Added.
+        * fast/dom/script-remove-child-id-map.html: Added.
+
+        Test imported from Blink r178976.
+
 2015-06-09  Said Abou-Hallawa  <[email protected]>
 
         SVG Fragment is not rendered if it is the css background image of an HTML element

Added: releases/WebKitGTK/webkit-2.8/LayoutTests/fast/dom/script-getElementById-during-insertion-expected.txt (0 => 186408)


--- releases/WebKitGTK/webkit-2.8/LayoutTests/fast/dom/script-getElementById-during-insertion-expected.txt	                        (rev 0)
+++ releases/WebKitGTK/webkit-2.8/LayoutTests/fast/dom/script-getElementById-during-insertion-expected.txt	2015-07-07 07:07:02 UTC (rev 186408)
@@ -0,0 +1,2 @@
+PASS
+

Added: releases/WebKitGTK/webkit-2.8/LayoutTests/fast/dom/script-getElementById-during-insertion.html (0 => 186408)


--- releases/WebKitGTK/webkit-2.8/LayoutTests/fast/dom/script-getElementById-during-insertion.html	                        (rev 0)
+++ releases/WebKitGTK/webkit-2.8/LayoutTests/fast/dom/script-getElementById-during-insertion.html	2015-07-07 07:07:02 UTC (rev 186408)
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+// Tests that we don't crash if a script is being executed as a result of appending a child to it.</p>
+executedScript = false;
+if (window.testRunner)
+  testRunner.dumpAsText();
+</script>
+</head>
+<body>
+<div id="test"></div>
+<script>
+var elem = document.getElementById("test");
+if (!executedScript) {
+    executedScript = true;
+
+    document.documentElement.appendChild(elem.cloneNode(true));
+
+    var range = document.createRange();
+    range.setStartBefore(document.body);
+    range.setEndAfter(document.body);
+    range.surroundContents(document.head.appendChild(document.createElement("script")));
+} else {
+    var span = document.createElement("span");
+    document.documentElement.appendChild(span);
+    span.innerHTML = 'PASS<br/>';
+}
+</script>
+</body>
+</html>

Added: releases/WebKitGTK/webkit-2.8/LayoutTests/fast/dom/script-remove-child-id-map-expected.txt (0 => 186408)


--- releases/WebKitGTK/webkit-2.8/LayoutTests/fast/dom/script-remove-child-id-map-expected.txt	                        (rev 0)
+++ releases/WebKitGTK/webkit-2.8/LayoutTests/fast/dom/script-remove-child-id-map-expected.txt	2015-07-07 07:07:02 UTC (rev 186408)
@@ -0,0 +1,10 @@
+Passes if it doesn't crash and the child is not in the id map
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS document.getElementById('child') is null
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: releases/WebKitGTK/webkit-2.8/LayoutTests/fast/dom/script-remove-child-id-map.html (0 => 186408)


--- releases/WebKitGTK/webkit-2.8/LayoutTests/fast/dom/script-remove-child-id-map.html	                        (rev 0)
+++ releases/WebKitGTK/webkit-2.8/LayoutTests/fast/dom/script-remove-child-id-map.html	2015-07-07 07:07:02 UTC (rev 186408)
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+
+<script src=""
+
+<script>
+description("Passes if it doesn't crash and the child is not in the id map");
+
+var script = document.createElement("script");
+script.type = "dont-execute";
+script.textContent = "script.remove()";
+child = document.createElement("div");
+child.id = "child";
+script.appendChild(child);
+
+// The script won't execute here because the type is invalid, but it also won't
+// get marked as being already run, so changing the children later will run it.
+document.documentElement.appendChild(script);
+
+// Per the spec setting the type has no effect
+script.type = "";
+
+// but changing the children *will* execute the script now that the type is
+// is valid.
+child.remove();
+
+child = null;
+gc();
+
+shouldBeNull("document.getElementById('child')");
+</script>

Modified: releases/WebKitGTK/webkit-2.8/Source/WebCore/ChangeLog (186407 => 186408)


--- releases/WebKitGTK/webkit-2.8/Source/WebCore/ChangeLog	2015-07-07 07:04:52 UTC (rev 186407)
+++ releases/WebKitGTK/webkit-2.8/Source/WebCore/ChangeLog	2015-07-07 07:07:02 UTC (rev 186408)
@@ -1,5 +1,58 @@
 2015-06-10  Chris Dumez  <[email protected]>
 
+        ASSERT_WITH_SECURITY_IMPLICATION in WebCore::DocumentOrderedMap::getElementById
+        https://bugs.webkit.org/show_bug.cgi?id=145857
+        <rdar://problem/16798440>
+
+        Reviewed by Darin Adler.
+
+        Make sure Node::insertedInto() gets called on the inserted node and its
+        descendants after its insertion into the tree but *before*
+        ContainerNode::childrenChanged() is called on the parent node. This is
+        needed so that the descendants know they've been inserted into the tree
+        (and their InDocumentFlag flag gets set) before the parent node does
+        anything with them in childrenChanged().
+
+        In the case of <rdar://problem/16798440>, executing HTMLScriptElement's
+        childrenChanged() after appending a child to a script element was causing
+        the script to be executed. The script would call getElementBy() which
+        would traverse the DOM tree and find a matching Element in the newly
+        inserted subtree. However, the matching Element's InDocumentFlag flag was
+        not set yet because the element's insertedInto() method has not been called
+        yet at this point. This would cause us to hit an assertion as
+        DocumentOrderedMap::getElementById() is only supposed to return elements
+        that are in a Document.
+
+        This patch is based on Blink r178976 by <[email protected]>:
+        https://src.chromium.org/viewvc/blink?view=rev&revision=178976
+
+        Tests: fast/dom/script-getElementById-during-insertion.html
+               fast/dom/script-remove-child-id-map.html
+
+        * dom/ContainerNode.cpp:
+        (WebCore::ContainerNode::notifyChildInserted):
+        (WebCore::ContainerNode::notifyChildRemoved):
+        (WebCore::ContainerNode::removeChildren):
+        (WebCore::ContainerNode::parserInsertBefore): Deleted.
+        (WebCore::ContainerNode::removeChild): Deleted.
+        (WebCore::ContainerNode::parserRemoveChild): Deleted.
+        (WebCore::ContainerNode::parserAppendChild): Deleted.
+        (WebCore::ContainerNode::childrenChanged): Deleted.
+        (WebCore::ContainerNode::setAttributeEventListener): Deleted.
+        (WebCore::ContainerNode::querySelector): Deleted.
+        * dom/ContainerNodeAlgorithms.cpp:
+        (WebCore::ChildNodeInsertionNotifier::notifyDescendantInsertedIntoDocument):
+        (WebCore::ChildNodeInsertionNotifier::notifyDescendantInsertedIntoTree):
+        * dom/ContainerNodeAlgorithms.h:
+        (WebCore::ChildNodeInsertionNotifier::notifyNodeInsertedIntoDocument):
+        (WebCore::ChildNodeInsertionNotifier::notifyNodeInsertedIntoTree):
+        (WebCore::ChildNodeInsertionNotifier::notify):
+        (WebCore::ChildNodeRemovalNotifier::notifyNodeRemovedFromDocument): Deleted.
+        * dom/Element.cpp:
+        (WebCore::Element::addShadowRoot):
+
+2015-06-10  Chris Dumez  <[email protected]>
+
         Drop unused argument for Node::didNotifySubtreeInsertions()
         https://bugs.webkit.org/show_bug.cgi?id=145845
 

Modified: releases/WebKitGTK/webkit-2.8/Source/WebCore/dom/ContainerNode.cpp (186407 => 186408)


--- releases/WebKitGTK/webkit-2.8/Source/WebCore/dom/ContainerNode.cpp	2015-07-07 07:04:52 UTC (rev 186407)
+++ releases/WebKitGTK/webkit-2.8/Source/WebCore/dom/ContainerNode.cpp	2015-07-07 07:07:02 UTC (rev 186408)
@@ -329,6 +329,11 @@
 
 void ContainerNode::notifyChildInserted(Node& child, ChildChangeSource source)
 {
+    ChildListMutationScope(*this).childAdded(child);
+
+    NodeVector postInsertionNotificationTargets;
+    ChildNodeInsertionNotifier(*this).notify(child, postInsertionNotificationTargets);
+
     ChildChange change;
     change.type = child.isElementNode() ? ElementInserted : child.isTextNode() ? TextInserted : NonContentsChildChanged;
     change.previousSiblingElement = ElementTraversal::previousSibling(child);
@@ -336,10 +341,15 @@
     change.source = source;
 
     childrenChanged(change);
+
+    for (auto& target : postInsertionNotificationTargets)
+        target->didNotifySubtreeInsertions();
 }
 
 void ContainerNode::notifyChildRemoved(Node& child, Node* previousSibling, Node* nextSibling, ChildChangeSource source)
 {
+    ChildNodeRemovalNotifier(*this).notify(child);
+
     ChildChange change;
     change.type = is<Element>(child) ? ElementRemoved : is<Text>(child) ? TextRemoved : NonContentsChildChanged;
     change.previousSiblingElement = (!previousSibling || is<Element>(*previousSibling)) ? downcast<Element>(previousSibling) : ElementTraversal::previousSibling(*previousSibling);
@@ -369,12 +379,8 @@
 
     newChild->updateAncestorConnectedSubframeCountForInsertion();
 
-    ChildListMutationScope(*this).childAdded(*newChild);
-
     notifyChildInserted(*newChild, ChildChangeSourceParser);
 
-    ChildNodeInsertionNotifier(*this).notify(*newChild);
-
     newChild->setNeedsStyleRecalc(ReconstructRenderTree);
 }
 
@@ -559,8 +565,6 @@
         removeBetween(prev, next, child);
 
         notifyChildRemoved(child, prev, next, ChildChangeSourceAPI);
-
-        ChildNodeRemovalNotifier(*this).notify(child);
     }
 
 
@@ -617,8 +621,6 @@
     removeBetween(prev, next, oldChild);
 
     notifyChildRemoved(oldChild, prev, next, ChildChangeSourceParser);
-
-    ChildNodeRemovalNotifier(*this).notify(oldChild);
 }
 
 // this differs from other remove functions because it forcibly removes all the children,
@@ -642,23 +644,18 @@
     // and remove... e.g. stop loading frames, fire unload events.
     willRemoveChildren(*this);
 
-    NodeVector removedChildren;
     {
         WidgetHierarchyUpdatesSuspensionScope suspendWidgetHierarchyUpdates;
         {
             NoEventDispatchAssertion assertNoEventDispatch;
-            removedChildren.reserveInitialCapacity(countChildNodes());
             while (RefPtr<Node> n = m_firstChild) {
-                removedChildren.append(*m_firstChild);
                 removeBetween(0, m_firstChild->nextSibling(), *m_firstChild);
+                ChildNodeRemovalNotifier(*this).notify(*n);
             }
         }
 
         ChildChange change = { AllChildrenRemoved, nullptr, nullptr, ChildChangeSourceAPI };
         childrenChanged(change);
-        
-        for (auto& removedChild : removedChildren)
-            ChildNodeRemovalNotifier(*this).notify(removedChild);
     }
 
     if (document().svgExtensions()) {
@@ -748,12 +745,8 @@
 
     newChild->updateAncestorConnectedSubframeCountForInsertion();
 
-    ChildListMutationScope(*this).childAdded(*newChild);
-
     notifyChildInserted(*newChild, ChildChangeSourceParser);
 
-    ChildNodeInsertionNotifier(*this).notify(*newChild);
-
     newChild->setNeedsStyleRecalc(ReconstructRenderTree);
 }
 
@@ -844,12 +837,8 @@
 {
     ASSERT(child.refCount());
 
-    ChildListMutationScope(*this).childAdded(child);
-
     notifyChildInserted(child, ChildChangeSourceAPI);
 
-    ChildNodeInsertionNotifier(*this).notify(child);
-
     child.setNeedsStyleRecalc(ReconstructRenderTree);
 
     dispatchChildInsertionEvents(child);

Modified: releases/WebKitGTK/webkit-2.8/Source/WebCore/dom/ContainerNodeAlgorithms.cpp (186407 => 186408)


--- releases/WebKitGTK/webkit-2.8/Source/WebCore/dom/ContainerNodeAlgorithms.cpp	2015-07-07 07:04:52 UTC (rev 186407)
+++ releases/WebKitGTK/webkit-2.8/Source/WebCore/dom/ContainerNodeAlgorithms.cpp	2015-07-07 07:07:02 UTC (rev 186408)
@@ -29,7 +29,7 @@
 
 namespace WebCore {
 
-void ChildNodeInsertionNotifier::notifyDescendantInsertedIntoDocument(ContainerNode& node)
+void ChildNodeInsertionNotifier::notifyDescendantInsertedIntoDocument(ContainerNode& node, NodeVector& postInsertionNotificationTargets)
 {
     ChildNodesLazySnapshot snapshot(node);
     while (RefPtr<Node> child = snapshot.nextNode()) {
@@ -37,7 +37,7 @@
         // we don't want to tell the rest of our children that they've been
         // inserted into the document because they haven't.
         if (node.inDocument() && child->parentNode() == &node)
-            notifyNodeInsertedIntoDocument(*child);
+            notifyNodeInsertedIntoDocument(*child, postInsertionNotificationTargets);
     }
 
     if (!is<Element>(node))
@@ -45,19 +45,19 @@
 
     if (RefPtr<ShadowRoot> root = downcast<Element>(node).shadowRoot()) {
         if (node.inDocument() && root->hostElement() == &node)
-            notifyNodeInsertedIntoDocument(*root);
+            notifyNodeInsertedIntoDocument(*root, postInsertionNotificationTargets);
     }
 }
 
-void ChildNodeInsertionNotifier::notifyDescendantInsertedIntoTree(ContainerNode& node)
+void ChildNodeInsertionNotifier::notifyDescendantInsertedIntoTree(ContainerNode& node, NodeVector& postInsertionNotificationTargets)
 {
     for (Node* child = node.firstChild(); child; child = child->nextSibling()) {
         if (is<ContainerNode>(*child))
-            notifyNodeInsertedIntoTree(downcast<ContainerNode>(*child));
+            notifyNodeInsertedIntoTree(downcast<ContainerNode>(*child), postInsertionNotificationTargets);
     }
 
     if (ShadowRoot* root = node.shadowRoot())
-        notifyNodeInsertedIntoTree(*root);
+        notifyNodeInsertedIntoTree(*root, postInsertionNotificationTargets);
 }
 
 void ChildNodeRemovalNotifier::notifyDescendantRemovedFromDocument(ContainerNode& node)

Modified: releases/WebKitGTK/webkit-2.8/Source/WebCore/dom/ContainerNodeAlgorithms.h (186407 => 186408)


--- releases/WebKitGTK/webkit-2.8/Source/WebCore/dom/ContainerNodeAlgorithms.h	2015-07-07 07:04:52 UTC (rev 186407)
+++ releases/WebKitGTK/webkit-2.8/Source/WebCore/dom/ContainerNodeAlgorithms.h	2015-07-07 07:07:02 UTC (rev 186408)
@@ -41,16 +41,15 @@
     {
     }
 
-    void notify(Node&);
+    void notify(Node&, NodeVector& postInsertionNotificationTargets);
 
 private:
-    void notifyDescendantInsertedIntoDocument(ContainerNode&);
-    void notifyDescendantInsertedIntoTree(ContainerNode&);
-    void notifyNodeInsertedIntoDocument(Node&);
-    void notifyNodeInsertedIntoTree(ContainerNode&);
+    void notifyDescendantInsertedIntoDocument(ContainerNode&, NodeVector& postInsertionNotificationTargets);
+    void notifyDescendantInsertedIntoTree(ContainerNode&, NodeVector& postInsertionNotificationTargets);
+    void notifyNodeInsertedIntoDocument(Node&, NodeVector& postInsertionNotificationTargets);
+    void notifyNodeInsertedIntoTree(ContainerNode&, NodeVector& postInsertionNotificationTargets);
 
     ContainerNode& m_insertionPoint;
-    Vector<Ref<Node>> m_postInsertionNotificationTargets;
 };
 
 class ChildNodeRemovalNotifier {
@@ -193,26 +192,26 @@
 
 } // namespace Private
 
-inline void ChildNodeInsertionNotifier::notifyNodeInsertedIntoDocument(Node& node)
+inline void ChildNodeInsertionNotifier::notifyNodeInsertedIntoDocument(Node& node, NodeVector& postInsertionNotificationTargets)
 {
     ASSERT(m_insertionPoint.inDocument());
     if (Node::InsertionShouldCallDidNotifySubtreeInsertions == node.insertedInto(m_insertionPoint))
-        m_postInsertionNotificationTargets.append(node);
+        postInsertionNotificationTargets.append(node);
     if (is<ContainerNode>(node))
-        notifyDescendantInsertedIntoDocument(downcast<ContainerNode>(node));
+        notifyDescendantInsertedIntoDocument(downcast<ContainerNode>(node), postInsertionNotificationTargets);
 }
 
-inline void ChildNodeInsertionNotifier::notifyNodeInsertedIntoTree(ContainerNode& node)
+inline void ChildNodeInsertionNotifier::notifyNodeInsertedIntoTree(ContainerNode& node, NodeVector& postInsertionNotificationTargets)
 {
     NoEventDispatchAssertion assertNoEventDispatch;
     ASSERT(!m_insertionPoint.inDocument());
 
     if (Node::InsertionShouldCallDidNotifySubtreeInsertions == node.insertedInto(m_insertionPoint))
-        m_postInsertionNotificationTargets.append(node);
-    notifyDescendantInsertedIntoTree(node);
+        postInsertionNotificationTargets.append(node);
+    notifyDescendantInsertedIntoTree(node, postInsertionNotificationTargets);
 }
 
-inline void ChildNodeInsertionNotifier::notify(Node& node)
+inline void ChildNodeInsertionNotifier::notify(Node& node, NodeVector& postInsertionNotificationTargets)
 {
     ASSERT_WITH_SECURITY_IMPLICATION(!NoEventDispatchAssertion::isEventDispatchForbidden());
 
@@ -222,12 +221,9 @@
     Ref<Node> protectNode(node);
 
     if (m_insertionPoint.inDocument())
-        notifyNodeInsertedIntoDocument(node);
+        notifyNodeInsertedIntoDocument(node, postInsertionNotificationTargets);
     else if (is<ContainerNode>(node))
-        notifyNodeInsertedIntoTree(downcast<ContainerNode>(node));
-
-    for (size_t i = 0; i < m_postInsertionNotificationTargets.size(); ++i)
-        m_postInsertionNotificationTargets[i]->didNotifySubtreeInsertions();
+        notifyNodeInsertedIntoTree(downcast<ContainerNode>(node), postInsertionNotificationTargets);
 }
 
 

Modified: releases/WebKitGTK/webkit-2.8/Source/WebCore/dom/Element.cpp (186407 => 186408)


--- releases/WebKitGTK/webkit-2.8/Source/WebCore/dom/Element.cpp	2015-07-07 07:04:52 UTC (rev 186407)
+++ releases/WebKitGTK/webkit-2.8/Source/WebCore/dom/Element.cpp	2015-07-07 07:07:02 UTC (rev 186408)
@@ -1493,8 +1493,12 @@
     shadowRoot.setParentTreeScope(&treeScope());
     shadowRoot.distributor().didShadowBoundaryChange(this);
 
-    ChildNodeInsertionNotifier(*this).notify(shadowRoot);
+    NodeVector postInsertionNotificationTargets;
+    ChildNodeInsertionNotifier(*this).notify(shadowRoot, postInsertionNotificationTargets);
 
+    for (auto& target : postInsertionNotificationTargets)
+        target->didNotifySubtreeInsertions();
+
     resetNeedsNodeRenderingTraversalSlowPath();
 
     setNeedsStyleRecalc(ReconstructRenderTree);
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to