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/dom_exception.h"
27 #include "xml/dom_textimpl.h"
28 #include "xml/dom_xmlimpl.h"
29 #include "xml/dom2_rangeimpl.h"
30 #include "xml/dom2_eventsimpl.h"
31 #include "xml/xml_tokenizer.h"
33 #include "xml_namespace_table.h"
35 #include "css/csshelper.h"
36 #include "css/cssstyleselector.h"
37 #include "css/css_stylesheetimpl.h"
38 #include "css/css_valueimpl.h"
39 #include "misc/htmlhashes.h"
40 #include "misc/helper.h"
41 #include "ecma/kjs_proxy.h"
42 #include "ecma/kjs_binding.h"
44 #include <qptrstack.h>
45 #include <qpaintdevicemetrics.h>
48 #include <kstaticdeleter.h>
50 #include "rendering/render_canvas.h"
51 #include "rendering/render_frames.h"
52 #include "rendering/render_image.h"
53 #include "rendering/render_object.h"
54 #include "render_arena.h"
56 #include "khtmlview.h"
57 #include "khtml_part.h"
59 #include <kglobalsettings.h>
60 #include <kstringhandler.h>
61 #include "khtml_settings.h"
62 #include "khtmlpart_p.h"
64 #include "html/html_baseimpl.h"
65 #include "html/html_blockimpl.h"
66 #include "html/html_canvasimpl.h"
67 #include "html/html_documentimpl.h"
68 #include "html/html_formimpl.h"
69 #include "html/html_headimpl.h"
70 #include "html/html_imageimpl.h"
71 #include "html/html_listimpl.h"
72 #include "html/html_miscimpl.h"
73 #include "html/html_tableimpl.h"
74 #include "html/html_objectimpl.h"
76 #include "cssvalues.h"
78 #include "editing/jsediting.h"
79 #include "editing/visible_position.h"
80 #include "editing/visible_text.h"
85 #include "xsl_stylesheetimpl.h"
86 #include "xslt_processorimpl.h"
90 #include "xbl/xbl_binding_manager.h"
91 using XBL::XBLBindingManager;
95 #include "KWQAccObjectCache.h"
96 #include "KWQLogging.h"
100 using namespace khtml;
102 // #define INSTRUMENT_LAYOUT_SCHEDULING 1
104 // This amount of time must have elapsed before we will even consider scheduling a layout without a delay.
105 // FIXME: For faster machines this value can really be lowered to 200. 250 is adequate, but a little high
107 const int cLayoutScheduleThreshold = 250;
109 DOMImplementationImpl *DOMImplementationImpl::m_instance = 0;
111 DOMImplementationImpl::DOMImplementationImpl()
115 DOMImplementationImpl::~DOMImplementationImpl()
119 bool DOMImplementationImpl::hasFeature ( const DOMString &feature, const DOMString &version )
121 QString lower = feature.string().lower();
122 if (lower == "core" || lower == "html" || lower == "xml")
123 return version.isEmpty() || version == "1.0" || version == "2.0";
127 || lower == "htmlevents"
128 || lower == "mouseevents"
129 || lower == "mutationevents"
131 || lower == "stylesheets"
132 || lower == "traversal"
133 || lower == "uievents"
135 return version.isEmpty() || version == "2.0";
139 DocumentTypeImpl *DOMImplementationImpl::createDocumentType( const DOMString &qualifiedName, const DOMString &publicId,
140 const DOMString &systemId, int &exceptioncode )
142 // Not mentioned in spec: throw NAMESPACE_ERR if no qualifiedName supplied
143 if (qualifiedName.isNull()) {
144 exceptioncode = DOMException::NAMESPACE_ERR;
148 // INVALID_CHARACTER_ERR: Raised if the specified qualified name contains an illegal character.
149 if (!Element::khtmlValidQualifiedName(qualifiedName)) {
150 exceptioncode = DOMException::INVALID_CHARACTER_ERR;
154 // NAMESPACE_ERR: Raised if the qualifiedName is malformed.
155 if (Element::khtmlMalformedQualifiedName(qualifiedName)) {
156 exceptioncode = DOMException::NAMESPACE_ERR;
160 return new DocumentTypeImpl(this,0,qualifiedName,publicId,systemId);
163 DOMImplementationImpl* DOMImplementationImpl::getInterface(const DOMString& /*feature*/) const
169 DocumentImpl *DOMImplementationImpl::createDocument( const DOMString &namespaceURI, const DOMString &qualifiedName,
170 const DocumentType &doctype, int &exceptioncode )
174 // Not mentioned in spec: throw NAMESPACE_ERR if no qualifiedName supplied
175 if (qualifiedName.isNull()) {
176 exceptioncode = DOMException::NAMESPACE_ERR;
180 // INVALID_CHARACTER_ERR: Raised if the specified qualified name contains an illegal character.
181 if (!Element::khtmlValidQualifiedName(qualifiedName)) {
182 exceptioncode = DOMException::INVALID_CHARACTER_ERR;
187 // - Raised if the qualifiedName is malformed,
188 // - if the qualifiedName has a prefix and the namespaceURI is null, or
189 // - if the qualifiedName has a prefix that is "xml" and the namespaceURI is different
190 // from "http://www.w3.org/XML/1998/namespace" [Namespaces].
193 DOMStringImpl *qname = qualifiedName.implementation();
194 for (i = 0; i < qname->l && colonpos < 0; i++) {
195 if ((*qname)[i] == ':')
199 if (Element::khtmlMalformedQualifiedName(qualifiedName) ||
200 (colonpos >= 0 && namespaceURI.isNull()) ||
201 (colonpos == 3 && qualifiedName[0] == 'x' && qualifiedName[1] == 'm' && qualifiedName[2] == 'l' &&
202 namespaceURI != "http://www.w3.org/XML/1998/namespace")) {
204 exceptioncode = DOMException::NAMESPACE_ERR;
208 DocumentTypeImpl *dtype = static_cast<DocumentTypeImpl*>(doctype.handle());
209 // WRONG_DOCUMENT_ERR: Raised if doctype has already been used with a different document or was
210 // created from a different implementation.
211 if (dtype && (dtype->getDocument() || dtype->implementation() != this)) {
212 exceptioncode = DOMException::WRONG_DOCUMENT_ERR;
216 // ### this is completely broken.. without a view it will not work (Dirk)
217 DocumentImpl *doc = new DocumentImpl(this, 0);
219 // now get the interesting parts of the doctype
220 // ### create new one if not there (currently always there)
221 if (doc->doctype() && dtype)
222 doc->doctype()->copyFrom(*dtype);
227 CSSStyleSheetImpl *DOMImplementationImpl::createCSSStyleSheet(DOMStringImpl */*title*/, DOMStringImpl *media,
228 int &/*exceptioncode*/)
230 // ### TODO : title should be set, and media could have wrong syntax, in which case we should
231 // generate an exception.
232 CSSStyleSheetImpl *parent = 0L;
233 CSSStyleSheetImpl *sheet = new CSSStyleSheetImpl(parent, DOMString());
234 sheet->setMedia(new MediaListImpl(sheet, media));
238 DocumentImpl *DOMImplementationImpl::createDocument( KHTMLView *v )
240 return new DocumentImpl(this, v);
243 HTMLDocumentImpl *DOMImplementationImpl::createHTMLDocument( KHTMLView *v )
245 return new HTMLDocumentImpl(this, v);
248 DOMImplementationImpl *DOMImplementationImpl::instance()
251 m_instance = new DOMImplementationImpl();
258 // ------------------------------------------------------------------------
260 KStaticDeleter< QPtrList<DocumentImpl> > s_changedDocumentsDeleter;
261 QPtrList<DocumentImpl> * DocumentImpl::changedDocuments = 0;
263 // KHTMLView might be 0
264 DocumentImpl::DocumentImpl(DOMImplementationImpl *_implementation, KHTMLView *v)
265 : NodeBaseImpl( new DocumentPtr() )
266 , m_domtree_version(0)
267 , m_imageLoadEventTimer(0)
269 , m_bindingManager(new XBLBindingManager(this))
272 , m_transformSource(NULL)
273 , m_transformSourceDocument(0)
276 , m_finishedParsing(this, SIGNAL(finishedParsing()))
277 , m_inPageCache(false)
279 , m_passwordFields(0)
282 , m_createRenderers(true)
283 , m_designMode(inherit)
284 , m_hasDashboardRegions(false)
285 , m_dashboardRegionsDirty(false)
288 document->doc = this;
291 m_paintDeviceMetrics = 0;
301 m_docLoader = new DocLoader(v->part(), this );
302 setPaintDevice( m_view );
305 m_docLoader = new DocLoader( 0, this );
307 visuallyOrdered = false;
308 m_loadingSheet = false;
310 m_docChanged = false;
315 // ### this should be created during parsing a <!DOCTYPE>
316 // not during construction. Not sure who added that and why (Dirk)
317 m_doctype = new DocumentTypeImpl(_implementation, document,
318 DOMString() /* qualifiedName */,
319 DOMString() /* publicId */,
320 DOMString() /* systemId */);
323 m_implementation = _implementation;
324 if (m_implementation)
325 m_implementation->ref();
328 m_textColor = Qt::black;
330 m_elementNameAlloc = 0;
331 m_elementNameCount = 0;
337 m_defaultView = new AbstractViewImpl(this);
338 m_defaultView->ref();
340 m_styleSheets = new StyleSheetListImpl;
341 m_styleSheets->ref();
343 m_styleSelectorDirty = false;
344 m_inStyleRecalc = false;
345 m_usesDescendantRules = false;
346 m_usesSiblingRules = false;
348 m_styleSelector = new CSSStyleSelector(this, m_usersheet, m_styleSheets, !inCompatMode());
349 m_windowEventListeners.setAutoDelete(true);
350 m_pendingStylesheets = 0;
351 m_ignorePendingStylesheets = false;
354 m_accessKeyDictValid = false;
357 resetVisitedLinkColor();
358 resetActiveLinkColor();
360 m_processingLoadEvent = false;
361 m_startTime.restart();
362 m_overMinimumLayoutThreshold = false;
367 DocumentImpl::~DocumentImpl()
371 assert(!m_inPageCache);
372 assert(m_savedRenderer == 0);
375 KJS::ScriptInterpreter::forgetDOMObjectsForDocument(this);
377 if (changedDocuments && m_docChanged)
378 changedDocuments->remove(this);
382 delete m_styleSelector;
384 if (m_elemSheet ) m_elemSheet->deref();
387 if (m_implementation)
388 m_implementation->deref();
389 delete m_paintDeviceMetrics;
391 if (m_elementNames) {
392 for (unsigned short id = 0; id < m_elementNameCount; id++)
393 m_elementNames[id]->deref();
394 delete [] m_elementNames;
397 for (unsigned short id = 0; id < m_attrNameCount; id++)
398 m_attrNames[id]->deref();
399 delete [] m_attrNames;
401 m_defaultView->deref();
402 m_styleSheets->deref();
405 m_focusNode->deref();
407 m_hoverNode->deref();
410 delete m_renderArena;
415 xmlFreeDoc((xmlDocPtr)m_transformSource);
416 if (m_transformSourceDocument)
417 m_transformSourceDocument->deref();
421 delete m_bindingManager;
442 void DocumentImpl::resetLinkColor()
444 m_linkColor = QColor(0, 0, 238);
447 void DocumentImpl::resetVisitedLinkColor()
449 m_visitedLinkColor = QColor(85, 26, 139);
452 void DocumentImpl::resetActiveLinkColor()
454 m_activeLinkColor.setNamedColor(QString("red"));
457 DocumentTypeImpl *DocumentImpl::doctype() const
462 DOMImplementationImpl *DocumentImpl::implementation() const
464 return m_implementation;
467 ElementImpl *DocumentImpl::documentElement() const
469 NodeImpl *n = firstChild();
470 while (n && n->nodeType() != Node::ELEMENT_NODE)
471 n = n->nextSibling();
472 return static_cast<ElementImpl*>(n);
475 ElementImpl *DocumentImpl::createElement( const DOMString &name, int &exceptioncode )
477 return new XMLElementImpl( document, name.implementation() );
480 DocumentFragmentImpl *DocumentImpl::createDocumentFragment( )
482 return new DocumentFragmentImpl( docPtr() );
485 TextImpl *DocumentImpl::createTextNode( const DOMString &data )
487 return new TextImpl( docPtr(), data);
490 CommentImpl *DocumentImpl::createComment ( const DOMString &data )
492 return new CommentImpl( docPtr(), data );
495 CDATASectionImpl *DocumentImpl::createCDATASection ( const DOMString &data )
497 return new CDATASectionImpl( docPtr(), data );
500 ProcessingInstructionImpl *DocumentImpl::createProcessingInstruction ( const DOMString &target, const DOMString &data )
502 return new ProcessingInstructionImpl( docPtr(),target,data);
505 Attr DocumentImpl::createAttribute( NodeImpl::Id id )
507 // Assume this is an HTML attribute, since createAttribute isn't namespace-aware. There's no harm to XML
508 // documents if we're wrong.
509 return new AttrImpl(0, docPtr(), new HTMLAttributeImpl(id, DOMString("").implementation()));
512 EntityReferenceImpl *DocumentImpl::createEntityReference ( const DOMString &name )
514 return new EntityReferenceImpl(docPtr(), name.implementation());
517 EditingTextImpl *DocumentImpl::createEditingTextNode(const DOMString &text)
519 return new EditingTextImpl(docPtr(), text);
522 CSSStyleDeclarationImpl *DocumentImpl::createCSSStyleDeclaration()
524 return new CSSMutableStyleDeclarationImpl;
527 NodeImpl *DocumentImpl::importNode(NodeImpl *importedNode, bool deep, int &exceptioncode)
529 NodeImpl *result = 0;
531 if(importedNode->nodeType() == Node::ELEMENT_NODE)
533 ElementImpl *tempElementImpl = createElementNS(getDocument()->namespaceURI(id()), importedNode->nodeName(), exceptioncode);
536 result = tempElementImpl;
538 if(static_cast<ElementImpl *>(importedNode)->attributes(true) && static_cast<ElementImpl *>(importedNode)->attributes(true)->length())
540 NamedNodeMapImpl *attr = static_cast<ElementImpl *>(importedNode)->attributes();
542 for(unsigned int i = 0; i < attr->length(); i++)
544 DOMString qualifiedName = attr->item(i)->nodeName();
545 DOMString value = attr->item(i)->nodeValue();
547 int colonpos = qualifiedName.find(':');
548 DOMString localName = qualifiedName;
551 localName.remove(0, colonpos + 1);
552 // ### extract and set new prefix
555 NodeImpl::Id nodeId = getDocument()->attrId(getDocument()->namespaceURI(id()), localName.implementation(), false /* allocate */);
556 tempElementImpl->setAttribute(nodeId, value.implementation(), exceptioncode);
558 if(exceptioncode != 0)
563 else if(importedNode->nodeType() == Node::TEXT_NODE)
565 result = createTextNode(importedNode->nodeValue());
568 else if(importedNode->nodeType() == Node::CDATA_SECTION_NODE)
570 result = createCDATASection(importedNode->nodeValue());
573 else if(importedNode->nodeType() == Node::ENTITY_REFERENCE_NODE)
574 result = createEntityReference(importedNode->nodeName());
575 else if(importedNode->nodeType() == Node::PROCESSING_INSTRUCTION_NODE)
577 result = createProcessingInstruction(importedNode->nodeName(), importedNode->nodeValue());
580 else if(importedNode->nodeType() == Node::COMMENT_NODE)
582 result = createComment(importedNode->nodeValue());
586 exceptioncode = DOMException::NOT_SUPPORTED_ERR;
590 for(Node n = importedNode->firstChild(); !n.isNull(); n = n.nextSibling())
591 result->appendChild(importNode(n.handle(), true, exceptioncode), exceptioncode);
597 ElementImpl *DocumentImpl::createElementNS( const DOMString &_namespaceURI, const DOMString &_qualifiedName, int &exceptioncode)
600 QString qName = _qualifiedName.string();
601 int colonPos = qName.find(':',0);
603 if (_namespaceURI == XHTML_NAMESPACE) {
604 // User requested an element in the XHTML namespace - this means we create a HTML element
605 // (elements not in this namespace are treated as normal XML elements)
606 e = createHTMLElement(qName.mid(colonPos+1), exceptioncode);
609 if (e && colonPos >= 0) {
610 e->setPrefix(qName.left(colonPos), exceptioncode);
618 e = new XMLElementImpl( document, _qualifiedName.implementation(), _namespaceURI.implementation() );
623 ElementImpl *DocumentImpl::getElementById( const DOMString &elementId ) const
625 if (elementId.length() == 0) {
629 return m_elementsById.find(elementId.string());
633 void DocumentImpl::addElementById(const DOMString &elementId, ElementImpl *element)
635 QString qId = elementId.string();
637 if (m_elementsById.find(qId) == NULL) {
638 m_elementsById.insert(qId, element);
639 m_accessKeyDictValid = false;
643 void DocumentImpl::removeElementById(const DOMString &elementId, ElementImpl *element)
645 QString qId = elementId.string();
647 if (m_elementsById.find(qId) == element) {
648 m_elementsById.remove(qId);
649 m_accessKeyDictValid = false;
653 ElementImpl *DocumentImpl::getElementByAccessKey( const DOMString &key )
655 if (key.length() == 0)
658 QString k(key.string());
659 if (!m_accessKeyDictValid) {
660 m_elementsByAccessKey.clear();
663 for (n = this; n != 0; n = n->traverseNextNode()) {
664 if (!n->isElementNode())
666 const ElementImpl *elementImpl = static_cast<const ElementImpl *>(n);
667 DOMString accessKey(elementImpl->getAttribute(ATTR_ACCESSKEY));
668 if (!accessKey.isEmpty()) {
669 QString ak = accessKey.string().lower();
670 if (m_elementsByAccessKey.find(ak) == NULL)
671 m_elementsByAccessKey.insert(ak, elementImpl);
674 m_accessKeyDictValid = true;
676 return m_elementsByAccessKey.find(k);
679 void DocumentImpl::setTitle(DOMString _title)
687 KWQ(part())->setTitle(_title);
689 QString titleStr = m_title.string();
690 for (int i = 0; i < titleStr.length(); ++i)
691 if (titleStr[i] < ' ')
693 titleStr = titleStr.stripWhiteSpace();
695 if ( !part()->parentPart() ) {
696 if (titleStr.isNull() || titleStr.isEmpty()) {
697 // empty title... set window caption as the URL
699 url.setRef(QString::null);
700 url.setQuery(QString::null);
701 titleStr = url.url();
704 emit part()->setWindowCaption( KStringHandler::csqueeze( titleStr, 128 ) );
709 DOMString DocumentImpl::nodeName() const
714 unsigned short DocumentImpl::nodeType() const
716 return Node::DOCUMENT_NODE;
719 ElementImpl *DocumentImpl::createHTMLElement( const DOMString &name, int &exceptioncode )
721 if (!isValidName(name)) {
722 exceptioncode = DOMException::INVALID_CHARACTER_ERR;
725 return createHTMLElement(tagId(0, name.implementation(), false));
728 ElementImpl *DocumentImpl::createHTMLElement(unsigned short tagID)
733 return new HTMLHtmlElementImpl(docPtr());
735 return new HTMLHeadElementImpl(docPtr());
737 return new HTMLBodyElementImpl(docPtr());
741 return new HTMLBaseElementImpl(docPtr());
743 return new HTMLLinkElementImpl(docPtr());
745 return new HTMLMetaElementImpl(docPtr());
747 return new HTMLStyleElementImpl(docPtr());
749 return new HTMLTitleElementImpl(docPtr());
753 return new HTMLFrameElementImpl(docPtr());
755 return new HTMLFrameSetElementImpl(docPtr());
757 return new HTMLIFrameElementImpl(docPtr());
760 // ### FIXME: we need a way to set form dependency after we have made the form elements
762 return new HTMLFormElementImpl(docPtr());
764 return new HTMLButtonElementImpl(docPtr());
766 return new HTMLFieldSetElementImpl(docPtr());
768 return new HTMLInputElementImpl(docPtr());
770 return new HTMLIsIndexElementImpl(docPtr());
772 return new HTMLLabelElementImpl(docPtr());
774 return new HTMLLegendElementImpl(docPtr());
776 return new HTMLOptGroupElementImpl(docPtr());
778 return new HTMLOptionElementImpl(docPtr());
780 return new HTMLSelectElementImpl(docPtr());
782 return new HTMLTextAreaElementImpl(docPtr());
786 return new HTMLDListElementImpl(docPtr());
788 return new HTMLGenericElementImpl(docPtr(), tagID);
790 return new HTMLGenericElementImpl(docPtr(), tagID);
792 return new HTMLUListElementImpl(docPtr());
794 return new HTMLOListElementImpl(docPtr());
796 return new HTMLDirectoryElementImpl(docPtr());
798 return new HTMLMenuElementImpl(docPtr());
800 return new HTMLLIElementImpl(docPtr());
802 // formatting elements (block)
804 return new HTMLBlockquoteElementImpl(docPtr());
806 return new HTMLDivElementImpl(docPtr());
813 return new HTMLHeadingElementImpl(docPtr(), tagID);
815 return new HTMLHRElementImpl(docPtr());
817 return new HTMLParagraphElementImpl(docPtr());
821 return new HTMLPreElementImpl(docPtr(), tagID);
823 return new HTMLLayerElementImpl(docPtr());
827 return new HTMLBaseFontElementImpl(docPtr());
829 return new HTMLFontElementImpl(docPtr());
834 return new HTMLGenericElementImpl(docPtr(), tagID);
838 return new HTMLAnchorElementImpl(docPtr());
842 return new HTMLImageElementImpl(docPtr());
844 return new HTMLMapElementImpl(docPtr());
846 return new HTMLAreaElementImpl(docPtr());
848 return new HTMLCanvasElementImpl(docPtr());
850 // objects, applets and scripts
852 return new HTMLAppletElementImpl(docPtr());
854 return new HTMLEmbedElementImpl(docPtr());
856 return new HTMLObjectElementImpl(docPtr());
858 return new HTMLParamElementImpl(docPtr());
860 return new HTMLScriptElementImpl(docPtr());
864 return new HTMLTableElementImpl(docPtr());
866 return new HTMLTableCaptionElementImpl(docPtr());
869 return new HTMLTableColElementImpl(docPtr(), tagID);
871 return new HTMLTableRowElementImpl(docPtr());
874 return new HTMLTableCellElementImpl(docPtr(), tagID);
878 return new HTMLTableSectionElementImpl(docPtr(), tagID, false);
882 return new HTMLBRElementImpl(docPtr());
884 return new HTMLGenericElementImpl(docPtr(), tagID);
886 // elements with no special representation in the DOM
924 return new HTMLGenericElementImpl(docPtr(), tagID);
927 return new HTMLMarqueeElementImpl(docPtr());
931 kdDebug( 6020 ) << "Use document->createTextNode()" << endl;
938 QString DocumentImpl::nextState()
941 if (!m_state.isEmpty())
943 state = m_state.first();
944 m_state.remove(m_state.begin());
949 QStringList DocumentImpl::docState()
952 for (QPtrListIterator<NodeImpl> it(m_maintainsState); it.current(); ++it)
953 s.append(it.current()->state());
958 KHTMLPart *DocumentImpl::part() const
960 return m_view ? m_view->part() : 0;
963 RangeImpl *DocumentImpl::createRange()
965 return new RangeImpl( docPtr() );
968 NodeIteratorImpl *DocumentImpl::createNodeIterator(NodeImpl *root, unsigned long whatToShow,
969 NodeFilterImpl *filter, bool expandEntityReferences, int &exceptioncode)
972 exceptioncode = DOMException::NOT_SUPPORTED_ERR;
975 return new NodeIteratorImpl(root, whatToShow, filter, expandEntityReferences);
978 TreeWalkerImpl *DocumentImpl::createTreeWalker(NodeImpl *root, unsigned long whatToShow,
979 NodeFilterImpl *filter, bool expandEntityReferences, int &exceptioncode)
982 exceptioncode = DOMException::NOT_SUPPORTED_ERR;
985 return new TreeWalkerImpl(root, whatToShow, filter, expandEntityReferences);
988 void DocumentImpl::setDocumentChanged(bool b)
990 if (!changedDocuments)
991 changedDocuments = s_changedDocumentsDeleter.setObject( new QPtrList<DocumentImpl>() );
993 if (b && !m_docChanged)
994 changedDocuments->append(this);
995 else if (!b && m_docChanged)
996 changedDocuments->remove(this);
1000 m_accessKeyDictValid = false;
1003 void DocumentImpl::recalcStyle( StyleChange change )
1005 // qDebug("recalcStyle(%p)", this);
1008 if (m_inStyleRecalc)
1009 return; // Guard against re-entrancy. -dwh
1011 m_inStyleRecalc = true;
1013 if( !m_render ) goto bail_out;
1015 if ( change == Force ) {
1016 RenderStyle* oldStyle = m_render->style();
1017 if ( oldStyle ) oldStyle->ref();
1018 RenderStyle* _style = new (m_renderArena) RenderStyle();
1020 _style->setDisplay(BLOCK);
1021 _style->setVisuallyOrdered( visuallyOrdered );
1022 // ### make the font stuff _really_ work!!!!
1024 khtml::FontDef fontDef;
1025 QFont f = KGlobalSettings::generalFont();
1026 fontDef.family = *(f.firstFamily());
1027 fontDef.italic = f.italic();
1028 fontDef.weight = f.weight();
1030 bool printing = m_paintDevice && (m_paintDevice->devType() == QInternal::Printer);
1031 fontDef.usePrinterFont = printing;
1034 const KHTMLSettings *settings = m_view->part()->settings();
1036 if (printing && !settings->shouldPrintBackgrounds()) {
1037 _style->setShouldCorrectTextColor(true);
1040 QString stdfont = settings->stdFontName();
1041 if ( !stdfont.isEmpty() ) {
1042 fontDef.family.setFamily(stdfont);
1043 fontDef.family.appendFamily(0);
1045 m_styleSelector->setFontSize(fontDef, m_styleSelector->fontSizeForKeyword(CSS_VAL_MEDIUM, inCompatMode()));
1048 //kdDebug() << "DocumentImpl::attach: setting to charset " << settings->charset() << endl;
1049 _style->setFontDef(fontDef);
1050 _style->htmlFont().update( paintDeviceMetrics() );
1051 if ( inCompatMode() )
1052 _style->setHtmlHacks(true); // enable html specific rendering tricks
1054 StyleChange ch = diff( _style, oldStyle );
1055 if(m_render && ch != NoChange)
1056 m_render->setStyle(_style);
1057 if ( change != Force )
1060 _style->deref(m_renderArena);
1062 oldStyle->deref(m_renderArena);
1066 for (n = _first; n; n = n->nextSibling())
1067 if ( change>= Inherit || n->hasChangedChild() || n->changed() )
1068 n->recalcStyle( change );
1069 //kdDebug( 6020 ) << "TIME: recalcStyle() dt=" << qt.elapsed() << endl;
1071 if (changed() && m_view)
1075 setChanged( false );
1076 setHasChangedChild( false );
1077 setDocumentChanged( false );
1079 m_inStyleRecalc = false;
1082 void DocumentImpl::updateRendering()
1084 if (!hasChangedChild()) return;
1088 // kdDebug() << "UPDATERENDERING: "<<endl;
1090 StyleChange change = NoChange;
1092 if ( m_styleSelectorDirty ) {
1093 recalcStyleSelector();
1097 recalcStyle( change );
1099 // kdDebug() << "UPDATERENDERING time used="<<time.elapsed()<<endl;
1102 void DocumentImpl::updateDocumentsRendering()
1104 if (!changedDocuments)
1107 while (DocumentImpl* doc = changedDocuments->take()) {
1108 doc->m_docChanged = false;
1109 doc->updateRendering();
1113 void DocumentImpl::updateLayout()
1115 // FIXME: Dave's pretty sure we can remove this because
1116 // layout calls recalcStyle as needed.
1119 // Only do a layout if changes have occurred that make it necessary.
1120 if (m_view && renderer() && renderer()->needsLayout())
1124 // FIXME: This is a bad idea and needs to be removed eventually.
1125 // Other browsers load stylesheets before they continue parsing the web page.
1126 // Since we don't, we can run JavaScript code that needs answers before the
1127 // stylesheets are loaded. Doing a layout ignoring the pending stylesheets
1128 // lets us get reasonable answers. The long term solution to this problem is
1129 // to instead suspend JavaScript execution.
1130 void DocumentImpl::updateLayoutIgnorePendingStylesheets()
1132 bool oldIgnore = m_ignorePendingStylesheets;
1134 if (!haveStylesheetsLoaded()) {
1135 m_ignorePendingStylesheets = true;
1136 updateStyleSelector();
1141 m_ignorePendingStylesheets = oldIgnore;
1144 void DocumentImpl::attach()
1146 assert(!attached());
1148 assert(!m_inPageCache);
1152 setPaintDevice( m_view );
1155 m_renderArena = new RenderArena();
1157 // Create the rendering tree
1158 m_render = new (m_renderArena) RenderCanvas(this, m_view);
1159 recalcStyle( Force );
1161 RenderObject* render = m_render;
1164 NodeBaseImpl::attach();
1168 void DocumentImpl::restoreRenderer(RenderObject* render)
1173 void DocumentImpl::detach()
1175 RenderObject* render = m_render;
1177 // indicate destruction mode, i.e. attached() but m_render == 0
1181 if (m_inPageCache) {
1183 getAccObjectCache()->detach(render);
1188 // Empty out these lists as a performance optimization, since detaching
1189 // all the individual render objects will cause all the RenderImage
1190 // objects to remove themselves from the lists.
1191 m_imageLoadEventDispatchSoonList.clear();
1192 m_imageLoadEventDispatchingList.clear();
1194 removeAllEventListenersFromAllNodes();
1196 NodeBaseImpl::detach();
1201 if (m_paintDevice == m_view)
1206 delete m_renderArena;
1211 void DocumentImpl::removeAllEventListenersFromAllNodes()
1213 m_windowEventListeners.clear();
1214 removeAllDisconnectedNodeEventListeners();
1215 for (NodeImpl *n = this; n; n = n->traverseNextNode()) {
1216 n->removeAllEventListeners();
1220 void DocumentImpl::registerDisconnectedNodeWithEventListeners(NodeImpl *node)
1222 m_disconnectedNodesWithEventListeners.insert(node, node);
1225 void DocumentImpl::unregisterDisconnectedNodeWithEventListeners(NodeImpl *node)
1227 m_disconnectedNodesWithEventListeners.remove(node);
1230 void DocumentImpl::removeAllDisconnectedNodeEventListeners()
1232 for (QPtrDictIterator<NodeImpl> iter(m_disconnectedNodesWithEventListeners);
1235 iter.current()->removeAllEventListeners();
1240 KWQAccObjectCache* DocumentImpl::getAccObjectCache()
1242 // The only document that actually has a KWQAccObjectCache is the top-level
1243 // document. This is because we need to be able to get from any KWQAccObject
1244 // to any other KWQAccObject on the same page. Using a single cache allows
1245 // lookups across nested webareas (i.e. multiple documents).
1248 // return already known top-level cache
1249 if (!ownerElement())
1252 // In some pages with frames, the cache is created before the sub-webarea is
1253 // inserted into the tree. Here, we catch that case and just toss the old
1254 // cache and start over.
1259 // look for top-level document
1260 ElementImpl *element = ownerElement();
1264 doc = element->getDocument();
1265 element = doc->ownerElement();
1268 // ask the top-level document for its cache
1269 return doc->getAccObjectCache();
1272 // this is the top-level document, so install a new cache
1273 m_accCache = new KWQAccObjectCache;
1278 void DocumentImpl::setVisuallyOrdered()
1280 visuallyOrdered = true;
1282 m_render->style()->setVisuallyOrdered(true);
1285 void DocumentImpl::updateSelection()
1290 RenderCanvas *canvas = static_cast<RenderCanvas*>(m_render);
1291 Selection s = part()->selection();
1293 canvas->clearSelection();
1296 Position startPos = VisiblePosition(s.start(), s.startAffinity(), khtml::VisiblePosition::INIT_UP).deepEquivalent();
1297 Position endPos = VisiblePosition(s.end(), s.endAffinity(), khtml::VisiblePosition::INIT_DOWN).deepEquivalent();
1298 if (startPos.isNotNull() && endPos.isNotNull()) {
1299 RenderObject *startRenderer = startPos.node()->renderer();
1300 RenderObject *endRenderer = endPos.node()->renderer();
1301 static_cast<RenderCanvas*>(m_render)->setSelection(startRenderer, startPos.offset(), endRenderer, endPos.offset());
1306 // send the AXSelectedTextChanged notification only if the new selection is non-null,
1307 // because null selections are only transitory (e.g. when starting an EditCommand, currently)
1308 if (KWQAccObjectCache::accessibilityEnabled() && s.start().isNotNull() && s.end().isNotNull()) {
1309 getAccObjectCache()->postNotificationToTopWebArea(renderer(), "AXSelectedTextChanged");
1315 Tokenizer *DocumentImpl::createTokenizer()
1317 return newXMLTokenizer(docPtr(), m_view);
1320 void DocumentImpl::setPaintDevice( QPaintDevice *dev )
1322 if (m_paintDevice == dev) {
1325 m_paintDevice = dev;
1326 delete m_paintDeviceMetrics;
1327 m_paintDeviceMetrics = dev ? new QPaintDeviceMetrics( dev ) : 0;
1330 void DocumentImpl::open( )
1332 if (parsing()) return;
1337 part()->didExplicitOpen();
1340 // This is work that we should probably do in clear(), but we can't have it
1341 // happen when implicitOpen() is called unless we reorganize KHTMLPart code.
1343 DocumentImpl *parent = parentDocument();
1345 setBaseURL(parent->baseURL());
1349 void DocumentImpl::implicitOpen()
1355 m_tokenizer = createTokenizer();
1356 connect(m_tokenizer,SIGNAL(finishedParsing()),this,SIGNAL(finishedParsing()));
1359 if (m_view && m_view->part()->jScript()) {
1360 m_view->part()->jScript()->setSourceFile(m_url,""); //fixme
1364 HTMLElementImpl* DocumentImpl::body()
1366 NodeImpl *de = documentElement();
1370 // try to prefer a FRAMESET element over BODY
1372 for (NodeImpl* i = de->firstChild(); i; i = i->nextSibling()) {
1373 if (i->id() == ID_FRAMESET)
1374 return static_cast<HTMLElementImpl*>(i);
1376 if (i->id() == ID_BODY)
1379 return static_cast<HTMLElementImpl *>(body);
1382 void DocumentImpl::close()
1385 part()->endIfNotLoading();
1389 void DocumentImpl::implicitClose()
1391 // First fire the onload.
1393 bool wasLocationChangePending = part() && part()->isScheduledLocationChangePending();
1394 bool doload = !parsing() && m_tokenizer && !m_processingLoadEvent && !wasLocationChangePending;
1397 m_processingLoadEvent = true;
1399 // We have to clear the tokenizer, in case someone document.write()s from the
1400 // onLoad event handler, as in Radar 3206524
1404 // Create a body element if we don't already have one.
1405 // In the case of Radar 3758785, the window.onload was set in some javascript, but never fired because there was no body.
1406 // This behavior now matches Firefox and IE.
1407 HTMLElementImpl *body = this->body();
1408 if (!body && isHTMLDocument()) {
1409 NodeImpl *de = documentElement();
1411 body = new HTMLBodyElementImpl(docPtr());
1412 int exceptionCode = 0;
1413 de->appendChild(body, exceptionCode);
1414 if (exceptionCode != 0)
1420 dispatchImageLoadEventsNow();
1421 body->dispatchWindowEvent(EventImpl::LOAD_EVENT, false, false);
1423 #ifdef INSTRUMENT_LAYOUT_SCHEDULING
1424 if (!ownerElement())
1425 printf("onload fired at %d\n", elapsedTime());
1429 m_processingLoadEvent = false;
1432 // Make sure both the initial layout and reflow happen after the onload
1433 // fires. This will improve onload scores, and other browsers do it.
1434 // If they wanna cheat, we can too. -dwh
1436 bool isLocationChangePending = part() && part()->isScheduledLocationChangePending();
1438 if (doload && isLocationChangePending && m_startTime.elapsed() < cLayoutScheduleThreshold) {
1439 // Just bail out. Before or during the onload we were shifted to another page.
1440 // The old i-Bench suite does this. When this happens don't bother painting or laying out.
1443 view()->unscheduleRelayout();
1448 // on an explicit document.close(), the tokenizer might still be waiting on scripts,
1449 // and in that case we don't want to destroy it because that will prevent the
1450 // scripts from getting processed.
1451 // FIXME: this check may no longer be necessary, since now it should be impossible
1452 // for parsing to be false while stil waiting for scripts
1453 if (m_tokenizer && !m_tokenizer->isWaitingForScripts()) {
1459 m_view->part()->checkEmitLoadEvent();
1462 // Now do our painting/layout, but only if we aren't in a subframe or if we're in a subframe
1463 // that has been sized already. Otherwise, our view size would be incorrect, so doing any
1464 // layout/painting now would be pointless.
1466 if (!ownerElement() || (ownerElement()->renderer() && !ownerElement()->renderer()->needsLayout())) {
1469 // Always do a layout after loading if needed.
1470 if (view() && renderer() && (!renderer()->firstChild() || renderer()->needsLayout()))
1474 if (renderer() && KWQAccObjectCache::accessibilityEnabled())
1475 getAccObjectCache()->postNotification(renderer(), "AXLoadComplete");
1480 void DocumentImpl::setParsing(bool b)
1483 if (!m_bParsing && view())
1484 view()->scheduleRelayout();
1486 #ifdef INSTRUMENT_LAYOUT_SCHEDULING
1487 if (!ownerElement() && !m_bParsing)
1488 printf("Parsing finished at %d\n", elapsedTime());
1492 bool DocumentImpl::shouldScheduleLayout()
1494 // We can update layout if:
1495 // (a) we actually need a layout
1496 // (b) our stylesheets are all loaded
1497 // (c) we have a <body>
1498 return (renderer() && renderer()->needsLayout() && haveStylesheetsLoaded() &&
1499 documentElement() && documentElement()->renderer() &&
1500 (documentElement()->id() != ID_HTML || body()));
1503 int DocumentImpl::minimumLayoutDelay()
1505 if (m_overMinimumLayoutThreshold)
1508 int elapsed = m_startTime.elapsed();
1509 m_overMinimumLayoutThreshold = elapsed > cLayoutScheduleThreshold;
1511 // We'll want to schedule the timer to fire at the minimum layout threshold.
1512 return kMax(0, cLayoutScheduleThreshold - elapsed);
1515 int DocumentImpl::elapsedTime() const
1517 return m_startTime.elapsed();
1520 void DocumentImpl::write( const DOMString &text )
1522 write(text.string());
1525 void DocumentImpl::write( const QString &text )
1527 #ifdef INSTRUMENT_LAYOUT_SCHEDULING
1528 if (!ownerElement())
1529 printf("Beginning a document.write at %d\n", elapsedTime());
1534 write(QString::fromLatin1("<html>"));
1536 m_tokenizer->write(text, false);
1538 if (m_view && m_view->part()->jScript())
1539 m_view->part()->jScript()->appendSourceFile(m_url,text);
1541 #ifdef INSTRUMENT_LAYOUT_SCHEDULING
1542 if (!ownerElement())
1543 printf("Ending a document.write at %d\n", elapsedTime());
1547 void DocumentImpl::writeln( const DOMString &text )
1550 write(DOMString("\n"));
1553 void DocumentImpl::finishParsing()
1555 #ifdef INSTRUMENT_LAYOUT_SCHEDULING
1556 if (!ownerElement())
1557 printf("Received all data at %d\n", elapsedTime());
1560 // Let the tokenizer go through as much data as it can. There will be three possible outcomes after
1561 // finish() is called:
1562 // (1) All remaining data is parsed, document isn't loaded yet
1563 // (2) All remaining data is parsed, document is loaded, tokenizer gets deleted
1564 // (3) Data is still remaining to be parsed.
1566 m_tokenizer->finish();
1569 void DocumentImpl::clear()
1575 QPtrListIterator<RegisteredEventListener> it(m_windowEventListeners);
1576 for (; it.current();)
1577 m_windowEventListeners.removeRef(it.current());
1580 void DocumentImpl::setURL(const QString& url)
1583 if (m_styleSelector)
1584 m_styleSelector->setEncodedURL(m_url);
1587 void DocumentImpl::setStyleSheet(const DOMString &url, const DOMString &sheet)
1589 // kdDebug( 6030 ) << "HTMLDocument::setStyleSheet()" << endl;
1590 m_sheet = new CSSStyleSheetImpl(this, url);
1592 m_sheet->parseString(sheet);
1593 m_loadingSheet = false;
1595 updateStyleSelector();
1598 void DocumentImpl::setUserStyleSheet( const QString& sheet )
1600 if ( m_usersheet != sheet ) {
1601 m_usersheet = sheet;
1602 updateStyleSelector();
1606 CSSStyleSheetImpl* DocumentImpl::elementSheet()
1609 m_elemSheet = new CSSStyleSheetImpl(this, baseURL() );
1615 void DocumentImpl::determineParseMode( const QString &/*str*/ )
1617 // For XML documents use strict parse mode. HTML docs will override this method to
1618 // determine their parse mode.
1621 kdDebug(6020) << " using strict parseMode" << endl;
1624 // Please see if there`s a possibility to merge that code
1625 // with the next function and getElementByID().
1626 NodeImpl *DocumentImpl::findElement( Id id )
1628 QPtrStack<NodeImpl> nodeStack;
1629 NodeImpl *current = _first;
1635 if(nodeStack.isEmpty()) break;
1636 current = nodeStack.pop();
1637 current = current->nextSibling();
1641 if(current->id() == id)
1644 NodeImpl *child = current->firstChild();
1647 nodeStack.push(current);
1652 current = current->nextSibling();
1660 NodeImpl *DocumentImpl::nextFocusNode(NodeImpl *fromNode)
1662 unsigned short fromTabIndex;
1665 // No starting node supplied; begin with the top of the document
1668 int lowestTabIndex = 65535;
1669 for (n = this; n != 0; n = n->traverseNextNode()) {
1670 if (n->isKeyboardFocusable()) {
1671 if ((n->tabIndex() > 0) && (n->tabIndex() < lowestTabIndex))
1672 lowestTabIndex = n->tabIndex();
1676 if (lowestTabIndex == 65535)
1679 // Go to the first node in the document that has the desired tab index
1680 for (n = this; n != 0; n = n->traverseNextNode()) {
1681 if (n->isKeyboardFocusable() && (n->tabIndex() == lowestTabIndex))
1688 fromTabIndex = fromNode->tabIndex();
1691 if (fromTabIndex == 0) {
1692 // Just need to find the next selectable node after fromNode (in document order) that doesn't have a tab index
1693 NodeImpl *n = fromNode->traverseNextNode();
1694 while (n && !(n->isKeyboardFocusable() && n->tabIndex() == 0))
1695 n = n->traverseNextNode();
1699 // Find the lowest tab index out of all the nodes except fromNode, that is greater than or equal to fromNode's
1700 // tab index. For nodes with the same tab index as fromNode, we are only interested in those that come after
1701 // fromNode in document order.
1702 // If we don't find a suitable tab index, the next focus node will be one with a tab index of 0.
1703 unsigned short lowestSuitableTabIndex = 65535;
1706 bool reachedFromNode = false;
1707 for (n = this; n != 0; n = n->traverseNextNode()) {
1708 if (n->isKeyboardFocusable() &&
1709 ((reachedFromNode && (n->tabIndex() >= fromTabIndex)) ||
1710 (!reachedFromNode && (n->tabIndex() > fromTabIndex))) &&
1711 (n->tabIndex() < lowestSuitableTabIndex) &&
1714 // We found a selectable node with a tab index at least as high as fromNode's. Keep searching though,
1715 // as there may be another node which has a lower tab index but is still suitable for use.
1716 lowestSuitableTabIndex = n->tabIndex();
1720 reachedFromNode = true;
1723 if (lowestSuitableTabIndex == 65535) {
1724 // No next node with a tab index -> just take first node with tab index of 0
1726 while (n && !(n->isKeyboardFocusable() && n->tabIndex() == 0))
1727 n = n->traverseNextNode();
1731 // Search forwards from fromNode
1732 for (n = fromNode->traverseNextNode(); n != 0; n = n->traverseNextNode()) {
1733 if (n->isKeyboardFocusable() && (n->tabIndex() == lowestSuitableTabIndex))
1737 // The next node isn't after fromNode, start from the beginning of the document
1738 for (n = this; n != fromNode; n = n->traverseNextNode()) {
1739 if (n->isKeyboardFocusable() && (n->tabIndex() == lowestSuitableTabIndex))
1743 assert(false); // should never get here
1748 NodeImpl *DocumentImpl::previousFocusNode(NodeImpl *fromNode)
1750 NodeImpl *lastNode = this;
1751 while (lastNode->lastChild())
1752 lastNode = lastNode->lastChild();
1755 // No starting node supplied; begin with the very last node in the document
1758 int highestTabIndex = 0;
1759 for (n = lastNode; n != 0; n = n->traversePreviousNode()) {
1760 if (n->isKeyboardFocusable()) {
1761 if (n->tabIndex() == 0)
1763 else if (n->tabIndex() > highestTabIndex)
1764 highestTabIndex = n->tabIndex();
1768 // No node with a tab index of 0; just go to the last node with the highest tab index
1769 for (n = lastNode; n != 0; n = n->traversePreviousNode()) {
1770 if (n->isKeyboardFocusable() && (n->tabIndex() == highestTabIndex))
1777 unsigned short fromTabIndex = fromNode->tabIndex();
1779 if (fromTabIndex == 0) {
1780 // Find the previous selectable node before fromNode (in document order) that doesn't have a tab index
1781 NodeImpl *n = fromNode->traversePreviousNode();
1782 while (n && !(n->isKeyboardFocusable() && n->tabIndex() == 0))
1783 n = n->traversePreviousNode();
1787 // No previous nodes with a 0 tab index, go to the last node in the document that has the highest tab index
1788 int highestTabIndex = 0;
1789 for (n = this; n != 0; n = n->traverseNextNode()) {
1790 if (n->isKeyboardFocusable() && (n->tabIndex() > highestTabIndex))
1791 highestTabIndex = n->tabIndex();
1794 if (highestTabIndex == 0)
1797 for (n = lastNode; n != 0; n = n->traversePreviousNode()) {
1798 if (n->isKeyboardFocusable() && (n->tabIndex() == highestTabIndex))
1802 assert(false); // should never get here
1806 // Find the lowest tab index out of all the nodes except fromNode, that is less than or equal to fromNode's
1807 // tab index. For nodes with the same tab index as fromNode, we are only interested in those before
1809 // If we don't find a suitable tab index, then there will be no previous focus node.
1810 unsigned short highestSuitableTabIndex = 0;
1813 bool reachedFromNode = false;
1814 for (n = this; n != 0; n = n->traverseNextNode()) {
1815 if (n->isKeyboardFocusable() &&
1816 ((!reachedFromNode && (n->tabIndex() <= fromTabIndex)) ||
1817 (reachedFromNode && (n->tabIndex() < fromTabIndex))) &&
1818 (n->tabIndex() > highestSuitableTabIndex) &&
1821 // We found a selectable node with a tab index no higher than fromNode's. Keep searching though, as
1822 // there may be another node which has a higher tab index but is still suitable for use.
1823 highestSuitableTabIndex = n->tabIndex();
1827 reachedFromNode = true;
1830 if (highestSuitableTabIndex == 0) {
1831 // No previous node with a tab index. Since the order specified by HTML is nodes with tab index > 0
1832 // first, this means that there is no previous node.
1836 // Search backwards from fromNode
1837 for (n = fromNode->traversePreviousNode(); n != 0; n = n->traversePreviousNode()) {
1838 if (n->isKeyboardFocusable() && (n->tabIndex() == highestSuitableTabIndex))
1841 // The previous node isn't before fromNode, start from the end of the document
1842 for (n = lastNode; n != fromNode; n = n->traversePreviousNode()) {
1843 if (n->isKeyboardFocusable() && (n->tabIndex() == highestSuitableTabIndex))
1847 assert(false); // should never get here
1853 int DocumentImpl::nodeAbsIndex(NodeImpl *node)
1855 assert(node->getDocument() == this);
1858 for (NodeImpl *n = node; n && n != this; n = n->traversePreviousNode())
1863 NodeImpl *DocumentImpl::nodeWithAbsIndex(int absIndex)
1866 for (int i = 0; n && (i < absIndex); i++) {
1867 n = n->traverseNextNode();
1872 void DocumentImpl::processHttpEquiv(const DOMString &equiv, const DOMString &content)
1874 assert(!equiv.isNull() && !content.isNull());
1876 KHTMLPart *part = getDocument()->part();
1878 if (strcasecmp(equiv, "default-style") == 0) {
1879 // The preferred style set has been overridden as per section
1880 // 14.3.2 of the HTML4.0 specification. We need to update the
1881 // sheet used variable and then update our style selector.
1882 // For more info, see the test at:
1883 // http://www.hixie.ch/tests/evil/css/import/main/preferred.html
1885 part->d->m_sheetUsed = content.string();
1886 m_preferredStylesheetSet = content;
1887 updateStyleSelector();
1889 else if(strcasecmp(equiv, "refresh") == 0 && part->metaRefreshEnabled())
1891 // get delay and url
1892 QString str = content.string().stripWhiteSpace();
1893 int pos = str.find(QRegExp("[;,]"));
1895 pos = str.find(QRegExp("[ \t]"));
1897 if (pos == -1) // There can be no url (David)
1901 delay = str.toInt(&ok);
1903 // We want a new history item if the refresh timeout > 1 second
1904 if(ok && part) part->scheduleRedirection(delay, part->url().url(), delay <= 1);
1906 if(ok && part) part->scheduleRedirection(delay, part->url().url() );
1911 delay = str.left(pos).stripWhiteSpace().toDouble(&ok);
1914 while(pos < (int)str.length() && str[pos].isSpace()) pos++;
1916 if(str.find("url", 0, false ) == 0) str = str.mid(3);
1917 str = str.stripWhiteSpace();
1918 if ( str.length() && str[0] == '=' ) str = str.mid( 1 ).stripWhiteSpace();
1919 str = parseURL( DOMString(str) ).string();
1922 // We want a new history item if the refresh timeout > 1 second
1923 part->scheduleRedirection(delay, getDocument()->completeURL( str ), delay <= 1);
1925 part->scheduleRedirection(delay, getDocument()->completeURL( str ));
1929 else if(strcasecmp(equiv, "expires") == 0)
1931 QString str = content.string().stripWhiteSpace();
1932 time_t expire_date = str.toLong();
1934 m_docLoader->setExpireDate(expire_date);
1936 else if(strcasecmp(equiv, "pragma") == 0 || strcasecmp(equiv, "cache-control") == 0 && part)
1938 QString str = content.string().lower().stripWhiteSpace();
1939 KURL url = part->url();
1940 if ((str == "no-cache") && url.protocol().startsWith("http"))
1942 KIO::http_update_cache(url, true, 0);
1945 else if( (strcasecmp(equiv, "set-cookie") == 0))
1947 // ### make setCookie work on XML documents too; e.g. in case of <html:meta .....>
1948 HTMLDocumentImpl *d = static_cast<HTMLDocumentImpl *>(this);
1949 d->setCookie(content);
1953 bool DocumentImpl::prepareMouseEvent( bool readonly, int _x, int _y, MouseEvent *ev )
1956 assert(m_render->isCanvas());
1957 RenderObject::NodeInfo renderInfo(readonly, ev->type == MousePress);
1958 bool isInside = m_render->layer()->hitTest(renderInfo, _x, _y);
1959 ev->innerNode = renderInfo.innerNode();
1961 if (renderInfo.URLElement()) {
1962 assert(renderInfo.URLElement()->isElementNode());
1963 ElementImpl* e = static_cast<ElementImpl*>(renderInfo.URLElement());
1964 DOMString href = khtml::parseURL(e->getAttribute(ATTR_HREF));
1965 DOMString target = e->getAttribute(ATTR_TARGET);
1967 if (!target.isNull() && !href.isNull()) {
1968 ev->target = target;
1973 // qDebug("url: *%s*", ev->url.string().latin1());
1986 // DOM Section 1.1.1
1987 bool DocumentImpl::childAllowed( NodeImpl *newChild )
1989 // Documents may contain a maximum of one Element child
1990 if (newChild->nodeType() == Node::ELEMENT_NODE) {
1992 for (c = firstChild(); c; c = c->nextSibling()) {
1993 if (c->nodeType() == Node::ELEMENT_NODE)
1998 // Documents may contain a maximum of one DocumentType child
1999 if (newChild->nodeType() == Node::DOCUMENT_TYPE_NODE) {
2001 for (c = firstChild(); c; c = c->nextSibling()) {
2002 if (c->nodeType() == Node::DOCUMENT_TYPE_NODE)
2007 return childTypeAllowed(newChild->nodeType());
2010 bool DocumentImpl::childTypeAllowed( unsigned short type )
2013 case Node::ELEMENT_NODE:
2014 case Node::PROCESSING_INSTRUCTION_NODE:
2015 case Node::COMMENT_NODE:
2016 case Node::DOCUMENT_TYPE_NODE:
2023 NodeImpl *DocumentImpl::cloneNode ( bool /*deep*/ )
2025 // Spec says cloning Document nodes is "implementation dependent"
2026 // so we do not support it...
2030 NodeImpl::Id DocumentImpl::attrId(DOMStringImpl* _namespaceURI, DOMStringImpl *_name, bool readonly)
2032 // Each document maintains a mapping of attrname -> id for every attr name
2033 // encountered in the document.
2034 // For attrnames without a prefix (no qualified element name) and without matching
2035 // namespace, the value defined in misc/htmlattrs.h is used.
2036 NodeImpl::Id id = 0;
2038 // First see if it's a HTML attribute name
2039 QConstString n(_name->s, _name->l);
2040 if (!_namespaceURI || !strcasecmp(_namespaceURI, XHTML_NAMESPACE)) {
2041 // we're in HTML namespace if we know the tag.
2042 // xhtml is lower case - case sensitive, easy to implement
2043 if ( htmlMode() == XHtml && (id = getAttrID(n.string().ascii(), _name->l)) )
2045 // compatibility: upper case - case insensitive
2046 if ( htmlMode() != XHtml && (id = getAttrID(n.string().lower().ascii(), _name->l )) )
2049 // ok, the fast path didn't work out, we need the full check
2052 // now lets find out the namespace
2053 Q_UINT16 ns = noNamespace;
2054 if (_namespaceURI) {
2055 DOMString nsU(_namespaceURI);
2056 int nsID = XmlNamespaceTable::getNamespaceID(nsU, readonly);
2058 ns = (Q_UINT16)nsID;
2061 // Look in the m_attrNames array for the name
2062 // ### yeah, this is lame. use a dictionary / map instead
2063 DOMString nme(n.string());
2064 // compatibility mode has to store upper case
2065 if (htmlMode() != XHtml) nme = nme.upper();
2066 for (id = 0; id < m_attrNameCount; id++)
2067 if (DOMString(m_attrNames[id]) == nme)
2068 return makeId(ns, ATTR_LAST_ATTR+id);
2071 if (readonly) return 0;
2073 // Name not found in m_attrNames, so let's add it
2074 // ### yeah, this is lame. use a dictionary / map instead
2075 if (m_attrNameCount+1 > m_attrNameAlloc) {
2076 m_attrNameAlloc += 100;
2077 DOMStringImpl **newNames = new DOMStringImpl* [m_attrNameAlloc];
2080 for (i = 0; i < m_attrNameCount; i++)
2081 newNames[i] = m_attrNames[i];
2082 delete [] m_attrNames;
2084 m_attrNames = newNames;
2087 id = m_attrNameCount++;
2088 m_attrNames[id] = nme.implementation();
2089 m_attrNames[id]->ref();
2091 return makeId(ns, ATTR_LAST_ATTR+id);
2094 DOMString DocumentImpl::attrName(NodeImpl::Id _id) const
2097 if (localNamePart(_id) >= ATTR_LAST_ATTR)
2098 result = m_attrNames[localNamePart(_id)-ATTR_LAST_ATTR];
2100 result = getAttrName(_id);
2102 // Attribute names are always lowercase in the DOM for both
2104 if (getDocument()->isHTMLDocument() ||
2105 getDocument()->htmlMode() == DocumentImpl::XHtml)
2106 return result.lower();
2111 NodeImpl::Id DocumentImpl::tagId(DOMStringImpl* _namespaceURI, DOMStringImpl *_name, bool readonly)
2113 if (!_name) return 0;
2114 // Each document maintains a mapping of tag name -> id for every tag name encountered
2116 NodeImpl::Id id = 0;
2118 // First see if it's a HTML element name
2119 QConstString n(_name->s, _name->l);
2120 if (!_namespaceURI || !strcasecmp(_namespaceURI, XHTML_NAMESPACE)) {
2121 // we're in HTML namespace if we know the tag.
2122 // xhtml is lower case - case sensitive, easy to implement
2123 if ( htmlMode() == XHtml && (id = getTagID(n.string().ascii(), _name->l)) )
2125 // compatibility: upper case - case insensitive
2126 if ( htmlMode() != XHtml && (id = getTagID(n.string().lower().ascii(), _name->l )) )
2129 // ok, the fast path didn't work out, we need the full check
2132 // now lets find out the namespace
2133 Q_UINT16 ns = noNamespace;
2134 if (_namespaceURI) {
2135 DOMString nsU(_namespaceURI);
2136 int nsID = XmlNamespaceTable::getNamespaceID(nsU, readonly);
2138 ns = (Q_UINT16)nsID;
2141 // Look in the m_elementNames array for the name
2142 // ### yeah, this is lame. use a dictionary / map instead
2143 DOMString nme(n.string());
2144 // compatibility mode has to store upper case
2145 if (htmlMode() != XHtml) nme = nme.upper();
2146 for (id = 0; id < m_elementNameCount; id++)
2147 if (DOMString(m_elementNames[id]) == nme)
2148 return makeId(ns, ID_LAST_TAG + 1 + id);
2151 if (readonly) return 0;
2153 // Name not found in m_elementNames, so let's add it
2154 if (m_elementNameCount+1 > m_elementNameAlloc) {
2155 m_elementNameAlloc += 100;
2156 DOMStringImpl **newNames = new DOMStringImpl* [m_elementNameAlloc];
2157 // ### yeah, this is lame. use a dictionary / map instead
2158 if (m_elementNames) {
2160 for (i = 0; i < m_elementNameCount; i++)
2161 newNames[i] = m_elementNames[i];
2162 delete [] m_elementNames;
2164 m_elementNames = newNames;
2167 id = m_elementNameCount++;
2168 m_elementNames[id] = nme.implementation();
2169 m_elementNames[id]->ref();
2171 return makeId(ns, ID_LAST_TAG + 1 + id);
2174 DOMString DocumentImpl::tagName(NodeImpl::Id _id) const
2176 if (localNamePart(_id) > ID_LAST_TAG)
2177 return m_elementNames[localNamePart(_id) - (ID_LAST_TAG + 1)];
2179 // ### put them in a cache
2180 if (getDocument()->htmlMode() == DocumentImpl::XHtml)
2181 return getTagName(_id).lower();
2183 return getTagName(_id);
2188 DOMStringImpl* DocumentImpl::namespaceURI(NodeImpl::Id _id) const
2190 if (_id <= ID_LAST_TAG)
2191 return htmlMode() == XHtml ? XmlNamespaceTable::getNamespaceURI(xhtmlNamespace).implementation() : 0;
2193 unsigned short ns = _id >> 16;
2197 return XmlNamespaceTable::getNamespaceURI(ns).implementation();
2200 StyleSheetListImpl* DocumentImpl::styleSheets()
2202 return m_styleSheets;
2206 DocumentImpl::preferredStylesheetSet()
2208 return m_preferredStylesheetSet;
2212 DocumentImpl::selectedStylesheetSet()
2214 return view() ? view()->part()->d->m_sheetUsed : DOMString();
2218 DocumentImpl::setSelectedStylesheetSet(const DOMString& aString)
2221 view()->part()->d->m_sheetUsed = aString.string();
2222 updateStyleSelector();
2224 renderer()->repaint();
2228 // This method is called whenever a top-level stylesheet has finished loading.
2229 void DocumentImpl::stylesheetLoaded()
2231 // Make sure we knew this sheet was pending, and that our count isn't out of sync.
2232 assert(m_pendingStylesheets > 0);
2234 m_pendingStylesheets--;
2236 #ifdef INSTRUMENT_LAYOUT_SCHEDULING
2237 if (!ownerElement())
2238 printf("Stylesheet loaded at time %d. %d stylesheets still remain.\n", elapsedTime(), m_pendingStylesheets);
2241 updateStyleSelector();
2244 void DocumentImpl::updateStyleSelector()
2246 // Don't bother updating, since we haven't loaded all our style info yet.
2247 if (!haveStylesheetsLoaded())
2250 #ifdef INSTRUMENT_LAYOUT_SCHEDULING
2251 if (!ownerElement())
2252 printf("Beginning update of style selector at time %d.\n", elapsedTime());
2255 recalcStyleSelector();
2259 m_styleSelectorDirty = true;
2262 #ifdef INSTRUMENT_LAYOUT_SCHEDULING
2263 if (!ownerElement())
2264 printf("Finished update of style selector at time %d\n", elapsedTime());
2268 renderer()->setNeedsLayoutAndMinMaxRecalc();
2270 view()->scheduleRelayout();
2275 QStringList DocumentImpl::availableStyleSheets() const
2277 return m_availableSheets;
2280 void DocumentImpl::recalcStyleSelector()
2282 if ( !m_render || !attached() ) return;
2284 QPtrList<StyleSheetImpl> oldStyleSheets = m_styleSheets->styleSheets;
2285 m_styleSheets->styleSheets.clear();
2286 m_availableSheets.clear();
2288 for (n = this; n; n = n->traverseNextNode()) {
2289 StyleSheetImpl *sheet = 0;
2291 if (n->nodeType() == Node::PROCESSING_INSTRUCTION_NODE)
2293 // Processing instruction (XML documents only)
2294 ProcessingInstructionImpl* pi = static_cast<ProcessingInstructionImpl*>(n);
2295 sheet = pi->sheet();
2298 applyXSLTransform(pi);
2302 if (!sheet && !pi->localHref().isEmpty())
2304 // Processing instruction with reference to an element in this document - e.g.
2305 // <?xml-stylesheet href="#mystyle">, with the element
2306 // <foo id="mystyle">heading { color: red; }</foo> at some location in
2308 ElementImpl* elem = getElementById(pi->localHref());
2310 DOMString sheetText("");
2312 for (c = elem->firstChild(); c; c = c->nextSibling()) {
2313 if (c->nodeType() == Node::TEXT_NODE || c->nodeType() == Node::CDATA_SECTION_NODE)
2314 sheetText += c->nodeValue();
2317 CSSStyleSheetImpl *cssSheet = new CSSStyleSheetImpl(this);
2318 cssSheet->parseString(sheetText);
2319 pi->setStyleSheet(cssSheet);
2325 else if (n->isHTMLElement() && (n->id() == ID_LINK || n->id() == ID_STYLE)) {
2326 ElementImpl *e = static_cast<ElementImpl *>(n);
2327 QString title = e->getAttribute( ATTR_TITLE ).string();
2328 bool enabledViaScript = false;
2329 if (n->id() == ID_LINK) {
2331 HTMLLinkElementImpl* l = static_cast<HTMLLinkElementImpl*>(n);
2332 if (l->isLoading() || l->isDisabled())
2335 title = QString::null;
2336 enabledViaScript = l->isEnabledViaScript();
2339 // Get the current preferred styleset. This is the
2340 // set of sheets that will be enabled.
2341 if ( n->id() == ID_LINK )
2342 sheet = static_cast<HTMLLinkElementImpl*>(n)->sheet();
2345 sheet = static_cast<HTMLStyleElementImpl*>(n)->sheet();
2347 // Check to see if this sheet belongs to a styleset
2348 // (thus making it PREFERRED or ALTERNATE rather than
2350 if (!enabledViaScript && !title.isEmpty()) {
2351 // Yes, we have a title.
2352 if (m_preferredStylesheetSet.isEmpty()) {
2353 // No preferred set has been established. If
2354 // we are NOT an alternate sheet, then establish
2355 // us as the preferred set. Otherwise, just ignore
2357 QString rel = e->getAttribute( ATTR_REL ).string();
2358 if (n->id() == ID_STYLE || !rel.contains("alternate"))
2359 m_preferredStylesheetSet = view()->part()->d->m_sheetUsed = title;
2362 if (!m_availableSheets.contains( title ) )
2363 m_availableSheets.append( title );
2365 if (title != m_preferredStylesheetSet)
2372 m_styleSheets->styleSheets.append(sheet);
2375 // For HTML documents, stylesheets are not allowed within/after the <BODY> tag. So we
2376 // can stop searching here.
2377 if (isHTMLDocument() && n->id() == ID_BODY)
2381 // De-reference all the stylesheets in the old list
2382 QPtrListIterator<StyleSheetImpl> it(oldStyleSheets);
2383 for (; it.current(); ++it)
2384 it.current()->deref();
2386 // Create a new style selector
2387 delete m_styleSelector;
2388 QString usersheet = m_usersheet;
2389 if ( m_view && m_view->mediaType() == "print" )
2390 usersheet += m_printSheet;
2391 m_styleSelector = new CSSStyleSelector(this, usersheet, m_styleSheets, !inCompatMode());
2392 m_styleSelector->setEncodedURL(m_url);
2393 m_styleSelectorDirty = false;
2396 void DocumentImpl::setHoverNode(NodeImpl* newHoverNode)
2398 if (m_hoverNode != newHoverNode) {
2400 m_hoverNode->deref();
2401 m_hoverNode = newHoverNode;
2409 bool DocumentImpl::relinquishesEditingFocus(NodeImpl *node)
2412 assert(node->isContentEditable());
2414 NodeImpl *rootImpl = node->rootEditableElement();
2415 if (!part() || !rootImpl)
2418 Node root(rootImpl);
2419 Range range(root, 0, root, rootImpl->childNodeCount());
2420 return part()->shouldEndEditing(range);
2423 bool DocumentImpl::acceptsEditingFocus(NodeImpl *node)
2426 assert(node->isContentEditable());
2428 NodeImpl *rootImpl = node->rootEditableElement();
2429 if (!part() || !rootImpl)
2432 Node root(rootImpl);
2433 Range range(root, 0, root, rootImpl->childNodeCount());
2434 return part()->shouldBeginEditing(range);
2437 const QValueList<DashboardRegionValue> & DocumentImpl::dashboardRegions() const
2439 return m_dashboardRegions;
2442 void DocumentImpl::setDashboardRegions (const QValueList<DashboardRegionValue>& regions)
2444 m_dashboardRegions = regions;
2445 setDashboardRegionsDirty (false);
2450 static QWidget *widgetForNode(NodeImpl *focusNode)
2452 RenderObject *renderer = focusNode->renderer();
2453 if (!renderer || !renderer->isWidget())
2455 return static_cast<RenderWidget *>(renderer)->widget();
2458 bool DocumentImpl::setFocusNode(NodeImpl *newFocusNode)
2460 // Make sure newFocusNode is actually in this document
2461 if (newFocusNode && (newFocusNode->getDocument() != this))
2464 if (m_focusNode == newFocusNode)
2468 if (m_focusNode && m_focusNode->isContentEditable() && !relinquishesEditingFocus(m_focusNode))
2472 bool focusChangeBlocked = false;
2473 NodeImpl *oldFocusNode = m_focusNode;
2476 // Remove focus from the existing focus node (if any)
2478 // This goes hand in hand with the Qt focus setting below.
2479 if (!newFocusNode && getDocument()->view()) {
2480 getDocument()->view()->setFocus();
2483 if (oldFocusNode->active())
2484 oldFocusNode->setActive(false);
2486 oldFocusNode->setFocus(false);
2487 oldFocusNode->dispatchHTMLEvent(EventImpl::BLUR_EVENT, false, false);
2488 if (m_focusNode != 0) {
2489 // handler shifted focus
2490 focusChangeBlocked = true;
2493 oldFocusNode->dispatchUIEvent(EventImpl::DOMFOCUSOUT_EVENT);
2494 if (m_focusNode != 0) {
2495 // handler shifted focus
2496 focusChangeBlocked = true;
2499 if ((oldFocusNode == this) && oldFocusNode->hasOneRef()) {
2500 oldFocusNode->deref(); // deletes this
2504 oldFocusNode->deref();
2508 // Clear the selection when changing the focus node to null or to a node that is not
2509 // contained by the current selection.
2511 NodeImpl *startContainer = part()->selection().start().node();
2512 if (!newFocusNode || (startContainer && startContainer != newFocusNode && !startContainer->isAncestor(newFocusNode)))
2513 part()->clearSelection();
2518 if (newFocusNode->isContentEditable() && !acceptsEditingFocus(newFocusNode)) {
2519 // delegate blocks focus change
2520 focusChangeBlocked = true;
2521 goto SetFocusNodeDone;
2524 // Set focus on the new node
2525 m_focusNode = newFocusNode;
2527 m_focusNode->dispatchHTMLEvent(EventImpl::FOCUS_EVENT, false, false);
2528 if (m_focusNode != newFocusNode) {
2529 // handler shifted focus
2530 focusChangeBlocked = true;
2531 goto SetFocusNodeDone;
2533 m_focusNode->dispatchUIEvent(EventImpl::DOMFOCUSIN_EVENT);
2534 if (m_focusNode != newFocusNode) {
2535 // handler shifted focus
2536 focusChangeBlocked = true;
2537 goto SetFocusNodeDone;
2539 m_focusNode->setFocus();
2540 // eww, I suck. set the qt focus correctly
2541 // ### find a better place in the code for this
2542 if (getDocument()->view()) {
2543 QWidget *focusWidget = widgetForNode(m_focusNode);
2545 // Make sure a widget has the right size before giving it focus.
2546 // Otherwise, we are testing edge cases of the QWidget code.
2547 // Specifically, in WebCore this does not work well for text fields.
2548 getDocument()->updateLayout();
2549 // Re-get the widget in case updating the layout changed things.
2550 focusWidget = widgetForNode(m_focusNode);
2553 focusWidget->setFocus();
2555 getDocument()->view()->setFocus();
2560 if (!focusChangeBlocked && m_focusNode && KWQAccObjectCache::accessibilityEnabled())
2561 getAccObjectCache()->handleFocusedUIElementChanged();
2566 return !focusChangeBlocked;
2569 void DocumentImpl::setCSSTarget(NodeImpl* n)
2572 m_cssTarget->setChanged();
2578 NodeImpl* DocumentImpl::getCSSTarget()
2583 void DocumentImpl::attachNodeIterator(NodeIteratorImpl *ni)
2585 m_nodeIterators.append(ni);
2588 void DocumentImpl::detachNodeIterator(NodeIteratorImpl *ni)
2590 m_nodeIterators.remove(ni);
2593 void DocumentImpl::notifyBeforeNodeRemoval(NodeImpl *n)
2595 QPtrListIterator<NodeIteratorImpl> it(m_nodeIterators);
2596 for (; it.current(); ++it)
2597 it.current()->notifyBeforeNodeRemoval(n);
2600 AbstractViewImpl *DocumentImpl::defaultView() const
2602 return m_defaultView;
2605 EventImpl *DocumentImpl::createEvent(const DOMString &eventType, int &exceptioncode)
2607 if (eventType == "UIEvents")
2608 return new UIEventImpl();
2609 else if (eventType == "MouseEvents")
2610 return new MouseEventImpl();
2611 else if (eventType == "MutationEvents")
2612 return new MutationEventImpl();
2613 else if (eventType == "KeyboardEvents")
2614 return new KeyboardEventImpl();
2615 else if (eventType == "HTMLEvents")
2616 return new EventImpl();
2618 exceptioncode = DOMException::NOT_SUPPORTED_ERR;
2623 CSSStyleDeclarationImpl *DocumentImpl::getOverrideStyle(ElementImpl */*elt*/, DOMStringImpl */*pseudoElt*/)
2628 void DocumentImpl::defaultEventHandler(EventImpl *evt)
2630 // if any html event listeners are registered on the window, then dispatch them here
2631 QPtrList<RegisteredEventListener> listenersCopy = m_windowEventListeners;
2632 QPtrListIterator<RegisteredEventListener> it(listenersCopy);
2634 for (; it.current(); ++it) {
2635 if (it.current()->id == evt->id()) {
2636 it.current()->listener->handleEvent(ev, true);
2641 if (evt->id()==EventImpl::KEYDOWN_EVENT) {
2642 KeyboardEventImpl *kevt = static_cast<KeyboardEventImpl *>(evt);
2643 if (kevt->ctrlKey()) {
2644 QString key = kevt->qKeyEvent()->unmodifiedText().lower();
2645 ElementImpl *elem = getElementByAccessKey(key);
2647 elem->accessKeyAction();
2648 evt->setDefaultHandled();
2654 void DocumentImpl::setHTMLWindowEventListener(int id, EventListener *listener)
2656 // If we already have it we don't want removeWindowEventListener to delete it
2659 removeHTMLWindowEventListener(id);
2661 addWindowEventListener(id, listener, false);
2666 EventListener *DocumentImpl::getHTMLWindowEventListener(int id)
2668 QPtrListIterator<RegisteredEventListener> it(m_windowEventListeners);
2669 for (; it.current(); ++it) {
2670 if (it.current()->id == id &&
2671 it.current()->listener->eventListenerType() == "_khtml_HTMLEventListener") {
2672 return it.current()->listener;
2679 void DocumentImpl::removeHTMLWindowEventListener(int id)
2681 QPtrListIterator<RegisteredEventListener> it(m_windowEventListeners);
2682 for (; it.current(); ++it) {
2683 if (it.current()->id == id &&
2684 it.current()->listener->eventListenerType() == "_khtml_HTMLEventListener") {
2685 m_windowEventListeners.removeRef(it.current());
2691 void DocumentImpl::addWindowEventListener(int id, EventListener *listener, const bool useCapture)
2695 // remove existing identical listener set with identical arguments - the DOM2
2696 // spec says that "duplicate instances are discarded" in this case.
2697 removeWindowEventListener(id,listener,useCapture);
2699 RegisteredEventListener *rl = new RegisteredEventListener(static_cast<EventImpl::EventId>(id), listener, useCapture);
2700 m_windowEventListeners.append(rl);
2705 void DocumentImpl::removeWindowEventListener(int id, EventListener *listener, bool useCapture)
2707 RegisteredEventListener rl(static_cast<EventImpl::EventId>(id),listener,useCapture);
2709 QPtrListIterator<RegisteredEventListener> it(m_windowEventListeners);
2710 for (; it.current(); ++it)
2711 if (*(it.current()) == rl) {
2712 m_windowEventListeners.removeRef(it.current());
2717 bool DocumentImpl::hasWindowEventListener(int id)
2719 QPtrListIterator<RegisteredEventListener> it(m_windowEventListeners);
2720 for (; it.current(); ++it) {
2721 if (it.current()->id == id) {
2729 EventListener *DocumentImpl::createHTMLEventListener(QString code, NodeImpl *node)
2732 return part()->createHTMLEventListener(code, node);
2738 void DocumentImpl::dispatchImageLoadEventSoon(HTMLImageLoader *image)
2740 m_imageLoadEventDispatchSoonList.append(image);
2741 if (!m_imageLoadEventTimer) {
2742 m_imageLoadEventTimer = startTimer(0);
2746 void DocumentImpl::removeImage(HTMLImageLoader* image)
2748 // Remove instances of this image from both lists.
2749 // Use loops because we allow multiple instances to get into the lists.
2750 while (m_imageLoadEventDispatchSoonList.removeRef(image)) { }
2751 while (m_imageLoadEventDispatchingList.removeRef(image)) { }
2752 if (m_imageLoadEventDispatchSoonList.isEmpty() && m_imageLoadEventTimer) {
2753 killTimer(m_imageLoadEventTimer);
2754 m_imageLoadEventTimer = 0;
2758 void DocumentImpl::dispatchImageLoadEventsNow()
2760 // need to avoid re-entering this function; if new dispatches are
2761 // scheduled before the parent finishes processing the list, they
2762 // will set a timer and eventually be processed
2763 if (!m_imageLoadEventDispatchingList.isEmpty()) {
2767 if (m_imageLoadEventTimer) {
2768 killTimer(m_imageLoadEventTimer);
2769 m_imageLoadEventTimer = 0;
2772 m_imageLoadEventDispatchingList = m_imageLoadEventDispatchSoonList;
2773 m_imageLoadEventDispatchSoonList.clear();
2774 for (QPtrListIterator<HTMLImageLoader> it(m_imageLoadEventDispatchingList); it.current(); ) {
2775 HTMLImageLoader* image = it.current();
2776 // Must advance iterator *before* dispatching call.
2777 // Otherwise, it might be advanced automatically if dispatching the call had a side effect
2778 // of destroying the current HTMLImageLoader, and then we would advance past the *next* item,
2779 // missing one altogether.
2781 image->dispatchLoadEvent();
2783 m_imageLoadEventDispatchingList.clear();
2786 void DocumentImpl::timerEvent(QTimerEvent *)
2788 dispatchImageLoadEventsNow();
2791 ElementImpl *DocumentImpl::ownerElement()
2793 KHTMLView *childView = view();
2796 KHTMLPart *childPart = childView->part();
2799 KHTMLPart *parent = childPart->parentPart();
2802 ChildFrame *childFrame = parent->childFrame(childPart);
2805 RenderPart *renderPart = childFrame->m_frame;
2808 return static_cast<ElementImpl *>(renderPart->element());
2811 DOMString DocumentImpl::domain() const
2813 if ( m_domain.isEmpty() ) // not set yet (we set it on demand to save time and space)
2814 m_domain = KURL(URL()).host(); // Initially set to the host
2818 void DocumentImpl::setDomain(const DOMString &newDomain, bool force /*=false*/)
2821 m_domain = newDomain;
2824 if ( m_domain.isEmpty() ) // not set yet (we set it on demand to save time and space)
2825 m_domain = KURL(URL()).host(); // Initially set to the host
2827 // Both NS and IE specify that changing the domain is only allowed when
2828 // the new domain is a suffix of the old domain.
2829 int oldLength = m_domain.length();
2830 int newLength = newDomain.length();
2831 if ( newLength < oldLength ) // e.g. newDomain=kde.org (7) and m_domain=www.kde.org (11)
2833 DOMString test = m_domain.copy();
2834 if ( test[oldLength - newLength - 1] == '.' ) // Check that it's a subdomain, not e.g. "de.org"
2836 test.remove( 0, oldLength - newLength ); // now test is "kde.org" from m_domain
2837 if ( test == newDomain ) // and we check that it's the same thing as newDomain
2838 m_domain = newDomain;
2843 bool DocumentImpl::isValidName(const DOMString &name)
2845 static const char validFirstCharacter[] = "ABCDEFGHIJKLMNOPQRSTUVWXZYabcdefghijklmnopqrstuvwxyz";
2846 static const char validSubsequentCharacter[] = "ABCDEFGHIJKLMNOPQRSTUVWXZYabcdefghijklmnopqrstuvwxyz0123456789-_:.";
2847 const unsigned length = name.length();
2850 const QChar * const characters = name.unicode();
2851 const char fc = characters[0];
2854 if (strchr(validFirstCharacter, fc) == 0)
2856 for (unsigned i = 1; i < length; ++i) {
2857 const char sc = characters[i];
2860 if (strchr(validSubsequentCharacter, sc) == 0)
2866 void DocumentImpl::addImageMap(HTMLMapElementImpl *imageMap)
2868 // Add the image map, unless there's already another with that name.
2869 // "First map wins" is the rule other browsers seem to implement.
2870 QString name = imageMap->getName().string();
2871 if (!m_imageMapsByName.contains(name))
2872 m_imageMapsByName.insert(name, imageMap);
2875 void DocumentImpl::removeImageMap(HTMLMapElementImpl *imageMap)
2877 // Remove the image map by name.
2878 // But don't remove some other image map that just happens to have the same name.
2879 QString name = imageMap->getName().string();
2880 QMapIterator<QString, HTMLMapElementImpl *> it = m_imageMapsByName.find(name);
2881 if (it != m_imageMapsByName.end() && *it == imageMap)
2882 m_imageMapsByName.remove(it);
2885 HTMLMapElementImpl *DocumentImpl::getImageMap(const DOMString &URL) const
2891 QString s = URL.string();
2892 int hashPos = s.find('#');
2894 s = s.mid(hashPos + 1);
2896 QMapConstIterator<QString, HTMLMapElementImpl *> it = m_imageMapsByName.find(s);
2897 if (it == m_imageMapsByName.end())
2904 void DocumentImpl::setDecoder(Decoder *decoder)
2910 m_decoder = decoder;
2913 QString DocumentImpl::completeURL(const QString &URL)
2915 return KURL(baseURL(), URL, m_decoder ? m_decoder->codec() : 0).url();
2918 bool DocumentImpl::inPageCache()
2920 return m_inPageCache;
2923 void DocumentImpl::setInPageCache(bool flag)
2925 if (m_inPageCache == flag)
2928 m_inPageCache = flag;
2930 assert(m_savedRenderer == 0);
2931 m_savedRenderer = m_render;
2933 m_view->resetScrollBars();
2936 assert(m_render == 0 || m_render == m_savedRenderer);
2937 m_render = m_savedRenderer;
2938 m_savedRenderer = 0;
2942 void DocumentImpl::passwordFieldAdded()
2947 void DocumentImpl::passwordFieldRemoved()
2949 assert(m_passwordFields > 0);
2953 bool DocumentImpl::hasPasswordField() const
2955 return m_passwordFields > 0;
2958 void DocumentImpl::secureFormAdded()
2963 void DocumentImpl::secureFormRemoved()
2965 assert(m_secureForms > 0);
2969 bool DocumentImpl::hasSecureForm() const
2971 return m_secureForms > 0;
2974 void DocumentImpl::setShouldCreateRenderers(bool f)
2976 m_createRenderers = f;
2979 bool DocumentImpl::shouldCreateRenderers()
2981 return m_createRenderers;
2984 DOMString DocumentImpl::toString() const
2988 for (NodeImpl *child = firstChild(); child != NULL; child = child->nextSibling()) {
2989 result += child->toString();
2995 #endif // APPLE_CHANGES
2997 // ----------------------------------------------------------------------------
2998 // Support for Javascript execCommand, and related methods
3000 JSEditor *DocumentImpl::jsEditor()
3003 m_jsEditor = new JSEditor(this);
3007 bool DocumentImpl::execCommand(const DOMString &command, bool userInterface, const DOMString &value)
3009 return jsEditor()->execCommand(command, userInterface, value);
3012 bool DocumentImpl::queryCommandEnabled(const DOMString &command)
3014 return jsEditor()->queryCommandEnabled(command);
3017 bool DocumentImpl::queryCommandIndeterm(const DOMString &command)
3019 return jsEditor()->queryCommandIndeterm(command);
3022 bool DocumentImpl::queryCommandState(const DOMString &command)
3024 return jsEditor()->queryCommandState(command);
3027 bool DocumentImpl::queryCommandSupported(const DOMString &command)
3029 return jsEditor()->queryCommandSupported(command);
3032 DOMString DocumentImpl::queryCommandValue(const DOMString &command)
3034 return jsEditor()->queryCommandValue(command);
3037 // ----------------------------------------------------------------------------
3039 void DocumentImpl::addMarker(Range range, DocumentMarker::MarkerType type)
3041 // Use a TextIterator to visit the potentially multiple nodes the range covers.
3042 for (TextIterator markedText(range); !markedText.atEnd(); markedText.advance()) {
3043 Range textPiece = markedText.range();
3044 DocumentMarker marker = {type, textPiece.startOffset(), textPiece.endOffset()};
3045 addMarker(textPiece.startContainer().handle(), marker);
3049 void DocumentImpl::removeMarker(Range range, DocumentMarker::MarkerType type)
3051 // Use a TextIterator to visit the potentially multiple nodes the range covers.
3052 for (TextIterator markedText(range); !markedText.atEnd(); markedText.advance()) {
3053 Range textPiece = markedText.range();
3054 DocumentMarker marker = {type, textPiece.startOffset(), textPiece.endOffset()};
3055 removeMarker(textPiece.startContainer().handle(), marker);
3059 // FIXME: We don't deal with markers of more than one type yet
3061 // Markers are stored in order sorted by their location. They do not overlap each other, as currently
3062 // required by the drawing code in render_text.cpp.
3064 void DocumentImpl::addMarker(NodeImpl *node, DocumentMarker newMarker)
3066 assert(newMarker.endOffset >= newMarker.startOffset);
3067 if (newMarker.endOffset == newMarker.startOffset) {
3068 return; // zero length markers are a NOP
3071 QValueList <DocumentMarker> *markers = m_markers.find(node);
3073 markers = new QValueList <DocumentMarker>();
3074 markers->append(newMarker);
3075 m_markers.insert(node, markers);
3077 QValueListIterator<DocumentMarker> it;
3078 for (it = markers->begin(); it != markers->end(); ) {
3079 DocumentMarker marker = *it;
3081 if (newMarker.endOffset < marker.startOffset+1) {
3082 // This is the first marker that is completely after newMarker, and disjoint from it.
3083 // We found our insertion point.
\10
3085 } else if (newMarker.startOffset > marker.endOffset) {
3086 // maker is before newMarker, and disjoint from it. Keep scanning.
3088 } else if (newMarker == marker) {
3089 // already have this one, NOP
3092 // marker and newMarker intersect or touch - merge them into newMarker
3093 newMarker.startOffset = kMin(newMarker.startOffset, marker.startOffset);
3094 newMarker.endOffset = kMax(newMarker.endOffset, marker.endOffset);
3095 // remove old one, we'll add newMarker later
3096 it = markers->remove(it);
3097 // it points to the next marker to consider
3100 // at this point it points to the node before which we want to insert
3101 markers->insert(it, newMarker);
3104 // repaint the affected node
3105 if (node->renderer())
3106 node->renderer()->repaint();
3109 void DocumentImpl::removeMarker(NodeImpl *node, DocumentMarker target)
3111 assert(target.endOffset >= target.startOffset);
3112 if (target.endOffset == target.startOffset) {
3113 return; // zero length markers are a NOP
3116 QValueList <DocumentMarker> *markers = m_markers.find(node);
3121 bool docDirty = false;
3122 QValueListIterator<DocumentMarker> it;
3123 for (it = markers->begin(); it != markers->end(); ) {
3124 DocumentMarker marker = *it;
3126 if (target.endOffset <= marker.startOffset) {
3127 // This is the first marker that is completely after target. All done.
3129 } else if (target.startOffset >= marker.endOffset) {
3130 // marker is before target. Keep scanning.
3133 // at this point we know that marker and target intersect in some way
3136 // pitch the old marker
3137 it = markers->remove(it);
3138 // it now points to the next node
3140 // add either of the resulting slices that are left after removing target
3141 if (target.startOffset > marker.startOffset) {
3142 DocumentMarker newLeft = marker;
3143 newLeft.endOffset = target.startOffset;
3144 markers->insert(it, newLeft);
3146 if (marker.endOffset > target.endOffset) {
3147 DocumentMarker newRight = marker;
3148 newRight.startOffset = target.endOffset;
3149 markers->insert(it, newRight);
3154 // repaint the affected node
3155 if (docDirty && node->renderer())
3156 node->renderer()->repaint();
3159 QValueList<DocumentMarker> DocumentImpl::markersForNode(NodeImpl *node)
3161 QValueList <DocumentMarker> *markers = m_markers.find(node);
3165 return QValueList <DocumentMarker> ();
3169 void DocumentImpl::removeAllMarkers(NodeImpl *node, ulong startOffset, long length)
3171 // FIXME - yet another cheat that relies on us only having one marker type
3172 DocumentMarker marker = {DocumentMarker::Spelling, startOffset, startOffset+length};
3173 removeMarker(node, marker);
3176 void DocumentImpl::removeAllMarkers(NodeImpl *node)
3178 QValueList <DocumentMarker> *markers = m_markers.find(node);
3183 void DocumentImpl::removeAllMarkers()
3188 void DocumentImpl::shiftMarkers(NodeImpl *node, ulong startOffset, long delta)
3190 if (m_markers.isEmpty())
3193 QValueList <DocumentMarker> *markers = m_markers.find(node);
3197 bool docDirty = false;
3198 QValueListIterator<DocumentMarker> it;
3199 for (it = markers->begin(); it != markers->end(); ++it) {
3200 DocumentMarker &marker = *it;
3201 if (marker.startOffset >= startOffset) {
3202 assert((int)marker.startOffset + delta >= 0);
3203 marker.startOffset += delta;
3204 marker.endOffset += delta;
3209 // repaint the affected node
3210 if (docDirty && node->renderer())
3211 node->renderer()->repaint();
3215 void DocumentImpl::applyXSLTransform(ProcessingInstructionImpl* pi)
3217 // Ref ourselves to keep from being destroyed.
3218 XSLTProcessorImpl processor(static_cast<XSLStyleSheetImpl*>(pi->sheet()), this);
3219 processor.transformDocument(this);
3221 // FIXME: If the transform failed we should probably report an error (like Mozilla does) in this
3225 void DocumentImpl::setTransformSourceDocument(DocumentImpl* doc)
3227 if (m_transformSourceDocument)
3228 m_transformSourceDocument->deref();
3229 m_transformSourceDocument = doc;
3236 void DocumentImpl::setDesignMode(InheritedBool value)
3238 m_designMode = value;
3241 DocumentImpl::InheritedBool DocumentImpl::getDesignMode() const
3243 return m_designMode;
3246 bool DocumentImpl::inDesignMode() const
3248 for (const DocumentImpl* d = this; d; d = d->parentDocument()) {
3249 if (d->m_designMode != inherit)
3250 return d->m_designMode;
3255 DocumentImpl *DocumentImpl::parentDocument() const
3257 KHTMLPart *childPart = part();
3260 KHTMLPart *parent = childPart->parentPart();
3263 return parent->xmlDocImpl();
3266 DocumentImpl *DocumentImpl::topDocument() const
3268 DocumentImpl *doc = const_cast<DocumentImpl *>(this);
3269 ElementImpl *element;
3270 while ((element = doc->ownerElement()) != 0) {
3271 doc = element->getDocument();
3272 element = doc ? doc->ownerElement() : 0;
3278 // ----------------------------------------------------------------------------
3280 DocumentFragmentImpl::DocumentFragmentImpl(DocumentPtr *doc) : NodeBaseImpl(doc)
3284 DOMString DocumentFragmentImpl::nodeName() const
3286 return "#document-fragment";
3289 unsigned short DocumentFragmentImpl::nodeType() const
3291 return Node::DOCUMENT_FRAGMENT_NODE;
3294 // DOM Section 1.1.1
3295 bool DocumentFragmentImpl::childTypeAllowed( unsigned short type )
3298 case Node::ELEMENT_NODE:
3299 case Node::PROCESSING_INSTRUCTION_NODE:
3300 case Node::COMMENT_NODE:
3301 case Node::TEXT_NODE:
3302 case Node::CDATA_SECTION_NODE:
3303 case Node::ENTITY_REFERENCE_NODE:
3310 DOMString DocumentFragmentImpl::toString() const
3314 for (NodeImpl *child = firstChild(); child != NULL; child = child->nextSibling()) {
3315 result += child->toString();
3322 NodeImpl *DocumentFragmentImpl::cloneNode ( bool deep )
3324 DocumentFragmentImpl *clone = new DocumentFragmentImpl( docPtr() );
3326 cloneChildNodes(clone);
3331 // ----------------------------------------------------------------------------
3333 DocumentTypeImpl::DocumentTypeImpl(DOMImplementationImpl *implementation, DocumentPtr *doc,
3334 const DOMString &qualifiedName, const DOMString &publicId,
3335 const DOMString &systemId)
3336 : NodeImpl(doc), m_implementation(implementation),
3337 m_qualifiedName(qualifiedName), m_publicId(publicId), m_systemId(systemId)
3339 if (m_implementation)
3340 m_implementation->ref();
3345 // if doc is 0, it is not attached to a document and / or
3346 // therefore does not provide entities or notations. (DOM Level 3)
3349 DocumentTypeImpl::~DocumentTypeImpl()
3351 if (m_implementation)
3352 m_implementation->deref();
3354 m_entities->deref();
3356 m_notations->deref();
3359 void DocumentTypeImpl::copyFrom(const DocumentTypeImpl& other)
3361 m_qualifiedName = other.m_qualifiedName;
3362 m_publicId = other.m_publicId;
3363 m_systemId = other.m_systemId;
3364 m_subset = other.m_subset;
3367 DOMString DocumentTypeImpl::toString() const
3370 if (m_qualifiedName.isEmpty()) {
3373 result = "<!DOCTYPE ";
3374 result += m_qualifiedName;
3376 if (!m_publicId.isEmpty()) {
3377 result += " PUBLIC \"";
3378 result += m_publicId;
3380 result += m_systemId;
3382 } else if (!m_systemId.isEmpty()) {
3383 result += " SYSTEM \"";
3384 result += m_systemId;
3387 if (!m_subset.isEmpty()) {
3396 DOMString DocumentTypeImpl::nodeName() const
3401 unsigned short DocumentTypeImpl::nodeType() const
3403 return Node::DOCUMENT_TYPE_NODE;
3406 // DOM Section 1.1.1
3407 bool DocumentTypeImpl::childTypeAllowed( unsigned short /*type*/ )
3412 NodeImpl *DocumentTypeImpl::cloneNode ( bool /*deep*/ )
3414 // Spec says cloning Document nodes is "implementation dependent"
3415 // so we do not support it...
3419 #include "dom_docimpl.moc"