Full parsing of doctype by the HTML Tokenizer
[kdelibs.git] / khtml / xml / dom_docimpl.cpp
blob3a76728c52fb68b89fc985f4db5d803cbb73ef90
1 /**
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 * (C) 2002-2006 Apple Computer, Inc.
8 * (C) 2006 Allan Sandfeld Jensen (kde@carewolf.com)
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Library General Public
12 * License as published by the Free Software Foundation; either
13 * version 2 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Library General Public License for more details.
20 * You should have received a copy of the GNU Library General Public License
21 * along with this library; see the file COPYING.LIB. If not, write to
22 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
23 * Boston, MA 02110-1301, USA.
26 #include "dom_docimpl.h"
28 #include <dom/dom_exception.h>
30 #include "dom_textimpl.h"
31 #include "dom_xmlimpl.h"
32 #include "dom2_rangeimpl.h"
33 #include "dom2_eventsimpl.h"
34 #include "xml_tokenizer.h"
35 #include <html/htmltokenizer.h>
36 #include "dom_restyler.h"
38 #include <css/csshelper.h>
39 #include <css/cssstyleselector.h>
40 #include <css/css_stylesheetimpl.h>
41 #include <misc/htmlhashes.h>
42 #include <misc/helper.h>
43 #include <misc/seed.h>
44 #include <misc/loader.h>
45 #include <ecma/kjs_proxy.h>
46 #include <ecma/kjs_binding.h>
48 #include <QtCore/QStack>
49 //Added by qt3to4:
50 #include <QTimerEvent>
51 #include <QtCore/QList>
52 #include <kdebug.h>
53 #include <klocale.h>
55 #include <rendering/counter_tree.h>
56 #include <rendering/render_canvas.h>
57 #include <rendering/render_replaced.h>
58 #include <rendering/render_arena.h>
59 #include <rendering/render_layer.h>
60 #include <rendering/render_frames.h>
61 #include <rendering/render_image.h>
63 #include <khtmlview.h>
64 #include <khtml_part.h>
65 #include <kauthorized.h>
66 #include <kglobalsettings.h>
67 #include <kstringhandler.h>
68 #include <kdatetime.h>
69 #include <khtml_settings.h>
70 #include <khtmlpart_p.h>
72 #include <html/html_baseimpl.h>
73 #include <html/html_blockimpl.h>
74 #include <html/html_canvasimpl.h>
75 #include <html/html_documentimpl.h>
76 #include <html/html_formimpl.h>
77 #include <html/html_headimpl.h>
78 #include <html/html_imageimpl.h>
79 #include <html/html_listimpl.h>
80 #include <html/html_miscimpl.h>
81 #include <html/html_tableimpl.h>
82 #include <html/html_objectimpl.h>
83 #include <html/HTMLAudioElement.h>
84 #include <html/HTMLVideoElement.h>
85 #include <html/HTMLSourceElement.h>
86 #include <editing/htmlediting.h>
87 #include <editing/jsediting.h>
89 #include <kapplication.h>
90 #include <kio/job.h>
92 #include <stdlib.h>
93 #include <limits.h>
95 using namespace DOM;
96 using namespace khtml;
98 // ------------------------------------------------------------------------
100 DOMImplementationImpl *DOMImplementationImpl::m_instance = 0;
102 DOMImplementationImpl::DOMImplementationImpl()
106 DOMImplementationImpl::~DOMImplementationImpl()
110 bool DOMImplementationImpl::hasFeature ( const DOMString &feature, const DOMString &version )
112 // ### update when we (fully) support the relevant features
113 QString lower = feature.string().toLower();
114 if ((lower == "html" || lower == "xml") &&
115 (version.isEmpty() || version == "1.0" || version == "2.0"))
116 return true;
118 // ## Do we support Core Level 3 ?
119 if ((lower == "core" ) &&
120 (version.isEmpty() || version == "2.0"))
121 return true;
123 if ((lower == "traversal") &&
124 (version.isEmpty() || version == "2.0"))
125 return true;
127 if ((lower == "events" || lower == "uievents" ||
128 lower == "mouseevents" || lower == "mutationevents" ||
129 lower == "htmlevents" || lower == "textevents" ) &&
130 (version.isEmpty() || version == "2.0" || version == "3.0"))
131 return true;
132 return false;
135 DocumentTypeImpl *DOMImplementationImpl::createDocumentType( const DOMString &qualifiedName, const DOMString &publicId,
136 const DOMString &systemId, int &exceptioncode )
138 // Not mentioned in spec: throw NAMESPACE_ERR if no qualifiedName supplied
139 if (qualifiedName.isNull()) {
140 exceptioncode = DOMException::NAMESPACE_ERR;
141 return 0;
144 // INVALID_CHARACTER_ERR: Raised if the specified qualified name contains an illegal character.
145 if (!Element::khtmlValidQualifiedName(qualifiedName)) {
146 exceptioncode = DOMException::INVALID_CHARACTER_ERR;
147 return 0;
150 // NAMESPACE_ERR: Raised if the qualifiedName is malformed.
151 // Added special case for the empty string, which seems to be a common pre-DOM2 misuse
152 if (!qualifiedName.isEmpty() && Element::khtmlMalformedQualifiedName(qualifiedName)) {
153 exceptioncode = DOMException::NAMESPACE_ERR;
154 return 0;
157 return new DocumentTypeImpl(this,0,qualifiedName,publicId,systemId);
160 DOMImplementationImpl* DOMImplementationImpl::getInterface(const DOMString& /*feature*/) const
162 // ###
163 return 0;
166 DocumentImpl *DOMImplementationImpl::createDocument( const DOMString &namespaceURI, const DOMString &qualifiedName,
167 DocumentTypeImpl* dtype, int &exceptioncode )
169 exceptioncode = 0;
171 if (!checkQualifiedName(qualifiedName, namespaceURI, 0, true/*nameCanBeNull*/,
172 true /*nameCanBeEmpty, see #61650*/, &exceptioncode) )
173 return 0;
175 // WRONG_DOCUMENT_ERR: Raised if doctype has already been used with a different document or was
176 // created from a different implementation.
177 if (dtype && (dtype->getDocument() || dtype->implementation() != this)) {
178 exceptioncode = DOMException::WRONG_DOCUMENT_ERR;
179 return 0;
182 // ### this is completely broken.. without a view it will not work (Dirk)
183 DocumentImpl *doc = new DocumentImpl(this, 0);
185 if (dtype) {
186 doc->setDocType(dtype);
189 // the document must be created empty if all parameters are null
190 // (or empty for qName/nsURI as a tolerance) - see DOM 3 Core.
191 if (dtype || !qualifiedName.isEmpty() || !namespaceURI.isEmpty()) {
192 ElementImpl *element = doc->createElementNS(namespaceURI,qualifiedName);
193 doc->appendChild(element,exceptioncode);
194 if (exceptioncode) {
195 delete element;
196 delete doc;
197 return 0;
200 return doc;
203 CSSStyleSheetImpl *DOMImplementationImpl::createCSSStyleSheet(DOMStringImpl* /*title*/, DOMStringImpl *media,
204 int &/*exceptioncode*/)
206 // ### TODO : title should be set, and media could have wrong syntax, in which case we should
207 // generate an exception.
208 CSSStyleSheetImpl *parent = 0L;
209 CSSStyleSheetImpl *sheet = new CSSStyleSheetImpl(parent, DOMString());
210 sheet->setMedia(new MediaListImpl(sheet, media, true /*fallbackToDescriptor*/));
211 return sheet;
214 DocumentImpl *DOMImplementationImpl::createDocument( KHTMLView *v )
216 DocumentImpl* doc = new DocumentImpl(this, v);
218 return doc;
221 HTMLDocumentImpl *DOMImplementationImpl::createHTMLDocument( KHTMLView *v )
223 HTMLDocumentImpl* doc = new HTMLDocumentImpl(this, v);
225 return doc;
228 HTMLDocumentImpl* DOMImplementationImpl::createHTMLDocument( const DOMString& title )
230 HTMLDocumentImpl* r = createHTMLDocument( 0 /* ### create a view otherwise it doesn't work */);
232 r->open();
234 r->write(QLatin1String("<HTML><HEAD><TITLE>") + title.string() +
235 QLatin1String("</TITLE></HEAD>"));
237 return r;
240 DOMImplementationImpl *DOMImplementationImpl::instance()
242 if (!m_instance) {
243 m_instance = new DOMImplementationImpl();
244 m_instance->ref();
247 return m_instance;
250 // ------------------------------------------------------------------------
252 ElementMappingCache::ElementMappingCache():m_dict()
256 ElementMappingCache::~ElementMappingCache()
258 qDeleteAll( m_dict );
261 void ElementMappingCache::add(const QString& id, ElementImpl* nd)
263 if (id.isEmpty()) return;
265 ItemInfo* info = m_dict.value(id);
266 if (info)
268 info->ref++;
269 info->nd = 0; //Now ambigous
271 else
273 ItemInfo* info = new ItemInfo();
274 info->ref = 1;
275 info->nd = nd;
276 m_dict.insert(id, info);
280 void ElementMappingCache::set(const QString& id, ElementImpl* nd)
282 if (id.isEmpty()) return;
284 assert(m_dict.contains(id));
285 ItemInfo* info = m_dict.value(id);
286 info->nd = nd;
289 void ElementMappingCache::remove(const QString& id, ElementImpl* nd)
291 if (id.isEmpty()) return;
293 assert(m_dict.contains(id));
294 ItemInfo* info = m_dict.value(id);
295 info->ref--;
296 if (info->ref == 0)
298 m_dict.take(id);
299 delete info;
301 else
303 if (info->nd == nd)
304 info->nd = 0;
308 bool ElementMappingCache::contains(const QString& id)
310 if (id.isEmpty()) return false;
311 return m_dict.contains(id);
314 ElementMappingCache::ItemInfo* ElementMappingCache::get(const QString& id)
316 if (id.isEmpty()) return 0;
317 return m_dict.value(id);
320 typedef QList<DocumentImpl*> ChangedDocuments ;
321 K_GLOBAL_STATIC(ChangedDocuments, s_changedDocuments)
323 // KHTMLView might be 0
324 DocumentImpl::DocumentImpl(DOMImplementationImpl *_implementation, KHTMLView *v)
325 : NodeBaseImpl( 0 ), m_domtree_version(0), m_counterDict(),
326 m_imageLoadEventTimer(0)
328 m_document.resetSkippingRef(this); //Make getDocument return us..
329 m_selfOnlyRefCount = 0;
331 m_paintDevice = 0;
332 //m_decoderMibEnum = 0;
333 m_textColor = Qt::black;
335 m_view = v;
336 m_renderArena.reset();
338 KHTMLGlobal::registerDocumentImpl(this);
340 if ( v ) {
341 m_docLoader = new DocLoader(v->part(), this );
342 setPaintDevice( m_view );
344 else
345 m_docLoader = new DocLoader( 0, this );
347 visuallyOrdered = false;
348 m_bParsing = false;
349 m_docChanged = false;
350 m_elemSheet = 0;
351 m_tokenizer = 0;
352 m_doctype = 0;
353 m_implementation = _implementation;
354 m_implementation->ref();
355 pMode = Strict;
356 hMode = XHtml;
357 m_textColor = "#000000";
358 m_attrMap = new IdNameMapping(ATTR_LAST_ATTR+1);
359 m_elementMap = new IdNameMapping(ID_LAST_TAG+1);
360 m_namespaceMap = new IdNameMapping(1);
361 QString xhtml(XHTML_NAMESPACE);
362 m_namespaceMap->names.insert(emptyNamespace, new DOMStringImpl(""));
363 m_namespaceMap->names.insert(xhtmlNamespace, new DOMStringImpl(xhtml.unicode(), xhtml.length()));
364 m_namespaceMap->names[emptyNamespace]->ref();
365 m_namespaceMap->names[xhtmlNamespace]->ref();
366 m_namespaceMap->count+=2;
367 m_focusNode = 0;
368 m_hoverNode = 0;
369 m_activeNode = 0;
370 m_defaultView = new AbstractViewImpl(this);
371 m_defaultView->ref();
372 m_listenerTypes = 0;
373 m_styleSheets = new StyleSheetListImpl;
374 m_styleSheets->ref();
375 m_addedStyleSheets = 0;
376 m_inDocument = true;
377 m_styleSelectorDirty = false;
378 m_styleSelector = 0;
380 m_inStyleRecalc = false;
381 m_pendingStylesheets = 0;
382 m_ignorePendingStylesheets = false;
383 m_async = true;
384 m_hadLoadError = false;
385 m_docLoading = false;
386 m_inSyncLoad = 0;
387 m_loadingXMLDoc = 0;
388 m_documentElement = 0;
389 m_cssTarget = 0;
390 m_jsEditor = 0;
391 m_dynamicDomRestyler = new khtml::DynamicDomRestyler();
392 m_stateRestorePos = 0;
395 void DocumentImpl::removedLastRef()
397 if (m_selfOnlyRefCount) {
398 /* In this case, the only references to us are from children,
399 so we have a cycle. We'll try to break it by disconnecting the
400 children from us; this sucks/is wrong, but it's pretty much
401 the best we can do without tracing.
403 Of course, if dumping the children causes the refcount from them to
404 drop to 0 we can get killed right here, so better hold
405 a temporary reference, too
407 DocPtr<DocumentImpl> guard(this);
409 // we must make sure not to be retaining any of our children through
410 // these extra pointers or we will create a reference cycle
411 if (m_doctype) {
412 m_doctype->deref();
413 m_doctype = 0;
416 if (m_cssTarget) {
417 m_cssTarget->deref();
418 m_cssTarget = 0;
421 if (m_focusNode) {
422 m_focusNode->deref();
423 m_focusNode = 0;
426 if (m_hoverNode) {
427 m_hoverNode->deref();
428 m_hoverNode = 0;
431 if (m_activeNode) {
432 m_activeNode->deref();
433 m_activeNode = 0;
436 if (m_documentElement) {
437 m_documentElement->deref();
438 m_documentElement = 0;
441 removeChildren();
443 delete m_tokenizer;
444 m_tokenizer = 0;
445 } else {
446 delete this;
450 DocumentImpl::~DocumentImpl()
452 //Important: if you need to remove stuff here,
453 //you may also have to fix removedLastRef() above - M.O.
454 assert( !m_render );
456 QHashIterator<long,NodeListImpl::Cache*> it(m_nodeListCache);
457 while (it.hasNext())
458 it.next().value()->deref();
460 if (m_loadingXMLDoc)
461 m_loadingXMLDoc->deref(this);
462 if (s_changedDocuments && m_docChanged)
463 s_changedDocuments->removeAll(this);
464 delete m_tokenizer;
465 m_document.resetSkippingRef(0);
466 delete m_styleSelector;
467 delete m_docLoader;
468 if (m_elemSheet ) m_elemSheet->deref();
469 if (m_doctype)
470 m_doctype->deref();
471 m_implementation->deref();
472 delete m_elementMap;
473 delete m_attrMap;
474 delete m_namespaceMap;
475 delete m_dynamicDomRestyler;
476 delete m_jsEditor;
477 m_defaultView->deref();
478 m_styleSheets->deref();
479 if (m_addedStyleSheets)
480 m_addedStyleSheets->deref();
481 if (m_cssTarget)
482 m_cssTarget->deref();
483 if (m_focusNode)
484 m_focusNode->deref();
485 if ( m_hoverNode )
486 m_hoverNode->deref();
487 if (m_activeNode)
488 m_activeNode->deref();
489 if (m_documentElement)
490 m_documentElement->deref();
491 qDeleteAll(m_counterDict);
493 m_renderArena.reset();
495 KHTMLGlobal::deregisterDocumentImpl(this);
499 DocumentTypeImpl *DocumentImpl::doctype() const
501 return m_doctype;
504 void DocumentImpl::setDocType(DocumentTypeImpl* dt)
506 assert(m_doctype == 0 && dt != 0);
507 m_doctype = dt;
508 m_doctype->ref();
509 m_doctype->setDocument(this);
512 DOMImplementationImpl *DocumentImpl::implementation() const
514 return m_implementation;
517 void DocumentImpl::childrenChanged()
519 // invalidate the document element we have cached in case it was replaced
520 if (m_documentElement)
521 m_documentElement->deref();
522 m_documentElement = 0;
525 ElementImpl *DocumentImpl::documentElement() const
527 if (!m_documentElement) {
528 NodeImpl* n = firstChild();
529 while (n && n->nodeType() != Node::ELEMENT_NODE)
530 n = n->nextSibling();
531 m_documentElement = static_cast<ElementImpl*>(n);
532 if (m_documentElement)
533 m_documentElement->ref();
535 return m_documentElement;
538 ElementImpl *DocumentImpl::createElement( const DOMString &name, int* pExceptioncode )
540 Id id = getId( NodeImpl::ElementId, name.implementation(),
541 false /* allocate */, false /*HTMLDocumentImpl::createElement looked for HTML elements already*/,
542 pExceptioncode);
543 if ( pExceptioncode && *pExceptioncode )
544 return 0;
546 XMLElementImpl* e = new XMLElementImpl( getDocument(), id );
547 e->setHTMLCompat( htmlMode() != XHtml ); // Not a real HTML element, but inside an html-compat doc all tags are uppercase.
548 return e;
551 AttrImpl *DocumentImpl::createAttribute( const DOMString &tagName, int* pExceptioncode )
553 Id id = getId( NodeImpl::AttributeId, tagName.implementation(),
554 false /* allocate */, isHTMLDocument(), pExceptioncode);
555 if ( pExceptioncode && *pExceptioncode )
556 return 0;
557 AttrImpl* attr = new AttrImpl( 0, getDocument(), id, DOMString("").implementation());
558 attr->setHTMLCompat( htmlMode() != XHtml );
559 return attr;
562 DocumentFragmentImpl *DocumentImpl::createDocumentFragment( )
564 return new DocumentFragmentImpl( docPtr() );
567 CommentImpl *DocumentImpl::createComment ( DOMStringImpl* data )
569 return new CommentImpl( docPtr(), data );
572 CDATASectionImpl *DocumentImpl::createCDATASection ( DOMStringImpl* data )
574 return new CDATASectionImpl( docPtr(), data );
577 ProcessingInstructionImpl *DocumentImpl::createProcessingInstruction ( const DOMString &target, DOMStringImpl* data )
579 return new ProcessingInstructionImpl( docPtr(),target,data);
582 EntityReferenceImpl *DocumentImpl::createEntityReference ( const DOMString &name )
584 return new EntityReferenceImpl(docPtr(), name.implementation());
587 EditingTextImpl *DocumentImpl::createEditingTextNode(const DOMString &text)
589 return new EditingTextImpl(docPtr(), text);
592 NodeImpl *DocumentImpl::importNode(NodeImpl *importedNode, bool deep, int &exceptioncode)
594 NodeImpl *result = 0;
596 // Not mentioned in spec: throw NOT_FOUND_ERR if evt is null
597 if (!importedNode) {
598 exceptioncode = DOMException::NOT_FOUND_ERR;
599 return 0;
602 if(importedNode->nodeType() == Node::ELEMENT_NODE)
604 // Why not use cloneNode?
605 ElementImpl *otherElem = static_cast<ElementImpl*>(importedNode);
606 NamedAttrMapImpl *otherMap = static_cast<ElementImpl *>(importedNode)->attributes(true);
608 ElementImpl *tempElementImpl;
609 if (!importedNode->localName().isNull())
610 tempElementImpl = createElementNS(otherElem->namespaceURI(),otherElem->nodeName());
611 else
612 tempElementImpl = createElement(otherElem->nodeName());
613 result = tempElementImpl;
616 if(otherMap) {
617 for(unsigned long i = 0; i < otherMap->length(); i++)
619 AttrImpl *otherAttr = otherMap->attrAt(i)->createAttr(otherElem,otherElem->docPtr());
621 if (!otherAttr->localName().isNull()) {
622 // attr was created via createElementNS()
623 tempElementImpl->setAttributeNS(otherAttr->namespaceURI(),
624 otherAttr->name(),
625 otherAttr->nodeValue(),
626 exceptioncode);
628 else {
629 // attr was created via createElement()
630 tempElementImpl->setAttribute(otherAttr->id(),
631 otherAttr->nodeValue(),
632 otherAttr->name(),
633 exceptioncode);
636 if(exceptioncode != 0)
637 break; // ### properly cleanup here
641 else if(importedNode->nodeType() == Node::TEXT_NODE)
643 result = createTextNode(static_cast<TextImpl*>(importedNode)->string());
644 deep = false;
646 else if(importedNode->nodeType() == Node::CDATA_SECTION_NODE)
648 result = createCDATASection(static_cast<CDATASectionImpl*>(importedNode)->string());
649 deep = false;
651 else if(importedNode->nodeType() == Node::ENTITY_REFERENCE_NODE)
652 result = createEntityReference(importedNode->nodeName());
653 else if(importedNode->nodeType() == Node::PROCESSING_INSTRUCTION_NODE)
655 result = createProcessingInstruction(importedNode->nodeName(), importedNode->nodeValue().implementation());
656 deep = false;
658 else if(importedNode->nodeType() == Node::COMMENT_NODE)
660 result = createComment(static_cast<CommentImpl*>(importedNode)->string());
661 deep = false;
663 else if (importedNode->nodeType() == Node::DOCUMENT_FRAGMENT_NODE)
664 result = createDocumentFragment();
665 else
666 exceptioncode = DOMException::NOT_SUPPORTED_ERR;
668 //### FIXME: This should handle Attributes, and a few other things
670 if(deep && result)
672 for(Node n = importedNode->firstChild(); !n.isNull(); n = n.nextSibling())
673 result->appendChild(importNode(n.handle(), true, exceptioncode), exceptioncode);
676 return result;
679 ElementImpl *DocumentImpl::createElementNS( const DOMString &_namespaceURI, const DOMString &_qualifiedName, int* pExceptioncode )
681 ElementImpl *e = 0;
682 int colonPos = -2;
683 // check NAMESPACE_ERR/INVALID_CHARACTER_ERR
684 if (pExceptioncode && !checkQualifiedName(_qualifiedName, _namespaceURI, &colonPos,
685 false/*nameCanBeNull*/, false/*nameCanBeEmpty*/,
686 pExceptioncode))
687 return 0;
688 DOMString prefix, localName;
689 splitPrefixLocalName(_qualifiedName.implementation(), prefix, localName, colonPos);
691 if ((isHTMLDocument() && _namespaceURI.isNull()) ||
692 (strcasecmp(_namespaceURI, XHTML_NAMESPACE) == 0 && localName == localName.lower())) {
693 e = createHTMLElement(localName);
694 if (e) {
695 int _exceptioncode = 0;
696 if (!prefix.isNull())
697 e->setPrefix(prefix, _exceptioncode);
698 if ( _exceptioncode ) {
699 if ( pExceptioncode ) *pExceptioncode = _exceptioncode;
700 delete e;
701 return 0;
703 e->setHTMLCompat( _namespaceURI.isNull() && htmlMode() != XHtml );
706 if (!e) {
707 Id id = getId(NodeImpl::ElementId, _namespaceURI.implementation(), prefix.implementation(),
708 localName.implementation(), false, false /*HTML already looked up*/);
709 e = new XMLElementImpl( getDocument(), id, prefix.implementation() );
712 return e;
715 AttrImpl *DocumentImpl::createAttributeNS( const DOMString &_namespaceURI,
716 const DOMString &_qualifiedName, int* pExceptioncode)
718 int colonPos = -2;
719 // check NAMESPACE_ERR/INVALID_CHARACTER_ERR
720 if (pExceptioncode && !checkQualifiedName(_qualifiedName, _namespaceURI, &colonPos,
721 false/*nameCanBeNull*/, false/*nameCanBeEmpty*/,
722 pExceptioncode))
723 return 0;
724 DOMString prefix, localName;
725 splitPrefixLocalName(_qualifiedName.implementation(), prefix, localName, colonPos);
726 Id id = getId(NodeImpl::AttributeId, _namespaceURI.implementation(), prefix.implementation(),
727 localName.implementation(), false, true /*lookupHTML*/);
728 AttrImpl* attr = new AttrImpl(0, getDocument(), id, DOMString("").implementation(),
729 prefix.implementation());
730 attr->setHTMLCompat( _namespaceURI.isNull() && htmlMode() != XHtml );
731 return attr;
734 ElementImpl *DocumentImpl::getElementById( const DOMString &elementId ) const
736 QString stringKey = elementId.string();
738 ElementMappingCache::ItemInfo* info = m_getElementByIdCache.get(stringKey);
740 if (!info)
741 return 0;
743 //See if cache has an unambiguous answer.
744 if (info->nd)
745 return info->nd;
747 //Now we actually have to walk.
748 QStack<NodeImpl*> nodeStack;
749 NodeImpl *current = _first;
751 while(1)
753 if(!current)
755 if(nodeStack.isEmpty()) break;
756 current = nodeStack.pop();
757 current = current->nextSibling();
759 else
761 if(current->isElementNode())
763 ElementImpl *e = static_cast<ElementImpl *>(current);
764 if(e->getAttribute(ATTR_ID) == elementId) {
765 info->nd = e;
766 return e;
770 NodeImpl *child = current->firstChild();
771 if(child)
773 nodeStack.push(current);
774 current = child;
776 else
778 current = current->nextSibling();
783 assert(0); //If there is no item with such an ID, we should never get here
785 //kDebug() << "WARNING: *DocumentImpl::getElementById not found " << elementId.string();
787 return 0;
790 void DocumentImpl::setTitle(const DOMString& _title)
792 if (_title == m_title && !m_title.isNull()) return;
794 m_title = _title;
796 QString titleStr = m_title.string();
797 for (int i = 0; i < titleStr.length(); ++i)
798 if (titleStr[i] < ' ')
799 titleStr[i] = ' ';
800 titleStr = titleStr.simplified();
801 if ( view() && !view()->part()->parentPart() ) {
802 if (titleStr.isNull() || titleStr.isEmpty()) {
803 // empty title... set window caption as the URL
804 KUrl url = m_url;
805 url.setRef(QString());
806 url.setQuery(QString());
807 titleStr = url.prettyUrl();
810 emit view()->part()->setWindowCaption( KStringHandler::csqueeze( titleStr, 128 ) );
814 DOMString DocumentImpl::nodeName() const
816 return "#document";
819 unsigned short DocumentImpl::nodeType() const
821 return Node::DOCUMENT_NODE;
824 ElementImpl *DocumentImpl::createHTMLElement( const DOMString &name )
826 uint id = khtml::getTagID( name.string().toLower().toLatin1().constData(), name.string().length() );
828 ElementImpl *n = 0;
829 switch(id)
831 case ID_HTML:
832 n = new HTMLHtmlElementImpl(docPtr());
833 break;
834 case ID_HEAD:
835 n = new HTMLHeadElementImpl(docPtr());
836 break;
837 case ID_BODY:
838 n = new HTMLBodyElementImpl(docPtr());
839 break;
841 // head elements
842 case ID_BASE:
843 n = new HTMLBaseElementImpl(docPtr());
844 break;
845 case ID_LINK:
846 n = new HTMLLinkElementImpl(docPtr());
847 break;
848 case ID_META:
849 n = new HTMLMetaElementImpl(docPtr());
850 break;
851 case ID_STYLE:
852 n = new HTMLStyleElementImpl(docPtr());
853 break;
854 case ID_TITLE:
855 n = new HTMLTitleElementImpl(docPtr());
856 break;
858 // frames
859 case ID_FRAME:
860 n = new HTMLFrameElementImpl(docPtr());
861 break;
862 case ID_FRAMESET:
863 n = new HTMLFrameSetElementImpl(docPtr());
864 break;
865 case ID_IFRAME:
866 n = new HTMLIFrameElementImpl(docPtr());
867 break;
869 // form elements
870 // ### FIXME: we need a way to set form dependency after we have made the form elements
871 case ID_FORM:
872 n = new HTMLFormElementImpl(docPtr(), false);
873 break;
874 case ID_BUTTON:
875 n = new HTMLButtonElementImpl(docPtr());
876 break;
877 case ID_FIELDSET:
878 n = new HTMLFieldSetElementImpl(docPtr());
879 break;
880 case ID_INPUT:
881 n = new HTMLInputElementImpl(docPtr());
882 break;
883 case ID_ISINDEX:
884 n = new HTMLIsIndexElementImpl(docPtr());
885 break;
886 case ID_LABEL:
887 n = new HTMLLabelElementImpl(docPtr());
888 break;
889 case ID_LEGEND:
890 n = new HTMLLegendElementImpl(docPtr());
891 break;
892 case ID_OPTGROUP:
893 n = new HTMLOptGroupElementImpl(docPtr());
894 break;
895 case ID_OPTION:
896 n = new HTMLOptionElementImpl(docPtr());
897 break;
898 case ID_SELECT:
899 n = new HTMLSelectElementImpl(docPtr());
900 break;
901 case ID_TEXTAREA:
902 n = new HTMLTextAreaElementImpl(docPtr());
903 break;
905 // lists
906 case ID_DL:
907 n = new HTMLDListElementImpl(docPtr());
908 break;
909 case ID_DD:
910 n = new HTMLGenericElementImpl(docPtr(), id);
911 break;
912 case ID_DT:
913 n = new HTMLGenericElementImpl(docPtr(), id);
914 break;
915 case ID_UL:
916 n = new HTMLUListElementImpl(docPtr());
917 break;
918 case ID_OL:
919 n = new HTMLOListElementImpl(docPtr());
920 break;
921 case ID_DIR:
922 n = new HTMLDirectoryElementImpl(docPtr());
923 break;
924 case ID_MENU:
925 n = new HTMLMenuElementImpl(docPtr());
926 break;
927 case ID_LI:
928 n = new HTMLLIElementImpl(docPtr());
929 break;
931 // formatting elements (block)
932 case ID_DIV:
933 case ID_P:
934 n = new HTMLDivElementImpl( docPtr(), id );
935 break;
936 case ID_BLOCKQUOTE:
937 case ID_H1:
938 case ID_H2:
939 case ID_H3:
940 case ID_H4:
941 case ID_H5:
942 case ID_H6:
943 n = new HTMLGenericElementImpl(docPtr(), id);
944 break;
945 case ID_HR:
946 n = new HTMLHRElementImpl(docPtr());
947 break;
948 case ID_PLAINTEXT:
949 case ID_XMP:
950 case ID_PRE:
951 n = new HTMLPreElementImpl(docPtr(), id);
952 break;
954 // font stuff
955 case ID_BASEFONT:
956 n = new HTMLBaseFontElementImpl(docPtr());
957 break;
958 case ID_FONT:
959 n = new HTMLFontElementImpl(docPtr());
960 break;
962 // ins/del
963 case ID_DEL:
964 case ID_INS:
965 n = new HTMLGenericElementImpl(docPtr(), id);
966 break;
968 // anchor
969 case ID_A:
970 n = new HTMLAnchorElementImpl(docPtr());
971 break;
973 // images
974 case ID_IMG:
975 n = new HTMLImageElementImpl(docPtr());
976 break;
977 case ID_CANVAS:
978 n = new HTMLCanvasElementImpl(docPtr());
979 break;
980 case ID_MAP:
981 n = new HTMLMapElementImpl(docPtr());
982 /*n = map;*/
983 break;
984 case ID_AREA:
985 n = new HTMLAreaElementImpl(docPtr());
986 break;
988 // objects, applets and scripts
989 case ID_APPLET:
990 n = new HTMLAppletElementImpl(docPtr());
991 break;
992 case ID_OBJECT:
993 n = new HTMLObjectElementImpl(docPtr());
994 break;
995 case ID_EMBED:
996 n = new HTMLEmbedElementImpl(docPtr());
997 break;
998 case ID_PARAM:
999 n = new HTMLParamElementImpl(docPtr());
1000 break;
1001 case ID_SCRIPT:
1002 n = new HTMLScriptElementImpl(docPtr());
1003 break;
1005 // media
1006 case ID_AUDIO:
1007 n = new HTMLAudioElement(docPtr());
1008 break;
1009 case ID_VIDEO:
1010 n = new HTMLVideoElement(docPtr());
1011 break;
1012 case ID_SOURCE:
1013 n = new HTMLSourceElement(docPtr());
1014 break;
1016 // tables
1017 case ID_TABLE:
1018 n = new HTMLTableElementImpl(docPtr());
1019 break;
1020 case ID_CAPTION:
1021 n = new HTMLTableCaptionElementImpl(docPtr());
1022 break;
1023 case ID_COLGROUP:
1024 case ID_COL:
1025 n = new HTMLTableColElementImpl(docPtr(), id);
1026 break;
1027 case ID_TR:
1028 n = new HTMLTableRowElementImpl(docPtr());
1029 break;
1030 case ID_TD:
1031 case ID_TH:
1032 n = new HTMLTableCellElementImpl(docPtr(), id);
1033 break;
1034 case ID_THEAD:
1035 case ID_TBODY:
1036 case ID_TFOOT:
1037 n = new HTMLTableSectionElementImpl(docPtr(), id, false);
1038 break;
1040 // inline elements
1041 case ID_BR:
1042 n = new HTMLBRElementImpl(docPtr());
1043 break;
1044 case ID_WBR:
1045 n = new HTMLWBRElementImpl(docPtr());
1046 break;
1047 case ID_Q:
1048 n = new HTMLGenericElementImpl(docPtr(), id);
1049 break;
1051 // elements with no special representation in the DOM
1053 // block:
1054 case ID_ADDRESS:
1055 case ID_CENTER:
1056 n = new HTMLGenericElementImpl(docPtr(), id);
1057 break;
1058 // inline
1059 // %fontstyle
1060 case ID_TT:
1061 case ID_U:
1062 case ID_B:
1063 case ID_I:
1064 case ID_S:
1065 case ID_STRIKE:
1066 case ID_BIG:
1067 case ID_SMALL:
1069 // %phrase
1070 case ID_EM:
1071 case ID_STRONG:
1072 case ID_DFN:
1073 case ID_CODE:
1074 case ID_SAMP:
1075 case ID_KBD:
1076 case ID_VAR:
1077 case ID_CITE:
1078 case ID_ABBR:
1079 case ID_ACRONYM:
1081 // %special
1082 case ID_SUB:
1083 case ID_SUP:
1084 case ID_SPAN:
1085 case ID_NOBR:
1086 case ID_BDO:
1087 case ID_NOFRAMES:
1088 n = new HTMLGenericElementImpl(docPtr(), id);
1089 break;
1091 case ID_MARQUEE:
1092 n = new HTMLMarqueeElementImpl(docPtr());
1093 break;
1094 // text
1095 case ID_TEXT:
1096 kDebug( 6020 ) << "Use document->createTextNode()";
1097 break;
1099 default:
1100 break;
1102 return n;
1105 void DocumentImpl::attemptRestoreState(NodeImpl* n)
1107 if (!n->isElementNode())
1108 return;
1110 ElementImpl* el = static_cast<ElementImpl*>(n);
1112 if (m_stateRestorePos >= m_state.size())
1113 return;
1115 // Grab the state and element info..
1116 QString idStr = m_state[m_stateRestorePos];
1117 QString nmStr = m_state[m_stateRestorePos + 1];
1118 QString tpStr = m_state[m_stateRestorePos + 2];
1119 QString stStr = m_state[m_stateRestorePos + 3];
1121 // Make sure it matches!
1122 if (idStr.toUInt() != el->id())
1123 return;
1124 if (nmStr != el->getAttribute(ATTR_NAME).string())
1125 return;
1126 if (tpStr != el->getAttribute(ATTR_TYPE).string())
1127 return;
1129 m_stateRestorePos += 4;
1130 if (!stStr.isNull())
1131 el->restoreState(stStr);
1134 QStringList DocumentImpl::docState()
1136 QStringList s;
1137 for (QListIterator<NodeImpl*> it(m_maintainsState); it.hasNext();) {
1138 NodeImpl* n = it.next();
1139 if (!n->isElementNode())
1140 continue;
1142 ElementImpl* el = static_cast<ElementImpl*>(n);
1143 // Encode the element ID, as well as the name and type attributes
1144 s.append(QString::number(el->id()));
1145 s.append(el->getAttribute(ATTR_NAME).string());
1146 s.append(el->getAttribute(ATTR_TYPE).string());
1147 s.append(el->state());
1150 return s;
1153 bool DocumentImpl::unsubmittedFormChanges()
1155 for (QListIterator<NodeImpl*> it(m_maintainsState); it.hasNext();)
1156 if (it.next()->state().endsWith('M'))
1157 return true;
1159 return false;
1162 RangeImpl *DocumentImpl::createRange()
1164 return new RangeImpl( docPtr() );
1167 NodeIteratorImpl *DocumentImpl::createNodeIterator(NodeImpl *root, unsigned long whatToShow,
1168 NodeFilterImpl* filter, bool entityReferenceExpansion,
1169 int &exceptioncode)
1171 if (!root) {
1172 exceptioncode = DOMException::NOT_SUPPORTED_ERR;
1173 return 0;
1176 return new NodeIteratorImpl(root,whatToShow,filter,entityReferenceExpansion);
1179 TreeWalkerImpl *DocumentImpl::createTreeWalker(NodeImpl *root, unsigned long whatToShow, NodeFilterImpl *filter,
1180 bool entityReferenceExpansion, int &exceptioncode)
1182 if (!root) {
1183 exceptioncode = DOMException::NOT_SUPPORTED_ERR;
1184 return 0;
1187 return new TreeWalkerImpl( root, whatToShow, filter, entityReferenceExpansion );
1190 void DocumentImpl::setDocumentChanged(bool b)
1192 if (b && !m_docChanged)
1193 s_changedDocuments->append(this);
1194 else if (!b && m_docChanged)
1195 s_changedDocuments->removeAll(this);
1196 m_docChanged = b;
1199 void DocumentImpl::recalcStyle( StyleChange change )
1201 // qDebug("recalcStyle(%p)", this);
1202 // QTime qt;
1203 // qt.start();
1204 if (m_inStyleRecalc)
1205 return; // Guard against re-entrancy. -dwh
1207 m_inStyleRecalc = true;
1209 if( !m_render ) goto bail_out;
1211 if ( change == Force ) {
1212 RenderStyle* oldStyle = m_render->style();
1213 if ( oldStyle ) oldStyle->ref();
1214 RenderStyle* _style = new RenderStyle();
1215 _style->setDisplay(BLOCK);
1216 _style->setVisuallyOrdered( visuallyOrdered );
1217 // ### make the font stuff _really_ work!!!!
1219 khtml::FontDef fontDef;
1220 QFont f = KGlobalSettings::generalFont();
1221 fontDef.family = f.family();
1222 fontDef.italic = f.italic();
1223 fontDef.weight = f.weight();
1224 if (m_view) {
1225 const KHTMLSettings *settings = m_view->part()->settings();
1226 QString stdfont = settings->stdFontName();
1227 if ( !stdfont.isEmpty() )
1228 fontDef.family = stdfont;
1230 fontDef.size = m_styleSelector->fontSizes()[3];
1233 //kDebug() << "DocumentImpl::attach: setting to charset " << settings->charset();
1234 _style->setFontDef(fontDef);
1235 _style->htmlFont().update( 0 );
1236 if ( inCompatMode() )
1237 _style->setHtmlHacks(true); // enable html specific rendering tricks
1239 StyleChange ch = diff( _style, oldStyle );
1240 if(m_render && ch != NoChange)
1241 m_render->setStyle(_style);
1242 else
1243 delete _style;
1245 if (oldStyle)
1246 oldStyle->deref();
1249 NodeImpl *n;
1250 for (n = _first; n; n = n->nextSibling())
1251 if ( change>= Inherit || n->hasChangedChild() || n->changed() )
1252 n->recalcStyle( change );
1253 //kDebug( 6020 ) << "TIME: recalcStyle() dt=" << qt.elapsed();
1255 if (changed() && m_view)
1256 m_view->layout();
1258 bail_out:
1259 setChanged( false );
1260 setHasChangedChild( false );
1261 setDocumentChanged( false );
1263 m_inStyleRecalc = false;
1266 void DocumentImpl::updateRendering()
1268 if (!hasChangedChild()) return;
1270 // QTime time;
1271 // time.start();
1272 // kDebug() << "UPDATERENDERING: ";
1274 StyleChange change = NoChange;
1275 #if 0
1276 if ( m_styleSelectorDirty ) {
1277 recalcStyleSelector();
1278 change = Force;
1280 #endif
1281 recalcStyle( change );
1283 // kDebug() << "UPDATERENDERING time used="<<time.elapsed();
1286 void DocumentImpl::updateDocumentsRendering()
1288 if (!s_changedDocuments)
1289 return;
1291 while ( !s_changedDocuments->isEmpty() ) {
1292 DocumentImpl* it = s_changedDocuments->takeFirst();
1293 if (it->isDocumentChanged())
1294 it->updateRendering();
1298 void DocumentImpl::updateLayout()
1300 if (ElementImpl* oe = ownerElement())
1301 oe->getDocument()->updateLayout();
1303 bool oldIgnore = m_ignorePendingStylesheets;
1305 if (!haveStylesheetsLoaded()) {
1306 m_ignorePendingStylesheets = true;
1307 updateStyleSelector();
1310 updateRendering();
1312 // Only do a layout if changes have occurred that make it necessary.
1313 if (m_view && renderer() && renderer()->needsLayout())
1314 m_view->layout();
1316 m_ignorePendingStylesheets = oldIgnore;
1319 void DocumentImpl::attach()
1321 assert(!attached());
1323 if ( m_view )
1324 setPaintDevice( m_view );
1326 if (!m_renderArena)
1327 m_renderArena.reset(new RenderArena());
1329 // Create the rendering tree
1330 assert(!m_styleSelector);
1331 m_styleSelector = new CSSStyleSelector( this, m_usersheet, m_styleSheets, m_url,
1332 !inCompatMode() );
1333 m_render = new (m_renderArena.get()) RenderCanvas(this, m_view);
1334 m_styleSelector->computeFontSizes(m_paintDevice->logicalDpiY(), m_view ? m_view->part()->fontScaleFactor() : 100);
1335 recalcStyle( Force );
1337 RenderObject* render = m_render;
1338 m_render = 0;
1340 NodeBaseImpl::attach();
1341 m_render = render;
1344 void DocumentImpl::detach()
1346 RenderObject* render = m_render;
1348 // indicate destruction mode, i.e. attached() but m_render == 0
1349 m_render = 0;
1351 delete m_tokenizer;
1352 m_tokenizer = 0;
1354 // Empty out these lists as a performance optimization
1355 m_imageLoadEventDispatchSoonList.clear();
1356 m_imageLoadEventDispatchingList.clear();
1357 NodeBaseImpl::detach();
1359 if ( render )
1360 render->detach();
1362 m_view = 0;
1364 m_renderArena.reset();
1367 void DocumentImpl::setVisuallyOrdered()
1369 visuallyOrdered = true;
1370 if (m_render)
1371 m_render->style()->setVisuallyOrdered(true);
1374 void DocumentImpl::setSelection(NodeImpl* s, int sp, NodeImpl* e, int ep)
1376 if ( m_render )
1377 static_cast<RenderCanvas*>(m_render)->setSelection(s->renderer(),sp,e->renderer(),ep);
1380 void DocumentImpl::clearSelection()
1382 if ( m_render )
1383 static_cast<RenderCanvas*>(m_render)->clearSelection();
1386 void DocumentImpl::updateSelection()
1388 if (!m_render)
1389 return;
1391 RenderCanvas *canvas = static_cast<RenderCanvas*>(m_render);
1392 Selection s = part()->caret();
1393 if (s.isEmpty() || s.state() == Selection::CARET) {
1394 canvas->clearSelection();
1396 else {
1397 RenderObject *startRenderer = s.start().node() ? s.start().node()->renderer() : 0;
1398 RenderObject *endRenderer = s.end().node() ? s.end().node()->renderer() : 0;
1399 static_cast<RenderCanvas*>(m_render)->setSelection(startRenderer, s.start().offset(), endRenderer, s.end().offset());
1403 khtml::Tokenizer *DocumentImpl::createTokenizer()
1405 return new khtml::XMLTokenizer(docPtr(),m_view);
1408 int DocumentImpl::logicalDpiY()
1410 return m_paintDevice->logicalDpiY();
1413 void DocumentImpl::open( bool clearEventListeners )
1415 if (parsing()) return;
1417 if (m_tokenizer)
1418 close();
1420 delete m_tokenizer;
1421 m_tokenizer = 0;
1423 KHTMLView* view = m_view;
1424 bool was_attached = attached();
1425 if ( was_attached )
1426 detach();
1428 removeChildren();
1429 delete m_styleSelector;
1430 m_styleSelector = 0;
1431 m_view = view;
1432 if ( was_attached )
1433 attach();
1435 if (clearEventListeners)
1436 m_windowEventListeners.clear();
1438 m_tokenizer = createTokenizer();
1439 //m_decoderMibEnum = 0;
1440 connect(m_tokenizer,SIGNAL(finishedParsing()),this,SIGNAL(finishedParsing()));
1441 m_tokenizer->begin();
1444 HTMLElementImpl* DocumentImpl::body() const
1446 NodeImpl *de = documentElement();
1447 if (!de)
1448 return 0;
1450 // try to prefer a FRAMESET element over BODY
1451 NodeImpl* body = 0;
1452 for (NodeImpl* i = de->firstChild(); i; i = i->nextSibling()) {
1453 if (i->id() == ID_FRAMESET)
1454 return static_cast<HTMLElementImpl*>(i);
1456 if (i->id() == ID_BODY)
1457 body = i;
1459 return static_cast<HTMLElementImpl *>(body);
1462 void DocumentImpl::close( )
1464 if (parsing() || !m_tokenizer) return;
1466 if ( m_render )
1467 m_render->close();
1469 // on an explicit document.close(), the tokenizer might still be waiting on scripts,
1470 // and in that case we don't want to destroy it because that will prevent the
1471 // scripts from getting processed.
1472 if (m_tokenizer && !m_tokenizer->isWaitingForScripts() && !m_tokenizer->isExecutingScript()) {
1473 delete m_tokenizer;
1474 m_tokenizer = 0;
1477 if (m_view)
1478 m_view->part()->checkEmitLoadEvent();
1481 void DocumentImpl::write( const DOMString &text )
1483 write(text.string());
1486 void DocumentImpl::write( const QString &text )
1488 if (!m_tokenizer) {
1489 open();
1490 if (m_view)
1491 m_view->part()->resetFromScript();
1492 m_tokenizer->setAutoClose();
1493 write(QLatin1String("<html>"));
1495 m_tokenizer->write(text, false);
1498 void DocumentImpl::writeln( const DOMString &text )
1500 write(text);
1501 write(DOMString("\n"));
1504 void DocumentImpl::finishParsing ( )
1506 if(m_tokenizer)
1507 m_tokenizer->finish();
1510 void DocumentImpl::setUserStyleSheet( const QString& sheet )
1512 if ( m_usersheet != sheet ) {
1513 m_usersheet = sheet;
1514 updateStyleSelector();
1518 CSSStyleSheetImpl* DocumentImpl::elementSheet()
1520 if (!m_elemSheet) {
1521 m_elemSheet = new CSSStyleSheetImpl(this, baseURL().url() );
1522 m_elemSheet->ref();
1524 return m_elemSheet;
1527 void DocumentImpl::determineParseMode()
1529 // For XML documents, use strict parse mode
1530 pMode = Strict;
1531 hMode = XHtml;
1532 kDebug(6020) << " using strict parseMode";
1535 NodeImpl *DocumentImpl::nextFocusNode(NodeImpl *fromNode)
1537 short fromTabIndex;
1539 if (!fromNode) {
1540 // No starting node supplied; begin with the top of the document
1541 NodeImpl *n;
1543 int lowestTabIndex = SHRT_MAX + 1;
1544 for (n = this; n != 0; n = n->traverseNextNode()) {
1545 if (n->isTabFocusable()) {
1546 if ((n->tabIndex() > 0) && (n->tabIndex() < lowestTabIndex))
1547 lowestTabIndex = n->tabIndex();
1551 if (lowestTabIndex == SHRT_MAX + 1)
1552 lowestTabIndex = 0;
1554 // Go to the first node in the document that has the desired tab index
1555 for (n = this; n != 0; n = n->traverseNextNode()) {
1556 if (n->isTabFocusable() && (n->tabIndex() == lowestTabIndex))
1557 return n;
1560 return 0;
1562 else {
1563 fromTabIndex = fromNode->tabIndex();
1566 if (fromTabIndex == 0) {
1567 // Just need to find the next selectable node after fromNode (in document order) that doesn't have a tab index
1568 NodeImpl *n = fromNode->traverseNextNode();
1569 while (n && !(n->isTabFocusable() && n->tabIndex() == 0))
1570 n = n->traverseNextNode();
1571 return n;
1573 else {
1574 // Find the lowest tab index out of all the nodes except fromNode, that is greater than or equal to fromNode's
1575 // tab index. For nodes with the same tab index as fromNode, we are only interested in those that come after
1576 // fromNode in document order.
1577 // If we don't find a suitable tab index, the next focus node will be one with a tab index of 0.
1578 int lowestSuitableTabIndex = SHRT_MAX + 1;
1579 NodeImpl *n;
1581 bool reachedFromNode = false;
1582 for (n = this; n != 0; n = n->traverseNextNode()) {
1583 if (n->isTabFocusable() &&
1584 ((reachedFromNode && (n->tabIndex() >= fromTabIndex)) ||
1585 (!reachedFromNode && (n->tabIndex() > fromTabIndex))) &&
1586 (n->tabIndex() < lowestSuitableTabIndex) &&
1587 (n != fromNode)) {
1589 // We found a selectable node with a tab index at least as high as fromNode's. Keep searching though,
1590 // as there may be another node which has a lower tab index but is still suitable for use.
1591 lowestSuitableTabIndex = n->tabIndex();
1594 if (n == fromNode)
1595 reachedFromNode = true;
1598 if (lowestSuitableTabIndex == SHRT_MAX + 1) {
1599 // No next node with a tab index -> just take first node with tab index of 0
1600 NodeImpl *n = this;
1601 while (n && !(n->isTabFocusable() && n->tabIndex() == 0))
1602 n = n->traverseNextNode();
1603 return n;
1606 // Search forwards from fromNode
1607 for (n = fromNode->traverseNextNode(); n != 0; n = n->traverseNextNode()) {
1608 if (n->isTabFocusable() && (n->tabIndex() == lowestSuitableTabIndex))
1609 return n;
1612 // The next node isn't after fromNode, start from the beginning of the document
1613 for (n = this; n != fromNode; n = n->traverseNextNode()) {
1614 if (n->isTabFocusable() && (n->tabIndex() == lowestSuitableTabIndex))
1615 return n;
1618 assert(false); // should never get here
1619 return 0;
1623 NodeImpl *DocumentImpl::previousFocusNode(NodeImpl *fromNode)
1625 NodeImpl *lastNode = this;
1626 while (lastNode->lastChild())
1627 lastNode = lastNode->lastChild();
1629 if (!fromNode) {
1630 // No starting node supplied; begin with the very last node in the document
1631 NodeImpl *n;
1633 int highestTabIndex = 0;
1634 for (n = lastNode; n != 0; n = n->traversePreviousNode()) {
1635 if (n->isTabFocusable()) {
1636 if (n->tabIndex() == 0)
1637 return n;
1638 else if (n->tabIndex() > highestTabIndex)
1639 highestTabIndex = n->tabIndex();
1643 // No node with a tab index of 0; just go to the last node with the highest tab index
1644 for (n = lastNode; n != 0; n = n->traversePreviousNode()) {
1645 if (n->isTabFocusable() && (n->tabIndex() == highestTabIndex))
1646 return n;
1649 return 0;
1651 else {
1652 short fromTabIndex = fromNode->tabIndex();
1654 if (fromTabIndex == 0) {
1655 // Find the previous selectable node before fromNode (in document order) that doesn't have a tab index
1656 NodeImpl *n = fromNode->traversePreviousNode();
1657 while (n && !(n->isTabFocusable() && n->tabIndex() == 0))
1658 n = n->traversePreviousNode();
1659 if (n)
1660 return n;
1662 // No previous nodes with a 0 tab index, go to the last node in the document that has the highest tab index
1663 int highestTabIndex = 0;
1664 for (n = this; n != 0; n = n->traverseNextNode()) {
1665 if (n->isTabFocusable() && (n->tabIndex() > highestTabIndex))
1666 highestTabIndex = n->tabIndex();
1669 if (highestTabIndex == 0)
1670 return 0;
1672 for (n = lastNode; n != 0; n = n->traversePreviousNode()) {
1673 if (n->isTabFocusable() && (n->tabIndex() == highestTabIndex))
1674 return n;
1677 assert(false); // should never get here
1678 return 0;
1680 else {
1681 // Find the lowest tab index out of all the nodes except fromNode, that is less than or equal to fromNode's
1682 // tab index. For nodes with the same tab index as fromNode, we are only interested in those before
1683 // fromNode.
1684 // If we don't find a suitable tab index, then there will be no previous focus node.
1685 short highestSuitableTabIndex = 0;
1686 NodeImpl *n;
1688 bool reachedFromNode = false;
1689 for (n = this; n != 0; n = n->traverseNextNode()) {
1690 if (n->isTabFocusable() &&
1691 ((!reachedFromNode && (n->tabIndex() <= fromTabIndex)) ||
1692 (reachedFromNode && (n->tabIndex() < fromTabIndex))) &&
1693 (n->tabIndex() > highestSuitableTabIndex) &&
1694 (n != fromNode)) {
1696 // We found a selectable node with a tab index no higher than fromNode's. Keep searching though, as
1697 // there may be another node which has a higher tab index but is still suitable for use.
1698 highestSuitableTabIndex = n->tabIndex();
1701 if (n == fromNode)
1702 reachedFromNode = true;
1705 if (highestSuitableTabIndex == 0) {
1706 // No previous node with a tab index. Since the order specified by HTML is nodes with tab index > 0
1707 // first, this means that there is no previous node.
1708 return 0;
1711 // Search backwards from fromNode
1712 for (n = fromNode->traversePreviousNode(); n != 0; n = n->traversePreviousNode()) {
1713 if (n->isTabFocusable() && (n->tabIndex() == highestSuitableTabIndex))
1714 return n;
1716 // The previous node isn't before fromNode, start from the end of the document
1717 for (n = lastNode; n != fromNode; n = n->traversePreviousNode()) {
1718 if (n->isTabFocusable() && (n->tabIndex() == highestSuitableTabIndex))
1719 return n;
1722 assert(false); // should never get here
1723 return 0;
1728 ElementImpl* DocumentImpl::findAccessKeyElement(QChar c)
1730 c = c.toUpper();
1731 for( NodeImpl* n = this;
1732 n != NULL;
1733 n = n->traverseNextNode()) {
1734 if( n->isElementNode()) {
1735 ElementImpl* en = static_cast< ElementImpl* >( n );
1736 DOMString s = en->getAttribute( ATTR_ACCESSKEY );
1737 if( s.length() == 1
1738 && s[ 0 ].toUpper() == c )
1739 return en;
1742 return NULL;
1745 int DocumentImpl::nodeAbsIndex(NodeImpl *node)
1747 assert(node->getDocument() == this);
1749 int absIndex = 0;
1750 for (NodeImpl *n = node; n && n != this; n = n->traversePreviousNode())
1751 absIndex++;
1752 return absIndex;
1755 NodeImpl *DocumentImpl::nodeWithAbsIndex(int absIndex)
1757 NodeImpl *n = this;
1758 for (int i = 0; n && (i < absIndex); i++) {
1759 n = n->traverseNextNode();
1761 return n;
1764 void DocumentImpl::processHttpEquiv(const DOMString &equiv, const DOMString &content)
1766 assert(!equiv.isNull() && !content.isNull());
1768 KHTMLView *v = getDocument()->view();
1770 if(strcasecmp(equiv, "refresh") == 0 && v && v->part()->metaRefreshEnabled())
1772 // get delay and url
1773 QString str = content.string().trimmed();
1774 int pos = str.indexOf(QRegExp("[;,]"));
1775 if ( pos == -1 )
1776 pos = str.indexOf(QRegExp("[ \t]"));
1778 bool ok = false;
1779 int delay = qMax( 0, content.implementation()->toInt(&ok) );
1780 if ( !ok && str.length() && str[0] == '.' )
1781 ok = true;
1783 if (pos == -1) // There can be no url (David)
1785 if(ok)
1786 v->part()->scheduleRedirection(delay, v->part()->url().url() );
1787 } else {
1788 pos++;
1789 while(pos < (int)str.length() && str[pos].isSpace()) pos++;
1790 str = str.mid(pos);
1791 if(str.indexOf("url", 0, Qt::CaseInsensitive ) == 0) str = str.mid(3);
1792 str = str.trimmed();
1793 if ( str.length() && str[0] == '=' ) str = str.mid( 1 ).trimmed();
1794 while(str.length() &&
1795 (str[str.length()-1] == ';' || str[str.length()-1] == ','))
1796 str.resize(str.length()-1);
1797 str = parseURL( DOMString(str) ).string();
1798 QString newURL = getDocument()->completeURL( str );
1799 if ( ok )
1800 v->part()->scheduleRedirection(delay, getDocument()->completeURL( str ), delay < 2 || newURL == URL().url());
1803 else if(strcasecmp(equiv, "expires") == 0)
1805 bool relative = false;
1806 QString str = content.string().trimmed();
1807 time_t expire_date = KDateTime::fromString(str, KDateTime::RFCDate).toTime_t();
1808 if (!expire_date)
1810 expire_date = str.toULong();
1811 relative = true;
1813 if (!expire_date)
1814 expire_date = 1; // expire now
1815 if (m_docLoader)
1816 m_docLoader->setExpireDate(expire_date, relative);
1818 else if(v && (strcasecmp(equiv, "pragma") == 0 || strcasecmp(equiv, "cache-control") == 0))
1820 QString str = content.string().toLower().trimmed();
1821 KUrl url = v->part()->url();
1822 if ((str == "no-cache") && url.protocol().startsWith("http"))
1824 KIO::http_update_cache(url, true, 0);
1827 else if( (strcasecmp(equiv, "set-cookie") == 0))
1829 // ### make setCookie work on XML documents too; e.g. in case of <html:meta .....>
1830 HTMLDocumentImpl *d = static_cast<HTMLDocumentImpl *>(this);
1831 d->setCookie(content);
1833 else if (strcasecmp(equiv, "default-style") == 0) {
1834 // HTML 4.0 14.3.2
1835 // http://www.hixie.ch/tests/evil/css/import/main/preferred.html
1836 m_preferredStylesheetSet = content;
1837 updateStyleSelector();
1839 else if (strcasecmp(equiv, "content-language") == 0) {
1840 m_contentLanguage = content.string();
1844 bool DocumentImpl::prepareMouseEvent( bool readonly, int _x, int _y, MouseEvent *ev )
1846 if ( m_render ) {
1847 assert(m_render->isCanvas());
1848 RenderObject::NodeInfo renderInfo(readonly, ev->type == MousePress);
1849 bool isInside = m_render->layer()->nodeAtPoint(renderInfo, _x, _y);
1850 ev->innerNode = renderInfo.innerNode();
1851 ev->innerNonSharedNode = renderInfo.innerNonSharedNode();
1853 if (renderInfo.URLElement()) {
1854 assert(renderInfo.URLElement()->isElementNode());
1855 //qDebug("urlnode: %s (%d)", getTagName(renderInfo.URLElement()->id()).string().toLatin1().constData(), renderInfo.URLElement()->id());
1857 ElementImpl* e = static_cast<ElementImpl*>(renderInfo.URLElement());
1858 DOMString href = khtml::parseURL(e->getAttribute(ATTR_HREF));
1859 DOMString target = e->getAttribute(ATTR_TARGET);
1861 if (!target.isNull() && !href.isNull()) {
1862 ev->target = target;
1863 ev->url = href;
1865 else
1866 ev->url = href;
1869 if (!readonly)
1870 updateRendering();
1872 return isInside;
1876 return false;
1880 // DOM Section 1.1.1
1881 bool DocumentImpl::childTypeAllowed( unsigned short type )
1883 switch (type) {
1884 case Node::ATTRIBUTE_NODE:
1885 case Node::CDATA_SECTION_NODE:
1886 case Node::DOCUMENT_FRAGMENT_NODE:
1887 case Node::DOCUMENT_NODE:
1888 case Node::ENTITY_NODE:
1889 case Node::ENTITY_REFERENCE_NODE:
1890 case Node::NOTATION_NODE:
1891 case Node::TEXT_NODE:
1892 // case Node::XPATH_NAMESPACE_NODE:
1893 return false;
1894 case Node::COMMENT_NODE:
1895 case Node::PROCESSING_INSTRUCTION_NODE:
1896 return true;
1897 case Node::DOCUMENT_TYPE_NODE:
1898 case Node::ELEMENT_NODE:
1899 // Documents may contain no more than one of each of these.
1900 // (One Element and one DocumentType.)
1901 for (NodeImpl* c = firstChild(); c; c = c->nextSibling())
1902 if (c->nodeType() == type)
1903 return false;
1904 return true;
1906 return false;
1909 NodeImpl *DocumentImpl::cloneNode ( bool deep )
1911 #if 0
1912 NodeImpl *dtn = m_doctype->cloneNode(deep);
1913 DocumentTypeImpl *dt = static_cast<DocumentTypeImpl*>(dtn);
1914 #endif
1916 int exceptioncode;
1917 DocumentImpl *clone = m_implementation->createDocument("",
1919 0, exceptioncode);
1920 assert( exceptioncode == 0 );
1922 // ### attributes, styles, ...
1924 if (deep)
1925 cloneChildNodes(clone);
1927 return clone;
1930 typedef const char* (*NameLookupFunction)(unsigned short id);
1931 typedef int (*IdLookupFunction)(const char *tagStr, int len);
1933 NodeImpl::Id DocumentImpl::getId( NodeImpl::IdType _type, DOMStringImpl* _nsURI, DOMStringImpl *_prefix,
1934 DOMStringImpl *_name, bool readonly, bool /*lookupHTML*/, int *pExceptioncode)
1936 /*kDebug() << "DocumentImpl::getId( type: " << _type << ", uri: " << DOMString(_nsURI).string()
1937 << ", prefix: " << DOMString(_prefix).string() << ", name: " << DOMString(_name).string()
1938 << ", readonly: " << readonly
1939 << ", lookupHTML: " << lookupHTML
1940 << ", exceptions: " << (pExceptioncode ? "yes" : "no")
1941 << " )" << endl;*/
1943 if(!_name) return 0;
1944 IdNameMapping *map;
1945 IdLookupFunction lookup;
1947 switch (_type) {
1948 case NodeImpl::ElementId:
1949 map = m_elementMap;
1950 lookup = getTagID;
1951 break;
1952 case NodeImpl::AttributeId:
1953 map = m_attrMap;
1954 lookup = getAttrID;
1955 break;
1956 case NodeImpl::NamespaceId:
1957 if( strcasecmp(_name, XHTML_NAMESPACE) == 0)
1958 return xhtmlNamespace;
1959 if( _name->l == 0)
1960 return emptyNamespace;
1961 // defaultNamespace handled by "if (!_name) return 0"
1962 map = m_namespaceMap;
1963 lookup= 0;
1964 break;
1965 default:
1966 return 0;
1968 // Names and attributes with ""
1969 if (_name->l == 0) return 0;
1971 NodeImpl::Id id, nsid;
1972 id = nsid = 0;
1973 const QString n = QString::fromRawData(_name->s, _name->l);
1974 bool cs = true; // case sensitive
1975 if (_type != NodeImpl::NamespaceId) {
1976 if (_nsURI)
1977 nsid = getId( NodeImpl::NamespaceId, 0, 0, _nsURI, false, false, 0 );
1979 // for attributes empty and default namespaces are the same
1980 if (_type == NodeImpl::AttributeId && nsid == emptyNamespace)
1981 nsid = defaultNamespace;
1983 // Each document maintains a mapping of tag name -> id for every tag name encountered
1984 // in the document.
1985 cs = (htmlMode() == XHtml) || (_nsURI && _type != NodeImpl::AttributeId);
1987 // First see if it's a HTML element name
1988 // xhtml is lower case - case sensitive, easy to implement
1989 if ( cs && (id = lookup(n.toAscii().constData(), _name->l)) ) {
1990 map->addAlias(_prefix, _name, cs, id);
1991 return makeId(nsid, id);
1993 // compatibility: upper case - case insensitive
1994 if ( !cs && (id = lookup(n.toLower().toAscii().constData(), _name->l )) ) {
1995 map->addAlias(_prefix, _name, cs, id);
1996 return makeId(nsid, id);
2000 // Look in the names array for the name
2001 // compatibility mode has to lookup upper case
2002 QString name = cs ? n : n.toUpper();
2004 if (!_nsURI) {
2005 id = (NodeImpl::Id)(long)map->ids.value( name );
2006 if (!id && _type != NodeImpl::NamespaceId) {
2007 id = (NodeImpl::Id)(long)map->ids.value( "aliases: " + name );
2009 } else {
2010 id = (NodeImpl::Id)(long)map->ids.value( name );
2011 if (!readonly && id && _prefix && _prefix->l) {
2012 // we were called in registration mode... check if the alias exists
2013 const QString px = QString::fromRawData( _prefix->s, _prefix->l );
2014 QString qn("aliases: " + (cs ? px : px.toUpper()) + ":" + name);
2015 if (!map->ids.contains( qn )) {
2016 map->ids.insert( qn, (void*)id );
2021 if (id) return makeId(nsid, id);
2023 // unknown
2024 if (readonly) return 0;
2026 if ( pExceptioncode && _type != NodeImpl::NamespaceId && !Element::khtmlValidQualifiedName(_name)) {
2027 *pExceptioncode = DOMException::INVALID_CHARACTER_ERR;
2028 return 0;
2031 // Name not found, so let's add it
2032 NodeImpl::Id cid = map->count++ + map->idStart;
2033 map->names.insert( cid, _name );
2034 _name->ref();
2036 map->ids.insert( name, (void*)cid );
2038 // and register an alias if needed for DOM1 methods compatibility
2039 map->addAlias(_prefix, _name, cs, cid);
2041 return makeId(nsid, cid);
2044 NodeImpl::Id DocumentImpl::getId( NodeImpl::IdType _type, DOMStringImpl *_nodeName, bool readonly, bool lookupHTML, int *pExceptioncode)
2046 return getId(_type, 0, 0, _nodeName, readonly, lookupHTML, pExceptioncode);
2049 DOMString DocumentImpl::getName( NodeImpl::IdType _type, NodeImpl::Id _id ) const
2051 IdNameMapping *map;
2052 NameLookupFunction lookup;
2053 bool hasNS = (namespacePart(_id) != defaultNamespace);
2054 switch (_type) {
2055 case NodeImpl::ElementId:
2056 map = m_elementMap;
2057 lookup = getTagName;
2058 break;
2059 case NodeImpl::AttributeId:
2060 map = m_attrMap;
2061 lookup = getAttrName;
2062 break;
2063 case NodeImpl::NamespaceId:
2064 if( _id == xhtmlNamespace )
2065 return XHTML_NAMESPACE;
2066 else
2067 if( _id == emptyNamespace )
2068 return DOMString("");
2069 else
2070 if ( _id == defaultNamespace )
2071 return DOMString();
2072 map = m_namespaceMap;
2073 lookup = 0;
2074 break;
2075 default:
2076 return DOMString();;
2078 _id = localNamePart(_id) ;
2079 if (_id >= map->idStart) {
2080 return map->names[_id];
2082 else if (lookup) {
2083 // ### put them in a cache
2084 if (hasNS)
2085 return DOMString(lookup(_id)).lower();
2086 else
2087 return lookup(_id);
2088 } else
2089 return DOMString();
2092 // This method is called whenever a top-level stylesheet has finished loading.
2093 void DocumentImpl::styleSheetLoaded()
2095 // Make sure we knew this sheet was pending, and that our count isn't out of sync.
2096 assert(m_pendingStylesheets > 0);
2098 m_pendingStylesheets--;
2099 updateStyleSelector();
2102 void DocumentImpl::addPendingSheet()
2104 m_pendingStylesheets++;
2107 DOMString DocumentImpl::selectedStylesheetSet() const
2109 if (!view()) return DOMString();
2111 return view()->part()->d->m_sheetUsed;
2114 void DocumentImpl::setSelectedStylesheetSet(const DOMString& s)
2116 // this code is evil
2117 if (view() && view()->part()->d->m_sheetUsed != s.string()) {
2118 view()->part()->d->m_sheetUsed = s.string();
2119 updateStyleSelector();
2123 void DocumentImpl::addStyleSheet(StyleSheetImpl *sheet, int *exceptioncode)
2125 int excode = 0;
2127 if (!m_addedStyleSheets) {
2128 m_addedStyleSheets = new StyleSheetListImpl;
2129 m_addedStyleSheets->ref();
2132 m_addedStyleSheets->add(sheet);
2133 if (sheet->isCSSStyleSheet()) updateStyleSelector();
2135 if (exceptioncode) *exceptioncode = excode;
2138 void DocumentImpl::removeStyleSheet(StyleSheetImpl *sheet, int *exceptioncode)
2140 int excode = 0;
2141 bool removed = false;
2142 bool is_css = sheet->isCSSStyleSheet();
2144 if (m_addedStyleSheets) {
2145 bool in_main_list = !sheet->hasOneRef();
2146 removed = m_addedStyleSheets->styleSheets.removeAll(sheet);
2147 sheet->deref();
2149 if (m_addedStyleSheets->styleSheets.count() == 0) {
2150 bool reset = m_addedStyleSheets->hasOneRef();
2151 m_addedStyleSheets->deref();
2152 if (reset) m_addedStyleSheets = 0;
2155 // remove from main list, too
2156 if (in_main_list) m_styleSheets->remove(sheet);
2159 if (removed) {
2160 if (is_css) updateStyleSelector();
2161 } else
2162 excode = DOMException::NOT_FOUND_ERR;
2164 if (exceptioncode) *exceptioncode = excode;
2167 void DocumentImpl::updateStyleSelector(bool shallow)
2169 // kDebug() << "PENDING " << m_pendingStylesheets;
2171 // Don't bother updating, since we haven't loaded all our style info yet.
2172 if (m_pendingStylesheets > 0)
2173 return;
2175 if (shallow)
2176 rebuildStyleSelector();
2177 else
2178 recalcStyleSelector();
2179 recalcStyle(Force);
2180 #if 0
2182 m_styleSelectorDirty = true;
2183 #endif
2184 if ( renderer() )
2185 renderer()->setNeedsLayoutAndMinMaxRecalc();
2188 bool DocumentImpl::readyForLayout() const
2190 return renderer() && haveStylesheetsLoaded() && (!isHTMLDocument() || (body() && body()->renderer()));
2193 void DocumentImpl::recalcStyleSelector()
2195 if ( !m_render || !attached() ) return;
2197 assert(m_pendingStylesheets==0);
2199 QList<StyleSheetImpl*> oldStyleSheets = m_styleSheets->styleSheets;
2200 m_styleSheets->styleSheets.clear();
2201 QString sheetUsed = view() ? view()->part()->d->m_sheetUsed.replace("&&", "&") : QString();
2202 bool autoselect = sheetUsed.isEmpty();
2203 if (autoselect && !m_preferredStylesheetSet.isEmpty())
2204 sheetUsed = m_preferredStylesheetSet.string();
2205 NodeImpl *n;
2206 for (int i=0 ; i<2 ; i++) {
2207 m_availableSheets.clear();
2208 m_availableSheets << i18n("Basic Page Style");
2209 bool canResetSheet = false;
2211 for (n = this; n; n = n->traverseNextNode()) {
2212 StyleSheetImpl *sheet = 0;
2214 if (n->nodeType() == Node::PROCESSING_INSTRUCTION_NODE)
2216 // Processing instruction (XML documents only)
2217 ProcessingInstructionImpl* pi = static_cast<ProcessingInstructionImpl*>(n);
2218 sheet = pi->sheet();
2219 if (!sheet && !pi->localHref().isEmpty())
2221 // Processing instruction with reference to an element in this document - e.g.
2222 // <?xml-stylesheet href="#mystyle">, with the element
2223 // <foo id="mystyle">heading { color: red; }</foo> at some location in
2224 // the document
2225 ElementImpl* elem = getElementById(pi->localHref());
2226 if (elem) {
2227 DOMString sheetText("");
2228 NodeImpl *c;
2229 for (c = elem->firstChild(); c; c = c->nextSibling()) {
2230 if (c->nodeType() == Node::TEXT_NODE || c->nodeType() == Node::CDATA_SECTION_NODE)
2231 sheetText += c->nodeValue();
2234 CSSStyleSheetImpl *cssSheet = new CSSStyleSheetImpl(this);
2235 cssSheet->parseString(sheetText);
2236 pi->setStyleSheet(cssSheet);
2237 sheet = cssSheet;
2242 else if (n->isHTMLElement() && ( n->id() == ID_LINK || n->id() == ID_STYLE) ) {
2243 QString title;
2244 if ( n->id() == ID_LINK ) {
2245 HTMLLinkElementImpl* l = static_cast<HTMLLinkElementImpl*>(n);
2246 if (l->isCSSStyleSheet()) {
2247 sheet = l->sheet();
2249 if (sheet || l->isLoading() || l->isAlternate() )
2250 title = l->getAttribute(ATTR_TITLE).string();
2252 if ((autoselect || title != sheetUsed) && l->isDisabled()) {
2253 sheet = 0;
2254 } else if (!title.isEmpty() && !l->isAlternate() && sheetUsed.isEmpty()) {
2255 sheetUsed = title;
2256 l->setDisabled(false);
2260 else {
2261 // <STYLE> element
2262 HTMLStyleElementImpl* s = static_cast<HTMLStyleElementImpl*>(n);
2263 if (!s->isLoading()) {
2264 sheet = s->sheet();
2265 if (sheet) title = s->getAttribute(ATTR_TITLE).string();
2267 if (!title.isEmpty() && sheetUsed.isEmpty())
2268 sheetUsed = title;
2271 if ( !title.isEmpty() ) {
2272 if ( title != sheetUsed )
2273 sheet = 0; // don't use it
2275 title = title.replace('&', "&&");
2277 if ( !m_availableSheets.contains( title ) )
2278 m_availableSheets.append( title );
2281 else if (n->isHTMLElement() && n->id() == ID_BODY) {
2282 // <BODY> element (doesn't contain styles as such but vlink="..." and friends
2283 // are treated as style declarations)
2284 sheet = static_cast<HTMLBodyElementImpl*>(n)->sheet();
2287 if (sheet) {
2288 sheet->ref();
2289 m_styleSheets->styleSheets.append(sheet);
2292 // For HTML documents, stylesheets are not allowed within/after the <BODY> tag. So we
2293 // can stop searching here.
2294 if (isHTMLDocument() && n->id() == ID_BODY) {
2295 canResetSheet = !canResetSheet;
2296 break;
2300 // we're done if we don't select an alternative sheet
2301 // or we found the sheet we selected
2302 if (sheetUsed.isEmpty() ||
2303 (!canResetSheet && tokenizer()) ||
2304 m_availableSheets.contains(sheetUsed)) {
2305 break;
2308 // the alternative sheet we used doesn't exist anymore
2309 // so try from scratch again
2310 if (view())
2311 view()->part()->d->m_sheetUsed.clear();
2312 if (!m_preferredStylesheetSet.isEmpty() && !(sheetUsed == m_preferredStylesheetSet))
2313 sheetUsed = m_preferredStylesheetSet.string();
2314 else
2315 sheetUsed.clear();
2316 autoselect = true;
2319 // Include programmatically added style sheets
2320 if (m_addedStyleSheets) {
2321 foreach (StyleSheetImpl* sh, m_addedStyleSheets->styleSheets) {
2322 if (sh->isCSSStyleSheet() && !sh->disabled())
2323 m_styleSheets->add(sh);
2327 // De-reference all the stylesheets in the old list
2328 foreach ( StyleSheetImpl* sh, oldStyleSheets)
2329 sh->deref();
2331 rebuildStyleSelector();
2334 void DocumentImpl::rebuildStyleSelector()
2336 // Create a new style selector
2337 delete m_styleSelector;
2338 QString usersheet = m_usersheet;
2339 if ( m_view && m_view->mediaType() == "print" )
2340 usersheet += m_printSheet;
2341 m_styleSelector = new CSSStyleSelector( this, usersheet, m_styleSheets, m_url,
2342 !inCompatMode() );
2344 m_styleSelectorDirty = false;
2347 void DocumentImpl::setBaseURL(const KUrl& _baseURL)
2349 m_baseURL = _baseURL;
2350 if (m_elemSheet)
2351 m_elemSheet->setHref( baseURL().url() );
2354 void DocumentImpl::setHoverNode(NodeImpl *newHoverNode)
2356 NodeImpl* oldHoverNode = m_hoverNode;
2357 if (newHoverNode ) newHoverNode->ref();
2358 m_hoverNode = newHoverNode;
2359 if ( oldHoverNode ) oldHoverNode->deref();
2362 void DocumentImpl::setActiveNode(NodeImpl* newActiveNode)
2364 NodeImpl* oldActiveNode = m_activeNode;
2365 if (newActiveNode ) newActiveNode->ref();
2366 m_activeNode = newActiveNode;
2367 if ( oldActiveNode ) oldActiveNode->deref();
2370 void DocumentImpl::quietResetFocus()
2372 assert(m_focusNode != this);
2373 if (m_focusNode) {
2374 if (m_focusNode->active())
2375 setActiveNode(0);
2377 m_focusNode->setFocus(false);
2378 m_focusNode->deref();
2380 m_focusNode = 0;
2382 //We're blurring. Better clear the Qt focus/give it to the view...
2383 if (view())
2384 view()->setFocus();
2387 void DocumentImpl::setFocusNode(NodeImpl *newFocusNode)
2389 // don't process focus changes while detaching
2390 if( !m_render ) return;
2392 // Make sure newFocusNode is actually in this document
2393 if (newFocusNode && (newFocusNode->getDocument() != this))
2394 return;
2396 if (m_focusNode != newFocusNode) {
2397 NodeImpl *oldFocusNode = m_focusNode;
2398 // Set focus on the new node
2399 m_focusNode = newFocusNode;
2400 // Remove focus from the existing focus node (if any)
2401 if (oldFocusNode) {
2402 if (oldFocusNode->active())
2403 oldFocusNode->setActive(false);
2405 oldFocusNode->setFocus(false);
2407 oldFocusNode->dispatchHTMLEvent(EventImpl::BLUR_EVENT,false,false);
2408 oldFocusNode->dispatchUIEvent(EventImpl::DOMFOCUSOUT_EVENT);
2410 if ((oldFocusNode == this) && oldFocusNode->hasOneRef()) {
2411 oldFocusNode->deref(); // deletes this
2412 return;
2414 else {
2415 oldFocusNode->deref();
2419 if (m_focusNode) {
2420 m_focusNode->ref();
2421 m_focusNode->dispatchHTMLEvent(EventImpl::FOCUS_EVENT,false,false);
2422 if (m_focusNode != newFocusNode) return;
2423 m_focusNode->dispatchUIEvent(EventImpl::DOMFOCUSIN_EVENT);
2424 if (m_focusNode != newFocusNode) return;
2425 m_focusNode->setFocus();
2426 if (m_focusNode != newFocusNode) return;
2428 // eww, I suck. set the qt focus correctly
2429 // ### find a better place in the code for this
2430 if (view()) {
2431 if (!m_focusNode->renderer() || !m_focusNode->renderer()->isWidget())
2432 view()->setFocus();
2433 else if (static_cast<RenderWidget*>(m_focusNode->renderer())->widget())
2435 if (view()->isVisible())
2436 static_cast<RenderWidget*>(m_focusNode->renderer())->widget()->setFocus();
2439 } else {
2440 //We're blurring. Better clear the Qt focus/give it to the view...
2441 if (view())
2442 view()->setFocus();
2445 updateRendering();
2449 void DocumentImpl::setCSSTarget(NodeImpl* n)
2451 if (n == m_cssTarget)
2452 return;
2454 if (m_cssTarget) {
2455 m_cssTarget->setChanged();
2456 m_cssTarget->deref();
2458 m_cssTarget = n;
2459 if (n) {
2460 n->setChanged();
2461 n->ref();
2465 void DocumentImpl::attachNodeIterator(NodeIteratorImpl *ni)
2467 m_nodeIterators.append(ni);
2470 void DocumentImpl::detachNodeIterator(NodeIteratorImpl *ni)
2472 int i = m_nodeIterators.indexOf(ni);
2473 if (i != -1)
2474 m_nodeIterators.removeAt(i);
2477 void DocumentImpl::notifyBeforeNodeRemoval(NodeImpl *n)
2479 QListIterator<NodeIteratorImpl*> it(m_nodeIterators);
2480 while (it.hasNext())
2481 it.next()->notifyBeforeNodeRemoval(n);
2484 bool DocumentImpl::isURLAllowed(const QString& url) const
2486 KHTMLPart *thisPart = part();
2488 KUrl newURL(completeURL(url));
2489 newURL.setRef(QString());
2491 if (KHTMLGlobal::defaultHTMLSettings()->isAdFiltered( newURL.url() ))
2492 return false;
2494 // Prohibit non-file URLs if we are asked to.
2495 if (!thisPart || thisPart->onlyLocalReferences() && newURL.protocol() != "file" && newURL.protocol() != "data")
2496 return false;
2498 // do we allow this suburl ?
2499 if ( !kapp || (newURL.protocol() != "javascript" && !KAuthorized::authorizeUrlAction("redirect", thisPart->url(), newURL)) )
2500 return false;
2502 // We allow one level of self-reference because some sites depend on that.
2503 // But we don't allow more than one.
2504 bool foundSelfReference = false;
2505 for (KHTMLPart *part = thisPart; part; part = part->parentPart()) {
2506 KUrl partURL = part->url();
2507 partURL.setRef(QString());
2508 if (partURL == newURL) {
2509 if (foundSelfReference)
2510 return false;
2511 foundSelfReference = true;
2515 return true;
2518 void DocumentImpl::setDesignMode(bool b)
2520 if (part())
2521 part()->setEditable(b);
2524 bool DocumentImpl::designMode() const
2526 return part() ? part()->isEditable() : false;
2529 EventImpl *DocumentImpl::createEvent(const DOMString &eventType, int &exceptioncode)
2531 if (eventType == "UIEvents" || eventType == "UIEvent")
2532 return new UIEventImpl();
2533 else if (eventType == "MouseEvents" || eventType == "MouseEvent")
2534 return new MouseEventImpl();
2535 else if (eventType == "TextEvent")
2536 return new TextEventImpl();
2537 else if (eventType == "KeyboardEvent")
2538 return new KeyboardEventImpl();
2539 else if (eventType == "MutationEvents" || eventType == "MutationEvent")
2540 return new MutationEventImpl();
2541 else if (eventType == "HTMLEvents" || eventType == "Events" ||
2542 eventType == "HTMLEvent" || eventType == "Event")
2543 return new EventImpl();
2544 else {
2545 exceptioncode = DOMException::NOT_SUPPORTED_ERR;
2546 return 0;
2550 CSSStyleDeclarationImpl *DocumentImpl::getOverrideStyle(ElementImpl* /*elt*/, DOMStringImpl* /*pseudoElt*/)
2552 return 0; // ###
2555 void DocumentImpl::abort()
2557 if (m_inSyncLoad) {
2558 assert(m_inSyncLoad->isRunning());
2559 m_inSyncLoad->exit();
2562 if (m_loadingXMLDoc)
2563 m_loadingXMLDoc->deref(this);
2564 m_loadingXMLDoc = 0;
2567 void DocumentImpl::load(const DOMString &uri)
2569 if (m_inSyncLoad) {
2570 assert(m_inSyncLoad->isRunning());
2571 m_inSyncLoad->exit();
2574 m_hadLoadError = false;
2575 if (m_loadingXMLDoc)
2576 m_loadingXMLDoc->deref(this);
2578 // Use the document loader to retrieve the XML file. We use CachedCSSStyleSheet because
2579 // this is an easy way to retrieve an arbitrary text file... it is not specific to
2580 // stylesheets.
2582 // ### Note: By loading the XML document this way we do not get the proper decoding
2583 // of the data retrieved from the server based on the character set, as happens with
2584 // HTML files. Need to look into a way of using the decoder in CachedCSSStyleSheet.
2585 m_docLoading = true;
2586 m_loadingXMLDoc = m_docLoader->requestStyleSheet(uri.string(),QString(),"text/xml");
2588 if (!m_loadingXMLDoc) {
2589 m_docLoading = false;
2590 return;
2593 m_loadingXMLDoc->ref(this);
2595 if (!m_async && m_docLoading) {
2596 assert(!m_inSyncLoad);
2597 m_inSyncLoad = new QEventLoop();
2598 m_inSyncLoad->exec();
2599 // returning from event loop:
2600 assert(!m_inSyncLoad->isRunning());
2601 delete m_inSyncLoad;
2602 m_inSyncLoad = 0;
2606 void DocumentImpl::loadXML(const DOMString &source)
2608 open(false);
2609 write(source);
2610 finishParsing();
2611 close();
2612 dispatchHTMLEvent(EventImpl::LOAD_EVENT,false,false);
2615 void DocumentImpl::setStyleSheet(const DOM::DOMString &url, const DOM::DOMString &sheet, const DOM::DOMString &/*charset*/, const DOM::DOMString &mimetype)
2617 if (!m_hadLoadError) {
2618 m_url = url.string();
2619 loadXML(khtml::isAcceptableCSSMimetype(mimetype) ? sheet : "");
2622 m_docLoading = false;
2623 if (m_inSyncLoad) {
2624 assert(m_inSyncLoad->isRunning());
2625 m_inSyncLoad->exit();
2628 assert(m_loadingXMLDoc != 0);
2629 m_loadingXMLDoc->deref(this);
2630 m_loadingXMLDoc = 0;
2633 void DocumentImpl::error(int err, const QString &text)
2635 m_docLoading = false;
2636 if (m_inSyncLoad) {
2637 assert(m_inSyncLoad->isRunning());
2638 m_inSyncLoad->exit();
2641 m_hadLoadError = true;
2643 int exceptioncode = 0;
2644 EventImpl *evt = new EventImpl(EventImpl::ERROR_EVENT,false,false);
2645 if (err != 0)
2646 evt->setMessage(KIO::buildErrorString(err,text));
2647 else
2648 evt->setMessage(text);
2649 evt->ref();
2650 dispatchEvent(evt,exceptioncode,true);
2651 evt->deref();
2653 assert(m_loadingXMLDoc != 0);
2654 m_loadingXMLDoc->deref(this);
2655 m_loadingXMLDoc = 0;
2658 void DocumentImpl::defaultEventHandler(EventImpl *evt)
2660 // if any html event listeners are registered on the window, then dispatch them here
2661 if (m_windowEventListeners.listeners && !evt->propagationStopped()) {
2663 QList<RegisteredEventListener>::iterator it;
2665 //Grab a copy in case of clear
2666 QList<RegisteredEventListener> listeners = *m_windowEventListeners.listeners;
2667 Event ev(evt);
2668 for (it = listeners.begin(); it != listeners.end(); ++it) {
2669 //Check to make sure it didn't get removed. KDE4: use Java-style iterators
2670 if (!m_windowEventListeners.stillContainsListener(*it))
2671 continue;
2673 if ((*it).eventName == evt->name()) {
2674 // currentTarget must be 0 in khtml for kjs_events to set "this" correctly.
2675 // (this is how we identify events dispatched to the window, like window.onmousedown)
2676 // ## currentTarget is unimplemented in IE, and is "window" in Mozilla (how? not a DOM node)
2677 evt->setCurrentTarget(0);
2678 (*it).listener->handleEvent(ev);
2682 if ( evt->id() == EventImpl::KHTML_CONTENTLOADED_EVENT && !evt->propagationStopped() && !evt->defaultPrevented() )
2683 contentLoaded();
2686 void DocumentImpl::setHTMLWindowEventListener(EventName id, EventListener *listener)
2688 m_windowEventListeners.setHTMLEventListener(id, listener);
2691 void DocumentImpl::setHTMLWindowEventListener(unsigned id, EventListener *listener)
2693 m_windowEventListeners.setHTMLEventListener(EventName::fromId(id), listener);
2696 EventListener *DocumentImpl::getHTMLWindowEventListener(EventName id)
2698 return m_windowEventListeners.getHTMLEventListener(id);
2701 EventListener *DocumentImpl::getHTMLWindowEventListener(unsigned id)
2703 return m_windowEventListeners.getHTMLEventListener(EventName::fromId(id));
2706 void DocumentImpl::addWindowEventListener(EventName id, EventListener *listener, const bool useCapture)
2708 m_windowEventListeners.addEventListener(id, listener, useCapture);
2711 void DocumentImpl::removeWindowEventListener(EventName id, EventListener *listener, bool useCapture)
2713 m_windowEventListeners.removeEventListener(id, listener, useCapture);
2716 bool DocumentImpl::hasWindowEventListener(EventName id)
2718 return m_windowEventListeners.hasEventListener(id);
2721 EventListener *DocumentImpl::createHTMLEventListener(const QString& code, const QString& name, NodeImpl* node)
2723 return part() ? part()->createHTMLEventListener(code, name, node) : 0;
2726 void DocumentImpl::dispatchImageLoadEventSoon(HTMLImageElementImpl *image)
2728 m_imageLoadEventDispatchSoonList.append(image);
2729 if (!m_imageLoadEventTimer) {
2730 m_imageLoadEventTimer = startTimer(0);
2734 void DocumentImpl::removeImage(HTMLImageElementImpl *image)
2736 // Remove instances of this image from both lists.
2737 m_imageLoadEventDispatchSoonList.removeAll(image);
2738 m_imageLoadEventDispatchingList.removeAll(image);
2739 if (m_imageLoadEventDispatchSoonList.isEmpty() && m_imageLoadEventTimer) {
2740 killTimer(m_imageLoadEventTimer);
2741 m_imageLoadEventTimer = 0;
2745 void DocumentImpl::dispatchImageLoadEventsNow()
2747 // need to avoid re-entering this function; if new dispatches are
2748 // scheduled before the parent finishes processing the list, they
2749 // will set a timer and eventually be processed
2750 if (!m_imageLoadEventDispatchingList.isEmpty()) {
2751 return;
2754 if (m_imageLoadEventTimer) {
2755 killTimer(m_imageLoadEventTimer);
2756 m_imageLoadEventTimer = 0;
2759 m_imageLoadEventDispatchingList = m_imageLoadEventDispatchSoonList;
2760 m_imageLoadEventDispatchSoonList.clear();
2761 for (QLinkedListIterator<HTMLImageElementImpl*> it(m_imageLoadEventDispatchingList); it.hasNext(); )
2762 it.next()->dispatchLoadEvent();
2763 m_imageLoadEventDispatchingList.clear();
2766 void DocumentImpl::timerEvent(QTimerEvent *)
2768 dispatchImageLoadEventsNow();
2771 /*void DocumentImpl::setDecoderCodec(const QTextCodec *codec)
2773 m_decoderMibEnum = codec->mibEnum();
2776 ElementImpl *DocumentImpl::ownerElement() const
2778 KHTMLPart *childPart = part();
2779 if (!childPart)
2780 return 0;
2781 ChildFrame *childFrame = childPart->d->m_frame;
2782 if (!childFrame)
2783 return 0;
2784 ElementImpl *el = childFrame->m_partContainerElement;
2785 return el;
2788 DOMString DocumentImpl::domain() const
2790 if ( m_domain.isEmpty() ) // not set yet (we set it on demand to save time and space)
2791 m_domain = URL().host(); // Initially set to the host
2792 return m_domain;
2795 void DocumentImpl::setDomain(const DOMString &newDomain)
2797 if ( m_domain.isEmpty() ) // not set yet (we set it on demand to save time and space)
2798 m_domain = URL().host().toLower(); // Initially set to the host
2800 if ( m_domain.isEmpty() /*&& part() && part()->openedByJS()*/ )
2801 m_domain = newDomain.lower();
2803 // Both NS and IE specify that changing the domain is only allowed when
2804 // the new domain is a suffix of the old domain.
2805 int oldLength = m_domain.length();
2806 int newLength = newDomain.length();
2807 if ( newLength < oldLength ) // e.g. newDomain=kde.org (7) and m_domain=www.kde.org (11)
2809 DOMString test = m_domain.copy();
2810 DOMString reference = newDomain.lower();
2811 if ( test[oldLength - newLength - 1] == '.' ) // Check that it's a subdomain, not e.g. "de.org"
2813 test.remove( 0, oldLength - newLength ); // now test is "kde.org" from m_domain
2814 if ( test == reference ) // and we check that it's the same thing as newDomain
2815 m_domain = reference;
2820 DOMString DocumentImpl::toString() const
2822 DOMString result;
2824 for (NodeImpl *child = firstChild(); child != NULL; child = child->nextSibling()) {
2825 result += child->toString();
2828 return result;
2831 void DOM::DocumentImpl::setRestoreState( const QStringList &s)
2833 m_state = s;
2834 m_stateRestorePos = 0;
2837 KHTMLView* DOM::DocumentImpl::view() const
2839 return m_view;
2842 KHTMLPart* DOM::DocumentImpl::part() const
2844 // ### TODO: make this independent from a KHTMLView one day.
2845 return view() ? view()->part() : 0;
2848 NodeListImpl::Cache* DOM::DocumentImpl::acquireCachedNodeListInfo(
2849 NodeListImpl::CacheFactory* factory, NodeImpl* base, int type)
2851 //### might want to flush the dict when the version number
2852 //changes
2853 NodeListImpl::CacheKey key(base, type);
2855 //Check to see if we have this sort of item cached.
2856 NodeListImpl::Cache* cached =
2857 (type == NodeListImpl::UNCACHEABLE) ? 0 : m_nodeListCache.value(key.hash());
2859 if (cached) {
2860 if (cached->key == key) {
2861 cached->ref(); //Add the nodelist's reference
2862 return cached;
2863 } else {
2864 //Conflict. Drop our reference to the old item.
2865 cached->deref();
2869 //Nothing to reuse, make a new item.
2870 NodeListImpl::Cache* newInfo = factory();
2871 newInfo->key = key;
2872 newInfo->clear(this);
2873 newInfo->ref(); //Add the nodelist's reference
2875 if (type != NodeListImpl::UNCACHEABLE) {
2876 newInfo->ref(); //Add the cache's reference
2877 m_nodeListCache.insert(key.hash(), newInfo);
2880 return newInfo;
2883 void DOM::DocumentImpl::releaseCachedNodeListInfo(NodeListImpl::Cache* entry)
2885 entry->deref();
2888 // ----------------------------------------------------------------------------
2889 // Support for Javascript execCommand, and related methods
2891 JSEditor *DocumentImpl::jsEditor()
2893 if (!m_jsEditor)
2894 m_jsEditor = new JSEditor(this);
2895 return m_jsEditor;
2898 bool DocumentImpl::execCommand(const DOMString &command, bool userInterface, const DOMString &value)
2900 return jsEditor()->execCommand(jsEditor()->commandImp(command), userInterface, value);
2903 bool DocumentImpl::queryCommandEnabled(const DOMString &command)
2905 return jsEditor()->queryCommandEnabled(jsEditor()->commandImp(command));
2908 bool DocumentImpl::queryCommandIndeterm(const DOMString &command)
2910 return jsEditor()->queryCommandIndeterm(jsEditor()->commandImp(command));
2913 bool DocumentImpl::queryCommandState(const DOMString &command)
2915 return jsEditor()->queryCommandState(jsEditor()->commandImp(command));
2918 bool DocumentImpl::queryCommandSupported(const DOMString &command)
2920 return jsEditor()->queryCommandSupported(jsEditor()->commandImp(command));
2923 DOMString DocumentImpl::queryCommandValue(const DOMString &command)
2925 return jsEditor()->queryCommandValue(jsEditor()->commandImp(command));
2928 // ----------------------------------------------------------------------------
2930 DocumentFragmentImpl::DocumentFragmentImpl(DocumentImpl *doc) : NodeBaseImpl(doc)
2934 DocumentFragmentImpl::DocumentFragmentImpl(const DocumentFragmentImpl &other)
2935 : NodeBaseImpl(other)
2939 DOMString DocumentFragmentImpl::nodeName() const
2941 return "#document-fragment";
2944 unsigned short DocumentFragmentImpl::nodeType() const
2946 return Node::DOCUMENT_FRAGMENT_NODE;
2949 // DOM Section 1.1.1
2950 bool DocumentFragmentImpl::childTypeAllowed( unsigned short type )
2952 switch (type) {
2953 case Node::ELEMENT_NODE:
2954 case Node::PROCESSING_INSTRUCTION_NODE:
2955 case Node::COMMENT_NODE:
2956 case Node::TEXT_NODE:
2957 case Node::CDATA_SECTION_NODE:
2958 case Node::ENTITY_REFERENCE_NODE:
2959 return true;
2960 break;
2961 default:
2962 return false;
2966 DOMString DocumentFragmentImpl::toString() const
2968 DOMString result;
2970 for (NodeImpl *child = firstChild(); child != NULL; child = child->nextSibling()) {
2971 if (child->nodeType() == Node::COMMENT_NODE || child->nodeType() == Node::PROCESSING_INSTRUCTION_NODE)
2972 continue;
2973 result += child->toString();
2976 return result;
2979 NodeImpl *DocumentFragmentImpl::cloneNode ( bool deep )
2981 DocumentFragmentImpl *clone = new DocumentFragmentImpl( docPtr() );
2982 if (deep)
2983 cloneChildNodes(clone);
2984 return clone;
2988 // ----------------------------------------------------------------------------
2990 DocumentTypeImpl::DocumentTypeImpl(DOMImplementationImpl *implementation, DocumentImpl *doc,
2991 const DOMString &qualifiedName, const DOMString &publicId,
2992 const DOMString &systemId)
2993 : NodeImpl(doc), m_implementation(implementation),
2994 m_qualifiedName(qualifiedName), m_publicId(publicId), m_systemId(systemId)
2996 m_implementation->ref();
2998 m_entities = 0;
2999 m_notations = 0;
3001 // if doc is 0, it is not attached to a document and / or
3002 // therefore does not provide entities or notations. (DOM Level 3)
3005 DocumentTypeImpl::~DocumentTypeImpl()
3007 m_implementation->deref();
3008 if (m_entities)
3009 m_entities->deref();
3010 if (m_notations)
3011 m_notations->deref();
3014 DOMString DocumentTypeImpl::toString() const
3016 DOMString result = "<!DOCTYPE ";
3017 result += m_qualifiedName;
3018 if (!m_publicId.isEmpty()) {
3019 result += " PUBLIC \"";
3020 result += m_publicId;
3021 result += "\" \"";
3022 result += m_systemId;
3023 result += "\"";
3024 } else if (!m_systemId.isEmpty()) {
3025 result += " SYSTEM \"";
3026 result += m_systemId;
3027 result += "\"";
3030 if (!m_subset.isEmpty()) {
3031 result += " [";
3032 result += m_subset;
3033 result += "]";
3036 result += ">";
3038 return result;
3041 DOMString DocumentTypeImpl::nodeName() const
3043 return name();
3046 unsigned short DocumentTypeImpl::nodeType() const
3048 return Node::DOCUMENT_TYPE_NODE;
3051 // DOM Section 1.1.1
3052 bool DocumentTypeImpl::childTypeAllowed( unsigned short /*type*/ )
3054 return false;
3057 NodeImpl *DocumentTypeImpl::cloneNode ( bool /*deep*/ )
3059 DocumentTypeImpl *clone = new DocumentTypeImpl(implementation(),
3061 name(), publicId(),
3062 systemId());
3063 // ### copy entities etc.
3064 return clone;
3067 NamedNodeMapImpl * DocumentTypeImpl::entities() const
3069 if ( !m_entities ) {
3070 m_entities = new GenericRONamedNodeMapImpl( docPtr() );
3071 m_entities->ref();
3073 return m_entities;
3076 NamedNodeMapImpl * DocumentTypeImpl::notations() const
3078 if ( !m_notations ) {
3079 m_notations = new GenericRONamedNodeMapImpl( docPtr() );
3080 m_notations->ref();
3082 return m_notations;
3085 #include "dom_docimpl.moc"