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