HTMLTreeBuilder.cpp revision cad810f21b803229eb11403f9209855525a25d57
1/* 2 * Copyright (C) 2010 Google, Inc. All Rights Reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE INC. OR 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26#include "config.h" 27#include "HTMLTreeBuilder.h" 28 29#include "CharacterNames.h" 30#include "Comment.h" 31#include "DocumentFragment.h" 32#include "DocumentType.h" 33#include "Frame.h" 34#include "HTMLDocument.h" 35#include "HTMLDocumentParser.h" 36#include "HTMLElementFactory.h" 37#include "HTMLFormElement.h" 38#include "HTMLHtmlElement.h" 39#include "HTMLNames.h" 40#include "HTMLParserIdioms.h" 41#include "HTMLScriptElement.h" 42#include "HTMLToken.h" 43#include "HTMLTokenizer.h" 44#include "LocalizedStrings.h" 45#include "MathMLNames.h" 46#include "NotImplemented.h" 47#include "SVGNames.h" 48#include "ScriptController.h" 49#include "Text.h" 50#include "XLinkNames.h" 51#include "XMLNSNames.h" 52#include "XMLNames.h" 53 54namespace WebCore { 55 56using namespace HTMLNames; 57 58static const int uninitializedLineNumberValue = -1; 59 60static TextPosition1 uninitializedPositionValue1() 61{ 62 return TextPosition1(WTF::OneBasedNumber::fromOneBasedInt(-1), WTF::OneBasedNumber::base()); 63} 64 65namespace { 66 67inline bool isHTMLSpaceOrReplacementCharacter(UChar character) 68{ 69 return isHTMLSpace(character) || character == replacementCharacter; 70} 71 72inline bool isAllWhitespace(const String& string) 73{ 74 return string.isAllSpecialCharacters<isHTMLSpace>(); 75} 76 77inline bool isAllWhitespaceOrReplacementCharacters(const String& string) 78{ 79 return string.isAllSpecialCharacters<isHTMLSpaceOrReplacementCharacter>(); 80} 81 82bool isNumberedHeaderTag(const AtomicString& tagName) 83{ 84 return tagName == h1Tag 85 || tagName == h2Tag 86 || tagName == h3Tag 87 || tagName == h4Tag 88 || tagName == h5Tag 89 || tagName == h6Tag; 90} 91 92bool isCaptionColOrColgroupTag(const AtomicString& tagName) 93{ 94 return tagName == captionTag 95 || tagName == colTag 96 || tagName == colgroupTag; 97} 98 99bool isTableCellContextTag(const AtomicString& tagName) 100{ 101 return tagName == thTag || tagName == tdTag; 102} 103 104bool isTableBodyContextTag(const AtomicString& tagName) 105{ 106 return tagName == tbodyTag 107 || tagName == tfootTag 108 || tagName == theadTag; 109} 110 111// http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#special 112bool isSpecialNode(Node* node) 113{ 114 if (node->hasTagName(MathMLNames::miTag) 115 || node->hasTagName(MathMLNames::moTag) 116 || node->hasTagName(MathMLNames::mnTag) 117 || node->hasTagName(MathMLNames::msTag) 118 || node->hasTagName(MathMLNames::mtextTag) 119 || node->hasTagName(MathMLNames::annotation_xmlTag) 120 || node->hasTagName(SVGNames::foreignObjectTag) 121 || node->hasTagName(SVGNames::descTag) 122 || node->hasTagName(SVGNames::titleTag)) 123 return true; 124 if (node->namespaceURI() != xhtmlNamespaceURI) 125 return false; 126 const AtomicString& tagName = node->localName(); 127 return tagName == addressTag 128 || tagName == appletTag 129 || tagName == areaTag 130 || tagName == articleTag 131 || tagName == asideTag 132 || tagName == baseTag 133 || tagName == basefontTag 134 || tagName == bgsoundTag 135 || tagName == blockquoteTag 136 || tagName == bodyTag 137 || tagName == brTag 138 || tagName == buttonTag 139 || tagName == captionTag 140 || tagName == centerTag 141 || tagName == colTag 142 || tagName == colgroupTag 143 || tagName == commandTag 144 || tagName == ddTag 145 || tagName == detailsTag 146 || tagName == dirTag 147 || tagName == divTag 148 || tagName == dlTag 149 || tagName == dtTag 150 || tagName == embedTag 151 || tagName == fieldsetTag 152 || tagName == figcaptionTag 153 || tagName == figureTag 154 || tagName == footerTag 155 || tagName == formTag 156 || tagName == frameTag 157 || tagName == framesetTag 158 || isNumberedHeaderTag(tagName) 159 || tagName == headTag 160 || tagName == headerTag 161 || tagName == hgroupTag 162 || tagName == hrTag 163 || tagName == htmlTag 164 || tagName == iframeTag 165 || tagName == imgTag 166 || tagName == inputTag 167 || tagName == isindexTag 168 || tagName == liTag 169 || tagName == linkTag 170 || tagName == listingTag 171 || tagName == marqueeTag 172 || tagName == menuTag 173 || tagName == metaTag 174 || tagName == navTag 175 || tagName == noembedTag 176 || tagName == noframesTag 177 || tagName == noscriptTag 178 || tagName == objectTag 179 || tagName == olTag 180 || tagName == pTag 181 || tagName == paramTag 182 || tagName == plaintextTag 183 || tagName == preTag 184 || tagName == scriptTag 185 || tagName == sectionTag 186 || tagName == selectTag 187 || tagName == styleTag 188 || tagName == summaryTag 189 || tagName == tableTag 190 || isTableBodyContextTag(tagName) 191 || tagName == tdTag 192 || tagName == textareaTag 193 || tagName == thTag 194 || tagName == titleTag 195 || tagName == trTag 196 || tagName == ulTag 197 || tagName == wbrTag 198 || tagName == xmpTag; 199} 200 201bool isNonAnchorNonNobrFormattingTag(const AtomicString& tagName) 202{ 203 return tagName == bTag 204 || tagName == bigTag 205 || tagName == codeTag 206 || tagName == emTag 207 || tagName == fontTag 208 || tagName == iTag 209 || tagName == sTag 210 || tagName == smallTag 211 || tagName == strikeTag 212 || tagName == strongTag 213 || tagName == ttTag 214 || tagName == uTag; 215} 216 217bool isNonAnchorFormattingTag(const AtomicString& tagName) 218{ 219 return tagName == nobrTag 220 || isNonAnchorNonNobrFormattingTag(tagName); 221} 222 223// http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#formatting 224bool isFormattingTag(const AtomicString& tagName) 225{ 226 return tagName == aTag || isNonAnchorFormattingTag(tagName); 227} 228 229HTMLFormElement* closestFormAncestor(Element* element) 230{ 231 while (element) { 232 if (element->hasTagName(formTag)) 233 return static_cast<HTMLFormElement*>(element); 234 ContainerNode* parent = element->parentNode(); 235 if (!parent || !parent->isElementNode()) 236 return 0; 237 element = static_cast<Element*>(parent); 238 } 239 return 0; 240} 241 242} // namespace 243 244class HTMLTreeBuilder::ExternalCharacterTokenBuffer : public Noncopyable { 245public: 246 explicit ExternalCharacterTokenBuffer(AtomicHTMLToken& token) 247 : m_current(token.characters().data()) 248 , m_end(m_current + token.characters().size()) 249 { 250 ASSERT(!isEmpty()); 251 } 252 253 explicit ExternalCharacterTokenBuffer(const String& string) 254 : m_current(string.characters()) 255 , m_end(m_current + string.length()) 256 { 257 ASSERT(!isEmpty()); 258 } 259 260 ~ExternalCharacterTokenBuffer() 261 { 262 ASSERT(isEmpty()); 263 } 264 265 bool isEmpty() const { return m_current == m_end; } 266 267 void skipLeadingWhitespace() 268 { 269 skipLeading<isHTMLSpace>(); 270 } 271 272 String takeLeadingWhitespace() 273 { 274 return takeLeading<isHTMLSpace>(); 275 } 276 277 String takeLeadingNonWhitespace() 278 { 279 return takeLeading<isNotHTMLSpace>(); 280 } 281 282 String takeRemaining() 283 { 284 ASSERT(!isEmpty()); 285 const UChar* start = m_current; 286 m_current = m_end; 287 return String(start, m_current - start); 288 } 289 290 void giveRemainingTo(Vector<UChar>& recipient) 291 { 292 recipient.append(m_current, m_end - m_current); 293 m_current = m_end; 294 } 295 296 String takeRemainingWhitespace() 297 { 298 ASSERT(!isEmpty()); 299 Vector<UChar> whitespace; 300 do { 301 UChar cc = *m_current++; 302 if (isHTMLSpace(cc)) 303 whitespace.append(cc); 304 } while (m_current < m_end); 305 // Returning the null string when there aren't any whitespace 306 // characters is slightly cleaner semantically because we don't want 307 // to insert a text node (as opposed to inserting an empty text node). 308 if (whitespace.isEmpty()) 309 return String(); 310 return String::adopt(whitespace); 311 } 312 313private: 314 template<bool characterPredicate(UChar)> 315 void skipLeading() 316 { 317 ASSERT(!isEmpty()); 318 while (characterPredicate(*m_current)) { 319 if (++m_current == m_end) 320 return; 321 } 322 } 323 324 template<bool characterPredicate(UChar)> 325 String takeLeading() 326 { 327 ASSERT(!isEmpty()); 328 const UChar* start = m_current; 329 skipLeading<characterPredicate>(); 330 if (start == m_current) 331 return String(); 332 return String(start, m_current - start); 333 } 334 335 const UChar* m_current; 336 const UChar* m_end; 337}; 338 339 340HTMLTreeBuilder::HTMLTreeBuilder(HTMLDocumentParser* parser, HTMLDocument* document, bool reportErrors, bool usePreHTML5ParserQuirks) 341 : m_framesetOk(true) 342 , m_document(document) 343 , m_tree(document, FragmentScriptingAllowed, false) 344 , m_reportErrors(reportErrors) 345 , m_isPaused(false) 346 , m_insertionMode(InitialMode) 347 , m_originalInsertionMode(InitialMode) 348 , m_parser(parser) 349 , m_scriptToProcessStartPosition(uninitializedPositionValue1()) 350 , m_lastScriptElementStartPosition(TextPosition0::belowRangePosition()) 351 , m_usePreHTML5ParserQuirks(usePreHTML5ParserQuirks) 352 , m_hasPendingForeignInsertionModeSteps(false) 353{ 354} 355 356// FIXME: Member variables should be grouped into self-initializing structs to 357// minimize code duplication between these constructors. 358HTMLTreeBuilder::HTMLTreeBuilder(HTMLDocumentParser* parser, DocumentFragment* fragment, Element* contextElement, FragmentScriptingPermission scriptingPermission, bool usePreHTML5ParserQuirks) 359 : m_framesetOk(true) 360 , m_fragmentContext(fragment, contextElement, scriptingPermission) 361 , m_document(m_fragmentContext.document()) 362 , m_tree(m_document, scriptingPermission, true) 363 , m_reportErrors(false) // FIXME: Why not report errors in fragments? 364 , m_isPaused(false) 365 , m_insertionMode(InitialMode) 366 , m_originalInsertionMode(InitialMode) 367 , m_parser(parser) 368 , m_scriptToProcessStartPosition(uninitializedPositionValue1()) 369 , m_lastScriptElementStartPosition(TextPosition0::belowRangePosition()) 370 , m_usePreHTML5ParserQuirks(usePreHTML5ParserQuirks) 371 , m_hasPendingForeignInsertionModeSteps(false) 372{ 373 if (contextElement) { 374 // Steps 4.2-4.6 of the HTML5 Fragment Case parsing algorithm: 375 // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-end.html#fragment-case 376 m_document->setCompatibilityMode(contextElement->document()->compatibilityMode()); 377 processFakeStartTag(htmlTag); 378 resetInsertionModeAppropriately(); 379 m_tree.setForm(closestFormAncestor(contextElement)); 380 } 381} 382 383HTMLTreeBuilder::~HTMLTreeBuilder() 384{ 385} 386 387void HTMLTreeBuilder::detach() 388{ 389 // This call makes little sense in fragment mode, but for consistency 390 // DocumentParser expects detach() to always be called before it's destroyed. 391 m_document = 0; 392 // HTMLConstructionSite might be on the callstack when detach() is called 393 // otherwise we'd just call m_tree.clear() here instead. 394 m_tree.detach(); 395} 396 397HTMLTreeBuilder::FragmentParsingContext::FragmentParsingContext() 398 : m_fragment(0) 399 , m_contextElement(0) 400 , m_scriptingPermission(FragmentScriptingAllowed) 401{ 402} 403 404HTMLTreeBuilder::FragmentParsingContext::FragmentParsingContext(DocumentFragment* fragment, Element* contextElement, FragmentScriptingPermission scriptingPermission) 405 : m_dummyDocumentForFragmentParsing(HTMLDocument::create(0, KURL(), fragment->document()->baseURI())) 406 , m_fragment(fragment) 407 , m_contextElement(contextElement) 408 , m_scriptingPermission(scriptingPermission) 409{ 410 m_dummyDocumentForFragmentParsing->setCompatibilityMode(fragment->document()->compatibilityMode()); 411} 412 413Document* HTMLTreeBuilder::FragmentParsingContext::document() const 414{ 415 ASSERT(m_fragment); 416 return m_dummyDocumentForFragmentParsing.get(); 417} 418 419void HTMLTreeBuilder::FragmentParsingContext::finished() 420{ 421 // Populate the DocumentFragment with the parsed content now that we're done. 422 ContainerNode* root = m_dummyDocumentForFragmentParsing.get(); 423 if (m_contextElement) 424 root = m_dummyDocumentForFragmentParsing->documentElement(); 425 m_fragment->takeAllChildrenFrom(root); 426} 427 428HTMLTreeBuilder::FragmentParsingContext::~FragmentParsingContext() 429{ 430} 431 432PassRefPtr<Element> HTMLTreeBuilder::takeScriptToProcess(TextPosition1& scriptStartPosition) 433{ 434 // Unpause ourselves, callers may pause us again when processing the script. 435 // The HTML5 spec is written as though scripts are executed inside the tree 436 // builder. We pause the parser to exit the tree builder, and then resume 437 // before running scripts. 438 m_isPaused = false; 439 scriptStartPosition = m_scriptToProcessStartPosition; 440 m_scriptToProcessStartPosition = uninitializedPositionValue1(); 441 return m_scriptToProcess.release(); 442} 443 444void HTMLTreeBuilder::constructTreeFromToken(HTMLToken& rawToken) 445{ 446 AtomicHTMLToken token(rawToken); 447 constructTreeFromAtomicToken(token); 448} 449 450void HTMLTreeBuilder::constructTreeFromAtomicToken(AtomicHTMLToken& token) 451{ 452 processToken(token); 453 454 // Swallowing U+0000 characters isn't in the HTML5 spec, but turning all 455 // the U+0000 characters into replacement characters has compatibility 456 // problems. 457 m_parser->tokenizer()->setForceNullCharacterReplacement(m_insertionMode == TextMode || m_insertionMode == InForeignContentMode); 458 m_parser->tokenizer()->setShouldAllowCDATA(m_insertionMode == InForeignContentMode && m_tree.currentElement()->namespaceURI() != xhtmlNamespaceURI); 459} 460 461void HTMLTreeBuilder::processToken(AtomicHTMLToken& token) 462{ 463 switch (token.type()) { 464 case HTMLToken::Uninitialized: 465 ASSERT_NOT_REACHED(); 466 break; 467 case HTMLToken::DOCTYPE: 468 processDoctypeToken(token); 469 break; 470 case HTMLToken::StartTag: 471 processStartTag(token); 472 break; 473 case HTMLToken::EndTag: 474 processEndTag(token); 475 break; 476 case HTMLToken::Comment: 477 processComment(token); 478 return; 479 case HTMLToken::Character: 480 processCharacter(token); 481 break; 482 case HTMLToken::EndOfFile: 483 processEndOfFile(token); 484 break; 485 } 486} 487 488void HTMLTreeBuilder::processDoctypeToken(AtomicHTMLToken& token) 489{ 490 ASSERT(token.type() == HTMLToken::DOCTYPE); 491 if (m_insertionMode == InitialMode) { 492 m_tree.insertDoctype(token); 493 setInsertionMode(BeforeHTMLMode); 494 return; 495 } 496 if (m_insertionMode == InTableTextMode) { 497 defaultForInTableText(); 498 processDoctypeToken(token); 499 return; 500 } 501 parseError(token); 502} 503 504void HTMLTreeBuilder::processFakeStartTag(const QualifiedName& tagName, PassRefPtr<NamedNodeMap> attributes) 505{ 506 // FIXME: We'll need a fancier conversion than just "localName" for SVG/MathML tags. 507 AtomicHTMLToken fakeToken(HTMLToken::StartTag, tagName.localName(), attributes); 508 processStartTag(fakeToken); 509} 510 511void HTMLTreeBuilder::processFakeEndTag(const QualifiedName& tagName) 512{ 513 // FIXME: We'll need a fancier conversion than just "localName" for SVG/MathML tags. 514 AtomicHTMLToken fakeToken(HTMLToken::EndTag, tagName.localName()); 515 processEndTag(fakeToken); 516} 517 518void HTMLTreeBuilder::processFakeCharacters(const String& characters) 519{ 520 ASSERT(!characters.isEmpty()); 521 ExternalCharacterTokenBuffer buffer(characters); 522 processCharacterBuffer(buffer); 523} 524 525void HTMLTreeBuilder::processFakePEndTagIfPInButtonScope() 526{ 527 if (!m_tree.openElements()->inButtonScope(pTag.localName())) 528 return; 529 AtomicHTMLToken endP(HTMLToken::EndTag, pTag.localName()); 530 processEndTag(endP); 531} 532 533PassRefPtr<NamedNodeMap> HTMLTreeBuilder::attributesForIsindexInput(AtomicHTMLToken& token) 534{ 535 RefPtr<NamedNodeMap> attributes = token.takeAtributes(); 536 if (!attributes) 537 attributes = NamedNodeMap::create(); 538 else { 539 attributes->removeAttribute(nameAttr); 540 attributes->removeAttribute(actionAttr); 541 attributes->removeAttribute(promptAttr); 542 } 543 544 RefPtr<Attribute> mappedAttribute = Attribute::createMapped(nameAttr, isindexTag.localName()); 545 attributes->insertAttribute(mappedAttribute.release(), false); 546 return attributes.release(); 547} 548 549void HTMLTreeBuilder::processIsindexStartTagForInBody(AtomicHTMLToken& token) 550{ 551 ASSERT(token.type() == HTMLToken::StartTag); 552 ASSERT(token.name() == isindexTag); 553 parseError(token); 554 if (m_tree.form()) 555 return; 556 notImplemented(); // Acknowledge self-closing flag 557 processFakeStartTag(formTag); 558 RefPtr<Attribute> actionAttribute = token.getAttributeItem(actionAttr); 559 if (actionAttribute) { 560 ASSERT(m_tree.currentElement()->hasTagName(formTag)); 561 m_tree.currentElement()->setAttribute(actionAttr, actionAttribute->value()); 562 } 563 processFakeStartTag(hrTag); 564 processFakeStartTag(labelTag); 565 RefPtr<Attribute> promptAttribute = token.getAttributeItem(promptAttr); 566 if (promptAttribute) 567 processFakeCharacters(promptAttribute->value()); 568 else 569 processFakeCharacters(searchableIndexIntroduction()); 570 processFakeStartTag(inputTag, attributesForIsindexInput(token)); 571 notImplemented(); // This second set of characters may be needed by non-english locales. 572 processFakeEndTag(labelTag); 573 processFakeStartTag(hrTag); 574 processFakeEndTag(formTag); 575} 576 577namespace { 578 579bool isLi(const Element* element) 580{ 581 return element->hasTagName(liTag); 582} 583 584bool isDdOrDt(const Element* element) 585{ 586 return element->hasTagName(ddTag) 587 || element->hasTagName(dtTag); 588} 589 590} 591 592template <bool shouldClose(const Element*)> 593void HTMLTreeBuilder::processCloseWhenNestedTag(AtomicHTMLToken& token) 594{ 595 m_framesetOk = false; 596 HTMLElementStack::ElementRecord* nodeRecord = m_tree.openElements()->topRecord(); 597 while (1) { 598 Element* node = nodeRecord->element(); 599 if (shouldClose(node)) { 600 processFakeEndTag(node->tagQName()); 601 break; 602 } 603 if (isSpecialNode(node) && !node->hasTagName(addressTag) && !node->hasTagName(divTag) && !node->hasTagName(pTag)) 604 break; 605 nodeRecord = nodeRecord->next(); 606 } 607 processFakePEndTagIfPInButtonScope(); 608 m_tree.insertHTMLElement(token); 609} 610 611namespace { 612 613typedef HashMap<AtomicString, QualifiedName> PrefixedNameToQualifiedNameMap; 614 615void mapLoweredLocalNameToName(PrefixedNameToQualifiedNameMap* map, QualifiedName** names, size_t length) 616{ 617 for (size_t i = 0; i < length; ++i) { 618 const QualifiedName& name = *names[i]; 619 const AtomicString& localName = name.localName(); 620 AtomicString loweredLocalName = localName.lower(); 621 if (loweredLocalName != localName) 622 map->add(loweredLocalName, name); 623 } 624} 625 626void adjustSVGTagNameCase(AtomicHTMLToken& token) 627{ 628 static PrefixedNameToQualifiedNameMap* caseMap = 0; 629 if (!caseMap) { 630 caseMap = new PrefixedNameToQualifiedNameMap; 631 size_t length = 0; 632 QualifiedName** svgTags = SVGNames::getSVGTags(&length); 633 mapLoweredLocalNameToName(caseMap, svgTags, length); 634 } 635 636 const QualifiedName& casedName = caseMap->get(token.name()); 637 if (casedName.localName().isNull()) 638 return; 639 token.setName(casedName.localName()); 640} 641 642template<QualifiedName** getAttrs(size_t* length)> 643void adjustAttributes(AtomicHTMLToken& token) 644{ 645 static PrefixedNameToQualifiedNameMap* caseMap = 0; 646 if (!caseMap) { 647 caseMap = new PrefixedNameToQualifiedNameMap; 648 size_t length = 0; 649 QualifiedName** attrs = getAttrs(&length); 650 mapLoweredLocalNameToName(caseMap, attrs, length); 651 } 652 653 NamedNodeMap* attributes = token.attributes(); 654 if (!attributes) 655 return; 656 657 for (unsigned x = 0; x < attributes->length(); ++x) { 658 Attribute* attribute = attributes->attributeItem(x); 659 const QualifiedName& casedName = caseMap->get(attribute->localName()); 660 if (!casedName.localName().isNull()) 661 attribute->parserSetName(casedName); 662 } 663} 664 665void adjustSVGAttributes(AtomicHTMLToken& token) 666{ 667 adjustAttributes<SVGNames::getSVGAttrs>(token); 668} 669 670void adjustMathMLAttributes(AtomicHTMLToken& token) 671{ 672 adjustAttributes<MathMLNames::getMathMLAttrs>(token); 673} 674 675void addNamesWithPrefix(PrefixedNameToQualifiedNameMap* map, const AtomicString& prefix, QualifiedName** names, size_t length) 676{ 677 for (size_t i = 0; i < length; ++i) { 678 QualifiedName* name = names[i]; 679 const AtomicString& localName = name->localName(); 680 AtomicString prefixColonLocalName(prefix + ":" + localName); 681 QualifiedName nameWithPrefix(prefix, localName, name->namespaceURI()); 682 map->add(prefixColonLocalName, nameWithPrefix); 683 } 684} 685 686void adjustForeignAttributes(AtomicHTMLToken& token) 687{ 688 static PrefixedNameToQualifiedNameMap* map = 0; 689 if (!map) { 690 map = new PrefixedNameToQualifiedNameMap; 691 size_t length = 0; 692 QualifiedName** attrs = XLinkNames::getXLinkAttrs(&length); 693 addNamesWithPrefix(map, "xlink", attrs, length); 694 695 attrs = XMLNames::getXMLAttrs(&length); 696 addNamesWithPrefix(map, "xml", attrs, length); 697 698 map->add("xmlns", XMLNSNames::xmlnsAttr); 699 map->add("xmlns:xlink", QualifiedName("xmlns", "xlink", XMLNSNames::xmlnsNamespaceURI)); 700 } 701 702 NamedNodeMap* attributes = token.attributes(); 703 if (!attributes) 704 return; 705 706 for (unsigned x = 0; x < attributes->length(); ++x) { 707 Attribute* attribute = attributes->attributeItem(x); 708 const QualifiedName& name = map->get(attribute->localName()); 709 if (!name.localName().isNull()) 710 attribute->parserSetName(name); 711 } 712} 713 714} 715 716void HTMLTreeBuilder::processStartTagForInBody(AtomicHTMLToken& token) 717{ 718 ASSERT(token.type() == HTMLToken::StartTag); 719 if (token.name() == htmlTag) { 720 m_tree.insertHTMLHtmlStartTagInBody(token); 721 return; 722 } 723 if (token.name() == baseTag 724 || token.name() == basefontTag 725 || token.name() == bgsoundTag 726 || token.name() == commandTag 727 || token.name() == linkTag 728 || token.name() == metaTag 729 || token.name() == noframesTag 730 || token.name() == scriptTag 731 || token.name() == styleTag 732 || token.name() == titleTag) { 733 bool didProcess = processStartTagForInHead(token); 734 ASSERT_UNUSED(didProcess, didProcess); 735 return; 736 } 737 if (token.name() == bodyTag) { 738 if (!m_tree.openElements()->secondElementIsHTMLBodyElement() || m_tree.openElements()->hasOnlyOneElement()) { 739 ASSERT(isParsingFragment()); 740 return; 741 } 742 m_tree.insertHTMLBodyStartTagInBody(token); 743 return; 744 } 745 if (token.name() == framesetTag) { 746 parseError(token); 747 if (!m_tree.openElements()->secondElementIsHTMLBodyElement() || m_tree.openElements()->hasOnlyOneElement()) { 748 ASSERT(isParsingFragment()); 749 return; 750 } 751 if (!m_framesetOk) 752 return; 753 ExceptionCode ec = 0; 754 m_tree.openElements()->bodyElement()->remove(ec); 755 ASSERT(!ec); 756 m_tree.openElements()->popUntil(m_tree.openElements()->bodyElement()); 757 m_tree.openElements()->popHTMLBodyElement(); 758 ASSERT(m_tree.openElements()->top() == m_tree.openElements()->htmlElement()); 759 m_tree.insertHTMLElement(token); 760 setInsertionMode(InFramesetMode); 761 return; 762 } 763 if (token.name() == addressTag 764 || token.name() == articleTag 765 || token.name() == asideTag 766 || token.name() == blockquoteTag 767 || token.name() == centerTag 768 || token.name() == detailsTag 769 || token.name() == dirTag 770 || token.name() == divTag 771 || token.name() == dlTag 772 || token.name() == fieldsetTag 773 || token.name() == figcaptionTag 774 || token.name() == figureTag 775 || token.name() == footerTag 776 || token.name() == headerTag 777 || token.name() == hgroupTag 778 || token.name() == menuTag 779 || token.name() == navTag 780 || token.name() == olTag 781 || token.name() == pTag 782 || token.name() == sectionTag 783 || token.name() == summaryTag 784 || token.name() == ulTag) { 785 processFakePEndTagIfPInButtonScope(); 786 m_tree.insertHTMLElement(token); 787 return; 788 } 789 if (isNumberedHeaderTag(token.name())) { 790 processFakePEndTagIfPInButtonScope(); 791 if (isNumberedHeaderTag(m_tree.currentElement()->localName())) { 792 parseError(token); 793 m_tree.openElements()->pop(); 794 } 795 m_tree.insertHTMLElement(token); 796 return; 797 } 798 if (token.name() == preTag || token.name() == listingTag) { 799 processFakePEndTagIfPInButtonScope(); 800 m_tree.insertHTMLElement(token); 801 m_parser->tokenizer()->setSkipLeadingNewLineForListing(true); 802 m_framesetOk = false; 803 return; 804 } 805 if (token.name() == formTag) { 806 if (m_tree.form()) { 807 parseError(token); 808 return; 809 } 810 processFakePEndTagIfPInButtonScope(); 811 m_tree.insertHTMLFormElement(token); 812 return; 813 } 814 if (token.name() == liTag) { 815 processCloseWhenNestedTag<isLi>(token); 816 return; 817 } 818 if (token.name() == ddTag || token.name() == dtTag) { 819 processCloseWhenNestedTag<isDdOrDt>(token); 820 return; 821 } 822 if (token.name() == plaintextTag) { 823 processFakePEndTagIfPInButtonScope(); 824 m_tree.insertHTMLElement(token); 825 m_parser->tokenizer()->setState(HTMLTokenizer::PLAINTEXTState); 826 return; 827 } 828 if (token.name() == buttonTag) { 829 if (m_tree.openElements()->inScope(buttonTag)) { 830 parseError(token); 831 processFakeEndTag(buttonTag); 832 reprocessStartTag(token); // FIXME: Could we just fall through here? 833 return; 834 } 835 m_tree.reconstructTheActiveFormattingElements(); 836 m_tree.insertHTMLElement(token); 837 m_framesetOk = false; 838 return; 839 } 840 if (token.name() == aTag) { 841 Element* activeATag = m_tree.activeFormattingElements()->closestElementInScopeWithName(aTag.localName()); 842 if (activeATag) { 843 parseError(token); 844 processFakeEndTag(aTag); 845 m_tree.activeFormattingElements()->remove(activeATag); 846 if (m_tree.openElements()->contains(activeATag)) 847 m_tree.openElements()->remove(activeATag); 848 } 849 m_tree.reconstructTheActiveFormattingElements(); 850 m_tree.insertFormattingElement(token); 851 return; 852 } 853 if (isNonAnchorNonNobrFormattingTag(token.name())) { 854 m_tree.reconstructTheActiveFormattingElements(); 855 m_tree.insertFormattingElement(token); 856 return; 857 } 858 if (token.name() == nobrTag) { 859 m_tree.reconstructTheActiveFormattingElements(); 860 if (m_tree.openElements()->inScope(nobrTag)) { 861 parseError(token); 862 processFakeEndTag(nobrTag); 863 m_tree.reconstructTheActiveFormattingElements(); 864 } 865 m_tree.insertFormattingElement(token); 866 return; 867 } 868 if (token.name() == appletTag 869 || token.name() == marqueeTag 870 || token.name() == objectTag) { 871 m_tree.reconstructTheActiveFormattingElements(); 872 m_tree.insertHTMLElement(token); 873 m_tree.activeFormattingElements()->appendMarker(); 874 m_framesetOk = false; 875 return; 876 } 877 if (token.name() == tableTag) { 878 if (!m_document->inQuirksMode() && m_tree.openElements()->inButtonScope(pTag)) 879 processFakeEndTag(pTag); 880 m_tree.insertHTMLElement(token); 881 m_framesetOk = false; 882 setInsertionMode(InTableMode); 883 return; 884 } 885 if (token.name() == imageTag) { 886 parseError(token); 887 // Apparently we're not supposed to ask. 888 token.setName(imgTag.localName()); 889 prepareToReprocessToken(); 890 // Note the fall through to the imgTag handling below! 891 } 892 if (token.name() == areaTag 893 || token.name() == brTag 894 || token.name() == embedTag 895 || token.name() == imgTag 896 || token.name() == keygenTag 897 || token.name() == wbrTag) { 898 m_tree.reconstructTheActiveFormattingElements(); 899 m_tree.insertSelfClosingHTMLElement(token); 900 m_framesetOk = false; 901 return; 902 } 903 if (token.name() == inputTag) { 904 RefPtr<Attribute> typeAttribute = token.getAttributeItem(typeAttr); 905 m_tree.reconstructTheActiveFormattingElements(); 906 m_tree.insertSelfClosingHTMLElement(token); 907 if (!typeAttribute || !equalIgnoringCase(typeAttribute->value(), "hidden")) 908 m_framesetOk = false; 909 return; 910 } 911 if (token.name() == paramTag 912 || token.name() == sourceTag 913 || token.name() == trackTag) { 914 m_tree.insertSelfClosingHTMLElement(token); 915 return; 916 } 917 if (token.name() == hrTag) { 918 processFakePEndTagIfPInButtonScope(); 919 m_tree.insertSelfClosingHTMLElement(token); 920 m_framesetOk = false; 921 return; 922 } 923 if (token.name() == isindexTag) { 924 processIsindexStartTagForInBody(token); 925 return; 926 } 927 if (token.name() == textareaTag) { 928 m_tree.insertHTMLElement(token); 929 m_parser->tokenizer()->setSkipLeadingNewLineForListing(true); 930 m_parser->tokenizer()->setState(HTMLTokenizer::RCDATAState); 931 m_originalInsertionMode = m_insertionMode; 932 m_framesetOk = false; 933 setInsertionMode(TextMode); 934 return; 935 } 936 if (token.name() == xmpTag) { 937 processFakePEndTagIfPInButtonScope(); 938 m_tree.reconstructTheActiveFormattingElements(); 939 m_framesetOk = false; 940 processGenericRawTextStartTag(token); 941 return; 942 } 943 if (token.name() == iframeTag) { 944 m_framesetOk = false; 945 processGenericRawTextStartTag(token); 946 return; 947 } 948 if (token.name() == noembedTag && pluginsEnabled(m_document->frame())) { 949 processGenericRawTextStartTag(token); 950 return; 951 } 952 if (token.name() == noscriptTag && scriptEnabled(m_document->frame())) { 953 processGenericRawTextStartTag(token); 954 return; 955 } 956 if (token.name() == selectTag) { 957 m_tree.reconstructTheActiveFormattingElements(); 958 m_tree.insertHTMLElement(token); 959 m_framesetOk = false; 960 if (m_insertionMode == InTableMode 961 || m_insertionMode == InCaptionMode 962 || m_insertionMode == InColumnGroupMode 963 || m_insertionMode == InTableBodyMode 964 || m_insertionMode == InRowMode 965 || m_insertionMode == InCellMode) 966 setInsertionMode(InSelectInTableMode); 967 else 968 setInsertionMode(InSelectMode); 969 return; 970 } 971 if (token.name() == optgroupTag || token.name() == optionTag) { 972 if (m_tree.openElements()->inScope(optionTag.localName())) { 973 AtomicHTMLToken endOption(HTMLToken::EndTag, optionTag.localName()); 974 processEndTag(endOption); 975 } 976 m_tree.reconstructTheActiveFormattingElements(); 977 m_tree.insertHTMLElement(token); 978 return; 979 } 980 if (token.name() == rpTag || token.name() == rtTag) { 981 if (m_tree.openElements()->inScope(rubyTag.localName())) { 982 m_tree.generateImpliedEndTags(); 983 if (!m_tree.currentElement()->hasTagName(rubyTag)) { 984 parseError(token); 985 m_tree.openElements()->popUntil(rubyTag.localName()); 986 } 987 } 988 m_tree.insertHTMLElement(token); 989 return; 990 } 991 if (token.name() == MathMLNames::mathTag.localName()) { 992 m_tree.reconstructTheActiveFormattingElements(); 993 adjustMathMLAttributes(token); 994 adjustForeignAttributes(token); 995 m_tree.insertForeignElement(token, MathMLNames::mathmlNamespaceURI); 996 if (m_insertionMode != InForeignContentMode) 997 setInsertionMode(InForeignContentMode); 998 return; 999 } 1000 if (token.name() == SVGNames::svgTag.localName()) { 1001 m_tree.reconstructTheActiveFormattingElements(); 1002 adjustSVGAttributes(token); 1003 adjustForeignAttributes(token); 1004 m_tree.insertForeignElement(token, SVGNames::svgNamespaceURI); 1005 if (m_insertionMode != InForeignContentMode) 1006 setInsertionMode(InForeignContentMode); 1007 return; 1008 } 1009 if (isCaptionColOrColgroupTag(token.name()) 1010 || token.name() == frameTag 1011 || token.name() == headTag 1012 || isTableBodyContextTag(token.name()) 1013 || isTableCellContextTag(token.name()) 1014 || token.name() == trTag) { 1015 parseError(token); 1016 return; 1017 } 1018 m_tree.reconstructTheActiveFormattingElements(); 1019 m_tree.insertHTMLElement(token); 1020} 1021 1022bool HTMLTreeBuilder::processColgroupEndTagForInColumnGroup() 1023{ 1024 if (m_tree.currentElement() == m_tree.openElements()->htmlElement()) { 1025 ASSERT(isParsingFragment()); 1026 // FIXME: parse error 1027 return false; 1028 } 1029 m_tree.openElements()->pop(); 1030 setInsertionMode(InTableMode); 1031 return true; 1032} 1033 1034// http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#close-the-cell 1035void HTMLTreeBuilder::closeTheCell() 1036{ 1037 ASSERT(insertionMode() == InCellMode); 1038 if (m_tree.openElements()->inTableScope(tdTag)) { 1039 ASSERT(!m_tree.openElements()->inTableScope(thTag)); 1040 processFakeEndTag(tdTag); 1041 return; 1042 } 1043 ASSERT(m_tree.openElements()->inTableScope(thTag)); 1044 processFakeEndTag(thTag); 1045 ASSERT(insertionMode() == InRowMode); 1046} 1047 1048void HTMLTreeBuilder::processStartTagForInTable(AtomicHTMLToken& token) 1049{ 1050 ASSERT(token.type() == HTMLToken::StartTag); 1051 if (token.name() == captionTag) { 1052 m_tree.openElements()->popUntilTableScopeMarker(); 1053 m_tree.activeFormattingElements()->appendMarker(); 1054 m_tree.insertHTMLElement(token); 1055 setInsertionMode(InCaptionMode); 1056 return; 1057 } 1058 if (token.name() == colgroupTag) { 1059 m_tree.openElements()->popUntilTableScopeMarker(); 1060 m_tree.insertHTMLElement(token); 1061 setInsertionMode(InColumnGroupMode); 1062 return; 1063 } 1064 if (token.name() == colTag) { 1065 processFakeStartTag(colgroupTag); 1066 ASSERT(InColumnGroupMode); 1067 reprocessStartTag(token); 1068 return; 1069 } 1070 if (isTableBodyContextTag(token.name())) { 1071 m_tree.openElements()->popUntilTableScopeMarker(); 1072 m_tree.insertHTMLElement(token); 1073 setInsertionMode(InTableBodyMode); 1074 return; 1075 } 1076 if (isTableCellContextTag(token.name()) 1077 || token.name() == trTag) { 1078 processFakeStartTag(tbodyTag); 1079 ASSERT(insertionMode() == InTableBodyMode); 1080 reprocessStartTag(token); 1081 return; 1082 } 1083 if (token.name() == tableTag) { 1084 parseError(token); 1085 if (!processTableEndTagForInTable()) { 1086 ASSERT(isParsingFragment()); 1087 return; 1088 } 1089 reprocessStartTag(token); 1090 return; 1091 } 1092 if (token.name() == styleTag || token.name() == scriptTag) { 1093 processStartTagForInHead(token); 1094 return; 1095 } 1096 if (token.name() == inputTag) { 1097 Attribute* typeAttribute = token.getAttributeItem(typeAttr); 1098 if (typeAttribute && equalIgnoringCase(typeAttribute->value(), "hidden")) { 1099 parseError(token); 1100 m_tree.insertSelfClosingHTMLElement(token); 1101 return; 1102 } 1103 // Fall through to "anything else" case. 1104 } 1105 if (token.name() == formTag) { 1106 parseError(token); 1107 if (m_tree.form()) 1108 return; 1109 m_tree.insertHTMLFormElement(token, true); 1110 m_tree.openElements()->pop(); 1111 return; 1112 } 1113 parseError(token); 1114 HTMLConstructionSite::RedirectToFosterParentGuard redirecter(m_tree); 1115 processStartTagForInBody(token); 1116} 1117 1118namespace { 1119 1120bool shouldProcessForeignContentUsingInBodyInsertionMode(AtomicHTMLToken& token, Element* currentElement) 1121{ 1122 ASSERT(token.type() == HTMLToken::StartTag); 1123 if (currentElement->hasTagName(MathMLNames::miTag) 1124 || currentElement->hasTagName(MathMLNames::moTag) 1125 || currentElement->hasTagName(MathMLNames::mnTag) 1126 || currentElement->hasTagName(MathMLNames::msTag) 1127 || currentElement->hasTagName(MathMLNames::mtextTag)) { 1128 return token.name() != MathMLNames::mglyphTag 1129 && token.name() != MathMLNames::malignmarkTag; 1130 } 1131 if (currentElement->hasTagName(MathMLNames::annotation_xmlTag)) 1132 return token.name() == SVGNames::svgTag; 1133 if (currentElement->hasTagName(SVGNames::foreignObjectTag) 1134 || currentElement->hasTagName(SVGNames::descTag) 1135 || currentElement->hasTagName(SVGNames::titleTag)) 1136 return true; 1137 return currentElement->namespaceURI() == HTMLNames::xhtmlNamespaceURI; 1138} 1139 1140} 1141 1142void HTMLTreeBuilder::processStartTag(AtomicHTMLToken& token) 1143{ 1144 ASSERT(token.type() == HTMLToken::StartTag); 1145 switch (insertionMode()) { 1146 case InitialMode: 1147 ASSERT(insertionMode() == InitialMode); 1148 defaultForInitial(); 1149 // Fall through. 1150 case BeforeHTMLMode: 1151 ASSERT(insertionMode() == BeforeHTMLMode); 1152 if (token.name() == htmlTag) { 1153 m_tree.insertHTMLHtmlStartTagBeforeHTML(token); 1154 setInsertionMode(BeforeHeadMode); 1155 return; 1156 } 1157 defaultForBeforeHTML(); 1158 // Fall through. 1159 case BeforeHeadMode: 1160 ASSERT(insertionMode() == BeforeHeadMode); 1161 if (token.name() == htmlTag) { 1162 m_tree.insertHTMLHtmlStartTagInBody(token); 1163 return; 1164 } 1165 if (token.name() == headTag) { 1166 m_tree.insertHTMLHeadElement(token); 1167 setInsertionMode(InHeadMode); 1168 return; 1169 } 1170 defaultForBeforeHead(); 1171 // Fall through. 1172 case InHeadMode: 1173 ASSERT(insertionMode() == InHeadMode); 1174 if (processStartTagForInHead(token)) 1175 return; 1176 defaultForInHead(); 1177 // Fall through. 1178 case AfterHeadMode: 1179 ASSERT(insertionMode() == AfterHeadMode); 1180 if (token.name() == htmlTag) { 1181 m_tree.insertHTMLHtmlStartTagInBody(token); 1182 return; 1183 } 1184 if (token.name() == bodyTag) { 1185 m_framesetOk = false; 1186 m_tree.insertHTMLBodyElement(token); 1187 setInsertionMode(InBodyMode); 1188 return; 1189 } 1190 if (token.name() == framesetTag) { 1191 m_tree.insertHTMLElement(token); 1192 setInsertionMode(InFramesetMode); 1193 return; 1194 } 1195 if (token.name() == baseTag 1196 || token.name() == basefontTag 1197 || token.name() == bgsoundTag 1198 || token.name() == linkTag 1199 || token.name() == metaTag 1200 || token.name() == noframesTag 1201 || token.name() == scriptTag 1202 || token.name() == styleTag 1203 || token.name() == titleTag) { 1204 parseError(token); 1205 ASSERT(m_tree.head()); 1206 m_tree.openElements()->pushHTMLHeadElement(m_tree.head()); 1207 processStartTagForInHead(token); 1208 m_tree.openElements()->removeHTMLHeadElement(m_tree.head()); 1209 return; 1210 } 1211 if (token.name() == headTag) { 1212 parseError(token); 1213 return; 1214 } 1215 defaultForAfterHead(); 1216 // Fall through 1217 case InBodyMode: 1218 ASSERT(insertionMode() == InBodyMode); 1219 processStartTagForInBody(token); 1220 break; 1221 case InTableMode: 1222 ASSERT(insertionMode() == InTableMode); 1223 processStartTagForInTable(token); 1224 break; 1225 case InCaptionMode: 1226 ASSERT(insertionMode() == InCaptionMode); 1227 if (isCaptionColOrColgroupTag(token.name()) 1228 || isTableBodyContextTag(token.name()) 1229 || isTableCellContextTag(token.name()) 1230 || token.name() == trTag) { 1231 parseError(token); 1232 if (!processCaptionEndTagForInCaption()) { 1233 ASSERT(isParsingFragment()); 1234 return; 1235 } 1236 reprocessStartTag(token); 1237 return; 1238 } 1239 processStartTagForInBody(token); 1240 break; 1241 case InColumnGroupMode: 1242 ASSERT(insertionMode() == InColumnGroupMode); 1243 if (token.name() == htmlTag) { 1244 m_tree.insertHTMLHtmlStartTagInBody(token); 1245 return; 1246 } 1247 if (token.name() == colTag) { 1248 m_tree.insertSelfClosingHTMLElement(token); 1249 return; 1250 } 1251 if (!processColgroupEndTagForInColumnGroup()) { 1252 ASSERT(isParsingFragment()); 1253 return; 1254 } 1255 reprocessStartTag(token); 1256 break; 1257 case InTableBodyMode: 1258 ASSERT(insertionMode() == InTableBodyMode); 1259 if (token.name() == trTag) { 1260 m_tree.openElements()->popUntilTableBodyScopeMarker(); // How is there ever anything to pop? 1261 m_tree.insertHTMLElement(token); 1262 setInsertionMode(InRowMode); 1263 return; 1264 } 1265 if (isTableCellContextTag(token.name())) { 1266 parseError(token); 1267 processFakeStartTag(trTag); 1268 ASSERT(insertionMode() == InRowMode); 1269 reprocessStartTag(token); 1270 return; 1271 } 1272 if (isCaptionColOrColgroupTag(token.name()) || isTableBodyContextTag(token.name())) { 1273 // FIXME: This is slow. 1274 if (!m_tree.openElements()->inTableScope(tbodyTag.localName()) && !m_tree.openElements()->inTableScope(theadTag.localName()) && !m_tree.openElements()->inTableScope(tfootTag.localName())) { 1275 ASSERT(isParsingFragment()); 1276 parseError(token); 1277 return; 1278 } 1279 m_tree.openElements()->popUntilTableBodyScopeMarker(); 1280 ASSERT(isTableBodyContextTag(m_tree.currentElement()->localName())); 1281 processFakeEndTag(m_tree.currentElement()->tagQName()); 1282 reprocessStartTag(token); 1283 return; 1284 } 1285 processStartTagForInTable(token); 1286 break; 1287 case InRowMode: 1288 ASSERT(insertionMode() == InRowMode); 1289 if (isTableCellContextTag(token.name())) { 1290 m_tree.openElements()->popUntilTableRowScopeMarker(); 1291 m_tree.insertHTMLElement(token); 1292 setInsertionMode(InCellMode); 1293 m_tree.activeFormattingElements()->appendMarker(); 1294 return; 1295 } 1296 if (token.name() == trTag 1297 || isCaptionColOrColgroupTag(token.name()) 1298 || isTableBodyContextTag(token.name())) { 1299 if (!processTrEndTagForInRow()) { 1300 ASSERT(isParsingFragment()); 1301 return; 1302 } 1303 ASSERT(insertionMode() == InTableBodyMode); 1304 reprocessStartTag(token); 1305 return; 1306 } 1307 processStartTagForInTable(token); 1308 break; 1309 case InCellMode: 1310 ASSERT(insertionMode() == InCellMode); 1311 if (isCaptionColOrColgroupTag(token.name()) 1312 || isTableCellContextTag(token.name()) 1313 || token.name() == trTag 1314 || isTableBodyContextTag(token.name())) { 1315 // FIXME: This could be more efficient. 1316 if (!m_tree.openElements()->inTableScope(tdTag) && !m_tree.openElements()->inTableScope(thTag)) { 1317 ASSERT(isParsingFragment()); 1318 parseError(token); 1319 return; 1320 } 1321 closeTheCell(); 1322 reprocessStartTag(token); 1323 return; 1324 } 1325 processStartTagForInBody(token); 1326 break; 1327 case AfterBodyMode: 1328 case AfterAfterBodyMode: 1329 ASSERT(insertionMode() == AfterBodyMode || insertionMode() == AfterAfterBodyMode); 1330 if (token.name() == htmlTag) { 1331 m_tree.insertHTMLHtmlStartTagInBody(token); 1332 return; 1333 } 1334 setInsertionMode(InBodyMode); 1335 reprocessStartTag(token); 1336 break; 1337 case InHeadNoscriptMode: 1338 ASSERT(insertionMode() == InHeadNoscriptMode); 1339 if (token.name() == htmlTag) { 1340 m_tree.insertHTMLHtmlStartTagInBody(token); 1341 return; 1342 } 1343 if (token.name() == basefontTag 1344 || token.name() == bgsoundTag 1345 || token.name() == linkTag 1346 || token.name() == metaTag 1347 || token.name() == noframesTag 1348 || token.name() == styleTag) { 1349 bool didProcess = processStartTagForInHead(token); 1350 ASSERT_UNUSED(didProcess, didProcess); 1351 return; 1352 } 1353 if (token.name() == htmlTag || token.name() == noscriptTag) { 1354 parseError(token); 1355 return; 1356 } 1357 defaultForInHeadNoscript(); 1358 processToken(token); 1359 break; 1360 case InFramesetMode: 1361 ASSERT(insertionMode() == InFramesetMode); 1362 if (token.name() == htmlTag) { 1363 m_tree.insertHTMLHtmlStartTagInBody(token); 1364 return; 1365 } 1366 if (token.name() == framesetTag) { 1367 m_tree.insertHTMLElement(token); 1368 return; 1369 } 1370 if (token.name() == frameTag) { 1371 m_tree.insertSelfClosingHTMLElement(token); 1372 return; 1373 } 1374 if (token.name() == noframesTag) { 1375 processStartTagForInHead(token); 1376 return; 1377 } 1378 parseError(token); 1379 break; 1380 case AfterFramesetMode: 1381 case AfterAfterFramesetMode: 1382 ASSERT(insertionMode() == AfterFramesetMode || insertionMode() == AfterAfterFramesetMode); 1383 if (token.name() == htmlTag) { 1384 m_tree.insertHTMLHtmlStartTagInBody(token); 1385 return; 1386 } 1387 if (token.name() == noframesTag) { 1388 processStartTagForInHead(token); 1389 return; 1390 } 1391 parseError(token); 1392 break; 1393 case InSelectInTableMode: 1394 ASSERT(insertionMode() == InSelectInTableMode); 1395 if (token.name() == captionTag 1396 || token.name() == tableTag 1397 || isTableBodyContextTag(token.name()) 1398 || token.name() == trTag 1399 || isTableCellContextTag(token.name())) { 1400 parseError(token); 1401 AtomicHTMLToken endSelect(HTMLToken::EndTag, selectTag.localName()); 1402 processEndTag(endSelect); 1403 reprocessStartTag(token); 1404 return; 1405 } 1406 // Fall through 1407 case InSelectMode: 1408 ASSERT(insertionMode() == InSelectMode || insertionMode() == InSelectInTableMode); 1409 if (token.name() == htmlTag) { 1410 m_tree.insertHTMLHtmlStartTagInBody(token); 1411 return; 1412 } 1413 if (token.name() == optionTag) { 1414 if (m_tree.currentElement()->hasTagName(optionTag)) { 1415 AtomicHTMLToken endOption(HTMLToken::EndTag, optionTag.localName()); 1416 processEndTag(endOption); 1417 } 1418 m_tree.insertHTMLElement(token); 1419 return; 1420 } 1421 if (token.name() == optgroupTag) { 1422 if (m_tree.currentElement()->hasTagName(optionTag)) { 1423 AtomicHTMLToken endOption(HTMLToken::EndTag, optionTag.localName()); 1424 processEndTag(endOption); 1425 } 1426 if (m_tree.currentElement()->hasTagName(optgroupTag)) { 1427 AtomicHTMLToken endOptgroup(HTMLToken::EndTag, optgroupTag.localName()); 1428 processEndTag(endOptgroup); 1429 } 1430 m_tree.insertHTMLElement(token); 1431 return; 1432 } 1433 if (token.name() == selectTag) { 1434 parseError(token); 1435 AtomicHTMLToken endSelect(HTMLToken::EndTag, selectTag.localName()); 1436 processEndTag(endSelect); 1437 return; 1438 } 1439 if (token.name() == inputTag 1440 || token.name() == keygenTag 1441 || token.name() == textareaTag) { 1442 parseError(token); 1443 if (!m_tree.openElements()->inSelectScope(selectTag)) { 1444 ASSERT(isParsingFragment()); 1445 return; 1446 } 1447 AtomicHTMLToken endSelect(HTMLToken::EndTag, selectTag.localName()); 1448 processEndTag(endSelect); 1449 reprocessStartTag(token); 1450 return; 1451 } 1452 if (token.name() == scriptTag) { 1453 bool didProcess = processStartTagForInHead(token); 1454 ASSERT_UNUSED(didProcess, didProcess); 1455 return; 1456 } 1457 break; 1458 case InTableTextMode: 1459 defaultForInTableText(); 1460 processStartTag(token); 1461 break; 1462 case InForeignContentMode: { 1463 if (shouldProcessForeignContentUsingInBodyInsertionMode(token, m_tree.currentElement())) { 1464 processForeignContentUsingInBodyModeAndResetMode(token); 1465 return; 1466 } 1467 if (token.name() == bTag 1468 || token.name() == bigTag 1469 || token.name() == blockquoteTag 1470 || token.name() == bodyTag 1471 || token.name() == brTag 1472 || token.name() == centerTag 1473 || token.name() == codeTag 1474 || token.name() == ddTag 1475 || token.name() == divTag 1476 || token.name() == dlTag 1477 || token.name() == dtTag 1478 || token.name() == emTag 1479 || token.name() == embedTag 1480 || isNumberedHeaderTag(token.name()) 1481 || token.name() == headTag 1482 || token.name() == hrTag 1483 || token.name() == iTag 1484 || token.name() == imgTag 1485 || token.name() == liTag 1486 || token.name() == listingTag 1487 || token.name() == menuTag 1488 || token.name() == metaTag 1489 || token.name() == nobrTag 1490 || token.name() == olTag 1491 || token.name() == pTag 1492 || token.name() == preTag 1493 || token.name() == rubyTag 1494 || token.name() == sTag 1495 || token.name() == smallTag 1496 || token.name() == spanTag 1497 || token.name() == strongTag 1498 || token.name() == strikeTag 1499 || token.name() == subTag 1500 || token.name() == supTag 1501 || token.name() == tableTag 1502 || token.name() == ttTag 1503 || token.name() == uTag 1504 || token.name() == ulTag 1505 || token.name() == varTag 1506 || (token.name() == fontTag && (token.getAttributeItem(colorAttr) || token.getAttributeItem(faceAttr) || token.getAttributeItem(sizeAttr)))) { 1507 parseError(token); 1508 m_tree.openElements()->popUntilForeignContentScopeMarker(); 1509 resetInsertionModeAppropriately(); 1510 reprocessStartTag(token); 1511 return; 1512 } 1513 const AtomicString& currentNamespace = m_tree.currentElement()->namespaceURI(); 1514 if (currentNamespace == MathMLNames::mathmlNamespaceURI) 1515 adjustMathMLAttributes(token); 1516 if (currentNamespace == SVGNames::svgNamespaceURI) { 1517 adjustSVGTagNameCase(token); 1518 adjustSVGAttributes(token); 1519 } 1520 adjustForeignAttributes(token); 1521 m_tree.insertForeignElement(token, currentNamespace); 1522 break; 1523 } 1524 case TextMode: 1525 ASSERT_NOT_REACHED(); 1526 break; 1527 } 1528} 1529 1530bool HTMLTreeBuilder::processBodyEndTagForInBody(AtomicHTMLToken& token) 1531{ 1532 ASSERT(token.type() == HTMLToken::EndTag); 1533 ASSERT(token.name() == bodyTag); 1534 if (!m_tree.openElements()->inScope(bodyTag.localName())) { 1535 parseError(token); 1536 return false; 1537 } 1538 notImplemented(); // Emit a more specific parse error based on stack contents. 1539 setInsertionMode(AfterBodyMode); 1540 return true; 1541} 1542 1543void HTMLTreeBuilder::processAnyOtherEndTagForInBody(AtomicHTMLToken& token) 1544{ 1545 ASSERT(token.type() == HTMLToken::EndTag); 1546 HTMLElementStack::ElementRecord* record = m_tree.openElements()->topRecord(); 1547 while (1) { 1548 Element* node = record->element(); 1549 if (node->hasLocalName(token.name())) { 1550 m_tree.generateImpliedEndTags(); 1551 if (!m_tree.currentElement()->hasLocalName(token.name())) { 1552 parseError(token); 1553 // FIXME: This is either a bug in the spec, or a bug in our 1554 // implementation. Filed a bug with HTML5: 1555 // http://www.w3.org/Bugs/Public/show_bug.cgi?id=10080 1556 // We might have already popped the node for the token in 1557 // generateImpliedEndTags, just abort. 1558 if (!m_tree.openElements()->contains(node)) 1559 return; 1560 } 1561 m_tree.openElements()->popUntilPopped(node); 1562 return; 1563 } 1564 if (isSpecialNode(node)) { 1565 parseError(token); 1566 return; 1567 } 1568 record = record->next(); 1569 } 1570} 1571 1572// FIXME: This probably belongs on HTMLElementStack. 1573HTMLElementStack::ElementRecord* HTMLTreeBuilder::furthestBlockForFormattingElement(Element* formattingElement) 1574{ 1575 HTMLElementStack::ElementRecord* furthestBlock = 0; 1576 HTMLElementStack::ElementRecord* record = m_tree.openElements()->topRecord(); 1577 for (; record; record = record->next()) { 1578 if (record->element() == formattingElement) 1579 return furthestBlock; 1580 if (isSpecialNode(record->element())) 1581 furthestBlock = record; 1582 } 1583 ASSERT_NOT_REACHED(); 1584 return 0; 1585} 1586 1587// http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#parsing-main-inbody 1588void HTMLTreeBuilder::callTheAdoptionAgency(AtomicHTMLToken& token) 1589{ 1590 // The adoption agency algorithm is N^2. We limit the number of iterations 1591 // to stop from hanging the whole browser. This limit is copied from the 1592 // legacy tree builder and might need to be tweaked in the future. 1593 static const int adoptionAgencyIterationLimit = 10; 1594 1595 for (int i = 0; i < adoptionAgencyIterationLimit; ++i) { 1596 // 1. 1597 Element* formattingElement = m_tree.activeFormattingElements()->closestElementInScopeWithName(token.name()); 1598 if (!formattingElement || ((m_tree.openElements()->contains(formattingElement)) && !m_tree.openElements()->inScope(formattingElement))) { 1599 parseError(token); 1600 notImplemented(); // Check the stack of open elements for a more specific parse error. 1601 return; 1602 } 1603 HTMLElementStack::ElementRecord* formattingElementRecord = m_tree.openElements()->find(formattingElement); 1604 if (!formattingElementRecord) { 1605 parseError(token); 1606 m_tree.activeFormattingElements()->remove(formattingElement); 1607 return; 1608 } 1609 if (formattingElement != m_tree.currentElement()) 1610 parseError(token); 1611 // 2. 1612 HTMLElementStack::ElementRecord* furthestBlock = furthestBlockForFormattingElement(formattingElement); 1613 // 3. 1614 if (!furthestBlock) { 1615 m_tree.openElements()->popUntilPopped(formattingElement); 1616 m_tree.activeFormattingElements()->remove(formattingElement); 1617 return; 1618 } 1619 // 4. 1620 ASSERT(furthestBlock->isAbove(formattingElementRecord)); 1621 Element* commonAncestor = formattingElementRecord->next()->element(); 1622 // 5. 1623 HTMLFormattingElementList::Bookmark bookmark = m_tree.activeFormattingElements()->bookmarkFor(formattingElement); 1624 // 6. 1625 HTMLElementStack::ElementRecord* node = furthestBlock; 1626 HTMLElementStack::ElementRecord* nextNode = node->next(); 1627 HTMLElementStack::ElementRecord* lastNode = furthestBlock; 1628 for (int i = 0; i < adoptionAgencyIterationLimit; ++i) { 1629 // 6.1 1630 node = nextNode; 1631 ASSERT(node); 1632 nextNode = node->next(); // Save node->next() for the next iteration in case node is deleted in 6.2. 1633 // 6.2 1634 if (!m_tree.activeFormattingElements()->contains(node->element())) { 1635 m_tree.openElements()->remove(node->element()); 1636 node = 0; 1637 continue; 1638 } 1639 // 6.3 1640 if (node == formattingElementRecord) 1641 break; 1642 // 6.5 1643 RefPtr<Element> newElement = m_tree.createHTMLElementFromElementRecord(node); 1644 HTMLFormattingElementList::Entry* nodeEntry = m_tree.activeFormattingElements()->find(node->element()); 1645 nodeEntry->replaceElement(newElement.get()); 1646 node->replaceElement(newElement.release()); 1647 // 6.4 -- Intentionally out of order to handle the case where node 1648 // was replaced in 6.5. 1649 // http://www.w3.org/Bugs/Public/show_bug.cgi?id=10096 1650 if (lastNode == furthestBlock) 1651 bookmark.moveToAfter(nodeEntry); 1652 // 6.6 1653 if (Element* parent = lastNode->element()->parentElement()) 1654 parent->parserRemoveChild(lastNode->element()); 1655 node->element()->parserAddChild(lastNode->element()); 1656 if (lastNode->element()->parentElement()->attached() && !lastNode->element()->attached()) 1657 lastNode->element()->lazyAttach(); 1658 // 6.7 1659 lastNode = node; 1660 } 1661 // 7 1662 const AtomicString& commonAncestorTag = commonAncestor->localName(); 1663 if (Element* parent = lastNode->element()->parentElement()) 1664 parent->parserRemoveChild(lastNode->element()); 1665 // FIXME: If this moves to HTMLConstructionSite, this check should use 1666 // causesFosterParenting(tagName) instead. 1667 if (commonAncestorTag == tableTag 1668 || commonAncestorTag == trTag 1669 || isTableBodyContextTag(commonAncestorTag)) 1670 m_tree.fosterParent(lastNode->element()); 1671 else { 1672 commonAncestor->parserAddChild(lastNode->element()); 1673 if (lastNode->element()->parentElement()->attached() && !lastNode->element()->attached()) 1674 lastNode->element()->lazyAttach(); 1675 } 1676 // 8 1677 RefPtr<Element> newElement = m_tree.createHTMLElementFromElementRecord(formattingElementRecord); 1678 // 9 1679 newElement->takeAllChildrenFrom(furthestBlock->element()); 1680 // 10 1681 Element* furthestBlockElement = furthestBlock->element(); 1682 // FIXME: All this creation / parserAddChild / attach business should 1683 // be in HTMLConstructionSite. My guess is that steps 8--12 1684 // should all be in some HTMLConstructionSite function. 1685 furthestBlockElement->parserAddChild(newElement); 1686 if (furthestBlockElement->attached() && !newElement->attached()) { 1687 // Notice that newElement might already be attached if, for example, one of the reparented 1688 // children is a style element, which attaches itself automatically. 1689 newElement->attach(); 1690 } 1691 // 11 1692 m_tree.activeFormattingElements()->swapTo(formattingElement, newElement.get(), bookmark); 1693 // 12 1694 m_tree.openElements()->remove(formattingElement); 1695 m_tree.openElements()->insertAbove(newElement, furthestBlock); 1696 } 1697} 1698 1699void HTMLTreeBuilder::resetInsertionModeAppropriately() 1700{ 1701 // http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#reset-the-insertion-mode-appropriately 1702 bool last = false; 1703 HTMLElementStack::ElementRecord* nodeRecord = m_tree.openElements()->topRecord(); 1704 while (1) { 1705 Element* node = nodeRecord->element(); 1706 if (node == m_tree.openElements()->bottom()) { 1707 ASSERT(isParsingFragment()); 1708 last = true; 1709 node = m_fragmentContext.contextElement(); 1710 } 1711 if (node->hasTagName(selectTag)) { 1712 ASSERT(isParsingFragment()); 1713 return setInsertionMode(InSelectMode); 1714 } 1715 if (node->hasTagName(tdTag) || node->hasTagName(thTag)) 1716 return setInsertionMode(InCellMode); 1717 if (node->hasTagName(trTag)) 1718 return setInsertionMode(InRowMode); 1719 if (node->hasTagName(tbodyTag) || node->hasTagName(theadTag) || node->hasTagName(tfootTag)) 1720 return setInsertionMode(InTableBodyMode); 1721 if (node->hasTagName(captionTag)) 1722 return setInsertionMode(InCaptionMode); 1723 if (node->hasTagName(colgroupTag)) { 1724 ASSERT(isParsingFragment()); 1725 return setInsertionMode(InColumnGroupMode); 1726 } 1727 if (node->hasTagName(tableTag)) 1728 return setInsertionMode(InTableMode); 1729 if (node->hasTagName(headTag)) { 1730 ASSERT(isParsingFragment()); 1731 return setInsertionMode(InBodyMode); 1732 } 1733 if (node->hasTagName(bodyTag)) 1734 return setInsertionMode(InBodyMode); 1735 if (node->hasTagName(framesetTag)) { 1736 ASSERT(isParsingFragment()); 1737 return setInsertionMode(InFramesetMode); 1738 } 1739 if (node->hasTagName(htmlTag)) { 1740 ASSERT(isParsingFragment()); 1741 return setInsertionMode(BeforeHeadMode); 1742 } 1743 if (node->namespaceURI() == SVGNames::svgNamespaceURI 1744 || node->namespaceURI() == MathMLNames::mathmlNamespaceURI) 1745 return setInsertionMode(InForeignContentMode); 1746 if (last) { 1747 ASSERT(isParsingFragment()); 1748 return setInsertionMode(InBodyMode); 1749 } 1750 nodeRecord = nodeRecord->next(); 1751 } 1752} 1753 1754void HTMLTreeBuilder::processEndTagForInTableBody(AtomicHTMLToken& token) 1755{ 1756 ASSERT(token.type() == HTMLToken::EndTag); 1757 if (isTableBodyContextTag(token.name())) { 1758 if (!m_tree.openElements()->inTableScope(token.name())) { 1759 parseError(token); 1760 return; 1761 } 1762 m_tree.openElements()->popUntilTableBodyScopeMarker(); 1763 m_tree.openElements()->pop(); 1764 setInsertionMode(InTableMode); 1765 return; 1766 } 1767 if (token.name() == tableTag) { 1768 // FIXME: This is slow. 1769 if (!m_tree.openElements()->inTableScope(tbodyTag.localName()) && !m_tree.openElements()->inTableScope(theadTag.localName()) && !m_tree.openElements()->inTableScope(tfootTag.localName())) { 1770 ASSERT(isParsingFragment()); 1771 parseError(token); 1772 return; 1773 } 1774 m_tree.openElements()->popUntilTableBodyScopeMarker(); 1775 ASSERT(isTableBodyContextTag(m_tree.currentElement()->localName())); 1776 processFakeEndTag(m_tree.currentElement()->tagQName()); 1777 reprocessEndTag(token); 1778 return; 1779 } 1780 if (token.name() == bodyTag 1781 || isCaptionColOrColgroupTag(token.name()) 1782 || token.name() == htmlTag 1783 || isTableCellContextTag(token.name()) 1784 || token.name() == trTag) { 1785 parseError(token); 1786 return; 1787 } 1788 processEndTagForInTable(token); 1789} 1790 1791void HTMLTreeBuilder::processEndTagForInRow(AtomicHTMLToken& token) 1792{ 1793 ASSERT(token.type() == HTMLToken::EndTag); 1794 if (token.name() == trTag) { 1795 processTrEndTagForInRow(); 1796 return; 1797 } 1798 if (token.name() == tableTag) { 1799 if (!processTrEndTagForInRow()) { 1800 ASSERT(isParsingFragment()); 1801 return; 1802 } 1803 ASSERT(insertionMode() == InTableBodyMode); 1804 reprocessEndTag(token); 1805 return; 1806 } 1807 if (isTableBodyContextTag(token.name())) { 1808 if (!m_tree.openElements()->inTableScope(token.name())) { 1809 parseError(token); 1810 return; 1811 } 1812 processFakeEndTag(trTag); 1813 ASSERT(insertionMode() == InTableBodyMode); 1814 reprocessEndTag(token); 1815 return; 1816 } 1817 if (token.name() == bodyTag 1818 || isCaptionColOrColgroupTag(token.name()) 1819 || token.name() == htmlTag 1820 || isTableCellContextTag(token.name())) { 1821 parseError(token); 1822 return; 1823 } 1824 processEndTagForInTable(token); 1825} 1826 1827void HTMLTreeBuilder::processEndTagForInCell(AtomicHTMLToken& token) 1828{ 1829 ASSERT(token.type() == HTMLToken::EndTag); 1830 if (isTableCellContextTag(token.name())) { 1831 if (!m_tree.openElements()->inTableScope(token.name())) { 1832 parseError(token); 1833 return; 1834 } 1835 m_tree.generateImpliedEndTags(); 1836 if (!m_tree.currentElement()->hasLocalName(token.name())) 1837 parseError(token); 1838 m_tree.openElements()->popUntilPopped(token.name()); 1839 m_tree.activeFormattingElements()->clearToLastMarker(); 1840 setInsertionMode(InRowMode); 1841 return; 1842 } 1843 if (token.name() == bodyTag 1844 || isCaptionColOrColgroupTag(token.name()) 1845 || token.name() == htmlTag) { 1846 parseError(token); 1847 return; 1848 } 1849 if (token.name() == tableTag 1850 || token.name() == trTag 1851 || isTableBodyContextTag(token.name())) { 1852 if (!m_tree.openElements()->inTableScope(token.name())) { 1853 ASSERT(isTableBodyContextTag(token.name()) || isParsingFragment()); 1854 parseError(token); 1855 return; 1856 } 1857 closeTheCell(); 1858 reprocessEndTag(token); 1859 return; 1860 } 1861 processEndTagForInBody(token); 1862} 1863 1864void HTMLTreeBuilder::processEndTagForInBody(AtomicHTMLToken& token) 1865{ 1866 ASSERT(token.type() == HTMLToken::EndTag); 1867 if (token.name() == bodyTag) { 1868 processBodyEndTagForInBody(token); 1869 return; 1870 } 1871 if (token.name() == htmlTag) { 1872 AtomicHTMLToken endBody(HTMLToken::EndTag, bodyTag.localName()); 1873 if (processBodyEndTagForInBody(endBody)) 1874 reprocessEndTag(token); 1875 return; 1876 } 1877 if (token.name() == addressTag 1878 || token.name() == articleTag 1879 || token.name() == asideTag 1880 || token.name() == blockquoteTag 1881 || token.name() == buttonTag 1882 || token.name() == centerTag 1883 || token.name() == detailsTag 1884 || token.name() == dirTag 1885 || token.name() == divTag 1886 || token.name() == dlTag 1887 || token.name() == fieldsetTag 1888 || token.name() == figcaptionTag 1889 || token.name() == figureTag 1890 || token.name() == footerTag 1891 || token.name() == headerTag 1892 || token.name() == hgroupTag 1893 || token.name() == listingTag 1894 || token.name() == menuTag 1895 || token.name() == navTag 1896 || token.name() == olTag 1897 || token.name() == preTag 1898 || token.name() == sectionTag 1899 || token.name() == summaryTag 1900 || token.name() == ulTag) { 1901 if (!m_tree.openElements()->inScope(token.name())) { 1902 parseError(token); 1903 return; 1904 } 1905 m_tree.generateImpliedEndTags(); 1906 if (!m_tree.currentElement()->hasLocalName(token.name())) 1907 parseError(token); 1908 m_tree.openElements()->popUntilPopped(token.name()); 1909 return; 1910 } 1911 if (token.name() == formTag) { 1912 RefPtr<Element> node = m_tree.takeForm(); 1913 if (!node || !m_tree.openElements()->inScope(node.get())) { 1914 parseError(token); 1915 return; 1916 } 1917 m_tree.generateImpliedEndTags(); 1918 if (m_tree.currentElement() != node.get()) 1919 parseError(token); 1920 m_tree.openElements()->remove(node.get()); 1921 } 1922 if (token.name() == pTag) { 1923 if (!m_tree.openElements()->inButtonScope(token.name())) { 1924 parseError(token); 1925 processFakeStartTag(pTag); 1926 ASSERT(m_tree.openElements()->inScope(token.name())); 1927 reprocessEndTag(token); 1928 return; 1929 } 1930 m_tree.generateImpliedEndTagsWithExclusion(token.name()); 1931 if (!m_tree.currentElement()->hasLocalName(token.name())) 1932 parseError(token); 1933 m_tree.openElements()->popUntilPopped(token.name()); 1934 return; 1935 } 1936 if (token.name() == liTag) { 1937 if (!m_tree.openElements()->inListItemScope(token.name())) { 1938 parseError(token); 1939 return; 1940 } 1941 m_tree.generateImpliedEndTagsWithExclusion(token.name()); 1942 if (!m_tree.currentElement()->hasLocalName(token.name())) 1943 parseError(token); 1944 m_tree.openElements()->popUntilPopped(token.name()); 1945 return; 1946 } 1947 if (token.name() == ddTag 1948 || token.name() == dtTag) { 1949 if (!m_tree.openElements()->inScope(token.name())) { 1950 parseError(token); 1951 return; 1952 } 1953 m_tree.generateImpliedEndTagsWithExclusion(token.name()); 1954 if (!m_tree.currentElement()->hasLocalName(token.name())) 1955 parseError(token); 1956 m_tree.openElements()->popUntilPopped(token.name()); 1957 return; 1958 } 1959 if (isNumberedHeaderTag(token.name())) { 1960 if (!m_tree.openElements()->hasNumberedHeaderElementInScope()) { 1961 parseError(token); 1962 return; 1963 } 1964 m_tree.generateImpliedEndTags(); 1965 if (!m_tree.currentElement()->hasLocalName(token.name())) 1966 parseError(token); 1967 m_tree.openElements()->popUntilNumberedHeaderElementPopped(); 1968 return; 1969 } 1970 if (isFormattingTag(token.name())) { 1971 callTheAdoptionAgency(token); 1972 return; 1973 } 1974 if (token.name() == appletTag 1975 || token.name() == marqueeTag 1976 || token.name() == objectTag) { 1977 if (!m_tree.openElements()->inScope(token.name())) { 1978 parseError(token); 1979 return; 1980 } 1981 m_tree.generateImpliedEndTags(); 1982 if (!m_tree.currentElement()->hasLocalName(token.name())) 1983 parseError(token); 1984 m_tree.openElements()->popUntilPopped(token.name()); 1985 m_tree.activeFormattingElements()->clearToLastMarker(); 1986 return; 1987 } 1988 if (token.name() == brTag) { 1989 parseError(token); 1990 processFakeStartTag(brTag); 1991 return; 1992 } 1993 processAnyOtherEndTagForInBody(token); 1994} 1995 1996bool HTMLTreeBuilder::processCaptionEndTagForInCaption() 1997{ 1998 if (!m_tree.openElements()->inTableScope(captionTag.localName())) { 1999 ASSERT(isParsingFragment()); 2000 // FIXME: parse error 2001 return false; 2002 } 2003 m_tree.generateImpliedEndTags(); 2004 // FIXME: parse error if (!m_tree.currentElement()->hasTagName(captionTag)) 2005 m_tree.openElements()->popUntilPopped(captionTag.localName()); 2006 m_tree.activeFormattingElements()->clearToLastMarker(); 2007 setInsertionMode(InTableMode); 2008 return true; 2009} 2010 2011bool HTMLTreeBuilder::processTrEndTagForInRow() 2012{ 2013 if (!m_tree.openElements()->inTableScope(trTag.localName())) { 2014 ASSERT(isParsingFragment()); 2015 // FIXME: parse error 2016 return false; 2017 } 2018 m_tree.openElements()->popUntilTableRowScopeMarker(); 2019 ASSERT(m_tree.currentElement()->hasTagName(trTag)); 2020 m_tree.openElements()->pop(); 2021 setInsertionMode(InTableBodyMode); 2022 return true; 2023} 2024 2025bool HTMLTreeBuilder::processTableEndTagForInTable() 2026{ 2027 if (!m_tree.openElements()->inTableScope(tableTag)) { 2028 ASSERT(isParsingFragment()); 2029 // FIXME: parse error. 2030 return false; 2031 } 2032 m_tree.openElements()->popUntilPopped(tableTag.localName()); 2033 resetInsertionModeAppropriately(); 2034 return true; 2035} 2036 2037void HTMLTreeBuilder::processEndTagForInTable(AtomicHTMLToken& token) 2038{ 2039 ASSERT(token.type() == HTMLToken::EndTag); 2040 if (token.name() == tableTag) { 2041 processTableEndTagForInTable(); 2042 return; 2043 } 2044 if (token.name() == bodyTag 2045 || isCaptionColOrColgroupTag(token.name()) 2046 || token.name() == htmlTag 2047 || isTableBodyContextTag(token.name()) 2048 || isTableCellContextTag(token.name()) 2049 || token.name() == trTag) { 2050 parseError(token); 2051 return; 2052 } 2053 // Is this redirection necessary here? 2054 HTMLConstructionSite::RedirectToFosterParentGuard redirecter(m_tree); 2055 processEndTagForInBody(token); 2056} 2057 2058void HTMLTreeBuilder::processEndTag(AtomicHTMLToken& token) 2059{ 2060 ASSERT(token.type() == HTMLToken::EndTag); 2061 switch (insertionMode()) { 2062 case InitialMode: 2063 ASSERT(insertionMode() == InitialMode); 2064 defaultForInitial(); 2065 // Fall through. 2066 case BeforeHTMLMode: 2067 ASSERT(insertionMode() == BeforeHTMLMode); 2068 if (token.name() != headTag && token.name() != bodyTag && token.name() != htmlTag && token.name() != brTag) { 2069 parseError(token); 2070 return; 2071 } 2072 defaultForBeforeHTML(); 2073 // Fall through. 2074 case BeforeHeadMode: 2075 ASSERT(insertionMode() == BeforeHeadMode); 2076 if (token.name() != headTag && token.name() != bodyTag && token.name() != htmlTag && token.name() != brTag) { 2077 parseError(token); 2078 return; 2079 } 2080 defaultForBeforeHead(); 2081 // Fall through. 2082 case InHeadMode: 2083 ASSERT(insertionMode() == InHeadMode); 2084 if (token.name() == headTag) { 2085 m_tree.openElements()->popHTMLHeadElement(); 2086 setInsertionMode(AfterHeadMode); 2087 return; 2088 } 2089 if (token.name() != bodyTag && token.name() != htmlTag && token.name() != brTag) { 2090 parseError(token); 2091 return; 2092 } 2093 defaultForInHead(); 2094 // Fall through. 2095 case AfterHeadMode: 2096 ASSERT(insertionMode() == AfterHeadMode); 2097 if (token.name() != bodyTag && token.name() != htmlTag && token.name() != brTag) { 2098 parseError(token); 2099 return; 2100 } 2101 defaultForAfterHead(); 2102 // Fall through 2103 case InBodyMode: 2104 ASSERT(insertionMode() == InBodyMode); 2105 processEndTagForInBody(token); 2106 break; 2107 case InTableMode: 2108 ASSERT(insertionMode() == InTableMode); 2109 processEndTagForInTable(token); 2110 break; 2111 case InCaptionMode: 2112 ASSERT(insertionMode() == InCaptionMode); 2113 if (token.name() == captionTag) { 2114 processCaptionEndTagForInCaption(); 2115 return; 2116 } 2117 if (token.name() == tableTag) { 2118 parseError(token); 2119 if (!processCaptionEndTagForInCaption()) { 2120 ASSERT(isParsingFragment()); 2121 return; 2122 } 2123 reprocessEndTag(token); 2124 return; 2125 } 2126 if (token.name() == bodyTag 2127 || token.name() == colTag 2128 || token.name() == colgroupTag 2129 || token.name() == htmlTag 2130 || isTableBodyContextTag(token.name()) 2131 || isTableCellContextTag(token.name()) 2132 || token.name() == trTag) { 2133 parseError(token); 2134 return; 2135 } 2136 processEndTagForInBody(token); 2137 break; 2138 case InColumnGroupMode: 2139 ASSERT(insertionMode() == InColumnGroupMode); 2140 if (token.name() == colgroupTag) { 2141 processColgroupEndTagForInColumnGroup(); 2142 return; 2143 } 2144 if (token.name() == colTag) { 2145 parseError(token); 2146 return; 2147 } 2148 if (!processColgroupEndTagForInColumnGroup()) { 2149 ASSERT(isParsingFragment()); 2150 return; 2151 } 2152 reprocessEndTag(token); 2153 break; 2154 case InRowMode: 2155 ASSERT(insertionMode() == InRowMode); 2156 processEndTagForInRow(token); 2157 break; 2158 case InCellMode: 2159 ASSERT(insertionMode() == InCellMode); 2160 processEndTagForInCell(token); 2161 break; 2162 case InTableBodyMode: 2163 ASSERT(insertionMode() == InTableBodyMode); 2164 processEndTagForInTableBody(token); 2165 break; 2166 case AfterBodyMode: 2167 ASSERT(insertionMode() == AfterBodyMode); 2168 if (token.name() == htmlTag) { 2169 if (isParsingFragment()) { 2170 parseError(token); 2171 return; 2172 } 2173 setInsertionMode(AfterAfterBodyMode); 2174 return; 2175 } 2176 prepareToReprocessToken(); 2177 // Fall through. 2178 case AfterAfterBodyMode: 2179 ASSERT(insertionMode() == AfterBodyMode || insertionMode() == AfterAfterBodyMode); 2180 parseError(token); 2181 setInsertionMode(InBodyMode); 2182 reprocessEndTag(token); 2183 break; 2184 case InHeadNoscriptMode: 2185 ASSERT(insertionMode() == InHeadNoscriptMode); 2186 if (token.name() == noscriptTag) { 2187 ASSERT(m_tree.currentElement()->hasTagName(noscriptTag)); 2188 m_tree.openElements()->pop(); 2189 ASSERT(m_tree.currentElement()->hasTagName(headTag)); 2190 setInsertionMode(InHeadMode); 2191 return; 2192 } 2193 if (token.name() != brTag) { 2194 parseError(token); 2195 return; 2196 } 2197 defaultForInHeadNoscript(); 2198 processToken(token); 2199 break; 2200 case TextMode: 2201 if (token.name() == scriptTag) { 2202 // Pause ourselves so that parsing stops until the script can be processed by the caller. 2203 m_isPaused = true; 2204 ASSERT(m_tree.currentElement()->hasTagName(scriptTag)); 2205 m_scriptToProcess = m_tree.currentElement(); 2206 m_scriptToProcessStartPosition = WTF::toOneBasedTextPosition(m_lastScriptElementStartPosition); 2207 m_tree.openElements()->pop(); 2208 if (isParsingFragment() && m_fragmentContext.scriptingPermission() == FragmentScriptingNotAllowed) 2209 m_scriptToProcess->removeAllChildren(); 2210 setInsertionMode(m_originalInsertionMode); 2211 2212 // This token will not have been created by the tokenizer if a 2213 // self-closing script tag was encountered and pre-HTML5 parser 2214 // quirks are enabled. We must set the tokenizer's state to 2215 // DataState explicitly if the tokenizer didn't have a chance to. 2216 ASSERT(m_parser->tokenizer()->state() == HTMLTokenizer::DataState || m_usePreHTML5ParserQuirks); 2217 m_parser->tokenizer()->setState(HTMLTokenizer::DataState); 2218 return; 2219 } 2220 m_tree.openElements()->pop(); 2221 setInsertionMode(m_originalInsertionMode); 2222 break; 2223 case InFramesetMode: 2224 ASSERT(insertionMode() == InFramesetMode); 2225 if (token.name() == framesetTag) { 2226 if (m_tree.currentElement() == m_tree.openElements()->htmlElement()) { 2227 parseError(token); 2228 return; 2229 } 2230 m_tree.openElements()->pop(); 2231 if (!isParsingFragment() && !m_tree.currentElement()->hasTagName(framesetTag)) 2232 setInsertionMode(AfterFramesetMode); 2233 return; 2234 } 2235 break; 2236 case AfterFramesetMode: 2237 ASSERT(insertionMode() == AfterFramesetMode); 2238 if (token.name() == htmlTag) { 2239 setInsertionMode(AfterAfterFramesetMode); 2240 return; 2241 } 2242 // Fall through. 2243 case AfterAfterFramesetMode: 2244 ASSERT(insertionMode() == AfterFramesetMode || insertionMode() == AfterAfterFramesetMode); 2245 parseError(token); 2246 break; 2247 case InSelectInTableMode: 2248 ASSERT(insertionMode() == InSelectInTableMode); 2249 if (token.name() == captionTag 2250 || token.name() == tableTag 2251 || isTableBodyContextTag(token.name()) 2252 || token.name() == trTag 2253 || isTableCellContextTag(token.name())) { 2254 parseError(token); 2255 if (m_tree.openElements()->inTableScope(token.name())) { 2256 AtomicHTMLToken endSelect(HTMLToken::EndTag, selectTag.localName()); 2257 processEndTag(endSelect); 2258 reprocessEndTag(token); 2259 } 2260 return; 2261 } 2262 // Fall through. 2263 case InSelectMode: 2264 ASSERT(insertionMode() == InSelectMode || insertionMode() == InSelectInTableMode); 2265 if (token.name() == optgroupTag) { 2266 if (m_tree.currentElement()->hasTagName(optionTag) && m_tree.oneBelowTop()->hasTagName(optgroupTag)) 2267 processFakeEndTag(optionTag); 2268 if (m_tree.currentElement()->hasTagName(optgroupTag)) { 2269 m_tree.openElements()->pop(); 2270 return; 2271 } 2272 parseError(token); 2273 return; 2274 } 2275 if (token.name() == optionTag) { 2276 if (m_tree.currentElement()->hasTagName(optionTag)) { 2277 m_tree.openElements()->pop(); 2278 return; 2279 } 2280 parseError(token); 2281 return; 2282 } 2283 if (token.name() == selectTag) { 2284 if (!m_tree.openElements()->inSelectScope(token.name())) { 2285 ASSERT(isParsingFragment()); 2286 parseError(token); 2287 return; 2288 } 2289 m_tree.openElements()->popUntilPopped(selectTag.localName()); 2290 resetInsertionModeAppropriately(); 2291 return; 2292 } 2293 break; 2294 case InTableTextMode: 2295 defaultForInTableText(); 2296 processEndTag(token); 2297 break; 2298 case InForeignContentMode: 2299 if (token.name() == SVGNames::scriptTag && m_tree.currentElement()->hasTagName(SVGNames::scriptTag)) { 2300 notImplemented(); 2301 return; 2302 } 2303 if (m_tree.currentElement()->namespaceURI() != xhtmlNamespaceURI) { 2304 // FIXME: This code just wants an Element* iterator, instead of an ElementRecord* 2305 HTMLElementStack::ElementRecord* nodeRecord = m_tree.openElements()->topRecord(); 2306 if (!nodeRecord->element()->hasLocalName(token.name())) 2307 parseError(token); 2308 while (1) { 2309 if (nodeRecord->element()->hasLocalName(token.name())) { 2310 m_tree.openElements()->popUntilPopped(nodeRecord->element()); 2311 resetForeignInsertionMode(); 2312 return; 2313 } 2314 nodeRecord = nodeRecord->next(); 2315 if (nodeRecord->element()->namespaceURI() == xhtmlNamespaceURI) 2316 break; 2317 } 2318 } 2319 // Any other end tag (also the last two steps of "An end tag, if the current node is not an element in the HTML namespace." 2320 processForeignContentUsingInBodyModeAndResetMode(token); 2321 break; 2322 } 2323} 2324 2325void HTMLTreeBuilder::prepareToReprocessToken() 2326{ 2327 if (m_hasPendingForeignInsertionModeSteps) { 2328 resetForeignInsertionMode(); 2329 m_hasPendingForeignInsertionModeSteps = false; 2330 } 2331} 2332 2333void HTMLTreeBuilder::reprocessStartTag(AtomicHTMLToken& token) 2334{ 2335 prepareToReprocessToken(); 2336 processStartTag(token); 2337} 2338 2339void HTMLTreeBuilder::reprocessEndTag(AtomicHTMLToken& token) 2340{ 2341 prepareToReprocessToken(); 2342 processEndTag(token); 2343} 2344 2345class HTMLTreeBuilder::FakeInsertionMode : public Noncopyable { 2346public: 2347 FakeInsertionMode(HTMLTreeBuilder* treeBuilder, InsertionMode mode) 2348 : m_treeBuilder(treeBuilder) 2349 , m_originalMode(treeBuilder->insertionMode()) 2350 { 2351 m_treeBuilder->setFakeInsertionMode(mode); 2352 } 2353 2354 ~FakeInsertionMode() 2355 { 2356 if (m_treeBuilder->isFakeInsertionMode()) 2357 m_treeBuilder->setInsertionMode(m_originalMode); 2358 } 2359 2360private: 2361 HTMLTreeBuilder* m_treeBuilder; 2362 InsertionMode m_originalMode; 2363}; 2364 2365void HTMLTreeBuilder::processForeignContentUsingInBodyModeAndResetMode(AtomicHTMLToken& token) 2366{ 2367 m_hasPendingForeignInsertionModeSteps = true; 2368 { 2369 FakeInsertionMode fakeMode(this, InBodyMode); 2370 processToken(token); 2371 } 2372 if (m_hasPendingForeignInsertionModeSteps) 2373 resetForeignInsertionMode(); 2374} 2375 2376void HTMLTreeBuilder::resetForeignInsertionMode() 2377{ 2378 if (insertionMode() == InForeignContentMode) 2379 resetInsertionModeAppropriately(); 2380} 2381 2382void HTMLTreeBuilder::processComment(AtomicHTMLToken& token) 2383{ 2384 ASSERT(token.type() == HTMLToken::Comment); 2385 if (m_insertionMode == InitialMode 2386 || m_insertionMode == BeforeHTMLMode 2387 || m_insertionMode == AfterAfterBodyMode 2388 || m_insertionMode == AfterAfterFramesetMode) { 2389 m_tree.insertCommentOnDocument(token); 2390 return; 2391 } 2392 if (m_insertionMode == AfterBodyMode) { 2393 m_tree.insertCommentOnHTMLHtmlElement(token); 2394 return; 2395 } 2396 if (m_insertionMode == InTableTextMode) { 2397 defaultForInTableText(); 2398 processComment(token); 2399 return; 2400 } 2401 m_tree.insertComment(token); 2402} 2403 2404void HTMLTreeBuilder::processCharacter(AtomicHTMLToken& token) 2405{ 2406 ASSERT(token.type() == HTMLToken::Character); 2407 ExternalCharacterTokenBuffer buffer(token); 2408 processCharacterBuffer(buffer); 2409} 2410 2411void HTMLTreeBuilder::processCharacterBuffer(ExternalCharacterTokenBuffer& buffer) 2412{ 2413ReprocessBuffer: 2414 switch (insertionMode()) { 2415 case InitialMode: { 2416 ASSERT(insertionMode() == InitialMode); 2417 buffer.skipLeadingWhitespace(); 2418 if (buffer.isEmpty()) 2419 return; 2420 defaultForInitial(); 2421 // Fall through. 2422 } 2423 case BeforeHTMLMode: { 2424 ASSERT(insertionMode() == BeforeHTMLMode); 2425 buffer.skipLeadingWhitespace(); 2426 if (buffer.isEmpty()) 2427 return; 2428 defaultForBeforeHTML(); 2429 // Fall through. 2430 } 2431 case BeforeHeadMode: { 2432 ASSERT(insertionMode() == BeforeHeadMode); 2433 buffer.skipLeadingWhitespace(); 2434 if (buffer.isEmpty()) 2435 return; 2436 defaultForBeforeHead(); 2437 // Fall through. 2438 } 2439 case InHeadMode: { 2440 ASSERT(insertionMode() == InHeadMode); 2441 String leadingWhitespace = buffer.takeLeadingWhitespace(); 2442 if (!leadingWhitespace.isEmpty()) 2443 m_tree.insertTextNode(leadingWhitespace); 2444 if (buffer.isEmpty()) 2445 return; 2446 defaultForInHead(); 2447 // Fall through. 2448 } 2449 case AfterHeadMode: { 2450 ASSERT(insertionMode() == AfterHeadMode); 2451 String leadingWhitespace = buffer.takeLeadingWhitespace(); 2452 if (!leadingWhitespace.isEmpty()) 2453 m_tree.insertTextNode(leadingWhitespace); 2454 if (buffer.isEmpty()) 2455 return; 2456 defaultForAfterHead(); 2457 // Fall through. 2458 } 2459 case InBodyMode: 2460 case InCaptionMode: 2461 case InCellMode: { 2462 ASSERT(insertionMode() == InBodyMode || insertionMode() == InCaptionMode || insertionMode() == InCellMode); 2463 m_tree.reconstructTheActiveFormattingElements(); 2464 String characters = buffer.takeRemaining(); 2465 m_tree.insertTextNode(characters); 2466 if (m_framesetOk && !isAllWhitespaceOrReplacementCharacters(characters)) 2467 m_framesetOk = false; 2468 break; 2469 } 2470 case InTableMode: 2471 case InTableBodyMode: 2472 case InRowMode: { 2473 ASSERT(insertionMode() == InTableMode || insertionMode() == InTableBodyMode || insertionMode() == InRowMode); 2474 ASSERT(m_pendingTableCharacters.isEmpty()); 2475 m_originalInsertionMode = m_insertionMode; 2476 setInsertionMode(InTableTextMode); 2477 prepareToReprocessToken(); 2478 // Fall through. 2479 } 2480 case InTableTextMode: { 2481 buffer.giveRemainingTo(m_pendingTableCharacters); 2482 break; 2483 } 2484 case InColumnGroupMode: { 2485 ASSERT(insertionMode() == InColumnGroupMode); 2486 String leadingWhitespace = buffer.takeLeadingWhitespace(); 2487 if (!leadingWhitespace.isEmpty()) 2488 m_tree.insertTextNode(leadingWhitespace); 2489 if (buffer.isEmpty()) 2490 return; 2491 if (!processColgroupEndTagForInColumnGroup()) { 2492 ASSERT(isParsingFragment()); 2493 // The spec tells us to drop these characters on the floor. 2494 buffer.takeLeadingNonWhitespace(); 2495 if (buffer.isEmpty()) 2496 return; 2497 } 2498 prepareToReprocessToken(); 2499 goto ReprocessBuffer; 2500 } 2501 case AfterBodyMode: 2502 case AfterAfterBodyMode: { 2503 ASSERT(insertionMode() == AfterBodyMode || insertionMode() == AfterAfterBodyMode); 2504 // FIXME: parse error 2505 setInsertionMode(InBodyMode); 2506 prepareToReprocessToken(); 2507 goto ReprocessBuffer; 2508 break; 2509 } 2510 case TextMode: { 2511 ASSERT(insertionMode() == TextMode); 2512 m_tree.insertTextNode(buffer.takeRemaining()); 2513 break; 2514 } 2515 case InHeadNoscriptMode: { 2516 ASSERT(insertionMode() == InHeadNoscriptMode); 2517 String leadingWhitespace = buffer.takeLeadingWhitespace(); 2518 if (!leadingWhitespace.isEmpty()) 2519 m_tree.insertTextNode(leadingWhitespace); 2520 if (buffer.isEmpty()) 2521 return; 2522 defaultForInHeadNoscript(); 2523 goto ReprocessBuffer; 2524 break; 2525 } 2526 case InFramesetMode: 2527 case AfterFramesetMode: { 2528 ASSERT(insertionMode() == InFramesetMode || insertionMode() == AfterFramesetMode || insertionMode() == AfterAfterFramesetMode); 2529 String leadingWhitespace = buffer.takeRemainingWhitespace(); 2530 if (!leadingWhitespace.isEmpty()) 2531 m_tree.insertTextNode(leadingWhitespace); 2532 // FIXME: We should generate a parse error if we skipped over any 2533 // non-whitespace characters. 2534 break; 2535 } 2536 case InSelectInTableMode: 2537 case InSelectMode: { 2538 ASSERT(insertionMode() == InSelectMode || insertionMode() == InSelectInTableMode); 2539 m_tree.insertTextNode(buffer.takeRemaining()); 2540 break; 2541 } 2542 case InForeignContentMode: { 2543 ASSERT(insertionMode() == InForeignContentMode); 2544 String characters = buffer.takeRemaining(); 2545 m_tree.insertTextNode(characters); 2546 if (m_framesetOk && !isAllWhitespace(characters)) 2547 m_framesetOk = false; 2548 break; 2549 } 2550 case AfterAfterFramesetMode: { 2551 String leadingWhitespace = buffer.takeRemainingWhitespace(); 2552 if (!leadingWhitespace.isEmpty()) { 2553 m_tree.reconstructTheActiveFormattingElements(); 2554 m_tree.insertTextNode(leadingWhitespace); 2555 } 2556 // FIXME: We should generate a parse error if we skipped over any 2557 // non-whitespace characters. 2558 break; 2559 } 2560 } 2561} 2562 2563void HTMLTreeBuilder::processEndOfFile(AtomicHTMLToken& token) 2564{ 2565 ASSERT(token.type() == HTMLToken::EndOfFile); 2566 switch (insertionMode()) { 2567 case InitialMode: 2568 ASSERT(insertionMode() == InitialMode); 2569 defaultForInitial(); 2570 // Fall through. 2571 case BeforeHTMLMode: 2572 ASSERT(insertionMode() == BeforeHTMLMode); 2573 defaultForBeforeHTML(); 2574 // Fall through. 2575 case BeforeHeadMode: 2576 ASSERT(insertionMode() == BeforeHeadMode); 2577 defaultForBeforeHead(); 2578 // Fall through. 2579 case InHeadMode: 2580 ASSERT(insertionMode() == InHeadMode); 2581 defaultForInHead(); 2582 // Fall through. 2583 case AfterHeadMode: 2584 ASSERT(insertionMode() == AfterHeadMode); 2585 defaultForAfterHead(); 2586 // Fall through 2587 case InBodyMode: 2588 case InCellMode: 2589 case InCaptionMode: 2590 case InRowMode: 2591 ASSERT(insertionMode() == InBodyMode || insertionMode() == InCellMode || insertionMode() == InCaptionMode || insertionMode() == InRowMode); 2592 notImplemented(); // Emit parse error based on what elements are still open. 2593 break; 2594 case AfterBodyMode: 2595 case AfterAfterBodyMode: 2596 ASSERT(insertionMode() == AfterBodyMode || insertionMode() == AfterAfterBodyMode); 2597 break; 2598 case InHeadNoscriptMode: 2599 ASSERT(insertionMode() == InHeadNoscriptMode); 2600 defaultForInHeadNoscript(); 2601 processEndOfFile(token); 2602 return; 2603 case AfterFramesetMode: 2604 case AfterAfterFramesetMode: 2605 ASSERT(insertionMode() == AfterFramesetMode || insertionMode() == AfterAfterFramesetMode); 2606 break; 2607 case InFramesetMode: 2608 case InTableMode: 2609 case InTableBodyMode: 2610 case InSelectInTableMode: 2611 case InSelectMode: 2612 ASSERT(insertionMode() == InSelectMode || insertionMode() == InSelectInTableMode || insertionMode() == InTableMode || insertionMode() == InFramesetMode || insertionMode() == InTableBodyMode); 2613 if (m_tree.currentElement() != m_tree.openElements()->htmlElement()) 2614 parseError(token); 2615 break; 2616 case InColumnGroupMode: 2617 if (m_tree.currentElement() == m_tree.openElements()->htmlElement()) { 2618 ASSERT(isParsingFragment()); 2619 return; // FIXME: Should we break here instead of returning? 2620 } 2621 if (!processColgroupEndTagForInColumnGroup()) { 2622 ASSERT(isParsingFragment()); 2623 return; // FIXME: Should we break here instead of returning? 2624 } 2625 prepareToReprocessToken(); 2626 processEndOfFile(token); 2627 return; 2628 case InForeignContentMode: 2629 setInsertionMode(InBodyMode); 2630 processEndOfFile(token); 2631 return; 2632 case InTableTextMode: 2633 defaultForInTableText(); 2634 processEndOfFile(token); 2635 return; 2636 case TextMode: 2637 parseError(token); 2638 if (m_tree.currentElement()->hasTagName(scriptTag)) 2639 notImplemented(); // mark the script element as "already started". 2640 m_tree.openElements()->pop(); 2641 setInsertionMode(m_originalInsertionMode); 2642 prepareToReprocessToken(); 2643 processEndOfFile(token); 2644 return; 2645 } 2646 ASSERT(m_tree.openElements()->top()); 2647 m_tree.openElements()->popAll(); 2648} 2649 2650void HTMLTreeBuilder::defaultForInitial() 2651{ 2652 notImplemented(); 2653 if (!m_fragmentContext.fragment()) 2654 m_document->setCompatibilityMode(Document::QuirksMode); 2655 // FIXME: parse error 2656 setInsertionMode(BeforeHTMLMode); 2657 prepareToReprocessToken(); 2658} 2659 2660void HTMLTreeBuilder::defaultForBeforeHTML() 2661{ 2662 AtomicHTMLToken startHTML(HTMLToken::StartTag, htmlTag.localName()); 2663 m_tree.insertHTMLHtmlStartTagBeforeHTML(startHTML); 2664 setInsertionMode(BeforeHeadMode); 2665 prepareToReprocessToken(); 2666} 2667 2668void HTMLTreeBuilder::defaultForBeforeHead() 2669{ 2670 AtomicHTMLToken startHead(HTMLToken::StartTag, headTag.localName()); 2671 processStartTag(startHead); 2672 prepareToReprocessToken(); 2673} 2674 2675void HTMLTreeBuilder::defaultForInHead() 2676{ 2677 AtomicHTMLToken endHead(HTMLToken::EndTag, headTag.localName()); 2678 processEndTag(endHead); 2679 prepareToReprocessToken(); 2680} 2681 2682void HTMLTreeBuilder::defaultForInHeadNoscript() 2683{ 2684 AtomicHTMLToken endNoscript(HTMLToken::EndTag, noscriptTag.localName()); 2685 processEndTag(endNoscript); 2686 prepareToReprocessToken(); 2687} 2688 2689void HTMLTreeBuilder::defaultForAfterHead() 2690{ 2691 AtomicHTMLToken startBody(HTMLToken::StartTag, bodyTag.localName()); 2692 processStartTag(startBody); 2693 m_framesetOk = true; 2694 prepareToReprocessToken(); 2695} 2696 2697void HTMLTreeBuilder::defaultForInTableText() 2698{ 2699 String characters = String::adopt(m_pendingTableCharacters); 2700 if (!isAllWhitespace(characters)) { 2701 // FIXME: parse error 2702 HTMLConstructionSite::RedirectToFosterParentGuard redirecter(m_tree); 2703 m_tree.reconstructTheActiveFormattingElements(); 2704 m_tree.insertTextNode(characters); 2705 m_framesetOk = false; 2706 setInsertionMode(m_originalInsertionMode); 2707 prepareToReprocessToken(); 2708 return; 2709 } 2710 m_tree.insertTextNode(characters); 2711 setInsertionMode(m_originalInsertionMode); 2712 prepareToReprocessToken(); 2713} 2714 2715bool HTMLTreeBuilder::processStartTagForInHead(AtomicHTMLToken& token) 2716{ 2717 ASSERT(token.type() == HTMLToken::StartTag); 2718 if (token.name() == htmlTag) { 2719 m_tree.insertHTMLHtmlStartTagInBody(token); 2720 return true; 2721 } 2722 if (token.name() == baseTag 2723 || token.name() == basefontTag 2724 || token.name() == bgsoundTag 2725 || token.name() == commandTag 2726 || token.name() == linkTag 2727 || token.name() == metaTag) { 2728 m_tree.insertSelfClosingHTMLElement(token); 2729 // Note: The custom processing for the <meta> tag is done in HTMLMetaElement::process(). 2730 return true; 2731 } 2732 if (token.name() == titleTag) { 2733 processGenericRCDATAStartTag(token); 2734 return true; 2735 } 2736 if (token.name() == noscriptTag) { 2737 if (scriptEnabled(m_document->frame())) { 2738 processGenericRawTextStartTag(token); 2739 return true; 2740 } 2741 m_tree.insertHTMLElement(token); 2742 setInsertionMode(InHeadNoscriptMode); 2743 return true; 2744 } 2745 if (token.name() == noframesTag || token.name() == styleTag) { 2746 processGenericRawTextStartTag(token); 2747 return true; 2748 } 2749 if (token.name() == scriptTag) { 2750 processScriptStartTag(token); 2751 if (m_usePreHTML5ParserQuirks && token.selfClosing()) 2752 processFakeEndTag(scriptTag); 2753 return true; 2754 } 2755 if (token.name() == headTag) { 2756 parseError(token); 2757 return true; 2758 } 2759 return false; 2760} 2761 2762void HTMLTreeBuilder::processGenericRCDATAStartTag(AtomicHTMLToken& token) 2763{ 2764 ASSERT(token.type() == HTMLToken::StartTag); 2765 m_tree.insertHTMLElement(token); 2766 m_parser->tokenizer()->setState(HTMLTokenizer::RCDATAState); 2767 m_originalInsertionMode = m_insertionMode; 2768 setInsertionMode(TextMode); 2769} 2770 2771void HTMLTreeBuilder::processGenericRawTextStartTag(AtomicHTMLToken& token) 2772{ 2773 ASSERT(token.type() == HTMLToken::StartTag); 2774 m_tree.insertHTMLElement(token); 2775 m_parser->tokenizer()->setState(HTMLTokenizer::RAWTEXTState); 2776 m_originalInsertionMode = m_insertionMode; 2777 setInsertionMode(TextMode); 2778} 2779 2780void HTMLTreeBuilder::processScriptStartTag(AtomicHTMLToken& token) 2781{ 2782 ASSERT(token.type() == HTMLToken::StartTag); 2783 m_tree.insertScriptElement(token); 2784 m_parser->tokenizer()->setState(HTMLTokenizer::ScriptDataState); 2785 m_originalInsertionMode = m_insertionMode; 2786 2787 TextPosition0 position = m_parser->textPosition(); 2788 2789 ASSERT(position.m_line.zeroBasedInt() == m_parser->tokenizer()->lineNumber()); 2790 2791 m_lastScriptElementStartPosition = position; 2792 2793 setInsertionMode(TextMode); 2794} 2795 2796void HTMLTreeBuilder::finished() 2797{ 2798 ASSERT(m_document); 2799 if (isParsingFragment()) { 2800 m_fragmentContext.finished(); 2801 return; 2802 } 2803 2804 // Warning, this may detach the parser. Do not do anything else after this. 2805 m_document->finishedParsing(); 2806} 2807 2808bool HTMLTreeBuilder::scriptEnabled(Frame* frame) 2809{ 2810 if (!frame) 2811 return false; 2812 return frame->script()->canExecuteScripts(NotAboutToExecuteScript); 2813} 2814 2815bool HTMLTreeBuilder::pluginsEnabled(Frame* frame) 2816{ 2817 if (!frame) 2818 return false; 2819 return frame->loader()->subframeLoader()->allowPlugins(NotAboutToInstantiatePlugin); 2820} 2821 2822} 2823