Index: third_party/WebKit/WebCore/dom/ContainerNode.cpp |
=================================================================== |
--- third_party/WebKit/WebCore/dom/ContainerNode.cpp (revision 9383) |
+++ third_party/WebKit/WebCore/dom/ContainerNode.cpp (working copy) |
@@ -1,920 +1,929 @@ |
-/* |
- * 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. |
- * |
- * This library is free software; you can redistribute it and/or |
- * modify it under the terms of the GNU Library General Public |
- * License as published by the Free Software Foundation; either |
- * version 2 of the License, or (at your option) any later version. |
- * |
- * This library is distributed in the hope that it will be useful, |
- * but WITHOUT ANY WARRANTY; without even the implied warranty of |
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
- * Library General Public License for more details. |
- * |
- * You should have received a copy of the GNU Library General Public License |
- * along with this library; see the file COPYING.LIB. If not, write to |
- * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
- * Boston, MA 02110-1301, USA. |
- */ |
- |
-#include "config.h" |
-#include "ContainerNode.h" |
- |
-#include "ContainerNodeAlgorithms.h" |
-#include "DeleteButtonController.h" |
-#include "EventNames.h" |
-#include "ExceptionCode.h" |
-#include "FloatRect.h" |
-#include "Frame.h" |
-#include "FrameView.h" |
-#include "InlineTextBox.h" |
-#include "MutationEvent.h" |
-#include "RenderTheme.h" |
-#include "RootInlineBox.h" |
-#include <wtf/CurrentTime.h> |
- |
-namespace WebCore { |
- |
-static void dispatchChildInsertionEvents(Node*, ExceptionCode&); |
-static void dispatchChildRemovalEvents(Node*, ExceptionCode&); |
- |
-typedef Vector<std::pair<NodeCallback, RefPtr<Node> > > NodeCallbackQueue; |
-static NodeCallbackQueue* s_postAttachCallbackQueue = 0; |
- |
-static size_t s_attachDepth = 0; |
- |
-void ContainerNode::removeAllChildren() |
-{ |
- removeAllChildrenInContainer<Node, ContainerNode>(this); |
-} |
- |
-ContainerNode::~ContainerNode() |
-{ |
- removeAllChildren(); |
-} |
- |
-bool ContainerNode::insertBefore(PassRefPtr<Node> newChild, Node* refChild, ExceptionCode& ec, bool shouldLazyAttach) |
-{ |
- // Check that this node is not "floating". |
- // If it is, it can be deleted as a side effect of sending mutation events. |
- ASSERT(refCount() || parent()); |
- |
- ec = 0; |
- |
- // insertBefore(node, 0) is equivalent to appendChild(node) |
- if (!refChild) |
- return appendChild(newChild, ec, shouldLazyAttach); |
- |
- // Make sure adding the new child is OK. |
- checkAddChild(newChild.get(), ec); |
- if (ec) |
- return false; |
- |
- // NOT_FOUND_ERR: Raised if refChild is not a child of this node |
- if (refChild->parentNode() != this) { |
- ec = NOT_FOUND_ERR; |
- 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()) |
- return true; |
- |
- // Now actually add the child(ren) |
- if (refChild->previousSibling() == newChild || refChild == newChild) // nothing to do |
- return true; |
- |
- RefPtr<Node> next = refChild; |
- RefPtr<Node> prev = refChild->previousSibling(); |
- |
- int childCountDelta = 0; |
- RefPtr<Node> child = isFragment ? newChild->firstChild() : newChild; |
- while (child) { |
- RefPtr<Node> nextChild = isFragment ? child->nextSibling() : 0; |
- |
- // 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); |
- if (ec) |
- return 0; |
- |
- // 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 |
- // writing, there are definitely callers who call that way). |
- |
- // Due to arbitrary code running in response to a DOM mutation event it's |
- // possible that "next" is no longer a child of "this". |
- // It's also possible that "child" has been inserted elsewhere. |
- // In either of those cases, we'll just stop. |
- if (next->parentNode() != this) |
- break; |
- if (child->parentNode()) |
- break; |
- |
- ASSERT(!child->nextSibling()); |
- ASSERT(!child->previousSibling()); |
- |
- childCountDelta++; |
- |
- // Add child before "next". |
- forbidEventDispatch(); |
- Node* prev = next->previousSibling(); |
- ASSERT(m_lastChild != prev); |
- next->setPreviousSibling(child.get()); |
- if (prev) { |
- ASSERT(m_firstChild != next); |
- ASSERT(prev->nextSibling() == next); |
- prev->setNextSibling(child.get()); |
- } else { |
- ASSERT(m_firstChild == next); |
- m_firstChild = child.get(); |
- } |
- child->setParent(this); |
- child->setPreviousSibling(prev); |
- child->setNextSibling(next.get()); |
- allowEventDispatch(); |
- |
- // Dispatch the mutation events. |
- dispatchChildInsertionEvents(child.get(), ec); |
- |
- // Add child to the rendering tree. |
- if (attached() && !child->attached() && child->parent() == this) { |
- if (shouldLazyAttach) |
- child->lazyAttach(); |
- else |
- child->attach(); |
- } |
- |
- child = nextChild.release(); |
- } |
- |
- document()->setDocumentChanged(true); |
- if (childCountDelta) |
- childrenChanged(false, prev.get(), next.get(), childCountDelta); |
- dispatchSubtreeModifiedEvent(); |
- return true; |
-} |
- |
-bool ContainerNode::replaceChild(PassRefPtr<Node> newChild, Node* oldChild, ExceptionCode& ec, bool shouldLazyAttach) |
-{ |
- // Check that this node is not "floating". |
- // If it is, it can be deleted as a side effect of sending mutation events. |
- ASSERT(refCount() || parent()); |
- |
- ec = 0; |
- |
- if (oldChild == newChild) // nothing to do |
- return true; |
- |
- // Make sure replacing the old child with the new is ok |
- checkReplaceChild(newChild.get(), oldChild, ec); |
- if (ec) |
- return false; |
- |
- // NOT_FOUND_ERR: Raised if oldChild is not a child of this node. |
- if (!oldChild || oldChild->parentNode() != this) { |
- ec = NOT_FOUND_ERR; |
- return false; |
- } |
- |
- RefPtr<Node> prev = oldChild->previousSibling(); |
- RefPtr<Node> next = oldChild->nextSibling(); |
- |
- // Remove the node we're replacing |
- RefPtr<Node> removedChild = oldChild; |
- removeChild(oldChild, ec); |
- if (ec) |
- 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 |
- // writing, there are definitely callers who call that way). |
- |
- bool isFragment = newChild->nodeType() == DOCUMENT_FRAGMENT_NODE; |
- |
- // Add the new child(ren) |
- int childCountDelta = 0; |
- RefPtr<Node> child = isFragment ? newChild->firstChild() : newChild; |
- while (child) { |
- // If the new child is already in the right place, we're done. |
- if (prev && (prev == child || prev == child->previousSibling())) |
- break; |
- |
- // For a fragment we have more children to do. |
- RefPtr<Node> nextChild = isFragment ? child->nextSibling() : 0; |
- |
- // Remove child from its old position. |
- if (Node* oldParent = child->parentNode()) |
- oldParent->removeChild(child.get(), ec); |
- if (ec) |
- return 0; |
- |
- // Due to arbitrary code running in response to a DOM mutation event it's |
- // possible that "prev" is no longer a child of "this". |
- // It's also possible that "child" has been inserted elsewhere. |
- // In either of those cases, we'll just stop. |
- if (prev && prev->parentNode() != this) |
- break; |
- if (child->parentNode()) |
- break; |
- |
- childCountDelta++; |
- |
- ASSERT(!child->nextSibling()); |
- ASSERT(!child->previousSibling()); |
- |
- // Add child after "prev". |
- forbidEventDispatch(); |
- Node* next; |
- if (prev) { |
- next = prev->nextSibling(); |
- ASSERT(m_firstChild != next); |
- prev->setNextSibling(child.get()); |
- } else { |
- next = m_firstChild; |
- m_firstChild = child.get(); |
- } |
- if (next) { |
- ASSERT(m_lastChild != prev); |
- ASSERT(next->previousSibling() == prev); |
- next->setPreviousSibling(child.get()); |
- } else { |
- ASSERT(m_lastChild == prev); |
- m_lastChild = child.get(); |
- } |
- child->setParent(this); |
- child->setPreviousSibling(prev.get()); |
- child->setNextSibling(next); |
- allowEventDispatch(); |
- |
- // Dispatch the mutation events |
- dispatchChildInsertionEvents(child.get(), ec); |
- |
- // Add child to the rendering tree |
- if (attached() && !child->attached() && child->parent() == this) { |
- if (shouldLazyAttach) |
- child->lazyAttach(); |
- else |
- child->attach(); |
- } |
- |
- prev = child; |
- child = nextChild.release(); |
- } |
- |
- document()->setDocumentChanged(true); |
- if (childCountDelta) |
- childrenChanged(false, prev.get(), next.get(), childCountDelta); |
- dispatchSubtreeModifiedEvent(); |
- return true; |
-} |
- |
-void ContainerNode::willRemove() |
-{ |
- for (Node *n = m_firstChild; n != 0; n = n->nextSibling()) |
- n->willRemove(); |
- Node::willRemove(); |
-} |
- |
-static ExceptionCode willRemoveChild(Node *child) |
-{ |
- ExceptionCode ec = 0; |
- |
- // fire removed from document mutation events. |
- dispatchChildRemovalEvents(child, ec); |
- if (ec) |
- return ec; |
- |
- if (child->attached()) |
- child->willRemove(); |
- |
- return 0; |
-} |
- |
-bool ContainerNode::removeChild(Node* oldChild, ExceptionCode& ec) |
-{ |
- // Check that this node is not "floating". |
- // If it is, it can be deleted as a side effect of sending mutation events. |
- ASSERT(refCount() || parent()); |
- |
- ec = 0; |
- |
- // NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly. |
- if (isReadOnlyNode()) { |
- ec = NO_MODIFICATION_ALLOWED_ERR; |
- return false; |
- } |
- |
- // NOT_FOUND_ERR: Raised if oldChild is not a child of this node. |
- if (!oldChild || oldChild->parentNode() != this) { |
- ec = NOT_FOUND_ERR; |
- return false; |
- } |
- |
- RefPtr<Node> child = oldChild; |
- |
- ec = willRemoveChild(child.get()); |
- if (ec) |
- return false; |
- |
- // Mutation events might have moved this child into a different parent. |
- if (child->parentNode() != this) { |
- ec = NOT_FOUND_ERR; |
- return false; |
- } |
- |
- document()->removeFocusedNodeOfSubtree(child.get()); |
- |
- // 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 |
- // writing, there are definitely callers who call that way). |
- |
- forbidEventDispatch(); |
- |
- // Remove from rendering tree |
- if (child->attached()) |
- child->detach(); |
- |
- // Remove the child |
- Node *prev, *next; |
- prev = child->previousSibling(); |
- next = child->nextSibling(); |
- |
- if (next) |
- next->setPreviousSibling(prev); |
- if (prev) |
- prev->setNextSibling(next); |
- if (m_firstChild == child) |
- m_firstChild = next; |
- if (m_lastChild == child) |
- m_lastChild = prev; |
- |
- child->setPreviousSibling(0); |
- child->setNextSibling(0); |
- child->setParent(0); |
- |
- allowEventDispatch(); |
- |
- document()->setDocumentChanged(true); |
- |
- // Dispatch post-removal mutation events |
- childrenChanged(false, prev, next, -1); |
- dispatchSubtreeModifiedEvent(); |
- |
- if (child->inDocument()) |
- child->removedFromDocument(); |
- else |
- child->removedFromTree(true); |
- |
- return child; |
-} |
- |
-// this differs from other remove functions because it forcibly removes all the children, |
-// regardless of read-only status or event exceptions, e.g. |
-bool ContainerNode::removeChildren() |
-{ |
- if (!m_firstChild) |
- return false; |
- |
- Node* n; |
- |
- // do any prep work needed before actually starting to detach |
- // and remove... e.g. stop loading frames, fire unload events |
- for (n = m_firstChild; n; n = n->nextSibling()) |
- willRemoveChild(n); |
- |
- // exclude this node when looking for removed focusedNode since only children will be removed |
- document()->removeFocusedNodeOfSubtree(this, true); |
- |
- forbidEventDispatch(); |
- int childCountDelta = 0; |
- while ((n = m_firstChild) != 0) { |
- childCountDelta--; |
- |
- Node *next = n->nextSibling(); |
- |
- n->ref(); |
- |
- // Remove the node from the tree before calling detach or removedFromDocument (4427024, 4129744) |
- 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(); |
- |
- n->deref(); |
- } |
- allowEventDispatch(); |
- |
- // Dispatch a single post-removal mutation event denoting a modified subtree. |
- childrenChanged(false, 0, 0, childCountDelta); |
- dispatchSubtreeModifiedEvent(); |
- |
- return true; |
-} |
- |
-bool ContainerNode::appendChild(PassRefPtr<Node> newChild, ExceptionCode& ec, bool shouldLazyAttach) |
-{ |
- // Check that this node is not "floating". |
- // If it is, it can be deleted as a side effect of sending mutation events. |
- ASSERT(refCount() || parent()); |
- |
- ec = 0; |
- |
- // Make sure adding the new child is ok |
- checkAddChild(newChild.get(), ec); |
- if (ec) |
- return 0; |
- |
- 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()) |
- return true; |
- |
- // Now actually add the child(ren) |
- int childCountDelta = 0; |
- 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; |
- |
- // If child is already present in the tree, first remove it |
- if (Node* oldParent = child->parentNode()) { |
- oldParent->removeChild(child.get(), ec); |
- if (ec) |
- return 0; |
- |
- // 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. |
- if (child->parentNode()) |
- break; |
- } |
- |
- // Append child to the end of the list |
- childCountDelta++; |
- forbidEventDispatch(); |
- child->setParent(this); |
- if (m_lastChild) { |
- child->setPreviousSibling(m_lastChild); |
- m_lastChild->setNextSibling(child.get()); |
- } else |
- m_firstChild = child.get(); |
- m_lastChild = child.get(); |
- allowEventDispatch(); |
- |
- // Dispatch the mutation events |
- dispatchChildInsertionEvents(child.get(), ec); |
- |
- // Add child to the rendering tree |
- if (attached() && !child->attached() && child->parent() == this) { |
- if (shouldLazyAttach) |
- child->lazyAttach(); |
- else |
- child->attach(); |
- } |
- |
- child = nextChild.release(); |
- } |
- |
- document()->setDocumentChanged(true); |
- childrenChanged(false, prev.get(), 0, childCountDelta); |
- dispatchSubtreeModifiedEvent(); |
- return true; |
-} |
- |
-ContainerNode* ContainerNode::addChild(PassRefPtr<Node> newChild) |
-{ |
- // This function is only used during parsing. |
- // It does not send any DOM mutation events. |
- |
- // Check for consistency with DTD, but only when parsing HTML. |
- if (document()->isHTMLDocument() && !childAllowed(newChild.get())) |
- return 0; |
- |
- forbidEventDispatch(); |
- Node* last = m_lastChild; |
- appendChildToContainer<Node, ContainerNode>(newChild.get(), this); |
- allowEventDispatch(); |
- |
- document()->incDOMTreeVersion(); |
- if (inDocument()) |
- newChild->insertedIntoDocument(); |
- childrenChanged(true, last, 0, 1); |
- |
- if (newChild->isElementNode()) |
- return static_cast<ContainerNode*>(newChild.get()); |
- return this; |
-} |
- |
-void ContainerNode::suspendPostAttachCallbacks() |
-{ |
- ++s_attachDepth; |
-} |
- |
-void ContainerNode::resumePostAttachCallbacks() |
-{ |
- if (s_attachDepth == 1 && s_postAttachCallbackQueue) |
- dispatchPostAttachCallbacks(); |
- --s_attachDepth; |
-} |
- |
-void ContainerNode::queuePostAttachCallback(NodeCallback callback, Node* node) |
-{ |
- if (!s_postAttachCallbackQueue) |
- s_postAttachCallbackQueue = new NodeCallbackQueue; |
- |
- s_postAttachCallbackQueue->append(std::pair<NodeCallback, RefPtr<Node> >(callback, node)); |
-} |
- |
-void ContainerNode::dispatchPostAttachCallbacks() |
-{ |
- // We recalculate size() each time through the loop because a callback |
- // can add more callbacks to the end of the queue. |
- for (size_t i = 0; i < s_postAttachCallbackQueue->size(); ++i) { |
- std::pair<NodeCallback, RefPtr<Node> >& pair = (*s_postAttachCallbackQueue)[i]; |
- NodeCallback callback = pair.first; |
- Node* node = pair.second.get(); |
- |
- callback(node); |
- } |
- s_postAttachCallbackQueue->clear(); |
-} |
- |
-void ContainerNode::attach() |
-{ |
- ++s_attachDepth; |
- |
- for (Node* child = m_firstChild; child; child = child->nextSibling()) |
- child->attach(); |
- Node::attach(); |
- |
- if (s_attachDepth == 1 && s_postAttachCallbackQueue) |
- dispatchPostAttachCallbacks(); |
- --s_attachDepth; |
-} |
- |
-void ContainerNode::detach() |
-{ |
- for (Node* child = m_firstChild; child; child = child->nextSibling()) |
- child->detach(); |
- setHasChangedChild(false); |
- Node::detach(); |
-} |
- |
-void ContainerNode::insertedIntoDocument() |
-{ |
- Node::insertedIntoDocument(); |
- insertedIntoTree(false); |
- for (Node* child = m_firstChild; child; child = child->nextSibling()) |
- child->insertedIntoDocument(); |
-} |
- |
-void ContainerNode::removedFromDocument() |
-{ |
- Node::removedFromDocument(); |
- setInDocument(false); |
- removedFromTree(false); |
- for (Node* child = m_firstChild; child; child = child->nextSibling()) |
- child->removedFromDocument(); |
-} |
- |
-void ContainerNode::insertedIntoTree(bool deep) |
-{ |
- if (!deep) |
- return; |
- for (Node* child = m_firstChild; child; child = child->nextSibling()) |
- child->insertedIntoTree(true); |
-} |
- |
-void ContainerNode::removedFromTree(bool deep) |
-{ |
- if (!deep) |
- return; |
- for (Node* child = m_firstChild; child; child = child->nextSibling()) |
- child->removedFromTree(true); |
-} |
- |
-void ContainerNode::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta) |
-{ |
- Node::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta); |
- if (!changedByParser && childCountDelta) |
- document()->nodeChildrenChanged(this); |
- if (document()->hasNodeListCaches()) |
- notifyNodeListsChildrenChanged(); |
-} |
- |
-void ContainerNode::cloneChildNodes(ContainerNode *clone) |
-{ |
- // disable the delete button so it's elements are not serialized into the markup |
- if (document()->frame()) |
- document()->frame()->editor()->deleteButtonController()->disable(); |
- ExceptionCode ec = 0; |
- for (Node* n = firstChild(); n && !ec; n = n->nextSibling()) |
- clone->appendChild(n->cloneNode(true), ec); |
- if (document()->frame()) |
- document()->frame()->editor()->deleteButtonController()->enable(); |
-} |
- |
-// FIXME: This doesn't work correctly with transforms. |
-bool ContainerNode::getUpperLeftCorner(FloatPoint& point) const |
-{ |
- if (!renderer()) |
- return false; |
- // What is this code really trying to do? |
- RenderObject *o = renderer(); |
- RenderObject *p = o; |
- |
- if (!o->isInline() || o->isReplaced()) { |
- point = o->localToAbsolute(); |
- return true; |
- } |
- |
- // find the next text/image child, to get a position |
- while (o) { |
- p = o; |
- if (o->firstChild()) |
- o = o->firstChild(); |
- else if (o->nextSibling()) |
- o = o->nextSibling(); |
- else { |
- RenderObject *next = 0; |
- while (!next && o->parent()) { |
- o = o->parent(); |
- next = o->nextSibling(); |
- } |
- o = next; |
- |
- if (!o) |
- break; |
- } |
- |
- if (!o->isInline() || o->isReplaced()) { |
- point = o->localToAbsolute(); |
- return true; |
- } |
- |
- if (p->element() && p->element() == this && o->isText() && !o->isBR() && !toRenderText(o)->firstTextBox()) { |
- // do nothing - skip unrendered whitespace that is a child or next sibling of the anchor |
- } else if ((o->isText() && !o->isBR()) || o->isReplaced()) { |
- point = o->container()->localToAbsolute(); |
- if (o->isText() && toRenderText(o)->firstTextBox()) { |
- point.move(toRenderText(o)->linesBoundingBox().x(), |
- toRenderText(o)->firstTextBox()->root()->topOverflow()); |
- } else if (o->isBox()) { |
- RenderBox* box = toRenderBox(o); |
- point.move(box->x(), box->y()); |
- } |
- return true; |
- } |
- } |
- |
- // If the target doesn't have any children or siblings that could be used to calculate the scroll position, we must be |
- // at the end of the document. Scroll to the bottom. FIXME: who said anything about scrolling? |
- if (!o && document()->view()) { |
- point = FloatPoint(0, document()->view()->contentsHeight()); |
- return true; |
- } |
- return false; |
-} |
- |
-// FIXME: This doesn't work correctly with transforms. |
-bool ContainerNode::getLowerRightCorner(FloatPoint& point) const |
-{ |
- if (!renderer()) |
- return false; |
- |
- RenderObject *o = renderer(); |
- if (!o->isInline() || o->isReplaced()) |
- { |
- RenderBox* box = toRenderBox(o); |
- point = o->localToAbsolute(); |
- point.move(box->width(), box->height()); |
- return true; |
- } |
- |
- // find the last text/image child, to get a position |
- while (o) { |
- if (o->lastChild()) |
- o = o->lastChild(); |
- else if (o->previousSibling()) |
- o = o->previousSibling(); |
- else { |
- RenderObject *prev = 0; |
- while(!prev) { |
- o = o->parent(); |
- if (!o) |
- return false; |
- prev = o->previousSibling(); |
- } |
- o = prev; |
- } |
- if (o->isText() || o->isReplaced()) { |
- point = o->container()->localToAbsolute(); |
- if (o->isText()) { |
- RenderText* text = toRenderText(o); |
- IntRect linesBox = text->linesBoundingBox(); |
- point.move(linesBox.x() + linesBox.width(), linesBox.height()); |
- } else { |
- RenderBox* box = toRenderBox(o); |
- point.move(box->x() + box->width(), box->y() + box->height()); |
- } |
- return true; |
- } |
- } |
- return true; |
-} |
- |
-IntRect ContainerNode::getRect() const |
-{ |
- FloatPoint upperLeft, lowerRight; |
- bool foundUpperLeft = getUpperLeftCorner(upperLeft); |
- bool foundLowerRight = getLowerRightCorner(lowerRight); |
- |
- // If we've found one corner, but not the other, |
- // then we should just return a point at the corner that we did find. |
- if (foundUpperLeft != foundLowerRight) |
- { |
- if (foundUpperLeft) |
- lowerRight = upperLeft; |
- else |
- upperLeft = lowerRight; |
- } |
- |
- lowerRight.setX(max(upperLeft.x(), lowerRight.x())); |
- lowerRight.setY(max(upperLeft.y(), lowerRight.y())); |
- |
- return enclosingIntRect(FloatRect(upperLeft, lowerRight - upperLeft)); |
-} |
- |
-void ContainerNode::setFocus(bool received) |
-{ |
- if (focused() == received) |
- return; |
- |
- Node::setFocus(received); |
- |
- // note that we need to recalc the style |
- setChanged(); |
-} |
- |
-void ContainerNode::setActive(bool down, bool pause) |
-{ |
- if (down == active()) return; |
- |
- Node::setActive(down); |
- |
- // note that we need to recalc the style |
- // FIXME: Move to Element |
- if (renderer()) { |
- bool reactsToPress = renderer()->style()->affectedByActiveRules(); |
- if (reactsToPress) |
- setChanged(); |
- if (renderer() && renderer()->style()->hasAppearance()) { |
- if (theme()->stateChanged(renderer(), PressedState)) |
- reactsToPress = true; |
- } |
- if (reactsToPress && pause) { |
- // The delay here is subtle. It relies on an assumption, namely that the amount of time it takes |
- // to repaint the "down" state of the control is about the same time as it would take to repaint the |
- // "up" state. Once you assume this, you can just delay for 100ms - that time (assuming that after you |
- // leave this method, it will be about that long before the flush of the up state happens again). |
-#ifdef HAVE_FUNC_USLEEP |
- double startTime = currentTime(); |
-#endif |
- |
- // Ensure there are no pending changes |
- Document::updateDocumentsRendering(); |
- // Do an immediate repaint. |
- if (renderer()) |
- renderer()->repaint(true); |
- |
- // FIXME: Find a substitute for usleep for Win32. |
- // Better yet, come up with a way of doing this that doesn't use this sort of thing at all. |
-#ifdef HAVE_FUNC_USLEEP |
- // Now pause for a small amount of time (1/10th of a second from before we repainted in the pressed state) |
- double remainingTime = 0.1 - (currentTime() - startTime); |
- if (remainingTime > 0) |
- usleep(static_cast<useconds_t>(remainingTime * 1000000.0)); |
-#endif |
- } |
- } |
-} |
- |
-void ContainerNode::setHovered(bool over) |
-{ |
- if (over == hovered()) return; |
- |
- Node::setHovered(over); |
- |
- // note that we need to recalc the style |
- // FIXME: Move to Element |
- if (renderer()) { |
- if (renderer()->style()->affectedByHoverRules()) |
- setChanged(); |
- if (renderer() && renderer()->style()->hasAppearance()) |
- theme()->stateChanged(renderer(), HoverState); |
- } |
-} |
- |
-unsigned ContainerNode::childNodeCount() const |
-{ |
- unsigned count = 0; |
- Node *n; |
- for (n = firstChild(); n; n = n->nextSibling()) |
- count++; |
- return count; |
-} |
- |
-Node *ContainerNode::childNode(unsigned index) const |
-{ |
- unsigned i; |
- Node *n = firstChild(); |
- for (i = 0; n != 0 && i < index; i++) |
- n = n->nextSibling(); |
- return n; |
-} |
- |
-static void dispatchChildInsertionEvents(Node* child, ExceptionCode& ec) |
-{ |
- ASSERT(!eventDispatchForbidden()); |
- |
- RefPtr<Node> c = child; |
- DocPtr<Document> doc = child->document(); |
- |
- if (c->parentNode() && c->parentNode()->inDocument()) |
- c->insertedIntoDocument(); |
- else |
- c->insertedIntoTree(true); |
- |
- if (c->parentNode() && doc->hasListenerType(Document::DOMNODEINSERTED_LISTENER)) { |
- ec = 0; |
- c->dispatchEvent(MutationEvent::create(eventNames().DOMNodeInsertedEvent, true, false, |
- c->parentNode(), String(), String(), String(), 0), ec); |
- if (ec) |
- return; |
- } |
- |
- // dispatch the DOMNodeInsertedIntoDocument event to all descendants |
- if (c->inDocument() && doc->hasListenerType(Document::DOMNODEINSERTEDINTODOCUMENT_LISTENER)) |
- for (; c; c = c->traverseNextNode(child)) { |
- ec = 0; |
- c->dispatchEvent(MutationEvent::create(eventNames().DOMNodeInsertedIntoDocumentEvent, false, false, |
- 0, String(), String(), String(), 0), ec); |
- if (ec) |
- return; |
- } |
-} |
- |
-static void dispatchChildRemovalEvents(Node* child, ExceptionCode& ec) |
-{ |
- RefPtr<Node> c = child; |
- DocPtr<Document> doc = child->document(); |
- |
- // update auxiliary doc info (e.g. iterators) to note that node is being removed |
- doc->nodeWillBeRemoved(child); |
- |
- // dispatch pre-removal mutation events |
- if (c->parentNode() && doc->hasListenerType(Document::DOMNODEREMOVED_LISTENER)) { |
- ec = 0; |
- c->dispatchEvent(MutationEvent::create(eventNames().DOMNodeRemovedEvent, true, false, |
- c->parentNode(), String(), String(), String(), 0), ec); |
- if (ec) |
- return; |
- } |
- |
- // dispatch the DOMNodeRemovedFromDocument event to all descendants |
- if (c->inDocument() && doc->hasListenerType(Document::DOMNODEREMOVEDFROMDOCUMENT_LISTENER)) |
- for (; c; c = c->traverseNextNode(child)) { |
- ec = 0; |
- c->dispatchEvent(MutationEvent::create(eventNames().DOMNodeRemovedFromDocumentEvent, false, false, |
- 0, String(), String(), String(), 0), ec); |
- if (ec) |
- return; |
- } |
-} |
- |
-} |
+/* |
+ * 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. |
+ * |
+ * This library is free software; you can redistribute it and/or |
+ * modify it under the terms of the GNU Library General Public |
+ * License as published by the Free Software Foundation; either |
+ * version 2 of the License, or (at your option) any later version. |
+ * |
+ * This library is distributed in the hope that it will be useful, |
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of |
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
+ * Library General Public License for more details. |
+ * |
+ * You should have received a copy of the GNU Library General Public License |
+ * along with this library; see the file COPYING.LIB. If not, write to |
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
+ * Boston, MA 02110-1301, USA. |
+ */ |
+ |
+#include "config.h" |
+#include "ContainerNode.h" |
+ |
+#include "ContainerNodeAlgorithms.h" |
+#include "DeleteButtonController.h" |
+#include "EventNames.h" |
+#include "ExceptionCode.h" |
+#include "FloatRect.h" |
+#include "Frame.h" |
+#include "FrameView.h" |
+#include "InlineTextBox.h" |
+#include "MutationEvent.h" |
+#include "RenderTheme.h" |
+#include "RootInlineBox.h" |
+#include <wtf/CurrentTime.h> |
+ |
+namespace WebCore { |
+ |
+static void dispatchChildInsertionEvents(Node*, ExceptionCode&); |
+static void dispatchChildRemovalEvents(Node*, ExceptionCode&); |
+ |
+typedef Vector<std::pair<NodeCallback, RefPtr<Node> > > NodeCallbackQueue; |
+static NodeCallbackQueue* s_postAttachCallbackQueue = 0; |
+ |
+static size_t s_attachDepth = 0; |
+ |
+void ContainerNode::removeAllChildren() |
+{ |
+ removeAllChildrenInContainer<Node, ContainerNode>(this); |
+} |
+ |
+ContainerNode::~ContainerNode() |
+{ |
+ removeAllChildren(); |
+} |
+ |
+bool ContainerNode::insertBefore(PassRefPtr<Node> newChild, Node* refChild, ExceptionCode& ec, bool shouldLazyAttach) |
+{ |
+ // Check that this node is not "floating". |
+ // If it is, it can be deleted as a side effect of sending mutation events. |
+ ASSERT(refCount() || parent()); |
+ |
+ ec = 0; |
+ |
+ // insertBefore(node, 0) is equivalent to appendChild(node) |
+ if (!refChild) |
+ return appendChild(newChild, ec, shouldLazyAttach); |
+ |
+ // Make sure adding the new child is OK. |
+ checkAddChild(newChild.get(), ec); |
+ if (ec) |
+ return false; |
+ |
+ // NOT_FOUND_ERR: Raised if refChild is not a child of this node |
+ if (refChild->parentNode() != this) { |
+ ec = NOT_FOUND_ERR; |
+ 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()) |
+ return true; |
+ |
+ // Now actually add the child(ren) |
+ if (refChild->previousSibling() == newChild || refChild == newChild) // nothing to do |
+ return true; |
+ |
+ RefPtr<Node> next = refChild; |
+ RefPtr<Node> prev = refChild->previousSibling(); |
+ |
+ int childCountDelta = 0; |
+ RefPtr<Node> child = isFragment ? newChild->firstChild() : newChild; |
+ while (child) { |
+ RefPtr<Node> nextChild = isFragment ? child->nextSibling() : 0; |
+ |
+ // 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); |
+ if (ec) |
+ return 0; |
+ |
+ // 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 |
+ // writing, there are definitely callers who call that way). |
+ |
+ // Due to arbitrary code running in response to a DOM mutation event it's |
+ // possible that "next" is no longer a child of "this". |
+ // It's also possible that "child" has been inserted elsewhere. |
+ // In either of those cases, we'll just stop. |
+ if (next->parentNode() != this) |
+ break; |
+ if (child->parentNode()) |
+ break; |
+ |
+ ASSERT(!child->nextSibling()); |
+ ASSERT(!child->previousSibling()); |
+ |
+ childCountDelta++; |
+ |
+ // Add child before "next". |
+ forbidEventDispatch(); |
+ Node* prev = next->previousSibling(); |
+ ASSERT(m_lastChild != prev); |
+ next->setPreviousSibling(child.get()); |
+ if (prev) { |
+ ASSERT(m_firstChild != next); |
+ ASSERT(prev->nextSibling() == next); |
+ prev->setNextSibling(child.get()); |
+ } else { |
+ ASSERT(m_firstChild == next); |
+ m_firstChild = child.get(); |
+ } |
+ child->setParent(this); |
+ child->setPreviousSibling(prev); |
+ child->setNextSibling(next.get()); |
+ allowEventDispatch(); |
+ |
+ // Dispatch the mutation events. |
+ dispatchChildInsertionEvents(child.get(), ec); |
+ |
+ // Add child to the rendering tree. |
+ if (attached() && !child->attached() && child->parent() == this) { |
+ if (shouldLazyAttach) |
+ child->lazyAttach(); |
+ else |
+ child->attach(); |
+ } |
+ |
+ child = nextChild.release(); |
+ } |
+ |
+ document()->setDocumentChanged(true); |
+ if (childCountDelta) |
+ childrenChanged(false, prev.get(), next.get(), childCountDelta); |
+ dispatchSubtreeModifiedEvent(); |
+ return true; |
+} |
+ |
+bool ContainerNode::replaceChild(PassRefPtr<Node> newChild, Node* oldChild, ExceptionCode& ec, bool shouldLazyAttach) |
+{ |
+ // Check that this node is not "floating". |
+ // If it is, it can be deleted as a side effect of sending mutation events. |
+ ASSERT(refCount() || parent()); |
+ |
+ ec = 0; |
+ |
+ if (oldChild == newChild) // nothing to do |
+ return true; |
+ |
+ // Make sure replacing the old child with the new is ok |
+ checkReplaceChild(newChild.get(), oldChild, ec); |
+ if (ec) |
+ return false; |
+ |
+ // NOT_FOUND_ERR: Raised if oldChild is not a child of this node. |
+ if (!oldChild || oldChild->parentNode() != this) { |
+ ec = NOT_FOUND_ERR; |
+ return false; |
+ } |
+ |
+ RefPtr<Node> prev = oldChild->previousSibling(); |
+ RefPtr<Node> next = oldChild->nextSibling(); |
+ |
+ // Remove the node we're replacing |
+ RefPtr<Node> removedChild = oldChild; |
+ removeChild(oldChild, ec); |
+ if (ec) |
+ 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 |
+ // writing, there are definitely callers who call that way). |
+ |
+ bool isFragment = newChild->nodeType() == DOCUMENT_FRAGMENT_NODE; |
+ |
+ // Add the new child(ren) |
+ int childCountDelta = 0; |
+ RefPtr<Node> child = isFragment ? newChild->firstChild() : newChild; |
+ while (child) { |
+ // If the new child is already in the right place, we're done. |
+ if (prev && (prev == child || prev == child->previousSibling())) |
+ break; |
+ |
+ // For a fragment we have more children to do. |
+ RefPtr<Node> nextChild = isFragment ? child->nextSibling() : 0; |
+ |
+ // Remove child from its old position. |
+ if (Node* oldParent = child->parentNode()) |
+ oldParent->removeChild(child.get(), ec); |
+ if (ec) |
+ return 0; |
+ |
+ // Due to arbitrary code running in response to a DOM mutation event it's |
+ // possible that "prev" is no longer a child of "this". |
+ // It's also possible that "child" has been inserted elsewhere. |
+ // In either of those cases, we'll just stop. |
+ if (prev && prev->parentNode() != this) |
+ break; |
+ if (child->parentNode()) |
+ break; |
+ |
+ childCountDelta++; |
+ |
+ ASSERT(!child->nextSibling()); |
+ ASSERT(!child->previousSibling()); |
+ |
+ // Add child after "prev". |
+ forbidEventDispatch(); |
+ Node* next; |
+ if (prev) { |
+ next = prev->nextSibling(); |
+ ASSERT(m_firstChild != next); |
+ prev->setNextSibling(child.get()); |
+ } else { |
+ next = m_firstChild; |
+ m_firstChild = child.get(); |
+ } |
+ if (next) { |
+ ASSERT(m_lastChild != prev); |
+ ASSERT(next->previousSibling() == prev); |
+ next->setPreviousSibling(child.get()); |
+ } else { |
+ ASSERT(m_lastChild == prev); |
+ m_lastChild = child.get(); |
+ } |
+ child->setParent(this); |
+ child->setPreviousSibling(prev.get()); |
+ child->setNextSibling(next); |
+ allowEventDispatch(); |
+ |
+ // Dispatch the mutation events |
+ dispatchChildInsertionEvents(child.get(), ec); |
+ |
+ // Add child to the rendering tree |
+ if (attached() && !child->attached() && child->parent() == this) { |
+ if (shouldLazyAttach) |
+ child->lazyAttach(); |
+ else |
+ child->attach(); |
+ } |
+ |
+ prev = child; |
+ child = nextChild.release(); |
+ } |
+ |
+ document()->setDocumentChanged(true); |
+ if (childCountDelta) |
+ childrenChanged(false, prev.get(), next.get(), childCountDelta); |
+ dispatchSubtreeModifiedEvent(); |
+ return true; |
+} |
+ |
+void ContainerNode::willRemove() |
+{ |
+ for (Node *n = m_firstChild; n != 0; n = n->nextSibling()) |
+ n->willRemove(); |
+ EventTargetNode::willRemove(); |
+} |
+ |
+static ExceptionCode willRemoveChild(Node *child) |
+{ |
+ ExceptionCode ec = 0; |
+ |
+ // fire removed from document mutation events. |
+ dispatchChildRemovalEvents(child, ec); |
+ if (ec) |
+ return ec; |
+ |
+ if (child->attached()) |
+ child->willRemove(); |
+ |
+ return 0; |
+} |
+ |
+bool ContainerNode::removeChild(Node* oldChild, ExceptionCode& ec) |
+{ |
+ // Check that this node is not "floating". |
+ // If it is, it can be deleted as a side effect of sending mutation events. |
+ ASSERT(refCount() || parent()); |
+ |
+ ec = 0; |
+ |
+ // NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly. |
+ if (isReadOnlyNode()) { |
+ ec = NO_MODIFICATION_ALLOWED_ERR; |
+ return false; |
+ } |
+ |
+ // NOT_FOUND_ERR: Raised if oldChild is not a child of this node. |
+ if (!oldChild || oldChild->parentNode() != this) { |
+ ec = NOT_FOUND_ERR; |
+ return false; |
+ } |
+ |
+ RefPtr<Node> child = oldChild; |
+ |
+ ec = willRemoveChild(child.get()); |
+ if (ec) |
+ return false; |
+ |
+ // Mutation events might have moved this child into a different parent. |
+ if (child->parentNode() != this) { |
+ ec = NOT_FOUND_ERR; |
+ return false; |
+ } |
+ |
+ document()->removeFocusedNodeOfSubtree(child.get()); |
+ |
+ // 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 |
+ // writing, there are definitely callers who call that way). |
+ |
+ forbidEventDispatch(); |
+ |
+ // Remove from rendering tree |
+ if (child->attached()) |
+ child->detach(); |
+ |
+ // Remove the child |
+ Node *prev, *next; |
+ prev = child->previousSibling(); |
+ next = child->nextSibling(); |
+ |
+ if (next) |
+ next->setPreviousSibling(prev); |
+ if (prev) |
+ prev->setNextSibling(next); |
+ if (m_firstChild == child) |
+ m_firstChild = next; |
+ if (m_lastChild == child) |
+ m_lastChild = prev; |
+ |
+ child->setPreviousSibling(0); |
+ child->setNextSibling(0); |
+ child->setParent(0); |
+ |
+ allowEventDispatch(); |
+ |
+ document()->setDocumentChanged(true); |
+ |
+ // Dispatch post-removal mutation events |
+ childrenChanged(false, prev, next, -1); |
+ dispatchSubtreeModifiedEvent(); |
+ |
+ if (child->inDocument()) |
+ child->removedFromDocument(); |
+ else |
+ child->removedFromTree(true); |
+ |
+ return child; |
+} |
+ |
+// this differs from other remove functions because it forcibly removes all the children, |
+// regardless of read-only status or event exceptions, e.g. |
+bool ContainerNode::removeChildren() |
+{ |
+ if (!m_firstChild) |
+ return false; |
+ |
+ Node* n; |
+ |
+ // do any prep work needed before actually starting to detach |
+ // and remove... e.g. stop loading frames, fire unload events |
+ for (n = m_firstChild; n; n = n->nextSibling()) |
+ willRemoveChild(n); |
+ |
+ // exclude this node when looking for removed focusedNode since only children will be removed |
+ document()->removeFocusedNodeOfSubtree(this, true); |
+ |
+ forbidEventDispatch(); |
+ int childCountDelta = 0; |
+ while ((n = m_firstChild) != 0) { |
+ childCountDelta--; |
+ |
+ Node *next = n->nextSibling(); |
+ |
+ n->ref(); |
+ |
+ // Remove the node from the tree before calling detach or removedFromDocument (4427024, 4129744) |
+ 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(); |
+ |
+ n->deref(); |
+ } |
+ allowEventDispatch(); |
+ |
+ // Dispatch a single post-removal mutation event denoting a modified subtree. |
+ childrenChanged(false, 0, 0, childCountDelta); |
+ dispatchSubtreeModifiedEvent(); |
+ |
+ return true; |
+} |
+ |
+bool ContainerNode::appendChild(PassRefPtr<Node> newChild, ExceptionCode& ec, bool shouldLazyAttach) |
+{ |
+ // Check that this node is not "floating". |
+ // If it is, it can be deleted as a side effect of sending mutation events. |
+ ASSERT(refCount() || parent()); |
+ |
+ ec = 0; |
+ |
+ // Make sure adding the new child is ok |
+ checkAddChild(newChild.get(), ec); |
+ if (ec) |
+ return 0; |
+ |
+ 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()) |
+ return true; |
+ |
+ // Now actually add the child(ren) |
+ int childCountDelta = 0; |
+ 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; |
+ |
+ // If child is already present in the tree, first remove it |
+ if (Node* oldParent = child->parentNode()) { |
+ oldParent->removeChild(child.get(), ec); |
+ if (ec) |
+ return 0; |
+ |
+ // 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. |
+ if (child->parentNode()) |
+ break; |
+ } |
+ |
+ // Append child to the end of the list |
+ childCountDelta++; |
+ forbidEventDispatch(); |
+ child->setParent(this); |
+ if (m_lastChild) { |
+ child->setPreviousSibling(m_lastChild); |
+ m_lastChild->setNextSibling(child.get()); |
+ } else |
+ m_firstChild = child.get(); |
+ m_lastChild = child.get(); |
+ allowEventDispatch(); |
+ |
+ // Dispatch the mutation events |
+ dispatchChildInsertionEvents(child.get(), ec); |
+ |
+ // Add child to the rendering tree |
+ if (attached() && !child->attached() && child->parent() == this) { |
+ if (shouldLazyAttach) |
+ child->lazyAttach(); |
+ else |
+ child->attach(); |
+ } |
+ |
+ child = nextChild.release(); |
+ } |
+ |
+ document()->setDocumentChanged(true); |
+ childrenChanged(false, prev.get(), 0, childCountDelta); |
+ dispatchSubtreeModifiedEvent(); |
+ return true; |
+} |
+ |
+ContainerNode* ContainerNode::addChild(PassRefPtr<Node> newChild) |
+{ |
+ // This function is only used during parsing. |
+ // It does not send any DOM mutation events. |
+ |
+ // Check for consistency with DTD, but only when parsing HTML. |
+ if (document()->isHTMLDocument() && !childAllowed(newChild.get())) |
+ return 0; |
+ |
+ forbidEventDispatch(); |
+ Node* last = m_lastChild; |
+ appendChildToContainer<Node, ContainerNode>(newChild.get(), this); |
+ allowEventDispatch(); |
+ |
+ document()->incDOMTreeVersion(); |
+ if (inDocument()) |
+ newChild->insertedIntoDocument(); |
+ childrenChanged(true, last, 0, 1); |
+ |
+ if (newChild->isElementNode()) |
+ return static_cast<ContainerNode*>(newChild.get()); |
+ return this; |
+} |
+ |
+void ContainerNode::suspendPostAttachCallbacks() |
+{ |
+ ++s_attachDepth; |
+} |
+ |
+void ContainerNode::resumePostAttachCallbacks() |
+{ |
+ if (s_attachDepth == 1 && s_postAttachCallbackQueue) |
+ dispatchPostAttachCallbacks(); |
+ --s_attachDepth; |
+} |
+ |
+void ContainerNode::queuePostAttachCallback(NodeCallback callback, Node* node) |
+{ |
+ if (!s_postAttachCallbackQueue) |
+ s_postAttachCallbackQueue = new NodeCallbackQueue; |
+ |
+ s_postAttachCallbackQueue->append(std::pair<NodeCallback, RefPtr<Node> >(callback, node)); |
+} |
+ |
+void ContainerNode::dispatchPostAttachCallbacks() |
+{ |
+ // We recalculate size() each time through the loop because a callback |
+ // can add more callbacks to the end of the queue. |
+ for (size_t i = 0; i < s_postAttachCallbackQueue->size(); ++i) { |
+ std::pair<NodeCallback, RefPtr<Node> >& pair = (*s_postAttachCallbackQueue)[i]; |
+ NodeCallback callback = pair.first; |
+ Node* node = pair.second.get(); |
+ |
+ callback(node); |
+ } |
+ s_postAttachCallbackQueue->clear(); |
+} |
+ |
+void ContainerNode::attach() |
+{ |
+ ++s_attachDepth; |
+ |
+ for (Node* child = m_firstChild; child; child = child->nextSibling()) |
+ child->attach(); |
+ EventTargetNode::attach(); |
+ |
+ if (s_attachDepth == 1 && s_postAttachCallbackQueue) |
+ dispatchPostAttachCallbacks(); |
+ --s_attachDepth; |
+} |
+ |
+void ContainerNode::detach() |
+{ |
+ for (Node* child = m_firstChild; child; child = child->nextSibling()) |
+ child->detach(); |
+ setHasChangedChild(false); |
+ EventTargetNode::detach(); |
+} |
+ |
+void ContainerNode::insertedIntoDocument() |
+{ |
+ EventTargetNode::insertedIntoDocument(); |
+ insertedIntoTree(false); |
+ for (Node* child = m_firstChild; child; child = child->nextSibling()) |
+ child->insertedIntoDocument(); |
+} |
+ |
+void ContainerNode::removedFromDocument() |
+{ |
+ EventTargetNode::removedFromDocument(); |
+ setInDocument(false); |
+ removedFromTree(false); |
+ for (Node* child = m_firstChild; child; child = child->nextSibling()) |
+ child->removedFromDocument(); |
+} |
+ |
+void ContainerNode::insertedIntoTree(bool deep) |
+{ |
+ if (!deep) |
+ return; |
+ for (Node* child = m_firstChild; child; child = child->nextSibling()) |
+ child->insertedIntoTree(true); |
+} |
+ |
+void ContainerNode::removedFromTree(bool deep) |
+{ |
+ if (!deep) |
+ return; |
+ for (Node* child = m_firstChild; child; child = child->nextSibling()) |
+ child->removedFromTree(true); |
+} |
+ |
+void ContainerNode::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta) |
+{ |
+ Node::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta); |
+ if (!changedByParser && childCountDelta) |
+ document()->nodeChildrenChanged(this); |
+ if (document()->hasNodeListCaches()) |
+ notifyNodeListsChildrenChanged(); |
+} |
+ |
+void ContainerNode::cloneChildNodes(ContainerNode *clone) |
+{ |
+ // disable the delete button so it's elements are not serialized into the markup |
+ if (document()->frame()) |
+ document()->frame()->editor()->deleteButtonController()->disable(); |
+ ExceptionCode ec = 0; |
+ for (Node* n = firstChild(); n && !ec; n = n->nextSibling()) |
+ clone->appendChild(n->cloneNode(true), ec); |
+ if (document()->frame()) |
+ document()->frame()->editor()->deleteButtonController()->enable(); |
+} |
+ |
+// FIXME: This doesn't work correctly with transforms. |
+bool ContainerNode::getUpperLeftCorner(FloatPoint& point) const |
+{ |
+ if (!renderer()) |
+ return false; |
+ // What is this code really trying to do? |
+ RenderObject *o = renderer(); |
+ RenderObject *p = o; |
+ |
+ if (!o->isInline() || o->isReplaced()) { |
+ point = o->localToAbsolute(); |
+ return true; |
+ } |
+ |
+ // find the next text/image child, to get a position |
+ while (o) { |
+ p = o; |
+ if (o->firstChild()) |
+ o = o->firstChild(); |
+ else if (o->nextSibling()) |
+ o = o->nextSibling(); |
+ else { |
+ RenderObject *next = 0; |
+ while (!next && o->parent()) { |
+ o = o->parent(); |
+ next = o->nextSibling(); |
+ } |
+ o = next; |
+ |
+ if (!o) |
+ break; |
+ } |
+ |
+ if (!o->isInline() || o->isReplaced()) { |
+ point = o->localToAbsolute(); |
+ return true; |
+ } |
+ |
+ if (p->element() && p->element() == this && o->isText() && !o->isBR() && !toRenderText(o)->firstTextBox()) { |
+ // do nothing - skip unrendered whitespace that is a child or next sibling of the anchor |
+ } else if ((o->isText() && !o->isBR()) || o->isReplaced()) { |
+ point = o->container()->localToAbsolute(); |
+ if (o->isText() && toRenderText(o)->firstTextBox()) { |
+ point.move(toRenderText(o)->linesBoundingBox().x(), |
+ toRenderText(o)->firstTextBox()->root()->topOverflow()); |
+ } else if (o->isBox()) { |
+ RenderBox* box = toRenderBox(o); |
+ point.move(box->x(), box->y()); |
+ } |
+ return true; |
+ } |
+ } |
+ |
+ // If the target doesn't have any children or siblings that could be used to calculate the scroll position, we must be |
+ // at the end of the document. Scroll to the bottom. FIXME: who said anything about scrolling? |
+ if (!o && document()->view()) { |
+ point = FloatPoint(0, document()->view()->contentsHeight()); |
+ return true; |
+ } |
+ return false; |
+} |
+ |
+// FIXME: This doesn't work correctly with transforms. |
+bool ContainerNode::getLowerRightCorner(FloatPoint& point) const |
+{ |
+ if (!renderer()) |
+ return false; |
+ |
+ RenderObject *o = renderer(); |
+ if (!o->isInline() || o->isReplaced()) |
+ { |
+ RenderBox* box = toRenderBox(o); |
+ point = o->localToAbsolute(); |
+ point.move(box->width(), box->height()); |
+ return true; |
+ } |
+ |
+ // find the last text/image child, to get a position |
+ while (o) { |
+ if (o->lastChild()) |
+ o = o->lastChild(); |
+ else if (o->previousSibling()) |
+ o = o->previousSibling(); |
+ else { |
+ RenderObject *prev = 0; |
+ while(!prev) { |
+ o = o->parent(); |
+ if (!o) |
+ return false; |
+ prev = o->previousSibling(); |
+ } |
+ o = prev; |
+ } |
+ if (o->isText() || o->isReplaced()) { |
+ point = o->container()->localToAbsolute(); |
+ if (o->isText()) { |
+ RenderText* text = toRenderText(o); |
+ IntRect linesBox = text->linesBoundingBox(); |
+ point.move(linesBox.x() + linesBox.width(), linesBox.height()); |
+ } else { |
+ RenderBox* box = toRenderBox(o); |
+ point.move(box->x() + box->width(), box->y() + box->height()); |
+ } |
+ return true; |
+ } |
+ } |
+ return true; |
+} |
+ |
+IntRect ContainerNode::getRect() const |
+{ |
+ FloatPoint upperLeft, lowerRight; |
+ bool foundUpperLeft = getUpperLeftCorner(upperLeft); |
+ bool foundLowerRight = getLowerRightCorner(lowerRight); |
+ |
+ // If we've found one corner, but not the other, |
+ // then we should just return a point at the corner that we did find. |
+ if (foundUpperLeft != foundLowerRight) |
+ { |
+ if (foundUpperLeft) |
+ lowerRight = upperLeft; |
+ else |
+ upperLeft = lowerRight; |
+ } |
+ |
+ lowerRight.setX(max(upperLeft.x(), lowerRight.x())); |
+ lowerRight.setY(max(upperLeft.y(), lowerRight.y())); |
+ |
+ return enclosingIntRect(FloatRect(upperLeft, lowerRight - upperLeft)); |
+} |
+ |
+void ContainerNode::setFocus(bool received) |
+{ |
+ if (focused() == received) |
+ return; |
+ |
+ EventTargetNode::setFocus(received); |
+ |
+ // note that we need to recalc the style |
+ setChanged(); |
+} |
+ |
+void ContainerNode::setActive(bool down, bool pause) |
+{ |
+ if (down == active()) return; |
+ |
+ EventTargetNode::setActive(down); |
+ |
+ // note that we need to recalc the style |
+ // FIXME: Move to Element |
+ if (renderer()) { |
+ bool reactsToPress = renderer()->style()->affectedByActiveRules(); |
+ if (reactsToPress) |
+ setChanged(); |
+ if (renderer() && renderer()->style()->hasAppearance()) { |
+ if (theme()->stateChanged(renderer(), PressedState)) |
+ reactsToPress = true; |
+ } |
+ if (reactsToPress && pause) { |
+ // The delay here is subtle. It relies on an assumption, namely that the amount of time it takes |
+ // to repaint the "down" state of the control is about the same time as it would take to repaint the |
+ // "up" state. Once you assume this, you can just delay for 100ms - that time (assuming that after you |
+ // leave this method, it will be about that long before the flush of the up state happens again). |
+#ifdef HAVE_FUNC_USLEEP |
+ double startTime = currentTime(); |
+#endif |
+ |
+ // Ensure there are no pending changes |
+ Document::updateDocumentsRendering(); |
+ // Do an immediate repaint. |
+ if (renderer()) |
+ renderer()->repaint(true); |
+ |
+ // FIXME: Find a substitute for usleep for Win32. |
+ // Better yet, come up with a way of doing this that doesn't use this sort of thing at all. |
+#ifdef HAVE_FUNC_USLEEP |
+ // Now pause for a small amount of time (1/10th of a second from before we repainted in the pressed state) |
+ double remainingTime = 0.1 - (currentTime() - startTime); |
+ if (remainingTime > 0) |
+ usleep(static_cast<useconds_t>(remainingTime * 1000000.0)); |
+#endif |
+ } |
+ } |
+} |
+ |
+void ContainerNode::setHovered(bool over) |
+{ |
+ if (over == hovered()) return; |
+ |
+ EventTargetNode::setHovered(over); |
+ |
+ // note that we need to recalc the style |
+ // FIXME: Move to Element |
+ if (renderer()) { |
+ if (renderer()->style()->affectedByHoverRules()) |
+ setChanged(); |
+ if (renderer() && renderer()->style()->hasAppearance()) |
+ theme()->stateChanged(renderer(), HoverState); |
+ } |
+} |
+ |
+unsigned ContainerNode::childNodeCount() const |
+{ |
+ unsigned count = 0; |
+ Node *n; |
+ for (n = firstChild(); n; n = n->nextSibling()) |
+ count++; |
+ return count; |
+} |
+ |
+Node *ContainerNode::childNode(unsigned index) const |
+{ |
+ unsigned i; |
+ Node *n = firstChild(); |
+ for (i = 0; n != 0 && i < index; i++) |
+ n = n->nextSibling(); |
+ return n; |
+} |
+ |
+static void dispatchChildInsertionEvents(Node* child, ExceptionCode& ec) |
+{ |
+ ASSERT(!eventDispatchForbidden()); |
+ |
+ RefPtr<Node> c = child; |
+ DocPtr<Document> doc = child->document(); |
+ |
+ if (c->parentNode() && c->parentNode()->inDocument()) |
+ c->insertedIntoDocument(); |
+ else |
+ c->insertedIntoTree(true); |
+ |
+ if (c->parentNode() && |
+ doc->hasListenerType(Document::DOMNODEINSERTED_LISTENER) && |
+ c->isEventTargetNode()) { |
+ ec = 0; |
+ EventTargetNodeCast(c.get())->dispatchEvent(MutationEvent::create(eventNames().DOMNodeInsertedEvent, true, false, |
+ c->parentNode(), String(), String(), String(), 0), ec); |
+ if (ec) |
+ return; |
+ } |
+ |
+ // dispatch the DOMNodeInsertedIntoDocument event to all descendants |
+ if (c->inDocument() && doc->hasListenerType(Document::DOMNODEINSERTEDINTODOCUMENT_LISTENER)) |
+ for (; c; c = c->traverseNextNode(child)) { |
+ if (!c->isEventTargetNode()) |
+ continue; |
+ |
+ ec = 0; |
+ EventTargetNodeCast(c.get())->dispatchEvent(MutationEvent::create(eventNames().DOMNodeInsertedIntoDocumentEvent, false, false, |
+ 0, String(), String(), String(), 0), ec); |
+ if (ec) |
+ return; |
+ } |
+} |
+ |
+static void dispatchChildRemovalEvents(Node* child, ExceptionCode& ec) |
+{ |
+ RefPtr<Node> c = child; |
+ DocPtr<Document> doc = child->document(); |
+ |
+ // update auxiliary doc info (e.g. iterators) to note that node is being removed |
+ doc->nodeWillBeRemoved(child); |
+ |
+ // dispatch pre-removal mutation events |
+ if (c->parentNode() && |
+ doc->hasListenerType(Document::DOMNODEREMOVED_LISTENER) && |
+ c->isEventTargetNode()) { |
+ ec = 0; |
+ EventTargetNodeCast(c.get())->dispatchEvent(MutationEvent::create(eventNames().DOMNodeRemovedEvent, true, false, |
+ c->parentNode(), String(), String(), String(), 0), ec); |
+ if (ec) |
+ return; |
+ } |
+ |
+ // dispatch the DOMNodeRemovedFromDocument event to all descendants |
+ if (c->inDocument() && doc->hasListenerType(Document::DOMNODEREMOVEDFROMDOCUMENT_LISTENER)) |
+ for (; c; c = c->traverseNextNode(child)) { |
+ if (!c->isEventTargetNode()) |
+ continue; |
+ ec = 0; |
+ EventTargetNodeCast(c.get())->dispatchEvent(MutationEvent::create(eventNames().DOMNodeRemovedFromDocumentEvent, false, false, |
+ 0, String(), String(), String(), 0), ec); |
+ if (ec) |
+ return; |
+ } |
+} |
+ |
+} |