Polish the XML error message so that it indicates when a document is the result...
[WebKit-https.git] / WebCore / khtml / xml / xml_tokenizer.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) 2004 Apple Computer, Inc.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public License
18  * along with this library; see the file COPYING.LIB.  If not, write to
19  * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23 #include "xml_tokenizer.h"
24 #include "xml/dom_docimpl.h"
25 #include "xml/dom_textimpl.h"
26 #include "xml/dom_xmlimpl.h"
27 #include "html/html_headimpl.h"
28 #include "misc/htmltags.h"
29 #include "misc/htmlattrs.h"
30 #include "misc/loader.h"
31
32 #include "khtmlview.h"
33 #include "khtml_part.h"
34 #include <kdebug.h>
35 #include <klocale.h>
36
37 #include <libxml/parser.h>
38 #include <libxml/parserInternals.h>
39
40 #include <qptrstack.h>
41
42 using DOM::DocumentImpl;
43 using DOM::DocumentPtr;
44 using DOM::DOMString;
45 using DOM::ElementImpl;
46 using DOM::HTMLScriptElementImpl;
47 using DOM::HTMLTableSectionElementImpl;
48 using DOM::Node;
49 using DOM::NodeImpl;
50 using DOM::ProcessingInstructionImpl;
51 using DOM::TextImpl;
52
53 namespace khtml {
54
55 const int maxErrors = 25;
56
57 // FIXME: Move to the newer libxml API that handles namespaces and dump XMLNamespace, XMLAttributes, and XMLNamespaceStack.
58
59 struct XMLNamespace {
60     QString m_prefix;
61     QString m_uri;
62     XMLNamespace* m_parent;
63     
64     int m_ref;
65     
66     XMLNamespace() :m_parent(0), m_ref(0) {}
67     
68     XMLNamespace(const QString& p, const QString& u, XMLNamespace* parent) 
69         :m_prefix(p),
70          m_uri(u),
71          m_parent(parent), 
72          m_ref(0) 
73     { 
74         if (m_parent) m_parent->ref();
75     }
76     
77     QString uriForPrefix(const QString& prefix) {
78         if (prefix == m_prefix)
79             return m_uri;
80         if (m_parent)
81             return m_parent->uriForPrefix(prefix);
82         return "";
83     }
84     
85     void ref() { m_ref++; }
86     void deref() { if (--m_ref == 0) { if (m_parent) m_parent->deref(); delete this; } }
87 };
88
89 class XMLAttributes {
90 public:
91     XMLAttributes() : _ref(0), _length(0), _names(0), _values(0), _uris(0) { }
92     XMLAttributes(const char **expatStyleAttributes);
93     ~XMLAttributes();
94     
95     XMLAttributes(const XMLAttributes &);
96     XMLAttributes &operator=(const XMLAttributes &);
97     
98     int length() const { return _length; }
99     QString qName(int index) const { return _names[index]; }
100     QString localName(int index) const;
101     QString uri(int index) const { if (!_uris) return QString::null; return _uris[index]; }
102     QString value(int index) const { return _values[index]; }
103
104     QString value(const QString &) const;
105
106     void split(XMLNamespace* ns);
107     
108 private:
109     mutable int *_ref;
110     int _length;
111     QString *_names;
112     QString *_values;
113     QString *_uris;
114 };
115
116 class XMLNamespaceStack
117 {
118 public:
119     ~XMLNamespaceStack();
120     XMLNamespace *pushNamespaces(XMLAttributes& attributes);
121     void popNamespaces();
122 private:
123     QPtrStack<XMLNamespace> m_namespaceStack;
124 };
125
126 class XMLTokenizer : public Tokenizer, public CachedObjectClient
127 {
128 public:
129     XMLTokenizer(DocumentPtr *, KHTMLView * = 0);
130     ~XMLTokenizer();
131
132     enum ErrorType { warning, nonFatal, fatal };
133
134     // from Tokenizer
135     virtual void write(const TokenizerString &str, bool);
136     virtual void finish();
137     virtual void setOnHold(bool onHold);
138     virtual bool isWaitingForScripts();
139
140 #ifdef KHTML_XSLT
141     void setTransformSource(DocumentImpl* doc);
142 #endif
143
144     // from CachedObjectClient
145     virtual void notifyFinished(CachedObject *finishedObj);
146
147     // callbacks from parser SAX
148     void error(ErrorType, const char *message, va_list args);
149     void startElement(const xmlChar *name, const xmlChar **libxmlAttributes);
150     void endElement();
151     void characters(const xmlChar *s, int len);
152     void processingInstruction(const xmlChar *target, const xmlChar *data);
153     void cdataBlock(const xmlChar *s, int len);
154     void comment(const xmlChar *s);
155
156 private:
157     void end();
158
159     int lineNumber() const;
160     int columnNumber() const;
161     void stopParsing();
162
163     void insertErrorMessageBlock();
164
165     void executeScripts();
166     void addScripts(NodeImpl *n);
167
168     XMLNamespace *pushNamespaces(XMLAttributes& attributes) { return m_namespaceStack.pushNamespaces(attributes); }
169     void popNamespaces() { m_namespaceStack.popNamespaces(); }
170
171     bool enterText();
172     void exitText();
173
174     DocumentPtr *m_doc;
175     KHTMLView *m_view;
176
177     QString m_xmlCode;
178
179     xmlParserCtxtPtr m_context;
180     DOM::NodeImpl *m_currentNode;
181     XMLNamespaceStack m_namespaceStack;
182
183     bool m_sawError;
184     bool m_parserStopped;
185     bool m_sawXSLTransform;
186     
187     int m_errorCount;
188     int m_lastErrorLine;
189     int m_lastErrorColumn;
190     DOMString m_errorMessages;
191
192     QPtrList<HTMLScriptElementImpl> m_scripts;
193     QPtrListIterator<HTMLScriptElementImpl> *m_scriptsIt;
194     CachedScript *m_cachedScript;
195 };
196
197 // --------------------------------
198
199 static int globalDescriptor = 0;
200
201 static int matchFunc(const char* uri)
202 {
203     return 1; // Match everything.
204 }
205
206 static void* openFunc(const char * uri) {
207     return &globalDescriptor;
208 }
209
210 static int readFunc(void* context, char* buffer, int len)
211 {
212     // Always just do 0-byte reads
213     return 0;
214 }
215
216 static int writeFunc(void* context, const char* buffer, int len)
217 {
218     // Always just do 0-byte writes
219     return 0;
220 }
221
222 static xmlParserCtxtPtr createQStringParser(xmlSAXHandlerPtr handlers, void *userData, const char* uri = NULL)
223 {
224     static bool didInit = false;
225     if (!didInit) {
226         xmlInitParser();
227         xmlRegisterInputCallbacks(matchFunc, openFunc, readFunc, NULL);
228         xmlRegisterOutputCallbacks(matchFunc, openFunc, writeFunc, NULL);
229         didInit = true;
230     }
231
232     xmlParserCtxtPtr parser = xmlCreatePushParserCtxt(handlers, userData, NULL, 0, uri);
233     const QChar BOM(0xFEFF);
234     const unsigned char BOMHighByte = *reinterpret_cast<const unsigned char *>(&BOM);
235     xmlSwitchEncoding(parser, BOMHighByte == 0xFF ? XML_CHAR_ENCODING_UTF16LE : XML_CHAR_ENCODING_UTF16BE);
236     return parser;
237 }
238
239 static void parseQString(xmlParserCtxtPtr parser, const QString &string)
240 {
241     xmlParseChunk(parser,
242         reinterpret_cast<const char *>(string.unicode()),
243         string.length() * sizeof(QChar), 1);
244 }
245
246 // --------------------------------
247
248 XMLTokenizer::XMLTokenizer(DocumentPtr *_doc, KHTMLView *_view)
249     : m_doc(_doc), m_view(_view),
250       m_context(NULL), m_currentNode(m_doc->document()),
251       m_sawError(false), m_parserStopped(false), m_errorCount(0),
252       m_lastErrorLine(0), m_scriptsIt(0), m_cachedScript(0)
253 {
254     if (m_doc)
255         m_doc->ref();
256 }
257
258 XMLTokenizer::~XMLTokenizer()
259 {
260     if (m_doc)
261         m_doc->deref();
262     delete m_scriptsIt;
263     if (m_cachedScript)
264         m_cachedScript->deref(this);
265 }
266
267 void XMLTokenizer::write(const TokenizerString &s, bool /*appendData*/ )
268 {
269     m_xmlCode += s.toString();
270 }
271
272 void XMLTokenizer::setOnHold(bool onHold)
273 {
274     // Will we need to implement this when we do incremental XML parsing?
275 }
276
277 void XMLTokenizer::startElement(const xmlChar *name, const xmlChar **libxmlAttributes)
278 {
279     if (m_parserStopped)
280         return;
281
282     XMLAttributes atts(reinterpret_cast<const char **>(libxmlAttributes));
283     XMLNamespace *ns = pushNamespaces(atts);
284     atts.split(ns);
285     
286     QString qName = QString::fromUtf8(reinterpret_cast<const char *>(name));
287     QString uri;
288     QString prefix;
289     int colonPos = qName.find(':');
290     if (colonPos != -1) {
291         prefix = qName.left(colonPos);
292     }
293     uri = ns->uriForPrefix(prefix);
294     
295     if (m_currentNode->nodeType() == Node::TEXT_NODE)
296         exitText();
297
298     int exceptioncode = 0;
299     ElementImpl *newElement = m_doc->document()->createElementNS(uri, qName, exceptioncode);
300     if (!newElement)
301         return;
302
303     int i;
304     for (i = 0; i < atts.length(); i++) {
305         // FIXME: qualified name not supported for attributes! The prefix has been lost.
306         DOMString uri(atts.uri(i));
307         DOMString ln(atts.localName(i));
308         DOMString val(atts.value(i));
309         NodeImpl::Id id = m_doc->document()->attrId(uri.implementation(),
310                                                     ln.implementation(),
311                                                     false /* allocate */);
312         newElement->setAttribute(id, val.implementation(), exceptioncode);
313         if (exceptioncode) // exception setting attributes
314             return;
315     }
316
317     // FIXME: This hack ensures implicit table bodies get constructed in XHTML and XML files.
318     // We want to consolidate this with the HTML parser and HTML DOM code at some point.
319     // For now, it's too risky to rip that code up.
320     if (m_currentNode->id() == ID_TABLE &&
321         newElement->id() == ID_TR &&
322         m_currentNode->isHTMLElement() && newElement->isHTMLElement()) {
323         NodeImpl* implicitTBody =
324            new HTMLTableSectionElementImpl( m_doc, ID_TBODY, true /* implicit */ );
325         m_currentNode->addChild(implicitTBody);
326         if (m_view && !implicitTBody->attached())
327             implicitTBody->attach();
328         m_currentNode = implicitTBody;
329     }
330
331     if (m_currentNode->addChild(newElement)) {
332         if (m_view && !newElement->attached())
333             newElement->attach();
334         m_currentNode = newElement;
335         return;
336     }
337     else {
338         delete newElement;
339         return;
340     }
341
342     // ### DOM spec states: "if there is no markup inside an element's content, the text is contained in a
343     // single object implementing the Text interface that is the only child of the element."... do we
344     // need to ensure that empty elements always have an empty text child?
345 }
346
347 void XMLTokenizer::endElement()
348 {
349     if (m_parserStopped) return;
350     
351     popNamespaces();
352
353     if (m_currentNode->nodeType() == Node::TEXT_NODE)
354         exitText();
355     if (m_currentNode->parentNode() != 0) {
356         do {
357             m_currentNode = m_currentNode->parentNode();
358         } while (m_currentNode && m_currentNode->implicitNode());
359     }
360 // ###  else error
361 }
362
363 void XMLTokenizer::characters(const xmlChar *s, int len)
364 {
365     if (m_parserStopped) return;
366     
367     if (m_currentNode->nodeType() == Node::TEXT_NODE ||
368         m_currentNode->nodeType() == Node::CDATA_SECTION_NODE ||
369         enterText()) {
370
371         int exceptioncode = 0;
372         static_cast<TextImpl*>(m_currentNode)->appendData(QString::fromUtf8(reinterpret_cast<const char *>(s), len),
373             exceptioncode);
374     }
375 }
376
377 bool XMLTokenizer::enterText()
378 {
379     NodeImpl *newNode = m_doc->document()->createTextNode("");
380     if (m_currentNode->addChild(newNode)) {
381         m_currentNode = newNode;
382         return true;
383     }
384     else {
385         delete newNode;
386         return false;
387     }
388 }
389
390 void XMLTokenizer::exitText()
391 {
392     if (m_view && m_currentNode && !m_currentNode->attached())
393         m_currentNode->attach();
394     
395     NodeImpl* par = m_currentNode->parentNode();
396     if (par != 0)
397         m_currentNode = par;
398 }
399
400 void XMLTokenizer::error(ErrorType type, const char *message, va_list args)
401 {
402     if (m_parserStopped) {
403         return;
404     }
405
406     if (type == fatal || (m_errorCount < maxErrors && m_lastErrorLine != lineNumber() && m_lastErrorColumn != columnNumber())) {
407
408         QString format;
409         switch (type) {
410             case warning:
411 #if APPLE_CHANGES
412                 format = QString("warning on line %2 at column %3: %1");
413 #else
414                 format = i18n( "warning: %1 in line %2, column %3\n" );
415 #endif
416                 break;
417             case fatal:
418 #if APPLE_CHANGES
419                 // fall through
420 #else
421                 format = i18n( "fatal error: %1 in line %2, column %3\n" );
422                 break;
423 #endif
424             default:
425 #if APPLE_CHANGES
426                 format = QString("error on line %2 at column %3: %1");
427 #else
428                 format = i18n( "error: %1 in line %2, column %3\n" );
429 #endif
430         }
431
432         char *m;
433         vasprintf(&m, message, args);
434         m_errorMessages += format.arg(m).arg(lineNumber()).arg(columnNumber());
435         free(m);
436
437         m_lastErrorLine = lineNumber();
438         m_lastErrorColumn = columnNumber();
439         ++m_errorCount;
440     }
441
442     if (type != warning)
443         m_sawError = true;
444
445     if (type == fatal)
446         stopParsing();
447 }
448
449 void XMLTokenizer::processingInstruction(const xmlChar *target, const xmlChar *data)
450 {
451     if (m_parserStopped) {
452         return;
453     }
454
455     if (m_currentNode->nodeType() == Node::TEXT_NODE)
456         exitText();
457     // ### handle exceptions
458     ProcessingInstructionImpl *pi = m_doc->document()->createProcessingInstruction(
459         QString::fromUtf8(reinterpret_cast<const char *>(target)),
460         QString::fromUtf8(reinterpret_cast<const char *>(data)));
461     m_currentNode->addChild(pi);
462     // don't load stylesheets for standalone documents
463     if (m_doc->document()->part()) {
464         m_sawXSLTransform = !pi->checkStyleSheet();
465         if (m_sawXSLTransform)
466             // Stop the SAX parser.
467             stopParsing();
468     }
469 }
470
471 void XMLTokenizer::cdataBlock(const xmlChar *s, int len)
472 {
473     if (m_parserStopped) {
474         return;
475     }
476
477     if (m_currentNode->nodeType() == Node::TEXT_NODE)
478         exitText();
479
480     NodeImpl *newNode = m_doc->document()->createCDATASection("");
481     if (m_currentNode->addChild(newNode)) {
482         if (m_view && !newNode->attached())
483             newNode->attach();
484         m_currentNode = newNode;
485     }
486     else {
487         delete newNode;
488         return;
489     }
490
491     characters(s, len);
492
493     if (m_currentNode->parentNode() != 0)
494         m_currentNode = m_currentNode->parentNode();
495 }
496
497 void XMLTokenizer::comment(const xmlChar *s)
498 {
499     if (m_parserStopped) return;
500     
501     if (m_currentNode->nodeType() == Node::TEXT_NODE)
502         exitText();
503     // ### handle exceptions
504     m_currentNode->addChild(m_doc->document()->createComment(QString::fromUtf8(reinterpret_cast<const char *>(s))));
505 }
506
507 static void startElementHandler(void *userData, const xmlChar *name, const xmlChar **libxmlAttributes)
508 {
509     static_cast<XMLTokenizer *>(userData)->startElement(name, libxmlAttributes);
510 }
511
512 static void endElementHandler(void *userData, const xmlChar *name)
513 {
514     static_cast<XMLTokenizer *>(userData)->endElement();
515 }
516
517 static void charactersHandler(void *userData, const xmlChar *s, int len)
518 {
519     static_cast<XMLTokenizer *>(userData)->characters(s, len);
520 }
521
522 static void processingInstructionHandler(void *userData, const xmlChar *target, const xmlChar *data)
523 {
524     static_cast<XMLTokenizer *>(userData)->processingInstruction(target, data);
525 }
526
527 static void cdataBlockHandler(void *userData, const xmlChar *s, int len)
528 {
529     static_cast<XMLTokenizer *>(userData)->cdataBlock(s, len);
530 }
531
532 static void commentHandler(void *userData, const xmlChar *comment)
533 {
534     static_cast<XMLTokenizer *>(userData)->comment(comment);
535 }
536
537 static void warningHandler(void *userData, const char *message, ...)
538 {
539     va_list args;
540     va_start(args, message);
541     static_cast<XMLTokenizer *>(userData)->error(XMLTokenizer::warning, message, args);
542     va_end(args);
543 }
544
545 static void fatalErrorHandler(void *userData, const char *message, ...)
546 {
547     va_list args;
548     va_start(args, message);
549     static_cast<XMLTokenizer *>(userData)->error(XMLTokenizer::fatal, message, args);
550     va_end(args);
551 }
552
553 static void normalErrorHandler(void *userData, const char *message, ...)
554 {
555     va_list args;
556     va_start(args, message);
557     static_cast<XMLTokenizer *>(userData)->error(XMLTokenizer::nonFatal, message, args);
558     va_end(args);
559 }
560
561 void XMLTokenizer::finish()
562 {
563     xmlSAXHandler sax;
564     memset(&sax, 0, sizeof(sax));
565     sax.error = normalErrorHandler;
566     sax.fatalError = fatalErrorHandler;
567     sax.characters = charactersHandler;
568     sax.endElement = endElementHandler;
569     sax.processingInstruction = processingInstructionHandler;
570     sax.startElement = startElementHandler;
571     sax.cdataBlock = cdataBlockHandler;
572     sax.comment = commentHandler;
573     sax.warning = warningHandler;
574     m_parserStopped = false;
575     m_sawError = false;
576     m_sawXSLTransform = false;
577     m_context = createQStringParser(&sax, this, m_doc->document()->URL().ascii());
578     parseQString(m_context, m_xmlCode);
579     xmlFreeParserCtxt(m_context);
580     m_context = NULL;
581
582     if (m_sawError) {
583         insertErrorMessageBlock();
584     } else {
585         // Parsing was successful. Now locate all html <script> tags in the document and execute them
586         // one by one.
587         addScripts(m_doc->document());
588         m_scriptsIt = new QPtrListIterator<HTMLScriptElementImpl>(m_scripts);
589         executeScripts();
590     }
591
592     emit finishedParsing();
593 }
594
595 void XMLTokenizer::insertErrorMessageBlock()
596 {
597     // One or more errors occurred during parsing of the code. Display an error block to the user above
598     // the normal content (the DOM tree is created manually and includes line/col info regarding 
599     // where the errors are located)
600
601     // Create elements for display
602     int exceptioncode = 0;
603     DocumentImpl *doc = m_doc->document();
604     NodeImpl* root = doc->documentElement();
605     if (!root) {
606         root = doc->createElementNS(XHTML_NAMESPACE, "html", exceptioncode);
607         NodeImpl* body = doc->createElementNS(XHTML_NAMESPACE, "body", exceptioncode);
608         root->appendChild(body, exceptioncode);
609         doc->appendChild(root, exceptioncode);
610         root = body;
611     }
612
613     ElementImpl* reportDiv = doc->createElementNS(XHTML_NAMESPACE, "div", exceptioncode);
614     reportDiv->setAttribute(ATTR_STYLE, "white-space: pre; border: 2px solid #c77; padding: 0 1em 0 1em; margin: 1em; background-color: #fdd; color: black");
615     ElementImpl* h3 = doc->createElementNS(XHTML_NAMESPACE, "h3", exceptioncode);
616     h3->appendChild(doc->createTextNode("This page contains the following errors:"), exceptioncode);
617     reportDiv->appendChild(h3, exceptioncode);
618     ElementImpl* fixed = doc->createElementNS(XHTML_NAMESPACE, "div", exceptioncode);
619     fixed->setAttribute(ATTR_STYLE, "font-family:monospace;font-size:12px");
620     NodeImpl* textNode = doc->createTextNode(m_errorMessages);
621     fixed->appendChild(textNode, exceptioncode);
622     reportDiv->appendChild(fixed, exceptioncode);
623     h3 = doc->createElementNS(XHTML_NAMESPACE, "h3", exceptioncode);
624     reportDiv->appendChild(h3, exceptioncode);
625     
626     h3->appendChild(doc->createTextNode("Below is a rendering of the page up to the first error."), exceptioncode);
627     if (doc->transformSourceDocument()) {
628         ElementImpl* par = doc->createElementNS(XHTML_NAMESPACE, "p", exceptioncode);
629         reportDiv->appendChild(par, exceptioncode);
630         par->setAttribute(ATTR_STYLE, "white-space: normal");
631         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."), exceptioncode);
632     }
633     root->insertBefore(reportDiv, root->firstChild(), exceptioncode);
634
635     doc->updateRendering();
636 }
637
638 void XMLTokenizer::addScripts(NodeImpl *n)
639 {
640     // Recursively go through the entire document tree, looking for html <script> tags. For each of these
641     // that is found, add it to the m_scripts list from which they will be executed
642
643     if (n->id() == ID_SCRIPT) {
644         m_scripts.append(static_cast<HTMLScriptElementImpl*>(n));
645     }
646
647     NodeImpl *child;
648     for (child = n->firstChild(); child; child = child->nextSibling())
649         addScripts(child);
650 }
651
652 void XMLTokenizer::executeScripts()
653 {
654     // Iterate through all of the html <script> tags in the document. For those that have a src attribute,
655     // start loading the script and return (executeScripts() will be called again once the script is loaded
656     // and continue where it left off). For scripts that don't have a src attribute, execute the code
657     // inside the tag
658     while (m_scriptsIt->current()) {
659         DOMString scriptSrc = m_scriptsIt->current()->getAttribute(ATTR_SRC);
660         QString charset = m_scriptsIt->current()->getAttribute(ATTR_CHARSET).string();
661
662         // don't load external scripts for standalone documents (for now)
663         if (scriptSrc != "" && m_doc->document()->part()) {
664             // we have a src attribute
665             m_cachedScript = m_doc->document()->docLoader()->requestScript(scriptSrc, charset);
666             ++(*m_scriptsIt);
667             m_cachedScript->ref(this); // will call executeScripts() again if already cached
668             return;
669         }
670         else {
671             // no src attribute - execute from contents of tag
672             QString scriptCode = "";
673             NodeImpl *child;
674             for (child = m_scriptsIt->current()->firstChild(); child; child = child->nextSibling()) {
675                 if (child->nodeType() == Node::TEXT_NODE || child->nodeType() == Node::CDATA_SECTION_NODE) {
676                     scriptCode += static_cast<TextImpl*>(child)->data().string();
677                 }
678             }
679             // the script cannot do document.write until we support incremental parsing
680             // ### handle the case where the script deletes the node or redirects to
681             // another page, etc. (also in notifyFinished())
682             // ### the script may add another script node after this one which should be executed
683             if (m_view) {
684                 m_view->part()->executeScript(scriptCode);
685             }
686             ++(*m_scriptsIt);
687         }
688     }
689
690     // All scripts have finished executing, so calculate the style for the document and close
691     // the last element
692     m_doc->document()->updateStyleSelector();
693 }
694
695 void XMLTokenizer::notifyFinished(CachedObject *finishedObj)
696 {
697     // This is called when a script has finished loading that was requested from executeScripts(). We execute
698     // the script, and then call executeScripts() again to continue iterating through the list of scripts in
699     // the document
700     if (finishedObj == m_cachedScript) {
701         DOMString scriptSource = m_cachedScript->script();
702         m_cachedScript->deref(this);
703         m_cachedScript = 0;
704         m_view->part()->executeScript(scriptSource.string());
705         executeScripts();
706     }
707 }
708
709 bool XMLTokenizer::isWaitingForScripts()
710 {
711     return m_cachedScript != 0;
712 }
713
714 #ifdef KHTML_XSLT
715 void XMLTokenizer::setTransformSource(DocumentImpl* doc)
716 {
717     // Time to spin up a new parse and save the xmlDocPtr.
718     // Parse in a single chunk into an xmlDocPtr
719     // FIXME: Hook up error handlers so that a failure to parse the main document results in
720     // good error messages.
721     const QChar BOM(0xFEFF);
722     const unsigned char BOMHighByte = *reinterpret_cast<const unsigned char *>(&BOM);
723     xmlDocPtr sourceDoc = xmlReadMemory(reinterpret_cast<const char *>(m_xmlCode.unicode()),
724                                         m_xmlCode.length() * sizeof(QChar),
725                                         doc->URL().ascii(),
726                                         BOMHighByte == 0xFF ? "UTF-16LE" : "UTF-16BE", 
727                                         XML_PARSE_NOCDATA|XML_PARSE_DTDATTR|XML_PARSE_NOENT);
728     doc->setTransformSource(sourceDoc);
729 }
730 #endif
731
732 Tokenizer *newXMLTokenizer(DocumentPtr *d, KHTMLView *v)
733 {
734     return new XMLTokenizer(d, v);
735 }
736
737 int XMLTokenizer::lineNumber() const
738 {
739     return m_context->input->line;
740 }
741
742 int XMLTokenizer::columnNumber() const
743 {
744     return m_context->input->col;
745 }
746
747 void XMLTokenizer::stopParsing()
748 {
749     xmlStopParser(m_context);
750     m_parserStopped = true;
751 }
752
753 #if 0
754
755 bool XMLHandler::attributeDecl(const QString &/*eName*/, const QString &/*aName*/, const QString &/*type*/,
756                                const QString &/*valueDefault*/, const QString &/*value*/)
757 {
758     // qt's xml parser (as of 2.2.3) does not currently give us values for type, valueDefault and
759     // value. When it does, we can store these somewhere and have default attributes on elements
760     return true;
761 }
762
763 bool XMLHandler::externalEntityDecl(const QString &/*name*/, const QString &/*publicId*/, const QString &/*systemId*/)
764 {
765     // ### insert these too - is there anything special we have to do here?
766     return true;
767 }
768
769 bool XMLHandler::internalEntityDecl(const QString &name, const QString &value)
770 {
771     EntityImpl *e = new EntityImpl(m_doc,name);
772     // ### further parse entities inside the value and add them as separate nodes (or entityreferences)?
773     e->addChild(m_doc->document()->createTextNode(value));
774 // ### FIXME
775 //     if (m_doc->document()->doctype())
776 //         static_cast<GenericRONamedNodeMapImpl*>(m_doc->document()->doctype()->entities())->addNode(e);
777     return true;
778 }
779
780 bool XMLHandler::notationDecl(const QString &name, const QString &publicId, const QString &systemId)
781 {
782 // ### FIXME
783 //     if (m_doc->document()->doctype()) {
784 //         NotationImpl *n = new NotationImpl(m_doc,name,publicId,systemId);
785 //         static_cast<GenericRONamedNodeMapImpl*>(m_doc->document()->doctype()->notations())->addNode(n);
786 //     }
787     return true;
788 }
789
790 #endif
791
792 // --------------------------------
793
794 XMLNamespaceStack::~XMLNamespaceStack()
795 {
796     while (XMLNamespace *ns = m_namespaceStack.pop())
797         ns->deref();
798 }
799
800 void XMLNamespaceStack::popNamespaces()
801 {
802     XMLNamespace *ns = m_namespaceStack.pop();
803     if (ns)
804         ns->deref();
805 }
806
807 XMLNamespace *XMLNamespaceStack::pushNamespaces(XMLAttributes& attrs)
808 {
809     XMLNamespace *ns = m_namespaceStack.current();
810     if (!ns)
811         ns = new XMLNamespace;
812
813     // Search for any xmlns attributes.
814     for (int i = 0; i < attrs.length(); i++) {
815         QString qName = attrs.qName(i);
816         if (qName == "xmlns")
817             ns = new XMLNamespace(QString::null, attrs.value(i), ns);
818         else if (qName.startsWith("xmlns:"))
819             ns = new XMLNamespace(qName.right(qName.length()-6), attrs.value(i), ns);
820     }
821
822     m_namespaceStack.push(ns);
823     ns->ref();
824     return ns;
825 }
826
827 // --------------------------------
828
829 struct AttributeParseState {
830     QMap<QString, QString> attributes;
831     bool gotAttributes;
832 };
833
834 static void attributesStartElementHandler(void *userData, const xmlChar *name, const xmlChar **libxmlAttributes)
835 {
836     if (strcmp(reinterpret_cast<const char *>(name), "attrs") != 0) {
837         return;
838     }
839         
840     AttributeParseState *state = static_cast<AttributeParseState *>(userData);
841     
842     state->gotAttributes = true;
843     
844     XMLAttributes attributes(reinterpret_cast<const char **>(libxmlAttributes));
845     XMLNamespaceStack stack;
846     attributes.split(stack.pushNamespaces(attributes));
847     int length = attributes.length();
848     for (int i = 0; i != length; ++i) {
849         state->attributes.insert(attributes.qName(i), attributes.value(i));
850     }
851 }
852
853 QMap<QString, QString> parseAttributes(const DOMString &string, bool &attrsOK)
854 {
855     AttributeParseState state;
856     state.gotAttributes = false;
857
858     xmlSAXHandler sax;
859     memset(&sax, 0, sizeof(sax));
860     sax.startElement = attributesStartElementHandler;
861     xmlParserCtxtPtr parser = createQStringParser(&sax, &state);
862     parseQString(parser, "<?xml version=\"1.0\"?><attrs " + string.string() + " />");
863     xmlFreeParserCtxt(parser);
864
865     attrsOK = state.gotAttributes;
866     return state.attributes;
867 }
868
869 // --------------------------------
870
871 XMLAttributes::XMLAttributes(const char **saxStyleAttributes)
872     : _ref(0), _uris(0)
873 {
874     int length = 0;
875     if (saxStyleAttributes) {
876         for (const char **p = saxStyleAttributes; *p; p += 2) {
877             ++length;
878         }
879     }
880
881     _length = length;
882     if (!length) {
883         _names = 0;
884         _values = 0;
885         _uris = 0;
886     } else {
887         _names = new QString [length];
888         _values = new QString [length];
889     }
890
891     if (saxStyleAttributes) {
892         int i = 0;
893         for (const char **p = saxStyleAttributes; *p; p += 2) {
894             _names[i] = QString::fromUtf8(p[0]);
895             _values[i] = QString::fromUtf8(p[1]);
896             ++i;
897         }
898     }
899 }
900
901 XMLAttributes::~XMLAttributes()
902 {
903     if (_ref && !--*_ref) {
904         delete _ref;
905         _ref = 0;
906     }
907     if (!_ref) {
908         delete [] _names;
909         delete [] _values;
910         delete [] _uris;
911     }
912 }
913
914 XMLAttributes::XMLAttributes(const XMLAttributes &other)
915     : _ref(other._ref)
916     , _length(other._length)
917     , _names(other._names)
918     , _values(other._values)
919     , _uris(other._uris)
920 {
921     if (!_ref) {
922         _ref = new int (2);
923         other._ref = _ref;
924     } else {
925         ++*_ref;
926     }
927 }
928
929 XMLAttributes &XMLAttributes::operator=(const XMLAttributes &other)
930 {
931     if (_ref && !--*_ref) {
932         delete _ref;
933         _ref = 0;
934     }
935     if (!_ref) {
936         delete [] _names;
937         delete [] _values;
938         delete [] _uris;
939     }
940
941     _ref = other._ref;
942     _length = other._length;
943     _names = other._names;
944     _values = other._values;
945     _uris = other._uris;
946
947     if (!_ref) {
948         _ref = new int (2);
949         other._ref = _ref;
950     } else {
951         ++*_ref;
952     }
953     
954     return *this;
955 }
956
957 QString XMLAttributes::localName(int index) const
958 {
959     int colonPos = _names[index].find(':');
960     if (colonPos != -1)
961         // Peel off the prefix to return the localName.
962         return _names[index].right(_names[index].length() - colonPos - 1);
963     return _names[index];
964 }
965
966 QString XMLAttributes::value(const QString &name) const
967 {
968     for (int i = 0; i != _length; ++i) {
969         if (name == _names[i]) {
970             return _values[i];
971         }
972     }
973     return QString::null;
974 }
975
976 void XMLAttributes::split(XMLNamespace* ns)
977 {
978     for (int i = 0; i < _length; ++i) {
979         int colonPos = _names[i].find(':');
980         if (colonPos != -1) {
981             QString prefix = _names[i].left(colonPos);
982             QString uri;
983             if (prefix == "xmlns") {
984                 // FIXME: The URI is the xmlns namespace? I seem to recall DOM lvl 3 saying something about this.
985             }
986             else
987                 uri = ns->uriForPrefix(prefix);
988             
989             if (!uri.isEmpty()) {
990                 if (!_uris)
991                     _uris = new QString[_length];
992                 _uris[i] = uri;
993             }
994         }
995     }
996 }
997
998 }
999
1000 #include "xml_tokenizer.moc"