Home | History | Annotate | Download | only in dom
      1 /*
      2  * Copyright (C) 2000 Peter Kelly (pmk (at) post.com)
      3  * Copyright (C) 2005, 2006, 2008 Apple Inc. All rights reserved.
      4  * Copyright (C) 2006 Alexey Proskuryakov (ap (at) webkit.org)
      5  * Copyright (C) 2007 Samuel Weinig (sam (at) webkit.org)
      6  * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
      7  * Copyright (C) 2008 Holger Hans Peter Freyther
      8  * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
      9  *
     10  * This library is free software; you can redistribute it and/or
     11  * modify it under the terms of the GNU Library General Public
     12  * License as published by the Free Software Foundation; either
     13  * version 2 of the License, or (at your option) any later version.
     14  *
     15  * This library is distributed in the hope that it will be useful,
     16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     18  * Library General Public License for more details.
     19  *
     20  * You should have received a copy of the GNU Library General Public License
     21  * along with this library; see the file COPYING.LIB.  If not, write to
     22  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     23  * Boston, MA 02110-1301, USA.
     24  */
     25 
     26 #include "config.h"
     27 #include "XMLTokenizer.h"
     28 
     29 #include "CDATASection.h"
     30 #include "CString.h"
     31 #include "CachedScript.h"
     32 #include "Comment.h"
     33 #include "DocLoader.h"
     34 #include "Document.h"
     35 #include "DocumentFragment.h"
     36 #include "DocumentType.h"
     37 #include "Frame.h"
     38 #include "FrameLoader.h"
     39 #include "FrameView.h"
     40 #include "HTMLLinkElement.h"
     41 #include "HTMLStyleElement.h"
     42 #include "HTMLTokenizer.h"
     43 #include "ProcessingInstruction.h"
     44 #include "ResourceError.h"
     45 #include "ResourceHandle.h"
     46 #include "ResourceRequest.h"
     47 #include "ResourceResponse.h"
     48 #include "ScriptController.h"
     49 #include "ScriptElement.h"
     50 #include "ScriptSourceCode.h"
     51 #include "ScriptValue.h"
     52 #include "TextResourceDecoder.h"
     53 #include "TransformSource.h"
     54 #include <QDebug>
     55 #include <wtf/Platform.h>
     56 #include <wtf/StringExtras.h>
     57 #include <wtf/Threading.h>
     58 #include <wtf/Vector.h>
     59 
     60 #if ENABLE(XHTMLMP)
     61 #include "HTMLNames.h"
     62 #include "HTMLScriptElement.h"
     63 #endif
     64 
     65 using namespace std;
     66 
     67 namespace WebCore {
     68 
     69 class EntityResolver : public QXmlStreamEntityResolver {
     70     virtual QString resolveUndeclaredEntity(const QString &name);
     71 };
     72 
     73 QString EntityResolver::resolveUndeclaredEntity(const QString &name)
     74 {
     75     UChar c = decodeNamedEntity(name.toUtf8().constData());
     76     return QString(c);
     77 }
     78 
     79 // --------------------------------
     80 
     81 XMLTokenizer::XMLTokenizer(Document* _doc, FrameView* _view)
     82     : m_doc(_doc)
     83     , m_view(_view)
     84     , m_wroteText(false)
     85     , m_currentNode(_doc)
     86     , m_sawError(false)
     87     , m_sawXSLTransform(false)
     88     , m_sawFirstElement(false)
     89     , m_isXHTMLDocument(false)
     90 #if ENABLE(XHTMLMP)
     91     , m_isXHTMLMPDocument(false)
     92     , m_hasDocTypeDeclaration(false)
     93 #endif
     94     , m_parserPaused(false)
     95     , m_requestingScript(false)
     96     , m_finishCalled(false)
     97     , m_errorCount(0)
     98     , m_lastErrorLine(0)
     99     , m_lastErrorColumn(0)
    100     , m_pendingScript(0)
    101     , m_scriptStartLine(0)
    102     , m_parsingFragment(false)
    103     , m_scriptingPermission(FragmentScriptingAllowed)
    104 {
    105     m_stream.setEntityResolver(new EntityResolver);
    106 }
    107 
    108 XMLTokenizer::XMLTokenizer(DocumentFragment* fragment, Element* parentElement, FragmentScriptingPermission permission)
    109     : m_doc(fragment->document())
    110     , m_view(0)
    111     , m_wroteText(false)
    112     , m_currentNode(fragment)
    113     , m_sawError(false)
    114     , m_sawXSLTransform(false)
    115     , m_sawFirstElement(false)
    116     , m_isXHTMLDocument(false)
    117 #if ENABLE(XHTMLMP)
    118     , m_isXHTMLMPDocument(false)
    119     , m_hasDocTypeDeclaration(false)
    120 #endif
    121     , m_parserPaused(false)
    122     , m_requestingScript(false)
    123     , m_finishCalled(false)
    124     , m_errorCount(0)
    125     , m_lastErrorLine(0)
    126     , m_lastErrorColumn(0)
    127     , m_pendingScript(0)
    128     , m_scriptStartLine(0)
    129     , m_parsingFragment(true)
    130     , m_scriptingPermission(permission)
    131 {
    132     fragment->ref();
    133     if (m_doc)
    134         m_doc->ref();
    135 
    136     // Add namespaces based on the parent node
    137     Vector<Element*> elemStack;
    138     while (parentElement) {
    139         elemStack.append(parentElement);
    140 
    141         Node* n = parentElement->parentNode();
    142         if (!n || !n->isElementNode())
    143             break;
    144         parentElement = static_cast<Element*>(n);
    145     }
    146 
    147     if (elemStack.isEmpty())
    148         return;
    149 
    150     QXmlStreamNamespaceDeclarations namespaces;
    151     for (Element* element = elemStack.last(); !elemStack.isEmpty(); elemStack.removeLast()) {
    152         if (NamedNodeMap* attrs = element->attributes()) {
    153             for (unsigned i = 0; i < attrs->length(); i++) {
    154                 Attribute* attr = attrs->attributeItem(i);
    155                 if (attr->localName() == "xmlns")
    156                     m_defaultNamespaceURI = attr->value();
    157                 else if (attr->prefix() == "xmlns")
    158                     namespaces.append(QXmlStreamNamespaceDeclaration(attr->localName(), attr->value()));
    159             }
    160         }
    161     }
    162     m_stream.addExtraNamespaceDeclarations(namespaces);
    163     m_stream.setEntityResolver(new EntityResolver);
    164 
    165     // If the parent element is not in document tree, there may be no xmlns attribute; just default to the parent's namespace.
    166     if (m_defaultNamespaceURI.isNull() && !parentElement->inDocument())
    167         m_defaultNamespaceURI = parentElement->namespaceURI();
    168 }
    169 
    170 XMLTokenizer::~XMLTokenizer()
    171 {
    172     clearCurrentNodeStack();
    173     if (m_parsingFragment && m_doc)
    174         m_doc->deref();
    175     if (m_pendingScript)
    176         m_pendingScript->removeClient(this);
    177     delete m_stream.entityResolver();
    178 }
    179 
    180 void XMLTokenizer::doWrite(const String& parseString)
    181 {
    182     m_wroteText = true;
    183 
    184     if (m_doc->decoder() && m_doc->decoder()->sawError()) {
    185         // If the decoder saw an error, report it as fatal (stops parsing)
    186         handleError(fatal, "Encoding error", lineNumber(), columnNumber());
    187         return;
    188     }
    189 
    190     QString data(parseString);
    191     if (!data.isEmpty()) {
    192         m_stream.addData(data);
    193         parse();
    194     }
    195 
    196     return;
    197 }
    198 
    199 void XMLTokenizer::initializeParserContext(const char*)
    200 {
    201     m_parserStopped = false;
    202     m_sawError = false;
    203     m_sawXSLTransform = false;
    204     m_sawFirstElement = false;
    205 }
    206 
    207 void XMLTokenizer::doEnd()
    208 {
    209 #if ENABLE(XSLT)
    210     if (m_sawXSLTransform) {
    211         m_doc->setTransformSource(new TransformSource(m_originalSourceForTransform));
    212         m_doc->setParsing(false); // Make the doc think it's done, so it will apply xsl sheets.
    213         m_doc->updateStyleSelector();
    214         m_doc->setParsing(true);
    215         m_parserStopped = true;
    216     }
    217 #endif
    218 
    219     if (m_stream.error() == QXmlStreamReader::PrematureEndOfDocumentError
    220         || (m_wroteText && !m_sawFirstElement && !m_sawXSLTransform && !m_sawError))
    221         handleError(fatal, qPrintable(m_stream.errorString()), lineNumber(), columnNumber());
    222 }
    223 
    224 int XMLTokenizer::lineNumber() const
    225 {
    226     return m_stream.lineNumber();
    227 }
    228 
    229 int XMLTokenizer::columnNumber() const
    230 {
    231     return m_stream.columnNumber();
    232 }
    233 
    234 void XMLTokenizer::stopParsing()
    235 {
    236     Tokenizer::stopParsing();
    237 }
    238 
    239 void XMLTokenizer::resumeParsing()
    240 {
    241     ASSERT(m_parserPaused);
    242 
    243     m_parserPaused = false;
    244 
    245     // First, execute any pending callbacks
    246     parse();
    247     if (m_parserPaused)
    248         return;
    249 
    250     // Then, write any pending data
    251     SegmentedString rest = m_pendingSrc;
    252     m_pendingSrc.clear();
    253     write(rest, false);
    254 
    255     // Finally, if finish() has been called and write() didn't result
    256     // in any further callbacks being queued, call end()
    257     if (m_finishCalled && !m_parserPaused && !m_pendingScript)
    258         end();
    259 }
    260 
    261 bool parseXMLDocumentFragment(const String& chunk, DocumentFragment* fragment, Element* parent, FragmentScriptingPermission scriptingPermission)
    262 {
    263     if (!chunk.length())
    264         return true;
    265 
    266     XMLTokenizer tokenizer(fragment, parent, scriptingPermission);
    267 
    268     tokenizer.write(String("<qxmlstreamdummyelement>"), false);
    269     tokenizer.write(chunk, false);
    270     tokenizer.write(String("</qxmlstreamdummyelement>"), false);
    271     tokenizer.finish();
    272     return !tokenizer.hasError();
    273 }
    274 
    275 // --------------------------------
    276 
    277 struct AttributeParseState {
    278     HashMap<String, String> attributes;
    279     bool gotAttributes;
    280 };
    281 
    282 static void attributesStartElementNsHandler(AttributeParseState* state, const QXmlStreamAttributes& attrs)
    283 {
    284     if (attrs.count() <= 0)
    285         return;
    286 
    287     state->gotAttributes = true;
    288 
    289     for (int i = 0; i < attrs.count(); i++) {
    290         const QXmlStreamAttribute& attr = attrs[i];
    291         String attrLocalName = attr.name();
    292         String attrValue     = attr.value();
    293         String attrURI       = attr.namespaceUri();
    294         String attrQName     = attr.qualifiedName();
    295         state->attributes.set(attrQName, attrValue);
    296     }
    297 }
    298 
    299 HashMap<String, String> parseAttributes(const String& string, bool& attrsOK)
    300 {
    301     AttributeParseState state;
    302     state.gotAttributes = false;
    303 
    304     QXmlStreamReader stream;
    305     QString dummy = QString(QLatin1String("<?xml version=\"1.0\"?><attrs %1 />")).arg(string);
    306     stream.addData(dummy);
    307     while (!stream.atEnd()) {
    308         stream.readNext();
    309         if (stream.isStartElement()) {
    310             attributesStartElementNsHandler(&state, stream.attributes());
    311         }
    312     }
    313     attrsOK = state.gotAttributes;
    314     return state.attributes;
    315 }
    316 
    317 static inline String prefixFromQName(const QString& qName)
    318 {
    319     const int offset = qName.indexOf(QLatin1Char(':'));
    320     if (offset <= 0)
    321         return String();
    322     else
    323         return qName.left(offset);
    324 }
    325 
    326 static inline void handleElementNamespaces(Element* newElement, const QXmlStreamNamespaceDeclarations &ns,
    327                                            ExceptionCode& ec, FragmentScriptingPermission scriptingPermission)
    328 {
    329     for (int i = 0; i < ns.count(); ++i) {
    330         const QXmlStreamNamespaceDeclaration &decl = ns[i];
    331         String namespaceURI = decl.namespaceUri();
    332         String namespaceQName = decl.prefix().isEmpty() ? String("xmlns") : String("xmlns:") + decl.prefix();
    333         newElement->setAttributeNS("http://www.w3.org/2000/xmlns/", namespaceQName, namespaceURI, ec, scriptingPermission);
    334         if (ec) // exception setting attributes
    335             return;
    336     }
    337 }
    338 
    339 static inline void handleElementAttributes(Element* newElement, const QXmlStreamAttributes &attrs, ExceptionCode& ec,
    340                                            FragmentScriptingPermission scriptingPermission)
    341 {
    342     for (int i = 0; i < attrs.count(); ++i) {
    343         const QXmlStreamAttribute &attr = attrs[i];
    344         String attrLocalName = attr.name();
    345         String attrValue     = attr.value();
    346         String attrURI       = attr.namespaceUri().isEmpty() ? String() : String(attr.namespaceUri());
    347         String attrQName     = attr.qualifiedName();
    348         newElement->setAttributeNS(attrURI, attrQName, attrValue, ec, scriptingPermission);
    349         if (ec) // exception setting attributes
    350             return;
    351     }
    352 }
    353 
    354 void XMLTokenizer::parse()
    355 {
    356     while (!m_parserStopped && !m_parserPaused && !m_stream.atEnd()) {
    357         m_stream.readNext();
    358         switch (m_stream.tokenType()) {
    359         case QXmlStreamReader::StartDocument: {
    360             startDocument();
    361         }
    362             break;
    363         case QXmlStreamReader::EndDocument: {
    364             endDocument();
    365         }
    366             break;
    367         case QXmlStreamReader::StartElement: {
    368 #if ENABLE(XHTMLMP)
    369             if (m_doc->isXHTMLMPDocument() && !m_hasDocTypeDeclaration) {
    370                 handleError(fatal, "DOCTYPE declaration lost.", lineNumber(), columnNumber());
    371                 break;
    372             }
    373 #endif
    374             parseStartElement();
    375         }
    376             break;
    377         case QXmlStreamReader::EndElement: {
    378             parseEndElement();
    379         }
    380             break;
    381         case QXmlStreamReader::Characters: {
    382             if (m_stream.isCDATA()) {
    383                 //cdata
    384                 parseCdata();
    385             } else {
    386                 //characters
    387                 parseCharacters();
    388             }
    389         }
    390             break;
    391         case QXmlStreamReader::Comment: {
    392             parseComment();
    393         }
    394             break;
    395         case QXmlStreamReader::DTD: {
    396             //qDebug()<<"------------- DTD";
    397             parseDtd();
    398 #if ENABLE(XHTMLMP)
    399             m_hasDocTypeDeclaration = true;
    400 #endif
    401         }
    402             break;
    403         case QXmlStreamReader::EntityReference: {
    404             //qDebug()<<"---------- ENTITY = "<<m_stream.name().toString()
    405             //        <<", t = "<<m_stream.text().toString();
    406             if (isXHTMLDocument()
    407 #if ENABLE(XHTMLMP)
    408                 || isXHTMLMPDocument()
    409 #endif
    410 #if ENABLE(WML)
    411                 || isWMLDocument()
    412 #endif
    413                ) {
    414                 QString entity = m_stream.name().toString();
    415                 UChar c = decodeNamedEntity(entity.toUtf8().constData());
    416                 if (m_currentNode->isTextNode() || enterText()) {
    417                     ExceptionCode ec = 0;
    418                     String str(&c, 1);
    419                     //qDebug()<<" ------- adding entity "<<str;
    420                     static_cast<Text*>(m_currentNode)->appendData(str, ec);
    421                 }
    422             }
    423         }
    424             break;
    425         case QXmlStreamReader::ProcessingInstruction: {
    426             parseProcessingInstruction();
    427         }
    428             break;
    429         default: {
    430             if (m_stream.error() != QXmlStreamReader::PrematureEndOfDocumentError) {
    431                 ErrorType type = (m_stream.error() == QXmlStreamReader::NotWellFormedError) ?
    432                                  fatal : warning;
    433                 handleError(type, qPrintable(m_stream.errorString()), lineNumber(),
    434                             columnNumber());
    435             }
    436         }
    437             break;
    438         }
    439     }
    440 }
    441 
    442 void XMLTokenizer::startDocument()
    443 {
    444     initializeParserContext();
    445     ExceptionCode ec = 0;
    446 
    447     if (!m_parsingFragment) {
    448         m_doc->setXMLStandalone(m_stream.isStandaloneDocument(), ec);
    449 
    450         QStringRef version = m_stream.documentVersion();
    451         if (!version.isEmpty())
    452             m_doc->setXMLVersion(version, ec);
    453         QStringRef encoding = m_stream.documentEncoding();
    454         if (!encoding.isEmpty())
    455             m_doc->setXMLEncoding(encoding);
    456     }
    457 }
    458 
    459 void XMLTokenizer::parseStartElement()
    460 {
    461     if (!m_sawFirstElement && m_parsingFragment) {
    462         // skip dummy element for fragments
    463         m_sawFirstElement = true;
    464         return;
    465     }
    466 
    467     exitText();
    468 
    469     String localName = m_stream.name();
    470     String uri       = m_stream.namespaceUri();
    471     String prefix    = prefixFromQName(m_stream.qualifiedName().toString());
    472 
    473     if (m_parsingFragment && uri.isNull()) {
    474         Q_ASSERT(prefix.isNull());
    475         uri = m_defaultNamespaceURI;
    476     }
    477 
    478     QualifiedName qName(prefix, localName, uri);
    479     RefPtr<Element> newElement = m_doc->createElement(qName, true);
    480     if (!newElement) {
    481         stopParsing();
    482         return;
    483     }
    484 
    485 #if ENABLE(XHTMLMP)
    486     if (!m_sawFirstElement && isXHTMLMPDocument()) {
    487         // As per 7.1 section of OMA-WAP-XHTMLMP-V1_1-20061020-A.pdf,
    488         // we should make sure that the root element MUST be 'html' and
    489         // ensure the name of the default namespace on the root elment 'html'
    490         // MUST be 'http://www.w3.org/1999/xhtml'
    491         if (localName != HTMLNames::htmlTag.localName()) {
    492             handleError(fatal, "XHTMLMP document expects 'html' as root element.", lineNumber(), columnNumber());
    493             return;
    494         }
    495 
    496         if (uri.isNull()) {
    497             m_defaultNamespaceURI = HTMLNames::xhtmlNamespaceURI;
    498             uri = m_defaultNamespaceURI;
    499             m_stream.addExtraNamespaceDeclaration(QXmlStreamNamespaceDeclaration(prefix, HTMLNames::xhtmlNamespaceURI));
    500         }
    501     }
    502 #endif
    503 
    504     bool isFirstElement = !m_sawFirstElement;
    505     m_sawFirstElement = true;
    506 
    507     ExceptionCode ec = 0;
    508     handleElementNamespaces(newElement.get(), m_stream.namespaceDeclarations(), ec, m_scriptingPermission);
    509     if (ec) {
    510         stopParsing();
    511         return;
    512     }
    513 
    514     handleElementAttributes(newElement.get(), m_stream.attributes(), ec, m_scriptingPermission);
    515     if (ec) {
    516         stopParsing();
    517         return;
    518     }
    519 
    520     ScriptElement* scriptElement = toScriptElement(newElement.get());
    521     if (scriptElement)
    522         m_scriptStartLine = lineNumber();
    523 
    524     if (!m_currentNode->addChild(newElement.get())) {
    525         stopParsing();
    526         return;
    527     }
    528 
    529     pushCurrentNode(newElement.get());
    530     if (m_view && !newElement->attached())
    531         newElement->attach();
    532 
    533     if (isFirstElement && m_doc->frame())
    534         m_doc->frame()->loader()->dispatchDocumentElementAvailable();
    535 }
    536 
    537 void XMLTokenizer::parseEndElement()
    538 {
    539     exitText();
    540 
    541     Node* n = m_currentNode;
    542     n->finishParsingChildren();
    543 
    544     if (m_scriptingPermission == FragmentScriptingNotAllowed && n->isElementNode() && toScriptElement(static_cast<Element*>(n))) {
    545         popCurrentNode();
    546         ExceptionCode ec;
    547         n->remove(ec);
    548         return;
    549     }
    550 
    551     if (!n->isElementNode() || !m_view) {
    552         if (!m_currentNodeStack.isEmpty())
    553             popCurrentNode();
    554         return;
    555     }
    556 
    557     Element* element = static_cast<Element*>(n);
    558 
    559     // The element's parent may have already been removed from document.
    560     // Parsing continues in this case, but scripts aren't executed.
    561     if (!element->inDocument()) {
    562         popCurrentNode();
    563         return;
    564     }
    565 
    566     ScriptElement* scriptElement = toScriptElement(element);
    567     if (!scriptElement) {
    568         popCurrentNode();
    569         return;
    570     }
    571 
    572     // don't load external scripts for standalone documents (for now)
    573     ASSERT(!m_pendingScript);
    574     m_requestingScript = true;
    575 
    576 #if ENABLE(XHTMLMP)
    577     if (!scriptElement->shouldExecuteAsJavaScript())
    578         m_doc->setShouldProcessNoscriptElement(true);
    579     else
    580 #endif
    581     {
    582         String scriptHref = scriptElement->sourceAttributeValue();
    583         if (!scriptHref.isEmpty()) {
    584             // we have a src attribute
    585             String scriptCharset = scriptElement->scriptCharset();
    586             if (element->dispatchBeforeLoadEvent(scriptHref) &&
    587                 (m_pendingScript = m_doc->docLoader()->requestScript(scriptHref, scriptCharset))) {
    588                 m_scriptElement = element;
    589                 m_pendingScript->addClient(this);
    590 
    591                 // m_pendingScript will be 0 if script was already loaded and ref() executed it
    592                 if (m_pendingScript)
    593                     pauseParsing();
    594             } else
    595                 m_scriptElement = 0;
    596         } else
    597             m_view->frame()->script()->executeScript(ScriptSourceCode(scriptElement->scriptContent(), m_doc->url(), m_scriptStartLine));
    598     }
    599     m_requestingScript = false;
    600     popCurrentNode();
    601 }
    602 
    603 void XMLTokenizer::parseCharacters()
    604 {
    605     if (m_currentNode->isTextNode() || enterText()) {
    606         ExceptionCode ec = 0;
    607         static_cast<Text*>(m_currentNode)->appendData(m_stream.text(), ec);
    608     }
    609 }
    610 
    611 void XMLTokenizer::parseProcessingInstruction()
    612 {
    613     exitText();
    614 
    615     // ### handle exceptions
    616     int exception = 0;
    617     RefPtr<ProcessingInstruction> pi = m_doc->createProcessingInstruction(
    618         m_stream.processingInstructionTarget(),
    619         m_stream.processingInstructionData(), exception);
    620     if (exception)
    621         return;
    622 
    623     pi->setCreatedByParser(true);
    624 
    625     if (!m_currentNode->addChild(pi.get()))
    626         return;
    627     if (m_view && !pi->attached())
    628         pi->attach();
    629 
    630     pi->finishParsingChildren();
    631 
    632 #if ENABLE(XSLT)
    633     m_sawXSLTransform = !m_sawFirstElement && pi->isXSL();
    634     if (m_sawXSLTransform && !m_doc->transformSourceDocument())
    635         stopParsing();
    636 #endif
    637 }
    638 
    639 void XMLTokenizer::parseCdata()
    640 {
    641     exitText();
    642 
    643     RefPtr<Node> newNode = CDATASection::create(m_doc, m_stream.text());
    644     if (!m_currentNode->addChild(newNode.get()))
    645         return;
    646     if (m_view && !newNode->attached())
    647         newNode->attach();
    648 }
    649 
    650 void XMLTokenizer::parseComment()
    651 {
    652     exitText();
    653 
    654     RefPtr<Node> newNode = Comment::create(m_doc, m_stream.text());
    655     m_currentNode->addChild(newNode.get());
    656     if (m_view && !newNode->attached())
    657         newNode->attach();
    658 }
    659 
    660 void XMLTokenizer::endDocument()
    661 {
    662 #if ENABLE(XHTMLMP)
    663     m_hasDocTypeDeclaration = false;
    664 #endif
    665 }
    666 
    667 bool XMLTokenizer::hasError() const
    668 {
    669     return m_stream.hasError();
    670 }
    671 
    672 void XMLTokenizer::parseDtd()
    673 {
    674     QStringRef name = m_stream.dtdName();
    675     QStringRef publicId = m_stream.dtdPublicId();
    676     QStringRef systemId = m_stream.dtdSystemId();
    677 
    678     //qDebug() << dtd << name << publicId << systemId;
    679     if ((publicId == QLatin1String("-//W3C//DTD XHTML 1.0 Transitional//EN"))
    680         || (publicId == QLatin1String("-//W3C//DTD XHTML 1.1//EN"))
    681         || (publicId == QLatin1String("-//W3C//DTD XHTML 1.0 Strict//EN"))
    682         || (publicId == QLatin1String("-//W3C//DTD XHTML 1.0 Frameset//EN"))
    683         || (publicId == QLatin1String("-//W3C//DTD XHTML Basic 1.0//EN"))
    684         || (publicId == QLatin1String("-//W3C//DTD XHTML 1.1 plus MathML 2.0//EN"))
    685         || (publicId == QLatin1String("-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN"))
    686 #if !ENABLE(XHTMLMP)
    687         || (publicId == QLatin1String("-//WAPFORUM//DTD XHTML Mobile 1.0//EN"))
    688 #endif
    689        )
    690         setIsXHTMLDocument(true); // controls if we replace entities or not.
    691 #if ENABLE(XHTMLMP)
    692     else if ((publicId == QLatin1String("-//WAPFORUM//DTD XHTML Mobile 1.1//EN"))
    693              || (publicId == QLatin1String("-//WAPFORUM//DTD XHTML Mobile 1.0//EN"))) {
    694         if (AtomicString(name) != HTMLNames::htmlTag.localName()) {
    695             handleError(fatal, "Invalid DOCTYPE declaration, expected 'html' as root element.", lineNumber(), columnNumber());
    696             return;
    697         }
    698 
    699         if (m_doc->isXHTMLMPDocument()) // check if the MIME type is correct with this method
    700             setIsXHTMLMPDocument(true);
    701         else
    702             setIsXHTMLDocument(true);
    703     }
    704 #endif
    705 #if ENABLE(WML)
    706     else if (m_doc->isWMLDocument()
    707              && publicId != QLatin1String("-//WAPFORUM//DTD WML 1.3//EN")
    708              && publicId != QLatin1String("-//WAPFORUM//DTD WML 1.2//EN")
    709              && publicId != QLatin1String("-//WAPFORUM//DTD WML 1.1//EN")
    710              && publicId != QLatin1String("-//WAPFORUM//DTD WML 1.0//EN"))
    711         handleError(fatal, "Invalid DTD Public ID", lineNumber(), columnNumber());
    712 #endif
    713     if (!m_parsingFragment)
    714         m_doc->addChild(DocumentType::create(m_doc, name, publicId, systemId));
    715 
    716 }
    717 }
    718 
    719