summaryrefslogtreecommitdiffstats
path: root/khtml/xml
diff options
context:
space:
mode:
Diffstat (limited to 'khtml/xml')
-rw-r--r--khtml/xml/Makefile.am48
-rw-r--r--khtml/xml/dom2_eventsimpl.cpp969
-rw-r--r--khtml/xml/dom2_eventsimpl.h513
-rw-r--r--khtml/xml/dom2_rangeimpl.cpp1640
-rw-r--r--khtml/xml/dom2_rangeimpl.h127
-rw-r--r--khtml/xml/dom2_traversalimpl.cpp667
-rw-r--r--khtml/xml/dom2_traversalimpl.h196
-rw-r--r--khtml/xml/dom2_viewsimpl.cpp50
-rw-r--r--khtml/xml/dom2_viewsimpl.h50
-rw-r--r--khtml/xml/dom_docimpl.cpp2892
-rw-r--r--khtml/xml/dom_docimpl.h763
-rw-r--r--khtml/xml/dom_elementimpl.cpp1301
-rw-r--r--khtml/xml/dom_elementimpl.h392
-rw-r--r--khtml/xml/dom_nodeimpl.cpp2068
-rw-r--r--khtml/xml/dom_nodeimpl.h736
-rw-r--r--khtml/xml/dom_restyler.cpp122
-rw-r--r--khtml/xml/dom_restyler.h102
-rw-r--r--khtml/xml/dom_stringimpl.cpp460
-rw-r--r--khtml/xml/dom_stringimpl.h104
-rw-r--r--khtml/xml/dom_textimpl.cpp522
-rw-r--r--khtml/xml/dom_textimpl.h176
-rw-r--r--khtml/xml/dom_xmlimpl.cpp498
-rw-r--r--khtml/xml/dom_xmlimpl.h181
-rw-r--r--khtml/xml/xml_tokenizer.cpp609
-rw-r--r--khtml/xml/xml_tokenizer.h198
25 files changed, 15384 insertions, 0 deletions
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 <kdebug.h>
+
+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<typename L, typename R, typename MemL>
+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<R,L> i( m_rToL.find(r) );
+ if (i != m_rToL.end())
+ return *i;
+ return L();
+ }
+
+ R toRight(L l) {
+ QMapIterator<L,R> i = m_lToR.find(l);
+ if (i != m_lToR.end())
+ return *i;
+ return R();
+ }
+
+private:
+ QMap<L, R> m_lToR;
+ QMap<R, L> m_rToL;
+};
+
+#define MAKE_TRANSLATOR(name,L,R,MR,table) static IDTranslator<L,R,MR>* s_##name; \
+ static IDTranslator<L,R,MR>* name() { if (!s_##name) s_##name = new IDTranslator<L,R,MR>(table); \
+ return s_##name; }
+
+//---------------------------------------------------------------------------------------------
+
+/* Mapping between special Qt keycodes and virtual DOM codes */
+IDTranslator<unsigned, unsigned, unsigned>::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<QCString, unsigned, const char*>::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<QCString, unsigned, const char*>::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<EventImpl>
+{
+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<CharacterDataImpl*>(m_startContainer->cloneNode(true));
+ c->deleteData(m_endOffset,static_cast<CharacterDataImpl*>(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<CharacterDataImpl*>(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<CharacterDataImpl*>(m_startContainer->cloneNode(true));
+ c->deleteData(0,m_startOffset,exceptioncode);
+ leftContents = c;
+ }
+ if (action == EXTRACT_CONTENTS || action == DELETE_CONTENTS)
+ static_cast<CharacterDataImpl*>(m_startContainer)->deleteData(
+ m_startOffset,static_cast<CharacterDataImpl*>(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<CharacterDataImpl*>(m_endContainer->cloneNode(true));
+ c->deleteData(m_endOffset,static_cast<CharacterDataImpl*>(m_endContainer)->length()-m_endOffset,exceptioncode);
+ rightContents = c;
+ }
+ if (action == EXTRACT_CONTENTS || action == DELETE_CONTENTS)
+ static_cast<CharacterDataImpl*>(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<TextImpl*>(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<TextImpl *>(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 <tagname key=value>otherstuff</tagname> */
+ if(n->nodeType() == DOM::Node::ELEMENT_NODE) {
+ int elementId = static_cast<ElementImpl *>(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 <h1/> etc. Just skip these nodes just to make the output html a bit nicer.
+ text += static_cast<ElementImpl *>(n)->openTagStartToString(true /*safely expand img urls*/); // adds "<tagname key=value"
+ if(n->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 += "<![CDATA[ ";
+ long long startOffset = (n == m_startContainer)?(long long)m_startOffset:-1;
+ long long endOffset = (n == m_endContainer)?(long long) m_endOffset:-1;
+ text += static_cast<TextImpl *>(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 <img src='hello.png'> how are you? <img src='goodbye.png'>"
+ //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<ElementImpl *>(n)->tagName();
+ int elementId = static_cast<ElementImpl *>(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 <b>Mr. John</b> How are you?" and we select "John" or even
+ //"John</b> How" and copy. We want to return "<b>John</b>" and "<b>John</b> 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</b></p><p>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. (<p><b>)
+ //
+ // 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<ElementImpl *>(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<ElementImpl *>(n)->openTagStartToString(true /*expand img urls*/)+">" +text; // prepends "<tagname key=value>"
+ }
+ 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<ElementImpl *>(n)->id()) {
+ case ID_TABLE:
+ num_tables++;
+ break;
+ case ID_OL:
+ case ID_UL:
+ in_li=false;
+ break;
+ }
+ text += "</";
+ text += static_cast<ElementImpl *>(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: "<b>hello</b>" and we select "ll", then we want to go down the tree and
+ // add "<b>" and "</b>" to it, to produce "<b>ll</b>".
+ //
+ // 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<ElementImpl *>(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<ElementImpl *>(n)->openTagStartToString(true /*expand img urls*/)+">" +text;
+ text += "</";
+ text += static_cast<ElementImpl *>(n)->tagName();
+ text += ">";
+
+ }
+ break;
+
+ case ID_LI:
+ if(!in_li) break;
+ text = static_cast<ElementImpl *>(n)->openTagStartToString(true /*expand img urls*/)+">" +text;
+ text += "</";
+ text += static_cast<ElementImpl *>(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<ElementImpl *>(n)->openTagStartToString(true /*expand img urls*/)+">" +text;
+ text += "</";
+ text += static_cast<ElementImpl *>(n)->tagName();
+ text += ">";
+ break;
+ }
+ }
+ }
+ }
+
+
+ if(!hasBodyTag) text = DOMString("<body>") + text + "</body>";
+ else if(!hasHtmlTag) {
+ text = DOMString("<html xmlns=\"http://www.w3.org/1999/xhtml\">\n"
+ "<head>\n"
+ "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />\n"
+ "<meta name=\"Generator\" content=\"KHTML, the KDE Web Page Viewer\" />\n"
+ "</head>\n") +
+ text +
+ "</html>";
+ }
+ text = DOMString("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"DTD/xhtml1-strict.dtd\">\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<HTMLElementImpl *>(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<CharacterDataImpl*>(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<ProcessingInstructionImpl*>(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<RangeImpl>
+{
+ 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 <em>with passwords stripped</em>
+ * 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<NodeIteratorImpl>
+{
+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<NodeFilterImpl>
+{
+public:
+ NodeFilterImpl();
+ ~NodeFilterImpl();
+
+ short acceptNode(const Node &n);
+
+ void setCustomNodeFilter(CustomNodeFilter *custom);
+ CustomNodeFilter *customNodeFilter();
+protected:
+ CustomNodeFilter *m_customNodeFilter;
+
+};
+
+class TreeWalkerImpl : public khtml::Shared<TreeWalkerImpl>
+{
+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<AbstractViewImpl>
+{
+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 <qptrstack.h>
+#include <qpaintdevicemetrics.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kstaticdeleter.h>
+
+#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 <kglobalsettings.h>
+#include <kstringhandler.h>
+#include <krfcdate.h>
+#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 <kapplication.h>
+#include <kio/job.h>
+
+#include <stdlib.h>
+#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<DocumentTypeImpl*>(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<DocumentImpl> > s_changedDocumentsDeleter;
+QPtrList<DocumentImpl> * 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 <!DOCTYPE>
+ // 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<DocumentImpl> 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<NodeListImpl::Cache> 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<ElementImpl*>(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<ElementImpl*>(importedNode);
+ NamedAttrMapImpl *otherMap = static_cast<ElementImpl *>(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<TextImpl*>(importedNode)->string());
+ deep = false;
+ }
+ else if(importedNode->nodeType() == Node::CDATA_SECTION_NODE)
+ {
+ result = createCDATASection(static_cast<CDATASectionImpl*>(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<CommentImpl*>(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<NodeImpl> 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<ElementImpl *>(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<NodeImpl> it(m_maintainsState); it.current(); ++it)
+ s.append(it.current()->state());
+
+ return s;
+}
+
+bool DocumentImpl::unsubmittedFormChanges()
+{
+ for (QPtrListIterator<NodeImpl> 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<DocumentImpl>() );
+
+ 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: "<<endl;
+
+ StyleChange change = NoChange;
+#if 0
+ if ( m_styleSelectorDirty ) {
+ recalcStyleSelector();
+ change = Force;
+ }
+#endif
+ recalcStyle( change );
+
+// kdDebug() << "UPDATERENDERING time used="<<time.elapsed()<<endl;
+}
+
+void DocumentImpl::updateDocumentsRendering()
+{
+ if (!changedDocuments)
+ return;
+
+ while ( !changedDocuments->isEmpty() ) {
+ changedDocuments->first();
+ DocumentImpl* it = changedDocuments->take();
+ if (it->isDocumentChanged())
+ it->updateRendering();
+ }
+}
+
+void DocumentImpl::updateLayout()
+{
+ bool oldIgnore = m_ignorePendingStylesheets;
+
+ if (!haveStylesheetsLoaded()) {
+ m_ignorePendingStylesheets = true;
+ updateStyleSelector();
+ }
+
+ updateRendering();
+
+ // Only do a layout if changes have occurred that make it necessary.
+ if (m_view && renderer() && renderer()->needsLayout())
+ m_view->layout();
+
+ m_ignorePendingStylesheets = oldIgnore;
+}
+
+void DocumentImpl::attach()
+{
+ assert(!attached());
+
+ if ( m_view )
+ setPaintDevice( m_view );
+
+ if (!m_renderArena)
+ m_renderArena.reset(new RenderArena());
+
+ // Create the rendering tree
+ assert(!m_styleSelector);
+ m_styleSelector = new CSSStyleSelector( this, m_usersheet, m_styleSheets, m_url,
+ !inCompatMode() );
+ m_render = new (m_renderArena.get()) RenderCanvas(this, m_view);
+ m_styleSelector->computeFontSizes(paintDeviceMetrics(), m_view ? m_view->part()->zoomFactor() : 100);
+ recalcStyle( Force );
+
+ RenderObject* render = m_render;
+ m_render = 0;
+
+ NodeBaseImpl::attach();
+ m_render = render;
+}
+
+void DocumentImpl::detach()
+{
+ RenderObject* render = m_render;
+
+ // indicate destruction mode, i.e. attached() but m_render == 0
+ m_render = 0;
+
+ delete m_tokenizer;
+ m_tokenizer = 0;
+
+ // Empty out these lists as a performance optimization
+ m_imageLoadEventDispatchSoonList.clear();
+ m_imageLoadEventDispatchingList.clear();
+ NodeBaseImpl::detach();
+
+ if ( render )
+ render->detach();
+
+ m_view = 0;
+
+ m_renderArena.reset();
+}
+
+void DocumentImpl::setVisuallyOrdered()
+{
+ visuallyOrdered = true;
+ if (m_render)
+ m_render->style()->setVisuallyOrdered(true);
+}
+
+void DocumentImpl::setSelection(NodeImpl* s, int sp, NodeImpl* e, int ep)
+{
+ if ( m_render )
+ static_cast<RenderCanvas*>(m_render)->setSelection(s->renderer(),sp,e->renderer(),ep);
+}
+
+void DocumentImpl::clearSelection()
+{
+ if ( m_render )
+ static_cast<RenderCanvas*>(m_render)->clearSelection();
+}
+
+khtml::Tokenizer *DocumentImpl::createTokenizer()
+{
+ return new khtml::XMLTokenizer(docPtr(),m_view);
+}
+
+void DocumentImpl::setPaintDevice( QPaintDevice *dev )
+{
+ if (m_paintDevice != dev) {
+ m_paintDevice = dev;
+ delete m_paintDeviceMetrics;
+ m_paintDeviceMetrics = new QPaintDeviceMetrics( dev );
+ }
+}
+
+void DocumentImpl::open( bool clearEventListeners )
+{
+ if (parsing()) return;
+
+ if (m_tokenizer)
+ close();
+
+ delete m_tokenizer;
+ m_tokenizer = 0;
+
+ KHTMLView* view = m_view;
+ bool was_attached = attached();
+ if ( was_attached )
+ detach();
+
+ removeChildren();
+ delete m_styleSelector;
+ m_styleSelector = 0;
+ m_view = view;
+ if ( was_attached )
+ attach();
+
+ if (clearEventListeners)
+ m_windowEventListeners.clear();
+
+ m_tokenizer = createTokenizer();
+ m_decoderMibEnum = 0;
+ connect(m_tokenizer,SIGNAL(finishedParsing()),this,SIGNAL(finishedParsing()));
+ m_tokenizer->begin();
+}
+
+HTMLElementImpl* DocumentImpl::body()
+{
+ NodeImpl *de = documentElement();
+ if (!de)
+ return 0;
+
+ // try to prefer a FRAMESET element over BODY
+ NodeImpl* body = 0;
+ for (NodeImpl* i = de->firstChild(); i; i = i->nextSibling()) {
+ if (i->id() == ID_FRAMESET)
+ return static_cast<HTMLElementImpl*>(i);
+
+ if (i->id() == ID_BODY)
+ body = i;
+ }
+ return static_cast<HTMLElementImpl *>(body);
+}
+
+void DocumentImpl::close( )
+{
+ if (parsing() || !m_tokenizer) return;
+
+ if ( m_render )
+ m_render->close();
+
+ // on an explicit document.close(), the tokenizer might still be waiting on scripts,
+ // and in that case we don't want to destroy it because that will prevent the
+ // scripts from getting processed.
+ if (m_tokenizer && !m_tokenizer->isWaitingForScripts() && !m_tokenizer->isExecutingScript()) {
+ delete m_tokenizer;
+ m_tokenizer = 0;
+ }
+
+ if (m_view)
+ m_view->part()->checkEmitLoadEvent();
+}
+
+void DocumentImpl::write( const DOMString &text )
+{
+ write(text.string());
+}
+
+void DocumentImpl::write( const QString &text )
+{
+ if (!m_tokenizer) {
+ open();
+ if (m_view)
+ m_view->part()->resetFromScript();
+ m_tokenizer->setAutoClose();
+ write(QString::fromLatin1("<html>"));
+ }
+ 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 <html:meta .....>
+ HTMLDocumentImpl *d = static_cast<HTMLDocumentImpl *>(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<ElementImpl*>(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<StyleSheetImpl> 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<ProcessingInstructionImpl*>(n);
+ sheet = pi->sheet();
+ if (!sheet && !pi->localHref().isEmpty())
+ {
+ // Processing instruction with reference to an element in this document - e.g.
+ // <?xml-stylesheet href="#mystyle">, with the element
+ // <foo id="mystyle">heading { color: red; }</foo> 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<HTMLLinkElementImpl*>(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 {
+ // <STYLE> element
+ HTMLStyleElementImpl* s = static_cast<HTMLStyleElementImpl*>(n);
+ if (!s->isLoading()) {
+ sheet = s->sheet();
+ if (sheet) title = s->getAttribute(ATTR_TITLE).string();
+ }
+ if (!title.isEmpty() && sheetUsed.isEmpty())
+ sheetUsed = title;
+ }
+
+ if ( !title.isEmpty() ) {
+ if ( title != sheetUsed )
+ sheet = 0; // don't use it
+
+ title = title.replace('&', "&&");
+
+ if ( !m_availableSheets.contains( title ) )
+ m_availableSheets.append( title );
+ }
+ }
+ else if (n->isHTMLElement() && n->id() == ID_BODY) {
+ // <BODY> element (doesn't contain styles as such but vlink="..." and friends
+ // are treated as style declarations)
+ sheet = static_cast<HTMLBodyElementImpl*>(n)->sheet();
+ }
+
+ if (sheet) {
+ sheet->ref();
+ m_styleSheets->styleSheets.append(sheet);
+ }
+
+ // For HTML documents, stylesheets are not allowed within/after the <BODY> tag. So we
+ // can stop searching here.
+ if (isHTMLDocument() && n->id() == ID_BODY) {
+ canResetSheet = !canResetSheet;
+ break;
+ }
+ }
+
+ // we're done if we don't select an alternative sheet
+ // or we found the sheet we selected
+ if (sheetUsed.isEmpty() ||
+ (!canResetSheet && tokenizer()) ||
+ m_availableSheets.contains(sheetUsed)) {
+ break;
+ }
+
+ // the alternative sheet we used doesn't exist anymore
+ // so try from scratch again
+ if (view())
+ view()->part()->d->m_sheetUsed = QString::null;
+ if (!m_preferredStylesheetSet.isEmpty() && !(sheetUsed == m_preferredStylesheetSet))
+ sheetUsed = m_preferredStylesheetSet.string();
+ else
+ sheetUsed = QString::null;
+ autoselect = true;
+ }
+
+ // Include programmatically added style sheets
+ if (m_addedStyleSheets) {
+ QPtrListIterator<StyleSheetImpl> it = m_addedStyleSheets->styleSheets;
+ for (; *it; ++it) {
+ if ((*it)->isCSSStyleSheet() && !(*it)->disabled())
+ m_styleSheets->add(*it);
+ }
+ }
+
+ // De-reference all the stylesheets in the old list
+ QPtrListIterator<StyleSheetImpl> it(oldStyleSheets);
+ for (; it.current(); ++it)
+ it.current()->deref();
+
+ rebuildStyleSelector();
+}
+
+void DocumentImpl::rebuildStyleSelector()
+{
+ // Create a new style selector
+ delete m_styleSelector;
+ QString usersheet = m_usersheet;
+ if ( m_view && m_view->mediaType() == "print" )
+ usersheet += m_printSheet;
+ m_styleSelector = new CSSStyleSelector( this, usersheet, m_styleSheets, m_url,
+ !inCompatMode() );
+
+ m_styleSelectorDirty = false;
+}
+
+void DocumentImpl::setHoverNode(NodeImpl *newHoverNode)
+{
+ NodeImpl* oldHoverNode = m_hoverNode;
+ if (newHoverNode ) newHoverNode->ref();
+ m_hoverNode = newHoverNode;
+ if ( oldHoverNode ) oldHoverNode->deref();
+}
+
+void DocumentImpl::setActiveNode(NodeImpl* newActiveNode)
+{
+ NodeImpl* oldActiveNode = m_activeNode;
+ if (newActiveNode ) newActiveNode->ref();
+ m_activeNode = newActiveNode;
+ if ( oldActiveNode ) oldActiveNode->deref();
+}
+
+void DocumentImpl::setFocusNode(NodeImpl *newFocusNode)
+{
+ // don't process focus changes while detaching
+ if( !m_render ) return;
+
+ // We do want to blur if a widget is being detached,
+ // but we don't want to emit events since that
+ // triggers updateLayout() and may recurse detach()
+ bool widgetDetach = m_focusNode && m_focusNode != this &&
+ m_focusNode->renderer() && !m_focusNode->renderer()->parent();
+
+ // Make sure newFocusNode is actually in this document
+ if (newFocusNode && (newFocusNode->getDocument() != this))
+ return;
+
+ if (m_focusNode != newFocusNode) {
+ NodeImpl *oldFocusNode = m_focusNode;
+ // Set focus on the new node
+ m_focusNode = newFocusNode;
+ // Remove focus from the existing focus node (if any)
+ if (oldFocusNode) {
+ if (oldFocusNode->active())
+ oldFocusNode->setActive(false);
+
+ oldFocusNode->setFocus(false);
+
+ if (!widgetDetach) {
+ oldFocusNode->dispatchHTMLEvent(EventImpl::BLUR_EVENT,false,false);
+ oldFocusNode->dispatchUIEvent(EventImpl::DOMFOCUSOUT_EVENT);
+ }
+ if ((oldFocusNode == this) && oldFocusNode->hasOneRef()) {
+ oldFocusNode->deref(); // deletes this
+ return;
+ }
+ else {
+ oldFocusNode->deref();
+ }
+ }
+
+ if (m_focusNode) {
+ m_focusNode->ref();
+ m_focusNode->dispatchHTMLEvent(EventImpl::FOCUS_EVENT,false,false);
+ if (m_focusNode != newFocusNode) return;
+ m_focusNode->dispatchUIEvent(EventImpl::DOMFOCUSIN_EVENT);
+ if (m_focusNode != newFocusNode) return;
+ m_focusNode->setFocus();
+ if (m_focusNode != newFocusNode) return;
+
+ // eww, I suck. set the qt focus correctly
+ // ### find a better place in the code for this
+ if (view()) {
+ if (!m_focusNode->renderer() || !m_focusNode->renderer()->isWidget())
+ view()->setFocus();
+ else if (static_cast<RenderWidget*>(m_focusNode->renderer())->widget())
+ {
+ if (view()->isVisible())
+ static_cast<RenderWidget*>(m_focusNode->renderer())->widget()->setFocus();
+ }
+ }
+ } else {
+ //We're blurring. Better clear the Qt focus/give it to the view...
+ if (view())
+ view()->setFocus();
+ }
+
+ if (!widgetDetach)
+ updateRendering();
+ }
+}
+
+void DocumentImpl::setCSSTarget(NodeImpl* n)
+{
+ if (n == m_cssTarget)
+ return;
+
+ if (m_cssTarget) {
+ m_cssTarget->setChanged();
+ m_cssTarget->deref();
+ }
+ m_cssTarget = n;
+ if (n) {
+ n->setChanged();
+ n->ref();
+ }
+}
+
+void DocumentImpl::attachNodeIterator(NodeIteratorImpl *ni)
+{
+ m_nodeIterators.append(ni);
+}
+
+void DocumentImpl::detachNodeIterator(NodeIteratorImpl *ni)
+{
+ m_nodeIterators.remove(ni);
+}
+
+void DocumentImpl::notifyBeforeNodeRemoval(NodeImpl *n)
+{
+ QPtrListIterator<NodeIteratorImpl> it(m_nodeIterators);
+ for (; it.current(); ++it)
+ it.current()->notifyBeforeNodeRemoval(n);
+}
+
+bool DocumentImpl::isURLAllowed(const QString& url) const
+{
+ KHTMLPart *thisPart = part();
+
+ KURL newURL(completeURL(url));
+ newURL.setRef(QString::null);
+
+ if (KHTMLFactory::defaultHTMLSettings()->isAdFiltered( newURL.url() ))
+ return false;
+
+ // Prohibit non-file URLs if we are asked to.
+ if (!thisPart || thisPart->onlyLocalReferences() && newURL.protocol() != "file" && newURL.protocol() != "data")
+ return false;
+
+ // do we allow this suburl ?
+ if ( !kapp || (newURL.protocol() != "javascript" && !kapp->authorizeURLAction("redirect", thisPart->url(), newURL)) )
+ return false;
+
+ // We allow one level of self-reference because some sites depend on that.
+ // But we don't allow more than one.
+ bool foundSelfReference = false;
+ for (KHTMLPart *part = thisPart; part; part = part->parentPart()) {
+ KURL partURL = part->url();
+ partURL.setRef(QString::null);
+ if (partURL == newURL) {
+ if (foundSelfReference)
+ return false;
+ foundSelfReference = true;
+ }
+ }
+
+ return true;
+}
+
+void DocumentImpl::setDesignMode(bool b)
+{
+ if (part())
+ part()->setEditable(b);
+}
+
+bool DocumentImpl::designMode() const
+{
+ return part() ? part()->isEditable() : false;
+}
+
+EventImpl *DocumentImpl::createEvent(const DOMString &eventType, int &exceptioncode)
+{
+ if (eventType == "UIEvents" || eventType == "UIEvent")
+ return new UIEventImpl();
+ else if (eventType == "MouseEvents" || eventType == "MouseEvent")
+ return new MouseEventImpl();
+ else if (eventType == "TextEvent")
+ return new TextEventImpl();
+ else if (eventType == "KeyboardEvent")
+ return new KeyboardEventImpl();
+ else if (eventType == "MutationEvents" || eventType == "MutationEvent")
+ return new MutationEventImpl();
+ else if (eventType == "HTMLEvents" || eventType == "Events" ||
+ eventType == "HTMLEvent" || eventType == "Event")
+ return new EventImpl();
+ else {
+ exceptioncode = DOMException::NOT_SUPPORTED_ERR;
+ return 0;
+ }
+}
+
+CSSStyleDeclarationImpl *DocumentImpl::getOverrideStyle(ElementImpl* /*elt*/, DOMStringImpl* /*pseudoElt*/)
+{
+ return 0; // ###
+}
+
+void DocumentImpl::abort()
+{
+ if (m_inSyncLoad) {
+ m_inSyncLoad = false;
+ kapp->exit_loop();
+ }
+
+ if (m_loadingXMLDoc)
+ m_loadingXMLDoc->deref(this);
+ m_loadingXMLDoc = 0;
+}
+
+void DocumentImpl::load(const DOMString &uri)
+{
+ if (m_inSyncLoad) {
+ m_inSyncLoad = false;
+ kapp->exit_loop();
+ }
+
+ m_hadLoadError = false;
+ if (m_loadingXMLDoc)
+ m_loadingXMLDoc->deref(this);
+
+ // Use the document loader to retrieve the XML file. We use CachedCSSStyleSheet because
+ // this is an easy way to retrieve an arbitrary text file... it is not specific to
+ // stylesheets.
+
+ // ### Note: By loading the XML document this way we do not get the proper decoding
+ // of the data retrieved from the server based on the character set, as happens with
+ // HTML files. Need to look into a way of using the decoder in CachedCSSStyleSheet.
+ m_docLoading = true;
+ m_loadingXMLDoc = m_docLoader->requestStyleSheet(uri.string(),QString(),"text/xml");
+
+ if (!m_loadingXMLDoc) {
+ m_docLoading = false;
+ return;
+ }
+
+ m_loadingXMLDoc->ref(this);
+
+ if (!m_async && m_docLoading) {
+ m_inSyncLoad = true;
+ kapp->enter_loop();
+ }
+}
+
+void DocumentImpl::loadXML(const DOMString &source)
+{
+ open(false);
+ write(source);
+ finishParsing();
+ close();
+ dispatchHTMLEvent(EventImpl::LOAD_EVENT,false,false);
+}
+
+void DocumentImpl::setStyleSheet(const DOM::DOMString &url, const DOM::DOMString &sheet, const DOM::DOMString &charset)
+{
+ if (!m_hadLoadError) {
+ m_url = url.string();
+ loadXML(sheet);
+ }
+
+ m_docLoading = false;
+ if (m_inSyncLoad) {
+ m_inSyncLoad = false;
+ kapp->exit_loop();
+ }
+
+ assert(m_loadingXMLDoc != 0);
+ m_loadingXMLDoc->deref(this);
+ m_loadingXMLDoc = 0;
+}
+
+void DocumentImpl::error(int err, const QString &text)
+{
+ m_docLoading = false;
+ if (m_inSyncLoad) {
+ m_inSyncLoad = false;
+ kapp->exit_loop();
+ }
+
+ m_hadLoadError = true;
+
+ int exceptioncode = 0;
+ EventImpl *evt = new EventImpl(EventImpl::ERROR_EVENT,false,false);
+ if (err != 0)
+ evt->setMessage(KIO::buildErrorString(err,text));
+ else
+ evt->setMessage(text);
+ evt->ref();
+ dispatchEvent(evt,exceptioncode,true);
+ evt->deref();
+
+ assert(m_loadingXMLDoc != 0);
+ m_loadingXMLDoc->deref(this);
+ m_loadingXMLDoc = 0;
+}
+
+void DocumentImpl::defaultEventHandler(EventImpl *evt)
+{
+ // if any html event listeners are registered on the window, then dispatch them here
+ if (!m_windowEventListeners.listeners || evt->propagationStopped())
+ return;
+
+ QValueList<RegisteredEventListener>::iterator it;
+
+ //Grab a copy in case of clear
+ QValueList<RegisteredEventListener> listeners = *m_windowEventListeners.listeners;
+ Event ev(evt);
+ for (it = listeners.begin(); it != listeners.end(); ++it) {
+ //Check to make sure it didn't get removed. KDE4: use Java-style iterators
+ if (!m_windowEventListeners.stillContainsListener(*it))
+ continue;
+
+ if ((*it).id == evt->id()) {
+ // currentTarget must be 0 in khtml for kjs_events to set "this" correctly.
+ // (this is how we identify events dispatched to the window, like window.onmousedown)
+ // ## currentTarget is unimplemented in IE, and is "window" in Mozilla (how? not a DOM node)
+ evt->setCurrentTarget(0);
+ (*it).listener->handleEvent(ev);
+ }
+ }
+}
+
+void DocumentImpl::setHTMLWindowEventListener(int id, EventListener *listener)
+{
+ m_windowEventListeners.setHTMLEventListener(id, listener);
+}
+
+EventListener *DocumentImpl::getHTMLWindowEventListener(int id)
+{
+ return m_windowEventListeners.getHTMLEventListener(id);
+}
+
+void DocumentImpl::addWindowEventListener(int id, EventListener *listener, const bool useCapture)
+{
+ m_windowEventListeners.addEventListener(id, listener, useCapture);
+}
+
+void DocumentImpl::removeWindowEventListener(int id, EventListener *listener, bool useCapture)
+{
+ m_windowEventListeners.removeEventListener(id, listener, useCapture);
+}
+
+bool DocumentImpl::hasWindowEventListener(int id)
+{
+ return m_windowEventListeners.hasEventListener(id);
+}
+
+EventListener *DocumentImpl::createHTMLEventListener(const QString& code, const QString& name, NodeImpl* node)
+{
+ return part() ? part()->createHTMLEventListener(code, name, node) : 0;
+}
+
+void DocumentImpl::dispatchImageLoadEventSoon(HTMLImageElementImpl *image)
+{
+ m_imageLoadEventDispatchSoonList.append(image);
+ if (!m_imageLoadEventTimer) {
+ m_imageLoadEventTimer = startTimer(0);
+ }
+}
+
+void DocumentImpl::removeImage(HTMLImageElementImpl *image)
+{
+ // Remove instances of this image from both lists.
+ // Use loops because we allow multiple instances to get into the lists.
+ while (m_imageLoadEventDispatchSoonList.removeRef(image)) { }
+ while (m_imageLoadEventDispatchingList.removeRef(image)) { }
+ if (m_imageLoadEventDispatchSoonList.isEmpty() && m_imageLoadEventTimer) {
+ killTimer(m_imageLoadEventTimer);
+ m_imageLoadEventTimer = 0;
+ }
+}
+
+void DocumentImpl::dispatchImageLoadEventsNow()
+{
+ // need to avoid re-entering this function; if new dispatches are
+ // scheduled before the parent finishes processing the list, they
+ // will set a timer and eventually be processed
+ if (!m_imageLoadEventDispatchingList.isEmpty()) {
+ return;
+ }
+
+ if (m_imageLoadEventTimer) {
+ killTimer(m_imageLoadEventTimer);
+ m_imageLoadEventTimer = 0;
+ }
+
+ m_imageLoadEventDispatchingList = m_imageLoadEventDispatchSoonList;
+ m_imageLoadEventDispatchSoonList.clear();
+ for (QPtrListIterator<HTMLImageElementImpl> it(m_imageLoadEventDispatchingList); it.current(); ) {
+ HTMLImageElementImpl* image = it.current();
+ // Must advance iterator *before* dispatching call.
+ // Otherwise, it might be advanced automatically if dispatching the call had a side effect
+ // of destroying the current HTMLImageElementImpl, and then we would advance past the *next*
+ // item, missing one altogether.
+ ++it;
+ image->dispatchLoadEvent();
+ }
+ m_imageLoadEventDispatchingList.clear();
+}
+
+void DocumentImpl::timerEvent(QTimerEvent *)
+{
+ dispatchImageLoadEventsNow();
+}
+
+void DocumentImpl::setDecoderCodec(const QTextCodec *codec)
+{
+ m_decoderMibEnum = codec->mibEnum();
+}
+
+ElementImpl *DocumentImpl::ownerElement() const
+{
+ KHTMLPart *childPart = part();
+ if (!childPart)
+ return 0;
+ ChildFrame *childFrame = childPart->d->m_frame;
+ if (!childFrame)
+ return 0;
+ RenderPart *renderPart = childFrame->m_frame;
+ if (!renderPart)
+ return 0;
+ return static_cast<ElementImpl *>(renderPart->element());
+}
+
+DOMString DocumentImpl::domain() const
+{
+ if ( m_domain.isEmpty() ) // not set yet (we set it on demand to save time and space)
+ m_domain = URL().host(); // Initially set to the host
+ return m_domain;
+}
+
+void DocumentImpl::setDomain(const DOMString &newDomain)
+{
+ if ( m_domain.isEmpty() ) // not set yet (we set it on demand to save time and space)
+ m_domain = URL().host().lower(); // Initially set to the host
+
+ if ( m_domain.isEmpty() /*&& part() && part()->openedByJS()*/ )
+ m_domain = newDomain.lower();
+
+ // Both NS and IE specify that changing the domain is only allowed when
+ // the new domain is a suffix of the old domain.
+ int oldLength = m_domain.length();
+ int newLength = newDomain.length();
+ if ( newLength < oldLength ) // e.g. newDomain=kde.org (7) and m_domain=www.kde.org (11)
+ {
+ DOMString test = m_domain.copy();
+ DOMString reference = newDomain.lower();
+ if ( test[oldLength - newLength - 1] == '.' ) // Check that it's a subdomain, not e.g. "de.org"
+ {
+ test.remove( 0, oldLength - newLength ); // now test is "kde.org" from m_domain
+ if ( test == reference ) // and we check that it's the same thing as newDomain
+ m_domain = reference;
+ }
+ }
+}
+
+DOMString DocumentImpl::toString() const
+{
+ DOMString result;
+
+ for (NodeImpl *child = firstChild(); child != NULL; child = child->nextSibling()) {
+ result += child->toString();
+ }
+
+ return result;
+}
+
+
+KHTMLPart* DOM::DocumentImpl::part() const
+{
+ // ### TODO: make this independent from a KHTMLView one day.
+ return view() ? view()->part() : 0;
+}
+
+NodeListImpl::Cache* DOM::DocumentImpl::acquireCachedNodeListInfo(
+ NodeListImpl::CacheFactory* factory, NodeImpl* base, int type)
+{
+ //### might want to flush the dict when the version number
+ //changes
+ NodeListImpl::CacheKey key(base, type);
+
+ //Check to see if we have this sort of item cached.
+ NodeListImpl::Cache* cached =
+ (type == NodeListImpl::UNCACHEABLE) ? 0 : m_nodeListCache.find(key.hash());
+
+ if (cached) {
+ if (cached->key == key) {
+ cached->ref(); //Add the nodelist's reference
+ return cached;
+ } else {
+ //Conflict. Drop our reference to the old item.
+ cached->deref();
+ }
+ }
+
+ //Nothing to reuse, make a new item.
+ NodeListImpl::Cache* newInfo = factory();
+ newInfo->key = key;
+ newInfo->clear(this);
+ newInfo->ref(); //Add the nodelist's reference
+
+ if (type != NodeListImpl::UNCACHEABLE) {
+ newInfo->ref(); //Add the cache's reference
+ m_nodeListCache.replace(key.hash(), newInfo);
+ }
+
+ return newInfo;
+}
+
+void DOM::DocumentImpl::releaseCachedNodeListInfo(NodeListImpl::Cache* entry)
+{
+ entry->deref();
+}
+
+// ----------------------------------------------------------------------------
+
+DocumentFragmentImpl::DocumentFragmentImpl(DocumentImpl *doc) : NodeBaseImpl(doc)
+{
+}
+
+DocumentFragmentImpl::DocumentFragmentImpl(const DocumentFragmentImpl &other)
+ : NodeBaseImpl(other)
+{
+}
+
+DOMString DocumentFragmentImpl::nodeName() const
+{
+ return "#document-fragment";
+}
+
+unsigned short DocumentFragmentImpl::nodeType() const
+{
+ return Node::DOCUMENT_FRAGMENT_NODE;
+}
+
+// DOM Section 1.1.1
+bool DocumentFragmentImpl::childTypeAllowed( unsigned short type )
+{
+ switch (type) {
+ case Node::ELEMENT_NODE:
+ case Node::PROCESSING_INSTRUCTION_NODE:
+ case Node::COMMENT_NODE:
+ case Node::TEXT_NODE:
+ case Node::CDATA_SECTION_NODE:
+ case Node::ENTITY_REFERENCE_NODE:
+ return true;
+ break;
+ default:
+ return false;
+ }
+}
+
+DOMString DocumentFragmentImpl::toString() const
+{
+ DOMString result;
+
+ for (NodeImpl *child = firstChild(); child != NULL; child = child->nextSibling()) {
+ result += child->toString();
+ }
+
+ return result;
+}
+
+NodeImpl *DocumentFragmentImpl::cloneNode ( bool deep )
+{
+ DocumentFragmentImpl *clone = new DocumentFragmentImpl( docPtr() );
+ if (deep)
+ cloneChildNodes(clone);
+ return clone;
+}
+
+
+// ----------------------------------------------------------------------------
+
+DocumentTypeImpl::DocumentTypeImpl(DOMImplementationImpl *implementation, DocumentImpl *doc,
+ const DOMString &qualifiedName, const DOMString &publicId,
+ const DOMString &systemId)
+ : NodeImpl(doc), m_implementation(implementation),
+ m_qualifiedName(qualifiedName), m_publicId(publicId), m_systemId(systemId)
+{
+ m_implementation->ref();
+
+ m_entities = 0;
+ m_notations = 0;
+
+ // if doc is 0, it is not attached to a document and / or
+ // therefore does not provide entities or notations. (DOM Level 3)
+}
+
+DocumentTypeImpl::~DocumentTypeImpl()
+{
+ m_implementation->deref();
+ if (m_entities)
+ m_entities->deref();
+ if (m_notations)
+ m_notations->deref();
+}
+
+void DocumentTypeImpl::copyFrom(const DocumentTypeImpl& other)
+{
+ m_qualifiedName = other.m_qualifiedName;
+ m_publicId = other.m_publicId;
+ m_systemId = other.m_systemId;
+ m_subset = other.m_subset;
+}
+
+DOMString DocumentTypeImpl::toString() const
+{
+ DOMString result = "<!DOCTYPE";
+ result += m_qualifiedName;
+ if (!m_publicId.isEmpty()) {
+ result += " PUBLIC \"";
+ result += m_publicId;
+ result += "\" \"";
+ result += m_systemId;
+ result += "\"";
+ } else if (!m_systemId.isEmpty()) {
+ result += " SYSTEM \"";
+ result += m_systemId;
+ result += "\"";
+ }
+
+ if (!m_subset.isEmpty()) {
+ result += " [";
+ result += m_subset;
+ result += "]";
+ }
+
+ result += ">";
+
+ return result;
+}
+
+DOMString DocumentTypeImpl::nodeName() const
+{
+ return name();
+}
+
+unsigned short DocumentTypeImpl::nodeType() const
+{
+ return Node::DOCUMENT_TYPE_NODE;
+}
+
+// DOM Section 1.1.1
+bool DocumentTypeImpl::childTypeAllowed( unsigned short /*type*/ )
+{
+ return false;
+}
+
+NodeImpl *DocumentTypeImpl::cloneNode ( bool /*deep*/ )
+{
+ // Spec says cloning Document nodes is "implementation dependent"
+ // so we do not support it...
+ return 0;
+}
+
+DOMStringImpl* DocumentTypeImpl::textContent() const
+{
+ return 0;
+}
+
+void DocumentTypeImpl::setTextContent( const DOMString&, int& )
+{}
+
+NamedNodeMapImpl * DocumentTypeImpl::entities() const
+{
+ if ( !m_entities ) {
+ m_entities = new GenericRONamedNodeMapImpl( docPtr() );
+ m_entities->ref();
+ }
+ return m_entities;
+}
+
+NamedNodeMapImpl * DocumentTypeImpl::notations() const
+{
+ if ( !m_notations ) {
+ m_notations = new GenericRONamedNodeMapImpl( docPtr() );
+ m_notations->ref();
+ }
+ return m_notations;
+}
+
+#include "dom_docimpl.moc"
diff --git a/khtml/xml/dom_docimpl.h b/khtml/xml/dom_docimpl.h
new file mode 100644
index 000000000..51d762325
--- /dev/null
+++ b/khtml/xml/dom_docimpl.h
@@ -0,0 +1,763 @@
+/*
+ * 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-2003 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.
+ *
+ */
+
+#ifndef _DOM_DocumentImpl_h_
+#define _DOM_DocumentImpl_h_
+
+#include "xml/dom_elementimpl.h"
+#include "xml/dom_textimpl.h"
+#include "xml/dom2_traversalimpl.h"
+#include "misc/shared.h"
+#include "misc/loader.h"
+#include "misc/seed.h"
+
+#include <qstringlist.h>
+#include <qptrlist.h>
+#include <qobject.h>
+#include <qintcache.h>
+#include <qintdict.h>
+#include <qdict.h>
+#include <qmap.h>
+
+#include <kurl.h>
+
+class QPaintDevice;
+class QTextCodec;
+class QPaintDeviceMetrics;
+class KHTMLView;
+
+namespace khtml {
+ class Tokenizer;
+ class CSSStyleSelector;
+ class DocLoader;
+ class CSSStyleSelectorList;
+ class RenderArena;
+ class RenderObject;
+ class CounterNode;
+ class CachedObject;
+ class CachedCSSStyleSheet;
+ class DynamicDomRestyler;
+}
+
+namespace DOM {
+
+ class AbstractViewImpl;
+ class AttrImpl;
+ class CDATASectionImpl;
+ class CSSStyleSheetImpl;
+ class CommentImpl;
+ class DocumentFragmentImpl;
+ class DocumentImpl;
+ class DocumentType;
+ class DocumentTypeImpl;
+ class ElementImpl;
+ class EntityReferenceImpl;
+ class EventImpl;
+ class EventListener;
+ class GenericRONamedNodeMapImpl;
+ class HTMLDocumentImpl;
+ class HTMLElementImpl;
+ class HTMLImageElementImpl;
+ class NodeFilter;
+ class NodeFilterImpl;
+ class NodeIteratorImpl;
+ class NodeListImpl;
+ class ProcessingInstructionImpl;
+ class RangeImpl;
+ class RegisteredEventListener;
+ class StyleSheetImpl;
+ class StyleSheetListImpl;
+ class TextImpl;
+ class TreeWalkerImpl;
+
+class DOMImplementationImpl : public khtml::Shared<DOMImplementationImpl>
+{
+public:
+ DOMImplementationImpl();
+ ~DOMImplementationImpl();
+
+ // DOM methods & attributes for DOMImplementation
+ bool hasFeature ( const DOMString &feature, const DOMString &version );
+ DocumentTypeImpl *createDocumentType( const DOMString &qualifiedName, const DOMString &publicId,
+ const DOMString &systemId, int &exceptioncode );
+ DocumentImpl *createDocument( const DOMString &namespaceURI, const DOMString &qualifiedName,
+ const DocumentType &doctype, int &exceptioncode );
+
+ DOMImplementationImpl* getInterface(const DOMString& feature) const;
+
+ // From the DOMImplementationCSS interface
+ CSSStyleSheetImpl *createCSSStyleSheet(DOMStringImpl *title, DOMStringImpl *media, int &exceptioncode);
+
+ // From the HTMLDOMImplementation interface
+ HTMLDocumentImpl* createHTMLDocument( const DOMString& title);
+
+ // Other methods (not part of DOM)
+ DocumentImpl *createDocument( KHTMLView *v = 0 );
+ HTMLDocumentImpl *createHTMLDocument( KHTMLView *v = 0 );
+
+ // Returns the static instance of this class - only one instance of this class should
+ // ever be present, and is used as a factory method for creating DocumentImpl objects
+ static DOMImplementationImpl *instance();
+
+protected:
+ static DOMImplementationImpl *m_instance;
+};
+
+/**
+ * @internal A cache of element name (or id) to pointer
+ * ### KDE4, QHash: better to store values here
+ */
+class ElementMappingCache
+{
+public:
+ /**
+ For each name, we hold a reference count, and a
+ pointer. If the item is in the table, which implies
+ reference count is > 1, the name is a valid key.
+ If the pointer is non-null, it points to the appropriate
+ mapping
+ */
+ struct ItemInfo
+ {
+ int ref;
+ ElementImpl* nd;
+ };
+
+ ElementMappingCache();
+
+ /**
+ Add a pointer as just one of candidates, not neccesserily the proper one
+ */
+ void add(const QString& id, ElementImpl* nd);
+
+ /**
+ Set the pointer as the definite mapping; it must have already been added
+ */
+ void set(const QString& id, ElementImpl* nd);
+
+ /**
+ Remove the item; it must have already been added.
+ */
+ void remove(const QString& id, ElementImpl* nd);
+
+ /**
+ Returns true if the item exists
+ */
+ bool contains(const QString& id);
+
+ /**
+ Returns the information for the given ID
+ */
+ ItemInfo* get(const QString& id);
+private:
+ QDict<ItemInfo> m_dict;
+};
+
+
+/**
+ * @internal
+ */
+class DocumentImpl : public QObject, private khtml::CachedObjectClient, public NodeBaseImpl
+{
+ Q_OBJECT
+public:
+ DocumentImpl(DOMImplementationImpl *_implementation, KHTMLView *v);
+ ~DocumentImpl();
+
+ // DOM methods & attributes for Document
+
+ DocumentTypeImpl *doctype() const;
+
+ DOMImplementationImpl *implementation() const;
+ ElementImpl *documentElement() const;
+ virtual ElementImpl *createElement ( const DOMString &tagName, int* pExceptioncode = 0 );
+ virtual AttrImpl *createAttribute( const DOMString &tagName, int* pExceptioncode = 0 );
+ DocumentFragmentImpl *createDocumentFragment ();
+ TextImpl *createTextNode ( DOMStringImpl* data ) { return new TextImpl( docPtr(), data); }
+ TextImpl *createTextNode ( const QString& data )
+ { return createTextNode(new DOMStringImpl(data.unicode(), data.length())); }
+ CommentImpl *createComment ( DOMStringImpl* data );
+ CDATASectionImpl *createCDATASection ( DOMStringImpl* data );
+ ProcessingInstructionImpl *createProcessingInstruction ( const DOMString &target, DOMStringImpl* data );
+ EntityReferenceImpl *createEntityReference ( const DOMString &name );
+ NodeImpl *importNode( NodeImpl *importedNode, bool deep, int &exceptioncode );
+ virtual ElementImpl *createElementNS ( const DOMString &_namespaceURI, const DOMString &_qualifiedName,
+ int* pExceptioncode = 0 );
+ virtual AttrImpl *createAttributeNS( const DOMString &_namespaceURI, const DOMString &_qualifiedName,
+ int* pExceptioncode = 0 );
+ ElementImpl *getElementById ( const DOMString &elementId ) const;
+
+ // Actually part of HTMLDocument, but used for giving XML documents a window title as well
+ DOMString title() const { return m_title; }
+ void setTitle(const DOMString& _title);
+
+ // DOM methods overridden from parent classes
+
+ virtual DOMString nodeName() const;
+ virtual unsigned short nodeType() const;
+
+ virtual DOMStringImpl* textContent() const;
+ virtual void setTextContent( const DOMString &text, int& exceptioncode );
+
+ // Other methods (not part of DOM)
+ virtual bool isDocumentNode() const { return true; }
+ virtual bool isHTMLDocument() const { return false; }
+
+ virtual ElementImpl *createHTMLElement ( const DOMString &tagName );
+
+ khtml::CSSStyleSelector *styleSelector() { return m_styleSelector; }
+
+ /**
+ * Updates the pending sheet count and then calls updateStyleSelector.
+ */
+ void styleSheetLoaded();
+
+ /**
+ * This method returns true if all top-level stylesheets have loaded (including
+ * any \@imports that they may be loading).
+ */
+ bool haveStylesheetsLoaded() { return m_pendingStylesheets <= 0 || m_ignorePendingStylesheets; }
+
+ /**
+ * Increments the number of pending sheets. The \<link\> elements
+ * invoke this to add themselves to the loading list.
+ */
+ void addPendingSheet() { m_pendingStylesheets++; }
+
+ /**
+ * Returns true if the document has pending stylesheets
+ * loading.
+ */
+ bool hasPendingSheets() const { return m_pendingStylesheets; }
+
+ /**
+ * Called when one or more stylesheets in the document may have been added, removed or changed.
+ *
+ * Creates a new style selector and assign it to this document. This is done by iterating through all nodes in
+ * document (or those before \<BODY\> in a HTML document), searching for stylesheets. Stylesheets can be contained in
+ * \<LINK\>, \<STYLE\> or \<BODY\> elements, as well as processing instructions (XML documents only). A list is
+ * constructed from these which is used to create the a new style selector which collates all of the stylesheets
+ * found and is used to calculate the derived styles for all rendering objects.
+ *
+ * @param shallow If the stylesheet list for the document is unchanged, with only added or removed rules
+ * in existing sheets, then set this argument to true for efficiency.
+ */
+ void updateStyleSelector(bool shallow=false);
+
+ void recalcStyleSelector();
+ void rebuildStyleSelector();
+
+ QString nextState();
+
+ // Query all registered elements for their state
+ QStringList docState();
+ bool unsubmittedFormChanges();
+ void registerMaintainsState(NodeImpl* e) { m_maintainsState.append(e); }
+ void deregisterMaintainsState(NodeImpl* e) { m_maintainsState.removeRef(e); }
+
+ // Set the state the document should restore to
+ void setRestoreState( const QStringList &s) { m_state = s; }
+
+ KHTMLView *view() const { return m_view; }
+ KHTMLPart* part() const;
+
+ RangeImpl *createRange();
+
+ NodeIteratorImpl *createNodeIterator(NodeImpl *root, unsigned long whatToShow,
+ NodeFilter &filter, bool entityReferenceExpansion, int &exceptioncode);
+
+ TreeWalkerImpl *createTreeWalker(NodeImpl *root, unsigned long whatToShow, NodeFilterImpl *filter,
+ bool entityReferenceExpansion, int &exceptioncode);
+
+ virtual void recalcStyle( StyleChange = NoChange );
+ static QPtrList<DocumentImpl> * changedDocuments;
+ virtual void updateRendering();
+ void updateLayout();
+ static void updateDocumentsRendering();
+ khtml::DocLoader *docLoader() { return m_docLoader; }
+
+ virtual void attach();
+ virtual void detach();
+
+ khtml::RenderArena* renderArena() { return m_renderArena.get(); }
+
+ // to get visually ordered hebrew and arabic pages right
+ void setVisuallyOrdered();
+ // to get URL decoding right
+ void setDecoderCodec(const QTextCodec *codec);
+
+ void setSelection(NodeImpl* s, int sp, NodeImpl* e, int ep);
+ void clearSelection();
+
+ void open ( bool clearEventListeners = true );
+ virtual void close ( );
+ void write ( const DOMString &text );
+ void write ( const QString &text );
+ void writeln ( const DOMString &text );
+ void finishParsing ( );
+
+ KURL URL() const { return m_url; }
+ void setURL(const QString& url) { m_url = url; }
+
+ KURL baseURL() const { return m_baseURL.isEmpty() ? m_url : m_baseURL; }
+ void setBaseURL(const KURL& baseURL) { m_baseURL = baseURL; }
+
+ QString baseTarget() const { return m_baseTarget; }
+ void setBaseTarget(const QString& baseTarget) { m_baseTarget = baseTarget; }
+
+ QString completeURL(const QString& url) const { return KURL(baseURL(),url,m_decoderMibEnum).url(); };
+ DOMString canonURL(const DOMString& url) const { return url.isEmpty() ? url : completeURL(url.string()); }
+
+ void setUserStyleSheet(const QString& sheet);
+ QString userStyleSheet() const { return m_usersheet; }
+ void setPrintStyleSheet(const QString& sheet) { m_printSheet = sheet; }
+ QString printStyleSheet() const { return m_printSheet; }
+
+ CSSStyleSheetImpl* elementSheet();
+ virtual khtml::Tokenizer *createTokenizer();
+ khtml::Tokenizer *tokenizer() { return m_tokenizer; }
+
+ QPaintDeviceMetrics *paintDeviceMetrics() { return m_paintDeviceMetrics; }
+ QPaintDevice *paintDevice() const { return m_paintDevice; }
+ void setPaintDevice( QPaintDevice *dev );
+
+ enum HTMLMode {
+ Html3 = 0,
+ Html4 = 1,
+ XHtml = 2
+ };
+
+ enum ParseMode {
+ Unknown,
+ Compat,
+ Transitional,
+ Strict
+ };
+ virtual void determineParseMode( const QString &str );
+ void setParseMode( ParseMode m ) { pMode = m; }
+ ParseMode parseMode() const { return pMode; }
+
+ bool inCompatMode() const { return pMode == Compat; }
+ bool inTransitionalMode() const { return pMode == Transitional; }
+ bool inStrictMode() const { return pMode == Strict; }
+
+ //void setHTMLMode( HTMLMode m ) { hMode = m; }
+ HTMLMode htmlMode() const { return hMode; }
+
+ void setParsing(bool b) { m_bParsing = b; }
+ bool parsing() const { return m_bParsing; }
+
+ void setTextColor( QColor color ) { m_textColor = color; }
+ QColor textColor() const { return m_textColor; }
+
+ void setDesignMode(bool b);
+ bool designMode() const;
+
+ // internal
+ bool prepareMouseEvent( bool readonly, int x, int y, MouseEvent *ev );
+
+ virtual bool childTypeAllowed( unsigned short nodeType );
+ virtual NodeImpl *cloneNode ( bool deep );
+
+ NodeImpl::Id getId( NodeImpl::IdType _type, DOMStringImpl* _nsURI, DOMStringImpl *_localName,
+ DOMStringImpl *_prefix, bool readonly, bool lookupHTML, int *pExceptioncode = 0);
+ NodeImpl::Id getId( NodeImpl::IdType _type, DOMStringImpl *_nodeName, bool readonly, bool lookupHTML,
+ int *pExceptioncode = 0);
+ DOMString getName( NodeImpl::IdType _type, NodeImpl::Id _id ) const;
+
+ StyleSheetListImpl* styleSheets() { return m_styleSheets; };
+
+ DOMString preferredStylesheetSet() const { return m_preferredStylesheetSet; }
+ DOMString selectedStylesheetSet() const;
+ void setSelectedStylesheetSet(const DOMString&);
+ void setPreferredStylesheetSet(const DOMString& s) { m_preferredStylesheetSet = s; }
+
+ void addStyleSheet(StyleSheetImpl *, int *exceptioncode = 0);
+ void removeStyleSheet(StyleSheetImpl *, int *exceptioncode = 0);
+
+ QStringList availableStyleSheets() const { return m_availableSheets; }
+
+ NodeImpl* hoverNode() const { return m_hoverNode; }
+ void setHoverNode(NodeImpl *newHoverNode);
+ NodeImpl *focusNode() const { return m_focusNode; }
+ void setFocusNode(NodeImpl *newFocusNode);
+ NodeImpl* activeNode() const { return m_activeNode; }
+ void setActiveNode(NodeImpl *newActiveNode);
+
+ // Updates for :target (CSS3 selector).
+ void setCSSTarget(NodeImpl* n);
+ NodeImpl* getCSSTarget() { return m_cssTarget; }
+
+ bool isDocumentChanged() { return m_docChanged; }
+ virtual void setDocumentChanged(bool = true);
+ void attachNodeIterator(NodeIteratorImpl *ni);
+ void detachNodeIterator(NodeIteratorImpl *ni);
+ void notifyBeforeNodeRemoval(NodeImpl *n);
+ AbstractViewImpl *defaultView() const { return m_defaultView; }
+ EventImpl *createEvent(const DOMString &eventType, int &exceptioncode);
+
+ // keep track of what types of event listeners are registered, so we don't
+ // dispatch events unnecessarily
+ enum ListenerType {
+ DOMSUBTREEMODIFIED_LISTENER = 0x01,
+ DOMNODEINSERTED_LISTENER = 0x02,
+ DOMNODEREMOVED_LISTENER = 0x04,
+ DOMNODEREMOVEDFROMDOCUMENT_LISTENER = 0x08,
+ DOMNODEINSERTEDINTODOCUMENT_LISTENER = 0x10,
+ DOMATTRMODIFIED_LISTENER = 0x20,
+ DOMCHARACTERDATAMODIFIED_LISTENER = 0x40
+ };
+
+ bool hasListenerType(ListenerType listenerType) const { return (m_listenerTypes & listenerType); }
+ void addListenerType(ListenerType listenerType) { m_listenerTypes = m_listenerTypes | listenerType; }
+
+ CSSStyleDeclarationImpl *getOverrideStyle(ElementImpl *elt, DOMStringImpl *pseudoElt);
+
+ bool async() const { return m_async; }
+ void setAsync(bool b) { m_async = b; }
+ void abort();
+ void load(const DOMString &uri);
+ void loadXML(const DOMString &source);
+ // from cachedObjectClient
+ void setStyleSheet(const DOM::DOMString &url, const DOM::DOMString &sheet, const DOM::DOMString &charset);
+ void error(int err, const QString &text);
+
+ typedef QMap<QString, ProcessingInstructionImpl*> LocalStyleRefs;
+ LocalStyleRefs* localStyleRefs() { return &m_localStyleRefs; }
+
+ virtual void defaultEventHandler(EventImpl *evt);
+ virtual void setHTMLWindowEventListener(int id, EventListener *listener);
+ EventListener *getHTMLWindowEventListener(int id);
+ EventListener *createHTMLEventListener(const QString& code, const QString& name, NodeImpl* node);
+
+ void addWindowEventListener(int id, EventListener *listener, const bool useCapture);
+ void removeWindowEventListener(int id, EventListener *listener, bool useCapture);
+ bool hasWindowEventListener(int id);
+
+ EventListener *createHTMLEventListener(QString code);
+
+ /**
+ * Searches through the document, starting from fromNode, for the next selectable element that comes after fromNode.
+ * The order followed is as specified in section 17.11.1 of the HTML4 spec, which is elements with tab indexes
+ * first (from lowest to highest), and then elements without tab indexes (in document order).
+ *
+ * @param fromNode The node from which to start searching. The node after this will be focused. May be null.
+ *
+ * @return The focus node that comes after fromNode
+ *
+ * See http://www.w3.org/TR/html4/interact/forms.html#h-17.11.1
+ */
+ NodeImpl *nextFocusNode(NodeImpl *fromNode);
+
+ /**
+ * Searches through the document, starting from fromNode, for the previous selectable element (that comes _before_)
+ * fromNode. The order followed is as specified in section 17.11.1 of the HTML4 spec, which is elements with tab
+ * indexes first (from lowest to highest), and then elements without tab indexes (in document order).
+ *
+ * @param fromNode The node from which to start searching. The node before this will be focused. May be null.
+ *
+ * @return The focus node that comes before fromNode
+ *
+ * See http://www.w3.org/TR/html4/interact/forms.html#h-17.11.1
+ */
+ NodeImpl *previousFocusNode(NodeImpl *fromNode);
+
+ ElementImpl* findAccessKeyElement(QChar c);
+
+ int nodeAbsIndex(NodeImpl *node);
+ NodeImpl *nodeWithAbsIndex(int absIndex);
+
+ /**
+ * Handles a HTTP header equivalent set by a meta tag using <meta http-equiv="..." content="...">. This is called
+ * when a meta tag is encountered during document parsing, and also when a script dynamically changes or adds a meta
+ * tag. This enables scripts to use meta tags to perform refreshes and set expiry dates in addition to them being
+ * specified in a HTML file.
+ *
+ * @param equiv The http header name (value of the meta tag's "equiv" attribute)
+ * @param content The header value (value of the meta tag's "content" attribute)
+ */
+ void processHttpEquiv(const DOMString &equiv, const DOMString &content);
+
+ void dispatchImageLoadEventSoon(HTMLImageElementImpl *);
+ void dispatchImageLoadEventsNow();
+ void removeImage(HTMLImageElementImpl *);
+ virtual void timerEvent(QTimerEvent *);
+
+ // Returns the owning element in the parent document.
+ // Returns 0 if this is the top level document.
+ ElementImpl *ownerElement() const;
+
+ DOMString domain() const;
+ void setDomain( const DOMString &newDomain ); // not part of the DOM
+
+ bool isURLAllowed(const QString& url) const;
+
+ HTMLElementImpl* body();
+
+ DOMString toString() const;
+
+ void incDOMTreeVersion() { ++m_domtree_version; }
+ unsigned int domTreeVersion() const { return m_domtree_version; }
+
+ QDict<khtml::CounterNode>* counters(const khtml::RenderObject* o) { return m_counterDict[(void*)o]; }
+ void setCounters(const khtml::RenderObject* o, QDict<khtml::CounterNode> *dict) { m_counterDict.insert((void*)o, dict);}
+ void removeCounters(const khtml::RenderObject* o) { m_counterDict.remove((void*)o); }
+
+
+ ElementMappingCache& underDocNamedCache() {
+ return m_underDocNamedCache;
+ }
+
+ NodeListImpl::Cache* acquireCachedNodeListInfo(NodeListImpl::CacheFactory* fact,
+ NodeImpl* base, int type);
+ void releaseCachedNodeListInfo(NodeListImpl::Cache* cache);
+
+ ElementMappingCache& getElementByIdCache() const {
+ return m_getElementByIdCache;
+ }
+
+ QString contentLanguage() const { return m_contentLanguage; }
+ void setContentLanguage(const QString& cl) { m_contentLanguage = cl; }
+
+ khtml::DynamicDomRestyler& dynamicDomRestyler() { return *m_dynamicDomRestyler; }
+ const khtml::DynamicDomRestyler& dynamicDomRestyler() const { return *m_dynamicDomRestyler; }
+
+signals:
+ void finishedParsing();
+
+protected:
+ khtml::CSSStyleSelector *m_styleSelector;
+ KHTMLView *m_view;
+ QStringList m_state;
+
+ khtml::DocLoader *m_docLoader;
+ khtml::Tokenizer *m_tokenizer;
+ KURL m_url;
+ KURL m_baseURL;
+ QString m_baseTarget;
+
+ DocumentTypeImpl *m_doctype;
+ DOMImplementationImpl *m_implementation;
+
+ QString m_usersheet;
+ QString m_printSheet;
+ QStringList m_availableSheets;
+
+ QString m_contentLanguage;
+
+ // Track the number of currently loading top-level stylesheets. Sheets
+ // loaded using the @import directive are not included in this count.
+ // We use this count of pending sheets to detect when we can begin attaching
+ // elements.
+ int m_pendingStylesheets;
+ bool m_ignorePendingStylesheets;
+
+ CSSStyleSheetImpl *m_elemSheet;
+
+ QPaintDevice *m_paintDevice;
+ QPaintDeviceMetrics *m_paintDeviceMetrics;
+ ParseMode pMode;
+ HTMLMode hMode;
+
+ QColor m_textColor;
+ NodeImpl *m_hoverNode;
+ NodeImpl *m_focusNode;
+ NodeImpl *m_activeNode;
+ NodeImpl *m_cssTarget;
+
+ unsigned int m_domtree_version;
+
+ struct IdNameMapping {
+ IdNameMapping(unsigned short _start)
+ : idStart(_start), count(0) {}
+ ~IdNameMapping() {
+ QIntDictIterator<DOM::DOMStringImpl> it(names);
+ for (; it.current() ; ++it)
+ it.current()->deref();
+ }
+ unsigned short idStart;
+ unsigned short count;
+ QIntDict<DOM::DOMStringImpl> names;
+ QDict<void> ids;
+
+ void expandIfNeeded() {
+ if (ids.size() <= ids.count() && ids.size() != khtml_MaxSeed)
+ ids.resize( khtml::nextSeed(ids.count()) );
+ if (names.size() <= names.count() && names.size() != khtml_MaxSeed)
+ names.resize( khtml::nextSeed(names.count()) );
+ }
+
+ void addAlias(DOMStringImpl* _prefix, DOMStringImpl* _name, bool cs, NodeImpl::Id id) {
+ if(_prefix && _prefix->l) {
+ QConstString n(_name->s, _name->l);
+ QConstString px( _prefix->s, _prefix->l );
+ QString name = cs ? n.string() : n.string().upper();
+ QString qn("aliases: " + (cs ? px.string() : px.string().upper()) + ":" + name);
+ if (!ids.find( qn )) {
+ ids.insert( qn, (void*)id );
+ }
+ }
+ expandIfNeeded();
+ }
+
+ };
+
+ IdNameMapping *m_attrMap;
+ IdNameMapping *m_elementMap;
+ IdNameMapping *m_namespaceMap;
+
+ QPtrList<NodeIteratorImpl> m_nodeIterators;
+ AbstractViewImpl *m_defaultView;
+
+ unsigned short m_listenerTypes;
+ StyleSheetListImpl* m_styleSheets;
+ StyleSheetListImpl *m_addedStyleSheets; // programmatically added style sheets
+ LocalStyleRefs m_localStyleRefs; // references to inlined style elements
+ RegisteredListenerList m_windowEventListeners;
+ QPtrList<NodeImpl> m_maintainsState;
+
+ // ### evaluate for placement in RenderStyle
+ QPtrDict<QDict<khtml::CounterNode> > m_counterDict;
+
+ khtml::DynamicDomRestyler *m_dynamicDomRestyler;
+
+ bool visuallyOrdered;
+ bool m_bParsing;
+ bool m_docChanged;
+ bool m_styleSelectorDirty;
+ bool m_inStyleRecalc;
+ bool m_async;
+ bool m_hadLoadError;
+ bool m_docLoading;
+ bool m_inSyncLoad;
+
+ DOMString m_title;
+ DOMString m_preferredStylesheetSet;
+ khtml::CachedCSSStyleSheet *m_loadingXMLDoc;
+
+ int m_decoderMibEnum;
+
+ //Forms, images, etc., must be quickly accessible via document.name.
+ ElementMappingCache m_underDocNamedCache;
+
+ //Cache for nodelists and collections.
+ QIntDict<NodeListImpl::Cache> m_nodeListCache;
+
+ QPtrList<HTMLImageElementImpl> m_imageLoadEventDispatchSoonList;
+ QPtrList<HTMLImageElementImpl> m_imageLoadEventDispatchingList;
+ int m_imageLoadEventTimer;
+
+ //Cache for getElementById
+ mutable ElementMappingCache m_getElementByIdCache;
+
+ khtml::SharedPtr<khtml::RenderArena> m_renderArena;
+private:
+ mutable DOMString m_domain;
+ int m_selfOnlyRefCount;
+public:
+ // Nodes belonging to this document hold "self-only" references -
+ // these are enough to keep the document from being destroyed, but
+ // not enough to keep it from removing its children. This allows a
+ // node that outlives its document to still have a valid document
+ // pointer without introducing reference cycles
+
+ void selfOnlyRef() { ++m_selfOnlyRefCount; }
+ void selfOnlyDeref() {
+ --m_selfOnlyRefCount;
+ if (!m_selfOnlyRefCount && !refCount())
+ delete this;
+ }
+
+ // This is called when our last outside reference dies
+ virtual void removedLastRef();
+};
+
+class DocumentFragmentImpl : public NodeBaseImpl
+{
+public:
+ DocumentFragmentImpl(DocumentImpl *doc);
+ DocumentFragmentImpl(const DocumentFragmentImpl &other);
+
+ // DOM methods overridden from parent classes
+ virtual DOMString nodeName() const;
+ virtual unsigned short nodeType() const;
+ virtual NodeImpl *cloneNode ( bool deep );
+
+ // Other methods (not part of DOM)
+ virtual bool childTypeAllowed( unsigned short type );
+
+ virtual DOMString toString() const;
+};
+
+
+class DocumentTypeImpl : public NodeImpl
+{
+public:
+ DocumentTypeImpl(DOMImplementationImpl *_implementation, DocumentImpl *doc,
+ const DOMString &qualifiedName, const DOMString &publicId,
+ const DOMString &systemId);
+ ~DocumentTypeImpl();
+
+ // DOM methods & attributes for DocumentType
+ NamedNodeMapImpl *entities() const;
+ NamedNodeMapImpl *notations() const;
+
+ DOMString name() const { return m_qualifiedName; }
+ DOMString publicId() const { return m_publicId; }
+ DOMString systemId() const { return m_systemId; }
+ DOMString internalSubset() const { return m_subset; }
+
+ // DOM methods overridden from parent classes
+ virtual DOMString nodeName() const;
+ virtual unsigned short nodeType() const;
+ virtual bool childTypeAllowed( unsigned short type );
+ virtual NodeImpl *cloneNode ( bool deep );
+
+ virtual DOMStringImpl* textContent() const;
+ virtual void setTextContent( const DOMString &text, int& exceptioncode );
+
+ // Other methods (not part of DOM)
+ void setName(const DOMString& n) { m_qualifiedName = n; }
+ void setPublicId(const DOMString& publicId) { m_publicId = publicId; }
+ void setSystemId(const DOMString& systemId) { m_systemId = systemId; }
+ DOMImplementationImpl *implementation() const { return m_implementation; }
+ void copyFrom(const DocumentTypeImpl&);
+
+ virtual DOMString toString() const;
+
+protected:
+ DOMImplementationImpl *m_implementation;
+ mutable NamedNodeMapImpl* m_entities;
+ mutable NamedNodeMapImpl* m_notations;
+
+ DOMString m_qualifiedName;
+ DOMString m_publicId;
+ DOMString m_systemId;
+ DOMString m_subset;
+};
+
+} //namespace
+#endif
diff --git a/khtml/xml/dom_elementimpl.cpp b/khtml/xml/dom_elementimpl.cpp
new file mode 100644
index 000000000..5db497e7d
--- /dev/null
+++ b/khtml/xml/dom_elementimpl.cpp
@@ -0,0 +1,1301 @@
+/**
+ * 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 Peter Kelly (pmk@post.com)
+ * (C) 2001 Dirk Mueller (mueller@kde.org)
+ * (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.
+ */
+
+//#define EVENT_DEBUG
+#include "dom/dom_exception.h"
+#include "dom/dom_node.h"
+#include "dom/html_image.h"
+#include "xml/dom_textimpl.h"
+#include "xml/dom_docimpl.h"
+#include "xml/dom2_eventsimpl.h"
+#include "xml/dom_elementimpl.h"
+#include "xml/dom_restyler.h"
+
+#include "html/dtd.h"
+#include "html/htmlparser.h"
+#include "html/html_imageimpl.h"
+
+#include "rendering/render_canvas.h"
+#include "misc/htmlhashes.h"
+#include "css/css_valueimpl.h"
+#include "css/css_stylesheetimpl.h"
+#include "css/cssstyleselector.h"
+#include "css/cssvalues.h"
+#include "css/cssproperties.h"
+#include "xml/dom_xmlimpl.h"
+
+#include <qtextstream.h>
+#include <kdebug.h>
+#include <stdlib.h>
+
+// ### support default attributes
+// ### dispatch mutation events
+// ### check for INVALID_CHARACTER_ERR where appropriate
+
+using namespace DOM;
+using namespace khtml;
+
+AttrImpl::AttrImpl(ElementImpl* element, DocumentImpl* docPtr, NodeImpl::Id attrId,
+ DOMStringImpl *value, DOMStringImpl *prefix)
+ : NodeBaseImpl(docPtr),
+ m_element(element),
+ m_attrId(attrId)
+{
+ m_value = value;
+ m_value->ref();
+
+ m_prefix = prefix;
+ if (m_prefix)
+ m_prefix->ref();
+ m_specified = true; // we don't yet support default attributes
+}
+
+AttrImpl::~AttrImpl()
+{
+ m_value->deref();
+ if (m_prefix)
+ m_prefix->deref();
+}
+
+DOMString AttrImpl::nodeName() const
+{
+ return name();
+}
+
+unsigned short AttrImpl::nodeType() const
+{
+ return Node::ATTRIBUTE_NODE;
+}
+
+DOMString AttrImpl::prefix() const
+{
+ return m_prefix;
+}
+
+void AttrImpl::setPrefix(const DOMString &_prefix, int &exceptioncode )
+{
+ checkSetPrefix(_prefix, exceptioncode);
+ if (exceptioncode)
+ return;
+
+ if (m_prefix == _prefix.implementation())
+ return;
+
+ if (m_prefix)
+ m_prefix->deref();
+ m_prefix = _prefix.implementation();
+ if (m_prefix)
+ m_prefix->ref();
+}
+
+DOMString AttrImpl::namespaceURI() const
+{
+ if (m_htmlCompat)
+ return DOMString();
+ return getDocument()->getName(NamespaceId, m_attrId >> 16);
+}
+
+DOMString AttrImpl::localName() const
+{
+ if (m_htmlCompat)
+ return DOMString();
+ return getDocument()->getName(AttributeId, m_attrId);
+}
+
+DOMString AttrImpl::nodeValue() const
+{
+ return m_value;
+}
+
+DOMString AttrImpl::name() const
+{
+ DOMString n = getDocument()->getName(AttributeId, m_attrId);
+
+ // compat mode always return attribute names in lowercase.
+ // that's not formally in the specification, but common
+ // practice - a w3c erratum to DOM L2 is pending.
+ if (m_htmlCompat)
+ n = n.lower();
+
+ if (m_prefix && m_prefix->l)
+ return DOMString(m_prefix) + ":" + n;
+
+ return n;
+}
+
+void AttrImpl::setValue( const DOMString &v, int &exceptioncode )
+{
+ exceptioncode = 0;
+
+ // ### according to the DOM docs, we should create an unparsed Text child
+ // node here
+ // do not interprete entities in the string, its literal!
+
+ // NO_MODIFICATION_ALLOWED_ERR: Raised when the node is readonly
+ if (isReadOnly()) {
+ exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR;
+ return;
+ }
+
+ // ### what to do on 0 ?
+ if (v.isNull()) {
+ exceptioncode = DOMException::DOMSTRING_SIZE_ERR;
+ return;
+ }
+
+ if (m_value == v.implementation())
+ return;
+
+ if (m_element && m_attrId == ATTR_ID)
+ m_element->updateId(m_value, v.implementation());
+
+ m_value->deref();
+ m_value = v.implementation();
+ m_value->ref();
+
+ if (m_element) {
+ m_element->parseAttribute(m_attrId,m_value);
+ m_element->attributeChanged(m_attrId);
+ }
+}
+
+void AttrImpl::setNodeValue( const DOMString &v, int &exceptioncode )
+{
+ exceptioncode = 0;
+ // NO_MODIFICATION_ALLOWED_ERR: taken care of by setValue()
+ setValue(v, exceptioncode);
+}
+
+NodeImpl *AttrImpl::cloneNode ( bool /*deep*/)
+{
+ AttrImpl* attr = new AttrImpl(0, docPtr(), m_attrId, m_value, m_prefix);
+ attr->setHTMLCompat(m_htmlCompat);
+ return attr;
+}
+
+// DOM Section 1.1.1
+bool AttrImpl::childAllowed( NodeImpl *newChild )
+{
+ if(!newChild)
+ return false;
+
+ return childTypeAllowed(newChild->nodeType());
+}
+
+bool AttrImpl::childTypeAllowed( unsigned short type )
+{
+ switch (type) {
+ case Node::TEXT_NODE:
+ case Node::ENTITY_REFERENCE_NODE:
+ return true;
+ break;
+ default:
+ return false;
+ }
+}
+
+DOMString AttrImpl::toString() const
+{
+ DOMString result;
+
+ result += nodeName();
+
+ // FIXME: substitute entities for any instances of " or ' --
+ // maybe easier to just use text value and ignore existing
+ // entity refs?
+
+ if ( firstChild() ) {
+ result += "=\"";
+
+ for (NodeImpl *child = firstChild(); child != NULL; child = child->nextSibling()) {
+ result += child->toString();
+ }
+
+ result += "\"";
+ } else if ( !nodeValue().isEmpty() ){
+ //remove the else once the AttributeImpl changes are merged
+ result += "=\"";
+ result += nodeValue();
+ result += "\"";
+ }
+
+ return result;
+}
+
+void AttrImpl::setElement(ElementImpl *element)
+{
+ m_element = element;
+}
+
+// Strictly speaking, these two methods should not be needed, but
+// we can't fully deal with the mess that are DOM attributes right..
+DOMStringImpl* AttrImpl::textContent() const
+{
+ if (m_value)
+ return new DOMStringImpl(m_value->s, m_value->l);
+ else
+ return 0;
+}
+
+void AttrImpl::setTextContent( const DOMString &text, int& exceptioncode )
+{
+ setValue(text, exceptioncode);
+}
+
+// -------------------------------------------------------------------------
+
+void AttributeImpl::setValue(DOMStringImpl *value, ElementImpl *element)
+{
+ assert(value);
+ if (m_attrId) {
+ if (m_data.value == value)
+ return;
+
+ if (element && m_attrId == ATTR_ID)
+ element->updateId(m_data.value, value);
+
+ m_data.value->deref();
+ m_data.value = value;
+ m_data.value->ref();
+
+ if (element) {
+ element->parseAttribute(this);
+ element->attributeChanged(m_attrId);
+ }
+ }
+ else {
+ int exceptioncode = 0;
+ m_data.attr->setValue(value,exceptioncode);
+ // AttrImpl::setValue() calls parseAttribute()
+ }
+}
+
+AttrImpl *AttributeImpl::createAttr(ElementImpl *element, DocumentImpl *docPtr)
+{
+ if (m_attrId) {
+ AttrImpl *attr = new AttrImpl(element,docPtr,m_attrId,m_data.value);
+ if (!attr) return 0;
+ attr->setHTMLCompat( docPtr->htmlMode() != DocumentImpl::XHtml );
+ m_data.value->deref();
+ m_data.attr = attr;
+ m_data.attr->ref();
+ m_attrId = 0; /* "has implementation" flag */
+ }
+
+ return m_data.attr;
+}
+
+void AttributeImpl::free()
+{
+ if (m_attrId) {
+ m_data.value->deref();
+ }
+ else {
+ m_data.attr->setElement(0);
+ m_data.attr->deref();
+ }
+}
+
+// -------------------------------------------------------------------------
+
+ElementImpl::ElementImpl(DocumentImpl *doc)
+ : NodeBaseImpl(doc)
+{
+ namedAttrMap = 0;
+ m_styleDecls = 0;
+ m_prefix = 0;
+}
+
+ElementImpl::~ElementImpl()
+{
+ if(namedAttrMap) {
+ namedAttrMap->detachFromElement();
+ namedAttrMap->deref();
+ }
+
+ if (m_styleDecls) {
+ m_styleDecls->setNode(0);
+ m_styleDecls->setParent(0);
+ m_styleDecls->deref();
+ }
+
+ if (m_prefix)
+ m_prefix->deref();
+}
+
+unsigned short ElementImpl::nodeType() const
+{
+ return Node::ELEMENT_NODE;
+}
+
+DOMStringImpl* ElementImpl::getAttributeImpl( NodeImpl::Id id, bool nsAware, DOMStringImpl* qName) const
+{
+ if (!namedAttrMap)
+ return 0;
+
+ DOMStringImpl *value = namedAttrMap->getValue(id, nsAware, qName);
+ if (value)
+ return value;
+
+ // then search in default attr in case it is not yet set
+ NamedAttrMapImpl* dm = defaultMap();
+ value = dm ? dm->getValue(id, nsAware, qName) : 0;
+ if (value)
+ return value;
+
+ return 0;
+}
+
+DOMString ElementImpl::getAttribute( NodeImpl::Id id, bool nsAware, const DOMString& qName) const
+{
+ return DOMString(getAttributeImpl(id, nsAware, qName.implementation()));
+}
+
+void ElementImpl::setAttribute(NodeImpl::Id id, const DOMString &value, const DOMString& qName, int &exceptioncode)
+{
+ // NO_MODIFICATION_ALLOWED_ERR: Raised when the node is readonly
+ if (isReadOnly()) {
+ exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR;
+ return;
+ }
+ attributes()->setValue(id, value.implementation(), (qName.isEmpty() ? 0: qName.implementation()));
+}
+
+void ElementImpl::setAttributeNS( const DOMString &namespaceURI, const DOMString &qualifiedName,
+ const DOMString &value, int &exceptioncode )
+{
+ // NO_MODIFICATION_ALLOWED_ERR: Raised when the node is readonly
+ if (isReadOnly()) {
+ exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR;
+ return;
+ }
+ int colonPos;
+ if (!DOM::checkQualifiedName(qualifiedName, namespaceURI, &colonPos,
+ false/*nameCanBeNull*/, false/*nameCanBeEmpty*/,
+ &exceptioncode))
+ return;
+ DOMString prefix, localName;
+ splitPrefixLocalName(qualifiedName.implementation(), prefix, localName, colonPos);
+ NodeImpl::Id id = getDocument()->getId(AttributeId, namespaceURI.implementation(),
+ prefix.implementation(), localName.implementation(), false, true /*lookupHTML*/);
+ attributes()->setValue(id, value.implementation(), 0, prefix.implementation(),
+ true /*nsAware*/, !namespaceURI.isNull() /*hasNS*/);
+}
+
+void ElementImpl::setAttribute(NodeImpl::Id id, const DOMString &value)
+{
+ int exceptioncode = 0;
+ setAttribute(id,value,DOMString(),exceptioncode);
+}
+
+void ElementImpl::setAttributeMap( NamedAttrMapImpl* list )
+{
+ // If setting the whole map changes the id attribute, we need to
+ // call updateId.
+ DOMStringImpl *oldId = namedAttrMap ? namedAttrMap->getValue(ATTR_ID) : 0;
+ DOMStringImpl *newId = list ? list->getValue(ATTR_ID) : 0;
+
+ if (oldId || newId) {
+ updateId(oldId, newId);
+ }
+
+ if (namedAttrMap) {
+ namedAttrMap->detachFromElement();
+ namedAttrMap->deref();
+ }
+
+ namedAttrMap = list;
+
+ if (namedAttrMap) {
+ namedAttrMap->ref();
+ assert(namedAttrMap->m_element == 0);
+ namedAttrMap->setElement(this);
+ unsigned long len = namedAttrMap->length();
+ for (unsigned long i = 0; i < len; i++) {
+ parseAttribute(&namedAttrMap->m_attrs[i]);
+ attributeChanged(namedAttrMap->m_attrs[i].id());
+ }
+ }
+}
+
+NodeImpl *ElementImpl::cloneNode(bool deep)
+{
+ ElementImpl *clone;
+ if ( !localName().isNull() )
+ clone = getDocument()->createElementNS( namespaceURI(), nodeName() );
+ else
+ clone = getDocument()->createElement( nodeName() );
+ if (!clone) return 0;
+ finishCloneNode( clone, deep );
+ return clone;
+}
+
+void ElementImpl::finishCloneNode( ElementImpl* clone, bool deep )
+{
+ // clone attributes
+ if (namedAttrMap)
+ clone->attributes()->copyAttributes(namedAttrMap);
+
+ // clone individual style rules
+ if (m_styleDecls)
+ *(clone->styleRules()) = *m_styleDecls;
+
+ if (deep)
+ cloneChildNodes(clone);
+}
+
+DOMString ElementImpl::nodeName() const
+{
+ return tagName();
+}
+
+DOMString ElementImpl::namespaceURI() const
+{
+ if (m_htmlCompat)
+ return DOMString();
+ return getDocument()->getName(NamespaceId, id() >> 16);
+}
+
+DOMString ElementImpl::prefix() const
+{
+ return m_prefix;
+}
+
+void ElementImpl::setPrefix( const DOMString &_prefix, int &exceptioncode )
+{
+ checkSetPrefix(_prefix, exceptioncode);
+ if (exceptioncode)
+ return;
+
+ if (m_prefix == _prefix.implementation())
+ return;
+
+ if (m_prefix)
+ m_prefix->deref();
+ m_prefix = _prefix.implementation();
+ if (m_prefix)
+ m_prefix->ref();
+}
+
+void ElementImpl::createAttributeMap() const
+{
+ namedAttrMap = new NamedAttrMapImpl(const_cast<ElementImpl*>(this));
+ namedAttrMap->ref();
+}
+
+NamedAttrMapImpl* ElementImpl::defaultMap() const
+{
+ return 0;
+}
+
+RenderStyle *ElementImpl::styleForRenderer(RenderObject * /*parentRenderer*/)
+{
+ return getDocument()->styleSelector()->styleForElement(this);
+}
+
+RenderObject *ElementImpl::createRenderer(RenderArena *arena, RenderStyle *style)
+{
+ if (getDocument()->documentElement() == this && style->display() == NONE) {
+ // Ignore display: none on root elements. Force a display of block in that case.
+ RenderBlock* result = new (arena) RenderBlock(this);
+ if (result) result->setStyle(style);
+ return result;
+ }
+ return RenderObject::createObject(this, style);
+}
+
+void ElementImpl::attach()
+{
+ assert(!attached());
+ assert(!m_render);
+ assert(parentNode());
+
+#if SPEED_DEBUG < 1
+ createRendererIfNeeded();
+#endif
+
+ NodeBaseImpl::attach();
+}
+
+void ElementImpl::close()
+{
+ NodeImpl::close();
+
+ // Trigger all the addChild changes as one large dynamic appendChildren change
+ if (attached())
+ backwardsStructureChanged();
+}
+
+void ElementImpl::detach()
+{
+ getDocument()->dynamicDomRestyler().resetDependencies(this);
+
+ NodeBaseImpl::detach();
+}
+
+void ElementImpl::structureChanged()
+{
+ NodeBaseImpl::structureChanged();
+
+ if (!getDocument()->renderer())
+ return; // the document is about to be destroyed
+
+ getDocument()->dynamicDomRestyler().restyleDepedent(this, StructuralDependency);
+ // In theory BackwardsStructurualDependencies are indifferent to prepend,
+ // but it's too rare to optimize.
+ getDocument()->dynamicDomRestyler().restyleDepedent(this, BackwardsStructuralDependency);
+}
+
+void ElementImpl::backwardsStructureChanged()
+{
+ NodeBaseImpl::backwardsStructureChanged();
+
+ if (!getDocument()->renderer())
+ return; // the document is about to be destroyed
+
+ // Most selectors are not affected by append. Fire the few that are.
+ getDocument()->dynamicDomRestyler().restyleDepedent(this, BackwardsStructuralDependency);
+}
+
+void ElementImpl::attributeChanged(NodeImpl::Id id)
+{
+ if (!getDocument()->renderer())
+ return; // the document is about to be destroyed
+
+#if 0 // one-one dependencies for attributes disabled
+ getDocument()->dynamicDomRestyler().restyleDepedent(this, AttributeDependency);
+#endif
+ if (getDocument()->dynamicDomRestyler().checkDependency(id, PersonalDependency))
+ setChanged(true);
+ if (getDocument()->dynamicDomRestyler().checkDependency(id, AncestorDependency))
+ setChangedAscendentAttribute(true);
+ if (getDocument()->dynamicDomRestyler().checkDependency(id, PredecessorDependency) && parent())
+ // Any element that dependt on a predecessors attribute, also depend structurally on parent
+ parent()->structureChanged();
+}
+
+void ElementImpl::recalcStyle( StyleChange change )
+{
+ // ### should go away and be done in renderobject
+ RenderStyle* _style = m_render ? m_render->style() : 0;
+ bool hasParentRenderer = parent() ? parent()->attached() : false;
+
+#if 0
+ const char* debug;
+ switch(change) {
+ case NoChange: debug = "NoChange";
+ break;
+ case NoInherit: debug= "NoInherit";
+ break;
+ case Inherit: debug = "Inherit";
+ break;
+ case Force: debug = "Force";
+ break;
+ }
+ qDebug("recalcStyle(%d: %s, changed: %d)[%p: %s]", change, debug, changed(), this, tagName().string().latin1());
+#endif
+ if ( hasParentRenderer && (change >= Inherit || changed()) ) {
+ RenderStyle *newStyle = getDocument()->styleSelector()->styleForElement(this);
+ newStyle->ref();
+ StyleChange ch = diff( _style, newStyle );
+ if (ch == Detach) {
+ if (attached()) detach();
+ // ### Suboptimal. Style gets calculated again.
+ attach();
+ // attach recalulates the style for all children. No need to do it twice.
+ setChanged( false );
+ setHasChangedChild( false );
+ newStyle->deref();
+ return;
+ }
+ else if (ch != NoChange) {
+ if( m_render && newStyle ) {
+ m_render->setStyle(newStyle);
+ }
+ }
+ newStyle->deref();
+
+ if ( change != Force)
+ change = ch;
+ }
+ // If a changed attribute has ancestor dependencies, restyle all children
+ if (changedAscendentAttribute()) {
+ change = Force;
+ setChangedAscendentAttribute(false);
+ }
+
+ NodeImpl *n;
+ for (n = _first; n; n = n->nextSibling()) {
+ if ( change >= Inherit || n->isTextNode() ||
+ n->hasChangedChild() || n->changed() ) {
+ //qDebug(" (%p) calling recalcStyle on child %p/%s, change=%d", this, n, n->isElementNode() ? ((ElementImpl *)n)->tagName().string().latin1() : n->isTextNode() ? "text" : "unknown", change );
+ n->recalcStyle( change );
+ }
+ }
+
+ setChanged( false );
+ setHasChangedChild( false );
+}
+
+bool ElementImpl::isFocusable() const
+{
+ // Only make editable elements selectable if its parent element
+ // is not editable. FIXME: this is not 100% right as non-editable elements
+ // within editable elements are focusable too.
+ return contentEditable() && !(parentNode() && parentNode()->contentEditable());
+}
+
+// DOM Section 1.1.1
+bool ElementImpl::childAllowed( NodeImpl *newChild )
+{
+ if (!childTypeAllowed(newChild->nodeType()))
+ return false;
+
+ // ### check xml element allowedness according to DTD
+
+ // If either this node or the other node is an XML element node, allow regardless (we don't do DTD checks for XML
+ // yet)
+ if (isXMLElementNode() || newChild->isXMLElementNode())
+ return true;
+ else
+ return checkChild(id(), newChild->id(), !getDocument()->inCompatMode());
+}
+
+bool ElementImpl::childTypeAllowed( unsigned short type )
+{
+ switch (type) {
+ case Node::ELEMENT_NODE:
+ case Node::TEXT_NODE:
+ case Node::COMMENT_NODE:
+ case Node::PROCESSING_INSTRUCTION_NODE:
+ case Node::CDATA_SECTION_NODE:
+ case Node::ENTITY_REFERENCE_NODE:
+ return true;
+ break;
+ default:
+ return false;
+ }
+}
+
+void ElementImpl::scrollIntoView(bool /*alignToTop*/)
+{
+ // ###
+ kdWarning() << "non-standard scrollIntoView() not implemented" << endl;
+}
+
+void ElementImpl::createDecl( )
+{
+ m_styleDecls = new CSSStyleDeclarationImpl(0);
+ m_styleDecls->ref();
+ m_styleDecls->setParent(getDocument()->elementSheet());
+ m_styleDecls->setNode(this);
+ m_styleDecls->setStrictParsing( !getDocument()->inCompatMode() );
+}
+
+void ElementImpl::dispatchAttrRemovalEvent(NodeImpl::Id /*id*/, DOMStringImpl * /*value*/)
+{
+ // ### enable this stuff again
+ if (!getDocument()->hasListenerType(DocumentImpl::DOMATTRMODIFIED_LISTENER))
+ return;
+ //int exceptioncode = 0;
+ //dispatchEvent(new MutationEventImpl(EventImpl::DOMATTRMODIFIED_EVENT,true,false,attr,attr->value(),
+ //attr->value(), getDocument()->attrName(attr->id()),MutationEvent::REMOVAL),exceptioncode);
+}
+
+void ElementImpl::dispatchAttrAdditionEvent(NodeImpl::Id /*id*/, DOMStringImpl * /*value*/)
+{
+ // ### enable this stuff again
+ if (!getDocument()->hasListenerType(DocumentImpl::DOMATTRMODIFIED_LISTENER))
+ return;
+ //int exceptioncode = 0;
+ //dispatchEvent(new MutationEventImpl(EventImpl::DOMATTRMODIFIED_EVENT,true,false,attr,attr->value(),
+ //attr->value(),getDocument()->attrName(attr->id()),MutationEvent::ADDITION),exceptioncode);
+}
+
+void ElementImpl::updateId(DOMStringImpl* oldId, DOMStringImpl* newId)
+{
+ if (!inDocument())
+ return;
+
+ if (oldId && oldId->l)
+ removeId(DOMString(oldId).string());
+
+ if (newId && newId->l)
+ addId(DOMString(newId).string());
+}
+
+void ElementImpl::removeId(const QString& id)
+{
+ getDocument()->getElementByIdCache().remove(id, this);
+}
+
+void ElementImpl::addId(const QString& id)
+{
+ getDocument()->getElementByIdCache().add(id, this);
+}
+
+void ElementImpl::insertedIntoDocument()
+{
+ // need to do superclass processing first so inDocument() is true
+ // by the time we reach updateId
+ NodeBaseImpl::insertedIntoDocument();
+
+ if (hasID()) {
+ DOMString id = getAttribute(ATTR_ID);
+ updateId(0, id.implementation());
+ }
+}
+
+void ElementImpl::removedFromDocument()
+{
+ if (hasID()) {
+ DOMString id = getAttribute(ATTR_ID);
+ updateId(id.implementation(), 0);
+ }
+
+ NodeBaseImpl::removedFromDocument();
+}
+
+DOMString ElementImpl::openTagStartToString(bool expandurls) const
+{
+ DOMString result = DOMString("<") + tagName();
+
+ NamedAttrMapImpl *attrMap = attributes(true);
+
+ if (attrMap) {
+ unsigned long numAttrs = attrMap->length();
+ for (unsigned long i = 0; i < numAttrs; i++) {
+ result += " ";
+
+ AttributeImpl *attribute = attrMap->attrAt(i);
+ AttrImpl *attr = attribute->attr();
+
+ if (attr) {
+ result += attr->toString();
+ } else {
+ result += getDocument()->getName( NodeImpl::AttributeId, attribute->id());
+ if (!attribute->value().isNull()) {
+ result += "=\"";
+ // FIXME: substitute entities for any instances of " or '
+ // Expand out all urls, i.e. the src and href attributes
+ if(expandurls && ( attribute->id() == ATTR_SRC || attribute->id() == ATTR_HREF))
+ if(getDocument()) {
+ //We need to sanitize the urls - strip out the passwords.
+ //FIXME: are src= and href= the only places that might have a password and need to be sanitized?
+ KURL safeURL(getDocument()->completeURL(attribute->value().string()));
+ safeURL.setPass(QString::null);
+ result += safeURL.htmlURL();
+ }
+ else {
+ kdWarning() << "getDocument() returned false";
+ result += attribute->value();
+ }
+ else
+ result += attribute->value();
+ result += "\"";
+ }
+ }
+ }
+ }
+
+ return result;
+}
+DOMString ElementImpl::selectionToString(NodeImpl *selectionStart, NodeImpl *selectionEnd, int startOffset, int endOffset, bool &found) const
+{
+ DOMString result = openTagStartToString();
+
+ if (hasChildNodes()) {
+ result += ">";
+
+ for (NodeImpl *child = firstChild(); child != NULL; child = child->nextSibling()) {
+ result += child->selectionToString(selectionStart, selectionEnd, startOffset, endOffset, found); // this might set found to true
+ if(child == selectionEnd)
+ found = true;
+ if(found) break;
+ }
+
+ result += "</";
+ result += tagName();
+ result += ">";
+ } else {
+ result += " />";
+ }
+
+ return result;
+}
+
+DOMString ElementImpl::toString() const
+{
+ QString result = openTagStartToString().string(); //Accumulate in QString, since DOMString can't append well.
+
+ if (hasChildNodes()) {
+ result += ">";
+
+ for (NodeImpl *child = firstChild(); child != NULL; child = child->nextSibling()) {
+ DOMString kid = child->toString();
+ result += QConstString(kid.unicode(), kid.length()).string();
+ }
+
+ result += "</";
+ result += tagName().string();
+ result += ">";
+ } else if (result.length() == 1) {
+ // ensure we dont get results like < /> can happen when serialize document
+ result = "";
+ } else {
+ result += " />";
+ }
+
+ return result;
+}
+
+bool ElementImpl::contentEditable() const {
+#if 0
+ DOM::CSSPrimitiveValueImpl *val = static_cast<DOM::CSSPrimitiveValueImpl *>
+ (const_cast<ElementImpl *>(this)->styleRules()
+ ->getPropertyCSSValue(CSS_PROP__KONQ_USER_INPUT));
+// kdDebug() << "val" << val << endl;
+ return val ? val->getIdent() == CSS_VAL_ENABLED : false;
+#endif
+ return NodeImpl::contentEditable();
+}
+
+void ElementImpl::setContentEditable(bool enabled) {
+ // FIXME: the approach is flawed, better use an enum instead of bool
+ int value;
+ if (enabled)
+ value = CSS_VAL_ENABLED;
+ else {
+ // Intelligently use "none" or "disabled", depending on the type of
+ // element
+ // FIXME: intelligence not impl'd yet
+ value = CSS_VAL_NONE;
+
+ // FIXME: reset caret if it is in this node or a child
+ }/*end if*/
+ // FIXME: use addCSSProperty when I get permission to move it here
+// kdDebug(6000) << "CSS_PROP__KHTML_USER_INPUT: "<< value << endl;
+ styleRules()->setProperty(CSS_PROP__KHTML_USER_INPUT, value, false, true);
+ setChanged();
+
+}
+
+// -------------------------------------------------------------------------
+
+XMLElementImpl::XMLElementImpl(DocumentImpl *doc, NodeImpl::Id id)
+ : ElementImpl(doc)
+{
+ // Called from createElement(). In this case localName, prefix, and namespaceURI all need to be null.
+ m_id = id;
+}
+
+XMLElementImpl::XMLElementImpl(DocumentImpl *doc, NodeImpl::Id id, DOMStringImpl *_prefix)
+ : ElementImpl(doc)
+{
+ // Called from createElementNS()
+ m_id = id;
+
+ m_prefix = _prefix;
+ if (m_prefix)
+ m_prefix->ref();
+}
+
+XMLElementImpl::~XMLElementImpl()
+{
+}
+
+DOMString XMLElementImpl::localName() const
+{
+ if ( m_htmlCompat )
+ return DOMString(); // was created with non-namespace-aware createElement()
+ return getDocument()->getName(ElementId, m_id);
+}
+
+DOMString XMLElementImpl::tagName() const
+{
+ DOMString tn = getDocument()->getName(ElementId, id());
+ if (m_htmlCompat)
+ tn = tn.upper();
+
+ if (m_prefix)
+ return DOMString(m_prefix) + ":" + tn;
+
+ return tn;
+}
+
+NodeImpl *XMLElementImpl::cloneNode ( bool deep )
+{
+ XMLElementImpl *clone = new XMLElementImpl(docPtr(), id(), m_prefix);
+ finishCloneNode( clone, deep );
+ return clone;
+}
+
+// -------------------------------------------------------------------------
+
+NamedAttrMapImpl::NamedAttrMapImpl(ElementImpl *element)
+ : m_element(element),
+ m_attrs(0),
+ m_attrCount(0)
+{
+}
+
+NamedAttrMapImpl::~NamedAttrMapImpl()
+{
+ for (unsigned long i = 0; i < m_attrCount; i++)
+ m_attrs[i].free();
+ free(m_attrs);
+}
+
+NodeImpl *NamedAttrMapImpl::getNamedItem ( NodeImpl::Id id, bool nsAware, DOMStringImpl* qName ) const
+{
+ if (!m_element)
+ return 0;
+ unsigned int mask = nsAware ? ~0L : NodeImpl_IdLocalMask;
+ id = (id & mask);
+
+ for (unsigned long i = 0; i < m_attrCount; i++) {
+ if ((m_attrs[i].id() & mask) == id) {
+ // if we are called with a qualified name, filter out NS-aware elements with non-matching name.
+ if (qName && (namespacePart(m_attrs[i].id()) != defaultNamespace) &&
+ strcasecmp(m_attrs[i].name(), DOMString(qName)))
+ continue;
+ return m_attrs[i].createAttr(m_element,m_element->docPtr());
+ }
+ }
+
+ return 0;
+}
+
+Node NamedAttrMapImpl::removeNamedItem ( NodeImpl::Id id, bool nsAware, DOMStringImpl* qName, int &exceptioncode )
+{
+ if (!m_element) {
+ exceptioncode = DOMException::NOT_FOUND_ERR;
+ return 0;
+ }
+
+ // NO_MODIFICATION_ALLOWED_ERR: Raised when the node is readonly
+ if (isReadOnly()) {
+ exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR;
+ return 0;
+ }
+ unsigned int mask = nsAware ? ~0L : NodeImpl_IdLocalMask;
+ id = (id & mask);
+
+ for (unsigned long i = 0; i < m_attrCount; i++) {
+ if ((m_attrs[i].id() & mask) == id) {
+ // if we are called with a qualified name, filter out NS-aware elements with non-matching name.
+ if (qName && (namespacePart(m_attrs[i].id()) != defaultNamespace) &&
+ strcasecmp(m_attrs[i].name(), DOMString(qName)))
+ continue;
+ id = m_attrs[i].id();
+ if (id == ATTR_ID)
+ m_element->updateId(m_attrs[i].val(), 0);
+ Node removed(m_attrs[i].createAttr(m_element,m_element->docPtr()));
+ m_attrs[i].free();
+ memmove(m_attrs+i,m_attrs+i+1,(m_attrCount-i-1)*sizeof(AttributeImpl));
+ m_attrCount--;
+ m_attrs = (AttributeImpl*)realloc(m_attrs,m_attrCount*sizeof(AttributeImpl));
+ m_element->parseAttribute(id,0);
+ m_element->attributeChanged(id);
+ return removed;
+ }
+ }
+
+ // NOT_FOUND_ERR: Raised if there is no node with the specified namespaceURI
+ // and localName in this map.
+ exceptioncode = DOMException::NOT_FOUND_ERR;
+ return 0;
+}
+
+Node NamedAttrMapImpl::setNamedItem ( NodeImpl* arg, bool nsAware, DOMStringImpl* qName, int &exceptioncode )
+{
+ if (!m_element) {
+ exceptioncode = DOMException::NOT_FOUND_ERR;
+ return 0;
+ }
+
+ // NO_MODIFICATION_ALLOWED_ERR: Raised if this map is readonly.
+ if (isReadOnly()) {
+ exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR;
+ return 0;
+ }
+
+ // WRONG_DOCUMENT_ERR: Raised if arg was created from a different document than the one that created this map.
+ if (arg->getDocument() != m_element->getDocument()) {
+ exceptioncode = DOMException::WRONG_DOCUMENT_ERR;
+ return 0;
+ }
+
+ // HIERARCHY_REQUEST_ERR: Raised if an attempt is made to add a node doesn't belong in this NamedNodeMap
+ if (!arg->isAttributeNode()) {
+ exceptioncode = DOMException::HIERARCHY_REQUEST_ERR;
+ return 0;
+ }
+ AttrImpl *attr = static_cast<AttrImpl*>(arg);
+
+ // INUSE_ATTRIBUTE_ERR: Raised if arg is an Attr that is already an attribute of another Element object.
+ // The DOM user must explicitly clone Attr nodes to re-use them in other elements.
+ if (attr->ownerElement() && attr->ownerElement() != m_element) {
+ exceptioncode = DOMException::INUSE_ATTRIBUTE_ERR;
+ return 0;
+ }
+
+ if (attr->ownerElement() == m_element) {
+ // Already have this attribute.
+ // DOMTS core-1 test "hc_elementreplaceattributewithself" says we should return it.
+ return attr;
+ }
+ unsigned int mask = nsAware ? ~0L : NodeImpl_IdLocalMask;
+ NodeImpl::Id id = (attr->id() & mask);
+
+ for (unsigned long i = 0; i < m_attrCount; i++) {
+ if ((m_attrs[i].id() & mask) == id) {
+ // if we are called with a qualified name, filter out NS-aware elements with non-matching name.
+ if (qName && (namespacePart(m_attrs[i].id()) != defaultNamespace) &&
+ strcasecmp(m_attrs[i].name(), DOMString(qName)))
+ continue;
+ // Attribute exists; replace it
+ if (id == ATTR_ID)
+ m_element->updateId(m_attrs[i].val(), attr->val());
+
+ Node replaced = m_attrs[i].createAttr(m_element,m_element->docPtr());
+ m_attrs[i].free();
+ m_attrs[i].m_attrId = 0; /* "has implementation" flag */
+ m_attrs[i].m_data.attr = attr;
+ m_attrs[i].m_data.attr->ref();
+ attr->setElement(m_element);
+ m_element->parseAttribute(&m_attrs[i]);
+ m_element->attributeChanged(m_attrs[i].id());
+ // ### dispatch mutation events
+ return replaced;
+ }
+ }
+
+ // No existing attribute; add to list
+ m_attrCount++;
+ m_attrs = (AttributeImpl*)realloc(m_attrs,m_attrCount*sizeof(AttributeImpl));
+ m_attrs[m_attrCount-1].m_attrId = 0; /* "has implementation" flag */
+ m_attrs[m_attrCount-1].m_data.attr = attr;
+ m_attrs[m_attrCount-1].m_data.attr->ref();
+ attr->setElement(m_element);
+ if (id == ATTR_ID)
+ m_element->updateId(0, attr->val());
+ m_element->parseAttribute(&m_attrs[m_attrCount-1]);
+ m_element->attributeChanged(m_attrs[m_attrCount-1].id());
+ // ### dispatch mutation events
+
+ return 0;
+}
+
+NodeImpl *NamedAttrMapImpl::item ( unsigned long index ) const
+{
+ if (!m_element)
+ return 0;
+
+ if (index >= m_attrCount)
+ return 0;
+ else
+ return m_attrs[index].createAttr(m_element,m_element->docPtr());
+}
+
+unsigned long NamedAttrMapImpl::length( ) const
+{
+ if (!m_element)
+ return 0;
+
+ return m_attrCount;
+}
+
+NodeImpl::Id NamedAttrMapImpl::idAt(unsigned long index) const
+{
+ assert(index <= m_attrCount);
+ return m_attrs[index].id();
+}
+
+DOMStringImpl *NamedAttrMapImpl::valueAt(unsigned long index) const
+{
+ assert(index <= m_attrCount);
+ return m_attrs[index].val();
+}
+
+DOMStringImpl *NamedAttrMapImpl::getValue(NodeImpl::Id id, bool nsAware, DOMStringImpl* qName) const
+{
+ unsigned int mask = nsAware ? ~0L : NodeImpl_IdLocalMask;
+ id = (id & mask);
+ for (unsigned long i = 0; i < m_attrCount; i++)
+ if ((m_attrs[i].id() & mask) == id) {
+ // if we are called with a qualified name, filter out NS-aware elements with non-matching name.
+ if (qName && (namespacePart(m_attrs[i].id()) != defaultNamespace) &&
+ strcasecmp(m_attrs[i].name(), qName))
+ continue;
+ return m_attrs[i].val();
+ }
+ return 0;
+}
+
+void NamedAttrMapImpl::setValue(NodeImpl::Id id, DOMStringImpl *value, DOMStringImpl* qName,
+ DOMStringImpl *prefix, bool nsAware, bool hasNS)
+{
+ assert( !(qName && nsAware) );
+ if (!id) return;
+ // Passing in a null value here causes the attribute to be removed. This is a khtml extension
+ // (the spec does not specify what to do in this situation).
+ int exceptioncode = 0;
+ if (!value) {
+ removeNamedItem(id, nsAware, qName, exceptioncode);
+ return;
+ }
+ unsigned int mask = nsAware ? ~0L : NodeImpl_IdLocalMask;
+ NodeImpl::Id mid = (id & mask);
+
+ // Check for an existing attribute.
+ for (unsigned long i = 0; i < m_attrCount; i++) {
+ if ((m_attrs[i].id() & mask) == mid) {
+ // if we are called with a qualified name, filter out NS-aware elements with non-matching name.
+ if (qName && (namespacePart(m_attrs[i].id()) != defaultNamespace) &&
+ strcasecmp(m_attrs[i].name(), DOMString(qName)))
+ continue;
+ if (prefix)
+ m_attrs[i].attr()->setPrefix(prefix,exceptioncode);
+ m_attrs[i].setValue(value,m_element);
+ // ### dispatch mutation events
+ return;
+ }
+ }
+
+ // No existing matching attribute; add a new one
+ m_attrCount++;
+ m_attrs = (AttributeImpl*)realloc(m_attrs,m_attrCount*sizeof(AttributeImpl));
+ if (!nsAware) {
+ // Called from setAttribute()... we only have a name
+ m_attrs[m_attrCount-1].m_attrId = id;
+ m_attrs[m_attrCount-1].m_data.value = value;
+ m_attrs[m_attrCount-1].m_data.value->ref();
+ }
+ else {
+ // Called from setAttributeNS()... need to create a full AttrImpl here
+ if(!m_element)
+ return;
+ m_attrs[m_attrCount-1].m_data.attr = new AttrImpl(m_element,m_element->docPtr(),
+ id,
+ value,
+ prefix);
+ m_attrs[m_attrCount-1].m_attrId = 0; /* "has implementation" flag */
+ m_attrs[m_attrCount-1].m_data.attr->ref();
+ m_attrs[m_attrCount-1].m_data.attr->setHTMLCompat( !hasNS &&
+ m_element->getDocument()->htmlMode() != DocumentImpl::XHtml );
+ }
+ if (m_element) {
+ if (id == ATTR_ID)
+ m_element->updateId(0, value);
+ m_element->parseAttribute(&m_attrs[m_attrCount-1]);
+ m_element->attributeChanged(m_attrs[m_attrCount-1].id());
+ }
+ // ### dispatch mutation events
+}
+
+Attr NamedAttrMapImpl::removeAttr(AttrImpl *attr)
+{
+ for (unsigned long i = 0; i < m_attrCount; i++) {
+ if (m_attrs[i].attr() == attr) {
+ NodeImpl::Id id = m_attrs[i].id();
+ if (id == ATTR_ID)
+ m_element->updateId(attr->val(), 0);
+ Node removed(m_attrs[i].createAttr(m_element,m_element->docPtr()));
+ m_attrs[i].free();
+ memmove(m_attrs+i,m_attrs+i+1,(m_attrCount-i-1)*sizeof(AttributeImpl));
+ m_attrCount--;
+ m_attrs = (AttributeImpl*)realloc(m_attrs,m_attrCount*sizeof(AttributeImpl));
+ m_element->parseAttribute(id,0);
+ m_element->attributeChanged(id);
+ // ### dispatch mutation events
+ return removed;
+ }
+ }
+
+ return 0;
+}
+
+NodeImpl::Id NamedAttrMapImpl::mapId(DOMStringImpl* namespaceURI,
+ DOMStringImpl* localName, bool readonly)
+{
+ if (!m_element)
+ return 0;
+
+ return m_element->getDocument()->getId(NodeImpl::AttributeId, namespaceURI, 0, localName, readonly,
+ true /*lookupHTML*/);
+}
+
+void NamedAttrMapImpl::copyAttributes(NamedAttrMapImpl *other)
+{
+ assert(m_element);
+ unsigned long i;
+ for (i = 0; i < m_attrCount; i++) {
+ if (m_attrs[i].id() == ATTR_ID)
+ m_element->updateId(m_attrs[i].val(), 0);
+ m_attrs[i].free();
+ }
+ m_attrCount = other->m_attrCount;
+ m_attrs = (AttributeImpl*)realloc(m_attrs,m_attrCount*sizeof(AttributeImpl));
+ for (i = 0; i < m_attrCount; i++) {
+ m_attrs[i].m_attrId = other->m_attrs[i].m_attrId;
+ if (m_attrs[i].m_attrId) {
+ m_attrs[i].m_data.value = other->m_attrs[i].m_data.value;
+ m_attrs[i].m_data.value->ref();
+ }
+ else {
+ m_attrs[i].m_data.attr = static_cast<AttrImpl*>(other->m_attrs[i].m_data.attr->cloneNode(true));
+ m_attrs[i].m_data.attr->ref();
+ m_attrs[i].m_data.attr->setElement(m_element);
+ }
+ if (m_attrs[i].id() == ATTR_ID)
+ m_element->updateId(0, m_attrs[i].val());
+ m_element->parseAttribute(&m_attrs[i]);
+ m_element->attributeChanged(m_attrs[i].id());
+ }
+}
+
+void NamedAttrMapImpl::setElement(ElementImpl *element)
+{
+ assert(!m_element);
+ m_element = element;
+
+ for (unsigned long i = 0; i < m_attrCount; i++)
+ if (m_attrs[i].attr())
+ m_attrs[i].attr()->setElement(element);
+}
+
+void NamedAttrMapImpl::detachFromElement()
+{
+ // This makes the map invalid; nothing can really be done with it now since it's not
+ // associated with an element. But we have to keep it around in memory in case there
+ // are still references to it.
+ m_element = 0;
+ for (unsigned long i = 0; i < m_attrCount; i++)
+ m_attrs[i].free();
+ free(m_attrs);
+ m_attrs = 0;
+ m_attrCount = 0;
+}
diff --git a/khtml/xml/dom_elementimpl.h b/khtml/xml/dom_elementimpl.h
new file mode 100644
index 000000000..1bc5148d5
--- /dev/null
+++ b/khtml/xml/dom_elementimpl.h
@@ -0,0 +1,392 @@
+/*
+ * 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 Peter Kelly (pmk@post.com)
+ * (C) 2001 Dirk Mueller (mueller@kde.org)
+ * (C) 2003 Apple Computer, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+#ifndef _DOM_ELEMENTImpl_h_
+#define _DOM_ELEMENTImpl_h_
+
+#include "dom_nodeimpl.h"
+#include "dom/dom_exception.h"
+#include "dom/dom_element.h"
+#include "xml/dom_stringimpl.h"
+#include "misc/shared.h"
+
+namespace khtml {
+ class CSSStyleSelector;
+}
+
+namespace DOM {
+
+class ElementImpl;
+class DocumentImpl;
+class NamedAttrMapImpl;
+
+// Attr can have Text and EntityReference children
+// therefore it has to be a fullblown Node. The plan
+// is to dynamically allocate a textchild and store the
+// resulting nodevalue in the AttributeImpl upon
+// destruction. however, this is not yet implemented.
+class AttrImpl : public NodeBaseImpl
+{
+ friend class ElementImpl;
+ friend class NamedAttrMapImpl;
+
+public:
+ AttrImpl(ElementImpl* element, DocumentImpl* docPtr, NodeImpl::Id attrId,
+ DOMStringImpl *value, DOMStringImpl *prefix = 0);
+ ~AttrImpl();
+
+private:
+ AttrImpl(const AttrImpl &other);
+ AttrImpl &operator = (const AttrImpl &other);
+public:
+
+ // DOM methods & attributes for Attr
+ bool specified() const { return m_specified; }
+ ElementImpl* ownerElement() const { return m_element; }
+ void setOwnerElement( ElementImpl* impl ) { m_element = impl; }
+ DOMString name() const;
+
+ //DOMString value() const;
+ void setValue( const DOMString &v, int &exceptioncode );
+
+ // DOM methods overridden from parent classes
+ virtual DOMString nodeName() const;
+ virtual unsigned short nodeType() const;
+ virtual DOMString prefix() const;
+ virtual void setPrefix(const DOMString &_prefix, int &exceptioncode );
+ virtual DOMString namespaceURI() const;
+ virtual DOMString localName() const;
+
+ virtual DOMString nodeValue() const;
+ virtual void setNodeValue( const DOMString &, int &exceptioncode );
+ virtual NodeImpl *cloneNode ( bool deep );
+
+ virtual DOMStringImpl* textContent() const;
+ virtual void setTextContent( const DOMString &text, int& exceptioncode );
+
+
+ // Other methods (not part of DOM)
+ virtual bool isAttributeNode() const { return true; }
+ virtual bool childAllowed( NodeImpl *newChild );
+ virtual bool childTypeAllowed( unsigned short type );
+ virtual NodeImpl::Id id() const { return m_attrId; }
+
+ virtual DOMString toString() const;
+
+ void setElement(ElementImpl *element);
+ DOMStringImpl *val() { return m_value; }
+
+protected:
+ ElementImpl *m_element;
+ NodeImpl::Id m_attrId;
+ DOMStringImpl *m_value;
+ DOMStringImpl *m_prefix;
+ DOMStringImpl *m_localName;
+};
+
+// Mini version of AttrImpl internal to NamedAttrMapImpl.
+// Stores either the id and value of an attribute
+// (in the case of m_attrId != 0), or a pointer to an AttrImpl (if m_attrId == 0)
+// The latter case only happens when the Attr node is requested by some DOM
+// code or is an XML attribute.
+// In most cases the id and value is all we need to store, which is more
+// memory efficient.
+struct AttributeImpl
+{
+ NodeImpl::Id id() const { return m_attrId ? m_attrId : m_data.attr->id(); }
+ DOMStringImpl *val() const { return m_attrId ? m_data.value : m_data.attr->val(); }
+ DOMString value() const { return val(); }
+ AttrImpl *attr() const { return m_attrId ? 0 : m_data.attr; }
+ DOMString namespaceURI() { return m_attrId ? DOMString() : m_data.attr->namespaceURI(); }
+ DOMString prefix() { return m_attrId ? DOMString() : m_data.attr->prefix(); }
+ DOMString localName() { return m_attrId ? DOMString() : m_data.attr->localName(); }
+ DOMString name() { return m_attrId ? DOMString() : m_data.attr->name(); }
+
+ void setValue(DOMStringImpl *value, ElementImpl *element);
+ AttrImpl *createAttr(ElementImpl *element, DocumentImpl *docPtr);
+ void free();
+
+ NodeImpl::Id m_attrId;
+ union {
+ DOMStringImpl *value;
+ AttrImpl *attr;
+ } m_data;
+};
+
+class ElementImpl : public NodeBaseImpl
+{
+ friend class DocumentImpl;
+ friend class NamedAttrMapImpl;
+ friend class AttrImpl;
+ friend class NodeImpl;
+ friend class khtml::CSSStyleSelector;
+public:
+ ElementImpl(DocumentImpl *doc);
+ ~ElementImpl();
+
+ DOMString getAttribute( NodeImpl::Id id, bool nsAware = 0, const DOMString& qName = DOMString() ) const;
+ DOMStringImpl* getAttributeImpl( NodeImpl::Id id, bool nsAware = 0, DOMStringImpl* qName = 0 ) const;
+ void setAttribute( NodeImpl::Id id, const DOMString &value, const DOMString &qName,
+ int &exceptioncode );
+ void setAttributeNS( const DOMString &namespaceURI, const DOMString &qualifiedName,
+ const DOMString& value, int &exceptioncode );
+ virtual DOMString prefix() const;
+ void setPrefix(const DOMString &_prefix, int &exceptioncode );
+ virtual DOMString namespaceURI() const;
+
+ // DOM methods overridden from parent classes
+ virtual DOMString tagName() const = 0;
+ virtual unsigned short nodeType() const;
+ virtual NodeImpl *cloneNode ( bool deep );
+ virtual DOMString nodeName() const;
+ virtual NodeImpl::Id id() const = 0;
+ virtual bool isElementNode() const { return true; }
+ virtual void insertedIntoDocument();
+ virtual void removedFromDocument();
+
+ // convenience methods which ignore exceptions
+ void setAttribute (NodeImpl::Id id, const DOMString &value);
+
+ NamedAttrMapImpl* attributes(bool readonly = false) const
+ {
+ if (!readonly && !namedAttrMap) createAttributeMap();
+ return namedAttrMap;
+ }
+
+ //This is always called, whenever an attribute changed
+ virtual void parseAttribute(AttributeImpl *) {}
+ void parseAttribute(NodeImpl::Id attrId, DOMStringImpl *value) {
+ AttributeImpl aimpl;
+ aimpl.m_attrId = attrId;
+ aimpl.m_data.value = value;
+ parseAttribute(&aimpl);
+ }
+
+ // not part of the DOM
+ void setAttributeMap ( NamedAttrMapImpl* list );
+
+ // State of the element.
+ virtual QString state() { return QString::null; }
+
+ virtual void attach();
+ virtual void close();
+ virtual void detach();
+ virtual void structureChanged();
+ virtual void backwardsStructureChanged();
+ virtual void attributeChanged(NodeImpl::Id attrId);
+
+ virtual khtml::RenderStyle *styleForRenderer(khtml::RenderObject *parent);
+ virtual khtml::RenderObject *createRenderer(khtml::RenderArena *, khtml::RenderStyle *);
+ virtual void recalcStyle( StyleChange = NoChange );
+
+ virtual void mouseEventHandler( MouseEvent* /*ev*/, bool /*inside*/ ) {}
+ virtual bool isFocusable() const;
+ virtual bool childAllowed( NodeImpl *newChild );
+ virtual bool childTypeAllowed( unsigned short type );
+
+ DOM::CSSStyleDeclarationImpl *styleRules() {
+ if (!m_styleDecls) createDecl();
+ return m_styleDecls;
+ }
+
+ void dispatchAttrRemovalEvent(NodeImpl::Id id, DOMStringImpl *value);
+ void dispatchAttrAdditionEvent(NodeImpl::Id id, DOMStringImpl *value);
+
+ virtual DOMString toString() const;
+ virtual DOMString selectionToString(NodeImpl *selectionStart, NodeImpl *selectionEnd, int startOffset, int endOffset, bool &found) const;
+
+ virtual bool contentEditable() const;
+ void setContentEditable(bool enabled);
+
+ void scrollIntoView(bool alignToTop);
+
+ /** Returns the opening tag and properties.
+ * Examples: '<b', '<img alt="hello" src="image.png"
+ *
+ * For security reasons, passwords are stripped out of all src= and
+ * href= tags if expandurls is turned on.
+ *
+ * @param expandurls If this is set then in the above example, it would give
+ * src="http://website.com/image.png". Note that the password
+ * is stripped out of the url.
+ *
+ * DOM::RangeImpl uses this which is why it is public.
+ */
+ DOMString openTagStartToString(bool expandurls = false) const;
+
+ void updateId(DOMStringImpl* oldId, DOMStringImpl* newId);
+ //Called when mapping from id to this node in document should be removed
+ virtual void removeId(const QString& id);
+ //Called when mapping from id to this node in document should be added
+ virtual void addId (const QString& id);
+
+protected:
+ void createAttributeMap() const;
+ void createDecl();
+ void finishCloneNode( ElementImpl *clone, bool deep );
+
+private:
+ // map of default attributes. derived element classes are responsible
+ // for setting this according to the corresponding element description
+ // in the DTD
+ virtual NamedAttrMapImpl* defaultMap() const;
+
+protected: // member variables
+ mutable NamedAttrMapImpl *namedAttrMap;
+
+ DOM::CSSStyleDeclarationImpl *m_styleDecls;
+ DOMStringImpl *m_prefix;
+};
+
+
+class XMLElementImpl : public ElementImpl
+{
+
+public:
+ XMLElementImpl(DocumentImpl *doc, NodeImpl::Id id);
+ XMLElementImpl(DocumentImpl *doc, NodeImpl::Id id, DOMStringImpl *_qualifiedName);
+ ~XMLElementImpl();
+
+ // DOM methods overridden from parent classes
+ virtual DOMString tagName() const;
+ virtual DOMString localName() const;
+ virtual NodeImpl *cloneNode ( bool deep );
+
+ // Other methods (not part of DOM)
+ virtual bool isXMLElementNode() const { return true; }
+ virtual Id id() const { return m_id; }
+
+protected:
+ Id m_id;
+};
+
+// the map of attributes of an element
+class NamedAttrMapImpl : public NamedNodeMapImpl
+{
+ friend class ElementImpl;
+public:
+ NamedAttrMapImpl(ElementImpl *element);
+ virtual ~NamedAttrMapImpl();
+
+ // DOM methods & attributes for NamedNodeMap
+ virtual NodeImpl *getNamedItem ( NodeImpl::Id id, bool nsAware = false, DOMStringImpl* qName = 0 ) const;
+ virtual Node removeNamedItem ( NodeImpl::Id id, bool nsAware, DOMStringImpl* qName, int &exceptioncode );
+ virtual Node setNamedItem ( NodeImpl* arg, bool nsAware, DOMStringImpl* qName, int &exceptioncode );
+
+ virtual NodeImpl *item ( unsigned long index ) const;
+ virtual unsigned long length( ) const;
+
+ // Other methods (not part of DOM)
+ virtual bool isReadOnly() { return false; }
+
+ AttributeImpl *attrAt(unsigned long index) const { return &m_attrs[index]; }
+ // ### replace idAt and getValueAt with attrAt
+ NodeImpl::Id idAt(unsigned long index) const;
+ DOMStringImpl *valueAt(unsigned long index) const;
+ DOMStringImpl *getValue(NodeImpl::Id id, bool nsAware = false, DOMStringImpl* qName = 0) const;
+ void setValue(NodeImpl::Id id, DOMStringImpl *value, DOMStringImpl* qName = 0,
+ DOMStringImpl *prefix = 0, bool nsAware = false, bool hasNS = false);
+ Attr removeAttr(AttrImpl *attr);
+ NodeImpl::Id mapId(DOMStringImpl* namespaceURI, DOMStringImpl* localName, bool readonly);
+ void copyAttributes(NamedAttrMapImpl *other);
+ void setElement(ElementImpl *element);
+ void detachFromElement();
+
+protected:
+ ElementImpl *m_element;
+ AttributeImpl *m_attrs;
+ unsigned long m_attrCount;
+};
+
+// ------------ inline DOM helper functions ---------------
+
+inline bool checkQualifiedName(const DOMString &qualifiedName, const DOMString &namespaceURI, int *colonPos,
+ bool nameCanBeNull, bool nameCanBeEmpty, int *pExceptioncode)
+{
+
+ // Not mentioned in spec: throw NAMESPACE_ERR if no qualifiedName supplied
+ if (!nameCanBeNull && qualifiedName.isNull()) {
+ if (pExceptioncode)
+ *pExceptioncode = DOMException::NAMESPACE_ERR;
+ return false;
+ }
+
+ // INVALID_CHARACTER_ERR: Raised if the specified qualified name contains an illegal character.
+ if (!qualifiedName.isNull() && !Element::khtmlValidQualifiedName(qualifiedName)
+ && ( !qualifiedName.isEmpty() || !nameCanBeEmpty ) ) {
+ if (pExceptioncode)
+ *pExceptioncode = DOMException::INVALID_CHARACTER_ERR;
+ return false;
+ }
+
+ // NAMESPACE_ERR:
+ // - Raised if the qualifiedName is malformed,
+ // - if the qualifiedName has a prefix and the namespaceURI is null, or
+ // - if the qualifiedName is null and the namespaceURI is different from null
+ // - if the qualifiedName has a prefix that is "xml" and the namespaceURI is different
+ // from "http://www.w3.org/XML/1998/namespace" [Namespaces].
+ int colonpos = -1;
+ uint i;
+ DOMStringImpl *qname = qualifiedName.implementation();
+ for (i = 0 ; i < qname->l ; i++) {
+ if ((*qname)[i] == ':') {
+ colonpos = i;
+ break;
+ }
+ }
+
+ if (!qualifiedName.isNull() && Element::khtmlMalformedQualifiedName(qualifiedName) ||
+ (colonpos >= 0 && namespaceURI.isNull()) ||
+ (qualifiedName.isNull() && !namespaceURI.isNull()) ||
+ (colonpos == 3 && qualifiedName[0] == 'x' && qualifiedName[1] == 'm' && qualifiedName[2] == 'l' &&
+ namespaceURI != "http://www.w3.org/XML/1998/namespace")) {
+ if (pExceptioncode)
+ *pExceptioncode = DOMException::NAMESPACE_ERR;
+ return false;
+ }
+ if(colonPos)
+ *colonPos = colonpos;
+ return true;
+}
+
+inline void splitPrefixLocalName(DOMStringImpl *qualifiedName, DOMString &prefix, DOMString &localName, int colonPos = -2)
+{
+ if (colonPos == -2)
+ for (uint i = 0 ; i < qualifiedName->l ; ++i)
+ if (qualifiedName->s[i] == ':') {
+ colonPos = i;
+ break;
+ }
+ if (colonPos >= 0) {
+ prefix = qualifiedName->copy();
+ localName = prefix.split(colonPos+1);
+ prefix.implementation()->truncate(colonPos);
+ } else
+ localName = qualifiedName->copy();
+}
+
+} //namespace
+
+#endif
diff --git a/khtml/xml/dom_nodeimpl.cpp b/khtml/xml/dom_nodeimpl.cpp
new file mode 100644
index 000000000..692ba4394
--- /dev/null
+++ b/khtml/xml/dom_nodeimpl.cpp
@@ -0,0 +1,2068 @@
+/*
+ * This file is part of the DOM implementation for KDE.
+ *
+ * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
+ * (C) 1999 Antti Koivisto (koivisto@kde.org)
+ * (C) 2001 Dirk Mueller (mueller@kde.org)
+ * (C) 2003-2006 Apple Computer, Inc.
+ * (C) 2006 Allan Sandfeld Jensen (kde@carewolf.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "dom/dom_exception.h"
+#include "misc/htmlattrs.h"
+#include "misc/htmltags.h"
+#include "xml/dom_elementimpl.h"
+#include "xml/dom_textimpl.h"
+#include "xml/dom2_eventsimpl.h"
+#include "xml/dom_docimpl.h"
+#include "xml/dom_nodeimpl.h"
+#include "xml/dom_restyler.h"
+
+#include <kglobal.h>
+#include <kdebug.h>
+
+#include "rendering/render_text.h"
+#include "rendering/render_flow.h"
+#include "rendering/render_line.h"
+
+#include "ecma/kjs_proxy.h"
+#include "khtmlview.h"
+#include "khtml_part.h"
+#include "dom_nodeimpl.h"
+
+// from khtml_caret_p.h
+namespace khtml {
+void /*KDE_NO_EXPORT*/ mapDOMPosToRenderPos(DOM::NodeImpl *node, long offset,
+ khtml::RenderObject *&r, long &r_ofs, bool &outside, bool &outsideEnd);
+}
+
+using namespace DOM;
+using namespace khtml;
+
+NodeImpl::NodeImpl(DocumentImpl *doc)
+ : m_document(doc),
+ m_previous(0),
+ m_next(0),
+ m_render(0),
+ m_tabIndex( 0 ),
+ m_hasId( false ),
+ m_attached(false),
+ m_closed(false),
+ m_changed( false ),
+ m_hasChangedChild( false ),
+ m_changedAscendentAttribute( false ),
+ m_inDocument( false ),
+ m_hasAnchor( false ),
+ m_specified( false ),
+ m_hovered( false ),
+ m_focused( false ),
+ m_active( false ),
+ m_implicit( false ),
+ m_htmlCompat( false ),
+ m_hasClassList( false ),
+ m_hasClass( false )
+{
+}
+
+NodeImpl::~NodeImpl()
+{
+ if (m_render)
+ detach();
+ if (m_previous)
+ m_previous->setNextSibling(0);
+ if (m_next)
+ m_next->setPreviousSibling(0);
+}
+
+DOMString NodeImpl::nodeValue() const
+{
+ return DOMString();
+}
+
+void NodeImpl::setNodeValue( const DOMString &/*_nodeValue*/, int &/*exceptioncode*/ )
+{
+ // by default nodeValue is null, so setting it has no effect
+ // don't throw NO_MODIFICATION_ALLOWED_ERR from here, DOMTS-Core-Level1's hc_nodevalue03
+ // (createEntityReference().setNodeValue())) says it would be wrong.
+ // This must be done by subclasses instead.
+}
+
+DOMString NodeImpl::nodeName() const
+{
+ return DOMString();
+}
+
+unsigned short NodeImpl::nodeType() const
+{
+ return 0;
+}
+
+NodeListImpl *NodeImpl::childNodes()
+{
+ return new ChildNodeListImpl(this);
+}
+
+NodeImpl *NodeImpl::firstChild() const
+{
+ return 0;
+}
+
+NodeImpl *NodeImpl::lastChild() const
+{
+ return 0;
+}
+
+NodeImpl *NodeImpl::insertBefore( NodeImpl *, NodeImpl *, int &exceptioncode )
+{
+ exceptioncode = DOMException::HIERARCHY_REQUEST_ERR;
+ return 0;
+}
+
+void NodeImpl::replaceChild( NodeImpl *, NodeImpl *, int &exceptioncode )
+{
+ exceptioncode = DOMException::HIERARCHY_REQUEST_ERR;
+}
+
+void NodeImpl::removeChild( NodeImpl *, int &exceptioncode )
+{
+ exceptioncode = DOMException::NOT_FOUND_ERR;
+}
+
+NodeImpl *NodeImpl::appendChild( NodeImpl *, int &exceptioncode )
+{
+ exceptioncode = DOMException::HIERARCHY_REQUEST_ERR;
+ return 0;
+}
+
+bool NodeImpl::hasChildNodes( ) const
+{
+ return false;
+}
+
+void NodeImpl::normalize ()
+{
+ // ### normalize attributes? (when we store attributes using child nodes)
+ int exceptioncode = 0;
+ NodeImpl *child = firstChild();
+
+ // Recursively go through the subtree beneath us, normalizing all nodes. In the case
+ // where there are two adjacent text nodes, they are merged together
+ while (child) {
+ NodeImpl *nextChild = child->nextSibling();
+
+ if (nextChild && child->nodeType() == Node::TEXT_NODE && nextChild->nodeType() == Node::TEXT_NODE) {
+ // Current child and the next one are both text nodes... merge them
+ TextImpl *currentText = static_cast<TextImpl*>(child);
+ TextImpl *nextText = static_cast<TextImpl*>(nextChild);
+
+ currentText->appendData(nextText->data(),exceptioncode);
+ if (exceptioncode)
+ return;
+
+ removeChild(nextChild,exceptioncode);
+ if (exceptioncode)
+ return;
+ }
+ else {
+ child->normalize();
+ child = nextChild;
+ }
+ }
+}
+
+DOMString NodeImpl::prefix() const
+{
+ // For nodes other than elements and attributes, the prefix is always null
+ return DOMString();
+}
+
+DOMString NodeImpl::namespaceURI() const
+{
+ return DOMString();
+}
+
+void NodeImpl::setPrefix(const DOMString &/*_prefix*/, int &exceptioncode )
+{
+ // The spec says that for nodes other than elements and attributes, prefix is always null.
+ // It does not say what to do when the user tries to set the prefix on another type of
+ // node, however mozilla throws a NAMESPACE_ERR exception
+ exceptioncode = DOMException::NAMESPACE_ERR;
+}
+
+DOMString NodeImpl::localName() const
+{
+ return DOMString();
+}
+
+void NodeImpl::setFirstChild(NodeImpl *)
+{
+}
+
+void NodeImpl::setLastChild(NodeImpl *)
+{
+}
+
+NodeImpl *NodeImpl::addChild(NodeImpl *)
+{
+ return 0;
+}
+
+void NodeImpl::getCaret(int offset, bool override, int &_x, int &_y, int &width, int &height)
+{
+ if (m_render) {
+ RenderObject *r;
+ long r_ofs;
+ bool outside, outsideEnd;
+#if 0
+kdDebug(6200) << "getCaret: node " << this << " " << nodeName().string() << " offset: " << offset << endl;
+#endif
+ mapDOMPosToRenderPos(this, offset, r, r_ofs, outside, outsideEnd);
+#if 0
+kdDebug(6200) << "getCaret: r " << r << " " << (r?r->renderName():QString::null) << " r_ofs: " << r_ofs << " outside " << outside << " outsideEnd " << outsideEnd << endl;
+#endif
+ if (r) {
+ r->caretPos(r_ofs, override*RenderObject::CFOverride
+ + outside*RenderObject::CFOutside
+ + outsideEnd*RenderObject::CFOutsideEnd, _x, _y, width, height);
+ } else
+ _x = _y = height = -1, width = 1;
+ } else _x = _y = height = -1, width = 1;
+}
+
+QRect NodeImpl::getRect() const
+{
+ int _x, _y;
+ if(m_render && m_render->absolutePosition(_x, _y))
+ return QRect( _x + m_render->inlineXPos(), _y + m_render->inlineYPos(),
+ m_render->width(), m_render->height() + renderer()->borderTopExtra() + renderer()->borderBottomExtra() );
+
+ return QRect();
+}
+
+void NodeImpl::setChanged(bool b)
+{
+ if (b && !attached()) // changed compared to what?
+ return;
+
+ m_changed = b;
+ if ( b ) {
+ NodeImpl *p = parentNode();
+ while ( p ) {
+ p->setHasChangedChild( true );
+ p = p->parentNode();
+ }
+ getDocument()->setDocumentChanged();
+ }
+}
+
+bool NodeImpl::isInline() const
+{
+ if (m_render) return m_render->style()->display() == khtml::INLINE;
+ return !isElementNode();
+}
+
+
+unsigned long NodeImpl::nodeIndex() const
+{
+ NodeImpl *_tempNode = previousSibling();
+ unsigned long count=0;
+ for( count=0; _tempNode; count++ )
+ _tempNode = _tempNode->previousSibling();
+ return count;
+}
+
+void NodeImpl::addEventListener(int id, EventListener *listener, const bool useCapture)
+{
+ switch (id) {
+ case EventImpl::DOMSUBTREEMODIFIED_EVENT:
+ getDocument()->addListenerType(DocumentImpl::DOMSUBTREEMODIFIED_LISTENER);
+ break;
+ case EventImpl::DOMNODEINSERTED_EVENT:
+ getDocument()->addListenerType(DocumentImpl::DOMNODEINSERTED_LISTENER);
+ break;
+ case EventImpl::DOMNODEREMOVED_EVENT:
+ getDocument()->addListenerType(DocumentImpl::DOMNODEREMOVED_LISTENER);
+ break;
+ case EventImpl::DOMNODEREMOVEDFROMDOCUMENT_EVENT:
+ getDocument()->addListenerType(DocumentImpl::DOMNODEREMOVEDFROMDOCUMENT_LISTENER);
+ break;
+ case EventImpl::DOMNODEINSERTEDINTODOCUMENT_EVENT:
+ getDocument()->addListenerType(DocumentImpl::DOMNODEINSERTEDINTODOCUMENT_LISTENER);
+ break;
+ case EventImpl::DOMATTRMODIFIED_EVENT:
+ getDocument()->addListenerType(DocumentImpl::DOMATTRMODIFIED_LISTENER);
+ break;
+ case EventImpl::DOMCHARACTERDATAMODIFIED_EVENT:
+ getDocument()->addListenerType(DocumentImpl::DOMCHARACTERDATAMODIFIED_LISTENER);
+ break;
+ default:
+ break;
+ }
+
+ m_regdListeners.addEventListener(id, listener, useCapture);
+}
+
+void NodeImpl::removeEventListener(int id, EventListener *listener, bool useCapture)
+{
+ m_regdListeners.removeEventListener(id, listener, useCapture);
+}
+
+void NodeImpl::setHTMLEventListener(int id, EventListener *listener)
+{
+ m_regdListeners.setHTMLEventListener(id, listener);
+}
+
+EventListener *NodeImpl::getHTMLEventListener(int id)
+{
+ return m_regdListeners.getHTMLEventListener(id);
+}
+
+void NodeImpl::dispatchEvent(EventImpl *evt, int &exceptioncode, bool tempEvent)
+{
+ evt->setTarget(this);
+
+ // Since event handling code could cause this object to be deleted, grab a reference to the view now
+ KHTMLView *view = getDocument()->view();
+
+ dispatchGenericEvent( evt, exceptioncode );
+
+ // If tempEvent is true, this means that the DOM implementation will not be storing a reference to the event, i.e.
+ // there is no way to retrieve it from javascript if a script does not already have a reference to it in a variable.
+ // So there is no need for the interpreter to keep the event in its cache
+ if (tempEvent && view && view->part() && view->part()->jScript())
+ view->part()->jScript()->finishedWithEvent(evt);
+}
+
+void NodeImpl::dispatchGenericEvent( EventImpl *evt, int &/*exceptioncode */)
+{
+ // ### check that type specified
+
+ // work out what nodes to send event to
+ QPtrList<NodeImpl> nodeChain;
+ NodeImpl *n;
+ for (n = this; n; n = n->parentNode()) {
+ n->ref();
+ nodeChain.prepend(n);
+ }
+
+ // trigger any capturing event handlers on our way down
+ evt->setEventPhase(Event::CAPTURING_PHASE);
+ QPtrListIterator<NodeImpl> it(nodeChain);
+ for (; it.current() && it.current() != this && !evt->propagationStopped(); ++it) {
+ evt->setCurrentTarget(it.current());
+ it.current()->handleLocalEvents(evt,true);
+ }
+
+ // dispatch to the actual target node
+ it.toLast();
+ NodeImpl* propagationSentinel = 0;
+ if (!evt->propagationStopped()) {
+ evt->setEventPhase(Event::AT_TARGET);
+ evt->setCurrentTarget(it.current());
+ it.current()->handleLocalEvents(evt, true);
+ if (!evt->propagationStopped())
+ it.current()->handleLocalEvents(evt,false);
+ else
+ propagationSentinel = it.current();
+ }
+ --it;
+
+ if (evt->bubbles()) {
+ evt->setEventPhase(Event::BUBBLING_PHASE);
+ for (; it.current() && !evt->propagationStopped(); --it) {
+ if (evt->propagationStopped()) propagationSentinel = it.current();
+ evt->setCurrentTarget(it.current());
+ it.current()->handleLocalEvents(evt,false);
+ }
+
+ // now we call all default event handlers (this is not part of DOM - it is internal to khtml)
+ evt->setCurrentTarget(0);
+ evt->setEventPhase(0); // I guess this is correct, the spec does not seem to say
+ for (it.toLast(); it.current() && it.current() != propagationSentinel &&
+ !evt->defaultPrevented() && !evt->defaultHandled(); --it)
+ it.current()->defaultEventHandler(evt);
+
+ if (evt->id() == EventImpl::CLICK_EVENT && !evt->defaultPrevented() &&
+ static_cast<MouseEventImpl*>( evt )->button() == 0) // LMB click
+ dispatchUIEvent(EventImpl::DOMACTIVATE_EVENT, static_cast<UIEventImpl*>(evt)->detail());
+ }
+
+ // copy this over into a local variable, as the following deref() calls might cause this to be deleted.
+ DocumentImpl *doc = m_document.get();
+ doc->ref();
+
+ // deref all nodes in chain
+ it.toFirst();
+ for (; it.current(); ++it)
+ it.current()->deref(); // this may delete us
+
+ DocumentImpl::updateDocumentsRendering();
+ doc->deref();
+}
+
+bool NodeImpl::dispatchHTMLEvent(int _id, bool canBubbleArg, bool cancelableArg)
+{
+ int exceptioncode = 0;
+ EventImpl* const evt = new EventImpl(static_cast<EventImpl::EventId>(_id),canBubbleArg,cancelableArg);
+ evt->ref();
+ dispatchEvent(evt,exceptioncode,true);
+ bool ret = !evt->defaultPrevented();
+ evt->deref();
+ return ret;
+}
+
+void NodeImpl::dispatchWindowEvent(int _id, bool canBubbleArg, bool cancelableArg)
+{
+ int exceptioncode = 0;
+ EventImpl* const evt = new EventImpl(static_cast<EventImpl::EventId>(_id),canBubbleArg,cancelableArg);
+ evt->setTarget( 0 );
+ evt->ref();
+ DocumentImpl *doc = getDocument();
+ doc->ref();
+ dispatchGenericEvent( evt, exceptioncode );
+ if (!evt->defaultPrevented() && doc)
+ doc->defaultEventHandler(evt);
+
+ if (_id == EventImpl::LOAD_EVENT && !evt->propagationStopped() && doc) {
+ // For onload events, send them to the enclosing frame only.
+ // This is a DOM extension and is independent of bubbling/capturing rules of
+ // the DOM. You send the event only to the enclosing frame. It does not
+ // bubble through the parent document.
+ DOM::ElementImpl* elt = doc->ownerElement();
+ if (elt && (elt->getDocument()->domain().isNull() ||
+ elt->getDocument()->domain() == doc->domain())) {
+ // We also do a security check, since we don't want to allow the enclosing
+ // iframe to see loads of child documents in other domains.
+ evt->setCurrentTarget(elt);
+
+ // Capturing first.
+ elt->handleLocalEvents(evt,true);
+
+ // Bubbling second.
+ if (!evt->propagationStopped())
+ elt->handleLocalEvents(evt,false);
+ }
+ }
+
+ doc->deref();
+ evt->deref();
+}
+
+void NodeImpl::dispatchMouseEvent(QMouseEvent *_mouse, int overrideId, int overrideDetail)
+{
+ bool cancelable = true;
+ int detail = overrideDetail; // defaults to 0
+ EventImpl::EventId evtId = EventImpl::UNKNOWN_EVENT;
+ if (overrideId) {
+ evtId = static_cast<EventImpl::EventId>(overrideId);
+ }
+ else {
+ switch (_mouse->type()) {
+ case QEvent::MouseButtonPress:
+ evtId = EventImpl::MOUSEDOWN_EVENT;
+ break;
+ case QEvent::MouseButtonRelease:
+ evtId = EventImpl::MOUSEUP_EVENT;
+ break;
+ case QEvent::MouseButtonDblClick:
+ evtId = EventImpl::CLICK_EVENT;
+ detail = 1; // ### support for multiple double clicks
+ break;
+ case QEvent::MouseMove:
+ evtId = EventImpl::MOUSEMOVE_EVENT;
+ cancelable = false;
+ break;
+ default:
+ break;
+ }
+ }
+ if (evtId == EventImpl::UNKNOWN_EVENT)
+ return; // shouldn't happen
+
+
+ int exceptioncode = 0;
+ int pageX = _mouse->x();
+ int pageY = _mouse->y();
+ int clientX = pageX;
+ int clientY = pageY;
+ if ( getDocument()->view() )
+ getDocument()->view()->viewportToContents( clientX, clientY, pageX, pageY );
+
+ int screenX = _mouse->globalX();
+ int screenY = _mouse->globalY();
+
+ int button = -1;
+ switch (_mouse->button()) {
+ case Qt::LeftButton:
+ button = 0;
+ break;
+ case Qt::MidButton:
+ button = 1;
+ break;
+ case Qt::RightButton:
+ button = 2;
+ break;
+ default:
+ break;
+ }
+ bool ctrlKey = (_mouse->state() & Qt::ControlButton);
+ bool altKey = (_mouse->state() & Qt::AltButton);
+ bool shiftKey = (_mouse->state() & Qt::ShiftButton);
+ bool metaKey = false; // ### qt support?
+
+ EventImpl* const evt = new MouseEventImpl(evtId,true,cancelable,getDocument()->defaultView(),
+ detail,screenX,screenY,clientX,clientY,pageX,pageY,ctrlKey,altKey,shiftKey,metaKey,
+ button,0);
+ evt->ref();
+ dispatchEvent(evt,exceptioncode,true);
+ evt->deref();
+}
+
+void NodeImpl::dispatchUIEvent(int _id, int detail)
+{
+ assert (!( (_id != EventImpl::DOMFOCUSIN_EVENT &&
+ _id != EventImpl::DOMFOCUSOUT_EVENT &&
+ _id != EventImpl::DOMACTIVATE_EVENT)));
+
+ bool cancelable = false;
+ if (_id == EventImpl::DOMACTIVATE_EVENT)
+ cancelable = true;
+
+ int exceptioncode = 0;
+ UIEventImpl* const evt = new UIEventImpl(static_cast<EventImpl::EventId>(_id),true,
+ cancelable,getDocument()->defaultView(),detail);
+ evt->ref();
+ dispatchEvent(evt,exceptioncode,true);
+ evt->deref();
+}
+
+void NodeImpl::dispatchSubtreeModifiedEvent()
+{
+ childrenChanged();
+ getDocument()->incDOMTreeVersion();
+ if (!getDocument()->hasListenerType(DocumentImpl::DOMSUBTREEMODIFIED_LISTENER))
+ return;
+ int exceptioncode = 0;
+ MutationEventImpl* const evt = new MutationEventImpl(EventImpl::DOMSUBTREEMODIFIED_EVENT,true,
+ false,0,DOMString(),DOMString(),DOMString(),0);
+ evt->ref();
+ dispatchEvent(evt,exceptioncode,true);
+ evt->deref();
+}
+
+bool NodeImpl::dispatchKeyEvent(QKeyEvent *key, bool keypress)
+{
+ int exceptioncode = 0;
+ //kdDebug(6010) << "DOM::NodeImpl: dispatching keyboard event" << endl;
+ EventImpl* keyEventImpl;
+ if (keypress)
+ keyEventImpl = new TextEventImpl(key, getDocument()->defaultView());
+ else
+ keyEventImpl = new KeyboardEventImpl(key, getDocument()->defaultView());
+ keyEventImpl->ref();
+ dispatchEvent(keyEventImpl,exceptioncode,true);
+ bool r = keyEventImpl->defaultHandled() || keyEventImpl->defaultPrevented();
+ keyEventImpl->deref();
+ return r;
+}
+
+void NodeImpl::handleLocalEvents(EventImpl *evt, bool useCapture)
+{
+ if (!m_regdListeners.listeners)
+ return;
+
+ Event ev = evt;
+ // removeEventListener (e.g. called from a JS event listener) might
+ // invalidate the item after the current iterator (which "it" is pointing to).
+ // So we make a copy of the list.
+ QValueList<RegisteredEventListener> listeners = *m_regdListeners.listeners;
+ QValueList<RegisteredEventListener>::iterator it;
+ for (it = listeners.begin(); it != listeners.end(); ++it) {
+ //Check whether this got removed...KDE4: use Java-style iterators
+ if (!m_regdListeners.stillContainsListener(*it))
+ continue;
+
+ RegisteredEventListener& current = (*it);
+ if (current.id == evt->id() && current.useCapture == useCapture)
+ current.listener->handleEvent(ev);
+
+ // ECMA legacy hack
+ if (current.useCapture == useCapture && evt->id() == EventImpl::CLICK_EVENT) {
+ MouseEventImpl* me = static_cast<MouseEventImpl*>(evt);
+ if (me->button() == 0) {
+ // To find whether to call onclick or ondblclick, we can't
+ // * use me->detail(), it's 2 when clicking twice w/o moving, even very slowly
+ // * use me->qEvent(), it's not available when using initMouseEvent/dispatchEvent
+ // So we currently store a bool in MouseEventImpl. If anyone needs to trigger
+ // dblclicks from the DOM API, we'll need a timer here (well in the doc).
+ if ( ( !me->isDoubleClick() && current.id == EventImpl::KHTML_ECMA_CLICK_EVENT) ||
+ ( me->isDoubleClick() && current.id == EventImpl::KHTML_ECMA_DBLCLICK_EVENT) )
+ current.listener->handleEvent(ev);
+ }
+ }
+ }
+}
+
+void NodeImpl::defaultEventHandler(EventImpl *)
+{
+}
+
+unsigned long NodeImpl::childNodeCount()
+{
+ return 0;
+}
+
+NodeImpl *NodeImpl::childNode(unsigned long /*index*/)
+{
+ return 0;
+}
+
+NodeImpl *NodeImpl::traverseNextNode(NodeImpl *stayWithin) const
+{
+ if (firstChild() || stayWithin == this)
+ return firstChild();
+ else if (nextSibling())
+ return nextSibling();
+ else {
+ const NodeImpl *n = this;
+ while (n && !n->nextSibling() && (!stayWithin || n->parentNode() != stayWithin))
+ n = n->parentNode();
+ if (n)
+ return n->nextSibling();
+ }
+ return 0;
+}
+
+NodeImpl *NodeImpl::traversePreviousNode() const
+{
+ if (previousSibling()) {
+ NodeImpl *n = previousSibling();
+ while (n->lastChild())
+ n = n->lastChild();
+ return n;
+ }
+ else if (parentNode()) {
+ return parentNode();
+ }
+ else {
+ return 0;
+ }
+}
+
+void NodeImpl::checkSetPrefix(const DOMString &_prefix, int &exceptioncode)
+{
+ // Perform error checking as required by spec for setting Node.prefix. Used by
+ // ElementImpl::setPrefix() and AttrImpl::setPrefix()
+
+ // INVALID_CHARACTER_ERR: Raised if the specified prefix contains an illegal character.
+ if (!Element::khtmlValidPrefix(_prefix)) {
+ exceptioncode = DOMException::INVALID_CHARACTER_ERR;
+ return;
+ }
+
+ // NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly.
+ if (isReadOnly()) {
+ exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR;
+ return;
+ }
+
+ // NAMESPACE_ERR: - Raised if the specified prefix is malformed
+ // - if the namespaceURI of this node is null,
+ // - if the specified prefix is "xml" and the namespaceURI of this node is different from
+ // "http://www.w3.org/XML/1998/namespace",
+ // - if this node is an attribute and the specified prefix is "xmlns" and
+ // the namespaceURI of this node is different from "http://www.w3.org/2000/xmlns/",
+ // - or if this node is an attribute and the qualifiedName of this node is "xmlns" [Namespaces].
+ if (Element::khtmlMalformedPrefix(_prefix) || (namespacePart(id()) == defaultNamespace && id() > ID_LAST_TAG) ||
+ (_prefix == "xml" && namespaceURI() != "http://www.w3.org/XML/1998/namespace")) {
+ exceptioncode = DOMException::NAMESPACE_ERR;
+ return;
+ }
+}
+
+void NodeImpl::checkAddChild(NodeImpl *newChild, int &exceptioncode)
+{
+ // Perform error checking as required by spec for adding a new child. Used by
+ // appendChild(), replaceChild() and insertBefore()
+
+ // Not mentioned in spec: throw NOT_FOUND_ERR if newChild is null
+ if (!newChild) {
+ exceptioncode = DOMException::NOT_FOUND_ERR;
+ return;
+ }
+
+ // NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly
+ if (isReadOnly()) {
+ exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR;
+ return;
+ }
+
+ // WRONG_DOCUMENT_ERR: Raised if newChild was created from a different document than the one that
+ // created this node.
+ // We assume that if newChild is a DocumentFragment, all children are created from the same document
+ // as the fragment itself (otherwise they could not have been added as children)
+ if (newChild->getDocument() != getDocument()) {
+ exceptioncode = DOMException::WRONG_DOCUMENT_ERR;
+ return;
+ }
+
+ // HIERARCHY_REQUEST_ERR: Raised if this node is of a type that does not allow children of the type of the
+ // newChild node, or if the node to append is one of this node's ancestors.
+
+ // check for ancestor/same node
+ if (isAncestor(newChild)) {
+ exceptioncode = DOMException::HIERARCHY_REQUEST_ERR;
+ return;
+ }
+
+ // check node allowed
+ if (newChild->nodeType() == Node::DOCUMENT_FRAGMENT_NODE) {
+ // newChild is a DocumentFragment... check all its children instead of newChild itself
+ NodeImpl *child;
+ for (child = newChild->firstChild(); child; child = child->nextSibling()) {
+ if (!childTypeAllowed(child->nodeType())) {
+ exceptioncode = DOMException::HIERARCHY_REQUEST_ERR;
+ return;
+ }
+ }
+ }
+ else {
+ // newChild is not a DocumentFragment... check if it's allowed directly
+ if(!childTypeAllowed(newChild->nodeType())) {
+ exceptioncode = DOMException::HIERARCHY_REQUEST_ERR;
+ return;
+ }
+ }
+}
+
+bool NodeImpl::isAncestor( NodeImpl *other )
+{
+ // Return true if other is the same as this node or an ancestor of it, otherwise false
+ NodeImpl *n;
+ for (n = this; n; n = n->parentNode()) {
+ if (n == other)
+ return true;
+ }
+ return false;
+}
+
+bool NodeImpl::childAllowed( NodeImpl *newChild )
+{
+ return childTypeAllowed(newChild->nodeType());
+}
+
+NodeImpl::StyleChange NodeImpl::diff( khtml::RenderStyle *s1, khtml::RenderStyle *s2 )
+{
+ // This method won't work when a style contains noninherited properties with "inherit" value.
+ StyleChange ch = NoInherit;
+
+ EDisplay display1 = s1 ? s1->display() : NONE;
+ EDisplay display2 = s2 ? s2->display() : NONE;
+ EPosition position1 = s1 ? s1->position() : STATIC;
+ EPosition position2 = s2 ? s2->position() : STATIC;
+
+ if (display1 != display2 || position1 != position2)
+ ch = Detach;
+ else if ( !s1 || !s2 )
+ ch = Inherit;
+ else if ( *s1 == *s2 )
+ ch = NoChange;
+ else if (s1->useNormalContent() != s2->useNormalContent())
+ ch = Detach; // when we add generated content all children must be detached
+ else if ( s1->inheritedNotEqual( s2 ) )
+ ch = Inherit;
+
+ // Because the first-letter implementation is so f..ked up, the easiest way
+ // to update first-letter is to remove the entire node and readd it.
+ if (ch < Detach && pseudoDiff(s1, s2, khtml::RenderStyle::FIRST_LETTER))
+ ch = Detach;
+ // If the other pseudoStyles have changed, we want to return NoInherit
+ if (ch == NoChange && pseudoDiff(s1, s2, khtml::RenderStyle::BEFORE))
+ ch = NoInherit;
+ if (ch == NoChange && pseudoDiff(s1, s2, khtml::RenderStyle::AFTER))
+ ch = NoInherit;
+ if (ch == NoChange && pseudoDiff(s1, s2, khtml::RenderStyle::MARKER))
+ ch = NoInherit;
+ if (ch == NoChange && pseudoDiff(s1, s2, khtml::RenderStyle::SELECTION))
+ ch = NoInherit;
+ if (ch == NoChange && pseudoDiff(s1, s2, khtml::RenderStyle::FIRST_LINE))
+ ch = NoInherit;
+
+ return ch;
+}
+
+bool NodeImpl::pseudoDiff( khtml::RenderStyle *s1, khtml::RenderStyle *s2, unsigned int pid)
+{
+ khtml::RenderStyle *ps1 = s1 ? s1->getPseudoStyle((khtml::RenderStyle::PseudoId)pid) : 0;
+ khtml::RenderStyle *ps2 = s2 ? s2->getPseudoStyle((khtml::RenderStyle::PseudoId)pid) : 0;
+
+ if (ps1 == ps2)
+ return false;
+ else
+ if (ps1 && ps2) {
+ if (*ps1 == *ps2)
+ return false;
+ else
+ return true;
+ }
+ else
+ return true;
+}
+
+void NodeImpl::close()
+{
+ if (m_render) m_render->close();
+ m_closed = true;
+}
+
+void NodeImpl::attach()
+{
+ assert(!attached());
+ assert(!m_render || (m_render->style() && m_render->parent()));
+ if (m_render) // set states to match node
+ {
+ if (closed()) m_render->close();
+ if (hovered()) m_render->setMouseInside();
+ }
+ getDocument()->incDOMTreeVersion();
+ m_attached = true;
+}
+
+void NodeImpl::detach()
+{
+// assert(m_attached);
+
+ if ( m_render )
+ m_render->detach();
+
+ m_render = 0;
+ getDocument()->incDOMTreeVersion();
+ m_attached = false;
+}
+
+bool NodeImpl::maintainsState()
+{
+ return false;
+}
+
+QString NodeImpl::state()
+{
+ return QString::null;
+}
+
+void NodeImpl::restoreState(const QString &/*state*/)
+{
+}
+
+void NodeImpl::insertedIntoDocument()
+{
+ setInDocument(true);
+}
+
+void NodeImpl::removedFromDocument()
+{
+ setInDocument(false);
+}
+
+void NodeImpl::childrenChanged()
+{
+ if (parentNode())
+ parentNode()->childrenChanged();
+}
+
+bool NodeImpl::isReadOnly()
+{
+ // Entity & Entity Reference nodes and their descendants are read-only
+ NodeImpl *n = this;
+ while (n) {
+ if (n->nodeType() == Node::ENTITY_NODE ||
+ n->nodeType() == Node::ENTITY_REFERENCE_NODE)
+ return true;
+ n = n->parentNode();
+ }
+ return false;
+}
+
+RenderObject * NodeImpl::previousRenderer()
+{
+ for (NodeImpl *n = previousSibling(); n; n = n->previousSibling()) {
+ if (n->renderer())
+ return n->renderer();
+ }
+ return 0;
+}
+
+RenderObject * NodeImpl::nextRenderer()
+{
+ for (NodeImpl *n = nextSibling(); n; n = n->nextSibling()) {
+ if (n->renderer())
+ return n->renderer();
+ }
+ return 0;
+}
+
+void NodeImpl::createRendererIfNeeded()
+{
+#ifdef APPLE_CHANGES
+ if (!getDocument()->shouldCreateRenderers())
+ return;
+#endif
+
+ assert(!m_render);
+
+ NodeImpl *parent = parentNode();
+ assert(parent);
+
+ RenderObject *parentRenderer = parent->renderer();
+ if (parentRenderer && parentRenderer->childAllowed()) {
+ RenderStyle *style = styleForRenderer(parentRenderer);
+ style->ref();
+ if (rendererIsNeeded(style)) {
+ m_render = createRenderer(getDocument()->renderArena(), style);
+ m_render->setStyle(style);
+ parentRenderer->addChild(m_render, nextRenderer());
+ }
+ style->deref();
+ }
+}
+
+RenderStyle *NodeImpl::styleForRenderer(RenderObject *parent)
+{
+ return parent->style();
+}
+
+bool NodeImpl::rendererIsNeeded(RenderStyle *style)
+{
+ return (getDocument()->documentElement() == this) || (style->display() != NONE);
+}
+
+RenderObject *NodeImpl::createRenderer(RenderArena* /*arena*/, RenderStyle* /*style*/)
+{
+ assert(false);
+ return 0;
+}
+
+bool NodeImpl::contentEditable() const
+{
+ RenderObject *r = renderer();
+ if (!r || !r->style()) return false;
+ return r->style()->userInput() == UI_ENABLED;
+}
+
+long NodeImpl::minOffset() const
+{
+ // Arrgh! You'd think *every* offset starts at zero, but loo,
+ // therefore we need this method
+ return renderer() ? renderer()->minOffset() : 0;
+}
+
+long NodeImpl::maxOffset() const
+{
+ return const_cast<NodeImpl *>(this)->childNodeCount();
+// return renderer() ? renderer()->maxOffset() : 1;
+}
+
+DOMStringImpl* NodeImpl::textContent() const
+{
+ QString out;
+ for (NodeImpl *child = firstChild(); child != 0; child = child->nextSibling()) {
+ short type = child->nodeType();
+ if (type != Node::COMMENT_NODE && type != Node::PROCESSING_INSTRUCTION_NODE) {
+ DOMStringImpl* kidText = child->textContent();
+ if (kidText)
+ out += QConstString(kidText->s, kidText->l).string();
+ delete kidText;
+ }
+ }
+ return new DOMStringImpl(out.unicode(), out.length());
+}
+
+//-------------------------------------------------------------------------
+
+NodeBaseImpl::~NodeBaseImpl()
+{
+ //kdDebug( 6020 ) << "NodeBaseImpl destructor" << endl;
+ // we have to tell all children, that the parent has died...
+ NodeImpl *n;
+ NodeImpl *next;
+
+ for( n = _first; n != 0; n = next ) {
+ next = n->nextSibling();
+ n->setPreviousSibling(0);
+ n->setNextSibling(0);
+ n->setParent(0);
+ if ( !n->refCount() )
+ delete n;
+ }
+}
+
+
+NodeImpl *NodeBaseImpl::firstChild() const
+{
+ return _first;
+}
+
+NodeImpl *NodeBaseImpl::lastChild() const
+{
+ return _last;
+}
+
+NodeImpl *NodeBaseImpl::insertBefore ( NodeImpl *newChild, NodeImpl *refChild, int &exceptioncode )
+{
+ exceptioncode = 0;
+
+ // insertBefore(...,null) is equivalent to appendChild()
+ if(!refChild)
+ return appendChild(newChild, exceptioncode);
+
+ // Make sure adding the new child is ok
+ checkAddChild(newChild, exceptioncode);
+ if (exceptioncode)
+ return 0;
+
+ // NOT_FOUND_ERR: Raised if refChild is not a child of this node
+ if (refChild->parentNode() != this) {
+ exceptioncode = DOMException::NOT_FOUND_ERR;
+ return 0;
+ }
+
+ bool isFragment = newChild->nodeType() == Node::DOCUMENT_FRAGMENT_NODE;
+
+ // If newChild is a DocumentFragment with no children.... there's nothing to do.
+ // Just return the document fragment
+ if (isFragment && !newChild->firstChild())
+ return newChild;
+
+ // Now actually add the child(ren)
+ NodeImpl *nextChild;
+ NodeImpl *child = isFragment ? newChild->firstChild() : newChild;
+
+ NodeImpl *prev = refChild->previousSibling();
+ if ( prev == newChild || refChild == newChild ) // nothing to do
+ return newChild;
+
+ while (child) {
+ nextChild = isFragment ? child->nextSibling() : 0;
+
+ // If child is already present in the tree, first remove it
+ NodeImpl *newParent = child->parentNode();
+
+ //...guard it in case we need to move it..
+ SharedPtr<NodeImpl> guard(child);
+
+ if(newParent)
+ newParent->removeChild( child, exceptioncode );
+ if ( exceptioncode )
+ return 0;
+
+ // Add child in the correct position
+ if (prev)
+ prev->setNextSibling(child);
+ else
+ _first = child;
+ refChild->setPreviousSibling(child);
+ child->setParent(this);
+ child->setPreviousSibling(prev);
+ child->setNextSibling(refChild);
+
+ // Add child to the rendering tree
+ // ### should we detach() it first if it's already attached?
+ if (attached() && !child->attached())
+ child->attach();
+
+ // Dispatch the mutation events
+ dispatchChildInsertedEvents(child,exceptioncode);
+
+ prev = child;
+ child = nextChild;
+ }
+
+ structureChanged();
+
+ // ### set style in case it's attached
+ dispatchSubtreeModifiedEvent();
+ return newChild;
+}
+
+void NodeBaseImpl::replaceChild ( NodeImpl *newChild, NodeImpl *oldChild, int &exceptioncode )
+{
+ exceptioncode = 0;
+
+ if ( oldChild == newChild ) // nothing to do
+ return;
+
+ // Make sure adding the new child is ok
+ checkAddChild(newChild, exceptioncode);
+ if (exceptioncode)
+ return;
+
+ // NOT_FOUND_ERR: Raised if oldChild is not a child of this node.
+ if (!oldChild || oldChild->parentNode() != this) {
+ exceptioncode = DOMException::NOT_FOUND_ERR;
+ return;
+ }
+
+ bool isFragment = newChild->nodeType() == Node::DOCUMENT_FRAGMENT_NODE;
+ NodeImpl *nextChild;
+ NodeImpl *child = isFragment ? newChild->firstChild() : newChild;
+
+
+ // Remove the old child
+ NodeImpl *prev = oldChild->previousSibling();
+ NodeImpl *next = oldChild->nextSibling();
+
+ removeChild(oldChild, exceptioncode);
+ if (exceptioncode)
+ return;
+
+ // Add the new child(ren)
+ while (child) {
+ nextChild = isFragment ? child->nextSibling() : 0;
+
+ // If child is already present in the tree, first remove it
+ NodeImpl *newParent = child->parentNode();
+ if ( child == next )
+ next = child->nextSibling();
+ if ( child == prev )
+ prev = child->previousSibling();
+ //...guard it in case we need to move it..
+ SharedPtr<NodeImpl> guard(child);
+ if(newParent)
+ newParent->removeChild( child, exceptioncode );
+ if (exceptioncode)
+ return;
+
+ // Add child in the correct position
+ if (prev) prev->setNextSibling(child);
+ if (next) next->setPreviousSibling(child);
+ if(!prev) _first = child;
+ if(!next) _last = child;
+ child->setParent(this);
+ child->setPreviousSibling(prev);
+ child->setNextSibling(next);
+
+ // Add child to the rendering tree
+ // ### should we detach() it first if it's already attached?
+ if (attached() && !child->attached())
+ child->attach();
+
+ // Dispatch the mutation events
+ dispatchChildInsertedEvents(child,exceptioncode);
+
+ prev = child;
+ child = nextChild;
+ }
+
+ structureChanged();
+
+ // ### set style in case it's attached
+ dispatchSubtreeModifiedEvent();
+ return;
+}
+
+void NodeBaseImpl::removeChild ( NodeImpl *oldChild, int &exceptioncode )
+{
+ exceptioncode = 0;
+
+ // NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly.
+ if (isReadOnly()) {
+ exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR;
+ return;
+ }
+
+ // NOT_FOUND_ERR: Raised if oldChild is not a child of this node.
+ if (!oldChild || oldChild->parentNode() != this) {
+ exceptioncode = DOMException::NOT_FOUND_ERR;
+ return;
+ }
+
+ dispatchChildRemovalEvents(oldChild,exceptioncode);
+ if (exceptioncode)
+ return;
+
+ SharedPtr<NodeImpl> memManage(oldChild); //Make sure to free if needed
+
+ // Remove from rendering tree
+ if (oldChild->attached())
+ oldChild->detach();
+
+ // Remove the child
+ NodeImpl *prev, *next;
+ prev = oldChild->previousSibling();
+ next = oldChild->nextSibling();
+
+ if(next) next->setPreviousSibling(prev);
+ if(prev) prev->setNextSibling(next);
+ if(_first == oldChild) _first = next;
+ if(_last == oldChild) _last = prev;
+
+ oldChild->setPreviousSibling(0);
+ oldChild->setNextSibling(0);
+ oldChild->setParent(0);
+
+ structureChanged();
+
+ // Dispatch post-removal mutation events
+ dispatchSubtreeModifiedEvent();
+
+ NodeImpl *p = this;
+ while (p->parentNode())
+ p = p->parentNode();
+ if (p->nodeType() == Node::DOCUMENT_NODE) {
+ for (NodeImpl *c = oldChild; c; c = c->traverseNextNode(oldChild))
+ c->removedFromDocument();
+ }
+}
+
+void NodeBaseImpl::removeChildren()
+{
+ bool inDoc = inDocument();
+ NodeImpl *n, *next;
+ for( n = _first, _first = 0; n; n = next )
+ {
+ next = n->nextSibling();
+ if (n->attached())
+ n->detach();
+ n->setPreviousSibling(0);
+ n->setNextSibling(0);
+ n->setParent(0);
+
+ if ( inDoc )
+ for ( NodeImpl* c = n; c; c = c->traverseNextNode( n ) )
+ c->removedFromDocument();
+
+ if( !n->refCount() )
+ delete n;
+ }
+ _last = 0;
+}
+
+
+NodeImpl *NodeBaseImpl::appendChild ( NodeImpl *newChild, int &exceptioncode )
+{
+ exceptioncode = 0;
+
+ // Make sure adding the new child is ok
+ checkAddChild(newChild, exceptioncode);
+ if (exceptioncode)
+ return 0;
+
+ if ( newChild == _last ) // nothing to do
+ return newChild;
+
+ bool isFragment = newChild->nodeType() == Node::DOCUMENT_FRAGMENT_NODE;
+
+ // If newChild is a DocumentFragment with no children.... there's nothing to do.
+ // Just return the document fragment
+ if (isFragment && !newChild->firstChild())
+ return newChild;
+
+ // Now actually add the child(ren)
+ NodeImpl *nextChild;
+ NodeImpl *child = isFragment ? newChild->firstChild() : newChild;
+
+ while (child) {
+ nextChild = isFragment ? child->nextSibling() : 0;
+
+ // If child is already present in the tree, first remove it
+ NodeImpl *oldParent = child->parentNode();
+ SharedPtr<NodeImpl> guard(child); //Guard in case we move it
+ if(oldParent) {
+ oldParent->removeChild( child, exceptioncode );
+ if (exceptioncode)
+ return 0;
+ }
+
+ // Append child to the end of the list
+ child->setParent(this);
+
+ if(_last)
+ {
+ child->setPreviousSibling(_last);
+ _last->setNextSibling(child);
+ _last = child;
+ }
+ else
+ {
+ _first = _last = child;
+ }
+
+ // Add child to the rendering tree
+ // ### should we detach() it first if it's already attached?
+ if (attached() && !child->attached())
+ child->attach();
+
+ // Dispatch the mutation events
+ dispatchChildInsertedEvents(child,exceptioncode);
+
+ child = nextChild;
+ }
+
+ backwardsStructureChanged();
+
+ // ### set style in case it's attached
+ dispatchSubtreeModifiedEvent();
+ return newChild;
+}
+
+bool NodeBaseImpl::hasChildNodes ( ) const
+{
+ return _first != 0;
+}
+
+// not part of the DOM
+void NodeBaseImpl::setFirstChild(NodeImpl *child)
+{
+ _first = child;
+}
+
+void NodeBaseImpl::setLastChild(NodeImpl *child)
+{
+ _last = child;
+}
+
+// check for same source document:
+bool NodeBaseImpl::checkSameDocument( NodeImpl *newChild, int &exceptioncode )
+{
+ exceptioncode = 0;
+ DocumentImpl *ownerDocThis = getDocument();
+ DocumentImpl *ownerDocNew = newChild->getDocument();
+ if(ownerDocThis != ownerDocNew) {
+ kdDebug(6010)<< "not same document, newChild = " << newChild << "document = " << getDocument() << endl;
+ exceptioncode = DOMException::WRONG_DOCUMENT_ERR;
+ return true;
+ }
+ return false;
+}
+
+// check for being child:
+bool NodeBaseImpl::checkIsChild( NodeImpl *oldChild, int &exceptioncode )
+{
+ if(!oldChild || oldChild->parentNode() != this) {
+ exceptioncode = DOMException::NOT_FOUND_ERR;
+ return true;
+ }
+ return false;
+}
+
+NodeImpl *NodeBaseImpl::addChild(NodeImpl *newChild)
+{
+ // do not add applyChanges here! This function is only used during parsing
+
+ // short check for consistency with DTD
+ if(getDocument()->isHTMLDocument() && !childAllowed(newChild))
+ {
+ //kdDebug( 6020 ) << "AddChild failed! id=" << id() << ", child->id=" << newChild->id() << endl;
+ return 0;
+ }
+
+ // just add it...
+ newChild->setParent(this);
+
+ if(_last)
+ {
+ newChild->setPreviousSibling(_last);
+ _last->setNextSibling(newChild);
+ _last = newChild;
+ }
+ else
+ {
+ _first = _last = newChild;
+ }
+
+ if (inDocument())
+ newChild->insertedIntoDocument();
+ childrenChanged();
+
+ if(newChild->nodeType() == Node::ELEMENT_NODE)
+ return newChild;
+ return this;
+}
+
+void NodeBaseImpl::attach()
+{
+ NodeImpl *child = _first;
+ while(child != 0)
+ {
+ child->attach();
+ child = child->nextSibling();
+ }
+ NodeImpl::attach();
+}
+
+void NodeBaseImpl::detach()
+{
+ NodeImpl *child = _first;
+ while(child != 0)
+ {
+ NodeImpl* prev = child;
+ child = child->nextSibling();
+ prev->detach();
+ }
+ NodeImpl::detach();
+}
+
+void NodeBaseImpl::cloneChildNodes(NodeImpl *clone)
+{
+ int exceptioncode = 0;
+ NodeImpl *n;
+ for(n = firstChild(); n && !exceptioncode; n = n->nextSibling())
+ {
+ clone->appendChild(n->cloneNode(true),exceptioncode);
+ }
+}
+
+// I don't like this way of implementing the method, but I didn't find any
+// other way. Lars
+bool NodeBaseImpl::getUpperLeftCorner(int &xPos, int &yPos) const
+{
+ if (!m_render)
+ return false;
+ RenderObject *o = m_render;
+ xPos = yPos = 0;
+ if ( !o->isInline() || o->isReplaced() ) {
+ o->absolutePosition( xPos, yPos );
+ return true;
+ }
+
+ // find the next text/image child, to get a position
+ while(o) {
+ if(o->firstChild())
+ o = o->firstChild();
+ else if(o->nextSibling())
+ o = o->nextSibling();
+ else {
+ RenderObject *next = 0;
+ while(!next) {
+ o = o->parent();
+ if(!o) return false;
+ next = o->nextSibling();
+ }
+ o = next;
+ }
+ if((o->isText() && !o->isBR()) || o->isReplaced()) {
+ o->container()->absolutePosition( xPos, yPos );
+ if (o->isText()) {
+ xPos += o->inlineXPos();
+ yPos += o->inlineYPos();
+ } else {
+ xPos += o->xPos();
+ yPos += o->yPos();
+ }
+ return true;
+ }
+ }
+ return true;
+}
+
+bool NodeBaseImpl::getLowerRightCorner(int &xPos, int &yPos) const
+{
+ if (!m_render)
+ return false;
+
+ RenderObject *o = m_render;
+ xPos = yPos = 0;
+ if (!o->isInline() || o->isReplaced())
+ {
+ o->absolutePosition( xPos, yPos );
+ xPos += o->width();
+ yPos += o->height() + o->borderTopExtra() + o->borderBottomExtra();
+ return true;
+ }
+ // find the last text/image child, to get a position
+ while(o) {
+ if(o->lastChild())
+ o = o->lastChild();
+ else if(o->previousSibling())
+ o = o->previousSibling();
+ else {
+ RenderObject *prev = 0;
+ while(!prev) {
+ o = o->parent();
+ if(!o) return false;
+ prev = o->previousSibling();
+ }
+ o = prev;
+ }
+ if((o->isText() && !o->isBR()) || o->isReplaced()) {
+ o->container()->absolutePosition(xPos, yPos);
+ if (o->isText()) {
+ xPos += o->inlineXPos() + o->width();
+ yPos += o->inlineYPos() + o->height();
+ } else {
+ xPos += o->xPos() + o->width();
+ yPos += o->yPos() + o->height();
+ }
+ return true;
+ }
+ }
+ return true;
+}
+
+void NodeBaseImpl::setFocus(bool received)
+{
+ if (m_focused == received) return;
+
+ NodeImpl::setFocus(received);
+
+ // note that we need to recalc the style
+ setChanged(); // *:focus is a default style, so we just assume personal dependency
+ if (isElementNode()) {
+ getDocument()->dynamicDomRestyler().restyleDepedent(static_cast<ElementImpl*>(this), OtherStateDependency);
+ }
+}
+
+void NodeBaseImpl::setActive(bool down)
+{
+ if (down == active()) return;
+
+ NodeImpl::setActive(down);
+
+ // note that we need to recalc the style
+ if (isElementNode())
+ getDocument()->dynamicDomRestyler().restyleDepedent(static_cast<ElementImpl*>(this), ActiveDependency);
+}
+
+void NodeBaseImpl::setHovered(bool hover)
+{
+ if (hover == hovered()) return;
+
+ NodeImpl::setHovered(hover);
+
+ // note that we need to recalc the style
+ if (isElementNode())
+ getDocument()->dynamicDomRestyler().restyleDepedent(static_cast<ElementImpl*>(this), HoverDependency);
+}
+
+unsigned long NodeBaseImpl::childNodeCount()
+{
+ unsigned long count = 0;
+ NodeImpl *n;
+ for (n = firstChild(); n; n = n->nextSibling())
+ count++;
+ return count;
+}
+
+NodeImpl *NodeBaseImpl::childNode(unsigned long index)
+{
+ unsigned long i;
+ NodeImpl *n = firstChild();
+ for (i = 0; n && i < index; i++)
+ n = n->nextSibling();
+ return n;
+}
+
+void NodeBaseImpl::dispatchChildInsertedEvents( NodeImpl *child, int &exceptioncode )
+{
+ if (getDocument()->hasListenerType(DocumentImpl::DOMNODEINSERTED_LISTENER)) {
+ MutationEventImpl* const evt = new MutationEventImpl(EventImpl::DOMNODEINSERTED_EVENT,true,false,this,DOMString(),DOMString(),DOMString(),0);
+ evt->ref();
+ child->dispatchEvent(evt,exceptioncode,true);
+ evt->deref();
+ if (exceptioncode)
+ return;
+ }
+
+ // dispatch the DOMNodeInsertedIntoDocument event to all descendants
+ bool hasInsertedListeners = getDocument()->hasListenerType(DocumentImpl::DOMNODEINSERTEDINTODOCUMENT_LISTENER);
+ NodeImpl *p = this;
+ while (p->parentNode())
+ p = p->parentNode();
+ if (p->nodeType() == Node::DOCUMENT_NODE) {
+ for (NodeImpl *c = child; c; c = c->traverseNextNode(child)) {
+ c->insertedIntoDocument();
+
+ if (hasInsertedListeners) {
+ MutationEventImpl* const evt = new MutationEventImpl(EventImpl::DOMNODEINSERTEDINTODOCUMENT_EVENT,false,false,0,DOMString(),DOMString(),DOMString(),0);
+ evt->ref();
+ c->dispatchEvent(evt,exceptioncode,true);
+ evt->deref();
+ if (exceptioncode)
+ return;
+ }
+ }
+ }
+}
+
+void NodeBaseImpl::dispatchChildRemovalEvents( NodeImpl *child, int &exceptioncode )
+{
+ // Dispatch pre-removal mutation events
+ getDocument()->notifyBeforeNodeRemoval(child); // ### use events instead
+ if (getDocument()->hasListenerType(DocumentImpl::DOMNODEREMOVED_LISTENER)) {
+ MutationEventImpl* const evt = new MutationEventImpl(EventImpl::DOMNODEREMOVED_EVENT,true,false,this,DOMString(),DOMString(),DOMString(),0);
+ evt->ref();
+ child->dispatchEvent(evt,exceptioncode,true);
+ evt->deref();
+ if (exceptioncode)
+ return;
+ }
+
+ bool hasRemovalListeners = getDocument()->hasListenerType(DocumentImpl::DOMNODEREMOVEDFROMDOCUMENT_LISTENER);
+
+ // dispatch the DOMNodeRemovedFromDocument event to all descendants
+ NodeImpl *p = this;
+ while (p->parentNode())
+ p = p->parentNode();
+ if (p->nodeType() == Node::DOCUMENT_NODE) {
+ for (NodeImpl *c = child; c; c = c->traverseNextNode(child)) {
+ if (hasRemovalListeners) {
+ MutationEventImpl* const evt = new MutationEventImpl(EventImpl::DOMNODEREMOVEDFROMDOCUMENT_EVENT,false,false,0,DOMString(),DOMString(),DOMString(),0);
+ evt->ref();
+ c->dispatchEvent(evt,exceptioncode,true);
+ evt->deref();
+ if (exceptioncode)
+ return;
+ }
+ }
+ }
+}
+
+void NodeBaseImpl::setTextContent( const DOMString &text, int& exceptioncode )
+{
+ // NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly.
+ if (isReadOnly()) {
+ exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR;
+ return;
+ }
+
+ removeChildren();
+
+ if ( !text.isEmpty() && !text.isNull() ) {
+ TextImpl *t = new TextImpl( docPtr(), text.implementation() );
+ appendChild( t, exceptioncode );
+ }
+}
+
+// ---------------------------------------------------------------------------
+NodeImpl *NodeListImpl::item( unsigned long index ) const
+{
+ unsigned long requestIndex = index;
+
+ m_cache->updateNodeListInfo(m_refNode->getDocument());
+
+ NodeImpl* n;
+ bool usedCache = false;
+ if (m_cache->current.node) {
+ //Compute distance from the requested index to the cache node
+ long cacheDist = QABS(long(index) - long(m_cache->position));
+
+ if (cacheDist < (long)index) { //Closer to the cached position
+ usedCache = true;
+ if (index >= m_cache->position) { //Go ahead
+ unsigned long relIndex = index - m_cache->position;
+ n = recursiveItem(m_refNode, m_cache->current.node, relIndex);
+ } else { //Go backwards
+ unsigned long relIndex = m_cache->position - index;
+ n = recursiveItemBack(m_refNode, m_cache->current.node, relIndex);
+ }
+ }
+ }
+
+ if (!usedCache)
+ n = recursiveItem(m_refNode, m_refNode->firstChild(), index);
+
+ //We always update the cache state, to make starting iteration
+ //where it was left off easy.
+ m_cache->current.node = n;
+ m_cache->position = requestIndex;
+ return n;
+}
+
+unsigned long NodeListImpl::length() const
+{
+ m_cache->updateNodeListInfo(m_refNode->getDocument());
+ if (!m_cache->hasLength) {
+ m_cache->length = calcLength( m_refNode );
+ m_cache->hasLength = true;
+ }
+ return m_cache->length;
+}
+
+unsigned long NodeListImpl::calcLength(NodeImpl *start) const
+{
+ unsigned long len = 0;
+ for(NodeImpl *n = start->firstChild(); n != 0; n = n->nextSibling()) {
+ bool recurse = true;
+ if (nodeMatches(n, recurse))
+ len++;
+ if (recurse)
+ len+= NodeListImpl::calcLength(n);
+ }
+
+ return len;
+}
+
+NodeListImpl::NodeListImpl( NodeImpl *n, int type, CacheFactory* factory )
+{
+ m_refNode = n;
+ m_refNode->ref();
+
+ m_cache = m_refNode->getDocument()->acquireCachedNodeListInfo(
+ factory ? factory : Cache::make,
+ n, type );
+}
+
+NodeListImpl::~NodeListImpl()
+{
+ m_refNode->getDocument()->releaseCachedNodeListInfo(m_cache);
+ m_refNode->deref();
+}
+
+
+/**
+ Next item in the pre-order walk of tree from node, but not going outside
+ absStart
+*/
+static NodeImpl* helperNext(NodeImpl* node, NodeImpl* absStart)
+{
+ //Walk up until we wind a sibling to go to.
+ while (!node->nextSibling() && node != absStart)
+ node = node->parentNode();
+
+ if (node != absStart)
+ return node->nextSibling();
+ else
+ return 0;
+}
+
+NodeImpl *NodeListImpl::recursiveItem ( NodeImpl* absStart, NodeImpl *start, unsigned long &offset ) const
+{
+ for(NodeImpl *n = start; n != 0; n = helperNext(n, absStart)) {
+ bool recurse = true;
+ if (nodeMatches(n, recurse))
+ if (!offset--)
+ return n;
+
+ NodeImpl *depthSearch = recurse ? recursiveItem(n, n->firstChild(), offset) : 0;
+ if (depthSearch)
+ return depthSearch;
+ }
+
+ return 0; // no matching node in this subtree
+}
+
+
+NodeImpl *NodeListImpl::recursiveItemBack ( NodeImpl* absStart, NodeImpl *start, unsigned long &offset ) const
+{
+ //### it might be cleaner/faster to split nodeMatches and recursion
+ //filtering.
+ bool dummy = true;
+ NodeImpl* n = start;
+
+ do {
+ bool recurse = true;
+
+ //Check whether the current node matches.
+ if (nodeMatches(n, dummy))
+ if (!offset--)
+ return n;
+
+ if (n->previousSibling()) {
+ //Move to the last node of this whole subtree that we should recurse into
+ n = n->previousSibling();
+ recurse = true;
+
+ while (n->lastChild()) {
+ (void)nodeMatches(n, recurse);
+ if (!recurse)
+ break; //Don't go there
+ n = n->lastChild();
+ }
+ } else {
+ //We're done with this whole subtree, so move up
+ n = n->parentNode();
+ }
+ }
+ while (n && n != absStart);
+
+ return 0;
+}
+
+
+NodeListImpl::Cache::~Cache()
+{}
+
+void NodeListImpl::Cache::clear(DocumentImpl* doc)
+{
+ hasLength = false;
+ current.node = 0;
+ version = doc->domTreeVersion();
+}
+
+void NodeListImpl::Cache::updateNodeListInfo(DocumentImpl* doc)
+{
+ //If version doesn't match, clear
+ if (doc->domTreeVersion() != version)
+ clear(doc);
+}
+
+ChildNodeListImpl::ChildNodeListImpl( NodeImpl *n ): NodeListImpl(n, CHILD_NODES)
+{}
+
+bool ChildNodeListImpl::nodeMatches( NodeImpl* /*testNode*/, bool& doRecurse ) const
+{
+ doRecurse = false;
+ return true;
+}
+
+TagNodeListImpl::TagNodeListImpl( NodeImpl *n, NodeImpl::Id id )
+ : NodeListImpl(n, UNCACHEABLE),
+ m_id(id),
+ m_namespaceAware(false)
+{
+ // An id of 0 here means "*" (match all nodes)
+ m_matchAllNames = (id == 0);
+ m_matchAllNamespaces = false;
+}
+
+TagNodeListImpl::TagNodeListImpl( NodeImpl *n, const DOMString &namespaceURI, const DOMString &localName )
+ : NodeListImpl(n, UNCACHEABLE),
+ m_id(0),
+ m_namespaceURI(namespaceURI),
+ m_localName(localName),
+ m_namespaceAware(true)
+{
+ m_matchAllNames = (localName == "*");
+ m_matchAllNamespaces = (namespaceURI == "*");
+}
+
+
+bool TagNodeListImpl::nodeMatches( NodeImpl *testNode, bool& /*doRecurse*/ ) const
+{
+ if ( testNode->nodeType() != Node::ELEMENT_NODE ) return false;
+ if (m_namespaceAware)
+ return (m_matchAllNamespaces || testNode->namespaceURI() == m_namespaceURI) &&
+ (m_matchAllNames || testNode->localName() == m_localName);
+ else {
+ NodeImpl::Id testId = testNode->id();
+ //we have to strip the namespaces if we compare in a namespace unaware fashion
+ if ( !m_namespaceAware ) testId = localNamePart(testId);
+ return (m_id == 0 || m_id == testId);
+ }
+}
+
+NameNodeListImpl::NameNodeListImpl(NodeImpl *n, const DOMString &t )
+ : NodeListImpl(n, UNCACHEABLE),
+ nodeName(t)
+{}
+
+bool NameNodeListImpl::nodeMatches( NodeImpl *testNode, bool& /*doRecurse*/ ) const
+{
+ if ( testNode->nodeType() != Node::ELEMENT_NODE ) return false;
+ return static_cast<ElementImpl *>(testNode)->getAttribute(ATTR_NAME) == nodeName;
+}
+
+// ---------------------------------------------------------------------------
+
+NamedNodeMapImpl::NamedNodeMapImpl()
+{
+}
+
+NamedNodeMapImpl::~NamedNodeMapImpl()
+{
+}
+
+// ----------------------------------------------------------------------------
+
+GenericRONamedNodeMapImpl::GenericRONamedNodeMapImpl(DocumentImpl* doc)
+ : NamedNodeMapImpl()
+{
+ m_doc = doc;
+ m_contents = new QPtrList<NodeImpl>;
+}
+
+GenericRONamedNodeMapImpl::~GenericRONamedNodeMapImpl()
+{
+ while (!m_contents->isEmpty())
+ m_contents->take(0)->deref();
+
+ delete m_contents;
+}
+
+NodeImpl *GenericRONamedNodeMapImpl::getNamedItem ( NodeImpl::Id id, bool /*nsAware*/, DOMStringImpl* /*qName*/ ) const
+{
+ // ## do we need namespace support in this class?
+ QPtrListIterator<NodeImpl> it(*m_contents);
+ for (; it.current(); ++it)
+ if (it.current()->id() == id)
+ return it.current();
+ return 0;
+}
+
+Node GenericRONamedNodeMapImpl::setNamedItem ( NodeImpl* /*arg*/, bool /*nsAware*/, DOMStringImpl* /*qName*/, int &exceptioncode )
+{
+ // can't modify this list through standard DOM functions
+ // NO_MODIFICATION_ALLOWED_ERR: Raised if this map is readonly
+ exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR;
+ return 0;
+}
+
+Node GenericRONamedNodeMapImpl::removeNamedItem ( NodeImpl::Id /*id*/, bool /*nsAware*/, DOMStringImpl* /*qName*/, int &exceptioncode )
+{
+ // can't modify this list through standard DOM functions
+ // NO_MODIFICATION_ALLOWED_ERR: Raised if this map is readonly
+ exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR;
+ return 0;
+}
+
+NodeImpl *GenericRONamedNodeMapImpl::item ( unsigned long index ) const
+{
+ if (index >= m_contents->count())
+ return 0;
+
+ return m_contents->at(index);
+}
+
+unsigned long GenericRONamedNodeMapImpl::length( ) const
+{
+ return m_contents->count();
+}
+
+void GenericRONamedNodeMapImpl::addNode(NodeImpl *n)
+{
+ // The spec says that in the case of duplicates we only keep the first one
+ if (getNamedItem(n->id(), false, 0))
+ return;
+
+ n->ref();
+ m_contents->append(n);
+}
+
+NodeImpl::Id GenericRONamedNodeMapImpl::mapId(DOMStringImpl* namespaceURI,
+ DOMStringImpl* localName, bool readonly)
+{
+ if (!m_doc)
+ return 0;
+
+ return m_doc->getId(NodeImpl::ElementId,
+ namespaceURI, 0, localName, readonly,
+ false /*don't lookupHTML*/);
+}
+
+// -----------------------------------------------------------------------------
+
+void RegisteredListenerList::addEventListener(int id, EventListener *listener, const bool useCapture)
+{
+ RegisteredEventListener rl(static_cast<EventImpl::EventId>(id),listener,useCapture);
+ if (!listeners)
+ listeners = new QValueList<RegisteredEventListener>;
+
+ // if this id/listener/useCapture combination is already registered, do nothing.
+ // the DOM2 spec says that "duplicate instances are discarded", and this keeps
+ // the listener order intact.
+ QValueList<RegisteredEventListener>::iterator it;
+ for (it = listeners->begin(); it != listeners->end(); ++it)
+ if (*it == rl)
+ return;
+
+ listeners->append(rl);
+}
+
+void RegisteredListenerList::removeEventListener(int id, EventListener *listener, bool useCapture)
+{
+ if (!listeners) // nothing to remove
+ return;
+
+ RegisteredEventListener rl(static_cast<EventImpl::EventId>(id),listener,useCapture);
+
+ QValueList<RegisteredEventListener>::iterator it;
+ for (it = listeners->begin(); it != listeners->end(); ++it)
+ if (*it == rl) {
+ listeners->remove(it);
+ return;
+ }
+}
+
+bool RegisteredListenerList::isHTMLEventListener(EventListener* listener)
+{
+ return (listener->eventListenerType() == "_khtml_HTMLEventListener");
+}
+
+void RegisteredListenerList::setHTMLEventListener(int id, EventListener *listener)
+{
+ if (!listeners)
+ listeners = new QValueList<RegisteredEventListener>;
+
+ QValueList<RegisteredEventListener>::iterator it;
+ if (!listener) {
+ for (it = listeners->begin(); it != listeners->end(); ++it) {
+ if ((*it).id == id && isHTMLEventListener((*it).listener)) {
+ listeners->remove(it);
+ break;
+ }
+ }
+ return;
+ }
+
+ // if this event already has a registered handler, insert the new one in
+ // place of the old one, to preserve the order.
+ RegisteredEventListener rl(static_cast<EventImpl::EventId>(id),listener,false);
+
+ int i;
+ for (i = 0, it = listeners->begin(); it != listeners->end(); ++it, ++i)
+ if ((*it).id == id && isHTMLEventListener((*it).listener)) {
+ listeners->insert(it, rl);
+ listeners->remove(it);
+ return;
+ }
+
+ listeners->append(rl);
+}
+
+EventListener *RegisteredListenerList::getHTMLEventListener(int id)
+{
+ if (!listeners)
+ return 0;
+
+ QValueList<RegisteredEventListener>::iterator it;
+ for (it = listeners->begin(); it != listeners->end(); ++it)
+ if ((*it).id == id && isHTMLEventListener((*it).listener)) {
+ return (*it).listener;
+ }
+ return 0;
+}
+
+bool RegisteredListenerList::hasEventListener(int id)
+{
+ if (!listeners)
+ return false;
+
+ QValueList<RegisteredEventListener>::iterator it;
+ for (it = listeners->begin(); it != listeners->end(); ++it)
+ if ((*it).id == id)
+ return true;
+
+ return false;
+}
+
+void RegisteredListenerList::clear()
+{
+ delete listeners;
+ listeners = 0;
+}
+
+bool RegisteredListenerList::stillContainsListener(const RegisteredEventListener& listener)
+{
+ if (!listeners)
+ return false;
+ return listeners->find(listener) != listeners->end();
+}
+
+RegisteredListenerList::~RegisteredListenerList() {
+ delete listeners; listeners = 0;
+}
diff --git a/khtml/xml/dom_nodeimpl.h b/khtml/xml/dom_nodeimpl.h
new file mode 100644
index 000000000..1ff150f6a
--- /dev/null
+++ b/khtml/xml/dom_nodeimpl.h
@@ -0,0 +1,736 @@
+/*
+ * This file is part of the DOM implementation for KDE.
+ *
+ * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
+ * (C) 1999 Antti Koivisto (koivisto@kde.org)
+ * (C) 2001 Dirk Mueller (mueller@kde.org)
+ * (C) 2003 Apple Computer, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+#ifndef _DOM_NodeImpl_h_
+#define _DOM_NodeImpl_h_
+
+#include "dom/dom_misc.h"
+#include "dom/dom_string.h"
+#include "dom/dom_node.h"
+#include "misc/helper.h"
+#include "misc/shared.h"
+
+// The namespace used for XHTML elements
+#define XHTML_NAMESPACE "http://www.w3.org/1999/xhtml"
+
+class QPainter;
+template <class type> class QPtrList;
+template <class type> class QValueList;
+class KHTMLView;
+class QRect;
+class QMouseEvent;
+class QKeyEvent;
+class QTextStream;
+
+namespace khtml {
+ class RenderStyle;
+ class RenderObject;
+ class RenderArena;
+}
+
+namespace DOM {
+
+class NodeListImpl;
+class NamedNodeMapImpl;
+class DocumentImpl;
+class CSSStyleDeclarationImpl;
+class RegisteredEventListener;
+class EventImpl;
+
+struct RegisteredListenerList {
+ RegisteredListenerList() : listeners(0)
+ {}
+
+ ~RegisteredListenerList();
+
+ void addEventListener(int id, EventListener *listener, const bool useCapture);
+ void removeEventListener(int id, EventListener *listener, bool useCapture);
+
+ void setHTMLEventListener(int id, EventListener *listener);
+ EventListener *getHTMLEventListener(int id);
+
+ bool hasEventListener(int id);
+ void clear();
+
+ //### KDE4: should disappear
+ bool stillContainsListener(const RegisteredEventListener& listener);
+
+ QValueList<RegisteredEventListener>* listeners;//The actual listener list - may be 0
+private:
+ bool isHTMLEventListener(EventListener* listener);
+};
+
+
+// this class implements nodes, which can have a parent but no children:
+#define NodeImpl_IdNSMask 0xffff0000
+#define NodeImpl_IdLocalMask 0x0000ffff
+
+const Q_UINT16 defaultNamespace = 0;
+const Q_UINT16 xhtmlNamespace = 1;
+const Q_UINT16 emptyNamespace = 2;
+const Q_UINT16 anyNamespace = 0xffff;
+const Q_UINT16 anyLocalName = 0xffff;
+
+inline Q_UINT16 localNamePart(Q_UINT32 id) { return id & NodeImpl_IdLocalMask; }
+inline Q_UINT16 namespacePart(Q_UINT32 id) { return (((unsigned int)id) & NodeImpl_IdNSMask) >> 16; }
+inline Q_UINT32 makeId(Q_UINT16 n, Q_UINT16 l) { return (n << 16) | l; }
+
+const Q_UINT32 anyQName = makeId(anyNamespace, anyLocalName);
+
+class NodeImpl : public khtml::TreeShared<NodeImpl>
+{
+ friend class DocumentImpl;
+public:
+ NodeImpl(DocumentImpl *doc);
+ virtual ~NodeImpl();
+
+ // DOM methods & attributes for Node
+ virtual DOMString nodeName() const;
+ virtual DOMString nodeValue() const;
+ virtual void setNodeValue( const DOMString &_nodeValue, int &exceptioncode );
+ virtual unsigned short nodeType() const;
+ NodeImpl *parentNode() const { return m_parent; }
+ NodeImpl *previousSibling() const { return m_previous; }
+ NodeImpl *nextSibling() const { return m_next; }
+ virtual NodeListImpl *childNodes();
+ virtual NodeImpl *firstChild() const;
+ virtual NodeImpl *lastChild() const;
+ // insertBefore, replaceChild and appendChild also close newChild
+ // unlike the speed optimized addChild (which is used by the parser)
+ virtual NodeImpl *insertBefore ( NodeImpl *newChild, NodeImpl *refChild, int &exceptioncode );
+
+ /* These two methods may delete the old node, so make sure to reference it if you need it */
+ virtual void replaceChild ( NodeImpl *newChild, NodeImpl *oldChild, int &exceptioncode );
+ virtual void removeChild ( NodeImpl *oldChild, int &exceptioncode );
+ virtual NodeImpl *appendChild ( NodeImpl *newChild, int &exceptioncode );
+ virtual bool hasChildNodes ( ) const;
+ virtual NodeImpl *cloneNode ( bool deep ) = 0;
+ virtual DOMString localName() const;
+ virtual DOMString prefix() const;
+ virtual DOMString namespaceURI() const;
+ virtual void setPrefix(const DOMString &_prefix, int &exceptioncode );
+ void normalize ();
+
+ // Other methods (not part of DOM)
+ virtual bool isElementNode() const { return false; }
+ virtual bool isHTMLElement() const { return false; }
+ virtual bool isAttributeNode() const { return false; }
+ virtual bool isTextNode() const { return false; }
+ virtual bool isDocumentNode() const { return false; }
+ virtual bool isXMLElementNode() const { return false; }
+ virtual bool isGenericFormElement() const { return false; }
+ virtual bool containsOnlyWhitespace() const { return false; }
+ virtual bool contentEditable() const;
+
+ // helper functions not being part of the DOM
+ // Attention: they assume that the caller did the consistency checking!
+ void setPreviousSibling(NodeImpl *previous) { m_previous = previous; }
+ void setNextSibling(NodeImpl *next) { m_next = next; }
+
+ virtual void setFirstChild(NodeImpl *child);
+ virtual void setLastChild(NodeImpl *child);
+
+ // used by the parser. Doesn't do as many error checkings as
+ // appendChild(), and returns the node into which will be parsed next.
+ virtual NodeImpl *addChild(NodeImpl *newChild);
+
+ typedef Q_UINT32 Id;
+ // id() is used to easily and exactly identify a node. It
+ // is optimized for quick comparison and low memory consumption.
+ // its value depends on the owner document of the node and is
+ // categorized in the following way:
+ // 1..ID_LAST_TAG: the node inherits HTMLElementImpl and is
+ // part of the HTML namespace.
+ // The HTML namespace is either the global
+ // one (no namespace) or the XHTML namespace
+ // depending on the owner document's doctype
+ // ID_LAST_TAG+1..0xffff: non-HTML elements in the global namespace
+ // others non-HTML elements in a namespace.
+ // the upper 16 bit identify the namespace
+ // the lower 16 bit identify the local part of the
+ // qualified element name.
+ virtual Id id() const { return 0; }
+
+ // These are the DOM 3 Core answer to innerText/setInnerText, and are used
+ // quite a bit since Mozilla doesn't do innerText. They do, however, behave slightly
+ // differently. The default implementation is for ELEMENT_NODE, ATTRIBUTE_NODE,
+ // ENTITY_NODE, ENTITY_REFERENCE_NODE, DOCUMENT_FRAGMENT_NODE.
+ virtual DOMStringImpl* textContent() const;
+ virtual void setTextContent( const DOMString &text, int& exceptioncode ) = 0;
+
+ enum IdType {
+ AttributeId,
+ ElementId,
+ NamespaceId
+ };
+
+ enum MouseEventType {
+ MousePress,
+ MouseRelease,
+ MouseClick,
+ MouseDblClick,
+ MouseMove
+ };
+
+ struct MouseEvent
+ {
+ MouseEvent( int _button, MouseEventType _type,
+ const DOMString &_url = DOMString(), const DOMString& _target = DOMString(),
+ NodeImpl *_innerNode = 0, NodeImpl *_innerNonSharedNode = 0)
+ {
+ button = _button; type = _type;
+ url = _url; target = _target;
+ innerNode = _innerNode;
+ innerNonSharedNode = _innerNonSharedNode;
+ }
+
+ int button;
+ MouseEventType type;
+ DOMString url; // url under mouse or empty
+ DOMString target;
+ Node innerNode;
+ Node innerNonSharedNode;
+ };
+
+ // for LINK and STYLE
+ virtual void sheetLoaded() {}
+
+ bool hasID() const { return m_hasId; }
+ bool hasClass() const { return m_hasClass; }
+ bool active() const { return m_active; }
+ bool focused() const { return m_focused; }
+ bool hovered() const { return m_hovered; }
+ bool attached() const { return m_attached; }
+ bool closed() const { return m_closed; }
+ bool changed() const { return m_changed; }
+ bool hasChangedChild() const { return m_hasChangedChild; }
+ bool hasAnchor() const { return m_hasAnchor; }
+ bool inDocument() const { return m_inDocument; }
+ bool implicitNode() const { return m_implicit; }
+ bool htmlCompat() const { return m_htmlCompat; }
+ void setHasID(bool b=true) { m_hasId = b; }
+ void setHasClass(bool b=true) { m_hasClass = b; }
+ void setHasChangedChild( bool b = true ) { m_hasChangedChild = b; }
+ void setInDocument(bool b=true) { m_inDocument = b; }
+ void setHTMLCompat(bool b) { m_htmlCompat = b; }
+ virtual void setFocus(bool b=true) { m_focused = b; }
+ virtual void setActive(bool b=true) { m_active = b; }
+ virtual void setHovered(bool b=true) { m_hovered = b; }
+ virtual void setChanged(bool b=true);
+
+ // for descending restyle when ID or CLASS changes
+ bool changedAscendentAttribute() const { return m_changedAscendentAttribute; }
+ void setChangedAscendentAttribute(bool b) { m_changedAscendentAttribute = b; }
+
+ // for style selection performance: whether the element matches several CSS Classes
+ bool hasClassList() const { return m_hasClassList; }
+ void setHasClassList(bool b) { m_hasClassList = b; }
+
+ unsigned short tabIndex() const { return m_tabIndex; }
+ void setTabIndex(unsigned short _tabIndex) { m_tabIndex = _tabIndex; }
+
+ virtual bool isFocusable() const { return false; }
+ virtual bool isMouseFocusable() const { return isFocusable(); }
+ virtual bool isTabFocusable() const { return isFocusable(); }
+
+ virtual bool isInline() const;
+
+ virtual void getCaret(int offset, bool override, int &_x, int &_y, int &width, int &height);
+ virtual QRect getRect() const;
+
+ enum StyleChange { NoChange, NoInherit, Inherit, Detach, Force };
+ virtual void recalcStyle( StyleChange = NoChange ) {}
+ static StyleChange diff( khtml::RenderStyle *s1, khtml::RenderStyle *s2 );
+ static bool pseudoDiff( khtml::RenderStyle *s1, khtml::RenderStyle *s2, unsigned int pid);
+
+ unsigned long nodeIndex() const;
+ // Returns the document that this node is associated with. This is guaranteed to always be non-null, as opposed to
+ // DOM's ownerDocument() which is null for Document nodes (and sometimes DocumentType nodes).
+ DocumentImpl* getDocument() const { return m_document.get(); }
+
+ void addEventListener(int id, EventListener *listener, const bool useCapture);
+ void removeEventListener(int id, EventListener *listener, bool useCapture);
+ void setHTMLEventListener(int id, EventListener *listener);
+ EventListener *getHTMLEventListener(int id);
+
+ void dispatchEvent(EventImpl *evt, int &exceptioncode, bool tempEvent = false);
+ void dispatchGenericEvent( EventImpl *evt, int &exceptioncode);
+ // return true if event not prevented
+ bool dispatchHTMLEvent(int _id, bool canBubbleArg, bool cancelableArg);
+ void dispatchWindowEvent(int _id, bool canBubbleArg, bool cancelableArg);
+ void dispatchMouseEvent(QMouseEvent *e, int overrideId = 0, int overrideDetail = 0);
+ void dispatchUIEvent(int _id, int detail = 0);
+ void dispatchSubtreeModifiedEvent();
+ // return true if defaultPrevented (i.e. event should be swallowed)
+ // this matches the logic in KHTMLView.
+ bool dispatchKeyEvent(QKeyEvent *key, bool keypress);
+
+ void handleLocalEvents(EventImpl *evt, bool useCapture);
+
+ /**
+ * Perform the default action for an event e.g. submitting a form
+ */
+ virtual void defaultEventHandler(EventImpl *evt);
+
+ virtual bool isReadOnly();
+ virtual bool childTypeAllowed( unsigned short /*type*/ ) { return false; }
+ virtual unsigned long childNodeCount();
+ virtual NodeImpl *childNode(unsigned long index);
+
+ /**
+ * Does a pre-order traversal of the tree to find the node next node after this one. This uses the same order that
+ * the tags appear in the source file.
+ *
+ * @param stayWithin If not null, the traversal will stop once the specified node is reached. This can be used to
+ * restrict traversal to a particular sub-tree.
+ *
+ * @return The next node, in document order
+ *
+ * see traversePreviousNode()
+ */
+ NodeImpl *traverseNextNode(NodeImpl *stayWithin = 0) const;
+
+ /**
+ * Does a reverse pre-order traversal to find the node that comes before the current one in document order
+ *
+ * see traverseNextNode()
+ */
+ NodeImpl *traversePreviousNode() const;
+
+ DocumentImpl *docPtr() const { return m_document.get(); }
+
+ khtml::RenderObject *renderer() const { return m_render; }
+ khtml::RenderObject *nextRenderer();
+ khtml::RenderObject *previousRenderer();
+ void setRenderer(khtml::RenderObject* renderer) { m_render = renderer; }
+
+ void checkSetPrefix(const DOMString &_prefix, int &exceptioncode);
+ void checkAddChild(NodeImpl *newChild, int &exceptioncode);
+ bool isAncestor( NodeImpl *other );
+ virtual bool childAllowed( NodeImpl *newChild );
+
+ /**
+ * Returns the minimum caret offset that is allowed for this node.
+ *
+ * This default implementation always returns 0. Textual child nodes
+ * may return other values.
+ */
+ virtual long minOffset() const;
+ /**
+ * Returns the maximum caret offset that is allowed for this node.
+ *
+ * This default implementation always returns the node count.
+ * Textual child nodes return the character count instead.
+ */
+ virtual long maxOffset() const;
+
+ // -----------------------------------------------------------------------------
+ // Integration with rendering tree
+
+ /**
+ * Attaches this node to the rendering tree. This calculates the style to be applied to the node and creates an
+ * appropriate RenderObject which will be inserted into the tree (except when the style has display: none). This
+ * makes the node visible in the KHTMLView.
+ */
+ virtual void attach();
+
+ /**
+ * Detaches the node from the rendering tree, making it invisible in the rendered view. This method will remove
+ * the node's rendering object from the rendering tree and delete it.
+ */
+ virtual void detach();
+
+ /**
+ * Notifies the node that no more children will be added during parsing.
+ * After a node has been closed all changes must go through the DOM interface.
+ */
+ virtual void close();
+
+ virtual void structureChanged() {};
+ virtual void backwardsStructureChanged() {};
+
+ void createRendererIfNeeded();
+ virtual khtml::RenderStyle *styleForRenderer(khtml::RenderObject *parent);
+ virtual bool rendererIsNeeded(khtml::RenderStyle *);
+ virtual khtml::RenderObject *createRenderer(khtml::RenderArena *, khtml::RenderStyle *);
+
+ // -----------------------------------------------------------------------------
+ // Methods for maintaining the state of the element between history navigation
+
+ /**
+ * Indicates whether or not this type of node maintains its state. If so, the state of the node will be stored when
+ * the user goes to a different page using the state() method, and restored using the restoreState() method if the
+ * user returns (e.g. using the back button). This is used to ensure that user-changeable elements such as form
+ * controls maintain their contents when the user returns to a previous page in the history.
+ */
+ virtual bool maintainsState();
+
+ /**
+ * Returns the state of this node represented as a string. This string will be passed to restoreState() if the user
+ * returns to the page.
+ *
+ * @return State information about the node represented as a string
+ */
+ virtual QString state();
+
+ /**
+ * Sets the state of the element based on a string previosuly returned by state(). This is used to initialize form
+ * controls with their old values when the user returns to the page in their history.
+ *
+ * @param state A string representation of the node's previously-stored state
+ */
+ virtual void restoreState(const QString &state);
+
+ // -----------------------------------------------------------------------------
+ // Notification of document stucture changes
+
+ /**
+ * Notifies the node that it has been inserted into the document. This is called during document parsing, and also
+ * when a node is added through the DOM methods insertBefore(), appendChild() or replaceChild(). Note that this only
+ * happens when the node becomes part of the document tree, i.e. only when the document is actually an ancestor of
+ * the node. The call happens _after_ the node has been added to the tree.
+ *
+ * This is similar to the DOMNodeInsertedIntoDocument DOM event, but does not require the overhead of event
+ * dispatching.
+ */
+ virtual void insertedIntoDocument();
+
+ /**
+ * Notifies the node that it is no longer part of the document tree, i.e. when the document is no longer an ancestor
+ * node.
+ *
+ * This is similar to the DOMNodeRemovedFromDocument DOM event, but does not require the overhead of event
+ * dispatching, and is called _after_ the node is removed from the tree.
+ */
+ virtual void removedFromDocument();
+
+ /**
+ * Notifies the node that its list of children have changed (either by adding or removing child nodes), or a child
+ * node that is of the type CDATA_SECTION_NODE, TEXT_NODE or COMMENT_NODE has changed its value.
+ */
+ virtual void childrenChanged();
+
+ virtual DOMString toString() const = 0;
+ /**
+ * Sometimes we need to get the string between two points on the DOM graph. Use this function to do this.
+ * For example, when the user copies some selected text to the clipboard as html.
+ * @param selectionStart Where to start the selection. If selectionStart != this, it is assumed we are after the start point
+ * @param selectionEnd Where to end the selection. If selectionEnd != this, it is assumed we are before the end point (unless found is true)
+ * @param startOffset Number of characters into the text in selectionStart that the start of the selection is.
+ * @param endOffset Number of characters into the text in selectionEnd that the end of the selection is.
+ * @param found When this is set to true, don't print anymore but closing tags.
+ * @return An html formatted string for this node and its children between the selectionStart and selectionEnd.
+ */
+ virtual DOMString selectionToString(NodeImpl * selectionStart,
+ NodeImpl * selectionEnd,
+ int startOffset,
+ int endOffset,
+ bool &found) const
+ { Q_UNUSED(selectionStart);
+ Q_UNUSED(selectionEnd);
+ Q_UNUSED(startOffset);
+ Q_UNUSED(endOffset);
+ Q_UNUSED(found);
+ return toString();
+ }
+
+private: // members
+ khtml::DocPtr<DocumentImpl> m_document;
+ NodeImpl *m_previous;
+ NodeImpl *m_next;
+protected:
+ khtml::RenderObject *m_render;
+ RegisteredListenerList m_regdListeners;
+
+ unsigned short m_tabIndex : 15;
+ bool m_hasTabIndex : 1;
+
+ bool m_hasId : 1;
+ bool m_attached : 1;
+ bool m_closed : 1;
+ bool m_changed : 1;
+ bool m_hasChangedChild : 1;
+ bool m_changedAscendentAttribute : 1;
+ bool m_inDocument : 1;
+ bool m_hasAnchor : 1;
+ bool m_specified : 1; // used in AttrImpl. Accessor functions there
+
+ bool m_hovered : 1;
+ bool m_focused : 1;
+ bool m_active : 1;
+ bool m_implicit : 1; // implicitely generated by the parser
+ bool m_htmlCompat : 1; // true if element was created in HTML compat mode
+ bool m_hasClassList : 1;
+ bool m_hasClass : 1; // true if element has a class property, as relevant to CSS
+};
+
+// this is the full Node Implementation with parents and children.
+class NodeBaseImpl : public NodeImpl
+{
+public:
+ NodeBaseImpl(DocumentImpl *doc)
+ : NodeImpl(doc), _first(0), _last(0) {}
+ virtual ~NodeBaseImpl();
+
+ // DOM methods overridden from parent classes
+ virtual NodeImpl *firstChild() const;
+ virtual NodeImpl *lastChild() const;
+ virtual NodeImpl *insertBefore ( NodeImpl *newChild, NodeImpl *refChild, int &exceptioncode );
+ virtual void replaceChild ( NodeImpl *newChild, NodeImpl *oldChild, int &exceptioncode );
+ virtual void removeChild ( NodeImpl *oldChild, int &exceptioncode );
+ virtual NodeImpl *appendChild ( NodeImpl *newChild, int &exceptioncode );
+ virtual bool hasChildNodes ( ) const;
+
+ virtual void setTextContent( const DOMString &text, int& exceptioncode );
+
+ // Other methods (not part of DOM)
+ void removeChildren();
+ void cloneChildNodes(NodeImpl *clone);
+
+ virtual void setFirstChild(NodeImpl *child);
+ virtual void setLastChild(NodeImpl *child);
+ virtual NodeImpl *addChild(NodeImpl *newChild);
+ virtual void attach();
+ virtual void detach();
+
+
+ bool getUpperLeftCorner(int &xPos, int &yPos) const;
+ bool getLowerRightCorner(int &xPos, int &yPos) const;
+
+ virtual void setFocus(bool=true);
+ virtual void setActive(bool=true);
+ virtual void setHovered(bool=true);
+ virtual unsigned long childNodeCount();
+ virtual NodeImpl *childNode(unsigned long index);
+
+protected:
+ NodeImpl *_first;
+ NodeImpl *_last;
+
+ // helper functions for inserting children:
+
+ // ### this should vanish. do it in dom/ !
+ // check for same source document:
+ bool checkSameDocument( NodeImpl *newchild, int &exceptioncode );
+ // check for being child:
+ bool checkIsChild( NodeImpl *oldchild, int &exceptioncode );
+ // ###
+
+ // find out if a node is allowed to be our child
+ void dispatchChildInsertedEvents( NodeImpl *child, int &exceptioncode );
+ void dispatchChildRemovalEvents( NodeImpl *child, int &exceptioncode );
+};
+
+// --------------------------------------------------------------------------
+class Node;
+class NodeImpl;
+
+class NodeListImpl : public khtml::Shared<NodeListImpl>
+{
+public:
+ //Type of the item stored in the cache.
+ enum Type {
+ UNCACHEABLE, //Too complex to be cached like this
+ CHILD_NODES,
+ LAST_NODE_LIST = CHILD_NODES
+ };
+
+ struct CacheKey
+ {
+ NodeImpl* baseNode;
+ int type;
+
+ CacheKey(): type(UNCACHEABLE) {}
+
+ CacheKey(NodeImpl* _baseNode, int _type):
+ baseNode(_baseNode), type(_type)
+ {}
+
+ int hash() const
+ {
+ return int(reinterpret_cast<unsigned long>(baseNode) >> 2) ^
+ (unsigned(type) << 26);
+ }
+
+ bool operator==(const CacheKey& other) const
+ {
+ return baseNode == other.baseNode &&
+ type == other.type;
+ }
+ };
+
+ struct Cache: public khtml::Shared<Cache>
+ {
+ static Cache* make() { return new Cache; }
+
+ CacheKey key;//### We must store this in here due to QCache in Qt3 sucking
+
+ unsigned int version;
+ union
+ {
+ NodeImpl* node;
+ unsigned int index;
+ } current;
+ unsigned int position;
+ unsigned int length;
+ bool hasLength;
+
+ void updateNodeListInfo(DocumentImpl* doc);
+
+ virtual void clear(DocumentImpl* doc);
+ virtual ~Cache();
+ };
+
+ typedef Cache* CacheFactory();
+
+ NodeListImpl(NodeImpl* node, int type, CacheFactory* factory = 0);
+ virtual ~NodeListImpl();
+
+ // DOM methods & attributes for NodeList
+ virtual unsigned long length() const;
+ virtual NodeImpl *item ( unsigned long index ) const;
+
+ // Other methods (not part of DOM)
+
+protected:
+ virtual unsigned long calcLength(NodeImpl *start) const;
+ // helper functions for searching all ElementImpls in a tree
+
+ NodeImpl *recursiveItem ( NodeImpl* absStart, NodeImpl *start, unsigned long &offset ) const;
+ NodeImpl *recursiveItemBack( NodeImpl* absStart, NodeImpl *start, unsigned long &offset ) const;
+
+ // Override this to determine what nodes to return. Set doRecurse to
+ // false if the children of this node do not need to be entered.
+ virtual bool nodeMatches( NodeImpl *testNode, bool& doRecurse ) const = 0;
+
+ NodeImpl* m_refNode;
+ mutable Cache* m_cache;
+};
+
+class ChildNodeListImpl : public NodeListImpl
+{
+public:
+
+ ChildNodeListImpl( NodeImpl *n);
+
+protected:
+ virtual bool nodeMatches( NodeImpl *testNode, bool& doRecurse ) const;
+};
+
+
+/**
+ * NodeList which lists all Nodes in a document with a given tag name
+ */
+class TagNodeListImpl : public NodeListImpl
+{
+public:
+ TagNodeListImpl( NodeImpl *n, NodeImpl::Id id );
+ TagNodeListImpl( NodeImpl *n, const DOMString &namespaceURI, const DOMString &localName );
+
+ // Other methods (not part of DOM)
+
+protected:
+ virtual bool nodeMatches( NodeImpl *testNode, bool& doRecurse ) const;
+ NodeImpl::Id m_id;
+ DOMString m_namespaceURI;
+ DOMString m_localName;
+
+ bool m_matchAllNames;
+ bool m_matchAllNamespaces;
+ bool m_namespaceAware;
+};
+
+
+/**
+ * NodeList which lists all Nodes in a Element with a given "name=" tag
+ */
+class NameNodeListImpl : public NodeListImpl
+{
+public:
+ NameNodeListImpl( NodeImpl *doc, const DOMString &t );
+
+ // Other methods (not part of DOM)
+
+protected:
+ virtual bool nodeMatches( NodeImpl *testNode, bool& doRecurse ) const;
+
+ DOMString nodeName;
+};
+
+// Generic NamedNodeMap interface
+// Other classes implement this for more specific situations e.g. attributes
+// of an element
+class NamedNodeMapImpl : public khtml::Shared<NamedNodeMapImpl>
+{
+public:
+ NamedNodeMapImpl();
+ virtual ~NamedNodeMapImpl();
+
+ // DOM methods & attributes for NamedNodeMap
+ virtual NodeImpl *getNamedItem ( NodeImpl::Id id, bool nsAware = false, DOMStringImpl* qName = 0 ) const = 0;
+ virtual Node removeNamedItem ( NodeImpl::Id id, bool nsAware, DOMStringImpl* qName, int &exceptioncode ) = 0;
+ virtual Node setNamedItem ( NodeImpl* arg, bool nsAware, DOMStringImpl* qName, int &exceptioncode ) = 0;
+
+ virtual NodeImpl *item ( unsigned long index ) const = 0;
+ virtual unsigned long length( ) const = 0;
+
+ // Other methods (not part of DOM)
+ virtual NodeImpl::Id mapId(DOMStringImpl* namespaceURI,
+ DOMStringImpl* localName, bool readonly) = 0;
+ virtual bool isReadOnly() { return false; }
+};
+
+// Generic read-only NamedNodeMap implementation
+// Used for e.g. entities and notations in DocumentType.
+// You can add nodes using addNode
+class GenericRONamedNodeMapImpl : public NamedNodeMapImpl
+{
+public:
+ GenericRONamedNodeMapImpl(DocumentImpl* doc);
+ virtual ~GenericRONamedNodeMapImpl();
+
+ // DOM methods & attributes for NamedNodeMap
+
+ virtual NodeImpl *getNamedItem ( NodeImpl::Id id, bool nsAware = false, DOMStringImpl* qName = 0 ) const;
+ virtual Node removeNamedItem ( NodeImpl::Id id, bool nsAware, DOMStringImpl* qName, int &exceptioncode );
+ virtual Node setNamedItem ( NodeImpl* arg, bool nsAware, DOMStringImpl* qName, int &exceptioncode );
+
+ virtual NodeImpl *item ( unsigned long index ) const;
+ virtual unsigned long length( ) const;
+
+ // Other methods (not part of DOM)
+ virtual NodeImpl::Id mapId(DOMStringImpl* namespaceURI,
+ DOMStringImpl* localName, bool readonly);
+
+ virtual bool isReadOnly() { return true; }
+
+ void addNode(NodeImpl *n);
+
+protected:
+ DocumentImpl* m_doc;
+ QPtrList<NodeImpl> *m_contents;
+};
+
+} //namespace
+#endif
diff --git a/khtml/xml/dom_restyler.cpp b/khtml/xml/dom_restyler.cpp
new file mode 100644
index 000000000..0050c4a03
--- /dev/null
+++ b/khtml/xml/dom_restyler.cpp
@@ -0,0 +1,122 @@
+/*
+ * This file is part of the DOM implementation for KDE.
+ *
+ * Copyright (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_restyler.h"
+#include "dom_elementimpl.h"
+
+namespace khtml {
+
+DynamicDomRestyler::DynamicDomRestyler()
+{
+}
+
+void DynamicDomRestyler::addDependency(ElementImpl* subject, ElementImpl* dependency, StructuralDependencyType type)
+{
+ assert(type < LastStructuralDependency);
+
+ dependency_map[type].append(dependency, subject);
+ reverse_map.append(subject,dependency);
+}
+
+void DynamicDomRestyler::removeDependency(ElementImpl* subject, ElementImpl* dependency, StructuralDependencyType type)
+{
+ dependency_map[type].remove(dependency, subject);
+ // don't remove from reverse_map as there might be other depencies to the same element
+}
+
+void DynamicDomRestyler::removeDependencies(ElementImpl* subject, StructuralDependencyType type)
+{
+ KMultiMap<ElementImpl>::List* my_dependencies = reverse_map.find(subject);
+
+ if (!my_dependencies) return;
+
+ for (my_dependencies->first(); my_dependencies->current() ; my_dependencies->next())
+ {
+ ElementImpl* e = my_dependencies->current();
+ dependency_map[type].remove(e,subject);
+ }
+
+ // don't remove from reverse_map as there might be other depencies to the same elements
+}
+
+void DynamicDomRestyler::resetDependencies(ElementImpl* subject)
+{
+ KMultiMap<ElementImpl>::List* my_dependencies = reverse_map.find(subject);
+
+ if (!my_dependencies) return;
+
+ for (my_dependencies->first(); my_dependencies->current() ; my_dependencies->next())
+ {
+ ElementImpl* e = my_dependencies->current();
+ for (int type = 0; type < LastStructuralDependency; type++) {
+ dependency_map[type].remove(e,subject);
+ }
+ }
+
+ reverse_map.remove(subject);
+}
+
+void DynamicDomRestyler::restyleDepedent(ElementImpl* dependency, StructuralDependencyType type)
+{
+ assert(type < LastStructuralDependency);
+ KMultiMap<ElementImpl>::List* dep = dependency_map[type].find(dependency);
+
+ if (!dep) return;
+
+ // take copy as the restyle will change the list
+ KMultiMap<ElementImpl>::List dependent(*dep);
+
+ for (dependent.first(); dependent.current() ; dependent.next())
+ {
+// kdDebug() << "Restyling dependent" << endl;
+ dependent.current()->setChanged(true);
+ }
+}
+
+void DynamicDomRestyler::dumpStats() const
+{
+/*
+ kdDebug() << "Keys in structural dependencies: " << dependency_map[StructuralDependency].size() << endl;
+ kdDebug() << "Keys in attribute dependencies: " << dependency_map[AttributeDependency].size() << endl;
+
+ kdDebug() << "Keys in reverse map: " << reverse_map.size() << endl;
+ */
+}
+
+void DynamicDomRestyler::addDependency(uint attrID, AttributeDependencyType type)
+{
+ assert(type < LastAttributeDependency);
+
+ unsigned int hash = (attrID * 257) % 512;
+ attribute_map[type][hash] = true;
+}
+
+bool DynamicDomRestyler::checkDependency(uint attrID, AttributeDependencyType type)
+{
+ assert(type < LastAttributeDependency);
+
+ unsigned int hash = (attrID * 257) % 512;
+ // ### gives false positives, but that's okay.
+ return attribute_map[type][hash];
+}
+
+} // namespace
diff --git a/khtml/xml/dom_restyler.h b/khtml/xml/dom_restyler.h
new file mode 100644
index 000000000..eb8d795d5
--- /dev/null
+++ b/khtml/xml/dom_restyler.h
@@ -0,0 +1,102 @@
+/*
+ * This file is part of the DOM implementation for KDE.
+ *
+ * Copyright (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.
+ *
+ */
+
+#ifndef _DOM_restyler_h_
+#define _DOM_restyler_h_
+
+#include "misc/multimap.h"
+#include <bitset>
+
+namespace DOM {
+ class ElementImpl;
+}
+
+// Restyle dependency tracker for dynamic DOM
+
+namespace khtml {
+
+using DOM::ElementImpl;
+
+// These types are different types of dependencies, and serves to identify which element should be
+// restyled when a change of that kind triggers on the element
+enum StructuralDependencyType {
+ // Style relies on the children of the element (unaffected by append & close)
+ StructuralDependency = 0,
+ // Style relies on the last children of the element (affected by append & close)
+ BackwardsStructuralDependency = 1,
+ // Style relies on the element having hover
+ HoverDependency = 2,
+ // Style relies on the element being active
+ ActiveDependency = 3,
+ // Style relies on another state of element (focus, disabled, checked, etc.)
+ // (focus is special cased though since elements always depend on their own focus)
+ OtherStateDependency = 4,
+ LastStructuralDependency
+};
+
+// Attribute dependencies are much coarser than structural, for memory reasons rather than performance
+// This tracks global depencies of various kinds.
+// The groups are separated into where possible depending elements might be:
+enum AttributeDependencyType {
+ // Style of the changed element depend on this attribute
+ PersonalDependency = 0,
+ // Style of the elements children depend on this attribute
+ AncestorDependency = 1,
+ // Style of the elements later siblings or their children depend on this attribute
+ PredecessorDependency = 2,
+ LastAttributeDependency
+};
+
+
+/**
+ * @internal
+ */
+class DynamicDomRestyler {
+public:
+ DynamicDomRestyler();
+
+ // Structural dependencies are tracked from element to subject
+ void addDependency(ElementImpl* subject, ElementImpl* dependency, StructuralDependencyType type);
+ void resetDependencies(ElementImpl* subject);
+ void removeDependency(ElementImpl* subject, ElementImpl* dependency, StructuralDependencyType type);
+ void removeDependencies(ElementImpl* subject, StructuralDependencyType type);
+ void restyleDepedent(ElementImpl* dependency, StructuralDependencyType type);
+
+ // Attribute dependencies are traced on attribute alone
+ void addDependency(uint attrID, AttributeDependencyType type);
+ bool checkDependency(uint attrID, AttributeDependencyType type);
+
+ void dumpStats() const;
+private:
+ // Map of dependencies.
+ KMultiMap<ElementImpl> dependency_map[LastStructuralDependency];
+ // Map of reverse dependencies. For fast reset
+ KMultiMap<ElementImpl> reverse_map;
+
+ // Map of the various attribute dependencies
+ std::bitset<512> attribute_map[LastAttributeDependency];
+};
+
+}
+
+#endif
+
diff --git a/khtml/xml/dom_stringimpl.cpp b/khtml/xml/dom_stringimpl.cpp
new file mode 100644
index 000000000..cb32f5903
--- /dev/null
+++ b/khtml/xml/dom_stringimpl.cpp
@@ -0,0 +1,460 @@
+/**
+ * This file is part of the DOM implementation for KDE.
+ *
+ * Copyright (C) 1999-2003 Lars Knoll (knoll@kde.org)
+ * (C) 1999 Antti Koivisto (koivisto@kde.org)
+ * (C) 2001-2003 Dirk Mueller ( mueller@kde.org )
+ * (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.
+ *
+ */
+
+#include "dom_stringimpl.h"
+
+#include <kdebug.h>
+
+#include <string.h>
+#include <qstringlist.h>
+
+using namespace DOM;
+using namespace khtml;
+
+
+DOMStringImpl::DOMStringImpl(const char *str)
+{
+ if(str && *str)
+ {
+ l = strlen(str);
+ s = QT_ALLOC_QCHAR_VEC( l );
+ int i = l;
+ QChar* ptr = s;
+ while( i-- )
+ *ptr++ = *str++;
+ }
+ else
+ {
+ s = QT_ALLOC_QCHAR_VEC( 1 ); // crash protection
+ s[0] = 0x0; // == QChar::null;
+ l = 0;
+ }
+}
+
+// FIXME: should be a cached flag maybe.
+bool DOMStringImpl::containsOnlyWhitespace() const
+{
+ if (!s)
+ return true;
+
+ for (uint i = 0; i < l; i++) {
+ QChar c = s[i];
+ if (c.unicode() <= 0x7F) {
+ if (c.unicode() > ' ')
+ return false;
+ } else {
+ if (c.direction() != QChar::DirWS)
+ return false;
+ }
+ }
+ return true;
+}
+
+
+void DOMStringImpl::append(DOMStringImpl *str)
+{
+ if(str && str->l != 0)
+ {
+ int newlen = l+str->l;
+ QChar *c = QT_ALLOC_QCHAR_VEC(newlen);
+ memcpy(c, s, l*sizeof(QChar));
+ memcpy(c+l, str->s, str->l*sizeof(QChar));
+ if(s) QT_DELETE_QCHAR_VEC(s);
+ s = c;
+ l = newlen;
+ }
+}
+
+void DOMStringImpl::insert(DOMStringImpl *str, unsigned int pos)
+{
+ if(pos > l)
+ {
+ append(str);
+ return;
+ }
+ if(str && str->l != 0)
+ {
+ int newlen = l+str->l;
+ QChar *c = QT_ALLOC_QCHAR_VEC(newlen);
+ memcpy(c, s, pos*sizeof(QChar));
+ memcpy(c+pos, str->s, str->l*sizeof(QChar));
+ memcpy(c+pos+str->l, s+pos, (l-pos)*sizeof(QChar));
+ if(s) QT_DELETE_QCHAR_VEC(s);
+ s = c;
+ l = newlen;
+ }
+}
+
+void DOMStringImpl::truncate(int len)
+{
+ if(len > (int)l) return;
+
+ int nl = len < 1 ? 1 : len;
+ QChar *c = QT_ALLOC_QCHAR_VEC(nl);
+ memcpy(c, s, nl*sizeof(QChar));
+ if(s) QT_DELETE_QCHAR_VEC(s);
+ s = c;
+ l = len;
+}
+
+void DOMStringImpl::remove(unsigned int pos, int len)
+{
+ if(pos >= l ) return;
+ if(pos+len > l)
+ len = l - pos;
+
+ uint newLen = l-len;
+ QChar *c = QT_ALLOC_QCHAR_VEC(newLen);
+ memcpy(c, s, pos*sizeof(QChar));
+ memcpy(c+pos, s+pos+len, (l-len-pos)*sizeof(QChar));
+ if(s) QT_DELETE_QCHAR_VEC(s);
+ s = c;
+ l = newLen;
+}
+
+DOMStringImpl *DOMStringImpl::split(unsigned int pos)
+{
+ if( pos >=l ) return new DOMStringImpl();
+
+ uint newLen = l-pos;
+ DOMStringImpl *str = new DOMStringImpl(s + pos, newLen);
+ truncate(pos);
+ return str;
+}
+
+DOMStringImpl *DOMStringImpl::substring(unsigned int pos, unsigned int len)
+{
+ if( pos >=l ) return new DOMStringImpl();
+ if(pos+len > l)
+ len = l - pos;
+
+ return new DOMStringImpl(s + pos, len);
+}
+
+// Collapses white-space according to CSS 2.1 rules
+DOMStringImpl *DOMStringImpl::collapseWhiteSpace(bool preserveLF, bool preserveWS)
+{
+ if (preserveLF && preserveWS) return this;
+
+ // Notice we are likely allocating more space than needed (worst case)
+ QChar *n = QT_ALLOC_QCHAR_VEC(l);
+
+ unsigned int pos = 0;
+ bool collapsing = false; // collapsing white-space
+ bool collapsingLF = false; // collapsing around linefeed
+ bool changedLF = false;
+ for(unsigned int i=0; i<l; i++) {
+ QChar ch = s[i];
+
+ // We act on \r as we would on \n because CSS uses it to indicate new-line
+ if (ch == '\r') ch = '\n';
+ else
+ // ### The XML parser lets \t through, for now treat them as spaces
+ if (ch == '\t') ch = ' ';
+
+ if (!preserveLF && ch == '\n') {
+ // ### Not strictly correct according to CSS3 text-module.
+ // - In ideographic languages linefeed should be ignored
+ // - and in Thai and Khmer it should be treated as a zero-width space
+ ch = ' '; // Treat as space
+ changedLF = true;
+ }
+
+ if (collapsing) {
+ if (ch == ' ')
+ continue;
+ if (ch == '\n') {
+ collapsingLF = true;
+ continue;
+ }
+
+ n[pos++] = (collapsingLF) ? '\n' : ' ';
+ collapsing = false;
+ collapsingLF = false;
+ }
+ else
+ if (!preserveWS && ch == ' ') {
+ collapsing = true;
+ continue;
+ }
+ else
+ if (!preserveWS && ch == '\n') {
+ collapsing = true;
+ collapsingLF = true;
+ continue;
+ }
+
+ n[pos++] = ch;
+ }
+ if (collapsing)
+ n[pos++] = ((collapsingLF) ? '\n' : ' ');
+
+ if (pos == l && !changedLF) {
+ QT_DELETE_QCHAR_VEC(n);
+ return this;
+ }
+ else {
+ DOMStringImpl* out = new DOMStringImpl();
+ out->s = n;
+ out->l = pos;
+
+ return out;
+ }
+}
+
+static Length parseLength(const QChar *s, unsigned int l)
+{
+ if (l == 0) {
+ return Length(1, Relative);
+ }
+
+ unsigned i = 0;
+ while (i < l && s[i].isSpace())
+ ++i;
+ if (i < l && (s[i] == '+' || s[i] == '-'))
+ ++i;
+ while (i < l && s[i].isDigit())
+ ++i;
+
+ bool ok;
+ int r = QConstString(s, i).string().toInt(&ok);
+
+ /* Skip over any remaining digits, we are not that accurate (5.5% => 5%) */
+ while (i < l && (s[i].isDigit() || s[i] == '.'))
+ ++i;
+
+ /* IE Quirk: Skip any whitespace (20 % => 20%) */
+ while (i < l && s[i].isSpace())
+ ++i;
+
+ if (ok) {
+ if (i == l) {
+ return Length(r, Fixed);
+ } else {
+ const QChar* next = s+i;
+
+ if (*next == '%')
+ return Length(r, Percent);
+
+ if (*next == '*')
+ return Length(r, Relative);
+ }
+ return Length(r, Fixed);
+ } else {
+ if (i < l) {
+ const QChar* next = s+i;
+
+ if (*next == '*')
+ return Length(1, Relative);
+
+ if (*next == '%')
+ return Length(1, Relative);
+ }
+ }
+ return Length(0, Relative);
+}
+
+khtml::Length* DOMStringImpl::toCoordsArray(int& len) const
+{
+ QString str(s, l);
+ for(unsigned int i=0; i < l; i++) {
+ QChar cc = s[i];
+ if (cc > '9' || (cc < '0' && cc != '-' && cc != '*' && cc != '.'))
+ str[i] = ' ';
+ }
+ str = str.simplifyWhiteSpace();
+
+ len = str.contains(' ') + 1;
+ khtml::Length* r = new khtml::Length[len];
+
+ int i = 0;
+ int pos = 0;
+ int pos2;
+
+ while((pos2 = str.find(' ', pos)) != -1) {
+ r[i++] = parseLength((QChar *) str.unicode()+pos, pos2-pos);
+ pos = pos2+1;
+ }
+ r[i] = parseLength((QChar *) str.unicode()+pos, str.length()-pos);
+
+ return r;
+}
+
+khtml::Length* DOMStringImpl::toLengthArray(int& len) const
+{
+ QString str(s, l);
+ str = str.simplifyWhiteSpace();
+
+ len = str.contains(',') + 1;
+
+ // If we have no commas, we have no array.
+ if( len == 1 )
+ return 0L;
+
+ khtml::Length* r = new khtml::Length[len];
+
+ int i = 0;
+ int pos = 0;
+ int pos2;
+
+ while((pos2 = str.find(',', pos)) != -1) {
+ r[i++] = parseLength((QChar *) str.unicode()+pos, pos2-pos);
+ pos = pos2+1;
+ }
+
+ /* IE Quirk: If the last comma is the last char skip it and reduce len by one */
+ if (str.length()-pos > 0)
+ r[i] = parseLength((QChar *) str.unicode()+pos, str.length()-pos);
+ else
+ len--;
+
+ return r;
+}
+
+bool DOMStringImpl::isLower() const
+{
+ unsigned int i;
+ for (i = 0; i < l; i++)
+ if (s[i].lower() != s[i])
+ return false;
+ return true;
+}
+
+DOMStringImpl *DOMStringImpl::lower() const
+{
+ DOMStringImpl *c = new DOMStringImpl;
+ if(!l) return c;
+
+ c->s = QT_ALLOC_QCHAR_VEC(l);
+ c->l = l;
+
+ for (unsigned int i = 0; i < l; i++)
+ c->s[i] = s[i].lower();
+
+ return c;
+}
+
+DOMStringImpl *DOMStringImpl::upper() const
+{
+ DOMStringImpl *c = new DOMStringImpl;
+ if(!l) return c;
+
+ c->s = QT_ALLOC_QCHAR_VEC(l);
+ c->l = l;
+
+ for (unsigned int i = 0; i < l; i++)
+ c->s[i] = s[i].upper();
+
+ return c;
+}
+
+DOMStringImpl *DOMStringImpl::capitalize(bool noFirstCap) const
+{
+ bool canCapitalize= !noFirstCap;
+ DOMStringImpl *c = new DOMStringImpl;
+ if(!l) return c;
+
+ c->s = QT_ALLOC_QCHAR_VEC(l);
+ c->l = l;
+
+ for (unsigned int i=0; i<l; i++)
+ {
+ if (s[i].isLetterOrNumber() && canCapitalize)
+ {
+ c->s[i]=s[i].upper();
+ canCapitalize=false;
+ }
+ else
+ {
+ c->s[i]=s[i];
+ if (s[i].isSpace())
+ canCapitalize=true;
+ }
+ }
+
+ return c;
+}
+
+QString DOMStringImpl::string() const
+{
+ return QString(s, l);
+}
+
+int DOMStringImpl::toInt(bool* ok) const
+{
+ // match \s*[+-]?\d*
+ unsigned i = 0;
+ while (i < l && s[i].isSpace())
+ ++i;
+ if (i < l && (s[i] == '+' || s[i] == '-'))
+ ++i;
+ while (i < l && s[i].isDigit())
+ ++i;
+
+ return QConstString(s, i).string().toInt(ok);
+}
+
+static const unsigned short amp[] = {'&', 'a', 'm', 'p', ';'};
+static const unsigned short lt[] = {'&', 'l', 't', ';'};
+static const unsigned short gt[] = {'&', 'g', 't', ';'};
+
+DOMStringImpl *DOMStringImpl::escapeHTML()
+{
+ unsigned outL = 0;
+ for (unsigned int i = 0; i < l; ++i ) {
+ if ( s[i] == '&' )
+ outL += 5; //&amp;
+ else if (s[i] == '<' || s[i] == '>')
+ outL += 4; //&gt;/&lt;
+ else
+ ++outL;
+ }
+ if (outL == l)
+ return this;
+
+
+ DOMStringImpl* toRet = new DOMStringImpl();
+ toRet->s = QT_ALLOC_QCHAR_VEC(outL);
+ toRet->l = outL;
+
+ unsigned outP = 0;
+ for (unsigned int i = 0; i < l; ++i ) {
+ if ( s[i] == '&' ) {
+ memcpy(&toRet->s[outP], amp, sizeof(amp));
+ outP += 5;
+ } else if (s[i] == '<') {
+ memcpy(&toRet->s[outP], lt, sizeof(lt));
+ outP += 4;
+ } else if (s[i] == '>') {
+ memcpy(&toRet->s[outP], gt, sizeof(gt));
+ outP += 4;
+ } else {
+ toRet->s[outP] = s[i];
+ ++outP;
+ }
+ }
+ return toRet;
+}
+
diff --git a/khtml/xml/dom_stringimpl.h b/khtml/xml/dom_stringimpl.h
new file mode 100644
index 000000000..b1430bc89
--- /dev/null
+++ b/khtml/xml/dom_stringimpl.h
@@ -0,0 +1,104 @@
+/*
+ * This file is part of the DOM implementation for KDE.
+ *
+ * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
+ * Copyright (C) 2003 Dirk Mueller (mueller@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.
+ *
+ */
+#ifndef _DOM_DOMStringImpl_h_
+#define _DOM_DOMStringImpl_h_
+
+#include <qstring.h>
+
+#include "dom/dom_misc.h"
+#include "misc/khtmllayout.h"
+#include "misc/shared.h"
+
+#define QT_ALLOC_QCHAR_VEC( N ) (QChar*) new char[ sizeof(QChar)*( N ) ]
+#define QT_DELETE_QCHAR_VEC( P ) delete[] ((char*)( P ))
+
+namespace DOM {
+
+class DOMStringImpl : public khtml::Shared<DOMStringImpl>
+{
+private:
+ DOMStringImpl(const DOMStringImpl&);
+ DOMStringImpl& operator=(const DOMStringImpl&);
+protected:
+ DOMStringImpl() { s = 0, l = 0; }
+public:
+ DOMStringImpl(const QChar *str, unsigned int len) {
+ bool havestr = str && len;
+ s = QT_ALLOC_QCHAR_VEC( havestr ? len : 1 );
+ if(str && len) {
+ memcpy( s, str, len * sizeof(QChar) );
+ l = len;
+ } else {
+ // crash protection
+ s[0] = 0x0;
+ l = 0;
+ }
+ }
+
+ explicit DOMStringImpl(const char *str);
+ explicit DOMStringImpl(const QChar &ch) {
+ s = QT_ALLOC_QCHAR_VEC( 1 );
+ s[0] = ch;
+ l = 1;
+ }
+ ~DOMStringImpl() {
+ if(s) QT_DELETE_QCHAR_VEC(s);
+ }
+
+ void append(DOMStringImpl *str);
+ void insert(DOMStringImpl *str, unsigned int pos);
+ void truncate(int len);
+ void remove(unsigned int pos, int len=1);
+ DOMStringImpl *split(unsigned int pos);
+ DOMStringImpl *copy() const {
+ return new DOMStringImpl(s,l);
+ };
+
+
+ DOMStringImpl *substring(unsigned int pos, unsigned int len);
+ DOMStringImpl *collapseWhiteSpace(bool preserveLF, bool preserveWS);
+
+ const QChar &operator [] (int pos) { return s[pos]; }
+ bool containsOnlyWhitespace() const;
+
+ // ignores trailing garbage, unlike QString
+ int toInt(bool* ok = 0) const;
+
+ khtml::Length* toLengthArray(int& len) const;
+ khtml::Length* toCoordsArray(int& len) const;
+ bool isLower() const;
+ DOMStringImpl *lower() const;
+ DOMStringImpl *upper() const;
+ DOMStringImpl *capitalize(bool noFirstCap=false) const;
+ DOMStringImpl *escapeHTML();
+
+ QChar *unicode() const { return s; }
+ uint length() const { return l; }
+ QString string() const;
+
+ unsigned int l;
+ QChar *s;
+};
+
+}
+#endif
diff --git a/khtml/xml/dom_textimpl.cpp b/khtml/xml/dom_textimpl.cpp
new file mode 100644
index 000000000..015135428
--- /dev/null
+++ b/khtml/xml/dom_textimpl.cpp
@@ -0,0 +1,522 @@
+/**
+ * This file is part of the DOM implementation for KDE.
+ *
+ * Copyright (C) 1999-2003 Lars Knoll (knoll@kde.org)
+ * (C) 2001-2003 Dirk Mueller (mueller@kde.org)
+ * (C) 1999 Antti Koivisto (koivisto@kde.org)
+ * (C) 2002-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 "css/cssstyleselector.h"
+#include "xml/dom2_eventsimpl.h"
+#include "xml/dom_textimpl.h"
+#include "xml/dom_docimpl.h"
+
+#include "misc/htmlhashes.h"
+#include "rendering/render_text.h"
+#include "rendering/render_flow.h"
+
+#include <kdebug.h>
+
+using namespace DOM;
+using namespace khtml;
+
+static DOMString escapeHTML( const DOMString& in )
+{
+ return in.implementation()->escapeHTML();
+}
+
+CharacterDataImpl::CharacterDataImpl(DocumentImpl *doc, DOMStringImpl* _text)
+ : NodeImpl(doc)
+{
+ str = _text ? _text : new DOMStringImpl(0, 0);
+ str->ref();
+}
+
+CharacterDataImpl::~CharacterDataImpl()
+{
+ if(str) str->deref();
+}
+
+void CharacterDataImpl::setData( const DOMString &_data, int &exceptioncode )
+{
+ // NO_MODIFICATION_ALLOWED_ERR: Raised when the node is readonly
+ if (isReadOnly()) {
+ exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR;
+ return;
+ }
+
+ if(str == _data.impl) return; // ### fire DOMCharacterDataModified if modified?
+ DOMStringImpl *oldStr = str;
+ str = _data.impl;
+ if(str) str->ref();
+ if (m_render)
+ (static_cast<RenderText*>(m_render))->setText(str);
+ setChanged(true);
+
+ dispatchModifiedEvent(oldStr);
+ if(oldStr) oldStr->deref();
+}
+
+unsigned long CharacterDataImpl::length() const
+{
+ return str->l;
+}
+
+DOMString CharacterDataImpl::substringData( const unsigned long offset, const unsigned long count, int &exceptioncode )
+{
+ exceptioncode = 0;
+ if ((long)count < 0)
+ exceptioncode = DOMException::INDEX_SIZE_ERR;
+ else
+ checkCharDataOperation(offset, exceptioncode);
+ if (exceptioncode)
+ return DOMString();
+
+ return str->substring(offset,count);
+}
+
+void CharacterDataImpl::appendData( const DOMString &arg, int &exceptioncode )
+{
+ exceptioncode = 0;
+
+ // NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly
+ if (isReadOnly()) {
+ exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR;
+ return;
+ }
+
+ DOMStringImpl *oldStr = str;
+ str = str->copy();
+ str->ref();
+ str->append(arg.impl);
+ if (m_render)
+ (static_cast<RenderText*>(m_render))->setText(str);
+ setChanged(true);
+
+ dispatchModifiedEvent(oldStr);
+ oldStr->deref();
+}
+
+void CharacterDataImpl::insertData( const unsigned long offset, const DOMString &arg, int &exceptioncode )
+{
+ exceptioncode = 0;
+ checkCharDataOperation(offset, exceptioncode);
+ if (exceptioncode)
+ return;
+
+ DOMStringImpl *oldStr = str;
+ str = str->copy();
+ str->ref();
+ str->insert(arg.impl, offset);
+ if (m_render)
+ (static_cast<RenderText*>(m_render))->setText(str);
+ setChanged(true);
+
+ dispatchModifiedEvent(oldStr);
+ oldStr->deref();
+}
+
+void CharacterDataImpl::deleteData( const unsigned long offset, const unsigned long count, int &exceptioncode )
+{
+ exceptioncode = 0;
+ if ((long)count < 0)
+ exceptioncode = DOMException::INDEX_SIZE_ERR;
+ else
+ checkCharDataOperation(offset, exceptioncode);
+ if (exceptioncode)
+ return;
+
+ DOMStringImpl *oldStr = str;
+ str = str->copy();
+ str->ref();
+ str->remove(offset,count);
+ if (m_render)
+ (static_cast<RenderText*>(m_render))->setText(str);
+ setChanged(true);
+
+ dispatchModifiedEvent(oldStr);
+ oldStr->deref();
+}
+
+void CharacterDataImpl::replaceData( const unsigned long offset, const unsigned long count, const DOMString &arg, int &exceptioncode )
+{
+ exceptioncode = 0;
+ if ((long)count < 0)
+ exceptioncode = DOMException::INDEX_SIZE_ERR;
+ else
+ checkCharDataOperation(offset, exceptioncode);
+ if (exceptioncode)
+ return;
+
+ unsigned long realCount;
+ if (offset + count > str->l)
+ realCount = str->l-offset;
+ else
+ realCount = count;
+
+ DOMStringImpl *oldStr = str;
+ str = str->copy();
+ str->ref();
+ str->remove(offset,realCount);
+ str->insert(arg.impl, offset);
+ if (m_render)
+ (static_cast<RenderText*>(m_render))->setText(str);
+ setChanged(true);
+
+ dispatchModifiedEvent(oldStr);
+ oldStr->deref();
+}
+
+DOMString CharacterDataImpl::nodeValue() const
+{
+ return str;
+}
+
+bool CharacterDataImpl::containsOnlyWhitespace() const
+{
+ return str->containsOnlyWhitespace();
+}
+
+void CharacterDataImpl::setNodeValue( const DOMString &_nodeValue, int &exceptioncode )
+{
+ // NO_MODIFICATION_ALLOWED_ERR: taken care of by setData()
+ setData(_nodeValue, exceptioncode);
+}
+
+void CharacterDataImpl::dispatchModifiedEvent(DOMStringImpl *prevValue)
+{
+ if (parentNode())
+ parentNode()->childrenChanged();
+ if (!getDocument()->hasListenerType(DocumentImpl::DOMCHARACTERDATAMODIFIED_LISTENER))
+ return;
+
+ DOMStringImpl *newValue = str->copy();
+ newValue->ref();
+ int exceptioncode = 0;
+ MutationEventImpl* const evt = new MutationEventImpl(EventImpl::DOMCHARACTERDATAMODIFIED_EVENT,true,false,0,prevValue,newValue,DOMString(),0);
+ evt->ref();
+ dispatchEvent(evt,exceptioncode);
+ evt->deref();
+ newValue->deref();
+ dispatchSubtreeModifiedEvent();
+}
+
+void CharacterDataImpl::checkCharDataOperation( const unsigned long offset, int &exceptioncode )
+{
+ exceptioncode = 0;
+
+ // INDEX_SIZE_ERR: Raised if the specified offset is negative or greater than the number of 16-bit
+ // units in data.
+ if (offset > str->l) {
+ exceptioncode = DOMException::INDEX_SIZE_ERR;
+ return;
+ }
+
+ // NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly
+ if (isReadOnly()) {
+ exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR;
+ return;
+ }
+}
+
+long CharacterDataImpl::minOffset() const
+{
+ RenderText *r = static_cast<RenderText *>(renderer());
+ if (!r || !r->isText()) return 0;
+
+ // take :first-letter into consideration
+#ifdef __GNUC__
+#warning FIXME
+#endif
+#if 0
+ if (r->forcedMinOffset()) {
+ RenderFlow *firstLetter = static_cast<RenderFlow *>(r->previousSibling());
+ if (firstLetter && firstLetter->isFlow() && firstLetter->isFirstLetter()) {
+ RenderText *letterText = static_cast<RenderText *>(firstLetter->firstChild());
+ return letterText->minOffset();
+ }
+ }
+#endif
+
+ return r->minOffset();
+}
+
+long CharacterDataImpl::maxOffset() const
+{
+ RenderText *r = static_cast<RenderText *>(renderer());
+ if (!r || !r->isText()) return (long)length();
+ return r->maxOffset();
+}
+
+DOMStringImpl* CharacterDataImpl::textContent() const
+{
+ return new DOMStringImpl(str->s, str->l);
+}
+
+void CharacterDataImpl::setTextContent( const DOMString &str, int& exceptioncode )
+{
+ setData(str, exceptioncode);
+}
+
+// ---------------------------------------------------------------------------
+
+DOMString CommentImpl::nodeName() const
+{
+ return "#comment";
+}
+
+unsigned short CommentImpl::nodeType() const
+{
+ return Node::COMMENT_NODE;
+}
+
+NodeImpl *CommentImpl::cloneNode(bool /*deep*/)
+{
+ return getDocument()->createComment( str );
+}
+
+NodeImpl::Id CommentImpl::id() const
+{
+ return ID_COMMENT;
+}
+
+// DOM Section 1.1.1
+bool CommentImpl::childTypeAllowed( unsigned short /*type*/ )
+{
+ return false;
+}
+
+DOMString CommentImpl::toString() const
+{
+ // FIXME: substitute entity references as needed!
+ return DOMString("<!--") + escapeHTML( nodeValue() ) + "-->";
+}
+
+// ---------------------------------------------------------------------------
+
+TextImpl *TextImpl::splitText( const unsigned long offset, int &exceptioncode )
+{
+ exceptioncode = 0;
+
+ // INDEX_SIZE_ERR: Raised if the specified offset is negative or greater than
+ // the number of 16-bit units in data.
+
+ // ### we explicitly check for a negative long that has been cast to an unsigned long
+ // ... this can happen if JS code passes in -1 - we need to catch this earlier! (in the
+ // kjs bindings)
+ if (offset > str->l || (long)offset < 0) {
+ exceptioncode = DOMException::INDEX_SIZE_ERR;
+ return 0;
+ }
+
+ // NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly.
+ if (isReadOnly()) {
+ exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR;
+ return 0;
+ }
+
+ DOMStringImpl *oldStr = str;
+ TextImpl *newText = createNew(str->substring(offset,str->l-offset));
+ str = str->copy();
+ str->ref();
+ str->remove(offset,str->l-offset);
+
+ dispatchModifiedEvent(oldStr);
+ oldStr->deref();
+
+ if (parentNode())
+ parentNode()->insertBefore(newText,nextSibling(), exceptioncode );
+ if ( exceptioncode )
+ return 0;
+
+ if (m_render)
+ (static_cast<RenderText*>(m_render))->setText(str);
+ setChanged(true);
+ return newText;
+}
+
+DOMString TextImpl::nodeName() const
+{
+ return "#text";
+}
+
+unsigned short TextImpl::nodeType() const
+{
+ return Node::TEXT_NODE;
+}
+
+NodeImpl *TextImpl::cloneNode(bool /*deep*/)
+{
+ return getDocument()->createTextNode(str);
+}
+
+bool TextImpl::rendererIsNeeded(RenderStyle *style)
+{
+ if (!CharacterDataImpl::rendererIsNeeded(style)) {
+ return false;
+ }
+ bool onlyWS = containsOnlyWhitespace();
+ if (!onlyWS) {
+ return true;
+ }
+
+ RenderObject *par = parentNode()->renderer();
+
+ if (par->isTable() || par->isTableRow() || par->isTableSection()) {
+ return false;
+ }
+
+ if (style->preserveWS() || style->preserveLF()) {
+ return true;
+ }
+
+ RenderObject *prev = previousRenderer();
+ if (par->isInlineFlow()) {
+ // <span><div/> <div/></span>
+ if (prev && !prev->isInline()) {
+ return false;
+ }
+ } else {
+ if (par->isRenderBlock() && !par->childrenInline() && (!prev || !prev->isInline())) {
+ return false;
+ }
+
+ RenderObject *first = par->firstChild();
+ while (first && first->isFloatingOrPositioned())
+ first = first->nextSibling();
+ RenderObject *next = nextRenderer();
+ if (!first || next == first) {
+ // Whitespace at the start of a block just goes away. Don't even
+ // make a render object for this text.
+ return false;
+ }
+ }
+
+ return true;
+}
+
+RenderObject *TextImpl::createRenderer(RenderArena *arena, RenderStyle *style)
+{
+ return new (arena) RenderText(this, str);
+}
+
+void TextImpl::attach()
+{
+ createRendererIfNeeded();
+ CharacterDataImpl::attach();
+}
+
+NodeImpl::Id TextImpl::id() const
+{
+ return ID_TEXT;
+}
+
+void TextImpl::recalcStyle( StyleChange change )
+{
+// qDebug("textImpl::recalcStyle");
+ // Create renderer if now needed
+ if ( changed() && !m_render) {
+ createRendererIfNeeded();
+ }
+ if (change != NoChange && parentNode()) {
+// qDebug("DomText::recalcStyle");
+ if(m_render)
+ m_render->setStyle(parentNode()->renderer()->style());
+ }
+ if ( changed() && m_render && m_render->isText() )
+ static_cast<RenderText*>(m_render)->setText(str);
+ setChanged( false );
+}
+
+// DOM Section 1.1.1
+bool TextImpl::childTypeAllowed( unsigned short /*type*/ )
+{
+ return false;
+}
+
+TextImpl *TextImpl::createNew(DOMStringImpl *_str)
+{
+ return new TextImpl(docPtr(),_str);
+}
+
+DOMStringImpl* TextImpl::renderString() const
+{
+ if (renderer())
+ return static_cast<RenderText*>(renderer())->string();
+ else
+ return string();
+}
+
+DOMString TextImpl::toString() const
+{
+ // FIXME: substitute entity references as needed!
+ return escapeHTML( nodeValue() );
+}
+
+DOMString TextImpl::toString(long long startOffset, long long endOffset) const
+{
+ // FIXME: substitute entity references as needed!
+
+ DOMString str = nodeValue();
+ if(endOffset >=0 || startOffset >0)
+ str = str.copy(); //we are going to modify this, so make a copy. I hope I'm doing this right.
+ if(endOffset >= 0)
+ str.truncate(endOffset);
+ if(startOffset > 0) //note the order of these 2 'if' statements so that it works right when n==m_startContainer==m_endContainer
+ str.remove(0, startOffset);
+ return escapeHTML( str );
+}
+
+// ---------------------------------------------------------------------------
+
+DOMString CDATASectionImpl::nodeName() const
+{
+ return "#cdata-section";
+}
+
+unsigned short CDATASectionImpl::nodeType() const
+{
+ return Node::CDATA_SECTION_NODE;
+}
+
+NodeImpl *CDATASectionImpl::cloneNode(bool /*deep*/)
+{
+ return getDocument()->createCDATASection(str);
+}
+
+// DOM Section 1.1.1
+bool CDATASectionImpl::childTypeAllowed( unsigned short /*type*/ )
+{
+ return false;
+}
+
+TextImpl *CDATASectionImpl::createNew(DOMStringImpl *_str)
+{
+ return new CDATASectionImpl(docPtr(),_str);
+}
+
+DOMString CDATASectionImpl::toString() const
+{
+ // FIXME: substitute entity references as needed!
+ return DOMString("<![CDATA[") + nodeValue() + "]]>";
+}
+
+
+
diff --git a/khtml/xml/dom_textimpl.h b/khtml/xml/dom_textimpl.h
new file mode 100644
index 000000000..07e4909f6
--- /dev/null
+++ b/khtml/xml/dom_textimpl.h
@@ -0,0 +1,176 @@
+/*
+ * This file is part of the DOM implementation for KDE.
+ *
+ * Copyright (C) 1999-2003 Lars Knoll (knoll@kde.org)
+ * (C) 2001-2003 Dirk Mueller (mueller@kde.org)
+ * (C) 1999 Antti Koivisto (koivisto@kde.org)
+ * (C) 2003 Apple Computer, Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+#ifndef _DOM_CharacterDataImpl_h_
+#define _DOM_CharacterDataImpl_h_
+
+#include "xml/dom_nodeimpl.h"
+#include "dom/dom_string.h"
+
+namespace DOM {
+
+ class DocumentImpl;
+ class CharacterData;
+ class Text;
+
+class CharacterDataImpl : public NodeImpl
+{
+public:
+ CharacterDataImpl(DocumentImpl *doc, DOMStringImpl* _text);
+ CharacterDataImpl(DocumentImpl *doc)
+ : NodeImpl(doc), str(0) {}
+
+ virtual ~CharacterDataImpl();
+
+ // DOM methods & attributes for CharacterData
+
+ virtual void setData( const DOMString &_data, int &exceptioncode );
+ virtual unsigned long length ( ) const;
+ virtual DOMString substringData ( const unsigned long offset, const unsigned long count, int &exceptioncode );
+ virtual void appendData ( const DOMString &arg, int &exceptioncode );
+ virtual void insertData ( const unsigned long offset, const DOMString &arg, int &exceptioncode );
+ virtual void deleteData ( const unsigned long offset, const unsigned long count, int &exceptioncode );
+ virtual void replaceData ( const unsigned long offset, const unsigned long count, const DOMString &arg, int &exceptioncode );
+
+ virtual bool containsOnlyWhitespace() const;
+
+ // DOM methods overridden from parent classes
+
+ virtual DOMString nodeValue() const;
+ virtual void setNodeValue( const DOMString &_nodeValue, int &exceptioncode );
+
+ virtual DOMStringImpl* textContent() const;
+ virtual void setTextContent( const DOMString &text, int& exceptioncode );
+
+ // Other methods (not part of DOM)
+
+ DOMStringImpl *string() const { return str; }
+ DOMString data() const { return str; }
+
+ virtual void checkCharDataOperation( const unsigned long offset, int &exceptioncode );
+
+ virtual long minOffset() const;
+ virtual long maxOffset() const;
+
+protected:
+ // note: since DOMStrings are shared, str should always be copied when making
+ // a change or returning a string
+ DOMStringImpl *str;
+
+ void dispatchModifiedEvent(DOMStringImpl *prevValue);
+};
+
+// ----------------------------------------------------------------------------
+
+class CommentImpl : public CharacterDataImpl
+{
+public:
+ CommentImpl(DocumentImpl *doc, DOMStringImpl* _text)
+ : CharacterDataImpl(doc, _text) {}
+ CommentImpl(DocumentImpl *doc)
+ : CharacterDataImpl(doc) {}
+ // DOM methods overridden from parent classes
+ virtual DOMString nodeName() const;
+ virtual unsigned short nodeType() const;
+ virtual NodeImpl *cloneNode(bool deep);
+
+ // Other methods (not part of DOM)
+
+ virtual Id id() const;
+ virtual bool childTypeAllowed( unsigned short type );
+
+ virtual DOMString toString() const;
+};
+
+// ----------------------------------------------------------------------------
+
+class TextImpl : public CharacterDataImpl
+{
+public:
+ TextImpl(DocumentImpl *impl, DOMStringImpl* _text)
+ : CharacterDataImpl(impl, _text) {}
+ TextImpl(DocumentImpl *impl)
+ : CharacterDataImpl(impl) {}
+
+ // DOM methods & attributes for CharacterData
+
+ TextImpl *splitText ( const unsigned long offset, int &exceptioncode );
+
+ // DOM methods overridden from parent classes
+ virtual DOMString nodeName() const;
+ virtual unsigned short nodeType() const;
+ virtual NodeImpl *cloneNode(bool deep);
+
+ // Other methods (not part of DOM)
+
+ virtual bool isTextNode() const { return true; }
+ virtual Id id() const;
+ virtual void attach();
+ virtual bool rendererIsNeeded(khtml::RenderStyle *);
+ virtual khtml::RenderObject *createRenderer(khtml::RenderArena *, khtml::RenderStyle *);
+ virtual void recalcStyle( StyleChange = NoChange );
+ virtual bool childTypeAllowed( unsigned short type );
+
+ DOMStringImpl *renderString() const;
+
+ virtual DOMString toString() const;
+ /** Return the text for the node, with < replaced with &lt; and so on.
+ * @param startOffset The number of characters counted from the left, zero indexed, counting "<" as one character, to start from. Use -1 to start from 0.
+ * @param endOffset The number of characters counted from the left, zero indexed, counting "<" as one character, to end on. Use -1 to end at the end of the string.
+ * @return An html escaped version of the substring.
+ */
+ DOMString toString(long long startOffset, long long endOffset) const;
+protected:
+ virtual TextImpl *createNew(DOMStringImpl *_str);
+};
+
+// ----------------------------------------------------------------------------
+
+class CDATASectionImpl : public TextImpl
+{
+public:
+ CDATASectionImpl(DocumentImpl *impl, DOMStringImpl* _text)
+ : TextImpl(impl, _text) {}
+ CDATASectionImpl(DocumentImpl *impl)
+ : TextImpl(impl) {}
+
+ // DOM methods overridden from parent classes
+ virtual DOMString nodeName() const;
+ virtual unsigned short nodeType() const;
+ virtual NodeImpl *cloneNode(bool deep);
+
+ // Other methods (not part of DOM)
+
+ virtual bool childTypeAllowed( unsigned short type );
+
+ virtual DOMString toString() const;
+
+protected:
+ virtual TextImpl *createNew(DOMStringImpl *_str);
+};
+
+
+
+} //namespace
+#endif
diff --git a/khtml/xml/dom_xmlimpl.cpp b/khtml/xml/dom_xmlimpl.cpp
new file mode 100644
index 000000000..90ec2b352
--- /dev/null
+++ b/khtml/xml/dom_xmlimpl.cpp
@@ -0,0 +1,498 @@
+/**
+ * This file is part of the DOM implementation for KDE.
+ *
+ * Copyright (C) 2000 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_xmlimpl.h"
+#include "xml/dom_docimpl.h"
+#include "xml/dom_stringimpl.h"
+#include "css/css_stylesheetimpl.h"
+#include "misc/loader.h"
+
+using namespace DOM;
+
+EntityImpl::EntityImpl(DocumentImpl *doc) : NodeBaseImpl(doc)
+{
+ m_publicId = 0;
+ m_systemId = 0;
+ m_notationName = 0;
+ m_name = 0;
+}
+
+EntityImpl::EntityImpl(DocumentImpl *doc, DOMString _name) : NodeBaseImpl(doc)
+{
+ m_publicId = 0;
+ m_systemId = 0;
+ m_notationName = 0;
+ m_name = _name.implementation();
+ if (m_name)
+ m_name->ref();
+}
+
+EntityImpl::EntityImpl(DocumentImpl *doc, DOMString _publicId, DOMString _systemId, DOMString _notationName) : NodeBaseImpl(doc)
+{
+ m_publicId = _publicId.implementation();
+ if (m_publicId)
+ m_publicId->ref();
+ m_systemId = _systemId.implementation();
+ if (m_systemId)
+ m_systemId->ref();
+ m_notationName = _notationName.implementation();
+ if (m_notationName)
+ m_notationName->ref();
+ m_name = 0;
+}
+
+
+EntityImpl::~EntityImpl()
+{
+ if (m_publicId)
+ m_publicId->deref();
+ if (m_systemId)
+ m_systemId->deref();
+ if (m_notationName)
+ m_notationName->deref();
+ if (m_name)
+ m_name->deref();
+}
+
+DOMString EntityImpl::publicId() const
+{
+ return m_publicId;
+}
+
+DOMString EntityImpl::systemId() const
+{
+ return m_systemId;
+}
+
+DOMString EntityImpl::notationName() const
+{
+ return m_notationName;
+}
+
+DOMString EntityImpl::nodeName() const
+{
+ return m_name;
+}
+
+unsigned short EntityImpl::nodeType() const
+{
+ return Node::ENTITY_NODE;
+}
+
+NodeImpl *EntityImpl::cloneNode ( bool /*deep*/)
+{
+ // Spec says cloning Document nodes is "implementation dependent"
+ // so we do not support it...
+ return 0;
+}
+
+// DOM Section 1.1.1
+bool EntityImpl::childTypeAllowed( unsigned short type )
+{
+ switch (type) {
+ case Node::ELEMENT_NODE:
+ case Node::PROCESSING_INSTRUCTION_NODE:
+ case Node::COMMENT_NODE:
+ case Node::TEXT_NODE:
+ case Node::CDATA_SECTION_NODE:
+ case Node::ENTITY_REFERENCE_NODE:
+ return true;
+ break;
+ default:
+ return false;
+ }
+}
+
+DOMString EntityImpl::toString() const
+{
+ DOMString result = "<!ENTITY' ";
+
+ if (m_name && m_name->l != 0) {
+ result += " ";
+ result += m_name;
+ }
+
+ if (m_publicId && m_publicId->l != 0) {
+ result += " PUBLIC \"";
+ result += m_publicId;
+ result += "\" \"";
+ result += m_systemId;
+ result += "\"";
+ } else if (m_systemId && m_systemId->l != 0) {
+ result += " SYSTEM \"";
+ result += m_systemId;
+ result += "\"";
+ }
+
+ if (m_notationName && m_notationName->l != 0) {
+ result += " NDATA ";
+ result += m_notationName;
+ }
+
+ result += ">";
+
+ return result;
+}
+
+// -------------------------------------------------------------------------
+
+EntityReferenceImpl::EntityReferenceImpl(DocumentImpl *doc) : NodeBaseImpl(doc)
+{
+ m_entityName = 0;
+}
+
+EntityReferenceImpl::EntityReferenceImpl(DocumentImpl *doc, DOMStringImpl *_entityName) : NodeBaseImpl(doc)
+{
+ m_entityName = _entityName;
+ if (m_entityName)
+ m_entityName->ref();
+}
+
+EntityReferenceImpl::~EntityReferenceImpl()
+{
+ if (m_entityName)
+ m_entityName->deref();
+}
+
+DOMString EntityReferenceImpl::nodeName() const
+{
+ return m_entityName;
+}
+
+unsigned short EntityReferenceImpl::nodeType() const
+{
+ return Node::ENTITY_REFERENCE_NODE;
+}
+
+NodeImpl *EntityReferenceImpl::cloneNode ( bool deep )
+{
+ EntityReferenceImpl *clone = new EntityReferenceImpl(docPtr(),m_entityName);
+ // ### make sure children are readonly
+ // ### since we are a reference, should we clone children anyway (even if not deep?)
+ if (deep)
+ cloneChildNodes(clone);
+ return clone;
+}
+
+// DOM Section 1.1.1
+bool EntityReferenceImpl::childTypeAllowed( unsigned short type )
+{
+ switch (type) {
+ case Node::ELEMENT_NODE:
+ case Node::PROCESSING_INSTRUCTION_NODE:
+ case Node::COMMENT_NODE:
+ case Node::TEXT_NODE:
+ case Node::CDATA_SECTION_NODE:
+ case Node::ENTITY_REFERENCE_NODE:
+ return true;
+ break;
+ default:
+ return false;
+ }
+}
+
+DOMString EntityReferenceImpl::toString() const
+{
+ DOMString result = "&";
+ result += m_entityName;
+ result += ";";
+
+ return result;
+}
+
+// -------------------------------------------------------------------------
+
+NotationImpl::NotationImpl(DocumentImpl *doc) : NodeBaseImpl(doc)
+{
+ m_publicId = 0;
+ m_systemId = 0;
+ m_name = 0;
+}
+
+NotationImpl::NotationImpl(DocumentImpl *doc, DOMString _name, DOMString _publicId, DOMString _systemId) : NodeBaseImpl(doc)
+{
+ m_name = _name.implementation();
+ if (m_name)
+ m_name->ref();
+ m_publicId = _publicId.implementation();
+ if (m_publicId)
+ m_publicId->ref();
+ m_systemId = _systemId.implementation();
+ if (m_systemId)
+ m_systemId->ref();
+}
+
+NotationImpl::~NotationImpl()
+{
+ if (m_name)
+ m_name->deref();
+ if (m_publicId)
+ m_publicId->deref();
+ if (m_systemId)
+ m_systemId->deref();
+}
+
+DOMString NotationImpl::publicId() const
+{
+ return m_publicId;
+}
+
+DOMString NotationImpl::systemId() const
+{
+ return m_systemId;
+}
+
+DOMString NotationImpl::nodeName() const
+{
+ return m_name;
+}
+
+unsigned short NotationImpl::nodeType() const
+{
+ return Node::NOTATION_NODE;
+}
+
+NodeImpl *NotationImpl::cloneNode ( bool /*deep*/)
+{
+ // Spec says cloning Document nodes is "implementation dependent"
+ // so we do not support it...
+ return 0;
+}
+
+// DOM Section 1.1.1
+bool NotationImpl::childTypeAllowed( unsigned short /*type*/ )
+{
+ return false;
+}
+
+// -------------------------------------------------------------------------
+
+// ### need a way of updating these properly whenever child nodes of the processing instruction
+// change or are added/removed
+
+ProcessingInstructionImpl::ProcessingInstructionImpl(DocumentImpl *doc) : NodeBaseImpl(doc)
+{
+ m_target = 0;
+ m_data = 0;
+ m_localHref = 0;
+ m_sheet = 0;
+ m_cachedSheet = 0;
+}
+
+ProcessingInstructionImpl::ProcessingInstructionImpl(DocumentImpl *doc, DOMString _target, DOMString _data) : NodeBaseImpl(doc)
+{
+ m_target = _target.implementation();
+ if (m_target)
+ m_target->ref();
+ m_data = _data.implementation();
+ if (m_data)
+ m_data->ref();
+ m_sheet = 0;
+ m_cachedSheet = 0;
+ m_localHref = 0;
+}
+
+ProcessingInstructionImpl::~ProcessingInstructionImpl()
+{
+ if (m_target)
+ m_target->deref();
+ if (m_data)
+ m_data->deref();
+ if (m_cachedSheet)
+ m_cachedSheet->deref(this);
+ if (m_sheet)
+ m_sheet->deref();
+}
+
+DOMString ProcessingInstructionImpl::target() const
+{
+ return m_target;
+}
+
+void ProcessingInstructionImpl::setData( const DOMString &_data, int &exceptioncode )
+{
+ // NO_MODIFICATION_ALLOWED_ERR: Raised when the node is readonly.
+ if (isReadOnly()) {
+ exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR;
+ return;
+ }
+
+ if (m_data)
+ m_data->deref();
+ m_data = _data.implementation();
+ if (m_data)
+ m_data->ref();
+}
+
+DOMString ProcessingInstructionImpl::nodeName() const
+{
+ return m_target;
+}
+
+unsigned short ProcessingInstructionImpl::nodeType() const
+{
+ return Node::PROCESSING_INSTRUCTION_NODE;
+}
+
+DOMString ProcessingInstructionImpl::nodeValue() const
+{
+ return m_data;
+}
+
+void ProcessingInstructionImpl::setNodeValue( const DOMString &_nodeValue, int &exceptioncode )
+{
+ // NO_MODIFICATION_ALLOWED_ERR: taken care of by setData()
+ setData(_nodeValue, exceptioncode);
+}
+
+NodeImpl *ProcessingInstructionImpl::cloneNode ( bool /*deep*/)
+{
+ // ### copy m_localHref
+ return new ProcessingInstructionImpl(docPtr(),m_target,m_data);
+}
+
+DOMString ProcessingInstructionImpl::localHref() const
+{
+ return m_localHref;
+}
+
+// DOM Section 1.1.1
+bool ProcessingInstructionImpl::childTypeAllowed( unsigned short /*type*/ )
+{
+ return false;
+}
+
+void ProcessingInstructionImpl::checkStyleSheet()
+{
+ if (m_target && DOMString(m_target) == "xml-stylesheet") {
+ // see http://www.w3.org/TR/xml-stylesheet/
+ // ### check that this occurs only in the prolog
+ // ### support stylesheet included in a fragment of this (or another) document
+ // ### make sure this gets called when adding from javascript
+ XMLAttributeReader attrReader(DOMString(m_data).string());
+ bool attrsOk;
+ QXmlAttributes attrs = attrReader.readAttrs(attrsOk);
+ if (!attrsOk)
+ return;
+ if (attrs.value("type") != "text/css" && !attrs.value("type").isEmpty())
+ return;
+
+ DOMString href = attrs.value("href");
+
+ if (href.length()>1)
+ {
+ if (href[0]=='#')
+ {
+ if (m_localHref)
+ m_localHref->deref();
+ m_localHref=href.implementation()->split(1);
+ if (m_localHref)
+ m_localHref->ref();
+ }
+ else
+ {
+ // ### some validation on the URL?
+ // ### FIXME charset
+ if (m_cachedSheet) m_cachedSheet->deref(this);
+ m_cachedSheet = getDocument()->docLoader()->requestStyleSheet(getDocument()->completeURL(href.string()), QString::null);
+ if (m_cachedSheet) {
+ getDocument()->addPendingSheet(); //before ref, because during the ref it might load!
+ m_cachedSheet->ref( this );
+ }
+ }
+
+ }
+ }
+}
+
+StyleSheetImpl *ProcessingInstructionImpl::sheet() const
+{
+ return m_sheet;
+}
+
+void ProcessingInstructionImpl::setStyleSheet(const DOM::DOMString &url, const DOM::DOMString &sheet, const DOM::DOMString &charset)
+{
+ if (m_sheet)
+ m_sheet->deref();
+ m_sheet = new CSSStyleSheetImpl(getDocument(), url);
+ m_sheet->ref();
+ m_sheet->setCharset(charset);
+ m_sheet->parseString(sheet);
+ if (m_cachedSheet)
+ m_cachedSheet->deref(this);
+ m_cachedSheet = 0;
+
+ getDocument()->styleSheetLoaded();
+}
+
+void ProcessingInstructionImpl::setStyleSheet(CSSStyleSheetImpl* sheet)
+{
+ if (m_sheet)
+ m_sheet->deref();
+ m_sheet = sheet;
+ if (m_sheet)
+ m_sheet->ref();
+}
+
+DOMString ProcessingInstructionImpl::toString() const
+{
+ DOMString result = "<?";
+ result += m_target;
+ result += " ";
+ result += m_data;
+ result += ">";
+ return result;
+}
+
+// -------------------------------------------------------------------------
+
+XMLAttributeReader::XMLAttributeReader(const QString& _attrString)
+{
+ m_attrString = _attrString;
+}
+
+XMLAttributeReader::~XMLAttributeReader()
+{
+}
+
+QXmlAttributes XMLAttributeReader::readAttrs(bool &ok)
+{
+ // parse xml file
+ QXmlInputSource source;
+ source.setData("<?xml version=\"1.0\"?><attrs "+m_attrString+" />");
+ QXmlSimpleReader reader;
+ reader.setContentHandler( this );
+ ok = reader.parse( source );
+ return attrs;
+}
+
+bool XMLAttributeReader::startElement(const QString& /*namespaceURI*/, const QString& localName,
+ const QString& /*qName*/, const QXmlAttributes& atts)
+{
+ if (localName == "attrs") {
+ attrs = atts;
+ return true;
+ }
+ else
+ return false; // we shouldn't have any other elements
+}
diff --git a/khtml/xml/dom_xmlimpl.h b/khtml/xml/dom_xmlimpl.h
new file mode 100644
index 000000000..6fa125af7
--- /dev/null
+++ b/khtml/xml/dom_xmlimpl.h
@@ -0,0 +1,181 @@
+/*
+ * This file is part of the DOM implementation for KDE.
+ *
+ * Copyright (C) 2000 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_XmlImpl_h_
+#define _DOM_XmlImpl_h_
+
+#include "xml/dom_nodeimpl.h"
+#include "misc/loader_client.h"
+
+#include <qxml.h>
+
+namespace khtml {
+class CachedCSSStyleSheet;
+}
+
+namespace DOM {
+
+class DocumentImpl;
+class CSSStyleSheetImpl;
+class StyleSheetImpl;
+class DOMString;
+
+class EntityImpl : public NodeBaseImpl
+{
+public:
+ EntityImpl(DocumentImpl *doc);
+ EntityImpl(DocumentImpl *doc, DOMString _name);
+ EntityImpl(DocumentImpl *doc, DOMString _publicId, DOMString _systemId, DOMString _notationName);
+ virtual ~EntityImpl();
+
+ // DOM methods & attributes for Entity
+
+ virtual DOMString publicId() const;
+ virtual DOMString systemId() const;
+ virtual DOMString notationName() const;
+
+ // DOM methods overridden from parent classes
+
+ virtual DOMString nodeName() const;
+ virtual unsigned short nodeType() const;
+ virtual NodeImpl *cloneNode ( bool deep );
+
+ // Other methods (not part of DOM)
+
+ virtual bool childTypeAllowed( unsigned short type );
+
+ virtual DOMString toString() const;
+
+protected:
+ DOMStringImpl *m_publicId;
+ DOMStringImpl *m_systemId;
+ DOMStringImpl *m_notationName;
+ DOMStringImpl *m_name;
+};
+
+
+class EntityReferenceImpl : public NodeBaseImpl
+{
+public:
+ EntityReferenceImpl(DocumentImpl *doc);
+ EntityReferenceImpl(DocumentImpl *doc, DOMStringImpl *_entityName);
+ virtual ~EntityReferenceImpl();
+
+ // DOM methods overridden from parent classes
+
+ virtual DOMString nodeName() const;
+ virtual unsigned short nodeType() const;
+ virtual NodeImpl *cloneNode ( bool deep );
+
+ // Other methods (not part of DOM)
+
+ virtual bool childTypeAllowed( unsigned short type );
+
+ virtual DOMString toString() const;
+protected:
+ DOMStringImpl *m_entityName;
+};
+
+class NotationImpl : public NodeBaseImpl
+{
+public:
+ NotationImpl(DocumentImpl *doc);
+ NotationImpl(DocumentImpl *doc, DOMString _name, DOMString _publicId, DOMString _systemId);
+ virtual ~NotationImpl();
+
+ // DOM methods & attributes for Notation
+
+ virtual DOMString publicId() const;
+ virtual DOMString systemId() const;
+
+ // DOM methods overridden from parent classes
+
+ virtual DOMString nodeName() const;
+ virtual unsigned short nodeType() const;
+ virtual NodeImpl *cloneNode ( bool deep );
+
+ // Other methods (not part of DOM)
+
+ virtual bool childTypeAllowed( unsigned short type );
+protected:
+ DOMStringImpl *m_name;
+ DOMStringImpl *m_publicId;
+ DOMStringImpl *m_systemId;
+};
+
+
+class ProcessingInstructionImpl : public NodeBaseImpl, private khtml::CachedObjectClient
+{
+public:
+ ProcessingInstructionImpl(DocumentImpl *doc);
+ ProcessingInstructionImpl(DocumentImpl *doc, DOMString _target, DOMString _data);
+ virtual ~ProcessingInstructionImpl();
+
+ // DOM methods & attributes for Notation
+
+ virtual DOMString target() const;
+ DOMString data() const { return m_data; }
+ virtual void setData( const DOMString &_data, int &exceptioncode );
+
+ // DOM methods overridden from parent classes
+
+ virtual DOMString nodeName() const;
+ virtual unsigned short nodeType() const;
+ virtual DOMString nodeValue() const;
+ virtual void setNodeValue( const DOMString &_nodeValue, int &exceptioncode );
+ virtual NodeImpl *cloneNode ( bool deep );
+
+ // Other methods (not part of DOM)
+
+ virtual DOMString localHref() const;
+ virtual bool childTypeAllowed( unsigned short type );
+ StyleSheetImpl *sheet() const;
+ void checkStyleSheet();
+ virtual void setStyleSheet(const DOM::DOMString &url, const DOM::DOMString &sheet, const DOM::DOMString &charset);
+ virtual void setStyleSheet(CSSStyleSheetImpl* sheet);
+
+ virtual DOMString toString() const;
+
+protected:
+ DOMStringImpl *m_target;
+ DOMStringImpl *m_data;
+ DOMStringImpl *m_localHref;
+ khtml::CachedCSSStyleSheet *m_cachedSheet;
+ CSSStyleSheetImpl *m_sheet;
+};
+
+class XMLAttributeReader : public QXmlDefaultHandler
+{
+public:
+ XMLAttributeReader(const QString& _attrString);
+ virtual ~XMLAttributeReader();
+ QXmlAttributes readAttrs(bool &ok);
+ bool startElement(const QString& namespaceURI, const QString& localName, const QString& qName, const QXmlAttributes& atts);
+
+protected:
+ QXmlAttributes attrs;
+ QString m_attrString;
+};
+
+} //namespace
+
+#endif
diff --git a/khtml/xml/xml_tokenizer.cpp b/khtml/xml/xml_tokenizer.cpp
new file mode 100644
index 000000000..5489b8c21
--- /dev/null
+++ b/khtml/xml/xml_tokenizer.cpp
@@ -0,0 +1,609 @@
+/**
+ * This file is part of the DOM implementation for KDE.
+ *
+ * Copyright (C) 2000 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 "xml_tokenizer.h"
+#include "xml/dom_docimpl.h"
+#include "xml/dom_textimpl.h"
+#include "xml/dom_xmlimpl.h"
+#include "html/html_tableimpl.h"
+#include "html/html_headimpl.h"
+#include "rendering/render_object.h"
+#include "misc/htmltags.h"
+#include "misc/htmlattrs.h"
+#include "misc/loader.h"
+
+#include "khtmlview.h"
+#include "khtml_part.h"
+#include <qvariant.h>
+#include <kdebug.h>
+#include <klocale.h>
+
+using namespace DOM;
+using namespace khtml;
+
+XMLIncrementalSource::XMLIncrementalSource()
+ : QXmlInputSource(), m_pos( 0 ), m_unicode( 0 ),
+ m_finished( false )
+{
+}
+
+void XMLIncrementalSource::fetchData()
+{
+ //just a dummy to overwrite default behavior
+}
+
+QChar XMLIncrementalSource::next()
+{
+ if ( m_finished )
+ return QXmlInputSource::EndOfDocument;
+ else if ( m_data.length() <= m_pos )
+ return QXmlInputSource::EndOfData;
+ else
+ return m_unicode[m_pos++];
+}
+
+void XMLIncrementalSource::setData( const QString& str )
+{
+ m_data = str;
+ m_unicode = m_data.unicode();
+ m_pos = 0;
+ if ( !str.isEmpty() )
+ m_finished = false;
+}
+void XMLIncrementalSource::setData( const QByteArray& data )
+{
+ setData( fromRawData( data, true ) );
+}
+
+void XMLIncrementalSource::appendXML( const QString& str )
+{
+ m_data += str;
+ m_unicode = m_data.unicode();
+}
+
+QString XMLIncrementalSource::data()
+{
+ return m_data;
+}
+
+void XMLIncrementalSource::setFinished( bool finished )
+{
+ m_finished = finished;
+}
+
+XMLHandler::XMLHandler(DocumentImpl *_doc, KHTMLView *_view)
+ : errorLine(0)
+{
+ m_doc = _doc;
+ m_view = _view;
+ pushNode( _doc );
+}
+
+XMLHandler::~XMLHandler()
+{
+}
+
+void XMLHandler::pushNode( NodeImpl *node )
+{
+ m_nodes.push( node );
+}
+
+NodeImpl *XMLHandler::popNode()
+{
+ return m_nodes.pop();
+}
+
+NodeImpl *XMLHandler::currentNode() const
+{
+ return m_nodes.current();
+}
+
+QString XMLHandler::errorProtocol()
+{
+ return errorProt;
+}
+
+
+bool XMLHandler::startDocument()
+{
+ // at the beginning of parsing: do some initialization
+ errorProt = "";
+ state = StateInit;
+
+ return true;
+}
+
+bool XMLHandler::startPrefixMapping(const QString& prefix, const QString& uri)
+{
+ namespaceInfo[prefix].push(uri);
+ return true;
+}
+
+bool XMLHandler::endPrefixMapping(const QString& prefix)
+{
+ QValueStack<QString>& stack = namespaceInfo[prefix];
+ stack.pop();
+ if (stack.isEmpty())
+ namespaceInfo.remove(prefix);
+ return true;
+}
+
+void XMLHandler::fixUpNSURI(QString& uri, const QString& qname)
+{
+ /* QXml does not resolve the namespaces of attributes in the same
+ tag that preceed the xmlns declaration. This fixes up that case */
+ if (uri.isEmpty() && qname.find(':') != -1) {
+ QXmlNamespaceSupport ns;
+ QString localName, prefix;
+ ns.splitName(qname, prefix, localName);
+ if (namespaceInfo.contains(prefix)) {
+ uri = namespaceInfo[prefix].top();
+ }
+ }
+}
+
+bool XMLHandler::startElement( const QString& namespaceURI, const QString& /*localName*/,
+ const QString& qName, const QXmlAttributes& atts )
+{
+ if (currentNode()->nodeType() == Node::TEXT_NODE)
+ exitText();
+
+ DOMString nsURI;
+ if (!namespaceURI.isNull())
+ nsURI = DOMString(namespaceURI);
+ else
+ // No namespace declared, default to the no namespace
+ nsURI = DOMString("");
+ ElementImpl *newElement = m_doc->createElementNS(nsURI,qName);
+ if (!newElement)
+ return false;
+ int i;
+ for (i = 0; i < atts.length(); i++) {
+ int exceptioncode = 0;
+ QString uriString = atts.uri(i);
+ QString qnString = atts.qName(i);
+ fixUpNSURI(uriString, qnString);
+ DOMString uri(uriString);
+ DOMString qn(qnString);
+ DOMString val(atts.value(i));
+ newElement->setAttributeNS(uri, qn, val, exceptioncode);
+ if (exceptioncode) // exception setting attributes
+ return false;
+ }
+
+ if (newElement->id() == ID_SCRIPT || newElement->id() == makeId(xhtmlNamespace, ID_SCRIPT))
+ static_cast<HTMLScriptElementImpl *>(newElement)->setCreatedByParser(true);
+
+ //this is tricky. in general the node doesn't have to attach to the one it's in. as far
+ //as standards go this is wrong, but there's literally thousands of documents where
+ //we see <p><ul>...</ul></p>. the following code is there for those cases.
+ //when we can't attach to the currently holding us node we try to attach to its parent
+ bool attached = false;
+ for ( NodeImpl *current = currentNode(); current; current = current->parent() ) {
+ attached = current->addChild( newElement );
+ if ( attached )
+ break;
+ }
+ if (attached) {
+ if (m_view && !newElement->attached() && !m_doc->hasPendingSheets())
+ newElement->attach();
+ pushNode( newElement );
+ return true;
+ }
+ else {
+ delete newElement;
+ return false;
+ }
+
+ // ### DOM spec states: "if there is no markup inside an element's content, the text is contained in a
+ // single object implementing the Text interface that is the only child of the element."... do we
+ // need to ensure that empty elements always have an empty text child?
+}
+
+
+bool XMLHandler::endElement( const QString& /*namespaceURI*/, const QString& /*localName*/, const QString& /*qName*/ )
+{
+ if (currentNode()->nodeType() == Node::TEXT_NODE)
+ exitText();
+
+ NodeImpl *node = popNode();
+ if ( node ) {
+ node->close();
+ while ( currentNode() && currentNode()->implicitNode() ) //for the implicit HTMLTableSectionElementImpl
+ popNode()->close();
+ } else
+ return false;
+
+ return true;
+}
+
+
+bool XMLHandler::startCDATA()
+{
+ if (currentNode()->nodeType() == Node::TEXT_NODE)
+ exitText();
+
+ NodeImpl *newNode = m_doc->createCDATASection(new DOMStringImpl(""));
+ if (currentNode()->addChild(newNode)) {
+ if (m_view && !newNode->attached() && !m_doc->hasPendingSheets())
+ newNode->attach();
+ pushNode( newNode );
+ return true;
+ }
+ else {
+ delete newNode;
+ return false;
+ }
+
+}
+
+bool XMLHandler::endCDATA()
+{
+ popNode();
+ Q_ASSERT( currentNode() );
+ return currentNode();
+}
+
+bool XMLHandler::characters( const QString& ch )
+{
+ if (currentNode()->nodeType() == Node::TEXT_NODE ||
+ currentNode()->nodeType() == Node::CDATA_SECTION_NODE ||
+ enterText()) {
+ int exceptioncode = 0;
+ static_cast<TextImpl*>(currentNode())->appendData(ch,exceptioncode);
+ if (exceptioncode)
+ return false;
+ return true;
+ }
+ else {
+ // Don't worry about white-space violating DTD
+ if (ch.stripWhiteSpace().isEmpty()) return true;
+
+ return false;
+ }
+
+}
+
+bool XMLHandler::comment(const QString & ch)
+{
+ if (currentNode()->nodeType() == Node::TEXT_NODE)
+ exitText();
+ // ### handle exceptions
+ currentNode()->addChild(m_doc->createComment(new DOMStringImpl(ch.unicode(), ch.length())));
+ return true;
+}
+
+bool XMLHandler::processingInstruction(const QString &target, const QString &data)
+{
+ if (currentNode()->nodeType() == Node::TEXT_NODE)
+ exitText();
+ // ### handle exceptions
+ ProcessingInstructionImpl *pi =
+ m_doc->createProcessingInstruction(target, new DOMStringImpl(data.unicode(), data.length()));
+ currentNode()->addChild(pi);
+ pi->checkStyleSheet();
+ return true;
+}
+
+
+QString XMLHandler::errorString()
+{
+ // ### Make better error-messages
+ return i18n("the document is not in the correct file format");
+}
+
+
+bool XMLHandler::fatalError( const QXmlParseException& exception )
+{
+ errorProt += i18n( "fatal parsing error: %1 in line %2, column %3" )
+ .arg( exception.message() )
+ .arg( exception.lineNumber() )
+ .arg( exception.columnNumber() );
+
+ errorLine = exception.lineNumber();
+ errorCol = exception.columnNumber();
+
+ return false;
+}
+
+bool XMLHandler::enterText()
+{
+ NodeImpl *newNode = m_doc->createTextNode("");
+ if (currentNode()->addChild(newNode)) {
+ pushNode( newNode );
+ return true;
+ }
+ else {
+ delete newNode;
+ return false;
+ }
+}
+
+void XMLHandler::exitText()
+{
+ if ( m_view && !currentNode()->attached() && !m_doc->hasPendingSheets() )
+ currentNode()->attach();
+ popNode();
+}
+
+bool XMLHandler::attributeDecl(const QString &/*eName*/, const QString &/*aName*/, const QString &/*type*/,
+ const QString &/*valueDefault*/, const QString &/*value*/)
+{
+ // qt's xml parser (as of 2.2.3) does not currently give us values for type, valueDefault and
+ // value. When it does, we can store these somewhere and have default attributes on elements
+ return true;
+}
+
+bool XMLHandler::externalEntityDecl(const QString &/*name*/, const QString &/*publicId*/, const QString &/*systemId*/)
+{
+ // ### insert these too - is there anything special we have to do here?
+ return true;
+}
+
+bool XMLHandler::internalEntityDecl(const QString &name, const QString &value)
+{
+ EntityImpl *e = new EntityImpl(m_doc,name);
+ // ### further parse entities inside the value and add them as separate nodes (or entityreferences)?
+ e->addChild(m_doc->createTextNode(new DOMStringImpl(value.unicode(), value.length())));
+ if (m_doc->doctype())
+ static_cast<GenericRONamedNodeMapImpl*>(m_doc->doctype()->entities())->addNode(e);
+ return true;
+}
+
+bool XMLHandler::notationDecl(const QString &/*name*/, const QString &/*publicId*/, const QString &/*systemId*/)
+{
+// ### FIXME
+// if (m_doc->document()->doctype()) {
+// NotationImpl *n = new NotationImpl(m_doc,name,publicId,systemId);
+// static_cast<GenericRONamedNodeMapImpl*>(m_doc->document()->doctype()->notations())->addNode(n);
+// }
+ return true;
+}
+
+bool XMLHandler::unparsedEntityDecl(const QString &/*name*/, const QString &/*publicId*/,
+ const QString &/*systemId*/, const QString &/*notationName*/)
+{
+ // ###
+ return true;
+}
+
+
+//------------------------------------------------------------------------------
+
+XMLTokenizer::XMLTokenizer(DOM::DocumentImpl *_doc, KHTMLView *_view)
+ : m_handler(_doc,_view)
+{
+ m_doc = _doc;
+ m_view = _view;
+ m_scriptsIt = 0;
+ m_cachedScript = 0;
+ m_noErrors = true;
+ m_reader.setContentHandler( &m_handler );
+ m_reader.setLexicalHandler( &m_handler );
+ m_reader.setErrorHandler( &m_handler );
+ m_reader.setDeclHandler( &m_handler );
+ m_reader.setDTDHandler( &m_handler );
+ m_reader.setFeature("http://xml.org/sax/features/namespace-prefixes", true);
+}
+
+XMLTokenizer::~XMLTokenizer()
+{
+ if (m_scriptsIt)
+ delete m_scriptsIt;
+ if (m_cachedScript)
+ m_cachedScript->deref(this);
+}
+
+
+void XMLTokenizer::begin()
+{
+ // parse xml file
+ m_reader.parse( &m_source, true );
+}
+
+void XMLTokenizer::write( const TokenizerString &str, bool appendData )
+{
+ if ( !m_noErrors && appendData )
+ return;
+ if ( appendData ) {
+ m_source.appendXML( str.toString() );
+
+ } else {
+ m_source.setData( str.toString() );
+ }
+ m_noErrors = m_reader.parseContinue();
+}
+
+void XMLTokenizer::end()
+{
+ m_source.setFinished( true );
+ //if ( m_noErrors )
+ //m_noErrors = m_reader.parseContinue();
+ emit finishedParsing();
+}
+
+void XMLTokenizer::finish()
+{
+ m_source.setFinished( true );
+ if (!m_noErrors) {
+ // An error occurred during parsing of the code. Display an error page to the user (the DOM
+ // tree is created manually and includes an excerpt from the code where the error is located)
+
+ // ### for multiple error messages, display the code for each (can this happen?)
+
+ // Clear the document
+ int exceptioncode = 0;
+ while (m_doc->hasChildNodes())
+ static_cast<NodeImpl*>(m_doc)->removeChild(m_doc->firstChild(),exceptioncode);
+
+ QString line, errorLocPtr;
+ if ( m_handler.errorLine ) {
+ QString xmlCode = m_source.data();
+ QTextIStream stream(&xmlCode);
+ for (unsigned long lineno = 0; lineno < m_handler.errorLine-1; lineno++)
+ stream.readLine();
+ line = stream.readLine();
+
+ for (unsigned long colno = 0; colno < m_handler.errorCol-1; colno++)
+ errorLocPtr += " ";
+ errorLocPtr += "^";
+ }
+
+ // Create elements for display
+ DocumentImpl *doc = m_doc;
+ NodeImpl *html = doc->createElementNS(XHTML_NAMESPACE,"html");
+ NodeImpl *body = doc->createElementNS(XHTML_NAMESPACE,"body");
+ NodeImpl *h1 = doc->createElementNS(XHTML_NAMESPACE,"h1");
+ NodeImpl *headingText = doc->createTextNode(i18n("XML parsing error"));
+ NodeImpl *errorText = doc->createTextNode(m_handler.errorProtocol());
+ NodeImpl *hr = 0;
+ NodeImpl *pre = 0;
+ NodeImpl *lineText = 0;
+ NodeImpl *errorLocText = 0;
+ if ( !line.isNull() ) {
+ hr = doc->createElementNS(XHTML_NAMESPACE,"hr");
+ pre = doc->createElementNS(XHTML_NAMESPACE,"pre");
+ lineText = doc->createTextNode(line+"\n");
+ errorLocText = doc->createTextNode(errorLocPtr);
+ }
+
+ // Construct DOM tree. We ignore exceptions as we assume they will not be thrown here (due to the
+ // fact we are using a known tag set)
+ doc->appendChild(html,exceptioncode);
+ html->appendChild(body,exceptioncode);
+ if ( body )
+ body->appendChild(h1,exceptioncode);
+ h1->appendChild(headingText,exceptioncode);
+ body->appendChild(errorText,exceptioncode);
+ body->appendChild(hr,exceptioncode);
+ body->appendChild(pre,exceptioncode);
+ if ( pre ) {
+ pre->appendChild(lineText,exceptioncode);
+ pre->appendChild(errorLocText,exceptioncode);
+ }
+
+ // Close the renderers so that they update their display correctly
+ // ### this should not be necessary, but requires changes in the rendering code...
+ h1->close();
+ if ( pre ) pre->close();
+ body->close();
+
+ m_doc->recalcStyle( NodeImpl::Inherit );
+ m_doc->updateRendering();
+
+ end();
+ }
+ else {
+ // Parsing was successful. Now locate all html <script> tags in the document and execute them
+ // one by one
+ addScripts(m_doc);
+ m_scriptsIt = new QPtrListIterator<HTMLScriptElementImpl>(m_scripts);
+ executeScripts();
+ }
+
+}
+
+void XMLTokenizer::addScripts(NodeImpl *n)
+{
+ // Recursively go through the entire document tree, looking for html <script> tags. For each of these
+ // that is found, add it to the m_scripts list from which they will be executed
+
+ if (n->id() == ID_SCRIPT || n->id() == makeId(xhtmlNamespace, ID_SCRIPT)) {
+ m_scripts.append(static_cast<HTMLScriptElementImpl*>(n));
+ }
+
+ NodeImpl *child;
+ for (child = n->firstChild(); child; child = child->nextSibling())
+ addScripts(child);
+}
+
+void XMLTokenizer::executeScripts()
+{
+ // Iterate through all of the html <script> tags in the document. For those that have a src attribute,
+ // start loading the script and return (executeScripts() will be called again once the script is loaded
+ // and continue where it left off). For scripts that don't have a src attribute, execute the code
+ // inside the tag
+ while (m_scriptsIt->current()) {
+ DOMString scriptSrc = m_scriptsIt->current()->getAttribute(ATTR_SRC);
+ QString charset = m_scriptsIt->current()->getAttribute(ATTR_CHARSET).string();
+
+ if (!scriptSrc.isEmpty()) {
+ // we have a src attribute
+ m_cachedScript = m_doc->docLoader()->requestScript(scriptSrc, charset);
+ ++(*m_scriptsIt);
+ if (m_cachedScript) {
+ m_cachedScript->ref(this); // will call executeScripts() again if already cached
+ return;
+ }
+ }
+ else {
+ // no src attribute - execute from contents of tag
+ QString scriptCode = "";
+ NodeImpl *child;
+ for (child = m_scriptsIt->current()->firstChild(); child; child = child->nextSibling()) {
+ if ( ( child->nodeType() == Node::TEXT_NODE || child->nodeType() == Node::CDATA_SECTION_NODE) &&
+ static_cast<TextImpl*>(child)->string() )
+ scriptCode += QConstString(static_cast<TextImpl*>(child)->string()->s,
+ static_cast<TextImpl*>(child)->string()->l).string();
+ }
+ // the script cannot do document.write until we support incremental parsing
+ // ### handle the case where the script deletes the node or redirects to
+ // another page, etc. (also in notifyFinished())
+ // ### the script may add another script node after this one which should be executed
+ if (m_view) {
+ m_view->part()->executeScript(DOM::Node(), scriptCode);
+ }
+ ++(*m_scriptsIt);
+ }
+ }
+
+ // All scripts have finished executing, so calculate the style for the document and close
+ // the last element
+ m_doc->updateStyleSelector();
+
+ // We are now finished parsing
+ end();
+}
+
+void XMLTokenizer::notifyFinished(CachedObject *finishedObj)
+{
+ // This is called when a script has finished loading that was requested from executeScripts(). We execute
+ // the script, and then call executeScripts() again to continue iterating through the list of scripts in
+ // the document
+ if (finishedObj == m_cachedScript) {
+ DOMString scriptSource = m_cachedScript->script();
+ m_cachedScript->deref(this);
+ m_cachedScript = 0;
+ if (m_view)
+ m_view->part()->executeScript(DOM::Node(), scriptSource.string());
+ executeScripts();
+ }
+}
+
+bool XMLTokenizer::isWaitingForScripts() const
+{
+ return m_cachedScript != 0;
+}
+
+#include "xml_tokenizer.moc"
+
diff --git a/khtml/xml/xml_tokenizer.h b/khtml/xml/xml_tokenizer.h
new file mode 100644
index 000000000..4042b905f
--- /dev/null
+++ b/khtml/xml/xml_tokenizer.h
@@ -0,0 +1,198 @@
+/*
+ * This file is part of the DOM implementation for KDE.
+ *
+ * Copyright (C) 2000 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 _XML_Tokenizer_h_
+#define _XML_Tokenizer_h_
+
+#include <qxml.h>
+#include <qptrlist.h>
+#include <qptrstack.h>
+#include <qvaluestack.h>
+#include <qobject.h>
+#include "misc/loader_client.h"
+#include "misc/stringit.h"
+
+class KHTMLView;
+
+namespace khtml {
+ class CachedObject;
+ class CachedScript;
+}
+
+namespace DOM {
+ class DocumentImpl;
+ class NodeImpl;
+ class HTMLScriptElementImpl;
+ class DocumentImpl;
+ class HTMLScriptElementImpl;
+}
+
+namespace khtml {
+
+class XMLHandler : public QXmlDefaultHandler
+{
+public:
+ XMLHandler(DOM::DocumentImpl *_doc, KHTMLView *_view);
+ virtual ~XMLHandler();
+
+ // return the error protocol if parsing failed
+ QString errorProtocol();
+
+ // overloaded handler functions
+ bool startDocument();
+ bool startElement(const QString& namespaceURI, const QString& localName, const QString& qName, const QXmlAttributes& atts);
+ bool endElement(const QString& namespaceURI, const QString& localName, const QString& qName);
+ bool startCDATA();
+ bool endCDATA();
+ bool characters(const QString& ch);
+ bool comment(const QString & ch);
+ bool processingInstruction(const QString &target, const QString &data);
+
+ // namespace handling, to workaround problem in QXML where some attributes
+ // do not get the namespace resolved properly
+ bool startPrefixMapping(const QString& prefix, const QString& uri);
+ bool endPrefixMapping(const QString& prefix);
+ void fixUpNSURI(QString& uri, const QString& qname);
+ QMap<QString, QValueStack<QString> > namespaceInfo;
+
+
+ // from QXmlDeclHandler
+ bool attributeDecl(const QString &eName, const QString &aName, const QString &type, const QString &valueDefault, const QString &value);
+ bool externalEntityDecl(const QString &name, const QString &publicId, const QString &systemId);
+ bool internalEntityDecl(const QString &name, const QString &value);
+
+ // from QXmlDTDHandler
+ bool notationDecl(const QString &name, const QString &publicId, const QString &systemId);
+ bool unparsedEntityDecl(const QString &name, const QString &publicId, const QString &systemId, const QString &notationName);
+
+ bool enterText();
+ void exitText();
+
+ QString errorString();
+
+ bool fatalError( const QXmlParseException& exception );
+
+ unsigned long errorLine;
+ unsigned long errorCol;
+
+private:
+ void pushNode( DOM::NodeImpl *node );
+ DOM::NodeImpl *popNode();
+ DOM::NodeImpl *currentNode() const;
+private:
+ QString errorProt;
+ DOM::DocumentImpl *m_doc;
+ KHTMLView *m_view;
+ QPtrStack<DOM::NodeImpl> m_nodes;
+ DOM::NodeImpl *m_rootNode;
+
+ enum State {
+ StateInit,
+ StateDocument,
+ StateQuote,
+ StateLine,
+ StateHeading,
+ StateP
+ };
+ State state;
+};
+
+class Tokenizer : public QObject
+{
+ Q_OBJECT
+public:
+ virtual void begin() = 0;
+ // script output must be prepended, while new data
+ // received during executing a script must be appended, hence the
+ // extra bool to be able to distinguish between both cases. document.write()
+ // always uses false, while khtmlpart uses true
+ virtual void write( const TokenizerString &str, bool appendData) = 0;
+ virtual void end() = 0;
+ virtual void finish() = 0;
+ virtual void setOnHold(bool /*_onHold*/) {}
+ virtual bool isWaitingForScripts() const = 0;
+ virtual bool isExecutingScript() const = 0;
+ virtual void abort() {}
+ virtual void setAutoClose(bool b=true) = 0;
+
+signals:
+ void finishedParsing();
+
+};
+
+class XMLIncrementalSource : public QXmlInputSource
+{
+public:
+ XMLIncrementalSource();
+ virtual void fetchData();
+ virtual QChar next();
+ virtual void setData( const QString& str );
+ virtual void setData( const QByteArray& data );
+ virtual QString data();
+
+ void appendXML( const QString& str );
+ void setFinished( bool );
+
+private:
+ QString m_data;
+ uint m_pos;
+ const QChar *m_unicode;
+ bool m_finished;
+};
+
+class XMLTokenizer : public Tokenizer, public khtml::CachedObjectClient
+{
+public:
+ XMLTokenizer(DOM::DocumentImpl *, KHTMLView * = 0);
+ virtual ~XMLTokenizer();
+ virtual void begin();
+ virtual void write( const TokenizerString &str, bool );
+ virtual void end();
+ virtual void finish();
+ virtual void setAutoClose(bool b=true) { qWarning("XMLTokenizer::setAutoClose: stub."); (void)b; }
+
+ // from CachedObjectClient
+ void notifyFinished(khtml::CachedObject *finishedObj);
+
+ virtual bool isWaitingForScripts() const;
+ virtual bool isExecutingScript() const { return false; }
+
+protected:
+ DOM::DocumentImpl *m_doc;
+ KHTMLView *m_view;
+
+ void executeScripts();
+ void addScripts(DOM::NodeImpl *n);
+
+ QPtrList<DOM::HTMLScriptElementImpl> m_scripts;
+ QPtrListIterator<DOM::HTMLScriptElementImpl> *m_scriptsIt;
+ khtml::CachedScript *m_cachedScript;
+
+ XMLHandler m_handler;
+ QXmlSimpleReader m_reader;
+ XMLIncrementalSource m_source;
+ bool m_noErrors;
+};
+
+} // end namespace
+
+#endif