diff options
Diffstat (limited to 'WebCore/dom/ContainerNode.cpp')
-rw-r--r-- | WebCore/dom/ContainerNode.cpp | 141 |
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())); |