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