Upstream version 10.38.220.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / dom / ContainerNode.cpp
index 815c331..114e63d 100644 (file)
@@ -2,7 +2,7 @@
  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
  *           (C) 2001 Dirk Mueller (mueller@kde.org)
- * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
+ * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2013 Apple Inc. All rights reserved.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
 #include "config.h"
 #include "core/dom/ContainerNode.h"
 
-#include "bindings/v8/ExceptionState.h"
+#include "bindings/core/v8/ExceptionState.h"
 #include "core/dom/ChildFrameDisconnector.h"
 #include "core/dom/ChildListMutationScope.h"
 #include "core/dom/ClassCollection.h"
-#include "core/dom/ContainerNodeAlgorithms.h"
 #include "core/dom/ElementTraversal.h"
 #include "core/dom/ExceptionCode.h"
-#include "core/dom/FullscreenElementStack.h"
 #include "core/dom/NameNodeList.h"
-#include "core/dom/NoEventDispatchAssertion.h"
 #include "core/dom/NodeChildRemovalTracker.h"
 #include "core/dom/NodeRareData.h"
 #include "core/dom/NodeRenderStyle.h"
 #include "core/dom/NodeTraversal.h"
-#include "core/dom/ScriptForbiddenScope.h"
 #include "core/dom/SelectorQuery.h"
 #include "core/dom/StaticNodeList.h"
+#include "core/dom/StyleEngine.h"
 #include "core/dom/shadow/ElementShadow.h"
 #include "core/dom/shadow/ShadowRoot.h"
 #include "core/events/MutationEvent.h"
 #include "core/html/HTMLCollection.h"
 #include "core/html/HTMLFrameOwnerElement.h"
+#include "core/html/HTMLTagCollection.h"
 #include "core/html/RadioNodeList.h"
 #include "core/inspector/InspectorInstrumentation.h"
 #include "core/rendering/InlineTextBox.h"
 #include "core/rendering/RenderText.h"
 #include "core/rendering/RenderTheme.h"
 #include "core/rendering/RenderView.h"
+#include "platform/EventDispatchForbiddenScope.h"
+#include "platform/ScriptForbiddenScope.h"
 
-namespace WebCore {
+namespace blink {
 
 using namespace HTMLNames;
 
 static void dispatchChildInsertionEvents(Node&);
 static void dispatchChildRemovalEvents(Node&);
 
-#ifndef NDEBUG
-unsigned NoEventDispatchAssertion::s_count = 0;
+#if ENABLE(ASSERT)
+unsigned EventDispatchForbiddenScope::s_count = 0;
 #endif
 
 static void collectChildrenAndRemoveFromOldParent(Node& node, NodeVector& nodes, ExceptionState& exceptionState)
 {
-    if (!node.isDocumentFragment()) {
-        nodes.append(&node);
-        if (ContainerNode* oldParent = node.parentNode())
-            oldParent->removeChild(&node, exceptionState);
+    if (node.isDocumentFragment()) {
+        DocumentFragment& fragment = toDocumentFragment(node);
+        getChildNodes(fragment, nodes);
+        fragment.removeChildren();
         return;
     }
-    getChildNodes(node, nodes);
-    toContainerNode(node).removeChildren();
+    nodes.append(&node);
+    if (ContainerNode* oldParent = node.parentNode())
+        oldParent->removeChild(&node, exceptionState);
 }
 
 #if !ENABLE(OILPAN)
@@ -80,7 +81,7 @@ void ContainerNode::removeDetachedChildren()
 {
     ASSERT(!connectedSubframeCount());
     ASSERT(needsAttach());
-    removeDetachedChildrenInContainer<Node, ContainerNode>(*this);
+    removeDetachedChildrenInContainer(*this);
 }
 #endif
 
@@ -175,7 +176,7 @@ bool ContainerNode::checkAcceptChildGuaranteedNodeTypes(const Node& newChild, Ex
     return true;
 }
 
-void ContainerNode::insertBefore(PassRefPtrWillBeRawPtr<Node> newChild, Node* refChild, ExceptionState& exceptionState)
+PassRefPtrWillBeRawPtr<Node> ContainerNode::insertBefore(PassRefPtrWillBeRawPtr<Node> newChild, Node* refChild, ExceptionState& exceptionState)
 {
 #if !ENABLE(OILPAN)
     // Check that this node is not "floating".
@@ -187,36 +188,42 @@ void ContainerNode::insertBefore(PassRefPtrWillBeRawPtr<Node> newChild, Node* re
 
     // insertBefore(node, 0) is equivalent to appendChild(node)
     if (!refChild) {
-        appendChild(newChild, exceptionState);
-        return;
+        return appendChild(newChild, exceptionState);
     }
 
     // Make sure adding the new child is OK.
-    if (!checkAcceptChild(newChild.get(), 0, exceptionState))
-        return;
+    if (!checkAcceptChild(newChild.get(), 0, exceptionState)) {
+        if (exceptionState.hadException())
+            return nullptr;
+        return newChild;
+    }
     ASSERT(newChild);
 
     // NotFoundError: Raised if refChild is not a child of this node
     if (refChild->parentNode() != this) {
         exceptionState.throwDOMException(NotFoundError, "The node before which the new node is to be inserted is not a child of this node.");
-        return;
+        return nullptr;
     }
 
-    if (refChild->previousSibling() == newChild || refChild == newChild) // nothing to do
-        return;
+    // nothing to do
+    if (refChild->previousSibling() == newChild || refChild == newChild)
+        return newChild;
 
     RefPtrWillBeRawPtr<Node> next = refChild;
 
     NodeVector targets;
     collectChildrenAndRemoveFromOldParent(*newChild, targets, exceptionState);
     if (exceptionState.hadException())
-        return;
+        return nullptr;
     if (targets.isEmpty())
-        return;
+        return newChild;
 
     // We need this extra check because collectChildrenAndRemoveFromOldParent() can fire mutation events.
-    if (!checkAcceptChildGuaranteedNodeTypes(*newChild, exceptionState))
-        return;
+    if (!checkAcceptChildGuaranteedNodeTypes(*newChild, exceptionState)) {
+        if (exceptionState.hadException())
+            return nullptr;
+        return newChild;
+    }
 
     InspectorInstrumentation::willInsertDOMNode(this);
 
@@ -242,11 +249,13 @@ void ContainerNode::insertBefore(PassRefPtrWillBeRawPtr<Node> newChild, Node* re
     }
 
     dispatchSubtreeModifiedEvent();
+
+    return newChild;
 }
 
 void ContainerNode::insertBeforeCommon(Node& nextChild, Node& newChild)
 {
-    NoEventDispatchAssertion assertNoEventDispatch;
+    EventDispatchForbiddenScope assertNoEventDispatch;
     ScriptForbiddenScope forbidScript;
 
     ASSERT(!newChild.parentNode()); // Use insertBefore if you need to handle reparenting (and want DOM mutation events).
@@ -308,7 +317,7 @@ void ContainerNode::parserInsertBefore(PassRefPtrWillBeRawPtr<Node> newChild, No
     notifyNodeInserted(*newChild, ChildrenChangeSourceParser);
 }
 
-void ContainerNode::replaceChild(PassRefPtrWillBeRawPtr<Node> newChild, Node* oldChild, ExceptionState& exceptionState)
+PassRefPtrWillBeRawPtr<Node> ContainerNode::replaceChild(PassRefPtrWillBeRawPtr<Node> newChild, PassRefPtrWillBeRawPtr<Node> oldChild, ExceptionState& exceptionState)
 {
 #if !ENABLE(OILPAN)
     // Check that this node is not "floating".
@@ -319,49 +328,58 @@ void ContainerNode::replaceChild(PassRefPtrWillBeRawPtr<Node> newChild, Node* ol
     RefPtrWillBeRawPtr<Node> protect(this);
 
     if (oldChild == newChild) // nothing to do
-        return;
+        return oldChild;
 
     if (!oldChild) {
         exceptionState.throwDOMException(NotFoundError, "The node to be replaced is null.");
-        return;
+        return nullptr;
     }
 
+    RefPtrWillBeRawPtr<Node> child = oldChild;
+
     // Make sure replacing the old child with the new is ok
-    if (!checkAcceptChild(newChild.get(), oldChild, exceptionState))
-        return;
+    if (!checkAcceptChild(newChild.get(), child.get(), exceptionState)) {
+        if (exceptionState.hadException())
+            return nullptr;
+        return child;
+    }
 
     // NotFoundError: Raised if oldChild is not a child of this node.
-    if (oldChild->parentNode() != this) {
+    if (child->parentNode() != this) {
         exceptionState.throwDOMException(NotFoundError, "The node to be replaced is not a child of this node.");
-        return;
+        return nullptr;
     }
 
     ChildListMutationScope mutation(*this);
 
-    RefPtrWillBeRawPtr<Node> next = oldChild->nextSibling();
+    RefPtrWillBeRawPtr<Node> next = child->nextSibling();
 
     // Remove the node we're replacing
-    RefPtrWillBeRawPtr<Node> protectRemovedChild = oldChild;
-    ASSERT_UNUSED(protectRemovedChild, protectRemovedChild);
-    removeChild(oldChild, exceptionState);
+    removeChild(child, exceptionState);
     if (exceptionState.hadException())
-        return;
+        return nullptr;
 
     if (next && (next->previousSibling() == newChild || next == newChild)) // nothing to do
-        return;
+        return child;
 
     // Does this one more time because removeChild() fires a MutationEvent.
-    if (!checkAcceptChild(newChild.get(), oldChild, exceptionState))
-        return;
+    if (!checkAcceptChild(newChild.get(), child.get(), exceptionState)) {
+        if (exceptionState.hadException())
+            return nullptr;
+        return child;
+    }
 
     NodeVector targets;
     collectChildrenAndRemoveFromOldParent(*newChild, targets, exceptionState);
     if (exceptionState.hadException())
-        return;
+        return nullptr;
 
     // Does this yet another check because collectChildrenAndRemoveFromOldParent() fires a MutationEvent.
-    if (!checkAcceptChild(newChild.get(), oldChild, exceptionState))
-        return;
+    if (!checkAcceptChild(newChild.get(), child.get(), exceptionState)) {
+        if (exceptionState.hadException())
+            return nullptr;
+        return child;
+    }
 
     InspectorInstrumentation::willInsertDOMNode(this);
 
@@ -383,7 +401,7 @@ void ContainerNode::replaceChild(PassRefPtrWillBeRawPtr<Node> newChild, Node* ol
 
         // Add child before "next".
         {
-            NoEventDispatchAssertion assertNoEventDispatch;
+            EventDispatchForbiddenScope assertNoEventDispatch;
             if (next)
                 insertBeforeCommon(*next, child);
             else
@@ -394,6 +412,7 @@ void ContainerNode::replaceChild(PassRefPtrWillBeRawPtr<Node> newChild, Node* ol
     }
 
     dispatchSubtreeModifiedEvent();
+    return child;
 }
 
 void ContainerNode::willRemoveChild(Node& child)
@@ -423,6 +442,72 @@ void ContainerNode::willRemoveChildren()
     ChildFrameDisconnector(*this).disconnect(ChildFrameDisconnector::DescendantsOnly);
 }
 
+#if !ENABLE(OILPAN)
+void ContainerNode::removeDetachedChildrenInContainer(ContainerNode& container)
+{
+    // List of nodes to be deleted.
+    Node* head = 0;
+    Node* tail = 0;
+
+    addChildNodesToDeletionQueue(head, tail, container);
+
+    Node* n;
+    Node* next;
+    while ((n = head) != 0) {
+        ASSERT_WITH_SECURITY_IMPLICATION(n->m_deletionHasBegun);
+
+        next = n->nextSibling();
+        n->setNextSibling(0);
+
+        head = next;
+        if (next == 0)
+            tail = 0;
+
+        if (n->hasChildren())
+            addChildNodesToDeletionQueue(head, tail, toContainerNode(*n));
+
+        delete n;
+    }
+}
+
+void ContainerNode::addChildNodesToDeletionQueue(Node*& head, Node*& tail, ContainerNode& container)
+{
+    // We have to tell all children that their parent has died.
+    Node* next = 0;
+    for (Node* n = container.firstChild(); n; n = next) {
+        ASSERT_WITH_SECURITY_IMPLICATION(!n->m_deletionHasBegun);
+
+        next = n->nextSibling();
+        n->setNextSibling(0);
+        n->setParentOrShadowHostNode(0);
+        container.setFirstChild(next);
+        if (next)
+            next->setPreviousSibling(0);
+
+        if (!n->refCount()) {
+#if ENABLE(SECURITY_ASSERT)
+            n->m_deletionHasBegun = true;
+#endif
+            // Add the node to the list of nodes to be deleted.
+            // Reuse the nextSibling pointer for this purpose.
+            if (tail)
+                tail->setNextSibling(n);
+            else
+                head = n;
+
+            tail = n;
+        } else {
+            RefPtrWillBeRawPtr<Node> protect(n); // removedFromDocument may remove all references to this node.
+            container.document().adoptIfNeeded(*n);
+            if (n->inDocument())
+                container.notifyNodeRemoved(*n);
+        }
+    }
+
+    container.setLastChild(0);
+}
+#endif
+
 void ContainerNode::disconnectDescendantFrames()
 {
     ChildFrameDisconnector(*this).disconnect();
@@ -435,7 +520,7 @@ void ContainerNode::trace(Visitor* visitor)
     Node::trace(visitor);
 }
 
-void ContainerNode::removeChild(Node* oldChild, ExceptionState& exceptionState)
+PassRefPtrWillBeRawPtr<Node> ContainerNode::removeChild(PassRefPtrWillBeRawPtr<Node> oldChild, ExceptionState& exceptionState)
 {
 #if !ENABLE(OILPAN)
     // Check that this node is not "floating".
@@ -451,21 +536,18 @@ void ContainerNode::removeChild(Node* oldChild, ExceptionState& exceptionState)
     // ASSERT(!oldChild->isPseudoElement())
     if (!oldChild || oldChild->parentNode() != this || oldChild->isPseudoElement()) {
         exceptionState.throwDOMException(NotFoundError, "The node to be removed is not a child of this node.");
-        return;
+        return nullptr;
     }
 
     RefPtrWillBeRawPtr<Node> child = oldChild;
 
     document().removeFocusedElementOfSubtree(child.get());
 
-    if (FullscreenElementStack* fullscreen = FullscreenElementStack::fromIfExists(document()))
-        fullscreen->removeFullScreenElementOfSubtree(child.get());
-
     // Events fired when blurring currently focused node might have moved this
     // child into a different parent.
     if (child->parentNode() != this) {
         exceptionState.throwDOMException(NotFoundError, "The node to be removed is no longer a child of this node. Perhaps it was moved in a 'blur' event handler?");
-        return;
+        return nullptr;
     }
 
     willRemoveChild(*child);
@@ -473,7 +555,7 @@ void ContainerNode::removeChild(Node* oldChild, ExceptionState& exceptionState)
     // Mutation events might have moved this child into a different parent.
     if (child->parentNode() != this) {
         exceptionState.throwDOMException(NotFoundError, "The node to be removed is no longer a child of this node. Perhaps it was moved in response to a mutation?");
-        return;
+        return nullptr;
     }
 
     {
@@ -483,14 +565,15 @@ void ContainerNode::removeChild(Node* oldChild, ExceptionState& exceptionState)
         Node* next = child->nextSibling();
         removeBetween(prev, next, *child);
         notifyNodeRemoved(*child);
-        childrenChanged(false, prev, next, -1);
+        childrenChanged(ChildrenChange::forRemoval(*child, prev, next, ChildrenChangeSourceAPI));
     }
     dispatchSubtreeModifiedEvent();
+    return child;
 }
 
 void ContainerNode::removeBetween(Node* previousChild, Node* nextChild, Node& oldChild)
 {
-    NoEventDispatchAssertion assertNoEventDispatch;
+    EventDispatchForbiddenScope assertNoEventDispatch;
 
     ASSERT(oldChild.parentNode() == this);
 
@@ -528,8 +611,8 @@ void ContainerNode::parserRemoveChild(Node& oldChild)
 
     removeBetween(prev, next, oldChild);
 
-    childrenChanged(true, prev, next, -1);
     notifyNodeRemoved(oldChild);
+    childrenChanged(ChildrenChange::forRemoval(oldChild, prev, next, ChildrenChangeSourceParser));
 }
 
 // this differs from other remove functions because it forcibly removes all the children,
@@ -542,9 +625,6 @@ void ContainerNode::removeChildren()
     // The container node can be removed from event handlers.
     RefPtrWillBeRawPtr<ContainerNode> protect(this);
 
-    if (FullscreenElementStack* fullscreen = FullscreenElementStack::fromIfExists(document()))
-        fullscreen->removeFullScreenElementOfSubtree(this, true);
-
     // Do any prep work needed before actually starting to detach
     // and remove... e.g. stop loading frames, fire unload events.
     willRemoveChildren();
@@ -574,7 +654,7 @@ void ContainerNode::removeChildren()
         HTMLFrameOwnerElement::UpdateSuspendScope suspendWidgetHierarchyUpdates;
 
         {
-            NoEventDispatchAssertion assertNoEventDispatch;
+            EventDispatchForbiddenScope assertNoEventDispatch;
             ScriptForbiddenScope forbidScript;
 
             removedChildren.reserveInitialCapacity(countChildren());
@@ -586,13 +666,18 @@ void ContainerNode::removeChildren()
             }
         }
 
-        childrenChanged(false, 0, 0, -static_cast<int>(removedChildren.size()));
+        ChildrenChange change = {AllChildrenRemoved, nullptr, nullptr, ChildrenChangeSourceAPI};
+        childrenChanged(change);
     }
 
-    dispatchSubtreeModifiedEvent();
+    // We don't fire the DOMSubtreeModified event for Attr Nodes. This matches the behavior
+    // of IE and Firefox. This event is fired synchronously and is a source of trouble for
+    // attributes as the JS callback could alter the attributes and leave us in a bad state.
+    if (!isAttributeNode())
+        dispatchSubtreeModifiedEvent();
 }
 
-void ContainerNode::appendChild(PassRefPtrWillBeRawPtr<Node> newChild, ExceptionState& exceptionState)
+PassRefPtrWillBeRawPtr<Node> ContainerNode::appendChild(PassRefPtrWillBeRawPtr<Node> newChild, ExceptionState& exceptionState)
 {
     RefPtrWillBeRawPtr<ContainerNode> protect(this);
 
@@ -603,24 +688,30 @@ void ContainerNode::appendChild(PassRefPtrWillBeRawPtr<Node> newChild, Exception
 #endif
 
     // Make sure adding the new child is ok
-    if (!checkAcceptChild(newChild.get(), 0, exceptionState))
-        return;
+    if (!checkAcceptChild(newChild.get(), 0, exceptionState)) {
+        if (exceptionState.hadException())
+            return nullptr;
+        return newChild;
+    }
     ASSERT(newChild);
 
     if (newChild == m_lastChild) // nothing to do
-        return;
+        return newChild;
 
     NodeVector targets;
     collectChildrenAndRemoveFromOldParent(*newChild, targets, exceptionState);
     if (exceptionState.hadException())
-        return;
+        return nullptr;
 
     if (targets.isEmpty())
-        return;
+        return newChild;
 
     // We need this extra check because collectChildrenAndRemoveFromOldParent() can fire mutation events.
-    if (!checkAcceptChildGuaranteedNodeTypes(*newChild, exceptionState))
-        return;
+    if (!checkAcceptChildGuaranteedNodeTypes(*newChild, exceptionState)) {
+        if (exceptionState.hadException())
+            return nullptr;
+        return newChild;
+    }
 
     InspectorInstrumentation::willInsertDOMNode(this);
 
@@ -637,7 +728,7 @@ void ContainerNode::appendChild(PassRefPtrWillBeRawPtr<Node> newChild, Exception
             break;
 
         {
-            NoEventDispatchAssertion assertNoEventDispatch;
+            EventDispatchForbiddenScope assertNoEventDispatch;
             ScriptForbiddenScope forbidScript;
 
             treeScope().adoptIfNeeded(child);
@@ -648,6 +739,7 @@ void ContainerNode::appendChild(PassRefPtrWillBeRawPtr<Node> newChild, Exception
     }
 
     dispatchSubtreeModifiedEvent();
+    return newChild;
 }
 
 void ContainerNode::parserAppendChild(PassRefPtrWillBeRawPtr<Node> newChild)
@@ -663,7 +755,7 @@ void ContainerNode::parserAppendChild(PassRefPtrWillBeRawPtr<Node> newChild)
         document().adoptNode(newChild.get(), ASSERT_NO_EXCEPTION);
 
     {
-        NoEventDispatchAssertion assertNoEventDispatch;
+        EventDispatchForbiddenScope assertNoEventDispatch;
         ScriptForbiddenScope forbidScript;
 
         treeScope().adoptIfNeeded(*newChild);
@@ -677,7 +769,8 @@ void ContainerNode::parserAppendChild(PassRefPtrWillBeRawPtr<Node> newChild)
 
 void ContainerNode::notifyNodeInserted(Node& root, ChildrenChangeSource source)
 {
-    ASSERT(!NoEventDispatchAssertion::isEventDispatchForbidden());
+    ASSERT(!EventDispatchForbiddenScope::isEventDispatchForbidden());
+    ASSERT(!root.isShadowRoot());
 
     InspectorInstrumentation::didInsertDOMNode(&root);
 
@@ -687,13 +780,7 @@ void ContainerNode::notifyNodeInserted(Node& root, ChildrenChangeSource source)
     NodeVector postInsertionNotificationTargets;
     notifyNodeInsertedInternal(root, postInsertionNotificationTargets);
 
-    // ShadowRoots are not real children, we don't need to tell host that it's
-    // children changed when one is added.
-    // FIXME: We should have a separate code path for ShadowRoot since it only
-    // needs to call insertedInto and the rest of this logic is not needed.
-    if (!root.isShadowRoot()) {
-        childrenChanged(source == ChildrenChangeSourceParser, root.previousSibling(), root.nextSibling(), 1);
-    }
+    childrenChanged(ChildrenChange::forInsertion(root, source));
 
     for (size_t i = 0; i < postInsertionNotificationTargets.size(); ++i) {
         Node* targetNode = postInsertionNotificationTargets[i].get();
@@ -704,7 +791,7 @@ void ContainerNode::notifyNodeInserted(Node& root, ChildrenChangeSource source)
 
 void ContainerNode::notifyNodeInsertedInternal(Node& root, NodeVector& postInsertionNotificationTargets)
 {
-    NoEventDispatchAssertion assertNoEventDispatch;
+    EventDispatchForbiddenScope assertNoEventDispatch;
     ScriptForbiddenScope forbidScript;
 
     for (Node* node = &root; node; node = NodeTraversal::next(*node, &root)) {
@@ -722,7 +809,7 @@ void ContainerNode::notifyNodeInsertedInternal(Node& root, NodeVector& postInser
 void ContainerNode::notifyNodeRemoved(Node& root)
 {
     ScriptForbiddenScope forbidScript;
-    NoEventDispatchAssertion assertNoEventDispatch;
+    EventDispatchForbiddenScope assertNoEventDispatch;
 
     Document& document = root.document();
     for (Node* node = &root; node; node = NodeTraversal::next(*node, &root)) {
@@ -753,13 +840,13 @@ void ContainerNode::detach(const AttachContext& context)
     Node::detach(context);
 }
 
-void ContainerNode::childrenChanged(bool changedByParser, Node*, Node*, int childCountDelta)
+void ContainerNode::childrenChanged(const ChildrenChange& change)
 {
     document().incDOMTreeVersion();
-    if (!changedByParser && childCountDelta)
+    if (!change.byParser && change.type != TextChanged)
         document().updateRangesAfterChildrenChanged(this);
     invalidateNodeListCachesInAncestors();
-    if (childCountDelta > 0 && !childNeedsStyleRecalc()) {
+    if (change.isChildInsertion() && !childNeedsStyleRecalc()) {
         setChildNeedsStyleRecalc();
         markAncestorsWithChildNeedsStyleRecalc();
     }
@@ -1001,7 +1088,7 @@ void ContainerNode::setHovered(bool over)
 
 PassRefPtrWillBeRawPtr<HTMLCollection> ContainerNode::children()
 {
-    return ensureRareData().ensureNodeLists().addCache<HTMLCollection>(*this, NodeChildren);
+    return ensureCachedCollection<HTMLCollection>(NodeChildren);
 }
 
 unsigned ContainerNode::countChildren() const
@@ -1013,15 +1100,6 @@ unsigned ContainerNode::countChildren() const
     return count;
 }
 
-Node* ContainerNode::traverseToChildAt(unsigned index) const
-{
-    unsigned i;
-    Node *n = firstChild();
-    for (i = 0; n != 0 && i < index; i++)
-        n = n->nextSibling();
-    return n;
-}
-
 PassRefPtrWillBeRawPtr<Element> ContainerNode::querySelector(const AtomicString& selectors, ExceptionState& exceptionState)
 {
     if (selectors.isEmpty()) {
@@ -1035,7 +1113,7 @@ PassRefPtrWillBeRawPtr<Element> ContainerNode::querySelector(const AtomicString&
     return selectorQuery->queryFirst(*this);
 }
 
-PassRefPtrWillBeRawPtr<StaticNodeList> ContainerNode::querySelectorAll(const AtomicString& selectors, ExceptionState& exceptionState)
+PassRefPtrWillBeRawPtr<StaticElementList> ContainerNode::querySelectorAll(const AtomicString& selectors, ExceptionState& exceptionState)
 {
     if (selectors.isEmpty()) {
         exceptionState.throwDOMException(SyntaxError, "The provided selector is empty.");
@@ -1054,7 +1132,7 @@ static void dispatchChildInsertionEvents(Node& child)
     if (child.isInShadowTree())
         return;
 
-    ASSERT(!NoEventDispatchAssertion::isEventDispatchForbidden());
+    ASSERT(!EventDispatchForbiddenScope::isEventDispatchForbidden());
 
     RefPtrWillBeRawPtr<Node> c(child);
     RefPtrWillBeRawPtr<Document> document(child.document());
@@ -1076,7 +1154,7 @@ static void dispatchChildRemovalEvents(Node& child)
         return;
     }
 
-    ASSERT(!NoEventDispatchAssertion::isEventDispatchForbidden());
+    ASSERT(!EventDispatchForbiddenScope::isEventDispatchForbidden());
 
     InspectorInstrumentation::willRemoveDOMNode(&child);
 
@@ -1154,7 +1232,7 @@ void ContainerNode::checkForChildrenAdjacentRuleChanges()
     }
 }
 
-void ContainerNode::checkForSiblingStyleChanges(bool finishedParsingCallback, Node* beforeChange, Node* afterChange, int childCountDelta)
+void ContainerNode::checkForSiblingStyleChanges(SiblingCheckType changeType, Node* nodeBeforeChange, Node* nodeAfterChange)
 {
     if (!inActiveDocument() || document().hasPendingForcedStyleRecalc() || styleChangeType() >= SubtreeStyleChange)
         return;
@@ -1170,8 +1248,8 @@ void ContainerNode::checkForSiblingStyleChanges(bool finishedParsingCallback, No
     // |afterChange| is 0 in the parser callback case, so we won't do any work for the forward case if we don't have to.
     // For performance reasons we just mark the parent node as changed, since we don't want to make childrenChanged O(n^2) by crawling all our kids
     // here. recalcStyle will then force a walk of the children when it sees that this has happened.
-    if (((childrenAffectedByForwardPositionalRules() || childrenAffectedByIndirectAdjacentRules()) && afterChange)
-        || (childrenAffectedByBackwardPositionalRules() && beforeChange)) {
+    if (((childrenAffectedByForwardPositionalRules() || childrenAffectedByIndirectAdjacentRules()) && nodeAfterChange)
+        || (childrenAffectedByBackwardPositionalRules() && nodeBeforeChange)) {
         setNeedsStyleRecalc(SubtreeStyleChange);
         return;
     }
@@ -1179,49 +1257,79 @@ void ContainerNode::checkForSiblingStyleChanges(bool finishedParsingCallback, No
     // :first-child. In the parser callback case, we don't have to check anything, since we were right the first time.
     // In the DOM case, we only need to do something if |afterChange| is not 0.
     // |afterChange| is 0 in the parser case, so it works out that we'll skip this block.
-    if (childrenAffectedByFirstChildRules() && afterChange) {
-        // Find our new first child.
-        Element* newFirstChild = ElementTraversal::firstWithin(*this);
-        RenderStyle* newFirstChildStyle = newFirstChild ? newFirstChild->renderStyle() : 0;
-
-        // Find the first element node following |afterChange|
-        Node* firstElementAfterInsertion = afterChange->isElementNode() ? afterChange : ElementTraversal::nextSibling(*afterChange);
-        RenderStyle* firstElementAfterInsertionStyle = firstElementAfterInsertion ? firstElementAfterInsertion->renderStyle() : 0;
-
-        // This is the insert/append case.
-        if (newFirstChild != firstElementAfterInsertion && firstElementAfterInsertionStyle && firstElementAfterInsertionStyle->firstChildState())
-            firstElementAfterInsertion->setNeedsStyleRecalc(SubtreeStyleChange);
+    if (childrenAffectedByFirstChildRules() && nodeAfterChange) {
+        ASSERT(changeType != FinishedParsingChildren);
+        // Find our new first child element.
+        Element* firstChildElement = ElementTraversal::firstChild(*this);
+        RenderStyle* firstChildElementStyle = firstChildElement ? firstChildElement->renderStyle() : 0;
+
+        // Find the first element after the change.
+        Element* elementAfterChange = nodeAfterChange->isElementNode() ? toElement(nodeAfterChange) : ElementTraversal::nextSibling(*nodeAfterChange);
+        RenderStyle* elementAfterChangeStyle = elementAfterChange ? elementAfterChange->renderStyle() : 0;
+
+        // This is the element insertion as first child element case.
+        if (firstChildElement != elementAfterChange && elementAfterChangeStyle && elementAfterChangeStyle->firstChildState()) {
+            ASSERT(changeType == SiblingElementInserted);
+            elementAfterChange->setNeedsStyleRecalc(SubtreeStyleChange);
+        }
 
-        // We also have to handle node removal.
-        if (childCountDelta < 0 && newFirstChild == firstElementAfterInsertion && newFirstChild && (!newFirstChildStyle || !newFirstChildStyle->firstChildState()))
-            newFirstChild->setNeedsStyleRecalc(SubtreeStyleChange);
+        // This is the first child element removal case.
+        if (changeType == SiblingElementRemoved && firstChildElement == elementAfterChange && firstChildElement && (!firstChildElementStyle || !firstChildElementStyle->firstChildState()))
+            firstChildElement->setNeedsStyleRecalc(SubtreeStyleChange);
     }
 
     // :last-child. In the parser callback case, we don't have to check anything, since we were right the first time.
     // In the DOM case, we only need to do something if |afterChange| is not 0.
-    if (childrenAffectedByLastChildRules() && beforeChange) {
-        // Find our new last child.
-        Node* newLastChild = ElementTraversal::lastChild(*this);
-        RenderStyle* newLastChildStyle = newLastChild ? newLastChild->renderStyle() : 0;
-
-        // Find the last element node going backwards from |beforeChange|
-        Node* lastElementBeforeInsertion = beforeChange->isElementNode() ? beforeChange : ElementTraversal::previousSibling(*beforeChange);
-        RenderStyle* lastElementBeforeInsertionStyle = lastElementBeforeInsertion ? lastElementBeforeInsertion->renderStyle() : 0;
-
-        if (newLastChild != lastElementBeforeInsertion && lastElementBeforeInsertionStyle && lastElementBeforeInsertionStyle->lastChildState())
-            lastElementBeforeInsertion->setNeedsStyleRecalc(SubtreeStyleChange);
+    if (childrenAffectedByLastChildRules() && nodeBeforeChange) {
+        // Find our new last child element.
+        Element* lastChildElement = ElementTraversal::lastChild(*this);
+        RenderStyle* lastChildElementStyle = lastChildElement ? lastChildElement->renderStyle() : 0;
+
+        // Find the last element before the change.
+        Element* elementBeforeChange = nodeBeforeChange->isElementNode() ? toElement(nodeBeforeChange) : ElementTraversal::previousSibling(*nodeBeforeChange);
+        RenderStyle* elementBeforeChangeStyle = elementBeforeChange ? elementBeforeChange->renderStyle() : 0;
+
+        // This is the element insertion as last child element case.
+        if (lastChildElement != elementBeforeChange && elementBeforeChangeStyle && elementBeforeChangeStyle->lastChildState()) {
+            ASSERT(SiblingElementInserted);
+            elementBeforeChange->setNeedsStyleRecalc(SubtreeStyleChange);
+        }
 
-        // We also have to handle node removal. The parser callback case is similar to node removal as well in that we need to change the last child
+        // This is the last child element removal case. The parser callback case is similar to node removal as well in that we need to change the last child
         // to match now.
-        if ((childCountDelta < 0 || finishedParsingCallback) && newLastChild == lastElementBeforeInsertion && newLastChild && (!newLastChildStyle || !newLastChildStyle->lastChildState()))
-            newLastChild->setNeedsStyleRecalc(SubtreeStyleChange);
+        if ((changeType == SiblingElementRemoved || changeType == FinishedParsingChildren) && lastChildElement == elementBeforeChange && lastChildElement && (!lastChildElementStyle || !lastChildElementStyle->lastChildState()))
+            lastChildElement->setNeedsStyleRecalc(SubtreeStyleChange);
     }
 
-    // The + selector. We need to invalidate the first element following the insertion point. It is the only possible element
+    // The + selector. We need to invalidate the first element following the change. It is the only possible element
     // that could be affected by this DOM change.
-    if (childrenAffectedByDirectAdjacentRules() && afterChange) {
-        if (Node* firstElementAfterInsertion = afterChange->isElementNode() ? afterChange : ElementTraversal::nextSibling(*afterChange))
-            firstElementAfterInsertion->setNeedsStyleRecalc(SubtreeStyleChange);
+    if (childrenAffectedByDirectAdjacentRules() && nodeAfterChange) {
+        if (Element* elementAfterChange = nodeAfterChange->isElementNode() ? toElement(nodeAfterChange) : ElementTraversal::nextSibling(*nodeAfterChange))
+            elementAfterChange->setNeedsStyleRecalc(SubtreeStyleChange);
+    }
+}
+
+void ContainerNode::invalidateNodeListCachesInAncestors(const QualifiedName* attrName, Element* attributeOwnerElement)
+{
+    if (hasRareData() && (!attrName || isAttributeNode())) {
+        if (NodeListsNodeData* lists = rareData()->nodeLists()) {
+            if (ChildNodeList* childNodeList = lists->childNodeList(*this))
+                childNodeList->invalidateCache();
+        }
+    }
+
+    // Modifications to attributes that are not associated with an Element can't invalidate NodeList caches.
+    if (attrName && !attributeOwnerElement)
+        return;
+
+    if (!document().shouldInvalidateNodeListCaches(attrName))
+        return;
+
+    document().invalidateNodeListCaches(attrName);
+
+    for (ContainerNode* node = this; node; node = node->parentNode()) {
+        if (NodeListsNodeData* lists = node->nodeLists())
+            lists->invalidateCaches(attrName);
     }
 }
 
@@ -1231,8 +1339,8 @@ PassRefPtrWillBeRawPtr<TagCollection> ContainerNode::getElementsByTagName(const
         return nullptr;
 
     if (document().isHTMLDocument())
-        return ensureRareData().ensureNodeLists().addCache<HTMLTagCollection>(*this, HTMLTagCollectionType, localName);
-    return ensureRareData().ensureNodeLists().addCache<TagCollection>(*this, TagCollectionType, localName);
+        return ensureCachedCollection<HTMLTagCollection>(HTMLTagCollectionType, localName);
+    return ensureCachedCollection<TagCollection>(TagCollectionType, localName);
 }
 
 PassRefPtrWillBeRawPtr<TagCollection> ContainerNode::getElementsByTagNameNS(const AtomicString& namespaceURI, const AtomicString& localName)
@@ -1243,28 +1351,28 @@ PassRefPtrWillBeRawPtr<TagCollection> ContainerNode::getElementsByTagNameNS(cons
     if (namespaceURI == starAtom)
         return getElementsByTagName(localName);
 
-    return ensureRareData().ensureNodeLists().addCache(*this, namespaceURI.isEmpty() ? nullAtom : namespaceURI, localName);
+    return ensureCachedCollection<TagCollection>(TagCollectionType, namespaceURI.isEmpty() ? nullAtom : namespaceURI, localName);
 }
 
 // Takes an AtomicString in argument because it is common for elements to share the same name attribute.
 // Therefore, the NameNodeList factory function expects an AtomicString type.
 PassRefPtrWillBeRawPtr<NameNodeList> ContainerNode::getElementsByName(const AtomicString& elementName)
 {
-    return ensureRareData().ensureNodeLists().addCache<NameNodeList>(*this, NameNodeListType, elementName);
+    return ensureCachedCollection<NameNodeList>(NameNodeListType, elementName);
 }
 
 // Takes an AtomicString in argument because it is common for elements to share the same set of class names.
 // Therefore, the ClassNodeList factory function expects an AtomicString type.
 PassRefPtrWillBeRawPtr<ClassCollection> ContainerNode::getElementsByClassName(const AtomicString& classNames)
 {
-    return ensureRareData().ensureNodeLists().addCache<ClassCollection>(*this, ClassCollectionType, classNames);
+    return ensureCachedCollection<ClassCollection>(ClassCollectionType, classNames);
 }
 
 PassRefPtrWillBeRawPtr<RadioNodeList> ContainerNode::radioNodeList(const AtomicString& name, bool onlyMatchImgElements)
 {
     ASSERT(isHTMLFormElement(this) || isHTMLFieldSetElement(this));
     CollectionType type = onlyMatchImgElements ? RadioImgNodeListType : RadioNodeListType;
-    return ensureRareData().ensureNodeLists().addCache<RadioNodeList>(*this, type, name);
+    return ensureCachedCollection<RadioNodeList>(type, name);
 }
 
 Element* ContainerNode::getElementById(const AtomicString& id) const
@@ -1287,7 +1395,12 @@ Element* ContainerNode::getElementById(const AtomicString& id) const
     return 0;
 }
 
-#ifndef NDEBUG
+NodeListsNodeData& ContainerNode::ensureNodeLists()
+{
+    return ensureRareData().ensureNodeLists();
+}
+
+#if ENABLE(ASSERT)
 bool childAttachedAllowedWhenAttachingChildren(ContainerNode* node)
 {
     if (node->isShadowRoot())
@@ -1303,4 +1416,4 @@ bool childAttachedAllowedWhenAttachingChildren(ContainerNode* node)
 }
 #endif
 
-} // namespace WebCore
+} // namespace blink