2 * This file is part of the DOM implementation for KDE.
4 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
5 * (C) 1999 Antti Koivisto (koivisto@kde.org)
6 * (C) 2001 Dirk Mueller (mueller@kde.org)
7 * Copyright (C) 2004 Apple Computer, Inc.
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
19 * You should have received a copy of the GNU Library General Public License
20 * along with this library; see the file COPYING.LIB. If not, write to
21 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22 * Boston, MA 02111-1307, USA.
25 #include "dom_docimpl.h"
27 #include "dom/dom_exception.h"
28 #include "dom/dom2_events.h"
30 #include "xml/dom_textimpl.h"
31 #include "xml/dom_xmlimpl.h"
32 #include "xml/dom2_rangeimpl.h"
33 #include "xml/dom2_eventsimpl.h"
34 #include "xml/dom2_viewsimpl.h"
35 #include "xml/xml_tokenizer.h"
37 #include "xml_namespace_table.h"
39 #include "css/csshelper.h"
40 #include "css/cssstyleselector.h"
41 #include "css/css_stylesheetimpl.h"
42 #include "css/css_valueimpl.h"
43 #include "misc/htmlhashes.h"
44 #include "misc/helper.h"
45 #include "ecma/kjs_proxy.h"
46 #include "ecma/kjs_binding.h"
48 #include <qptrstack.h>
49 #include <qpaintdevicemetrics.h>
52 #include <kstaticdeleter.h>
54 #include "rendering/render_canvas.h"
55 #include "rendering/render_frames.h"
56 #include "rendering/render_image.h"
57 #include "rendering/render_object.h"
58 #include "render_arena.h"
60 #include "khtmlview.h"
61 #include "khtml_part.h"
63 #include <kglobalsettings.h>
64 #include <kstringhandler.h>
65 #include "khtml_settings.h"
66 #include "khtmlpart_p.h"
68 #include "html/html_baseimpl.h"
69 #include "html/html_blockimpl.h"
70 #include "html/html_canvasimpl.h"
71 #include "html/html_documentimpl.h"
72 #include "html/html_formimpl.h"
73 #include "html/html_headimpl.h"
74 #include "html/html_imageimpl.h"
75 #include "html/html_listimpl.h"
76 #include "html/html_miscimpl.h"
77 #include "html/html_tableimpl.h"
78 #include "html/html_objectimpl.h"
80 #include "cssvalues.h"
82 #include "editing/jsediting.h"
83 #include "editing/visible_position.h"
84 #include "editing/visible_text.h"
89 #include "xsl_stylesheetimpl.h"
90 #include "xslt_processorimpl.h"
94 #include "xbl/xbl_binding_manager.h"
95 using XBL::XBLBindingManager;
99 #include "KWQAccObjectCache.h"
100 #include "KWQLogging.h"
104 using namespace khtml;
106 // #define INSTRUMENT_LAYOUT_SCHEDULING 1
108 // This amount of time must have elapsed before we will even consider scheduling a layout without a delay.
109 // FIXME: For faster machines this value can really be lowered to 200. 250 is adequate, but a little high
111 const int cLayoutScheduleThreshold = 250;
113 DOMImplementationImpl *DOMImplementationImpl::m_instance = 0;
115 DOMImplementationImpl::DOMImplementationImpl()
119 DOMImplementationImpl::~DOMImplementationImpl()
123 bool DOMImplementationImpl::hasFeature ( const DOMString &feature, const DOMString &version )
125 QString lower = feature.string().lower();
126 if (lower == "core" || lower == "html" || lower == "xml")
127 return version.isEmpty() || version == "1.0" || version == "2.0";
131 || lower == "htmlevents"
132 || lower == "mouseevents"
133 || lower == "mutationevents"
135 || lower == "stylesheets"
136 || lower == "traversal"
137 || lower == "uievents"
139 return version.isEmpty() || version == "2.0";
143 DocumentTypeImpl *DOMImplementationImpl::createDocumentType( const DOMString &qualifiedName, const DOMString &publicId,
144 const DOMString &systemId, int &exceptioncode )
146 // Not mentioned in spec: throw NAMESPACE_ERR if no qualifiedName supplied
147 if (qualifiedName.isNull()) {
148 exceptioncode = DOMException::NAMESPACE_ERR;
153 // FIXME: Add these checks (but not in a way that depends on the C++ DOM!)
155 // INVALID_CHARACTER_ERR: Raised if the specified qualified name contains an illegal character.
156 if (!Element::khtmlValidQualifiedName(qualifiedName)) {
157 exceptioncode = DOMException::INVALID_CHARACTER_ERR;
161 // NAMESPACE_ERR: Raised if the qualifiedName is malformed.
162 if (Element::khtmlMalformedQualifiedName(qualifiedName)) {
163 exceptioncode = DOMException::NAMESPACE_ERR;
168 return new DocumentTypeImpl(this,0,qualifiedName,publicId,systemId);
171 DOMImplementationImpl* DOMImplementationImpl::getInterface(const DOMString& /*feature*/) const
177 DocumentImpl *DOMImplementationImpl::createDocument( const DOMString &namespaceURI, const DOMString &qualifiedName,
178 DocumentTypeImpl *doctype, int &exceptioncode )
182 // Not mentioned in spec: throw NAMESPACE_ERR if no qualifiedName supplied
183 if (qualifiedName.isNull()) {
184 exceptioncode = DOMException::NAMESPACE_ERR;
189 // FIXME: Add this check (but not in a way that depends on the C++ DOM!)
190 // INVALID_CHARACTER_ERR: Raised if the specified qualified name contains an illegal character.
191 if (!Element::khtmlValidQualifiedName(qualifiedName)) {
192 exceptioncode = DOMException::INVALID_CHARACTER_ERR;
198 // - Raised if the qualifiedName is malformed,
199 // - if the qualifiedName has a prefix and the namespaceURI is null, or
200 // - if the qualifiedName has a prefix that is "xml" and the namespaceURI is different
201 // from "http://www.w3.org/XML/1998/namespace" [Namespaces].
204 DOMStringImpl *qname = qualifiedName.implementation();
205 for (i = 0; i < qname->l && colonpos < 0; i++) {
206 if ((*qname)[i] == ':')
212 // FIXME: Add this check (but not in a way that depends on the C++ DOM!)
213 Element::khtmlMalformedQualifiedName(qualifiedName) ||
215 (colonpos >= 0 && namespaceURI.isNull()) ||
216 (colonpos == 3 && qualifiedName[0] == 'x' && qualifiedName[1] == 'm' && qualifiedName[2] == 'l' &&
217 namespaceURI != "http://www.w3.org/XML/1998/namespace")) {
219 exceptioncode = DOMException::NAMESPACE_ERR;
223 // WRONG_DOCUMENT_ERR: Raised if doctype has already been used with a different document or was
224 // created from a different implementation.
225 if (doctype && (doctype->getDocument() || doctype->implementation() != this)) {
226 exceptioncode = DOMException::WRONG_DOCUMENT_ERR;
230 // ### this is completely broken.. without a view it will not work (Dirk)
231 DocumentImpl *doc = new DocumentImpl(this, 0);
233 // now get the interesting parts of the doctype
235 doc->realDocType()->copyFrom(*doctype);
240 CSSStyleSheetImpl *DOMImplementationImpl::createCSSStyleSheet(const DOMString &/*title*/, const DOMString &media, int &/*exception*/)
242 // ### TODO : title should be set, and media could have wrong syntax, in which case we should generate an exception.
243 CSSStyleSheetImpl * const nullSheet = 0;
244 CSSStyleSheetImpl *sheet = new CSSStyleSheetImpl(nullSheet);
245 sheet->setMedia(new MediaListImpl(sheet, media));
249 DocumentImpl *DOMImplementationImpl::createDocument( KHTMLView *v )
251 return new DocumentImpl(this, v);
254 HTMLDocumentImpl *DOMImplementationImpl::createHTMLDocument( KHTMLView *v )
256 return new HTMLDocumentImpl(this, v);
259 DOMImplementationImpl *DOMImplementationImpl::instance()
262 m_instance = new DOMImplementationImpl();
269 HTMLDocumentImpl *DOMImplementationImpl::createHTMLDocument(const DOMString &title)
271 HTMLDocumentImpl *d = createHTMLDocument( 0 /* ### create a view otherwise it doesn't work */);
273 // FIXME: Need to escape special characters in the title?
274 d->write("<html><head><title>" + title.string() + "</title></head>");
278 // ------------------------------------------------------------------------
280 KStaticDeleter< QPtrList<DocumentImpl> > s_changedDocumentsDeleter;
281 QPtrList<DocumentImpl> * DocumentImpl::changedDocuments = 0;
283 // KHTMLView might be 0
284 DocumentImpl::DocumentImpl(DOMImplementationImpl *_implementation, KHTMLView *v)
285 : ContainerNodeImpl( new DocumentPtr() )
286 , m_domtree_version(0)
287 , m_imageLoadEventTimer(0)
289 , m_bindingManager(new XBLBindingManager(this))
292 , m_transformSource(NULL)
293 , m_transformSourceDocument(0)
296 , m_finishedParsing(this, SIGNAL(finishedParsing()))
297 , m_inPageCache(false)
299 , m_passwordFields(0)
302 , m_createRenderers(true)
303 , m_designMode(inherit)
304 , m_hasDashboardRegions(false)
305 , m_dashboardRegionsDirty(false)
308 document->doc = this;
311 m_paintDeviceMetrics = 0;
321 m_docLoader = new DocLoader(v->part(), this );
322 setPaintDevice( m_view );
325 m_docLoader = new DocLoader( 0, this );
327 visuallyOrdered = false;
328 m_loadingSheet = false;
330 m_docChanged = false;
335 // ### this should be created during parsing a <!DOCTYPE>
336 // not during construction. Not sure who added that and why (Dirk)
337 m_doctype = new DocumentTypeImpl(_implementation, document,
338 DOMString() /* qualifiedName */,
339 DOMString() /* publicId */,
340 DOMString() /* systemId */);
343 m_implementation = _implementation;
344 if (m_implementation)
345 m_implementation->ref();
348 m_textColor = Qt::black;
350 m_elementNameAlloc = 0;
351 m_elementNameCount = 0;
357 m_defaultView = new AbstractViewImpl(this);
358 m_defaultView->ref();
360 m_styleSheets = new StyleSheetListImpl;
361 m_styleSheets->ref();
363 m_styleSelectorDirty = false;
364 m_inStyleRecalc = false;
365 m_closeAfterStyleRecalc = false;
366 m_usesDescendantRules = false;
367 m_usesSiblingRules = false;
369 m_styleSelector = new CSSStyleSelector(this, m_usersheet, m_styleSheets, !inCompatMode());
370 m_windowEventListeners.setAutoDelete(true);
371 m_pendingStylesheets = 0;
372 m_ignorePendingStylesheets = false;
375 m_accessKeyDictValid = false;
378 resetVisitedLinkColor();
379 resetActiveLinkColor();
381 m_processingLoadEvent = false;
382 m_startTime.restart();
383 m_overMinimumLayoutThreshold = false;
387 m_markers.setAutoDelete(true);
389 static int docID = 0;
393 DocumentImpl::~DocumentImpl()
397 assert(!m_inPageCache);
398 assert(m_savedRenderer == 0);
401 KJS::ScriptInterpreter::forgetDOMObjectsForDocument(this);
403 if (changedDocuments && m_docChanged)
404 changedDocuments->remove(this);
408 delete m_styleSelector;
410 if (m_elemSheet ) m_elemSheet->deref();
413 if (m_implementation)
414 m_implementation->deref();
415 delete m_paintDeviceMetrics;
417 if (m_elementNames) {
418 for (unsigned short id = 0; id < m_elementNameCount; id++)
419 m_elementNames[id]->deref();
420 delete [] m_elementNames;
423 for (unsigned short id = 0; id < m_attrNameCount; id++)
424 m_attrNames[id]->deref();
425 delete [] m_attrNames;
427 m_defaultView->deref();
428 m_styleSheets->deref();
431 m_focusNode->deref();
433 m_hoverNode->deref();
436 delete m_renderArena;
441 xmlFreeDoc((xmlDocPtr)m_transformSource);
442 if (m_transformSourceDocument)
443 m_transformSourceDocument->deref();
447 delete m_bindingManager;
468 void DocumentImpl::resetLinkColor()
470 m_linkColor = QColor(0, 0, 238);
473 void DocumentImpl::resetVisitedLinkColor()
475 m_visitedLinkColor = QColor(85, 26, 139);
478 void DocumentImpl::resetActiveLinkColor()
480 m_activeLinkColor.setNamedColor(QString("red"));
483 DocumentTypeImpl *DocumentImpl::doctype() const
488 DOMImplementationImpl *DocumentImpl::implementation() const
490 return m_implementation;
493 ElementImpl *DocumentImpl::documentElement() const
495 NodeImpl *n = firstChild();
496 while (n && n->nodeType() != Node::ELEMENT_NODE)
497 n = n->nextSibling();
498 return static_cast<ElementImpl*>(n);
501 ElementImpl *DocumentImpl::createElement( const DOMString &name, int &exceptioncode )
503 return new XMLElementImpl( document, name.implementation() );
506 DocumentFragmentImpl *DocumentImpl::createDocumentFragment( )
508 return new DocumentFragmentImpl( docPtr() );
511 TextImpl *DocumentImpl::createTextNode( const DOMString &data )
513 return new TextImpl( docPtr(), data);
516 CommentImpl *DocumentImpl::createComment ( const DOMString &data )
518 return new CommentImpl( docPtr(), data );
521 CDATASectionImpl *DocumentImpl::createCDATASection ( const DOMString &data )
523 return new CDATASectionImpl( docPtr(), data );
526 ProcessingInstructionImpl *DocumentImpl::createProcessingInstruction ( const DOMString &target, const DOMString &data )
528 return new ProcessingInstructionImpl( docPtr(),target,data);
531 AttrImpl *DocumentImpl::createAttribute( NodeImpl::Id id )
533 // Assume this is an HTML attribute, since createAttribute isn't namespace-aware. There's no harm to XML
534 // documents if we're wrong.
535 return new AttrImpl(0, docPtr(), new MappedAttributeImpl(id, DOMString("").implementation()));
538 EntityReferenceImpl *DocumentImpl::createEntityReference ( const DOMString &name )
540 return new EntityReferenceImpl(docPtr(), name.implementation());
543 EditingTextImpl *DocumentImpl::createEditingTextNode(const DOMString &text)
545 return new EditingTextImpl(docPtr(), text);
548 CSSStyleDeclarationImpl *DocumentImpl::createCSSStyleDeclaration()
550 return new CSSMutableStyleDeclarationImpl;
553 NodeImpl *DocumentImpl::importNode(NodeImpl *importedNode, bool deep, int &exceptioncode)
557 switch (importedNode->nodeType()) {
558 case Node::TEXT_NODE:
559 return createTextNode(importedNode->nodeValue());
560 case Node::CDATA_SECTION_NODE:
561 return createCDATASection(importedNode->nodeValue());
562 case Node::ENTITY_REFERENCE_NODE:
563 return createEntityReference(importedNode->nodeName());
564 case Node::PROCESSING_INSTRUCTION_NODE:
565 return createProcessingInstruction(importedNode->nodeName(), importedNode->nodeValue());
566 case Node::COMMENT_NODE:
567 return createComment(importedNode->nodeValue());
568 case Node::ELEMENT_NODE: {
569 ElementImpl *oldElement = static_cast<ElementImpl *>(importedNode);
570 DocumentImpl *oldDoc = oldElement->getDocument();
571 static DOMString HTMLNamespace(XHTML_NAMESPACE);
572 DOMString elementNamespace = oldElement->isHTMLElement() ? HTMLNamespace : oldElement->namespaceURI();
573 ElementImpl *newElement = createElementNS(elementNamespace.implementation(), oldElement->tagName(), exceptioncode);
574 if (exceptioncode != 0)
579 NamedAttrMapImpl *attrs = oldElement->attributes(true);
581 unsigned length = attrs->length();
582 for (unsigned i = 0; i < length; i++) {
583 AttrImpl *attr = attrs->item(i);
584 DOMString qualifiedName = attr->nodeName();
585 DOMString value = attr->nodeValue();
587 int colonpos = qualifiedName.find(':');
588 DOMString localName = qualifiedName;
590 localName.remove(0, colonpos + 1);
591 // ### extract and set new prefix
594 NodeImpl::Id nodeId = attrId(oldDoc->namespaceURI(attr->attrImpl()->id()), localName.implementation(), false /* allocate */);
595 newElement->setAttribute(nodeId, value.implementation(), exceptioncode);
596 if (exceptioncode != 0) {
604 for (NodeImpl *oldChild = oldElement->firstChild(); oldChild; oldChild = oldChild->nextSibling()) {
605 NodeImpl *newChild = importNode(oldChild, true, exceptioncode);
606 if (exceptioncode != 0) {
610 newElement->appendChild(newChild, exceptioncode);
611 if (exceptioncode != 0) {
618 // Trick to get the result back to the floating state, with 0 refs but not destroyed.
619 newElement->setParent(this);
621 newElement->setParent(0);
627 exceptioncode = DOMException::NOT_SUPPORTED_ERR;
631 ElementImpl *DocumentImpl::createElementNS( const DOMString &_namespaceURI, const DOMString &_qualifiedName, int &exceptioncode)
634 QString qName = _qualifiedName.string();
635 int colonPos = qName.find(':',0);
637 if (_namespaceURI == XHTML_NAMESPACE) {
638 // User requested an element in the XHTML namespace - this means we create a specific element
639 // (elements not in this namespace are treated as normal XML elements)
640 e = createHTMLElement(qName.mid(colonPos+1), exceptioncode);
643 if (e && colonPos >= 0) {
644 e->setPrefix(qName.left(colonPos), exceptioncode);
653 e = new XMLElementImpl( document, _qualifiedName.implementation(), _namespaceURI.implementation() );
658 ElementImpl *DocumentImpl::getElementById( const DOMString &elementId ) const
660 if (elementId.length() == 0) {
664 return m_elementsById.find(elementId.string());
667 ElementImpl *DocumentImpl::elementFromPoint( const int _x, const int _y ) const
669 if (!m_render) return 0;
671 RenderObject::NodeInfo nodeInfo(true, true);
672 m_render->layer()->hitTest(nodeInfo, _x, _y);
673 NodeImpl* n = nodeInfo.innerNode();
675 while ( n && !n->isElementNode() ) {
679 return static_cast<ElementImpl*>(n);
682 void DocumentImpl::addElementById(const DOMString &elementId, ElementImpl *element)
684 QString qId = elementId.string();
686 if (m_elementsById.find(qId) == NULL) {
687 m_elementsById.insert(qId, element);
688 m_accessKeyDictValid = false;
692 void DocumentImpl::removeElementById(const DOMString &elementId, ElementImpl *element)
694 QString qId = elementId.string();
696 if (m_elementsById.find(qId) == element) {
697 m_elementsById.remove(qId);
698 m_accessKeyDictValid = false;
702 ElementImpl *DocumentImpl::getElementByAccessKey( const DOMString &key )
704 if (key.length() == 0)
707 QString k(key.string());
708 if (!m_accessKeyDictValid) {
709 m_elementsByAccessKey.clear();
712 for (n = this; n != 0; n = n->traverseNextNode()) {
713 if (!n->isElementNode())
715 const ElementImpl *elementImpl = static_cast<const ElementImpl *>(n);
716 DOMString accessKey(elementImpl->getAttribute(ATTR_ACCESSKEY));
717 if (!accessKey.isEmpty()) {
718 QString ak = accessKey.string().lower();
719 if (m_elementsByAccessKey.find(ak) == NULL)
720 m_elementsByAccessKey.insert(ak, elementImpl);
723 m_accessKeyDictValid = true;
725 return m_elementsByAccessKey.find(k);
728 void DocumentImpl::setTitle(DOMString _title)
736 KWQ(part())->setTitle(_title);
738 QString titleStr = m_title.string();
739 for (int i = 0; i < titleStr.length(); ++i)
740 if (titleStr[i] < ' ')
742 titleStr = titleStr.stripWhiteSpace();
744 if ( !part()->parentPart() ) {
745 if (titleStr.isNull() || titleStr.isEmpty()) {
746 // empty title... set window caption as the URL
748 url.setRef(QString::null);
749 url.setQuery(QString::null);
750 titleStr = url.url();
753 emit part()->setWindowCaption( KStringHandler::csqueeze( titleStr, 128 ) );
758 DOMString DocumentImpl::nodeName() const
763 unsigned short DocumentImpl::nodeType() const
765 return Node::DOCUMENT_NODE;
768 ElementImpl *DocumentImpl::createHTMLElement( const DOMString &name, int &exceptioncode )
770 if (!isValidName(name)) {
771 exceptioncode = DOMException::INVALID_CHARACTER_ERR;
774 return createHTMLElement(tagId(0, name.implementation(), false));
777 ElementImpl *DocumentImpl::createHTMLElement(unsigned short tagID)
782 return new HTMLHtmlElementImpl(docPtr());
784 return new HTMLHeadElementImpl(docPtr());
786 return new HTMLBodyElementImpl(docPtr());
790 return new HTMLBaseElementImpl(docPtr());
792 return new HTMLLinkElementImpl(docPtr());
794 return new HTMLMetaElementImpl(docPtr());
796 return new HTMLStyleElementImpl(docPtr());
798 return new HTMLTitleElementImpl(docPtr());
802 return new HTMLFrameElementImpl(docPtr());
804 return new HTMLFrameSetElementImpl(docPtr());
806 return new HTMLIFrameElementImpl(docPtr());
809 // ### FIXME: we need a way to set form dependency after we have made the form elements
811 return new HTMLFormElementImpl(docPtr());
813 return new HTMLButtonElementImpl(docPtr());
815 return new HTMLFieldSetElementImpl(docPtr());
817 return new HTMLInputElementImpl(docPtr());
819 return new HTMLIsIndexElementImpl(docPtr());
821 return new HTMLLabelElementImpl(docPtr());
823 return new HTMLLegendElementImpl(docPtr());
825 return new HTMLOptGroupElementImpl(docPtr());
827 return new HTMLOptionElementImpl(docPtr());
829 return new HTMLSelectElementImpl(docPtr());
831 return new HTMLTextAreaElementImpl(docPtr());
835 return new HTMLDListElementImpl(docPtr());
837 return new HTMLGenericElementImpl(docPtr(), tagID);
839 return new HTMLGenericElementImpl(docPtr(), tagID);
841 return new HTMLUListElementImpl(docPtr());
843 return new HTMLOListElementImpl(docPtr());
845 return new HTMLDirectoryElementImpl(docPtr());
847 return new HTMLMenuElementImpl(docPtr());
849 return new HTMLLIElementImpl(docPtr());
851 // formatting elements (block)
853 return new HTMLBlockquoteElementImpl(docPtr());
855 return new HTMLDivElementImpl(docPtr());
862 return new HTMLHeadingElementImpl(docPtr(), tagID);
864 return new HTMLHRElementImpl(docPtr());
866 return new HTMLParagraphElementImpl(docPtr());
870 return new HTMLPreElementImpl(docPtr(), tagID);
872 return new HTMLLayerElementImpl(docPtr());
876 return new HTMLBaseFontElementImpl(docPtr());
878 return new HTMLFontElementImpl(docPtr());
883 return new HTMLModElementImpl(docPtr(), tagID);
887 return new HTMLAnchorElementImpl(docPtr());
891 return new HTMLImageElementImpl(docPtr());
893 return new HTMLMapElementImpl(docPtr());
895 return new HTMLAreaElementImpl(docPtr());
897 return new HTMLCanvasElementImpl(docPtr());
899 // objects, applets and scripts
901 return new HTMLAppletElementImpl(docPtr());
903 return new HTMLEmbedElementImpl(docPtr());
905 return new HTMLObjectElementImpl(docPtr());
907 return new HTMLParamElementImpl(docPtr());
909 return new HTMLScriptElementImpl(docPtr());
913 return new HTMLTableElementImpl(docPtr());
915 return new HTMLTableCaptionElementImpl(docPtr());
918 return new HTMLTableColElementImpl(docPtr(), tagID);
920 return new HTMLTableRowElementImpl(docPtr());
923 return new HTMLTableCellElementImpl(docPtr(), tagID);
927 return new HTMLTableSectionElementImpl(docPtr(), tagID, false);
931 return new HTMLBRElementImpl(docPtr());
933 return new HTMLQuoteElementImpl(docPtr());
936 return new HTMLMarqueeElementImpl(docPtr());
938 // elements with no special representation in the DOM
976 return new HTMLGenericElementImpl(docPtr(), tagID);
982 QString DocumentImpl::nextState()
985 if (!m_state.isEmpty())
987 state = m_state.first();
988 m_state.remove(m_state.begin());
993 QStringList DocumentImpl::docState()
996 for (QPtrListIterator<NodeImpl> it(m_maintainsState); it.current(); ++it)
997 s.append(it.current()->state());
1002 KHTMLPart *DocumentImpl::part() const
1004 return m_view ? m_view->part() : 0;
1007 RangeImpl *DocumentImpl::createRange()
1009 return new RangeImpl( docPtr() );
1012 NodeIteratorImpl *DocumentImpl::createNodeIterator(NodeImpl *root, unsigned long whatToShow,
1013 NodeFilterImpl *filter, bool expandEntityReferences, int &exceptioncode)
1016 exceptioncode = DOMException::NOT_SUPPORTED_ERR;
1019 return new NodeIteratorImpl(root, whatToShow, filter, expandEntityReferences);
1022 TreeWalkerImpl *DocumentImpl::createTreeWalker(NodeImpl *root, unsigned long whatToShow,
1023 NodeFilterImpl *filter, bool expandEntityReferences, int &exceptioncode)
1026 exceptioncode = DOMException::NOT_SUPPORTED_ERR;
1029 return new TreeWalkerImpl(root, whatToShow, filter, expandEntityReferences);
1032 void DocumentImpl::setDocumentChanged(bool b)
1034 if (!changedDocuments)
1035 changedDocuments = s_changedDocumentsDeleter.setObject( new QPtrList<DocumentImpl>() );
1037 if (b && !m_docChanged)
1038 changedDocuments->append(this);
1039 else if (!b && m_docChanged)
1040 changedDocuments->remove(this);
1044 m_accessKeyDictValid = false;
1047 void DocumentImpl::recalcStyle( StyleChange change )
1049 // qDebug("recalcStyle(%p)", this);
1052 if (m_inStyleRecalc)
1053 return; // Guard against re-entrancy. -dwh
1055 m_inStyleRecalc = true;
1057 if( !m_render ) goto bail_out;
1059 if ( change == Force ) {
1060 RenderStyle* oldStyle = m_render->style();
1061 if ( oldStyle ) oldStyle->ref();
1062 RenderStyle* _style = new (m_renderArena) RenderStyle();
1064 _style->setDisplay(BLOCK);
1065 _style->setVisuallyOrdered( visuallyOrdered );
1066 // ### make the font stuff _really_ work!!!!
1068 khtml::FontDef fontDef;
1069 QFont f = KGlobalSettings::generalFont();
1070 fontDef.family = *(f.firstFamily());
1071 fontDef.italic = f.italic();
1072 fontDef.weight = f.weight();
1074 bool printing = m_paintDevice && (m_paintDevice->devType() == QInternal::Printer);
1075 fontDef.usePrinterFont = printing;
1078 const KHTMLSettings *settings = m_view->part()->settings();
1080 if (printing && !settings->shouldPrintBackgrounds()) {
1081 _style->setForceBackgroundsToWhite(true);
1084 QString stdfont = settings->stdFontName();
1085 if ( !stdfont.isEmpty() ) {
1086 fontDef.family.setFamily(stdfont);
1087 fontDef.family.appendFamily(0);
1089 m_styleSelector->setFontSize(fontDef, m_styleSelector->fontSizeForKeyword(CSS_VAL_MEDIUM, inCompatMode()));
1092 //kdDebug() << "DocumentImpl::attach: setting to charset " << settings->charset() << endl;
1093 _style->setFontDef(fontDef);
1094 _style->htmlFont().update( paintDeviceMetrics() );
1095 if ( inCompatMode() )
1096 _style->setHtmlHacks(true); // enable html specific rendering tricks
1098 StyleChange ch = diff( _style, oldStyle );
1099 if(m_render && ch != NoChange)
1100 m_render->setStyle(_style);
1101 if ( change != Force )
1104 _style->deref(m_renderArena);
1106 oldStyle->deref(m_renderArena);
1110 for (n = _first; n; n = n->nextSibling())
1111 if ( change>= Inherit || n->hasChangedChild() || n->changed() )
1112 n->recalcStyle( change );
1113 //kdDebug( 6020 ) << "TIME: recalcStyle() dt=" << qt.elapsed() << endl;
1115 if (changed() && m_view)
1119 setChanged( false );
1120 setHasChangedChild( false );
1121 setDocumentChanged( false );
1123 m_inStyleRecalc = false;
1125 // If we wanted to emit the implicitClose() during recalcStyle, do so now that we're finished.
1126 if (m_closeAfterStyleRecalc) {
1127 m_closeAfterStyleRecalc = false;
1132 void DocumentImpl::updateRendering()
1134 if (!hasChangedChild()) return;
1138 // kdDebug() << "UPDATERENDERING: "<<endl;
1140 StyleChange change = NoChange;
1142 if ( m_styleSelectorDirty ) {
1143 recalcStyleSelector();
1147 recalcStyle( change );
1149 // kdDebug() << "UPDATERENDERING time used="<<time.elapsed()<<endl;
1152 void DocumentImpl::updateDocumentsRendering()
1154 if (!changedDocuments)
1157 while (DocumentImpl* doc = changedDocuments->take()) {
1158 doc->m_docChanged = false;
1159 doc->updateRendering();
1163 void DocumentImpl::updateLayout()
1165 // FIXME: Dave's pretty sure we can remove this because
1166 // layout calls recalcStyle as needed.
1169 // Only do a layout if changes have occurred that make it necessary.
1170 if (m_view && renderer() && renderer()->needsLayout())
1174 // FIXME: This is a bad idea and needs to be removed eventually.
1175 // Other browsers load stylesheets before they continue parsing the web page.
1176 // Since we don't, we can run JavaScript code that needs answers before the
1177 // stylesheets are loaded. Doing a layout ignoring the pending stylesheets
1178 // lets us get reasonable answers. The long term solution to this problem is
1179 // to instead suspend JavaScript execution.
1180 void DocumentImpl::updateLayoutIgnorePendingStylesheets()
1182 bool oldIgnore = m_ignorePendingStylesheets;
1184 if (!haveStylesheetsLoaded()) {
1185 m_ignorePendingStylesheets = true;
1186 updateStyleSelector();
1191 m_ignorePendingStylesheets = oldIgnore;
1194 void DocumentImpl::attach()
1196 assert(!attached());
1198 assert(!m_inPageCache);
1202 setPaintDevice( m_view );
1205 m_renderArena = new RenderArena();
1207 // Create the rendering tree
1208 m_render = new (m_renderArena) RenderCanvas(this, m_view);
1209 recalcStyle( Force );
1211 RenderObject* render = m_render;
1214 ContainerNodeImpl::attach();
1218 void DocumentImpl::restoreRenderer(RenderObject* render)
1223 void DocumentImpl::detach()
1225 RenderObject* render = m_render;
1227 // indicate destruction mode, i.e. attached() but m_render == 0
1231 if (m_inPageCache) {
1233 getAccObjectCache()->detach(render);
1238 // Empty out these lists as a performance optimization, since detaching
1239 // all the individual render objects will cause all the RenderImage
1240 // objects to remove themselves from the lists.
1241 m_imageLoadEventDispatchSoonList.clear();
1242 m_imageLoadEventDispatchingList.clear();
1245 // FIXME: UNLOAD_EVENT will not dispatch due to deleting event listeners prior to closeURL().
1246 removeAllEventListenersFromAllNodes();
1248 ContainerNodeImpl::detach();
1253 if (m_paintDevice == m_view)
1258 delete m_renderArena;
1263 void DocumentImpl::removeAllEventListenersFromAllNodes()
1265 m_windowEventListeners.clear();
1266 removeAllDisconnectedNodeEventListeners();
1267 for (NodeImpl *n = this; n; n = n->traverseNextNode()) {
1268 n->removeAllEventListeners();
1272 void DocumentImpl::registerDisconnectedNodeWithEventListeners(NodeImpl *node)
1274 m_disconnectedNodesWithEventListeners.insert(node, node);
1277 void DocumentImpl::unregisterDisconnectedNodeWithEventListeners(NodeImpl *node)
1279 m_disconnectedNodesWithEventListeners.remove(node);
1282 void DocumentImpl::removeAllDisconnectedNodeEventListeners()
1284 for (QPtrDictIterator<NodeImpl> iter(m_disconnectedNodesWithEventListeners);
1287 iter.current()->removeAllEventListeners();
1292 KWQAccObjectCache* DocumentImpl::getAccObjectCache()
1294 // The only document that actually has a KWQAccObjectCache is the top-level
1295 // document. This is because we need to be able to get from any KWQAccObject
1296 // to any other KWQAccObject on the same page. Using a single cache allows
1297 // lookups across nested webareas (i.e. multiple documents).
1300 // return already known top-level cache
1301 if (!ownerElement())
1304 // In some pages with frames, the cache is created before the sub-webarea is
1305 // inserted into the tree. Here, we catch that case and just toss the old
1306 // cache and start over.
1311 // look for top-level document
1312 ElementImpl *element = ownerElement();
1316 doc = element->getDocument();
1317 element = doc->ownerElement();
1320 // ask the top-level document for its cache
1321 return doc->getAccObjectCache();
1324 // this is the top-level document, so install a new cache
1325 m_accCache = new KWQAccObjectCache;
1330 void DocumentImpl::setVisuallyOrdered()
1332 visuallyOrdered = true;
1334 m_render->style()->setVisuallyOrdered(true);
1337 void DocumentImpl::updateSelection()
1342 RenderCanvas *canvas = static_cast<RenderCanvas*>(m_render);
1343 Selection s = part()->selection();
1345 canvas->clearSelection();
1348 Position startPos = VisiblePosition(s.start(), s.startAffinity()).deepEquivalent();
1349 Position endPos = VisiblePosition(s.end(), s.endAffinity()).deepEquivalent();
1350 if (startPos.isNotNull() && endPos.isNotNull()) {
1351 RenderObject *startRenderer = startPos.node()->renderer();
1352 RenderObject *endRenderer = endPos.node()->renderer();
1353 static_cast<RenderCanvas*>(m_render)->setSelection(startRenderer, startPos.offset(), endRenderer, endPos.offset());
1358 // send the AXSelectedTextChanged notification only if the new selection is non-null,
1359 // because null selections are only transitory (e.g. when starting an EditCommand, currently)
1360 if (KWQAccObjectCache::accessibilityEnabled() && s.start().isNotNull() && s.end().isNotNull()) {
1361 getAccObjectCache()->postNotificationToTopWebArea(renderer(), "AXSelectedTextChanged");
1367 Tokenizer *DocumentImpl::createTokenizer()
1369 return newXMLTokenizer(docPtr(), m_view);
1372 void DocumentImpl::setPaintDevice( QPaintDevice *dev )
1374 if (m_paintDevice == dev) {
1377 m_paintDevice = dev;
1378 delete m_paintDeviceMetrics;
1379 m_paintDeviceMetrics = dev ? new QPaintDeviceMetrics( dev ) : 0;
1382 void DocumentImpl::open( )
1384 if (parsing()) return;
1389 part()->didExplicitOpen();
1392 // This is work that we should probably do in clear(), but we can't have it
1393 // happen when implicitOpen() is called unless we reorganize KHTMLPart code.
1395 DocumentImpl *parent = parentDocument();
1397 setBaseURL(parent->baseURL());
1401 void DocumentImpl::implicitOpen()
1407 m_tokenizer = createTokenizer();
1408 connect(m_tokenizer,SIGNAL(finishedParsing()),this,SIGNAL(finishedParsing()));
1411 if (m_view && m_view->part()->jScript()) {
1412 m_view->part()->jScript()->setSourceFile(m_url,""); //fixme
1416 HTMLElementImpl* DocumentImpl::body()
1418 NodeImpl *de = documentElement();
1422 // try to prefer a FRAMESET element over BODY
1424 for (NodeImpl* i = de->firstChild(); i; i = i->nextSibling()) {
1425 if (i->id() == ID_FRAMESET)
1426 return static_cast<HTMLElementImpl*>(i);
1428 if (i->id() == ID_BODY)
1431 return static_cast<HTMLElementImpl *>(body);
1434 void DocumentImpl::close()
1437 part()->endIfNotLoading();
1441 void DocumentImpl::implicitClose()
1443 // If we're in the middle of recalcStyle, we need to defer the close until the style information is accurate and all elements are re-attached.
1444 if (m_inStyleRecalc) {
1445 m_closeAfterStyleRecalc = true;
1449 // First fire the onload.
1451 bool wasLocationChangePending = part() && part()->isScheduledLocationChangePending();
1452 bool doload = !parsing() && m_tokenizer && !m_processingLoadEvent && !wasLocationChangePending;
1455 m_processingLoadEvent = true;
1457 // We have to clear the tokenizer, in case someone document.write()s from the
1458 // onLoad event handler, as in Radar 3206524
1462 // Create a body element if we don't already have one.
1463 // In the case of Radar 3758785, the window.onload was set in some javascript, but never fired because there was no body.
1464 // This behavior now matches Firefox and IE.
1465 HTMLElementImpl *body = this->body();
1466 if (!body && isHTMLDocument()) {
1467 NodeImpl *de = documentElement();
1469 body = new HTMLBodyElementImpl(docPtr());
1470 int exceptionCode = 0;
1471 de->appendChild(body, exceptionCode);
1472 if (exceptionCode != 0)
1478 dispatchImageLoadEventsNow();
1479 body->dispatchWindowEvent(EventImpl::LOAD_EVENT, false, false);
1481 #ifdef INSTRUMENT_LAYOUT_SCHEDULING
1482 if (!ownerElement())
1483 printf("onload fired at %d\n", elapsedTime());
1487 m_processingLoadEvent = false;
1490 // Make sure both the initial layout and reflow happen after the onload
1491 // fires. This will improve onload scores, and other browsers do it.
1492 // If they wanna cheat, we can too. -dwh
1494 bool isLocationChangePending = part() && part()->isScheduledLocationChangePending();
1496 if (doload && isLocationChangePending && m_startTime.elapsed() < cLayoutScheduleThreshold) {
1497 // Just bail out. Before or during the onload we were shifted to another page.
1498 // The old i-Bench suite does this. When this happens don't bother painting or laying out.
1501 view()->unscheduleRelayout();
1506 // on an explicit document.close(), the tokenizer might still be waiting on scripts,
1507 // and in that case we don't want to destroy it because that will prevent the
1508 // scripts from getting processed.
1509 // FIXME: this check may no longer be necessary, since now it should be impossible
1510 // for parsing to be false while stil waiting for scripts
1511 if (m_tokenizer && !m_tokenizer->isWaitingForScripts()) {
1517 m_view->part()->checkEmitLoadEvent();
1520 // Now do our painting/layout, but only if we aren't in a subframe or if we're in a subframe
1521 // that has been sized already. Otherwise, our view size would be incorrect, so doing any
1522 // layout/painting now would be pointless.
1524 if (!ownerElement() || (ownerElement()->renderer() && !ownerElement()->renderer()->needsLayout())) {
1527 // Always do a layout after loading if needed.
1528 if (view() && renderer() && (!renderer()->firstChild() || renderer()->needsLayout()))
1532 if (renderer() && KWQAccObjectCache::accessibilityEnabled())
1533 getAccObjectCache()->postNotification(renderer(), "AXLoadComplete");
1538 void DocumentImpl::setParsing(bool b)
1541 if (!m_bParsing && view())
1542 view()->scheduleRelayout();
1544 #ifdef INSTRUMENT_LAYOUT_SCHEDULING
1545 if (!ownerElement() && !m_bParsing)
1546 printf("Parsing finished at %d\n", elapsedTime());
1550 bool DocumentImpl::shouldScheduleLayout()
1552 // We can update layout if:
1553 // (a) we actually need a layout
1554 // (b) our stylesheets are all loaded
1555 // (c) we have a <body>
1556 return (renderer() && renderer()->needsLayout() && haveStylesheetsLoaded() &&
1557 documentElement() && documentElement()->renderer() &&
1558 (documentElement()->id() != ID_HTML || body()));
1561 int DocumentImpl::minimumLayoutDelay()
1563 if (m_overMinimumLayoutThreshold)
1566 int elapsed = m_startTime.elapsed();
1567 m_overMinimumLayoutThreshold = elapsed > cLayoutScheduleThreshold;
1569 // We'll want to schedule the timer to fire at the minimum layout threshold.
1570 return kMax(0, cLayoutScheduleThreshold - elapsed);
1573 int DocumentImpl::elapsedTime() const
1575 return m_startTime.elapsed();
1578 void DocumentImpl::write( const DOMString &text )
1580 write(text.string());
1583 void DocumentImpl::write( const QString &text )
1585 #ifdef INSTRUMENT_LAYOUT_SCHEDULING
1586 if (!ownerElement())
1587 printf("Beginning a document.write at %d\n", elapsedTime());
1592 write(QString::fromLatin1("<html>"));
1594 m_tokenizer->write(text, false);
1596 if (m_view && m_view->part()->jScript())
1597 m_view->part()->jScript()->appendSourceFile(m_url,text);
1599 #ifdef INSTRUMENT_LAYOUT_SCHEDULING
1600 if (!ownerElement())
1601 printf("Ending a document.write at %d\n", elapsedTime());
1605 void DocumentImpl::writeln( const DOMString &text )
1608 write(DOMString("\n"));
1611 void DocumentImpl::finishParsing()
1613 #ifdef INSTRUMENT_LAYOUT_SCHEDULING
1614 if (!ownerElement())
1615 printf("Received all data at %d\n", elapsedTime());
1618 // Let the tokenizer go through as much data as it can. There will be three possible outcomes after
1619 // finish() is called:
1620 // (1) All remaining data is parsed, document isn't loaded yet
1621 // (2) All remaining data is parsed, document is loaded, tokenizer gets deleted
1622 // (3) Data is still remaining to be parsed.
1624 m_tokenizer->finish();
1627 void DocumentImpl::clear()
1633 QPtrListIterator<RegisteredEventListener> it(m_windowEventListeners);
1634 for (; it.current();)
1635 m_windowEventListeners.removeRef(it.current());
1638 void DocumentImpl::setURL(const QString& url)
1641 if (m_styleSelector)
1642 m_styleSelector->setEncodedURL(m_url);
1645 void DocumentImpl::setStyleSheet(const DOMString &url, const DOMString &sheet)
1647 // kdDebug( 6030 ) << "HTMLDocument::setStyleSheet()" << endl;
1648 m_sheet = new CSSStyleSheetImpl(this, url);
1650 m_sheet->parseString(sheet);
1651 m_loadingSheet = false;
1653 updateStyleSelector();
1656 void DocumentImpl::setUserStyleSheet( const QString& sheet )
1658 if ( m_usersheet != sheet ) {
1659 m_usersheet = sheet;
1660 updateStyleSelector();
1664 CSSStyleSheetImpl* DocumentImpl::elementSheet()
1667 m_elemSheet = new CSSStyleSheetImpl(this, baseURL() );
1673 void DocumentImpl::determineParseMode( const QString &/*str*/ )
1675 // For XML documents use strict parse mode. HTML docs will override this method to
1676 // determine their parse mode.
1679 kdDebug(6020) << " using strict parseMode" << endl;
1682 // Please see if there`s a possibility to merge that code
1683 // with the next function and getElementByID().
1684 NodeImpl *DocumentImpl::findElement( Id id )
1686 QPtrStack<NodeImpl> nodeStack;
1687 NodeImpl *current = _first;
1693 if(nodeStack.isEmpty()) break;
1694 current = nodeStack.pop();
1695 current = current->nextSibling();
1699 if(current->id() == id)
1702 NodeImpl *child = current->firstChild();
1705 nodeStack.push(current);
1710 current = current->nextSibling();
1718 NodeImpl *DocumentImpl::nextFocusNode(NodeImpl *fromNode)
1720 unsigned short fromTabIndex;
1723 // No starting node supplied; begin with the top of the document
1726 int lowestTabIndex = 65535;
1727 for (n = this; n != 0; n = n->traverseNextNode()) {
1728 if (n->isKeyboardFocusable()) {
1729 if ((n->tabIndex() > 0) && (n->tabIndex() < lowestTabIndex))
1730 lowestTabIndex = n->tabIndex();
1734 if (lowestTabIndex == 65535)
1737 // Go to the first node in the document that has the desired tab index
1738 for (n = this; n != 0; n = n->traverseNextNode()) {
1739 if (n->isKeyboardFocusable() && (n->tabIndex() == lowestTabIndex))
1746 fromTabIndex = fromNode->tabIndex();
1749 if (fromTabIndex == 0) {
1750 // Just need to find the next selectable node after fromNode (in document order) that doesn't have a tab index
1751 NodeImpl *n = fromNode->traverseNextNode();
1752 while (n && !(n->isKeyboardFocusable() && n->tabIndex() == 0))
1753 n = n->traverseNextNode();
1757 // Find the lowest tab index out of all the nodes except fromNode, that is greater than or equal to fromNode's
1758 // tab index. For nodes with the same tab index as fromNode, we are only interested in those that come after
1759 // fromNode in document order.
1760 // If we don't find a suitable tab index, the next focus node will be one with a tab index of 0.
1761 unsigned short lowestSuitableTabIndex = 65535;
1764 bool reachedFromNode = false;
1765 for (n = this; n != 0; n = n->traverseNextNode()) {
1766 if (n->isKeyboardFocusable() &&
1767 ((reachedFromNode && (n->tabIndex() >= fromTabIndex)) ||
1768 (!reachedFromNode && (n->tabIndex() > fromTabIndex))) &&
1769 (n->tabIndex() < lowestSuitableTabIndex) &&
1772 // We found a selectable node with a tab index at least as high as fromNode's. Keep searching though,
1773 // as there may be another node which has a lower tab index but is still suitable for use.
1774 lowestSuitableTabIndex = n->tabIndex();
1778 reachedFromNode = true;
1781 if (lowestSuitableTabIndex == 65535) {
1782 // No next node with a tab index -> just take first node with tab index of 0
1784 while (n && !(n->isKeyboardFocusable() && n->tabIndex() == 0))
1785 n = n->traverseNextNode();
1789 // Search forwards from fromNode
1790 for (n = fromNode->traverseNextNode(); n != 0; n = n->traverseNextNode()) {
1791 if (n->isKeyboardFocusable() && (n->tabIndex() == lowestSuitableTabIndex))
1795 // The next node isn't after fromNode, start from the beginning of the document
1796 for (n = this; n != fromNode; n = n->traverseNextNode()) {
1797 if (n->isKeyboardFocusable() && (n->tabIndex() == lowestSuitableTabIndex))
1801 assert(false); // should never get here
1806 NodeImpl *DocumentImpl::previousFocusNode(NodeImpl *fromNode)
1808 NodeImpl *lastNode = this;
1809 while (lastNode->lastChild())
1810 lastNode = lastNode->lastChild();
1813 // No starting node supplied; begin with the very last node in the document
1816 int highestTabIndex = 0;
1817 for (n = lastNode; n != 0; n = n->traversePreviousNode()) {
1818 if (n->isKeyboardFocusable()) {
1819 if (n->tabIndex() == 0)
1821 else if (n->tabIndex() > highestTabIndex)
1822 highestTabIndex = n->tabIndex();
1826 // No node with a tab index of 0; just go to the last node with the highest tab index
1827 for (n = lastNode; n != 0; n = n->traversePreviousNode()) {
1828 if (n->isKeyboardFocusable() && (n->tabIndex() == highestTabIndex))
1835 unsigned short fromTabIndex = fromNode->tabIndex();
1837 if (fromTabIndex == 0) {
1838 // Find the previous selectable node before fromNode (in document order) that doesn't have a tab index
1839 NodeImpl *n = fromNode->traversePreviousNode();
1840 while (n && !(n->isKeyboardFocusable() && n->tabIndex() == 0))
1841 n = n->traversePreviousNode();
1845 // No previous nodes with a 0 tab index, go to the last node in the document that has the highest tab index
1846 int highestTabIndex = 0;
1847 for (n = this; n != 0; n = n->traverseNextNode()) {
1848 if (n->isKeyboardFocusable() && (n->tabIndex() > highestTabIndex))
1849 highestTabIndex = n->tabIndex();
1852 if (highestTabIndex == 0)
1855 for (n = lastNode; n != 0; n = n->traversePreviousNode()) {
1856 if (n->isKeyboardFocusable() && (n->tabIndex() == highestTabIndex))
1860 assert(false); // should never get here
1864 // Find the lowest tab index out of all the nodes except fromNode, that is less than or equal to fromNode's
1865 // tab index. For nodes with the same tab index as fromNode, we are only interested in those before
1867 // If we don't find a suitable tab index, then there will be no previous focus node.
1868 unsigned short highestSuitableTabIndex = 0;
1871 bool reachedFromNode = false;
1872 for (n = this; n != 0; n = n->traverseNextNode()) {
1873 if (n->isKeyboardFocusable() &&
1874 ((!reachedFromNode && (n->tabIndex() <= fromTabIndex)) ||
1875 (reachedFromNode && (n->tabIndex() < fromTabIndex))) &&
1876 (n->tabIndex() > highestSuitableTabIndex) &&
1879 // We found a selectable node with a tab index no higher than fromNode's. Keep searching though, as
1880 // there may be another node which has a higher tab index but is still suitable for use.
1881 highestSuitableTabIndex = n->tabIndex();
1885 reachedFromNode = true;
1888 if (highestSuitableTabIndex == 0) {
1889 // No previous node with a tab index. Since the order specified by HTML is nodes with tab index > 0
1890 // first, this means that there is no previous node.
1894 // Search backwards from fromNode
1895 for (n = fromNode->traversePreviousNode(); n != 0; n = n->traversePreviousNode()) {
1896 if (n->isKeyboardFocusable() && (n->tabIndex() == highestSuitableTabIndex))
1899 // The previous node isn't before fromNode, start from the end of the document
1900 for (n = lastNode; n != fromNode; n = n->traversePreviousNode()) {
1901 if (n->isKeyboardFocusable() && (n->tabIndex() == highestSuitableTabIndex))
1905 assert(false); // should never get here
1911 int DocumentImpl::nodeAbsIndex(NodeImpl *node)
1913 assert(node->getDocument() == this);
1916 for (NodeImpl *n = node; n && n != this; n = n->traversePreviousNode())
1921 NodeImpl *DocumentImpl::nodeWithAbsIndex(int absIndex)
1924 for (int i = 0; n && (i < absIndex); i++) {
1925 n = n->traverseNextNode();
1930 void DocumentImpl::processHttpEquiv(const DOMString &equiv, const DOMString &content)
1932 assert(!equiv.isNull() && !content.isNull());
1934 KHTMLPart *part = this->part();
1936 if (strcasecmp(equiv, "default-style") == 0) {
1937 // The preferred style set has been overridden as per section
1938 // 14.3.2 of the HTML4.0 specification. We need to update the
1939 // sheet used variable and then update our style selector.
1940 // For more info, see the test at:
1941 // http://www.hixie.ch/tests/evil/css/import/main/preferred.html
1943 part->d->m_sheetUsed = content.string();
1944 m_preferredStylesheetSet = content;
1945 updateStyleSelector();
1947 else if(strcasecmp(equiv, "refresh") == 0 && part->metaRefreshEnabled())
1949 // get delay and url
1950 QString str = content.string().stripWhiteSpace();
1951 int pos = str.find(QRegExp("[;,]"));
1953 pos = str.find(QRegExp("[ \t]"));
1955 if (pos == -1) // There can be no url (David)
1959 delay = str.toInt(&ok);
1961 // We want a new history item if the refresh timeout > 1 second
1962 if(ok && part) part->scheduleRedirection(delay, part->url().url(), delay <= 1);
1964 if(ok && part) part->scheduleRedirection(delay, part->url().url() );
1969 delay = str.left(pos).stripWhiteSpace().toDouble(&ok);
1972 while(pos < (int)str.length() && str[pos].isSpace()) pos++;
1974 if(str.find("url", 0, false ) == 0) str = str.mid(3);
1975 str = str.stripWhiteSpace();
1976 if ( str.length() && str[0] == '=' ) str = str.mid( 1 ).stripWhiteSpace();
1977 str = parseURL( DOMString(str) ).string();
1980 // We want a new history item if the refresh timeout > 1 second
1981 part->scheduleRedirection(delay, completeURL( str ), delay <= 1);
1983 part->scheduleRedirection(delay, completeURL( str ));
1987 else if(strcasecmp(equiv, "expires") == 0)
1989 QString str = content.string().stripWhiteSpace();
1990 time_t expire_date = str.toLong();
1992 m_docLoader->setExpireDate(expire_date);
1994 else if(strcasecmp(equiv, "pragma") == 0 || strcasecmp(equiv, "cache-control") == 0 && part)
1996 QString str = content.string().lower().stripWhiteSpace();
1997 KURL url = part->url();
1998 if ((str == "no-cache") && url.protocol().startsWith("http"))
2000 KIO::http_update_cache(url, true, 0);
2003 else if( (strcasecmp(equiv, "set-cookie") == 0))
2005 // ### make setCookie work on XML documents too; e.g. in case of <html:meta .....>
2006 HTMLDocumentImpl *d = static_cast<HTMLDocumentImpl *>(this);
2007 d->setCookie(content);
2011 bool DocumentImpl::prepareMouseEvent( bool readonly, int _x, int _y, MouseEvent *ev )
2014 assert(m_render->isCanvas());
2015 RenderObject::NodeInfo renderInfo(readonly, ev->type == MousePress);
2016 bool isInside = m_render->layer()->hitTest(renderInfo, _x, _y);
2017 ev->innerNode.reset(renderInfo.innerNode());
2019 if (renderInfo.URLElement()) {
2020 assert(renderInfo.URLElement()->isElementNode());
2021 ElementImpl* e = static_cast<ElementImpl*>(renderInfo.URLElement());
2022 DOMString href = khtml::parseURL(e->getAttribute(ATTR_HREF));
2023 DOMString target = e->getAttribute(ATTR_TARGET);
2025 if (!target.isNull() && !href.isNull()) {
2026 ev->target = target;
2031 // qDebug("url: *%s*", ev->url.string().latin1());
2044 // DOM Section 1.1.1
2045 bool DocumentImpl::childAllowed( NodeImpl *newChild )
2047 // Documents may contain a maximum of one Element child
2048 if (newChild->nodeType() == Node::ELEMENT_NODE) {
2050 for (c = firstChild(); c; c = c->nextSibling()) {
2051 if (c->nodeType() == Node::ELEMENT_NODE)
2056 // Documents may contain a maximum of one DocumentType child
2057 if (newChild->nodeType() == Node::DOCUMENT_TYPE_NODE) {
2059 for (c = firstChild(); c; c = c->nextSibling()) {
2060 if (c->nodeType() == Node::DOCUMENT_TYPE_NODE)
2065 return childTypeAllowed(newChild->nodeType());
2068 bool DocumentImpl::childTypeAllowed( unsigned short type )
2071 case Node::ELEMENT_NODE:
2072 case Node::PROCESSING_INSTRUCTION_NODE:
2073 case Node::COMMENT_NODE:
2074 case Node::DOCUMENT_TYPE_NODE:
2081 NodeImpl *DocumentImpl::cloneNode ( bool /*deep*/ )
2083 // Spec says cloning Document nodes is "implementation dependent"
2084 // so we do not support it...
2088 NodeImpl::Id DocumentImpl::attrId(DOMStringImpl* _namespaceURI, DOMStringImpl *_name, bool readonly)
2090 // Each document maintains a mapping of attrname -> id for every attr name
2091 // encountered in the document.
2092 // For attrnames without a prefix (no qualified element name) and without matching
2093 // namespace, the value defined in misc/htmlattrs.h is used.
2094 NodeImpl::Id id = 0;
2096 // First see if it's a HTML attribute name
2097 QConstString n(_name->s, _name->l);
2098 if (!_namespaceURI || !strcasecmp(_namespaceURI, XHTML_NAMESPACE)) {
2099 // we're in HTML namespace if we know the tag.
2100 // xhtml is lower case - case sensitive, easy to implement
2101 if ( htmlMode() == XHtml && (id = getAttrID(n.string().ascii(), _name->l)) )
2103 // compatibility: upper case - case insensitive
2104 if ( htmlMode() != XHtml && (id = getAttrID(n.string().lower().ascii(), _name->l )) )
2107 // ok, the fast path didn't work out, we need the full check
2110 // now lets find out the namespace
2111 Q_UINT16 ns = noNamespace;
2112 if (_namespaceURI) {
2113 DOMString nsU(_namespaceURI);
2114 int nsID = XmlNamespaceTable::getNamespaceID(nsU, readonly);
2116 ns = (Q_UINT16)nsID;
2119 // Look in the m_attrNames array for the name
2120 // ### yeah, this is lame. use a dictionary / map instead
2121 DOMString nme(n.string());
2122 // compatibility mode has to store upper case
2123 if (htmlMode() != XHtml) nme = nme.upper();
2124 for (id = 0; id < m_attrNameCount; id++)
2125 if (DOMString(m_attrNames[id]) == nme)
2126 return makeId(ns, ATTR_LAST_ATTR+id);
2129 if (readonly) return 0;
2131 // Name not found in m_attrNames, so let's add it
2132 // ### yeah, this is lame. use a dictionary / map instead
2133 if (m_attrNameCount+1 > m_attrNameAlloc) {
2134 m_attrNameAlloc += 100;
2135 DOMStringImpl **newNames = new DOMStringImpl* [m_attrNameAlloc];
2138 for (i = 0; i < m_attrNameCount; i++)
2139 newNames[i] = m_attrNames[i];
2140 delete [] m_attrNames;
2142 m_attrNames = newNames;
2145 id = m_attrNameCount++;
2146 m_attrNames[id] = nme.implementation();
2147 m_attrNames[id]->ref();
2149 return makeId(ns, ATTR_LAST_ATTR+id);
2152 DOMString DocumentImpl::attrName(NodeImpl::Id _id) const
2155 if (localNamePart(_id) >= ATTR_LAST_ATTR)
2156 result = m_attrNames[localNamePart(_id)-ATTR_LAST_ATTR];
2158 result = getAttrName(_id);
2160 // Attribute names are always lowercase in the DOM for both
2162 if (isHTMLDocument() || htmlMode() == XHtml)
2163 return result.lower();
2168 NodeImpl::Id DocumentImpl::tagId(DOMStringImpl* _namespaceURI, DOMStringImpl *_name, bool readonly)
2170 if (!_name) return 0;
2171 // Each document maintains a mapping of tag name -> id for every tag name encountered
2173 NodeImpl::Id id = 0;
2175 // First see if it's a HTML element name
2176 QConstString n(_name->s, _name->l);
2177 if (!_namespaceURI || !strcasecmp(_namespaceURI, XHTML_NAMESPACE)) {
2178 // we're in HTML namespace if we know the tag.
2179 // xhtml is lower case - case sensitive, easy to implement
2180 if ( htmlMode() == XHtml && (id = getTagID(n.string().ascii(), _name->l)) )
2182 // compatibility: upper case - case insensitive
2183 if ( htmlMode() != XHtml && (id = getTagID(n.string().lower().ascii(), _name->l )) )
2186 // ok, the fast path didn't work out, we need the full check
2189 // now lets find out the namespace
2190 Q_UINT16 ns = noNamespace;
2191 if (_namespaceURI) {
2192 DOMString nsU(_namespaceURI);
2193 int nsID = XmlNamespaceTable::getNamespaceID(nsU, readonly);
2195 ns = (Q_UINT16)nsID;
2198 // Look in the m_elementNames array for the name
2199 // ### yeah, this is lame. use a dictionary / map instead
2200 DOMString nme(n.string());
2201 // compatibility mode has to store upper case
2202 if (htmlMode() != XHtml) nme = nme.upper();
2203 for (id = 0; id < m_elementNameCount; id++)
2204 if (DOMString(m_elementNames[id]) == nme)
2205 return makeId(ns, ID_LAST_TAG + 1 + id);
2208 if (readonly) return 0;
2210 // Name not found in m_elementNames, so let's add it
2211 if (m_elementNameCount+1 > m_elementNameAlloc) {
2212 m_elementNameAlloc += 100;
2213 DOMStringImpl **newNames = new DOMStringImpl* [m_elementNameAlloc];
2214 // ### yeah, this is lame. use a dictionary / map instead
2215 if (m_elementNames) {
2217 for (i = 0; i < m_elementNameCount; i++)
2218 newNames[i] = m_elementNames[i];
2219 delete [] m_elementNames;
2221 m_elementNames = newNames;
2224 id = m_elementNameCount++;
2225 m_elementNames[id] = nme.implementation();
2226 m_elementNames[id]->ref();
2228 return makeId(ns, ID_LAST_TAG + 1 + id);
2231 DOMString DocumentImpl::tagName(NodeImpl::Id _id) const
2233 if (localNamePart(_id) > ID_LAST_TAG)
2234 return m_elementNames[localNamePart(_id) - (ID_LAST_TAG + 1)];
2236 // ### put them in a cache
2237 if (htmlMode() == XHtml)
2238 return getTagName(_id).lower();
2240 return getTagName(_id);
2245 DOMStringImpl* DocumentImpl::namespaceURI(NodeImpl::Id _id) const
2247 if (_id <= ID_LAST_TAG)
2248 return htmlMode() == XHtml ? XmlNamespaceTable::getNamespaceURI(xhtmlNamespace).implementation() : 0;
2250 unsigned short ns = _id >> 16;
2254 return XmlNamespaceTable::getNamespaceURI(ns).implementation();
2257 StyleSheetListImpl* DocumentImpl::styleSheets()
2259 return m_styleSheets;
2262 DOMString DocumentImpl::preferredStylesheetSet()
2264 return m_preferredStylesheetSet;
2267 DOMString DocumentImpl::selectedStylesheetSet()
2269 return view() ? view()->part()->d->m_sheetUsed : DOMString();
2273 DocumentImpl::setSelectedStylesheetSet(const DOMString& aString)
2276 view()->part()->d->m_sheetUsed = aString.string();
2277 updateStyleSelector();
2279 renderer()->repaint();
2283 // This method is called whenever a top-level stylesheet has finished loading.
2284 void DocumentImpl::stylesheetLoaded()
2286 // Make sure we knew this sheet was pending, and that our count isn't out of sync.
2287 assert(m_pendingStylesheets > 0);
2289 m_pendingStylesheets--;
2291 #ifdef INSTRUMENT_LAYOUT_SCHEDULING
2292 if (!ownerElement())
2293 printf("Stylesheet loaded at time %d. %d stylesheets still remain.\n", elapsedTime(), m_pendingStylesheets);
2296 updateStyleSelector();
2299 void DocumentImpl::updateStyleSelector()
2301 // Don't bother updating, since we haven't loaded all our style info yet.
2302 if (!haveStylesheetsLoaded())
2305 #ifdef INSTRUMENT_LAYOUT_SCHEDULING
2306 if (!ownerElement())
2307 printf("Beginning update of style selector at time %d.\n", elapsedTime());
2310 recalcStyleSelector();
2314 m_styleSelectorDirty = true;
2317 #ifdef INSTRUMENT_LAYOUT_SCHEDULING
2318 if (!ownerElement())
2319 printf("Finished update of style selector at time %d\n", elapsedTime());
2323 renderer()->setNeedsLayoutAndMinMaxRecalc();
2325 view()->scheduleRelayout();
2330 QStringList DocumentImpl::availableStyleSheets() const
2332 return m_availableSheets;
2335 void DocumentImpl::recalcStyleSelector()
2337 if ( !m_render || !attached() ) return;
2339 QPtrList<StyleSheetImpl> oldStyleSheets = m_styleSheets->styleSheets;
2340 m_styleSheets->styleSheets.clear();
2341 m_availableSheets.clear();
2343 for (n = this; n; n = n->traverseNextNode()) {
2344 StyleSheetImpl *sheet = 0;
2346 if (n->nodeType() == Node::PROCESSING_INSTRUCTION_NODE)
2348 // Processing instruction (XML documents only)
2349 ProcessingInstructionImpl* pi = static_cast<ProcessingInstructionImpl*>(n);
2350 sheet = pi->sheet();
2353 applyXSLTransform(pi);
2357 if (!sheet && !pi->localHref().isEmpty())
2359 // Processing instruction with reference to an element in this document - e.g.
2360 // <?xml-stylesheet href="#mystyle">, with the element
2361 // <foo id="mystyle">heading { color: red; }</foo> at some location in
2363 ElementImpl* elem = getElementById(pi->localHref());
2365 DOMString sheetText("");
2367 for (c = elem->firstChild(); c; c = c->nextSibling()) {
2368 if (c->nodeType() == Node::TEXT_NODE || c->nodeType() == Node::CDATA_SECTION_NODE)
2369 sheetText += c->nodeValue();
2372 CSSStyleSheetImpl *cssSheet = new CSSStyleSheetImpl(this);
2373 cssSheet->parseString(sheetText);
2374 pi->setStyleSheet(cssSheet);
2380 else if (n->isHTMLElement() && (n->id() == ID_LINK || n->id() == ID_STYLE)) {
2381 ElementImpl *e = static_cast<ElementImpl *>(n);
2382 QString title = e->getAttribute( ATTR_TITLE ).string();
2383 bool enabledViaScript = false;
2384 if (n->id() == ID_LINK) {
2386 HTMLLinkElementImpl* l = static_cast<HTMLLinkElementImpl*>(n);
2387 if (l->isLoading() || l->isDisabled())
2390 title = QString::null;
2391 enabledViaScript = l->isEnabledViaScript();
2394 // Get the current preferred styleset. This is the
2395 // set of sheets that will be enabled.
2396 if ( n->id() == ID_LINK )
2397 sheet = static_cast<HTMLLinkElementImpl*>(n)->sheet();
2400 sheet = static_cast<HTMLStyleElementImpl*>(n)->sheet();
2402 // Check to see if this sheet belongs to a styleset
2403 // (thus making it PREFERRED or ALTERNATE rather than
2405 if (!enabledViaScript && !title.isEmpty()) {
2406 // Yes, we have a title.
2407 if (m_preferredStylesheetSet.isEmpty()) {
2408 // No preferred set has been established. If
2409 // we are NOT an alternate sheet, then establish
2410 // us as the preferred set. Otherwise, just ignore
2412 QString rel = e->getAttribute( ATTR_REL ).string();
2413 if (n->id() == ID_STYLE || !rel.contains("alternate"))
2414 m_preferredStylesheetSet = view()->part()->d->m_sheetUsed = title;
2417 if (!m_availableSheets.contains( title ) )
2418 m_availableSheets.append( title );
2420 if (title != m_preferredStylesheetSet)
2427 m_styleSheets->styleSheets.append(sheet);
2430 // For HTML documents, stylesheets are not allowed within/after the <BODY> tag. So we
2431 // can stop searching here.
2432 if (isHTMLDocument() && n->id() == ID_BODY)
2436 // De-reference all the stylesheets in the old list
2437 QPtrListIterator<StyleSheetImpl> it(oldStyleSheets);
2438 for (; it.current(); ++it)
2439 it.current()->deref();
2441 // Create a new style selector
2442 delete m_styleSelector;
2443 QString usersheet = m_usersheet;
2444 if ( m_view && m_view->mediaType() == "print" )
2445 usersheet += m_printSheet;
2446 m_styleSelector = new CSSStyleSelector(this, usersheet, m_styleSheets, !inCompatMode());
2447 m_styleSelector->setEncodedURL(m_url);
2448 m_styleSelectorDirty = false;
2451 void DocumentImpl::setHoverNode(NodeImpl* newHoverNode)
2453 if (m_hoverNode != newHoverNode) {
2455 m_hoverNode->deref();
2456 m_hoverNode = newHoverNode;
2464 bool DocumentImpl::relinquishesEditingFocus(NodeImpl *node)
2467 assert(node->isContentEditable());
2469 NodeImpl *root = node->rootEditableElement();
2470 if (!part() || !root)
2473 return part()->shouldEndEditing(rangeOfContents(root).get());
2476 bool DocumentImpl::acceptsEditingFocus(NodeImpl *node)
2479 assert(node->isContentEditable());
2481 NodeImpl *root = node->rootEditableElement();
2482 if (!part() || !root)
2485 return part()->shouldBeginEditing(rangeOfContents(root).get());
2488 const QValueList<DashboardRegionValue> & DocumentImpl::dashboardRegions() const
2490 return m_dashboardRegions;
2493 void DocumentImpl::setDashboardRegions (const QValueList<DashboardRegionValue>& regions)
2495 m_dashboardRegions = regions;
2496 setDashboardRegionsDirty (false);
2501 static QWidget *widgetForNode(NodeImpl *focusNode)
2505 RenderObject *renderer = focusNode->renderer();
2506 if (!renderer || !renderer->isWidget())
2508 return static_cast<RenderWidget *>(renderer)->widget();
2511 bool DocumentImpl::setFocusNode(NodeImpl *newFocusNode)
2513 // Make sure newFocusNode is actually in this document
2514 if (newFocusNode && (newFocusNode->getDocument() != this))
2517 if (m_focusNode == newFocusNode)
2521 if (m_focusNode && m_focusNode->isContentEditable() && !relinquishesEditingFocus(m_focusNode))
2525 bool focusChangeBlocked = false;
2526 NodeImpl *oldFocusNode = m_focusNode;
2529 // Remove focus from the existing focus node (if any)
2531 // This goes hand in hand with the Qt focus setting below.
2532 if (!newFocusNode && view()) {
2536 if (oldFocusNode->active())
2537 oldFocusNode->setActive(false);
2539 oldFocusNode->setFocus(false);
2540 oldFocusNode->dispatchHTMLEvent(EventImpl::BLUR_EVENT, false, false);
2541 if (m_focusNode != 0) {
2542 // handler shifted focus
2543 focusChangeBlocked = true;
2546 oldFocusNode->dispatchUIEvent(EventImpl::DOMFOCUSOUT_EVENT);
2547 if (m_focusNode != 0) {
2548 // handler shifted focus
2549 focusChangeBlocked = true;
2552 if ((oldFocusNode == this) && oldFocusNode->hasOneRef()) {
2553 oldFocusNode->deref(); // deletes this
2557 oldFocusNode->deref();
2561 // Clear the selection when changing the focus node to null or to a node that is not
2562 // contained by the current selection.
2564 NodeImpl *startContainer = part()->selection().start().node();
2565 if (!newFocusNode || (startContainer && startContainer != newFocusNode && !startContainer->isAncestor(newFocusNode)))
2566 part()->clearSelection();
2571 if (newFocusNode->isContentEditable() && !acceptsEditingFocus(newFocusNode)) {
2572 // delegate blocks focus change
2573 focusChangeBlocked = true;
2574 goto SetFocusNodeDone;
2577 // Set focus on the new node
2578 m_focusNode = newFocusNode;
2580 m_focusNode->dispatchHTMLEvent(EventImpl::FOCUS_EVENT, false, false);
2581 if (m_focusNode != newFocusNode) {
2582 // handler shifted focus
2583 focusChangeBlocked = true;
2584 goto SetFocusNodeDone;
2586 m_focusNode->dispatchUIEvent(EventImpl::DOMFOCUSIN_EVENT);
2587 if (m_focusNode != newFocusNode) {
2588 // handler shifted focus
2589 focusChangeBlocked = true;
2590 goto SetFocusNodeDone;
2592 m_focusNode->setFocus();
2593 // eww, I suck. set the qt focus correctly
2594 // ### find a better place in the code for this
2596 QWidget *focusWidget = widgetForNode(m_focusNode);
2598 // Make sure a widget has the right size before giving it focus.
2599 // Otherwise, we are testing edge cases of the QWidget code.
2600 // Specifically, in WebCore this does not work well for text fields.
2602 // Re-get the widget in case updating the layout changed things.
2603 focusWidget = widgetForNode(m_focusNode);
2606 focusWidget->setFocus();
2613 if (!focusChangeBlocked && m_focusNode && KWQAccObjectCache::accessibilityEnabled())
2614 getAccObjectCache()->handleFocusedUIElementChanged();
2619 return !focusChangeBlocked;
2622 void DocumentImpl::setCSSTarget(NodeImpl* n)
2625 m_cssTarget->setChanged();
2631 NodeImpl* DocumentImpl::getCSSTarget()
2636 void DocumentImpl::attachNodeIterator(NodeIteratorImpl *ni)
2638 m_nodeIterators.append(ni);
2641 void DocumentImpl::detachNodeIterator(NodeIteratorImpl *ni)
2643 m_nodeIterators.remove(ni);
2646 void DocumentImpl::notifyBeforeNodeRemoval(NodeImpl *n)
2648 QPtrListIterator<NodeIteratorImpl> it(m_nodeIterators);
2649 for (; it.current(); ++it)
2650 it.current()->notifyBeforeNodeRemoval(n);
2653 AbstractViewImpl *DocumentImpl::defaultView() const
2655 return m_defaultView;
2658 EventImpl *DocumentImpl::createEvent(const DOMString &eventType, int &exceptioncode)
2660 if (eventType == "UIEvents")
2661 return new UIEventImpl();
2662 else if (eventType == "MouseEvents")
2663 return new MouseEventImpl();
2664 else if (eventType == "MutationEvents")
2665 return new MutationEventImpl();
2666 else if (eventType == "KeyboardEvents")
2667 return new KeyboardEventImpl();
2668 else if (eventType == "HTMLEvents")
2669 return new EventImpl();
2671 exceptioncode = DOMException::NOT_SUPPORTED_ERR;
2676 CSSStyleDeclarationImpl *DocumentImpl::getOverrideStyle(ElementImpl */*elt*/, const DOMString &/*pseudoElt*/)
2681 void DocumentImpl::defaultEventHandler(EventImpl *evt)
2683 // if any html event listeners are registered on the window, then dispatch them here
2684 QPtrList<RegisteredEventListener> listenersCopy = m_windowEventListeners;
2685 QPtrListIterator<RegisteredEventListener> it(listenersCopy);
2686 for (; it.current(); ++it) {
2687 if (it.current()->id == evt->id()) {
2688 it.current()->listener->handleEventImpl(evt, true);
2693 if (evt->id()==EventImpl::KEYDOWN_EVENT) {
2694 KeyboardEventImpl *kevt = static_cast<KeyboardEventImpl *>(evt);
2695 if (kevt->ctrlKey()) {
2696 QString key = kevt->qKeyEvent()->unmodifiedText().lower();
2697 ElementImpl *elem = getElementByAccessKey(key);
2699 elem->accessKeyAction(false);
2700 evt->setDefaultHandled();
2706 void DocumentImpl::setHTMLWindowEventListener(int id, EventListener *listener)
2708 // If we already have it we don't want removeWindowEventListener to delete it
2711 removeHTMLWindowEventListener(id);
2713 addWindowEventListener(id, listener, false);
2718 EventListener *DocumentImpl::getHTMLWindowEventListener(int id)
2720 QPtrListIterator<RegisteredEventListener> it(m_windowEventListeners);
2721 for (; it.current(); ++it) {
2722 if (it.current()->id == id &&
2723 it.current()->listener->eventListenerType() == "_khtml_HTMLEventListener") {
2724 return it.current()->listener;
2731 void DocumentImpl::removeHTMLWindowEventListener(int id)
2733 QPtrListIterator<RegisteredEventListener> it(m_windowEventListeners);
2734 for (; it.current(); ++it) {
2735 if (it.current()->id == id &&
2736 it.current()->listener->eventListenerType() == "_khtml_HTMLEventListener") {
2737 m_windowEventListeners.removeRef(it.current());
2743 void DocumentImpl::addWindowEventListener(int id, EventListener *listener, const bool useCapture)
2747 // remove existing identical listener set with identical arguments - the DOM2
2748 // spec says that "duplicate instances are discarded" in this case.
2749 removeWindowEventListener(id,listener,useCapture);
2751 RegisteredEventListener *rl = new RegisteredEventListener(static_cast<EventImpl::EventId>(id), listener, useCapture);
2752 m_windowEventListeners.append(rl);
2757 void DocumentImpl::removeWindowEventListener(int id, EventListener *listener, bool useCapture)
2759 RegisteredEventListener rl(static_cast<EventImpl::EventId>(id),listener,useCapture);
2761 QPtrListIterator<RegisteredEventListener> it(m_windowEventListeners);
2762 for (; it.current(); ++it)
2763 if (*(it.current()) == rl) {
2764 m_windowEventListeners.removeRef(it.current());
2769 bool DocumentImpl::hasWindowEventListener(int id)
2771 QPtrListIterator<RegisteredEventListener> it(m_windowEventListeners);
2772 for (; it.current(); ++it) {
2773 if (it.current()->id == id) {
2781 EventListener *DocumentImpl::createHTMLEventListener(QString code, NodeImpl *node)
2784 return part()->createHTMLEventListener(code, node);
2790 void DocumentImpl::dispatchImageLoadEventSoon(HTMLImageLoader *image)
2792 m_imageLoadEventDispatchSoonList.append(image);
2793 if (!m_imageLoadEventTimer) {
2794 m_imageLoadEventTimer = startTimer(0);
2798 void DocumentImpl::removeImage(HTMLImageLoader* image)
2800 // Remove instances of this image from both lists.
2801 // Use loops because we allow multiple instances to get into the lists.
2802 while (m_imageLoadEventDispatchSoonList.removeRef(image)) { }
2803 while (m_imageLoadEventDispatchingList.removeRef(image)) { }
2804 if (m_imageLoadEventDispatchSoonList.isEmpty() && m_imageLoadEventTimer) {
2805 killTimer(m_imageLoadEventTimer);
2806 m_imageLoadEventTimer = 0;
2810 void DocumentImpl::dispatchImageLoadEventsNow()
2812 // need to avoid re-entering this function; if new dispatches are
2813 // scheduled before the parent finishes processing the list, they
2814 // will set a timer and eventually be processed
2815 if (!m_imageLoadEventDispatchingList.isEmpty()) {
2819 if (m_imageLoadEventTimer) {
2820 killTimer(m_imageLoadEventTimer);
2821 m_imageLoadEventTimer = 0;
2824 m_imageLoadEventDispatchingList = m_imageLoadEventDispatchSoonList;
2825 m_imageLoadEventDispatchSoonList.clear();
2826 for (QPtrListIterator<HTMLImageLoader> it(m_imageLoadEventDispatchingList); it.current(); ) {
2827 HTMLImageLoader* image = it.current();
2828 // Must advance iterator *before* dispatching call.
2829 // Otherwise, it might be advanced automatically if dispatching the call had a side effect
2830 // of destroying the current HTMLImageLoader, and then we would advance past the *next* item,
2831 // missing one altogether.
2833 image->dispatchLoadEvent();
2835 m_imageLoadEventDispatchingList.clear();
2838 void DocumentImpl::timerEvent(QTimerEvent *)
2840 dispatchImageLoadEventsNow();
2843 ElementImpl *DocumentImpl::ownerElement()
2845 KHTMLView *childView = view();
2848 KHTMLPart *childPart = childView->part();
2851 KHTMLPart *parent = childPart->parentPart();
2854 ChildFrame *childFrame = parent->childFrame(childPart);
2857 RenderPart *renderPart = childFrame->m_frame;
2860 return static_cast<ElementImpl *>(renderPart->element());
2863 DOMString DocumentImpl::domain() const
2865 if ( m_domain.isEmpty() ) // not set yet (we set it on demand to save time and space)
2866 m_domain = KURL(URL()).host(); // Initially set to the host
2870 void DocumentImpl::setDomain(const DOMString &newDomain, bool force /*=false*/)
2873 m_domain = newDomain;
2876 if ( m_domain.isEmpty() ) // not set yet (we set it on demand to save time and space)
2877 m_domain = KURL(URL()).host(); // Initially set to the host
2879 // Both NS and IE specify that changing the domain is only allowed when
2880 // the new domain is a suffix of the old domain.
2881 int oldLength = m_domain.length();
2882 int newLength = newDomain.length();
2883 if ( newLength < oldLength ) // e.g. newDomain=kde.org (7) and m_domain=www.kde.org (11)
2885 DOMString test = m_domain.copy();
2886 if ( test[oldLength - newLength - 1] == '.' ) // Check that it's a subdomain, not e.g. "de.org"
2888 test.remove( 0, oldLength - newLength ); // now test is "kde.org" from m_domain
2889 if ( test == newDomain ) // and we check that it's the same thing as newDomain
2890 m_domain = newDomain;
2895 bool DocumentImpl::isValidName(const DOMString &name)
2897 static const char validFirstCharacter[] = "ABCDEFGHIJKLMNOPQRSTUVWXZYabcdefghijklmnopqrstuvwxyz";
2898 static const char validSubsequentCharacter[] = "ABCDEFGHIJKLMNOPQRSTUVWXZYabcdefghijklmnopqrstuvwxyz0123456789-_:.";
2899 const unsigned length = name.length();
2902 const QChar * const characters = name.unicode();
2903 const char fc = characters[0];
2906 if (strchr(validFirstCharacter, fc) == 0)
2908 for (unsigned i = 1; i < length; ++i) {
2909 const char sc = characters[i];
2912 if (strchr(validSubsequentCharacter, sc) == 0)
2918 void DocumentImpl::addImageMap(HTMLMapElementImpl *imageMap)
2920 // Add the image map, unless there's already another with that name.
2921 // "First map wins" is the rule other browsers seem to implement.
2922 QString name = imageMap->getName().string();
2923 if (!m_imageMapsByName.contains(name))
2924 m_imageMapsByName.insert(name, imageMap);
2927 void DocumentImpl::removeImageMap(HTMLMapElementImpl *imageMap)
2929 // Remove the image map by name.
2930 // But don't remove some other image map that just happens to have the same name.
2931 QString name = imageMap->getName().string();
2932 QMapIterator<QString, HTMLMapElementImpl *> it = m_imageMapsByName.find(name);
2933 if (it != m_imageMapsByName.end() && *it == imageMap)
2934 m_imageMapsByName.remove(it);
2937 HTMLMapElementImpl *DocumentImpl::getImageMap(const DOMString &URL) const
2943 QString s = URL.string();
2944 int hashPos = s.find('#');
2946 s = s.mid(hashPos + 1);
2948 QMapConstIterator<QString, HTMLMapElementImpl *> it = m_imageMapsByName.find(s);
2949 if (it == m_imageMapsByName.end())
2956 void DocumentImpl::setDecoder(Decoder *decoder)
2962 m_decoder = decoder;
2965 QString DocumentImpl::completeURL(const QString &URL)
2967 return KURL(baseURL(), URL, m_decoder ? m_decoder->codec() : 0).url();
2970 DOMString DocumentImpl::completeURL(const DOMString &URL)
2974 return completeURL(URL.string());
2977 bool DocumentImpl::inPageCache()
2979 return m_inPageCache;
2982 void DocumentImpl::setInPageCache(bool flag)
2984 if (m_inPageCache == flag)
2987 m_inPageCache = flag;
2989 assert(m_savedRenderer == 0);
2990 m_savedRenderer = m_render;
2992 m_view->resetScrollBars();
2995 assert(m_render == 0 || m_render == m_savedRenderer);
2996 m_render = m_savedRenderer;
2997 m_savedRenderer = 0;
3001 void DocumentImpl::passwordFieldAdded()
3006 void DocumentImpl::passwordFieldRemoved()
3008 assert(m_passwordFields > 0);
3012 bool DocumentImpl::hasPasswordField() const
3014 return m_passwordFields > 0;
3017 void DocumentImpl::secureFormAdded()
3022 void DocumentImpl::secureFormRemoved()
3024 assert(m_secureForms > 0);
3028 bool DocumentImpl::hasSecureForm() const
3030 return m_secureForms > 0;
3033 void DocumentImpl::setShouldCreateRenderers(bool f)
3035 m_createRenderers = f;
3038 bool DocumentImpl::shouldCreateRenderers()
3040 return m_createRenderers;
3043 DOMString DocumentImpl::toString() const
3047 for (NodeImpl *child = firstChild(); child != NULL; child = child->nextSibling()) {
3048 result += child->toString();
3054 #endif // APPLE_CHANGES
3056 // ----------------------------------------------------------------------------
3057 // Support for Javascript execCommand, and related methods
3059 JSEditor *DocumentImpl::jsEditor()
3062 m_jsEditor = new JSEditor(this);
3066 bool DocumentImpl::execCommand(const DOMString &command, bool userInterface, const DOMString &value)
3068 return jsEditor()->execCommand(command, userInterface, value);
3071 bool DocumentImpl::queryCommandEnabled(const DOMString &command)
3073 return jsEditor()->queryCommandEnabled(command);
3076 bool DocumentImpl::queryCommandIndeterm(const DOMString &command)
3078 return jsEditor()->queryCommandIndeterm(command);
3081 bool DocumentImpl::queryCommandState(const DOMString &command)
3083 return jsEditor()->queryCommandState(command);
3086 bool DocumentImpl::queryCommandSupported(const DOMString &command)
3088 return jsEditor()->queryCommandSupported(command);
3091 DOMString DocumentImpl::queryCommandValue(const DOMString &command)
3093 return jsEditor()->queryCommandValue(command);
3096 // ----------------------------------------------------------------------------
3098 void DocumentImpl::addMarker(RangeImpl *range, DocumentMarker::MarkerType type)
3100 // Use a TextIterator to visit the potentially multiple nodes the range covers.
3101 for (TextIterator markedText(range); !markedText.atEnd(); markedText.advance()) {
3102 SharedPtr<RangeImpl> textPiece = markedText.range();
3104 DocumentMarker marker = {type, textPiece->startOffset(exception), textPiece->endOffset(exception)};
3105 addMarker(textPiece->startContainer(exception), marker);
3109 void DocumentImpl::removeMarker(RangeImpl *range, DocumentMarker::MarkerType type)
3111 // Use a TextIterator to visit the potentially multiple nodes the range covers.
3112 for (TextIterator markedText(range); !markedText.atEnd(); markedText.advance()) {
3113 SharedPtr<RangeImpl> textPiece = markedText.range();
3115 DocumentMarker marker = {type, textPiece->startOffset(exception), textPiece->endOffset(exception)};
3116 removeMarker(textPiece->startContainer(exception), marker);
3120 // FIXME: We don't deal with markers of more than one type yet
3122 // Markers are stored in order sorted by their location. They do not overlap each other, as currently
3123 // required by the drawing code in render_text.cpp.
3125 void DocumentImpl::addMarker(NodeImpl *node, DocumentMarker newMarker)
3127 assert(newMarker.endOffset >= newMarker.startOffset);
3128 if (newMarker.endOffset == newMarker.startOffset) {
3129 return; // zero length markers are a NOP
3132 QValueList <DocumentMarker> *markers = m_markers.find(node);
3134 markers = new QValueList <DocumentMarker>;
3135 markers->append(newMarker);
3136 m_markers.insert(node, markers);
3138 QValueListIterator<DocumentMarker> it;
3139 for (it = markers->begin(); it != markers->end(); ) {
3140 DocumentMarker marker = *it;
3142 if (newMarker.endOffset < marker.startOffset+1) {
3143 // This is the first marker that is completely after newMarker, and disjoint from it.
3144 // We found our insertion point.
\10
3146 } else if (newMarker.startOffset > marker.endOffset) {
3147 // maker is before newMarker, and disjoint from it. Keep scanning.
3149 } else if (newMarker == marker) {
3150 // already have this one, NOP
3153 // marker and newMarker intersect or touch - merge them into newMarker
3154 newMarker.startOffset = kMin(newMarker.startOffset, marker.startOffset);
3155 newMarker.endOffset = kMax(newMarker.endOffset, marker.endOffset);
3156 // remove old one, we'll add newMarker later
3157 it = markers->remove(it);
3158 // it points to the next marker to consider
3161 // at this point it points to the node before which we want to insert
3162 markers->insert(it, newMarker);
3165 // repaint the affected node
3166 if (node->renderer())
3167 node->renderer()->repaint();
3170 void DocumentImpl::removeMarker(NodeImpl *node, DocumentMarker target)
3172 assert(target.endOffset >= target.startOffset);
3173 if (target.endOffset == target.startOffset) {
3174 return; // zero length markers are a NOP
3177 QValueList <DocumentMarker> *markers = m_markers.find(node);
3182 bool docDirty = false;
3183 QValueListIterator<DocumentMarker> it;
3184 for (it = markers->begin(); it != markers->end(); ) {
3185 DocumentMarker marker = *it;
3187 if (target.endOffset <= marker.startOffset) {
3188 // This is the first marker that is completely after target. All done.
3190 } else if (target.startOffset >= marker.endOffset) {
3191 // marker is before target. Keep scanning.
3194 // at this point we know that marker and target intersect in some way
3197 // pitch the old marker
3198 it = markers->remove(it);
3199 // it now points to the next node
3201 // add either of the resulting slices that are left after removing target
3202 if (target.startOffset > marker.startOffset) {
3203 DocumentMarker newLeft = marker;
3204 newLeft.endOffset = target.startOffset;
3205 markers->insert(it, newLeft);
3207 if (marker.endOffset > target.endOffset) {
3208 DocumentMarker newRight = marker;
3209 newRight.startOffset = target.endOffset;
3210 markers->insert(it, newRight);
3215 if (markers->isEmpty())
3216 m_markers.remove(node);
3218 // repaint the affected node
3219 if (docDirty && node->renderer())
3220 node->renderer()->repaint();
3223 QValueList<DocumentMarker> DocumentImpl::markersForNode(NodeImpl *node)
3225 QValueList <DocumentMarker> *markers = m_markers.find(node);
3229 return QValueList <DocumentMarker> ();
3233 void DocumentImpl::removeAllMarkers(NodeImpl *node, ulong startOffset, long length)
3235 // FIXME - yet another cheat that relies on us only having one marker type
3236 DocumentMarker marker = {DocumentMarker::Spelling, startOffset, startOffset+length};
3237 removeMarker(node, marker);
3240 void DocumentImpl::removeAllMarkers(NodeImpl *node)
3242 QValueList<DocumentMarker> *markers = m_markers.take(node);
3244 RenderObject *renderer = node->renderer();
3246 renderer->repaint();
3251 void DocumentImpl::removeAllMarkers()
3253 QPtrDictIterator< QValueList<DocumentMarker> > it(m_markers);
3254 for (; NodeImpl *node = static_cast<NodeImpl *>(it.currentKey()); ++it) {
3255 RenderObject *renderer = node->renderer();
3257 renderer->repaint();
3262 void DocumentImpl::shiftMarkers(NodeImpl *node, ulong startOffset, long delta)
3264 QValueList <DocumentMarker> *markers = m_markers.find(node);
3268 bool docDirty = false;
3269 QValueListIterator<DocumentMarker> it;
3270 for (it = markers->begin(); it != markers->end(); ++it) {
3271 DocumentMarker &marker = *it;
3272 if (marker.startOffset >= startOffset) {
3273 assert((int)marker.startOffset + delta >= 0);
3274 marker.startOffset += delta;
3275 marker.endOffset += delta;
3280 // repaint the affected node
3281 if (docDirty && node->renderer())
3282 node->renderer()->repaint();
3286 void DocumentImpl::applyXSLTransform(ProcessingInstructionImpl* pi)
3288 // Ref ourselves to keep from being destroyed.
3289 XSLTProcessorImpl processor(static_cast<XSLStyleSheetImpl*>(pi->sheet()), this);
3290 processor.transformDocument(this);
3292 // FIXME: If the transform failed we should probably report an error (like Mozilla does) in this
3296 void DocumentImpl::setTransformSourceDocument(DocumentImpl* doc)
3298 if (m_transformSourceDocument)
3299 m_transformSourceDocument->deref();
3300 m_transformSourceDocument = doc;
3307 void DocumentImpl::setDesignMode(InheritedBool value)
3309 m_designMode = value;
3312 DocumentImpl::InheritedBool DocumentImpl::getDesignMode() const
3314 return m_designMode;
3317 bool DocumentImpl::inDesignMode() const
3319 for (const DocumentImpl* d = this; d; d = d->parentDocument()) {
3320 if (d->m_designMode != inherit)
3321 return d->m_designMode;
3326 DocumentImpl *DocumentImpl::parentDocument() const
3328 KHTMLPart *childPart = part();
3331 KHTMLPart *parent = childPart->parentPart();
3334 return parent->xmlDocImpl();
3337 DocumentImpl *DocumentImpl::topDocument() const
3339 DocumentImpl *doc = const_cast<DocumentImpl *>(this);
3340 ElementImpl *element;
3341 while ((element = doc->ownerElement()) != 0) {
3342 doc = element->getDocument();
3343 element = doc ? doc->ownerElement() : 0;
3349 AttrImpl *DocumentImpl::createAttributeNS(const DOMString &namespaceURI, const DOMString &qualifiedName, int &exception)
3351 if (qualifiedName.isNull()) {
3352 exception = DOMException::NAMESPACE_ERR;
3356 DOMString localName(qualifiedName.copy());
3359 if ((colonpos = qualifiedName.find(':')) >= 0) {
3360 prefix = qualifiedName.copy();
3361 prefix.truncate(colonpos);
3362 localName.remove(0, colonpos+1);
3365 if (!isValidName(localName)) {
3366 exception = DOMException::INVALID_CHARACTER_ERR;
3369 // ### check correctness of namespace, prefix?
3371 Id id = attrId(namespaceURI.implementation(), localName.implementation(), false /* allocate */);
3372 AttrImpl *attr = createAttribute(id);
3373 if (!prefix.isNull())
3374 attr->setPrefix(prefix.implementation(), exception);
3378 SharedPtr<HTMLCollectionImpl> DocumentImpl::images()
3380 return SharedPtr<HTMLCollectionImpl>(new HTMLCollectionImpl(this, HTMLCollectionImpl::DOC_IMAGES));
3383 SharedPtr<HTMLCollectionImpl> DocumentImpl::applets()
3385 return SharedPtr<HTMLCollectionImpl>(new HTMLCollectionImpl(this, HTMLCollectionImpl::DOC_APPLETS));
3388 SharedPtr<HTMLCollectionImpl> DocumentImpl::embeds()
3390 return SharedPtr<HTMLCollectionImpl>(new HTMLCollectionImpl(this, HTMLCollectionImpl::DOC_EMBEDS));
3393 SharedPtr<HTMLCollectionImpl> DocumentImpl::objects()
3395 return SharedPtr<HTMLCollectionImpl>(new HTMLCollectionImpl(this, HTMLCollectionImpl::DOC_OBJECTS));
3398 SharedPtr<HTMLCollectionImpl> DocumentImpl::links()
3400 return SharedPtr<HTMLCollectionImpl>(new HTMLCollectionImpl(this, HTMLCollectionImpl::DOC_LINKS));
3403 SharedPtr<HTMLCollectionImpl> DocumentImpl::forms()
3405 return SharedPtr<HTMLCollectionImpl>(new HTMLCollectionImpl(this, HTMLCollectionImpl::DOC_FORMS));
3408 SharedPtr<HTMLCollectionImpl> DocumentImpl::anchors()
3410 return SharedPtr<HTMLCollectionImpl>(new HTMLCollectionImpl(this, HTMLCollectionImpl::DOC_ANCHORS));
3413 SharedPtr<HTMLCollectionImpl> DocumentImpl::all()
3415 return SharedPtr<HTMLCollectionImpl>(new HTMLCollectionImpl(this, HTMLCollectionImpl::DOC_ALL));
3418 SharedPtr<HTMLCollectionImpl> DocumentImpl::nameableItems()
3420 return SharedPtr<HTMLCollectionImpl>(new HTMLCollectionImpl(this, HTMLCollectionImpl::DOC_NAMEABLE_ITEMS));
3423 SharedPtr<NameNodeListImpl> DocumentImpl::getElementsByName(const DOMString &elementName)
3425 return SharedPtr<NameNodeListImpl>(new NameNodeListImpl(this, elementName));
3428 // ----------------------------------------------------------------------------
3430 DocumentFragmentImpl::DocumentFragmentImpl(DocumentPtr *doc) : ContainerNodeImpl(doc)
3434 DOMString DocumentFragmentImpl::nodeName() const
3436 return "#document-fragment";
3439 unsigned short DocumentFragmentImpl::nodeType() const
3441 return Node::DOCUMENT_FRAGMENT_NODE;
3444 // DOM Section 1.1.1
3445 bool DocumentFragmentImpl::childTypeAllowed( unsigned short type )
3448 case Node::ELEMENT_NODE:
3449 case Node::PROCESSING_INSTRUCTION_NODE:
3450 case Node::COMMENT_NODE:
3451 case Node::TEXT_NODE:
3452 case Node::CDATA_SECTION_NODE:
3453 case Node::ENTITY_REFERENCE_NODE:
3460 DOMString DocumentFragmentImpl::toString() const
3464 for (NodeImpl *child = firstChild(); child != NULL; child = child->nextSibling()) {
3465 result += child->toString();
3472 NodeImpl *DocumentFragmentImpl::cloneNode ( bool deep )
3474 DocumentFragmentImpl *clone = new DocumentFragmentImpl( docPtr() );
3476 cloneChildNodes(clone);
3481 // ----------------------------------------------------------------------------
3483 DocumentTypeImpl::DocumentTypeImpl(DOMImplementationImpl *implementation, DocumentPtr *doc,
3484 const DOMString &qualifiedName, const DOMString &publicId,
3485 const DOMString &systemId)
3486 : NodeImpl(doc), m_implementation(implementation),
3487 m_qualifiedName(qualifiedName), m_publicId(publicId), m_systemId(systemId)
3489 if (m_implementation)
3490 m_implementation->ref();
3495 // if doc is 0, it is not attached to a document and / or
3496 // therefore does not provide entities or notations. (DOM Level 3)
3499 DocumentTypeImpl::~DocumentTypeImpl()
3501 if (m_implementation)
3502 m_implementation->deref();
3504 m_entities->deref();
3506 m_notations->deref();
3509 void DocumentTypeImpl::copyFrom(const DocumentTypeImpl& other)
3511 m_qualifiedName = other.m_qualifiedName;
3512 m_publicId = other.m_publicId;
3513 m_systemId = other.m_systemId;
3514 m_subset = other.m_subset;
3517 DOMString DocumentTypeImpl::toString() const
3520 if (m_qualifiedName.isEmpty()) {
3523 result = "<!DOCTYPE ";
3524 result += m_qualifiedName;
3526 if (!m_publicId.isEmpty()) {
3527 result += " PUBLIC \"";
3528 result += m_publicId;
3530 result += m_systemId;
3532 } else if (!m_systemId.isEmpty()) {
3533 result += " SYSTEM \"";
3534 result += m_systemId;
3537 if (!m_subset.isEmpty()) {
3546 DOMString DocumentTypeImpl::nodeName() const
3551 unsigned short DocumentTypeImpl::nodeType() const
3553 return Node::DOCUMENT_TYPE_NODE;
3556 // DOM Section 1.1.1
3557 bool DocumentTypeImpl::childTypeAllowed( unsigned short /*type*/ )
3562 NodeImpl *DocumentTypeImpl::cloneNode ( bool /*deep*/ )
3564 // Spec says cloning Document nodes is "implementation dependent"
3565 // so we do not support it...
3569 #include "dom_docimpl.moc"