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