From ce4a32fe52ef09d8f5ff1dd22c001110902b60a2 Mon Sep 17 00:00:00 2001 From: toma Date: Wed, 25 Nov 2009 17:56:58 +0000 Subject: Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features. BUG:215923 git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdelibs@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da --- khtml/xml/Makefile.am | 48 + khtml/xml/dom2_eventsimpl.cpp | 969 +++++++++++++ khtml/xml/dom2_eventsimpl.h | 513 +++++++ khtml/xml/dom2_rangeimpl.cpp | 1640 +++++++++++++++++++++ khtml/xml/dom2_rangeimpl.h | 127 ++ khtml/xml/dom2_traversalimpl.cpp | 667 +++++++++ khtml/xml/dom2_traversalimpl.h | 196 +++ khtml/xml/dom2_viewsimpl.cpp | 50 + khtml/xml/dom2_viewsimpl.h | 50 + khtml/xml/dom_docimpl.cpp | 2892 ++++++++++++++++++++++++++++++++++++++ khtml/xml/dom_docimpl.h | 763 ++++++++++ khtml/xml/dom_elementimpl.cpp | 1301 +++++++++++++++++ khtml/xml/dom_elementimpl.h | 392 ++++++ khtml/xml/dom_nodeimpl.cpp | 2068 +++++++++++++++++++++++++++ khtml/xml/dom_nodeimpl.h | 736 ++++++++++ khtml/xml/dom_restyler.cpp | 122 ++ khtml/xml/dom_restyler.h | 102 ++ khtml/xml/dom_stringimpl.cpp | 460 ++++++ khtml/xml/dom_stringimpl.h | 104 ++ khtml/xml/dom_textimpl.cpp | 522 +++++++ khtml/xml/dom_textimpl.h | 176 +++ khtml/xml/dom_xmlimpl.cpp | 498 +++++++ khtml/xml/dom_xmlimpl.h | 181 +++ khtml/xml/xml_tokenizer.cpp | 609 ++++++++ khtml/xml/xml_tokenizer.h | 198 +++ 25 files changed, 15384 insertions(+) create mode 100644 khtml/xml/Makefile.am create mode 100644 khtml/xml/dom2_eventsimpl.cpp create mode 100644 khtml/xml/dom2_eventsimpl.h create mode 100644 khtml/xml/dom2_rangeimpl.cpp create mode 100644 khtml/xml/dom2_rangeimpl.h create mode 100644 khtml/xml/dom2_traversalimpl.cpp create mode 100644 khtml/xml/dom2_traversalimpl.h create mode 100644 khtml/xml/dom2_viewsimpl.cpp create mode 100644 khtml/xml/dom2_viewsimpl.h create mode 100644 khtml/xml/dom_docimpl.cpp create mode 100644 khtml/xml/dom_docimpl.h create mode 100644 khtml/xml/dom_elementimpl.cpp create mode 100644 khtml/xml/dom_elementimpl.h create mode 100644 khtml/xml/dom_nodeimpl.cpp create mode 100644 khtml/xml/dom_nodeimpl.h create mode 100644 khtml/xml/dom_restyler.cpp create mode 100644 khtml/xml/dom_restyler.h create mode 100644 khtml/xml/dom_stringimpl.cpp create mode 100644 khtml/xml/dom_stringimpl.h create mode 100644 khtml/xml/dom_textimpl.cpp create mode 100644 khtml/xml/dom_textimpl.h create mode 100644 khtml/xml/dom_xmlimpl.cpp create mode 100644 khtml/xml/dom_xmlimpl.h create mode 100644 khtml/xml/xml_tokenizer.cpp create mode 100644 khtml/xml/xml_tokenizer.h (limited to 'khtml/xml') diff --git a/khtml/xml/Makefile.am b/khtml/xml/Makefile.am new file mode 100644 index 000000000..8f7cd6b7f --- /dev/null +++ b/khtml/xml/Makefile.am @@ -0,0 +1,48 @@ +# This file is part of the KDE libraries +# Copyright (C) 1997 Martin Jones (mjones@kde.org) +# (C) 1997 Torben Weis (weis@kde.org) + +# 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. + +KDE_CXXFLAGS = $(WOVERLOADED_VIRTUAL) + +noinst_LTLIBRARIES = libkhtmlxml.la +libkhtmlxml_la_SOURCES = \ + dom_docimpl.cpp dom_nodeimpl.cpp dom_textimpl.cpp \ + dom_elementimpl.cpp dom_stringimpl.cpp dom2_rangeimpl.cpp \ + dom2_traversalimpl.cpp xml_tokenizer.cpp dom_xmlimpl.cpp \ + dom2_eventsimpl.cpp dom2_viewsimpl.cpp dom_restyler.cpp + +#libkhtmlxml_la_LDFLAGS = -no-undefined +libkhtmlxml_la_METASOURCES = AUTO + +noinst_HEADERS = \ + dom_docimpl.h dom_nodeimpl.h dom_textimpl.h \ + dom_elementimpl.h dom_stringimpl.h dom2_rangeimpl.h \ + dom2_traversalimpl.h xml_tokenizer.h dom_xmlimpl.h \ + dom2_eventsimpl.h dom2_viewsimpl.h dom_restyler.h + +INCLUDES = -I$(top_srcdir)/kimgio -I$(top_srcdir)/kio -I$(top_srcdir)/dcop \ + -I$(top_srcdir)/khtml -I$(top_srcdir) -I$(top_srcdir)/kwallet/client \ + -I$(top_srcdir)/kutils -I$(top_builddir)/kjs $(all_includes) + +SRCDOC_DEST=$(kde_htmldir)/en/kdelibs/khtml + +## generate lib documentation +srcdoc: + $(mkinstalldirs) $(SRCDOC_DEST) + kdoc -H -d $(SRCDOC_DEST) kdecore -lqt + diff --git a/khtml/xml/dom2_eventsimpl.cpp b/khtml/xml/dom2_eventsimpl.cpp new file mode 100644 index 000000000..db8b17b6f --- /dev/null +++ b/khtml/xml/dom2_eventsimpl.cpp @@ -0,0 +1,969 @@ +/** + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * (C) 2001 Tobias Anton (anton@stud.fbi.fh-darmstadt.de) + * (C) 2003 Apple Computer, Inc. + * (C) 2006 Maksim Orlovich (maksim@kde.org) + * + * 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/dom2_views.h" + +#include "xml/dom2_eventsimpl.h" +#include "xml/dom_stringimpl.h" +#include "xml/dom_nodeimpl.h" +#include "xml/dom_docimpl.h" +#include "rendering/render_layer.h" +#include "khtmlview.h" + +#include + +using namespace DOM; +using namespace khtml; + +EventImpl::EventImpl() +{ + m_type = 0; + m_canBubble = false; + m_cancelable = false; + + m_propagationStopped = false; + m_defaultPrevented = false; + m_id = UNKNOWN_EVENT; + m_currentTarget = 0; + m_eventPhase = 0; + m_target = 0; + m_createTime = QDateTime::currentDateTime(); + m_defaultHandled = false; +} + +EventImpl::EventImpl(EventId _id, bool canBubbleArg, bool cancelableArg) +{ + DOMString t = EventImpl::idToType(_id); + m_type = t.implementation(); + if (m_type) + m_type->ref(); + m_canBubble = canBubbleArg; + m_cancelable = cancelableArg; + + m_propagationStopped = false; + m_defaultPrevented = false; + m_id = _id; + m_currentTarget = 0; + m_eventPhase = 0; + m_target = 0; + m_createTime = QDateTime::currentDateTime(); + m_defaultHandled = false; +} + +EventImpl::~EventImpl() +{ + if (m_type) + m_type->deref(); + if (m_target) + m_target->deref(); +} + +void EventImpl::setTarget(NodeImpl *_target) +{ + if (m_target) + m_target->deref(); + m_target = _target; + if (m_target) + m_target->ref(); +} + +DOMTimeStamp EventImpl::timeStamp() +{ + QDateTime epoch(QDate(1970,1,1),QTime(0,0)); + // ### kjs does not yet support long long (?) so the value wraps around + return epoch.secsTo(m_createTime)*1000+m_createTime.time().msec(); +} + +void EventImpl::initEvent(const DOMString &eventTypeArg, bool canBubbleArg, bool cancelableArg) +{ + // ### ensure this is not called after we have been dispatched (also for subclasses) + + if (m_type) + m_type->deref(); + + m_type = eventTypeArg.implementation(); + if (m_type) + m_type->ref(); + + m_id = typeToId(eventTypeArg); + + m_canBubble = canBubbleArg; + m_cancelable = cancelableArg; +} + +EventImpl::EventId EventImpl::typeToId(DOMString type) +{ + if (type == "DOMFocusIn") + return DOMFOCUSIN_EVENT; + else if (type == "DOMFocusOut") + return DOMFOCUSOUT_EVENT; + else if (type == "DOMActivate") + return DOMACTIVATE_EVENT; + else if (type == "click") + return CLICK_EVENT; + else if (type == "mousedown") + return MOUSEDOWN_EVENT; + else if (type == "mouseup") + return MOUSEUP_EVENT; + else if (type == "mouseover") + return MOUSEOVER_EVENT; + else if (type == "mousemove") + return MOUSEMOVE_EVENT; + else if (type == "mouseout") + return MOUSEOUT_EVENT; + else if (type == "DOMSubtreeModified") + return DOMSUBTREEMODIFIED_EVENT; + else if (type == "DOMNodeInserted") + return DOMNODEINSERTED_EVENT; + else if (type == "DOMNodeRemoved") + return DOMNODEREMOVED_EVENT; + else if (type == "DOMNodeRemovedFromDocument") + return DOMNODEREMOVEDFROMDOCUMENT_EVENT; + else if (type == "DOMNodeInsertedIntoDocument") + return DOMNODEINSERTEDINTODOCUMENT_EVENT; + else if (type == "DOMAttrModified") + return DOMATTRMODIFIED_EVENT; + else if (type == "DOMCharacterDataModified") + return DOMCHARACTERDATAMODIFIED_EVENT; + else if (type == "load") + return LOAD_EVENT; + else if (type == "unload") + return UNLOAD_EVENT; + else if (type == "abort") + return ABORT_EVENT; + else if (type == "error") + return ERROR_EVENT; + else if (type == "select") + return SELECT_EVENT; + else if (type == "change") + return CHANGE_EVENT; + else if (type == "submit") + return SUBMIT_EVENT; + else if (type == "reset") + return RESET_EVENT; + else if (type == "focus") + return FOCUS_EVENT; + else if (type == "blur") + return BLUR_EVENT; + else if (type == "resize") + return RESIZE_EVENT; + else if (type == "scroll") + return SCROLL_EVENT; + else if ( type == "keydown" ) + return KEYDOWN_EVENT; + else if ( type == "keyup" ) + return KEYUP_EVENT; + else if ( type == "textInput" ) + return KEYPRESS_EVENT; + else if ( type == "keypress" ) + return KEYPRESS_EVENT; + else if ( type == "readystatechange" ) + return KHTML_READYSTATECHANGE_EVENT; + else if ( type == "dblclick" ) + return KHTML_ECMA_DBLCLICK_EVENT; + + // ignore: KHTML_CLICK_EVENT + return UNKNOWN_EVENT; +} + +DOMString EventImpl::idToType(EventImpl::EventId id) +{ + switch (id) { + case DOMFOCUSIN_EVENT: + return "DOMFocusIn"; + case DOMFOCUSOUT_EVENT: + return "DOMFocusOut"; + case DOMACTIVATE_EVENT: + return "DOMActivate"; + case CLICK_EVENT: + return "click"; + case MOUSEDOWN_EVENT: + return "mousedown"; + case MOUSEUP_EVENT: + return "mouseup"; + case MOUSEOVER_EVENT: + return "mouseover"; + case MOUSEMOVE_EVENT: + return "mousemove"; + case MOUSEOUT_EVENT: + return "mouseout"; + case DOMSUBTREEMODIFIED_EVENT: + return "DOMSubtreeModified"; + case DOMNODEINSERTED_EVENT: + return "DOMNodeInserted"; + case DOMNODEREMOVED_EVENT: + return "DOMNodeRemoved"; + case DOMNODEREMOVEDFROMDOCUMENT_EVENT: + return "DOMNodeRemovedFromDocument"; + case DOMNODEINSERTEDINTODOCUMENT_EVENT: + return "DOMNodeInsertedIntoDocument"; + case DOMATTRMODIFIED_EVENT: + return "DOMAttrModified"; + case DOMCHARACTERDATAMODIFIED_EVENT: + return "DOMCharacterDataModified"; + case LOAD_EVENT: + return "load"; + case UNLOAD_EVENT: + return "unload"; + case ABORT_EVENT: + return "abort"; + case ERROR_EVENT: + return "error"; + case SELECT_EVENT: + return "select"; + case CHANGE_EVENT: + return "change"; + case SUBMIT_EVENT: + return "submit"; + case RESET_EVENT: + return "reset"; + case FOCUS_EVENT: + return "focus"; + case BLUR_EVENT: + return "blur"; + case RESIZE_EVENT: + return "resize"; + case SCROLL_EVENT: + return "scroll"; + case KEYDOWN_EVENT: + return "keydown"; + case KEYUP_EVENT: + return "keyup"; + case KEYPRESS_EVENT: + return "keypress"; //DOM3 ev. suggests textInput, but it's better for compat this way + + //khtml extensions + case KHTML_ECMA_DBLCLICK_EVENT: + return "dblclick"; + case KHTML_ECMA_CLICK_EVENT: + return "click"; + case KHTML_DRAGDROP_EVENT: + return "khtml_dragdrop"; + case KHTML_MOVE_EVENT: + return "khtml_move"; + case KHTML_READYSTATECHANGE_EVENT: + return "readystatechange"; + + default: + return DOMString(); + break; + } +} + +bool EventImpl::isUIEvent() const +{ + return false; +} + +bool EventImpl::isMouseEvent() const +{ + return false; +} + +bool EventImpl::isMutationEvent() const +{ + return false; +} + +bool EventImpl::isTextInputEvent() const +{ + return false; +} + +bool EventImpl::isKeyboardEvent() const +{ + return false; +} + +// ----------------------------------------------------------------------------- + +UIEventImpl::UIEventImpl(EventId _id, bool canBubbleArg, bool cancelableArg, + AbstractViewImpl *viewArg, long detailArg) + : EventImpl(_id,canBubbleArg,cancelableArg) +{ + m_view = viewArg; + if (m_view) + m_view->ref(); + m_detail = detailArg; +} + +UIEventImpl::~UIEventImpl() +{ + if (m_view) + m_view->deref(); +} + +void UIEventImpl::initUIEvent(const DOMString &typeArg, + bool canBubbleArg, + bool cancelableArg, + const AbstractView &viewArg, + long detailArg) +{ + EventImpl::initEvent(typeArg,canBubbleArg,cancelableArg); + + if (m_view) + m_view->deref(); + + m_view = viewArg.handle(); + if (m_view) + m_view->ref(); + m_detail = detailArg; +} + +bool UIEventImpl::isUIEvent() const +{ + return true; +} + +// ----------------------------------------------------------------------------- + +MouseEventImpl::MouseEventImpl() +{ + m_screenX = 0; + m_screenY = 0; + m_clientX = 0; + m_clientY = 0; + m_pageX = 0; + m_pageY = 0; + m_ctrlKey = false; + m_altKey = false; + m_shiftKey = false; + m_metaKey = false; + m_button = 0; + m_relatedTarget = 0; + m_qevent = 0; + m_isDoubleClick = false; +} + +MouseEventImpl::MouseEventImpl(EventId _id, + bool canBubbleArg, + bool cancelableArg, + AbstractViewImpl *viewArg, + long detailArg, + long screenXArg, + long screenYArg, + long clientXArg, + long clientYArg, + long pageXArg, + long pageYArg, + bool ctrlKeyArg, + bool altKeyArg, + bool shiftKeyArg, + bool metaKeyArg, + unsigned short buttonArg, + NodeImpl *relatedTargetArg, + QMouseEvent *qe, + bool isDoubleClick) + : UIEventImpl(_id,canBubbleArg,cancelableArg,viewArg,detailArg) +{ + m_screenX = screenXArg; + m_screenY = screenYArg; + m_clientX = clientXArg; + m_clientY = clientYArg; + m_pageX = pageXArg; + m_pageY = pageYArg; + m_ctrlKey = ctrlKeyArg; + m_altKey = altKeyArg; + m_shiftKey = shiftKeyArg; + m_metaKey = metaKeyArg; + m_button = buttonArg; + m_relatedTarget = relatedTargetArg; + if (m_relatedTarget) + m_relatedTarget->ref(); + computeLayerPos(); + m_qevent = qe; + m_isDoubleClick = isDoubleClick; +} + +MouseEventImpl::~MouseEventImpl() +{ + if (m_relatedTarget) + m_relatedTarget->deref(); +} + +void MouseEventImpl::computeLayerPos() +{ + m_layerX = m_pageX; + m_layerY = m_pageY; + + DocumentImpl* doc = view() ? view()->document() : 0; + if (doc) { + khtml::RenderObject::NodeInfo renderInfo(true, false); + doc->renderer()->layer()->nodeAtPoint(renderInfo, m_pageX, m_pageY); + + NodeImpl *node = renderInfo.innerNonSharedNode(); + while (node && !node->renderer()) + node = node->parent(); + + if (node) { + node->renderer()->enclosingLayer()->updateLayerPosition(); + for (RenderLayer* layer = node->renderer()->enclosingLayer(); layer; + layer = layer->parent()) { + m_layerX -= layer->xPos(); + m_layerY -= layer->yPos(); + } + } + } +} + +void MouseEventImpl::initMouseEvent(const DOMString &typeArg, + bool canBubbleArg, + bool cancelableArg, + const AbstractView &viewArg, + long detailArg, + long screenXArg, + long screenYArg, + long clientXArg, + long clientYArg, + bool ctrlKeyArg, + bool altKeyArg, + bool shiftKeyArg, + bool metaKeyArg, + unsigned short buttonArg, + const Node &relatedTargetArg) +{ + UIEventImpl::initUIEvent(typeArg,canBubbleArg,cancelableArg,viewArg,detailArg); + + if (m_relatedTarget) + m_relatedTarget->deref(); + + m_screenX = screenXArg; + m_screenY = screenYArg; + m_clientX = clientXArg; + m_clientY = clientYArg; + m_pageX = clientXArg; + m_pageY = clientYArg; + KHTMLView* v; + if ( view() && view()->document() && ( v = view()->document()->view() ) ) { + m_pageX += v->contentsX(); + m_pageY += v->contentsY(); + } + m_ctrlKey = ctrlKeyArg; + m_altKey = altKeyArg; + m_shiftKey = shiftKeyArg; + m_metaKey = metaKeyArg; + m_button = buttonArg; + m_relatedTarget = relatedTargetArg.handle(); + if (m_relatedTarget) + m_relatedTarget->ref(); + + + // ### make this on-demand. its soo sloooow + computeLayerPos(); + m_qevent = 0; +} + +bool MouseEventImpl::isMouseEvent() const +{ + return true; +} + +//--------------------------------------------------------------------------------------------- +/* This class is used to do remapping between different encodings reasonably compactly */ + +template +class IDTranslator +{ +public: + struct Info { + MemL l; + R r; + }; + + IDTranslator(const Info* table) { + for (const Info* cursor = table; cursor->l; ++cursor) { + m_lToR.insert(cursor->l, cursor->r); + m_rToL.insert(cursor->r, cursor->l); + } + } + + L toLeft(R r) { + QMapIterator i( m_rToL.find(r) ); + if (i != m_rToL.end()) + return *i; + return L(); + } + + R toRight(L l) { + QMapIterator i = m_lToR.find(l); + if (i != m_lToR.end()) + return *i; + return R(); + } + +private: + QMap m_lToR; + QMap m_rToL; +}; + +#define MAKE_TRANSLATOR(name,L,R,MR,table) static IDTranslator* s_##name; \ + static IDTranslator* name() { if (!s_##name) s_##name = new IDTranslator(table); \ + return s_##name; } + +//--------------------------------------------------------------------------------------------- + +/* Mapping between special Qt keycodes and virtual DOM codes */ +IDTranslator::Info virtKeyToQtKeyTable[] = +{ + {KeyEventBaseImpl::DOM_VK_BACK_SPACE, Qt::Key_Backspace}, + {KeyEventBaseImpl::DOM_VK_ENTER, Qt::Key_Enter}, + {KeyEventBaseImpl::DOM_VK_ENTER, Qt::Key_Return}, + {KeyEventBaseImpl::DOM_VK_NUM_LOCK, Qt::Key_NumLock}, + {KeyEventBaseImpl::DOM_VK_RIGHT_ALT, Qt::Key_Alt}, + {KeyEventBaseImpl::DOM_VK_LEFT_CONTROL, Qt::Key_Control}, + {KeyEventBaseImpl::DOM_VK_LEFT_SHIFT, Qt::Key_Shift}, + {KeyEventBaseImpl::DOM_VK_META, Qt::Key_Meta}, + {KeyEventBaseImpl::DOM_VK_CAPS_LOCK, Qt::Key_CapsLock}, + {KeyEventBaseImpl::DOM_VK_DELETE, Qt::Key_Delete}, + {KeyEventBaseImpl::DOM_VK_END, Qt::Key_End}, + {KeyEventBaseImpl::DOM_VK_ESCAPE, Qt::Key_Escape}, + {KeyEventBaseImpl::DOM_VK_HOME, Qt::Key_Home}, + {KeyEventBaseImpl::DOM_VK_PAUSE, Qt::Key_Pause}, + {KeyEventBaseImpl::DOM_VK_PRINTSCREEN, Qt::Key_Print}, + {KeyEventBaseImpl::DOM_VK_SCROLL_LOCK, Qt::Key_ScrollLock}, + {KeyEventBaseImpl::DOM_VK_LEFT, Qt::Key_Left}, + {KeyEventBaseImpl::DOM_VK_RIGHT, Qt::Key_Right}, + {KeyEventBaseImpl::DOM_VK_UP, Qt::Key_Up}, + {KeyEventBaseImpl::DOM_VK_DOWN, Qt::Key_Down}, + {KeyEventBaseImpl::DOM_VK_PAGE_DOWN, Qt::Key_Next}, + {KeyEventBaseImpl::DOM_VK_PAGE_UP, Qt::Key_Prior}, + {KeyEventBaseImpl::DOM_VK_F1, Qt::Key_F1}, + {KeyEventBaseImpl::DOM_VK_F2, Qt::Key_F2}, + {KeyEventBaseImpl::DOM_VK_F3, Qt::Key_F3}, + {KeyEventBaseImpl::DOM_VK_F4, Qt::Key_F4}, + {KeyEventBaseImpl::DOM_VK_F5, Qt::Key_F5}, + {KeyEventBaseImpl::DOM_VK_F6, Qt::Key_F6}, + {KeyEventBaseImpl::DOM_VK_F7, Qt::Key_F7}, + {KeyEventBaseImpl::DOM_VK_F8, Qt::Key_F8}, + {KeyEventBaseImpl::DOM_VK_F9, Qt::Key_F9}, + {KeyEventBaseImpl::DOM_VK_F10, Qt::Key_F10}, + {KeyEventBaseImpl::DOM_VK_F11, Qt::Key_F11}, + {KeyEventBaseImpl::DOM_VK_F12, Qt::Key_F12}, + {KeyEventBaseImpl::DOM_VK_F13, Qt::Key_F13}, + {KeyEventBaseImpl::DOM_VK_F14, Qt::Key_F14}, + {KeyEventBaseImpl::DOM_VK_F15, Qt::Key_F15}, + {KeyEventBaseImpl::DOM_VK_F16, Qt::Key_F16}, + {KeyEventBaseImpl::DOM_VK_F17, Qt::Key_F17}, + {KeyEventBaseImpl::DOM_VK_F18, Qt::Key_F18}, + {KeyEventBaseImpl::DOM_VK_F19, Qt::Key_F19}, + {KeyEventBaseImpl::DOM_VK_F20, Qt::Key_F20}, + {KeyEventBaseImpl::DOM_VK_F21, Qt::Key_F21}, + {KeyEventBaseImpl::DOM_VK_F22, Qt::Key_F22}, + {KeyEventBaseImpl::DOM_VK_F23, Qt::Key_F23}, + {KeyEventBaseImpl::DOM_VK_F24, Qt::Key_F24}, + {0, 0} +}; + +MAKE_TRANSLATOR(virtKeyToQtKey, unsigned, unsigned, unsigned, virtKeyToQtKeyTable) + +KeyEventBaseImpl::KeyEventBaseImpl(EventId id, bool canBubbleArg, bool cancelableArg, AbstractViewImpl *viewArg, + QKeyEvent *key) : + UIEventImpl(id, canBubbleArg, cancelableArg, viewArg, 0) +{ + m_synthetic = false; + + //Here, we need to map Qt's internal info to browser-style info. + m_keyEvent = new QKeyEvent(key->type(), key->key(), key->ascii(), key->state(), key->text(), key->isAutoRepeat(), key->count() ); + + m_detail = key->count(); + m_keyVal = key->ascii(); + m_virtKeyVal = virtKeyToQtKey()->toLeft(key->key()); + + // m_keyVal should contain the unicode value + // of the pressed key if available. + if (m_virtKeyVal == DOM_VK_UNDEFINED && !key->text().isEmpty()) + m_keyVal = key->text().unicode()[0]; + + // key->state returns enum ButtonState, which is ShiftButton, ControlButton and AltButton or'ed together. + m_modifier = key->state(); +} + +KeyEventBaseImpl::~KeyEventBaseImpl() +{ + delete m_keyEvent; +} + +void KeyEventBaseImpl::initKeyBaseEvent(const DOMString &typeArg, + bool canBubbleArg, + bool cancelableArg, + const AbstractView &viewArg, + unsigned long keyValArg, + unsigned long virtKeyValArg, + unsigned long modifiersArg) +{ + m_synthetic = true; + delete m_keyEvent; + m_keyEvent = 0; + initUIEvent(typeArg, canBubbleArg, cancelableArg, viewArg, 1); + m_virtKeyVal = virtKeyValArg; + m_keyVal = keyValArg; + m_modifier = modifiersArg; +} + +bool KeyEventBaseImpl::checkModifier(unsigned long modifierArg) +{ + return ((m_modifier & modifierArg) == modifierArg); +} + +void KeyEventBaseImpl::initModifier(unsigned long modifierArg, + bool valueArg) +{ + if (valueArg) + m_modifier |= modifierArg; + else + m_modifier &= (modifierArg ^ 0xFFFFFFFF); +} + +void KeyEventBaseImpl::buildQKeyEvent() const +{ + delete m_keyEvent; + + assert(m_synthetic); + //IMPORTANT: we ignore modifers on purpose. + //this is to prevent a website from synthesizing something + //like Ctrl-V or Shift-Insert and stealing contents of the user's clipboard. + unsigned modifiers = 0; + + int key = 0; + int ascii = 0; + QString text; + if (m_virtKeyVal) + key = virtKeyToQtKey()->toRight(m_virtKeyVal); + if (!key) { + ascii = m_keyVal; //###? + key = m_keyVal; + text = QChar(key); + } + + //Neuter F keys as well. + if (key >= Qt::Key_F1 && key <= Qt::Key_F35) + key = Qt::Key_ScrollLock; + + m_keyEvent = new QKeyEvent(id() == KEYUP_EVENT ? QEvent::KeyRelease : QEvent::KeyPress, + key, ascii, modifiers, text); +} + +//------------------------------------------------------------------------------ + + +static const IDTranslator::Info keyIdentifiersToVirtKeysTable[] = { + {"Alt", KeyEventBaseImpl::DOM_VK_LEFT_ALT}, + {"Control", KeyEventBaseImpl::DOM_VK_LEFT_CONTROL}, + {"Shift", KeyEventBaseImpl::DOM_VK_LEFT_SHIFT}, + {"Meta", KeyEventBaseImpl::DOM_VK_META}, + {"\0x08", KeyEventBaseImpl::DOM_VK_SPACE}, //1-char virt! + {"CapsLock", KeyEventBaseImpl::DOM_VK_CAPS_LOCK}, + {"\x7F", KeyEventBaseImpl::DOM_VK_DELETE}, //1-char virt! + {"End", KeyEventBaseImpl::DOM_VK_END}, + {"Enter", KeyEventBaseImpl::DOM_VK_ENTER}, + {"\x1b", KeyEventBaseImpl::DOM_VK_ESCAPE}, //1-char virt! + {"Home", KeyEventBaseImpl::DOM_VK_HOME}, + {"NumLock", KeyEventBaseImpl::DOM_VK_NUM_LOCK}, + {"Pause", KeyEventBaseImpl::DOM_VK_PAUSE}, + {"PrintScreen", KeyEventBaseImpl::DOM_VK_PRINTSCREEN}, + {"Scroll", KeyEventBaseImpl::DOM_VK_SCROLL_LOCK}, + {" ", KeyEventBaseImpl::DOM_VK_SPACE}, //1-char virt! + {"\t", KeyEventBaseImpl::DOM_VK_TAB}, //1-char virt! + {"Left", KeyEventBaseImpl::DOM_VK_LEFT}, + {"Left", KeyEventBaseImpl::DOM_VK_LEFT}, + {"Right", KeyEventBaseImpl::DOM_VK_RIGHT}, + {"Up", KeyEventBaseImpl::DOM_VK_UP}, + {"Down", KeyEventBaseImpl::DOM_VK_DOWN}, + {"PageDown", KeyEventBaseImpl::DOM_VK_PAGE_DOWN}, + {"PageUp", KeyEventBaseImpl::DOM_VK_PAGE_UP}, + {"F1", KeyEventBaseImpl::DOM_VK_F1}, + {"F2", KeyEventBaseImpl::DOM_VK_F2}, + {"F3", KeyEventBaseImpl::DOM_VK_F3}, + {"F4", KeyEventBaseImpl::DOM_VK_F4}, + {"F5", KeyEventBaseImpl::DOM_VK_F5}, + {"F6", KeyEventBaseImpl::DOM_VK_F6}, + {"F7", KeyEventBaseImpl::DOM_VK_F7}, + {"F8", KeyEventBaseImpl::DOM_VK_F8}, + {"F9", KeyEventBaseImpl::DOM_VK_F9}, + {"F10", KeyEventBaseImpl::DOM_VK_F10}, + {"F11", KeyEventBaseImpl::DOM_VK_F11}, + {"F12", KeyEventBaseImpl::DOM_VK_F12}, + {"F13", KeyEventBaseImpl::DOM_VK_F13}, + {"F14", KeyEventBaseImpl::DOM_VK_F14}, + {"F15", KeyEventBaseImpl::DOM_VK_F15}, + {"F16", KeyEventBaseImpl::DOM_VK_F16}, + {"F17", KeyEventBaseImpl::DOM_VK_F17}, + {"F18", KeyEventBaseImpl::DOM_VK_F18}, + {"F19", KeyEventBaseImpl::DOM_VK_F19}, + {"F20", KeyEventBaseImpl::DOM_VK_F20}, + {"F21", KeyEventBaseImpl::DOM_VK_F21}, + {"F22", KeyEventBaseImpl::DOM_VK_F22}, + {"F23", KeyEventBaseImpl::DOM_VK_F23}, + {"F24", KeyEventBaseImpl::DOM_VK_F24}, + {0, 0} +}; + +MAKE_TRANSLATOR(keyIdentifiersToVirtKeys, QCString, unsigned, const char*, keyIdentifiersToVirtKeysTable) + +/** These are the modifiers we currently support */ +static const IDTranslator::Info keyModifiersToCodeTable[] = { + {"Alt", Qt::AltButton}, + {"Control", Qt::ControlButton}, + {"Shift", Qt::ShiftButton}, + {"Meta", Qt::MetaButton}, + {0, 0} +}; + +MAKE_TRANSLATOR(keyModifiersToCode, QCString, unsigned, const char*, keyModifiersToCodeTable) + +KeyboardEventImpl::KeyboardEventImpl() : m_keyLocation(DOM_KEY_LOCATION_STANDARD) +{} + +DOMString KeyboardEventImpl::keyIdentifier() const +{ + if (unsigned special = virtKeyVal()) + if (const char* id = keyIdentifiersToVirtKeys()->toLeft(special)) + return QString::fromLatin1(id); + + if (unsigned unicode = keyVal()) + return QString(QChar(unicode)); + + return "Unidentified"; +} + +bool KeyboardEventImpl::getModifierState (const DOMString& keyIdentifierArg) const +{ + unsigned mask = keyModifiersToCode()->toRight(keyIdentifierArg.string().latin1()); + return m_modifier & mask; +} + +bool KeyboardEventImpl::isKeyboardEvent() const +{ + return true; +} + +void KeyboardEventImpl::initKeyboardEvent(const DOMString &typeArg, + bool canBubbleArg, + bool cancelableArg, + const AbstractView &viewArg, + const DOMString &keyIdentifierArg, + unsigned long keyLocationArg, + const DOMString& modifiersList) +{ + unsigned keyVal = 0; + unsigned virtKeyVal = 0; + + m_keyLocation = keyLocationArg; + + //Figure out the code information from the key identifier. + if (keyIdentifierArg.length() == 1) { + //Likely to be normal unicode id, unless it's one of the few + //special values. + unsigned short code = keyIdentifierArg.unicode()[0]; + if (code > 0x20 && code != 0x7F) + keyVal = code; + } + + if (!keyVal) //One of special keys, likely. + virtKeyVal = keyIdentifiersToVirtKeys()->toRight(keyIdentifierArg.string().latin1()); + + //Process modifier list. + QStringList mods = + QStringList::split(' ', + modifiersList.string().stripWhiteSpace().simplifyWhiteSpace()); + + unsigned modifiers = 0; + for (QStringList::Iterator i = mods.begin(); i != mods.end(); ++i) + if (unsigned mask = keyModifiersToCode()->toRight((*i).latin1())) + modifiers |= mask; + + initKeyBaseEvent(typeArg, canBubbleArg, cancelableArg, viewArg, + keyVal, virtKeyVal, modifiers); +} + +KeyboardEventImpl::KeyboardEventImpl(QKeyEvent* key, DOM::AbstractViewImpl* view) : + KeyEventBaseImpl(key->type() == QEvent::KeyRelease ? KEYUP_EVENT : KEYDOWN_EVENT, true, true, view, key) +{ + //Try to put something reasonable in location... + //we don't know direction, so guess left + m_keyLocation = DOM_KEY_LOCATION_STANDARD; + switch (m_virtKeyVal) { + case DOM_VK_LEFT_ALT: + case DOM_VK_LEFT_SHIFT: + case DOM_VK_LEFT_CONTROL: + case DOM_VK_META: + m_keyLocation = DOM_KEY_LOCATION_LEFT; + } +} + +int KeyboardEventImpl::keyCode() const +{ + //Keycode on key events always identifies the -key- and not the input, + //so e.g. 'a' will get 'A' + if (m_virtKeyVal != DOM_VK_UNDEFINED) + return m_virtKeyVal; + else + return QChar((unsigned short)m_keyVal).upper().unicode(); +} + +int KeyboardEventImpl::charCode() const +{ + //IE doesn't support charCode at all, and mozilla returns 0 + //on key events. So return 0 here + return 0; +} + + +// ----------------------------------------------------------------------------- +TextEventImpl::TextEventImpl() +{} + +bool TextEventImpl::isTextInputEvent() const +{ + return true; +} + +TextEventImpl::TextEventImpl(QKeyEvent* key, DOM::AbstractViewImpl* view) : + KeyEventBaseImpl(KEYPRESS_EVENT, true, true, view, key) +{ + m_outputString = key->text(); +} + +void TextEventImpl::initTextEvent(const DOMString &typeArg, + bool canBubbleArg, + bool cancelableArg, + const AbstractView &viewArg, + const DOMString& text) +{ + m_outputString = text; + + //See whether we can get a key out of this. + unsigned keyCode = 0; + if (text.length() == 1) + keyCode = text.unicode()[0].unicode(); + initKeyBaseEvent(typeArg, canBubbleArg, cancelableArg, viewArg, + keyCode, 0, 0); +} + +int TextEventImpl::keyCode() const +{ + //Mozilla returns 0 here unless this is a non-unicode key. + //IE stuffs everything here, and so we try to match it.. + if (m_keyVal) + return m_keyVal; + return m_virtKeyVal; +} + +int TextEventImpl::charCode() const +{ + //On text events, in Mozilla charCode is 0 for non-unicode keys, + //and the unicode key otherwise... IE doesn't support this. + if (m_virtKeyVal) + return 0; + return m_keyVal; +} + + +// ----------------------------------------------------------------------------- +MutationEventImpl::MutationEventImpl() +{ + m_relatedNode = 0; + m_prevValue = 0; + m_newValue = 0; + m_attrName = 0; + m_attrChange = 0; +} + +MutationEventImpl::MutationEventImpl(EventId _id, + bool canBubbleArg, + bool cancelableArg, + const Node &relatedNodeArg, + const DOMString &prevValueArg, + const DOMString &newValueArg, + const DOMString &attrNameArg, + unsigned short attrChangeArg) + : EventImpl(_id,canBubbleArg,cancelableArg) +{ + m_relatedNode = relatedNodeArg.handle(); + if (m_relatedNode) + m_relatedNode->ref(); + m_prevValue = prevValueArg.implementation(); + if (m_prevValue) + m_prevValue->ref(); + m_newValue = newValueArg.implementation(); + if (m_newValue) + m_newValue->ref(); + m_attrName = attrNameArg.implementation(); + if (m_attrName) + m_attrName->ref(); + m_attrChange = attrChangeArg; +} + +MutationEventImpl::~MutationEventImpl() +{ + if (m_relatedNode) + m_relatedNode->deref(); + if (m_prevValue) + m_prevValue->deref(); + if (m_newValue) + m_newValue->deref(); + if (m_attrName) + m_attrName->deref(); +} + +void MutationEventImpl::initMutationEvent(const DOMString &typeArg, + bool canBubbleArg, + bool cancelableArg, + const Node &relatedNodeArg, + const DOMString &prevValueArg, + const DOMString &newValueArg, + const DOMString &attrNameArg, + unsigned short attrChangeArg) +{ + EventImpl::initEvent(typeArg,canBubbleArg,cancelableArg); + + if (m_relatedNode) + m_relatedNode->deref(); + if (m_prevValue) + m_prevValue->deref(); + if (m_newValue) + m_newValue->deref(); + if (m_attrName) + m_attrName->deref(); + + m_relatedNode = relatedNodeArg.handle(); + if (m_relatedNode) + m_relatedNode->ref(); + m_prevValue = prevValueArg.implementation(); + if (m_prevValue) + m_prevValue->ref(); + m_newValue = newValueArg.implementation(); + if (m_newValue) + m_newValue->ref(); + m_attrName = attrNameArg.implementation(); + if (m_newValue) + m_newValue->ref(); + m_attrChange = attrChangeArg; +} + +bool MutationEventImpl::isMutationEvent() const +{ + return true; +} + diff --git a/khtml/xml/dom2_eventsimpl.h b/khtml/xml/dom2_eventsimpl.h new file mode 100644 index 000000000..2f2034088 --- /dev/null +++ b/khtml/xml/dom2_eventsimpl.h @@ -0,0 +1,513 @@ +/* + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 2001 Peter Kelly (pmk@post.com) + * (C) 2001 Tobias Anton (anton@stud.fbi.fh-darmstadt.de) + * (C) 2002 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. + * + */ + +#ifndef _DOM_EventsImpl_h_ +#define _DOM_EventsImpl_h_ + +#include "dom/dom2_events.h" +#include "xml/dom2_viewsimpl.h" + +class KHTMLPart; +class QMouseEvent; + +namespace DOM { + +class AbstractViewImpl; +class DOMStringImpl; +class NodeImpl; + +// ### support user-defined events + +class EventImpl : public khtml::Shared +{ +public: + enum EventId { + UNKNOWN_EVENT = 0, + // UI events + DOMFOCUSIN_EVENT, + DOMFOCUSOUT_EVENT, + DOMACTIVATE_EVENT, + // Mouse events + CLICK_EVENT, + MOUSEDOWN_EVENT, + MOUSEUP_EVENT, + MOUSEOVER_EVENT, + MOUSEMOVE_EVENT, + MOUSEOUT_EVENT, + // Mutation events + DOMSUBTREEMODIFIED_EVENT, + DOMNODEINSERTED_EVENT, + DOMNODEREMOVED_EVENT, + DOMNODEREMOVEDFROMDOCUMENT_EVENT, + DOMNODEINSERTEDINTODOCUMENT_EVENT, + DOMATTRMODIFIED_EVENT, + DOMCHARACTERDATAMODIFIED_EVENT, + // HTML events + LOAD_EVENT, + UNLOAD_EVENT, + ABORT_EVENT, + ERROR_EVENT, + SELECT_EVENT, + CHANGE_EVENT, + SUBMIT_EVENT, + RESET_EVENT, + FOCUS_EVENT, + BLUR_EVENT, + RESIZE_EVENT, + SCROLL_EVENT, + // keyboard events + KEYDOWN_EVENT, + KEYUP_EVENT, + KEYPRESS_EVENT, //Mostly corresponds to DOM3 textInput event. + // khtml events (not part of DOM) + KHTML_ECMA_DBLCLICK_EVENT, // for html ondblclick + KHTML_ECMA_CLICK_EVENT, // for html onclick + KHTML_DRAGDROP_EVENT, + KHTML_MOVE_EVENT, + // XMLHttpRequest events + KHTML_READYSTATECHANGE_EVENT + }; + + EventImpl(); + EventImpl(EventId _id, bool canBubbleArg, bool cancelableArg); + virtual ~EventImpl(); + + EventId id() const { return m_id; } + + DOMString type() const { return m_type; } + NodeImpl *target() const { return m_target; } + void setTarget(NodeImpl *_target); + NodeImpl *currentTarget() const { return m_currentTarget; } + void setCurrentTarget(NodeImpl *_currentTarget) { m_currentTarget = _currentTarget; } + unsigned short eventPhase() const { return m_eventPhase; } + void setEventPhase(unsigned short _eventPhase) { m_eventPhase = _eventPhase; } + bool bubbles() const { return m_canBubble; } + bool cancelable() const { return m_cancelable; } + DOMTimeStamp timeStamp(); + void stopPropagation(bool stop) { m_propagationStopped = stop; } + void preventDefault(bool prevent) { if ( m_cancelable ) m_defaultPrevented = prevent; } + + void initEvent(const DOMString &eventTypeArg, bool canBubbleArg, bool cancelableArg); + + virtual bool isUIEvent() const; + virtual bool isMouseEvent() const; + virtual bool isMutationEvent() const; + virtual bool isTextInputEvent() const; + virtual bool isKeyboardEvent() const; + bool isKeyRelatedEvent() const { return isTextInputEvent() || isKeyboardEvent(); } + + bool propagationStopped() const { return m_propagationStopped; } + bool defaultPrevented() const { return m_defaultPrevented; } + + static EventId typeToId(DOMString type); + static DOMString idToType(EventId id); + + void setDefaultHandled() { m_defaultHandled = true; } + bool defaultHandled() const { return m_defaultHandled; } + + DOMString message() const { return m_message; } + void setMessage(const DOMString &_message) { m_message = _message; } + +protected: + DOMStringImpl *m_type; + bool m_canBubble; + bool m_cancelable; + + bool m_propagationStopped; + bool m_defaultPrevented; + bool m_defaultHandled; + EventId m_id : 6; + unsigned short m_eventPhase : 2; + NodeImpl *m_currentTarget; // ref > 0 maintained externally + NodeImpl *m_target; + QDateTime m_createTime; + DOMString m_message; +}; + + + +class UIEventImpl : public EventImpl +{ +public: + UIEventImpl() : m_view(0), m_detail(0) {} + UIEventImpl(EventId _id, + bool canBubbleArg, + bool cancelableArg, + AbstractViewImpl *viewArg, + long detailArg); + virtual ~UIEventImpl(); + AbstractViewImpl *view() const { return m_view; } + long detail() const { return m_detail; } + void initUIEvent(const DOMString &typeArg, + bool canBubbleArg, + bool cancelableArg, + const AbstractView &viewArg, + long detailArg); + virtual bool isUIEvent() const; + +protected: + AbstractViewImpl *m_view; + long m_detail; + +}; + +// Introduced in DOM Level 2: - internal +class MouseEventImpl : public UIEventImpl { +public: + MouseEventImpl(); + MouseEventImpl(EventId _id, + bool canBubbleArg, + bool cancelableArg, + AbstractViewImpl *viewArg, + long detailArg, + long screenXArg, + long screenYArg, + long clientXArg, + long clientYArg, + long pageXArg, + long pageYArg, + bool ctrlKeyArg, + bool altKeyArg, + bool shiftKeyArg, + bool metaKeyArg, + unsigned short buttonArg, + NodeImpl *relatedTargetArg, + QMouseEvent *qe = 0, + bool isDoubleClick = false); + virtual ~MouseEventImpl(); + long screenX() const { return m_screenX; } + long screenY() const { return m_screenY; } + long clientX() const { return m_clientX; } + long clientY() const { return m_clientY; } + long layerX() const { return m_layerX; } // non-DOM extension + long layerY() const { return m_layerY; } // non-DOM extension + long pageX() const { return m_pageX; } // non-DOM extension + long pageY() const { return m_pageY; } // non-DOM extension + bool isDoubleClick() const { return m_isDoubleClick; } // non-DOM extension + bool ctrlKey() const { return m_ctrlKey; } + bool shiftKey() const { return m_shiftKey; } + bool altKey() const { return m_altKey; } + bool metaKey() const { return m_metaKey; } + unsigned short button() const { return m_button; } + NodeImpl *relatedTarget() const { return m_relatedTarget; } + + void computeLayerPos(); + + void initMouseEvent(const DOMString &typeArg, + bool canBubbleArg, + bool cancelableArg, + const AbstractView &viewArg, + long detailArg, + long screenXArg, + long screenYArg, + long clientXArg, + long clientYArg, + bool ctrlKeyArg, + bool altKeyArg, + bool shiftKeyArg, + bool metaKeyArg, + unsigned short buttonArg, + const Node &relatedTargetArg); + virtual bool isMouseEvent() const; + + QMouseEvent *qEvent() const { return m_qevent; } +protected: + long m_screenX; + long m_screenY; + long m_clientX; + long m_clientY; + long m_layerX; + long m_layerY; + long m_pageX; + long m_pageY; + bool m_ctrlKey : 1; + bool m_altKey : 1; + bool m_shiftKey : 1; + bool m_metaKey : 1; + bool m_isDoubleClick : 1; + unsigned short m_button; + NodeImpl *m_relatedTarget; + QMouseEvent *m_qevent; +}; + + +class KeyEventBaseImpl : public UIEventImpl { +public: + // VirtualKeyCode + enum KeyCodes { + DOM_VK_UNDEFINED = 0x0, + DOM_VK_RIGHT_ALT = 0x12, + DOM_VK_LEFT_ALT = 0x12, + DOM_VK_LEFT_CONTROL = 0x11, + DOM_VK_RIGHT_CONTROL = 0x11, + DOM_VK_LEFT_SHIFT = 0x10, + DOM_VK_RIGHT_SHIFT = 0x10, + DOM_VK_META = 0x9D, + DOM_VK_BACK_SPACE = 0x08, + DOM_VK_CAPS_LOCK = 0x14, + DOM_VK_DELETE = 0x7F, + DOM_VK_END = 0x23, + DOM_VK_ENTER = 0x0D, + DOM_VK_ESCAPE = 0x1B, + DOM_VK_HOME = 0x24, + DOM_VK_NUM_LOCK = 0x90, + DOM_VK_PAUSE = 0x13, + DOM_VK_PRINTSCREEN = 0x9A, + DOM_VK_SCROLL_LOCK = 0x91, + DOM_VK_SPACE = 0x20, + DOM_VK_TAB = 0x09, + DOM_VK_LEFT = 0x25, + DOM_VK_RIGHT = 0x27, + DOM_VK_UP = 0x26, + DOM_VK_DOWN = 0x28, + DOM_VK_PAGE_DOWN = 0x22, + DOM_VK_PAGE_UP = 0x21, + DOM_VK_F1 = 0x70, + DOM_VK_F2 = 0x71, + DOM_VK_F3 = 0x72, + DOM_VK_F4 = 0x73, + DOM_VK_F5 = 0x74, + DOM_VK_F6 = 0x75, + DOM_VK_F7 = 0x76, + DOM_VK_F8 = 0x77, + DOM_VK_F9 = 0x78, + DOM_VK_F10 = 0x79, + DOM_VK_F11 = 0x7A, + DOM_VK_F12 = 0x7B, + DOM_VK_F13 = 0xF000, + DOM_VK_F14 = 0xF001, + DOM_VK_F15 = 0xF002, + DOM_VK_F16 = 0xF003, + DOM_VK_F17 = 0xF004, + DOM_VK_F18 = 0xF005, + DOM_VK_F19 = 0xF006, + DOM_VK_F20 = 0xF007, + DOM_VK_F21 = 0xF008, + DOM_VK_F22 = 0xF009, + DOM_VK_F23 = 0xF00A, + DOM_VK_F24 = 0xF00B + }; + + void initKeyBaseEvent(const DOMString &typeArg, + bool canBubbleArg, + bool cancelableArg, + const AbstractView &viewArg, + unsigned long keyVal, + unsigned long virtKeyVal, + unsigned long modifiers); + + bool ctrlKey() const { return m_modifier & Qt::ControlButton; } + bool shiftKey() const { return m_modifier & Qt::ShiftButton; } + bool altKey() const { return m_modifier & Qt::AltButton; } + bool metaKey() const { return m_modifier & Qt::MetaButton; } + + bool inputGenerated() const { return m_virtKeyVal == 0; } + unsigned long keyVal() const { return m_keyVal; } + unsigned long virtKeyVal() const { return m_virtKeyVal; } + + QKeyEvent *qKeyEvent() const { if (!m_keyEvent) buildQKeyEvent(); return m_keyEvent; } + + //Legacy key stuff... + virtual int keyCode() const = 0; + virtual int charCode() const = 0; + + //### KDE4: remove these 2 + void initModifier(unsigned long modifierArg, bool valueArg); + bool checkModifier(unsigned long modifierArg); + + ~KeyEventBaseImpl(); + + //Returns true if the event was synthesized by client use of DOM + bool isSynthetic() const { return m_synthetic; } +protected: + KeyEventBaseImpl(): m_keyEvent(0), m_keyVal(0), m_virtKeyVal(0), m_modifier(0), m_synthetic(false) + { m_detail = 0; } + + KeyEventBaseImpl(EventId id, + bool canBubbleArg, + bool cancelableArg, + AbstractViewImpl *viewArg, + QKeyEvent *key); + + + mutable QKeyEvent *m_keyEvent; + unsigned long m_keyVal; //Unicode key value + unsigned long m_virtKeyVal; //Virtual key value for keys like arrows, Fn, etc. + + // bitfield containing state of modifiers. not part of the dom. + unsigned long m_modifier; + + bool m_synthetic; + + void buildQKeyEvent() const; //Construct a Qt key event from m_keyVal/m_virtKeyVal +}; + +class TextEventImpl : public KeyEventBaseImpl { +public: + TextEventImpl(); + + TextEventImpl(QKeyEvent* key, DOM::AbstractViewImpl* view); + + void initTextEvent(const DOMString &typeArg, + bool canBubbleArg, + bool cancelableArg, + const AbstractView &viewArg, + const DOMString& text); + + virtual bool isTextInputEvent() const; + + //Legacy key stuff... + int keyCode() const; + int charCode() const; + + DOMString data() const { return m_outputString; } +private: + DOMString m_outputString; +}; + +class KeyboardEventImpl : public KeyEventBaseImpl { +public: + KeyboardEventImpl(); + KeyboardEventImpl(QKeyEvent* key, DOM::AbstractViewImpl* view); + + virtual bool isKeyboardEvent() const; + + enum KeyLocation { + DOM_KEY_LOCATION_STANDARD = 0x00, + DOM_KEY_LOCATION_LEFT = 0x01, + DOM_KEY_LOCATION_RIGHT = 0x02, + DOM_KEY_LOCATION_NUMPAD = 0x03 + }; + + //Legacy key stuff... + int keyCode() const; + int charCode() const; + + DOMString keyIdentifier() const; + unsigned long keyLocation() const { return m_keyLocation; } + + bool getModifierState(const DOMString& keyIdentifierArg) const; + + void initKeyboardEvent(const DOMString &typeArg, + bool canBubbleArg, + bool cancelableArg, + const AbstractView &viewArg, + const DOMString &keyIdentifierArg, + unsigned long keyLocationArg, + const DOMString& modifiersList); + + //### KDE4: remove this, it's only for compatibility with + //the old TextEvent wrapper + void initKeyboardEvent(const DOMString &typeArg, + bool canBubbleArg, + bool cancelableArg, + const AbstractView &viewArg, + unsigned long keyVal, + unsigned long virtKeyVal, + unsigned long modifiers, + unsigned long keyLocationArg) { + initKeyBaseEvent(typeArg, canBubbleArg, cancelableArg, viewArg, + keyVal, virtKeyVal, modifiers); + m_keyLocation = keyLocationArg; + } +private: + unsigned long m_keyLocation; +}; + + + +class MutationEventImpl : public EventImpl { +// ### fire these during parsing (if necessary) +public: + MutationEventImpl(); + MutationEventImpl(EventId _id, + bool canBubbleArg, + bool cancelableArg, + const Node &relatedNodeArg, + const DOMString &prevValueArg, + const DOMString &newValueArg, + const DOMString &attrNameArg, + unsigned short attrChangeArg); + ~MutationEventImpl(); + + Node relatedNode() const { return m_relatedNode; } + DOMString prevValue() const { return m_prevValue; } + DOMString newValue() const { return m_newValue; } + DOMString attrName() const { return m_attrName; } + unsigned short attrChange() const { return m_attrChange; } + void initMutationEvent(const DOMString &typeArg, + bool canBubbleArg, + bool cancelableArg, + const Node &relatedNodeArg, + const DOMString &prevValueArg, + const DOMString &newValueArg, + const DOMString &attrNameArg, + unsigned short attrChangeArg); + virtual bool isMutationEvent() const; +protected: + NodeImpl *m_relatedNode; + DOMStringImpl *m_prevValue; + DOMStringImpl *m_newValue; + DOMStringImpl *m_attrName; + unsigned short m_attrChange; +}; + + +class RegisteredEventListener { +public: + RegisteredEventListener() : id(EventImpl::EventId(0)), useCapture(false), listener(0) {} + + RegisteredEventListener(EventImpl::EventId _id, EventListener *_listener, bool _useCapture) + : id(_id), useCapture(_useCapture), listener(_listener) { if (listener) listener->ref(); } + + ~RegisteredEventListener() { if (listener) listener->deref(); listener = 0; } + + bool operator==(const RegisteredEventListener &other) const + { return id == other.id && listener == other.listener && useCapture == other.useCapture; } + + + EventImpl::EventId id : 6; + bool useCapture; + EventListener *listener; + + RegisteredEventListener( const RegisteredEventListener &other ) : + id(other.id), useCapture(other.useCapture), listener(other.listener) + { if (listener) listener->ref(); } + + RegisteredEventListener & operator=( const RegisteredEventListener &other ) { + id = other.id; + useCapture = other.useCapture; + if (other.listener) + other.listener->ref(); + if (listener) + listener->deref(); + listener = other.listener; + return *this; + } +}; + + + +} //namespace +#endif diff --git a/khtml/xml/dom2_rangeimpl.cpp b/khtml/xml/dom2_rangeimpl.cpp new file mode 100644 index 000000000..b160ce23f --- /dev/null +++ b/khtml/xml/dom2_rangeimpl.cpp @@ -0,0 +1,1640 @@ +/** + * This file is part of the DOM implementation for KDE. + * + * (C) 1999-2003 Lars Knoll (knoll@kde.org) + * (C) 2001-2003 Dirk Mueller (mueller@kde.org) + * (C) 2000 Gunnstein Lye (gunnstein@netcom.no) + * (C) 2000 Frederik Holljen (frederik.holljen@hig.no) + * (C) 2001 Peter Kelly (pmk@post.com) + * Copyright (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 "dom_docimpl.h" +#include "dom2_rangeimpl.h" +#include "dom_textimpl.h" +#include "dom_xmlimpl.h" +#include "html/html_elementimpl.h" +#include "misc/htmltags.h" + +using namespace DOM; + + +RangeImpl::RangeImpl(DocumentImpl *_ownerDocument) +{ + m_ownerDocument = _ownerDocument; + m_ownerDocument->ref(); + m_startContainer = _ownerDocument; + m_startContainer->ref(); + m_endContainer = _ownerDocument; + m_endContainer->ref(); + m_startOffset = 0; + m_endOffset = 0; + m_detached = false; +} + +RangeImpl::RangeImpl(DocumentImpl *_ownerDocument, + NodeImpl *_startContainer, long _startOffset, + NodeImpl *_endContainer, long _endOffset) +{ + m_ownerDocument = _ownerDocument; + m_ownerDocument->ref(); + m_startContainer = _startContainer; + m_startContainer->ref(); + m_startOffset = _startOffset; + m_endContainer = _endContainer; + m_endContainer->ref(); + m_endOffset = _endOffset; + m_detached = false; +} + +RangeImpl::~RangeImpl() +{ + m_ownerDocument->deref(); + int exceptioncode = 0; + if (!m_detached) + detach(exceptioncode); +} + +NodeImpl *RangeImpl::startContainer(int &exceptioncode) const +{ + if (m_detached) { + exceptioncode = DOMException::INVALID_STATE_ERR; + return 0; + } + + return m_startContainer; +} + +long RangeImpl::startOffset(int &exceptioncode) const +{ + if (m_detached) { + exceptioncode = DOMException::INVALID_STATE_ERR; + return 0; + } + + return m_startOffset; +} + +NodeImpl *RangeImpl::endContainer(int &exceptioncode) const +{ + if (m_detached) { + exceptioncode = DOMException::INVALID_STATE_ERR; + return 0; + } + + return m_endContainer; +} + +long RangeImpl::endOffset(int &exceptioncode) const +{ + if (m_detached) { + exceptioncode = DOMException::INVALID_STATE_ERR; + return 0; + } + + return m_endOffset; +} + +NodeImpl *RangeImpl::commonAncestorContainer(int &exceptioncode) +{ + if (m_detached) { + exceptioncode = DOMException::INVALID_STATE_ERR; + return 0; + } + + NodeImpl *com = commonAncestorContainer(m_startContainer,m_endContainer); + if (!com) // should never happen + exceptioncode = DOMException::WRONG_DOCUMENT_ERR; + return com; +} + +NodeImpl *RangeImpl::commonAncestorContainer(NodeImpl *containerA, NodeImpl *containerB) +{ + NodeImpl *parentStart; + + for (parentStart = containerA; parentStart; parentStart = parentStart->parentNode()) { + NodeImpl *parentEnd = containerB; + while( parentEnd && (parentStart != parentEnd) ) + parentEnd = parentEnd->parentNode(); + + if(parentStart == parentEnd) break; + } + + return parentStart; +} + +bool RangeImpl::collapsed(int &exceptioncode) const +{ + if (m_detached) { + exceptioncode = DOMException::INVALID_STATE_ERR; + return 0; + } + + return (m_startContainer == m_endContainer && m_startOffset == m_endOffset); +} + +void RangeImpl::setStart( NodeImpl *refNode, long offset, int &exceptioncode ) +{ + if (m_detached) { + exceptioncode = DOMException::INVALID_STATE_ERR; + return; + } + + if (!refNode) { + exceptioncode = DOMException::NOT_FOUND_ERR; + return; + } + + if (refNode->getDocument() != m_ownerDocument) { + exceptioncode = DOMException::WRONG_DOCUMENT_ERR; + return; + } + + checkNodeWOffset( refNode, offset, exceptioncode ); + if (exceptioncode) + return; + + setStartContainer(refNode); + m_startOffset = offset; + + // check if different root container + NodeImpl *endRootContainer = m_endContainer; + while (endRootContainer->parentNode()) + endRootContainer = endRootContainer->parentNode(); + NodeImpl *startRootContainer = m_startContainer; + while (startRootContainer->parentNode()) + startRootContainer = startRootContainer->parentNode(); + if (startRootContainer != endRootContainer) + collapse(true,exceptioncode); + // check if new start after end + else if (compareBoundaryPoints(m_startContainer,m_startOffset,m_endContainer,m_endOffset) > 0) + collapse(true,exceptioncode); +} + +void RangeImpl::setEnd( NodeImpl *refNode, long offset, int &exceptioncode ) +{ + if (m_detached) { + exceptioncode = DOMException::INVALID_STATE_ERR; + return; + } + + if (!refNode) { + exceptioncode = DOMException::NOT_FOUND_ERR; + return; + } + + if (refNode->getDocument() != m_ownerDocument) { + exceptioncode = DOMException::WRONG_DOCUMENT_ERR; + return; + } + + checkNodeWOffset( refNode, offset, exceptioncode ); + if (exceptioncode) + return; + + setEndContainer(refNode); + m_endOffset = offset; + + // check if different root container + NodeImpl *endRootContainer = m_endContainer; + while (endRootContainer->parentNode()) + endRootContainer = endRootContainer->parentNode(); + NodeImpl *startRootContainer = m_startContainer; + while (startRootContainer->parentNode()) + startRootContainer = startRootContainer->parentNode(); + if (startRootContainer != endRootContainer) + collapse(false,exceptioncode); + // check if new end before start + if (compareBoundaryPoints(m_startContainer,m_startOffset,m_endContainer,m_endOffset) > 0) + collapse(false,exceptioncode); +} + +void RangeImpl::collapse( bool toStart, int &exceptioncode ) +{ + if (m_detached) { + exceptioncode = DOMException::INVALID_STATE_ERR; + return; + } + + if( toStart ) // collapse to start + { + setEndContainer(m_startContainer); + m_endOffset = m_startOffset; + } + else // collapse to end + { + setStartContainer(m_endContainer); + m_startOffset = m_endOffset; + } +} + +short RangeImpl::compareBoundaryPoints( Range::CompareHow how, RangeImpl *sourceRange, int &exceptioncode ) +{ + if (m_detached) { + exceptioncode = DOMException::INVALID_STATE_ERR; + return 0; + } + + if (!sourceRange) { + exceptioncode = DOMException::NOT_FOUND_ERR; + return 0; + } + + NodeImpl *thisCont = commonAncestorContainer(exceptioncode); + NodeImpl *sourceCont = sourceRange->commonAncestorContainer(exceptioncode); + if (exceptioncode) + return 0; + + if (thisCont->getDocument() != sourceCont->getDocument()) { + exceptioncode = DOMException::WRONG_DOCUMENT_ERR; + return 0; + } + + NodeImpl *thisTop = thisCont; + NodeImpl *sourceTop = sourceCont; + while (thisTop->parentNode()) + thisTop = thisTop->parentNode(); + while (sourceTop->parentNode()) + sourceTop = sourceTop->parentNode(); + if (thisTop != sourceTop) { // in different DocumentFragments + exceptioncode = DOMException::WRONG_DOCUMENT_ERR; + return 0; + } + + switch(how) + { + case Range::START_TO_START: + return compareBoundaryPoints( m_startContainer, m_startOffset, + sourceRange->startContainer(exceptioncode), sourceRange->startOffset(exceptioncode) ); + break; + case Range::START_TO_END: + return compareBoundaryPoints( m_startContainer, m_startOffset, + sourceRange->endContainer(exceptioncode), sourceRange->endOffset(exceptioncode) ); + break; + case Range::END_TO_END: + return compareBoundaryPoints( m_endContainer, m_endOffset, + sourceRange->endContainer(exceptioncode), sourceRange->endOffset(exceptioncode) ); + break; + case Range::END_TO_START: + return compareBoundaryPoints( m_endContainer, m_endOffset, + sourceRange->startContainer(exceptioncode), sourceRange->startOffset(exceptioncode) ); + break; + default: + exceptioncode = DOMException::SYNTAX_ERR; + return 0; + } +} + +short RangeImpl::compareBoundaryPoints( NodeImpl *containerA, long offsetA, NodeImpl *containerB, long offsetB ) +{ + // see DOM2 traversal & range section 2.5 + + // case 1: both points have the same container + if( containerA == containerB ) + { + if( offsetA == offsetB ) return 0; // A is equal to B + if( offsetA < offsetB ) return -1; // A is before B + else return 1; // A is after B + } + + // case 2: node C (container B or an ancestor) is a child node of A + NodeImpl *c = containerB; + while (c && c->parentNode() != containerA) + c = c->parentNode(); + if (c) { + int offsetC = 0; + NodeImpl* n = containerA->firstChild(); + while (n != c) { + offsetC++; + n = n->nextSibling(); + } + + if( offsetA <= offsetC ) return -1; // A is before B + else return 1; // A is after B + } + + // case 3: node C (container A or an ancestor) is a child node of B + c = containerA; + while (c && c->parentNode() != containerB) + c = c->parentNode(); + if (c) { + int offsetC = 0; + NodeImpl* n = containerB->firstChild(); + while (n != c) { + offsetC++; + n = n->nextSibling(); + } + + if( offsetC < offsetB ) return -1; // A is before B + else return 1; // A is after B + } + + // case 4: containers A & B are siblings, or children of siblings + // ### we need to do a traversal here instead + NodeImpl *cmnRoot = commonAncestorContainer(containerA,containerB); + if (!cmnRoot) return -1; // Whatever... + NodeImpl *childA = containerA; + while (childA->parentNode() != cmnRoot) + childA = childA->parentNode(); + NodeImpl *childB = containerB; + while (childB->parentNode() != cmnRoot) + childB = childB->parentNode(); + + NodeImpl *n = cmnRoot->firstChild(); + int i = 0; + int childAOffset = -1; + int childBOffset = -1; + while (childAOffset < 0 || childBOffset < 0) { + if (n == childA) + childAOffset = i; + if (n == childB) + childBOffset = i; + n = n->nextSibling(); + i++; + } + + if( childAOffset == childBOffset ) return 0; // A is equal to B + if( childAOffset < childBOffset ) return -1; // A is before B + else return 1; // A is after B +} + +bool RangeImpl::boundaryPointsValid( ) +{ + short valid = compareBoundaryPoints( m_startContainer, m_startOffset, + m_endContainer, m_endOffset ); + if( valid == 1 ) return false; + else return true; + +} + +void RangeImpl::deleteContents( int &exceptioncode ) { + if (m_detached) { + exceptioncode = DOMException::INVALID_STATE_ERR; + return; + } + + checkDeleteExtract(exceptioncode); + if (exceptioncode) + return; + + processContents(DELETE_CONTENTS,exceptioncode); +} + +DocumentFragmentImpl *RangeImpl::processContents ( ActionType action, int &exceptioncode ) +{ + // ### when mutation events are implemented, we will have to take into account + // situations where the tree is being transformed while we delete - ugh! + + // ### perhaps disable node deletion notification for this range while we do this? + + if (collapsed(exceptioncode)) + return 0; + if (exceptioncode) + return 0; + + NodeImpl *cmnRoot = commonAncestorContainer(exceptioncode); + if (exceptioncode) + return 0; + + // what is the highest node that partially selects the start of the range? + NodeImpl *partialStart = 0; + if (m_startContainer != cmnRoot) { + partialStart = m_startContainer; + while (partialStart->parentNode() != cmnRoot) + partialStart = partialStart->parentNode(); + } + + // what is the highest node that partially selects the end of the range? + NodeImpl *partialEnd = 0; + if (m_endContainer != cmnRoot) { + partialEnd = m_endContainer; + while (partialEnd->parentNode() != cmnRoot) + partialEnd = partialEnd->parentNode(); + } + + DocumentFragmentImpl *fragment = 0; + if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) + fragment = new DocumentFragmentImpl(m_ownerDocument); + + // Simple case: the start and end containers are the same. We just grab + // everything >= start offset and < end offset + if (m_startContainer == m_endContainer) { + if(m_startContainer->nodeType() == Node::TEXT_NODE || + m_startContainer->nodeType() == Node::CDATA_SECTION_NODE || + m_startContainer->nodeType() == Node::COMMENT_NODE) { + + if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) { + CharacterDataImpl *c = static_cast(m_startContainer->cloneNode(true)); + c->deleteData(m_endOffset,static_cast(m_startContainer)->length()-m_endOffset,exceptioncode); + c->deleteData(0,m_startOffset,exceptioncode); + fragment->appendChild(c,exceptioncode); + } + if (action == EXTRACT_CONTENTS || action == DELETE_CONTENTS) + static_cast(m_startContainer)->deleteData(m_startOffset,m_endOffset-m_startOffset,exceptioncode); + } + else if (m_startContainer->nodeType() == Node::PROCESSING_INSTRUCTION_NODE) { + // ### operate just on data ? + } + else { + NodeImpl *n = m_startContainer->firstChild(); + unsigned long i; + for(i = 0; i < m_startOffset; i++) // skip until m_startOffset + n = n->nextSibling(); + while (n && i < m_endOffset) { // delete until m_endOffset + NodeImpl *next = n->nextSibling(); + if (action == EXTRACT_CONTENTS) + fragment->appendChild(n,exceptioncode); // will remove n from its parent + else if (action == CLONE_CONTENTS) + fragment->appendChild(n->cloneNode(true),exceptioncode); + else + m_startContainer->removeChild(n,exceptioncode); + n = next; + i++; + } + } + collapse(true,exceptioncode); + return fragment; + } + + // Complex case: Start and end containers are different. + // There are three possiblities here: + // 1. Start container == cmnRoot (End container must be a descendant) + // 2. End container == cmnRoot (Start container must be a descendant) + // 3. Neither is cmnRoot, they are both descendants + // + // In case 3, we grab everything after the start (up until a direct child + // of cmnRoot) into leftContents, and everything before the end (up until + // a direct child of cmnRoot) into rightContents. Then we process all + // cmnRoot children between leftContents and rightContents + // + // In case 1 or 2, we skip either processing of leftContents or rightContents, + // in which case the last lot of nodes either goes from the first or last + // child of cmnRoot. + // + // These are deleted, cloned, or extracted (i.e. both) depending on action. + + NodeImpl *leftContents = 0; + if (m_startContainer != cmnRoot) { + // process the left-hand side of the range, up until the last ancestor of + // m_startContainer before cmnRoot + if(m_startContainer->nodeType() == Node::TEXT_NODE || + m_startContainer->nodeType() == Node::CDATA_SECTION_NODE || + m_startContainer->nodeType() == Node::COMMENT_NODE) { + + if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) { + CharacterDataImpl *c = static_cast(m_startContainer->cloneNode(true)); + c->deleteData(0,m_startOffset,exceptioncode); + leftContents = c; + } + if (action == EXTRACT_CONTENTS || action == DELETE_CONTENTS) + static_cast(m_startContainer)->deleteData( + m_startOffset,static_cast(m_startContainer)->length()-m_startOffset,exceptioncode); + } + else if (m_startContainer->nodeType() == Node::PROCESSING_INSTRUCTION_NODE) { + // ### operate just on data ? + // leftContents = ... + } + else { + if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) + leftContents = m_startContainer->cloneNode(false); + NodeImpl *n = m_startContainer->firstChild(); + unsigned long i; + for(i = 0; i < m_startOffset; i++) // skip until m_startOffset + n = n->nextSibling(); + while (n) { // process until end + NodeImpl *next = n->nextSibling(); + if (action == EXTRACT_CONTENTS) + leftContents->appendChild(n,exceptioncode); // will remove n from m_startContainer + else if (action == CLONE_CONTENTS) + leftContents->appendChild(n->cloneNode(true),exceptioncode); + else + m_startContainer->removeChild(n,exceptioncode); + n = next; + } + } + + NodeImpl *leftParent = m_startContainer->parentNode(); + NodeImpl *n = m_startContainer->nextSibling(); + for (; leftParent != cmnRoot; leftParent = leftParent->parentNode()) { + if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) { + NodeImpl *leftContentsParent = leftParent->cloneNode(false); + leftContentsParent->appendChild(leftContents,exceptioncode); + leftContents = leftContentsParent; + } + + NodeImpl *next; + for (; n; n = next ) { + next = n->nextSibling(); + if (action == EXTRACT_CONTENTS) + leftContents->appendChild(n,exceptioncode); // will remove n from leftParent + else if (action == CLONE_CONTENTS) + leftContents->appendChild(n->cloneNode(true),exceptioncode); + else + leftParent->removeChild(n,exceptioncode); + } + n = leftParent->nextSibling(); + } + } + + NodeImpl *rightContents = 0; + if (m_endContainer != cmnRoot) { + // delete the right-hand side of the range, up until the last ancestor of + // m_endContainer before cmnRoot + if(m_endContainer->nodeType() == Node::TEXT_NODE || + m_endContainer->nodeType() == Node::CDATA_SECTION_NODE || + m_endContainer->nodeType() == Node::COMMENT_NODE) { + + if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) { + CharacterDataImpl *c = static_cast(m_endContainer->cloneNode(true)); + c->deleteData(m_endOffset,static_cast(m_endContainer)->length()-m_endOffset,exceptioncode); + rightContents = c; + } + if (action == EXTRACT_CONTENTS || action == DELETE_CONTENTS) + static_cast(m_endContainer)->deleteData(0,m_endOffset,exceptioncode); + } + else if (m_startContainer->nodeType() == Node::PROCESSING_INSTRUCTION_NODE) { + // ### operate just on data ? + // rightContents = ... + } + else { + if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) + rightContents = m_endContainer->cloneNode(false); + NodeImpl *n = m_endContainer->firstChild(); + unsigned long i; + for(i = 0; i+1 < m_endOffset; i++) // skip to m_endOffset + n = n->nextSibling(); + NodeImpl *prev; + for (; n; n = prev ) { + prev = n->previousSibling(); + if (action == EXTRACT_CONTENTS) + rightContents->insertBefore(n,rightContents->firstChild(),exceptioncode); // will remove n from its parent + else if (action == CLONE_CONTENTS) + rightContents->insertBefore(n->cloneNode(true),rightContents->firstChild(),exceptioncode); + else + m_endContainer->removeChild(n,exceptioncode); + } + } + + NodeImpl *rightParent = m_endContainer->parentNode(); + NodeImpl *n = m_endContainer->previousSibling(); + for (; rightParent != cmnRoot; rightParent = rightParent->parentNode()) { + if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) { + NodeImpl *rightContentsParent = rightParent->cloneNode(false); + rightContentsParent->appendChild(rightContents,exceptioncode); + rightContents = rightContentsParent; + } + + NodeImpl *prev; + for (; n; n = prev ) { + prev = n->previousSibling(); + if (action == EXTRACT_CONTENTS) + rightContents->insertBefore(n,rightContents->firstChild(),exceptioncode); // will remove n from its parent + else if (action == CLONE_CONTENTS) + rightContents->insertBefore(n->cloneNode(true),rightContents->firstChild(),exceptioncode); + else + rightParent->removeChild(n,exceptioncode); + + } + n = rightParent->previousSibling(); + } + } + + // delete all children of cmnRoot between the start and end container + + NodeImpl *processStart; // child of cmnRooot + if (m_startContainer == cmnRoot) { + unsigned long i; + processStart = m_startContainer->firstChild(); + for (i = 0; i < m_startOffset; i++) + processStart = processStart->nextSibling(); + } + else { + processStart = m_startContainer; + while (processStart->parentNode() != cmnRoot) + processStart = processStart->parentNode(); + processStart = processStart->nextSibling(); + } + NodeImpl *processEnd; // child of cmnRooot + if (m_endContainer == cmnRoot) { + unsigned long i; + processEnd = m_endContainer->firstChild(); + for (i = 0; i < m_endOffset; i++) + processEnd = processEnd->nextSibling(); + } + else { + processEnd = m_endContainer; + while (processEnd->parentNode() != cmnRoot) + processEnd = processEnd->parentNode(); + } + + // Now add leftContents, stuff in between, and rightContents to the fragment + // (or just delete the stuff in between) + + if ((action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) && leftContents) + fragment->appendChild(leftContents,exceptioncode); + + NodeImpl *next; + NodeImpl *n; + if (processStart) { + for (n = processStart; n && n != processEnd; n = next) { + next = n->nextSibling(); + + if (action == EXTRACT_CONTENTS) + fragment->appendChild(n,exceptioncode); // will remove from cmnRoot + else if (action == CLONE_CONTENTS) + fragment->appendChild(n->cloneNode(true),exceptioncode); + else + cmnRoot->removeChild(n,exceptioncode); + } + } + + if ((action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) && rightContents) + fragment->appendChild(rightContents,exceptioncode); + + // collapse to the proper position - see spec section 2.6 + if (action == EXTRACT_CONTENTS || action == DELETE_CONTENTS) { + if (!partialStart && !partialEnd) + collapse(true,exceptioncode); + else if (partialStart) { + setStartContainer(partialStart->parentNode()); + setEndContainer(partialStart->parentNode()); + m_startOffset = m_endOffset = partialStart->nodeIndex()+1; + } + else if (partialEnd) { + setStartContainer(partialEnd->parentNode()); + setEndContainer(partialEnd->parentNode()); + m_startOffset = m_endOffset = partialEnd->nodeIndex(); + } + } + return fragment; +} + + +DocumentFragmentImpl *RangeImpl::extractContents( int &exceptioncode ) +{ + if (m_detached) { + exceptioncode = DOMException::INVALID_STATE_ERR; + return 0; + } + + checkDeleteExtract(exceptioncode); + if (exceptioncode) + return 0; + + return processContents(EXTRACT_CONTENTS,exceptioncode); +} + +DocumentFragmentImpl *RangeImpl::cloneContents( int &exceptioncode ) +{ + if (m_detached) { + exceptioncode = DOMException::INVALID_STATE_ERR; + return 0; + } + + return processContents(CLONE_CONTENTS,exceptioncode); +} + +void RangeImpl::insertNode( NodeImpl *newNode, int &exceptioncode ) +{ + if (m_detached) { + exceptioncode = DOMException::INVALID_STATE_ERR; + return; + } + + // NO_MODIFICATION_ALLOWED_ERR: Raised if an ancestor container of either boundary-point of + // the Range is read-only. + NodeImpl *n = m_startContainer; + while (n && !n->isReadOnly()) + n = n->parentNode(); + if (n) { + exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR; + return; + } + + n = m_endContainer; + while (n && !n->isReadOnly()) + n = n->parentNode(); + if (n) { + exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR; + return; + } + + // WRONG_DOCUMENT_ERR: Raised if newParent and the container of the start of the Range were + // not created from the same document. + if (newNode->getDocument() != m_startContainer->getDocument()) { + exceptioncode = DOMException::WRONG_DOCUMENT_ERR; + return; + } + + + // HIERARCHY_REQUEST_ERR: Raised if the container of the start of the Range is of a type that + // does not allow children of the type of newNode or if newNode is an ancestor of the container. + + // an extra one here - if a text node is going to split, it must have a parent to insert into + if (m_startContainer->nodeType() == Node::TEXT_NODE && !m_startContainer->parentNode()) { + exceptioncode = DOMException::HIERARCHY_REQUEST_ERR; + return; + } + + // In the case where the container is a text node, we check against the container's parent, because + // text nodes get split up upon insertion. + NodeImpl *checkAgainst; + if (m_startContainer->nodeType() == Node::TEXT_NODE) + checkAgainst = m_startContainer->parentNode(); + else + checkAgainst = m_startContainer; + + if (newNode->nodeType() == Node::DOCUMENT_FRAGMENT_NODE) { + // check each child node, not the DocumentFragment itself + NodeImpl *c; + for (c = newNode->firstChild(); c; c = c->nextSibling()) { + if (!checkAgainst->childTypeAllowed(c->nodeType())) { + exceptioncode = DOMException::HIERARCHY_REQUEST_ERR; + return; + } + } + } + else { + if (!checkAgainst->childTypeAllowed(newNode->nodeType())) { + exceptioncode = DOMException::HIERARCHY_REQUEST_ERR; + return; + } + } + + for (n = m_startContainer; n; n = n->parentNode()) { + if (n == newNode) { + exceptioncode = DOMException::HIERARCHY_REQUEST_ERR; + return; + } + } + + // INVALID_NODE_TYPE_ERR: Raised if newNode is an Attr, Entity, Notation, or Document node. + if( newNode->nodeType() == Node::ATTRIBUTE_NODE || + newNode->nodeType() == Node::ENTITY_NODE || + newNode->nodeType() == Node::NOTATION_NODE || + newNode->nodeType() == Node::DOCUMENT_NODE) { + exceptioncode = RangeException::INVALID_NODE_TYPE_ERR + RangeException::_EXCEPTION_OFFSET; + return; + } + + if( m_startContainer->nodeType() == Node::TEXT_NODE || + m_startContainer->nodeType() == Node::CDATA_SECTION_NODE ) + { + TextImpl *newText = static_cast(m_startContainer)->splitText(m_startOffset,exceptioncode); + if (exceptioncode) + return; + m_startContainer->parentNode()->insertBefore( newNode, newText, exceptioncode ); + } + else { + m_startContainer->insertBefore( newNode, m_startContainer->childNode( m_startOffset ), exceptioncode ); + } +} + +DOMString RangeImpl::toString( int &exceptioncode ) +{ + if (m_detached) { + exceptioncode = DOMException::INVALID_STATE_ERR; + return DOMString(); + } + + DOMString text = ""; + NodeImpl *n = m_startContainer; + + /* This function converts a dom range to the plain text string that the user would see in this + * portion of rendered html. + * + * There are several ways ranges can be used. + * + * The simplest is the start and endContainer is a text node. The start and end offset is the + * number of characters into the text to remove/truncate. + * + * The next case is the start and endContainer is, well, a container, such a P tag or DIV tag. + * In this case the start and end offset is the number of children into the container to start + * from and end at. + * + * The other cases are different arrangements of the first two. + * + * psuedo code: + * + * if start container is not text: + * count through the children to find where we start (m_startOffset children) + * + * loop from the start position: + * if the current node is text, add the text to our variable 'text', truncating/removing if at the end/start. + * + * if the node has children, step to the first child. + * if the node has no children but does have siblings, step to the next sibling + * until we find a sibling, go to next the parent but: + * make sure this sibling isn't past the end of where we are supposed to go. (position > endOffset and the parent is the endContainer) + * + */ + + + if( m_startContainer == m_endContainer && m_startOffset >= m_endOffset) + return text; + + + if(n->firstChild()) { + n = n->firstChild(); + int current_offset = m_startOffset; + while(current_offset-- && n) { + n = n->nextSibling(); + } + } + + while(n) { + if(n->nodeType() == DOM::Node::TEXT_NODE || + n->nodeType() == DOM::Node::CDATA_SECTION_NODE) { + + DOMString str; + str = static_cast(n)->string(); + if( n == m_endContainer || n == m_startContainer) + str = str.copy(); //copy if we are going to modify. + + if (n == m_endContainer) + str.truncate(m_endOffset); + if (n == m_startContainer) + str.remove(0,m_startOffset); + text += str; + if (n == m_endContainer) + break; + } + + + NodeImpl *next = n->firstChild(); + if(!next) + next = n->nextSibling(); + + while( !next && n->parentNode() ) { + if (n == m_endContainer) return text; + n = n->parentNode(); + if (n == m_endContainer) return text; + next = n->nextSibling(); + } + + if(n->parentNode() == m_endContainer) { + if(!next) break; + unsigned long current_offset = 0; + NodeImpl *it = n; + while((it = it->previousSibling())) ++current_offset; + if(current_offset >= m_endOffset) { + break; + } + } + + n = next; + } + return text; +} + +DOMString RangeImpl::toHTML( int &exceptioncode ) +{ + bool hasHtmlTag = false; + bool hasBodyTag = false; + //FIXME: What is this section of code below exactly? Do I want it here? + if (m_detached) { + exceptioncode = DOMException::INVALID_STATE_ERR; + return DOMString(); + } + DOMString text = ""; + NodeImpl *n = m_startContainer; + int num_tables=0; + bool in_li = false; //whether we have an li in the text, without an ol/ul + int depth_difference = 0; + int lowest_depth_difference = 0; + + if( m_startContainer == m_endContainer && m_startOffset >= m_endOffset) + return text; + + while(n) { + /* First, we could have an tag otherstuff */ + if(n->nodeType() == DOM::Node::ELEMENT_NODE) { + int elementId = static_cast(n)->id(); + if(elementId == ID_TABLE) num_tables++; + if(elementId == ID_BODY) hasBodyTag = true; + if(elementId == ID_HTML) hasHtmlTag = true; + if(elementId == ID_LI) in_li=true; + if(num_tables==0 && ( elementId == ID_TD || elementId == ID_TR || elementId == ID_TH || elementId == ID_TBODY || elementId == ID_TFOOT || elementId == ID_THEAD)) num_tables++; + if(!( !n->hasChildNodes() && (elementId == ID_H1 || elementId == ID_H2 || elementId == ID_H3 || elementId == ID_H4 || elementId ==ID_H5))) { //Don't add

etc. Just skip these nodes just to make the output html a bit nicer. + text += static_cast(n)->openTagStartToString(true /*safely expand img urls*/); // adds "hasChildNodes()) { + depth_difference++; + text += ">"; + } else { + text += "/>"; + } + } + } else + if(n->nodeType() == DOM::Node::TEXT_NODE || + n->nodeType() == DOM::Node::CDATA_SECTION_NODE) { + if(n->nodeType() == DOM::Node::CDATA_SECTION_NODE) text += "(n)->toString(startOffset, endOffset); //Note this should always work since CDataImpl inherits TextImpl + if(n->nodeType() == DOM::Node::CDATA_SECTION_NODE) text += " ]]>"; + if(n == m_endContainer) { + break; + } + } + if(n->parentNode() == m_endContainer && !n->nextSibling()) { + break; + } + + //if (n == m_endContainer) break; + NodeImpl *next = n->firstChild(); + if(next) { + if(n == m_startContainer) { + //This is the start of our selection, so we have to move to where we have started selecting. + //For example, if 'n' is "hello how are you? " + //then this has four children. If our selection started on the image, then we need to start from there. + unsigned long current_offset = 0; + while(current_offset < m_startOffset && next) { + next = next->nextSibling(); + ++current_offset; + } + } + } else { + next = n->nextSibling(); + + if(n->parentNode() == m_endContainer) { + unsigned long current_offset = 1; + NodeImpl *it = n; + while((it = it->previousSibling())) ++current_offset; + + if(current_offset >= m_endOffset) { + break; + } + } + } + + while( !next && n->parentNode() ) { + n = n->parentNode(); + if(n->nodeType() == DOM::Node::ELEMENT_NODE) { + text += "(n)->tagName(); + int elementId = static_cast(n)->id(); + if(elementId == ID_TABLE) num_tables--; + depth_difference--; + if(lowest_depth_difference > depth_difference) lowest_depth_difference=depth_difference; + if(num_tables==0 && ( elementId == ID_TD || elementId == ID_TR || elementId == ID_TH || elementId == ID_TBODY || elementId == ID_TFOOT || elementId == ID_THEAD)) num_tables--; + if(elementId == ID_OL || elementId == ID_UL) in_li=false; + text += ">"; + } + next = n->nextSibling(); + } + n = next; + } + + //We have the html in the selection. But now we need to properly add the opening and closing tags. + //For example say we have: "Hello Mr. John How are you?" and we select "John" or even + //"John How" and copy. We want to return "John" and "John How" respectively + + //To do this, we need to go up the tree from the start, and prepend those tags. + //Imagine our selection was this: + // + // hello

there + // + // The difference in depths between the start and end is -1, and the lowest depth + // difference from the starting point is -2 + // + // So from the start of the selection, we want to go down to the lowest_depth_difference + // and prepend those tags. (

) + // + // From the end of the selection, we want to also go down to the lowest_depth_difference. + // We know the depth of the end of the selection - i.e. depth_difference. + // + // + n = m_startContainer; + int startdepth = 0; //by definition - we are counting from zero. + while((n = n->parentNode()) && startdepth>lowest_depth_difference) { + if(n->nodeType() == DOM::Node::ELEMENT_NODE) { //This should always be true.. right? + switch (static_cast(n)->id()) { + case ID_TABLE: + num_tables--; + break; + case ID_BODY: + hasBodyTag = true; + break; + case ID_HTML: + hasHtmlTag = true; + break; + case ID_LI: + in_li = true; + break; + } + text = static_cast(n)->openTagStartToString(true /*expand img urls*/)+">" +text; // prepends "" + } + startdepth--; + } + n = m_endContainer; + while( depth_difference>lowest_depth_difference && (n = n->parentNode())) { + if(n->nodeType() == DOM::Node::ELEMENT_NODE) { //This should always be true.. right? + switch (static_cast(n)->id()) { + case ID_TABLE: + num_tables++; + break; + case ID_OL: + case ID_UL: + in_li=false; + break; + } + text += "(n)->tagName(); + text += ">"; + } + depth_difference--; + } + + // Now our text string is the same depth on both sides, with nothing lower (in other words all the + // tags in it match up.) This also means that the end value for n in the first loop is a sibling of the + // end value for n in the second loop. + // + // We now need to go down the tree, and for certain tags, add them in on both ends of the text. + // For example, if have: "hello" and we select "ll", then we want to go down the tree and + // add "" and "" to it, to produce "ll". + // + // I just guessed at which tags you'd want to keep (bold, italic etc) and which you wouldn't (tables etc). + // It's just wild guessing. feel free to change. + // + // Note we can carry on with the value of n + if(n) { + while((n = n->parentNode())) { + if(n->nodeType() == DOM::Node::ELEMENT_NODE) { //This should always be true.. right? + int elementId = static_cast(n)->id(); + switch (elementId) { + case ID_TABLE: + case ID_TD: + case ID_TR: + case ID_TH: + case ID_TBODY: + case ID_TFOOT: + case ID_THEAD: + if(num_tables>0) { + if(elementId == ID_TABLE) num_tables--; + text = static_cast(n)->openTagStartToString(true /*expand img urls*/)+">" +text; + text += "(n)->tagName(); + text += ">"; + + } + break; + + case ID_LI: + if(!in_li) break; + text = static_cast(n)->openTagStartToString(true /*expand img urls*/)+">" +text; + text += "(n)->tagName(); + text += ">"; + break; + + case ID_UL: + case ID_OL: + if(!in_li) break; + in_li = false; + case ID_B: + case ID_I: + case ID_U: + case ID_FONT: + case ID_S: + case ID_STRONG: + case ID_STRIKE: + case ID_DEL: + case ID_A: + case ID_H1: + case ID_H2: + case ID_H3: + case ID_H4: + case ID_H5: + //should small, etc be here? so hard to decide. this is such a hack :( + //There's probably tons of others you'd want here. + text = static_cast(n)->openTagStartToString(true /*expand img urls*/)+">" +text; + text += "(n)->tagName(); + text += ">"; + break; + } + } + } + } + + + if(!hasBodyTag) text = DOMString("") + text + ""; + else if(!hasHtmlTag) { + text = DOMString("\n" + "\n" + "\n" + "\n" + "\n") + + text + + ""; + } + text = DOMString("\n") + text; + + return text; + +} + +DocumentFragment RangeImpl::createContextualFragment ( const DOMString &html, int &exceptioncode ) +{ + if (m_detached) { + exceptioncode = DOMException::INVALID_STATE_ERR; + return DocumentFragment(); + } + + if (! m_startContainer->isHTMLElement()) { + exceptioncode = DOMException::NOT_SUPPORTED_ERR; + return DocumentFragment(); + } + + HTMLElementImpl *e = static_cast(m_startContainer); + DocumentFragment fragment = e->createContextualFragment(html); + if (fragment.isNull()) { + exceptioncode = DOMException::NOT_SUPPORTED_ERR; + return DocumentFragment(); + } + + return fragment; +} + + +void RangeImpl::detach( int &exceptioncode ) +{ + if (m_detached) { + exceptioncode = DOMException::INVALID_STATE_ERR; + return; + } + + if (m_startContainer) + m_startContainer->deref(); + m_startContainer = 0; + if (m_endContainer) + m_endContainer->deref(); + m_endContainer = 0; + m_detached = true; +} + +bool RangeImpl::isDetached() const +{ + return m_detached; +} + +void RangeImpl::checkNodeWOffset( NodeImpl *n, int offset, int &exceptioncode) const +{ + if( offset < 0 ) { + exceptioncode = DOMException::INDEX_SIZE_ERR; + } + + switch (n->nodeType()) { + case Node::ENTITY_NODE: + case Node::NOTATION_NODE: + case Node::DOCUMENT_TYPE_NODE: + exceptioncode = RangeException::INVALID_NODE_TYPE_ERR + RangeException::_EXCEPTION_OFFSET; + break; + case Node::TEXT_NODE: + case Node::COMMENT_NODE: + case Node::CDATA_SECTION_NODE: + if ( (unsigned long)offset > static_cast(n)->length() ) + exceptioncode = DOMException::INDEX_SIZE_ERR; + break; + case Node::PROCESSING_INSTRUCTION_NODE: + // ### are we supposed to check with just data or the whole contents? + if ( (unsigned long)offset > static_cast(n)->data().length() ) + exceptioncode = DOMException::INDEX_SIZE_ERR; + break; + default: + if ( (unsigned long)offset > n->childNodeCount() ) + exceptioncode = DOMException::INDEX_SIZE_ERR; + break; + } +} + +void RangeImpl::checkNodeBA( NodeImpl *n, int &exceptioncode ) const +{ + // INVALID_NODE_TYPE_ERR: Raised if the root container of refNode is not an + // Attr, Document or DocumentFragment node or if refNode is a Document, + // DocumentFragment, Attr, Entity, or Notation node. + NodeImpl *root = n; + while (root->parentNode()) + root = root->parentNode(); + if (!(root->nodeType() == Node::ATTRIBUTE_NODE || + root->nodeType() == Node::DOCUMENT_NODE || + root->nodeType() == Node::DOCUMENT_FRAGMENT_NODE)) { + exceptioncode = RangeException::INVALID_NODE_TYPE_ERR + RangeException::_EXCEPTION_OFFSET; + return; + } + + if( n->nodeType() == Node::DOCUMENT_NODE || + n->nodeType() == Node::DOCUMENT_FRAGMENT_NODE || + n->nodeType() == Node::ATTRIBUTE_NODE || + n->nodeType() == Node::ENTITY_NODE || + n->nodeType() == Node::NOTATION_NODE ) + exceptioncode = RangeException::INVALID_NODE_TYPE_ERR + RangeException::_EXCEPTION_OFFSET; + +} + +RangeImpl *RangeImpl::cloneRange(int &exceptioncode) +{ + if (m_detached) { + exceptioncode = DOMException::INVALID_STATE_ERR; + return 0; + } + + return new RangeImpl(m_ownerDocument,m_startContainer,m_startOffset,m_endContainer,m_endOffset); +} + +void RangeImpl::setStartAfter( NodeImpl *refNode, int &exceptioncode ) +{ + if (m_detached) { + exceptioncode = DOMException::INVALID_STATE_ERR; + return; + } + + if (!refNode) { + exceptioncode = DOMException::NOT_FOUND_ERR; + return; + } + + if (refNode->getDocument() != m_ownerDocument) { + exceptioncode = DOMException::WRONG_DOCUMENT_ERR; + return; + } + + checkNodeBA( refNode, exceptioncode ); + if (exceptioncode) + return; + + setStart( refNode->parentNode(), refNode->nodeIndex()+1, exceptioncode ); +} + +void RangeImpl::setEndBefore( NodeImpl *refNode, int &exceptioncode ) +{ + if (m_detached) { + exceptioncode = DOMException::INVALID_STATE_ERR; + return; + } + + if (!refNode) { + exceptioncode = DOMException::NOT_FOUND_ERR; + return; + } + + if (refNode->getDocument() != m_ownerDocument) { + exceptioncode = DOMException::WRONG_DOCUMENT_ERR; + return; + } + + checkNodeBA( refNode, exceptioncode ); + if (exceptioncode) + return; + + setEnd( refNode->parentNode(), refNode->nodeIndex(), exceptioncode ); +} + +void RangeImpl::setEndAfter( NodeImpl *refNode, int &exceptioncode ) +{ + if (m_detached) { + exceptioncode = DOMException::INVALID_STATE_ERR; + return; + } + + if (!refNode) { + exceptioncode = DOMException::NOT_FOUND_ERR; + return; + } + + if (refNode->getDocument() != m_ownerDocument) { + exceptioncode = DOMException::WRONG_DOCUMENT_ERR; + return; + } + + checkNodeBA( refNode, exceptioncode ); + if (exceptioncode) + return; + + setEnd( refNode->parentNode(), refNode->nodeIndex()+1, exceptioncode ); + +} + +void RangeImpl::selectNode( NodeImpl *refNode, int &exceptioncode ) +{ + if (m_detached) { + exceptioncode = DOMException::INVALID_STATE_ERR; + return; + } + + if (!refNode) { + exceptioncode = DOMException::NOT_FOUND_ERR; + return; + } + + // INVALID_NODE_TYPE_ERR: Raised if an ancestor of refNode is an Entity, Notation or + // DocumentType node or if refNode is a Document, DocumentFragment, Attr, Entity, or Notation + // node. + NodeImpl *anc; + for (anc = refNode->parentNode(); anc; anc = anc->parentNode()) { + if (anc->nodeType() == Node::ENTITY_NODE || + anc->nodeType() == Node::NOTATION_NODE || + anc->nodeType() == Node::DOCUMENT_TYPE_NODE) { + + exceptioncode = RangeException::INVALID_NODE_TYPE_ERR + RangeException::_EXCEPTION_OFFSET; + return; + } + } + + if (refNode->nodeType() == Node::DOCUMENT_NODE || + refNode->nodeType() == Node::DOCUMENT_FRAGMENT_NODE || + refNode->nodeType() == Node::ATTRIBUTE_NODE || + refNode->nodeType() == Node::ENTITY_NODE || + refNode->nodeType() == Node::NOTATION_NODE) { + + exceptioncode = RangeException::INVALID_NODE_TYPE_ERR + RangeException::_EXCEPTION_OFFSET; + return; + } + + setStartBefore( refNode, exceptioncode ); + if (exceptioncode) + return; + setEndAfter( refNode, exceptioncode ); +} + +void RangeImpl::selectNodeContents( NodeImpl *refNode, int &exceptioncode ) +{ + if (m_detached) { + exceptioncode = DOMException::INVALID_STATE_ERR; + return; + } + + if (!refNode) { + exceptioncode = DOMException::NOT_FOUND_ERR; + return; + } + + // INVALID_NODE_TYPE_ERR: Raised if refNode or an ancestor of refNode is an Entity, Notation + // or DocumentType node. + NodeImpl *n; + for (n = refNode; n; n = n->parentNode()) { + if (n->nodeType() == Node::ENTITY_NODE || + n->nodeType() == Node::NOTATION_NODE || + n->nodeType() == Node::DOCUMENT_TYPE_NODE) { + + exceptioncode = RangeException::INVALID_NODE_TYPE_ERR + RangeException::_EXCEPTION_OFFSET; + return; + } + } + + setStartContainer(refNode); + m_startOffset = 0; + setEndContainer(refNode); + m_endOffset = refNode->childNodeCount(); +} + +void RangeImpl::surroundContents( NodeImpl *newParent, int &exceptioncode ) +{ + if (m_detached) { + exceptioncode = DOMException::INVALID_STATE_ERR; + return; + } + + if( !newParent ) { + exceptioncode = DOMException::NOT_FOUND_ERR; + return; + } + + // INVALID_NODE_TYPE_ERR: Raised if node is an Attr, Entity, DocumentType, Notation, + // Document, or DocumentFragment node. + if( newParent->nodeType() == Node::ATTRIBUTE_NODE || + newParent->nodeType() == Node::ENTITY_NODE || + newParent->nodeType() == Node::NOTATION_NODE || + newParent->nodeType() == Node::DOCUMENT_TYPE_NODE || + newParent->nodeType() == Node::DOCUMENT_NODE || + newParent->nodeType() == Node::DOCUMENT_FRAGMENT_NODE) { + exceptioncode = RangeException::INVALID_NODE_TYPE_ERR + RangeException::_EXCEPTION_OFFSET; + return; + } + + // NO_MODIFICATION_ALLOWED_ERR: Raised if an ancestor container of either boundary-point of + // the Range is read-only. + if (readOnly()) { + exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR; + return; + } + + NodeImpl *n = m_startContainer; + while (n && !n->isReadOnly()) + n = n->parentNode(); + if (n) { + exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR; + return; + } + + n = m_endContainer; + while (n && !n->isReadOnly()) + n = n->parentNode(); + if (n) { + exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR; + return; + } + + // WRONG_DOCUMENT_ERR: Raised if newParent and the container of the start of the Range were + // not created from the same document. + if (newParent->getDocument() != m_startContainer->getDocument()) { + exceptioncode = DOMException::WRONG_DOCUMENT_ERR; + return; + } + + // HIERARCHY_REQUEST_ERR: Raised if the container of the start of the Range is of a type that + // does not allow children of the type of newParent or if newParent is an ancestor of the container + // or if node would end up with a child node of a type not allowed by the type of node. + if (!m_startContainer->childTypeAllowed(newParent->nodeType())) { + exceptioncode = DOMException::HIERARCHY_REQUEST_ERR; + return; + } + + for (n = m_startContainer; n; n = n->parentNode()) { + if (n == newParent) { + exceptioncode = DOMException::HIERARCHY_REQUEST_ERR; + return; + } + } + + // ### check if node would end up with a child node of a type not allowed by the type of node + + // BAD_BOUNDARYPOINTS_ERR: Raised if the Range partially selects a non-text node. + if (m_startContainer->nodeType() != Node::TEXT_NODE && + m_startContainer->nodeType() != Node::COMMENT_NODE && + m_startContainer->nodeType() != Node::CDATA_SECTION_NODE && + m_startContainer->nodeType() != Node::PROCESSING_INSTRUCTION_NODE) { + + if (m_startOffset > 0 && m_startOffset < m_startContainer->childNodeCount()) { + exceptioncode = RangeException::BAD_BOUNDARYPOINTS_ERR + RangeException::_EXCEPTION_OFFSET; + return; + } + } + + if (m_endContainer->nodeType() != Node::TEXT_NODE && + m_endContainer->nodeType() != Node::COMMENT_NODE && + m_endContainer->nodeType() != Node::CDATA_SECTION_NODE && + m_endContainer->nodeType() != Node::PROCESSING_INSTRUCTION_NODE) { + + if (m_endOffset > 0 && m_endOffset < m_endContainer->childNodeCount()) { + exceptioncode = RangeException::BAD_BOUNDARYPOINTS_ERR + RangeException::_EXCEPTION_OFFSET; + return; + } + } + + while (newParent->firstChild()) { + newParent->removeChild(newParent->firstChild(),exceptioncode); + if (exceptioncode) + return; + } + DocumentFragmentImpl *fragment = extractContents(exceptioncode); + if (exceptioncode) + return; + insertNode( newParent, exceptioncode ); + if (exceptioncode) + return; + newParent->appendChild( fragment, exceptioncode ); + if (exceptioncode) + return; + selectNode( newParent, exceptioncode ); +} + +void RangeImpl::setStartBefore( NodeImpl *refNode, int &exceptioncode ) +{ + if (m_detached) { + exceptioncode = DOMException::INVALID_STATE_ERR; + return; + } + + if (!refNode) { + exceptioncode = DOMException::NOT_FOUND_ERR; + return; + } + + if (refNode->getDocument() != m_ownerDocument) { + exceptioncode = DOMException::WRONG_DOCUMENT_ERR; + return; + } + + checkNodeBA( refNode, exceptioncode ); + if (exceptioncode) + return; + + setStart( refNode->parentNode(), refNode->nodeIndex(), exceptioncode ); +} + +void RangeImpl::setStartContainer(NodeImpl *_startContainer) +{ + if (m_startContainer == _startContainer) + return; + + if (m_startContainer) + m_startContainer->deref(); + m_startContainer = _startContainer; + if (m_startContainer) + m_startContainer->ref(); +} + +void RangeImpl::setEndContainer(NodeImpl *_endContainer) +{ + if (m_endContainer == _endContainer) + return; + + if (m_endContainer) + m_endContainer->deref(); + m_endContainer = _endContainer; + if (m_endContainer) + m_endContainer->ref(); +} + +void RangeImpl::checkDeleteExtract(int &exceptioncode) { + + NodeImpl *start; + if (m_startContainer->nodeType() != Node::TEXT_NODE && + m_startContainer->nodeType() != Node::CDATA_SECTION_NODE && + m_startContainer->nodeType() != Node::COMMENT_NODE && + m_startContainer->nodeType() != Node::PROCESSING_INSTRUCTION_NODE) { + + start = m_startContainer->childNode(m_startOffset); + if (!start) { + if (m_startContainer->lastChild()) + start = m_startContainer->lastChild()->traverseNextNode(); + else + start = m_startContainer->traverseNextNode(); + } + } + else + start = m_startContainer; + + NodeImpl *end; + if (m_endContainer->nodeType() != Node::TEXT_NODE && + m_endContainer->nodeType() != Node::CDATA_SECTION_NODE && + m_endContainer->nodeType() != Node::COMMENT_NODE && + m_endContainer->nodeType() != Node::PROCESSING_INSTRUCTION_NODE) { + + end = m_endContainer->childNode(m_endOffset); + if (!end) { + if (m_endContainer->lastChild()) + end = m_endContainer->lastChild()->traverseNextNode(); + else + end = m_endContainer->traverseNextNode(); + } + } + else + end = m_endContainer; + + NodeImpl *n; + for (n = start; n != end; n = n->traverseNextNode()) { + if (n->isReadOnly()) { + exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR; + return; + } + if (n->nodeType() == Node::DOCUMENT_TYPE_NODE) { // ### is this for only directly under the DF, or anywhere? + exceptioncode = DOMException::HIERARCHY_REQUEST_ERR; + return; + } + } + + if (containedByReadOnly()) { + exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR; + return; + } +} + + +bool RangeImpl::containedByReadOnly() { + NodeImpl *n; + for (n = m_startContainer; n; n = n->parentNode()) { + if (n->isReadOnly()) + return true; + } + for (n = m_endContainer; n; n = n->parentNode()) { + if (n->isReadOnly()) + return true; + } + return false; +} + + + + + + + + diff --git a/khtml/xml/dom2_rangeimpl.h b/khtml/xml/dom2_rangeimpl.h new file mode 100644 index 000000000..35d532f23 --- /dev/null +++ b/khtml/xml/dom2_rangeimpl.h @@ -0,0 +1,127 @@ +/* + * This file is part of the DOM implementation for KDE. + * + * (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 2000 Gunnstein Lye (gunnstein@netcom.no) + * (C) 2000 Frederik Holljen (frederik.holljen@hig.no) + * (C) 2001 Peter Kelly (pmk@post.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. + * + */ + +#ifndef _DOM2_RangeImpl_h_ +#define _DOM2_RangeImpl_h_ + +#include "dom/dom2_range.h" +#include "misc/shared.h" + +namespace DOM { + +class RangeImpl : public khtml::Shared +{ + friend class DocumentImpl; +public: + RangeImpl(DocumentImpl *_ownerDocument); + RangeImpl(DocumentImpl *_ownerDocument, + NodeImpl *_startContainer, long _startOffset, + NodeImpl *_endContainer, long _endOffset); + + ~RangeImpl(); + + NodeImpl *startContainer(int &exceptioncode) const; + long startOffset(int &exceptioncode) const; + NodeImpl *endContainer(int &exceptioncode) const; + long endOffset(int &exceptioncode) const; + bool collapsed(int &exceptioncode) const; + + NodeImpl *commonAncestorContainer(int &exceptioncode); + static NodeImpl *commonAncestorContainer(NodeImpl *containerA, NodeImpl *containerB); + void setStart ( NodeImpl *refNode, long offset, int &exceptioncode ); + void setEnd ( NodeImpl *refNode, long offset, int &exceptioncode ); + void collapse ( bool toStart, int &exceptioncode ); + short compareBoundaryPoints ( Range::CompareHow how, RangeImpl *sourceRange, int &exceptioncode ); + static short compareBoundaryPoints ( NodeImpl *containerA, long offsetA, NodeImpl *containerB, long offsetB ); + bool boundaryPointsValid ( ); + void deleteContents ( int &exceptioncode ); + DocumentFragmentImpl *extractContents ( int &exceptioncode ); + DocumentFragmentImpl *cloneContents ( int &exceptioncode ); + void insertNode( NodeImpl *newNode, int &exceptioncode ); + DOMString toString ( int &exceptioncode ); + /** Converts the selection to HTML. The returned string will have matching + * tags, and all td, tr, etc tags will be inside a table tag. CSS is not + * used at this stage - This needs to be fixed. + * + * This is guaranteed to produce an xml valid snippet, no matter how crappy the input + * html page is. It will have html and body tags. + * + * Any urls in images or links will be expanded to full urls with passwords stripped + * for security reasons. + * + * Note: Originally this function didn't have the exceptioncode argument. I added it + * since all the other functions do. If this is correct, please remove this comment. + * + * @param exceptioncode This will be set if m_detached is true. + * @return A string with html tags for this range. + * + * @since 3.4 + */ + DOMString toHTML ( int &exceptioncode ); + + DocumentFragment createContextualFragment ( const DOMString &html, int &exceptioncode ); + + void detach ( int &exceptioncode ); + bool isDetached() const; + RangeImpl *cloneRange(int &exceptioncode); + + void setStartAfter( NodeImpl *refNode, int &exceptioncode ); + void setEndBefore( NodeImpl *refNode, int &exceptioncode ); + void setEndAfter( NodeImpl *refNode, int &exceptioncode ); + void selectNode( NodeImpl *refNode, int &exceptioncode ); + void selectNodeContents( NodeImpl *refNode, int &exceptioncode ); + void surroundContents( NodeImpl *newParent, int &exceptioncode ); + void setStartBefore( NodeImpl *refNode, int &exceptioncode ); + + enum ActionType { + DELETE_CONTENTS, + EXTRACT_CONTENTS, + CLONE_CONTENTS + }; + DocumentFragmentImpl *processContents ( ActionType action, int &exceptioncode ); + + bool readOnly() { return false; } + +protected: + DocumentImpl *m_ownerDocument; + NodeImpl *m_startContainer; + unsigned long m_startOffset; + NodeImpl *m_endContainer; + unsigned long m_endOffset; + bool m_detached; + +private: + void checkNodeWOffset( NodeImpl *n, int offset, int &exceptioncode) const; + void checkNodeBA( NodeImpl *n, int &exceptioncode ) const; + void setStartContainer(NodeImpl *_startContainer); + void setEndContainer(NodeImpl *_endContainer); + void checkDeleteExtract(int &exceptioncode); + bool containedByReadOnly(); +}; + +} // namespace + +#endif + diff --git a/khtml/xml/dom2_traversalimpl.cpp b/khtml/xml/dom2_traversalimpl.cpp new file mode 100644 index 000000000..2abdadead --- /dev/null +++ b/khtml/xml/dom2_traversalimpl.cpp @@ -0,0 +1,667 @@ +/** + * This file is part of the DOM implementation for KDE. + * + * (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 2000 Frederik Holljen (frederik.holljen@hig.no) + * (C) 2001 Peter Kelly (pmk@post.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 "xml/dom_docimpl.h" + +using namespace DOM; + +NodeIteratorImpl::NodeIteratorImpl(NodeImpl *_root, unsigned long _whatToShow, + NodeFilter _filter, bool _entityReferenceExpansion) +{ + m_root = _root; + m_whatToShow = _whatToShow; + m_filter = _filter; + m_expandEntityReferences = _entityReferenceExpansion; + + m_referenceNode = _root; + m_inFront = false; + + m_doc = m_root->getDocument(); + m_doc->attachNodeIterator(this); + m_doc->ref(); + + m_detached = false; +} + +NodeIteratorImpl::~NodeIteratorImpl() +{ + m_doc->detachNodeIterator(this); + m_doc->deref(); +} + +NodeImpl *NodeIteratorImpl::root() +{ + return m_root; +} + +unsigned long NodeIteratorImpl::whatToShow() +{ + return m_whatToShow; +} + +NodeFilter NodeIteratorImpl::filter() +{ + return m_filter; +} + +bool NodeIteratorImpl::expandEntityReferences() +{ + return m_expandEntityReferences; +} + +NodeImpl *NodeIteratorImpl::nextNode( int &exceptioncode ) +{ + if (m_detached) { + exceptioncode = DOMException::INVALID_STATE_ERR; + return 0; + } + + if (!m_referenceNode) { + m_inFront = true; + return 0; + } + + if (!m_inFront) { + m_inFront = true; + if (isAccepted(m_referenceNode) == NodeFilter::FILTER_ACCEPT) + return m_referenceNode; + } + + NodeImpl *_tempCurrent = getNextNode(m_referenceNode); + while( _tempCurrent ) { + m_referenceNode = _tempCurrent; + if(isAccepted(_tempCurrent) == NodeFilter::FILTER_ACCEPT) + return m_referenceNode; + _tempCurrent = getNextNode(_tempCurrent); + } + + return 0; +} + +NodeImpl *NodeIteratorImpl::getNextNode(NodeImpl *n) +{ + /* 1. my first child + * 2. my next sibling + * 3. my parents sibling, or their parents sibling (loop) + * 4. not found + */ + + if( !n ) + return 0; + + if( n->hasChildNodes() ) + return n->firstChild(); + + if( m_root == n) + return 0; + + if( n->nextSibling() ) + return n->nextSibling(); + + NodeImpl *parent = n->parentNode(); + while( parent ) + { + if( m_root == parent ) + return 0; + + n = parent->nextSibling(); + if( n ) + return n; + + parent = parent->parentNode(); + } + return 0; +} + +NodeImpl *NodeIteratorImpl::previousNode( int &exceptioncode ) +{ + if (m_detached) { + exceptioncode = DOMException::INVALID_STATE_ERR; + return 0; + } + + if (!m_referenceNode) { + m_inFront = false; + return 0; + } + + if (m_inFront) { + m_inFront = false; + if (isAccepted(m_referenceNode) == NodeFilter::FILTER_ACCEPT) + return m_referenceNode; + } + + NodeImpl *_tempCurrent = getPreviousNode(m_referenceNode); + while( _tempCurrent ) { + m_referenceNode = _tempCurrent; + if(isAccepted(_tempCurrent) == NodeFilter::FILTER_ACCEPT) + return m_referenceNode; + _tempCurrent = getPreviousNode(_tempCurrent); + } + + return 0; +} + +NodeImpl *NodeIteratorImpl::getPreviousNode(NodeImpl *n) +{ +/* 1. my previous sibling.lastchild + * 2. my previous sibling + * 3. my parent + */ + NodeImpl *_tempCurrent; + + if( !n || m_root == n ) + return 0; + + _tempCurrent = n->previousSibling(); + if( _tempCurrent ) + { + if( _tempCurrent->lastChild() ) + { + while( _tempCurrent->lastChild() ) + _tempCurrent = _tempCurrent->lastChild(); + return _tempCurrent; + } + else + return _tempCurrent; + } + + return n->parentNode(); + +} + +void NodeIteratorImpl::detach(int &/*exceptioncode*/) +{ + m_doc->detachNodeIterator(this); + m_detached = true; +} + + +void NodeIteratorImpl::notifyBeforeNodeRemoval(NodeImpl *removed) +{ + // make sure the deleted node is with the root (but not the root itself) + if (removed == m_root) + return; + + NodeImpl *maybeRoot = removed->parentNode(); + while (maybeRoot && maybeRoot != m_root) + maybeRoot = maybeRoot->parentNode(); + if (!maybeRoot) + return; + + // did I get deleted, or one of my parents? + NodeImpl *_tempDeleted = m_referenceNode; + while( _tempDeleted && _tempDeleted != removed) + _tempDeleted = _tempDeleted->parentNode(); + + if( !_tempDeleted ) // someone that didn't consern me got deleted + return; + + if( !m_inFront) + { + NodeImpl *_next = getNextNode(_tempDeleted); + if( _next ) + m_referenceNode = _next; + else + { + // deleted node was at end of list + m_inFront = true; + m_referenceNode = getPreviousNode(_tempDeleted); + } + } + else { + NodeImpl *_prev = getPreviousNode(_tempDeleted); + if ( _prev ) + m_referenceNode = _prev; + else + { + // deleted node was at start of list + m_inFront = false; + m_referenceNode = getNextNode(_tempDeleted); + } + } + +} + +short NodeIteratorImpl::isAccepted(NodeImpl *n) +{ + // if XML is implemented we have to check expandEntityRerefences in this function + if( ( ( 1 << ( n->nodeType()-1 ) ) & m_whatToShow) != 0 ) + { + if(!m_filter.isNull()) + return m_filter.acceptNode(n); + else + return NodeFilter::FILTER_ACCEPT; + } + return NodeFilter::FILTER_SKIP; +} + +// -------------------------------------------------------------- + + +NodeFilterImpl::NodeFilterImpl() +{ + m_customNodeFilter = 0; +} + +NodeFilterImpl::~NodeFilterImpl() +{ + if (m_customNodeFilter) + m_customNodeFilter->deref(); +} + +short NodeFilterImpl::acceptNode(const Node &n) +{ + if (m_customNodeFilter) + return m_customNodeFilter->acceptNode(n); + else + return NodeFilter::FILTER_ACCEPT; +} + +void NodeFilterImpl::setCustomNodeFilter(CustomNodeFilter *custom) +{ + m_customNodeFilter = custom; + if (m_customNodeFilter) + m_customNodeFilter->ref(); +} + +CustomNodeFilter *NodeFilterImpl::customNodeFilter() +{ + return m_customNodeFilter; +} + +// -------------------------------------------------------------- + +TreeWalkerImpl::TreeWalkerImpl(NodeImpl *n, long _whatToShow, NodeFilterImpl *f, + bool entityReferenceExpansion) +{ + m_currentNode = n; + m_rootNode = n; + m_whatToShow = _whatToShow; + m_filter = f; + if ( m_filter ) + m_filter->ref(); + m_expandEntityReferences = entityReferenceExpansion; + m_doc = m_rootNode->getDocument(); + m_doc->ref(); +} + +TreeWalkerImpl::~TreeWalkerImpl() +{ + m_doc->deref(); + if ( m_filter ) + m_filter->deref(); +} + +NodeImpl *TreeWalkerImpl::getRoot() const +{ + return m_rootNode; +} + +unsigned long TreeWalkerImpl::getWhatToShow() const +{ + return m_whatToShow; +} + +NodeFilterImpl *TreeWalkerImpl::getFilter() const +{ + return m_filter; +} + +bool TreeWalkerImpl::getExpandEntityReferences() const +{ + return m_expandEntityReferences; +} + +NodeImpl *TreeWalkerImpl::getCurrentNode() const +{ + return m_currentNode; +} + +void TreeWalkerImpl::setWhatToShow(long _whatToShow) +{ + // do some testing wether this is an accepted value + m_whatToShow = _whatToShow; +} + +void TreeWalkerImpl::setFilter(NodeFilterImpl *_filter) +{ + m_filter->deref(); + m_filter = _filter; + if ( m_filter ) + m_filter->ref(); +} + +void TreeWalkerImpl::setExpandEntityReferences(bool value) +{ + m_expandEntityReferences = value; +} + +void TreeWalkerImpl::setCurrentNode( NodeImpl *n ) +{ + if ( n ) + { + //m_rootNode = n; + m_currentNode = n; + } +// else +// throw( DOMException::NOT_SUPPORTED_ERR ); +} + +NodeImpl *TreeWalkerImpl::parentNode( ) +{ + NodeImpl *n = getParentNode( m_currentNode ); + if ( n ) + m_currentNode = n; + return n; +} + + +NodeImpl *TreeWalkerImpl::firstChild( ) +{ + NodeImpl *n = getFirstChild( m_currentNode ); + if ( n ) + m_currentNode = n; + return n; +} + + +NodeImpl *TreeWalkerImpl::lastChild( ) +{ + NodeImpl *n = getLastChild(m_currentNode); + if( n ) + m_currentNode = n; + return n; +} + +NodeImpl *TreeWalkerImpl::previousSibling( ) +{ + NodeImpl *n = getPreviousSibling( m_currentNode ); + if( n ) + m_currentNode = n; + return n; +} + +NodeImpl *TreeWalkerImpl::nextSibling( ) +{ + NodeImpl *n = getNextSibling( m_currentNode ); + if( n ) + m_currentNode = n; + return n; +} + +NodeImpl *TreeWalkerImpl::previousNode( ) +{ +/* 1. my previous sibling.lastchild + * 2. my previous sibling + * 3. my parent + */ + + NodeImpl *n = getPreviousSibling( m_currentNode ); + if( !n ) + { + n = getParentNode( m_currentNode ); + if( n ) //parent + { + m_currentNode = n; + return m_currentNode; + } + else // parent failed.. no previous node + return 0; + } + + NodeImpl *child = getLastChild( n ); + if( child ) // previous siblings last child + { + m_currentNode = child; + return m_currentNode; + } + else // previous sibling + { + m_currentNode = n; + return m_currentNode; + } + return 0; // should never get here! +} + +NodeImpl *TreeWalkerImpl::nextNode( ) +{ +/* 1. my first child + * 2. my next sibling + * 3. my parents sibling, or their parents sibling (loop) + * 4. not found + */ + + NodeImpl *n = getFirstChild( m_currentNode ); + if( n ) // my first child + { + m_currentNode = n; + return n; + } + + n = getNextSibling( m_currentNode ); // my next sibling + if( n ) + { + m_currentNode = n; + return m_currentNode; + } + NodeImpl *parent = getParentNode( m_currentNode ); + while( parent ) // parents sibling + { + n = getNextSibling( parent ); + if( n ) + { + m_currentNode = n; + return m_currentNode; + } + else + parent = getParentNode( parent ); + } + return 0; +} + +short TreeWalkerImpl::isAccepted(NodeImpl *n) +{ + // if XML is implemented we have to check expandEntityRerefences in this function + if( ( ( 1 << ( n->nodeType()-1 ) ) & m_whatToShow) != 0 ) + { + if(m_filter) + return m_filter->acceptNode(n); + else + return NodeFilter::FILTER_ACCEPT; + } + return NodeFilter::FILTER_SKIP; +} + +NodeImpl *TreeWalkerImpl::getParentNode(NodeImpl *n) +{ + short _result = NodeFilter::FILTER_ACCEPT; + + if( n == m_rootNode /*|| !n*/ ) + return 0; + + NodeImpl *_tempCurrent = n->parentNode(); + + if( !_tempCurrent ) + return 0; + + _result = isAccepted( _tempCurrent ); + if ( _result == NodeFilter::FILTER_ACCEPT ) + return _tempCurrent; // match found + + return getParentNode( _tempCurrent ); +} + +NodeImpl *TreeWalkerImpl::getFirstChild(NodeImpl *n) +{ + short _result; + + if( !n || !n->firstChild() ) + return 0; + n = n->firstChild(); + + _result = isAccepted( n ); + + switch( _result ) + { + case NodeFilter::FILTER_ACCEPT: + return n; + break; + case NodeFilter::FILTER_SKIP: + if( n->hasChildNodes() ) + return getFirstChild( n ); + else + return getNextSibling( n ); + break; + + case NodeFilter::FILTER_REJECT: + return getNextSibling( n ); + break; + } + return 0; // should never get here! +} + +NodeImpl *TreeWalkerImpl::getLastChild(NodeImpl *n) +{ + short _result; + + if( !n || !n->lastChild() ) + return 0; + n = n->lastChild(); + _result = isAccepted( n ); + + switch( _result ) + { + case NodeFilter::FILTER_ACCEPT: + return n; + break; + + case NodeFilter::FILTER_SKIP: + if( n->hasChildNodes() ) + return getLastChild( n ); + else + return getPreviousSibling( n ); + break; + + case NodeFilter::FILTER_REJECT: + return getPreviousSibling( n ); + break; + } + return 0; +} + +NodeImpl *TreeWalkerImpl::getPreviousSibling(NodeImpl *n) +{ + short _result; + NodeImpl *_tempCurrent; + + if( !n ) + return 0; + //first the cases if we have a previousSibling + _tempCurrent = n->previousSibling(); + if( _tempCurrent ) + { + _result = isAccepted( _tempCurrent ); + switch ( _result ) + { + case NodeFilter::FILTER_ACCEPT: + return _tempCurrent; + break; + + case NodeFilter::FILTER_SKIP: + { + NodeImpl *nskip = getLastChild( _tempCurrent ); + if( nskip ) + return nskip; + return getPreviousSibling( _tempCurrent ); + break; + } + + case NodeFilter::FILTER_REJECT: + return getPreviousSibling( _tempCurrent ); + break; + } + } + // now the case if we don't have previous sibling + else + { + _tempCurrent = n->parentNode(); + if( !_tempCurrent || _tempCurrent == m_rootNode) + return 0; + _result = isAccepted( _tempCurrent ); + if ( _result == NodeFilter::FILTER_SKIP ) + return getPreviousSibling( _tempCurrent ); + + return 0; + + } + return 0; // should never get here! +} + +NodeImpl *TreeWalkerImpl::getNextSibling(NodeImpl *n) +{ + NodeImpl *_tempCurrent = 0; + short _result; + + if( !n ) + return 0; + + _tempCurrent = n->nextSibling(); + if( _tempCurrent ) + { + _result = isAccepted( _tempCurrent ); + switch ( _result ) + { + case NodeFilter::FILTER_ACCEPT: + return _tempCurrent; + break; + + case NodeFilter::FILTER_SKIP: + { + NodeImpl *nskip = getFirstChild( _tempCurrent ); + if( nskip ) + return nskip; + return getNextSibling( _tempCurrent ); + break; + } + + case NodeFilter::FILTER_REJECT: + return getNextSibling( _tempCurrent ); + break; + } + } + else + { + _tempCurrent = n->parentNode(); + if( !_tempCurrent || _tempCurrent == m_rootNode) + return 0; + _result = isAccepted( _tempCurrent ); + if( _result == NodeFilter::FILTER_SKIP ) + return getNextSibling( _tempCurrent ); + + return 0; + } + return 0; +} + diff --git a/khtml/xml/dom2_traversalimpl.h b/khtml/xml/dom2_traversalimpl.h new file mode 100644 index 000000000..3c5ea1dac --- /dev/null +++ b/khtml/xml/dom2_traversalimpl.h @@ -0,0 +1,196 @@ +/* + * This file is part of the DOM implementation for KDE. + * + * (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 2000 Frederik Holljen (frederik.holljen@hig.no) + * (C) 2001 Peter Kelly (pmk@post.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. + * + */ + +#ifndef _DOM2_TraversalImpl_h_ +#define _DOM2_TraversalImpl_h_ + +#include "dom/dom_node.h" +#include "dom/dom_misc.h" +#include "misc/shared.h" +#include "dom/dom2_traversal.h" + +namespace DOM { + +class NodeImpl; +class DocumentImpl; + +class NodeIteratorImpl : public khtml::Shared +{ +public: + NodeIteratorImpl(NodeImpl *_root, unsigned long _whatToShow, NodeFilter _filter, bool _entityReferenceExpansion); + ~NodeIteratorImpl(); + + + NodeImpl *root(); + unsigned long whatToShow(); + NodeFilter filter(); + bool expandEntityReferences(); + + NodeImpl *nextNode(int &exceptioncode); + NodeImpl *previousNode(int &exceptioncode); + void detach(int &exceptioncode); + + + /** + * This function has to be called if you delete a node from the + * document tree and you want the Iterator to react if there + * are any changes concerning it. + */ + void notifyBeforeNodeRemoval(NodeImpl *removed); + + short isAccepted(NodeImpl *n); + NodeImpl *getNextNode(NodeImpl *n); + NodeImpl *getPreviousNode(NodeImpl *n); +protected: + NodeImpl *m_root; + long m_whatToShow; + NodeFilter m_filter; + bool m_expandEntityReferences; + + bool m_inFront; + NodeImpl *m_referenceNode; + bool m_detached; + DocumentImpl *m_doc; +}; + +class NodeFilterImpl : public khtml::Shared +{ +public: + NodeFilterImpl(); + ~NodeFilterImpl(); + + short acceptNode(const Node &n); + + void setCustomNodeFilter(CustomNodeFilter *custom); + CustomNodeFilter *customNodeFilter(); +protected: + CustomNodeFilter *m_customNodeFilter; + +}; + +class TreeWalkerImpl : public khtml::Shared +{ +public: + TreeWalkerImpl(); + TreeWalkerImpl(const TreeWalkerImpl &other); + TreeWalkerImpl(NodeImpl *n, NodeFilter f); + TreeWalkerImpl(NodeImpl *n, long _whatToShow, NodeFilterImpl *f, + bool entityReferenceExpansion); + TreeWalkerImpl & operator = (const TreeWalkerImpl &other); + + + ~TreeWalkerImpl(); + + NodeImpl *getRoot() const; + + unsigned long getWhatToShow() const; + + NodeFilterImpl *getFilter() const; + + bool getExpandEntityReferences() const; + + NodeImpl *getCurrentNode() const; + + void setCurrentNode( NodeImpl *_currentNode); + + NodeImpl *parentNode(); + + NodeImpl *firstChild(); + + NodeImpl *lastChild (); + + NodeImpl *previousSibling (); + + NodeImpl *nextSibling(); + + NodeImpl *previousNode(); + + NodeImpl *nextNode(); + + + /** + * Sets which node types are to be presented via the TreeWalker + */ + void setWhatToShow(long _whatToShow); + void setFilter(NodeFilterImpl *_filter); + void setExpandEntityReferences(bool value); + + NodeImpl *getParentNode(NodeImpl *n); + NodeImpl *getFirstChild(NodeImpl *n); + NodeImpl *getLastChild(NodeImpl *n); + NodeImpl *getPreviousSibling(NodeImpl *n); + NodeImpl *getNextSibling(NodeImpl *n); + + short isAccepted(NodeImpl *n); + +protected: + /** + * This attribute determines which node types are presented via + * the TreeWalker. + * + */ + long m_whatToShow; + + /** + * The filter used to screen nodes. + * + */ + NodeFilterImpl *m_filter; + + /** + * The value of this flag determines whether entity reference + * nodes are expanded. To produce a view of the document that has + * entity references expanded and does not expose the entity + * reference node itself, use the whatToShow flags to hide the + * entity reference node and set expandEntityReferences to true + * when creating the iterator. To produce a view of the document + * that has entity reference nodes but no entity expansion, use + * the whatToShow flags to show the entity reference node and set + * expandEntityReferences to true. + * + * This is not implemented (always true) + */ + bool m_expandEntityReferences; + + /** + * The current node. + * + * The value must not be null. Attempting to set it to null will + * raise a NOT_SUPPORTED_ERR exception. When setting a node, the + * whatToShow flags and any Filter associated with the TreeWalker + * are not checked. The currentNode may be set to any Node of any + * type. + * + */ + NodeImpl *m_currentNode; + + NodeImpl *m_rootNode; + DocumentImpl *m_doc; +}; + + +} // namespace + +#endif + diff --git a/khtml/xml/dom2_viewsimpl.cpp b/khtml/xml/dom2_viewsimpl.cpp new file mode 100644 index 000000000..2195a9db1 --- /dev/null +++ b/khtml/xml/dom2_viewsimpl.cpp @@ -0,0 +1,50 @@ +/** + * This file is part of the DOM implementation for KDE. + * + * (C) 2001 Peter Kelly (pmk@post.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 "dom2_viewsimpl.h" +#include "dom_elementimpl.h" +#include "dom_docimpl.h" +#include "css/css_renderstyledeclarationimpl.h" +#include "css/cssproperties.h" +#include "css/css_stylesheetimpl.h" + +using namespace khtml; +using namespace DOM; + +AbstractViewImpl::AbstractViewImpl(DocumentImpl *_document) +{ + m_document = _document; +} + +AbstractViewImpl::~AbstractViewImpl() +{ +} + +CSSStyleDeclarationImpl *AbstractViewImpl::getComputedStyle(ElementImpl* elt, DOMStringImpl* /*pseudoElt*/) +{ + if (!elt) + return 0; + + CSSStyleDeclarationImpl* style = new RenderStyleDeclarationImpl( elt ); + return style; +} + diff --git a/khtml/xml/dom2_viewsimpl.h b/khtml/xml/dom2_viewsimpl.h new file mode 100644 index 000000000..caff63e11 --- /dev/null +++ b/khtml/xml/dom2_viewsimpl.h @@ -0,0 +1,50 @@ +/* + * This file is part of the DOM implementation for KDE. + * + * (C) 2001 Peter Kelly (pmk@post.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. + * + */ + +#ifndef _DOM_ViewsImpl_h_ +#define _DOM_ViewsImpl_h_ + +#include "dom/dom_misc.h" +#include "css/css_valueimpl.h" +#include "misc/shared.h" + +namespace DOM { + +class DocumentImpl; +class CSSStyleDeclarationImpl; +class ElementImpl; +class DOMStringImpl; + +// Introduced in DOM Level 2: +class AbstractViewImpl : public khtml::Shared +{ +public: + AbstractViewImpl(DocumentImpl *_document); + ~AbstractViewImpl(); + DocumentImpl *document() const { return m_document; } + CSSStyleDeclarationImpl *getComputedStyle(ElementImpl *elt, DOMStringImpl *pseudoElt); +protected: + DocumentImpl *m_document; +}; + +} //namespace +#endif diff --git a/khtml/xml/dom_docimpl.cpp b/khtml/xml/dom_docimpl.cpp new file mode 100644 index 000000000..f6cc0fa64 --- /dev/null +++ b/khtml/xml/dom_docimpl.cpp @@ -0,0 +1,2892 @@ +/** + * 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) 2002-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 "xml/dom_textimpl.h" +#include "xml/dom_xmlimpl.h" +#include "xml/dom2_rangeimpl.h" +#include "xml/dom2_eventsimpl.h" +#include "xml/xml_tokenizer.h" +#include "html/htmltokenizer.h" +#include "xml/dom_restyler.h" + +#include "css/csshelper.h" +#include "css/cssstyleselector.h" +#include "css/css_stylesheetimpl.h" +#include "misc/htmlhashes.h" +#include "misc/helper.h" +#include "misc/seed.h" +#include "misc/loader.h" +#include "ecma/kjs_proxy.h" +#include "ecma/kjs_binding.h" + +#include +#include +#include +#include +#include + +#include "rendering/counter_tree.h" +#include "rendering/render_canvas.h" +#include "rendering/render_replaced.h" +#include "rendering/render_arena.h" +#include "rendering/render_layer.h" +#include "rendering/render_frames.h" +#include "rendering/render_image.h" + +#include "khtmlview.h" +#include "khtml_part.h" + +#include +#include +#include +#include "khtml_settings.h" +#include "khtmlpart_p.h" + +#include "html/html_baseimpl.h" +#include "html/html_blockimpl.h" +#include "html/html_documentimpl.h" +#include "html/html_formimpl.h" +#include "html/html_headimpl.h" +#include "html/html_imageimpl.h" +#include "html/html_listimpl.h" +#include "html/html_miscimpl.h" +#include "html/html_tableimpl.h" +#include "html/html_objectimpl.h" + +#include +#include + +#include +#include "dom_docimpl.h" + +using namespace DOM; +using namespace khtml; + +// ------------------------------------------------------------------------ + +DOMImplementationImpl *DOMImplementationImpl::m_instance = 0; + +DOMImplementationImpl::DOMImplementationImpl() +{ +} + +DOMImplementationImpl::~DOMImplementationImpl() +{ +} + +bool DOMImplementationImpl::hasFeature ( const DOMString &feature, const DOMString &version ) +{ + // ### update when we (fully) support the relevant features + QString lower = feature.string().lower(); + if ((lower == "html" || lower == "xml") && + (version.isEmpty() || version == "1.0" || version == "2.0" || version == "null")) + return true; + + // ## Do we support Core Level 3 ? + if ((lower == "core" ) && + (version.isEmpty() || version == "2.0" || version == "null")) + return true; + + if ((lower == "events" || lower == "uievents" || + lower == "mouseevents" || lower == "mutationevents" || + lower == "htmlevents" || lower == "textevents" ) && + (version.isEmpty() || version == "2.0" || version == "3.0" || version == "null")) + return true; + return false; +} + +DocumentTypeImpl *DOMImplementationImpl::createDocumentType( const DOMString &qualifiedName, const DOMString &publicId, + const DOMString &systemId, int &exceptioncode ) +{ + // Not mentioned in spec: throw NAMESPACE_ERR if no qualifiedName supplied + if (qualifiedName.isNull()) { + exceptioncode = DOMException::NAMESPACE_ERR; + return 0; + } + + // INVALID_CHARACTER_ERR: Raised if the specified qualified name contains an illegal character. + if (!Element::khtmlValidQualifiedName(qualifiedName)) { + exceptioncode = DOMException::INVALID_CHARACTER_ERR; + return 0; + } + + // NAMESPACE_ERR: Raised if the qualifiedName is malformed. + // Added special case for the empty string, which seems to be a common pre-DOM2 misuse + if (!qualifiedName.isEmpty() && Element::khtmlMalformedQualifiedName(qualifiedName)) { + exceptioncode = DOMException::NAMESPACE_ERR; + return 0; + } + + return new DocumentTypeImpl(this,0,qualifiedName,publicId,systemId); +} + +DOMImplementationImpl* DOMImplementationImpl::getInterface(const DOMString& /*feature*/) const +{ + // ### + return 0; +} + +DocumentImpl *DOMImplementationImpl::createDocument( const DOMString &namespaceURI, const DOMString &qualifiedName, + const DocumentType &doctype, int &exceptioncode ) +{ + exceptioncode = 0; + + if (!checkQualifiedName(qualifiedName, namespaceURI, 0, true/*nameCanBeNull*/, + true /*nameCanBeEmpty, see #61650*/, &exceptioncode) ) + return 0; + + DocumentTypeImpl *dtype = static_cast(doctype.handle()); + // WRONG_DOCUMENT_ERR: Raised if doctype has already been used with a different document or was + // created from a different implementation. + if (dtype && (dtype->getDocument() || dtype->implementation() != this)) { + exceptioncode = DOMException::WRONG_DOCUMENT_ERR; + return 0; + } + + // ### this is completely broken.. without a view it will not work (Dirk) + DocumentImpl *doc = new DocumentImpl(this, 0); + + // now get the interesting parts of the doctype + // ### create new one if not there (currently always there) + if (doc->doctype() && dtype) + doc->doctype()->copyFrom(*dtype); + + // the document must be created empty if all parameters are null + // (or empty for qName/nsURI as a tolerance) - see DOM 3 Core. + if (dtype || !qualifiedName.isEmpty() || !namespaceURI.isEmpty()) { + ElementImpl *element = doc->createElementNS(namespaceURI,qualifiedName); + doc->appendChild(element,exceptioncode); + if (exceptioncode) { + delete element; + delete doc; + return 0; + } + } + return doc; +} + +CSSStyleSheetImpl *DOMImplementationImpl::createCSSStyleSheet(DOMStringImpl* /*title*/, DOMStringImpl *media, + int &/*exceptioncode*/) +{ + // ### TODO : title should be set, and media could have wrong syntax, in which case we should + // generate an exception. + CSSStyleSheetImpl *parent = 0L; + CSSStyleSheetImpl *sheet = new CSSStyleSheetImpl(parent, DOMString()); + sheet->setMedia(new MediaListImpl(sheet, media)); + return sheet; +} + +DocumentImpl *DOMImplementationImpl::createDocument( KHTMLView *v ) +{ + return new DocumentImpl(this, v); +} + +HTMLDocumentImpl *DOMImplementationImpl::createHTMLDocument( KHTMLView *v ) +{ + return new HTMLDocumentImpl(this, v); +} + +DOMImplementationImpl *DOMImplementationImpl::instance() +{ + if (!m_instance) { + m_instance = new DOMImplementationImpl(); + m_instance->ref(); + } + + return m_instance; +} + +// ------------------------------------------------------------------------ + + +ElementMappingCache::ElementMappingCache():m_dict(257) +{ + m_dict.setAutoDelete(true); +} + +void ElementMappingCache::add(const QString& id, ElementImpl* nd) +{ + if (id.isEmpty()) return; + + ItemInfo* info = m_dict.find(id); + if (info) + { + info->ref++; + info->nd = 0; //Now ambigous + } + else + { + ItemInfo* info = new ItemInfo(); + info->ref = 1; + info->nd = nd; + m_dict.insert(id, info); + } +} + +void ElementMappingCache::set(const QString& id, ElementImpl* nd) +{ + if (id.isEmpty()) return; + + ItemInfo* info = m_dict.find(id); + info->nd = nd; +} + +void ElementMappingCache::remove(const QString& id, ElementImpl* nd) +{ + if (id.isEmpty()) return; + + ItemInfo* info = m_dict.find(id); + info->ref--; + if (info->ref == 0) + { + m_dict.take(id); + delete info; + } + else + { + if (info->nd == nd) + info->nd = 0; + } +} + +bool ElementMappingCache::contains(const QString& id) +{ + if (id.isEmpty()) return false; + return m_dict.find(id); +} + +ElementMappingCache::ItemInfo* ElementMappingCache::get(const QString& id) +{ + if (id.isEmpty()) return 0; + return m_dict.find(id); +} + +static KStaticDeleter< QPtrList > s_changedDocumentsDeleter; +QPtrList * DocumentImpl::changedDocuments; + +// KHTMLView might be 0 +DocumentImpl::DocumentImpl(DOMImplementationImpl *_implementation, KHTMLView *v) + : NodeBaseImpl( 0 ), m_domtree_version(0), m_counterDict(257), + m_imageLoadEventTimer(0) +{ + m_document.resetSkippingRef(this); //Make getDocument return us.. + m_selfOnlyRefCount = 0; + + m_paintDeviceMetrics = 0; + m_paintDevice = 0; + m_decoderMibEnum = 0; + m_textColor = Qt::black; + + m_view = v; + m_renderArena.reset(); + + KHTMLFactory::ref(); + + if ( v ) { + m_docLoader = new DocLoader(v->part(), this ); + setPaintDevice( m_view ); + } + else + m_docLoader = new DocLoader( 0, this ); + + visuallyOrdered = false; + m_bParsing = false; + m_docChanged = false; + m_elemSheet = 0; + m_tokenizer = 0; + + // ### this should be created during parsing a + // not during construction. Not sure who added that and why (Dirk) + m_doctype = new DocumentTypeImpl(_implementation, getDocument(), + DOMString() /* qualifiedName */, + DOMString() /* publicId */, + DOMString() /* systemId */); + m_doctype->ref(); + + m_implementation = _implementation; + m_implementation->ref(); + pMode = Strict; + hMode = XHtml; + m_textColor = "#000000"; + m_attrMap = new IdNameMapping(ATTR_LAST_ATTR+1); + m_elementMap = new IdNameMapping(ID_LAST_TAG+1); + m_namespaceMap = new IdNameMapping(1); + QString xhtml(XHTML_NAMESPACE); + m_namespaceMap->names.insert(emptyNamespace, new DOMStringImpl("")); + m_namespaceMap->names.insert(xhtmlNamespace, new DOMStringImpl(xhtml.unicode(), xhtml.length())); + m_namespaceMap->names[emptyNamespace]->ref(); + m_namespaceMap->names[xhtmlNamespace]->ref(); + m_namespaceMap->count+=2; + m_focusNode = 0; + m_hoverNode = 0; + m_activeNode = 0; + m_defaultView = new AbstractViewImpl(this); + m_defaultView->ref(); + m_listenerTypes = 0; + m_styleSheets = new StyleSheetListImpl; + m_styleSheets->ref(); + m_addedStyleSheets = 0; + m_inDocument = true; + m_styleSelectorDirty = false; + m_styleSelector = 0; + m_counterDict.setAutoDelete(true); + + m_inStyleRecalc = false; + m_pendingStylesheets = 0; + m_ignorePendingStylesheets = false; + m_async = true; + m_hadLoadError = false; + m_docLoading = false; + m_inSyncLoad = false; + m_loadingXMLDoc = 0; + m_cssTarget = 0; + m_dynamicDomRestyler = new khtml::DynamicDomRestyler(); +} + +void DocumentImpl::removedLastRef() +{ + if (m_selfOnlyRefCount) { + /* In this case, the only references to us are from children, + so we have a cycle. We'll try to break it by disconnecting the + children from us; this sucks/is wrong, but it's pretty much + the best we can do without tracing. + + Of course, if dumping the children causes the refcount from them to + drop to 0 we can get killed right here, so better hold + a temporary reference, too + */ + DocPtr guard(this); + + // we must make sure not to be retaining any of our children through + // these extra pointers or we will create a reference cycle + if (m_doctype) { + m_doctype->deref(); + m_doctype = 0; + } + + if (m_cssTarget) { + m_cssTarget->deref(); + m_cssTarget = 0; + } + + if (m_focusNode) { + m_focusNode->deref(); + m_focusNode = 0; + } + + if (m_hoverNode) { + m_hoverNode->deref(); + m_hoverNode = 0; + } + + if (m_activeNode) { + m_activeNode->deref(); + m_activeNode = 0; + } + + removeChildren(); + + delete m_tokenizer; + m_tokenizer = 0; + } else { + delete this; + } +} + +DocumentImpl::~DocumentImpl() +{ + //Important: if you need to remove stuff here, + //you may also have to fix removedLastRef() above - M.O. + assert( !m_render ); + + QIntDictIterator it(m_nodeListCache); + for (; it.current(); ++it) + it.current()->deref(); + + if (m_loadingXMLDoc) + m_loadingXMLDoc->deref(this); + if (changedDocuments && m_docChanged) + changedDocuments->remove(this); + delete m_tokenizer; + m_document.resetSkippingRef(0); + delete m_styleSelector; + delete m_docLoader; + if (m_elemSheet ) m_elemSheet->deref(); + if (m_doctype) + m_doctype->deref(); + m_implementation->deref(); + delete m_paintDeviceMetrics; + delete m_elementMap; + delete m_attrMap; + delete m_namespaceMap; + delete m_dynamicDomRestyler; + m_defaultView->deref(); + m_styleSheets->deref(); + if (m_addedStyleSheets) + m_addedStyleSheets->deref(); + if (m_cssTarget) + m_cssTarget->deref(); + if (m_focusNode) + m_focusNode->deref(); + if ( m_hoverNode ) + m_hoverNode->deref(); + if (m_activeNode) + m_activeNode->deref(); + + m_renderArena.reset(); + + KHTMLFactory::deref(); +} + + +DocumentTypeImpl *DocumentImpl::doctype() const +{ + return m_doctype; +} + +DOMImplementationImpl *DocumentImpl::implementation() const +{ + return m_implementation; +} + +ElementImpl *DocumentImpl::documentElement() const +{ + NodeImpl *n = firstChild(); + while (n && n->nodeType() != Node::ELEMENT_NODE) + n = n->nextSibling(); + return static_cast(n); +} + +ElementImpl *DocumentImpl::createElement( const DOMString &name, int* pExceptioncode ) +{ + Id id = getId( NodeImpl::ElementId, name.implementation(), + false /* allocate */, false /*HTMLDocumentImpl::createElement looked for HTML elements already*/, + pExceptioncode); + if ( pExceptioncode && *pExceptioncode ) + return 0; + + XMLElementImpl* e = new XMLElementImpl( getDocument(), id ); + e->setHTMLCompat( htmlMode() != XHtml ); // Not a real HTML element, but inside an html-compat doc all tags are uppercase. + return e; +} + +AttrImpl *DocumentImpl::createAttribute( const DOMString &tagName, int* pExceptioncode ) +{ + Id id = getId( NodeImpl::AttributeId, tagName.implementation(), + false /* allocate */, isHTMLDocument(), pExceptioncode); + if ( pExceptioncode && *pExceptioncode ) + return 0; + AttrImpl* attr = new AttrImpl( 0, getDocument(), id, DOMString("").implementation()); + attr->setHTMLCompat( htmlMode() != XHtml ); + return attr; +} + +DocumentFragmentImpl *DocumentImpl::createDocumentFragment( ) +{ + return new DocumentFragmentImpl( docPtr() ); +} + +CommentImpl *DocumentImpl::createComment ( DOMStringImpl* data ) +{ + return new CommentImpl( docPtr(), data ); +} + +CDATASectionImpl *DocumentImpl::createCDATASection ( DOMStringImpl* data ) +{ + return new CDATASectionImpl( docPtr(), data ); +} + +ProcessingInstructionImpl *DocumentImpl::createProcessingInstruction ( const DOMString &target, DOMStringImpl* data ) +{ + return new ProcessingInstructionImpl( docPtr(),target,data); +} + +EntityReferenceImpl *DocumentImpl::createEntityReference ( const DOMString &name ) +{ + return new EntityReferenceImpl(docPtr(), name.implementation()); +} + +NodeImpl *DocumentImpl::importNode(NodeImpl *importedNode, bool deep, int &exceptioncode) +{ + NodeImpl *result = 0; + + // Not mentioned in spec: throw NOT_FOUND_ERR if evt is null + if (!importedNode) { + exceptioncode = DOMException::NOT_FOUND_ERR; + return 0; + } + + if(importedNode->nodeType() == Node::ELEMENT_NODE) + { + // Why not use cloneNode? + ElementImpl *otherElem = static_cast(importedNode); + NamedAttrMapImpl *otherMap = static_cast(importedNode)->attributes(true); + + ElementImpl *tempElementImpl; + if (!importedNode->localName().isNull()) + tempElementImpl = createElementNS(otherElem->namespaceURI(),otherElem->nodeName()); + else + tempElementImpl = createElement(otherElem->nodeName()); + result = tempElementImpl; + + + if(otherMap) { + for(unsigned long i = 0; i < otherMap->length(); i++) + { + AttrImpl *otherAttr = otherMap->attrAt(i)->createAttr(otherElem,otherElem->docPtr()); + + if (!otherAttr->localName().isNull()) { + // attr was created via createElementNS() + tempElementImpl->setAttributeNS(otherAttr->namespaceURI(), + otherAttr->name(), + otherAttr->nodeValue(), + exceptioncode); + } + else { + // attr was created via createElement() + tempElementImpl->setAttribute(otherAttr->id(), + otherAttr->nodeValue(), + otherAttr->name(), + exceptioncode); + } + + if(exceptioncode != 0) + break; // ### properly cleanup here + } + } + } + else if(importedNode->nodeType() == Node::TEXT_NODE) + { + result = createTextNode(static_cast(importedNode)->string()); + deep = false; + } + else if(importedNode->nodeType() == Node::CDATA_SECTION_NODE) + { + result = createCDATASection(static_cast(importedNode)->string()); + deep = false; + } + else if(importedNode->nodeType() == Node::ENTITY_REFERENCE_NODE) + result = createEntityReference(importedNode->nodeName()); + else if(importedNode->nodeType() == Node::PROCESSING_INSTRUCTION_NODE) + { + result = createProcessingInstruction(importedNode->nodeName(), importedNode->nodeValue().implementation()); + deep = false; + } + else if(importedNode->nodeType() == Node::COMMENT_NODE) + { + result = createComment(static_cast(importedNode)->string()); + deep = false; + } + else if (importedNode->nodeType() == Node::DOCUMENT_FRAGMENT_NODE) + result = createDocumentFragment(); + else + exceptioncode = DOMException::NOT_SUPPORTED_ERR; + + //### FIXME: This should handle Attributes, and a few other things + + if(deep && result) + { + for(Node n = importedNode->firstChild(); !n.isNull(); n = n.nextSibling()) + result->appendChild(importNode(n.handle(), true, exceptioncode), exceptioncode); + } + + return result; +} + +ElementImpl *DocumentImpl::createElementNS( const DOMString &_namespaceURI, const DOMString &_qualifiedName, int* pExceptioncode ) +{ + ElementImpl *e = 0; + int colonPos = -2; + // check NAMESPACE_ERR/INVALID_CHARACTER_ERR + if (pExceptioncode && !checkQualifiedName(_qualifiedName, _namespaceURI, &colonPos, + false/*nameCanBeNull*/, false/*nameCanBeEmpty*/, + pExceptioncode)) + return 0; + DOMString prefix, localName; + splitPrefixLocalName(_qualifiedName.implementation(), prefix, localName, colonPos); + + if ((isHTMLDocument() && _namespaceURI.isNull()) || + (strcasecmp(_namespaceURI, XHTML_NAMESPACE) == 0 && localName == localName.lower())) { + e = createHTMLElement(localName); + if (e) { + int _exceptioncode = 0; + if (!prefix.isNull()) + e->setPrefix(prefix, _exceptioncode); + if ( _exceptioncode ) { + if ( pExceptioncode ) *pExceptioncode = _exceptioncode; + delete e; + return 0; + } + e->setHTMLCompat( _namespaceURI.isNull() && htmlMode() != XHtml ); + } + } + if (!e) { + Id id = getId(NodeImpl::ElementId, _namespaceURI.implementation(), prefix.implementation(), + localName.implementation(), false, false /*HTML already looked up*/); + e = new XMLElementImpl( getDocument(), id, prefix.implementation() ); + } + + return e; +} + +AttrImpl *DocumentImpl::createAttributeNS( const DOMString &_namespaceURI, + const DOMString &_qualifiedName, int* pExceptioncode) +{ + int colonPos = -2; + // check NAMESPACE_ERR/INVALID_CHARACTER_ERR + if (pExceptioncode && !checkQualifiedName(_qualifiedName, _namespaceURI, &colonPos, + false/*nameCanBeNull*/, false/*nameCanBeEmpty*/, + pExceptioncode)) + return 0; + DOMString prefix, localName; + splitPrefixLocalName(_qualifiedName.implementation(), prefix, localName, colonPos); + Id id = getId(NodeImpl::AttributeId, _namespaceURI.implementation(), prefix.implementation(), + localName.implementation(), false, true /*lookupHTML*/); + AttrImpl* attr = new AttrImpl(0, getDocument(), id, DOMString("").implementation(), + prefix.implementation()); + attr->setHTMLCompat( _namespaceURI.isNull() && htmlMode() != XHtml ); + return attr; +} + +ElementImpl *DocumentImpl::getElementById( const DOMString &elementId ) const +{ + QString stringKey = elementId.string(); + + ElementMappingCache::ItemInfo* info = m_getElementByIdCache.get(stringKey); + + if (!info) + return 0; + + //See if cache has an unambiguous answer. + if (info->nd) + return info->nd; + + //Now we actually have to walk. + QPtrStack nodeStack; + NodeImpl *current = _first; + + while(1) + { + if(!current) + { + if(nodeStack.isEmpty()) break; + current = nodeStack.pop(); + current = current->nextSibling(); + } + else + { + if(current->isElementNode()) + { + ElementImpl *e = static_cast(current); + if(e->getAttribute(ATTR_ID) == elementId) { + info->nd = e; + return e; + } + } + + NodeImpl *child = current->firstChild(); + if(child) + { + nodeStack.push(current); + current = child; + } + else + { + current = current->nextSibling(); + } + } + } + + assert(0); //If there is no item with such an ID, we should never get here + + //kdDebug() << "WARNING: *DocumentImpl::getElementById not found " << elementId.string() << endl; + + return 0; +} + +void DocumentImpl::setTitle(const DOMString& _title) +{ + if (_title == m_title && !m_title.isNull()) return; + + m_title = _title; + + QString titleStr = m_title.string(); + for (unsigned int i = 0; i < titleStr.length(); ++i) + if (titleStr[i] < ' ') + titleStr[i] = ' '; + titleStr = titleStr.simplifyWhiteSpace(); + titleStr.compose(); + if ( view() && !view()->part()->parentPart() ) { + if (titleStr.isNull() || titleStr.isEmpty()) { + // empty title... set window caption as the URL + KURL url = m_url; + url.setRef(QString::null); + url.setQuery(QString::null); + titleStr = url.prettyURL(); + } + + emit view()->part()->setWindowCaption( KStringHandler::csqueeze( titleStr, 128 ) ); + } +} + +DOMString DocumentImpl::nodeName() const +{ + return "#document"; +} + +unsigned short DocumentImpl::nodeType() const +{ + return Node::DOCUMENT_NODE; +} + +DOMStringImpl* DocumentImpl::textContent() const +{ + return 0; +} + +void DocumentImpl::setTextContent( const DOMString&, int& ) +{} + +ElementImpl *DocumentImpl::createHTMLElement( const DOMString &name ) +{ + uint id = khtml::getTagID( name.string().lower().latin1(), name.string().length() ); +// id = makeId(xhtmlNamespace, id); + + ElementImpl *n = 0; + switch(id) + { + case ID_HTML: + n = new HTMLHtmlElementImpl(docPtr()); + break; + case ID_HEAD: + n = new HTMLHeadElementImpl(docPtr()); + break; + case ID_BODY: + n = new HTMLBodyElementImpl(docPtr()); + break; + +// head elements + case ID_BASE: + n = new HTMLBaseElementImpl(docPtr()); + break; + case ID_LINK: + n = new HTMLLinkElementImpl(docPtr()); + break; + case ID_META: + n = new HTMLMetaElementImpl(docPtr()); + break; + case ID_STYLE: + n = new HTMLStyleElementImpl(docPtr()); + break; + case ID_TITLE: + n = new HTMLTitleElementImpl(docPtr()); + break; + +// frames + case ID_FRAME: + n = new HTMLFrameElementImpl(docPtr()); + break; + case ID_FRAMESET: + n = new HTMLFrameSetElementImpl(docPtr()); + break; + case ID_IFRAME: + n = new HTMLIFrameElementImpl(docPtr()); + break; + +// form elements +// ### FIXME: we need a way to set form dependency after we have made the form elements + case ID_FORM: + n = new HTMLFormElementImpl(docPtr(), false); + break; + case ID_BUTTON: + n = new HTMLButtonElementImpl(docPtr()); + break; + case ID_FIELDSET: + n = new HTMLFieldSetElementImpl(docPtr()); + break; + case ID_INPUT: + n = new HTMLInputElementImpl(docPtr()); + break; + case ID_ISINDEX: + n = new HTMLIsIndexElementImpl(docPtr()); + break; + case ID_LABEL: + n = new HTMLLabelElementImpl(docPtr()); + break; + case ID_LEGEND: + n = new HTMLLegendElementImpl(docPtr()); + break; + case ID_OPTGROUP: + n = new HTMLOptGroupElementImpl(docPtr()); + break; + case ID_OPTION: + n = new HTMLOptionElementImpl(docPtr()); + break; + case ID_SELECT: + n = new HTMLSelectElementImpl(docPtr()); + break; + case ID_TEXTAREA: + n = new HTMLTextAreaElementImpl(docPtr()); + break; + +// lists + case ID_DL: + n = new HTMLDListElementImpl(docPtr()); + break; + case ID_DD: + n = new HTMLGenericElementImpl(docPtr(), id); + break; + case ID_DT: + n = new HTMLGenericElementImpl(docPtr(), id); + break; + case ID_UL: + n = new HTMLUListElementImpl(docPtr()); + break; + case ID_OL: + n = new HTMLOListElementImpl(docPtr()); + break; + case ID_DIR: + n = new HTMLDirectoryElementImpl(docPtr()); + break; + case ID_MENU: + n = new HTMLMenuElementImpl(docPtr()); + break; + case ID_LI: + n = new HTMLLIElementImpl(docPtr()); + break; + +// formatting elements (block) + case ID_DIV: + case ID_P: + n = new HTMLDivElementImpl( docPtr(), id ); + break; + case ID_BLOCKQUOTE: + case ID_H1: + case ID_H2: + case ID_H3: + case ID_H4: + case ID_H5: + case ID_H6: + n = new HTMLGenericElementImpl(docPtr(), id); + break; + case ID_HR: + n = new HTMLHRElementImpl(docPtr()); + break; + case ID_PLAINTEXT: + case ID_XMP: + case ID_PRE: + n = new HTMLPreElementImpl(docPtr(), id); + break; + +// font stuff + case ID_BASEFONT: + n = new HTMLBaseFontElementImpl(docPtr()); + break; + case ID_FONT: + n = new HTMLFontElementImpl(docPtr()); + break; + +// ins/del + case ID_DEL: + case ID_INS: + n = new HTMLGenericElementImpl(docPtr(), id); + break; + +// anchor + case ID_A: + n = new HTMLAnchorElementImpl(docPtr()); + break; + +// images + case ID_IMG: + n = new HTMLImageElementImpl(docPtr()); + break; + case ID_MAP: + n = new HTMLMapElementImpl(docPtr()); + /*n = map;*/ + break; + case ID_AREA: + n = new HTMLAreaElementImpl(docPtr()); + break; + +// objects, applets and scripts + case ID_APPLET: + n = new HTMLAppletElementImpl(docPtr()); + break; + case ID_OBJECT: + n = new HTMLObjectElementImpl(docPtr()); + break; + case ID_EMBED: + n = new HTMLEmbedElementImpl(docPtr()); + break; + case ID_PARAM: + n = new HTMLParamElementImpl(docPtr()); + break; + case ID_SCRIPT: + n = new HTMLScriptElementImpl(docPtr()); + break; + +// tables + case ID_TABLE: + n = new HTMLTableElementImpl(docPtr()); + break; + case ID_CAPTION: + n = new HTMLTableCaptionElementImpl(docPtr()); + break; + case ID_COLGROUP: + case ID_COL: + n = new HTMLTableColElementImpl(docPtr(), id); + break; + case ID_TR: + n = new HTMLTableRowElementImpl(docPtr()); + break; + case ID_TD: + case ID_TH: + n = new HTMLTableCellElementImpl(docPtr(), id); + break; + case ID_THEAD: + case ID_TBODY: + case ID_TFOOT: + n = new HTMLTableSectionElementImpl(docPtr(), id, false); + break; + +// inline elements + case ID_BR: + n = new HTMLBRElementImpl(docPtr()); + break; + case ID_Q: + n = new HTMLGenericElementImpl(docPtr(), id); + break; + +// elements with no special representation in the DOM + +// block: + case ID_ADDRESS: + case ID_CENTER: + n = new HTMLGenericElementImpl(docPtr(), id); + break; +// inline + // %fontstyle + case ID_TT: + case ID_U: + case ID_B: + case ID_I: + case ID_S: + case ID_STRIKE: + case ID_BIG: + case ID_SMALL: + + // %phrase + case ID_EM: + case ID_STRONG: + case ID_DFN: + case ID_CODE: + case ID_SAMP: + case ID_KBD: + case ID_VAR: + case ID_CITE: + case ID_ABBR: + case ID_ACRONYM: + + // %special + case ID_SUB: + case ID_SUP: + case ID_SPAN: + case ID_NOBR: + case ID_WBR: + case ID_BDO: + case ID_NOFRAMES: + n = new HTMLGenericElementImpl(docPtr(), id); + break; + + case ID_MARQUEE: + n = new HTMLMarqueeElementImpl(docPtr()); + break; +// text + case ID_TEXT: + kdDebug( 6020 ) << "Use document->createTextNode()" << endl; + break; + + default: + break; + } + return n; +} + +QString DocumentImpl::nextState() +{ + QString state; + if (!m_state.isEmpty()) + { + state = m_state.first(); + m_state.remove(m_state.begin()); + } + return state; +} + +QStringList DocumentImpl::docState() +{ + QStringList s; + for (QPtrListIterator it(m_maintainsState); it.current(); ++it) + s.append(it.current()->state()); + + return s; +} + +bool DocumentImpl::unsubmittedFormChanges() +{ + for (QPtrListIterator it(m_maintainsState); it.current(); ++it) + if (it.current()->state().right(1)=="M") + return true; + + return false; +} + +RangeImpl *DocumentImpl::createRange() +{ + return new RangeImpl( docPtr() ); +} + +NodeIteratorImpl *DocumentImpl::createNodeIterator(NodeImpl *root, unsigned long whatToShow, + NodeFilter &filter, bool entityReferenceExpansion, + int &exceptioncode) +{ + if (!root) { + exceptioncode = DOMException::NOT_SUPPORTED_ERR; + return 0; + } + + return new NodeIteratorImpl(root,whatToShow,filter,entityReferenceExpansion); +} + +TreeWalkerImpl *DocumentImpl::createTreeWalker(NodeImpl *root, unsigned long whatToShow, NodeFilterImpl *filter, + bool entityReferenceExpansion, int &exceptioncode) +{ + if (!root) { + exceptioncode = DOMException::NOT_SUPPORTED_ERR; + return 0; + } + + return new TreeWalkerImpl( root, whatToShow, filter, entityReferenceExpansion ); +} + +void DocumentImpl::setDocumentChanged(bool b) +{ + if (!changedDocuments) + changedDocuments = s_changedDocumentsDeleter.setObject( changedDocuments, new QPtrList() ); + + if (b && !m_docChanged) + changedDocuments->append(this); + else if (!b && m_docChanged) + changedDocuments->remove(this); + m_docChanged = b; +} + +void DocumentImpl::recalcStyle( StyleChange change ) +{ +// qDebug("recalcStyle(%p)", this); +// QTime qt; +// qt.start(); + if (m_inStyleRecalc) + return; // Guard against re-entrancy. -dwh + + m_inStyleRecalc = true; + + if( !m_render ) goto bail_out; + + if ( change == Force ) { + RenderStyle* oldStyle = m_render->style(); + if ( oldStyle ) oldStyle->ref(); + RenderStyle* _style = new RenderStyle(); + _style->setDisplay(BLOCK); + _style->setVisuallyOrdered( visuallyOrdered ); + // ### make the font stuff _really_ work!!!! + + khtml::FontDef fontDef; + QFont f = KGlobalSettings::generalFont(); + fontDef.family = f.family(); + fontDef.italic = f.italic(); + fontDef.weight = f.weight(); + if (m_view) { + const KHTMLSettings *settings = m_view->part()->settings(); + QString stdfont = settings->stdFontName(); + if ( !stdfont.isEmpty() ) + fontDef.family = stdfont; + + fontDef.size = m_styleSelector->fontSizes()[3]; + } + + //kdDebug() << "DocumentImpl::attach: setting to charset " << settings->charset() << endl; + _style->setFontDef(fontDef); + _style->htmlFont().update( paintDeviceMetrics() ); + if ( inCompatMode() ) + _style->setHtmlHacks(true); // enable html specific rendering tricks + + StyleChange ch = diff( _style, oldStyle ); + if(m_render && ch != NoChange) + m_render->setStyle(_style); + else + delete _style; + if ( change != Force ) + change = ch; + + if (oldStyle) + oldStyle->deref(); + } + + NodeImpl *n; + for (n = _first; n; n = n->nextSibling()) + if ( change>= Inherit || n->hasChangedChild() || n->changed() ) + n->recalcStyle( change ); + //kdDebug( 6020 ) << "TIME: recalcStyle() dt=" << qt.elapsed() << endl; + + if (changed() && m_view) + m_view->layout(); + +bail_out: + setChanged( false ); + setHasChangedChild( false ); + setDocumentChanged( false ); + + m_inStyleRecalc = false; +} + +void DocumentImpl::updateRendering() +{ + if (!hasChangedChild()) return; + +// QTime time; +// time.start(); +// kdDebug() << "UPDATERENDERING: "<")); + } + m_tokenizer->write(text, false); +} + +void DocumentImpl::writeln( const DOMString &text ) +{ + write(text); + write(DOMString("\n")); +} + +void DocumentImpl::finishParsing ( ) +{ + if(m_tokenizer) + m_tokenizer->finish(); +} + +void DocumentImpl::setUserStyleSheet( const QString& sheet ) +{ + if ( m_usersheet != sheet ) { + m_usersheet = sheet; + updateStyleSelector(); + } +} + +CSSStyleSheetImpl* DocumentImpl::elementSheet() +{ + if (!m_elemSheet) { + m_elemSheet = new CSSStyleSheetImpl(this, baseURL().url() ); + m_elemSheet->ref(); + } + return m_elemSheet; +} + +void DocumentImpl::determineParseMode( const QString &/*str*/ ) +{ + // For XML documents, use strict parse mode + pMode = Strict; + hMode = XHtml; + kdDebug(6020) << " using strict parseMode" << endl; +} + +NodeImpl *DocumentImpl::nextFocusNode(NodeImpl *fromNode) +{ + unsigned short fromTabIndex; + + if (!fromNode) { + // No starting node supplied; begin with the top of the document + NodeImpl *n; + + int lowestTabIndex = 65535; + for (n = this; n != 0; n = n->traverseNextNode()) { + if (n->isTabFocusable()) { + if ((n->tabIndex() > 0) && (n->tabIndex() < lowestTabIndex)) + lowestTabIndex = n->tabIndex(); + } + } + + if (lowestTabIndex == 65535) + lowestTabIndex = 0; + + // Go to the first node in the document that has the desired tab index + for (n = this; n != 0; n = n->traverseNextNode()) { + if (n->isTabFocusable() && (n->tabIndex() == lowestTabIndex)) + return n; + } + + return 0; + } + else { + fromTabIndex = fromNode->tabIndex(); + } + + if (fromTabIndex == 0) { + // Just need to find the next selectable node after fromNode (in document order) that doesn't have a tab index + NodeImpl *n = fromNode->traverseNextNode(); + while (n && !(n->isTabFocusable() && n->tabIndex() == 0)) + n = n->traverseNextNode(); + return n; + } + else { + // Find the lowest tab index out of all the nodes except fromNode, that is greater than or equal to fromNode's + // tab index. For nodes with the same tab index as fromNode, we are only interested in those that come after + // fromNode in document order. + // If we don't find a suitable tab index, the next focus node will be one with a tab index of 0. + unsigned short lowestSuitableTabIndex = 65535; + NodeImpl *n; + + bool reachedFromNode = false; + for (n = this; n != 0; n = n->traverseNextNode()) { + if (n->isTabFocusable() && + ((reachedFromNode && (n->tabIndex() >= fromTabIndex)) || + (!reachedFromNode && (n->tabIndex() > fromTabIndex))) && + (n->tabIndex() < lowestSuitableTabIndex) && + (n != fromNode)) { + + // We found a selectable node with a tab index at least as high as fromNode's. Keep searching though, + // as there may be another node which has a lower tab index but is still suitable for use. + lowestSuitableTabIndex = n->tabIndex(); + } + + if (n == fromNode) + reachedFromNode = true; + } + + if (lowestSuitableTabIndex == 65535) { + // No next node with a tab index -> just take first node with tab index of 0 + NodeImpl *n = this; + while (n && !(n->isTabFocusable() && n->tabIndex() == 0)) + n = n->traverseNextNode(); + return n; + } + + // Search forwards from fromNode + for (n = fromNode->traverseNextNode(); n != 0; n = n->traverseNextNode()) { + if (n->isTabFocusable() && (n->tabIndex() == lowestSuitableTabIndex)) + return n; + } + + // The next node isn't after fromNode, start from the beginning of the document + for (n = this; n != fromNode; n = n->traverseNextNode()) { + if (n->isTabFocusable() && (n->tabIndex() == lowestSuitableTabIndex)) + return n; + } + + assert(false); // should never get here + return 0; + } +} + +NodeImpl *DocumentImpl::previousFocusNode(NodeImpl *fromNode) +{ + NodeImpl *lastNode = this; + while (lastNode->lastChild()) + lastNode = lastNode->lastChild(); + + if (!fromNode) { + // No starting node supplied; begin with the very last node in the document + NodeImpl *n; + + int highestTabIndex = 0; + for (n = lastNode; n != 0; n = n->traversePreviousNode()) { + if (n->isTabFocusable()) { + if (n->tabIndex() == 0) + return n; + else if (n->tabIndex() > highestTabIndex) + highestTabIndex = n->tabIndex(); + } + } + + // No node with a tab index of 0; just go to the last node with the highest tab index + for (n = lastNode; n != 0; n = n->traversePreviousNode()) { + if (n->isTabFocusable() && (n->tabIndex() == highestTabIndex)) + return n; + } + + return 0; + } + else { + unsigned short fromTabIndex = fromNode->tabIndex(); + + if (fromTabIndex == 0) { + // Find the previous selectable node before fromNode (in document order) that doesn't have a tab index + NodeImpl *n = fromNode->traversePreviousNode(); + while (n && !(n->isTabFocusable() && n->tabIndex() == 0)) + n = n->traversePreviousNode(); + if (n) + return n; + + // No previous nodes with a 0 tab index, go to the last node in the document that has the highest tab index + int highestTabIndex = 0; + for (n = this; n != 0; n = n->traverseNextNode()) { + if (n->isTabFocusable() && (n->tabIndex() > highestTabIndex)) + highestTabIndex = n->tabIndex(); + } + + if (highestTabIndex == 0) + return 0; + + for (n = lastNode; n != 0; n = n->traversePreviousNode()) { + if (n->isTabFocusable() && (n->tabIndex() == highestTabIndex)) + return n; + } + + assert(false); // should never get here + return 0; + } + else { + // Find the lowest tab index out of all the nodes except fromNode, that is less than or equal to fromNode's + // tab index. For nodes with the same tab index as fromNode, we are only interested in those before + // fromNode. + // If we don't find a suitable tab index, then there will be no previous focus node. + unsigned short highestSuitableTabIndex = 0; + NodeImpl *n; + + bool reachedFromNode = false; + for (n = this; n != 0; n = n->traverseNextNode()) { + if (n->isTabFocusable() && + ((!reachedFromNode && (n->tabIndex() <= fromTabIndex)) || + (reachedFromNode && (n->tabIndex() < fromTabIndex))) && + (n->tabIndex() > highestSuitableTabIndex) && + (n != fromNode)) { + + // We found a selectable node with a tab index no higher than fromNode's. Keep searching though, as + // there may be another node which has a higher tab index but is still suitable for use. + highestSuitableTabIndex = n->tabIndex(); + } + + if (n == fromNode) + reachedFromNode = true; + } + + if (highestSuitableTabIndex == 0) { + // No previous node with a tab index. Since the order specified by HTML is nodes with tab index > 0 + // first, this means that there is no previous node. + return 0; + } + + // Search backwards from fromNode + for (n = fromNode->traversePreviousNode(); n != 0; n = n->traversePreviousNode()) { + if (n->isTabFocusable() && (n->tabIndex() == highestSuitableTabIndex)) + return n; + } + // The previous node isn't before fromNode, start from the end of the document + for (n = lastNode; n != fromNode; n = n->traversePreviousNode()) { + if (n->isTabFocusable() && (n->tabIndex() == highestSuitableTabIndex)) + return n; + } + + assert(false); // should never get here + return 0; + } + } +} + +ElementImpl* DocumentImpl::findAccessKeyElement(QChar c) +{ + c = c.upper(); + for( NodeImpl* n = this; + n != NULL; + n = n->traverseNextNode()) { + if( n->isElementNode()) { + ElementImpl* en = static_cast< ElementImpl* >( n ); + DOMString s = en->getAttribute( ATTR_ACCESSKEY ); + if( s.length() == 1 + && s[ 0 ].upper() == c ) + return en; + } + } + return NULL; +} + +int DocumentImpl::nodeAbsIndex(NodeImpl *node) +{ + assert(node->getDocument() == this); + + int absIndex = 0; + for (NodeImpl *n = node; n && n != this; n = n->traversePreviousNode()) + absIndex++; + return absIndex; +} + +NodeImpl *DocumentImpl::nodeWithAbsIndex(int absIndex) +{ + NodeImpl *n = this; + for (int i = 0; n && (i < absIndex); i++) { + n = n->traverseNextNode(); + } + return n; +} + +void DocumentImpl::processHttpEquiv(const DOMString &equiv, const DOMString &content) +{ + assert(!equiv.isNull() && !content.isNull()); + + KHTMLView *v = getDocument()->view(); + + if(strcasecmp(equiv, "refresh") == 0 && v && v->part()->metaRefreshEnabled()) + { + // get delay and url + QString str = content.string().stripWhiteSpace(); + int pos = str.find(QRegExp("[;,]")); + if ( pos == -1 ) + pos = str.find(QRegExp("[ \t]")); + + bool ok = false; + int delay = kMax( 0, content.implementation()->toInt(&ok) ); + if ( !ok && str.length() && str[0] == '.' ) + ok = true; + + if (pos == -1) // There can be no url (David) + { + if(ok) + v->part()->scheduleRedirection(delay, v->part()->url().url() ); + } else { + pos++; + while(pos < (int)str.length() && str[pos].isSpace()) pos++; + str = str.mid(pos); + if(str.find("url", 0, false ) == 0) str = str.mid(3); + str = str.stripWhiteSpace(); + if ( str.length() && str[0] == '=' ) str = str.mid( 1 ).stripWhiteSpace(); + while(str.length() && + (str[str.length()-1] == ';' || str[str.length()-1] == ',')) + str.setLength(str.length()-1); + str = parseURL( DOMString(str) ).string(); + QString newURL = getDocument()->completeURL( str ); + if ( ok ) + v->part()->scheduleRedirection(delay, getDocument()->completeURL( str ), delay < 2 || newURL == URL().url()); + } + } + else if(strcasecmp(equiv, "expires") == 0) + { + bool relative = false; + QString str = content.string().stripWhiteSpace(); + time_t expire_date = KRFCDate::parseDate(str); + if (!expire_date) + { + expire_date = str.toULong(); + relative = true; + } + if (!expire_date) + expire_date = 1; // expire now + if (m_docLoader) + m_docLoader->setExpireDate(expire_date, relative); + } + else if(v && (strcasecmp(equiv, "pragma") == 0 || strcasecmp(equiv, "cache-control") == 0)) + { + QString str = content.string().lower().stripWhiteSpace(); + KURL url = v->part()->url(); + if ((str == "no-cache") && url.protocol().startsWith("http")) + { + KIO::http_update_cache(url, true, 0); + } + } + else if( (strcasecmp(equiv, "set-cookie") == 0)) + { + // ### make setCookie work on XML documents too; e.g. in case of + HTMLDocumentImpl *d = static_cast(this); + d->setCookie(content); + } + else if (strcasecmp(equiv, "default-style") == 0) { + // HTML 4.0 14.3.2 + // http://www.hixie.ch/tests/evil/css/import/main/preferred.html + m_preferredStylesheetSet = content; + updateStyleSelector(); + } + else if (strcasecmp(equiv, "content-language") == 0) { + m_contentLanguage = content.string(); + } +} + +bool DocumentImpl::prepareMouseEvent( bool readonly, int _x, int _y, MouseEvent *ev ) +{ + if ( m_render ) { + assert(m_render->isCanvas()); + RenderObject::NodeInfo renderInfo(readonly, ev->type == MousePress); + bool isInside = m_render->layer()->nodeAtPoint(renderInfo, _x, _y); + ev->innerNode = renderInfo.innerNode(); + ev->innerNonSharedNode = renderInfo.innerNonSharedNode(); + + if (renderInfo.URLElement()) { + assert(renderInfo.URLElement()->isElementNode()); + //qDebug("urlnode: %s (%d)", getTagName(renderInfo.URLElement()->id()).string().latin1(), renderInfo.URLElement()->id()); + + ElementImpl* e = static_cast(renderInfo.URLElement()); + DOMString href = khtml::parseURL(e->getAttribute(ATTR_HREF)); + DOMString target = e->getAttribute(ATTR_TARGET); + + if (!target.isNull() && !href.isNull()) { + ev->target = target; + ev->url = href; + } + else + ev->url = href; + } + + if (!readonly) + updateRendering(); + + return isInside; + } + + + return false; +} + + +// DOM Section 1.1.1 +bool DocumentImpl::childTypeAllowed( unsigned short type ) +{ + switch (type) { + case Node::ATTRIBUTE_NODE: + case Node::CDATA_SECTION_NODE: + case Node::DOCUMENT_FRAGMENT_NODE: + case Node::DOCUMENT_NODE: + case Node::ENTITY_NODE: + case Node::ENTITY_REFERENCE_NODE: + case Node::NOTATION_NODE: + case Node::TEXT_NODE: +// case Node::XPATH_NAMESPACE_NODE: + return false; + case Node::COMMENT_NODE: + case Node::PROCESSING_INSTRUCTION_NODE: + return true; + case Node::DOCUMENT_TYPE_NODE: + case Node::ELEMENT_NODE: + // Documents may contain no more than one of each of these. + // (One Element and one DocumentType.) + for (NodeImpl* c = firstChild(); c; c = c->nextSibling()) + if (c->nodeType() == type) + return false; + return true; + } + return false; +} + +NodeImpl *DocumentImpl::cloneNode ( bool /*deep*/ ) +{ + // Spec says cloning Document nodes is "implementation dependent" + // so we do not support it... + return 0; +} + + +typedef const char* (*NameLookupFunction)(unsigned short id); +typedef int (*IdLookupFunction)(const char *tagStr, int len); + +NodeImpl::Id DocumentImpl::getId( NodeImpl::IdType _type, DOMStringImpl* _nsURI, DOMStringImpl *_prefix, + DOMStringImpl *_name, bool readonly, bool /*lookupHTML*/, int *pExceptioncode) +{ + /*kdDebug() << "DocumentImpl::getId( type: " << _type << ", uri: " << DOMString(_nsURI).string() + << ", prefix: " << DOMString(_prefix).string() << ", name: " << DOMString(_name).string() + << ", readonly: " << readonly + << ", lookupHTML: " << lookupHTML + << ", exceptions: " << (pExceptioncode ? "yes" : "no") + << " )" << endl;*/ + + if(!_name) return 0; + IdNameMapping *map; + IdLookupFunction lookup; + + switch (_type) { + case NodeImpl::ElementId: + map = m_elementMap; + lookup = getTagID; + break; + case NodeImpl::AttributeId: + map = m_attrMap; + lookup = getAttrID; + break; + case NodeImpl::NamespaceId: + if( strcasecmp(_name, XHTML_NAMESPACE) == 0) + return xhtmlNamespace; + if( _name->l == 0) + return emptyNamespace; + // defaultNamespace handled by "if (!_name) return 0" + map = m_namespaceMap; + lookup = 0; + break; + default: + return 0; + } + // Names and attributes with "" + if (_name->l == 0) return 0; + + NodeImpl::Id id, nsid = 0; + QConstString n(_name->s, _name->l); + bool cs = true; // case sensitive + if (_type != NodeImpl::NamespaceId) { + if (_nsURI) + nsid = getId( NodeImpl::NamespaceId, 0, 0, _nsURI, false, false, 0 ) << 16; + + // Each document maintains a mapping of tag name -> id for every tag name encountered + // in the document. + cs = (htmlMode() == XHtml) || (_nsURI && _type != NodeImpl::AttributeId); + + // First see if it's a HTML element name + // xhtml is lower case - case sensitive, easy to implement + if ( cs && (id = lookup(n.string().ascii(), _name->l)) ) { + map->addAlias(_prefix, _name, cs, id); + return nsid + id; + } + // compatibility: upper case - case insensitive + if ( !cs && (id = lookup(n.string().lower().ascii(), _name->l )) ) { + map->addAlias(_prefix, _name, cs, id); + return nsid + id; + } + } + + // Look in the names array for the name + // compatibility mode has to lookup upper case + QString name = cs ? n.string() : n.string().upper(); + + if (!_nsURI) { + id = (NodeImpl::Id)(long) map->ids.find( name ); + if (!id && _type != NodeImpl::NamespaceId) { + id = (NodeImpl::Id)(long) map->ids.find( "aliases: " + name ); + } + } else { + id = (NodeImpl::Id)(long) map->ids.find( name ); + if (!readonly && id && _prefix && _prefix->l) { + // we were called in registration mode... check if the alias exists + QConstString px( _prefix->s, _prefix->l ); + QString qn("aliases: " + (cs ? px.string() : px.string().upper()) + ":" + name); + if (!map->ids.find( qn )) { + map->ids.insert( qn, (void*)id ); + } + } + } + + if (id) return nsid + id; + + // unknown + if (readonly) return 0; + + if ( pExceptioncode && _type != NodeImpl::NamespaceId && !Element::khtmlValidQualifiedName(_name)) { + *pExceptioncode = DOMException::INVALID_CHARACTER_ERR; + return 0; + } + + // Name not found, so let's add it + NodeImpl::Id cid = map->count++ + map->idStart; + map->names.insert( cid, _name ); + _name->ref(); + + map->ids.insert( name, (void*)cid ); + + // and register an alias if needed for DOM1 methods compatibility + map->addAlias(_prefix, _name, cs, cid); + + return nsid + cid; + } + +NodeImpl::Id DocumentImpl::getId( NodeImpl::IdType _type, DOMStringImpl *_nodeName, bool readonly, bool lookupHTML, int *pExceptioncode) +{ + return getId(_type, 0, 0, _nodeName, readonly, lookupHTML, pExceptioncode); +} + +DOMString DocumentImpl::getName( NodeImpl::IdType _type, NodeImpl::Id _id ) const +{ + IdNameMapping *map; + NameLookupFunction lookup; + bool hasNS = (namespacePart(_id) != defaultNamespace); + switch (_type) { + case NodeImpl::ElementId: + map = m_elementMap; + lookup = getTagName; + break; + case NodeImpl::AttributeId: + map = m_attrMap; + lookup = getAttrName; + break; + case NodeImpl::NamespaceId: + if( _id == xhtmlNamespace ) + return XHTML_NAMESPACE; + else + if( _id == emptyNamespace ) + return DOMString(""); + else + if ( _id == defaultNamespace ) + return DOMString(); + map = m_namespaceMap; + lookup = 0; + break; + default: + return DOMString();; + } + _id = localNamePart(_id) ; + if (_id >= map->idStart) { + return map->names[_id]; + } + else if (lookup) { + // ### put them in a cache + if (hasNS) + return DOMString(lookup(_id)).lower(); + else + return lookup(_id); + } else + return DOMString(); +} + +// This method is called whenever a top-level stylesheet has finished loading. +void DocumentImpl::styleSheetLoaded() +{ + // Make sure we knew this sheet was pending, and that our count isn't out of sync. + assert(m_pendingStylesheets > 0); + + m_pendingStylesheets--; + updateStyleSelector(); +} + +DOMString DocumentImpl::selectedStylesheetSet() const +{ + if (!view()) return DOMString(); + + return view()->part()->d->m_sheetUsed; +} + +void DocumentImpl::setSelectedStylesheetSet(const DOMString& s) +{ + // this code is evil + if (view() && view()->part()->d->m_sheetUsed != s.string()) { + view()->part()->d->m_sheetUsed = s.string(); + updateStyleSelector(); + } +} + +void DocumentImpl::addStyleSheet(StyleSheetImpl *sheet, int *exceptioncode) +{ + int excode = 0; + + if (!m_addedStyleSheets) { + m_addedStyleSheets = new StyleSheetListImpl; + m_addedStyleSheets->ref(); + } + + m_addedStyleSheets->add(sheet); + if (sheet->isCSSStyleSheet()) updateStyleSelector(); + + if (exceptioncode) *exceptioncode = excode; +} + +void DocumentImpl::removeStyleSheet(StyleSheetImpl *sheet, int *exceptioncode) +{ + int excode = 0; + bool removed = false; + bool is_css = sheet->isCSSStyleSheet(); + + if (m_addedStyleSheets) { + bool in_main_list = !sheet->hasOneRef(); + removed = m_addedStyleSheets->styleSheets.removeRef(sheet); + sheet->deref(); + + if (m_addedStyleSheets->styleSheets.count() == 0) { + bool reset = m_addedStyleSheets->hasOneRef(); + m_addedStyleSheets->deref(); + if (reset) m_addedStyleSheets = 0; + } + + // remove from main list, too + if (in_main_list) m_styleSheets->remove(sheet); + } + + if (removed) { + if (is_css) updateStyleSelector(); + } else + excode = DOMException::NOT_FOUND_ERR; + + if (exceptioncode) *exceptioncode = excode; +} + +void DocumentImpl::updateStyleSelector(bool shallow) +{ +// kdDebug() << "PENDING " << m_pendingStylesheets << endl; + + // Don't bother updating, since we haven't loaded all our style info yet. + if (m_pendingStylesheets > 0) + return; + + if (shallow) + rebuildStyleSelector(); + else + recalcStyleSelector(); + recalcStyle(Force); +#if 0 + + m_styleSelectorDirty = true; +#endif + if ( renderer() ) + renderer()->setNeedsLayoutAndMinMaxRecalc(); +} + +void DocumentImpl::recalcStyleSelector() +{ + if ( !m_render || !attached() ) return; + + assert(m_pendingStylesheets==0); + + QPtrList oldStyleSheets = m_styleSheets->styleSheets; + m_styleSheets->styleSheets.clear(); + QString sheetUsed = view() ? view()->part()->d->m_sheetUsed.replace("&&", "&") : QString(); + bool autoselect = sheetUsed.isEmpty(); + if (autoselect && !m_preferredStylesheetSet.isEmpty()) + sheetUsed = m_preferredStylesheetSet.string(); + NodeImpl *n; + for (int i=0 ; i<2 ; i++) { + m_availableSheets.clear(); + m_availableSheets << i18n("Basic Page Style"); + bool canResetSheet = false; + + for (n = this; n; n = n->traverseNextNode()) { + StyleSheetImpl *sheet = 0; + + if (n->nodeType() == Node::PROCESSING_INSTRUCTION_NODE) + { + // Processing instruction (XML documents only) + ProcessingInstructionImpl* pi = static_cast(n); + sheet = pi->sheet(); + if (!sheet && !pi->localHref().isEmpty()) + { + // Processing instruction with reference to an element in this document - e.g. + // , with the element + // heading { color: red; } at some location in + // the document + ElementImpl* elem = getElementById(pi->localHref()); + if (elem) { + DOMString sheetText(""); + NodeImpl *c; + for (c = elem->firstChild(); c; c = c->nextSibling()) { + if (c->nodeType() == Node::TEXT_NODE || c->nodeType() == Node::CDATA_SECTION_NODE) + sheetText += c->nodeValue(); + } + + CSSStyleSheetImpl *cssSheet = new CSSStyleSheetImpl(this); + cssSheet->parseString(sheetText); + pi->setStyleSheet(cssSheet); + sheet = cssSheet; + } + } + + } + else if (n->isHTMLElement() && ( n->id() == ID_LINK || n->id() == ID_STYLE) ) { + QString title; + if ( n->id() == ID_LINK ) { + HTMLLinkElementImpl* l = static_cast(n); + if (l->isCSSStyleSheet()) { + sheet = l->sheet(); + + if (sheet || l->isLoading() || l->isAlternate() ) + title = l->getAttribute(ATTR_TITLE).string(); + + if ((autoselect || title != sheetUsed) && l->isDisabled()) { + sheet = 0; + } else if (!title.isEmpty() && !l->isAlternate() && sheetUsed.isEmpty()) { + sheetUsed = title; + l->setDisabled(false); + } + } + } + else { + //