diff options
author | ojan@google.com <ojan@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-10-07 02:25:13 +0000 |
---|---|---|
committer | ojan@google.com <ojan@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-10-07 02:25:13 +0000 |
commit | 1e01bac547a8b7eead67d4d1b042fa8e7aff4c11 (patch) | |
tree | c6c4aa398ce060f46f4bfc936eeb8dfc43b2ea91 /webkit/pending | |
parent | 5423d771075f89f84ce2f5188396c3052078a338 (diff) | |
download | chromium_src-1e01bac547a8b7eead67d4d1b042fa8e7aff4c11.zip chromium_src-1e01bac547a8b7eead67d4d1b042fa8e7aff4c11.tar.gz chromium_src-1e01bac547a8b7eead67d4d1b042fa8e7aff4c11.tar.bz2 |
Roolback more in an attempt to get build compling.
Review URL: http://codereview.chromium.org/6297
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@2930 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'webkit/pending')
-rw-r--r-- | webkit/pending/HTMLPlugInElement.cpp | 211 | ||||
-rw-r--r-- | webkit/pending/HTMLPlugInElement.h | 84 | ||||
-rw-r--r-- | webkit/pending/Node.cpp | 2010 | ||||
-rw-r--r-- | webkit/pending/Node.h | 549 |
4 files changed, 2854 insertions, 0 deletions
diff --git a/webkit/pending/HTMLPlugInElement.cpp b/webkit/pending/HTMLPlugInElement.cpp new file mode 100644 index 0000000..19d494e --- /dev/null +++ b/webkit/pending/HTMLPlugInElement.cpp @@ -0,0 +1,211 @@ +/** + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Stefan Schimanski (1Stein@gmx.de) + * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. + * + * 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 "HTMLPlugInElement.h" + +#include "CSSPropertyNames.h" +#include "Document.h" +#include "Frame.h" +#include "FrameLoader.h" +#include "FrameTree.h" +#include "HTMLNames.h" +#include "Page.h" +#include "RenderWidget.h" +#include "Settings.h" +#include "Widget.h" +#include "ScriptController.h" + +#if USE(JSC) +#include "runtime.h" +#endif + +#if ENABLE(NETSCAPE_PLUGIN_API) && USE(JSC) +#include "JSNode.h" +#include "NP_jsobject.h" +#include "npruntime_impl.h" +#include "runtime_root.h" +#endif + +namespace WebCore { + +using namespace HTMLNames; + +HTMLPlugInElement::HTMLPlugInElement(const QualifiedName& tagName, Document* doc) + : HTMLFrameOwnerElement(tagName, doc) +#if ENABLE(NETSCAPE_PLUGIN_API) + , m_NPObject(0) +#endif +{ +} + +HTMLPlugInElement::~HTMLPlugInElement() +{ +#if USE(JSC) + ASSERT(!m_instance); // cleared in detach() +#endif + +#if ENABLE(NETSCAPE_PLUGIN_API) + if (m_NPObject) { + // NOTE: mbelshe - can the frame be inaccessible here? If so, + // do we leak objects? + if (document() && document()->frame()) + document()->frame()->script()->functions()->releaseObject(m_NPObject); + m_NPObject = 0; + } +#endif +} + +JSInstance HTMLPlugInElement::getInstance() const +{ + Frame* frame = document()->frame(); + if (!frame) + return JSInstanceHolder::EmptyInstance(); + + // If the host dynamically turns off JavaScript (or Java) we will still return + // the cached allocated Bindings::Instance. Not supporting this edge-case is OK. + if (!m_instance.IsEmpty()) + return m_instance.Get(); + + RenderWidget* renderWidget = renderWidgetForJSBindings(); + if (renderWidget && renderWidget->widget()) + m_instance = frame->script()->createScriptInstanceForWidget(renderWidget->widget()); + + return m_instance.Get(); +} + +void HTMLPlugInElement::detach() +{ + m_instance.Clear(); + HTMLFrameOwnerElement::detach(); +} + +String HTMLPlugInElement::align() const +{ + return getAttribute(alignAttr); +} + +void HTMLPlugInElement::setAlign(const String& value) +{ + setAttribute(alignAttr, value); +} + +String HTMLPlugInElement::height() const +{ + return getAttribute(heightAttr); +} + +void HTMLPlugInElement::setHeight(const String& value) +{ + setAttribute(heightAttr, value); +} + +String HTMLPlugInElement::name() const +{ + return getAttribute(nameAttr); +} + +void HTMLPlugInElement::setName(const String& value) +{ + setAttribute(nameAttr, value); +} + +String HTMLPlugInElement::width() const +{ + return getAttribute(widthAttr); +} + +void HTMLPlugInElement::setWidth(const String& value) +{ + setAttribute(widthAttr, value); +} + +bool HTMLPlugInElement::mapToEntry(const QualifiedName& attrName, MappedAttributeEntry& result) const +{ + if (attrName == widthAttr || + attrName == heightAttr || + attrName == vspaceAttr || + attrName == hspaceAttr) { + result = eUniversal; + return false; + } + + if (attrName == alignAttr) { + result = eReplaced; // Share with <img> since the alignment behavior is the same. + return false; + } + + return HTMLFrameOwnerElement::mapToEntry(attrName, result); +} + +void HTMLPlugInElement::parseMappedAttribute(MappedAttribute* attr) +{ + if (attr->name() == widthAttr) + addCSSLength(attr, CSSPropertyWidth, attr->value()); + else if (attr->name() == heightAttr) + addCSSLength(attr, CSSPropertyHeight, attr->value()); + else if (attr->name() == vspaceAttr) { + addCSSLength(attr, CSSPropertyMarginTop, attr->value()); + addCSSLength(attr, CSSPropertyMarginBottom, attr->value()); + } else if (attr->name() == hspaceAttr) { + addCSSLength(attr, CSSPropertyMarginLeft, attr->value()); + addCSSLength(attr, CSSPropertyMarginRight, attr->value()); + } else if (attr->name() == alignAttr) + addHTMLAlignment(attr); + else + HTMLFrameOwnerElement::parseMappedAttribute(attr); +} + +bool HTMLPlugInElement::checkDTD(const Node* newChild) +{ + return newChild->hasTagName(paramTag) || HTMLFrameOwnerElement::checkDTD(newChild); +} + +void HTMLPlugInElement::defaultEventHandler(Event* event) +{ + RenderObject* r = renderer(); + if (!r || !r->isWidget()) + return; + + if (Widget* widget = static_cast<RenderWidget*>(r)->widget()) + widget->handleEvent(event); +} + +#if ENABLE(NETSCAPE_PLUGIN_API) + +NPObject* HTMLPlugInElement::getNPObject() +{ + ASSERT(document()->frame()); + if (!m_NPObject) + m_NPObject = document()->frame()->script()->createScriptObjectForPluginElement(this); + return m_NPObject; +} + +#endif /* ENABLE(NETSCAPE_PLUGIN_API) */ + +void HTMLPlugInElement::updateWidgetCallback(Node* n) +{ + static_cast<HTMLPlugInElement*>(n)->updateWidget(); +} + +} diff --git a/webkit/pending/HTMLPlugInElement.h b/webkit/pending/HTMLPlugInElement.h new file mode 100644 index 0000000..599c192 --- /dev/null +++ b/webkit/pending/HTMLPlugInElement.h @@ -0,0 +1,84 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2004, 2006, 2007, 2008 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. + * + */ + +#ifndef HTMLPlugInElement_h +#define HTMLPlugInElement_h + +#include "HTMLFrameOwnerElement.h" +#include "ScriptController.h" + +#if ENABLE(NETSCAPE_PLUGIN_API) +struct NPObject; +#endif + +namespace WebCore { + +class RenderWidget; + +class HTMLPlugInElement : public HTMLFrameOwnerElement { +public: + HTMLPlugInElement(const QualifiedName& tagName, Document*); + virtual ~HTMLPlugInElement(); + + virtual bool mapToEntry(const QualifiedName& attrName, MappedAttributeEntry& result) const; + virtual void parseMappedAttribute(MappedAttribute*); + + virtual HTMLTagStatus endTagRequirement() const { return TagStatusRequired; } + virtual bool checkDTD(const Node* newChild); + + virtual void updateWidget() { } + + String align() const; + void setAlign(const String&); + + String height() const; + void setHeight(const String&); + + String name() const; + void setName(const String&); + + String width() const; + void setWidth(const String&); + + virtual void defaultEventHandler(Event*); + + virtual RenderWidget* renderWidgetForJSBindings() const = 0; + virtual void detach(); + JSInstance getInstance() const; + +#if ENABLE(NETSCAPE_PLUGIN_API) + virtual NPObject* getNPObject(); +#endif + +protected: + static void updateWidgetCallback(Node*); + + AtomicString m_name; + mutable JSInstanceHolder m_instance; +#if ENABLE(NETSCAPE_PLUGIN_API) + NPObject* m_NPObject; +#endif +}; + +} // namespace WebCore + +#endif // HTMLPlugInElement_h diff --git a/webkit/pending/Node.cpp b/webkit/pending/Node.cpp new file mode 100644 index 0000000..d16a007 --- /dev/null +++ b/webkit/pending/Node.cpp @@ -0,0 +1,2010 @@ +/* + * 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) 2007 Trolltech ASA + * + * 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 "Node.h" + +#include "CSSParser.h" +#include "CSSRule.h" +#include "CSSRuleList.h" +#include "CSSSelector.h" +#include "CSSStyleRule.h" +#include "CSSStyleSelector.h" +#include "CSSStyleSheet.h" +#include "CString.h" +#include "ChildNodeList.h" +#include "ClassNodeList.h" +#include "DOMImplementation.h" +#include "Document.h" +#include "DynamicNodeList.h" +#include "Element.h" +#include "ExceptionCode.h" +#include "ExceptionContext.h" +#include "Frame.h" +#include "HTMLNames.h" +#include "Logging.h" +#include "NSResolver.h" +#include "NameNodeList.h" +#include "NamedAttrMap.h" +#include "ProcessingInstruction.h" +#include "RenderObject.h" +#include "ScriptController.h" +#include "SelectorNodeList.h" +#include "StringBuilder.h" +#include "TagNodeList.h" +#include "Text.h" +#include "XMLNames.h" +#include "htmlediting.h" +#include <wtf/RefCountedLeakCounter.h> + +#if USE(JSC) +#include <kjs/JSLock.h> +#endif + +namespace WebCore { + +using namespace HTMLNames; + +struct NodeListsNodeData { + typedef HashSet<DynamicNodeList*> NodeListSet; + NodeListSet m_listsWithCaches; + + DynamicNodeList::Caches m_childNodeListCaches; + + typedef HashMap<String, DynamicNodeList::Caches*> CacheMap; + CacheMap m_classNodeListCaches; + CacheMap m_nameNodeListCaches; + + ~NodeListsNodeData() + { + deleteAllValues(m_classNodeListCaches); + deleteAllValues(m_nameNodeListCaches); + } + + void invalidateCaches(); + void invalidateCachesThatDependOnAttributes(); + bool isEmpty() const; +}; + +// -------- + +bool Node::isSupported(const String& feature, const String& version) +{ + return DOMImplementation::hasFeature(feature, version); +} + +#ifndef NDEBUG +static WTF::RefCountedLeakCounter nodeCounter("WebCoreNode"); + +static bool shouldIgnoreLeaks = false; +static HashSet<Node*> ignoreSet; +#endif + +void Node::startIgnoringLeaks() +{ +#ifndef NDEBUG + shouldIgnoreLeaks = true; +#endif +} + +void Node::stopIgnoringLeaks() +{ +#ifndef NDEBUG + shouldIgnoreLeaks = false; +#endif +} + +Node::StyleChange Node::diff( RenderStyle *s1, RenderStyle *s2 ) +{ + // FIXME: The behavior of this function is just totally wrong. It doesn't handle + // explicit inheritance of non-inherited properties and so you end up not re-resolving + // style in cases where you need to. + StyleChange ch = NoInherit; + EDisplay display1 = s1 ? s1->display() : NONE; + bool fl1 = s1 && s1->hasPseudoStyle(RenderStyle::FIRST_LETTER); + EDisplay display2 = s2 ? s2->display() : NONE; + bool fl2 = s2 && s2->hasPseudoStyle(RenderStyle::FIRST_LETTER); + + if (display1 != display2 || fl1 != fl2 || (s1 && s2 && !s1->contentDataEquivalent(s2))) + ch = Detach; + else if (!s1 || !s2) + ch = Inherit; + else if (*s1 == *s2) + ch = NoChange; + else if (s1->inheritedNotEqual(s2)) + ch = Inherit; + + // If the pseudoStyles have changed, we want any StyleChange that is not NoChange + // because setStyle will do the right thing with anything else. + if (ch == NoChange && s1->hasPseudoStyle(RenderStyle::BEFORE)) { + RenderStyle* ps2 = s2->getPseudoStyle(RenderStyle::BEFORE); + if (!ps2) + ch = NoInherit; + else { + RenderStyle* ps1 = s1->getPseudoStyle(RenderStyle::BEFORE); + ch = ps1 && *ps1 == *ps2 ? NoChange : NoInherit; + } + } + if (ch == NoChange && s1->hasPseudoStyle(RenderStyle::AFTER)) { + RenderStyle* ps2 = s2->getPseudoStyle(RenderStyle::AFTER); + if (!ps2) + ch = NoInherit; + else { + RenderStyle* ps1 = s1->getPseudoStyle(RenderStyle::AFTER); + ch = ps2 && *ps1 == *ps2 ? NoChange : NoInherit; + } + } + + return ch; +} + +Node::Node(Document* doc, bool isElement) + : m_document(doc) + , m_previous(0) + , m_next(0) + , m_renderer(0) + , m_tabIndex(0) + , m_styleChange(NoStyleChange) + , m_hasId(false) + , m_hasClass(false) + , m_attached(false) + , m_hasChangedChild(false) + , m_inDocument(false) + , m_isLink(false) + , m_focused(false) + , m_active(false) + , m_hovered(false) + , m_inActiveChain(false) + , m_inDetach(false) + , m_inSubtreeMark(false) + , m_tabIndexSetExplicitly(false) + , m_isElement(isElement) +{ +#ifndef NDEBUG + if (shouldIgnoreLeaks) + ignoreSet.add(this); + else + nodeCounter.increment(); +#endif +} + +void Node::setDocument(Document* doc) +{ + if (inDocument() || m_document == doc) + return; + + willMoveToNewOwnerDocument(); + + { +#if USE(JSC) + KJS::JSLock lock(false); + ScriptInterpreter::updateDOMNodeDocument(this, m_document.get(), doc); +#endif + } + m_document = doc; + + didMoveToNewOwnerDocument(); +} + +Node::~Node() +{ +#ifndef NDEBUG + HashSet<Node*>::iterator it = ignoreSet.find(this); + if (it != ignoreSet.end()) + ignoreSet.remove(it); + else + nodeCounter.decrement(); +#endif + + if (m_nodeLists && m_document) + document()->removeNodeListCache(); + + if (renderer()) + detach(); + + if (m_previous) + m_previous->setNextSibling(0); + if (m_next) + m_next->setPreviousSibling(0); +} + +String Node::nodeValue() const +{ + return String(); +} + +void Node::setNodeValue(const String& /*nodeValue*/, ExceptionCode& ec) +{ + // NO_MODIFICATION_ALLOWED_ERR: Raised when the node is readonly + if (isReadOnlyNode()) { + ec = NO_MODIFICATION_ALLOWED_ERR; + return; + } + + // By default, setting nodeValue has no effect. +} + +PassRefPtr<NodeList> Node::childNodes() +{ + if (!m_nodeLists) { + m_nodeLists.set(new NodeListsNodeData); + document()->addNodeListCache(); + } + + return ChildNodeList::create(this, &m_nodeLists->m_childNodeListCaches); +} + +Node* Node::virtualFirstChild() const +{ + return 0; +} + +Node* Node::virtualLastChild() const +{ + return 0; +} + +bool Node::virtualHasTagName(const QualifiedName&) const +{ + return false; +} + +Node *Node::lastDescendant() const +{ + Node *n = const_cast<Node *>(this); + while (n && n->lastChild()) + n = n->lastChild(); + return n; +} + +Node* Node::firstDescendant() const +{ + Node *n = const_cast<Node *>(this); + while (n && n->firstChild()) + n = n->firstChild(); + return n; +} + +bool Node::insertBefore(PassRefPtr<Node>, Node*, ExceptionCode& ec, bool) +{ + ec = HIERARCHY_REQUEST_ERR; + return false; +} + +bool Node::replaceChild(PassRefPtr<Node>, Node*, ExceptionCode& ec, bool) +{ + ec = HIERARCHY_REQUEST_ERR; + return false; +} + +bool Node::removeChild(Node*, ExceptionCode& ec) +{ + ec = NOT_FOUND_ERR; + return false; +} + +bool Node::appendChild(PassRefPtr<Node>, ExceptionCode& ec, bool) +{ + ec = HIERARCHY_REQUEST_ERR; + return false; +} + +void Node::remove(ExceptionCode& ec) +{ + ref(); + if (Node *p = parentNode()) + p->removeChild(this, ec); + else + ec = HIERARCHY_REQUEST_ERR; + deref(); +} + +void Node::normalize() +{ + // Go through the subtree beneath us, normalizing all nodes. This means that + // any two adjacent text nodes are merged together. + + RefPtr<Node> node = this; + while (Node* firstChild = node->firstChild()) + node = firstChild; + for (; node; node = node->traverseNextNodePostOrder()) { + NodeType type = node->nodeType(); + if (type == ELEMENT_NODE) + static_cast<Element*>(node.get())->normalizeAttributes(); + + Node* firstChild = node->firstChild(); + if (firstChild && !firstChild->nextSibling() && firstChild->isTextNode()) { + Text* text = static_cast<Text*>(firstChild); + if (!text->length()) { + ExceptionCode ec; + text->remove(ec); + } + } + + if (node == this) + break; + + if (type == TEXT_NODE) { + while (1) { + Node* nextSibling = node->nextSibling(); + if (!nextSibling || !nextSibling->isTextNode()) + break; + // Current child and the next one are both text nodes. Merge them. + Text* text = static_cast<Text*>(node.get()); + RefPtr<Text> nextText = static_cast<Text*>(nextSibling); + unsigned offset = text->length(); + ExceptionCode ec; + text->appendData(nextText->data(), ec); + document()->textNodesMerged(nextText.get(), offset); + nextText->remove(ec); + } + } + } +} + +const AtomicString& Node::prefix() const +{ + // For nodes other than elements and attributes, the prefix is always null + return nullAtom; +} + +void Node::setPrefix(const AtomicString& /*prefix*/, ExceptionCode& ec) +{ + // The spec says that for nodes other than elements and attributes, prefix is always null. + // It does not say what to do when the user tries to set the prefix on another type of + // node, however Mozilla throws a NAMESPACE_ERR exception. + ec = NAMESPACE_ERR; +} + +const AtomicString& Node::localName() const +{ + return nullAtom; +} + +const AtomicString& Node::namespaceURI() const +{ + return nullAtom; +} + +ContainerNode* Node::addChild(PassRefPtr<Node>) +{ + return 0; +} + +bool Node::isContentEditable() const +{ + return parent() && parent()->isContentEditable(); +} + +bool Node::isContentRichlyEditable() const +{ + return parent() && parent()->isContentRichlyEditable(); +} + +bool Node::shouldUseInputMethod() const +{ + return isContentEditable(); +} + +IntRect Node::getRect() const +{ + int _x, _y; + if (renderer() && renderer()->absolutePosition(_x, _y)) + return IntRect( _x, _y, renderer()->width(), renderer()->height() + renderer()->borderTopExtra() + renderer()->borderBottomExtra()); + + return IntRect(); +} + +void Node::setChanged(StyleChangeType changeType) +{ + if ((changeType != NoStyleChange) && !attached()) // changed compared to what? + return; + + if (!(changeType == InlineStyleChange && (m_styleChange == FullStyleChange || m_styleChange == AnimationStyleChange))) + m_styleChange = changeType; + + if (m_styleChange != NoStyleChange) { + for (Node* p = parentNode(); p && !p->hasChangedChild(); p = p->parentNode()) + p->setHasChangedChild(true); + document()->setDocumentChanged(true); + } +} + +static Node* outermostLazyAttachedAncestor(Node* start) +{ + Node* p = start; + for (Node* next = p->parentNode(); !next->renderer(); p = next, next = next->parentNode()) {} + return p; +} + +void Node::lazyAttach() +{ + bool mustDoFullAttach = false; + + for (Node* n = this; n; n = n->traverseNextNode(this)) { + if (!n->canLazyAttach()) { + mustDoFullAttach = true; + break; + } + + if (n->firstChild()) + n->setHasChangedChild(true); + n->m_styleChange = FullStyleChange; + n->m_attached = true; + } + + if (mustDoFullAttach) { + Node* lazyAttachedAncestor = outermostLazyAttachedAncestor(this); + if (lazyAttachedAncestor->attached()) + lazyAttachedAncestor->detach(); + lazyAttachedAncestor->attach(); + } else { + for (Node* p = parentNode(); p && !p->hasChangedChild(); p = p->parentNode()) + p->setHasChangedChild(true); + document()->setDocumentChanged(true); + } +} + +bool Node::canLazyAttach() +{ + return shadowAncestorNode() == this; +} + +bool Node::isFocusable() const +{ + return m_tabIndexSetExplicitly; +} + +bool Node::isKeyboardFocusable(KeyboardEvent*) const +{ + return isFocusable() && m_tabIndex >= 0; +} + +bool Node::isMouseFocusable() const +{ + return isFocusable(); +} + +unsigned Node::nodeIndex() const +{ + Node *_tempNode = previousSibling(); + unsigned count=0; + for( count=0; _tempNode; count++ ) + _tempNode = _tempNode->previousSibling(); + return count; +} + +void Node::registerDynamicNodeList(DynamicNodeList* list) +{ + if (!m_nodeLists) { + m_nodeLists.set(new NodeListsNodeData); + document()->addNodeListCache(); + } else if (!m_document->hasNodeListCaches()) { + // We haven't been receiving notifications while there were no registered lists, so the cache is invalid now. + m_nodeLists->invalidateCaches(); + } + + if (list->hasOwnCaches()) + m_nodeLists->m_listsWithCaches.add(list); +} + +void Node::unregisterDynamicNodeList(DynamicNodeList* list) +{ + ASSERT(m_nodeLists); + if (list->hasOwnCaches()) { + m_nodeLists->m_listsWithCaches.remove(list); + if (m_nodeLists->isEmpty()) { + m_nodeLists.clear(); + document()->removeNodeListCache(); + } + } +} + +void Node::notifyLocalNodeListsAttributeChanged() +{ + if (!m_nodeLists) + return; + + m_nodeLists->invalidateCachesThatDependOnAttributes(); + + if (m_nodeLists->isEmpty()) { + m_nodeLists.clear(); + document()->removeNodeListCache(); + } +} + +void Node::notifyNodeListsAttributeChanged() +{ + for (Node *n = this; n; n = n->parentNode()) + n->notifyLocalNodeListsAttributeChanged(); +} + +void Node::notifyLocalNodeListsChildrenChanged() +{ + if (!m_nodeLists) + return; + + m_nodeLists->invalidateCaches(); + + NodeListsNodeData::NodeListSet::iterator end = m_nodeLists->m_listsWithCaches.end(); + for (NodeListsNodeData::NodeListSet::iterator i = m_nodeLists->m_listsWithCaches.begin(); i != end; ++i) + (*i)->invalidateCache(); + + if (m_nodeLists->isEmpty()) { + m_nodeLists.clear(); + document()->removeNodeListCache(); + } +} + +void Node::notifyNodeListsChildrenChanged() +{ + for (Node* n = this; n; n = n->parentNode()) + n->notifyLocalNodeListsChildrenChanged(); +} + +unsigned Node::childNodeCount() const +{ + return 0; +} + +Node *Node::childNode(unsigned /*index*/) const +{ + return 0; +} + +Node *Node::traverseNextNode(const Node *stayWithin) const +{ + if (firstChild()) + return firstChild(); + if (this == stayWithin) + return 0; + if (nextSibling()) + return nextSibling(); + const Node *n = this; + while (n && !n->nextSibling() && (!stayWithin || n->parentNode() != stayWithin)) + n = n->parentNode(); + if (n) + return n->nextSibling(); + return 0; +} + +Node *Node::traverseNextSibling(const Node *stayWithin) const +{ + if (this == stayWithin) + return 0; + if (nextSibling()) + return nextSibling(); + const Node *n = this; + while (n && !n->nextSibling() && (!stayWithin || n->parentNode() != stayWithin)) + n = n->parentNode(); + if (n) + return n->nextSibling(); + return 0; +} + +Node* Node::traverseNextNodePostOrder() const +{ + Node* next = nextSibling(); + if (!next) + return parentNode(); + while (Node* firstChild = next->firstChild()) + next = firstChild; + return next; +} + +Node *Node::traversePreviousNode(const Node *stayWithin) const +{ + if (this == stayWithin) + return 0; + if (previousSibling()) { + Node *n = previousSibling(); + while (n->lastChild()) + n = n->lastChild(); + return n; + } + return parentNode(); +} + +Node *Node::traversePreviousNodePostOrder(const Node *stayWithin) const +{ + if (lastChild()) + return lastChild(); + if (this == stayWithin) + return 0; + if (previousSibling()) + return previousSibling(); + const Node *n = this; + while (n && !n->previousSibling() && (!stayWithin || n->parentNode() != stayWithin)) + n = n->parentNode(); + if (n) + return n->previousSibling(); + return 0; +} + +Node* Node::traversePreviousSiblingPostOrder(const Node* stayWithin) const +{ + if (this == stayWithin) + return 0; + if (previousSibling()) + return previousSibling(); + const Node *n = this; + while (n && !n->previousSibling() && (!stayWithin || n->parentNode() != stayWithin)) + n = n->parentNode(); + if (n) + return n->previousSibling(); + return 0; +} + +void Node::checkSetPrefix(const AtomicString &_prefix, ExceptionCode& ec) +{ + // Perform error checking as required by spec for setting Node.prefix. Used by + // Element::setPrefix() and Attr::setPrefix() + + // FIXME: Implement support for INVALID_CHARACTER_ERR: Raised if the specified prefix contains an illegal character. + + // NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly. + if (isReadOnlyNode()) { + ec = NO_MODIFICATION_ALLOWED_ERR; + return; + } + + // FIXME: Implement NAMESPACE_ERR: - Raised if the specified prefix is malformed + // We have to comment this out, since it's used for attributes and tag names, and we've only + // switched one over. + /* + // - if the namespaceURI of this node is null, + // - if the specified prefix is "xml" and the namespaceURI of this node is different from + // "http://www.w3.org/XML/1998/namespace", + // - if this node is an attribute and the specified prefix is "xmlns" and + // the namespaceURI of this node is different from "http://www.w3.org/2000/xmlns/", + // - or if this node is an attribute and the qualifiedName of this node is "xmlns" [Namespaces]. + if ((namespacePart(id()) == noNamespace && id() > ID_LAST_TAG) || + (_prefix == "xml" && String(document()->namespaceURI(id())) != "http://www.w3.org/XML/1998/namespace")) { + ec = NAMESPACE_ERR; + return; + }*/ +} + +bool Node::canReplaceChild(Node* newChild, Node* oldChild) +{ + if (newChild->nodeType() != DOCUMENT_FRAGMENT_NODE) { + if (!childTypeAllowed(newChild->nodeType())) + return false; + } + else { + for (Node *n = newChild->firstChild(); n; n = n->nextSibling()) { + if (!childTypeAllowed(n->nodeType())) + return false; + } + } + + return true; +} + +void Node::checkReplaceChild(Node* newChild, Node* oldChild, ExceptionCode& ec) +{ + // Perform error checking as required by spec for adding a new child. Used by + // appendChild(), replaceChild() and insertBefore() + + // Not mentioned in spec: throw NOT_FOUND_ERR if newChild is null + if (!newChild) { + ec = NOT_FOUND_ERR; + return; + } + + // NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly + if (isReadOnlyNode()) { + ec = NO_MODIFICATION_ALLOWED_ERR; + return; + } + + bool shouldAdoptChild = false; + + // WRONG_DOCUMENT_ERR: Raised if newChild was created from a different document than the one that + // created this node. + // We assume that if newChild is a DocumentFragment, all children are created from the same document + // as the fragment itself (otherwise they could not have been added as children) + if (newChild->document() != document()) { + // but if the child is not in a document yet then loosen the + // restriction, so that e.g. creating an element with the Option() + // constructor and then adding it to a different document works, + // as it does in Mozilla and Mac IE. + if (!newChild->inDocument()) { + shouldAdoptChild = true; + } else { + ec = WRONG_DOCUMENT_ERR; + return; + } + } + + // HIERARCHY_REQUEST_ERR: Raised if this node is of a type that does not allow children of the type of the + // newChild node, or if the node to append is one of this node's ancestors. + + // check for ancestor/same node + if (newChild == this || isDescendantOf(newChild)) { + ec = HIERARCHY_REQUEST_ERR; + return; + } + + if (!canReplaceChild(newChild, oldChild)) { + ec = HIERARCHY_REQUEST_ERR; + return; + } + + // change the document pointer of newChild and all of its children to be the new document + if (shouldAdoptChild) + for (Node* node = newChild; node; node = node->traverseNextNode(newChild)) + node->setDocument(document()); +} + +void Node::checkAddChild(Node *newChild, ExceptionCode& ec) +{ + // Perform error checking as required by spec for adding a new child. Used by + // appendChild(), replaceChild() and insertBefore() + + // Not mentioned in spec: throw NOT_FOUND_ERR if newChild is null + if (!newChild) { + ec = NOT_FOUND_ERR; + return; + } + + // NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly + if (isReadOnlyNode()) { + ec = NO_MODIFICATION_ALLOWED_ERR; + return; + } + + bool shouldAdoptChild = false; + + // WRONG_DOCUMENT_ERR: Raised if newChild was created from a different document than the one that + // created this node. + // We assume that if newChild is a DocumentFragment, all children are created from the same document + // as the fragment itself (otherwise they could not have been added as children) + if (newChild->document() != document()) { + // but if the child is not in a document yet then loosen the + // restriction, so that e.g. creating an element with the Option() + // constructor and then adding it to a different document works, + // as it does in Mozilla and Mac IE. + if (!newChild->inDocument()) { + shouldAdoptChild = true; + } else { + ec = WRONG_DOCUMENT_ERR; + return; + } + } + + // HIERARCHY_REQUEST_ERR: Raised if this node is of a type that does not allow children of the type of the + // newChild node, or if the node to append is one of this node's ancestors. + + // check for ancestor/same node + if (newChild == this || isDescendantOf(newChild)) { + ec = HIERARCHY_REQUEST_ERR; + return; + } + + if (newChild->nodeType() != DOCUMENT_FRAGMENT_NODE) { + if (!childTypeAllowed(newChild->nodeType())) { + ec = HIERARCHY_REQUEST_ERR; + return; + } + } + else { + for (Node *n = newChild->firstChild(); n; n = n->nextSibling()) { + if (!childTypeAllowed(n->nodeType())) { + ec = HIERARCHY_REQUEST_ERR; + return; + } + } + } + + // change the document pointer of newChild and all of its children to be the new document + if (shouldAdoptChild) + for (Node* node = newChild; node; node = node->traverseNextNode(newChild)) + node->setDocument(document()); +} + +bool Node::isDescendantOf(const Node *other) const +{ + // Return true if other is an ancestor of this, otherwise false + if (!other) + return false; + for (const Node *n = parentNode(); n; n = n->parentNode()) { + if (n == other) + return true; + } + return false; +} + +bool Node::childAllowed(Node* newChild) +{ + return childTypeAllowed(newChild->nodeType()); +} + +void Node::attach() +{ + ASSERT(!attached()); + ASSERT(!renderer() || (renderer()->style() && renderer()->parent())); + + // If this node got a renderer it may be the previousRenderer() of sibling text nodes and thus affect the + // result of Text::rendererIsNeeded() for those nodes. + if (renderer()) { + for (Node* next = nextSibling(); next; next = next->nextSibling()) { + if (next->renderer()) + break; + if (!next->attached()) + break; // Assume this means none of the following siblings are attached. + if (next->isTextNode()) + next->createRendererIfNeeded(); + } + } + + m_attached = true; +} + +void Node::willRemove() +{ +} + +void Node::detach() +{ + m_inDetach = true; + + if (renderer()) + renderer()->destroy(); + setRenderer(0); + + Document* doc = document(); + if (m_hovered) + doc->hoveredNodeDetached(this); + if (m_inActiveChain) + doc->activeChainNodeDetached(this); + + m_active = false; + m_hovered = false; + m_inActiveChain = false; + m_attached = false; + m_inDetach = false; +} + +void Node::insertedIntoDocument() +{ + setInDocument(true); + insertedIntoTree(false); +} + +void Node::removedFromDocument() +{ + if (m_document && m_document->getCSSTarget() == this) + m_document->setCSSTarget(0); + + setInDocument(false); + removedFromTree(false); +} + +Node *Node::previousEditable() const +{ + Node *node = previousLeafNode(); + while (node) { + if (node->isContentEditable()) + return node; + node = node->previousLeafNode(); + } + return 0; +} + +Node *Node::nextEditable() const +{ + Node *node = nextLeafNode(); + while (node) { + if (node->isContentEditable()) + return node; + node = node->nextLeafNode(); + } + return 0; +} + +RenderObject * Node::previousRenderer() +{ + for (Node *n = previousSibling(); n; n = n->previousSibling()) { + if (n->renderer()) + return n->renderer(); + } + return 0; +} + +RenderObject * Node::nextRenderer() +{ + // Avoid an O(n^2) problem with this function by not checking for nextRenderer() when the parent element hasn't even + // been attached yet. + if (parent() && !parent()->attached()) + return 0; + + for (Node *n = nextSibling(); n; n = n->nextSibling()) { + if (n->renderer()) + return n->renderer(); + } + return 0; +} + +// FIXME: This code is used by editing. Seems like it could move over there and not pollute Node. +Node *Node::previousNodeConsideringAtomicNodes() const +{ + if (previousSibling()) { + Node *n = previousSibling(); + while (!isAtomicNode(n) && n->lastChild()) + n = n->lastChild(); + return n; + } + else if (parentNode()) { + return parentNode(); + } + else { + return 0; + } +} + +Node *Node::nextNodeConsideringAtomicNodes() const +{ + if (!isAtomicNode(this) && firstChild()) + return firstChild(); + if (nextSibling()) + return nextSibling(); + const Node *n = this; + while (n && !n->nextSibling()) + n = n->parentNode(); + if (n) + return n->nextSibling(); + return 0; +} + +Node *Node::previousLeafNode() const +{ + Node *node = previousNodeConsideringAtomicNodes(); + while (node) { + if (isAtomicNode(node)) + return node; + node = node->previousNodeConsideringAtomicNodes(); + } + return 0; +} + +Node *Node::nextLeafNode() const +{ + Node *node = nextNodeConsideringAtomicNodes(); + while (node) { + if (isAtomicNode(node)) + return node; + node = node->nextNodeConsideringAtomicNodes(); + } + return 0; +} + +void Node::createRendererIfNeeded() +{ + if (!document()->shouldCreateRenderers()) + return; + + ASSERT(!renderer()); + + Node *parent = parentNode(); + ASSERT(parent); + + RenderObject *parentRenderer = parent->renderer(); + if (parentRenderer && parentRenderer->canHaveChildren() +#if ENABLE(SVG) + && parent->childShouldCreateRenderer(this) +#endif + ) { + RenderStyle* style = styleForRenderer(parentRenderer); + if (rendererIsNeeded(style)) { + if (RenderObject* r = createRenderer(document()->renderArena(), style)) { + if (!parentRenderer->isChildAllowed(r, style)) + r->destroy(); + else { + setRenderer(r); + renderer()->setAnimatableStyle(style); + parentRenderer->addChild(renderer(), nextRenderer()); + } + } + } + style->deref(document()->renderArena()); + } +} + +RenderStyle *Node::styleForRenderer(RenderObject *parent) +{ + RenderStyle *style = parent->style(); + style->ref(); + return style; +} + +bool Node::rendererIsNeeded(RenderStyle *style) +{ + return (document()->documentElement() == this) || (style->display() != NONE); +} + +RenderObject *Node::createRenderer(RenderArena *arena, RenderStyle *style) +{ + ASSERT(false); + return 0; +} + +RenderStyle* Node::renderStyle() const +{ + return m_renderer ? m_renderer->style() : 0; +} + +void Node::setRenderStyle(RenderStyle* s) +{ + if (m_renderer) + m_renderer->setAnimatableStyle(s); +} + +RenderStyle* Node::computedStyle() +{ + return parent() ? parent()->computedStyle() : 0; +} + +int Node::maxCharacterOffset() const +{ + ASSERT_NOT_REACHED(); + return 0; +} + +// FIXME: Shouldn't these functions be in the editing code? Code that asks questions about HTML in the core DOM class +// is obviously misplaced. +bool Node::canStartSelection() const +{ + if (isContentEditable()) + return true; + return parent() ? parent()->canStartSelection() : true; +} + +Node* Node::shadowAncestorNode() +{ +#if ENABLE(SVG) + // SVG elements living in a shadow tree only occour when <use> created them. + // For these cases we do NOT want to return the shadowParentNode() here + // but the actual shadow tree element - as main difference to the HTML forms + // shadow tree concept. (This function _could_ be made virtual - opinions?) + if (isSVGElement()) + return this; +#endif + + Node* root = shadowTreeRootNode(); + if (root) + return root->shadowParentNode(); + return this; +} + +Node* Node::shadowTreeRootNode() +{ + Node* root = this; + while (root) { + if (root->isShadowNode()) + return root; + root = root->parentNode(); + } + return 0; +} + +bool Node::isInShadowTree() +{ + for (Node* n = this; n; n = n->parentNode()) + if (n->isShadowNode()) + return true; + return false; +} + +bool Node::isBlockFlow() const +{ + return renderer() && renderer()->isBlockFlow(); +} + +bool Node::isBlockFlowOrBlockTable() const +{ + return renderer() && (renderer()->isBlockFlow() || renderer()->isTable() && !renderer()->isInline()); +} + +bool Node::isEditableBlock() const +{ + return isContentEditable() && isBlockFlow(); +} + +Element *Node::enclosingBlockFlowElement() const +{ + Node *n = const_cast<Node *>(this); + if (isBlockFlow()) + return static_cast<Element *>(n); + + while (1) { + n = n->parentNode(); + if (!n) + break; + if (n->isBlockFlow() || n->hasTagName(bodyTag)) + return static_cast<Element *>(n); + } + return 0; +} + +Element *Node::enclosingInlineElement() const +{ + Node *n = const_cast<Node *>(this); + Node *p; + + while (1) { + p = n->parentNode(); + if (!p || p->isBlockFlow() || p->hasTagName(bodyTag)) + return static_cast<Element *>(n); + // Also stop if any previous sibling is a block + for (Node *sibling = n->previousSibling(); sibling; sibling = sibling->previousSibling()) { + if (sibling->isBlockFlow()) + return static_cast<Element *>(n); + } + n = p; + } + ASSERT_NOT_REACHED(); + return 0; +} + +Element* Node::rootEditableElement() const +{ + Element* result = 0; + for (Node* n = const_cast<Node*>(this); n && n->isContentEditable(); n = n->parentNode()) { + if (n->isElementNode()) + result = static_cast<Element*>(n); + if (n->hasTagName(bodyTag)) + break; + } + return result; +} + +bool Node::inSameContainingBlockFlowElement(Node *n) +{ + return n ? enclosingBlockFlowElement() == n->enclosingBlockFlowElement() : false; +} + +// FIXME: End of obviously misplaced HTML editing functions. Try to move these out of Node. + +PassRefPtr<NodeList> Node::getElementsByTagName(const String& name) +{ + return getElementsByTagNameNS("*", name); +} + +PassRefPtr<NodeList> Node::getElementsByTagNameNS(const String& namespaceURI, const String& localName) +{ + if (localName.isNull()) + return 0; + + String name = localName; + if (document()->isHTMLDocument()) + name = localName.lower(); + return TagNodeList::create(this, namespaceURI.isEmpty() ? nullAtom : AtomicString(namespaceURI), name); +} + +PassRefPtr<NodeList> Node::getElementsByName(const String& elementName) +{ + if (!m_nodeLists) { + m_nodeLists.set(new NodeListsNodeData); + document()->addNodeListCache(); + } + + pair<NodeListsNodeData::CacheMap::iterator, bool> result = m_nodeLists->m_nameNodeListCaches.add(elementName, 0); + if (result.second) + result.first->second = new DynamicNodeList::Caches; + + return NameNodeList::create(this, elementName, result.first->second); +} + +PassRefPtr<NodeList> Node::getElementsByClassName(const String& classNames) +{ + if (!m_nodeLists) { + m_nodeLists.set(new NodeListsNodeData); + document()->addNodeListCache(); + } + + pair<NodeListsNodeData::CacheMap::iterator, bool> result = m_nodeLists->m_classNodeListCaches.add(classNames, 0); + if (result.second) + result.first->second = new DynamicNodeList::Caches; + + return ClassNodeList::create(this, classNames, result.first->second); +} + +template <typename Functor> +static bool forEachTagSelector(Functor& functor, CSSSelector* selector) +{ + ASSERT(selector); + + do { + if (functor(selector)) + return true; + if (CSSSelector* simpleSelector = selector->m_simpleSelector) { + if (forEachTagSelector(functor, simpleSelector)) + return true; + } + } while ((selector = selector->m_tagHistory)); + + return false; +} + +template <typename Functor> +static bool forEachSelector(Functor& functor, CSSSelector* selector) +{ + for (; selector; selector = selector->next()) { + if (forEachTagSelector(functor, selector)) + return true; + } + + return false; +} + +class SelectorNeedsNamespaceResolutionFunctor { +public: + bool operator()(CSSSelector* selector) + { + if (selector->hasTag() && selector->m_tag.prefix() != nullAtom && selector->m_tag.prefix() != starAtom) + return true; + if (selector->hasAttribute() && selector->m_attr.prefix() != nullAtom && selector->m_attr.prefix() != starAtom) + return true; + return false; + } +}; + +class ResolveNamespaceFunctor { +public: + ResolveNamespaceFunctor(NSResolver* resolver, ExceptionCode& ec, ExceptionContext* exec) + : m_resolver(resolver) + , m_exceptionCode(ec) + , m_exec(exec) + { + } + + bool operator()(CSSSelector* selector) + { + if (selector->hasTag() && selector->m_tag.prefix() != nullAtom && selector->m_tag.prefix() != starAtom) { + String resolvedNamespaceURI = m_resolver->lookupNamespaceURI(m_exec, selector->m_tag.prefix()); + if (m_exec && m_exec->hadException()) + return true; + if (resolvedNamespaceURI.isEmpty()) { + m_exceptionCode = NAMESPACE_ERR; + return true; + } + QualifiedName newQualifiedName(selector->m_tag.prefix(), selector->m_tag.localName(), resolvedNamespaceURI); + selector->m_tag = newQualifiedName; + } + if (selector->hasAttribute() && selector->m_attr.prefix() != nullAtom && selector->m_attr.prefix() != starAtom) { + String resolvedNamespaceURI = m_resolver->lookupNamespaceURI(m_exec, selector->m_attr.prefix()); + if (m_exec && m_exec->hadException()) + return true; + if (resolvedNamespaceURI.isEmpty()) { + m_exceptionCode = NAMESPACE_ERR; + return true; + } + QualifiedName newQualifiedName(selector->m_attr.prefix(), selector->m_attr.localName(), resolvedNamespaceURI); + selector->m_attr = newQualifiedName; + } + + return false; + } + +private: + NSResolver* m_resolver; + ExceptionCode& m_exceptionCode; + ExceptionContext* m_exec; +}; + +static bool selectorNeedsNamespaceResolution(CSSSelector* currentSelector) +{ + SelectorNeedsNamespaceResolutionFunctor functor; + return forEachSelector(functor, currentSelector); +} + +static bool resolveNamespacesForSelector(CSSSelector* currentSelector, NSResolver* resolver, ExceptionCode& ec, ExceptionContext* exec) +{ + ResolveNamespaceFunctor functor(resolver, ec, exec); + return forEachSelector(functor, currentSelector); +} + +PassRefPtr<Element> Node::querySelector(const String& selectors, ExceptionCode& ec) +{ + return querySelector(selectors, 0, ec, ExceptionContext::createFromNode(this)); +} + +PassRefPtr<NodeList> Node::querySelectorAll(const String& selectors, ExceptionCode& ec) +{ + return querySelectorAll(selectors, 0, ec, ExceptionContext::createFromNode(this)); +} + +PassRefPtr<Element> Node::querySelector(const String& selectors, NSResolver* resolver, ExceptionCode& ec, ExceptionContext* exec) +{ + if (selectors.isEmpty()) { + ec = SYNTAX_ERR; + return 0; + } + bool strictParsing = !document()->inCompatMode(); + CSSParser p(strictParsing); + if (resolver) { + String defaultNamespace = resolver->lookupNamespaceURI(exec, String()); + if (exec && exec->hadException()) + return 0; + if (!defaultNamespace.isEmpty()) + p.m_defaultNamespace = defaultNamespace; + } + + std::auto_ptr<CSSSelector> querySelector = p.parseSelector(selectors); + if (!querySelector.get()) { + ec = SYNTAX_ERR; + return 0; + } + + if (resolver) { + if (resolveNamespacesForSelector(querySelector.get(), resolver, ec, exec)) + return 0; + } else { + // No NSResolver was passed, so throw a NAMESPACE_ERR if the selector includes any + // namespace prefixes. + if (selectorNeedsNamespaceResolution(querySelector.get())) { + ec = NAMESPACE_ERR; + return 0; + } + } + + // FIXME: we could also optimize for the the [id="foo"] case + if (strictParsing && inDocument() && !querySelector->next() && querySelector->m_match == CSSSelector::Id + && !querySelector->hasTag() && !querySelector->m_tagHistory && !querySelector->m_simpleSelector) { + ASSERT(querySelector->m_attr == idAttr); + Element* element = document()->getElementById(querySelector->m_value); + if (element && (isDocumentNode() || element->isDescendantOf(this))) + return element; + return 0; + } + + CSSStyleSelector::SelectorChecker selectorChecker(document(), strictParsing); + + // FIXME: We can speed this up by implementing caching similar to the one use by getElementById + for (Node* n = firstChild(); n; n = n->traverseNextNode(this)) { + if (n->isElementNode()) { + Element* element = static_cast<Element*>(n); + for (CSSSelector* selector = querySelector.get(); selector; selector = selector->next()) { + if (selectorChecker.checkSelector(selector, element)) + return element; + } + } + } + + return 0; +} + +PassRefPtr<NodeList> Node::querySelectorAll(const String& selectors, NSResolver* resolver, ExceptionCode& ec, ExceptionContext* exec) +{ + if (selectors.isEmpty()) { + ec = SYNTAX_ERR; + return 0; + } + bool strictParsing = !document()->inCompatMode(); + CSSParser p(strictParsing); + if (resolver) { + String defaultNamespace = resolver->lookupNamespaceURI(exec, String()); + if (exec && exec->hadException()) + return false; + if (!defaultNamespace.isEmpty()) + p.m_defaultNamespace = defaultNamespace; + } + + std::auto_ptr<CSSSelector> querySelector = p.parseSelector(selectors); + + if (!querySelector.get()) { + ec = SYNTAX_ERR; + return 0; + } + + if (resolver) { + if (resolveNamespacesForSelector(querySelector.get(), resolver, ec, exec)) + return 0; + } else { + // No NSResolver was passed, so throw a NAMESPACE_ERR if the selector includes any + // namespace prefixes. + if (selectorNeedsNamespaceResolution(querySelector.get())) { + ec = NAMESPACE_ERR; + return 0; + } + } + + return createSelectorNodeList(this, querySelector.get()); +} + +Document *Node::ownerDocument() const +{ + Document *doc = document(); + return doc == this ? 0 : doc; +} + +bool Node::hasAttributes() const +{ + return false; +} + +NamedAttrMap* Node::attributes() const +{ + return 0; +} + +KURL Node::baseURI() const +{ + return parentNode() ? parentNode()->baseURI() : KURL(); +} + +bool Node::isEqualNode(Node *other) const +{ + if (!other) + return false; + + if (nodeType() != other->nodeType()) + return false; + + if (nodeName() != other->nodeName()) + return false; + + if (localName() != other->localName()) + return false; + + if (namespaceURI() != other->namespaceURI()) + return false; + + if (prefix() != other->prefix()) + return false; + + if (nodeValue() != other->nodeValue()) + return false; + + NamedAttrMap *attrs = attributes(); + NamedAttrMap *otherAttrs = other->attributes(); + + if (!attrs && otherAttrs) + return false; + + if (attrs && !attrs->mapsEquivalent(otherAttrs)) + return false; + + Node *child = firstChild(); + Node *otherChild = other->firstChild(); + + while (child) { + if (!child->isEqualNode(otherChild)) + return false; + + child = child->nextSibling(); + otherChild = otherChild->nextSibling(); + } + + if (otherChild) + return false; + + // FIXME: For DocumentType nodes we should check equality on + // the entities and notations NamedNodeMaps as well. + + return true; +} + +bool Node::isDefaultNamespace(const String &namespaceURI) const +{ + // Implemented according to + // http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/namespaces-algorithms.html#isDefaultNamespaceAlgo + + switch (nodeType()) { + case ELEMENT_NODE: { + const Element *elem = static_cast<const Element *>(this); + + if (elem->prefix().isNull()) + return elem->namespaceURI() == namespaceURI; + + if (elem->hasAttributes()) { + NamedAttrMap *attrs = elem->attributes(); + + for (unsigned i = 0; i < attrs->length(); i++) { + Attribute *attr = attrs->attributeItem(i); + + if (attr->localName() == "xmlns") + return attr->value() == namespaceURI; + } + } + + if (Element* ancestor = ancestorElement()) + return ancestor->isDefaultNamespace(namespaceURI); + + return false; + } + case DOCUMENT_NODE: + if (Element* de = static_cast<const Document*>(this)->documentElement()) + return de->isDefaultNamespace(namespaceURI); + return false; + case ENTITY_NODE: + case NOTATION_NODE: + case DOCUMENT_TYPE_NODE: + case DOCUMENT_FRAGMENT_NODE: + return false; + case ATTRIBUTE_NODE: { + const Attr *attr = static_cast<const Attr *>(this); + if (attr->ownerElement()) + return attr->ownerElement()->isDefaultNamespace(namespaceURI); + return false; + } + default: + if (Element* ancestor = ancestorElement()) + return ancestor->isDefaultNamespace(namespaceURI); + return false; + } +} + +String Node::lookupPrefix(const String &namespaceURI) const +{ + // Implemented according to + // http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/namespaces-algorithms.html#lookupNamespacePrefixAlgo + + if (namespaceURI.isEmpty()) + return String(); + + switch (nodeType()) { + case ELEMENT_NODE: + return lookupNamespacePrefix(namespaceURI, static_cast<const Element *>(this)); + case DOCUMENT_NODE: + if (Element* de = static_cast<const Document*>(this)->documentElement()) + return de->lookupPrefix(namespaceURI); + return String(); + case ENTITY_NODE: + case NOTATION_NODE: + case DOCUMENT_FRAGMENT_NODE: + case DOCUMENT_TYPE_NODE: + return String(); + case ATTRIBUTE_NODE: { + const Attr *attr = static_cast<const Attr *>(this); + if (attr->ownerElement()) + return attr->ownerElement()->lookupPrefix(namespaceURI); + return String(); + } + default: + if (Element* ancestor = ancestorElement()) + return ancestor->lookupPrefix(namespaceURI); + return String(); + } +} + +String Node::lookupNamespaceURI(const String &prefix) const +{ + // Implemented according to + // http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/namespaces-algorithms.html#lookupNamespaceURIAlgo + + if (!prefix.isNull() && prefix.isEmpty()) + return String(); + + switch (nodeType()) { + case ELEMENT_NODE: { + const Element *elem = static_cast<const Element *>(this); + + if (!elem->namespaceURI().isNull() && elem->prefix() == prefix) + return elem->namespaceURI(); + + if (elem->hasAttributes()) { + NamedAttrMap *attrs = elem->attributes(); + + for (unsigned i = 0; i < attrs->length(); i++) { + Attribute *attr = attrs->attributeItem(i); + + if (attr->prefix() == "xmlns" && attr->localName() == prefix) { + if (!attr->value().isEmpty()) + return attr->value(); + + return String(); + } else if (attr->localName() == "xmlns" && prefix.isNull()) { + if (!attr->value().isEmpty()) + return attr->value(); + + return String(); + } + } + } + if (Element* ancestor = ancestorElement()) + return ancestor->lookupNamespaceURI(prefix); + return String(); + } + case DOCUMENT_NODE: + if (Element* de = static_cast<const Document*>(this)->documentElement()) + return de->lookupNamespaceURI(prefix); + return String(); + case ENTITY_NODE: + case NOTATION_NODE: + case DOCUMENT_TYPE_NODE: + case DOCUMENT_FRAGMENT_NODE: + return String(); + case ATTRIBUTE_NODE: { + const Attr *attr = static_cast<const Attr *>(this); + + if (attr->ownerElement()) + return attr->ownerElement()->lookupNamespaceURI(prefix); + else + return String(); + } + default: + if (Element* ancestor = ancestorElement()) + return ancestor->lookupNamespaceURI(prefix); + return String(); + } +} + +String Node::lookupNamespacePrefix(const String &_namespaceURI, const Element *originalElement) const +{ + if (_namespaceURI.isNull()) + return String(); + + if (originalElement->lookupNamespaceURI(prefix()) == _namespaceURI) + return prefix(); + + if (hasAttributes()) { + NamedAttrMap *attrs = attributes(); + + for (unsigned i = 0; i < attrs->length(); i++) { + Attribute *attr = attrs->attributeItem(i); + + if (attr->prefix() == "xmlns" && + attr->value() == _namespaceURI && + originalElement->lookupNamespaceURI(attr->localName()) == _namespaceURI) + return attr->localName(); + } + } + + if (Element* ancestor = ancestorElement()) + return ancestor->lookupNamespacePrefix(_namespaceURI, originalElement); + return String(); +} + +void Node::appendTextContent(bool convertBRsToNewlines, StringBuilder& content) const +{ + switch (nodeType()) { + case TEXT_NODE: + case CDATA_SECTION_NODE: + case COMMENT_NODE: + content.append(static_cast<const CharacterData*>(this)->CharacterData::nodeValue()); + break; + + case PROCESSING_INSTRUCTION_NODE: + content.append(static_cast<const ProcessingInstruction*>(this)->ProcessingInstruction::nodeValue()); + break; + + case ELEMENT_NODE: + if (hasTagName(brTag) && convertBRsToNewlines) { + content.append('\n'); + break; + } + // Fall through. + case ATTRIBUTE_NODE: + case ENTITY_NODE: + case ENTITY_REFERENCE_NODE: + case DOCUMENT_FRAGMENT_NODE: + content.setNonNull(); + + for (Node *child = firstChild(); child; child = child->nextSibling()) { + if (child->nodeType() == COMMENT_NODE || child->nodeType() == PROCESSING_INSTRUCTION_NODE) + continue; + + child->appendTextContent(convertBRsToNewlines, content); + } + break; + + case DOCUMENT_NODE: + case DOCUMENT_TYPE_NODE: + case NOTATION_NODE: + case XPATH_NAMESPACE_NODE: + break; + } +} + +String Node::textContent(bool convertBRsToNewlines) const +{ + StringBuilder content; + appendTextContent(convertBRsToNewlines, content); + return content.toString(); +} + +void Node::setTextContent(const String &text, ExceptionCode& ec) +{ + switch (nodeType()) { + case TEXT_NODE: + case CDATA_SECTION_NODE: + case COMMENT_NODE: + case PROCESSING_INSTRUCTION_NODE: + setNodeValue(text, ec); + break; + case ELEMENT_NODE: + case ATTRIBUTE_NODE: + case ENTITY_NODE: + case ENTITY_REFERENCE_NODE: + case DOCUMENT_FRAGMENT_NODE: { + ContainerNode *container = static_cast<ContainerNode *>(this); + + container->removeChildren(); + + if (!text.isEmpty()) + appendChild(document()->createTextNode(text), ec); + break; + } + case DOCUMENT_NODE: + case DOCUMENT_TYPE_NODE: + case NOTATION_NODE: + default: + // Do nothing + break; + } +} + +Element* Node::ancestorElement() const +{ + // In theory, there can be EntityReference nodes between elements, but this is currently not supported. + for (Node* n = parentNode(); n; n = n->parentNode()) { + if (n->isElementNode()) + return static_cast<Element*>(n); + } + return 0; +} + +bool Node::offsetInCharacters() const +{ + return false; +} + +unsigned short Node::compareDocumentPosition(Node* otherNode) +{ + // It is not clear what should be done if |otherNode| is 0. + if (!otherNode) + return DOCUMENT_POSITION_DISCONNECTED; + + if (otherNode == this) + return DOCUMENT_POSITION_EQUIVALENT; + + Attr* attr1 = nodeType() == ATTRIBUTE_NODE ? static_cast<Attr*>(this) : 0; + Attr* attr2 = otherNode->nodeType() == ATTRIBUTE_NODE ? static_cast<Attr*>(otherNode) : 0; + + Node* start1 = attr1 ? attr1->ownerElement() : this; + Node* start2 = attr2 ? attr2->ownerElement() : otherNode; + + // If either of start1 or start2 is null, then we are disconnected, since one of the nodes is + // an orphaned attribute node. + if (!start1 || !start2) + return DOCUMENT_POSITION_DISCONNECTED | DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC; + + Vector<Node*, 16> chain1; + Vector<Node*, 16> chain2; + if (attr1) + chain1.append(attr1); + if (attr2) + chain2.append(attr2); + + if (attr1 && attr2 && start1 == start2 && start1) { + // We are comparing two attributes on the same node. Crawl our attribute map + // and see which one we hit first. + NamedAttrMap* map = attr1->ownerElement()->attributes(true); + unsigned length = map->length(); + for (unsigned i = 0; i < length; ++i) { + // If neither of the two determining nodes is a child node and nodeType is the same for both determining nodes, then an + // implementation-dependent order between the determining nodes is returned. This order is stable as long as no nodes of + // the same nodeType are inserted into or removed from the direct container. This would be the case, for example, + // when comparing two attributes of the same element, and inserting or removing additional attributes might change + // the order between existing attributes. + Attribute* attr = map->attributeItem(i); + if (attr1->attr() == attr) + return DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC | DOCUMENT_POSITION_FOLLOWING; + if (attr2->attr() == attr) + return DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC | DOCUMENT_POSITION_PRECEDING; + } + + ASSERT_NOT_REACHED(); + return DOCUMENT_POSITION_DISCONNECTED; + } + + // If one node is in the document and the other is not, we must be disconnected. + // If the nodes have different owning documents, they must be disconnected. Note that we avoid + // comparing Attr nodes here, since they return false from inDocument() all the time (which seems like a bug). + if (start1->inDocument() != start2->inDocument() || + start1->document() != start2->document()) + return DOCUMENT_POSITION_DISCONNECTED | DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC; + + // We need to find a common ancestor container, and then compare the indices of the two immediate children. + Node* current; + for (current = start1; current; current = current->parentNode()) + chain1.append(current); + for (current = start2; current; current = current->parentNode()) + chain2.append(current); + + // Walk the two chains backwards and look for the first difference. + unsigned index1 = chain1.size(); + unsigned index2 = chain2.size(); + for (unsigned i = std::min(index1, index2); i; --i) { + Node* child1 = chain1[--index1]; + Node* child2 = chain2[--index2]; + if (child1 != child2) { + // If one of the children is an attribute, it wins. + if (child1->nodeType() == ATTRIBUTE_NODE) + return DOCUMENT_POSITION_FOLLOWING; + if (child2->nodeType() == ATTRIBUTE_NODE) + return DOCUMENT_POSITION_PRECEDING; + + if (!child2->nextSibling()) + return DOCUMENT_POSITION_FOLLOWING; + if (!child1->nextSibling()) + return DOCUMENT_POSITION_PRECEDING; + + // Otherwise we need to see which node occurs first. Crawl backwards from child2 looking for child1. + for (Node* child = child2->previousSibling(); child; child = child->previousSibling()) { + if (child == child1) + return DOCUMENT_POSITION_FOLLOWING; + } + return DOCUMENT_POSITION_PRECEDING; + } + } + + // There was no difference between the two parent chains, i.e., one was a subset of the other. The shorter + // chain is the ancestor. + return index1 < index2 ? + DOCUMENT_POSITION_FOLLOWING | DOCUMENT_POSITION_CONTAINED_BY : + DOCUMENT_POSITION_PRECEDING | DOCUMENT_POSITION_CONTAINS; +} + +#ifndef NDEBUG + +static void appendAttributeDesc(const Node* node, String& string, const QualifiedName& name, const char* attrDesc) +{ + if (node->isElementNode()) { + String attr = static_cast<const Element*>(node)->getAttribute(name); + if (!attr.isEmpty()) { + string += attrDesc; + string += attr; + } + } +} + +void Node::showNode(const char* prefix) const +{ + if (!prefix) + prefix = ""; + if (isTextNode()) { + String value = nodeValue(); + value.replace('\\', "\\\\"); + value.replace('\n', "\\n"); + fprintf(stderr, "%s%s\t%p \"%s\"\n", prefix, nodeName().utf8().data(), this, value.utf8().data()); + } else { + String attrs = ""; + appendAttributeDesc(this, attrs, classAttr, " CLASS="); + appendAttributeDesc(this, attrs, styleAttr, " STYLE="); + fprintf(stderr, "%s%s\t%p%s\n", prefix, nodeName().utf8().data(), this, attrs.utf8().data()); + } +} + +void Node::showTreeForThis() const +{ + showTreeAndMark(this, "*"); +} + +void Node::showTreeAndMark(const Node* markedNode1, const char* markedLabel1, const Node* markedNode2, const char * markedLabel2) const +{ + const Node* rootNode; + const Node* node = this; + while (node->parentNode() && !node->hasTagName(bodyTag)) + node = node->parentNode(); + rootNode = node; + + for (node = rootNode; node; node = node->traverseNextNode()) { + if (node == markedNode1) + fprintf(stderr, "%s", markedLabel1); + if (node == markedNode2) + fprintf(stderr, "%s", markedLabel2); + + for (const Node* tmpNode = node; tmpNode && tmpNode != rootNode; tmpNode = tmpNode->parentNode()) + fprintf(stderr, "\t"); + node->showNode(); + } +} + +void Node::formatForDebugger(char* buffer, unsigned length) const +{ + String result; + String s; + + s = nodeName(); + if (s.length() == 0) + result += "<none>"; + else + result += s; + + strncpy(buffer, result.utf8().data(), length - 1); +} + +#endif + +// -------- + +void NodeListsNodeData::invalidateCaches() +{ + m_childNodeListCaches.reset(); + invalidateCachesThatDependOnAttributes(); +} + +void NodeListsNodeData::invalidateCachesThatDependOnAttributes() +{ + CacheMap::iterator classCachesEnd = m_classNodeListCaches.end(); + for (CacheMap::iterator it = m_classNodeListCaches.begin(); it != classCachesEnd; ++it) + it->second->reset(); + + CacheMap::iterator nameCachesEnd = m_nameNodeListCaches.end(); + for (CacheMap::iterator it = m_nameNodeListCaches.begin(); it != nameCachesEnd; ++it) + it->second->reset(); +} + +bool NodeListsNodeData::isEmpty() const +{ + if (!m_listsWithCaches.isEmpty()) + return false; + + if (m_childNodeListCaches.refCount) + return false; + + CacheMap::const_iterator classCachesEnd = m_classNodeListCaches.end(); + for (CacheMap::const_iterator it = m_classNodeListCaches.begin(); it != classCachesEnd; ++it) { + if (it->second->refCount) + return false; + } + + CacheMap::const_iterator nameCachesEnd = m_nameNodeListCaches.end(); + for (CacheMap::const_iterator it = m_nameNodeListCaches.begin(); it != nameCachesEnd; ++it) { + if (it->second->refCount) + return false; + } + + return true; +} + +void Node::getSubresourceURLs(Vector<KURL>& urls) const +{ + Vector<String> subresourceStrings; + getSubresourceAttributeStrings(subresourceStrings); + + for (unsigned i = 0; i < subresourceStrings.size(); ++i) { + String& subresourceString(subresourceStrings[i]); + + // FIXME: Is parseURL appropriate here? + if (subresourceString.length()) + urls.append(document()->completeURL(parseURL(subresourceString))); + } +} + +// -------- + +} // namespace WebCore + +#ifndef NDEBUG + +void showTree(const WebCore::Node* node) +{ + if (node) + node->showTreeForThis(); +} + +#endif diff --git a/webkit/pending/Node.h b/webkit/pending/Node.h new file mode 100644 index 0000000..81e4e3c --- /dev/null +++ b/webkit/pending/Node.h @@ -0,0 +1,549 @@ +/* + * 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. + * + * 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. + * + */ + +#ifndef Node_h +#define Node_h + +#include "DocPtr.h" +#include "PlatformString.h" +#include "TreeShared.h" +#include <wtf/Assertions.h> +#include <wtf/HashSet.h> +#include <wtf/OwnPtr.h> +#include <wtf/PassRefPtr.h> + +namespace WebCore { + +class AtomicString; +class ContainerNode; +class Document; +class DynamicNodeList; +class Element; +class Event; +class EventListener; +class IntRect; +class KURL; +class KeyboardEvent; +class NSResolver; +class NamedAttrMap; +class NodeList; +class PlatformKeyboardEvent; +class PlatformMouseEvent; +class PlatformWheelEvent; +class QualifiedName; +class RegisteredEventListener; +class RenderArena; +class RenderObject; +class RenderStyle; +class StringBuilder; +class ExceptionContext; + +struct NodeListsNodeData; + +typedef int ExceptionCode; + +enum StyleChangeType { NoStyleChange, InlineStyleChange, FullStyleChange, AnimationStyleChange }; + +const unsigned short DOCUMENT_POSITION_EQUIVALENT = 0x00; +const unsigned short DOCUMENT_POSITION_DISCONNECTED = 0x01; +const unsigned short DOCUMENT_POSITION_PRECEDING = 0x02; +const unsigned short DOCUMENT_POSITION_FOLLOWING = 0x04; +const unsigned short DOCUMENT_POSITION_CONTAINS = 0x08; +const unsigned short DOCUMENT_POSITION_CONTAINED_BY = 0x10; +const unsigned short DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC = 0x20; + +// this class implements nodes, which can have a parent but no children: +class Node : public TreeShared<Node> { + friend class Document; +public: + enum NodeType { + ELEMENT_NODE = 1, + ATTRIBUTE_NODE = 2, + TEXT_NODE = 3, + CDATA_SECTION_NODE = 4, + ENTITY_REFERENCE_NODE = 5, + ENTITY_NODE = 6, + PROCESSING_INSTRUCTION_NODE = 7, + COMMENT_NODE = 8, + DOCUMENT_NODE = 9, + DOCUMENT_TYPE_NODE = 10, + DOCUMENT_FRAGMENT_NODE = 11, + NOTATION_NODE = 12, + XPATH_NAMESPACE_NODE = 13 + }; + + static bool isSupported(const String& feature, const String& version); + + static void startIgnoringLeaks(); + static void stopIgnoringLeaks(); + + enum StyleChange { NoChange, NoInherit, Inherit, Detach, Force }; + static StyleChange diff(RenderStyle*, RenderStyle*); + + Node(Document*, bool isElement = false); + virtual ~Node(); + + // DOM methods & attributes for Node + + bool hasTagName(const QualifiedName& name) const { return virtualHasTagName(name); } + virtual String nodeName() const = 0; + virtual String nodeValue() const; + virtual void setNodeValue(const String&, ExceptionCode&); + virtual NodeType nodeType() const = 0; + Node* parentNode() const { return parent(); } + Node* parentElement() const { return parent(); } // IE extension + Node* previousSibling() const { return m_previous; } + Node* nextSibling() const { return m_next; } + virtual PassRefPtr<NodeList> childNodes(); + Node* firstChild() const { return virtualFirstChild(); } + Node* lastChild() const { return virtualLastChild(); } + virtual bool hasAttributes() const; + virtual NamedAttrMap* attributes() const; + + virtual KURL baseURI() const; + + void getSubresourceURLs(Vector<KURL>&) const; + + // These should all actually return a node, but this is only important for language bindings, + // which will already know and hold a ref on the right node to return. Returning bool allows + // these methods to be more efficient since they don't need to return a ref + virtual bool insertBefore(PassRefPtr<Node> newChild, Node* refChild, ExceptionCode&, bool shouldLazyAttach = false); + virtual bool replaceChild(PassRefPtr<Node> newChild, Node* oldChild, ExceptionCode&, bool shouldLazyAttach = false); + virtual bool removeChild(Node* child, ExceptionCode&); + virtual bool appendChild(PassRefPtr<Node> newChild, ExceptionCode&, bool shouldLazyAttach = false); + + virtual void remove(ExceptionCode&); + bool hasChildNodes() const { return firstChild(); } + virtual PassRefPtr<Node> cloneNode(bool deep) = 0; + virtual const AtomicString& localName() const; + virtual const AtomicString& namespaceURI() const; + virtual const AtomicString& prefix() const; + virtual void setPrefix(const AtomicString&, ExceptionCode&); + void normalize(); + + bool isSameNode(Node* other) const { return this == other; } + bool isEqualNode(Node*) const; + bool isDefaultNamespace(const String& namespaceURI) const; + String lookupPrefix(const String& namespaceURI) const; + String lookupNamespaceURI(const String& prefix) const; + String lookupNamespacePrefix(const String& namespaceURI, const Element* originalElement) const; + + String textContent(bool convertBRsToNewlines = false) const; + void setTextContent(const String&, ExceptionCode&); + + Node* lastDescendant() const; + Node* firstDescendant() const; + + // Other methods (not part of DOM) + + bool isElementNode() const { return m_isElement; } + virtual bool isHTMLElement() const { return false; } + +#if ENABLE(SVG) + virtual +#endif + bool isSVGElement() const { return false; } + + virtual bool isStyledElement() const { return false; } + virtual bool isFrameOwnerElement() const { return false; } + virtual bool isAttributeNode() const { return false; } + virtual bool isTextNode() const { return false; } + virtual bool isCommentNode() const { return false; } + virtual bool isCharacterDataNode() const { return false; } + virtual bool isDocumentNode() const { return false; } + virtual bool isEventTargetNode() const { return false; } + virtual bool isShadowNode() const { return false; } + virtual Node* shadowParentNode() { return 0; } + Node* shadowAncestorNode(); + Node* shadowTreeRootNode(); + bool isInShadowTree(); + + // The node's parent for the purpose of event capture and bubbling. + virtual Node* eventParentNode() { return parentNode(); } + + bool isBlockFlow() const; + bool isBlockFlowOrBlockTable() const; + + // Used by <form> elements to indicate a malformed state of some kind, typically + // used to keep from applying the bottom margin of the form. + virtual bool isMalformed() { return false; } + virtual void setMalformed(bool malformed) { } + + // These low-level calls give the caller responsibility for maintaining the integrity of the tree. + void setPreviousSibling(Node* previous) { m_previous = previous; } + void setNextSibling(Node* next) { m_next = next; } + + // FIXME: These two functions belong in editing -- "atomic node" is an editing concept. + Node* previousNodeConsideringAtomicNodes() const; + Node* nextNodeConsideringAtomicNodes() const; + + /** (Not part of the official DOM) + * Returns the next leaf node. + * + * Using this function delivers leaf nodes as if the whole DOM tree were a linear chain of its leaf nodes. + * @return next leaf node or 0 if there are no more. + */ + Node* nextLeafNode() const; + + /** (Not part of the official DOM) + * Returns the previous leaf node. + * + * Using this function delivers leaf nodes as if the whole DOM tree were a linear chain of its leaf nodes. + * @return previous leaf node or 0 if there are no more. + */ + Node* previousLeafNode() const; + + bool isEditableBlock() const; + + // enclosingBlockFlowElement() is deprecated. Use enclosingBlock instead. + Element* enclosingBlockFlowElement() const; + + Element* enclosingInlineElement() const; + Element* rootEditableElement() const; + + bool inSameContainingBlockFlowElement(Node*); + + // Used by the parser. Checks against the DTD, unlike DOM operations like appendChild(). + // Also does not dispatch DOM mutation events. + // Returns the appropriate container node for future insertions as you parse, or 0 for failure. + virtual ContainerNode* addChild(PassRefPtr<Node>); + + // Called by the parser when this element's close tag is reached, + // signalling that all child tags have been parsed and added. + // This is needed for <applet> and <object> elements, which can't lay themselves out + // until they know all of their nested <param>s. [Radar 3603191, 4040848]. + // Also used for script elements and some SVG elements for similar purposes, + // but making parsing a special case in this respect should be avoided if possible. + virtual void finishParsingChildren() { } + virtual void beginParsingChildren() { } + + // Called by the frame right before dispatching an unloadEvent. [Radar 4532113] + // This is needed for HTMLInputElements to tell the frame that it is done editing + // (sends textFieldDidEndEditing notification) + virtual void aboutToUnload() { } + + // For <link> and <style> elements. + virtual bool sheetLoaded() { return true; } + + bool hasID() const { return m_hasId; } + bool hasClass() const { return m_hasClass; } + bool active() const { return m_active; } + bool inActiveChain() const { return m_inActiveChain; } + bool inDetach() const { return m_inDetach; } + bool hovered() const { return m_hovered; } + bool focused() const { return m_focused; } + bool attached() const { return m_attached; } + void setAttached(bool b = true) { m_attached = b; } + bool changed() const { return m_styleChange != NoStyleChange; } + StyleChangeType styleChangeType() const { return static_cast<StyleChangeType>(m_styleChange); } + bool hasChangedChild() const { return m_hasChangedChild; } + bool isLink() const { return m_isLink; } + void setHasID(bool b = true) { m_hasId = b; } + void setHasClass(bool b = true) { m_hasClass = b; } + void setHasChangedChild( bool b = true ) { m_hasChangedChild = b; } + void setInDocument(bool b = true) { m_inDocument = b; } + void setInActiveChain(bool b = true) { m_inActiveChain = b; } + void setChanged(StyleChangeType changeType = FullStyleChange); + void setIsLink(bool b = true) { m_isLink = b; } + + bool inSubtreeMark() const { return m_inSubtreeMark; } + void setInSubtreeMark(bool b = true) { m_inSubtreeMark = b; } + + void lazyAttach(); + virtual bool canLazyAttach(); + + virtual void setFocus(bool b = true) { m_focused = b; } + virtual void setActive(bool b = true, bool pause=false) { m_active = b; } + virtual void setHovered(bool b = true) { m_hovered = b; } + + virtual short tabIndex() const { return m_tabIndex; } + + /** + * Whether this node can receive the keyboard focus. + */ + virtual bool supportsFocus() const { return isFocusable(); } + virtual bool isFocusable() const; + virtual bool isKeyboardFocusable(KeyboardEvent*) const; + virtual bool isMouseFocusable() const; + + virtual bool isControl() const { return false; } // Eventually the notion of what is a control will be extensible. + virtual bool isEnabled() const { return true; } + virtual bool isChecked() const { return false; } + virtual bool isIndeterminate() const { return false; } + virtual bool isReadOnlyControl() const { return false; } + virtual bool isTextControl() const { return false; } + + virtual bool isContentEditable() const; + virtual bool isContentRichlyEditable() const; + virtual bool shouldUseInputMethod() const; + virtual IntRect getRect() const; + + virtual void recalcStyle(StyleChange = NoChange) { } + + unsigned nodeIndex() const; + + // Returns the DOM ownerDocument attribute. This method never returns NULL, except in the case + // of (1) a Document node or (2) a DocumentType node that is not used with any Document yet. + virtual Document* ownerDocument() const; + + // Returns the document associated with this node. This method never returns NULL, except in the case + // of a DocumentType node that is not used with any Document yet. A Document node returns itself. + Document* document() const + { + ASSERT(this); + ASSERT(m_document || nodeType() == DOCUMENT_TYPE_NODE && !inDocument()); + return m_document.get(); + } + void setDocument(Document*); + + // Returns true if this node is associated with a document and is in its associated document's + // node tree, false otherwise. + bool inDocument() const + { + ASSERT(m_document || !m_inDocument); + return m_inDocument; + } + + bool isReadOnlyNode() const { return nodeType() == ENTITY_REFERENCE_NODE; } + virtual bool childTypeAllowed(NodeType) { return false; } + virtual unsigned childNodeCount() const; + virtual Node* childNode(unsigned index) const; + + /** + * Does a pre-order traversal of the tree to find the node next node after this one. This uses the same order that + * the tags appear in the source file. + * + * @param stayWithin If not null, the traversal will stop once the specified node is reached. This can be used to + * restrict traversal to a particular sub-tree. + * + * @return The next node, in document order + * + * see @ref traversePreviousNode() + */ + Node* traverseNextNode(const Node* stayWithin = 0) const; + + // Like traverseNextNode, but skips children and starts with the next sibling. + Node* traverseNextSibling(const Node* stayWithin = 0) const; + + /** + * Does a reverse pre-order traversal to find the node that comes before the current one in document order + * + * see @ref traverseNextNode() + */ + Node* traversePreviousNode(const Node * stayWithin = 0) const; + + // Like traverseNextNode, but visits parents after their children. + Node* traverseNextNodePostOrder() const; + + // Like traversePreviousNode, but visits parents before their children. + Node* traversePreviousNodePostOrder(const Node *stayWithin = 0) const; + Node* traversePreviousSiblingPostOrder(const Node *stayWithin = 0) const; + + /** + * Finds previous or next editable leaf node. + */ + Node* previousEditable() const; + Node* nextEditable() const; + + RenderObject* renderer() const { return m_renderer; } + RenderObject* nextRenderer(); + RenderObject* previousRenderer(); + void setRenderer(RenderObject* renderer) { m_renderer = renderer; } + + void checkSetPrefix(const AtomicString& prefix, ExceptionCode&); + bool isDescendantOf(const Node*) const; + + // These two methods are mutually exclusive. The former is used to do strict error-checking + // when adding children via the public DOM API (e.g., appendChild()). The latter is called only when parsing, + // to sanity-check against the DTD for error recovery. + void checkAddChild(Node* newChild, ExceptionCode&); // Error-checking when adding via the DOM API + virtual bool childAllowed(Node* newChild); // Error-checking during parsing that checks the DTD + + void checkReplaceChild(Node* newChild, Node* oldChild, ExceptionCode&); + virtual bool canReplaceChild(Node* newChild, Node* oldChild); + + // Used to determine whether range offsets use characters or node indices. + virtual bool offsetInCharacters() const; + // Number of DOM 16-bit units contained in node. Note that rendered text length can be different - e.g. because of + // css-transform:capitalize breaking up precomposed characters and ligatures. + virtual int maxCharacterOffset() const; + + // FIXME: We should try to find a better location for these methods. + virtual bool canSelectAll() const { return false; } + virtual void selectAll() { } + + // Whether or not a selection can be started in this object + virtual bool canStartSelection() const; + + // ----------------------------------------------------------------------------- + // Integration with rendering tree + + /** + * Attaches this node to the rendering tree. This calculates the style to be applied to the node and creates an + * appropriate RenderObject which will be inserted into the tree (except when the style has display: none). This + * makes the node visible in the FrameView. + */ + virtual void attach(); + + /** + * Detaches the node from the rendering tree, making it invisible in the rendered view. This method will remove + * the node's rendering object from the rendering tree and delete it. + */ + virtual void detach(); + + virtual void willRemove(); + void createRendererIfNeeded(); + virtual RenderStyle* styleForRenderer(RenderObject* parent); + virtual bool rendererIsNeeded(RenderStyle*); +#if ENABLE(SVG) + virtual bool childShouldCreateRenderer(Node*) const { return true; } +#endif + virtual RenderObject* createRenderer(RenderArena*, RenderStyle*); + + // Wrapper for nodes that don't have a renderer, but still cache the style (like HTMLOptionElement). + virtual RenderStyle* renderStyle() const; + virtual void setRenderStyle(RenderStyle*); + + virtual RenderStyle* computedStyle(); + + // ----------------------------------------------------------------------------- + // Notification of document structure changes + + /** + * Notifies the node that it has been inserted into the document. This is called during document parsing, and also + * when a node is added through the DOM methods insertBefore(), appendChild() or replaceChild(). Note that this only + * happens when the node becomes part of the document tree, i.e. only when the document is actually an ancestor of + * the node. The call happens _after_ the node has been added to the tree. + * + * This is similar to the DOMNodeInsertedIntoDocument DOM event, but does not require the overhead of event + * dispatching. + */ + virtual void insertedIntoDocument(); + + /** + * Notifies the node that it is no longer part of the document tree, i.e. when the document is no longer an ancestor + * node. + * + * This is similar to the DOMNodeRemovedFromDocument DOM event, but does not require the overhead of event + * dispatching, and is called _after_ the node is removed from the tree. + */ + virtual void removedFromDocument(); + + // These functions are called whenever you are connected or disconnected from a tree. That tree may be the main + // document tree, or it could be another disconnected tree. Override these functions to do any work that depends + // on connectedness to some ancestor (e.g., an ancestor <form> for example). + virtual void insertedIntoTree(bool deep) { } + virtual void removedFromTree(bool deep) { } + + /** + * Notifies the node that it's list of children have changed (either by adding or removing child nodes), or a child + * node that is of the type CDATA_SECTION_NODE, TEXT_NODE or COMMENT_NODE has changed its value. + */ + virtual void childrenChanged(bool changedByParser = false, Node* beforeChange = 0, Node* afterChange = 0, int childCountDelta = 0) {}; + +#ifndef NDEBUG + virtual void formatForDebugger(char* buffer, unsigned length) const; + + void showNode(const char* prefix = "") const; + void showTreeForThis() const; + void showTreeAndMark(const Node* markedNode1, const char* markedLabel1, const Node* markedNode2 = 0, const char* markedLabel2 = 0) const; +#endif + + void registerDynamicNodeList(DynamicNodeList*); + void unregisterDynamicNodeList(DynamicNodeList*); + void notifyNodeListsChildrenChanged(); + void notifyLocalNodeListsChildrenChanged(); + void notifyNodeListsAttributeChanged(); + void notifyLocalNodeListsAttributeChanged(); + + PassRefPtr<NodeList> getElementsByTagName(const String&); + PassRefPtr<NodeList> getElementsByTagNameNS(const String& namespaceURI, const String& localName); + PassRefPtr<NodeList> getElementsByName(const String& elementName); + PassRefPtr<NodeList> getElementsByClassName(const String& classNames); + + PassRefPtr<Element> querySelector(const String& selectors, NSResolver*, ExceptionCode&, ExceptionContext*); + PassRefPtr<NodeList> querySelectorAll(const String& selectors, NSResolver*, ExceptionCode&, ExceptionContext*); + // For non-JS bindings. Silently ignores the JavaScript exception if any. + // FIXME: We should support the NSResolver interface for non-JS bindings. + PassRefPtr<Element> querySelector(const String& selectors, ExceptionCode&); + PassRefPtr<NodeList> querySelectorAll(const String& selectors, ExceptionCode&); + + unsigned short compareDocumentPosition(Node*); + +protected: + virtual void willMoveToNewOwnerDocument() { } + virtual void didMoveToNewOwnerDocument() { } + + virtual void getSubresourceAttributeStrings(Vector<String>&) const { } + void setTabIndexExplicitly(short i) + { + m_tabIndex = i; + m_tabIndexSetExplicitly = true; + } + +private: + DocPtr<Document> m_document; + Node* m_previous; + Node* m_next; + RenderObject* m_renderer; + + OwnPtr<NodeListsNodeData> m_nodeLists; + + short m_tabIndex; + + // make sure we don't use more than 16 bits here -- adding more would increase the size of all Nodes + + unsigned m_styleChange : 2; + bool m_hasId : 1; + bool m_hasClass : 1; + bool m_attached : 1; + bool m_hasChangedChild : 1; + bool m_inDocument : 1; + bool m_isLink : 1; + bool m_focused : 1; + bool m_active : 1; + bool m_hovered : 1; + bool m_inActiveChain : 1; + bool m_inDetach : 1; + bool m_inSubtreeMark : 1; + bool m_tabIndexSetExplicitly : 1; + const bool m_isElement : 1; + // no bits left + + Element* ancestorElement() const; + + void appendTextContent(bool convertBRsToNewlines, StringBuilder&) const; + + virtual Node* virtualFirstChild() const; + virtual Node* virtualLastChild() const; + virtual bool virtualHasTagName(const QualifiedName&) const; +}; + +} //namespace + +#ifndef NDEBUG +// Outside the WebCore namespace for ease of invocation from gdb. +void showTree(const WebCore::Node*); +#endif + +#endif |