summaryrefslogtreecommitdiff
path: root/Source/WebCore/dom/ContainerNodeAlgorithms.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/dom/ContainerNodeAlgorithms.cpp')
-rw-r--r--Source/WebCore/dom/ContainerNodeAlgorithms.cpp211
1 files changed, 172 insertions, 39 deletions
diff --git a/Source/WebCore/dom/ContainerNodeAlgorithms.cpp b/Source/WebCore/dom/ContainerNodeAlgorithms.cpp
index 9750df147..2958ec28f 100644
--- a/Source/WebCore/dom/ContainerNodeAlgorithms.cpp
+++ b/Source/WebCore/dom/ContainerNodeAlgorithms.cpp
@@ -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, 2010, 2011 Apple Inc. All rights reserved.
+ * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2015 Apple Inc. All rights reserved.
* Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
* Copyright (C) 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
* Copyright (C) 2012 Google Inc. All rights reserved.
@@ -26,75 +26,204 @@
#include "config.h"
#include "ContainerNodeAlgorithms.h"
+#include "HTMLFrameOwnerElement.h"
+#include "HTMLTextAreaElement.h"
+#include "InspectorInstrumentation.h"
+#include "NoEventDispatchAssertion.h"
+#include "ShadowRoot.h"
namespace WebCore {
-void ChildNodeInsertionNotifier::notifyDescendantInsertedIntoDocument(ContainerNode& node)
+static void notifyNodeInsertedIntoTree(ContainerNode& insertionPoint, ContainerNode&, NodeVector& postInsertionNotificationTargets);
+static void notifyNodeInsertedIntoDocument(ContainerNode& insertionPoint, Node&, NodeVector& postInsertionNotificationTargets);
+static void notifyNodeRemovedFromTree(ContainerNode& insertionPoint, ContainerNode&);
+static void notifyNodeRemovedFromDocument(ContainerNode& insertionPoint, Node&);
+
+static void notifyDescendantInsertedIntoDocument(ContainerNode& insertionPoint, ContainerNode& node, NodeVector& postInsertionNotificationTargets)
{
ChildNodesLazySnapshot snapshot(node);
while (RefPtr<Node> child = snapshot.nextNode()) {
// If we have been removed from the document during this loop, then
// 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.get());
+ if (node.isConnected() && child->parentNode() == &node)
+ notifyNodeInsertedIntoDocument(insertionPoint, *child, postInsertionNotificationTargets);
}
- if (!node.isElementNode())
+ if (!is<Element>(node))
return;
- if (RefPtr<ShadowRoot> root = toElement(node).shadowRoot()) {
- if (node.inDocument() && root->hostElement() == &node)
- notifyNodeInsertedIntoDocument(*root.get());
+ if (RefPtr<ShadowRoot> root = downcast<Element>(node).shadowRoot()) {
+ if (node.isConnected() && root->host() == &node)
+ notifyNodeInsertedIntoDocument(insertionPoint, *root, postInsertionNotificationTargets);
}
}
-void ChildNodeInsertionNotifier::notifyDescendantInsertedIntoTree(ContainerNode& node)
+static void notifyDescendantInsertedIntoTree(ContainerNode& insertionPoint, ContainerNode& node, NodeVector& postInsertionNotificationTargets)
{
for (Node* child = node.firstChild(); child; child = child->nextSibling()) {
- if (child->isContainerNode())
- notifyNodeInsertedIntoTree(*toContainerNode(child));
+ if (is<ContainerNode>(*child))
+ notifyNodeInsertedIntoTree(insertionPoint, downcast<ContainerNode>(*child), postInsertionNotificationTargets);
}
if (ShadowRoot* root = node.shadowRoot())
- notifyNodeInsertedIntoTree(*root);
+ notifyNodeInsertedIntoTree(insertionPoint, *root, postInsertionNotificationTargets);
+}
+
+void notifyNodeInsertedIntoDocument(ContainerNode& insertionPoint, Node& node, NodeVector& postInsertionNotificationTargets)
+{
+ ASSERT(insertionPoint.isConnected());
+ if (node.insertedInto(insertionPoint) == Node::InsertionShouldCallFinishedInsertingSubtree)
+ postInsertionNotificationTargets.append(node);
+ if (is<ContainerNode>(node))
+ notifyDescendantInsertedIntoDocument(insertionPoint, downcast<ContainerNode>(node), postInsertionNotificationTargets);
}
-void ChildNodeRemovalNotifier::notifyDescendantRemovedFromDocument(ContainerNode& node)
+void notifyNodeInsertedIntoTree(ContainerNode& insertionPoint, ContainerNode& node, NodeVector& postInsertionNotificationTargets)
{
+ NoEventDispatchAssertion assertNoEventDispatch;
+ ASSERT(!insertionPoint.isConnected());
+
+ if (node.insertedInto(insertionPoint) == Node::InsertionShouldCallFinishedInsertingSubtree)
+ postInsertionNotificationTargets.append(node);
+ notifyDescendantInsertedIntoTree(insertionPoint, node, postInsertionNotificationTargets);
+}
+
+void notifyChildNodeInserted(ContainerNode& insertionPoint, Node& node, NodeVector& postInsertionNotificationTargets)
+{
+ ASSERT_WITH_SECURITY_IMPLICATION(NoEventDispatchAssertion::isEventDispatchAllowedInSubtree(insertionPoint));
+
+ InspectorInstrumentation::didInsertDOMNode(node.document(), node);
+
+ Ref<Document> protectDocument(node.document());
+ Ref<Node> protectNode(node);
+
+ if (insertionPoint.isConnected())
+ notifyNodeInsertedIntoDocument(insertionPoint, node, postInsertionNotificationTargets);
+ else if (is<ContainerNode>(node))
+ notifyNodeInsertedIntoTree(insertionPoint, downcast<ContainerNode>(node), postInsertionNotificationTargets);
+}
+
+void notifyNodeRemovedFromDocument(ContainerNode& insertionPoint, Node& node)
+{
+ ASSERT(insertionPoint.isConnected());
+ node.removedFrom(insertionPoint);
+
+ if (!is<ContainerNode>(node))
+ return;
ChildNodesLazySnapshot snapshot(node);
while (RefPtr<Node> child = snapshot.nextNode()) {
// If we have been added to the document during this loop, then we
// don't want to tell the rest of our children that they've been
// removed from the document because they haven't.
- if (!node.inDocument() && child->parentNode() == &node)
- notifyNodeRemovedFromDocument(*child.get());
+ if (!node.isConnected() && child->parentNode() == &node)
+ notifyNodeRemovedFromDocument(insertionPoint, *child.get());
}
- if (!node.isElementNode())
+ if (!is<Element>(node))
return;
if (node.document().cssTarget() == &node)
- node.document().setCSSTarget(0);
+ node.document().setCSSTarget(nullptr);
- if (RefPtr<ShadowRoot> root = toElement(node).shadowRoot()) {
- if (!node.inDocument() && root->hostElement() == &node)
- notifyNodeRemovedFromDocument(*root.get());
+ if (RefPtr<ShadowRoot> root = downcast<Element>(node).shadowRoot()) {
+ if (!node.isConnected() && root->host() == &node)
+ notifyNodeRemovedFromDocument(insertionPoint, *root.get());
}
}
-void ChildNodeRemovalNotifier::notifyDescendantRemovedFromTree(ContainerNode& node)
+void notifyNodeRemovedFromTree(ContainerNode& insertionPoint, ContainerNode& node)
{
+ NoEventDispatchAssertion assertNoEventDispatch;
+ ASSERT(!insertionPoint.isConnected());
+
+ node.removedFrom(insertionPoint);
+
for (Node* child = node.firstChild(); child; child = child->nextSibling()) {
- if (child->isContainerNode())
- notifyNodeRemovedFromTree(*toContainerNode(child));
+ if (is<ContainerNode>(*child))
+ notifyNodeRemovedFromTree(insertionPoint, downcast<ContainerNode>(*child));
}
- if (!node.isElementNode())
+ if (!is<Element>(node))
return;
- if (RefPtr<ShadowRoot> root = toElement(node).shadowRoot())
- notifyNodeRemovedFromTree(*root.get());
+ if (RefPtr<ShadowRoot> root = downcast<Element>(node).shadowRoot())
+ notifyNodeRemovedFromTree(insertionPoint, *root.get());
+}
+
+void notifyChildNodeRemoved(ContainerNode& insertionPoint, Node& child)
+{
+ if (!child.isConnected()) {
+ if (is<ContainerNode>(child))
+ notifyNodeRemovedFromTree(insertionPoint, downcast<ContainerNode>(child));
+ return;
+ }
+ notifyNodeRemovedFromDocument(insertionPoint, child);
+}
+
+void addChildNodesToDeletionQueue(Node*& head, Node*& tail, ContainerNode& container)
+{
+ // We have to tell all children that their parent has died.
+ Node* next = nullptr;
+ for (auto* node = container.firstChild(); node; node = next) {
+ ASSERT(!node->m_deletionHasBegun);
+
+ next = node->nextSibling();
+ node->setNextSibling(nullptr);
+ node->setParentNode(nullptr);
+ container.setFirstChild(next);
+ if (next)
+ next->setPreviousSibling(nullptr);
+
+ if (!node->refCount()) {
+#ifndef NDEBUG
+ node->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(node);
+ else
+ head = node;
+
+ tail = node;
+ } else {
+ Ref<Node> protect(*node); // removedFromDocument may remove remove all references to this node.
+ if (Document* containerDocument = container.ownerDocument())
+ containerDocument->adoptIfNeeded(*node);
+ if (node->isInTreeScope())
+ notifyChildNodeRemoved(container, *node);
+ }
+ }
+
+ container.setLastChild(nullptr);
+}
+
+void removeDetachedChildrenInContainer(ContainerNode& container)
+{
+ // List of nodes to be deleted.
+ Node* head = nullptr;
+ Node* tail = nullptr;
+
+ addChildNodesToDeletionQueue(head, tail, container);
+
+ Node* node;
+ Node* next;
+ while ((node = head)) {
+ ASSERT(node->m_deletionHasBegun);
+
+ next = node->nextSibling();
+ node->setNextSibling(nullptr);
+
+ head = next;
+ if (!next)
+ tail = nullptr;
+
+ if (is<ContainerNode>(*node))
+ addChildNodesToDeletionQueue(head, tail, downcast<ContainerNode>(*node));
+
+ delete node;
+ }
}
#ifndef NDEBUG
@@ -102,11 +231,11 @@ static unsigned assertConnectedSubrameCountIsConsistent(ContainerNode& node)
{
unsigned count = 0;
- if (node.isElementNode()) {
- if (node.isFrameOwnerElement() && toHTMLFrameOwnerElement(node).contentFrame())
- count++;
+ if (is<Element>(node)) {
+ if (is<HTMLFrameOwnerElement>(node) && downcast<HTMLFrameOwnerElement>(node).contentFrame())
+ ++count;
- if (ShadowRoot* root = toElement(node).shadowRoot())
+ if (ShadowRoot* root = downcast<Element>(node).shadowRoot())
count += assertConnectedSubrameCountIsConsistent(*root);
}
@@ -138,8 +267,8 @@ static void collectFrameOwners(Vector<Ref<HTMLFrameOwnerElement>>& frameOwners,
continue;
}
- if (element.isHTMLElement() && element.isFrameOwnerElement())
- frameOwners.append(toHTMLFrameOwnerElement(element));
+ if (is<HTMLFrameOwnerElement>(element))
+ frameOwners.append(downcast<HTMLFrameOwnerElement>(element));
if (ShadowRoot* shadowRoot = element.shadowRoot())
collectFrameOwners(frameOwners, *shadowRoot);
@@ -157,22 +286,26 @@ void disconnectSubframes(ContainerNode& root, SubframeDisconnectPolicy policy)
Vector<Ref<HTMLFrameOwnerElement>> frameOwners;
if (policy == RootAndDescendants) {
- if (root.isHTMLElement() && root.isFrameOwnerElement())
- frameOwners.append(toHTMLFrameOwnerElement(root));
+ if (is<HTMLFrameOwnerElement>(root))
+ frameOwners.append(downcast<HTMLFrameOwnerElement>(root));
}
collectFrameOwners(frameOwners, root);
+ if (auto* shadowRoot = root.shadowRoot())
+ collectFrameOwners(frameOwners, *shadowRoot);
+
// Must disable frame loading in the subtree so an unload handler cannot
// insert more frames and create loaded frames in detached subtrees.
- SubframeLoadingDisabler disabler(root);
+ SubframeLoadingDisabler disabler(&root);
- for (unsigned i = 0; i < frameOwners.size(); ++i) {
- auto& owner = frameOwners[i].get();
+ bool isFirst = true;
+ for (auto& owner : frameOwners) {
// Don't need to traverse up the tree for the first owner since no
// script could have moved it.
- if (!i || root.containsIncludingShadowDOM(&owner))
- owner.disconnectContentFrame();
+ if (isFirst || root.containsIncludingShadowDOM(&owner.get()))
+ owner.get().disconnectContentFrame();
+ isFirst = false;
}
}