Reviewed by Vicki.
[WebKit-https.git] / WebCore / khtml / xml / dom_docimpl.cpp
1 /**
2  * This file is part of the DOM implementation for KDE.
3  *
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.
8  *
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.
13  *
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.
18  *
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.
23  */
24
25 #include "dom/dom_exception.h"
26
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"
32
33 #include "xml_namespace_table.h"
34
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"
43
44 #include <qptrstack.h>
45 #include <qpaintdevicemetrics.h>
46 #include <qregexp.h>
47 #include <kdebug.h>
48 #include <kstaticdeleter.h>
49
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"
55
56 #include "khtmlview.h"
57 #include "khtml_part.h"
58
59 #include <kglobalsettings.h>
60 #include <kstringhandler.h>
61 #include "khtml_settings.h"
62 #include "khtmlpart_p.h"
63
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"
75
76 #include "cssvalues.h"
77
78 #include "editing/jsediting.h"
79 #include "editing/visible_position.h"
80 #include "editing/visible_text.h"
81
82 #include <kio/job.h>
83
84 #ifdef KHTML_XSLT
85 #include "xsl_stylesheetimpl.h"
86 #include "xslt_processorimpl.h"
87 #endif
88
89 #ifndef KHTML_NO_XBL
90 #include "xbl/xbl_binding_manager.h"
91 using XBL::XBLBindingManager;
92 #endif
93
94 #if APPLE_CHANGES
95 #include "KWQAccObjectCache.h"
96 #include "KWQLogging.h"
97 #endif
98
99 using namespace DOM;
100 using namespace khtml;
101
102 // #define INSTRUMENT_LAYOUT_SCHEDULING 1
103
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
106 // for dual G5s. :)
107 const int cLayoutScheduleThreshold = 250;
108
109 DOMImplementationImpl *DOMImplementationImpl::m_instance = 0;
110
111 DOMImplementationImpl::DOMImplementationImpl()
112 {
113 }
114
115 DOMImplementationImpl::~DOMImplementationImpl()
116 {
117 }
118
119 bool DOMImplementationImpl::hasFeature ( const DOMString &feature, const DOMString &version )
120 {
121     QString lower = feature.string().lower();
122     if (lower == "core" || lower == "html" || lower == "xml")
123         return version.isEmpty() || version == "1.0" || version == "2.0";
124     if (lower == "css"
125             || lower == "css2"
126             || lower == "events"
127             || lower == "htmlevents"
128             || lower == "mouseevents"
129             || lower == "mutationevents"
130             || lower == "range"
131             || lower == "stylesheets"
132             || lower == "traversal"
133             || lower == "uievents"
134             || lower == "views")
135         return version.isEmpty() || version == "2.0";
136     return false;
137 }
138
139 DocumentTypeImpl *DOMImplementationImpl::createDocumentType( const DOMString &qualifiedName, const DOMString &publicId,
140                                                              const DOMString &systemId, int &exceptioncode )
141 {
142     // Not mentioned in spec: throw NAMESPACE_ERR if no qualifiedName supplied
143     if (qualifiedName.isNull()) {
144         exceptioncode = DOMException::NAMESPACE_ERR;
145         return 0;
146     }
147
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;
151         return 0;
152     }
153
154     // NAMESPACE_ERR: Raised if the qualifiedName is malformed.
155     if (Element::khtmlMalformedQualifiedName(qualifiedName)) {
156         exceptioncode = DOMException::NAMESPACE_ERR;
157         return 0;
158     }
159
160     return new DocumentTypeImpl(this,0,qualifiedName,publicId,systemId);
161 }
162
163 DOMImplementationImpl* DOMImplementationImpl::getInterface(const DOMString& /*feature*/) const
164 {
165     // ###
166     return 0;
167 }
168
169 DocumentImpl *DOMImplementationImpl::createDocument( const DOMString &namespaceURI, const DOMString &qualifiedName,
170                                                      const DocumentType &doctype, int &exceptioncode )
171 {
172     exceptioncode = 0;
173
174     // Not mentioned in spec: throw NAMESPACE_ERR if no qualifiedName supplied
175     if (qualifiedName.isNull()) {
176         exceptioncode = DOMException::NAMESPACE_ERR;
177         return 0;
178     }
179
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;
183         return 0;
184     }
185
186     // NAMESPACE_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].
191     int colonpos = -1;
192     uint i;
193     DOMStringImpl *qname = qualifiedName.implementation();
194     for (i = 0; i < qname->l && colonpos < 0; i++) {
195         if ((*qname)[i] == ':')
196             colonpos = i;
197     }
198
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")) {
203
204         exceptioncode = DOMException::NAMESPACE_ERR;
205         return 0;
206     }
207
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;
213         return 0;
214     }
215
216     // ### this is completely broken.. without a view it will not work (Dirk)
217     DocumentImpl *doc = new DocumentImpl(this, 0);
218
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);
223
224     return doc;
225 }
226
227 CSSStyleSheetImpl *DOMImplementationImpl::createCSSStyleSheet(DOMStringImpl */*title*/, DOMStringImpl *media,
228                                                               int &/*exceptioncode*/)
229 {
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));
235         return sheet;
236 }
237
238 DocumentImpl *DOMImplementationImpl::createDocument( KHTMLView *v )
239 {
240     return new DocumentImpl(this, v);
241 }
242
243 HTMLDocumentImpl *DOMImplementationImpl::createHTMLDocument( KHTMLView *v )
244 {
245     return new HTMLDocumentImpl(this, v);
246 }
247
248 DOMImplementationImpl *DOMImplementationImpl::instance()
249 {
250     if (!m_instance) {
251         m_instance = new DOMImplementationImpl();
252         m_instance->ref();
253     }
254
255     return m_instance;
256 }
257
258 // ------------------------------------------------------------------------
259
260 KStaticDeleter< QPtrList<DocumentImpl> > s_changedDocumentsDeleter;
261 QPtrList<DocumentImpl> * DocumentImpl::changedDocuments = 0;
262
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)
268 #ifndef KHTML_NO_XBL
269       , m_bindingManager(new XBLBindingManager(this))
270 #endif
271 #ifdef KHTML_XSLT
272     , m_transformSource(NULL)
273     , m_transformSourceDocument(0)
274 #endif
275 #if APPLE_CHANGES
276     , m_finishedParsing(this, SIGNAL(finishedParsing()))
277     , m_inPageCache(false)
278     , m_savedRenderer(0)
279     , m_passwordFields(0)
280     , m_secureForms(0)
281     , m_decoder(0)
282     , m_createRenderers(true)
283     , m_designMode(inherit)
284     , m_hasDashboardRegions(false)
285     , m_dashboardRegionsDirty(false)
286 #endif
287 {
288     document->doc = this;
289
290     m_paintDevice = 0;
291     m_paintDeviceMetrics = 0;
292
293     m_view = v;
294     m_renderArena = 0;
295
296 #if APPLE_CHANGES
297     m_accCache = 0;
298 #endif
299     
300     if ( v ) {
301         m_docLoader = new DocLoader(v->part(), this );
302         setPaintDevice( m_view );
303     }
304     else
305         m_docLoader = new DocLoader( 0, this );
306
307     visuallyOrdered = false;
308     m_loadingSheet = false;
309     m_bParsing = false;
310     m_docChanged = false;
311     m_sheet = 0;
312     m_elemSheet = 0;
313     m_tokenizer = 0;
314
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 */);
321     m_doctype->ref();
322
323     m_implementation = _implementation;
324     if (m_implementation)
325         m_implementation->ref();
326     pMode = Strict;
327     hMode = XHtml;
328     m_textColor = Qt::black;
329     m_elementNames = 0;
330     m_elementNameAlloc = 0;
331     m_elementNameCount = 0;
332     m_attrNames = 0;
333     m_attrNameAlloc = 0;
334     m_attrNameCount = 0;
335     m_focusNode = 0;
336     m_hoverNode = 0;
337     m_defaultView = new AbstractViewImpl(this);
338     m_defaultView->ref();
339     m_listenerTypes = 0;
340     m_styleSheets = new StyleSheetListImpl;
341     m_styleSheets->ref();
342     m_inDocument = true;
343     m_styleSelectorDirty = false;
344     m_inStyleRecalc = false;
345     m_usesDescendantRules = false;
346     m_usesSiblingRules = false;
347
348     m_styleSelector = new CSSStyleSelector(this, m_usersheet, m_styleSheets, !inCompatMode());
349     m_windowEventListeners.setAutoDelete(true);
350     m_pendingStylesheets = 0;
351     m_ignorePendingStylesheets = false;
352
353     m_cssTarget = 0;
354     m_accessKeyDictValid = false;
355
356     resetLinkColor();
357     resetVisitedLinkColor();
358     resetActiveLinkColor();
359
360     m_processingLoadEvent = false;
361     m_startTime.restart();
362     m_overMinimumLayoutThreshold = false;
363     
364     m_jsEditor = 0;
365 }
366
367 DocumentImpl::~DocumentImpl()
368 {
369     assert(!m_render);
370 #if APPLE_CHANGES
371     assert(!m_inPageCache);
372     assert(m_savedRenderer == 0);
373 #endif
374     
375     KJS::ScriptInterpreter::forgetDOMObjectsForDocument(this);
376
377     if (changedDocuments && m_docChanged)
378         changedDocuments->remove(this);
379     delete m_tokenizer;
380     document->doc = 0;
381     delete m_sheet;
382     delete m_styleSelector;
383     delete m_docLoader;
384     if (m_elemSheet )  m_elemSheet->deref();
385     if (m_doctype)
386         m_doctype->deref();
387     if (m_implementation)
388         m_implementation->deref();
389     delete m_paintDeviceMetrics;
390
391     if (m_elementNames) {
392         for (unsigned short id = 0; id < m_elementNameCount; id++)
393             m_elementNames[id]->deref();
394         delete [] m_elementNames;
395     }
396     if (m_attrNames) {
397         for (unsigned short id = 0; id < m_attrNameCount; id++)
398             m_attrNames[id]->deref();
399         delete [] m_attrNames;
400     }
401     m_defaultView->deref();
402     m_styleSheets->deref();
403
404     if (m_focusNode)
405         m_focusNode->deref();
406     if (m_hoverNode)
407         m_hoverNode->deref();
408     
409     if (m_renderArena){
410         delete m_renderArena;
411         m_renderArena = 0;
412     }
413
414 #ifdef KHTML_XSLT
415     xmlFreeDoc((xmlDocPtr)m_transformSource);
416     if (m_transformSourceDocument)
417         m_transformSourceDocument->deref();
418 #endif
419
420 #ifndef KHTML_NO_XBL
421     delete m_bindingManager;
422 #endif
423
424 #if APPLE_CHANGES
425     if (m_accCache){
426         delete m_accCache;
427         m_accCache = 0;
428     }
429 #endif
430     
431     if (m_decoder){
432         m_decoder->deref();
433         m_decoder = 0;
434     }
435     
436     if (m_jsEditor) {
437         delete m_jsEditor;
438         m_jsEditor = 0;
439     }
440 }
441
442 void DocumentImpl::resetLinkColor()
443 {
444     m_linkColor = QColor(0, 0, 238);
445 }
446
447 void DocumentImpl::resetVisitedLinkColor()
448 {
449     m_visitedLinkColor = QColor(85, 26, 139);    
450 }
451
452 void DocumentImpl::resetActiveLinkColor()
453 {
454     m_activeLinkColor.setNamedColor(QString("red"));
455 }
456
457 DocumentTypeImpl *DocumentImpl::doctype() const
458 {
459     return m_doctype;
460 }
461
462 DOMImplementationImpl *DocumentImpl::implementation() const
463 {
464     return m_implementation;
465 }
466
467 ElementImpl *DocumentImpl::documentElement() const
468 {
469     NodeImpl *n = firstChild();
470     while (n && n->nodeType() != Node::ELEMENT_NODE)
471       n = n->nextSibling();
472     return static_cast<ElementImpl*>(n);
473 }
474
475 ElementImpl *DocumentImpl::createElement( const DOMString &name, int &exceptioncode )
476 {
477     return new XMLElementImpl( document, name.implementation() );
478 }
479
480 DocumentFragmentImpl *DocumentImpl::createDocumentFragment(  )
481 {
482     return new DocumentFragmentImpl( docPtr() );
483 }
484
485 TextImpl *DocumentImpl::createTextNode( const DOMString &data )
486 {
487     return new TextImpl( docPtr(), data);
488 }
489
490 CommentImpl *DocumentImpl::createComment ( const DOMString &data )
491 {
492     return new CommentImpl( docPtr(), data );
493 }
494
495 CDATASectionImpl *DocumentImpl::createCDATASection ( const DOMString &data )
496 {
497     return new CDATASectionImpl( docPtr(), data );
498 }
499
500 ProcessingInstructionImpl *DocumentImpl::createProcessingInstruction ( const DOMString &target, const DOMString &data )
501 {
502     return new ProcessingInstructionImpl( docPtr(),target,data);
503 }
504
505 Attr DocumentImpl::createAttribute( NodeImpl::Id id )
506 {
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()));
510 }
511
512 EntityReferenceImpl *DocumentImpl::createEntityReference ( const DOMString &name )
513 {
514     return new EntityReferenceImpl(docPtr(), name.implementation());
515 }
516
517 EditingTextImpl *DocumentImpl::createEditingTextNode(const DOMString &text)
518 {
519     return new EditingTextImpl(docPtr(), text);
520 }
521
522 CSSStyleDeclarationImpl *DocumentImpl::createCSSStyleDeclaration()
523 {
524     return new CSSMutableStyleDeclarationImpl;
525 }
526
527 NodeImpl *DocumentImpl::importNode(NodeImpl *importedNode, bool deep, int &exceptioncode)
528 {
529         NodeImpl *result = 0;
530
531         if(importedNode->nodeType() == Node::ELEMENT_NODE)
532         {
533                 ElementImpl *tempElementImpl = createElementNS(getDocument()->namespaceURI(id()), importedNode->nodeName(), exceptioncode);
534                 if (exceptioncode)
535                     return 0;
536                 result = tempElementImpl;
537
538                 if(static_cast<ElementImpl *>(importedNode)->attributes(true) && static_cast<ElementImpl *>(importedNode)->attributes(true)->length())
539                 {
540                         NamedNodeMapImpl *attr = static_cast<ElementImpl *>(importedNode)->attributes();
541
542                         for(unsigned int i = 0; i < attr->length(); i++)
543                         {
544                                 DOMString qualifiedName = attr->item(i)->nodeName();
545                                 DOMString value = attr->item(i)->nodeValue();
546
547                                 int colonpos = qualifiedName.find(':');
548                                 DOMString localName = qualifiedName;
549                                 if(colonpos >= 0)
550                                 {
551                                         localName.remove(0, colonpos + 1);
552                                         // ### extract and set new prefix
553                                 }
554
555                                 NodeImpl::Id nodeId = getDocument()->attrId(getDocument()->namespaceURI(id()), localName.implementation(), false /* allocate */);
556                                 tempElementImpl->setAttribute(nodeId, value.implementation(), exceptioncode);
557
558                                 if(exceptioncode != 0)
559                                         break;
560                         }
561                 }
562         }
563         else if(importedNode->nodeType() == Node::TEXT_NODE)
564         {
565                 result = createTextNode(importedNode->nodeValue());
566                 deep = false;
567         }
568         else if(importedNode->nodeType() == Node::CDATA_SECTION_NODE)
569         {
570                 result = createCDATASection(importedNode->nodeValue());
571                 deep = false;
572         }
573         else if(importedNode->nodeType() == Node::ENTITY_REFERENCE_NODE)
574                 result = createEntityReference(importedNode->nodeName());
575         else if(importedNode->nodeType() == Node::PROCESSING_INSTRUCTION_NODE)
576         {
577                 result = createProcessingInstruction(importedNode->nodeName(), importedNode->nodeValue());
578                 deep = false;
579         }
580         else if(importedNode->nodeType() == Node::COMMENT_NODE)
581         {
582                 result = createComment(importedNode->nodeValue());
583                 deep = false;
584         }
585         else
586                 exceptioncode = DOMException::NOT_SUPPORTED_ERR;
587
588         if(deep)
589         {
590                 for(Node n = importedNode->firstChild(); !n.isNull(); n = n.nextSibling())
591                         result->appendChild(importNode(n.handle(), true, exceptioncode), exceptioncode);
592         }
593
594         return result;
595 }
596
597 ElementImpl *DocumentImpl::createElementNS( const DOMString &_namespaceURI, const DOMString &_qualifiedName, int &exceptioncode)
598 {
599     ElementImpl *e = 0;
600     QString qName = _qualifiedName.string();
601     int colonPos = qName.find(':',0);
602
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);
607         if (exceptioncode)
608             return 0;
609         if (e && colonPos >= 0) {
610             e->setPrefix(qName.left(colonPos), exceptioncode);
611             if (exceptioncode) {
612                 delete e;
613                 return 0;
614             }
615         }
616     }
617     if (!e)
618         e = new XMLElementImpl( document, _qualifiedName.implementation(), _namespaceURI.implementation() );
619
620     return e;
621 }
622
623 ElementImpl *DocumentImpl::getElementById( const DOMString &elementId ) const
624 {
625     if (elementId.length() == 0) {
626         return 0;
627     }
628
629     return m_elementsById.find(elementId.string());
630 }
631
632
633 void DocumentImpl::addElementById(const DOMString &elementId, ElementImpl *element)
634 {
635     QString qId = elementId.string();
636
637     if (m_elementsById.find(qId) == NULL) {
638         m_elementsById.insert(qId, element);
639         m_accessKeyDictValid = false;
640     }
641 }
642
643 void DocumentImpl::removeElementById(const DOMString &elementId, ElementImpl *element)
644 {
645     QString qId = elementId.string();
646
647     if (m_elementsById.find(qId) == element) {
648         m_elementsById.remove(qId);
649         m_accessKeyDictValid = false;
650     }
651 }
652
653 ElementImpl *DocumentImpl::getElementByAccessKey( const DOMString &key )
654 {
655     if (key.length() == 0)
656         return 0;
657
658     QString k(key.string());
659     if (!m_accessKeyDictValid) {
660         m_elementsByAccessKey.clear();
661     
662         const NodeImpl *n;
663         for (n = this; n != 0; n = n->traverseNextNode()) {
664             if (!n->isElementNode())
665                 continue;
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);
672             }
673         }
674         m_accessKeyDictValid = true;
675     }
676     return m_elementsByAccessKey.find(k);
677 }
678
679 void DocumentImpl::setTitle(DOMString _title)
680 {
681     m_title = _title;
682
683     if (!part())
684         return;
685
686 #if APPLE_CHANGES
687     KWQ(part())->setTitle(_title);
688 #else
689     QString titleStr = m_title.string();
690     for (int i = 0; i < titleStr.length(); ++i)
691         if (titleStr[i] < ' ')
692             titleStr[i] = ' ';
693     titleStr = titleStr.stripWhiteSpace();
694     titleStr.compose();
695     if ( !part()->parentPart() ) {
696         if (titleStr.isNull() || titleStr.isEmpty()) {
697             // empty title... set window caption as the URL
698             KURL url = m_url;
699             url.setRef(QString::null);
700             url.setQuery(QString::null);
701             titleStr = url.url();
702         }
703
704         emit part()->setWindowCaption( KStringHandler::csqueeze( titleStr, 128 ) );
705     }
706 #endif
707 }
708
709 DOMString DocumentImpl::nodeName() const
710 {
711     return "#document";
712 }
713
714 unsigned short DocumentImpl::nodeType() const
715 {
716     return Node::DOCUMENT_NODE;
717 }
718
719 ElementImpl *DocumentImpl::createHTMLElement( const DOMString &name, int &exceptioncode )
720 {
721     if (!isValidName(name)) {
722         exceptioncode = DOMException::INVALID_CHARACTER_ERR;
723         return 0;
724     }
725     return createHTMLElement(tagId(0, name.implementation(), false));
726 }
727
728 ElementImpl *DocumentImpl::createHTMLElement(unsigned short tagID)
729 {
730     switch (tagID)
731     {
732     case ID_HTML:
733         return new HTMLHtmlElementImpl(docPtr());
734     case ID_HEAD:
735         return new HTMLHeadElementImpl(docPtr());
736     case ID_BODY:
737         return new HTMLBodyElementImpl(docPtr());
738
739 // head elements
740     case ID_BASE:
741         return new HTMLBaseElementImpl(docPtr());
742     case ID_LINK:
743         return new HTMLLinkElementImpl(docPtr());
744     case ID_META:
745         return new HTMLMetaElementImpl(docPtr());
746     case ID_STYLE:
747         return new HTMLStyleElementImpl(docPtr());
748     case ID_TITLE:
749         return new HTMLTitleElementImpl(docPtr());
750
751 // frames
752     case ID_FRAME:
753         return new HTMLFrameElementImpl(docPtr());
754     case ID_FRAMESET:
755         return new HTMLFrameSetElementImpl(docPtr());
756     case ID_IFRAME:
757         return new HTMLIFrameElementImpl(docPtr());
758
759 // form elements
760 // ### FIXME: we need a way to set form dependency after we have made the form elements
761     case ID_FORM:
762         return new HTMLFormElementImpl(docPtr());
763     case ID_BUTTON:
764         return new HTMLButtonElementImpl(docPtr());
765     case ID_FIELDSET:
766         return new HTMLFieldSetElementImpl(docPtr());
767     case ID_INPUT:
768         return new HTMLInputElementImpl(docPtr());
769     case ID_ISINDEX:
770         return new HTMLIsIndexElementImpl(docPtr());
771     case ID_LABEL:
772         return new HTMLLabelElementImpl(docPtr());
773     case ID_LEGEND:
774         return new HTMLLegendElementImpl(docPtr());
775     case ID_OPTGROUP:
776         return new HTMLOptGroupElementImpl(docPtr());
777     case ID_OPTION:
778         return new HTMLOptionElementImpl(docPtr());
779     case ID_SELECT:
780         return new HTMLSelectElementImpl(docPtr());
781     case ID_TEXTAREA:
782         return new HTMLTextAreaElementImpl(docPtr());
783
784 // lists
785     case ID_DL:
786         return new HTMLDListElementImpl(docPtr());
787     case ID_DD:
788         return new HTMLGenericElementImpl(docPtr(), tagID);
789     case ID_DT:
790         return new HTMLGenericElementImpl(docPtr(), tagID);
791     case ID_UL:
792         return new HTMLUListElementImpl(docPtr());
793     case ID_OL:
794         return new HTMLOListElementImpl(docPtr());
795     case ID_DIR:
796         return new HTMLDirectoryElementImpl(docPtr());
797     case ID_MENU:
798         return new HTMLMenuElementImpl(docPtr());
799     case ID_LI:
800         return new HTMLLIElementImpl(docPtr());
801
802 // formatting elements (block)
803     case ID_BLOCKQUOTE:
804         return new HTMLBlockquoteElementImpl(docPtr());
805     case ID_DIV:
806         return new HTMLDivElementImpl(docPtr());
807     case ID_H1:
808     case ID_H2:
809     case ID_H3:
810     case ID_H4:
811     case ID_H5:
812     case ID_H6:
813         return new HTMLHeadingElementImpl(docPtr(), tagID);
814     case ID_HR:
815         return new HTMLHRElementImpl(docPtr());
816     case ID_P:
817         return new HTMLParagraphElementImpl(docPtr());
818     case ID_PRE:
819     case ID_XMP:
820     case ID_PLAINTEXT:
821         return new HTMLPreElementImpl(docPtr(), tagID);
822     case ID_LAYER:
823         return new HTMLLayerElementImpl(docPtr());
824
825 // font stuff
826     case ID_BASEFONT:
827         return new HTMLBaseFontElementImpl(docPtr());
828     case ID_FONT:
829         return new HTMLFontElementImpl(docPtr());
830
831 // ins/del
832     case ID_DEL:
833     case ID_INS:
834         return new HTMLGenericElementImpl(docPtr(), tagID);
835
836 // anchor
837     case ID_A:
838         return new HTMLAnchorElementImpl(docPtr());
839
840 // images
841     case ID_IMG:
842         return new HTMLImageElementImpl(docPtr());
843     case ID_MAP:
844         return new HTMLMapElementImpl(docPtr());
845     case ID_AREA:
846         return new HTMLAreaElementImpl(docPtr());
847     case ID_CANVAS:
848         return new HTMLCanvasElementImpl(docPtr());
849
850 // objects, applets and scripts
851     case ID_APPLET:
852         return new HTMLAppletElementImpl(docPtr());
853     case ID_EMBED:
854         return new HTMLEmbedElementImpl(docPtr());
855     case ID_OBJECT:
856         return new HTMLObjectElementImpl(docPtr());
857     case ID_PARAM:
858         return new HTMLParamElementImpl(docPtr());
859     case ID_SCRIPT:
860         return new HTMLScriptElementImpl(docPtr());
861
862 // tables
863     case ID_TABLE:
864         return new HTMLTableElementImpl(docPtr());
865     case ID_CAPTION:
866         return new HTMLTableCaptionElementImpl(docPtr());
867     case ID_COLGROUP:
868     case ID_COL:
869         return new HTMLTableColElementImpl(docPtr(), tagID);
870     case ID_TR:
871         return new HTMLTableRowElementImpl(docPtr());
872     case ID_TD:
873     case ID_TH:
874         return new HTMLTableCellElementImpl(docPtr(), tagID);
875     case ID_THEAD:
876     case ID_TBODY:
877     case ID_TFOOT:
878         return new HTMLTableSectionElementImpl(docPtr(), tagID, false);
879
880 // inline elements
881     case ID_BR:
882         return new HTMLBRElementImpl(docPtr());
883     case ID_Q:
884         return new HTMLGenericElementImpl(docPtr(), tagID);
885
886 // elements with no special representation in the DOM
887
888 // block:
889     case ID_ADDRESS:
890     case ID_CENTER:
891
892 // inline
893         // %fontstyle
894     case ID_TT:
895     case ID_U:
896     case ID_B:
897     case ID_I:
898     case ID_S:
899     case ID_STRIKE:
900     case ID_BIG:
901     case ID_SMALL:
902
903         // %phrase
904     case ID_EM:
905     case ID_STRONG:
906     case ID_DFN:
907     case ID_CODE:
908     case ID_SAMP:
909     case ID_KBD:
910     case ID_VAR:
911     case ID_CITE:
912     case ID_ABBR:
913     case ID_ACRONYM:
914
915         // %special
916     case ID_SUB:
917     case ID_SUP:
918     case ID_SPAN:
919     case ID_NOBR:
920     case ID_WBR:
921
922     case ID_BDO:
923     default:
924         return new HTMLGenericElementImpl(docPtr(), tagID);
925
926     case ID_MARQUEE:
927         return new HTMLMarqueeElementImpl(docPtr());
928         
929 // text
930     case ID_TEXT:
931         kdDebug( 6020 ) << "Use document->createTextNode()" << endl;
932         return 0;
933     }
934
935     return 0;
936 }
937
938 QString DocumentImpl::nextState()
939 {
940    QString state;
941    if (!m_state.isEmpty())
942    {
943       state = m_state.first();
944       m_state.remove(m_state.begin());
945    }
946    return state;
947 }
948
949 QStringList DocumentImpl::docState()
950 {
951     QStringList s;
952     for (QPtrListIterator<NodeImpl> it(m_maintainsState); it.current(); ++it)
953         s.append(it.current()->state());
954
955     return s;
956 }
957
958 KHTMLPart *DocumentImpl::part() const 
959 {
960     return m_view ? m_view->part() : 0; 
961 }
962
963 RangeImpl *DocumentImpl::createRange()
964 {
965     return new RangeImpl( docPtr() );
966 }
967
968 NodeIteratorImpl *DocumentImpl::createNodeIterator(NodeImpl *root, unsigned long whatToShow, 
969     NodeFilterImpl *filter, bool expandEntityReferences, int &exceptioncode)
970 {
971     if (!root) {
972         exceptioncode = DOMException::NOT_SUPPORTED_ERR;
973         return 0;
974     }
975     return new NodeIteratorImpl(root, whatToShow, filter, expandEntityReferences);
976 }
977
978 TreeWalkerImpl *DocumentImpl::createTreeWalker(NodeImpl *root, unsigned long whatToShow, 
979     NodeFilterImpl *filter, bool expandEntityReferences, int &exceptioncode)
980 {
981     if (!root) {
982         exceptioncode = DOMException::NOT_SUPPORTED_ERR;
983         return 0;
984     }
985     return new TreeWalkerImpl(root, whatToShow, filter, expandEntityReferences);
986 }
987
988 void DocumentImpl::setDocumentChanged(bool b)
989 {
990     if (!changedDocuments)
991         changedDocuments = s_changedDocumentsDeleter.setObject( new QPtrList<DocumentImpl>() );
992
993     if (b && !m_docChanged)
994         changedDocuments->append(this);
995     else if (!b && m_docChanged)
996         changedDocuments->remove(this);
997     m_docChanged = b;
998     
999     if (m_docChanged)
1000         m_accessKeyDictValid = false;
1001 }
1002
1003 void DocumentImpl::recalcStyle( StyleChange change )
1004 {
1005 //     qDebug("recalcStyle(%p)", this);
1006 //     QTime qt;
1007 //     qt.start();
1008     if (m_inStyleRecalc)
1009         return; // Guard against re-entrancy. -dwh
1010         
1011     m_inStyleRecalc = true;
1012     
1013     if( !m_render ) goto bail_out;
1014
1015     if ( change == Force ) {
1016         RenderStyle* oldStyle = m_render->style();
1017         if ( oldStyle ) oldStyle->ref();
1018         RenderStyle* _style = new (m_renderArena) RenderStyle();
1019         _style->ref();
1020         _style->setDisplay(BLOCK);
1021         _style->setVisuallyOrdered( visuallyOrdered );
1022         // ### make the font stuff _really_ work!!!!
1023
1024         khtml::FontDef fontDef;
1025         QFont f = KGlobalSettings::generalFont();
1026         fontDef.family = *(f.firstFamily());
1027         fontDef.italic = f.italic();
1028         fontDef.weight = f.weight();
1029 #if APPLE_CHANGES
1030         bool printing = m_paintDevice && (m_paintDevice->devType() == QInternal::Printer);
1031         fontDef.usePrinterFont = printing;
1032 #endif
1033         if (m_view) {
1034             const KHTMLSettings *settings = m_view->part()->settings();
1035 #if APPLE_CHANGES
1036             if (printing && !settings->shouldPrintBackgrounds()) {
1037                 _style->setShouldCorrectTextColor(true);
1038             }
1039 #endif
1040             QString stdfont = settings->stdFontName();
1041             if ( !stdfont.isEmpty() ) {
1042                 fontDef.family.setFamily(stdfont);
1043                 fontDef.family.appendFamily(0);
1044             }
1045             m_styleSelector->setFontSize(fontDef, m_styleSelector->fontSizeForKeyword(CSS_VAL_MEDIUM, inCompatMode()));
1046         }
1047
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
1053
1054         StyleChange ch = diff( _style, oldStyle );
1055         if(m_render && ch != NoChange)
1056             m_render->setStyle(_style);
1057         if ( change != Force )
1058             change = ch;
1059
1060         _style->deref(m_renderArena);
1061         if (oldStyle)
1062             oldStyle->deref(m_renderArena);
1063     }
1064
1065     NodeImpl *n;
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;
1070
1071     if (changed() && m_view)
1072         m_view->layout();
1073
1074 bail_out:
1075     setChanged( false );
1076     setHasChangedChild( false );
1077     setDocumentChanged( false );
1078     
1079     m_inStyleRecalc = false;
1080 }
1081
1082 void DocumentImpl::updateRendering()
1083 {
1084     if (!hasChangedChild()) return;
1085
1086 //     QTime time;
1087 //     time.start();
1088 //     kdDebug() << "UPDATERENDERING: "<<endl;
1089
1090     StyleChange change = NoChange;
1091 #if 0
1092     if ( m_styleSelectorDirty ) {
1093         recalcStyleSelector();
1094         change = Force;
1095     }
1096 #endif
1097     recalcStyle( change );
1098
1099 //    kdDebug() << "UPDATERENDERING time used="<<time.elapsed()<<endl;
1100 }
1101
1102 void DocumentImpl::updateDocumentsRendering()
1103 {
1104     if (!changedDocuments)
1105         return;
1106
1107     while (DocumentImpl* doc = changedDocuments->take()) {
1108         doc->m_docChanged = false;
1109         doc->updateRendering();
1110     }
1111 }
1112
1113 void DocumentImpl::updateLayout()
1114 {
1115     // FIXME: Dave's pretty sure we can remove this because
1116     // layout calls recalcStyle as needed.
1117     updateRendering();
1118
1119     // Only do a layout if changes have occurred that make it necessary.      
1120     if (m_view && renderer() && renderer()->needsLayout())
1121         m_view->layout();
1122 }
1123
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()
1131 {
1132     bool oldIgnore = m_ignorePendingStylesheets;
1133     
1134     if (!haveStylesheetsLoaded()) {
1135         m_ignorePendingStylesheets = true;
1136         updateStyleSelector();    
1137     }
1138
1139     updateLayout();
1140
1141     m_ignorePendingStylesheets = oldIgnore;
1142 }
1143
1144 void DocumentImpl::attach()
1145 {
1146     assert(!attached());
1147 #if APPLE_CHANGES
1148     assert(!m_inPageCache);
1149 #endif
1150
1151     if ( m_view )
1152         setPaintDevice( m_view );
1153
1154     if (!m_renderArena)
1155         m_renderArena = new RenderArena();
1156     
1157     // Create the rendering tree
1158     m_render = new (m_renderArena) RenderCanvas(this, m_view);
1159     recalcStyle( Force );
1160
1161     RenderObject* render = m_render;
1162     m_render = 0;
1163
1164     NodeBaseImpl::attach();
1165     m_render = render;
1166 }
1167
1168 void DocumentImpl::restoreRenderer(RenderObject* render)
1169 {
1170     m_render = render;
1171 }
1172
1173 void DocumentImpl::detach()
1174 {
1175     RenderObject* render = m_render;
1176
1177     // indicate destruction mode,  i.e. attached() but m_render == 0
1178     m_render = 0;
1179     
1180 #if APPLE_CHANGES
1181     if (m_inPageCache) {
1182         if ( render )
1183             getAccObjectCache()->detach(render);
1184         return;
1185     }
1186 #endif
1187
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();
1193
1194     removeAllEventListenersFromAllNodes();
1195
1196     NodeBaseImpl::detach();
1197
1198     if ( render )
1199         render->detach();
1200
1201     if (m_paintDevice == m_view)
1202         setPaintDevice(0);
1203     m_view = 0;
1204     
1205     if (m_renderArena){
1206         delete m_renderArena;
1207         m_renderArena = 0;
1208     }
1209 }
1210
1211 void DocumentImpl::removeAllEventListenersFromAllNodes()
1212 {
1213     m_windowEventListeners.clear();
1214     removeAllDisconnectedNodeEventListeners();
1215     for (NodeImpl *n = this; n; n = n->traverseNextNode()) {
1216         n->removeAllEventListeners();
1217     }
1218 }
1219
1220 void DocumentImpl::registerDisconnectedNodeWithEventListeners(NodeImpl *node)
1221 {
1222     m_disconnectedNodesWithEventListeners.insert(node, node);
1223 }
1224
1225 void DocumentImpl::unregisterDisconnectedNodeWithEventListeners(NodeImpl *node)
1226 {
1227     m_disconnectedNodesWithEventListeners.remove(node);
1228 }
1229
1230 void DocumentImpl::removeAllDisconnectedNodeEventListeners()
1231 {
1232     for (QPtrDictIterator<NodeImpl> iter(m_disconnectedNodesWithEventListeners);
1233          iter.current();
1234          ++iter) {
1235         iter.current()->removeAllEventListeners();
1236     }
1237 }
1238
1239 #if APPLE_CHANGES
1240 KWQAccObjectCache* DocumentImpl::getAccObjectCache()
1241 {
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).
1246     
1247     if (m_accCache) {
1248         // return already known top-level cache
1249         if (!ownerElement())
1250             return m_accCache;
1251         
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.
1255         delete m_accCache;
1256         m_accCache = 0;
1257     }
1258     
1259     // look for top-level document
1260     ElementImpl *element = ownerElement();
1261     if (element) {
1262         DocumentImpl *doc;
1263         while (element) {
1264             doc = element->getDocument();
1265             element = doc->ownerElement();
1266         }
1267         
1268         // ask the top-level document for its cache
1269         return doc->getAccObjectCache();
1270     }
1271     
1272     // this is the top-level document, so install a new cache
1273     m_accCache = new KWQAccObjectCache;
1274     return m_accCache;
1275 }
1276 #endif
1277
1278 void DocumentImpl::setVisuallyOrdered()
1279 {
1280     visuallyOrdered = true;
1281     if (m_render)
1282         m_render->style()->setVisuallyOrdered(true);
1283 }
1284
1285 void DocumentImpl::updateSelection()
1286 {
1287     if (!m_render)
1288         return;
1289     
1290     RenderCanvas *canvas = static_cast<RenderCanvas*>(m_render);
1291     Selection s = part()->selection();
1292     if (!s.isRange()) {
1293         canvas->clearSelection();
1294     }
1295     else {
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());
1302         }
1303     }
1304     
1305 #if APPLE_CHANGES
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");
1310     }
1311 #endif
1312
1313 }
1314
1315 Tokenizer *DocumentImpl::createTokenizer()
1316 {
1317     return newXMLTokenizer(docPtr(), m_view);
1318 }
1319
1320 void DocumentImpl::setPaintDevice( QPaintDevice *dev )
1321 {
1322     if (m_paintDevice == dev) {
1323         return;
1324     }
1325     m_paintDevice = dev;
1326     delete m_paintDeviceMetrics;
1327     m_paintDeviceMetrics = dev ? new QPaintDeviceMetrics( dev ) : 0;
1328 }
1329
1330 void DocumentImpl::open(  )
1331 {
1332     if (parsing()) return;
1333
1334     implicitOpen();
1335
1336     if (part()) {
1337         part()->didExplicitOpen();
1338     }
1339
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.
1342     setURL(QString());
1343     DocumentImpl *parent = parentDocument();
1344     if (parent) {
1345         setBaseURL(parent->baseURL());
1346     }
1347 }
1348
1349 void DocumentImpl::implicitOpen()
1350 {
1351     if (m_tokenizer)
1352         close();
1353
1354     clear();
1355     m_tokenizer = createTokenizer();
1356     connect(m_tokenizer,SIGNAL(finishedParsing()),this,SIGNAL(finishedParsing()));
1357     setParsing(true);
1358
1359     if (m_view && m_view->part()->jScript()) {
1360         m_view->part()->jScript()->setSourceFile(m_url,""); //fixme
1361     }
1362 }
1363
1364 HTMLElementImpl* DocumentImpl::body()
1365 {
1366     NodeImpl *de = documentElement();
1367     if (!de)
1368         return 0;
1369     
1370     // try to prefer a FRAMESET element over BODY
1371     NodeImpl* body = 0;
1372     for (NodeImpl* i = de->firstChild(); i; i = i->nextSibling()) {
1373         if (i->id() == ID_FRAMESET)
1374             return static_cast<HTMLElementImpl*>(i);
1375         
1376         if (i->id() == ID_BODY)
1377             body = i;
1378     }
1379     return static_cast<HTMLElementImpl *>(body);
1380 }
1381
1382 void DocumentImpl::close()
1383 {
1384     if (part())
1385         part()->endIfNotLoading();
1386     implicitClose();
1387 }
1388
1389 void DocumentImpl::implicitClose()
1390 {
1391     // First fire the onload.
1392     
1393     bool wasLocationChangePending = part() && part()->isScheduledLocationChangePending();
1394     bool doload = !parsing() && m_tokenizer && !m_processingLoadEvent && !wasLocationChangePending;
1395     
1396     if (doload) {
1397         m_processingLoadEvent = true;
1398
1399         // We have to clear the tokenizer, in case someone document.write()s from the
1400         // onLoad event handler, as in Radar 3206524
1401         delete m_tokenizer;
1402         m_tokenizer = 0;
1403
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();
1410             if (de) {
1411                 body = new HTMLBodyElementImpl(docPtr());
1412                 int exceptionCode = 0;
1413                 de->appendChild(body, exceptionCode);
1414                 if (exceptionCode != 0)
1415                     body = 0;
1416             }
1417         }
1418
1419         if (body) {
1420             dispatchImageLoadEventsNow();
1421             body->dispatchWindowEvent(EventImpl::LOAD_EVENT, false, false);
1422
1423 #ifdef INSTRUMENT_LAYOUT_SCHEDULING
1424             if (!ownerElement())
1425                 printf("onload fired at %d\n", elapsedTime());
1426 #endif
1427         }
1428
1429         m_processingLoadEvent = false;
1430     }
1431     
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
1435     
1436     bool isLocationChangePending = part() && part()->isScheduledLocationChangePending();
1437     
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.        
1441         delete m_tokenizer;
1442         m_tokenizer = 0;
1443         view()->unscheduleRelayout();
1444         return;
1445     }
1446     
1447     if (doload) {
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()) {
1454             delete m_tokenizer;
1455             m_tokenizer = 0;
1456         }
1457
1458         if (m_view)
1459             m_view->part()->checkEmitLoadEvent();
1460     }
1461
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.
1465     if (doload) {
1466         if (!ownerElement() || (ownerElement()->renderer() && !ownerElement()->renderer()->needsLayout())) {
1467             updateRendering();
1468             
1469             // Always do a layout after loading if needed.
1470             if (view() && renderer() && (!renderer()->firstChild() || renderer()->needsLayout()))
1471                 view()->layout();
1472         }
1473 #if APPLE_CHANGES
1474         if (renderer() && KWQAccObjectCache::accessibilityEnabled())
1475             getAccObjectCache()->postNotification(renderer(), "AXLoadComplete");
1476 #endif
1477     }
1478 }
1479
1480 void DocumentImpl::setParsing(bool b)
1481 {
1482     m_bParsing = b;
1483     if (!m_bParsing && view())
1484         view()->scheduleRelayout();
1485
1486 #ifdef INSTRUMENT_LAYOUT_SCHEDULING
1487     if (!ownerElement() && !m_bParsing)
1488         printf("Parsing finished at %d\n", elapsedTime());
1489 #endif
1490 }
1491
1492 bool DocumentImpl::shouldScheduleLayout()
1493 {
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()));
1501 }
1502
1503 int DocumentImpl::minimumLayoutDelay()
1504 {
1505     if (m_overMinimumLayoutThreshold)
1506         return 0;
1507     
1508     int elapsed = m_startTime.elapsed();
1509     m_overMinimumLayoutThreshold = elapsed > cLayoutScheduleThreshold;
1510     
1511     // We'll want to schedule the timer to fire at the minimum layout threshold.
1512     return kMax(0, cLayoutScheduleThreshold - elapsed);
1513 }
1514
1515 int DocumentImpl::elapsedTime() const
1516 {
1517     return m_startTime.elapsed();
1518 }
1519
1520 void DocumentImpl::write( const DOMString &text )
1521 {
1522     write(text.string());
1523 }
1524
1525 void DocumentImpl::write( const QString &text )
1526 {
1527 #ifdef INSTRUMENT_LAYOUT_SCHEDULING
1528     if (!ownerElement())
1529         printf("Beginning a document.write at %d\n", elapsedTime());
1530 #endif
1531     
1532     if (!m_tokenizer) {
1533         open();
1534         write(QString::fromLatin1("<html>"));
1535     }
1536     m_tokenizer->write(text, false);
1537
1538     if (m_view && m_view->part()->jScript())
1539         m_view->part()->jScript()->appendSourceFile(m_url,text);
1540     
1541 #ifdef INSTRUMENT_LAYOUT_SCHEDULING
1542     if (!ownerElement())
1543         printf("Ending a document.write at %d\n", elapsedTime());
1544 #endif    
1545 }
1546
1547 void DocumentImpl::writeln( const DOMString &text )
1548 {
1549     write(text);
1550     write(DOMString("\n"));
1551 }
1552
1553 void DocumentImpl::finishParsing()
1554 {
1555 #ifdef INSTRUMENT_LAYOUT_SCHEDULING
1556     if (!ownerElement())
1557         printf("Received all data at %d\n", elapsedTime());
1558 #endif
1559     
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.
1565     if (m_tokenizer)
1566         m_tokenizer->finish();
1567 }
1568
1569 void DocumentImpl::clear()
1570 {
1571     delete m_tokenizer;
1572     m_tokenizer = 0;
1573
1574     removeChildren();
1575     QPtrListIterator<RegisteredEventListener> it(m_windowEventListeners);
1576     for (; it.current();)
1577         m_windowEventListeners.removeRef(it.current());
1578 }
1579
1580 void DocumentImpl::setURL(const QString& url)
1581 {
1582     m_url = url;
1583     if (m_styleSelector)
1584         m_styleSelector->setEncodedURL(m_url);
1585 }
1586
1587 void DocumentImpl::setStyleSheet(const DOMString &url, const DOMString &sheet)
1588 {
1589 //    kdDebug( 6030 ) << "HTMLDocument::setStyleSheet()" << endl;
1590     m_sheet = new CSSStyleSheetImpl(this, url);
1591     m_sheet->ref();
1592     m_sheet->parseString(sheet);
1593     m_loadingSheet = false;
1594
1595     updateStyleSelector();
1596 }
1597
1598 void DocumentImpl::setUserStyleSheet( const QString& sheet )
1599 {
1600     if ( m_usersheet != sheet ) {
1601         m_usersheet = sheet;
1602         updateStyleSelector();
1603     }
1604 }
1605
1606 CSSStyleSheetImpl* DocumentImpl::elementSheet()
1607 {
1608     if (!m_elemSheet) {
1609         m_elemSheet = new CSSStyleSheetImpl(this, baseURL() );
1610         m_elemSheet->ref();
1611     }
1612     return m_elemSheet;
1613 }
1614
1615 void DocumentImpl::determineParseMode( const QString &/*str*/ )
1616 {
1617     // For XML documents use strict parse mode.  HTML docs will override this method to
1618     // determine their parse mode.
1619     pMode = Strict;
1620     hMode = XHtml;
1621     kdDebug(6020) << " using strict parseMode" << endl;
1622 }
1623
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 )
1627 {
1628     QPtrStack<NodeImpl> nodeStack;
1629     NodeImpl *current = _first;
1630
1631     while(1)
1632     {
1633         if(!current)
1634         {
1635             if(nodeStack.isEmpty()) break;
1636             current = nodeStack.pop();
1637             current = current->nextSibling();
1638         }
1639         else
1640         {
1641             if(current->id() == id)
1642                 return current;
1643
1644             NodeImpl *child = current->firstChild();
1645             if(child)
1646             {
1647                 nodeStack.push(current);
1648                 current = child;
1649             }
1650             else
1651             {
1652                 current = current->nextSibling();
1653             }
1654         }
1655     }
1656
1657     return 0;
1658 }
1659
1660 NodeImpl *DocumentImpl::nextFocusNode(NodeImpl *fromNode)
1661 {
1662     unsigned short fromTabIndex;
1663
1664     if (!fromNode) {
1665         // No starting node supplied; begin with the top of the document
1666         NodeImpl *n;
1667
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();
1673             }
1674         }
1675
1676         if (lowestTabIndex == 65535)
1677             lowestTabIndex = 0;
1678
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))
1682                 return n;
1683         }
1684
1685         return 0;
1686     }
1687     else {
1688         fromTabIndex = fromNode->tabIndex();
1689     }
1690
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();
1696         return n;
1697     }
1698     else {
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;
1704         NodeImpl *n;
1705
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) &&
1712                 (n != fromNode)) {
1713
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();
1717             }
1718
1719             if (n == fromNode)
1720                 reachedFromNode = true;
1721         }
1722
1723         if (lowestSuitableTabIndex == 65535) {
1724             // No next node with a tab index -> just take first node with tab index of 0
1725             NodeImpl *n = this;
1726             while (n && !(n->isKeyboardFocusable() && n->tabIndex() == 0))
1727                 n = n->traverseNextNode();
1728             return n;
1729         }
1730
1731         // Search forwards from fromNode
1732         for (n = fromNode->traverseNextNode(); n != 0; n = n->traverseNextNode()) {
1733             if (n->isKeyboardFocusable() && (n->tabIndex() == lowestSuitableTabIndex))
1734                 return n;
1735         }
1736
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))
1740                 return n;
1741         }
1742
1743         assert(false); // should never get here
1744         return 0;
1745     }
1746 }
1747
1748 NodeImpl *DocumentImpl::previousFocusNode(NodeImpl *fromNode)
1749 {
1750     NodeImpl *lastNode = this;
1751     while (lastNode->lastChild())
1752         lastNode = lastNode->lastChild();
1753
1754     if (!fromNode) {
1755         // No starting node supplied; begin with the very last node in the document
1756         NodeImpl *n;
1757
1758         int highestTabIndex = 0;
1759         for (n = lastNode; n != 0; n = n->traversePreviousNode()) {
1760             if (n->isKeyboardFocusable()) {
1761                 if (n->tabIndex() == 0)
1762                     return n;
1763                 else if (n->tabIndex() > highestTabIndex)
1764                     highestTabIndex = n->tabIndex();
1765             }
1766         }
1767
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))
1771                 return n;
1772         }
1773
1774         return 0;
1775     }
1776     else {
1777         unsigned short fromTabIndex = fromNode->tabIndex();
1778
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();
1784             if (n)
1785                 return n;
1786
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();
1792             }
1793
1794             if (highestTabIndex == 0)
1795                 return 0;
1796
1797             for (n = lastNode; n != 0; n = n->traversePreviousNode()) {
1798                 if (n->isKeyboardFocusable() && (n->tabIndex() == highestTabIndex))
1799                     return n;
1800             }
1801
1802             assert(false); // should never get here
1803             return 0;
1804         }
1805         else {
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
1808             // fromNode.
1809             // If we don't find a suitable tab index, then there will be no previous focus node.
1810             unsigned short highestSuitableTabIndex = 0;
1811             NodeImpl *n;
1812
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) &&
1819                     (n != fromNode)) {
1820
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();
1824                 }
1825
1826                 if (n == fromNode)
1827                     reachedFromNode = true;
1828             }
1829
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.
1833                 return 0;
1834             }
1835
1836             // Search backwards from fromNode
1837             for (n = fromNode->traversePreviousNode(); n != 0; n = n->traversePreviousNode()) {
1838                 if (n->isKeyboardFocusable() && (n->tabIndex() == highestSuitableTabIndex))
1839                     return n;
1840             }
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))
1844                     return n;
1845             }
1846
1847             assert(false); // should never get here
1848             return 0;
1849         }
1850     }
1851 }
1852
1853 int DocumentImpl::nodeAbsIndex(NodeImpl *node)
1854 {
1855     assert(node->getDocument() == this);
1856
1857     int absIndex = 0;
1858     for (NodeImpl *n = node; n && n != this; n = n->traversePreviousNode())
1859         absIndex++;
1860     return absIndex;
1861 }
1862
1863 NodeImpl *DocumentImpl::nodeWithAbsIndex(int absIndex)
1864 {
1865     NodeImpl *n = this;
1866     for (int i = 0; n && (i < absIndex); i++) {
1867         n = n->traverseNextNode();
1868     }
1869     return n;
1870 }
1871
1872 void DocumentImpl::processHttpEquiv(const DOMString &equiv, const DOMString &content)
1873 {
1874     assert(!equiv.isNull() && !content.isNull());
1875
1876     KHTMLPart *part = getDocument()->part();
1877
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
1884         // -dwh
1885         part->d->m_sheetUsed = content.string();
1886         m_preferredStylesheetSet = content;
1887         updateStyleSelector();
1888     }
1889     else if(strcasecmp(equiv, "refresh") == 0 && part->metaRefreshEnabled())
1890     {
1891         // get delay and url
1892         QString str = content.string().stripWhiteSpace();
1893         int pos = str.find(QRegExp("[;,]"));
1894         if ( pos == -1 )
1895             pos = str.find(QRegExp("[ \t]"));
1896
1897         if (pos == -1) // There can be no url (David)
1898         {
1899             bool ok = false;
1900             int delay = 0;
1901             delay = str.toInt(&ok);
1902 #if APPLE_CHANGES
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);
1905 #else
1906             if(ok && part) part->scheduleRedirection(delay, part->url().url() );
1907 #endif
1908         } else {
1909             double delay = 0;
1910             bool ok = false;
1911             delay = str.left(pos).stripWhiteSpace().toDouble(&ok);
1912
1913             pos++;
1914             while(pos < (int)str.length() && str[pos].isSpace()) pos++;
1915             str = str.mid(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();
1920             if ( ok && part )
1921 #if APPLE_CHANGES
1922                 // We want a new history item if the refresh timeout > 1 second
1923                 part->scheduleRedirection(delay, getDocument()->completeURL( str ), delay <= 1);
1924 #else
1925                 part->scheduleRedirection(delay, getDocument()->completeURL( str ));
1926 #endif
1927         }
1928     }
1929     else if(strcasecmp(equiv, "expires") == 0)
1930     {
1931         QString str = content.string().stripWhiteSpace();
1932         time_t expire_date = str.toLong();
1933         if (m_docLoader)
1934             m_docLoader->setExpireDate(expire_date);
1935     }
1936     else if(strcasecmp(equiv, "pragma") == 0 || strcasecmp(equiv, "cache-control") == 0 && part)
1937     {
1938         QString str = content.string().lower().stripWhiteSpace();
1939         KURL url = part->url();
1940         if ((str == "no-cache") && url.protocol().startsWith("http"))
1941         {
1942            KIO::http_update_cache(url, true, 0);
1943         }
1944     }
1945     else if( (strcasecmp(equiv, "set-cookie") == 0))
1946     {
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);
1950     }
1951 }
1952
1953 bool DocumentImpl::prepareMouseEvent( bool readonly, int _x, int _y, MouseEvent *ev )
1954 {
1955     if ( m_render ) {
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();
1960
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);
1966
1967             if (!target.isNull() && !href.isNull()) {
1968                 ev->target = target;
1969                 ev->url = href;
1970             }
1971             else
1972                 ev->url = href;
1973 //            qDebug("url: *%s*", ev->url.string().latin1());
1974         }
1975
1976         if (!readonly)
1977             updateRendering();
1978
1979         return isInside;
1980     }
1981
1982
1983     return false;
1984 }
1985
1986 // DOM Section 1.1.1
1987 bool DocumentImpl::childAllowed( NodeImpl *newChild )
1988 {
1989     // Documents may contain a maximum of one Element child
1990     if (newChild->nodeType() == Node::ELEMENT_NODE) {
1991         NodeImpl *c;
1992         for (c = firstChild(); c; c = c->nextSibling()) {
1993             if (c->nodeType() == Node::ELEMENT_NODE)
1994                 return false;
1995         }
1996     }
1997
1998     // Documents may contain a maximum of one DocumentType child
1999     if (newChild->nodeType() == Node::DOCUMENT_TYPE_NODE) {
2000         NodeImpl *c;
2001         for (c = firstChild(); c; c = c->nextSibling()) {
2002             if (c->nodeType() == Node::DOCUMENT_TYPE_NODE)
2003                 return false;
2004         }
2005     }
2006
2007     return childTypeAllowed(newChild->nodeType());
2008 }
2009
2010 bool DocumentImpl::childTypeAllowed( unsigned short type )
2011 {
2012     switch (type) {
2013         case Node::ELEMENT_NODE:
2014         case Node::PROCESSING_INSTRUCTION_NODE:
2015         case Node::COMMENT_NODE:
2016         case Node::DOCUMENT_TYPE_NODE:
2017             return true;
2018         default:
2019             return false;
2020     }
2021 }
2022
2023 NodeImpl *DocumentImpl::cloneNode ( bool /*deep*/ )
2024 {
2025     // Spec says cloning Document nodes is "implementation dependent"
2026     // so we do not support it...
2027     return 0;
2028 }
2029
2030 NodeImpl::Id DocumentImpl::attrId(DOMStringImpl* _namespaceURI, DOMStringImpl *_name, bool readonly)
2031 {
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;
2037
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)) )
2044             return id;
2045         // compatibility: upper case - case insensitive
2046         if ( htmlMode() != XHtml && (id = getAttrID(n.string().lower().ascii(), _name->l )) )
2047             return id;
2048
2049         // ok, the fast path didn't work out, we need the full check
2050     }
2051
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);
2057         if (nsID != -1)
2058             ns = (Q_UINT16)nsID;
2059     }
2060     
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);
2069
2070     // unknown
2071     if (readonly) return 0;
2072
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];
2078         if (m_attrNames) {
2079             unsigned short i;
2080             for (i = 0; i < m_attrNameCount; i++)
2081                 newNames[i] = m_attrNames[i];
2082             delete [] m_attrNames;
2083         }
2084         m_attrNames = newNames;
2085     }
2086
2087     id = m_attrNameCount++;
2088     m_attrNames[id] = nme.implementation();
2089     m_attrNames[id]->ref();
2090
2091     return makeId(ns, ATTR_LAST_ATTR+id);
2092 }
2093
2094 DOMString DocumentImpl::attrName(NodeImpl::Id _id) const
2095 {
2096     DOMString result;
2097     if (localNamePart(_id) >= ATTR_LAST_ATTR)
2098         result = m_attrNames[localNamePart(_id)-ATTR_LAST_ATTR];
2099     else
2100         result = getAttrName(_id);
2101
2102     // Attribute names are always lowercase in the DOM for both
2103     // HTML and XHTML.
2104     if (getDocument()->isHTMLDocument() ||
2105         getDocument()->htmlMode() == DocumentImpl::XHtml)
2106         return result.lower();
2107
2108     return result;
2109 }
2110
2111 NodeImpl::Id DocumentImpl::tagId(DOMStringImpl* _namespaceURI, DOMStringImpl *_name, bool readonly)
2112 {
2113     if (!_name) return 0;
2114     // Each document maintains a mapping of tag name -> id for every tag name encountered
2115     // in the document.
2116     NodeImpl::Id id = 0;
2117
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)) )
2124             return id;
2125         // compatibility: upper case - case insensitive
2126         if ( htmlMode() != XHtml && (id = getTagID(n.string().lower().ascii(), _name->l )) )
2127             return id;
2128
2129         // ok, the fast path didn't work out, we need the full check
2130     }
2131
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);
2137         if (nsID != -1)
2138             ns = (Q_UINT16)nsID;
2139     }
2140
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);
2149
2150     // unknown
2151     if (readonly) return 0;
2152
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) {
2159             unsigned short i;
2160             for (i = 0; i < m_elementNameCount; i++)
2161                 newNames[i] = m_elementNames[i];
2162             delete [] m_elementNames;
2163         }
2164         m_elementNames = newNames;
2165     }
2166
2167     id = m_elementNameCount++;
2168     m_elementNames[id] = nme.implementation();
2169     m_elementNames[id]->ref();
2170
2171     return makeId(ns, ID_LAST_TAG + 1 + id);
2172 }
2173
2174 DOMString DocumentImpl::tagName(NodeImpl::Id _id) const
2175 {
2176     if (localNamePart(_id) > ID_LAST_TAG)
2177         return m_elementNames[localNamePart(_id) - (ID_LAST_TAG + 1)];
2178     else {
2179         // ### put them in a cache
2180         if (getDocument()->htmlMode() == DocumentImpl::XHtml)
2181             return getTagName(_id).lower();
2182         else
2183             return getTagName(_id);
2184     }
2185 }
2186
2187
2188 DOMStringImpl* DocumentImpl::namespaceURI(NodeImpl::Id _id) const
2189 {
2190     if (_id <= ID_LAST_TAG)
2191         return htmlMode() == XHtml ? XmlNamespaceTable::getNamespaceURI(xhtmlNamespace).implementation() : 0;
2192
2193     unsigned short ns = _id >> 16;
2194
2195     if (!ns) return 0;
2196
2197     return XmlNamespaceTable::getNamespaceURI(ns).implementation();
2198 }
2199
2200 StyleSheetListImpl* DocumentImpl::styleSheets()
2201 {
2202     return m_styleSheets;
2203 }
2204
2205 DOMString 
2206 DocumentImpl::preferredStylesheetSet()
2207 {
2208   return m_preferredStylesheetSet;
2209 }
2210
2211 DOMString 
2212 DocumentImpl::selectedStylesheetSet()
2213 {
2214   return view() ? view()->part()->d->m_sheetUsed : DOMString();
2215 }
2216
2217 void 
2218 DocumentImpl::setSelectedStylesheetSet(const DOMString& aString)
2219 {
2220   if (view()) {
2221     view()->part()->d->m_sheetUsed = aString.string();
2222     updateStyleSelector();
2223     if (renderer())
2224       renderer()->repaint();
2225   }
2226 }
2227
2228 // This method is called whenever a top-level stylesheet has finished loading.
2229 void DocumentImpl::stylesheetLoaded()
2230 {
2231   // Make sure we knew this sheet was pending, and that our count isn't out of sync.
2232   assert(m_pendingStylesheets > 0);
2233
2234   m_pendingStylesheets--;
2235   
2236 #ifdef INSTRUMENT_LAYOUT_SCHEDULING
2237   if (!ownerElement())
2238       printf("Stylesheet loaded at time %d. %d stylesheets still remain.\n", elapsedTime(), m_pendingStylesheets);
2239 #endif
2240
2241   updateStyleSelector();    
2242 }
2243
2244 void DocumentImpl::updateStyleSelector()
2245 {
2246     // Don't bother updating, since we haven't loaded all our style info yet.
2247     if (!haveStylesheetsLoaded())
2248         return;
2249
2250 #ifdef INSTRUMENT_LAYOUT_SCHEDULING
2251     if (!ownerElement())
2252         printf("Beginning update of style selector at time %d.\n", elapsedTime());
2253 #endif
2254
2255     recalcStyleSelector();
2256     recalcStyle(Force);
2257 #if 0
2258
2259     m_styleSelectorDirty = true;
2260 #endif
2261
2262 #ifdef INSTRUMENT_LAYOUT_SCHEDULING
2263     if (!ownerElement())
2264         printf("Finished update of style selector at time %d\n", elapsedTime());
2265 #endif
2266
2267     if (renderer()) {
2268         renderer()->setNeedsLayoutAndMinMaxRecalc();
2269         if (view())
2270             view()->scheduleRelayout();
2271     }
2272 }
2273
2274
2275 QStringList DocumentImpl::availableStyleSheets() const
2276 {
2277     return m_availableSheets;
2278 }
2279
2280 void DocumentImpl::recalcStyleSelector()
2281 {
2282     if ( !m_render || !attached() ) return;
2283
2284     QPtrList<StyleSheetImpl> oldStyleSheets = m_styleSheets->styleSheets;
2285     m_styleSheets->styleSheets.clear();
2286     m_availableSheets.clear();
2287     NodeImpl *n;
2288     for (n = this; n; n = n->traverseNextNode()) {
2289         StyleSheetImpl *sheet = 0;
2290
2291         if (n->nodeType() == Node::PROCESSING_INSTRUCTION_NODE)
2292         {
2293             // Processing instruction (XML documents only)
2294             ProcessingInstructionImpl* pi = static_cast<ProcessingInstructionImpl*>(n);
2295             sheet = pi->sheet();
2296 #ifdef KHTML_XSLT
2297             if (pi->isXSL()) {
2298                 applyXSLTransform(pi);
2299                 return;
2300             }
2301 #endif
2302             if (!sheet && !pi->localHref().isEmpty())
2303             {
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
2307                 // the document
2308                 ElementImpl* elem = getElementById(pi->localHref());
2309                 if (elem) {
2310                     DOMString sheetText("");
2311                     NodeImpl *c;
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();
2315                     }
2316
2317                     CSSStyleSheetImpl *cssSheet = new CSSStyleSheetImpl(this);
2318                     cssSheet->parseString(sheetText);
2319                     pi->setStyleSheet(cssSheet);
2320                     sheet = cssSheet;
2321                 }
2322             }
2323
2324         }
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) {
2330                 // <LINK> element
2331                 HTMLLinkElementImpl* l = static_cast<HTMLLinkElementImpl*>(n);
2332                 if (l->isLoading() || l->isDisabled())
2333                     continue;
2334                 if (!l->sheet())
2335                     title = QString::null;
2336                 enabledViaScript = l->isEnabledViaScript();
2337             }
2338
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();
2343             else
2344                 // <STYLE> element
2345                 sheet = static_cast<HTMLStyleElementImpl*>(n)->sheet();
2346
2347             // Check to see if this sheet belongs to a styleset
2348             // (thus making it PREFERRED or ALTERNATE rather than
2349             // PERSISTENT).
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
2356                     // this sheet.
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;
2360                 }
2361                       
2362                 if (!m_availableSheets.contains( title ) )
2363                     m_availableSheets.append( title );
2364                 
2365                 if (title != m_preferredStylesheetSet)
2366                     sheet = 0;
2367             }
2368         }
2369
2370         if (sheet) {
2371             sheet->ref();
2372             m_styleSheets->styleSheets.append(sheet);
2373         }
2374     
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)
2378             break;
2379     }
2380
2381     // De-reference all the stylesheets in the old list
2382     QPtrListIterator<StyleSheetImpl> it(oldStyleSheets);
2383     for (; it.current(); ++it)
2384         it.current()->deref();
2385
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;
2394 }
2395
2396 void DocumentImpl::setHoverNode(NodeImpl* newHoverNode)
2397 {
2398     if (m_hoverNode != newHoverNode) {
2399         if (m_hoverNode)
2400             m_hoverNode->deref();
2401         m_hoverNode = newHoverNode;
2402         if (m_hoverNode)
2403             m_hoverNode->ref();
2404     }    
2405 }
2406
2407 #if APPLE_CHANGES
2408
2409 bool DocumentImpl::relinquishesEditingFocus(NodeImpl *node)
2410 {
2411     assert(node);
2412     assert(node->isContentEditable());
2413
2414     NodeImpl *rootImpl = node->rootEditableElement();
2415     if (!part() || !rootImpl)
2416         return false;
2417
2418     Node root(rootImpl);
2419     Range range(root, 0, root, rootImpl->childNodeCount());
2420     return part()->shouldEndEditing(range);
2421 }
2422
2423 bool DocumentImpl::acceptsEditingFocus(NodeImpl *node)
2424 {
2425     assert(node);
2426     assert(node->isContentEditable());
2427
2428     NodeImpl *rootImpl = node->rootEditableElement();
2429     if (!part() || !rootImpl)
2430         return false;
2431
2432     Node root(rootImpl);
2433     Range range(root, 0, root, rootImpl->childNodeCount());
2434     return part()->shouldBeginEditing(range);
2435 }
2436
2437 const QValueList<DashboardRegionValue> & DocumentImpl::dashboardRegions() const
2438 {
2439     return m_dashboardRegions;
2440 }
2441
2442 void DocumentImpl::setDashboardRegions (const QValueList<DashboardRegionValue>& regions)
2443 {
2444     m_dashboardRegions = regions;
2445     setDashboardRegionsDirty (false);
2446 }
2447
2448 #endif
2449
2450 static QWidget *widgetForNode(NodeImpl *focusNode)
2451 {
2452     RenderObject *renderer = focusNode->renderer();
2453     if (!renderer || !renderer->isWidget())
2454         return 0;
2455     return static_cast<RenderWidget *>(renderer)->widget();
2456 }
2457
2458 bool DocumentImpl::setFocusNode(NodeImpl *newFocusNode)
2459 {    
2460     // Make sure newFocusNode is actually in this document
2461     if (newFocusNode && (newFocusNode->getDocument() != this))
2462         return true;
2463
2464     if (m_focusNode == newFocusNode)
2465         return true;
2466
2467 #if APPLE_CHANGES
2468     if (m_focusNode && m_focusNode->isContentEditable() && !relinquishesEditingFocus(m_focusNode))
2469         return false;
2470 #endif     
2471        
2472     bool focusChangeBlocked = false;
2473     NodeImpl *oldFocusNode = m_focusNode;
2474     m_focusNode = 0;
2475
2476     // Remove focus from the existing focus node (if any)
2477     if (oldFocusNode) {
2478         // This goes hand in hand with the Qt focus setting below.
2479         if (!newFocusNode && getDocument()->view()) {
2480             getDocument()->view()->setFocus();
2481         }
2482
2483         if (oldFocusNode->active())
2484             oldFocusNode->setActive(false);
2485
2486         oldFocusNode->setFocus(false);
2487         oldFocusNode->dispatchHTMLEvent(EventImpl::BLUR_EVENT, false, false);
2488         if (m_focusNode != 0) {
2489             // handler shifted focus
2490             focusChangeBlocked = true;
2491             newFocusNode = 0;
2492         }
2493         oldFocusNode->dispatchUIEvent(EventImpl::DOMFOCUSOUT_EVENT);
2494         if (m_focusNode != 0) {
2495             // handler shifted focus
2496             focusChangeBlocked = true;
2497             newFocusNode = 0;
2498         }
2499         if ((oldFocusNode == this) && oldFocusNode->hasOneRef()) {
2500             oldFocusNode->deref(); // deletes this
2501             return true;
2502         }
2503         else {
2504             oldFocusNode->deref();
2505         }
2506     }
2507
2508     // Clear the selection when changing the focus node to null or to a node that is not 
2509     // contained by the current selection.
2510     if (part()) {
2511         NodeImpl *startContainer = part()->selection().start().node();
2512         if (!newFocusNode || (startContainer && startContainer != newFocusNode && !startContainer->isAncestor(newFocusNode)))
2513             part()->clearSelection();
2514     }
2515
2516     if (newFocusNode) {
2517 #if APPLE_CHANGES            
2518         if (newFocusNode->isContentEditable() && !acceptsEditingFocus(newFocusNode)) {
2519             // delegate blocks focus change
2520             focusChangeBlocked = true;
2521             goto SetFocusNodeDone;
2522         }
2523 #endif
2524         // Set focus on the new node
2525         m_focusNode = newFocusNode;
2526         m_focusNode->ref();
2527         m_focusNode->dispatchHTMLEvent(EventImpl::FOCUS_EVENT, false, false);
2528         if (m_focusNode != newFocusNode) {
2529             // handler shifted focus
2530             focusChangeBlocked = true;
2531             goto SetFocusNodeDone;
2532         }
2533         m_focusNode->dispatchUIEvent(EventImpl::DOMFOCUSIN_EVENT);
2534         if (m_focusNode != newFocusNode) { 
2535             // handler shifted focus
2536             focusChangeBlocked = true;
2537             goto SetFocusNodeDone;
2538         }
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);
2544             if (focusWidget) {
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);
2551             }
2552             if (focusWidget)
2553                 focusWidget->setFocus();
2554             else
2555                 getDocument()->view()->setFocus();
2556         }
2557    }
2558
2559 #if APPLE_CHANGES
2560     if (!focusChangeBlocked && m_focusNode && KWQAccObjectCache::accessibilityEnabled())
2561         getAccObjectCache()->handleFocusedUIElementChanged();
2562 #endif
2563
2564 SetFocusNodeDone:
2565     updateRendering();
2566     return !focusChangeBlocked;
2567 }
2568
2569 void DocumentImpl::setCSSTarget(NodeImpl* n)
2570 {
2571     if (m_cssTarget)
2572         m_cssTarget->setChanged();
2573     m_cssTarget = n;
2574     if (n)
2575         n->setChanged();
2576 }
2577
2578 NodeImpl* DocumentImpl::getCSSTarget()
2579 {
2580     return m_cssTarget;
2581 }
2582
2583 void DocumentImpl::attachNodeIterator(NodeIteratorImpl *ni)
2584 {
2585     m_nodeIterators.append(ni);
2586 }
2587
2588 void DocumentImpl::detachNodeIterator(NodeIteratorImpl *ni)
2589 {
2590     m_nodeIterators.remove(ni);
2591 }
2592
2593 void DocumentImpl::notifyBeforeNodeRemoval(NodeImpl *n)
2594 {
2595     QPtrListIterator<NodeIteratorImpl> it(m_nodeIterators);
2596     for (; it.current(); ++it)
2597         it.current()->notifyBeforeNodeRemoval(n);
2598 }
2599
2600 AbstractViewImpl *DocumentImpl::defaultView() const
2601 {
2602     return m_defaultView;
2603 }
2604
2605 EventImpl *DocumentImpl::createEvent(const DOMString &eventType, int &exceptioncode)
2606 {
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();
2617     else {
2618         exceptioncode = DOMException::NOT_SUPPORTED_ERR;
2619         return 0;
2620     }
2621 }
2622
2623 CSSStyleDeclarationImpl *DocumentImpl::getOverrideStyle(ElementImpl */*elt*/, DOMStringImpl */*pseudoElt*/)
2624 {
2625     return 0; // ###
2626 }
2627
2628 void DocumentImpl::defaultEventHandler(EventImpl *evt)
2629 {
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);
2633     Event ev(evt);
2634     for (; it.current(); ++it) {
2635         if (it.current()->id == evt->id()) {
2636             it.current()->listener->handleEvent(ev, true);
2637         }
2638     }
2639
2640     // handle accesskey
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);
2646             if (elem) {
2647                 elem->accessKeyAction();
2648                 evt->setDefaultHandled();
2649             }
2650         }
2651     }
2652 }
2653
2654 void DocumentImpl::setHTMLWindowEventListener(int id, EventListener *listener)
2655 {
2656     // If we already have it we don't want removeWindowEventListener to delete it
2657     if (listener)
2658         listener->ref();
2659     removeHTMLWindowEventListener(id);
2660     if (listener) {
2661         addWindowEventListener(id, listener, false);
2662         listener->deref();
2663     }
2664 }
2665
2666 EventListener *DocumentImpl::getHTMLWindowEventListener(int id)
2667 {
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;
2673         }
2674     }
2675
2676     return 0;
2677 }
2678
2679 void DocumentImpl::removeHTMLWindowEventListener(int id)
2680 {
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());
2686             return;
2687         }
2688     }
2689 }
2690
2691 void DocumentImpl::addWindowEventListener(int id, EventListener *listener, const bool useCapture)
2692 {
2693     listener->ref();
2694
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);
2698
2699     RegisteredEventListener *rl = new RegisteredEventListener(static_cast<EventImpl::EventId>(id), listener, useCapture);
2700     m_windowEventListeners.append(rl);
2701
2702     listener->deref();
2703 }
2704
2705 void DocumentImpl::removeWindowEventListener(int id, EventListener *listener, bool useCapture)
2706 {
2707     RegisteredEventListener rl(static_cast<EventImpl::EventId>(id),listener,useCapture);
2708
2709     QPtrListIterator<RegisteredEventListener> it(m_windowEventListeners);
2710     for (; it.current(); ++it)
2711         if (*(it.current()) == rl) {
2712             m_windowEventListeners.removeRef(it.current());
2713             return;
2714         }
2715 }
2716
2717 bool DocumentImpl::hasWindowEventListener(int id)
2718 {
2719     QPtrListIterator<RegisteredEventListener> it(m_windowEventListeners);
2720     for (; it.current(); ++it) {
2721         if (it.current()->id == id) {
2722             return true;
2723         }
2724     }
2725
2726     return false;
2727 }
2728
2729 EventListener *DocumentImpl::createHTMLEventListener(QString code, NodeImpl *node)
2730 {
2731     if (part()) {
2732         return part()->createHTMLEventListener(code, node);
2733     } else {
2734         return NULL;
2735     }
2736 }
2737
2738 void DocumentImpl::dispatchImageLoadEventSoon(HTMLImageLoader *image)
2739 {
2740     m_imageLoadEventDispatchSoonList.append(image);
2741     if (!m_imageLoadEventTimer) {
2742         m_imageLoadEventTimer = startTimer(0);
2743     }
2744 }
2745
2746 void DocumentImpl::removeImage(HTMLImageLoader* image)
2747 {
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;
2755     }
2756 }
2757
2758 void DocumentImpl::dispatchImageLoadEventsNow()
2759 {
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()) {
2764         return;
2765     }
2766
2767     if (m_imageLoadEventTimer) {
2768         killTimer(m_imageLoadEventTimer);
2769         m_imageLoadEventTimer = 0;
2770     }
2771     
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.
2780         ++it;
2781         image->dispatchLoadEvent();
2782     }
2783     m_imageLoadEventDispatchingList.clear();
2784 }
2785
2786 void DocumentImpl::timerEvent(QTimerEvent *)
2787 {
2788     dispatchImageLoadEventsNow();
2789 }
2790
2791 ElementImpl *DocumentImpl::ownerElement()
2792 {
2793     KHTMLView *childView = view();
2794     if (!childView)
2795         return 0;
2796     KHTMLPart *childPart = childView->part();
2797     if (!childPart)
2798         return 0;
2799     KHTMLPart *parent = childPart->parentPart();
2800     if (!parent)
2801         return 0;
2802     ChildFrame *childFrame = parent->childFrame(childPart);
2803     if (!childFrame)
2804         return 0;
2805     RenderPart *renderPart = childFrame->m_frame;
2806     if (!renderPart)
2807         return 0;
2808     return static_cast<ElementImpl *>(renderPart->element());
2809 }
2810
2811 DOMString DocumentImpl::domain() const
2812 {
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
2815     return m_domain;
2816 }
2817
2818 void DocumentImpl::setDomain(const DOMString &newDomain, bool force /*=false*/)
2819 {
2820     if ( force ) {
2821         m_domain = newDomain;
2822         return;
2823     }
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
2826
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)
2832     {
2833         DOMString test = m_domain.copy();
2834         if ( test[oldLength - newLength - 1] == '.' ) // Check that it's a subdomain, not e.g. "de.org"
2835         {
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;
2839         }
2840     }
2841 }
2842
2843 bool DocumentImpl::isValidName(const DOMString &name)
2844 {
2845     static const char validFirstCharacter[] = "ABCDEFGHIJKLMNOPQRSTUVWXZYabcdefghijklmnopqrstuvwxyz";
2846     static const char validSubsequentCharacter[] = "ABCDEFGHIJKLMNOPQRSTUVWXZYabcdefghijklmnopqrstuvwxyz0123456789-_:.";
2847     const unsigned length = name.length();
2848     if (length == 0)
2849         return false;
2850     const QChar * const characters = name.unicode();
2851     const char fc = characters[0];
2852     if (!fc)
2853         return false;
2854     if (strchr(validFirstCharacter, fc) == 0)
2855         return false;
2856     for (unsigned i = 1; i < length; ++i) {
2857         const char sc = characters[i];
2858         if (!sc)
2859             return false;
2860         if (strchr(validSubsequentCharacter, sc) == 0)
2861             return false;
2862     }
2863     return true;
2864 }
2865
2866 void DocumentImpl::addImageMap(HTMLMapElementImpl *imageMap)
2867 {
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);
2873 }
2874
2875 void DocumentImpl::removeImageMap(HTMLMapElementImpl *imageMap)
2876 {
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);
2883 }
2884
2885 HTMLMapElementImpl *DocumentImpl::getImageMap(const DOMString &URL) const
2886 {
2887     if (URL.isNull()) {
2888         return 0;
2889     }
2890
2891     QString s = URL.string();
2892     int hashPos = s.find('#');
2893     if (hashPos >= 0)
2894         s = s.mid(hashPos + 1);
2895
2896     QMapConstIterator<QString, HTMLMapElementImpl *> it = m_imageMapsByName.find(s);
2897     if (it == m_imageMapsByName.end())
2898         return 0;
2899     return *it;
2900 }
2901
2902 #if APPLE_CHANGES
2903
2904 void DocumentImpl::setDecoder(Decoder *decoder)
2905 {
2906     decoder->ref();
2907     if (m_decoder) {
2908         m_decoder->deref();
2909     }
2910     m_decoder = decoder;
2911 }
2912
2913 QString DocumentImpl::completeURL(const QString &URL)
2914 {
2915     return KURL(baseURL(), URL, m_decoder ? m_decoder->codec() : 0).url();
2916 }
2917
2918 bool DocumentImpl::inPageCache()
2919 {
2920     return m_inPageCache;
2921 }
2922
2923 void DocumentImpl::setInPageCache(bool flag)
2924 {
2925     if (m_inPageCache == flag)
2926         return;
2927
2928     m_inPageCache = flag;
2929     if (flag) {
2930         assert(m_savedRenderer == 0);
2931         m_savedRenderer = m_render;
2932         if (m_view) {
2933             m_view->resetScrollBars();
2934         }
2935     } else {
2936         assert(m_render == 0 || m_render == m_savedRenderer);
2937         m_render = m_savedRenderer;
2938         m_savedRenderer = 0;
2939     }
2940 }
2941
2942 void DocumentImpl::passwordFieldAdded()
2943 {
2944     m_passwordFields++;
2945 }
2946
2947 void DocumentImpl::passwordFieldRemoved()
2948 {
2949     assert(m_passwordFields > 0);
2950     m_passwordFields--;
2951 }
2952
2953 bool DocumentImpl::hasPasswordField() const
2954 {
2955     return m_passwordFields > 0;
2956 }
2957
2958 void DocumentImpl::secureFormAdded()
2959 {
2960     m_secureForms++;
2961 }
2962
2963 void DocumentImpl::secureFormRemoved()
2964 {
2965     assert(m_secureForms > 0);
2966     m_secureForms--;
2967 }
2968
2969 bool DocumentImpl::hasSecureForm() const
2970 {
2971     return m_secureForms > 0;
2972 }
2973
2974 void DocumentImpl::setShouldCreateRenderers(bool f)
2975 {
2976     m_createRenderers = f;
2977 }
2978
2979 bool DocumentImpl::shouldCreateRenderers()
2980 {
2981     return m_createRenderers;
2982 }
2983
2984 DOMString DocumentImpl::toString() const
2985 {
2986     DOMString result;
2987
2988     for (NodeImpl *child = firstChild(); child != NULL; child = child->nextSibling()) {
2989         result += child->toString();
2990     }
2991
2992     return result;
2993 }
2994
2995 #endif // APPLE_CHANGES
2996
2997 // ----------------------------------------------------------------------------
2998 // Support for Javascript execCommand, and related methods
2999
3000 JSEditor *DocumentImpl::jsEditor()
3001 {
3002     if (!m_jsEditor)
3003         m_jsEditor = new JSEditor(this);
3004     return m_jsEditor;
3005 }
3006
3007 bool DocumentImpl::execCommand(const DOMString &command, bool userInterface, const DOMString &value)
3008 {
3009     return jsEditor()->execCommand(command, userInterface, value);
3010 }
3011
3012 bool DocumentImpl::queryCommandEnabled(const DOMString &command)
3013 {
3014     return jsEditor()->queryCommandEnabled(command);
3015 }
3016
3017 bool DocumentImpl::queryCommandIndeterm(const DOMString &command)
3018 {
3019     return jsEditor()->queryCommandIndeterm(command);
3020 }
3021
3022 bool DocumentImpl::queryCommandState(const DOMString &command)
3023 {
3024     return jsEditor()->queryCommandState(command);
3025 }
3026
3027 bool DocumentImpl::queryCommandSupported(const DOMString &command)
3028 {
3029     return jsEditor()->queryCommandSupported(command);
3030 }
3031
3032 DOMString DocumentImpl::queryCommandValue(const DOMString &command)
3033 {
3034     return jsEditor()->queryCommandValue(command);
3035 }
3036
3037 // ----------------------------------------------------------------------------
3038
3039 void DocumentImpl::addMarker(Range range, DocumentMarker::MarkerType type)
3040 {
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);
3046     }
3047 }
3048
3049 void DocumentImpl::removeMarker(Range range, DocumentMarker::MarkerType type)
3050 {
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);
3056     }
3057 }
3058
3059 // FIXME:  We don't deal with markers of more than one type yet
3060
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.
3063
3064 void DocumentImpl::addMarker(NodeImpl *node, DocumentMarker newMarker) 
3065 {
3066     assert(newMarker.endOffset >= newMarker.startOffset);
3067     if (newMarker.endOffset == newMarker.startOffset) {
3068         return;     // zero length markers are a NOP
3069     }
3070     
3071     QValueList <DocumentMarker> *markers = m_markers.find(node);
3072     if (!markers) {
3073         markers = new QValueList <DocumentMarker>();
3074         markers->append(newMarker);
3075         m_markers.insert(node, markers);
3076     } else {
3077         QValueListIterator<DocumentMarker> it;
3078         for (it = markers->begin(); it != markers->end(); ) {
3079             DocumentMarker marker = *it;
3080             
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
3084                 break;
3085             } else if (newMarker.startOffset > marker.endOffset) {
3086                 // maker is before newMarker, and disjoint from it.  Keep scanning.
3087                 it++;
3088             } else if (newMarker == marker) {
3089                 // already have this one, NOP
3090                 return;
3091             } else {
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
3098             }
3099         }
3100         // at this point it points to the node before which we want to insert
3101         markers->insert(it, newMarker);
3102     }
3103     
3104     // repaint the affected node
3105     if (node->renderer())
3106         node->renderer()->repaint();
3107 }
3108
3109 void DocumentImpl::removeMarker(NodeImpl *node, DocumentMarker target)
3110 {
3111     assert(target.endOffset >= target.startOffset);
3112     if (target.endOffset == target.startOffset) {
3113         return;     // zero length markers are a NOP
3114     }
3115
3116     QValueList <DocumentMarker> *markers = m_markers.find(node);
3117     if (!markers) {
3118         return;
3119     }
3120     
3121     bool docDirty = false;
3122     QValueListIterator<DocumentMarker> it;
3123     for (it = markers->begin(); it != markers->end(); ) {
3124         DocumentMarker marker = *it;
3125
3126         if (target.endOffset <= marker.startOffset) {
3127             // This is the first marker that is completely after target.  All done.
3128             break;
3129         } else if (target.startOffset >= marker.endOffset) {
3130             // marker is before target.  Keep scanning.
3131             it++;
3132         } else {
3133             // at this point we know that marker and target intersect in some way
3134             docDirty = true;
3135
3136             // pitch the old marker
3137             it = markers->remove(it);
3138             // it now points to the next node
3139             
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);
3145             }
3146             if (marker.endOffset > target.endOffset) {
3147                 DocumentMarker newRight = marker;
3148                 newRight.startOffset = target.endOffset;
3149                 markers->insert(it, newRight);
3150             }
3151         }
3152     }
3153
3154     // repaint the affected node
3155     if (docDirty && node->renderer())
3156         node->renderer()->repaint();
3157 }
3158
3159 QValueList<DocumentMarker> DocumentImpl::markersForNode(NodeImpl *node)
3160 {
3161     QValueList <DocumentMarker> *markers = m_markers.find(node);
3162     if (markers) {
3163         return *markers;
3164     } else {
3165         return QValueList <DocumentMarker> ();
3166     }
3167 }
3168
3169 void DocumentImpl::removeAllMarkers(NodeImpl *node, ulong startOffset, long length)
3170 {
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);
3174 }
3175
3176 void DocumentImpl::removeAllMarkers(NodeImpl *node)
3177 {
3178     QValueList <DocumentMarker> *markers = m_markers.find(node);
3179     if (markers)
3180         markers->clear();
3181 }
3182
3183 void DocumentImpl::removeAllMarkers()
3184 {
3185     m_markers.clear();
3186 }
3187
3188 void DocumentImpl::shiftMarkers(NodeImpl *node, ulong startOffset, long delta)
3189 {
3190     if (m_markers.isEmpty())
3191         return;
3192
3193     QValueList <DocumentMarker> *markers = m_markers.find(node);
3194     if (!markers)
3195         return;
3196
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;
3205             docDirty = true;
3206         }
3207     }
3208     
3209     // repaint the affected node
3210     if (docDirty && node->renderer())
3211         node->renderer()->repaint();
3212 }
3213
3214 #ifdef KHTML_XSLT
3215 void DocumentImpl::applyXSLTransform(ProcessingInstructionImpl* pi)
3216 {
3217     // Ref ourselves to keep from being destroyed.
3218     XSLTProcessorImpl processor(static_cast<XSLStyleSheetImpl*>(pi->sheet()), this);
3219     processor.transformDocument(this);
3220
3221     // FIXME: If the transform failed we should probably report an error (like Mozilla does) in this
3222     // case.
3223 }
3224
3225 void DocumentImpl::setTransformSourceDocument(DocumentImpl* doc)
3226
3227     if (m_transformSourceDocument)
3228         m_transformSourceDocument->deref(); 
3229     m_transformSourceDocument = doc;
3230     if (doc)
3231         doc->ref();
3232 }
3233
3234 #endif
3235
3236 void DocumentImpl::setDesignMode(InheritedBool value)
3237 {
3238     m_designMode = value;
3239 }
3240
3241 DocumentImpl::InheritedBool DocumentImpl::getDesignMode() const
3242 {
3243     return m_designMode;
3244 }
3245
3246 bool DocumentImpl::inDesignMode() const
3247 {
3248     for (const DocumentImpl* d = this; d; d = d->parentDocument()) {
3249         if (d->m_designMode != inherit)
3250             return d->m_designMode;      
3251     }
3252     return false;
3253 }
3254
3255 DocumentImpl *DocumentImpl::parentDocument() const
3256 {
3257     KHTMLPart *childPart = part();
3258     if (!childPart)
3259         return 0;
3260     KHTMLPart *parent = childPart->parentPart();
3261     if (!parent)
3262         return 0;
3263     return parent->xmlDocImpl();
3264 }
3265
3266 DocumentImpl *DocumentImpl::topDocument() const
3267 {
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;
3273     }
3274     
3275     return doc;
3276 }
3277
3278 // ----------------------------------------------------------------------------
3279
3280 DocumentFragmentImpl::DocumentFragmentImpl(DocumentPtr *doc) : NodeBaseImpl(doc)
3281 {
3282 }
3283
3284 DOMString DocumentFragmentImpl::nodeName() const
3285 {
3286   return "#document-fragment";
3287 }
3288
3289 unsigned short DocumentFragmentImpl::nodeType() const
3290 {
3291     return Node::DOCUMENT_FRAGMENT_NODE;
3292 }
3293
3294 // DOM Section 1.1.1
3295 bool DocumentFragmentImpl::childTypeAllowed( unsigned short type )
3296 {
3297     switch (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:
3304             return true;
3305         default:
3306             return false;
3307     }
3308 }
3309
3310 DOMString DocumentFragmentImpl::toString() const
3311 {
3312     DOMString result;
3313
3314     for (NodeImpl *child = firstChild(); child != NULL; child = child->nextSibling()) {
3315         result += child->toString();
3316     }
3317
3318     return result;
3319 }
3320
3321
3322 NodeImpl *DocumentFragmentImpl::cloneNode ( bool deep )
3323 {
3324     DocumentFragmentImpl *clone = new DocumentFragmentImpl( docPtr() );
3325     if (deep)
3326         cloneChildNodes(clone);
3327     return clone;
3328 }
3329
3330
3331 // ----------------------------------------------------------------------------
3332
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)
3338 {
3339     if (m_implementation)
3340         m_implementation->ref();
3341
3342     m_entities = 0;
3343     m_notations = 0;
3344
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)
3347 }
3348
3349 DocumentTypeImpl::~DocumentTypeImpl()
3350 {
3351     if (m_implementation)
3352         m_implementation->deref();
3353     if (m_entities)
3354         m_entities->deref();
3355     if (m_notations)
3356         m_notations->deref();
3357 }
3358
3359 void DocumentTypeImpl::copyFrom(const DocumentTypeImpl& other)
3360 {
3361     m_qualifiedName = other.m_qualifiedName;
3362     m_publicId = other.m_publicId;
3363     m_systemId = other.m_systemId;
3364     m_subset = other.m_subset;
3365 }
3366
3367 DOMString DocumentTypeImpl::toString() const
3368 {
3369     DOMString result;
3370     if (m_qualifiedName.isEmpty()) {
3371         return "";
3372     } else {
3373         result = "<!DOCTYPE ";
3374         result += m_qualifiedName;
3375     }
3376     if (!m_publicId.isEmpty()) {
3377         result += " PUBLIC \"";
3378         result += m_publicId;
3379         result += "\" \"";
3380         result += m_systemId;
3381         result += "\"";
3382     } else if (!m_systemId.isEmpty()) {
3383         result += " SYSTEM \"";
3384         result += m_systemId;
3385         result += "\"";
3386     }
3387     if (!m_subset.isEmpty()) {
3388         result += " [";
3389         result += m_subset;
3390         result += "]";
3391     }
3392     result += ">";
3393     return result;
3394 }
3395
3396 DOMString DocumentTypeImpl::nodeName() const
3397 {
3398     return name();
3399 }
3400
3401 unsigned short DocumentTypeImpl::nodeType() const
3402 {
3403     return Node::DOCUMENT_TYPE_NODE;
3404 }
3405
3406 // DOM Section 1.1.1
3407 bool DocumentTypeImpl::childTypeAllowed( unsigned short /*type*/ )
3408 {
3409     return false;
3410 }
3411
3412 NodeImpl *DocumentTypeImpl::cloneNode ( bool /*deep*/ )
3413 {
3414     // Spec says cloning Document nodes is "implementation dependent"
3415     // so we do not support it...
3416     return 0;
3417 }
3418
3419 #include "dom_docimpl.moc"