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