#include "config.h"
#include "ContainerNode.h"
#include "Document.h"
#include "EventNames.h"
#include "ExceptionCode.h"
#include "FrameView.h"
#include "InlineTextBox.h"
#include "MutationEvent.h"
#include "RenderTheme.h"
#include "RootInlineBox.h"
#include "SystemTime.h"
namespace WebCore {
using namespace EventNames;
static void dispatchChildInsertionEvents(Node*, ExceptionCode&);
static void dispatchChildRemovalEvents(Node*, ExceptionCode&);
ContainerNode::ContainerNode(Document* doc)
: EventTargetNode(doc), m_firstChild(0), m_lastChild(0)
{
}
void ContainerNode::removeAllChildren()
{
static bool alreadyInsideDestructor;
bool topLevel = !alreadyInsideDestructor;
if (topLevel)
alreadyInsideDestructor = true;
static Node *head;
static Node *tail;
Node *n;
Node *next;
for (n = m_firstChild; n != 0; n = next ) {
next = n->nextSibling();
n->setPreviousSibling(0);
n->setNextSibling(0);
n->setParent(0);
if ( !n->refCount() ) {
if (tail)
tail->setNextSibling(n);
else
head = n;
tail = n;
} else if (n->inDocument())
n->removedFromDocument();
}
if (topLevel) {
while ((n = head) != 0) {
next = n->nextSibling();
n->setNextSibling(0);
head = next;
if (next == 0)
tail = 0;
delete n;
}
alreadyInsideDestructor = false;
m_firstChild = 0;
m_lastChild = 0;
}
}
ContainerNode::~ContainerNode()
{
removeAllChildren();
}
Node* ContainerNode::firstChild() const
{
return m_firstChild;
}
Node* ContainerNode::lastChild() const
{
return m_lastChild;
}
bool ContainerNode::insertBefore(PassRefPtr<Node> newChild, Node* refChild, ExceptionCode& ec)
{
ASSERT(refCount() || parent());
ec = 0;
if (!refChild)
return appendChild(newChild, ec);
checkAddChild(newChild.get(), ec);
if (ec)
return false;
if (refChild->parentNode() != this) {
ec = NOT_FOUND_ERR;
return false;
}
bool isFragment = newChild->nodeType() == DOCUMENT_FRAGMENT_NODE;
if (isFragment && !newChild->firstChild())
return true;
if (refChild->previousSibling() == newChild || refChild == newChild) return true;
RefPtr<Node> next = refChild;
RefPtr<Node> child = isFragment ? newChild->firstChild() : newChild;
while (child) {
RefPtr<Node> nextChild = isFragment ? child->nextSibling() : 0;
if (Node* oldParent = child->parentNode())
oldParent->removeChild(child.get(), ec);
if (ec)
return 0;
if (next->parentNode() != this)
break;
if (child->parentNode())
break;
assert(!child->nextSibling());
assert(!child->previousSibling());
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();
dispatchChildInsertionEvents(child.get(), ec);
if (attached() && !child->attached())
child->attach();
child = nextChild.release();
}
document()->setDocumentChanged(true);
dispatchSubtreeModifiedEvent();
return true;
}
bool ContainerNode::replaceChild(PassRefPtr<Node> newChild, Node* oldChild, ExceptionCode& ec)
{
ASSERT(refCount() || parent());
ec = 0;
if (oldChild == newChild) return true;
checkAddChild(newChild.get(), ec);
if (ec)
return false;
if (!oldChild || oldChild->parentNode() != this) {
ec = NOT_FOUND_ERR;
return false;
}
RefPtr<Node> prev = oldChild->previousSibling();
RefPtr<Node> removedChild = oldChild;
removeChild(oldChild, ec);
if (ec)
return false;
bool isFragment = newChild->nodeType() == DOCUMENT_FRAGMENT_NODE;
RefPtr<Node> child = isFragment ? newChild->firstChild() : newChild;
while (child) {
if (prev && (prev == child || prev == child->previousSibling()))
break;
RefPtr<Node> nextChild = isFragment ? child->nextSibling() : 0;
if (Node* oldParent = child->parentNode())
oldParent->removeChild(child.get(), ec);
if (ec)
return 0;
if (prev && prev->parentNode() != this)
break;
if (child->parentNode())
break;
assert(!child->nextSibling());
assert(!child->previousSibling());
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();
dispatchChildInsertionEvents(child.get(), ec);
if (attached() && !child->attached())
child->attach();
prev = child;
child = nextChild.release();
}
document()->setDocumentChanged(true);
dispatchSubtreeModifiedEvent();
return true;
}
void ContainerNode::willRemove()
{
for (Node *n = m_firstChild; n != 0; n = n->nextSibling())
n->willRemove();
}
static ExceptionCode willRemoveChild(Node *child)
{
ExceptionCode ec = 0;
dispatchChildRemovalEvents(child, ec);
if (ec)
return ec;
if (child->attached())
child->willRemove();
return 0;
}
bool ContainerNode::removeChild(Node* oldChild, ExceptionCode& ec)
{
ASSERT(refCount() || parent());
ec = 0;
if (isReadOnlyNode()) {
ec = NO_MODIFICATION_ALLOWED_ERR;
return false;
}
if (!oldChild || oldChild->parentNode() != this) {
ec = NOT_FOUND_ERR;
return false;
}
RefPtr<Node> child = oldChild;
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)
return false;
if (child->parentNode() != this) {
ec = NOT_FOUND_ERR;
return false;
}
forbidEventDispatch();
if (child->attached())
child->detach();
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);
dispatchSubtreeModifiedEvent();
if (child->inDocument())
child->removedFromDocument();
else
child->removedFromTree(true);
return child;
}
void ContainerNode::removeChildren()
{
Node *n;
if (!m_firstChild)
return;
for (n = m_firstChild; n; n = n->nextSibling())
willRemoveChild(n);
forbidEventDispatch();
while ((n = m_firstChild) != 0) {
Node *next = n->nextSibling();
n->ref();
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();
dispatchSubtreeModifiedEvent();
}
bool ContainerNode::appendChild(PassRefPtr<Node> newChild, ExceptionCode& ec)
{
ASSERT(refCount() || parent());
ec = 0;
checkAddChild(newChild.get(), ec);
if (ec)
return 0;
if (newChild == m_lastChild) return newChild;
bool isFragment = newChild->nodeType() == DOCUMENT_FRAGMENT_NODE;
if (isFragment && !newChild->firstChild())
return true;
RefPtr<Node> child = isFragment ? newChild->firstChild() : newChild;
while (child) {
RefPtr<Node> nextChild = isFragment ? child->nextSibling() : 0;
if (Node* oldParent = child->parentNode()) {
oldParent->removeChild(child.get(), ec);
if (ec)
return 0;
if (child->parentNode())
break;
}
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();
dispatchChildInsertionEvents(child.get(), ec);
if (attached() && !child->attached())
child->attach();
child = nextChild.release();
}
document()->setDocumentChanged(true);
dispatchSubtreeModifiedEvent();
return true;
}
bool ContainerNode::hasChildNodes() const
{
return m_firstChild;
}
ContainerNode* ContainerNode::addChild(PassRefPtr<Node> newChild)
{
if (document()->isHTMLDocument() && !childAllowed(newChild.get()))
return 0;
forbidEventDispatch();
newChild->setParent(this);
if (m_lastChild) {
newChild->setPreviousSibling(m_lastChild);
m_lastChild->setNextSibling(newChild.get());
} else
m_firstChild = newChild.get();
m_lastChild = newChild.get();
allowEventDispatch();
if (inDocument())
newChild->insertedIntoDocument();
childrenChanged();
if (newChild->isElementNode())
return static_cast<ContainerNode*>(newChild.get());
return this;
}
void ContainerNode::attach()
{
for (Node* child = m_firstChild; child; child = child->nextSibling())
child->attach();
EventTargetNode::attach();
}
void ContainerNode::detach()
{
for (Node* child = m_firstChild; child; child = child->nextSibling())
child->detach();
EventTargetNode::detach();
}
void ContainerNode::insertedIntoDocument()
{
EventTargetNode::insertedIntoDocument();
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())
child->removedFromDocument();
}
void ContainerNode::insertedIntoTree(bool deep)
{
EventTargetNode::insertedIntoTree(deep);
if (deep) {
for (Node *child = m_firstChild; child; child = child->nextSibling())
child->insertedIntoTree(deep);
}
}
void ContainerNode::removedFromTree(bool deep)
{
EventTargetNode::removedFromTree(deep);
if (deep) {
for (Node *child = m_firstChild; child; child = child->nextSibling())
child->removedFromTree(deep);
}
}
void ContainerNode::cloneChildNodes(Node *clone)
{
ExceptionCode ec = 0;
for (Node* n = firstChild(); n && !ec; n = n->nextSibling())
clone->appendChild(n->cloneNode(true), ec);
}
bool ContainerNode::getUpperLeftCorner(int &xPos, int &yPos) const
{
if (!renderer())
return false;
RenderObject *o = renderer();
RenderObject *p = o;
xPos = yPos = 0;
if (!o->isInline() || o->isReplaced()) {
o->absolutePosition(xPos, yPos);
return true;
}
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()) {
o->absolutePosition(xPos, yPos);
return true;
}
if (p->element() && p->element() == this && o->isText() && !o->isBR() && !static_cast<RenderText*>(o)->firstTextBox()) {
} 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();
}
return true;
}
}
if (!o) {
yPos += document()->view()->contentsHeight();
return true;
}
return false;
}
bool ContainerNode::getLowerRightCorner(int &xPos, int &yPos) 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();
return true;
}
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()) {
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();
return true;
}
}
return true;
}
IntRect ContainerNode::getRect() const
{
int xPos = 0, yPos = 0, xEnd = 0, yEnd = 0;
bool foundUpperLeft = getUpperLeftCorner(xPos,yPos);
bool foundLowerRight = getLowerRightCorner(xEnd,yEnd);
if (foundUpperLeft != foundLowerRight)
{
if (foundUpperLeft) {
xEnd = xPos;
yEnd = yPos;
} else {
xPos = xEnd;
yPos = yEnd;
}
}
if (xEnd < xPos)
xEnd = xPos;
if (yEnd < yPos)
yEnd = yPos;
return IntRect(xPos, yPos, xEnd - xPos, yEnd - yPos);
}
void ContainerNode::setFocus(bool received)
{
if (m_focused == received) return;
EventTargetNode::setFocus(received);
setChanged();
}
void ContainerNode::setActive(bool down, bool pause)
{
if (down == active()) return;
EventTargetNode::setActive(down);
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) {
#if HAVE_FUNC_USLEEP
double startTime = currentTime();
#endif
renderer()->repaint(true);
#if HAVE_FUNC_USLEEP
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);
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;
RefPtr<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(new MutationEvent(DOMNodeInsertedEvent, true, false,
c->parentNode(), String(), String(), String(), 0), ec, true);
if (ec)
return;
}
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);
if (ec)
return;
}
}
static void dispatchChildRemovalEvents(Node* child, ExceptionCode& ec)
{
RefPtr<Node> c = child;
RefPtr<Document> doc = child->document();
doc->notifyBeforeNodeRemoval(child);
if (c->parentNode() &&
doc->hasListenerType(Document::DOMNODEREMOVED_LISTENER) &&
c->isEventTargetNode()) {
ec = 0;
EventTargetNodeCast(c.get())->dispatchEvent(new MutationEvent(DOMNodeRemovedEvent, true, false,
c->parentNode(), String(), String(), String(), 0), ec, true);
if (ec)
return;
}
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);
if (ec)
return;
}
}
}