📄 dom_nodeimpl.cpp
字号:
/** * 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 Apple Computer, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */#include "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 <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.hnamespace 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(DocumentPtr *doc) : document(doc), m_previous(0), m_next(0), m_render(0), m_tabIndex( 0 ), m_hasId( false ), m_hasStyle( false ), m_attached(false), m_closed(false), m_changed( false ), m_hasChangedChild( false ), m_inDocument( false ), m_hasAnchor( false ), m_specified( false ), m_focused( false ), m_active( false ), m_styleElement( false ), m_implicit( false ), m_rendererNeedsClose( false ), m_htmlCompat( false ){ if (document) document->ref();}NodeImpl::~NodeImpl(){ if (m_render) detach(); if (document) document->deref(); 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;}NodeImpl *NodeImpl::replaceChild( NodeImpl *, NodeImpl *, int &exceptioncode ){ exceptioncode = DOMException::HIERARCHY_REQUEST_ERR; return 0;}NodeImpl *NodeImpl::removeChild( NodeImpl *, int &exceptioncode ){ exceptioncode = DOMException::NOT_FOUND_ERR; return 0;}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 0kdDebug(6200) << "getCaret: node " << this << " " << nodeName().string() << " offset: " << offset << endl;#endif mapDOMPosToRenderPos(this, offset, r, r_ofs, outside, outsideEnd);#if 0kdDebug(6200) << "getCaret: r " << r << " " << (r?r->renderName():QString::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;}QRect NodeImpl::getRect() const{ int _x, _y; if(m_render && m_render->absolutePosition(_x, _y)) return QRect( _x + m_render->inlineXPos(), _y + m_render->inlineYPos(), m_render->width(), m_render->height() ); return QRect();}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 = document->document()->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 QPtrList<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); QPtrListIterator<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. DocumentPtr *doc = document; 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(); DocumentPtr *doc = document; doc->ref(); dispatchGenericEvent( evt, exceptioncode ); if (!evt->defaultPrevented() && doc->document()) doc->document()->defaultEventHandler(evt); if (_id == EventImpl::LOAD_EVENT && !evt->propagationStopped() && doc->document()) { // 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->document()->ownerElement(); if (elt && (elt->getDocument()->domain().isNull() || elt->getDocument()->domain() == doc->document()->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(QMouseEvent *_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 QEvent::MouseButtonPress: evtId = EventImpl::MOUSEDOWN_EVENT; break; case QEvent::MouseButtonRelease: evtId = EventImpl::MOUSEUP_EVENT; break; case QEvent::MouseButtonDblClick: evtId = EventImpl::CLICK_EVENT; detail = 1; // ### support for multiple double clicks break; case QEvent::MouseMove: evtId = EventImpl::MOUSEMOVE_EVENT; cancelable = false; break; default: break; } }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -