2009-02-10 Adam Treat <adam.treat@torchmobile.com>
[WebKit-https.git] / WebCore / dom / ContainerNode.cpp
index 7082fe6..2950fb3 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 Apple Inc. All rights reserved.
+ * 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
 #include "config.h"
 #include "ContainerNode.h"
 
+#include "ContainerNodeAlgorithms.h"
 #include "DeleteButtonController.h"
-#include "Document.h"
-#include "Editor.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 "SystemTime.h"
-#include <wtf/Vector.h>
+#include <wtf/CurrentTime.h>
 
 namespace WebCore {
 
-using namespace EventNames;
-
 static void dispatchChildInsertionEvents(Node*, ExceptionCode&);
 static void dispatchChildRemovalEvents(Node*, ExceptionCode&);
 
@@ -49,67 +46,9 @@ static NodeCallbackQueue* s_postAttachCallbackQueue = 0;
 
 static size_t s_attachDepth = 0;
 
-ContainerNode::ContainerNode(Document* doc)
-    : EventTargetNode(doc), m_firstChild(0), m_lastChild(0)
-{
-}
-
-void ContainerNode::addChildNodesToDeletionQueue(Node*& head, Node*& tail, ContainerNode* container)
-{
-    // We have to tell all children that their parent has died.
-    Node* n;
-    Node* next;
-    for (n = container->firstChild(); n != 0; n = next) {
-        ASSERT(!n->m_deletionHasBegun);
-        
-        next = n->nextSibling();
-        n->setPreviousSibling(0);
-        n->setNextSibling(0);
-        n->setParent(0);
-        
-        if (!n->refCount()) {
-#ifndef NDEBUG
-            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 if (n->inDocument())
-            n->removedFromDocument();
-    }
-    container->setFirstChild(0);
-    container->setLastChild(0);
-}
-
 void ContainerNode::removeAllChildren()
 {
-    // List of nodes to be deleted.
-    Node* head = 0;
-    Node* tail = 0;
-
-    addChildNodesToDeletionQueue(head, tail, this);
-
-    Node* n;
-    Node* next;
-    while ((n = head) != 0) {
-        ASSERT(n->m_deletionHasBegun);
-
-        next = n->nextSibling();
-        n->setNextSibling(0);
-
-        head = next;
-        if (next == 0)
-            tail = 0;
-        
-        if (n->hasChildNodes())
-            addChildNodesToDeletionQueue(head, tail, static_cast<ContainerNode*>(n));
-        
-        delete n;
-    }
+    removeAllChildrenInContainer<Node, ContainerNode>(this);
 }
 
 ContainerNode::~ContainerNode()
@@ -117,18 +56,7 @@ ContainerNode::~ContainerNode()
     removeAllChildren();
 }
 
-
-Node* ContainerNode::virtualFirstChild() const
-{
-    return m_firstChild;
-}
-
-Node* ContainerNode::virtualLastChild() const
-{
-    return m_lastChild;
-}
-
-bool ContainerNode::insertBefore(PassRefPtr<Node> newChild, Node* refChild, ExceptionCode& ec)
+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.
@@ -138,7 +66,7 @@ bool ContainerNode::insertBefore(PassRefPtr<Node> newChild, Node* refChild, Exce
 
     // insertBefore(node, 0) is equivalent to appendChild(node)
     if (!refChild)
-        return appendChild(newChild, ec);
+        return appendChild(newChild, ec, shouldLazyAttach);
 
     // Make sure adding the new child is OK.
     checkAddChild(newChild.get(), ec);
@@ -217,8 +145,12 @@ bool ContainerNode::insertBefore(PassRefPtr<Node> newChild, Node* refChild, Exce
         dispatchChildInsertionEvents(child.get(), ec);
                 
         // Add child to the rendering tree.
-        if (attached() && !child->attached() && child->parent() == this)
-            child->attach();
+        if (attached() && !child->attached() && child->parent() == this) {
+            if (shouldLazyAttach)
+                child->lazyAttach();
+            else
+                child->attach();
+        }
 
         child = nextChild.release();
     }
@@ -230,7 +162,7 @@ bool ContainerNode::insertBefore(PassRefPtr<Node> newChild, Node* refChild, Exce
     return true;
 }
 
-bool ContainerNode::replaceChild(PassRefPtr<Node> newChild, Node* oldChild, ExceptionCode& ec)
+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.
@@ -283,7 +215,7 @@ bool ContainerNode::replaceChild(PassRefPtr<Node> newChild, Node* oldChild, Exce
         if (Node* oldParent = child->parentNode())
             oldParent->removeChild(child.get(), ec);
         if (ec)
-            return 0;
+            return false;
 
         // Due to arbitrary code running in response to a DOM mutation event it's
         // possible that "prev" is no longer a child of "this".
@@ -327,8 +259,12 @@ bool ContainerNode::replaceChild(PassRefPtr<Node> newChild, Node* oldChild, Exce
         dispatchChildInsertionEvents(child.get(), ec);
                 
         // Add child to the rendering tree
-        if (attached() && !child->attached() && child->parent() == this)
-            child->attach();
+        if (attached() && !child->attached() && child->parent() == this) {
+            if (shouldLazyAttach)
+                child->lazyAttach();
+            else
+                child->attach();
+        }
 
         prev = child;
         child = nextChild.release();
@@ -345,7 +281,7 @@ void ContainerNode::willRemove()
 {
     for (Node *n = m_firstChild; n != 0; n = n->nextSibling())
         n->willRemove();
-    EventTargetNode::willRemove();
+    Node::willRemove();
 }
 
 static ExceptionCode willRemoveChild(Node *child)
@@ -384,14 +320,6 @@ bool ContainerNode::removeChild(Node* oldChild, ExceptionCode& ec)
     }
 
     RefPtr<Node> child = oldChild;
-    
-    // dispatch pre-removal mutation events
-    if (document()->hasListenerType(Document::DOMNODEREMOVED_LISTENER)) {
-        EventTargetNodeCast(child.get())->dispatchEvent(new MutationEvent(DOMNodeRemovedEvent, true, false,
-            this, String(), String(), String(), 0), ec, true);
-        if (ec)
-            return false;
-    }
 
     ec = willRemoveChild(child.get());
     if (ec)
@@ -502,7 +430,7 @@ bool ContainerNode::removeChildren()
     return true;
 }
 
-bool ContainerNode::appendChild(PassRefPtr<Node> newChild, ExceptionCode& ec)
+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.
@@ -562,8 +490,12 @@ bool ContainerNode::appendChild(PassRefPtr<Node> newChild, ExceptionCode& ec)
         dispatchChildInsertionEvents(child.get(), ec);
 
         // Add child to the rendering tree
-        if (attached() && !child->attached() && child->parent() == this)
-            child->attach();
+        if (attached() && !child->attached() && child->parent() == this) {
+            if (shouldLazyAttach)
+                child->lazyAttach();
+            else
+                child->attach();
+        }
         
         child = nextChild.release();
     }
@@ -584,14 +516,8 @@ ContainerNode* ContainerNode::addChild(PassRefPtr<Node> newChild)
         return 0;
 
     forbidEventDispatch();
-    newChild->setParent(this);
     Node* last = m_lastChild;
-    if (m_lastChild) {
-        newChild->setPreviousSibling(m_lastChild);
-        m_lastChild->setNextSibling(newChild.get());
-    } else
-        m_firstChild = newChild.get();
-    m_lastChild = newChild.get();
+    appendChildToContainer<Node, ContainerNode>(newChild.get(), this);
     allowEventDispatch();
 
     document()->incDOMTreeVersion();
@@ -644,7 +570,7 @@ void ContainerNode::attach()
 
     for (Node* child = m_firstChild; child; child = child->nextSibling())
         child->attach();
-    EventTargetNode::attach();
+    Node::attach();
 
     if (s_attachDepth == 1 && s_postAttachCallbackQueue)
         dispatchPostAttachCallbacks();
@@ -655,39 +581,41 @@ void ContainerNode::detach()
 {
     for (Node* child = m_firstChild; child; child = child->nextSibling())
         child->detach();
-    EventTargetNode::detach();
+    setHasChangedChild(false);
+    Node::detach();
 }
 
 void ContainerNode::insertedIntoDocument()
 {
-    EventTargetNode::insertedIntoDocument();
-    for (Node *child = m_firstChild; child; child = child->nextSibling())
+    Node::insertedIntoDocument();
+    insertedIntoTree(false);
+    for (Node* child = m_firstChild; child; child = child->nextSibling())
         child->insertedIntoDocument();
 }
 
 void ContainerNode::removedFromDocument()
 {
-    EventTargetNode::removedFromDocument();
-    for (Node *child = m_firstChild; child; child = child->nextSibling())
+    Node::removedFromDocument();
+    setInDocument(false);
+    removedFromTree(false);
+    for (Node* child = m_firstChild; child; child = child->nextSibling())
         child->removedFromDocument();
 }
 
 void ContainerNode::insertedIntoTree(bool deep)
 {
-    EventTargetNode::insertedIntoTree(deep);
-    if (deep) {
-        for (Node *child = m_firstChild; child; child = child->nextSibling())
-            child->insertedIntoTree(deep);
-    }
+    if (!deep)
+        return;
+    for (Node* child = m_firstChild; child; child = child->nextSibling())
+        child->insertedIntoTree(true);
 }
 
 void ContainerNode::removedFromTree(bool deep)
 {
-    EventTargetNode::removedFromTree(deep);
-    if (deep) {
-        for (Node *child = m_firstChild; child; child = child->nextSibling())
-            child->removedFromTree(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)
@@ -711,25 +639,26 @@ void ContainerNode::cloneChildNodes(ContainerNode *clone)
         document()->frame()->editor()->deleteButtonController()->enable();
 }
 
-bool ContainerNode::getUpperLeftCorner(int &xPos, int &yPos) const
+// 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;
 
-    xPos = yPos = 0;
     if (!o->isInline() || o->isReplaced()) {
-        o->absolutePosition(xPos, yPos);
+        point = o->localToAbsolute();
         return true;
     }
 
     // find the next text/image child, to get a position
-    while(o) {
+    while (o) {
         p = o;
         if (o->firstChild())
             o = o->firstChild();
-        else if(o->nextSibling())
+        else if (o->nextSibling())
             o = o->nextSibling();
         else {
             RenderObject *next = 0;
@@ -744,70 +673,75 @@ bool ContainerNode::getUpperLeftCorner(int &xPos, int &yPos) const
         }
 
         if (!o->isInline() || o->isReplaced()) {
-            o->absolutePosition(xPos, yPos);
+            point = o->localToAbsolute();
             return true;
         }
 
-        if (p->element() && p->element() == this && o->isText() && !o->isBR() && !static_cast<RenderText*>(o)->firstTextBox()) {
+        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()) {
-            o->container()->absolutePosition(xPos, yPos);
-            if (o->isText() && static_cast<RenderText *>(o)->firstTextBox()) {
-                xPos += static_cast<RenderText *>(o)->minXPos();
-                yPos += static_cast<RenderText *>(o)->firstTextBox()->root()->topOverflow();
-            } else {
-                xPos += o->xPos();
-                yPos += o->yPos();
+            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.
+    // at the end of the document.  Scroll to the bottom. FIXME: who said anything about scrolling?
     if (!o && document()->view()) {
-        yPos += document()->view()->contentsHeight();
+        point = FloatPoint(0, document()->view()->contentsHeight());
         return true;
     }
     return false;
 }
 
-bool ContainerNode::getLowerRightCorner(int &xPos, int &yPos) const
+// FIXME: This doesn't work correctly with transforms.
+bool ContainerNode::getLowerRightCorner(FloatPoint& point) const
 {
     if (!renderer())
         return false;
 
     RenderObject *o = renderer();
-    xPos = yPos = 0;
     if (!o->isInline() || o->isReplaced())
     {
-        o->absolutePosition(xPos, yPos);
-        xPos += o->width();
-        yPos += o->height() + o->borderTopExtra() + o->borderBottomExtra();
+        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())
+    while (o) {
+        if (o->lastChild())
             o = o->lastChild();
-        else if(o->previousSibling())
+        else if (o->previousSibling())
             o = o->previousSibling();
         else {
             RenderObject *prev = 0;
             while(!prev) {
                 o = o->parent();
-                if(!o) return false;
+                if (!o)
+                    return false;
                 prev = o->previousSibling();
             }
             o = prev;
         }
         if (o->isText() || o->isReplaced()) {
-            o->container()->absolutePosition(xPos, yPos);
-            if (o->isText())
-                xPos += static_cast<RenderText *>(o)->minXPos() + o->width();
-            else
-                xPos += o->xPos()+o->width();
-            yPos += o->yPos()+o->height();
+            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;
         }
     }
@@ -816,36 +750,32 @@ bool ContainerNode::getLowerRightCorner(int &xPos, int &yPos) const
 
 IntRect ContainerNode::getRect() const
 {
-    int xPos = 0, yPos = 0, xEnd = 0, yEnd = 0;
-    bool foundUpperLeft = getUpperLeftCorner(xPos,yPos);
-    bool foundLowerRight = getLowerRightCorner(xEnd,yEnd);
+    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) {
-            xEnd = xPos;
-            yEnd = yPos;
-        } else {
-            xPos = xEnd;
-            yPos = yEnd;
-        }
+        if (foundUpperLeft)
+            lowerRight = upperLeft;
+        else
+            upperLeft = lowerRight;
     } 
 
-    if (xEnd < xPos)
-        xEnd = xPos;
-    if (yEnd < yPos)
-        yEnd = yPos;
-        
-    return IntRect(xPos, yPos, xEnd - xPos, yEnd - yPos);
+    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 (m_focused == received) return;
+    if (focused() == received)
+        return;
 
-    EventTargetNode::setFocus(received);
+    Node::setFocus(received);
 
     // note that we need to recalc the style
     setChanged();
@@ -855,7 +785,7 @@ void ContainerNode::setActive(bool down, bool pause)
 {
     if (down == active()) return;
 
-    EventTargetNode::setActive(down);
+    Node::setActive(down);
 
     // note that we need to recalc the style
     // FIXME: Move to Element
@@ -898,7 +828,7 @@ void ContainerNode::setHovered(bool over)
 {
     if (over == hovered()) return;
 
-    EventTargetNode::setHovered(over);
+    Node::setHovered(over);
 
     // note that we need to recalc the style
     // FIXME: Move to Element
@@ -940,12 +870,10 @@ static void dispatchChildInsertionEvents(Node* child, ExceptionCode& ec)
     else
         c->insertedIntoTree(true);
 
-    if (c->parentNode() && 
-        doc->hasListenerType(Document::DOMNODEINSERTED_LISTENER) &&
-        c->isEventTargetNode()) {
+    if (c->parentNode() && doc->hasListenerType(Document::DOMNODEINSERTED_LISTENER)) {
         ec = 0;
-        EventTargetNodeCast(c.get())->dispatchEvent(new MutationEvent(DOMNodeInsertedEvent, true, false,
-            c->parentNode(), String(), String(), String(), 0), ec, true);
+        c->dispatchEvent(MutationEvent::create(eventNames().DOMNodeInsertedEvent, true, false,
+            c->parentNode(), String(), String(), String(), 0), ec);
         if (ec)
             return;
     }
@@ -953,12 +881,9 @@ static void dispatchChildInsertionEvents(Node* child, ExceptionCode& ec)
     // 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(new MutationEvent(DOMNodeInsertedIntoDocumentEvent, false, false,
-                0, String(), String(), String(), 0), ec, true);
+            c->dispatchEvent(MutationEvent::create(eventNames().DOMNodeInsertedIntoDocumentEvent, false, false,
+                0, String(), String(), String(), 0), ec);
             if (ec)
                 return;
         }
@@ -973,12 +898,10 @@ static void dispatchChildRemovalEvents(Node* child, ExceptionCode& ec)
     doc->nodeWillBeRemoved(child);
 
     // dispatch pre-removal mutation events
-    if (c->parentNode() && 
-        doc->hasListenerType(Document::DOMNODEREMOVED_LISTENER) &&
-        c->isEventTargetNode()) {
+    if (c->parentNode() && doc->hasListenerType(Document::DOMNODEREMOVED_LISTENER)) {
         ec = 0;
-        EventTargetNodeCast(c.get())->dispatchEvent(new MutationEvent(DOMNodeRemovedEvent, true, false,
-            c->parentNode(), String(), String(), String(), 0), ec, true);
+        c->dispatchEvent(MutationEvent::create(eventNames().DOMNodeRemovedEvent, true, false,
+            c->parentNode(), String(), String(), String(), 0), ec);
         if (ec)
             return;
     }
@@ -986,11 +909,9 @@ static void dispatchChildRemovalEvents(Node* child, ExceptionCode& ec)
     // 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(new MutationEvent(DOMNodeRemovedFromDocumentEvent, false, false,
-                0, String(), String(), String(), 0), ec, true);
+            c->dispatchEvent(MutationEvent::create(eventNames().DOMNodeRemovedFromDocumentEvent, false, false,
+                0, String(), String(), String(), 0), ec);
             if (ec)
                 return;
         }