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 : ContainerNodeImpl( 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_closeAfterStyleRecalc = false;
346 m_usesDescendantRules = false;
347 m_usesSiblingRules = false;
349 m_styleSelector = new CSSStyleSelector(this, m_usersheet, m_styleSheets, !inCompatMode());
350 m_windowEventListeners.setAutoDelete(true);
351 m_pendingStylesheets = 0;
352 m_ignorePendingStylesheets = false;
355 m_accessKeyDictValid = false;
358 resetVisitedLinkColor();
359 resetActiveLinkColor();
361 m_processingLoadEvent = false;
362 m_startTime.restart();
363 m_overMinimumLayoutThreshold = false;
367 m_markers.setAutoDelete(true);
369 static int docID = 0;
373 DocumentImpl::~DocumentImpl()
377 assert(!m_inPageCache);
378 assert(m_savedRenderer == 0);
381 KJS::ScriptInterpreter::forgetDOMObjectsForDocument(this);
383 if (changedDocuments && m_docChanged)
384 changedDocuments->remove(this);
388 delete m_styleSelector;
390 if (m_elemSheet ) m_elemSheet->deref();
393 if (m_implementation)
394 m_implementation->deref();
395 delete m_paintDeviceMetrics;
397 if (m_elementNames) {
398 for (unsigned short id = 0; id < m_elementNameCount; id++)
399 m_elementNames[id]->deref();
400 delete [] m_elementNames;
403 for (unsigned short id = 0; id < m_attrNameCount; id++)
404 m_attrNames[id]->deref();
405 delete [] m_attrNames;
407 m_defaultView->deref();
408 m_styleSheets->deref();
411 m_focusNode->deref();
413 m_hoverNode->deref();
416 delete m_renderArena;
421 xmlFreeDoc((xmlDocPtr)m_transformSource);
422 if (m_transformSourceDocument)
423 m_transformSourceDocument->deref();
427 delete m_bindingManager;
448 void DocumentImpl::resetLinkColor()
450 m_linkColor = QColor(0, 0, 238);
453 void DocumentImpl::resetVisitedLinkColor()
455 m_visitedLinkColor = QColor(85, 26, 139);
458 void DocumentImpl::resetActiveLinkColor()
460 m_activeLinkColor.setNamedColor(QString("red"));
463 DocumentTypeImpl *DocumentImpl::doctype() const
468 DOMImplementationImpl *DocumentImpl::implementation() const
470 return m_implementation;
473 ElementImpl *DocumentImpl::documentElement() const
475 NodeImpl *n = firstChild();
476 while (n && n->nodeType() != Node::ELEMENT_NODE)
477 n = n->nextSibling();
478 return static_cast<ElementImpl*>(n);
481 ElementImpl *DocumentImpl::createElement( const DOMString &name, int &exceptioncode )
483 return new XMLElementImpl( document, name.implementation() );
486 DocumentFragmentImpl *DocumentImpl::createDocumentFragment( )
488 return new DocumentFragmentImpl( docPtr() );
491 TextImpl *DocumentImpl::createTextNode( const DOMString &data )
493 return new TextImpl( docPtr(), data);
496 CommentImpl *DocumentImpl::createComment ( const DOMString &data )
498 return new CommentImpl( docPtr(), data );
501 CDATASectionImpl *DocumentImpl::createCDATASection ( const DOMString &data )
503 return new CDATASectionImpl( docPtr(), data );
506 ProcessingInstructionImpl *DocumentImpl::createProcessingInstruction ( const DOMString &target, const DOMString &data )
508 return new ProcessingInstructionImpl( docPtr(),target,data);
511 Attr DocumentImpl::createAttribute( NodeImpl::Id id )
513 // Assume this is an HTML attribute, since createAttribute isn't namespace-aware. There's no harm to XML
514 // documents if we're wrong.
515 return new AttrImpl(0, docPtr(), new HTMLAttributeImpl(id, DOMString("").implementation()));
518 EntityReferenceImpl *DocumentImpl::createEntityReference ( const DOMString &name )
520 return new EntityReferenceImpl(docPtr(), name.implementation());
523 EditingTextImpl *DocumentImpl::createEditingTextNode(const DOMString &text)
525 return new EditingTextImpl(docPtr(), text);
528 CSSStyleDeclarationImpl *DocumentImpl::createCSSStyleDeclaration()
530 return new CSSMutableStyleDeclarationImpl;
533 NodeImpl *DocumentImpl::importNode(NodeImpl *importedNode, bool deep, int &exceptioncode)
537 switch (importedNode->nodeType()) {
538 case Node::TEXT_NODE:
539 return createTextNode(importedNode->nodeValue());
540 case Node::CDATA_SECTION_NODE:
541 return createCDATASection(importedNode->nodeValue());
542 case Node::ENTITY_REFERENCE_NODE:
543 return createEntityReference(importedNode->nodeName());
544 case Node::PROCESSING_INSTRUCTION_NODE:
545 return createProcessingInstruction(importedNode->nodeName(), importedNode->nodeValue());
546 case Node::COMMENT_NODE:
547 return createComment(importedNode->nodeValue());
548 case Node::ELEMENT_NODE: {
549 ElementImpl *oldElement = static_cast<ElementImpl *>(importedNode);
550 DocumentImpl *oldDoc = oldElement->getDocument();
551 static DOMString HTMLNamespace(XHTML_NAMESPACE);
552 DOMString elementNamespace = oldElement->isHTMLElement() ? HTMLNamespace : oldElement->namespaceURI();
553 ElementImpl *newElement = createElementNS(elementNamespace.implementation(), oldElement->tagName(), exceptioncode);
554 if (exceptioncode != 0)
559 NamedAttrMapImpl *attrs = oldElement->attributes(true);
561 unsigned length = attrs->length();
562 for (unsigned i = 0; i < length; i++) {
563 AttrImpl *attr = attrs->item(i);
564 DOMString qualifiedName = attr->nodeName();
565 DOMString value = attr->nodeValue();
567 int colonpos = qualifiedName.find(':');
568 DOMString localName = qualifiedName;
570 localName.remove(0, colonpos + 1);
571 // ### extract and set new prefix
574 NodeImpl::Id nodeId = attrId(oldDoc->namespaceURI(attr->attrImpl()->id()), localName.implementation(), false /* allocate */);
575 newElement->setAttribute(nodeId, value.implementation(), exceptioncode);
576 if (exceptioncode != 0) {
584 for (NodeImpl *oldChild = oldElement->firstChild(); oldChild; oldChild = oldChild->nextSibling()) {
585 NodeImpl *newChild = importNode(oldChild, true, exceptioncode);
586 if (exceptioncode != 0) {
590 newElement->appendChild(newChild, exceptioncode);
591 if (exceptioncode != 0) {
598 // Trick to get the result back to the floating state, with 0 refs but not destroyed.
599 newElement->setParent(this);
601 newElement->setParent(0);
607 exceptioncode = DOMException::NOT_SUPPORTED_ERR;
611 ElementImpl *DocumentImpl::createElementNS( const DOMString &_namespaceURI, const DOMString &_qualifiedName, int &exceptioncode)
614 QString qName = _qualifiedName.string();
615 int colonPos = qName.find(':',0);
617 if (_namespaceURI == XHTML_NAMESPACE) {
618 // User requested an element in the XHTML namespace - this means we create a HTML element
619 // (elements not in this namespace are treated as normal XML elements)
620 e = createHTMLElement(qName.mid(colonPos+1), exceptioncode);
623 if (e && colonPos >= 0) {
624 e->setPrefix(qName.left(colonPos), exceptioncode);
632 e = new XMLElementImpl( document, _qualifiedName.implementation(), _namespaceURI.implementation() );
637 ElementImpl *DocumentImpl::getElementById( const DOMString &elementId ) const
639 if (elementId.length() == 0) {
643 return m_elementsById.find(elementId.string());
646 ElementImpl *DocumentImpl::elementFromPoint( const int _x, const int _y ) const
648 if (!m_render) return 0;
650 RenderObject::NodeInfo nodeInfo(true, true);
651 m_render->layer()->hitTest(nodeInfo, _x, _y);
652 NodeImpl* n = nodeInfo.innerNode();
654 while ( n && !n->isElementNode() ) {
658 return static_cast<ElementImpl*>(n);
661 void DocumentImpl::addElementById(const DOMString &elementId, ElementImpl *element)
663 QString qId = elementId.string();
665 if (m_elementsById.find(qId) == NULL) {
666 m_elementsById.insert(qId, element);
667 m_accessKeyDictValid = false;
671 void DocumentImpl::removeElementById(const DOMString &elementId, ElementImpl *element)
673 QString qId = elementId.string();
675 if (m_elementsById.find(qId) == element) {
676 m_elementsById.remove(qId);
677 m_accessKeyDictValid = false;
681 ElementImpl *DocumentImpl::getElementByAccessKey( const DOMString &key )
683 if (key.length() == 0)
686 QString k(key.string());
687 if (!m_accessKeyDictValid) {
688 m_elementsByAccessKey.clear();
691 for (n = this; n != 0; n = n->traverseNextNode()) {
692 if (!n->isElementNode())
694 const ElementImpl *elementImpl = static_cast<const ElementImpl *>(n);
695 DOMString accessKey(elementImpl->getAttribute(ATTR_ACCESSKEY));
696 if (!accessKey.isEmpty()) {
697 QString ak = accessKey.string().lower();
698 if (m_elementsByAccessKey.find(ak) == NULL)
699 m_elementsByAccessKey.insert(ak, elementImpl);
702 m_accessKeyDictValid = true;
704 return m_elementsByAccessKey.find(k);
707 void DocumentImpl::setTitle(DOMString _title)
715 KWQ(part())->setTitle(_title);
717 QString titleStr = m_title.string();
718 for (int i = 0; i < titleStr.length(); ++i)
719 if (titleStr[i] < ' ')
721 titleStr = titleStr.stripWhiteSpace();
723 if ( !part()->parentPart() ) {
724 if (titleStr.isNull() || titleStr.isEmpty()) {
725 // empty title... set window caption as the URL
727 url.setRef(QString::null);
728 url.setQuery(QString::null);
729 titleStr = url.url();
732 emit part()->setWindowCaption( KStringHandler::csqueeze( titleStr, 128 ) );
737 DOMString DocumentImpl::nodeName() const
742 unsigned short DocumentImpl::nodeType() const
744 return Node::DOCUMENT_NODE;
747 ElementImpl *DocumentImpl::createHTMLElement( const DOMString &name, int &exceptioncode )
749 if (!isValidName(name)) {
750 exceptioncode = DOMException::INVALID_CHARACTER_ERR;
753 return createHTMLElement(tagId(0, name.implementation(), false));
756 ElementImpl *DocumentImpl::createHTMLElement(unsigned short tagID)
761 return new HTMLHtmlElementImpl(docPtr());
763 return new HTMLHeadElementImpl(docPtr());
765 return new HTMLBodyElementImpl(docPtr());
769 return new HTMLBaseElementImpl(docPtr());
771 return new HTMLLinkElementImpl(docPtr());
773 return new HTMLMetaElementImpl(docPtr());
775 return new HTMLStyleElementImpl(docPtr());
777 return new HTMLTitleElementImpl(docPtr());
781 return new HTMLFrameElementImpl(docPtr());
783 return new HTMLFrameSetElementImpl(docPtr());
785 return new HTMLIFrameElementImpl(docPtr());
788 // ### FIXME: we need a way to set form dependency after we have made the form elements
790 return new HTMLFormElementImpl(docPtr());
792 return new HTMLButtonElementImpl(docPtr());
794 return new HTMLFieldSetElementImpl(docPtr());
796 return new HTMLInputElementImpl(docPtr());
798 return new HTMLIsIndexElementImpl(docPtr());
800 return new HTMLLabelElementImpl(docPtr());
802 return new HTMLLegendElementImpl(docPtr());
804 return new HTMLOptGroupElementImpl(docPtr());
806 return new HTMLOptionElementImpl(docPtr());
808 return new HTMLSelectElementImpl(docPtr());
810 return new HTMLTextAreaElementImpl(docPtr());
814 return new HTMLDListElementImpl(docPtr());
816 return new HTMLGenericElementImpl(docPtr(), tagID);
818 return new HTMLGenericElementImpl(docPtr(), tagID);
820 return new HTMLUListElementImpl(docPtr());
822 return new HTMLOListElementImpl(docPtr());
824 return new HTMLDirectoryElementImpl(docPtr());
826 return new HTMLMenuElementImpl(docPtr());
828 return new HTMLLIElementImpl(docPtr());
830 // formatting elements (block)
832 return new HTMLBlockquoteElementImpl(docPtr());
834 return new HTMLDivElementImpl(docPtr());
841 return new HTMLHeadingElementImpl(docPtr(), tagID);
843 return new HTMLHRElementImpl(docPtr());
845 return new HTMLParagraphElementImpl(docPtr());
849 return new HTMLPreElementImpl(docPtr(), tagID);
851 return new HTMLLayerElementImpl(docPtr());
855 return new HTMLBaseFontElementImpl(docPtr());
857 return new HTMLFontElementImpl(docPtr());
862 return new HTMLGenericElementImpl(docPtr(), tagID);
866 return new HTMLAnchorElementImpl(docPtr());
870 return new HTMLImageElementImpl(docPtr());
872 return new HTMLMapElementImpl(docPtr());
874 return new HTMLAreaElementImpl(docPtr());
876 return new HTMLCanvasElementImpl(docPtr());
878 // objects, applets and scripts
880 return new HTMLAppletElementImpl(docPtr());
882 return new HTMLEmbedElementImpl(docPtr());
884 return new HTMLObjectElementImpl(docPtr());
886 return new HTMLParamElementImpl(docPtr());
888 return new HTMLScriptElementImpl(docPtr());
892 return new HTMLTableElementImpl(docPtr());
894 return new HTMLTableCaptionElementImpl(docPtr());
897 return new HTMLTableColElementImpl(docPtr(), tagID);
899 return new HTMLTableRowElementImpl(docPtr());
902 return new HTMLTableCellElementImpl(docPtr(), tagID);
906 return new HTMLTableSectionElementImpl(docPtr(), tagID, false);
910 return new HTMLBRElementImpl(docPtr());
912 return new HTMLGenericElementImpl(docPtr(), tagID);
914 // elements with no special representation in the DOM
952 return new HTMLGenericElementImpl(docPtr(), tagID);
955 return new HTMLMarqueeElementImpl(docPtr());
959 kdDebug( 6020 ) << "Use document->createTextNode()" << endl;
966 QString DocumentImpl::nextState()
969 if (!m_state.isEmpty())
971 state = m_state.first();
972 m_state.remove(m_state.begin());
977 QStringList DocumentImpl::docState()
980 for (QPtrListIterator<NodeImpl> it(m_maintainsState); it.current(); ++it)
981 s.append(it.current()->state());
986 KHTMLPart *DocumentImpl::part() const
988 return m_view ? m_view->part() : 0;
991 RangeImpl *DocumentImpl::createRange()
993 return new RangeImpl( docPtr() );
996 NodeIteratorImpl *DocumentImpl::createNodeIterator(NodeImpl *root, unsigned long whatToShow,
997 NodeFilterImpl *filter, bool expandEntityReferences, int &exceptioncode)
1000 exceptioncode = DOMException::NOT_SUPPORTED_ERR;
1003 return new NodeIteratorImpl(root, whatToShow, filter, expandEntityReferences);
1006 TreeWalkerImpl *DocumentImpl::createTreeWalker(NodeImpl *root, unsigned long whatToShow,
1007 NodeFilterImpl *filter, bool expandEntityReferences, int &exceptioncode)
1010 exceptioncode = DOMException::NOT_SUPPORTED_ERR;
1013 return new TreeWalkerImpl(root, whatToShow, filter, expandEntityReferences);
1016 void DocumentImpl::setDocumentChanged(bool b)
1018 if (!changedDocuments)
1019 changedDocuments = s_changedDocumentsDeleter.setObject( new QPtrList<DocumentImpl>() );
1021 if (b && !m_docChanged)
1022 changedDocuments->append(this);
1023 else if (!b && m_docChanged)
1024 changedDocuments->remove(this);
1028 m_accessKeyDictValid = false;
1031 void DocumentImpl::recalcStyle( StyleChange change )
1033 // qDebug("recalcStyle(%p)", this);
1036 if (m_inStyleRecalc)
1037 return; // Guard against re-entrancy. -dwh
1039 m_inStyleRecalc = true;
1041 if( !m_render ) goto bail_out;
1043 if ( change == Force ) {
1044 RenderStyle* oldStyle = m_render->style();
1045 if ( oldStyle ) oldStyle->ref();
1046 RenderStyle* _style = new (m_renderArena) RenderStyle();
1048 _style->setDisplay(BLOCK);
1049 _style->setVisuallyOrdered( visuallyOrdered );
1050 // ### make the font stuff _really_ work!!!!
1052 khtml::FontDef fontDef;
1053 QFont f = KGlobalSettings::generalFont();
1054 fontDef.family = *(f.firstFamily());
1055 fontDef.italic = f.italic();
1056 fontDef.weight = f.weight();
1058 bool printing = m_paintDevice && (m_paintDevice->devType() == QInternal::Printer);
1059 fontDef.usePrinterFont = printing;
1062 const KHTMLSettings *settings = m_view->part()->settings();
1064 if (printing && !settings->shouldPrintBackgrounds()) {
1065 _style->setForceBackgroundsToWhite(true);
1068 QString stdfont = settings->stdFontName();
1069 if ( !stdfont.isEmpty() ) {
1070 fontDef.family.setFamily(stdfont);
1071 fontDef.family.appendFamily(0);
1073 m_styleSelector->setFontSize(fontDef, m_styleSelector->fontSizeForKeyword(CSS_VAL_MEDIUM, inCompatMode()));
1076 //kdDebug() << "DocumentImpl::attach: setting to charset " << settings->charset() << endl;
1077 _style->setFontDef(fontDef);
1078 _style->htmlFont().update( paintDeviceMetrics() );
1079 if ( inCompatMode() )
1080 _style->setHtmlHacks(true); // enable html specific rendering tricks
1082 StyleChange ch = diff( _style, oldStyle );
1083 if(m_render && ch != NoChange)
1084 m_render->setStyle(_style);
1085 if ( change != Force )
1088 _style->deref(m_renderArena);
1090 oldStyle->deref(m_renderArena);
1094 for (n = _first; n; n = n->nextSibling())
1095 if ( change>= Inherit || n->hasChangedChild() || n->changed() )
1096 n->recalcStyle( change );
1097 //kdDebug( 6020 ) << "TIME: recalcStyle() dt=" << qt.elapsed() << endl;
1099 if (changed() && m_view)
1103 setChanged( false );
1104 setHasChangedChild( false );
1105 setDocumentChanged( false );
1107 m_inStyleRecalc = false;
1109 // If we wanted to emit the implicitClose() during recalcStyle, do so now that we're finished.
1110 if (m_closeAfterStyleRecalc) {
1111 m_closeAfterStyleRecalc = false;
1116 void DocumentImpl::updateRendering()
1118 if (!hasChangedChild()) return;
1122 // kdDebug() << "UPDATERENDERING: "<<endl;
1124 StyleChange change = NoChange;
1126 if ( m_styleSelectorDirty ) {
1127 recalcStyleSelector();
1131 recalcStyle( change );
1133 // kdDebug() << "UPDATERENDERING time used="<<time.elapsed()<<endl;
1136 void DocumentImpl::updateDocumentsRendering()
1138 if (!changedDocuments)
1141 while (DocumentImpl* doc = changedDocuments->take()) {
1142 doc->m_docChanged = false;
1143 doc->updateRendering();
1147 void DocumentImpl::updateLayout()
1149 // FIXME: Dave's pretty sure we can remove this because
1150 // layout calls recalcStyle as needed.
1153 // Only do a layout if changes have occurred that make it necessary.
1154 if (m_view && renderer() && renderer()->needsLayout())
1158 // FIXME: This is a bad idea and needs to be removed eventually.
1159 // Other browsers load stylesheets before they continue parsing the web page.
1160 // Since we don't, we can run JavaScript code that needs answers before the
1161 // stylesheets are loaded. Doing a layout ignoring the pending stylesheets
1162 // lets us get reasonable answers. The long term solution to this problem is
1163 // to instead suspend JavaScript execution.
1164 void DocumentImpl::updateLayoutIgnorePendingStylesheets()
1166 bool oldIgnore = m_ignorePendingStylesheets;
1168 if (!haveStylesheetsLoaded()) {
1169 m_ignorePendingStylesheets = true;
1170 updateStyleSelector();
1175 m_ignorePendingStylesheets = oldIgnore;
1178 void DocumentImpl::attach()
1180 assert(!attached());
1182 assert(!m_inPageCache);
1186 setPaintDevice( m_view );
1189 m_renderArena = new RenderArena();
1191 // Create the rendering tree
1192 m_render = new (m_renderArena) RenderCanvas(this, m_view);
1193 recalcStyle( Force );
1195 RenderObject* render = m_render;
1198 ContainerNodeImpl::attach();
1202 void DocumentImpl::restoreRenderer(RenderObject* render)
1207 void DocumentImpl::detach()
1209 RenderObject* render = m_render;
1211 // indicate destruction mode, i.e. attached() but m_render == 0
1215 if (m_inPageCache) {
1217 getAccObjectCache()->detach(render);
1222 // Empty out these lists as a performance optimization, since detaching
1223 // all the individual render objects will cause all the RenderImage
1224 // objects to remove themselves from the lists.
1225 m_imageLoadEventDispatchSoonList.clear();
1226 m_imageLoadEventDispatchingList.clear();
1229 // FIXME: UNLOAD_EVENT will not dispatch due to deleting event listeners prior to closeURL().
1230 removeAllEventListenersFromAllNodes();
1232 ContainerNodeImpl::detach();
1237 if (m_paintDevice == m_view)
1242 delete m_renderArena;
1247 void DocumentImpl::removeAllEventListenersFromAllNodes()
1249 m_windowEventListeners.clear();
1250 removeAllDisconnectedNodeEventListeners();
1251 for (NodeImpl *n = this; n; n = n->traverseNextNode()) {
1252 n->removeAllEventListeners();
1256 void DocumentImpl::registerDisconnectedNodeWithEventListeners(NodeImpl *node)
1258 m_disconnectedNodesWithEventListeners.insert(node, node);
1261 void DocumentImpl::unregisterDisconnectedNodeWithEventListeners(NodeImpl *node)
1263 m_disconnectedNodesWithEventListeners.remove(node);
1266 void DocumentImpl::removeAllDisconnectedNodeEventListeners()
1268 for (QPtrDictIterator<NodeImpl> iter(m_disconnectedNodesWithEventListeners);
1271 iter.current()->removeAllEventListeners();
1276 KWQAccObjectCache* DocumentImpl::getAccObjectCache()
1278 // The only document that actually has a KWQAccObjectCache is the top-level
1279 // document. This is because we need to be able to get from any KWQAccObject
1280 // to any other KWQAccObject on the same page. Using a single cache allows
1281 // lookups across nested webareas (i.e. multiple documents).
1284 // return already known top-level cache
1285 if (!ownerElement())
1288 // In some pages with frames, the cache is created before the sub-webarea is
1289 // inserted into the tree. Here, we catch that case and just toss the old
1290 // cache and start over.
1295 // look for top-level document
1296 ElementImpl *element = ownerElement();
1300 doc = element->getDocument();
1301 element = doc->ownerElement();
1304 // ask the top-level document for its cache
1305 return doc->getAccObjectCache();
1308 // this is the top-level document, so install a new cache
1309 m_accCache = new KWQAccObjectCache;
1314 void DocumentImpl::setVisuallyOrdered()
1316 visuallyOrdered = true;
1318 m_render->style()->setVisuallyOrdered(true);
1321 void DocumentImpl::updateSelection()
1326 RenderCanvas *canvas = static_cast<RenderCanvas*>(m_render);
1327 Selection s = part()->selection();
1329 canvas->clearSelection();
1332 Position startPos = VisiblePosition(s.start(), s.startAffinity(), khtml::VisiblePosition::INIT_UP).deepEquivalent();
1333 Position endPos = VisiblePosition(s.end(), s.endAffinity(), khtml::VisiblePosition::INIT_DOWN).deepEquivalent();
1334 if (startPos.isNotNull() && endPos.isNotNull()) {
1335 RenderObject *startRenderer = startPos.node()->renderer();
1336 RenderObject *endRenderer = endPos.node()->renderer();
1337 static_cast<RenderCanvas*>(m_render)->setSelection(startRenderer, startPos.offset(), endRenderer, endPos.offset());
1342 // send the AXSelectedTextChanged notification only if the new selection is non-null,
1343 // because null selections are only transitory (e.g. when starting an EditCommand, currently)
1344 if (KWQAccObjectCache::accessibilityEnabled() && s.start().isNotNull() && s.end().isNotNull()) {
1345 getAccObjectCache()->postNotificationToTopWebArea(renderer(), "AXSelectedTextChanged");
1351 Tokenizer *DocumentImpl::createTokenizer()
1353 return newXMLTokenizer(docPtr(), m_view);
1356 void DocumentImpl::setPaintDevice( QPaintDevice *dev )
1358 if (m_paintDevice == dev) {
1361 m_paintDevice = dev;
1362 delete m_paintDeviceMetrics;
1363 m_paintDeviceMetrics = dev ? new QPaintDeviceMetrics( dev ) : 0;
1366 void DocumentImpl::open( )
1368 if (parsing()) return;
1373 part()->didExplicitOpen();
1376 // This is work that we should probably do in clear(), but we can't have it
1377 // happen when implicitOpen() is called unless we reorganize KHTMLPart code.
1379 DocumentImpl *parent = parentDocument();
1381 setBaseURL(parent->baseURL());
1385 void DocumentImpl::implicitOpen()
1391 m_tokenizer = createTokenizer();
1392 connect(m_tokenizer,SIGNAL(finishedParsing()),this,SIGNAL(finishedParsing()));
1395 if (m_view && m_view->part()->jScript()) {
1396 m_view->part()->jScript()->setSourceFile(m_url,""); //fixme
1400 HTMLElementImpl* DocumentImpl::body()
1402 NodeImpl *de = documentElement();
1406 // try to prefer a FRAMESET element over BODY
1408 for (NodeImpl* i = de->firstChild(); i; i = i->nextSibling()) {
1409 if (i->id() == ID_FRAMESET)
1410 return static_cast<HTMLElementImpl*>(i);
1412 if (i->id() == ID_BODY)
1415 return static_cast<HTMLElementImpl *>(body);
1418 void DocumentImpl::close()
1421 part()->endIfNotLoading();
1425 void DocumentImpl::implicitClose()
1427 // If we're in the middle of recalcStyle, we need to defer the close until the style information is accurate and all elements are re-attached.
1428 if (m_inStyleRecalc) {
1429 m_closeAfterStyleRecalc = true;
1433 // First fire the onload.
1435 bool wasLocationChangePending = part() && part()->isScheduledLocationChangePending();
1436 bool doload = !parsing() && m_tokenizer && !m_processingLoadEvent && !wasLocationChangePending;
1439 m_processingLoadEvent = true;
1441 // We have to clear the tokenizer, in case someone document.write()s from the
1442 // onLoad event handler, as in Radar 3206524
1446 // Create a body element if we don't already have one.
1447 // In the case of Radar 3758785, the window.onload was set in some javascript, but never fired because there was no body.
1448 // This behavior now matches Firefox and IE.
1449 HTMLElementImpl *body = this->body();
1450 if (!body && isHTMLDocument()) {
1451 NodeImpl *de = documentElement();
1453 body = new HTMLBodyElementImpl(docPtr());
1454 int exceptionCode = 0;
1455 de->appendChild(body, exceptionCode);
1456 if (exceptionCode != 0)
1462 dispatchImageLoadEventsNow();
1463 body->dispatchWindowEvent(EventImpl::LOAD_EVENT, false, false);
1465 #ifdef INSTRUMENT_LAYOUT_SCHEDULING
1466 if (!ownerElement())
1467 printf("onload fired at %d\n", elapsedTime());
1471 m_processingLoadEvent = false;
1474 // Make sure both the initial layout and reflow happen after the onload
1475 // fires. This will improve onload scores, and other browsers do it.
1476 // If they wanna cheat, we can too. -dwh
1478 bool isLocationChangePending = part() && part()->isScheduledLocationChangePending();
1480 if (doload && isLocationChangePending && m_startTime.elapsed() < cLayoutScheduleThreshold) {
1481 // Just bail out. Before or during the onload we were shifted to another page.
1482 // The old i-Bench suite does this. When this happens don't bother painting or laying out.
1485 view()->unscheduleRelayout();
1490 // on an explicit document.close(), the tokenizer might still be waiting on scripts,
1491 // and in that case we don't want to destroy it because that will prevent the
1492 // scripts from getting processed.
1493 // FIXME: this check may no longer be necessary, since now it should be impossible
1494 // for parsing to be false while stil waiting for scripts
1495 if (m_tokenizer && !m_tokenizer->isWaitingForScripts()) {
1501 m_view->part()->checkEmitLoadEvent();
1504 // Now do our painting/layout, but only if we aren't in a subframe or if we're in a subframe
1505 // that has been sized already. Otherwise, our view size would be incorrect, so doing any
1506 // layout/painting now would be pointless.
1508 if (!ownerElement() || (ownerElement()->renderer() && !ownerElement()->renderer()->needsLayout())) {
1511 // Always do a layout after loading if needed.
1512 if (view() && renderer() && (!renderer()->firstChild() || renderer()->needsLayout()))
1516 if (renderer() && KWQAccObjectCache::accessibilityEnabled())
1517 getAccObjectCache()->postNotification(renderer(), "AXLoadComplete");
1522 void DocumentImpl::setParsing(bool b)
1525 if (!m_bParsing && view())
1526 view()->scheduleRelayout();
1528 #ifdef INSTRUMENT_LAYOUT_SCHEDULING
1529 if (!ownerElement() && !m_bParsing)
1530 printf("Parsing finished at %d\n", elapsedTime());
1534 bool DocumentImpl::shouldScheduleLayout()
1536 // We can update layout if:
1537 // (a) we actually need a layout
1538 // (b) our stylesheets are all loaded
1539 // (c) we have a <body>
1540 return (renderer() && renderer()->needsLayout() && haveStylesheetsLoaded() &&
1541 documentElement() && documentElement()->renderer() &&
1542 (documentElement()->id() != ID_HTML || body()));
1545 int DocumentImpl::minimumLayoutDelay()
1547 if (m_overMinimumLayoutThreshold)
1550 int elapsed = m_startTime.elapsed();
1551 m_overMinimumLayoutThreshold = elapsed > cLayoutScheduleThreshold;
1553 // We'll want to schedule the timer to fire at the minimum layout threshold.
1554 return kMax(0, cLayoutScheduleThreshold - elapsed);
1557 int DocumentImpl::elapsedTime() const
1559 return m_startTime.elapsed();
1562 void DocumentImpl::write( const DOMString &text )
1564 write(text.string());
1567 void DocumentImpl::write( const QString &text )
1569 #ifdef INSTRUMENT_LAYOUT_SCHEDULING
1570 if (!ownerElement())
1571 printf("Beginning a document.write at %d\n", elapsedTime());
1576 write(QString::fromLatin1("<html>"));
1578 m_tokenizer->write(text, false);
1580 if (m_view && m_view->part()->jScript())
1581 m_view->part()->jScript()->appendSourceFile(m_url,text);
1583 #ifdef INSTRUMENT_LAYOUT_SCHEDULING
1584 if (!ownerElement())
1585 printf("Ending a document.write at %d\n", elapsedTime());
1589 void DocumentImpl::writeln( const DOMString &text )
1592 write(DOMString("\n"));
1595 void DocumentImpl::finishParsing()
1597 #ifdef INSTRUMENT_LAYOUT_SCHEDULING
1598 if (!ownerElement())
1599 printf("Received all data at %d\n", elapsedTime());
1602 // Let the tokenizer go through as much data as it can. There will be three possible outcomes after
1603 // finish() is called:
1604 // (1) All remaining data is parsed, document isn't loaded yet
1605 // (2) All remaining data is parsed, document is loaded, tokenizer gets deleted
1606 // (3) Data is still remaining to be parsed.
1608 m_tokenizer->finish();
1611 void DocumentImpl::clear()
1617 QPtrListIterator<RegisteredEventListener> it(m_windowEventListeners);
1618 for (; it.current();)
1619 m_windowEventListeners.removeRef(it.current());
1622 void DocumentImpl::setURL(const QString& url)
1625 if (m_styleSelector)
1626 m_styleSelector->setEncodedURL(m_url);
1629 void DocumentImpl::setStyleSheet(const DOMString &url, const DOMString &sheet)
1631 // kdDebug( 6030 ) << "HTMLDocument::setStyleSheet()" << endl;
1632 m_sheet = new CSSStyleSheetImpl(this, url);
1634 m_sheet->parseString(sheet);
1635 m_loadingSheet = false;
1637 updateStyleSelector();
1640 void DocumentImpl::setUserStyleSheet( const QString& sheet )
1642 if ( m_usersheet != sheet ) {
1643 m_usersheet = sheet;
1644 updateStyleSelector();
1648 CSSStyleSheetImpl* DocumentImpl::elementSheet()
1651 m_elemSheet = new CSSStyleSheetImpl(this, baseURL() );
1657 void DocumentImpl::determineParseMode( const QString &/*str*/ )
1659 // For XML documents use strict parse mode. HTML docs will override this method to
1660 // determine their parse mode.
1663 kdDebug(6020) << " using strict parseMode" << endl;
1666 // Please see if there`s a possibility to merge that code
1667 // with the next function and getElementByID().
1668 NodeImpl *DocumentImpl::findElement( Id id )
1670 QPtrStack<NodeImpl> nodeStack;
1671 NodeImpl *current = _first;
1677 if(nodeStack.isEmpty()) break;
1678 current = nodeStack.pop();
1679 current = current->nextSibling();
1683 if(current->id() == id)
1686 NodeImpl *child = current->firstChild();
1689 nodeStack.push(current);
1694 current = current->nextSibling();
1702 NodeImpl *DocumentImpl::nextFocusNode(NodeImpl *fromNode)
1704 unsigned short fromTabIndex;
1707 // No starting node supplied; begin with the top of the document
1710 int lowestTabIndex = 65535;
1711 for (n = this; n != 0; n = n->traverseNextNode()) {
1712 if (n->isKeyboardFocusable()) {
1713 if ((n->tabIndex() > 0) && (n->tabIndex() < lowestTabIndex))
1714 lowestTabIndex = n->tabIndex();
1718 if (lowestTabIndex == 65535)
1721 // Go to the first node in the document that has the desired tab index
1722 for (n = this; n != 0; n = n->traverseNextNode()) {
1723 if (n->isKeyboardFocusable() && (n->tabIndex() == lowestTabIndex))
1730 fromTabIndex = fromNode->tabIndex();
1733 if (fromTabIndex == 0) {
1734 // Just need to find the next selectable node after fromNode (in document order) that doesn't have a tab index
1735 NodeImpl *n = fromNode->traverseNextNode();
1736 while (n && !(n->isKeyboardFocusable() && n->tabIndex() == 0))
1737 n = n->traverseNextNode();
1741 // Find the lowest tab index out of all the nodes except fromNode, that is greater than or equal to fromNode's
1742 // tab index. For nodes with the same tab index as fromNode, we are only interested in those that come after
1743 // fromNode in document order.
1744 // If we don't find a suitable tab index, the next focus node will be one with a tab index of 0.
1745 unsigned short lowestSuitableTabIndex = 65535;
1748 bool reachedFromNode = false;
1749 for (n = this; n != 0; n = n->traverseNextNode()) {
1750 if (n->isKeyboardFocusable() &&
1751 ((reachedFromNode && (n->tabIndex() >= fromTabIndex)) ||
1752 (!reachedFromNode && (n->tabIndex() > fromTabIndex))) &&
1753 (n->tabIndex() < lowestSuitableTabIndex) &&
1756 // We found a selectable node with a tab index at least as high as fromNode's. Keep searching though,
1757 // as there may be another node which has a lower tab index but is still suitable for use.
1758 lowestSuitableTabIndex = n->tabIndex();
1762 reachedFromNode = true;
1765 if (lowestSuitableTabIndex == 65535) {
1766 // No next node with a tab index -> just take first node with tab index of 0
1768 while (n && !(n->isKeyboardFocusable() && n->tabIndex() == 0))
1769 n = n->traverseNextNode();
1773 // Search forwards from fromNode
1774 for (n = fromNode->traverseNextNode(); n != 0; n = n->traverseNextNode()) {
1775 if (n->isKeyboardFocusable() && (n->tabIndex() == lowestSuitableTabIndex))
1779 // The next node isn't after fromNode, start from the beginning of the document
1780 for (n = this; n != fromNode; n = n->traverseNextNode()) {
1781 if (n->isKeyboardFocusable() && (n->tabIndex() == lowestSuitableTabIndex))
1785 assert(false); // should never get here
1790 NodeImpl *DocumentImpl::previousFocusNode(NodeImpl *fromNode)
1792 NodeImpl *lastNode = this;
1793 while (lastNode->lastChild())
1794 lastNode = lastNode->lastChild();
1797 // No starting node supplied; begin with the very last node in the document
1800 int highestTabIndex = 0;
1801 for (n = lastNode; n != 0; n = n->traversePreviousNode()) {
1802 if (n->isKeyboardFocusable()) {
1803 if (n->tabIndex() == 0)
1805 else if (n->tabIndex() > highestTabIndex)
1806 highestTabIndex = n->tabIndex();
1810 // No node with a tab index of 0; just go to the last node with the highest tab index
1811 for (n = lastNode; n != 0; n = n->traversePreviousNode()) {
1812 if (n->isKeyboardFocusable() && (n->tabIndex() == highestTabIndex))
1819 unsigned short fromTabIndex = fromNode->tabIndex();
1821 if (fromTabIndex == 0) {
1822 // Find the previous selectable node before fromNode (in document order) that doesn't have a tab index
1823 NodeImpl *n = fromNode->traversePreviousNode();
1824 while (n && !(n->isKeyboardFocusable() && n->tabIndex() == 0))
1825 n = n->traversePreviousNode();
1829 // No previous nodes with a 0 tab index, go to the last node in the document that has the highest tab index
1830 int highestTabIndex = 0;
1831 for (n = this; n != 0; n = n->traverseNextNode()) {
1832 if (n->isKeyboardFocusable() && (n->tabIndex() > highestTabIndex))
1833 highestTabIndex = n->tabIndex();
1836 if (highestTabIndex == 0)
1839 for (n = lastNode; n != 0; n = n->traversePreviousNode()) {
1840 if (n->isKeyboardFocusable() && (n->tabIndex() == highestTabIndex))
1844 assert(false); // should never get here
1848 // Find the lowest tab index out of all the nodes except fromNode, that is less than or equal to fromNode's
1849 // tab index. For nodes with the same tab index as fromNode, we are only interested in those before
1851 // If we don't find a suitable tab index, then there will be no previous focus node.
1852 unsigned short highestSuitableTabIndex = 0;
1855 bool reachedFromNode = false;
1856 for (n = this; n != 0; n = n->traverseNextNode()) {
1857 if (n->isKeyboardFocusable() &&
1858 ((!reachedFromNode && (n->tabIndex() <= fromTabIndex)) ||
1859 (reachedFromNode && (n->tabIndex() < fromTabIndex))) &&
1860 (n->tabIndex() > highestSuitableTabIndex) &&
1863 // We found a selectable node with a tab index no higher than fromNode's. Keep searching though, as
1864 // there may be another node which has a higher tab index but is still suitable for use.
1865 highestSuitableTabIndex = n->tabIndex();
1869 reachedFromNode = true;
1872 if (highestSuitableTabIndex == 0) {
1873 // No previous node with a tab index. Since the order specified by HTML is nodes with tab index > 0
1874 // first, this means that there is no previous node.
1878 // Search backwards from fromNode
1879 for (n = fromNode->traversePreviousNode(); n != 0; n = n->traversePreviousNode()) {
1880 if (n->isKeyboardFocusable() && (n->tabIndex() == highestSuitableTabIndex))
1883 // The previous node isn't before fromNode, start from the end of the document
1884 for (n = lastNode; n != fromNode; n = n->traversePreviousNode()) {
1885 if (n->isKeyboardFocusable() && (n->tabIndex() == highestSuitableTabIndex))
1889 assert(false); // should never get here
1895 int DocumentImpl::nodeAbsIndex(NodeImpl *node)
1897 assert(node->getDocument() == this);
1900 for (NodeImpl *n = node; n && n != this; n = n->traversePreviousNode())
1905 NodeImpl *DocumentImpl::nodeWithAbsIndex(int absIndex)
1908 for (int i = 0; n && (i < absIndex); i++) {
1909 n = n->traverseNextNode();
1914 void DocumentImpl::processHttpEquiv(const DOMString &equiv, const DOMString &content)
1916 assert(!equiv.isNull() && !content.isNull());
1918 KHTMLPart *part = this->part();
1920 if (strcasecmp(equiv, "default-style") == 0) {
1921 // The preferred style set has been overridden as per section
1922 // 14.3.2 of the HTML4.0 specification. We need to update the
1923 // sheet used variable and then update our style selector.
1924 // For more info, see the test at:
1925 // http://www.hixie.ch/tests/evil/css/import/main/preferred.html
1927 part->d->m_sheetUsed = content.string();
1928 m_preferredStylesheetSet = content;
1929 updateStyleSelector();
1931 else if(strcasecmp(equiv, "refresh") == 0 && part->metaRefreshEnabled())
1933 // get delay and url
1934 QString str = content.string().stripWhiteSpace();
1935 int pos = str.find(QRegExp("[;,]"));
1937 pos = str.find(QRegExp("[ \t]"));
1939 if (pos == -1) // There can be no url (David)
1943 delay = str.toInt(&ok);
1945 // We want a new history item if the refresh timeout > 1 second
1946 if(ok && part) part->scheduleRedirection(delay, part->url().url(), delay <= 1);
1948 if(ok && part) part->scheduleRedirection(delay, part->url().url() );
1953 delay = str.left(pos).stripWhiteSpace().toDouble(&ok);
1956 while(pos < (int)str.length() && str[pos].isSpace()) pos++;
1958 if(str.find("url", 0, false ) == 0) str = str.mid(3);
1959 str = str.stripWhiteSpace();
1960 if ( str.length() && str[0] == '=' ) str = str.mid( 1 ).stripWhiteSpace();
1961 str = parseURL( DOMString(str) ).string();
1964 // We want a new history item if the refresh timeout > 1 second
1965 part->scheduleRedirection(delay, completeURL( str ), delay <= 1);
1967 part->scheduleRedirection(delay, completeURL( str ));
1971 else if(strcasecmp(equiv, "expires") == 0)
1973 QString str = content.string().stripWhiteSpace();
1974 time_t expire_date = str.toLong();
1976 m_docLoader->setExpireDate(expire_date);
1978 else if(strcasecmp(equiv, "pragma") == 0 || strcasecmp(equiv, "cache-control") == 0 && part)
1980 QString str = content.string().lower().stripWhiteSpace();
1981 KURL url = part->url();
1982 if ((str == "no-cache") && url.protocol().startsWith("http"))
1984 KIO::http_update_cache(url, true, 0);
1987 else if( (strcasecmp(equiv, "set-cookie") == 0))
1989 // ### make setCookie work on XML documents too; e.g. in case of <html:meta .....>
1990 HTMLDocumentImpl *d = static_cast<HTMLDocumentImpl *>(this);
1991 d->setCookie(content);
1995 bool DocumentImpl::prepareMouseEvent( bool readonly, int _x, int _y, MouseEvent *ev )
1998 assert(m_render->isCanvas());
1999 RenderObject::NodeInfo renderInfo(readonly, ev->type == MousePress);
2000 bool isInside = m_render->layer()->hitTest(renderInfo, _x, _y);
2001 ev->innerNode = renderInfo.innerNode();
2003 if (renderInfo.URLElement()) {
2004 assert(renderInfo.URLElement()->isElementNode());
2005 ElementImpl* e = static_cast<ElementImpl*>(renderInfo.URLElement());
2006 DOMString href = khtml::parseURL(e->getAttribute(ATTR_HREF));
2007 DOMString target = e->getAttribute(ATTR_TARGET);
2009 if (!target.isNull() && !href.isNull()) {
2010 ev->target = target;
2015 // qDebug("url: *%s*", ev->url.string().latin1());
2028 // DOM Section 1.1.1
2029 bool DocumentImpl::childAllowed( NodeImpl *newChild )
2031 // Documents may contain a maximum of one Element child
2032 if (newChild->nodeType() == Node::ELEMENT_NODE) {
2034 for (c = firstChild(); c; c = c->nextSibling()) {
2035 if (c->nodeType() == Node::ELEMENT_NODE)
2040 // Documents may contain a maximum of one DocumentType child
2041 if (newChild->nodeType() == Node::DOCUMENT_TYPE_NODE) {
2043 for (c = firstChild(); c; c = c->nextSibling()) {
2044 if (c->nodeType() == Node::DOCUMENT_TYPE_NODE)
2049 return childTypeAllowed(newChild->nodeType());
2052 bool DocumentImpl::childTypeAllowed( unsigned short type )
2055 case Node::ELEMENT_NODE:
2056 case Node::PROCESSING_INSTRUCTION_NODE:
2057 case Node::COMMENT_NODE:
2058 case Node::DOCUMENT_TYPE_NODE:
2065 NodeImpl *DocumentImpl::cloneNode ( bool /*deep*/ )
2067 // Spec says cloning Document nodes is "implementation dependent"
2068 // so we do not support it...
2072 NodeImpl::Id DocumentImpl::attrId(DOMStringImpl* _namespaceURI, DOMStringImpl *_name, bool readonly)
2074 // Each document maintains a mapping of attrname -> id for every attr name
2075 // encountered in the document.
2076 // For attrnames without a prefix (no qualified element name) and without matching
2077 // namespace, the value defined in misc/htmlattrs.h is used.
2078 NodeImpl::Id id = 0;
2080 // First see if it's a HTML attribute name
2081 QConstString n(_name->s, _name->l);
2082 if (!_namespaceURI || !strcasecmp(_namespaceURI, XHTML_NAMESPACE)) {
2083 // we're in HTML namespace if we know the tag.
2084 // xhtml is lower case - case sensitive, easy to implement
2085 if ( htmlMode() == XHtml && (id = getAttrID(n.string().ascii(), _name->l)) )
2087 // compatibility: upper case - case insensitive
2088 if ( htmlMode() != XHtml && (id = getAttrID(n.string().lower().ascii(), _name->l )) )
2091 // ok, the fast path didn't work out, we need the full check
2094 // now lets find out the namespace
2095 Q_UINT16 ns = noNamespace;
2096 if (_namespaceURI) {
2097 DOMString nsU(_namespaceURI);
2098 int nsID = XmlNamespaceTable::getNamespaceID(nsU, readonly);
2100 ns = (Q_UINT16)nsID;
2103 // Look in the m_attrNames array for the name
2104 // ### yeah, this is lame. use a dictionary / map instead
2105 DOMString nme(n.string());
2106 // compatibility mode has to store upper case
2107 if (htmlMode() != XHtml) nme = nme.upper();
2108 for (id = 0; id < m_attrNameCount; id++)
2109 if (DOMString(m_attrNames[id]) == nme)
2110 return makeId(ns, ATTR_LAST_ATTR+id);
2113 if (readonly) return 0;
2115 // Name not found in m_attrNames, so let's add it
2116 // ### yeah, this is lame. use a dictionary / map instead
2117 if (m_attrNameCount+1 > m_attrNameAlloc) {
2118 m_attrNameAlloc += 100;
2119 DOMStringImpl **newNames = new DOMStringImpl* [m_attrNameAlloc];
2122 for (i = 0; i < m_attrNameCount; i++)
2123 newNames[i] = m_attrNames[i];
2124 delete [] m_attrNames;
2126 m_attrNames = newNames;
2129 id = m_attrNameCount++;
2130 m_attrNames[id] = nme.implementation();
2131 m_attrNames[id]->ref();
2133 return makeId(ns, ATTR_LAST_ATTR+id);
2136 DOMString DocumentImpl::attrName(NodeImpl::Id _id) const
2139 if (localNamePart(_id) >= ATTR_LAST_ATTR)
2140 result = m_attrNames[localNamePart(_id)-ATTR_LAST_ATTR];
2142 result = getAttrName(_id);
2144 // Attribute names are always lowercase in the DOM for both
2146 if (isHTMLDocument() || htmlMode() == XHtml)
2147 return result.lower();
2152 NodeImpl::Id DocumentImpl::tagId(DOMStringImpl* _namespaceURI, DOMStringImpl *_name, bool readonly)
2154 if (!_name) return 0;
2155 // Each document maintains a mapping of tag name -> id for every tag name encountered
2157 NodeImpl::Id id = 0;
2159 // First see if it's a HTML element name
2160 QConstString n(_name->s, _name->l);
2161 if (!_namespaceURI || !strcasecmp(_namespaceURI, XHTML_NAMESPACE)) {
2162 // we're in HTML namespace if we know the tag.
2163 // xhtml is lower case - case sensitive, easy to implement
2164 if ( htmlMode() == XHtml && (id = getTagID(n.string().ascii(), _name->l)) )
2166 // compatibility: upper case - case insensitive
2167 if ( htmlMode() != XHtml && (id = getTagID(n.string().lower().ascii(), _name->l )) )
2170 // ok, the fast path didn't work out, we need the full check
2173 // now lets find out the namespace
2174 Q_UINT16 ns = noNamespace;
2175 if (_namespaceURI) {
2176 DOMString nsU(_namespaceURI);
2177 int nsID = XmlNamespaceTable::getNamespaceID(nsU, readonly);
2179 ns = (Q_UINT16)nsID;
2182 // Look in the m_elementNames array for the name
2183 // ### yeah, this is lame. use a dictionary / map instead
2184 DOMString nme(n.string());
2185 // compatibility mode has to store upper case
2186 if (htmlMode() != XHtml) nme = nme.upper();
2187 for (id = 0; id < m_elementNameCount; id++)
2188 if (DOMString(m_elementNames[id]) == nme)
2189 return makeId(ns, ID_LAST_TAG + 1 + id);
2192 if (readonly) return 0;
2194 // Name not found in m_elementNames, so let's add it
2195 if (m_elementNameCount+1 > m_elementNameAlloc) {
2196 m_elementNameAlloc += 100;
2197 DOMStringImpl **newNames = new DOMStringImpl* [m_elementNameAlloc];
2198 // ### yeah, this is lame. use a dictionary / map instead
2199 if (m_elementNames) {
2201 for (i = 0; i < m_elementNameCount; i++)
2202 newNames[i] = m_elementNames[i];
2203 delete [] m_elementNames;
2205 m_elementNames = newNames;
2208 id = m_elementNameCount++;
2209 m_elementNames[id] = nme.implementation();
2210 m_elementNames[id]->ref();
2212 return makeId(ns, ID_LAST_TAG + 1 + id);
2215 DOMString DocumentImpl::tagName(NodeImpl::Id _id) const
2217 if (localNamePart(_id) > ID_LAST_TAG)
2218 return m_elementNames[localNamePart(_id) - (ID_LAST_TAG + 1)];
2220 // ### put them in a cache
2221 if (htmlMode() == XHtml)
2222 return getTagName(_id).lower();
2224 return getTagName(_id);
2229 DOMStringImpl* DocumentImpl::namespaceURI(NodeImpl::Id _id) const
2231 if (_id <= ID_LAST_TAG)
2232 return htmlMode() == XHtml ? XmlNamespaceTable::getNamespaceURI(xhtmlNamespace).implementation() : 0;
2234 unsigned short ns = _id >> 16;
2238 return XmlNamespaceTable::getNamespaceURI(ns).implementation();
2241 StyleSheetListImpl* DocumentImpl::styleSheets()
2243 return m_styleSheets;
2246 DOMString DocumentImpl::preferredStylesheetSet()
2248 return m_preferredStylesheetSet;
2251 DOMString DocumentImpl::selectedStylesheetSet()
2253 return view() ? view()->part()->d->m_sheetUsed : DOMString();
2257 DocumentImpl::setSelectedStylesheetSet(const DOMString& aString)
2260 view()->part()->d->m_sheetUsed = aString.string();
2261 updateStyleSelector();
2263 renderer()->repaint();
2267 // This method is called whenever a top-level stylesheet has finished loading.
2268 void DocumentImpl::stylesheetLoaded()
2270 // Make sure we knew this sheet was pending, and that our count isn't out of sync.
2271 assert(m_pendingStylesheets > 0);
2273 m_pendingStylesheets--;
2275 #ifdef INSTRUMENT_LAYOUT_SCHEDULING
2276 if (!ownerElement())
2277 printf("Stylesheet loaded at time %d. %d stylesheets still remain.\n", elapsedTime(), m_pendingStylesheets);
2280 updateStyleSelector();
2283 void DocumentImpl::updateStyleSelector()
2285 // Don't bother updating, since we haven't loaded all our style info yet.
2286 if (!haveStylesheetsLoaded())
2289 #ifdef INSTRUMENT_LAYOUT_SCHEDULING
2290 if (!ownerElement())
2291 printf("Beginning update of style selector at time %d.\n", elapsedTime());
2294 recalcStyleSelector();
2298 m_styleSelectorDirty = true;
2301 #ifdef INSTRUMENT_LAYOUT_SCHEDULING
2302 if (!ownerElement())
2303 printf("Finished update of style selector at time %d\n", elapsedTime());
2307 renderer()->setNeedsLayoutAndMinMaxRecalc();
2309 view()->scheduleRelayout();
2314 QStringList DocumentImpl::availableStyleSheets() const
2316 return m_availableSheets;
2319 void DocumentImpl::recalcStyleSelector()
2321 if ( !m_render || !attached() ) return;
2323 QPtrList<StyleSheetImpl> oldStyleSheets = m_styleSheets->styleSheets;
2324 m_styleSheets->styleSheets.clear();
2325 m_availableSheets.clear();
2327 for (n = this; n; n = n->traverseNextNode()) {
2328 StyleSheetImpl *sheet = 0;
2330 if (n->nodeType() == Node::PROCESSING_INSTRUCTION_NODE)
2332 // Processing instruction (XML documents only)
2333 ProcessingInstructionImpl* pi = static_cast<ProcessingInstructionImpl*>(n);
2334 sheet = pi->sheet();
2337 applyXSLTransform(pi);
2341 if (!sheet && !pi->localHref().isEmpty())
2343 // Processing instruction with reference to an element in this document - e.g.
2344 // <?xml-stylesheet href="#mystyle">, with the element
2345 // <foo id="mystyle">heading { color: red; }</foo> at some location in
2347 ElementImpl* elem = getElementById(pi->localHref());
2349 DOMString sheetText("");
2351 for (c = elem->firstChild(); c; c = c->nextSibling()) {
2352 if (c->nodeType() == Node::TEXT_NODE || c->nodeType() == Node::CDATA_SECTION_NODE)
2353 sheetText += c->nodeValue();
2356 CSSStyleSheetImpl *cssSheet = new CSSStyleSheetImpl(this);
2357 cssSheet->parseString(sheetText);
2358 pi->setStyleSheet(cssSheet);
2364 else if (n->isHTMLElement() && (n->id() == ID_LINK || n->id() == ID_STYLE)) {
2365 ElementImpl *e = static_cast<ElementImpl *>(n);
2366 QString title = e->getAttribute( ATTR_TITLE ).string();
2367 bool enabledViaScript = false;
2368 if (n->id() == ID_LINK) {
2370 HTMLLinkElementImpl* l = static_cast<HTMLLinkElementImpl*>(n);
2371 if (l->isLoading() || l->isDisabled())
2374 title = QString::null;
2375 enabledViaScript = l->isEnabledViaScript();
2378 // Get the current preferred styleset. This is the
2379 // set of sheets that will be enabled.
2380 if ( n->id() == ID_LINK )
2381 sheet = static_cast<HTMLLinkElementImpl*>(n)->sheet();
2384 sheet = static_cast<HTMLStyleElementImpl*>(n)->sheet();
2386 // Check to see if this sheet belongs to a styleset
2387 // (thus making it PREFERRED or ALTERNATE rather than
2389 if (!enabledViaScript && !title.isEmpty()) {
2390 // Yes, we have a title.
2391 if (m_preferredStylesheetSet.isEmpty()) {
2392 // No preferred set has been established. If
2393 // we are NOT an alternate sheet, then establish
2394 // us as the preferred set. Otherwise, just ignore
2396 QString rel = e->getAttribute( ATTR_REL ).string();
2397 if (n->id() == ID_STYLE || !rel.contains("alternate"))
2398 m_preferredStylesheetSet = view()->part()->d->m_sheetUsed = title;
2401 if (!m_availableSheets.contains( title ) )
2402 m_availableSheets.append( title );
2404 if (title != m_preferredStylesheetSet)
2411 m_styleSheets->styleSheets.append(sheet);
2414 // For HTML documents, stylesheets are not allowed within/after the <BODY> tag. So we
2415 // can stop searching here.
2416 if (isHTMLDocument() && n->id() == ID_BODY)
2420 // De-reference all the stylesheets in the old list
2421 QPtrListIterator<StyleSheetImpl> it(oldStyleSheets);
2422 for (; it.current(); ++it)
2423 it.current()->deref();
2425 // Create a new style selector
2426 delete m_styleSelector;
2427 QString usersheet = m_usersheet;
2428 if ( m_view && m_view->mediaType() == "print" )
2429 usersheet += m_printSheet;
2430 m_styleSelector = new CSSStyleSelector(this, usersheet, m_styleSheets, !inCompatMode());
2431 m_styleSelector->setEncodedURL(m_url);
2432 m_styleSelectorDirty = false;
2435 void DocumentImpl::setHoverNode(NodeImpl* newHoverNode)
2437 if (m_hoverNode != newHoverNode) {
2439 m_hoverNode->deref();
2440 m_hoverNode = newHoverNode;
2448 bool DocumentImpl::relinquishesEditingFocus(NodeImpl *node)
2451 assert(node->isContentEditable());
2453 NodeImpl *rootImpl = node->rootEditableElement();
2454 if (!part() || !rootImpl)
2457 Node root(rootImpl);
2458 Range range(root, 0, root, rootImpl->childNodeCount());
2459 return part()->shouldEndEditing(range);
2462 bool DocumentImpl::acceptsEditingFocus(NodeImpl *node)
2465 assert(node->isContentEditable());
2467 NodeImpl *rootImpl = node->rootEditableElement();
2468 if (!part() || !rootImpl)
2471 Node root(rootImpl);
2472 Range range(root, 0, root, rootImpl->childNodeCount());
2473 return part()->shouldBeginEditing(range);
2476 const QValueList<DashboardRegionValue> & DocumentImpl::dashboardRegions() const
2478 return m_dashboardRegions;
2481 void DocumentImpl::setDashboardRegions (const QValueList<DashboardRegionValue>& regions)
2483 m_dashboardRegions = regions;
2484 setDashboardRegionsDirty (false);
2489 static QWidget *widgetForNode(NodeImpl *focusNode)
2493 RenderObject *renderer = focusNode->renderer();
2494 if (!renderer || !renderer->isWidget())
2496 return static_cast<RenderWidget *>(renderer)->widget();
2499 bool DocumentImpl::setFocusNode(NodeImpl *newFocusNode)
2501 // Make sure newFocusNode is actually in this document
2502 if (newFocusNode && (newFocusNode->getDocument() != this))
2505 if (m_focusNode == newFocusNode)
2509 if (m_focusNode && m_focusNode->isContentEditable() && !relinquishesEditingFocus(m_focusNode))
2513 bool focusChangeBlocked = false;
2514 NodeImpl *oldFocusNode = m_focusNode;
2517 // Remove focus from the existing focus node (if any)
2519 // This goes hand in hand with the Qt focus setting below.
2520 if (!newFocusNode && view()) {
2524 if (oldFocusNode->active())
2525 oldFocusNode->setActive(false);
2527 oldFocusNode->setFocus(false);
2528 oldFocusNode->dispatchHTMLEvent(EventImpl::BLUR_EVENT, false, false);
2529 if (m_focusNode != 0) {
2530 // handler shifted focus
2531 focusChangeBlocked = true;
2534 oldFocusNode->dispatchUIEvent(EventImpl::DOMFOCUSOUT_EVENT);
2535 if (m_focusNode != 0) {
2536 // handler shifted focus
2537 focusChangeBlocked = true;
2540 if ((oldFocusNode == this) && oldFocusNode->hasOneRef()) {
2541 oldFocusNode->deref(); // deletes this
2545 oldFocusNode->deref();
2549 // Clear the selection when changing the focus node to null or to a node that is not
2550 // contained by the current selection.
2552 NodeImpl *startContainer = part()->selection().start().node();
2553 if (!newFocusNode || (startContainer && startContainer != newFocusNode && !startContainer->isAncestor(newFocusNode)))
2554 part()->clearSelection();
2559 if (newFocusNode->isContentEditable() && !acceptsEditingFocus(newFocusNode)) {
2560 // delegate blocks focus change
2561 focusChangeBlocked = true;
2562 goto SetFocusNodeDone;
2565 // Set focus on the new node
2566 m_focusNode = newFocusNode;
2568 m_focusNode->dispatchHTMLEvent(EventImpl::FOCUS_EVENT, false, false);
2569 if (m_focusNode != newFocusNode) {
2570 // handler shifted focus
2571 focusChangeBlocked = true;
2572 goto SetFocusNodeDone;
2574 m_focusNode->dispatchUIEvent(EventImpl::DOMFOCUSIN_EVENT);
2575 if (m_focusNode != newFocusNode) {
2576 // handler shifted focus
2577 focusChangeBlocked = true;
2578 goto SetFocusNodeDone;
2580 m_focusNode->setFocus();
2581 // eww, I suck. set the qt focus correctly
2582 // ### find a better place in the code for this
2584 QWidget *focusWidget = widgetForNode(m_focusNode);
2586 // Make sure a widget has the right size before giving it focus.
2587 // Otherwise, we are testing edge cases of the QWidget code.
2588 // Specifically, in WebCore this does not work well for text fields.
2590 // Re-get the widget in case updating the layout changed things.
2591 focusWidget = widgetForNode(m_focusNode);
2594 focusWidget->setFocus();
2601 if (!focusChangeBlocked && m_focusNode && KWQAccObjectCache::accessibilityEnabled())
2602 getAccObjectCache()->handleFocusedUIElementChanged();
2607 return !focusChangeBlocked;
2610 void DocumentImpl::setCSSTarget(NodeImpl* n)
2613 m_cssTarget->setChanged();
2619 NodeImpl* DocumentImpl::getCSSTarget()
2624 void DocumentImpl::attachNodeIterator(NodeIteratorImpl *ni)
2626 m_nodeIterators.append(ni);
2629 void DocumentImpl::detachNodeIterator(NodeIteratorImpl *ni)
2631 m_nodeIterators.remove(ni);
2634 void DocumentImpl::notifyBeforeNodeRemoval(NodeImpl *n)
2636 QPtrListIterator<NodeIteratorImpl> it(m_nodeIterators);
2637 for (; it.current(); ++it)
2638 it.current()->notifyBeforeNodeRemoval(n);
2641 AbstractViewImpl *DocumentImpl::defaultView() const
2643 return m_defaultView;
2646 EventImpl *DocumentImpl::createEvent(const DOMString &eventType, int &exceptioncode)
2648 if (eventType == "UIEvents")
2649 return new UIEventImpl();
2650 else if (eventType == "MouseEvents")
2651 return new MouseEventImpl();
2652 else if (eventType == "MutationEvents")
2653 return new MutationEventImpl();
2654 else if (eventType == "KeyboardEvents")
2655 return new KeyboardEventImpl();
2656 else if (eventType == "HTMLEvents")
2657 return new EventImpl();
2659 exceptioncode = DOMException::NOT_SUPPORTED_ERR;
2664 CSSStyleDeclarationImpl *DocumentImpl::getOverrideStyle(ElementImpl */*elt*/, DOMStringImpl */*pseudoElt*/)
2669 void DocumentImpl::defaultEventHandler(EventImpl *evt)
2671 // if any html event listeners are registered on the window, then dispatch them here
2672 QPtrList<RegisteredEventListener> listenersCopy = m_windowEventListeners;
2673 QPtrListIterator<RegisteredEventListener> it(listenersCopy);
2675 for (; it.current(); ++it) {
2676 if (it.current()->id == evt->id()) {
2677 it.current()->listener->handleEvent(ev, true);
2682 if (evt->id()==EventImpl::KEYDOWN_EVENT) {
2683 KeyboardEventImpl *kevt = static_cast<KeyboardEventImpl *>(evt);
2684 if (kevt->ctrlKey()) {
2685 QString key = kevt->qKeyEvent()->unmodifiedText().lower();
2686 ElementImpl *elem = getElementByAccessKey(key);
2688 elem->accessKeyAction(false);
2689 evt->setDefaultHandled();
2695 void DocumentImpl::setHTMLWindowEventListener(int id, EventListener *listener)
2697 // If we already have it we don't want removeWindowEventListener to delete it
2700 removeHTMLWindowEventListener(id);
2702 addWindowEventListener(id, listener, false);
2707 EventListener *DocumentImpl::getHTMLWindowEventListener(int id)
2709 QPtrListIterator<RegisteredEventListener> it(m_windowEventListeners);
2710 for (; it.current(); ++it) {
2711 if (it.current()->id == id &&
2712 it.current()->listener->eventListenerType() == "_khtml_HTMLEventListener") {
2713 return it.current()->listener;
2720 void DocumentImpl::removeHTMLWindowEventListener(int id)
2722 QPtrListIterator<RegisteredEventListener> it(m_windowEventListeners);
2723 for (; it.current(); ++it) {
2724 if (it.current()->id == id &&
2725 it.current()->listener->eventListenerType() == "_khtml_HTMLEventListener") {
2726 m_windowEventListeners.removeRef(it.current());
2732 void DocumentImpl::addWindowEventListener(int id, EventListener *listener, const bool useCapture)
2736 // remove existing identical listener set with identical arguments - the DOM2
2737 // spec says that "duplicate instances are discarded" in this case.
2738 removeWindowEventListener(id,listener,useCapture);
2740 RegisteredEventListener *rl = new RegisteredEventListener(static_cast<EventImpl::EventId>(id), listener, useCapture);
2741 m_windowEventListeners.append(rl);
2746 void DocumentImpl::removeWindowEventListener(int id, EventListener *listener, bool useCapture)
2748 RegisteredEventListener rl(static_cast<EventImpl::EventId>(id),listener,useCapture);
2750 QPtrListIterator<RegisteredEventListener> it(m_windowEventListeners);
2751 for (; it.current(); ++it)
2752 if (*(it.current()) == rl) {
2753 m_windowEventListeners.removeRef(it.current());
2758 bool DocumentImpl::hasWindowEventListener(int id)
2760 QPtrListIterator<RegisteredEventListener> it(m_windowEventListeners);
2761 for (; it.current(); ++it) {
2762 if (it.current()->id == id) {
2770 EventListener *DocumentImpl::createHTMLEventListener(QString code, NodeImpl *node)
2773 return part()->createHTMLEventListener(code, node);
2779 void DocumentImpl::dispatchImageLoadEventSoon(HTMLImageLoader *image)
2781 m_imageLoadEventDispatchSoonList.append(image);
2782 if (!m_imageLoadEventTimer) {
2783 m_imageLoadEventTimer = startTimer(0);
2787 void DocumentImpl::removeImage(HTMLImageLoader* image)
2789 // Remove instances of this image from both lists.
2790 // Use loops because we allow multiple instances to get into the lists.
2791 while (m_imageLoadEventDispatchSoonList.removeRef(image)) { }
2792 while (m_imageLoadEventDispatchingList.removeRef(image)) { }
2793 if (m_imageLoadEventDispatchSoonList.isEmpty() && m_imageLoadEventTimer) {
2794 killTimer(m_imageLoadEventTimer);
2795 m_imageLoadEventTimer = 0;
2799 void DocumentImpl::dispatchImageLoadEventsNow()
2801 // need to avoid re-entering this function; if new dispatches are
2802 // scheduled before the parent finishes processing the list, they
2803 // will set a timer and eventually be processed
2804 if (!m_imageLoadEventDispatchingList.isEmpty()) {
2808 if (m_imageLoadEventTimer) {
2809 killTimer(m_imageLoadEventTimer);
2810 m_imageLoadEventTimer = 0;
2813 m_imageLoadEventDispatchingList = m_imageLoadEventDispatchSoonList;
2814 m_imageLoadEventDispatchSoonList.clear();
2815 for (QPtrListIterator<HTMLImageLoader> it(m_imageLoadEventDispatchingList); it.current(); ) {
2816 HTMLImageLoader* image = it.current();
2817 // Must advance iterator *before* dispatching call.
2818 // Otherwise, it might be advanced automatically if dispatching the call had a side effect
2819 // of destroying the current HTMLImageLoader, and then we would advance past the *next* item,
2820 // missing one altogether.
2822 image->dispatchLoadEvent();
2824 m_imageLoadEventDispatchingList.clear();
2827 void DocumentImpl::timerEvent(QTimerEvent *)
2829 dispatchImageLoadEventsNow();
2832 ElementImpl *DocumentImpl::ownerElement()
2834 KHTMLView *childView = view();
2837 KHTMLPart *childPart = childView->part();
2840 KHTMLPart *parent = childPart->parentPart();
2843 ChildFrame *childFrame = parent->childFrame(childPart);
2846 RenderPart *renderPart = childFrame->m_frame;
2849 return static_cast<ElementImpl *>(renderPart->element());
2852 DOMString DocumentImpl::domain() const
2854 if ( m_domain.isEmpty() ) // not set yet (we set it on demand to save time and space)
2855 m_domain = KURL(URL()).host(); // Initially set to the host
2859 void DocumentImpl::setDomain(const DOMString &newDomain, bool force /*=false*/)
2862 m_domain = newDomain;
2865 if ( m_domain.isEmpty() ) // not set yet (we set it on demand to save time and space)
2866 m_domain = KURL(URL()).host(); // Initially set to the host
2868 // Both NS and IE specify that changing the domain is only allowed when
2869 // the new domain is a suffix of the old domain.
2870 int oldLength = m_domain.length();
2871 int newLength = newDomain.length();
2872 if ( newLength < oldLength ) // e.g. newDomain=kde.org (7) and m_domain=www.kde.org (11)
2874 DOMString test = m_domain.copy();
2875 if ( test[oldLength - newLength - 1] == '.' ) // Check that it's a subdomain, not e.g. "de.org"
2877 test.remove( 0, oldLength - newLength ); // now test is "kde.org" from m_domain
2878 if ( test == newDomain ) // and we check that it's the same thing as newDomain
2879 m_domain = newDomain;
2884 bool DocumentImpl::isValidName(const DOMString &name)
2886 static const char validFirstCharacter[] = "ABCDEFGHIJKLMNOPQRSTUVWXZYabcdefghijklmnopqrstuvwxyz";
2887 static const char validSubsequentCharacter[] = "ABCDEFGHIJKLMNOPQRSTUVWXZYabcdefghijklmnopqrstuvwxyz0123456789-_:.";
2888 const unsigned length = name.length();
2891 const QChar * const characters = name.unicode();
2892 const char fc = characters[0];
2895 if (strchr(validFirstCharacter, fc) == 0)
2897 for (unsigned i = 1; i < length; ++i) {
2898 const char sc = characters[i];
2901 if (strchr(validSubsequentCharacter, sc) == 0)
2907 void DocumentImpl::addImageMap(HTMLMapElementImpl *imageMap)
2909 // Add the image map, unless there's already another with that name.
2910 // "First map wins" is the rule other browsers seem to implement.
2911 QString name = imageMap->getName().string();
2912 if (!m_imageMapsByName.contains(name))
2913 m_imageMapsByName.insert(name, imageMap);
2916 void DocumentImpl::removeImageMap(HTMLMapElementImpl *imageMap)
2918 // Remove the image map by name.
2919 // But don't remove some other image map that just happens to have the same name.
2920 QString name = imageMap->getName().string();
2921 QMapIterator<QString, HTMLMapElementImpl *> it = m_imageMapsByName.find(name);
2922 if (it != m_imageMapsByName.end() && *it == imageMap)
2923 m_imageMapsByName.remove(it);
2926 HTMLMapElementImpl *DocumentImpl::getImageMap(const DOMString &URL) const
2932 QString s = URL.string();
2933 int hashPos = s.find('#');
2935 s = s.mid(hashPos + 1);
2937 QMapConstIterator<QString, HTMLMapElementImpl *> it = m_imageMapsByName.find(s);
2938 if (it == m_imageMapsByName.end())
2945 void DocumentImpl::setDecoder(Decoder *decoder)
2951 m_decoder = decoder;
2954 QString DocumentImpl::completeURL(const QString &URL)
2956 return KURL(baseURL(), URL, m_decoder ? m_decoder->codec() : 0).url();
2959 bool DocumentImpl::inPageCache()
2961 return m_inPageCache;
2964 void DocumentImpl::setInPageCache(bool flag)
2966 if (m_inPageCache == flag)
2969 m_inPageCache = flag;
2971 assert(m_savedRenderer == 0);
2972 m_savedRenderer = m_render;
2974 m_view->resetScrollBars();
2977 assert(m_render == 0 || m_render == m_savedRenderer);
2978 m_render = m_savedRenderer;
2979 m_savedRenderer = 0;
2983 void DocumentImpl::passwordFieldAdded()
2988 void DocumentImpl::passwordFieldRemoved()
2990 assert(m_passwordFields > 0);
2994 bool DocumentImpl::hasPasswordField() const
2996 return m_passwordFields > 0;
2999 void DocumentImpl::secureFormAdded()
3004 void DocumentImpl::secureFormRemoved()
3006 assert(m_secureForms > 0);
3010 bool DocumentImpl::hasSecureForm() const
3012 return m_secureForms > 0;
3015 void DocumentImpl::setShouldCreateRenderers(bool f)
3017 m_createRenderers = f;
3020 bool DocumentImpl::shouldCreateRenderers()
3022 return m_createRenderers;
3025 DOMString DocumentImpl::toString() const
3029 for (NodeImpl *child = firstChild(); child != NULL; child = child->nextSibling()) {
3030 result += child->toString();
3036 #endif // APPLE_CHANGES
3038 // ----------------------------------------------------------------------------
3039 // Support for Javascript execCommand, and related methods
3041 JSEditor *DocumentImpl::jsEditor()
3044 m_jsEditor = new JSEditor(this);
3048 bool DocumentImpl::execCommand(const DOMString &command, bool userInterface, const DOMString &value)
3050 return jsEditor()->execCommand(command, userInterface, value);
3053 bool DocumentImpl::queryCommandEnabled(const DOMString &command)
3055 return jsEditor()->queryCommandEnabled(command);
3058 bool DocumentImpl::queryCommandIndeterm(const DOMString &command)
3060 return jsEditor()->queryCommandIndeterm(command);
3063 bool DocumentImpl::queryCommandState(const DOMString &command)
3065 return jsEditor()->queryCommandState(command);
3068 bool DocumentImpl::queryCommandSupported(const DOMString &command)
3070 return jsEditor()->queryCommandSupported(command);
3073 DOMString DocumentImpl::queryCommandValue(const DOMString &command)
3075 return jsEditor()->queryCommandValue(command);
3078 // ----------------------------------------------------------------------------
3080 void DocumentImpl::addMarker(Range range, DocumentMarker::MarkerType type)
3082 // Use a TextIterator to visit the potentially multiple nodes the range covers.
3083 for (TextIterator markedText(range); !markedText.atEnd(); markedText.advance()) {
3084 Range textPiece = markedText.range();
3085 DocumentMarker marker = {type, textPiece.startOffset(), textPiece.endOffset()};
3086 addMarker(textPiece.startContainer().handle(), marker);
3090 void DocumentImpl::removeMarker(Range range, DocumentMarker::MarkerType type)
3092 // Use a TextIterator to visit the potentially multiple nodes the range covers.
3093 for (TextIterator markedText(range); !markedText.atEnd(); markedText.advance()) {
3094 Range textPiece = markedText.range();
3095 DocumentMarker marker = {type, textPiece.startOffset(), textPiece.endOffset()};
3096 removeMarker(textPiece.startContainer().handle(), marker);
3100 // FIXME: We don't deal with markers of more than one type yet
3102 // Markers are stored in order sorted by their location. They do not overlap each other, as currently
3103 // required by the drawing code in render_text.cpp.
3105 void DocumentImpl::addMarker(NodeImpl *node, DocumentMarker newMarker)
3107 assert(newMarker.endOffset >= newMarker.startOffset);
3108 if (newMarker.endOffset == newMarker.startOffset) {
3109 return; // zero length markers are a NOP
3112 QValueList <DocumentMarker> *markers = m_markers.find(node);
3114 markers = new QValueList <DocumentMarker>;
3115 markers->append(newMarker);
3116 m_markers.insert(node, markers);
3118 QValueListIterator<DocumentMarker> it;
3119 for (it = markers->begin(); it != markers->end(); ) {
3120 DocumentMarker marker = *it;
3122 if (newMarker.endOffset < marker.startOffset+1) {
3123 // This is the first marker that is completely after newMarker, and disjoint from it.
3124 // We found our insertion point.
\10
3126 } else if (newMarker.startOffset > marker.endOffset) {
3127 // maker is before newMarker, and disjoint from it. Keep scanning.
3129 } else if (newMarker == marker) {
3130 // already have this one, NOP
3133 // marker and newMarker intersect or touch - merge them into newMarker
3134 newMarker.startOffset = kMin(newMarker.startOffset, marker.startOffset);
3135 newMarker.endOffset = kMax(newMarker.endOffset, marker.endOffset);
3136 // remove old one, we'll add newMarker later
3137 it = markers->remove(it);
3138 // it points to the next marker to consider
3141 // at this point it points to the node before which we want to insert
3142 markers->insert(it, newMarker);
3145 // repaint the affected node
3146 if (node->renderer())
3147 node->renderer()->repaint();
3150 void DocumentImpl::removeMarker(NodeImpl *node, DocumentMarker target)
3152 assert(target.endOffset >= target.startOffset);
3153 if (target.endOffset == target.startOffset) {
3154 return; // zero length markers are a NOP
3157 QValueList <DocumentMarker> *markers = m_markers.find(node);
3162 bool docDirty = false;
3163 QValueListIterator<DocumentMarker> it;
3164 for (it = markers->begin(); it != markers->end(); ) {
3165 DocumentMarker marker = *it;
3167 if (target.endOffset <= marker.startOffset) {
3168 // This is the first marker that is completely after target. All done.
3170 } else if (target.startOffset >= marker.endOffset) {
3171 // marker is before target. Keep scanning.
3174 // at this point we know that marker and target intersect in some way
3177 // pitch the old marker
3178 it = markers->remove(it);
3179 // it now points to the next node
3181 // add either of the resulting slices that are left after removing target
3182 if (target.startOffset > marker.startOffset) {
3183 DocumentMarker newLeft = marker;
3184 newLeft.endOffset = target.startOffset;
3185 markers->insert(it, newLeft);
3187 if (marker.endOffset > target.endOffset) {
3188 DocumentMarker newRight = marker;
3189 newRight.startOffset = target.endOffset;
3190 markers->insert(it, newRight);
3195 if (markers->isEmpty())
3196 m_markers.remove(node);
3198 // repaint the affected node
3199 if (docDirty && node->renderer())
3200 node->renderer()->repaint();
3203 QValueList<DocumentMarker> DocumentImpl::markersForNode(NodeImpl *node)
3205 QValueList <DocumentMarker> *markers = m_markers.find(node);
3209 return QValueList <DocumentMarker> ();
3213 void DocumentImpl::removeAllMarkers(NodeImpl *node, ulong startOffset, long length)
3215 // FIXME - yet another cheat that relies on us only having one marker type
3216 DocumentMarker marker = {DocumentMarker::Spelling, startOffset, startOffset+length};
3217 removeMarker(node, marker);
3220 void DocumentImpl::removeAllMarkers(NodeImpl *node)
3222 QValueList<DocumentMarker> *markers = m_markers.take(node);
3224 RenderObject *renderer = node->renderer();
3226 renderer->repaint();
3231 void DocumentImpl::removeAllMarkers()
3233 QPtrDictIterator< QValueList<DocumentMarker> > it(m_markers);
3234 for (; NodeImpl *node = static_cast<NodeImpl *>(it.currentKey()); ++it) {
3235 RenderObject *renderer = node->renderer();
3237 renderer->repaint();
3242 void DocumentImpl::shiftMarkers(NodeImpl *node, ulong startOffset, long delta)
3244 QValueList <DocumentMarker> *markers = m_markers.find(node);
3248 bool docDirty = false;
3249 QValueListIterator<DocumentMarker> it;
3250 for (it = markers->begin(); it != markers->end(); ++it) {
3251 DocumentMarker &marker = *it;
3252 if (marker.startOffset >= startOffset) {
3253 assert((int)marker.startOffset + delta >= 0);
3254 marker.startOffset += delta;
3255 marker.endOffset += delta;
3260 // repaint the affected node
3261 if (docDirty && node->renderer())
3262 node->renderer()->repaint();
3266 void DocumentImpl::applyXSLTransform(ProcessingInstructionImpl* pi)
3268 // Ref ourselves to keep from being destroyed.
3269 XSLTProcessorImpl processor(static_cast<XSLStyleSheetImpl*>(pi->sheet()), this);
3270 processor.transformDocument(this);
3272 // FIXME: If the transform failed we should probably report an error (like Mozilla does) in this
3276 void DocumentImpl::setTransformSourceDocument(DocumentImpl* doc)
3278 if (m_transformSourceDocument)
3279 m_transformSourceDocument->deref();
3280 m_transformSourceDocument = doc;
3287 void DocumentImpl::setDesignMode(InheritedBool value)
3289 m_designMode = value;
3292 DocumentImpl::InheritedBool DocumentImpl::getDesignMode() const
3294 return m_designMode;
3297 bool DocumentImpl::inDesignMode() const
3299 for (const DocumentImpl* d = this; d; d = d->parentDocument()) {
3300 if (d->m_designMode != inherit)
3301 return d->m_designMode;
3306 DocumentImpl *DocumentImpl::parentDocument() const
3308 KHTMLPart *childPart = part();
3311 KHTMLPart *parent = childPart->parentPart();
3314 return parent->xmlDocImpl();
3317 DocumentImpl *DocumentImpl::topDocument() const
3319 DocumentImpl *doc = const_cast<DocumentImpl *>(this);
3320 ElementImpl *element;
3321 while ((element = doc->ownerElement()) != 0) {
3322 doc = element->getDocument();
3323 element = doc ? doc->ownerElement() : 0;
3329 // ----------------------------------------------------------------------------
3331 DocumentFragmentImpl::DocumentFragmentImpl(DocumentPtr *doc) : ContainerNodeImpl(doc)
3335 DOMString DocumentFragmentImpl::nodeName() const
3337 return "#document-fragment";
3340 unsigned short DocumentFragmentImpl::nodeType() const
3342 return Node::DOCUMENT_FRAGMENT_NODE;
3345 // DOM Section 1.1.1
3346 bool DocumentFragmentImpl::childTypeAllowed( unsigned short type )
3349 case Node::ELEMENT_NODE:
3350 case Node::PROCESSING_INSTRUCTION_NODE:
3351 case Node::COMMENT_NODE:
3352 case Node::TEXT_NODE:
3353 case Node::CDATA_SECTION_NODE:
3354 case Node::ENTITY_REFERENCE_NODE:
3361 DOMString DocumentFragmentImpl::toString() const
3365 for (NodeImpl *child = firstChild(); child != NULL; child = child->nextSibling()) {
3366 result += child->toString();
3373 NodeImpl *DocumentFragmentImpl::cloneNode ( bool deep )
3375 DocumentFragmentImpl *clone = new DocumentFragmentImpl( docPtr() );
3377 cloneChildNodes(clone);
3382 // ----------------------------------------------------------------------------
3384 DocumentTypeImpl::DocumentTypeImpl(DOMImplementationImpl *implementation, DocumentPtr *doc,
3385 const DOMString &qualifiedName, const DOMString &publicId,
3386 const DOMString &systemId)
3387 : NodeImpl(doc), m_implementation(implementation),
3388 m_qualifiedName(qualifiedName), m_publicId(publicId), m_systemId(systemId)
3390 if (m_implementation)
3391 m_implementation->ref();
3396 // if doc is 0, it is not attached to a document and / or
3397 // therefore does not provide entities or notations. (DOM Level 3)
3400 DocumentTypeImpl::~DocumentTypeImpl()
3402 if (m_implementation)
3403 m_implementation->deref();
3405 m_entities->deref();
3407 m_notations->deref();
3410 void DocumentTypeImpl::copyFrom(const DocumentTypeImpl& other)
3412 m_qualifiedName = other.m_qualifiedName;
3413 m_publicId = other.m_publicId;
3414 m_systemId = other.m_systemId;
3415 m_subset = other.m_subset;
3418 DOMString DocumentTypeImpl::toString() const
3421 if (m_qualifiedName.isEmpty()) {
3424 result = "<!DOCTYPE ";
3425 result += m_qualifiedName;
3427 if (!m_publicId.isEmpty()) {
3428 result += " PUBLIC \"";
3429 result += m_publicId;
3431 result += m_systemId;
3433 } else if (!m_systemId.isEmpty()) {
3434 result += " SYSTEM \"";
3435 result += m_systemId;
3438 if (!m_subset.isEmpty()) {
3447 DOMString DocumentTypeImpl::nodeName() const
3452 unsigned short DocumentTypeImpl::nodeType() const
3454 return Node::DOCUMENT_TYPE_NODE;
3457 // DOM Section 1.1.1
3458 bool DocumentTypeImpl::childTypeAllowed( unsigned short /*type*/ )
3463 NodeImpl *DocumentTypeImpl::cloneNode ( bool /*deep*/ )
3465 // Spec says cloning Document nodes is "implementation dependent"
3466 // so we do not support it...
3470 #include "dom_docimpl.moc"