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