Reviewed by Darin.
[WebKit-https.git] / WebCore / dom / XMLTokenizer.cpp
1 /**
2  * This file is part of the DOM implementation for KDE.
3  *
4  * Copyright (C) 2000 Peter Kelly (pmk@post.com)
5  * Copyright (C) 2005, 2006 Apple Computer, Inc.
6  * Copyright (C) 2006 Alexey Proskuryakov (ap@webkit.org)
7  * Copyright (C) 2007 Samuel Weinig (sam@webkit.org)
8  * Copyright (C) 2007 Trolltech ASA
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Library General Public
12  * License as published by the Free Software Foundation; either
13  * version 2 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Library General Public License for more details.
19  *
20  * You should have received a copy of the GNU Library General Public License
21  * along with this library; see the file COPYING.LIB.  If not, write to
22  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
23  * Boston, MA 02110-1301, USA.
24  */
25
26 #include "config.h"
27 #include "XMLTokenizer.h"
28
29 #include "CDATASection.h"
30 #include "CString.h"
31 #include "Cache.h"
32 #include "CachedScript.h"
33 #include "Comment.h"
34 #include "DocLoader.h"
35 #include "Document.h"
36 #include "DocumentFragment.h"
37 #include "DocumentType.h"
38 #include "EventNames.h"
39 #include "Frame.h"
40 #include "FrameLoader.h"
41 #include "FrameView.h"
42 #include "HTMLNames.h"
43 #include "HTMLScriptElement.h"
44 #include "HTMLStyleElement.h"
45 #include "HTMLTokenizer.h"
46 #include "ProcessingInstruction.h"
47 #include "ResourceHandle.h"
48 #include "ResourceRequest.h"
49 #include "ResourceResponse.h"
50 #ifndef USE_QXMLSTREAM
51 #include <libxml/parser.h>
52 #include <libxml/parserInternals.h>
53 #else
54 #include <QDebug>
55 #endif
56 #include <wtf/Platform.h>
57 #include <wtf/StringExtras.h>
58 #include <wtf/Vector.h>
59
60 #if ENABLE(XSLT)
61 #include <libxslt/xslt.h>
62 #endif
63
64 #if ENABLE(SVG)
65 #include "SVGNames.h"
66 #include "SVGStyleElement.h"
67 #include "XLinkNames.h"
68 #endif
69
70 using namespace std;
71
72 namespace WebCore {
73
74 using namespace EventNames;
75 using namespace HTMLNames;
76
77 const int maxErrors = 25;
78
79 #ifndef USE_QXMLSTREAM
80 class PendingCallbacks {
81 public:
82     PendingCallbacks()
83     {
84         m_callbacks.setAutoDelete(true);
85     }
86     
87     void appendStartElementNSCallback(const xmlChar* xmlLocalName, const xmlChar* xmlPrefix, const xmlChar* xmlURI, int nb_namespaces,
88                                       const xmlChar** namespaces, int nb_attributes, int nb_defaulted, const xmlChar** attributes)
89     {
90         PendingStartElementNSCallback* callback = new PendingStartElementNSCallback;
91         
92         callback->xmlLocalName = xmlStrdup(xmlLocalName);
93         callback->xmlPrefix = xmlStrdup(xmlPrefix);
94         callback->xmlURI = xmlStrdup(xmlURI);
95         callback->nb_namespaces = nb_namespaces;
96         callback->namespaces = reinterpret_cast<xmlChar**>(xmlMalloc(sizeof(xmlChar*) * nb_namespaces * 2));
97         for (int i = 0; i < nb_namespaces * 2 ; i++)
98             callback->namespaces[i] = xmlStrdup(namespaces[i]);
99         callback->nb_attributes = nb_attributes;
100         callback->nb_defaulted = nb_defaulted;
101         callback->attributes =  reinterpret_cast<xmlChar**>(xmlMalloc(sizeof(xmlChar*) * nb_attributes * 5));
102         for (int i = 0; i < nb_attributes; i++) {
103             // Each attribute has 5 elements in the array:
104             // name, prefix, uri, value and an end pointer.
105             
106             for (int j = 0; j < 3; j++)
107                 callback->attributes[i * 5 + j] = xmlStrdup(attributes[i * 5 + j]);
108             
109             int len = attributes[i * 5 + 4] - attributes[i * 5 + 3];
110
111             callback->attributes[i * 5 + 3] = xmlStrndup(attributes[i * 5 + 3], len);
112             callback->attributes[i * 5 + 4] = callback->attributes[i * 5 + 3] + len;
113         }
114         
115         m_callbacks.append(callback);
116     }
117
118     void appendEndElementNSCallback()
119     {
120         PendingEndElementNSCallback* callback = new PendingEndElementNSCallback;
121         
122         m_callbacks.append(callback);
123     }
124     
125     void appendCharactersCallback(const xmlChar* s, int len)
126     {
127         PendingCharactersCallback* callback = new PendingCharactersCallback;
128         
129         callback->s = xmlStrndup(s, len);
130         callback->len = len;
131         
132         m_callbacks.append(callback);        
133     }
134     
135     void appendProcessingInstructionCallback(const xmlChar* target, const xmlChar* data)
136     {
137         PendingProcessingInstructionCallback* callback = new PendingProcessingInstructionCallback;
138         
139         callback->target = xmlStrdup(target);
140         callback->data = xmlStrdup(data);
141         
142         m_callbacks.append(callback);
143     }
144     
145     void appendCDATABlockCallback(const xmlChar* s, int len)
146     {
147         PendingCDATABlockCallback* callback = new PendingCDATABlockCallback;
148         
149         callback->s = xmlStrndup(s, len);
150         callback->len = len;
151         
152         m_callbacks.append(callback);        
153     }
154
155     void appendCommentCallback(const xmlChar* s)
156     {
157         PendingCommentCallback* callback = new PendingCommentCallback;
158         
159         callback->s = xmlStrdup(s);
160         
161         m_callbacks.append(callback);        
162     }
163
164     void appendInternalSubsetCallback(const xmlChar* name, const xmlChar* externalID, const xmlChar* systemID)
165     {
166         PendingInternalSubsetCallback* callback = new PendingInternalSubsetCallback;
167         
168         callback->name = xmlStrdup(name);
169         callback->externalID = xmlStrdup(externalID);
170         callback->systemID = xmlStrdup(systemID);
171         
172         m_callbacks.append(callback);        
173     }
174     
175     void appendErrorCallback(XMLTokenizer::ErrorType type, const char* message, int lineNumber, int columnNumber)
176     {
177         PendingErrorCallback* callback = new PendingErrorCallback;
178         
179         callback->message = strdup(message);
180         callback->type = type;
181         callback->lineNumber = lineNumber;
182         callback->columnNumber = columnNumber;
183         
184         m_callbacks.append(callback);
185     }
186
187     void callAndRemoveFirstCallback(XMLTokenizer* tokenizer)
188     {
189         PendingCallback* cb = m_callbacks.getFirst();
190             
191         cb->call(tokenizer);
192         m_callbacks.removeFirst();
193     }
194     
195     bool isEmpty() const { return m_callbacks.isEmpty(); }
196     
197 private:    
198     struct PendingCallback {        
199         
200         virtual ~PendingCallback() { } 
201
202         virtual void call(XMLTokenizer* tokenizer) = 0;
203     };  
204     
205     struct PendingStartElementNSCallback : public PendingCallback {        
206         virtual ~PendingStartElementNSCallback() {
207             xmlFree(xmlLocalName);
208             xmlFree(xmlPrefix);
209             xmlFree(xmlURI);
210             for (int i = 0; i < nb_namespaces * 2; i++)
211                 xmlFree(namespaces[i]);
212             xmlFree(namespaces);
213             for (int i = 0; i < nb_attributes; i++)
214                 for (int j = 0; j < 4; j++) 
215                     xmlFree(attributes[i * 5 + j]);
216             xmlFree(attributes);
217         }
218         
219         virtual void call(XMLTokenizer* tokenizer) {
220             tokenizer->startElementNs(xmlLocalName, xmlPrefix, xmlURI, 
221                                       nb_namespaces, (const xmlChar**)namespaces,
222                                       nb_attributes, nb_defaulted, (const xmlChar**)(attributes));
223         }
224
225         xmlChar* xmlLocalName;
226         xmlChar* xmlPrefix;
227         xmlChar* xmlURI;
228         int nb_namespaces;
229         xmlChar** namespaces;
230         int nb_attributes;
231         int nb_defaulted;
232         xmlChar** attributes;
233     };
234     
235     struct PendingEndElementNSCallback : public PendingCallback {
236         virtual void call(XMLTokenizer* tokenizer) 
237         {
238             tokenizer->endElementNs();
239         }
240     };
241     
242     struct PendingCharactersCallback : public PendingCallback {
243         virtual ~PendingCharactersCallback() 
244         {
245             xmlFree(s);
246         }
247     
248         virtual void call(XMLTokenizer* tokenizer) 
249         {
250             tokenizer->characters(s, len);
251         }
252         
253         xmlChar* s;
254         int len;
255     };
256
257     struct PendingProcessingInstructionCallback : public PendingCallback {
258         virtual ~PendingProcessingInstructionCallback() 
259         {
260             xmlFree(target);
261             xmlFree(data);
262         }
263         
264         virtual void call(XMLTokenizer* tokenizer) 
265         {
266             tokenizer->processingInstruction(target, data);
267         }
268         
269         xmlChar* target;
270         xmlChar* data;
271     };
272     
273     struct PendingCDATABlockCallback : public PendingCallback {
274         virtual ~PendingCDATABlockCallback() 
275         {
276             xmlFree(s);
277         }
278         
279         virtual void call(XMLTokenizer* tokenizer) 
280         {
281             tokenizer->cdataBlock(s, len);
282         }
283         
284         xmlChar* s;
285         int len;
286     };
287
288     struct PendingCommentCallback : public PendingCallback {
289         virtual ~PendingCommentCallback() 
290         {
291             xmlFree(s);
292         }
293         
294         virtual void call(XMLTokenizer* tokenizer) 
295         {
296             tokenizer->comment(s);
297         }
298
299         xmlChar* s;
300     };
301     
302     struct PendingInternalSubsetCallback : public PendingCallback {
303         virtual ~PendingInternalSubsetCallback() 
304         {
305             xmlFree(name);
306             xmlFree(externalID);
307             xmlFree(systemID);
308         }
309         
310         virtual void call(XMLTokenizer* tokenizer)
311         {
312             tokenizer->internalSubset(name, externalID, systemID);
313         }
314         
315         xmlChar* name;
316         xmlChar* externalID;
317         xmlChar* systemID;        
318     };
319     
320     struct PendingErrorCallback: public PendingCallback {
321         virtual ~PendingErrorCallback() 
322         {
323             free (message);
324         }
325         
326         virtual void call(XMLTokenizer* tokenizer) 
327         {
328             tokenizer->handleError(type, message, lineNumber, columnNumber);
329         }
330         
331         XMLTokenizer::ErrorType type;
332         char* message;
333         int lineNumber;
334         int columnNumber;
335     };
336     
337 public:
338     DeprecatedPtrList<PendingCallback> m_callbacks;
339 };
340 // --------------------------------
341
342 static int globalDescriptor = 0;
343
344 static int matchFunc(const char* uri)
345 {
346     return 1; // Match everything.
347 }
348
349 static DocLoader* globalDocLoader = 0;
350
351 class OffsetBuffer {
352 public:
353     OffsetBuffer(const Vector<char>& b) : m_buffer(b), m_currentOffset(0) { }
354     
355     int readOutBytes(char* outputBuffer, unsigned askedToRead) {
356         unsigned bytesLeft = m_buffer.size() - m_currentOffset;
357         unsigned lenToCopy = min(askedToRead, bytesLeft);
358         if (lenToCopy) {
359             memcpy(outputBuffer, m_buffer.data() + m_currentOffset, lenToCopy);
360             m_currentOffset += lenToCopy;
361         }
362         return lenToCopy;
363     }
364
365 private:
366     Vector<char> m_buffer;
367     unsigned m_currentOffset;
368 };
369
370 static bool shouldAllowExternalLoad(const char* inURI)
371 {
372     if (strstr(inURI, "/etc/xml/catalog")
373             || strstr(inURI, "http://www.w3.org/Graphics/SVG") == inURI
374             || strstr(inURI, "http://www.w3.org/TR/xhtml") == inURI)
375         return false;
376     return true;
377 }
378 static void* openFunc(const char* uri)
379 {
380     if (!globalDocLoader || !shouldAllowExternalLoad(uri))
381         return &globalDescriptor;
382
383     ResourceError error;
384     ResourceResponse response;
385     Vector<char> data;
386     
387     if (globalDocLoader->frame()) 
388         globalDocLoader->frame()->loader()->loadResourceSynchronously(KURL(uri), error, response, data);
389
390     return new OffsetBuffer(data);
391 }
392
393 static int readFunc(void* context, char* buffer, int len)
394 {
395     // Do 0-byte reads in case of a null descriptor
396     if (context == &globalDescriptor)
397         return 0;
398         
399     OffsetBuffer* data = static_cast<OffsetBuffer*>(context);
400     return data->readOutBytes(buffer, len);
401 }
402
403 static int writeFunc(void* context, const char* buffer, int len)
404 {
405     // Always just do 0-byte writes
406     return 0;
407 }
408
409 static int closeFunc(void* context)
410 {
411     if (context != &globalDescriptor) {
412         OffsetBuffer* data = static_cast<OffsetBuffer*>(context);
413         delete data;
414     }
415     return 0;
416 }
417
418 static void errorFunc(void*, const char*, ...)
419 {
420     // FIXME: It would be nice to display error messages somewhere.
421 }
422
423 void setLoaderForLibXMLCallbacks(DocLoader* docLoader)
424 {
425     globalDocLoader = docLoader;
426 }
427
428 static xmlParserCtxtPtr createStringParser(xmlSAXHandlerPtr handlers, void* userData)
429 {
430     static bool didInit = false;
431     if (!didInit) {
432         xmlInitParser();
433         xmlRegisterInputCallbacks(matchFunc, openFunc, readFunc, closeFunc);
434         xmlRegisterOutputCallbacks(matchFunc, openFunc, writeFunc, closeFunc);
435         didInit = true;
436     }
437
438     xmlParserCtxtPtr parser = xmlCreatePushParserCtxt(handlers, 0, 0, 0, 0);
439     parser->_private = userData;
440     parser->replaceEntities = true;
441     const UChar BOM = 0xFEFF;
442     const unsigned char BOMHighByte = *reinterpret_cast<const unsigned char*>(&BOM);
443     xmlSwitchEncoding(parser, BOMHighByte == 0xFF ? XML_CHAR_ENCODING_UTF16LE : XML_CHAR_ENCODING_UTF16BE);
444     return parser;
445 }
446 #endif
447
448 #if defined(USE_QXMLSTREAM) && QT_VERSION >= 0x040400
449 class EntityResolver : public QXmlStreamEntityResolver
450 {
451     virtual QString resolveUndeclaredEntity(const QString &name);
452 };
453
454 QString EntityResolver::resolveUndeclaredEntity(const QString &name)
455 {
456     UChar c = decodeNamedEntity(name.toUtf8().constData());
457     return QString(c);
458 }
459 #endif
460
461 // --------------------------------
462
463 XMLTokenizer::XMLTokenizer(Document* _doc, FrameView* _view)
464     : m_doc(_doc)
465     , m_view(_view)
466 #ifdef USE_QXMLSTREAM
467     , m_wroteText(false)
468 #else
469     , m_context(0)
470 #endif
471     , m_currentNode(_doc)
472     , m_currentNodeIsReferenced(false)
473     , m_sawError(false)
474     , m_sawXSLTransform(false)
475     , m_sawFirstElement(false)
476     , m_isXHTMLDocument(false)
477     , m_parserPaused(false)
478     , m_requestingScript(false)
479     , m_finishCalled(false)
480     , m_errorCount(0)
481     , m_lastErrorLine(0)
482     , m_lastErrorColumn(0)
483     , m_pendingScript(0)
484     , m_scriptStartLine(0)
485     , m_parsingFragment(false)
486 #ifndef USE_QXMLSTREAM
487     , m_pendingCallbacks(new PendingCallbacks)
488 #endif
489 {
490 #if defined(USE_QXMLSTREAM) && QT_VERSION >= 0x040400
491     m_stream.setEntityResolver(new EntityResolver);
492 #endif
493 }
494
495 XMLTokenizer::XMLTokenizer(DocumentFragment* fragment, Element* parentElement)
496     : m_doc(fragment->document())
497     , m_view(0)
498 #ifdef USE_QXMLSTREAM
499     , m_wroteText(false)
500 #else
501     , m_context(0)
502 #endif
503     , m_currentNode(fragment)
504     , m_currentNodeIsReferenced(fragment)
505     , m_sawError(false)
506     , m_sawXSLTransform(false)
507     , m_sawFirstElement(false)
508     , m_isXHTMLDocument(false)
509     , m_parserPaused(false)
510     , m_requestingScript(false)
511     , m_finishCalled(false)
512     , m_errorCount(0)
513     , m_lastErrorLine(0)
514     , m_lastErrorColumn(0)
515     , m_pendingScript(0)
516     , m_scriptStartLine(0)
517     , m_parsingFragment(true)
518 #ifndef USE_QXMLSTREAM
519     , m_pendingCallbacks(new PendingCallbacks)
520 #endif
521 {
522     if (fragment)
523         fragment->ref();
524     if (m_doc)
525         m_doc->ref();
526           
527     // Add namespaces based on the parent node
528     Vector<Element*> elemStack;
529     while (parentElement) {
530         elemStack.append(parentElement);
531         
532         Node* n = parentElement->parentNode();
533         if (!n || !n->isElementNode())
534             break;
535         parentElement = static_cast<Element*>(n);
536     }
537     
538     if (elemStack.isEmpty())
539         return;
540     
541 #if !PLATFORM(QT) || QT_VERSION < 0x040400
542     for (Element* element = elemStack.last(); !elemStack.isEmpty(); elemStack.removeLast()) {
543         if (NamedAttrMap* attrs = element->attributes()) {
544             for (unsigned i = 0; i < attrs->length(); i++) {
545                 Attribute* attr = attrs->attributeItem(i);
546                 if (attr->localName() == "xmlns")
547                     m_defaultNamespaceURI = attr->value();
548                 else if (attr->prefix() == "xmlns")
549                     m_prefixToNamespaceMap.set(attr->localName(), attr->value());
550             }
551         }
552     }
553 #else
554     QXmlStreamNamespaceDeclarations namespaces;
555     for (Element* element = elemStack.last(); !elemStack.isEmpty(); elemStack.removeLast()) {
556         if (NamedAttrMap* attrs = element->attributes()) {
557             for (unsigned i = 0; i < attrs->length(); i++) {
558                 Attribute* attr = attrs->attributeItem(i);
559                 if (attr->localName() == "xmlns")
560                     m_defaultNamespaceURI = attr->value();
561                 else if (attr->prefix() == "xmlns")
562                     namespaces.append(QXmlStreamNamespaceDeclaration(attr->localName(), attr->value()));
563             }
564         }
565     }
566     m_stream.addExtraNamespaceDeclarations(namespaces);
567     m_stream.setEntityResolver(new EntityResolver);
568 #endif
569 }
570
571 XMLTokenizer::~XMLTokenizer()
572 {
573     setCurrentNode(0);
574     if (m_parsingFragment && m_doc)
575         m_doc->deref();
576     if (m_pendingScript)
577         m_pendingScript->deref(this);
578 #if defined(USE_QXMLSTREAM) && QT_VERSION >= 0x040400
579     delete m_stream.entityResolver();
580 #endif
581 }
582
583 void XMLTokenizer::setCurrentNode(Node* n)
584 {
585     bool nodeNeedsReference = n && n != m_doc;
586     if (nodeNeedsReference)
587         n->ref(); 
588     if (m_currentNodeIsReferenced) 
589         m_currentNode->deref(); 
590     m_currentNode = n;
591     m_currentNodeIsReferenced = nodeNeedsReference;
592 }
593
594 bool XMLTokenizer::write(const SegmentedString& s, bool /*appendData*/)
595 {
596     String parseString = s.toString();
597     
598     if (m_sawXSLTransform || !m_sawFirstElement)
599         m_originalSourceForTransform += parseString;
600
601     if (m_parserStopped || m_sawXSLTransform)
602         return false;
603     
604     if (m_parserPaused) {
605         m_pendingSrc.append(s);
606         return false;
607     }
608     
609 #ifndef USE_QXMLSTREAM
610     if (!m_context)
611         initializeParserContext();
612     
613     // libXML throws an error if you try to switch the encoding for an empty string.
614     if (parseString.length()) {
615         // Hack around libxml2's lack of encoding overide support by manually
616         // resetting the encoding to UTF-16 before every chunk.  Otherwise libxml
617         // will detect <?xml version="1.0" encoding="<encoding name>"?> blocks 
618         // and switch encodings, causing the parse to fail.
619         const UChar BOM = 0xFEFF;
620         const unsigned char BOMHighByte = *reinterpret_cast<const unsigned char*>(&BOM);
621         xmlSwitchEncoding(m_context, BOMHighByte == 0xFF ? XML_CHAR_ENCODING_UTF16LE : XML_CHAR_ENCODING_UTF16BE);
622
623         xmlParseChunk(m_context, reinterpret_cast<const char*>(parseString.characters()), sizeof(UChar) * parseString.length(), 0);
624     }
625 #else
626     m_wroteText = true;
627     QString data(parseString);
628     if (!data.isEmpty()) {
629 #if QT_VERSION < 0x040400
630         if (!m_sawFirstElement) {
631             int idx = data.indexOf(QLatin1String("<?xml"));
632             if (idx != -1) {
633                 int start = idx + 5;
634                 int end = data.indexOf(QLatin1String("?>"), start);
635                 QString content = data.mid(start, end-start);
636                 bool ok = true;
637                 HashMap<String, String> attrs = parseAttributes(content, ok);
638                 String version = attrs.get("version");
639                 String encoding = attrs.get("encoding");
640                 ExceptionCode ec = 0;
641                 if (!m_parsingFragment) {
642                     if (!version.isEmpty())
643                         m_doc->setXMLVersion(version, ec);
644                     if (!encoding.isEmpty())
645                         m_doc->setXMLEncoding(encoding);
646                 }
647             }
648         }
649 #endif
650         m_stream.addData(data);
651         parse();
652     }
653 #endif
654     
655     return false;
656 }
657 #ifndef USE_QXMLSTREAM
658 static inline String toString(const xmlChar* str, unsigned len)
659 {
660     return UTF8Encoding().decode(reinterpret_cast<const char*>(str), len);
661 }
662
663 static inline String toString(const xmlChar* str)
664 {
665     if (!str)
666         return String();
667     
668     return UTF8Encoding().decode(reinterpret_cast<const char*>(str), strlen(reinterpret_cast<const char*>(str)));
669 }
670
671 struct _xmlSAX2Namespace {
672     const xmlChar* prefix;
673     const xmlChar* uri;
674 };
675 typedef struct _xmlSAX2Namespace xmlSAX2Namespace;
676
677 static inline void handleElementNamespaces(Element* newElement, const xmlChar** libxmlNamespaces, int nb_namespaces, ExceptionCode& ec)
678 {
679     xmlSAX2Namespace* namespaces = reinterpret_cast<xmlSAX2Namespace*>(libxmlNamespaces);
680     for(int i = 0; i < nb_namespaces; i++) {
681         String namespaceQName = "xmlns";
682         String namespaceURI = toString(namespaces[i].uri);
683         if (namespaces[i].prefix)
684             namespaceQName = "xmlns:" + toString(namespaces[i].prefix);
685         newElement->setAttributeNS("http://www.w3.org/2000/xmlns/", namespaceQName, namespaceURI, ec);
686         if (ec) // exception setting attributes
687             return;
688     }
689 }
690
691 struct _xmlSAX2Attributes {
692     const xmlChar* localname;
693     const xmlChar* prefix;
694     const xmlChar* uri;
695     const xmlChar* value;
696     const xmlChar* end;
697 };
698 typedef struct _xmlSAX2Attributes xmlSAX2Attributes;
699
700 static inline void handleElementAttributes(Element* newElement, const xmlChar** libxmlAttributes, int nb_attributes, ExceptionCode& ec)
701 {
702     xmlSAX2Attributes* attributes = reinterpret_cast<xmlSAX2Attributes*>(libxmlAttributes);
703     for(int i = 0; i < nb_attributes; i++) {
704         String attrLocalName = toString(attributes[i].localname);
705         int valueLength = (int) (attributes[i].end - attributes[i].value);
706         String attrValue = toString(attributes[i].value, valueLength);
707         String attrPrefix = toString(attributes[i].prefix);
708         String attrURI = attrPrefix.isEmpty() ? String() : toString(attributes[i].uri);
709         String attrQName = attrPrefix.isEmpty() ? attrLocalName : attrPrefix + ":" + attrLocalName;
710         
711         newElement->setAttributeNS(attrURI, attrQName, attrValue, ec);
712         if (ec) // exception setting attributes
713             return;
714     }
715 }
716
717 void XMLTokenizer::startElementNs(const xmlChar* xmlLocalName, const xmlChar* xmlPrefix, const xmlChar* xmlURI, int nb_namespaces,
718                                   const xmlChar** libxmlNamespaces, int nb_attributes, int nb_defaulted, const xmlChar** libxmlAttributes)
719 {
720     if (m_parserStopped)
721         return;
722     
723     if (m_parserPaused) {
724         m_pendingCallbacks->appendStartElementNSCallback(xmlLocalName, xmlPrefix, xmlURI, nb_namespaces, libxmlNamespaces,
725                                                          nb_attributes, nb_defaulted, libxmlAttributes);
726         return;
727     }
728     
729     m_sawFirstElement = true;
730
731     exitText();
732
733     String localName = toString(xmlLocalName);
734     String uri = toString(xmlURI);
735     String prefix = toString(xmlPrefix);
736
737     if (m_parsingFragment && uri.isNull()) {
738         if (!prefix.isNull())
739             uri = m_prefixToNamespaceMap.get(prefix);
740         else
741             uri = m_defaultNamespaceURI;
742     }
743
744     ExceptionCode ec = 0;
745     QualifiedName qName(prefix, localName, uri);
746     RefPtr<Element> newElement = m_doc->createElement(qName, true, ec);
747     if (!newElement) {
748         stopParsing();
749         return;
750     }
751     
752     handleElementNamespaces(newElement.get(), libxmlNamespaces, nb_namespaces, ec);
753     if (ec) {
754         stopParsing();
755         return;
756     }
757     
758     handleElementAttributes(newElement.get(), libxmlAttributes, nb_attributes, ec);
759     if (ec) {
760         stopParsing();
761         return;
762     }
763
764     if (newElement->hasTagName(scriptTag))
765         static_cast<HTMLScriptElement*>(newElement.get())->setCreatedByParser(true);
766     else if (newElement->hasTagName(HTMLNames::styleTag))
767         static_cast<HTMLStyleElement*>(newElement.get())->setCreatedByParser(true);
768 #if ENABLE(SVG)
769     else if (newElement->hasTagName(SVGNames::styleTag))
770         static_cast<SVGStyleElement*>(newElement.get())->setCreatedByParser(true);
771 #endif
772     
773     if (newElement->hasTagName(HTMLNames::scriptTag)
774 #if ENABLE(SVG)
775         || newElement->hasTagName(SVGNames::scriptTag)
776 #endif
777         )
778         m_scriptStartLine = lineNumber();
779     
780     if (!m_currentNode->addChild(newElement.get())) {
781         stopParsing();
782         return;
783     }
784     
785     setCurrentNode(newElement.get());
786     if (m_view && !newElement->attached())
787         newElement->attach();
788 }
789
790 void XMLTokenizer::endElementNs()
791 {
792     if (m_parserStopped)
793         return;
794
795     if (m_parserPaused) {
796         m_pendingCallbacks->appendEndElementNSCallback();
797         return;
798     }
799     
800     exitText();
801
802     Node* n = m_currentNode;
803     RefPtr<Node> parent = n->parentNode();
804     n->finishedParsing();
805     
806     // don't load external scripts for standalone documents (for now)
807     if (n->isElementNode() && m_view && (static_cast<Element*>(n)->hasTagName(scriptTag) 
808 #if ENABLE(SVG)
809                                          || static_cast<Element*>(n)->hasTagName(SVGNames::scriptTag)
810 #endif
811                                          )) {
812
813                                          
814         ASSERT(!m_pendingScript);
815         
816         m_requestingScript = true;
817         
818         Element* scriptElement = static_cast<Element*>(n);        
819         String scriptHref;
820         
821         if (static_cast<Element*>(n)->hasTagName(scriptTag))
822             scriptHref = scriptElement->getAttribute(srcAttr);
823 #if ENABLE(SVG)
824         else if (static_cast<Element*>(n)->hasTagName(SVGNames::scriptTag))
825             scriptHref = scriptElement->getAttribute(XLinkNames::hrefAttr);
826 #endif
827         
828         if (!scriptHref.isEmpty()) {
829             // we have a src attribute 
830             const AtomicString& charset = scriptElement->getAttribute(charsetAttr);
831             if ((m_pendingScript = m_doc->docLoader()->requestScript(scriptHref, charset))) {
832                 m_scriptElement = scriptElement;
833                 m_pendingScript->ref(this);
834                     
835                 // m_pendingScript will be 0 if script was already loaded and ref() executed it
836                 if (m_pendingScript)
837                     pauseParsing();
838             } else 
839                 m_scriptElement = 0;
840
841         } else {
842             String scriptCode = "";
843             for (Node* child = scriptElement->firstChild(); child; child = child->nextSibling()) {
844                 if (child->isTextNode() || child->nodeType() == Node::CDATA_SECTION_NODE)
845                     scriptCode += static_cast<CharacterData*>(child)->data();
846             }
847             m_view->frame()->loader()->executeScript(m_doc->url(), m_scriptStartLine - 1, scriptCode);
848         }
849         
850         m_requestingScript = false;
851     }
852
853     setCurrentNode(parent.get());
854 }
855
856 void XMLTokenizer::characters(const xmlChar* s, int len)
857 {
858     if (m_parserStopped)
859         return;
860     
861     if (m_parserPaused) {
862         m_pendingCallbacks->appendCharactersCallback(s, len);
863         return;
864     }
865     
866     if (m_currentNode->isTextNode() || enterText())
867         m_bufferedText.append(s, len);
868 }
869
870 void XMLTokenizer::error(ErrorType type, const char* message, va_list args)
871 {
872     if (m_parserStopped)
873         return;
874
875 #if PLATFORM(WIN_OS)
876     char m[1024];
877     vsnprintf(m, sizeof(m) - 1, message, args);
878 #else
879     char* m;
880     vasprintf(&m, message, args);
881 #endif
882     
883     if (m_parserPaused)
884         m_pendingCallbacks->appendErrorCallback(type, m, lineNumber(), columnNumber());
885     else
886         handleError(type, m, lineNumber(), columnNumber());
887
888 #if !PLATFORM(WIN_OS)
889     free(m);
890 #endif
891 }
892
893 void XMLTokenizer::processingInstruction(const xmlChar* target, const xmlChar* data)
894 {
895     if (m_parserStopped)
896         return;
897
898     if (m_parserPaused) {
899         m_pendingCallbacks->appendProcessingInstructionCallback(target, data);
900         return;
901     }
902     
903     exitText();
904
905     // ### handle exceptions
906     int exception = 0;
907     RefPtr<ProcessingInstruction> pi = m_doc->createProcessingInstruction(
908         toString(target), toString(data), exception);
909     if (exception)
910         return;
911
912     if (!m_currentNode->addChild(pi.get()))
913         return;
914     if (m_view && !pi->attached())
915         pi->attach();
916
917     // don't load stylesheets for standalone documents
918     if (m_doc->frame()) {
919         m_sawXSLTransform = !m_sawFirstElement && !pi->checkStyleSheet();
920 #if ENABLE(XSLT)
921         // Pretend we didn't see this PI if we're the result of a transform.
922         if (m_sawXSLTransform && !m_doc->transformSourceDocument())
923 #else
924         if (m_sawXSLTransform)
925 #endif
926             // Stop the SAX parser.
927             stopParsing();
928     }
929 }
930
931 void XMLTokenizer::cdataBlock(const xmlChar* s, int len)
932 {
933     if (m_parserStopped)
934         return;
935
936     if (m_parserPaused) {
937         m_pendingCallbacks->appendCDATABlockCallback(s, len);
938         return;
939     }
940     
941     exitText();
942
943     RefPtr<Node> newNode = new CDATASection(m_doc, toString(s, len));
944     if (!m_currentNode->addChild(newNode.get()))
945         return;
946     if (m_view && !newNode->attached())
947         newNode->attach();
948 }
949
950 void XMLTokenizer::comment(const xmlChar* s)
951 {
952     if (m_parserStopped)
953         return;
954
955     if (m_parserPaused) {
956         m_pendingCallbacks->appendCommentCallback(s);
957         return;
958     }
959     
960     exitText();
961
962     RefPtr<Node> newNode = new Comment(m_doc, toString(s));
963     m_currentNode->addChild(newNode.get());
964     if (m_view && !newNode->attached())
965         newNode->attach();
966 }
967
968 void XMLTokenizer::startDocument(const xmlChar* version, const xmlChar* encoding, int standalone)
969 {
970     ExceptionCode ec = 0;
971
972     if (version)
973         m_doc->setXMLVersion(toString(version), ec);
974     m_doc->setXMLStandalone(standalone == 1, ec); // possible values are 0, 1, and -1
975     if (encoding)
976         m_doc->setXMLEncoding(toString(encoding));
977 }
978
979 void XMLTokenizer::endDocument()
980 {
981     exitText();
982 }
983
984 void XMLTokenizer::internalSubset(const xmlChar* name, const xmlChar* externalID, const xmlChar* systemID)
985 {
986     if (m_parserStopped)
987         return;
988
989     if (m_parserPaused) {
990         m_pendingCallbacks->appendInternalSubsetCallback(name, externalID, systemID);
991         return;
992     }
993     
994     Document* doc = m_doc;
995     if (!doc)
996         return;
997
998     doc->setDocType(new DocumentType(doc, toString(name), toString(externalID), toString(systemID)));
999 }
1000
1001 static inline XMLTokenizer* getTokenizer(void* closure)
1002 {
1003     xmlParserCtxtPtr ctxt = static_cast<xmlParserCtxtPtr>(closure);
1004     return static_cast<XMLTokenizer*>(ctxt->_private);
1005 }
1006
1007 // This is a hack around http://bugzilla.gnome.org/show_bug.cgi?id=159219
1008 // Otherwise libxml seems to call all the SAX callbacks twice for any replaced entity.
1009 static inline bool hackAroundLibXMLEntityBug(void* closure)
1010 {
1011 #if LIBXML_VERSION >= 20627
1012     // This bug has been fixed in libxml 2.6.27.
1013     return false;
1014 #else
1015     return static_cast<xmlParserCtxtPtr>(closure)->node;
1016 #endif
1017 }
1018
1019 static void startElementNsHandler(void* closure, const xmlChar* localname, const xmlChar* prefix, const xmlChar* uri, int nb_namespaces, const xmlChar** namespaces, int nb_attributes, int nb_defaulted, const xmlChar** libxmlAttributes)
1020 {
1021     if (hackAroundLibXMLEntityBug(closure))
1022         return;
1023
1024     getTokenizer(closure)->startElementNs(localname, prefix, uri, nb_namespaces, namespaces, nb_attributes, nb_defaulted, libxmlAttributes);
1025 }
1026
1027 static void endElementNsHandler(void* closure, const xmlChar* localname, const xmlChar* prefix, const xmlChar* uri)
1028 {
1029     if (hackAroundLibXMLEntityBug(closure))
1030         return;
1031     
1032     getTokenizer(closure)->endElementNs();
1033 }
1034
1035 static void charactersHandler(void* closure, const xmlChar* s, int len)
1036 {
1037     if (hackAroundLibXMLEntityBug(closure))
1038         return;
1039     
1040     getTokenizer(closure)->characters(s, len);
1041 }
1042
1043 static void processingInstructionHandler(void* closure, const xmlChar* target, const xmlChar* data)
1044 {
1045     if (hackAroundLibXMLEntityBug(closure))
1046         return;
1047     
1048     getTokenizer(closure)->processingInstruction(target, data);
1049 }
1050
1051 static void cdataBlockHandler(void* closure, const xmlChar* s, int len)
1052 {
1053     if (hackAroundLibXMLEntityBug(closure))
1054         return;
1055     
1056     getTokenizer(closure)->cdataBlock(s, len);
1057 }
1058
1059 static void commentHandler(void* closure, const xmlChar* comment)
1060 {
1061     if (hackAroundLibXMLEntityBug(closure))
1062         return;
1063     
1064     getTokenizer(closure)->comment(comment);
1065 }
1066
1067 WTF_ATTRIBUTE_PRINTF(2, 3)
1068 static void warningHandler(void* closure, const char* message, ...)
1069 {
1070     va_list args;
1071     va_start(args, message);
1072     getTokenizer(closure)->error(XMLTokenizer::warning, message, args);
1073     va_end(args);
1074 }
1075
1076 WTF_ATTRIBUTE_PRINTF(2, 3)
1077 static void fatalErrorHandler(void* closure, const char* message, ...)
1078 {
1079     va_list args;
1080     va_start(args, message);
1081     getTokenizer(closure)->error(XMLTokenizer::fatal, message, args);
1082     va_end(args);
1083 }
1084
1085 WTF_ATTRIBUTE_PRINTF(2, 3)
1086 static void normalErrorHandler(void* closure, const char* message, ...)
1087 {
1088     va_list args;
1089     va_start(args, message);
1090     getTokenizer(closure)->error(XMLTokenizer::nonFatal, message, args);
1091     va_end(args);
1092 }
1093
1094 // Using a global variable entity and marking it XML_INTERNAL_PREDEFINED_ENTITY is
1095 // a hack to avoid malloc/free. Using a global variable like this could cause trouble
1096 // if libxml implementation details were to change
1097 static xmlChar sharedXHTMLEntityResult[5] = {0,0,0,0,0};
1098 static xmlEntity sharedXHTMLEntity = {
1099     0, XML_ENTITY_DECL, 0, 0, 0, 0, 0, 0, 0, 
1100     sharedXHTMLEntityResult, sharedXHTMLEntityResult, 0,
1101     XML_INTERNAL_PREDEFINED_ENTITY, 0, 0, 0, 0, 0
1102 };
1103
1104 static xmlEntityPtr getXHTMLEntity(const xmlChar* name)
1105 {
1106     UChar c = decodeNamedEntity(reinterpret_cast<const char*>(name));
1107     if (!c)
1108         return 0;
1109
1110     CString value = String(&c, 1).utf8();
1111     ASSERT(value.length() < 5);
1112     sharedXHTMLEntity.length = value.length();
1113     sharedXHTMLEntity.name = name;
1114     memcpy(sharedXHTMLEntityResult, value.data(), sharedXHTMLEntity.length + 1);
1115
1116     return &sharedXHTMLEntity;
1117 }
1118
1119 static xmlEntityPtr getEntityHandler(void* closure, const xmlChar* name)
1120 {
1121     xmlParserCtxtPtr ctxt = static_cast<xmlParserCtxtPtr>(closure);
1122     xmlEntityPtr ent = xmlGetPredefinedEntity(name);
1123     if (ent) {
1124         ent->etype = XML_INTERNAL_PREDEFINED_ENTITY;
1125         return ent;
1126     }
1127
1128     ent = xmlGetDocEntity(ctxt->myDoc, name);
1129     if (!ent && getTokenizer(closure)->isXHTMLDocument()) {
1130         ent = getXHTMLEntity(name);
1131         if (ent)
1132             ent->etype = XML_INTERNAL_GENERAL_ENTITY;
1133     }
1134     
1135     return ent;
1136 }
1137
1138 static void startDocumentHandler(void* closure)
1139 {
1140     xmlParserCtxt* ctxt = static_cast<xmlParserCtxt*>(closure);
1141     getTokenizer(closure)->startDocument(ctxt->version, ctxt->encoding, ctxt->standalone);
1142     xmlSAX2StartDocument(closure);
1143 }
1144
1145 static void endDocumentHandler(void* closure)
1146 {
1147     getTokenizer(closure)->endDocument();
1148     xmlSAX2EndDocument(closure);
1149 }
1150
1151 static void internalSubsetHandler(void* closure, const xmlChar* name, const xmlChar* externalID, const xmlChar* systemID)
1152 {
1153     getTokenizer(closure)->internalSubset(name, externalID, systemID);
1154     xmlSAX2InternalSubset(closure, name, externalID, systemID);
1155 }
1156
1157 static void externalSubsetHandler(void* closure, const xmlChar* name, const xmlChar* externalId, const xmlChar* systemId)
1158 {
1159     String extId = toString(externalId);
1160     if ((extId == "-//W3C//DTD XHTML 1.0 Transitional//EN")
1161         || (extId == "-//W3C//DTD XHTML 1.1//EN")
1162         || (extId == "-//W3C//DTD XHTML 1.0 Strict//EN")
1163         || (extId == "-//W3C//DTD XHTML 1.0 Frameset//EN")
1164         || (extId == "-//W3C//DTD XHTML Basic 1.0//EN")
1165         || (extId == "-//W3C//DTD XHTML 1.1 plus MathML 2.0//EN")
1166         || (extId == "-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN")
1167         || (extId == "-//WAPFORUM//DTD XHTML Mobile 1.0//EN"))
1168         getTokenizer(closure)->setIsXHTMLDocument(true); // controls if we replace entities or not.
1169 }
1170
1171 static void ignorableWhitespaceHandler(void* ctx, const xmlChar* ch, int len)
1172 {
1173     // nothing to do, but we need this to work around a crasher
1174     // http://bugzilla.gnome.org/show_bug.cgi?id=172255
1175     // http://bugs.webkit.org/show_bug.cgi?id=5792
1176 }
1177 #endif
1178
1179 void XMLTokenizer::handleError(ErrorType type, const char* m, int lineNumber, int columnNumber)
1180 {
1181     if (type == fatal || (m_errorCount < maxErrors && m_lastErrorLine != lineNumber && m_lastErrorColumn != columnNumber)) {
1182         switch (type) {
1183             case warning:
1184                 m_errorMessages += String::format("warning on line %d at column %d: %s", lineNumber, columnNumber, m);
1185                 break;
1186             case fatal:
1187             case nonFatal:
1188                 m_errorMessages += String::format("error on line %d at column %d: %s", lineNumber, columnNumber, m);
1189         }
1190         
1191         m_lastErrorLine = lineNumber;
1192         m_lastErrorColumn = columnNumber;
1193         ++m_errorCount;
1194     }
1195     
1196     if (type != warning)
1197         m_sawError = true;
1198     
1199     if (type == fatal)
1200         stopParsing();    
1201 }
1202
1203 bool XMLTokenizer::enterText()
1204 {
1205 #ifndef USE_QXMLSTREAM
1206     ASSERT(m_bufferedText.size() == 0);
1207 #endif
1208     RefPtr<Node> newNode = new Text(m_doc, "");
1209     if (!m_currentNode->addChild(newNode.get()))
1210         return false;
1211     setCurrentNode(newNode.get());
1212     return true;
1213 }
1214
1215 void XMLTokenizer::exitText()
1216 {
1217     if (m_parserStopped)
1218         return;
1219
1220     if (!m_currentNode || !m_currentNode->isTextNode())
1221         return;
1222
1223 #ifndef USE_QXMLSTREAM
1224     ExceptionCode ec = 0;
1225     static_cast<Text*>(m_currentNode)->appendData(toString(m_bufferedText.data(), m_bufferedText.size()), ec);
1226     Vector<xmlChar> empty;
1227     m_bufferedText.swap(empty);
1228 #endif
1229
1230     if (m_view && m_currentNode && !m_currentNode->attached())
1231         m_currentNode->attach();
1232
1233     // FIXME: What's the right thing to do if the parent is really 0?
1234     // Just leaving the current node set to the text node doesn't make much sense.
1235     if (Node* par = m_currentNode->parentNode())
1236         setCurrentNode(par);
1237 }
1238
1239 void XMLTokenizer::initializeParserContext()
1240 {
1241 #ifndef USE_QXMLSTREAM
1242     xmlSAXHandler sax;
1243     memset(&sax, 0, sizeof(sax));
1244     sax.error = normalErrorHandler;
1245     sax.fatalError = fatalErrorHandler;
1246     sax.characters = charactersHandler;
1247     sax.processingInstruction = processingInstructionHandler;
1248     sax.cdataBlock = cdataBlockHandler;
1249     sax.comment = commentHandler;
1250     sax.warning = warningHandler;
1251     sax.startElementNs = startElementNsHandler;
1252     sax.endElementNs = endElementNsHandler;
1253     sax.getEntity = getEntityHandler;
1254     sax.startDocument = startDocumentHandler;
1255     sax.endDocument = endDocumentHandler;
1256     sax.internalSubset = internalSubsetHandler;
1257     sax.externalSubset = externalSubsetHandler;
1258     sax.ignorableWhitespace = ignorableWhitespaceHandler;
1259     sax.entityDecl = xmlSAX2EntityDecl;
1260     sax.initialized = XML_SAX2_MAGIC;
1261 #endif
1262     m_parserStopped = false;
1263     m_sawError = false;
1264     m_sawXSLTransform = false;
1265     m_sawFirstElement = false;
1266     
1267 #ifndef USE_QXMLSTREAM
1268     m_context = createStringParser(&sax, this);
1269 #endif
1270 }
1271
1272 void XMLTokenizer::end()
1273 {
1274 #if ENABLE(XSLT)
1275     if (m_sawXSLTransform) {
1276         m_doc->setTransformSource(xmlDocPtrForString(m_doc->docLoader(), m_originalSourceForTransform, m_doc->url()));
1277         
1278         m_doc->setParsing(false); // Make the doc think it's done, so it will apply xsl sheets.
1279         m_doc->updateStyleSelector();
1280         m_doc->setParsing(true);
1281         m_parserStopped = true;
1282     }
1283 #endif
1284
1285 #ifndef USE_QXMLSTREAM
1286     if (m_context) {
1287         // Tell libxml we're done.
1288         xmlParseChunk(m_context, 0, 0, 1);
1289         
1290         if (m_context->myDoc)
1291             xmlFreeDoc(m_context->myDoc);
1292         xmlFreeParserCtxt(m_context);
1293         m_context = 0;
1294     }
1295 #else
1296     if (m_stream.error() == QXmlStreamReader::PrematureEndOfDocumentError || (m_wroteText && !m_sawFirstElement)) {
1297         handleError(fatal, qPrintable(m_stream.errorString()), lineNumber(),
1298                     columnNumber());
1299     }
1300 #endif
1301     
1302     if (m_sawError)
1303         insertErrorMessageBlock();
1304     else {
1305         exitText();
1306         m_doc->updateStyleSelector();
1307     }
1308     
1309     setCurrentNode(0);
1310     if (!m_parsingFragment)
1311         m_doc->finishedParsing();    
1312 }
1313
1314 void XMLTokenizer::finish()
1315 {
1316     if (m_parserPaused)
1317         m_finishCalled = true;
1318     else
1319         end();
1320 }
1321
1322 static inline RefPtr<Element> createXHTMLParserErrorHeader(Document* doc, const String& errorMessages) 
1323 {
1324     ExceptionCode ec = 0;
1325     RefPtr<Element> reportElement = doc->createElementNS(xhtmlNamespaceURI, "parsererror", ec);
1326     reportElement->setAttribute(styleAttr, "display: block; white-space: pre; border: 2px solid #c77; padding: 0 1em 0 1em; margin: 1em; background-color: #fdd; color: black");
1327     
1328     RefPtr<Element> h3 = doc->createElementNS(xhtmlNamespaceURI, "h3", ec);
1329     reportElement->appendChild(h3.get(), ec);
1330     h3->appendChild(doc->createTextNode("This page contains the following errors:"), ec);
1331     
1332     RefPtr<Element> fixed = doc->createElementNS(xhtmlNamespaceURI, "div", ec);
1333     reportElement->appendChild(fixed.get(), ec);
1334     fixed->setAttribute(styleAttr, "font-family:monospace;font-size:12px");
1335     fixed->appendChild(doc->createTextNode(errorMessages), ec);
1336     
1337     h3 = doc->createElementNS(xhtmlNamespaceURI, "h3", ec);
1338     reportElement->appendChild(h3.get(), ec);
1339     h3->appendChild(doc->createTextNode("Below is a rendering of the page up to the first error."), ec);
1340     
1341     return reportElement;
1342 }
1343
1344 void XMLTokenizer::insertErrorMessageBlock()
1345 {
1346     // One or more errors occurred during parsing of the code. Display an error block to the user above
1347     // the normal content (the DOM tree is created manually and includes line/col info regarding 
1348     // where the errors are located)
1349
1350     // Create elements for display
1351     ExceptionCode ec = 0;
1352     Document* doc = m_doc;
1353     Node* documentElement = doc->documentElement();
1354     if (!documentElement) {
1355         RefPtr<Node> rootElement = doc->createElementNS(xhtmlNamespaceURI, "html", ec);
1356         doc->appendChild(rootElement, ec);
1357         RefPtr<Node> body = doc->createElementNS(xhtmlNamespaceURI, "body", ec);
1358         rootElement->appendChild(body, ec);
1359         documentElement = body.get();
1360     }
1361 #if ENABLE(SVG)
1362     else if (documentElement->namespaceURI() == SVGNames::svgNamespaceURI) {
1363         // Until our SVG implementation has text support, it is best if we 
1364         // wrap the erroneous SVG document in an xhtml document and render
1365         // the combined document with error messages.
1366         RefPtr<Node> rootElement = doc->createElementNS(xhtmlNamespaceURI, "html", ec);
1367         RefPtr<Node> body = doc->createElementNS(xhtmlNamespaceURI, "body", ec);
1368         rootElement->appendChild(body, ec);
1369         body->appendChild(documentElement, ec);
1370         doc->appendChild(rootElement.get(), ec);
1371         documentElement = body.get();
1372     }
1373 #endif
1374
1375     RefPtr<Element> reportElement = createXHTMLParserErrorHeader(doc, m_errorMessages);
1376     documentElement->insertBefore(reportElement, documentElement->firstChild(), ec);
1377 #if ENABLE(XSLT)
1378     if (doc->transformSourceDocument()) {
1379         RefPtr<Element> par = doc->createElementNS(xhtmlNamespaceURI, "p", ec);
1380         reportElement->appendChild(par, ec);
1381         par->setAttribute(styleAttr, "white-space: normal");
1382         par->appendChild(doc->createTextNode("This document was created as the result of an XSL transformation. The line and column numbers given are from the transformed result."), ec);
1383     }
1384 #endif
1385     doc->updateRendering();
1386 }
1387
1388 void XMLTokenizer::notifyFinished(CachedResource* finishedObj)
1389 {
1390     ASSERT(m_pendingScript == finishedObj);
1391     ASSERT(m_pendingScript->accessCount() > 0);
1392         
1393     String cachedScriptUrl = m_pendingScript->url();
1394     String scriptSource = m_pendingScript->script();
1395     bool errorOccurred = m_pendingScript->errorOccurred();
1396     m_pendingScript->deref(this);
1397     m_pendingScript = 0;
1398     
1399     RefPtr<Element> e = m_scriptElement;
1400     m_scriptElement = 0;
1401     
1402     if (errorOccurred) 
1403         EventTargetNodeCast(e.get())->dispatchHTMLEvent(errorEvent, true, false);
1404     else {
1405         m_view->frame()->loader()->executeScript(cachedScriptUrl, 0, scriptSource);
1406         EventTargetNodeCast(e.get())->dispatchHTMLEvent(loadEvent, false, false);
1407     }
1408     
1409     m_scriptElement = 0;
1410     
1411     if (!m_requestingScript)
1412         resumeParsing();
1413 }
1414
1415 bool XMLTokenizer::isWaitingForScripts() const
1416 {
1417     return m_pendingScript != 0;
1418 }
1419
1420 #if ENABLE(XSLT)
1421 void* xmlDocPtrForString(DocLoader* docLoader, const String& source, const DeprecatedString& url)
1422 {
1423     if (source.isEmpty())
1424         return 0;
1425
1426     // Parse in a single chunk into an xmlDocPtr
1427     // FIXME: Hook up error handlers so that a failure to parse the main document results in
1428     // good error messages.
1429     const UChar BOM = 0xFEFF;
1430     const unsigned char BOMHighByte = *reinterpret_cast<const unsigned char*>(&BOM);
1431
1432     xmlGenericErrorFunc oldErrorFunc = xmlGenericError;
1433     void* oldErrorContext = xmlGenericErrorContext;
1434     
1435     setLoaderForLibXMLCallbacks(docLoader);        
1436     xmlSetGenericErrorFunc(0, errorFunc);
1437     
1438     xmlDocPtr sourceDoc = xmlReadMemory(reinterpret_cast<const char*>(source.characters()),
1439                                         source.length() * sizeof(UChar),
1440                                         url.ascii(),
1441                                         BOMHighByte == 0xFF ? "UTF-16LE" : "UTF-16BE", 
1442                                         XSLT_PARSE_OPTIONS);
1443     
1444     setLoaderForLibXMLCallbacks(0);
1445     xmlSetGenericErrorFunc(oldErrorContext, oldErrorFunc);
1446     
1447     return sourceDoc;
1448 }
1449 #endif
1450
1451 int XMLTokenizer::lineNumber() const
1452 {
1453 #ifndef USE_QXMLSTREAM
1454     return m_context ? m_context->input->line : 1;
1455 #else
1456     return m_stream.lineNumber();
1457 #endif
1458 }
1459
1460 int XMLTokenizer::columnNumber() const
1461 {
1462 #ifndef USE_QXMLSTREAM
1463     return m_context ? m_context->input->col : 1;
1464 #else
1465     return m_stream.columnNumber();
1466 #endif
1467 }
1468
1469 void XMLTokenizer::stopParsing()
1470 {
1471     Tokenizer::stopParsing();
1472 #ifndef USE_QXMLSTREAM
1473     xmlStopParser(m_context);
1474 #endif
1475 }
1476
1477 void XMLTokenizer::pauseParsing()
1478 {
1479     if (m_parsingFragment)
1480         return;
1481     
1482     m_parserPaused = true;
1483 }
1484
1485 void XMLTokenizer::resumeParsing()
1486 {
1487     ASSERT(m_parserPaused);
1488     
1489     m_parserPaused = false;
1490
1491     // First, execute any pending callbacks
1492 #ifndef USE_QXMLSTREAM
1493     while (!m_pendingCallbacks->isEmpty()) {
1494         m_pendingCallbacks->callAndRemoveFirstCallback(this);
1495         
1496         // A callback paused the parser
1497         if (m_parserPaused)
1498             return;
1499     }
1500 #else
1501     parse();
1502     if (m_parserPaused)
1503         return;
1504 #endif
1505
1506     // Then, write any pending data
1507     SegmentedString rest = m_pendingSrc;
1508     m_pendingSrc.clear();
1509     write(rest, false);
1510
1511     // Finally, if finish() has been called and write() didn't result
1512     // in any further callbacks being queued, call end()
1513     if (m_finishCalled
1514 #ifndef USE_QXMLSTREAM
1515         && m_pendingCallbacks->isEmpty())
1516 #else
1517         )
1518 #endif
1519         end();
1520 }
1521
1522 #ifndef USE_QXMLSTREAM
1523 static void balancedStartElementNsHandler(void* closure, const xmlChar* localname, const xmlChar* prefix,
1524                                           const xmlChar* uri, int nb_namespaces, const xmlChar** namespaces,
1525                                           int nb_attributes, int nb_defaulted, const xmlChar** libxmlAttributes)
1526 {
1527    static_cast<XMLTokenizer*>(closure)->startElementNs(localname, prefix, uri, nb_namespaces, namespaces, nb_attributes, nb_defaulted, libxmlAttributes);
1528 }
1529
1530 static void balancedEndElementNsHandler(void* closure, const xmlChar* localname, const xmlChar* prefix, const xmlChar* uri)
1531 {
1532     static_cast<XMLTokenizer*>(closure)->endElementNs();
1533 }
1534
1535 static void balancedCharactersHandler(void* closure, const xmlChar* s, int len)
1536 {
1537     static_cast<XMLTokenizer*>(closure)->characters(s, len);
1538 }
1539
1540 static void balancedProcessingInstructionHandler(void* closure, const xmlChar* target, const xmlChar* data)
1541 {
1542     static_cast<XMLTokenizer*>(closure)->processingInstruction(target, data);
1543 }
1544
1545 static void balancedCdataBlockHandler(void* closure, const xmlChar* s, int len)
1546 {
1547     static_cast<XMLTokenizer*>(closure)->cdataBlock(s, len);
1548 }
1549
1550 static void balancedCommentHandler(void* closure, const xmlChar* comment)
1551 {
1552     static_cast<XMLTokenizer*>(closure)->comment(comment);
1553 }
1554
1555 WTF_ATTRIBUTE_PRINTF(2, 3)
1556 static void balancedWarningHandler(void* closure, const char* message, ...)
1557 {
1558     va_list args;
1559     va_start(args, message);
1560     static_cast<XMLTokenizer*>(closure)->error(XMLTokenizer::warning, message, args);
1561     va_end(args);
1562 }
1563 #endif
1564 bool parseXMLDocumentFragment(const String& string, DocumentFragment* fragment, Element* parent)
1565 {
1566     if (!string.length())
1567         return true;
1568
1569     XMLTokenizer tokenizer(fragment, parent);
1570     
1571 #ifndef USE_QXMLSTREAM
1572     xmlSAXHandler sax;
1573     memset(&sax, 0, sizeof(sax));
1574
1575     sax.characters = balancedCharactersHandler;
1576     sax.processingInstruction = balancedProcessingInstructionHandler;
1577     sax.startElementNs = balancedStartElementNsHandler;
1578     sax.endElementNs = balancedEndElementNsHandler;
1579     sax.cdataBlock = balancedCdataBlockHandler;
1580     sax.ignorableWhitespace = balancedCharactersHandler;
1581     sax.comment = balancedCommentHandler;
1582     sax.warning = balancedWarningHandler;
1583     sax.initialized = XML_SAX2_MAGIC;
1584     
1585     int result = xmlParseBalancedChunkMemory(0, &sax, &tokenizer, 0, (const xmlChar*)string.utf8().data(), 0);
1586     tokenizer.endDocument();
1587     return result == 0;
1588 #else
1589     tokenizer.write(String("<qxmlstreamdummyelement>"), false);
1590     tokenizer.write(string, false);
1591     tokenizer.write(String("</qxmlstreamdummyelement>"), false);
1592     tokenizer.finish();
1593     return !tokenizer.hasError();
1594 #endif
1595 }
1596
1597 // --------------------------------
1598
1599 struct AttributeParseState {
1600     HashMap<String, String> attributes;
1601     bool gotAttributes;
1602 };
1603
1604 #ifndef USE_QXMLSTREAM
1605 static void attributesStartElementNsHandler(void* closure, const xmlChar* xmlLocalName, const xmlChar* xmlPrefix,
1606                                             const xmlChar* xmlURI, int nb_namespaces, const xmlChar** namespaces,
1607                                             int nb_attributes, int nb_defaulted, const xmlChar** libxmlAttributes)
1608 {
1609     if (strcmp(reinterpret_cast<const char*>(xmlLocalName), "attrs") != 0)
1610         return;
1611     
1612     xmlParserCtxtPtr ctxt = static_cast<xmlParserCtxtPtr>(closure);
1613     AttributeParseState* state = static_cast<AttributeParseState*>(ctxt->_private);
1614     
1615     state->gotAttributes = true;
1616     
1617     xmlSAX2Attributes* attributes = reinterpret_cast<xmlSAX2Attributes*>(libxmlAttributes);
1618     for(int i = 0; i < nb_attributes; i++) {
1619         String attrLocalName = toString(attributes[i].localname);
1620         int valueLength = (int) (attributes[i].end - attributes[i].value);
1621         String attrValue = toString(attributes[i].value, valueLength);
1622         String attrPrefix = toString(attributes[i].prefix);
1623         String attrQName = attrPrefix.isEmpty() ? attrLocalName : attrPrefix + ":" + attrLocalName;
1624         
1625         state->attributes.set(attrQName, attrValue);
1626     }
1627 }
1628 #else
1629 static void attributesStartElementNsHandler(AttributeParseState* state, const QXmlStreamAttributes& attrs)
1630 {
1631     if (attrs.count() <= 0)
1632         return;
1633
1634     state->gotAttributes = true;
1635
1636     for(int i = 0; i < attrs.count(); i++) {
1637         const QXmlStreamAttribute& attr = attrs[i];
1638         String attrLocalName = attr.name();
1639         String attrValue     = attr.value();
1640         String attrURI       = attr.namespaceUri();
1641         String attrQName     = attr.qualifiedName();
1642         state->attributes.set(attrQName, attrValue);
1643     }
1644 }
1645 #endif
1646
1647 HashMap<String, String> parseAttributes(const String& string, bool& attrsOK)
1648 {
1649     AttributeParseState state;
1650     state.gotAttributes = false;
1651
1652 #ifndef USE_QXMLSTREAM
1653     xmlSAXHandler sax;
1654     memset(&sax, 0, sizeof(sax));
1655     sax.startElementNs = attributesStartElementNsHandler;
1656     sax.initialized = XML_SAX2_MAGIC;
1657     xmlParserCtxtPtr parser = createStringParser(&sax, &state);
1658     String parseString = "<?xml version=\"1.0\"?><attrs " + string + " />";
1659     xmlParseChunk(parser, reinterpret_cast<const char*>(parseString.characters()), parseString.length() * sizeof(UChar), 1);
1660     if (parser->myDoc)
1661         xmlFreeDoc(parser->myDoc);
1662     xmlFreeParserCtxt(parser);
1663 #else
1664     QXmlStreamReader stream;
1665     QString dummy = QString("<?xml version=\"1.0\"?><attrs %1 />").arg(string);
1666     stream.addData(dummy);
1667     while (!stream.atEnd()) {
1668         stream.readNext();
1669         if (stream.isStartElement()) {
1670             attributesStartElementNsHandler(&state, stream.attributes());
1671         }
1672     }
1673 #endif
1674     attrsOK = state.gotAttributes;
1675     return state.attributes;
1676 }
1677
1678 #ifdef USE_QXMLSTREAM
1679 static inline String prefixFromQName(const QString& qName)
1680 {
1681     const int offset = qName.indexOf(QLatin1Char(':'));
1682     if (offset <= 0)
1683         return String();
1684     else
1685         return qName.left(offset);
1686 }
1687
1688 static inline void handleElementNamespaces(Element* newElement, const QXmlStreamNamespaceDeclarations &ns,
1689                                            ExceptionCode& ec)
1690 {
1691     for (int i = 0; i < ns.count(); ++i) {
1692         const QXmlStreamNamespaceDeclaration &decl = ns[i];
1693         String namespaceURI = decl.namespaceUri();
1694         String namespaceQName = decl.prefix().isEmpty() ? String("xmlns") : String("xmlns:") + decl.prefix();
1695         newElement->setAttributeNS("http://www.w3.org/2000/xmlns/", namespaceQName, namespaceURI, ec);
1696         if (ec) // exception setting attributes
1697             return;
1698     }
1699 }
1700
1701 static inline void handleElementAttributes(Element* newElement, const QXmlStreamAttributes &attrs, ExceptionCode& ec)
1702 {
1703     for (int i = 0; i < attrs.count(); ++i) {
1704         const QXmlStreamAttribute &attr = attrs[i];
1705         String attrLocalName = attr.name();
1706         String attrValue     = attr.value();
1707         String attrURI       = attr.namespaceUri().isEmpty() ? String() : String(attr.namespaceUri());
1708         String attrQName     = attr.qualifiedName();
1709         newElement->setAttributeNS(attrURI, attrQName, attrValue, ec);
1710         if (ec) // exception setting attributes
1711             return;
1712     }
1713 }
1714
1715 void XMLTokenizer::parse()
1716 {
1717     while (!m_parserStopped && !m_parserPaused && !m_stream.atEnd()) {
1718         m_stream.readNext();
1719         switch (m_stream.tokenType()) {
1720         case QXmlStreamReader::StartDocument: {
1721             startDocument();
1722         }
1723             break;
1724         case QXmlStreamReader::EndDocument: {
1725             endDocument();
1726         }
1727             break;
1728         case QXmlStreamReader::StartElement: {
1729             parseStartElement();
1730         }
1731             break;
1732         case QXmlStreamReader::EndElement: {
1733             parseEndElement();
1734         }
1735             break;
1736         case QXmlStreamReader::Characters: {
1737             if (m_stream.isCDATA()) {
1738                 //cdata
1739                 parseCdata();
1740             } else {
1741                 //characters
1742                 parseCharacters();
1743             }
1744         }
1745             break;
1746         case QXmlStreamReader::Comment: {
1747             parseComment();
1748         }
1749             break;
1750         case QXmlStreamReader::DTD: {
1751             //qDebug()<<"------------- DTD";
1752             parseDtd();
1753         }
1754             break;
1755         case QXmlStreamReader::EntityReference: {
1756             //qDebug()<<"---------- ENTITY = "<<m_stream.name().toString()
1757             //        <<", t = "<<m_stream.text().toString();
1758             if (isXHTMLDocument()) {
1759                 QString entity = m_stream.name().toString();
1760                 UChar c = decodeNamedEntity(entity.toUtf8().constData());
1761                 if (m_currentNode->isTextNode() || enterText()) {
1762                     ExceptionCode ec = 0;
1763                     String str(&c, 1);
1764                     //qDebug()<<" ------- adding entity "<<str;
1765                     static_cast<Text*>(m_currentNode)->appendData(str, ec);
1766                 }
1767             }
1768         }
1769             break;
1770         case QXmlStreamReader::ProcessingInstruction: {
1771             parseProcessingInstruction();
1772         }
1773             break;
1774         default: {
1775             if (m_stream.error() != QXmlStreamReader::PrematureEndOfDocumentError) {
1776                 ErrorType type = (m_stream.error() == QXmlStreamReader::NotWellFormedError) ?
1777                                  fatal : warning;
1778                 handleError(type, qPrintable(m_stream.errorString()), lineNumber(),
1779                             columnNumber());
1780             }
1781         }
1782             break;
1783         }
1784     }
1785 }
1786
1787 void XMLTokenizer::startDocument()
1788 {
1789     initializeParserContext();
1790     ExceptionCode ec = 0;
1791
1792     if (!m_parsingFragment) {
1793         m_doc->setXMLStandalone(m_stream.isStandaloneDocument(), ec);
1794
1795 #if QT_VERSION >= 0x040400
1796         QStringRef version = m_stream.documentVersion();
1797         if (!version.isEmpty())
1798             m_doc->setXMLVersion(version, ec);
1799         QStringRef encoding = m_stream.documentEncoding();
1800         if (!encoding.isEmpty())
1801             m_doc->setXMLEncoding(encoding);
1802 #endif
1803     }
1804 }
1805
1806 void XMLTokenizer::parseStartElement()
1807 {
1808     if (!m_sawFirstElement && m_parsingFragment) {
1809         // skip dummy element for fragments
1810         m_sawFirstElement = true;
1811         return;
1812     }
1813     m_sawFirstElement = true;
1814
1815     exitText();
1816
1817     String localName = m_stream.name();
1818     String uri       = m_stream.namespaceUri();
1819     String prefix    = prefixFromQName(m_stream.qualifiedName().toString());
1820
1821     if (m_parsingFragment && uri.isNull()) {
1822         Q_ASSERT (prefix.isNull());
1823         uri = m_defaultNamespaceURI;
1824     }
1825
1826     ExceptionCode ec = 0;
1827     QualifiedName qName(prefix, localName, uri);
1828     RefPtr<Element> newElement = m_doc->createElement(qName, true, ec);
1829     if (!newElement) {
1830         stopParsing();
1831         return;
1832     }
1833
1834     handleElementNamespaces(newElement.get(), m_stream.namespaceDeclarations(), ec);
1835     if (ec) {
1836         stopParsing();
1837         return;
1838     }
1839
1840     handleElementAttributes(newElement.get(), m_stream.attributes(), ec);
1841     if (ec) {
1842         stopParsing();
1843         return;
1844     }
1845
1846     if (newElement->hasTagName(scriptTag))
1847         static_cast<HTMLScriptElement*>(newElement.get())->setCreatedByParser(true);
1848
1849     if (newElement->hasTagName(HTMLNames::scriptTag)
1850 #if ENABLE(SVG)
1851         || newElement->hasTagName(SVGNames::scriptTag)
1852 #endif
1853         )
1854         m_scriptStartLine = lineNumber();
1855
1856     if (!m_currentNode->addChild(newElement.get())) {
1857         stopParsing();
1858         return;
1859     }
1860
1861     setCurrentNode(newElement.get());
1862     if (m_view && !newElement->attached())
1863         newElement->attach();
1864 }
1865
1866 void XMLTokenizer::parseEndElement()
1867 {
1868     exitText();
1869
1870     Node* n = m_currentNode;
1871
1872     // skip end of dummy element
1873 //     if (m_parsingFragment & n->nodeType() == Node::DOCUMENT_FRAGMENT_NODE)
1874 //         return;
1875     
1876     RefPtr<Node> parent = n->parentNode();
1877     n->finishedParsing();
1878
1879     // don't load external scripts for standalone documents (for now)
1880     if (n->isElementNode() && m_view && (static_cast<Element*>(n)->hasTagName(scriptTag) 
1881 #if ENABLE(SVG)
1882                                          || static_cast<Element*>(n)->hasTagName(SVGNames::scriptTag)
1883 #endif
1884                                          )) {
1885
1886
1887         ASSERT(!m_pendingScript);
1888
1889         m_requestingScript = true;
1890
1891         Element* scriptElement = static_cast<Element*>(n);
1892         String scriptHref;
1893
1894         if (static_cast<Element*>(n)->hasTagName(scriptTag))
1895             scriptHref = scriptElement->getAttribute(srcAttr);
1896 #if ENABLE(SVG)
1897         else if (static_cast<Element*>(n)->hasTagName(SVGNames::scriptTag))
1898             scriptHref = scriptElement->getAttribute(XLinkNames::hrefAttr);
1899 #endif
1900         if (!scriptHref.isEmpty()) {
1901             // we have a src attribute
1902             const AtomicString& charset = scriptElement->getAttribute(charsetAttr);
1903             if ((m_pendingScript = m_doc->docLoader()->requestScript(scriptHref, charset))) {
1904                 m_scriptElement = scriptElement;
1905                 m_pendingScript->ref(this);
1906
1907                 // m_pendingScript will be 0 if script was already loaded and ref() executed it
1908                 if (m_pendingScript)
1909                     pauseParsing();
1910             } else
1911                 m_scriptElement = 0;
1912
1913         } else {
1914             String scriptCode = "";
1915             for (Node* child = scriptElement->firstChild(); child; child = child->nextSibling()) {
1916                 if (child->isTextNode() || child->nodeType() == Node::CDATA_SECTION_NODE)
1917                     scriptCode += static_cast<CharacterData*>(child)->data();
1918             }
1919             m_view->frame()->loader()->executeScript(m_doc->url(), m_scriptStartLine - 1, scriptCode);
1920         }
1921         m_requestingScript = false;
1922     }
1923
1924     setCurrentNode(parent.get());
1925 }
1926
1927 void XMLTokenizer::parseCharacters()
1928 {
1929     if (m_currentNode->isTextNode() || enterText()) {
1930         ExceptionCode ec = 0;
1931         static_cast<Text*>(m_currentNode)->appendData(m_stream.text(), ec);
1932     }
1933 }
1934
1935 void XMLTokenizer::parseProcessingInstruction()
1936 {
1937     exitText();
1938
1939     // ### handle exceptions
1940     int exception = 0;
1941     RefPtr<ProcessingInstruction> pi = m_doc->createProcessingInstruction(
1942         m_stream.processingInstructionTarget(),
1943         m_stream.processingInstructionData(), exception);
1944     if (exception)
1945         return;
1946
1947     if (!m_currentNode->addChild(pi.get()))
1948         return;
1949     if (m_view && !pi->attached())
1950         pi->attach();
1951
1952     // don't load stylesheets for standalone documents
1953     if (m_doc->frame()) {
1954         m_sawXSLTransform = !m_sawFirstElement && !pi->checkStyleSheet();
1955         if (m_sawXSLTransform)
1956             stopParsing();
1957     }
1958 }
1959
1960 void XMLTokenizer::parseCdata()
1961 {
1962     exitText();
1963
1964     RefPtr<Node> newNode = new CDATASection(m_doc, m_stream.text());
1965     if (!m_currentNode->addChild(newNode.get()))
1966         return;
1967     if (m_view && !newNode->attached())
1968         newNode->attach();
1969 }
1970
1971 void XMLTokenizer::parseComment()
1972 {
1973     exitText();
1974
1975     RefPtr<Node> newNode = new Comment(m_doc, m_stream.text());
1976     m_currentNode->addChild(newNode.get());
1977     if (m_view && !newNode->attached())
1978         newNode->attach();
1979 }
1980
1981 void XMLTokenizer::endDocument()
1982 {
1983 }
1984
1985 bool XMLTokenizer::hasError() const
1986 {
1987     return m_stream.hasError();
1988 }
1989
1990 #if QT_VERSION < 0x040400
1991 static QString parseId(const QString &dtd, int *pos, bool *ok)
1992 {
1993     *ok = true;
1994     int start = *pos + 1;
1995     int end = start;
1996     if (dtd.at(*pos) == QLatin1Char('\''))
1997         while (start < dtd.length() && dtd.at(end) != QLatin1Char('\''))
1998             ++end;
1999     else if (dtd.at(*pos) == QLatin1Char('\"'))
2000         while (start < dtd.length() && dtd.at(end) != QLatin1Char('\"'))
2001             ++end;
2002     else {
2003         *ok = false;
2004         return QString();
2005     }
2006     *pos = end + 1;
2007     return dtd.mid(start, end - start);
2008 }
2009 #endif
2010
2011 void XMLTokenizer::parseDtd()
2012 {
2013 #if QT_VERSION >= 0x040400
2014     QStringRef name = m_stream.dtdName();
2015     QStringRef publicId = m_stream.dtdPublicId();
2016     QStringRef systemId = m_stream.dtdSystemId();
2017 #else
2018     QString dtd = m_stream.text().toString();
2019
2020     int start = dtd.indexOf("<!DOCTYPE ") + 10;
2021     while (start < dtd.length() && dtd.at(start).isSpace())
2022         ++start;
2023     int end = start;
2024     while (start < dtd.length() && !dtd.at(end).isSpace())
2025         ++end;
2026     QString name = dtd.mid(start, end - start);
2027
2028     start = end;
2029     while (start < dtd.length() && dtd.at(start).isSpace())
2030         ++start;
2031     end = start;
2032     while (start < dtd.length() && !dtd.at(end).isSpace())
2033         ++end;
2034     QString id = dtd.mid(start, end - start);
2035     start = end;
2036     while (start < dtd.length() && dtd.at(start).isSpace())
2037         ++start;
2038     QString publicId;
2039     QString systemId;
2040     if (id == QLatin1String("PUBLIC")) {
2041         bool ok;
2042         publicId = parseId(dtd, &start, &ok);
2043         if (!ok) {
2044             handleError(fatal, "Invalid DOCTYPE", lineNumber(), columnNumber());
2045             return;
2046         }
2047         while (start < dtd.length() && dtd.at(start).isSpace())
2048             ++start;
2049         systemId = parseId(dtd, &start, &ok);
2050         if (!ok) {
2051             handleError(fatal, "Invalid DOCTYPE", lineNumber(), columnNumber());
2052             return;
2053         }
2054     } else if (id == QLatin1String("SYSTEM")) {
2055         bool ok;
2056         systemId = parseId(dtd, &start, &ok);
2057         if (!ok) {
2058             handleError(fatal, "Invalid DOCTYPE", lineNumber(), columnNumber());
2059             return;
2060         }
2061     } else if (id == QLatin1String("[") || id == QLatin1String(">")) {
2062     } else {
2063         handleError(fatal, "Invalid DOCTYPE", lineNumber(), columnNumber());
2064         return;
2065     }
2066 #endif    
2067
2068     //qDebug() << dtd << name << publicId << systemId;
2069     if ((publicId == "-//W3C//DTD XHTML 1.0 Transitional//EN")
2070         || (publicId == "-//W3C//DTD XHTML 1.1//EN")
2071         || (publicId == "-//W3C//DTD XHTML 1.0 Strict//EN")
2072         || (publicId == "-//W3C//DTD XHTML 1.0 Frameset//EN")
2073         || (publicId == "-//W3C//DTD XHTML Basic 1.0//EN")
2074         || (publicId == "-//W3C//DTD XHTML 1.1 plus MathML 2.0//EN")
2075         || (publicId == "-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN")
2076         || (publicId == "-//WAPFORUM//DTD XHTML Mobile 1.0//EN")) {
2077         setIsXHTMLDocument(true); // controls if we replace entities or not.
2078     }
2079     if (!m_parsingFragment)
2080         m_doc->setDocType(new DocumentType(m_doc, name, publicId, systemId));
2081     
2082 }
2083 #endif
2084 }
2085
2086