summaryrefslogtreecommitdiffstats
path: root/WebCore/dom/ContainerNode.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'WebCore/dom/ContainerNode.cpp')
-rw-r--r--WebCore/dom/ContainerNode.cpp141
1 files changed, 89 insertions, 52 deletions
diff --git a/WebCore/dom/ContainerNode.cpp b/WebCore/dom/ContainerNode.cpp
index 39cd3b4..f42b9cf 100644
--- a/WebCore/dom/ContainerNode.cpp
+++ b/WebCore/dom/ContainerNode.cpp
@@ -40,18 +40,32 @@
#include "RootInlineBox.h"
#include "loader.h"
#include <wtf/CurrentTime.h>
+#include <wtf/Vector.h>
namespace WebCore {
+static void notifyChildInserted(Node*);
static void dispatchChildInsertionEvents(Node*);
static void dispatchChildRemovalEvents(Node*);
typedef Vector<std::pair<NodeCallback, RefPtr<Node> > > NodeCallbackQueue;
+typedef Vector<RefPtr<Node>, 1> NodeVector;
static NodeCallbackQueue* s_postAttachCallbackQueue;
static size_t s_attachDepth;
static bool s_shouldReEnableMemoryCacheCallsAfterAttach;
+static void collectTargetNodes(Node* node, NodeVector& nodes)
+{
+ if (node->nodeType() != Node::DOCUMENT_FRAGMENT_NODE) {
+ nodes.append(node);
+ return;
+ }
+
+ for (Node* child = node->firstChild(); child; child = child->nextSibling())
+ nodes.append(child);
+}
+
void ContainerNode::removeAllChildren()
{
removeAllChildrenInContainer<Node, ContainerNode>(this);
@@ -85,11 +99,9 @@ bool ContainerNode::insertBefore(PassRefPtr<Node> newChild, Node* refChild, Exce
return false;
}
- bool isFragment = newChild->nodeType() == DOCUMENT_FRAGMENT_NODE;
-
- // If newChild is a DocumentFragment with no children; there's nothing to do.
- // Just return true
- if (isFragment && !newChild->firstChild())
+ NodeVector targets;
+ collectTargetNodes(newChild.get(), targets);
+ if (targets.isEmpty())
return true;
// Now actually add the child(ren)
@@ -98,16 +110,14 @@ bool ContainerNode::insertBefore(PassRefPtr<Node> newChild, Node* refChild, Exce
RefPtr<Node> next = refChild;
RefPtr<Node> refChildPreviousSibling = refChild->previousSibling();
-
- RefPtr<Node> child = isFragment ? newChild->firstChild() : newChild;
- while (child) {
- RefPtr<Node> nextChild = isFragment ? child->nextSibling() : 0;
+ for (NodeVector::const_iterator it = targets.begin(); it != targets.end(); ++it) {
+ Node* child = it->get();
// If child is already present in the tree, first remove it from the old location.
if (Node* oldParent = child->parentNode())
- oldParent->removeChild(child.get(), ec);
+ oldParent->removeChild(child, ec);
if (ec)
- return 0;
+ return false;
// FIXME: After sending the mutation events, "this" could be destroyed.
// We can prevent that by doing a "ref", but first we have to make sure
@@ -130,23 +140,23 @@ bool ContainerNode::insertBefore(PassRefPtr<Node> newChild, Node* refChild, Exce
forbidEventDispatch();
Node* prev = next->previousSibling();
ASSERT(m_lastChild != prev);
- next->setPreviousSibling(child.get());
+ next->setPreviousSibling(child);
if (prev) {
ASSERT(m_firstChild != next);
ASSERT(prev->nextSibling() == next);
- prev->setNextSibling(child.get());
+ prev->setNextSibling(child);
} else {
ASSERT(m_firstChild == next);
- m_firstChild = child.get();
+ m_firstChild = child;
}
child->setParent(this);
child->setPreviousSibling(prev);
child->setNextSibling(next.get());
allowEventDispatch();
- // Dispatch the mutation events.
+ // Send notification about the children change.
childrenChanged(false, refChildPreviousSibling.get(), next.get(), 1);
- dispatchChildInsertionEvents(child.get());
+ notifyChildInserted(child);
// Add child to the rendering tree.
if (attached() && !child->attached() && child->parent() == this) {
@@ -156,7 +166,9 @@ bool ContainerNode::insertBefore(PassRefPtr<Node> newChild, Node* refChild, Exce
child->attach();
}
- child = nextChild.release();
+ // Now that the child is attached to the render tree, dispatch
+ // the relevant mutation events.
+ dispatchChildInsertionEvents(child);
}
dispatchSubtreeModifiedEvent();
@@ -256,8 +268,7 @@ bool ContainerNode::replaceChild(PassRefPtr<Node> newChild, Node* oldChild, Exce
child->setNextSibling(next);
allowEventDispatch();
- // Dispatch the mutation events
- dispatchChildInsertionEvents(child.get());
+ notifyChildInserted(child.get());
// Add child to the rendering tree
if (attached() && !child->attached() && child->parent() == this) {
@@ -267,6 +278,10 @@ bool ContainerNode::replaceChild(PassRefPtr<Node> newChild, Node* oldChild, Exce
child->attach();
}
+ // Now that the child is attached to the render tree, dispatch
+ // the relevant mutation events.
+ dispatchChildInsertionEvents(child.get());
+
prev = child;
child = nextChild.release();
}
@@ -332,7 +347,14 @@ bool ContainerNode::removeChild(Node* oldChild, ExceptionCode& ec)
}
document()->removeFocusedNodeOfSubtree(child.get());
-
+
+ // Events fired when blurring currently focused node might have moved this
+ // child into a different parent.
+ if (child->parentNode() != this) {
+ ec = NOT_FOUND_ERR;
+ return false;
+ }
+
// FIXME: After sending the mutation events, "this" could be destroyed.
// We can prevent that by doing a "ref", but first we have to make sure
// that no callers call with ref count == 0 and parent = 0 (as of this
@@ -396,33 +418,43 @@ bool ContainerNode::removeChildren()
document()->removeFocusedNodeOfSubtree(this, true);
forbidEventDispatch();
- int childCountDelta = 0;
+ Vector<RefPtr<Node> > removedChildren;
while (RefPtr<Node> n = m_firstChild) {
- childCountDelta--;
-
Node* next = n->nextSibling();
- // Remove the node from the tree before calling detach or removedFromDocument (4427024, 4129744)
+ // Remove the node from the tree before calling detach or removedFromDocument (4427024, 4129744).
+ // removeChild() does this after calling detach(). There is no explanation for
+ // this discrepancy between removeChild() and its optimized version removeChildren().
n->setPreviousSibling(0);
n->setNextSibling(0);
n->setParent(0);
-
+
m_firstChild = next;
if (n == m_lastChild)
m_lastChild = 0;
if (n->attached())
n->detach();
-
- if (n->inDocument())
- n->removedFromDocument();
+
+ removedChildren.append(n.release());
}
allowEventDispatch();
+ size_t removedChildrenCount = removedChildren.size();
+
// Dispatch a single post-removal mutation event denoting a modified subtree.
- childrenChanged(false, 0, 0, childCountDelta);
+ childrenChanged(false, 0, 0, -static_cast<int>(removedChildrenCount));
dispatchSubtreeModifiedEvent();
+ for (size_t i = 0; i < removedChildrenCount; ++i) {
+ Node* removedChild = removedChildren[i].get();
+ if (removedChild->inDocument())
+ removedChild->removedFromDocument();
+ // removeChild() calls removedFromTree(true) if the child was not in the
+ // document. There is no explanation for this discrepancy between removeChild()
+ // and its optimized version removeChildren().
+ }
+
return true;
}
@@ -437,31 +469,26 @@ bool ContainerNode::appendChild(PassRefPtr<Node> newChild, ExceptionCode& ec, bo
// Make sure adding the new child is ok
checkAddChild(newChild.get(), ec);
if (ec)
- return 0;
-
+ return false;
+
if (newChild == m_lastChild) // nothing to do
return newChild;
- bool isFragment = newChild->nodeType() == DOCUMENT_FRAGMENT_NODE;
-
- // If newChild is a DocumentFragment with no children.... there's nothing to do.
- // Just return the document fragment
- if (isFragment && !newChild->firstChild())
+ NodeVector targets;
+ collectTargetNodes(newChild.get(), targets);
+ if (targets.isEmpty())
return true;
// Now actually add the child(ren)
RefPtr<Node> prev = lastChild();
- RefPtr<Node> child = isFragment ? newChild->firstChild() : newChild;
- while (child) {
- // For a fragment we have more children to do.
- RefPtr<Node> nextChild = isFragment ? child->nextSibling() : 0;
-
+ for (NodeVector::const_iterator it = targets.begin(); it != targets.end(); ++it) {
+ Node* child = it->get();
// If child is already present in the tree, first remove it
if (Node* oldParent = child->parentNode()) {
- oldParent->removeChild(child.get(), ec);
+ oldParent->removeChild(child, ec);
if (ec)
- return 0;
-
+ return false;
+
// If the child has a parent again, just stop what we're doing, because
// that means someone is doing something with DOM mutation -- can't re-parent
// a child that already has a parent.
@@ -474,15 +501,15 @@ bool ContainerNode::appendChild(PassRefPtr<Node> newChild, ExceptionCode& ec, bo
child->setParent(this);
if (m_lastChild) {
child->setPreviousSibling(m_lastChild);
- m_lastChild->setNextSibling(child.get());
+ m_lastChild->setNextSibling(child);
} else
- m_firstChild = child.get();
- m_lastChild = child.get();
+ m_firstChild = child;
+ m_lastChild = child;
allowEventDispatch();
- // Dispatch the mutation events
+ // Send notification about the children change.
childrenChanged(false, prev.get(), 0, 1);
- dispatchChildInsertionEvents(child.get());
+ notifyChildInserted(child);
// Add child to the rendering tree
if (attached() && !child->attached() && child->parent() == this) {
@@ -491,8 +518,10 @@ bool ContainerNode::appendChild(PassRefPtr<Node> newChild, ExceptionCode& ec, bo
else
child->attach();
}
-
- child = nextChild.release();
+
+ // Now that the child is attached to the render tree, dispatch
+ // the relevant mutation events.
+ dispatchChildInsertionEvents(child);
}
dispatchSubtreeModifiedEvent();
@@ -866,7 +895,7 @@ Node *ContainerNode::childNode(unsigned index) const
return n;
}
-static void dispatchChildInsertionEvents(Node* child)
+static void notifyChildInserted(Node* child)
{
ASSERT(!eventDispatchForbidden());
@@ -886,6 +915,14 @@ static void dispatchChildInsertionEvents(Node* child)
c->insertedIntoTree(true);
document->incDOMTreeVersion();
+}
+
+static void dispatchChildInsertionEvents(Node* child)
+{
+ ASSERT(!eventDispatchForbidden());
+
+ RefPtr<Node> c = child;
+ RefPtr<Document> document = child->document();
if (c->parentNode() && document->hasListenerType(Document::DOMNODEINSERTED_LISTENER))
c->dispatchEvent(MutationEvent::create(eventNames().DOMNodeInsertedEvent, true, c->parentNode()));