2 * Copyright (C) 2010 Google, Inc. All Rights Reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 #include "HTMLTreeBuilder.h"
29 #include "CharacterNames.h"
31 #include "DocumentFragment.h"
32 #include "DocumentType.h"
34 #include "HTMLDocument.h"
35 #include "HTMLDocumentParser.h"
36 #include "HTMLElementFactory.h"
37 #include "HTMLFormElement.h"
38 #include "HTMLHtmlElement.h"
39 #include "HTMLNames.h"
40 #include "HTMLParserIdioms.h"
41 #include "HTMLScriptElement.h"
42 #include "HTMLToken.h"
43 #include "HTMLTokenizer.h"
44 #include "LocalizedStrings.h"
45 #include "MathMLNames.h"
46 #include "NotImplemented.h"
48 #include "ScriptController.h"
50 #include "XLinkNames.h"
51 #include "XMLNSNames.h"
56 using namespace HTMLNames;
58 static const int uninitializedLineNumberValue = -1;
60 static TextPosition1 uninitializedPositionValue1()
62 return TextPosition1(WTF::OneBasedNumber::fromOneBasedInt(-1), WTF::OneBasedNumber::base());
67 inline bool isHTMLSpaceOrReplacementCharacter(UChar character)
69 return isHTMLSpace(character) || character == replacementCharacter;
72 inline bool isAllWhitespace(const String& string)
74 return string.isAllSpecialCharacters<isHTMLSpace>();
77 inline bool isAllWhitespaceOrReplacementCharacters(const String& string)
79 return string.isAllSpecialCharacters<isHTMLSpaceOrReplacementCharacter>();
82 bool isNumberedHeaderTag(const AtomicString& tagName)
84 return tagName == h1Tag
92 bool isCaptionColOrColgroupTag(const AtomicString& tagName)
94 return tagName == captionTag
96 || tagName == colgroupTag;
99 bool isTableCellContextTag(const AtomicString& tagName)
101 return tagName == thTag || tagName == tdTag;
104 bool isTableBodyContextTag(const AtomicString& tagName)
106 return tagName == tbodyTag
107 || tagName == tfootTag
108 || tagName == theadTag;
111 // http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#special
112 bool isSpecialNode(Node* node)
114 if (node->hasTagName(MathMLNames::miTag)
115 || node->hasTagName(MathMLNames::moTag)
116 || node->hasTagName(MathMLNames::mnTag)
117 || node->hasTagName(MathMLNames::msTag)
118 || node->hasTagName(MathMLNames::mtextTag)
119 || node->hasTagName(MathMLNames::annotation_xmlTag)
120 || node->hasTagName(SVGNames::foreignObjectTag)
121 || node->hasTagName(SVGNames::descTag)
122 || node->hasTagName(SVGNames::titleTag))
124 if (node->namespaceURI() != xhtmlNamespaceURI)
126 const AtomicString& tagName = node->localName();
127 return tagName == addressTag
128 || tagName == appletTag
129 || tagName == areaTag
130 || tagName == articleTag
131 || tagName == asideTag
132 || tagName == baseTag
133 || tagName == basefontTag
134 || tagName == bgsoundTag
135 || tagName == blockquoteTag
136 || tagName == bodyTag
138 || tagName == buttonTag
139 || tagName == captionTag
140 || tagName == centerTag
142 || tagName == colgroupTag
143 || tagName == commandTag
145 || tagName == detailsTag
150 || tagName == embedTag
151 || tagName == fieldsetTag
152 || tagName == figcaptionTag
153 || tagName == figureTag
154 || tagName == footerTag
155 || tagName == formTag
156 || tagName == frameTag
157 || tagName == framesetTag
158 || isNumberedHeaderTag(tagName)
159 || tagName == headTag
160 || tagName == headerTag
161 || tagName == hgroupTag
163 || tagName == htmlTag
164 || tagName == iframeTag
166 || tagName == inputTag
167 || tagName == isindexTag
169 || tagName == linkTag
170 || tagName == listingTag
171 || tagName == marqueeTag
172 || tagName == menuTag
173 || tagName == metaTag
175 || tagName == noembedTag
176 || tagName == noframesTag
177 || tagName == noscriptTag
178 || tagName == objectTag
181 || tagName == paramTag
182 || tagName == plaintextTag
184 || tagName == scriptTag
185 || tagName == sectionTag
186 || tagName == selectTag
187 || tagName == styleTag
188 || tagName == summaryTag
189 || tagName == tableTag
190 || isTableBodyContextTag(tagName)
192 || tagName == textareaTag
194 || tagName == titleTag
198 || tagName == xmpTag;
201 bool isNonAnchorNonNobrFormattingTag(const AtomicString& tagName)
203 return tagName == bTag
205 || tagName == codeTag
207 || tagName == fontTag
210 || tagName == smallTag
211 || tagName == strikeTag
212 || tagName == strongTag
217 bool isNonAnchorFormattingTag(const AtomicString& tagName)
219 return tagName == nobrTag
220 || isNonAnchorNonNobrFormattingTag(tagName);
223 // http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#formatting
224 bool isFormattingTag(const AtomicString& tagName)
226 return tagName == aTag || isNonAnchorFormattingTag(tagName);
229 HTMLFormElement* closestFormAncestor(Element* element)
232 if (element->hasTagName(formTag))
233 return static_cast<HTMLFormElement*>(element);
234 ContainerNode* parent = element->parentNode();
235 if (!parent || !parent->isElementNode())
237 element = static_cast<Element*>(parent);
244 class HTMLTreeBuilder::ExternalCharacterTokenBuffer {
245 WTF_MAKE_NONCOPYABLE(ExternalCharacterTokenBuffer);
247 explicit ExternalCharacterTokenBuffer(AtomicHTMLToken& token)
248 : m_current(token.characters().data())
249 , m_end(m_current + token.characters().size())
254 explicit ExternalCharacterTokenBuffer(const String& string)
255 : m_current(string.characters())
256 , m_end(m_current + string.length())
261 ~ExternalCharacterTokenBuffer()
266 bool isEmpty() const { return m_current == m_end; }
268 void skipLeadingWhitespace()
270 skipLeading<isHTMLSpace>();
273 String takeLeadingWhitespace()
275 return takeLeading<isHTMLSpace>();
278 String takeLeadingNonWhitespace()
280 return takeLeading<isNotHTMLSpace>();
283 String takeRemaining()
286 const UChar* start = m_current;
288 return String(start, m_current - start);
291 void giveRemainingTo(Vector<UChar>& recipient)
293 recipient.append(m_current, m_end - m_current);
297 String takeRemainingWhitespace()
300 Vector<UChar> whitespace;
302 UChar cc = *m_current++;
304 whitespace.append(cc);
305 } while (m_current < m_end);
306 // Returning the null string when there aren't any whitespace
307 // characters is slightly cleaner semantically because we don't want
308 // to insert a text node (as opposed to inserting an empty text node).
309 if (whitespace.isEmpty())
311 return String::adopt(whitespace);
315 template<bool characterPredicate(UChar)>
319 while (characterPredicate(*m_current)) {
320 if (++m_current == m_end)
325 template<bool characterPredicate(UChar)>
329 const UChar* start = m_current;
330 skipLeading<characterPredicate>();
331 if (start == m_current)
333 return String(start, m_current - start);
336 const UChar* m_current;
341 HTMLTreeBuilder::HTMLTreeBuilder(HTMLDocumentParser* parser, HTMLDocument* document, bool reportErrors, bool usePreHTML5ParserQuirks)
343 , m_document(document)
344 , m_tree(document, FragmentScriptingAllowed, false)
345 , m_reportErrors(reportErrors)
347 , m_insertionMode(InitialMode)
348 , m_originalInsertionMode(InitialMode)
350 , m_scriptToProcessStartPosition(uninitializedPositionValue1())
351 , m_lastScriptElementStartPosition(TextPosition0::belowRangePosition())
352 , m_usePreHTML5ParserQuirks(usePreHTML5ParserQuirks)
353 , m_hasPendingForeignInsertionModeSteps(false)
357 // FIXME: Member variables should be grouped into self-initializing structs to
358 // minimize code duplication between these constructors.
359 HTMLTreeBuilder::HTMLTreeBuilder(HTMLDocumentParser* parser, DocumentFragment* fragment, Element* contextElement, FragmentScriptingPermission scriptingPermission, bool usePreHTML5ParserQuirks)
361 , m_fragmentContext(fragment, contextElement, scriptingPermission)
362 , m_document(m_fragmentContext.document())
363 , m_tree(m_document, scriptingPermission, true)
364 , m_reportErrors(false) // FIXME: Why not report errors in fragments?
366 , m_insertionMode(InitialMode)
367 , m_originalInsertionMode(InitialMode)
369 , m_scriptToProcessStartPosition(uninitializedPositionValue1())
370 , m_lastScriptElementStartPosition(TextPosition0::belowRangePosition())
371 , m_usePreHTML5ParserQuirks(usePreHTML5ParserQuirks)
372 , m_hasPendingForeignInsertionModeSteps(false)
374 if (contextElement) {
375 // Steps 4.2-4.6 of the HTML5 Fragment Case parsing algorithm:
376 // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-end.html#fragment-case
377 m_document->setCompatibilityMode(contextElement->document()->compatibilityMode());
378 processFakeStartTag(htmlTag);
379 resetInsertionModeAppropriately();
380 m_tree.setForm(closestFormAncestor(contextElement));
384 HTMLTreeBuilder::~HTMLTreeBuilder()
388 void HTMLTreeBuilder::detach()
390 // This call makes little sense in fragment mode, but for consistency
391 // DocumentParser expects detach() to always be called before it's destroyed.
393 // HTMLConstructionSite might be on the callstack when detach() is called
394 // otherwise we'd just call m_tree.clear() here instead.
398 HTMLTreeBuilder::FragmentParsingContext::FragmentParsingContext()
400 , m_contextElement(0)
401 , m_scriptingPermission(FragmentScriptingAllowed)
405 HTMLTreeBuilder::FragmentParsingContext::FragmentParsingContext(DocumentFragment* fragment, Element* contextElement, FragmentScriptingPermission scriptingPermission)
406 : m_dummyDocumentForFragmentParsing(HTMLDocument::create(0, KURL(), fragment->document()->baseURI()))
407 , m_fragment(fragment)
408 , m_contextElement(contextElement)
409 , m_scriptingPermission(scriptingPermission)
411 m_dummyDocumentForFragmentParsing->setCompatibilityMode(fragment->document()->compatibilityMode());
414 Document* HTMLTreeBuilder::FragmentParsingContext::document() const
417 return m_dummyDocumentForFragmentParsing.get();
420 void HTMLTreeBuilder::FragmentParsingContext::finished()
422 // Populate the DocumentFragment with the parsed content now that we're done.
423 ContainerNode* root = m_dummyDocumentForFragmentParsing.get();
424 if (m_contextElement)
425 root = m_dummyDocumentForFragmentParsing->documentElement();
426 m_fragment->takeAllChildrenFrom(root);
429 HTMLTreeBuilder::FragmentParsingContext::~FragmentParsingContext()
433 PassRefPtr<Element> HTMLTreeBuilder::takeScriptToProcess(TextPosition1& scriptStartPosition)
435 // Unpause ourselves, callers may pause us again when processing the script.
436 // The HTML5 spec is written as though scripts are executed inside the tree
437 // builder. We pause the parser to exit the tree builder, and then resume
438 // before running scripts.
440 scriptStartPosition = m_scriptToProcessStartPosition;
441 m_scriptToProcessStartPosition = uninitializedPositionValue1();
442 return m_scriptToProcess.release();
445 void HTMLTreeBuilder::constructTreeFromToken(HTMLToken& rawToken)
447 AtomicHTMLToken token(rawToken);
448 constructTreeFromAtomicToken(token);
451 void HTMLTreeBuilder::constructTreeFromAtomicToken(AtomicHTMLToken& token)
455 // Swallowing U+0000 characters isn't in the HTML5 spec, but turning all
456 // the U+0000 characters into replacement characters has compatibility
458 m_parser->tokenizer()->setForceNullCharacterReplacement(m_insertionMode == TextMode || m_insertionMode == InForeignContentMode);
459 m_parser->tokenizer()->setShouldAllowCDATA(m_insertionMode == InForeignContentMode && m_tree.currentElement()->namespaceURI() != xhtmlNamespaceURI);
462 void HTMLTreeBuilder::processToken(AtomicHTMLToken& token)
464 switch (token.type()) {
465 case HTMLToken::Uninitialized:
466 ASSERT_NOT_REACHED();
468 case HTMLToken::DOCTYPE:
469 processDoctypeToken(token);
471 case HTMLToken::StartTag:
472 processStartTag(token);
474 case HTMLToken::EndTag:
475 processEndTag(token);
477 case HTMLToken::Comment:
478 processComment(token);
480 case HTMLToken::Character:
481 processCharacter(token);
483 case HTMLToken::EndOfFile:
484 processEndOfFile(token);
489 void HTMLTreeBuilder::processDoctypeToken(AtomicHTMLToken& token)
491 ASSERT(token.type() == HTMLToken::DOCTYPE);
492 if (m_insertionMode == InitialMode) {
493 m_tree.insertDoctype(token);
494 setInsertionMode(BeforeHTMLMode);
497 if (m_insertionMode == InTableTextMode) {
498 defaultForInTableText();
499 processDoctypeToken(token);
505 void HTMLTreeBuilder::processFakeStartTag(const QualifiedName& tagName, PassRefPtr<NamedNodeMap> attributes)
507 // FIXME: We'll need a fancier conversion than just "localName" for SVG/MathML tags.
508 AtomicHTMLToken fakeToken(HTMLToken::StartTag, tagName.localName(), attributes);
509 processStartTag(fakeToken);
512 void HTMLTreeBuilder::processFakeEndTag(const QualifiedName& tagName)
514 // FIXME: We'll need a fancier conversion than just "localName" for SVG/MathML tags.
515 AtomicHTMLToken fakeToken(HTMLToken::EndTag, tagName.localName());
516 processEndTag(fakeToken);
519 void HTMLTreeBuilder::processFakeCharacters(const String& characters)
521 ASSERT(!characters.isEmpty());
522 ExternalCharacterTokenBuffer buffer(characters);
523 processCharacterBuffer(buffer);
526 void HTMLTreeBuilder::processFakePEndTagIfPInButtonScope()
528 if (!m_tree.openElements()->inButtonScope(pTag.localName()))
530 AtomicHTMLToken endP(HTMLToken::EndTag, pTag.localName());
534 PassRefPtr<NamedNodeMap> HTMLTreeBuilder::attributesForIsindexInput(AtomicHTMLToken& token)
536 RefPtr<NamedNodeMap> attributes = token.takeAtributes();
538 attributes = NamedNodeMap::create();
540 attributes->removeAttribute(nameAttr);
541 attributes->removeAttribute(actionAttr);
542 attributes->removeAttribute(promptAttr);
545 RefPtr<Attribute> mappedAttribute = Attribute::createMapped(nameAttr, isindexTag.localName());
546 attributes->insertAttribute(mappedAttribute.release(), false);
547 return attributes.release();
550 void HTMLTreeBuilder::processIsindexStartTagForInBody(AtomicHTMLToken& token)
552 ASSERT(token.type() == HTMLToken::StartTag);
553 ASSERT(token.name() == isindexTag);
557 notImplemented(); // Acknowledge self-closing flag
558 processFakeStartTag(formTag);
559 RefPtr<Attribute> actionAttribute = token.getAttributeItem(actionAttr);
560 if (actionAttribute) {
561 ASSERT(m_tree.currentElement()->hasTagName(formTag));
562 m_tree.currentElement()->setAttribute(actionAttr, actionAttribute->value());
564 processFakeStartTag(hrTag);
565 processFakeStartTag(labelTag);
566 RefPtr<Attribute> promptAttribute = token.getAttributeItem(promptAttr);
568 processFakeCharacters(promptAttribute->value());
570 processFakeCharacters(searchableIndexIntroduction());
571 processFakeStartTag(inputTag, attributesForIsindexInput(token));
572 notImplemented(); // This second set of characters may be needed by non-english locales.
573 processFakeEndTag(labelTag);
574 processFakeStartTag(hrTag);
575 processFakeEndTag(formTag);
580 bool isLi(const Element* element)
582 return element->hasTagName(liTag);
585 bool isDdOrDt(const Element* element)
587 return element->hasTagName(ddTag)
588 || element->hasTagName(dtTag);
593 template <bool shouldClose(const Element*)>
594 void HTMLTreeBuilder::processCloseWhenNestedTag(AtomicHTMLToken& token)
596 m_framesetOk = false;
597 HTMLElementStack::ElementRecord* nodeRecord = m_tree.openElements()->topRecord();
599 Element* node = nodeRecord->element();
600 if (shouldClose(node)) {
601 processFakeEndTag(node->tagQName());
604 if (isSpecialNode(node) && !node->hasTagName(addressTag) && !node->hasTagName(divTag) && !node->hasTagName(pTag))
606 nodeRecord = nodeRecord->next();
608 processFakePEndTagIfPInButtonScope();
609 m_tree.insertHTMLElement(token);
614 typedef HashMap<AtomicString, QualifiedName> PrefixedNameToQualifiedNameMap;
616 void mapLoweredLocalNameToName(PrefixedNameToQualifiedNameMap* map, QualifiedName** names, size_t length)
618 for (size_t i = 0; i < length; ++i) {
619 const QualifiedName& name = *names[i];
620 const AtomicString& localName = name.localName();
621 AtomicString loweredLocalName = localName.lower();
622 if (loweredLocalName != localName)
623 map->add(loweredLocalName, name);
627 void adjustSVGTagNameCase(AtomicHTMLToken& token)
629 static PrefixedNameToQualifiedNameMap* caseMap = 0;
631 caseMap = new PrefixedNameToQualifiedNameMap;
633 QualifiedName** svgTags = SVGNames::getSVGTags(&length);
634 mapLoweredLocalNameToName(caseMap, svgTags, length);
637 const QualifiedName& casedName = caseMap->get(token.name());
638 if (casedName.localName().isNull())
640 token.setName(casedName.localName());
643 template<QualifiedName** getAttrs(size_t* length)>
644 void adjustAttributes(AtomicHTMLToken& token)
646 static PrefixedNameToQualifiedNameMap* caseMap = 0;
648 caseMap = new PrefixedNameToQualifiedNameMap;
650 QualifiedName** attrs = getAttrs(&length);
651 mapLoweredLocalNameToName(caseMap, attrs, length);
654 NamedNodeMap* attributes = token.attributes();
658 for (unsigned x = 0; x < attributes->length(); ++x) {
659 Attribute* attribute = attributes->attributeItem(x);
660 const QualifiedName& casedName = caseMap->get(attribute->localName());
661 if (!casedName.localName().isNull())
662 attribute->parserSetName(casedName);
666 void adjustSVGAttributes(AtomicHTMLToken& token)
668 adjustAttributes<SVGNames::getSVGAttrs>(token);
671 void adjustMathMLAttributes(AtomicHTMLToken& token)
673 adjustAttributes<MathMLNames::getMathMLAttrs>(token);
676 void addNamesWithPrefix(PrefixedNameToQualifiedNameMap* map, const AtomicString& prefix, QualifiedName** names, size_t length)
678 for (size_t i = 0; i < length; ++i) {
679 QualifiedName* name = names[i];
680 const AtomicString& localName = name->localName();
681 AtomicString prefixColonLocalName(prefix + ":" + localName);
682 QualifiedName nameWithPrefix(prefix, localName, name->namespaceURI());
683 map->add(prefixColonLocalName, nameWithPrefix);
687 void adjustForeignAttributes(AtomicHTMLToken& token)
689 static PrefixedNameToQualifiedNameMap* map = 0;
691 map = new PrefixedNameToQualifiedNameMap;
693 QualifiedName** attrs = XLinkNames::getXLinkAttrs(&length);
694 addNamesWithPrefix(map, "xlink", attrs, length);
696 attrs = XMLNames::getXMLAttrs(&length);
697 addNamesWithPrefix(map, "xml", attrs, length);
699 map->add("xmlns", XMLNSNames::xmlnsAttr);
700 map->add("xmlns:xlink", QualifiedName("xmlns", "xlink", XMLNSNames::xmlnsNamespaceURI));
703 NamedNodeMap* attributes = token.attributes();
707 for (unsigned x = 0; x < attributes->length(); ++x) {
708 Attribute* attribute = attributes->attributeItem(x);
709 const QualifiedName& name = map->get(attribute->localName());
710 if (!name.localName().isNull())
711 attribute->parserSetName(name);
717 void HTMLTreeBuilder::processStartTagForInBody(AtomicHTMLToken& token)
719 ASSERT(token.type() == HTMLToken::StartTag);
720 if (token.name() == htmlTag) {
721 m_tree.insertHTMLHtmlStartTagInBody(token);
724 if (token.name() == baseTag
725 || token.name() == basefontTag
726 || token.name() == bgsoundTag
727 || token.name() == commandTag
728 || token.name() == linkTag
729 || token.name() == metaTag
730 || token.name() == noframesTag
731 || token.name() == scriptTag
732 || token.name() == styleTag
733 || token.name() == titleTag) {
734 bool didProcess = processStartTagForInHead(token);
735 ASSERT_UNUSED(didProcess, didProcess);
738 if (token.name() == bodyTag) {
739 if (!m_tree.openElements()->secondElementIsHTMLBodyElement() || m_tree.openElements()->hasOnlyOneElement()) {
740 ASSERT(isParsingFragment());
743 m_tree.insertHTMLBodyStartTagInBody(token);
746 if (token.name() == framesetTag) {
748 if (!m_tree.openElements()->secondElementIsHTMLBodyElement() || m_tree.openElements()->hasOnlyOneElement()) {
749 ASSERT(isParsingFragment());
754 ExceptionCode ec = 0;
755 m_tree.openElements()->bodyElement()->remove(ec);
757 m_tree.openElements()->popUntil(m_tree.openElements()->bodyElement());
758 m_tree.openElements()->popHTMLBodyElement();
759 ASSERT(m_tree.openElements()->top() == m_tree.openElements()->htmlElement());
760 m_tree.insertHTMLElement(token);
761 setInsertionMode(InFramesetMode);
764 if (token.name() == addressTag
765 || token.name() == articleTag
766 || token.name() == asideTag
767 || token.name() == blockquoteTag
768 || token.name() == centerTag
769 || token.name() == detailsTag
770 || token.name() == dirTag
771 || token.name() == divTag
772 || token.name() == dlTag
773 || token.name() == fieldsetTag
774 || token.name() == figcaptionTag
775 || token.name() == figureTag
776 || token.name() == footerTag
777 || token.name() == headerTag
778 || token.name() == hgroupTag
779 || token.name() == menuTag
780 || token.name() == navTag
781 || token.name() == olTag
782 || token.name() == pTag
783 || token.name() == sectionTag
784 || token.name() == summaryTag
785 || token.name() == ulTag) {
786 processFakePEndTagIfPInButtonScope();
787 m_tree.insertHTMLElement(token);
790 if (isNumberedHeaderTag(token.name())) {
791 processFakePEndTagIfPInButtonScope();
792 if (isNumberedHeaderTag(m_tree.currentElement()->localName())) {
794 m_tree.openElements()->pop();
796 m_tree.insertHTMLElement(token);
799 if (token.name() == preTag || token.name() == listingTag) {
800 processFakePEndTagIfPInButtonScope();
801 m_tree.insertHTMLElement(token);
802 m_parser->tokenizer()->setSkipLeadingNewLineForListing(true);
803 m_framesetOk = false;
806 if (token.name() == formTag) {
811 processFakePEndTagIfPInButtonScope();
812 m_tree.insertHTMLFormElement(token);
815 if (token.name() == liTag) {
816 processCloseWhenNestedTag<isLi>(token);
819 if (token.name() == ddTag || token.name() == dtTag) {
820 processCloseWhenNestedTag<isDdOrDt>(token);
823 if (token.name() == plaintextTag) {
824 processFakePEndTagIfPInButtonScope();
825 m_tree.insertHTMLElement(token);
826 m_parser->tokenizer()->setState(HTMLTokenizer::PLAINTEXTState);
829 if (token.name() == buttonTag) {
830 if (m_tree.openElements()->inScope(buttonTag)) {
832 processFakeEndTag(buttonTag);
833 reprocessStartTag(token); // FIXME: Could we just fall through here?
836 m_tree.reconstructTheActiveFormattingElements();
837 m_tree.insertHTMLElement(token);
838 m_framesetOk = false;
841 if (token.name() == aTag) {
842 Element* activeATag = m_tree.activeFormattingElements()->closestElementInScopeWithName(aTag.localName());
845 processFakeEndTag(aTag);
846 m_tree.activeFormattingElements()->remove(activeATag);
847 if (m_tree.openElements()->contains(activeATag))
848 m_tree.openElements()->remove(activeATag);
850 m_tree.reconstructTheActiveFormattingElements();
851 m_tree.insertFormattingElement(token);
854 if (isNonAnchorNonNobrFormattingTag(token.name())) {
855 m_tree.reconstructTheActiveFormattingElements();
856 m_tree.insertFormattingElement(token);
859 if (token.name() == nobrTag) {
860 m_tree.reconstructTheActiveFormattingElements();
861 if (m_tree.openElements()->inScope(nobrTag)) {
863 processFakeEndTag(nobrTag);
864 m_tree.reconstructTheActiveFormattingElements();
866 m_tree.insertFormattingElement(token);
869 if (token.name() == appletTag
870 || token.name() == marqueeTag
871 || token.name() == objectTag) {
872 m_tree.reconstructTheActiveFormattingElements();
873 m_tree.insertHTMLElement(token);
874 m_tree.activeFormattingElements()->appendMarker();
875 m_framesetOk = false;
878 if (token.name() == tableTag) {
879 if (!m_document->inQuirksMode() && m_tree.openElements()->inButtonScope(pTag))
880 processFakeEndTag(pTag);
881 m_tree.insertHTMLElement(token);
882 m_framesetOk = false;
883 setInsertionMode(InTableMode);
886 if (token.name() == imageTag) {
888 // Apparently we're not supposed to ask.
889 token.setName(imgTag.localName());
890 prepareToReprocessToken();
891 // Note the fall through to the imgTag handling below!
893 if (token.name() == areaTag
894 || token.name() == brTag
895 || token.name() == embedTag
896 || token.name() == imgTag
897 || token.name() == keygenTag
898 || token.name() == wbrTag) {
899 m_tree.reconstructTheActiveFormattingElements();
900 m_tree.insertSelfClosingHTMLElement(token);
901 m_framesetOk = false;
904 if (token.name() == inputTag) {
905 RefPtr<Attribute> typeAttribute = token.getAttributeItem(typeAttr);
906 m_tree.reconstructTheActiveFormattingElements();
907 m_tree.insertSelfClosingHTMLElement(token);
908 if (!typeAttribute || !equalIgnoringCase(typeAttribute->value(), "hidden"))
909 m_framesetOk = false;
912 if (token.name() == paramTag
913 || token.name() == sourceTag
914 || token.name() == trackTag) {
915 m_tree.insertSelfClosingHTMLElement(token);
918 if (token.name() == hrTag) {
919 processFakePEndTagIfPInButtonScope();
920 m_tree.insertSelfClosingHTMLElement(token);
921 m_framesetOk = false;
924 if (token.name() == isindexTag) {
925 processIsindexStartTagForInBody(token);
928 if (token.name() == textareaTag) {
929 m_tree.insertHTMLElement(token);
930 m_parser->tokenizer()->setSkipLeadingNewLineForListing(true);
931 m_parser->tokenizer()->setState(HTMLTokenizer::RCDATAState);
932 m_originalInsertionMode = m_insertionMode;
933 m_framesetOk = false;
934 setInsertionMode(TextMode);
937 if (token.name() == xmpTag) {
938 processFakePEndTagIfPInButtonScope();
939 m_tree.reconstructTheActiveFormattingElements();
940 m_framesetOk = false;
941 processGenericRawTextStartTag(token);
944 if (token.name() == iframeTag) {
945 m_framesetOk = false;
946 processGenericRawTextStartTag(token);
949 if (token.name() == noembedTag && pluginsEnabled(m_document->frame())) {
950 processGenericRawTextStartTag(token);
953 if (token.name() == noscriptTag && scriptEnabled(m_document->frame())) {
954 processGenericRawTextStartTag(token);
957 if (token.name() == selectTag) {
958 m_tree.reconstructTheActiveFormattingElements();
959 m_tree.insertHTMLElement(token);
960 m_framesetOk = false;
961 if (m_insertionMode == InTableMode
962 || m_insertionMode == InCaptionMode
963 || m_insertionMode == InColumnGroupMode
964 || m_insertionMode == InTableBodyMode
965 || m_insertionMode == InRowMode
966 || m_insertionMode == InCellMode)
967 setInsertionMode(InSelectInTableMode);
969 setInsertionMode(InSelectMode);
972 if (token.name() == optgroupTag || token.name() == optionTag) {
973 if (m_tree.openElements()->inScope(optionTag.localName())) {
974 AtomicHTMLToken endOption(HTMLToken::EndTag, optionTag.localName());
975 processEndTag(endOption);
977 m_tree.reconstructTheActiveFormattingElements();
978 m_tree.insertHTMLElement(token);
981 if (token.name() == rpTag || token.name() == rtTag) {
982 if (m_tree.openElements()->inScope(rubyTag.localName())) {
983 m_tree.generateImpliedEndTags();
984 if (!m_tree.currentElement()->hasTagName(rubyTag)) {
986 m_tree.openElements()->popUntil(rubyTag.localName());
989 m_tree.insertHTMLElement(token);
992 if (token.name() == MathMLNames::mathTag.localName()) {
993 m_tree.reconstructTheActiveFormattingElements();
994 adjustMathMLAttributes(token);
995 adjustForeignAttributes(token);
996 m_tree.insertForeignElement(token, MathMLNames::mathmlNamespaceURI);
997 if (m_insertionMode != InForeignContentMode)
998 setInsertionMode(InForeignContentMode);
1001 if (token.name() == SVGNames::svgTag.localName()) {
1002 m_tree.reconstructTheActiveFormattingElements();
1003 adjustSVGAttributes(token);
1004 adjustForeignAttributes(token);
1005 m_tree.insertForeignElement(token, SVGNames::svgNamespaceURI);
1006 if (m_insertionMode != InForeignContentMode)
1007 setInsertionMode(InForeignContentMode);
1010 if (isCaptionColOrColgroupTag(token.name())
1011 || token.name() == frameTag
1012 || token.name() == headTag
1013 || isTableBodyContextTag(token.name())
1014 || isTableCellContextTag(token.name())
1015 || token.name() == trTag) {
1019 m_tree.reconstructTheActiveFormattingElements();
1020 m_tree.insertHTMLElement(token);
1023 bool HTMLTreeBuilder::processColgroupEndTagForInColumnGroup()
1025 if (m_tree.currentElement() == m_tree.openElements()->htmlElement()) {
1026 ASSERT(isParsingFragment());
1027 // FIXME: parse error
1030 m_tree.openElements()->pop();
1031 setInsertionMode(InTableMode);
1035 // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#close-the-cell
1036 void HTMLTreeBuilder::closeTheCell()
1038 ASSERT(insertionMode() == InCellMode);
1039 if (m_tree.openElements()->inTableScope(tdTag)) {
1040 ASSERT(!m_tree.openElements()->inTableScope(thTag));
1041 processFakeEndTag(tdTag);
1044 ASSERT(m_tree.openElements()->inTableScope(thTag));
1045 processFakeEndTag(thTag);
1046 ASSERT(insertionMode() == InRowMode);
1049 void HTMLTreeBuilder::processStartTagForInTable(AtomicHTMLToken& token)
1051 ASSERT(token.type() == HTMLToken::StartTag);
1052 if (token.name() == captionTag) {
1053 m_tree.openElements()->popUntilTableScopeMarker();
1054 m_tree.activeFormattingElements()->appendMarker();
1055 m_tree.insertHTMLElement(token);
1056 setInsertionMode(InCaptionMode);
1059 if (token.name() == colgroupTag) {
1060 m_tree.openElements()->popUntilTableScopeMarker();
1061 m_tree.insertHTMLElement(token);
1062 setInsertionMode(InColumnGroupMode);
1065 if (token.name() == colTag) {
1066 processFakeStartTag(colgroupTag);
1067 ASSERT(InColumnGroupMode);
1068 reprocessStartTag(token);
1071 if (isTableBodyContextTag(token.name())) {
1072 m_tree.openElements()->popUntilTableScopeMarker();
1073 m_tree.insertHTMLElement(token);
1074 setInsertionMode(InTableBodyMode);
1077 if (isTableCellContextTag(token.name())
1078 || token.name() == trTag) {
1079 processFakeStartTag(tbodyTag);
1080 ASSERT(insertionMode() == InTableBodyMode);
1081 reprocessStartTag(token);
1084 if (token.name() == tableTag) {
1086 if (!processTableEndTagForInTable()) {
1087 ASSERT(isParsingFragment());
1090 reprocessStartTag(token);
1093 if (token.name() == styleTag || token.name() == scriptTag) {
1094 processStartTagForInHead(token);
1097 if (token.name() == inputTag) {
1098 Attribute* typeAttribute = token.getAttributeItem(typeAttr);
1099 if (typeAttribute && equalIgnoringCase(typeAttribute->value(), "hidden")) {
1101 m_tree.insertSelfClosingHTMLElement(token);
1104 // Fall through to "anything else" case.
1106 if (token.name() == formTag) {
1110 m_tree.insertHTMLFormElement(token, true);
1111 m_tree.openElements()->pop();
1115 HTMLConstructionSite::RedirectToFosterParentGuard redirecter(m_tree);
1116 processStartTagForInBody(token);
1121 bool shouldProcessForeignContentUsingInBodyInsertionMode(AtomicHTMLToken& token, Element* currentElement)
1123 ASSERT(token.type() == HTMLToken::StartTag);
1124 if (currentElement->hasTagName(MathMLNames::miTag)
1125 || currentElement->hasTagName(MathMLNames::moTag)
1126 || currentElement->hasTagName(MathMLNames::mnTag)
1127 || currentElement->hasTagName(MathMLNames::msTag)
1128 || currentElement->hasTagName(MathMLNames::mtextTag)) {
1129 return token.name() != MathMLNames::mglyphTag
1130 && token.name() != MathMLNames::malignmarkTag;
1132 if (currentElement->hasTagName(MathMLNames::annotation_xmlTag))
1133 return token.name() == SVGNames::svgTag;
1134 if (currentElement->hasTagName(SVGNames::foreignObjectTag)
1135 || currentElement->hasTagName(SVGNames::descTag)
1136 || currentElement->hasTagName(SVGNames::titleTag))
1138 return currentElement->namespaceURI() == HTMLNames::xhtmlNamespaceURI;
1143 void HTMLTreeBuilder::processStartTag(AtomicHTMLToken& token)
1145 ASSERT(token.type() == HTMLToken::StartTag);
1146 switch (insertionMode()) {
1148 ASSERT(insertionMode() == InitialMode);
1149 defaultForInitial();
1151 case BeforeHTMLMode:
1152 ASSERT(insertionMode() == BeforeHTMLMode);
1153 if (token.name() == htmlTag) {
1154 m_tree.insertHTMLHtmlStartTagBeforeHTML(token);
1155 setInsertionMode(BeforeHeadMode);
1158 defaultForBeforeHTML();
1160 case BeforeHeadMode:
1161 ASSERT(insertionMode() == BeforeHeadMode);
1162 if (token.name() == htmlTag) {
1163 m_tree.insertHTMLHtmlStartTagInBody(token);
1166 if (token.name() == headTag) {
1167 m_tree.insertHTMLHeadElement(token);
1168 setInsertionMode(InHeadMode);
1171 defaultForBeforeHead();
1174 ASSERT(insertionMode() == InHeadMode);
1175 if (processStartTagForInHead(token))
1180 ASSERT(insertionMode() == AfterHeadMode);
1181 if (token.name() == htmlTag) {
1182 m_tree.insertHTMLHtmlStartTagInBody(token);
1185 if (token.name() == bodyTag) {
1186 m_framesetOk = false;
1187 m_tree.insertHTMLBodyElement(token);
1188 setInsertionMode(InBodyMode);
1191 if (token.name() == framesetTag) {
1192 m_tree.insertHTMLElement(token);
1193 setInsertionMode(InFramesetMode);
1196 if (token.name() == baseTag
1197 || token.name() == basefontTag
1198 || token.name() == bgsoundTag
1199 || token.name() == linkTag
1200 || token.name() == metaTag
1201 || token.name() == noframesTag
1202 || token.name() == scriptTag
1203 || token.name() == styleTag
1204 || token.name() == titleTag) {
1206 ASSERT(m_tree.head());
1207 m_tree.openElements()->pushHTMLHeadElement(m_tree.head());
1208 processStartTagForInHead(token);
1209 m_tree.openElements()->removeHTMLHeadElement(m_tree.head());
1212 if (token.name() == headTag) {
1216 defaultForAfterHead();
1219 ASSERT(insertionMode() == InBodyMode);
1220 processStartTagForInBody(token);
1223 ASSERT(insertionMode() == InTableMode);
1224 processStartTagForInTable(token);
1227 ASSERT(insertionMode() == InCaptionMode);
1228 if (isCaptionColOrColgroupTag(token.name())
1229 || isTableBodyContextTag(token.name())
1230 || isTableCellContextTag(token.name())
1231 || token.name() == trTag) {
1233 if (!processCaptionEndTagForInCaption()) {
1234 ASSERT(isParsingFragment());
1237 reprocessStartTag(token);
1240 processStartTagForInBody(token);
1242 case InColumnGroupMode:
1243 ASSERT(insertionMode() == InColumnGroupMode);
1244 if (token.name() == htmlTag) {
1245 m_tree.insertHTMLHtmlStartTagInBody(token);
1248 if (token.name() == colTag) {
1249 m_tree.insertSelfClosingHTMLElement(token);
1252 if (!processColgroupEndTagForInColumnGroup()) {
1253 ASSERT(isParsingFragment());
1256 reprocessStartTag(token);
1258 case InTableBodyMode:
1259 ASSERT(insertionMode() == InTableBodyMode);
1260 if (token.name() == trTag) {
1261 m_tree.openElements()->popUntilTableBodyScopeMarker(); // How is there ever anything to pop?
1262 m_tree.insertHTMLElement(token);
1263 setInsertionMode(InRowMode);
1266 if (isTableCellContextTag(token.name())) {
1268 processFakeStartTag(trTag);
1269 ASSERT(insertionMode() == InRowMode);
1270 reprocessStartTag(token);
1273 if (isCaptionColOrColgroupTag(token.name()) || isTableBodyContextTag(token.name())) {
1274 // FIXME: This is slow.
1275 if (!m_tree.openElements()->inTableScope(tbodyTag.localName()) && !m_tree.openElements()->inTableScope(theadTag.localName()) && !m_tree.openElements()->inTableScope(tfootTag.localName())) {
1276 ASSERT(isParsingFragment());
1280 m_tree.openElements()->popUntilTableBodyScopeMarker();
1281 ASSERT(isTableBodyContextTag(m_tree.currentElement()->localName()));
1282 processFakeEndTag(m_tree.currentElement()->tagQName());
1283 reprocessStartTag(token);
1286 processStartTagForInTable(token);
1289 ASSERT(insertionMode() == InRowMode);
1290 if (isTableCellContextTag(token.name())) {
1291 m_tree.openElements()->popUntilTableRowScopeMarker();
1292 m_tree.insertHTMLElement(token);
1293 setInsertionMode(InCellMode);
1294 m_tree.activeFormattingElements()->appendMarker();
1297 if (token.name() == trTag
1298 || isCaptionColOrColgroupTag(token.name())
1299 || isTableBodyContextTag(token.name())) {
1300 if (!processTrEndTagForInRow()) {
1301 ASSERT(isParsingFragment());
1304 ASSERT(insertionMode() == InTableBodyMode);
1305 reprocessStartTag(token);
1308 processStartTagForInTable(token);
1311 ASSERT(insertionMode() == InCellMode);
1312 if (isCaptionColOrColgroupTag(token.name())
1313 || isTableCellContextTag(token.name())
1314 || token.name() == trTag
1315 || isTableBodyContextTag(token.name())) {
1316 // FIXME: This could be more efficient.
1317 if (!m_tree.openElements()->inTableScope(tdTag) && !m_tree.openElements()->inTableScope(thTag)) {
1318 ASSERT(isParsingFragment());
1323 reprocessStartTag(token);
1326 processStartTagForInBody(token);
1329 case AfterAfterBodyMode:
1330 ASSERT(insertionMode() == AfterBodyMode || insertionMode() == AfterAfterBodyMode);
1331 if (token.name() == htmlTag) {
1332 m_tree.insertHTMLHtmlStartTagInBody(token);
1335 setInsertionMode(InBodyMode);
1336 reprocessStartTag(token);
1338 case InHeadNoscriptMode:
1339 ASSERT(insertionMode() == InHeadNoscriptMode);
1340 if (token.name() == htmlTag) {
1341 m_tree.insertHTMLHtmlStartTagInBody(token);
1344 if (token.name() == basefontTag
1345 || token.name() == bgsoundTag
1346 || token.name() == linkTag
1347 || token.name() == metaTag
1348 || token.name() == noframesTag
1349 || token.name() == styleTag) {
1350 bool didProcess = processStartTagForInHead(token);
1351 ASSERT_UNUSED(didProcess, didProcess);
1354 if (token.name() == htmlTag || token.name() == noscriptTag) {
1358 defaultForInHeadNoscript();
1359 processToken(token);
1361 case InFramesetMode:
1362 ASSERT(insertionMode() == InFramesetMode);
1363 if (token.name() == htmlTag) {
1364 m_tree.insertHTMLHtmlStartTagInBody(token);
1367 if (token.name() == framesetTag) {
1368 m_tree.insertHTMLElement(token);
1371 if (token.name() == frameTag) {
1372 m_tree.insertSelfClosingHTMLElement(token);
1375 if (token.name() == noframesTag) {
1376 processStartTagForInHead(token);
1381 case AfterFramesetMode:
1382 case AfterAfterFramesetMode:
1383 ASSERT(insertionMode() == AfterFramesetMode || insertionMode() == AfterAfterFramesetMode);
1384 if (token.name() == htmlTag) {
1385 m_tree.insertHTMLHtmlStartTagInBody(token);
1388 if (token.name() == noframesTag) {
1389 processStartTagForInHead(token);
1394 case InSelectInTableMode:
1395 ASSERT(insertionMode() == InSelectInTableMode);
1396 if (token.name() == captionTag
1397 || token.name() == tableTag
1398 || isTableBodyContextTag(token.name())
1399 || token.name() == trTag
1400 || isTableCellContextTag(token.name())) {
1402 AtomicHTMLToken endSelect(HTMLToken::EndTag, selectTag.localName());
1403 processEndTag(endSelect);
1404 reprocessStartTag(token);
1409 ASSERT(insertionMode() == InSelectMode || insertionMode() == InSelectInTableMode);
1410 if (token.name() == htmlTag) {
1411 m_tree.insertHTMLHtmlStartTagInBody(token);
1414 if (token.name() == optionTag) {
1415 if (m_tree.currentElement()->hasTagName(optionTag)) {
1416 AtomicHTMLToken endOption(HTMLToken::EndTag, optionTag.localName());
1417 processEndTag(endOption);
1419 m_tree.insertHTMLElement(token);
1422 if (token.name() == optgroupTag) {
1423 if (m_tree.currentElement()->hasTagName(optionTag)) {
1424 AtomicHTMLToken endOption(HTMLToken::EndTag, optionTag.localName());
1425 processEndTag(endOption);
1427 if (m_tree.currentElement()->hasTagName(optgroupTag)) {
1428 AtomicHTMLToken endOptgroup(HTMLToken::EndTag, optgroupTag.localName());
1429 processEndTag(endOptgroup);
1431 m_tree.insertHTMLElement(token);
1434 if (token.name() == selectTag) {
1436 AtomicHTMLToken endSelect(HTMLToken::EndTag, selectTag.localName());
1437 processEndTag(endSelect);
1440 if (token.name() == inputTag
1441 || token.name() == keygenTag
1442 || token.name() == textareaTag) {
1444 if (!m_tree.openElements()->inSelectScope(selectTag)) {
1445 ASSERT(isParsingFragment());
1448 AtomicHTMLToken endSelect(HTMLToken::EndTag, selectTag.localName());
1449 processEndTag(endSelect);
1450 reprocessStartTag(token);
1453 if (token.name() == scriptTag) {
1454 bool didProcess = processStartTagForInHead(token);
1455 ASSERT_UNUSED(didProcess, didProcess);
1459 case InTableTextMode:
1460 defaultForInTableText();
1461 processStartTag(token);
1463 case InForeignContentMode: {
1464 if (shouldProcessForeignContentUsingInBodyInsertionMode(token, m_tree.currentElement())) {
1465 processForeignContentUsingInBodyModeAndResetMode(token);
1468 if (token.name() == bTag
1469 || token.name() == bigTag
1470 || token.name() == blockquoteTag
1471 || token.name() == bodyTag
1472 || token.name() == brTag
1473 || token.name() == centerTag
1474 || token.name() == codeTag
1475 || token.name() == ddTag
1476 || token.name() == divTag
1477 || token.name() == dlTag
1478 || token.name() == dtTag
1479 || token.name() == emTag
1480 || token.name() == embedTag
1481 || isNumberedHeaderTag(token.name())
1482 || token.name() == headTag
1483 || token.name() == hrTag
1484 || token.name() == iTag
1485 || token.name() == imgTag
1486 || token.name() == liTag
1487 || token.name() == listingTag
1488 || token.name() == menuTag
1489 || token.name() == metaTag
1490 || token.name() == nobrTag
1491 || token.name() == olTag
1492 || token.name() == pTag
1493 || token.name() == preTag
1494 || token.name() == rubyTag
1495 || token.name() == sTag
1496 || token.name() == smallTag
1497 || token.name() == spanTag
1498 || token.name() == strongTag
1499 || token.name() == strikeTag
1500 || token.name() == subTag
1501 || token.name() == supTag
1502 || token.name() == tableTag
1503 || token.name() == ttTag
1504 || token.name() == uTag
1505 || token.name() == ulTag
1506 || token.name() == varTag
1507 || (token.name() == fontTag && (token.getAttributeItem(colorAttr) || token.getAttributeItem(faceAttr) || token.getAttributeItem(sizeAttr)))) {
1509 m_tree.openElements()->popUntilForeignContentScopeMarker();
1510 resetInsertionModeAppropriately();
1511 reprocessStartTag(token);
1514 const AtomicString& currentNamespace = m_tree.currentElement()->namespaceURI();
1515 if (currentNamespace == MathMLNames::mathmlNamespaceURI)
1516 adjustMathMLAttributes(token);
1517 if (currentNamespace == SVGNames::svgNamespaceURI) {
1518 adjustSVGTagNameCase(token);
1519 adjustSVGAttributes(token);
1521 adjustForeignAttributes(token);
1522 m_tree.insertForeignElement(token, currentNamespace);
1526 ASSERT_NOT_REACHED();
1531 bool HTMLTreeBuilder::processBodyEndTagForInBody(AtomicHTMLToken& token)
1533 ASSERT(token.type() == HTMLToken::EndTag);
1534 ASSERT(token.name() == bodyTag);
1535 if (!m_tree.openElements()->inScope(bodyTag.localName())) {
1539 notImplemented(); // Emit a more specific parse error based on stack contents.
1540 setInsertionMode(AfterBodyMode);
1544 void HTMLTreeBuilder::processAnyOtherEndTagForInBody(AtomicHTMLToken& token)
1546 ASSERT(token.type() == HTMLToken::EndTag);
1547 HTMLElementStack::ElementRecord* record = m_tree.openElements()->topRecord();
1549 Element* node = record->element();
1550 if (node->hasLocalName(token.name())) {
1551 m_tree.generateImpliedEndTags();
1552 if (!m_tree.currentElement()->hasLocalName(token.name())) {
1554 // FIXME: This is either a bug in the spec, or a bug in our
1555 // implementation. Filed a bug with HTML5:
1556 // http://www.w3.org/Bugs/Public/show_bug.cgi?id=10080
1557 // We might have already popped the node for the token in
1558 // generateImpliedEndTags, just abort.
1559 if (!m_tree.openElements()->contains(node))
1562 m_tree.openElements()->popUntilPopped(node);
1565 if (isSpecialNode(node)) {
1569 record = record->next();
1573 // FIXME: This probably belongs on HTMLElementStack.
1574 HTMLElementStack::ElementRecord* HTMLTreeBuilder::furthestBlockForFormattingElement(Element* formattingElement)
1576 HTMLElementStack::ElementRecord* furthestBlock = 0;
1577 HTMLElementStack::ElementRecord* record = m_tree.openElements()->topRecord();
1578 for (; record; record = record->next()) {
1579 if (record->element() == formattingElement)
1580 return furthestBlock;
1581 if (isSpecialNode(record->element()))
1582 furthestBlock = record;
1584 ASSERT_NOT_REACHED();
1588 // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#parsing-main-inbody
1589 void HTMLTreeBuilder::callTheAdoptionAgency(AtomicHTMLToken& token)
1591 // The adoption agency algorithm is N^2. We limit the number of iterations
1592 // to stop from hanging the whole browser. This limit is copied from the
1593 // legacy tree builder and might need to be tweaked in the future.
1594 static const int adoptionAgencyIterationLimit = 10;
1596 for (int i = 0; i < adoptionAgencyIterationLimit; ++i) {
1598 Element* formattingElement = m_tree.activeFormattingElements()->closestElementInScopeWithName(token.name());
1599 if (!formattingElement || ((m_tree.openElements()->contains(formattingElement)) && !m_tree.openElements()->inScope(formattingElement))) {
1601 notImplemented(); // Check the stack of open elements for a more specific parse error.
1604 HTMLElementStack::ElementRecord* formattingElementRecord = m_tree.openElements()->find(formattingElement);
1605 if (!formattingElementRecord) {
1607 m_tree.activeFormattingElements()->remove(formattingElement);
1610 if (formattingElement != m_tree.currentElement())
1613 HTMLElementStack::ElementRecord* furthestBlock = furthestBlockForFormattingElement(formattingElement);
1615 if (!furthestBlock) {
1616 m_tree.openElements()->popUntilPopped(formattingElement);
1617 m_tree.activeFormattingElements()->remove(formattingElement);
1621 ASSERT(furthestBlock->isAbove(formattingElementRecord));
1622 Element* commonAncestor = formattingElementRecord->next()->element();
1624 HTMLFormattingElementList::Bookmark bookmark = m_tree.activeFormattingElements()->bookmarkFor(formattingElement);
1626 HTMLElementStack::ElementRecord* node = furthestBlock;
1627 HTMLElementStack::ElementRecord* nextNode = node->next();
1628 HTMLElementStack::ElementRecord* lastNode = furthestBlock;
1629 for (int i = 0; i < adoptionAgencyIterationLimit; ++i) {
1633 nextNode = node->next(); // Save node->next() for the next iteration in case node is deleted in 6.2.
1635 if (!m_tree.activeFormattingElements()->contains(node->element())) {
1636 m_tree.openElements()->remove(node->element());
1641 if (node == formattingElementRecord)
1644 RefPtr<Element> newElement = m_tree.createHTMLElementFromElementRecord(node);
1645 HTMLFormattingElementList::Entry* nodeEntry = m_tree.activeFormattingElements()->find(node->element());
1646 nodeEntry->replaceElement(newElement.get());
1647 node->replaceElement(newElement.release());
1648 // 6.4 -- Intentionally out of order to handle the case where node
1649 // was replaced in 6.5.
1650 // http://www.w3.org/Bugs/Public/show_bug.cgi?id=10096
1651 if (lastNode == furthestBlock)
1652 bookmark.moveToAfter(nodeEntry);
1654 if (Element* parent = lastNode->element()->parentElement())
1655 parent->parserRemoveChild(lastNode->element());
1656 node->element()->parserAddChild(lastNode->element());
1657 if (lastNode->element()->parentElement()->attached() && !lastNode->element()->attached())
1658 lastNode->element()->lazyAttach();
1663 const AtomicString& commonAncestorTag = commonAncestor->localName();
1664 if (Element* parent = lastNode->element()->parentElement())
1665 parent->parserRemoveChild(lastNode->element());
1666 // FIXME: If this moves to HTMLConstructionSite, this check should use
1667 // causesFosterParenting(tagName) instead.
1668 if (commonAncestorTag == tableTag
1669 || commonAncestorTag == trTag
1670 || isTableBodyContextTag(commonAncestorTag))
1671 m_tree.fosterParent(lastNode->element());
1673 commonAncestor->parserAddChild(lastNode->element());
1674 if (lastNode->element()->parentElement()->attached() && !lastNode->element()->attached())
1675 lastNode->element()->lazyAttach();
1678 RefPtr<Element> newElement = m_tree.createHTMLElementFromElementRecord(formattingElementRecord);
1680 newElement->takeAllChildrenFrom(furthestBlock->element());
1682 Element* furthestBlockElement = furthestBlock->element();
1683 // FIXME: All this creation / parserAddChild / attach business should
1684 // be in HTMLConstructionSite. My guess is that steps 8--12
1685 // should all be in some HTMLConstructionSite function.
1686 furthestBlockElement->parserAddChild(newElement);
1687 if (furthestBlockElement->attached() && !newElement->attached()) {
1688 // Notice that newElement might already be attached if, for example, one of the reparented
1689 // children is a style element, which attaches itself automatically.
1690 newElement->attach();
1693 m_tree.activeFormattingElements()->swapTo(formattingElement, newElement.get(), bookmark);
1695 m_tree.openElements()->remove(formattingElement);
1696 m_tree.openElements()->insertAbove(newElement, furthestBlock);
1700 void HTMLTreeBuilder::resetInsertionModeAppropriately()
1702 // http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#reset-the-insertion-mode-appropriately
1704 HTMLElementStack::ElementRecord* nodeRecord = m_tree.openElements()->topRecord();
1706 Element* node = nodeRecord->element();
1707 if (node == m_tree.openElements()->bottom()) {
1708 ASSERT(isParsingFragment());
1710 node = m_fragmentContext.contextElement();
1712 if (node->hasTagName(selectTag)) {
1713 ASSERT(isParsingFragment());
1714 return setInsertionMode(InSelectMode);
1716 if (node->hasTagName(tdTag) || node->hasTagName(thTag))
1717 return setInsertionMode(InCellMode);
1718 if (node->hasTagName(trTag))
1719 return setInsertionMode(InRowMode);
1720 if (node->hasTagName(tbodyTag) || node->hasTagName(theadTag) || node->hasTagName(tfootTag))
1721 return setInsertionMode(InTableBodyMode);
1722 if (node->hasTagName(captionTag))
1723 return setInsertionMode(InCaptionMode);
1724 if (node->hasTagName(colgroupTag)) {
1725 ASSERT(isParsingFragment());
1726 return setInsertionMode(InColumnGroupMode);
1728 if (node->hasTagName(tableTag))
1729 return setInsertionMode(InTableMode);
1730 if (node->hasTagName(headTag)) {
1731 ASSERT(isParsingFragment());
1732 return setInsertionMode(InBodyMode);
1734 if (node->hasTagName(bodyTag))
1735 return setInsertionMode(InBodyMode);
1736 if (node->hasTagName(framesetTag)) {
1737 ASSERT(isParsingFragment());
1738 return setInsertionMode(InFramesetMode);
1740 if (node->hasTagName(htmlTag)) {
1741 ASSERT(isParsingFragment());
1742 return setInsertionMode(BeforeHeadMode);
1744 if (node->namespaceURI() == SVGNames::svgNamespaceURI
1745 || node->namespaceURI() == MathMLNames::mathmlNamespaceURI)
1746 return setInsertionMode(InForeignContentMode);
1748 ASSERT(isParsingFragment());
1749 return setInsertionMode(InBodyMode);
1751 nodeRecord = nodeRecord->next();
1755 void HTMLTreeBuilder::processEndTagForInTableBody(AtomicHTMLToken& token)
1757 ASSERT(token.type() == HTMLToken::EndTag);
1758 if (isTableBodyContextTag(token.name())) {
1759 if (!m_tree.openElements()->inTableScope(token.name())) {
1763 m_tree.openElements()->popUntilTableBodyScopeMarker();
1764 m_tree.openElements()->pop();
1765 setInsertionMode(InTableMode);
1768 if (token.name() == tableTag) {
1769 // FIXME: This is slow.
1770 if (!m_tree.openElements()->inTableScope(tbodyTag.localName()) && !m_tree.openElements()->inTableScope(theadTag.localName()) && !m_tree.openElements()->inTableScope(tfootTag.localName())) {
1771 ASSERT(isParsingFragment());
1775 m_tree.openElements()->popUntilTableBodyScopeMarker();
1776 ASSERT(isTableBodyContextTag(m_tree.currentElement()->localName()));
1777 processFakeEndTag(m_tree.currentElement()->tagQName());
1778 reprocessEndTag(token);
1781 if (token.name() == bodyTag
1782 || isCaptionColOrColgroupTag(token.name())
1783 || token.name() == htmlTag
1784 || isTableCellContextTag(token.name())
1785 || token.name() == trTag) {
1789 processEndTagForInTable(token);
1792 void HTMLTreeBuilder::processEndTagForInRow(AtomicHTMLToken& token)
1794 ASSERT(token.type() == HTMLToken::EndTag);
1795 if (token.name() == trTag) {
1796 processTrEndTagForInRow();
1799 if (token.name() == tableTag) {
1800 if (!processTrEndTagForInRow()) {
1801 ASSERT(isParsingFragment());
1804 ASSERT(insertionMode() == InTableBodyMode);
1805 reprocessEndTag(token);
1808 if (isTableBodyContextTag(token.name())) {
1809 if (!m_tree.openElements()->inTableScope(token.name())) {
1813 processFakeEndTag(trTag);
1814 ASSERT(insertionMode() == InTableBodyMode);
1815 reprocessEndTag(token);
1818 if (token.name() == bodyTag
1819 || isCaptionColOrColgroupTag(token.name())
1820 || token.name() == htmlTag
1821 || isTableCellContextTag(token.name())) {
1825 processEndTagForInTable(token);
1828 void HTMLTreeBuilder::processEndTagForInCell(AtomicHTMLToken& token)
1830 ASSERT(token.type() == HTMLToken::EndTag);
1831 if (isTableCellContextTag(token.name())) {
1832 if (!m_tree.openElements()->inTableScope(token.name())) {
1836 m_tree.generateImpliedEndTags();
1837 if (!m_tree.currentElement()->hasLocalName(token.name()))
1839 m_tree.openElements()->popUntilPopped(token.name());
1840 m_tree.activeFormattingElements()->clearToLastMarker();
1841 setInsertionMode(InRowMode);
1844 if (token.name() == bodyTag
1845 || isCaptionColOrColgroupTag(token.name())
1846 || token.name() == htmlTag) {
1850 if (token.name() == tableTag
1851 || token.name() == trTag
1852 || isTableBodyContextTag(token.name())) {
1853 if (!m_tree.openElements()->inTableScope(token.name())) {
1854 ASSERT(isTableBodyContextTag(token.name()) || isParsingFragment());
1859 reprocessEndTag(token);
1862 processEndTagForInBody(token);
1865 void HTMLTreeBuilder::processEndTagForInBody(AtomicHTMLToken& token)
1867 ASSERT(token.type() == HTMLToken::EndTag);
1868 if (token.name() == bodyTag) {
1869 processBodyEndTagForInBody(token);
1872 if (token.name() == htmlTag) {
1873 AtomicHTMLToken endBody(HTMLToken::EndTag, bodyTag.localName());
1874 if (processBodyEndTagForInBody(endBody))
1875 reprocessEndTag(token);
1878 if (token.name() == addressTag
1879 || token.name() == articleTag
1880 || token.name() == asideTag
1881 || token.name() == blockquoteTag
1882 || token.name() == buttonTag
1883 || token.name() == centerTag
1884 || token.name() == detailsTag
1885 || token.name() == dirTag
1886 || token.name() == divTag
1887 || token.name() == dlTag
1888 || token.name() == fieldsetTag
1889 || token.name() == figcaptionTag
1890 || token.name() == figureTag
1891 || token.name() == footerTag
1892 || token.name() == headerTag
1893 || token.name() == hgroupTag
1894 || token.name() == listingTag
1895 || token.name() == menuTag
1896 || token.name() == navTag
1897 || token.name() == olTag
1898 || token.name() == preTag
1899 || token.name() == sectionTag
1900 || token.name() == summaryTag
1901 || token.name() == ulTag) {
1902 if (!m_tree.openElements()->inScope(token.name())) {
1906 m_tree.generateImpliedEndTags();
1907 if (!m_tree.currentElement()->hasLocalName(token.name()))
1909 m_tree.openElements()->popUntilPopped(token.name());
1912 if (token.name() == formTag) {
1913 RefPtr<Element> node = m_tree.takeForm();
1914 if (!node || !m_tree.openElements()->inScope(node.get())) {
1918 m_tree.generateImpliedEndTags();
1919 if (m_tree.currentElement() != node.get())
1921 m_tree.openElements()->remove(node.get());
1923 if (token.name() == pTag) {
1924 if (!m_tree.openElements()->inButtonScope(token.name())) {
1926 processFakeStartTag(pTag);
1927 ASSERT(m_tree.openElements()->inScope(token.name()));
1928 reprocessEndTag(token);
1931 m_tree.generateImpliedEndTagsWithExclusion(token.name());
1932 if (!m_tree.currentElement()->hasLocalName(token.name()))
1934 m_tree.openElements()->popUntilPopped(token.name());
1937 if (token.name() == liTag) {
1938 if (!m_tree.openElements()->inListItemScope(token.name())) {
1942 m_tree.generateImpliedEndTagsWithExclusion(token.name());
1943 if (!m_tree.currentElement()->hasLocalName(token.name()))
1945 m_tree.openElements()->popUntilPopped(token.name());
1948 if (token.name() == ddTag
1949 || token.name() == dtTag) {
1950 if (!m_tree.openElements()->inScope(token.name())) {
1954 m_tree.generateImpliedEndTagsWithExclusion(token.name());
1955 if (!m_tree.currentElement()->hasLocalName(token.name()))
1957 m_tree.openElements()->popUntilPopped(token.name());
1960 if (isNumberedHeaderTag(token.name())) {
1961 if (!m_tree.openElements()->hasNumberedHeaderElementInScope()) {
1965 m_tree.generateImpliedEndTags();
1966 if (!m_tree.currentElement()->hasLocalName(token.name()))
1968 m_tree.openElements()->popUntilNumberedHeaderElementPopped();
1971 if (isFormattingTag(token.name())) {
1972 callTheAdoptionAgency(token);
1975 if (token.name() == appletTag
1976 || token.name() == marqueeTag
1977 || token.name() == objectTag) {
1978 if (!m_tree.openElements()->inScope(token.name())) {
1982 m_tree.generateImpliedEndTags();
1983 if (!m_tree.currentElement()->hasLocalName(token.name()))
1985 m_tree.openElements()->popUntilPopped(token.name());
1986 m_tree.activeFormattingElements()->clearToLastMarker();
1989 if (token.name() == brTag) {
1991 processFakeStartTag(brTag);
1994 processAnyOtherEndTagForInBody(token);
1997 bool HTMLTreeBuilder::processCaptionEndTagForInCaption()
1999 if (!m_tree.openElements()->inTableScope(captionTag.localName())) {
2000 ASSERT(isParsingFragment());
2001 // FIXME: parse error
2004 m_tree.generateImpliedEndTags();
2005 // FIXME: parse error if (!m_tree.currentElement()->hasTagName(captionTag))
2006 m_tree.openElements()->popUntilPopped(captionTag.localName());
2007 m_tree.activeFormattingElements()->clearToLastMarker();
2008 setInsertionMode(InTableMode);
2012 bool HTMLTreeBuilder::processTrEndTagForInRow()
2014 if (!m_tree.openElements()->inTableScope(trTag.localName())) {
2015 ASSERT(isParsingFragment());
2016 // FIXME: parse error
2019 m_tree.openElements()->popUntilTableRowScopeMarker();
2020 ASSERT(m_tree.currentElement()->hasTagName(trTag));
2021 m_tree.openElements()->pop();
2022 setInsertionMode(InTableBodyMode);
2026 bool HTMLTreeBuilder::processTableEndTagForInTable()
2028 if (!m_tree.openElements()->inTableScope(tableTag)) {
2029 ASSERT(isParsingFragment());
2030 // FIXME: parse error.
2033 m_tree.openElements()->popUntilPopped(tableTag.localName());
2034 resetInsertionModeAppropriately();
2038 void HTMLTreeBuilder::processEndTagForInTable(AtomicHTMLToken& token)
2040 ASSERT(token.type() == HTMLToken::EndTag);
2041 if (token.name() == tableTag) {
2042 processTableEndTagForInTable();
2045 if (token.name() == bodyTag
2046 || isCaptionColOrColgroupTag(token.name())
2047 || token.name() == htmlTag
2048 || isTableBodyContextTag(token.name())
2049 || isTableCellContextTag(token.name())
2050 || token.name() == trTag) {
2054 // Is this redirection necessary here?
2055 HTMLConstructionSite::RedirectToFosterParentGuard redirecter(m_tree);
2056 processEndTagForInBody(token);
2059 void HTMLTreeBuilder::processEndTag(AtomicHTMLToken& token)
2061 ASSERT(token.type() == HTMLToken::EndTag);
2062 switch (insertionMode()) {
2064 ASSERT(insertionMode() == InitialMode);
2065 defaultForInitial();
2067 case BeforeHTMLMode:
2068 ASSERT(insertionMode() == BeforeHTMLMode);
2069 if (token.name() != headTag && token.name() != bodyTag && token.name() != htmlTag && token.name() != brTag) {
2073 defaultForBeforeHTML();
2075 case BeforeHeadMode:
2076 ASSERT(insertionMode() == BeforeHeadMode);
2077 if (token.name() != headTag && token.name() != bodyTag && token.name() != htmlTag && token.name() != brTag) {
2081 defaultForBeforeHead();
2084 ASSERT(insertionMode() == InHeadMode);
2085 if (token.name() == headTag) {
2086 m_tree.openElements()->popHTMLHeadElement();
2087 setInsertionMode(AfterHeadMode);
2090 if (token.name() != bodyTag && token.name() != htmlTag && token.name() != brTag) {
2097 ASSERT(insertionMode() == AfterHeadMode);
2098 if (token.name() != bodyTag && token.name() != htmlTag && token.name() != brTag) {
2102 defaultForAfterHead();
2105 ASSERT(insertionMode() == InBodyMode);
2106 processEndTagForInBody(token);
2109 ASSERT(insertionMode() == InTableMode);
2110 processEndTagForInTable(token);
2113 ASSERT(insertionMode() == InCaptionMode);
2114 if (token.name() == captionTag) {
2115 processCaptionEndTagForInCaption();
2118 if (token.name() == tableTag) {
2120 if (!processCaptionEndTagForInCaption()) {
2121 ASSERT(isParsingFragment());
2124 reprocessEndTag(token);
2127 if (token.name() == bodyTag
2128 || token.name() == colTag
2129 || token.name() == colgroupTag
2130 || token.name() == htmlTag
2131 || isTableBodyContextTag(token.name())
2132 || isTableCellContextTag(token.name())
2133 || token.name() == trTag) {
2137 processEndTagForInBody(token);
2139 case InColumnGroupMode:
2140 ASSERT(insertionMode() == InColumnGroupMode);
2141 if (token.name() == colgroupTag) {
2142 processColgroupEndTagForInColumnGroup();
2145 if (token.name() == colTag) {
2149 if (!processColgroupEndTagForInColumnGroup()) {
2150 ASSERT(isParsingFragment());
2153 reprocessEndTag(token);
2156 ASSERT(insertionMode() == InRowMode);
2157 processEndTagForInRow(token);
2160 ASSERT(insertionMode() == InCellMode);
2161 processEndTagForInCell(token);
2163 case InTableBodyMode:
2164 ASSERT(insertionMode() == InTableBodyMode);
2165 processEndTagForInTableBody(token);
2168 ASSERT(insertionMode() == AfterBodyMode);
2169 if (token.name() == htmlTag) {
2170 if (isParsingFragment()) {
2174 setInsertionMode(AfterAfterBodyMode);
2177 prepareToReprocessToken();
2179 case AfterAfterBodyMode:
2180 ASSERT(insertionMode() == AfterBodyMode || insertionMode() == AfterAfterBodyMode);
2182 setInsertionMode(InBodyMode);
2183 reprocessEndTag(token);
2185 case InHeadNoscriptMode:
2186 ASSERT(insertionMode() == InHeadNoscriptMode);
2187 if (token.name() == noscriptTag) {
2188 ASSERT(m_tree.currentElement()->hasTagName(noscriptTag));
2189 m_tree.openElements()->pop();
2190 ASSERT(m_tree.currentElement()->hasTagName(headTag));
2191 setInsertionMode(InHeadMode);
2194 if (token.name() != brTag) {
2198 defaultForInHeadNoscript();
2199 processToken(token);
2202 if (token.name() == scriptTag) {
2203 // Pause ourselves so that parsing stops until the script can be processed by the caller.
2205 ASSERT(m_tree.currentElement()->hasTagName(scriptTag));
2206 m_scriptToProcess = m_tree.currentElement();
2207 m_scriptToProcessStartPosition = WTF::toOneBasedTextPosition(m_lastScriptElementStartPosition);
2208 m_tree.openElements()->pop();
2209 if (isParsingFragment() && m_fragmentContext.scriptingPermission() == FragmentScriptingNotAllowed)
2210 m_scriptToProcess->removeAllChildren();
2211 setInsertionMode(m_originalInsertionMode);
2213 // This token will not have been created by the tokenizer if a
2214 // self-closing script tag was encountered and pre-HTML5 parser
2215 // quirks are enabled. We must set the tokenizer's state to
2216 // DataState explicitly if the tokenizer didn't have a chance to.
2217 ASSERT(m_parser->tokenizer()->state() == HTMLTokenizer::DataState || m_usePreHTML5ParserQuirks);
2218 m_parser->tokenizer()->setState(HTMLTokenizer::DataState);
2221 m_tree.openElements()->pop();
2222 setInsertionMode(m_originalInsertionMode);
2224 case InFramesetMode:
2225 ASSERT(insertionMode() == InFramesetMode);
2226 if (token.name() == framesetTag) {
2227 if (m_tree.currentElement() == m_tree.openElements()->htmlElement()) {
2231 m_tree.openElements()->pop();
2232 if (!isParsingFragment() && !m_tree.currentElement()->hasTagName(framesetTag))
2233 setInsertionMode(AfterFramesetMode);
2237 case AfterFramesetMode:
2238 ASSERT(insertionMode() == AfterFramesetMode);
2239 if (token.name() == htmlTag) {
2240 setInsertionMode(AfterAfterFramesetMode);
2244 case AfterAfterFramesetMode:
2245 ASSERT(insertionMode() == AfterFramesetMode || insertionMode() == AfterAfterFramesetMode);
2248 case InSelectInTableMode:
2249 ASSERT(insertionMode() == InSelectInTableMode);
2250 if (token.name() == captionTag
2251 || token.name() == tableTag
2252 || isTableBodyContextTag(token.name())
2253 || token.name() == trTag
2254 || isTableCellContextTag(token.name())) {
2256 if (m_tree.openElements()->inTableScope(token.name())) {
2257 AtomicHTMLToken endSelect(HTMLToken::EndTag, selectTag.localName());
2258 processEndTag(endSelect);
2259 reprocessEndTag(token);
2265 ASSERT(insertionMode() == InSelectMode || insertionMode() == InSelectInTableMode);
2266 if (token.name() == optgroupTag) {
2267 if (m_tree.currentElement()->hasTagName(optionTag) && m_tree.oneBelowTop()->hasTagName(optgroupTag))
2268 processFakeEndTag(optionTag);
2269 if (m_tree.currentElement()->hasTagName(optgroupTag)) {
2270 m_tree.openElements()->pop();
2276 if (token.name() == optionTag) {
2277 if (m_tree.currentElement()->hasTagName(optionTag)) {
2278 m_tree.openElements()->pop();
2284 if (token.name() == selectTag) {
2285 if (!m_tree.openElements()->inSelectScope(token.name())) {
2286 ASSERT(isParsingFragment());
2290 m_tree.openElements()->popUntilPopped(selectTag.localName());
2291 resetInsertionModeAppropriately();
2295 case InTableTextMode:
2296 defaultForInTableText();
2297 processEndTag(token);
2299 case InForeignContentMode:
2300 if (token.name() == SVGNames::scriptTag && m_tree.currentElement()->hasTagName(SVGNames::scriptTag)) {
2304 if (m_tree.currentElement()->namespaceURI() != xhtmlNamespaceURI) {
2305 // FIXME: This code just wants an Element* iterator, instead of an ElementRecord*
2306 HTMLElementStack::ElementRecord* nodeRecord = m_tree.openElements()->topRecord();
2307 if (!nodeRecord->element()->hasLocalName(token.name()))
2310 if (nodeRecord->element()->hasLocalName(token.name())) {
2311 m_tree.openElements()->popUntilPopped(nodeRecord->element());
2312 resetForeignInsertionMode();
2315 nodeRecord = nodeRecord->next();
2316 if (nodeRecord->element()->namespaceURI() == xhtmlNamespaceURI)
2320 // Any other end tag (also the last two steps of "An end tag, if the current node is not an element in the HTML namespace."
2321 processForeignContentUsingInBodyModeAndResetMode(token);
2326 void HTMLTreeBuilder::prepareToReprocessToken()
2328 if (m_hasPendingForeignInsertionModeSteps) {
2329 resetForeignInsertionMode();
2330 m_hasPendingForeignInsertionModeSteps = false;
2334 void HTMLTreeBuilder::reprocessStartTag(AtomicHTMLToken& token)
2336 prepareToReprocessToken();
2337 processStartTag(token);
2340 void HTMLTreeBuilder::reprocessEndTag(AtomicHTMLToken& token)
2342 prepareToReprocessToken();
2343 processEndTag(token);
2346 class HTMLTreeBuilder::FakeInsertionMode {
2347 WTF_MAKE_NONCOPYABLE(FakeInsertionMode);
2349 FakeInsertionMode(HTMLTreeBuilder* treeBuilder, InsertionMode mode)
2350 : m_treeBuilder(treeBuilder)
2351 , m_originalMode(treeBuilder->insertionMode())
2353 m_treeBuilder->setFakeInsertionMode(mode);
2356 ~FakeInsertionMode()
2358 if (m_treeBuilder->isFakeInsertionMode())
2359 m_treeBuilder->setInsertionMode(m_originalMode);
2363 HTMLTreeBuilder* m_treeBuilder;
2364 InsertionMode m_originalMode;
2367 void HTMLTreeBuilder::processForeignContentUsingInBodyModeAndResetMode(AtomicHTMLToken& token)
2369 m_hasPendingForeignInsertionModeSteps = true;
2371 FakeInsertionMode fakeMode(this, InBodyMode);
2372 processToken(token);
2374 if (m_hasPendingForeignInsertionModeSteps)
2375 resetForeignInsertionMode();
2378 void HTMLTreeBuilder::resetForeignInsertionMode()
2380 if (insertionMode() == InForeignContentMode)
2381 resetInsertionModeAppropriately();
2384 void HTMLTreeBuilder::processComment(AtomicHTMLToken& token)
2386 ASSERT(token.type() == HTMLToken::Comment);
2387 if (m_insertionMode == InitialMode
2388 || m_insertionMode == BeforeHTMLMode
2389 || m_insertionMode == AfterAfterBodyMode
2390 || m_insertionMode == AfterAfterFramesetMode) {
2391 m_tree.insertCommentOnDocument(token);
2394 if (m_insertionMode == AfterBodyMode) {
2395 m_tree.insertCommentOnHTMLHtmlElement(token);
2398 if (m_insertionMode == InTableTextMode) {
2399 defaultForInTableText();
2400 processComment(token);
2403 m_tree.insertComment(token);
2406 void HTMLTreeBuilder::processCharacter(AtomicHTMLToken& token)
2408 ASSERT(token.type() == HTMLToken::Character);
2409 ExternalCharacterTokenBuffer buffer(token);
2410 processCharacterBuffer(buffer);
2413 void HTMLTreeBuilder::processCharacterBuffer(ExternalCharacterTokenBuffer& buffer)
2416 switch (insertionMode()) {
2418 ASSERT(insertionMode() == InitialMode);
2419 buffer.skipLeadingWhitespace();
2420 if (buffer.isEmpty())
2422 defaultForInitial();
2425 case BeforeHTMLMode: {
2426 ASSERT(insertionMode() == BeforeHTMLMode);
2427 buffer.skipLeadingWhitespace();
2428 if (buffer.isEmpty())
2430 defaultForBeforeHTML();
2433 case BeforeHeadMode: {
2434 ASSERT(insertionMode() == BeforeHeadMode);
2435 buffer.skipLeadingWhitespace();
2436 if (buffer.isEmpty())
2438 defaultForBeforeHead();
2442 ASSERT(insertionMode() == InHeadMode);
2443 String leadingWhitespace = buffer.takeLeadingWhitespace();
2444 if (!leadingWhitespace.isEmpty())
2445 m_tree.insertTextNode(leadingWhitespace);
2446 if (buffer.isEmpty())
2451 case AfterHeadMode: {
2452 ASSERT(insertionMode() == AfterHeadMode);
2453 String leadingWhitespace = buffer.takeLeadingWhitespace();
2454 if (!leadingWhitespace.isEmpty())
2455 m_tree.insertTextNode(leadingWhitespace);
2456 if (buffer.isEmpty())
2458 defaultForAfterHead();
2464 ASSERT(insertionMode() == InBodyMode || insertionMode() == InCaptionMode || insertionMode() == InCellMode);
2465 m_tree.reconstructTheActiveFormattingElements();
2466 String characters = buffer.takeRemaining();
2467 m_tree.insertTextNode(characters);
2468 if (m_framesetOk && !isAllWhitespaceOrReplacementCharacters(characters))
2469 m_framesetOk = false;
2473 case InTableBodyMode:
2475 ASSERT(insertionMode() == InTableMode || insertionMode() == InTableBodyMode || insertionMode() == InRowMode);
2476 ASSERT(m_pendingTableCharacters.isEmpty());
2477 m_originalInsertionMode = m_insertionMode;
2478 setInsertionMode(InTableTextMode);
2479 prepareToReprocessToken();
2482 case InTableTextMode: {
2483 buffer.giveRemainingTo(m_pendingTableCharacters);
2486 case InColumnGroupMode: {
2487 ASSERT(insertionMode() == InColumnGroupMode);
2488 String leadingWhitespace = buffer.takeLeadingWhitespace();
2489 if (!leadingWhitespace.isEmpty())
2490 m_tree.insertTextNode(leadingWhitespace);
2491 if (buffer.isEmpty())
2493 if (!processColgroupEndTagForInColumnGroup()) {
2494 ASSERT(isParsingFragment());
2495 // The spec tells us to drop these characters on the floor.
2496 buffer.takeLeadingNonWhitespace();
2497 if (buffer.isEmpty())
2500 prepareToReprocessToken();
2501 goto ReprocessBuffer;
2504 case AfterAfterBodyMode: {
2505 ASSERT(insertionMode() == AfterBodyMode || insertionMode() == AfterAfterBodyMode);
2506 // FIXME: parse error
2507 setInsertionMode(InBodyMode);
2508 prepareToReprocessToken();
2509 goto ReprocessBuffer;
2513 ASSERT(insertionMode() == TextMode);
2514 m_tree.insertTextNode(buffer.takeRemaining());
2517 case InHeadNoscriptMode: {
2518 ASSERT(insertionMode() == InHeadNoscriptMode);
2519 String leadingWhitespace = buffer.takeLeadingWhitespace();
2520 if (!leadingWhitespace.isEmpty())
2521 m_tree.insertTextNode(leadingWhitespace);
2522 if (buffer.isEmpty())
2524 defaultForInHeadNoscript();
2525 goto ReprocessBuffer;
2528 case InFramesetMode:
2529 case AfterFramesetMode: {
2530 ASSERT(insertionMode() == InFramesetMode || insertionMode() == AfterFramesetMode || insertionMode() == AfterAfterFramesetMode);
2531 String leadingWhitespace = buffer.takeRemainingWhitespace();
2532 if (!leadingWhitespace.isEmpty())
2533 m_tree.insertTextNode(leadingWhitespace);
2534 // FIXME: We should generate a parse error if we skipped over any
2535 // non-whitespace characters.
2538 case InSelectInTableMode:
2539 case InSelectMode: {
2540 ASSERT(insertionMode() == InSelectMode || insertionMode() == InSelectInTableMode);
2541 m_tree.insertTextNode(buffer.takeRemaining());
2544 case InForeignContentMode: {
2545 ASSERT(insertionMode() == InForeignContentMode);
2546 String characters = buffer.takeRemaining();
2547 m_tree.insertTextNode(characters);
2548 if (m_framesetOk && !isAllWhitespace(characters))
2549 m_framesetOk = false;
2552 case AfterAfterFramesetMode: {
2553 String leadingWhitespace = buffer.takeRemainingWhitespace();
2554 if (!leadingWhitespace.isEmpty()) {
2555 m_tree.reconstructTheActiveFormattingElements();
2556 m_tree.insertTextNode(leadingWhitespace);
2558 // FIXME: We should generate a parse error if we skipped over any
2559 // non-whitespace characters.
2565 void HTMLTreeBuilder::processEndOfFile(AtomicHTMLToken& token)
2567 ASSERT(token.type() == HTMLToken::EndOfFile);
2568 switch (insertionMode()) {
2570 ASSERT(insertionMode() == InitialMode);
2571 defaultForInitial();
2573 case BeforeHTMLMode:
2574 ASSERT(insertionMode() == BeforeHTMLMode);
2575 defaultForBeforeHTML();
2577 case BeforeHeadMode:
2578 ASSERT(insertionMode() == BeforeHeadMode);
2579 defaultForBeforeHead();
2582 ASSERT(insertionMode() == InHeadMode);
2586 ASSERT(insertionMode() == AfterHeadMode);
2587 defaultForAfterHead();
2593 ASSERT(insertionMode() == InBodyMode || insertionMode() == InCellMode || insertionMode() == InCaptionMode || insertionMode() == InRowMode);
2594 notImplemented(); // Emit parse error based on what elements are still open.
2597 case AfterAfterBodyMode:
2598 ASSERT(insertionMode() == AfterBodyMode || insertionMode() == AfterAfterBodyMode);
2600 case InHeadNoscriptMode:
2601 ASSERT(insertionMode() == InHeadNoscriptMode);
2602 defaultForInHeadNoscript();
2603 processEndOfFile(token);
2605 case AfterFramesetMode:
2606 case AfterAfterFramesetMode:
2607 ASSERT(insertionMode() == AfterFramesetMode || insertionMode() == AfterAfterFramesetMode);
2609 case InFramesetMode:
2611 case InTableBodyMode:
2612 case InSelectInTableMode:
2614 ASSERT(insertionMode() == InSelectMode || insertionMode() == InSelectInTableMode || insertionMode() == InTableMode || insertionMode() == InFramesetMode || insertionMode() == InTableBodyMode);
2615 if (m_tree.currentElement() != m_tree.openElements()->htmlElement())
2618 case InColumnGroupMode:
2619 if (m_tree.currentElement() == m_tree.openElements()->htmlElement()) {
2620 ASSERT(isParsingFragment());
2621 return; // FIXME: Should we break here instead of returning?
2623 if (!processColgroupEndTagForInColumnGroup()) {
2624 ASSERT(isParsingFragment());
2625 return; // FIXME: Should we break here instead of returning?
2627 prepareToReprocessToken();
2628 processEndOfFile(token);
2630 case InForeignContentMode:
2631 setInsertionMode(InBodyMode);
2632 processEndOfFile(token);
2634 case InTableTextMode:
2635 defaultForInTableText();
2636 processEndOfFile(token);
2640 if (m_tree.currentElement()->hasTagName(scriptTag))
2641 notImplemented(); // mark the script element as "already started".
2642 m_tree.openElements()->pop();
2643 setInsertionMode(m_originalInsertionMode);
2644 prepareToReprocessToken();
2645 processEndOfFile(token);
2648 ASSERT(m_tree.openElements()->top());
2649 m_tree.openElements()->popAll();
2652 void HTMLTreeBuilder::defaultForInitial()
2655 if (!m_fragmentContext.fragment())
2656 m_document->setCompatibilityMode(Document::QuirksMode);
2657 // FIXME: parse error
2658 setInsertionMode(BeforeHTMLMode);
2659 prepareToReprocessToken();
2662 void HTMLTreeBuilder::defaultForBeforeHTML()
2664 AtomicHTMLToken startHTML(HTMLToken::StartTag, htmlTag.localName());
2665 m_tree.insertHTMLHtmlStartTagBeforeHTML(startHTML);
2666 setInsertionMode(BeforeHeadMode);
2667 prepareToReprocessToken();
2670 void HTMLTreeBuilder::defaultForBeforeHead()
2672 AtomicHTMLToken startHead(HTMLToken::StartTag, headTag.localName());
2673 processStartTag(startHead);
2674 prepareToReprocessToken();
2677 void HTMLTreeBuilder::defaultForInHead()
2679 AtomicHTMLToken endHead(HTMLToken::EndTag, headTag.localName());
2680 processEndTag(endHead);
2681 prepareToReprocessToken();
2684 void HTMLTreeBuilder::defaultForInHeadNoscript()
2686 AtomicHTMLToken endNoscript(HTMLToken::EndTag, noscriptTag.localName());
2687 processEndTag(endNoscript);
2688 prepareToReprocessToken();
2691 void HTMLTreeBuilder::defaultForAfterHead()
2693 AtomicHTMLToken startBody(HTMLToken::StartTag, bodyTag.localName());
2694 processStartTag(startBody);
2695 m_framesetOk = true;
2696 prepareToReprocessToken();
2699 void HTMLTreeBuilder::defaultForInTableText()
2701 String characters = String::adopt(m_pendingTableCharacters);
2702 if (!isAllWhitespace(characters)) {
2703 // FIXME: parse error
2704 HTMLConstructionSite::RedirectToFosterParentGuard redirecter(m_tree);
2705 m_tree.reconstructTheActiveFormattingElements();
2706 m_tree.insertTextNode(characters);
2707 m_framesetOk = false;
2708 setInsertionMode(m_originalInsertionMode);
2709 prepareToReprocessToken();
2712 m_tree.insertTextNode(characters);
2713 setInsertionMode(m_originalInsertionMode);
2714 prepareToReprocessToken();
2717 bool HTMLTreeBuilder::processStartTagForInHead(AtomicHTMLToken& token)
2719 ASSERT(token.type() == HTMLToken::StartTag);
2720 if (token.name() == htmlTag) {
2721 m_tree.insertHTMLHtmlStartTagInBody(token);
2724 if (token.name() == baseTag
2725 || token.name() == basefontTag
2726 || token.name() == bgsoundTag
2727 || token.name() == commandTag
2728 || token.name() == linkTag
2729 || token.name() == metaTag) {
2730 m_tree.insertSelfClosingHTMLElement(token);
2731 // Note: The custom processing for the <meta> tag is done in HTMLMetaElement::process().
2734 if (token.name() == titleTag) {
2735 processGenericRCDATAStartTag(token);
2738 if (token.name() == noscriptTag) {
2739 if (scriptEnabled(m_document->frame())) {
2740 processGenericRawTextStartTag(token);
2743 m_tree.insertHTMLElement(token);
2744 setInsertionMode(InHeadNoscriptMode);
2747 if (token.name() == noframesTag || token.name() == styleTag) {
2748 processGenericRawTextStartTag(token);
2751 if (token.name() == scriptTag) {
2752 processScriptStartTag(token);
2753 if (m_usePreHTML5ParserQuirks && token.selfClosing())
2754 processFakeEndTag(scriptTag);
2757 if (token.name() == headTag) {
2764 void HTMLTreeBuilder::processGenericRCDATAStartTag(AtomicHTMLToken& token)
2766 ASSERT(token.type() == HTMLToken::StartTag);
2767 m_tree.insertHTMLElement(token);
2768 m_parser->tokenizer()->setState(HTMLTokenizer::RCDATAState);
2769 m_originalInsertionMode = m_insertionMode;
2770 setInsertionMode(TextMode);
2773 void HTMLTreeBuilder::processGenericRawTextStartTag(AtomicHTMLToken& token)
2775 ASSERT(token.type() == HTMLToken::StartTag);
2776 m_tree.insertHTMLElement(token);
2777 m_parser->tokenizer()->setState(HTMLTokenizer::RAWTEXTState);
2778 m_originalInsertionMode = m_insertionMode;
2779 setInsertionMode(TextMode);
2782 void HTMLTreeBuilder::processScriptStartTag(AtomicHTMLToken& token)
2784 ASSERT(token.type() == HTMLToken::StartTag);
2785 m_tree.insertScriptElement(token);
2786 m_parser->tokenizer()->setState(HTMLTokenizer::ScriptDataState);
2787 m_originalInsertionMode = m_insertionMode;
2789 TextPosition0 position = m_parser->textPosition();
2791 ASSERT(position.m_line.zeroBasedInt() == m_parser->tokenizer()->lineNumber());
2793 m_lastScriptElementStartPosition = position;
2795 setInsertionMode(TextMode);
2798 void HTMLTreeBuilder::finished()
2801 if (isParsingFragment()) {
2802 m_fragmentContext.finished();
2806 // Warning, this may detach the parser. Do not do anything else after this.
2807 m_document->finishedParsing();
2810 bool HTMLTreeBuilder::scriptEnabled(Frame* frame)
2814 return frame->script()->canExecuteScripts(NotAboutToExecuteScript);
2817 bool HTMLTreeBuilder::pluginsEnabled(Frame* frame)
2821 return frame->loader()->subframeLoader()->allowPlugins(NotAboutToInstantiatePlugin);