/* * 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) 2001 Dirk Mueller (mueller@kde.org) * (C) 2003-2006 Apple Computer, Inc. * (C) 2006 Allan Sandfeld Jensen (kde@carewolf.com) * * 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 "dom/dom_exception.h" #include "misc/htmlattrs.h" #include "misc/htmltags.h" #include "xml/dom_elementimpl.h" #include "xml/dom_textimpl.h" #include "xml/dom2_eventsimpl.h" #include "xml/dom_docimpl.h" #include "xml/dom_nodeimpl.h" #include "xml/dom_restyler.h" #include #include #include "rendering/render_text.h" #include "rendering/render_flow.h" #include "rendering/render_line.h" #include "ecma/kjs_proxy.h" #include "khtmlview.h" #include "khtml_part.h" #include "dom_nodeimpl.h" // from khtml_caret_p.h namespace khtml { void /*KDE_NO_EXPORT*/ mapDOMPosToRenderPos(DOM::NodeImpl *node, long offset, khtml::RenderObject *&r, long &r_ofs, bool &outside, bool &outsideEnd); } using namespace DOM; using namespace khtml; NodeImpl::NodeImpl(DocumentImpl *doc) : m_document(doc), m_previous(0), m_next(0), m_render(0), m_tabIndex( 0 ), m_hasId( false ), m_attached(false), m_closed(false), m_changed( false ), m_hasChangedChild( false ), m_changedAscendentAttribute( false ), m_inDocument( false ), m_hasAnchor( false ), m_specified( false ), m_hovered( false ), m_focused( false ), m_active( false ), m_implicit( false ), m_htmlCompat( false ), m_hasClassList( false ), m_hasClass( false ) { } NodeImpl::~NodeImpl() { if (m_render) detach(); if (m_previous) m_previous->setNextSibling(0); if (m_next) m_next->setPreviousSibling(0); } DOMString NodeImpl::nodeValue() const { return DOMString(); } void NodeImpl::setNodeValue( const DOMString &/*_nodeValue*/, int &/*exceptioncode*/ ) { // by default nodeValue is null, so setting it has no effect // don't throw NO_MODIFICATION_ALLOWED_ERR from here, DOMTS-Core-Level1's hc_nodevalue03 // (createEntityReference().setNodeValue())) says it would be wrong. // This must be done by subclasses instead. } DOMString NodeImpl::nodeName() const { return DOMString(); } unsigned short NodeImpl::nodeType() const { return 0; } NodeListImpl *NodeImpl::childNodes() { return new ChildNodeListImpl(this); } NodeImpl *NodeImpl::firstChild() const { return 0; } NodeImpl *NodeImpl::lastChild() const { return 0; } NodeImpl *NodeImpl::insertBefore( NodeImpl *, NodeImpl *, int &exceptioncode ) { exceptioncode = DOMException::HIERARCHY_REQUEST_ERR; return 0; } void NodeImpl::replaceChild( NodeImpl *, NodeImpl *, int &exceptioncode ) { exceptioncode = DOMException::HIERARCHY_REQUEST_ERR; } void NodeImpl::removeChild( NodeImpl *, int &exceptioncode ) { exceptioncode = DOMException::NOT_FOUND_ERR; } NodeImpl *NodeImpl::appendChild( NodeImpl *, int &exceptioncode ) { exceptioncode = DOMException::HIERARCHY_REQUEST_ERR; return 0; } bool NodeImpl::hasChildNodes( ) const { return false; } void NodeImpl::normalize () { // ### normalize attributes? (when we store attributes using child nodes) int exceptioncode = 0; NodeImpl *child = firstChild(); // Recursively go through the subtree beneath us, normalizing all nodes. In the case // where there are two adjacent text nodes, they are merged together while (child) { NodeImpl *nextChild = child->nextSibling(); if (nextChild && child->nodeType() == Node::TEXT_NODE && nextChild->nodeType() == Node::TEXT_NODE) { // Current child and the next one are both text nodes... merge them TextImpl *currentText = static_cast(child); TextImpl *nextText = static_cast(nextChild); currentText->appendData(nextText->data(),exceptioncode); if (exceptioncode) return; removeChild(nextChild,exceptioncode); if (exceptioncode) return; } else { child->normalize(); child = nextChild; } } } DOMString NodeImpl::prefix() const { // For nodes other than elements and attributes, the prefix is always null return DOMString(); } DOMString NodeImpl::namespaceURI() const { return DOMString(); } void NodeImpl::setPrefix(const DOMString &/*_prefix*/, int &exceptioncode ) { // 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 exceptioncode = DOMException::NAMESPACE_ERR; } DOMString NodeImpl::localName() const { return DOMString(); } void NodeImpl::setFirstChild(NodeImpl *) { } void NodeImpl::setLastChild(NodeImpl *) { } NodeImpl *NodeImpl::addChild(NodeImpl *) { return 0; } void NodeImpl::getCaret(int offset, bool override, int &_x, int &_y, int &width, int &height) { if (m_render) { RenderObject *r; long r_ofs; bool outside, outsideEnd; #if 0 kdDebug(6200) << "getCaret: node " << this << " " << nodeName().string() << " offset: " << offset << endl; #endif mapDOMPosToRenderPos(this, offset, r, r_ofs, outside, outsideEnd); #if 0 kdDebug(6200) << "getCaret: r " << r << " " << (r?r->renderName():TQString::null) << " r_ofs: " << r_ofs << " outside " << outside << " outsideEnd " << outsideEnd << endl; #endif if (r) { r->caretPos(r_ofs, override*RenderObject::CFOverride + outside*RenderObject::CFOutside + outsideEnd*RenderObject::CFOutsideEnd, _x, _y, width, height); } else _x = _y = height = -1, width = 1; } else _x = _y = height = -1, width = 1; } TQRect NodeImpl::getRect() const { int _x, _y; if(m_render && m_render->absolutePosition(_x, _y)) return TQRect( _x + m_render->inlineXPos(), _y + m_render->inlineYPos(), m_render->width(), m_render->height() + renderer()->borderTopExtra() + renderer()->borderBottomExtra() ); return TQRect(); } void NodeImpl::setChanged(bool b) { if (b && !attached()) // changed compared to what? return; m_changed = b; if ( b ) { NodeImpl *p = parentNode(); while ( p ) { p->setHasChangedChild( true ); p = p->parentNode(); } getDocument()->setDocumentChanged(); } } bool NodeImpl::isInline() const { if (m_render) return m_render->style()->display() == khtml::INLINE; return !isElementNode(); } unsigned long NodeImpl::nodeIndex() const { NodeImpl *_tempNode = previousSibling(); unsigned long count=0; for( count=0; _tempNode; count++ ) _tempNode = _tempNode->previousSibling(); return count; } void NodeImpl::addEventListener(int id, EventListener *listener, const bool useCapture) { switch (id) { case EventImpl::DOMSUBTREEMODIFIED_EVENT: getDocument()->addListenerType(DocumentImpl::DOMSUBTREEMODIFIED_LISTENER); break; case EventImpl::DOMNODEINSERTED_EVENT: getDocument()->addListenerType(DocumentImpl::DOMNODEINSERTED_LISTENER); break; case EventImpl::DOMNODEREMOVED_EVENT: getDocument()->addListenerType(DocumentImpl::DOMNODEREMOVED_LISTENER); break; case EventImpl::DOMNODEREMOVEDFROMDOCUMENT_EVENT: getDocument()->addListenerType(DocumentImpl::DOMNODEREMOVEDFROMDOCUMENT_LISTENER); break; case EventImpl::DOMNODEINSERTEDINTODOCUMENT_EVENT: getDocument()->addListenerType(DocumentImpl::DOMNODEINSERTEDINTODOCUMENT_LISTENER); break; case EventImpl::DOMATTRMODIFIED_EVENT: getDocument()->addListenerType(DocumentImpl::DOMATTRMODIFIED_LISTENER); break; case EventImpl::DOMCHARACTERDATAMODIFIED_EVENT: getDocument()->addListenerType(DocumentImpl::DOMCHARACTERDATAMODIFIED_LISTENER); break; default: break; } m_regdListeners.addEventListener(id, listener, useCapture); } void NodeImpl::removeEventListener(int id, EventListener *listener, bool useCapture) { m_regdListeners.removeEventListener(id, listener, useCapture); } void NodeImpl::setHTMLEventListener(int id, EventListener *listener) { m_regdListeners.setHTMLEventListener(id, listener); } EventListener *NodeImpl::getHTMLEventListener(int id) { return m_regdListeners.getHTMLEventListener(id); } void NodeImpl::dispatchEvent(EventImpl *evt, int &exceptioncode, bool tempEvent) { evt->setTarget(this); // Since event handling code could cause this object to be deleted, grab a reference to the view now KHTMLView *view = getDocument()->view(); dispatchGenericEvent( evt, exceptioncode ); // If tempEvent is true, this means that the DOM implementation will not be storing a reference to the event, i.e. // there is no way to retrieve it from javascript if a script does not already have a reference to it in a variable. // So there is no need for the interpreter to keep the event in its cache if (tempEvent && view && view->part() && view->part()->jScript()) view->part()->jScript()->finishedWithEvent(evt); } void NodeImpl::dispatchGenericEvent( EventImpl *evt, int &/*exceptioncode */) { // ### check that type specified // work out what nodes to send event to TQPtrList nodeChain; NodeImpl *n; for (n = this; n; n = n->parentNode()) { n->ref(); nodeChain.prepend(n); } // trigger any capturing event handlers on our way down evt->setEventPhase(Event::CAPTURING_PHASE); TQPtrListIterator it(nodeChain); for (; it.current() && it.current() != this && !evt->propagationStopped(); ++it) { evt->setCurrentTarget(it.current()); it.current()->handleLocalEvents(evt,true); } // dispatch to the actual target node it.toLast(); NodeImpl* propagationSentinel = 0; if (!evt->propagationStopped()) { evt->setEventPhase(Event::AT_TARGET); evt->setCurrentTarget(it.current()); it.current()->handleLocalEvents(evt, true); if (!evt->propagationStopped()) it.current()->handleLocalEvents(evt,false); else propagationSentinel = it.current(); } --it; if (evt->bubbles()) { evt->setEventPhase(Event::BUBBLING_PHASE); for (; it.current() && !evt->propagationStopped(); --it) { if (evt->propagationStopped()) propagationSentinel = it.current(); evt->setCurrentTarget(it.current()); it.current()->handleLocalEvents(evt,false); } // now we call all default event handlers (this is not part of DOM - it is internal to khtml) evt->setCurrentTarget(0); evt->setEventPhase(0); // I guess this is correct, the spec does not seem to say for (it.toLast(); it.current() && it.current() != propagationSentinel && !evt->defaultPrevented() && !evt->defaultHandled(); --it) it.current()->defaultEventHandler(evt); if (evt->id() == EventImpl::CLICK_EVENT && !evt->defaultPrevented() && static_cast( evt )->button() == 0) // LMB click dispatchUIEvent(EventImpl::DOMACTIVATE_EVENT, static_cast(evt)->detail()); } // copy this over into a local variable, as the following deref() calls might cause this to be deleted. DocumentImpl *doc = m_document.get(); doc->ref(); // deref all nodes in chain it.toFirst(); for (; it.current(); ++it) it.current()->deref(); // this may delete us DocumentImpl::updateDocumentsRendering(); doc->deref(); } bool NodeImpl::dispatchHTMLEvent(int _id, bool canBubbleArg, bool cancelableArg) { int exceptioncode = 0; EventImpl* const evt = new EventImpl(static_cast(_id),canBubbleArg,cancelableArg); evt->ref(); dispatchEvent(evt,exceptioncode,true); bool ret = !evt->defaultPrevented(); evt->deref(); return ret; } void NodeImpl::dispatchWindowEvent(int _id, bool canBubbleArg, bool cancelableArg) { int exceptioncode = 0; EventImpl* const evt = new EventImpl(static_cast(_id),canBubbleArg,cancelableArg); evt->setTarget( 0 ); evt->ref(); DocumentImpl *doc = getDocument(); doc->ref(); dispatchGenericEvent( evt, exceptioncode ); if (!evt->defaultPrevented() && doc) doc->defaultEventHandler(evt); if (_id == EventImpl::LOAD_EVENT && !evt->propagationStopped() && doc) { // For onload events, send them to the enclosing frame only. // This is a DOM extension and is independent of bubbling/capturing rules of // the DOM. You send the event only to the enclosing frame. It does not // bubble through the parent document. DOM::ElementImpl* elt = doc->ownerElement(); if (elt && (elt->getDocument()->domain().isNull() || elt->getDocument()->domain() == doc->domain())) { // We also do a security check, since we don't want to allow the enclosing // iframe to see loads of child documents in other domains. evt->setCurrentTarget(elt); // Capturing first. elt->handleLocalEvents(evt,true); // Bubbling second. if (!evt->propagationStopped()) elt->handleLocalEvents(evt,false); } } doc->deref(); evt->deref(); } void NodeImpl::dispatchMouseEvent(TQMouseEvent *_mouse, int overrideId, int overrideDetail) { bool cancelable = true; int detail = overrideDetail; // defaults to 0 EventImpl::EventId evtId = EventImpl::UNKNOWN_EVENT; if (overrideId) { evtId = static_cast(overrideId); } else { switch (_mouse->type()) { case TQEvent::MouseButtonPress: evtId = EventImpl::MOUSEDOWN_EVENT; break; case TQEvent::MouseButtonRelease: evtId = EventImpl::MOUSEUP_EVENT; break; case TQEvent::MouseButtonDblClick: evtId = EventImpl::CLICK_EVENT; detail = 1; // ### support for multiple double clicks break; case TQEvent::MouseMove: evtId = EventImpl::MOUSEMOVE_EVENT; cancelable = false; break; default: break; } } if (evtId == EventImpl::UNKNOWN_EVENT) return; // shouldn't happen int exceptioncode = 0; int pageX = _mouse->x(); int pageY = _mouse->y(); int clientX = pageX; int clientY = pageY; if ( getDocument()->view() ) getDocument()->view()->viewportToContents( clientX, clientY, pageX, pageY ); int screenX = _mouse->globalX(); int screenY = _mouse->globalY(); int button = -1; switch (_mouse->button()) { case Qt::LeftButton: button = 0; break; case Qt::MidButton: button = 1; break; case Qt::RightButton: button = 2; break; default: break; } bool ctrlKey = (_mouse->state() & TQt::ControlButton); bool altKey = (_mouse->state() & TQt::AltButton); bool shiftKey = (_mouse->state() & TQt::ShiftButton); bool metaKey = false; // ### qt support? EventImpl* const evt = new MouseEventImpl(evtId,true,cancelable,getDocument()->defaultView(), detail,screenX,screenY,clientX,clientY,pageX,pageY,ctrlKey,altKey,shiftKey,metaKey, button,0); evt->ref(); dispatchEvent(evt,exceptioncode,true); evt->deref(); } void NodeImpl::dispatchUIEvent(int _id, int detail) { assert (!( (_id != EventImpl::DOMFOCUSIN_EVENT && _id != EventImpl::DOMFOCUSOUT_EVENT && _id != EventImpl::DOMACTIVATE_EVENT))); bool cancelable = false; if (_id == EventImpl::DOMACTIVATE_EVENT) cancelable = true; int exceptioncode = 0; UIEventImpl* const evt = new UIEventImpl(static_cast(_id),true, cancelable,getDocument()->defaultView(),detail); evt->ref(); dispatchEvent(evt,exceptioncode,true); evt->deref(); } void NodeImpl::dispatchSubtreeModifiedEvent() { childrenChanged(); getDocument()->incDOMTreeVersion(); if (!getDocument()->hasListenerType(DocumentImpl::DOMSUBTREEMODIFIED_LISTENER)) return; int exceptioncode = 0; MutationEventImpl* const evt = new MutationEventImpl(EventImpl::DOMSUBTREEMODIFIED_EVENT,true, false,0,DOMString(),DOMString(),DOMString(),0); evt->ref(); dispatchEvent(evt,exceptioncode,true); evt->deref(); } bool NodeImpl::dispatchKeyEvent(TQKeyEvent *key, bool keypress) { int exceptioncode = 0; //kdDebug(6010) << "DOM::NodeImpl: dispatching keyboard event" << endl; EventImpl* keyEventImpl; if (keypress) keyEventImpl = new TextEventImpl(key, getDocument()->defaultView()); else keyEventImpl = new KeyboardEventImpl(key, getDocument()->defaultView()); keyEventImpl->ref(); dispatchEvent(keyEventImpl,exceptioncode,true); bool r = keyEventImpl->defaultHandled() || keyEventImpl->defaultPrevented(); keyEventImpl->deref(); return r; } void NodeImpl::handleLocalEvents(EventImpl *evt, bool useCapture) { if (!m_regdListeners.listeners) return; Event ev = evt; // removeEventListener (e.g. called from a JS event listener) might // tqinvalidate the item after the current iterator (which "it" is pointing to). // So we make a copy of the list. TQValueList listeners = *m_regdListeners.listeners; TQValueList::iterator it; for (it = listeners.begin(); it != listeners.end(); ++it) { //Check whether this got removed...KDE4: use Java-style iterators if (!m_regdListeners.stillContainsListener(*it)) continue; RegisteredEventListener& current = (*it); if (current.id == evt->id() && current.useCapture == useCapture) current.listener->handleEvent(ev); // ECMA legacy hack if (current.useCapture == useCapture && evt->id() == EventImpl::CLICK_EVENT) { MouseEventImpl* me = static_cast(evt); if (me->button() == 0) { // To find whether to call onclick or ondblclick, we can't // * use me->detail(), it's 2 when clicking twice w/o moving, even very slowly // * use me->qEvent(), it's not available when using initMouseEvent/dispatchEvent // So we currently store a bool in MouseEventImpl. If anyone needs to trigger // dblclicks from the DOM API, we'll need a timer here (well in the doc). if ( ( !me->isDoubleClick() && current.id == EventImpl::KHTML_ECMA_CLICK_EVENT) || ( me->isDoubleClick() && current.id == EventImpl::KHTML_ECMA_DBLCLICK_EVENT) ) current.listener->handleEvent(ev); } } } } void NodeImpl::defaultEventHandler(EventImpl *) { } unsigned long NodeImpl::childNodeCount() { return 0; } NodeImpl *NodeImpl::childNode(unsigned long /*index*/) { return 0; } NodeImpl *NodeImpl::traverseNextNode(NodeImpl *stayWithin) const { if (firstChild() || stayWithin == this) return firstChild(); else if (nextSibling()) return nextSibling(); else { const NodeImpl *n = this; while (n && !n->nextSibling() && (!stayWithin || n->parentNode() != stayWithin)) n = n->parentNode(); if (n) return n->nextSibling(); } return 0; } NodeImpl *NodeImpl::traversePreviousNode() const { if (previousSibling()) { NodeImpl *n = previousSibling(); while (n->lastChild()) n = n->lastChild(); return n; } else if (parentNode()) { return parentNode(); } else { return 0; } } void NodeImpl::checkSetPrefix(const DOMString &_prefix, int &exceptioncode) { // Perform error checking as required by spec for setting Node.prefix. Used by // ElementImpl::setPrefix() and AttrImpl::setPrefix() // INVALID_CHARACTER_ERR: Raised if the specified prefix contains an illegal character. if (!Element::khtmlValidPrefix(_prefix)) { exceptioncode = DOMException::INVALID_CHARACTER_ERR; return; } // NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly. if (isReadOnly()) { exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR; return; } // NAMESPACE_ERR: - Raised if the specified prefix is malformed // - 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 (Element::khtmlMalformedPrefix(_prefix) || (namespacePart(id()) == defaultNamespace && id() > ID_LAST_TAG) || (_prefix == "xml" && namespaceURI() != "http://www.w3.org/XML/1998/namespace")) { exceptioncode = DOMException::NAMESPACE_ERR; return; } } void NodeImpl::checkAddChild(NodeImpl *newChild, int &exceptioncode) { // 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) { exceptioncode = DOMException::NOT_FOUND_ERR; return; } // NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly if (isReadOnly()) { exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR; return; } // 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->getDocument() != getDocument()) { exceptioncode = DOMException::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 (isAncestor(newChild)) { exceptioncode = DOMException::HIERARCHY_REQUEST_ERR; return; } // check node allowed if (newChild->nodeType() == Node::DOCUMENT_FRAGMENT_NODE) { // newChild is a DocumentFragment... check all its children instead of newChild itself NodeImpl *child; for (child = newChild->firstChild(); child; child = child->nextSibling()) { if (!childTypeAllowed(child->nodeType())) { exceptioncode = DOMException::HIERARCHY_REQUEST_ERR; return; } } } else { // newChild is not a DocumentFragment... check if it's allowed directly if(!childTypeAllowed(newChild->nodeType())) { exceptioncode = DOMException::HIERARCHY_REQUEST_ERR; return; } } } bool NodeImpl::isAncestor( NodeImpl *other ) { // Return true if other is the same as this node or an ancestor of it, otherwise false NodeImpl *n; for (n = this; n; n = n->parentNode()) { if (n == other) return true; } return false; } bool NodeImpl::childAllowed( NodeImpl *newChild ) { return childTypeAllowed(newChild->nodeType()); } NodeImpl::StyleChange NodeImpl::diff( khtml::RenderStyle *s1, khtml::RenderStyle *s2 ) { // This method won't work when a style contains noninherited properties with "inherit" value. StyleChange ch = NoInherit; EDisplay display1 = s1 ? s1->display() : NONE; EDisplay display2 = s2 ? s2->display() : NONE; EPosition position1 = s1 ? s1->position() : STATIC; EPosition position2 = s2 ? s2->position() : STATIC; if (display1 != display2 || position1 != position2) ch = Detach; else if ( !s1 || !s2 ) ch = Inherit; else if ( *s1 == *s2 ) ch = NoChange; else if (s1->useNormalContent() != s2->useNormalContent()) ch = Detach; // when we add generated content all children must be detached else if ( s1->inheritedNotEqual( s2 ) ) ch = Inherit; // Because the first-letter implementation is so f..ked up, the easiest way // to update first-letter is to remove the entire node and readd it. if (ch < Detach && pseudoDiff(s1, s2, khtml::RenderStyle::FIRST_LETTER)) ch = Detach; // If the other pseudoStyles have changed, we want to return NoInherit if (ch == NoChange && pseudoDiff(s1, s2, khtml::RenderStyle::BEFORE)) ch = NoInherit; if (ch == NoChange && pseudoDiff(s1, s2, khtml::RenderStyle::AFTER)) ch = NoInherit; if (ch == NoChange && pseudoDiff(s1, s2, khtml::RenderStyle::MARKER)) ch = NoInherit; if (ch == NoChange && pseudoDiff(s1, s2, khtml::RenderStyle::SELECTION)) ch = NoInherit; if (ch == NoChange && pseudoDiff(s1, s2, khtml::RenderStyle::FIRST_LINE)) ch = NoInherit; return ch; } bool NodeImpl::pseudoDiff( khtml::RenderStyle *s1, khtml::RenderStyle *s2, unsigned int pid) { khtml::RenderStyle *ps1 = s1 ? s1->getPseudoStyle((khtml::RenderStyle::PseudoId)pid) : 0; khtml::RenderStyle *ps2 = s2 ? s2->getPseudoStyle((khtml::RenderStyle::PseudoId)pid) : 0; if (ps1 == ps2) return false; else if (ps1 && ps2) { if (*ps1 == *ps2) return false; else return true; } else return true; } void NodeImpl::close() { if (m_render) m_render->close(); m_closed = true; } void NodeImpl::attach() { assert(!attached()); assert(!m_render || (m_render->style() && m_render->parent())); if (m_render) // set states to match node { if (closed()) m_render->close(); if (hovered()) m_render->setMouseInside(); } getDocument()->incDOMTreeVersion(); m_attached = true; } void NodeImpl::detach() { // assert(m_attached); if ( m_render ) m_render->detach(); m_render = 0; getDocument()->incDOMTreeVersion(); m_attached = false; } bool NodeImpl::maintainsState() { return false; } TQString NodeImpl::state() { return TQString::null; } void NodeImpl::restoreState(const TQString &/*state*/) { } void NodeImpl::insertedIntoDocument() { setInDocument(true); } void NodeImpl::removedFromDocument() { setInDocument(false); } void NodeImpl::childrenChanged() { if (parentNode()) parentNode()->childrenChanged(); } bool NodeImpl::isReadOnly() { // Entity & Entity Reference nodes and their descendants are read-only NodeImpl *n = this; while (n) { if (n->nodeType() == Node::ENTITY_NODE || n->nodeType() == Node::ENTITY_REFERENCE_NODE) return true; n = n->parentNode(); } return false; } RenderObject * NodeImpl::previousRenderer() { for (NodeImpl *n = previousSibling(); n; n = n->previousSibling()) { if (n->renderer()) return n->renderer(); } return 0; } RenderObject * NodeImpl::nextRenderer() { for (NodeImpl *n = nextSibling(); n; n = n->nextSibling()) { if (n->renderer()) return n->renderer(); } return 0; } void NodeImpl::createRendererIfNeeded() { #ifdef APPLE_CHANGES if (!getDocument()->shouldCreateRenderers()) return; #endif assert(!m_render); NodeImpl *parent = parentNode(); assert(parent); RenderObject *parentRenderer = parent->renderer(); if (parentRenderer && parentRenderer->childAllowed()) { RenderStyle *style = styleForRenderer(parentRenderer); style->ref(); if (rendererIsNeeded(style)) { m_render = createRenderer(getDocument()->renderArena(), style); m_render->setStyle(style); parentRenderer->addChild(m_render, nextRenderer()); } style->deref(); } } RenderStyle *NodeImpl::styleForRenderer(RenderObject *parent) { return parent->style(); } bool NodeImpl::rendererIsNeeded(RenderStyle *style) { return (getDocument()->documentElement() == this) || (style->display() != NONE); } RenderObject *NodeImpl::createRenderer(RenderArena* /*arena*/, RenderStyle* /*style*/) { assert(false); return 0; } bool NodeImpl::contentEditable() const { RenderObject *r = renderer(); if (!r || !r->style()) return false; return r->style()->userInput() == UI_ENABLED; } long NodeImpl::minOffset() const { // Arrgh! You'd think *every* offset starts at zero, but loo, // therefore we need this method return renderer() ? renderer()->minOffset() : 0; } long NodeImpl::maxOffset() const { return const_cast(this)->childNodeCount(); // return renderer() ? renderer()->maxOffset() : 1; } DOMStringImpl* NodeImpl::textContent() const { TQString out; for (NodeImpl *child = firstChild(); child != 0; child = child->nextSibling()) { short type = child->nodeType(); if (type != Node::COMMENT_NODE && type != Node::PROCESSING_INSTRUCTION_NODE) { DOMStringImpl* kidText = child->textContent(); if (kidText) out += TQConstString(kidText->s, kidText->l).string(); delete kidText; } } return new DOMStringImpl(out.tqunicode(), out.length()); } //------------------------------------------------------------------------- NodeBaseImpl::~NodeBaseImpl() { //kdDebug( 6020 ) << "NodeBaseImpl destructor" << endl; // we have to tell all children, that the parent has died... NodeImpl *n; NodeImpl *next; for( n = _first; n != 0; n = next ) { next = n->nextSibling(); n->setPreviousSibling(0); n->setNextSibling(0); n->setParent(0); if ( !n->refCount() ) delete n; } } NodeImpl *NodeBaseImpl::firstChild() const { return _first; } NodeImpl *NodeBaseImpl::lastChild() const { return _last; } NodeImpl *NodeBaseImpl::insertBefore ( NodeImpl *newChild, NodeImpl *refChild, int &exceptioncode ) { exceptioncode = 0; // insertBefore(...,null) is equivalent to appendChild() if(!refChild) return appendChild(newChild, exceptioncode); // Make sure adding the new child is ok checkAddChild(newChild, exceptioncode); if (exceptioncode) return 0; // NOT_FOUND_ERR: Raised if refChild is not a child of this node if (refChild->parentNode() != this) { exceptioncode = DOMException::NOT_FOUND_ERR; return 0; } bool isFragment = newChild->nodeType() == Node::DOCUMENT_FRAGMENT_NODE; // If newChild is a DocumentFragment with no children.... there's nothing to do. // Just return the document fragment if (isFragment && !newChild->firstChild()) return newChild; // Now actually add the child(ren) NodeImpl *nextChild; NodeImpl *child = isFragment ? newChild->firstChild() : newChild; NodeImpl *prev = refChild->previousSibling(); if ( prev == newChild || refChild == newChild ) // nothing to do return newChild; while (child) { nextChild = isFragment ? child->nextSibling() : 0; // If child is already present in the tree, first remove it NodeImpl *newParent = child->parentNode(); //...guard it in case we need to move it.. SharedPtr guard(child); if(newParent) newParent->removeChild( child, exceptioncode ); if ( exceptioncode ) return 0; // Add child in the correct position if (prev) prev->setNextSibling(child); else _first = child; refChild->setPreviousSibling(child); child->setParent(this); child->setPreviousSibling(prev); child->setNextSibling(refChild); // Add child to the rendering tree // ### should we detach() it first if it's already attached? if (attached() && !child->attached()) child->attach(); // Dispatch the mutation events dispatchChildInsertedEvents(child,exceptioncode); prev = child; child = nextChild; } structureChanged(); // ### set style in case it's attached dispatchSubtreeModifiedEvent(); return newChild; } void NodeBaseImpl::replaceChild ( NodeImpl *newChild, NodeImpl *oldChild, int &exceptioncode ) { exceptioncode = 0; if ( oldChild == newChild ) // nothing to do return; // Make sure adding the new child is ok checkAddChild(newChild, exceptioncode); if (exceptioncode) return; // NOT_FOUND_ERR: Raised if oldChild is not a child of this node. if (!oldChild || oldChild->parentNode() != this) { exceptioncode = DOMException::NOT_FOUND_ERR; return; } bool isFragment = newChild->nodeType() == Node::DOCUMENT_FRAGMENT_NODE; NodeImpl *nextChild; NodeImpl *child = isFragment ? newChild->firstChild() : newChild; // Remove the old child NodeImpl *prev = oldChild->previousSibling(); NodeImpl *next = oldChild->nextSibling(); removeChild(oldChild, exceptioncode); if (exceptioncode) return; // Add the new child(ren) while (child) { nextChild = isFragment ? child->nextSibling() : 0; // If child is already present in the tree, first remove it NodeImpl *newParent = child->parentNode(); if ( child == next ) next = child->nextSibling(); if ( child == prev ) prev = child->previousSibling(); //...guard it in case we need to move it.. SharedPtr guard(child); if(newParent) newParent->removeChild( child, exceptioncode ); if (exceptioncode) return; // Add child in the correct position if (prev) prev->setNextSibling(child); if (next) next->setPreviousSibling(child); if(!prev) _first = child; if(!next) _last = child; child->setParent(this); child->setPreviousSibling(prev); child->setNextSibling(next); // Add child to the rendering tree // ### should we detach() it first if it's already attached? if (attached() && !child->attached()) child->attach(); // Dispatch the mutation events dispatchChildInsertedEvents(child,exceptioncode); prev = child; child = nextChild; } structureChanged(); // ### set style in case it's attached dispatchSubtreeModifiedEvent(); return; } void NodeBaseImpl::removeChild ( NodeImpl *oldChild, int &exceptioncode ) { exceptioncode = 0; // NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly. if (isReadOnly()) { exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR; return; } // NOT_FOUND_ERR: Raised if oldChild is not a child of this node. if (!oldChild || oldChild->parentNode() != this) { exceptioncode = DOMException::NOT_FOUND_ERR; return; } dispatchChildRemovalEvents(oldChild,exceptioncode); if (exceptioncode) return; SharedPtr memManage(oldChild); //Make sure to free if needed // Remove from rendering tree if (oldChild->attached()) oldChild->detach(); // Remove the child NodeImpl *prev, *next; prev = oldChild->previousSibling(); next = oldChild->nextSibling(); if(next) next->setPreviousSibling(prev); if(prev) prev->setNextSibling(next); if(_first == oldChild) _first = next; if(_last == oldChild) _last = prev; oldChild->setPreviousSibling(0); oldChild->setNextSibling(0); oldChild->setParent(0); structureChanged(); // Dispatch post-removal mutation events dispatchSubtreeModifiedEvent(); NodeImpl *p = this; while (p->parentNode()) p = p->parentNode(); if (p->nodeType() == Node::DOCUMENT_NODE) { for (NodeImpl *c = oldChild; c; c = c->traverseNextNode(oldChild)) c->removedFromDocument(); } } void NodeBaseImpl::removeChildren() { bool inDoc = inDocument(); NodeImpl *n, *next; for( n = _first, _first = 0; n; n = next ) { next = n->nextSibling(); if (n->attached()) n->detach(); n->setPreviousSibling(0); n->setNextSibling(0); n->setParent(0); if ( inDoc ) for ( NodeImpl* c = n; c; c = c->traverseNextNode( n ) ) c->removedFromDocument(); if( !n->refCount() ) delete n; } _last = 0; } NodeImpl *NodeBaseImpl::appendChild ( NodeImpl *newChild, int &exceptioncode ) { exceptioncode = 0; // Make sure adding the new child is ok checkAddChild(newChild, exceptioncode); if (exceptioncode) return 0; if ( newChild == _last ) // nothing to do return newChild; bool isFragment = newChild->nodeType() == Node::DOCUMENT_FRAGMENT_NODE; // If newChild is a DocumentFragment with no children.... there's nothing to do. // Just return the document fragment if (isFragment && !newChild->firstChild()) return newChild; // Now actually add the child(ren) NodeImpl *nextChild; NodeImpl *child = isFragment ? newChild->firstChild() : newChild; while (child) { nextChild = isFragment ? child->nextSibling() : 0; // If child is already present in the tree, first remove it NodeImpl *oldParent = child->parentNode(); SharedPtr guard(child); //Guard in case we move it if(oldParent) { oldParent->removeChild( child, exceptioncode ); if (exceptioncode) return 0; } // Append child to the end of the list child->setParent(this); if(_last) { child->setPreviousSibling(_last); _last->setNextSibling(child); _last = child; } else { _first = _last = child; } // Add child to the rendering tree // ### should we detach() it first if it's already attached? if (attached() && !child->attached()) child->attach(); // Dispatch the mutation events dispatchChildInsertedEvents(child,exceptioncode); child = nextChild; } backwardsStructureChanged(); // ### set style in case it's attached dispatchSubtreeModifiedEvent(); return newChild; } bool NodeBaseImpl::hasChildNodes ( ) const { return _first != 0; } // not part of the DOM void NodeBaseImpl::setFirstChild(NodeImpl *child) { _first = child; } void NodeBaseImpl::setLastChild(NodeImpl *child) { _last = child; } // check for same source document: bool NodeBaseImpl::checkSameDocument( NodeImpl *newChild, int &exceptioncode ) { exceptioncode = 0; DocumentImpl *ownerDocThis = getDocument(); DocumentImpl *ownerDocNew = newChild->getDocument(); if(ownerDocThis != ownerDocNew) { kdDebug(6010)<< "not same document, newChild = " << newChild << "document = " << getDocument() << endl; exceptioncode = DOMException::WRONG_DOCUMENT_ERR; return true; } return false; } // check for being child: bool NodeBaseImpl::checkIsChild( NodeImpl *oldChild, int &exceptioncode ) { if(!oldChild || oldChild->parentNode() != this) { exceptioncode = DOMException::NOT_FOUND_ERR; return true; } return false; } NodeImpl *NodeBaseImpl::addChild(NodeImpl *newChild) { // do not add applyChanges here! This function is only used during parsing // short check for consistency with DTD if(getDocument()->isHTMLDocument() && !childAllowed(newChild)) { //kdDebug( 6020 ) << "AddChild failed! id=" << id() << ", child->id=" << newChild->id() << endl; return 0; } // just add it... newChild->setParent(this); if(_last) { newChild->setPreviousSibling(_last); _last->setNextSibling(newChild); _last = newChild; } else { _first = _last = newChild; } if (inDocument()) newChild->insertedIntoDocument(); childrenChanged(); if(newChild->nodeType() == Node::ELEMENT_NODE) return newChild; return this; } void NodeBaseImpl::attach() { NodeImpl *child = _first; while(child != 0) { child->attach(); child = child->nextSibling(); } NodeImpl::attach(); } void NodeBaseImpl::detach() { NodeImpl *child = _first; while(child != 0) { NodeImpl* prev = child; child = child->nextSibling(); prev->detach(); } NodeImpl::detach(); } void NodeBaseImpl::cloneChildNodes(NodeImpl *clone) { int exceptioncode = 0; NodeImpl *n; for(n = firstChild(); n && !exceptioncode; n = n->nextSibling()) { clone->appendChild(n->cloneNode(true),exceptioncode); } } // I don't like this way of implementing the method, but I didn't find any // other way. Lars bool NodeBaseImpl::getUpperLeftCorner(int &xPos, int &yPos) const { if (!m_render) return false; RenderObject *o = m_render; xPos = yPos = 0; if ( !o->isInline() || o->isReplaced() ) { o->absolutePosition( xPos, yPos ); return true; } // find the next text/image child, to get a position while(o) { if(o->firstChild()) o = o->firstChild(); else if(o->nextSibling()) o = o->nextSibling(); else { RenderObject *next = 0; while(!next) { o = o->parent(); if(!o) return false; next = o->nextSibling(); } o = next; } if((o->isText() && !o->isBR()) || o->isReplaced()) { o->container()->absolutePosition( xPos, yPos ); if (o->isText()) { xPos += o->inlineXPos(); yPos += o->inlineYPos(); } else { xPos += o->xPos(); yPos += o->yPos(); } return true; } } return true; } bool NodeBaseImpl::getLowerRightCorner(int &xPos, int &yPos) const { if (!m_render) return false; RenderObject *o = m_render; xPos = yPos = 0; if (!o->isInline() || o->isReplaced()) { o->absolutePosition( xPos, yPos ); xPos += o->width(); yPos += o->height() + o->borderTopExtra() + o->borderBottomExtra(); return true; } // find the last text/image child, to get a position while(o) { if(o->lastChild()) o = o->lastChild(); else if(o->previousSibling()) o = o->previousSibling(); else { RenderObject *prev = 0; while(!prev) { o = o->parent(); if(!o) return false; prev = o->previousSibling(); } o = prev; } if((o->isText() && !o->isBR()) || o->isReplaced()) { o->container()->absolutePosition(xPos, yPos); if (o->isText()) { xPos += o->inlineXPos() + o->width(); yPos += o->inlineYPos() + o->height(); } else { xPos += o->xPos() + o->width(); yPos += o->yPos() + o->height(); } return true; } } return true; } void NodeBaseImpl::setFocus(bool received) { if (m_focused == received) return; NodeImpl::setFocus(received); // note that we need to recalc the style setChanged(); // *:focus is a default style, so we just assume personal dependency if (isElementNode()) { getDocument()->dynamicDomRestyler().restyleDepedent(static_cast(this), OtherStateDependency); } } void NodeBaseImpl::setActive(bool down) { if (down == active()) return; NodeImpl::setActive(down); // note that we need to recalc the style if (isElementNode()) getDocument()->dynamicDomRestyler().restyleDepedent(static_cast(this), ActiveDependency); } void NodeBaseImpl::setHovered(bool hover) { if (hover == hovered()) return; NodeImpl::setHovered(hover); // note that we need to recalc the style if (isElementNode()) getDocument()->dynamicDomRestyler().restyleDepedent(static_cast(this), HoverDependency); } unsigned long NodeBaseImpl::childNodeCount() { unsigned long count = 0; NodeImpl *n; for (n = firstChild(); n; n = n->nextSibling()) count++; return count; } NodeImpl *NodeBaseImpl::childNode(unsigned long index) { unsigned long i; NodeImpl *n = firstChild(); for (i = 0; n && i < index; i++) n = n->nextSibling(); return n; } void NodeBaseImpl::dispatchChildInsertedEvents( NodeImpl *child, int &exceptioncode ) { if (getDocument()->hasListenerType(DocumentImpl::DOMNODEINSERTED_LISTENER)) { MutationEventImpl* const evt = new MutationEventImpl(EventImpl::DOMNODEINSERTED_EVENT,true,false,this,DOMString(),DOMString(),DOMString(),0); evt->ref(); child->dispatchEvent(evt,exceptioncode,true); evt->deref(); if (exceptioncode) return; } // dispatch the DOMNodeInsertedIntoDocument event to all descendants bool hasInsertedListeners = getDocument()->hasListenerType(DocumentImpl::DOMNODEINSERTEDINTODOCUMENT_LISTENER); NodeImpl *p = this; while (p->parentNode()) p = p->parentNode(); if (p->nodeType() == Node::DOCUMENT_NODE) { for (NodeImpl *c = child; c; c = c->traverseNextNode(child)) { c->insertedIntoDocument(); if (hasInsertedListeners) { MutationEventImpl* const evt = new MutationEventImpl(EventImpl::DOMNODEINSERTEDINTODOCUMENT_EVENT,false,false,0,DOMString(),DOMString(),DOMString(),0); evt->ref(); c->dispatchEvent(evt,exceptioncode,true); evt->deref(); if (exceptioncode) return; } } } } void NodeBaseImpl::dispatchChildRemovalEvents( NodeImpl *child, int &exceptioncode ) { // Dispatch pre-removal mutation events getDocument()->notifyBeforeNodeRemoval(child); // ### use events instead if (getDocument()->hasListenerType(DocumentImpl::DOMNODEREMOVED_LISTENER)) { MutationEventImpl* const evt = new MutationEventImpl(EventImpl::DOMNODEREMOVED_EVENT,true,false,this,DOMString(),DOMString(),DOMString(),0); evt->ref(); child->dispatchEvent(evt,exceptioncode,true); evt->deref(); if (exceptioncode) return; } bool hasRemovalListeners = getDocument()->hasListenerType(DocumentImpl::DOMNODEREMOVEDFROMDOCUMENT_LISTENER); // dispatch the DOMNodeRemovedFromDocument event to all descendants NodeImpl *p = this; while (p->parentNode()) p = p->parentNode(); if (p->nodeType() == Node::DOCUMENT_NODE) { for (NodeImpl *c = child; c; c = c->traverseNextNode(child)) { if (hasRemovalListeners) { MutationEventImpl* const evt = new MutationEventImpl(EventImpl::DOMNODEREMOVEDFROMDOCUMENT_EVENT,false,false,0,DOMString(),DOMString(),DOMString(),0); evt->ref(); c->dispatchEvent(evt,exceptioncode,true); evt->deref(); if (exceptioncode) return; } } } } void NodeBaseImpl::setTextContent( const DOMString &text, int& exceptioncode ) { // NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly. if (isReadOnly()) { exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR; return; } removeChildren(); if ( !text.isEmpty() && !text.isNull() ) { TextImpl *t = new TextImpl( docPtr(), text.implementation() ); appendChild( t, exceptioncode ); } } // --------------------------------------------------------------------------- NodeImpl *NodeListImpl::item( unsigned long index ) const { unsigned long requestIndex = index; m_cache->updateNodeListInfo(m_refNode->getDocument()); NodeImpl* n; bool usedCache = false; if (m_cache->current.node) { //Compute distance from the requested index to the cache node long cacheDist = QABS(long(index) - long(m_cache->position)); if (cacheDist < (long)index) { //Closer to the cached position usedCache = true; if (index >= m_cache->position) { //Go ahead unsigned long relIndex = index - m_cache->position; n = recursiveItem(m_refNode, m_cache->current.node, relIndex); } else { //Go backwards unsigned long relIndex = m_cache->position - index; n = recursiveItemBack(m_refNode, m_cache->current.node, relIndex); } } } if (!usedCache) n = recursiveItem(m_refNode, m_refNode->firstChild(), index); //We always update the cache state, to make starting iteration //where it was left off easy. m_cache->current.node = n; m_cache->position = requestIndex; return n; } unsigned long NodeListImpl::length() const { m_cache->updateNodeListInfo(m_refNode->getDocument()); if (!m_cache->hasLength) { m_cache->length = calcLength( m_refNode ); m_cache->hasLength = true; } return m_cache->length; } unsigned long NodeListImpl::calcLength(NodeImpl *start) const { unsigned long len = 0; for(NodeImpl *n = start->firstChild(); n != 0; n = n->nextSibling()) { bool recurse = true; if (nodeMatches(n, recurse)) len++; if (recurse) len+= NodeListImpl::calcLength(n); } return len; } NodeListImpl::NodeListImpl( NodeImpl *n, int type, CacheFactory* factory ) { m_refNode = n; m_refNode->ref(); m_cache = m_refNode->getDocument()->acquireCachedNodeListInfo( factory ? factory : Cache::make, n, type ); } NodeListImpl::~NodeListImpl() { m_refNode->getDocument()->releaseCachedNodeListInfo(m_cache); m_refNode->deref(); } /** Next item in the pre-order walk of tree from node, but not going outside absStart */ static NodeImpl* helperNext(NodeImpl* node, NodeImpl* absStart) { //Walk up until we wind a sibling to go to. while (!node->nextSibling() && node != absStart) node = node->parentNode(); if (node != absStart) return node->nextSibling(); else return 0; } NodeImpl *NodeListImpl::recursiveItem ( NodeImpl* absStart, NodeImpl *start, unsigned long &offset ) const { for(NodeImpl *n = start; n != 0; n = helperNext(n, absStart)) { bool recurse = true; if (nodeMatches(n, recurse)) if (!offset--) return n; NodeImpl *depthSearch = recurse ? recursiveItem(n, n->firstChild(), offset) : 0; if (depthSearch) return depthSearch; } return 0; // no matching node in this subtree } NodeImpl *NodeListImpl::recursiveItemBack ( NodeImpl* absStart, NodeImpl *start, unsigned long &offset ) const { //### it might be cleaner/faster to split nodeMatches and recursion //filtering. bool dummy = true; NodeImpl* n = start; do { bool recurse = true; //Check whether the current node matches. if (nodeMatches(n, dummy)) if (!offset--) return n; if (n->previousSibling()) { //Move to the last node of this whole subtree that we should recurse into n = n->previousSibling(); recurse = true; while (n->lastChild()) { (void)nodeMatches(n, recurse); if (!recurse) break; //Don't go there n = n->lastChild(); } } else { //We're done with this whole subtree, so move up n = n->parentNode(); } } while (n && n != absStart); return 0; } NodeListImpl::Cache::~Cache() {} void NodeListImpl::Cache::clear(DocumentImpl* doc) { hasLength = false; current.node = 0; version = doc->domTreeVersion(); } void NodeListImpl::Cache::updateNodeListInfo(DocumentImpl* doc) { //If version doesn't match, clear if (doc->domTreeVersion() != version) clear(doc); } ChildNodeListImpl::ChildNodeListImpl( NodeImpl *n ): NodeListImpl(n, CHILD_NODES) {} bool ChildNodeListImpl::nodeMatches( NodeImpl* /*testNode*/, bool& doRecurse ) const { doRecurse = false; return true; } TagNodeListImpl::TagNodeListImpl( NodeImpl *n, NodeImpl::Id id ) : NodeListImpl(n, UNCACHEABLE), m_id(id), m_namespaceAware(false) { // An id of 0 here means "*" (match all nodes) m_matchAllNames = (id == 0); m_matchAllNamespaces = false; } TagNodeListImpl::TagNodeListImpl( NodeImpl *n, const DOMString &namespaceURI, const DOMString &localName ) : NodeListImpl(n, UNCACHEABLE), m_id(0), m_namespaceURI(namespaceURI), m_localName(localName), m_namespaceAware(true) { m_matchAllNames = (localName == "*"); m_matchAllNamespaces = (namespaceURI == "*"); } bool TagNodeListImpl::nodeMatches( NodeImpl *testNode, bool& /*doRecurse*/ ) const { if ( testNode->nodeType() != Node::ELEMENT_NODE ) return false; if (m_namespaceAware) return (m_matchAllNamespaces || testNode->namespaceURI() == m_namespaceURI) && (m_matchAllNames || testNode->localName() == m_localName); else { NodeImpl::Id testId = testNode->id(); //we have to strip the namespaces if we compare in a namespace unaware fashion if ( !m_namespaceAware ) testId = localNamePart(testId); return (m_id == 0 || m_id == testId); } } NameNodeListImpl::NameNodeListImpl(NodeImpl *n, const DOMString &t ) : NodeListImpl(n, UNCACHEABLE), nodeName(t) {} bool NameNodeListImpl::nodeMatches( NodeImpl *testNode, bool& /*doRecurse*/ ) const { if ( testNode->nodeType() != Node::ELEMENT_NODE ) return false; return static_cast(testNode)->getAttribute(ATTR_NAME) == nodeName; } // --------------------------------------------------------------------------- NamedNodeMapImpl::NamedNodeMapImpl() { } NamedNodeMapImpl::~NamedNodeMapImpl() { } // ---------------------------------------------------------------------------- GenericRONamedNodeMapImpl::GenericRONamedNodeMapImpl(DocumentImpl* doc) : NamedNodeMapImpl() { m_doc = doc; m_contents = new TQPtrList; } GenericRONamedNodeMapImpl::~GenericRONamedNodeMapImpl() { while (!m_contents->isEmpty()) m_contents->take(0)->deref(); delete m_contents; } NodeImpl *GenericRONamedNodeMapImpl::getNamedItem ( NodeImpl::Id id, bool /*nsAware*/, DOMStringImpl* /*qName*/ ) const { // ## do we need namespace support in this class? TQPtrListIterator it(*m_contents); for (; it.current(); ++it) if (it.current()->id() == id) return it.current(); return 0; } Node GenericRONamedNodeMapImpl::setNamedItem ( NodeImpl* /*arg*/, bool /*nsAware*/, DOMStringImpl* /*qName*/, int &exceptioncode ) { // can't modify this list through standard DOM functions // NO_MODIFICATION_ALLOWED_ERR: Raised if this map is readonly exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR; return 0; } Node GenericRONamedNodeMapImpl::removeNamedItem ( NodeImpl::Id /*id*/, bool /*nsAware*/, DOMStringImpl* /*qName*/, int &exceptioncode ) { // can't modify this list through standard DOM functions // NO_MODIFICATION_ALLOWED_ERR: Raised if this map is readonly exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR; return 0; } NodeImpl *GenericRONamedNodeMapImpl::item ( unsigned long index ) const { if (index >= m_contents->count()) return 0; return m_contents->at(index); } unsigned long GenericRONamedNodeMapImpl::length( ) const { return m_contents->count(); } void GenericRONamedNodeMapImpl::addNode(NodeImpl *n) { // The spec says that in the case of duplicates we only keep the first one if (getNamedItem(n->id(), false, 0)) return; n->ref(); m_contents->append(n); } NodeImpl::Id GenericRONamedNodeMapImpl::mapId(DOMStringImpl* namespaceURI, DOMStringImpl* localName, bool readonly) { if (!m_doc) return 0; return m_doc->getId(NodeImpl::ElementId, namespaceURI, 0, localName, readonly, false /*don't lookupHTML*/); } // ----------------------------------------------------------------------------- void RegisteredListenerList::addEventListener(int id, EventListener *listener, const bool useCapture) { RegisteredEventListener rl(static_cast(id),listener,useCapture); if (!listeners) listeners = new TQValueList; // if this id/listener/useCapture combination is already registered, do nothing. // the DOM2 spec says that "duplicate instances are discarded", and this keeps // the listener order intact. TQValueList::iterator it; for (it = listeners->begin(); it != listeners->end(); ++it) if (*it == rl) return; listeners->append(rl); } void RegisteredListenerList::removeEventListener(int id, EventListener *listener, bool useCapture) { if (!listeners) // nothing to remove return; RegisteredEventListener rl(static_cast(id),listener,useCapture); TQValueList::iterator it; for (it = listeners->begin(); it != listeners->end(); ++it) if (*it == rl) { listeners->remove(it); return; } } bool RegisteredListenerList::isHTMLEventListener(EventListener* listener) { return (listener->eventListenerType() == "_khtml_HTMLEventListener"); } void RegisteredListenerList::setHTMLEventListener(int id, EventListener *listener) { if (!listeners) listeners = new TQValueList; TQValueList::iterator it; if (!listener) { for (it = listeners->begin(); it != listeners->end(); ++it) { if ((*it).id == id && isHTMLEventListener((*it).listener)) { listeners->remove(it); break; } } return; } // if this event already has a registered handler, insert the new one in // place of the old one, to preserve the order. RegisteredEventListener rl(static_cast(id),listener,false); int i; for (i = 0, it = listeners->begin(); it != listeners->end(); ++it, ++i) if ((*it).id == id && isHTMLEventListener((*it).listener)) { listeners->insert(it, rl); listeners->remove(it); return; } listeners->append(rl); } EventListener *RegisteredListenerList::getHTMLEventListener(int id) { if (!listeners) return 0; TQValueList::iterator it; for (it = listeners->begin(); it != listeners->end(); ++it) if ((*it).id == id && isHTMLEventListener((*it).listener)) { return (*it).listener; } return 0; } bool RegisteredListenerList::hasEventListener(int id) { if (!listeners) return false; TQValueList::iterator it; for (it = listeners->begin(); it != listeners->end(); ++it) if ((*it).id == id) return true; return false; } void RegisteredListenerList::clear() { delete listeners; listeners = 0; } bool RegisteredListenerList::stillContainsListener(const RegisteredEventListener& listener) { if (!listeners) return false; return listeners->tqfind(listener) != listeners->end(); } RegisteredListenerList::~RegisteredListenerList() { delete listeners; listeners = 0; }