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 += "";
+ text += static_cast(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 += "";
+ text += static_cast(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 += "";
+ text += static_cast(n)->tagName();
+ text += ">";
+
+ }
+ break;
+
+ case ID_LI:
+ if(!in_li) break;
+ text = static_cast(n)->openTagStartToString(true /*expand img urls*/)+">" +text;
+ text += "";
+ text += static_cast(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 += "";
+ text += static_cast(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 {
+ //