You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2069 lines
60 KiB
2069 lines
60 KiB
/*
|
|
* 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 <kglobal.h>
|
|
#include <kdebug.h>
|
|
|
|
#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<TextImpl*>(child);
|
|
TextImpl *nextText = static_cast<TextImpl*>(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<NodeImpl> 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<NodeImpl> 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<MouseEventImpl*>( evt )->button() == 0) // LMB click
|
|
dispatchUIEvent(EventImpl::DOMACTIVATE_EVENT, static_cast<UIEventImpl*>(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<EventImpl::EventId>(_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<EventImpl::EventId>(_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<EventImpl::EventId>(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<EventImpl::EventId>(_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
|
|
// invalidate the item after the current iterator (which "it" is pointing to).
|
|
// So we make a copy of the list.
|
|
TQValueList<RegisteredEventListener> listeners = *m_regdListeners.listeners;
|
|
TQValueList<RegisteredEventListener>::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<MouseEventImpl*>(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<NodeImpl *>(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<NodeImpl> 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<NodeImpl> 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<NodeImpl> 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<NodeImpl> 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<ElementImpl*>(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<ElementImpl*>(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<ElementImpl*>(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<ElementImpl *>(testNode)->getAttribute(ATTR_NAME) == nodeName;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
NamedNodeMapImpl::NamedNodeMapImpl()
|
|
{
|
|
}
|
|
|
|
NamedNodeMapImpl::~NamedNodeMapImpl()
|
|
{
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
GenericRONamedNodeMapImpl::GenericRONamedNodeMapImpl(DocumentImpl* doc)
|
|
: NamedNodeMapImpl()
|
|
{
|
|
m_doc = doc;
|
|
m_contents = new TQPtrList<NodeImpl>;
|
|
}
|
|
|
|
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<NodeImpl> 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<EventImpl::EventId>(id),listener,useCapture);
|
|
if (!listeners)
|
|
listeners = new TQValueList<RegisteredEventListener>;
|
|
|
|
// 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<RegisteredEventListener>::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<EventImpl::EventId>(id),listener,useCapture);
|
|
|
|
TQValueList<RegisteredEventListener>::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<RegisteredEventListener>;
|
|
|
|
TQValueList<RegisteredEventListener>::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<EventImpl::EventId>(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<RegisteredEventListener>::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<RegisteredEventListener>::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->find(listener) != listeners->end();
|
|
}
|
|
|
|
RegisteredListenerList::~RegisteredListenerList() {
|
|
delete listeners; listeners = 0;
|
|
}
|