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 "HTMLElementFactory.h"
36 #include "HTMLFormElement.h"
37 #include "HTMLHtmlElement.h"
38 #include "HTMLNames.h"
39 #include "HTMLParserIdioms.h"
40 #include "HTMLScriptElement.h"
41 #include "HTMLToken.h"
42 #include "HTMLTokenizer.h"
43 #include "LocalizedStrings.h"
44 #include "MathMLNames.h"
45 #include "NotImplemented.h"
47 #include "ScriptController.h"
49 #include "XLinkNames.h"
50 #include "XMLNSNames.h"
55 using namespace HTMLNames;
57 static const int uninitializedLineNumberValue = -1;
59 static TextPosition1 uninitializedPositionValue1()
61 return TextPosition1(WTF::OneBasedNumber::fromOneBasedInt(-1), WTF::OneBasedNumber::base());
66 inline bool isHTMLSpaceOrReplacementCharacter(UChar character)
68 return isHTMLSpace(character) || character == replacementCharacter;
71 inline bool isAllWhitespace(const String& string)
73 return string.isAllSpecialCharacters<isHTMLSpace>();
76 inline bool isAllWhitespaceOrReplacementCharacters(const String& string)
78 return string.isAllSpecialCharacters<isHTMLSpaceOrReplacementCharacter>();
81 bool isNumberedHeaderTag(const AtomicString& tagName)
83 return tagName == h1Tag
91 bool isCaptionColOrColgroupTag(const AtomicString& tagName)
93 return tagName == captionTag
95 || tagName == colgroupTag;
98 bool isTableCellContextTag(const AtomicString& tagName)
100 return tagName == thTag || tagName == tdTag;
103 bool isTableBodyContextTag(const AtomicString& tagName)
105 return tagName == tbodyTag
106 || tagName == tfootTag
107 || tagName == theadTag;
110 // http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#special
111 bool isSpecialNode(Node* node)
113 if (node->hasTagName(MathMLNames::miTag)
114 || node->hasTagName(MathMLNames::moTag)
115 || node->hasTagName(MathMLNames::mnTag)
116 || node->hasTagName(MathMLNames::msTag)
117 || node->hasTagName(MathMLNames::mtextTag)
118 || node->hasTagName(MathMLNames::annotation_xmlTag)
119 || node->hasTagName(SVGNames::foreignObjectTag)
120 || node->hasTagName(SVGNames::descTag)
121 || node->hasTagName(SVGNames::titleTag))
123 if (node->namespaceURI() != xhtmlNamespaceURI)
125 const AtomicString& tagName = node->localName();
126 return tagName == addressTag
127 || tagName == appletTag
128 || tagName == areaTag
129 || tagName == articleTag
130 || tagName == asideTag
131 || tagName == baseTag
132 || tagName == basefontTag
133 || tagName == bgsoundTag
134 || tagName == blockquoteTag
135 || tagName == bodyTag
137 || tagName == buttonTag
138 || tagName == captionTag
139 || tagName == centerTag
141 || tagName == colgroupTag
142 || tagName == commandTag
144 || tagName == detailsTag
149 || tagName == embedTag
150 || tagName == fieldsetTag
151 || tagName == figcaptionTag
152 || tagName == figureTag
153 || tagName == footerTag
154 || tagName == formTag
155 || tagName == frameTag
156 || tagName == framesetTag
157 || isNumberedHeaderTag(tagName)
158 || tagName == headTag
159 || tagName == headerTag
160 || tagName == hgroupTag
162 || tagName == htmlTag
163 || tagName == iframeTag
165 || tagName == inputTag
166 || tagName == isindexTag
168 || tagName == linkTag
169 || tagName == listingTag
170 || tagName == marqueeTag
171 || tagName == menuTag
172 || tagName == metaTag
174 || tagName == noembedTag
175 || tagName == noframesTag
176 || tagName == noscriptTag
177 || tagName == objectTag
180 || tagName == paramTag
181 || tagName == plaintextTag
183 || tagName == scriptTag
184 || tagName == sectionTag
185 || tagName == selectTag
186 || tagName == styleTag
187 || tagName == summaryTag
188 || tagName == tableTag
189 || isTableBodyContextTag(tagName)
191 || tagName == textareaTag
193 || tagName == titleTag
197 || tagName == xmpTag;
200 bool isNonAnchorNonNobrFormattingTag(const AtomicString& tagName)
202 return tagName == bTag
204 || tagName == codeTag
206 || tagName == fontTag
209 || tagName == smallTag
210 || tagName == strikeTag
211 || tagName == strongTag
216 bool isNonAnchorFormattingTag(const AtomicString& tagName)
218 return tagName == nobrTag
219 || isNonAnchorNonNobrFormattingTag(tagName);
222 // http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#formatting
223 bool isFormattingTag(const AtomicString& tagName)
225 return tagName == aTag || isNonAnchorFormattingTag(tagName);
228 HTMLFormElement* closestFormAncestor(Element* element)
231 if (element->hasTagName(formTag))
232 return static_cast<HTMLFormElement*>(element);
233 Node* parent = element->parent();
234 if (!parent || !parent->isElementNode())
236 element = static_cast<Element*>(parent);
243 class HTMLTreeBuilder::ExternalCharacterTokenBuffer : public Noncopyable {
245 explicit ExternalCharacterTokenBuffer(AtomicHTMLToken& token)
246 : m_current(token.characters().data())
247 , m_end(m_current + token.characters().size())
252 explicit ExternalCharacterTokenBuffer(const String& string)
253 : m_current(string.characters())
254 , m_end(m_current + string.length())
259 ~ExternalCharacterTokenBuffer()
264 bool isEmpty() const { return m_current == m_end; }
266 void skipLeadingWhitespace()
268 skipLeading<isHTMLSpace>();
271 String takeLeadingWhitespace()
273 return takeLeading<isHTMLSpace>();
276 String takeLeadingNonWhitespace()
278 return takeLeading<isNotHTMLSpace>();
281 String takeRemaining()
284 const UChar* start = m_current;
286 return String(start, m_current - start);
289 void giveRemainingTo(Vector<UChar>& recipient)
291 recipient.append(m_current, m_end - m_current);
295 String takeRemainingWhitespace()
298 Vector<UChar> whitespace;
300 UChar cc = *m_current++;
302 whitespace.append(cc);
303 } while (m_current < m_end);
304 // Returning the null string when there aren't any whitespace
305 // characters is slightly cleaner semantically because we don't want
306 // to insert a text node (as opposed to inserting an empty text node).
307 if (whitespace.isEmpty())
309 return String::adopt(whitespace);
313 template<bool characterPredicate(UChar)>
317 while (characterPredicate(*m_current)) {
318 if (++m_current == m_end)
323 template<bool characterPredicate(UChar)>
327 const UChar* start = m_current;
328 skipLeading<characterPredicate>();
329 if (start == m_current)
331 return String(start, m_current - start);
334 const UChar* m_current;
339 HTMLTreeBuilder::HTMLTreeBuilder(HTMLTokenizer* tokenizer, HTMLDocument* document, bool reportErrors, bool usePreHTML5ParserQuirks)
341 , m_document(document)
342 , m_tree(document, FragmentScriptingAllowed, false)
343 , m_reportErrors(reportErrors)
345 , m_insertionMode(InitialMode)
346 , m_originalInsertionMode(InitialMode)
347 , m_tokenizer(tokenizer)
348 , m_scriptToProcessStartPosition(uninitializedPositionValue1())
349 , m_lastScriptElementStartPosition(TextPosition0::belowRangePosition())
350 , m_usePreHTML5ParserQuirks(usePreHTML5ParserQuirks)
351 , m_hasPendingForeignInsertionModeSteps(false)
355 // FIXME: Member variables should be grouped into self-initializing structs to
356 // minimize code duplication between these constructors.
357 HTMLTreeBuilder::HTMLTreeBuilder(HTMLTokenizer* tokenizer, DocumentFragment* fragment, Element* contextElement, FragmentScriptingPermission scriptingPermission, bool usePreHTML5ParserQuirks)
359 , m_fragmentContext(fragment, contextElement, scriptingPermission)
360 , m_document(m_fragmentContext.document())
361 , m_tree(m_document, scriptingPermission, true)
362 , m_reportErrors(false) // FIXME: Why not report errors in fragments?
364 , m_insertionMode(InitialMode)
365 , m_originalInsertionMode(InitialMode)
366 , m_tokenizer(tokenizer)
367 , m_scriptToProcessStartPosition(uninitializedPositionValue1())
368 , m_lastScriptElementStartPosition(TextPosition0::belowRangePosition())
369 , m_usePreHTML5ParserQuirks(usePreHTML5ParserQuirks)
370 , m_hasPendingForeignInsertionModeSteps(false)
372 if (contextElement) {
373 // Steps 4.2-4.6 of the HTML5 Fragment Case parsing algorithm:
374 // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-end.html#fragment-case
375 m_document->setCompatibilityMode(contextElement->document()->compatibilityMode());
376 processFakeStartTag(htmlTag);
377 resetInsertionModeAppropriately();
378 m_tree.setForm(closestFormAncestor(contextElement));
382 HTMLTreeBuilder::~HTMLTreeBuilder()
386 void HTMLTreeBuilder::detach()
388 // This call makes little sense in fragment mode, but for consistency
389 // DocumentParser expects detach() to always be called before it's destroyed.
391 // HTMLConstructionSite might be on the callstack when detach() is called
392 // otherwise we'd just call m_tree.clear() here instead.
396 HTMLTreeBuilder::FragmentParsingContext::FragmentParsingContext()
398 , m_contextElement(0)
399 , m_scriptingPermission(FragmentScriptingAllowed)
403 HTMLTreeBuilder::FragmentParsingContext::FragmentParsingContext(DocumentFragment* fragment, Element* contextElement, FragmentScriptingPermission scriptingPermission)
404 : m_dummyDocumentForFragmentParsing(HTMLDocument::create(0, KURL(), fragment->document()->baseURI()))
405 , m_fragment(fragment)
406 , m_contextElement(contextElement)
407 , m_scriptingPermission(scriptingPermission)
409 m_dummyDocumentForFragmentParsing->setCompatibilityMode(fragment->document()->compatibilityMode());
412 Document* HTMLTreeBuilder::FragmentParsingContext::document() const
415 return m_dummyDocumentForFragmentParsing.get();
418 void HTMLTreeBuilder::FragmentParsingContext::finished()
420 // Populate the DocumentFragment with the parsed content now that we're done.
421 ContainerNode* root = m_dummyDocumentForFragmentParsing.get();
422 if (m_contextElement)
423 root = m_dummyDocumentForFragmentParsing->documentElement();
424 m_fragment->takeAllChildrenFrom(root);
427 HTMLTreeBuilder::FragmentParsingContext::~FragmentParsingContext()
431 PassRefPtr<Element> HTMLTreeBuilder::takeScriptToProcess(TextPosition1& scriptStartPosition)
433 // Unpause ourselves, callers may pause us again when processing the script.
434 // The HTML5 spec is written as though scripts are executed inside the tree
435 // builder. We pause the parser to exit the tree builder, and then resume
436 // before running scripts.
438 scriptStartPosition = m_scriptToProcessStartPosition;
439 m_scriptToProcessStartPosition = uninitializedPositionValue1();
440 return m_scriptToProcess.release();
443 void HTMLTreeBuilder::constructTreeFromToken(HTMLToken& rawToken)
445 AtomicHTMLToken token(rawToken);
446 constructTreeFromAtomicToken(token);
449 void HTMLTreeBuilder::constructTreeFromAtomicToken(AtomicHTMLToken& token)
453 // Swallowing U+0000 characters isn't in the HTML5 spec, but turning all
454 // the U+0000 characters into replacement characters has compatibility
456 m_tokenizer->setForceNullCharacterReplacement(m_insertionMode == TextMode || m_insertionMode == InForeignContentMode);
457 m_tokenizer->setShouldAllowCDATA(m_insertionMode == InForeignContentMode && m_tree.currentElement()->namespaceURI() != xhtmlNamespaceURI);
460 void HTMLTreeBuilder::processToken(AtomicHTMLToken& token)
462 switch (token.type()) {
463 case HTMLToken::Uninitialized:
464 ASSERT_NOT_REACHED();
466 case HTMLToken::DOCTYPE:
467 processDoctypeToken(token);
469 case HTMLToken::StartTag:
470 processStartTag(token);
472 case HTMLToken::EndTag:
473 processEndTag(token);
475 case HTMLToken::Comment:
476 processComment(token);
478 case HTMLToken::Character:
479 processCharacter(token);
481 case HTMLToken::EndOfFile:
482 processEndOfFile(token);
487 void HTMLTreeBuilder::processDoctypeToken(AtomicHTMLToken& token)
489 ASSERT(token.type() == HTMLToken::DOCTYPE);
490 if (m_insertionMode == InitialMode) {
491 m_tree.insertDoctype(token);
492 setInsertionMode(BeforeHTMLMode);
495 if (m_insertionMode == InTableTextMode) {
496 defaultForInTableText();
497 processDoctypeToken(token);
503 void HTMLTreeBuilder::processFakeStartTag(const QualifiedName& tagName, PassRefPtr<NamedNodeMap> attributes)
505 // FIXME: We'll need a fancier conversion than just "localName" for SVG/MathML tags.
506 AtomicHTMLToken fakeToken(HTMLToken::StartTag, tagName.localName(), attributes);
507 processStartTag(fakeToken);
510 void HTMLTreeBuilder::processFakeEndTag(const QualifiedName& tagName)
512 // FIXME: We'll need a fancier conversion than just "localName" for SVG/MathML tags.
513 AtomicHTMLToken fakeToken(HTMLToken::EndTag, tagName.localName());
514 processEndTag(fakeToken);
517 void HTMLTreeBuilder::processFakeCharacters(const String& characters)
519 ASSERT(!characters.isEmpty());
520 ExternalCharacterTokenBuffer buffer(characters);
521 processCharacterBuffer(buffer);
524 void HTMLTreeBuilder::processFakePEndTagIfPInButtonScope()
526 if (!m_tree.openElements()->inButtonScope(pTag.localName()))
528 AtomicHTMLToken endP(HTMLToken::EndTag, pTag.localName());
532 PassRefPtr<NamedNodeMap> HTMLTreeBuilder::attributesForIsindexInput(AtomicHTMLToken& token)
534 RefPtr<NamedNodeMap> attributes = token.takeAtributes();
536 attributes = NamedNodeMap::create();
538 attributes->removeAttribute(nameAttr);
539 attributes->removeAttribute(actionAttr);
540 attributes->removeAttribute(promptAttr);
543 RefPtr<Attribute> mappedAttribute = Attribute::createMapped(nameAttr, isindexTag.localName());
544 attributes->insertAttribute(mappedAttribute.release(), false);
545 return attributes.release();
548 void HTMLTreeBuilder::processIsindexStartTagForInBody(AtomicHTMLToken& token)
550 ASSERT(token.type() == HTMLToken::StartTag);
551 ASSERT(token.name() == isindexTag);
555 notImplemented(); // Acknowledge self-closing flag
556 processFakeStartTag(formTag);
557 Attribute* actionAttribute = token.getAttributeItem(actionAttr);
558 if (actionAttribute) {
559 ASSERT(m_tree.currentElement()->hasTagName(formTag));
560 m_tree.currentElement()->setAttribute(actionAttr, actionAttribute->value());
562 processFakeStartTag(hrTag);
563 processFakeStartTag(labelTag);
564 Attribute* promptAttribute = token.getAttributeItem(promptAttr);
566 processFakeCharacters(promptAttribute->value());
568 processFakeCharacters(searchableIndexIntroduction());
569 processFakeStartTag(inputTag, attributesForIsindexInput(token));
570 notImplemented(); // This second set of characters may be needed by non-english locales.
571 processFakeEndTag(labelTag);
572 processFakeStartTag(hrTag);
573 processFakeEndTag(formTag);
578 bool isLi(const Element* element)
580 return element->hasTagName(liTag);
583 bool isDdOrDt(const Element* element)
585 return element->hasTagName(ddTag)
586 || element->hasTagName(dtTag);
591 template <bool shouldClose(const Element*)>
592 void HTMLTreeBuilder::processCloseWhenNestedTag(AtomicHTMLToken& token)
594 m_framesetOk = false;
595 HTMLElementStack::ElementRecord* nodeRecord = m_tree.openElements()->topRecord();
597 Element* node = nodeRecord->element();
598 if (shouldClose(node)) {
599 processFakeEndTag(node->tagQName());
602 if (isSpecialNode(node) && !node->hasTagName(addressTag) && !node->hasTagName(divTag) && !node->hasTagName(pTag))
604 nodeRecord = nodeRecord->next();
606 processFakePEndTagIfPInButtonScope();
607 m_tree.insertHTMLElement(token);
612 typedef HashMap<AtomicString, QualifiedName> PrefixedNameToQualifiedNameMap;
614 void mapLoweredLocalNameToName(PrefixedNameToQualifiedNameMap* map, QualifiedName** names, size_t length)
616 for (size_t i = 0; i < length; ++i) {
617 const QualifiedName& name = *names[i];
618 const AtomicString& localName = name.localName();
619 AtomicString loweredLocalName = localName.lower();
620 if (loweredLocalName != localName)
621 map->add(loweredLocalName, name);
625 void adjustSVGTagNameCase(AtomicHTMLToken& token)
627 static PrefixedNameToQualifiedNameMap* caseMap = 0;
629 caseMap = new PrefixedNameToQualifiedNameMap;
631 QualifiedName** svgTags = SVGNames::getSVGTags(&length);
632 mapLoweredLocalNameToName(caseMap, svgTags, length);
635 const QualifiedName& casedName = caseMap->get(token.name());
636 if (casedName.localName().isNull())
638 token.setName(casedName.localName());
641 template<QualifiedName** getAttrs(size_t* length)>
642 void adjustAttributes(AtomicHTMLToken& token)
644 static PrefixedNameToQualifiedNameMap* caseMap = 0;
646 caseMap = new PrefixedNameToQualifiedNameMap;
648 QualifiedName** attrs = getAttrs(&length);
649 mapLoweredLocalNameToName(caseMap, attrs, length);
652 NamedNodeMap* attributes = token.attributes();
656 for (unsigned x = 0; x < attributes->length(); ++x) {
657 Attribute* attribute = attributes->attributeItem(x);
658 const QualifiedName& casedName = caseMap->get(attribute->localName());
659 if (!casedName.localName().isNull())
660 attribute->parserSetName(casedName);
664 void adjustSVGAttributes(AtomicHTMLToken& token)
666 adjustAttributes<SVGNames::getSVGAttrs>(token);
669 void adjustMathMLAttributes(AtomicHTMLToken& token)
671 adjustAttributes<MathMLNames::getMathMLAttrs>(token);
674 void addNamesWithPrefix(PrefixedNameToQualifiedNameMap* map, const AtomicString& prefix, QualifiedName** names, size_t length)
676 for (size_t i = 0; i < length; ++i) {
677 QualifiedName* name = names[i];
678 const AtomicString& localName = name->localName();
679 AtomicString prefixColonLocalName(prefix + ":" + localName);
680 QualifiedName nameWithPrefix(prefix, localName, name->namespaceURI());
681 map->add(prefixColonLocalName, nameWithPrefix);
685 void adjustForeignAttributes(AtomicHTMLToken& token)
687 static PrefixedNameToQualifiedNameMap* map = 0;
689 map = new PrefixedNameToQualifiedNameMap;
691 QualifiedName** attrs = XLinkNames::getXLinkAttrs(&length);
692 addNamesWithPrefix(map, "xlink", attrs, length);
694 attrs = XMLNames::getXMLAttrs(&length);
695 addNamesWithPrefix(map, "xml", attrs, length);
697 map->add("xmlns", XMLNSNames::xmlnsAttr);
698 map->add("xmlns:xlink", QualifiedName("xmlns", "xlink", XMLNSNames::xmlnsNamespaceURI));
701 NamedNodeMap* attributes = token.attributes();
705 for (unsigned x = 0; x < attributes->length(); ++x) {
706 Attribute* attribute = attributes->attributeItem(x);
707 const QualifiedName& name = map->get(attribute->localName());
708 if (!name.localName().isNull())
709 attribute->parserSetName(name);
715 void HTMLTreeBuilder::processStartTagForInBody(AtomicHTMLToken& token)
717 ASSERT(token.type() == HTMLToken::StartTag);
718 if (token.name() == htmlTag) {
719 m_tree.insertHTMLHtmlStartTagInBody(token);
722 if (token.name() == baseTag
723 || token.name() == basefontTag
724 || token.name() == bgsoundTag
725 || token.name() == commandTag
726 || token.name() == linkTag
727 || token.name() == metaTag
728 || token.name() == noframesTag
729 || token.name() == scriptTag
730 || token.name() == styleTag
731 || token.name() == titleTag) {
732 bool didProcess = processStartTagForInHead(token);
733 ASSERT_UNUSED(didProcess, didProcess);
736 if (token.name() == bodyTag) {
737 if (!m_tree.openElements()->secondElementIsHTMLBodyElement() || m_tree.openElements()->hasOnlyOneElement()) {
738 ASSERT(isParsingFragment());
741 m_tree.insertHTMLBodyStartTagInBody(token);
744 if (token.name() == framesetTag) {
746 if (!m_tree.openElements()->secondElementIsHTMLBodyElement() || m_tree.openElements()->hasOnlyOneElement()) {
747 ASSERT(isParsingFragment());
752 ExceptionCode ec = 0;
753 m_tree.openElements()->bodyElement()->remove(ec);
755 m_tree.openElements()->popUntil(m_tree.openElements()->bodyElement());
756 m_tree.openElements()->popHTMLBodyElement();
757 ASSERT(m_tree.openElements()->top() == m_tree.openElements()->htmlElement());
758 m_tree.insertHTMLElement(token);
759 setInsertionMode(InFramesetMode);
762 if (token.name() == addressTag
763 || token.name() == articleTag
764 || token.name() == asideTag
765 || token.name() == blockquoteTag
766 || token.name() == centerTag
767 || token.name() == detailsTag
768 || token.name() == dirTag
769 || token.name() == divTag
770 || token.name() == dlTag
771 || token.name() == fieldsetTag
772 || token.name() == figcaptionTag
773 || token.name() == figureTag
774 || token.name() == footerTag
775 || token.name() == headerTag
776 || token.name() == hgroupTag
777 || token.name() == menuTag
778 || token.name() == navTag
779 || token.name() == olTag
780 || token.name() == pTag
781 || token.name() == sectionTag
782 || token.name() == summaryTag
783 || token.name() == ulTag) {
784 processFakePEndTagIfPInButtonScope();
785 m_tree.insertHTMLElement(token);
788 if (isNumberedHeaderTag(token.name())) {
789 processFakePEndTagIfPInButtonScope();
790 if (isNumberedHeaderTag(m_tree.currentElement()->localName())) {
792 m_tree.openElements()->pop();
794 m_tree.insertHTMLElement(token);
797 if (token.name() == preTag || token.name() == listingTag) {
798 processFakePEndTagIfPInButtonScope();
799 m_tree.insertHTMLElement(token);
800 m_tokenizer->setSkipLeadingNewLineForListing(true);
801 m_framesetOk = false;
804 if (token.name() == formTag) {
809 processFakePEndTagIfPInButtonScope();
810 m_tree.insertHTMLFormElement(token);
813 if (token.name() == liTag) {
814 processCloseWhenNestedTag<isLi>(token);
817 if (token.name() == ddTag || token.name() == dtTag) {
818 processCloseWhenNestedTag<isDdOrDt>(token);
821 if (token.name() == plaintextTag) {
822 processFakePEndTagIfPInButtonScope();
823 m_tree.insertHTMLElement(token);
824 m_tokenizer->setState(HTMLTokenizer::PLAINTEXTState);
827 if (token.name() == buttonTag) {
828 if (m_tree.openElements()->inScope(buttonTag)) {
830 processFakeEndTag(buttonTag);
831 reprocessStartTag(token); // FIXME: Could we just fall through here?
834 m_tree.reconstructTheActiveFormattingElements();
835 m_tree.insertHTMLElement(token);
836 m_framesetOk = false;
839 if (token.name() == aTag) {
840 Element* activeATag = m_tree.activeFormattingElements()->closestElementInScopeWithName(aTag.localName());
843 processFakeEndTag(aTag);
844 m_tree.activeFormattingElements()->remove(activeATag);
845 if (m_tree.openElements()->contains(activeATag))
846 m_tree.openElements()->remove(activeATag);
848 m_tree.reconstructTheActiveFormattingElements();
849 m_tree.insertFormattingElement(token);
852 if (isNonAnchorNonNobrFormattingTag(token.name())) {
853 m_tree.reconstructTheActiveFormattingElements();
854 m_tree.insertFormattingElement(token);
857 if (token.name() == nobrTag) {
858 m_tree.reconstructTheActiveFormattingElements();
859 if (m_tree.openElements()->inScope(nobrTag)) {
861 processFakeEndTag(nobrTag);
862 m_tree.reconstructTheActiveFormattingElements();
864 m_tree.insertFormattingElement(token);
867 if (token.name() == appletTag
868 || token.name() == marqueeTag
869 || token.name() == objectTag) {
870 m_tree.reconstructTheActiveFormattingElements();
871 m_tree.insertHTMLElement(token);
872 m_tree.activeFormattingElements()->appendMarker();
873 m_framesetOk = false;
876 if (token.name() == tableTag) {
877 if (!m_document->inQuirksMode() && m_tree.openElements()->inButtonScope(pTag))
878 processFakeEndTag(pTag);
879 m_tree.insertHTMLElement(token);
880 m_framesetOk = false;
881 setInsertionMode(InTableMode);
884 if (token.name() == imageTag) {
886 // Apparently we're not supposed to ask.
887 token.setName(imgTag.localName());
888 prepareToReprocessToken();
889 // Note the fall through to the imgTag handling below!
891 if (token.name() == areaTag
892 || token.name() == brTag
893 || token.name() == embedTag
894 || token.name() == imgTag
895 || token.name() == inputTag
896 || token.name() == keygenTag
897 || token.name() == wbrTag) {
898 m_tree.reconstructTheActiveFormattingElements();
899 m_tree.insertSelfClosingHTMLElement(token);
900 m_framesetOk = false;
903 if (token.name() == paramTag
904 || token.name() == sourceTag
905 || token.name() == trackTag) {
906 m_tree.insertSelfClosingHTMLElement(token);
909 if (token.name() == hrTag) {
910 processFakePEndTagIfPInButtonScope();
911 m_tree.insertSelfClosingHTMLElement(token);
912 m_framesetOk = false;
915 if (token.name() == isindexTag) {
916 processIsindexStartTagForInBody(token);
919 if (token.name() == textareaTag) {
920 m_tree.insertHTMLElement(token);
921 m_tokenizer->setSkipLeadingNewLineForListing(true);
922 m_tokenizer->setState(HTMLTokenizer::RCDATAState);
923 m_originalInsertionMode = m_insertionMode;
924 m_framesetOk = false;
925 setInsertionMode(TextMode);
928 if (token.name() == xmpTag) {
929 processFakePEndTagIfPInButtonScope();
930 m_tree.reconstructTheActiveFormattingElements();
931 m_framesetOk = false;
932 processGenericRawTextStartTag(token);
935 if (token.name() == iframeTag) {
936 m_framesetOk = false;
937 processGenericRawTextStartTag(token);
940 if (token.name() == noembedTag && pluginsEnabled(m_document->frame())) {
941 processGenericRawTextStartTag(token);
944 if (token.name() == noscriptTag && scriptEnabled(m_document->frame())) {
945 processGenericRawTextStartTag(token);
948 if (token.name() == selectTag) {
949 m_tree.reconstructTheActiveFormattingElements();
950 m_tree.insertHTMLElement(token);
951 m_framesetOk = false;
952 if (m_insertionMode == InTableMode
953 || m_insertionMode == InCaptionMode
954 || m_insertionMode == InColumnGroupMode
955 || m_insertionMode == InTableBodyMode
956 || m_insertionMode == InRowMode
957 || m_insertionMode == InCellMode)
958 setInsertionMode(InSelectInTableMode);
960 setInsertionMode(InSelectMode);
963 if (token.name() == optgroupTag || token.name() == optionTag) {
964 if (m_tree.openElements()->inScope(optionTag.localName())) {
965 AtomicHTMLToken endOption(HTMLToken::EndTag, optionTag.localName());
966 processEndTag(endOption);
968 m_tree.reconstructTheActiveFormattingElements();
969 m_tree.insertHTMLElement(token);
972 if (token.name() == rpTag || token.name() == rtTag) {
973 if (m_tree.openElements()->inScope(rubyTag.localName())) {
974 m_tree.generateImpliedEndTags();
975 if (!m_tree.currentElement()->hasTagName(rubyTag)) {
977 m_tree.openElements()->popUntil(rubyTag.localName());
980 m_tree.insertHTMLElement(token);
983 if (token.name() == MathMLNames::mathTag.localName()) {
984 m_tree.reconstructTheActiveFormattingElements();
985 adjustMathMLAttributes(token);
986 adjustForeignAttributes(token);
987 m_tree.insertForeignElement(token, MathMLNames::mathmlNamespaceURI);
988 if (m_insertionMode != InForeignContentMode)
989 setInsertionMode(InForeignContentMode);
992 if (token.name() == SVGNames::svgTag.localName()) {
993 m_tree.reconstructTheActiveFormattingElements();
994 adjustSVGAttributes(token);
995 adjustForeignAttributes(token);
996 m_tree.insertForeignElement(token, SVGNames::svgNamespaceURI);
997 if (m_insertionMode != InForeignContentMode)
998 setInsertionMode(InForeignContentMode);
1001 if (isCaptionColOrColgroupTag(token.name())
1002 || token.name() == frameTag
1003 || token.name() == headTag
1004 || isTableBodyContextTag(token.name())
1005 || isTableCellContextTag(token.name())
1006 || token.name() == trTag) {
1010 m_tree.reconstructTheActiveFormattingElements();
1011 m_tree.insertHTMLElement(token);
1014 bool HTMLTreeBuilder::processColgroupEndTagForInColumnGroup()
1016 if (m_tree.currentElement() == m_tree.openElements()->htmlElement()) {
1017 ASSERT(isParsingFragment());
1018 // FIXME: parse error
1021 m_tree.openElements()->pop();
1022 setInsertionMode(InTableMode);
1026 // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#close-the-cell
1027 void HTMLTreeBuilder::closeTheCell()
1029 ASSERT(insertionMode() == InCellMode);
1030 if (m_tree.openElements()->inTableScope(tdTag)) {
1031 ASSERT(!m_tree.openElements()->inTableScope(thTag));
1032 processFakeEndTag(tdTag);
1035 ASSERT(m_tree.openElements()->inTableScope(thTag));
1036 processFakeEndTag(thTag);
1037 ASSERT(insertionMode() == InRowMode);
1040 void HTMLTreeBuilder::processStartTagForInTable(AtomicHTMLToken& token)
1042 ASSERT(token.type() == HTMLToken::StartTag);
1043 if (token.name() == captionTag) {
1044 m_tree.openElements()->popUntilTableScopeMarker();
1045 m_tree.activeFormattingElements()->appendMarker();
1046 m_tree.insertHTMLElement(token);
1047 setInsertionMode(InCaptionMode);
1050 if (token.name() == colgroupTag) {
1051 m_tree.openElements()->popUntilTableScopeMarker();
1052 m_tree.insertHTMLElement(token);
1053 setInsertionMode(InColumnGroupMode);
1056 if (token.name() == colTag) {
1057 processFakeStartTag(colgroupTag);
1058 ASSERT(InColumnGroupMode);
1059 reprocessStartTag(token);
1062 if (isTableBodyContextTag(token.name())) {
1063 m_tree.openElements()->popUntilTableScopeMarker();
1064 m_tree.insertHTMLElement(token);
1065 setInsertionMode(InTableBodyMode);
1068 if (isTableCellContextTag(token.name())
1069 || token.name() == trTag) {
1070 processFakeStartTag(tbodyTag);
1071 ASSERT(insertionMode() == InTableBodyMode);
1072 reprocessStartTag(token);
1075 if (token.name() == tableTag) {
1077 if (!processTableEndTagForInTable()) {
1078 ASSERT(isParsingFragment());
1081 reprocessStartTag(token);
1084 if (token.name() == styleTag || token.name() == scriptTag) {
1085 processStartTagForInHead(token);
1088 if (token.name() == inputTag) {
1089 Attribute* typeAttribute = token.getAttributeItem(typeAttr);
1090 if (typeAttribute && equalIgnoringCase(typeAttribute->value(), "hidden")) {
1092 m_tree.insertSelfClosingHTMLElement(token);
1095 // Fall through to "anything else" case.
1097 if (token.name() == formTag) {
1101 m_tree.insertHTMLFormElement(token, true);
1102 m_tree.openElements()->pop();
1106 HTMLConstructionSite::RedirectToFosterParentGuard redirecter(m_tree);
1107 processStartTagForInBody(token);
1112 bool shouldProcessForeignContentUsingInBodyInsertionMode(AtomicHTMLToken& token, Element* currentElement)
1114 ASSERT(token.type() == HTMLToken::StartTag);
1115 if (currentElement->hasTagName(MathMLNames::miTag)
1116 || currentElement->hasTagName(MathMLNames::moTag)
1117 || currentElement->hasTagName(MathMLNames::mnTag)
1118 || currentElement->hasTagName(MathMLNames::msTag)
1119 || currentElement->hasTagName(MathMLNames::mtextTag)) {
1120 return token.name() != MathMLNames::mglyphTag
1121 && token.name() != MathMLNames::malignmarkTag;
1123 if (currentElement->hasTagName(MathMLNames::annotation_xmlTag))
1124 return token.name() == SVGNames::svgTag;
1125 if (currentElement->hasTagName(SVGNames::foreignObjectTag)
1126 || currentElement->hasTagName(SVGNames::descTag)
1127 || currentElement->hasTagName(SVGNames::titleTag))
1129 return currentElement->namespaceURI() == HTMLNames::xhtmlNamespaceURI;
1134 void HTMLTreeBuilder::processStartTag(AtomicHTMLToken& token)
1136 ASSERT(token.type() == HTMLToken::StartTag);
1137 switch (insertionMode()) {
1139 ASSERT(insertionMode() == InitialMode);
1140 defaultForInitial();
1142 case BeforeHTMLMode:
1143 ASSERT(insertionMode() == BeforeHTMLMode);
1144 if (token.name() == htmlTag) {
1145 m_tree.insertHTMLHtmlStartTagBeforeHTML(token);
1146 setInsertionMode(BeforeHeadMode);
1149 defaultForBeforeHTML();
1151 case BeforeHeadMode:
1152 ASSERT(insertionMode() == BeforeHeadMode);
1153 if (token.name() == htmlTag) {
1154 m_tree.insertHTMLHtmlStartTagInBody(token);
1157 if (token.name() == headTag) {
1158 m_tree.insertHTMLHeadElement(token);
1159 setInsertionMode(InHeadMode);
1162 defaultForBeforeHead();
1165 ASSERT(insertionMode() == InHeadMode);
1166 if (processStartTagForInHead(token))
1171 ASSERT(insertionMode() == AfterHeadMode);
1172 if (token.name() == htmlTag) {
1173 m_tree.insertHTMLHtmlStartTagInBody(token);
1176 if (token.name() == bodyTag) {
1177 m_framesetOk = false;
1178 m_tree.insertHTMLBodyElement(token);
1179 setInsertionMode(InBodyMode);
1182 if (token.name() == framesetTag) {
1183 m_tree.insertHTMLElement(token);
1184 setInsertionMode(InFramesetMode);
1187 if (token.name() == baseTag
1188 || token.name() == basefontTag
1189 || token.name() == bgsoundTag
1190 || token.name() == linkTag
1191 || token.name() == metaTag
1192 || token.name() == noframesTag
1193 || token.name() == scriptTag
1194 || token.name() == styleTag
1195 || token.name() == titleTag) {
1197 ASSERT(m_tree.head());
1198 m_tree.openElements()->pushHTMLHeadElement(m_tree.head());
1199 processStartTagForInHead(token);
1200 m_tree.openElements()->removeHTMLHeadElement(m_tree.head());
1203 if (token.name() == headTag) {
1207 defaultForAfterHead();
1210 ASSERT(insertionMode() == InBodyMode);
1211 processStartTagForInBody(token);
1214 ASSERT(insertionMode() == InTableMode);
1215 processStartTagForInTable(token);
1218 ASSERT(insertionMode() == InCaptionMode);
1219 if (isCaptionColOrColgroupTag(token.name())
1220 || isTableBodyContextTag(token.name())
1221 || isTableCellContextTag(token.name())
1222 || token.name() == trTag) {
1224 if (!processCaptionEndTagForInCaption()) {
1225 ASSERT(isParsingFragment());
1228 reprocessStartTag(token);
1231 processStartTagForInBody(token);
1233 case InColumnGroupMode:
1234 ASSERT(insertionMode() == InColumnGroupMode);
1235 if (token.name() == htmlTag) {
1236 m_tree.insertHTMLHtmlStartTagInBody(token);
1239 if (token.name() == colTag) {
1240 m_tree.insertSelfClosingHTMLElement(token);
1243 if (!processColgroupEndTagForInColumnGroup()) {
1244 ASSERT(isParsingFragment());
1247 reprocessStartTag(token);
1249 case InTableBodyMode:
1250 ASSERT(insertionMode() == InTableBodyMode);
1251 if (token.name() == trTag) {
1252 m_tree.openElements()->popUntilTableBodyScopeMarker(); // How is there ever anything to pop?
1253 m_tree.insertHTMLElement(token);
1254 setInsertionMode(InRowMode);
1257 if (isTableCellContextTag(token.name())) {
1259 processFakeStartTag(trTag);
1260 ASSERT(insertionMode() == InRowMode);
1261 reprocessStartTag(token);
1264 if (isCaptionColOrColgroupTag(token.name()) || isTableBodyContextTag(token.name())) {
1265 // FIXME: This is slow.
1266 if (!m_tree.openElements()->inTableScope(tbodyTag.localName()) && !m_tree.openElements()->inTableScope(theadTag.localName()) && !m_tree.openElements()->inTableScope(tfootTag.localName())) {
1267 ASSERT(isParsingFragment());
1271 m_tree.openElements()->popUntilTableBodyScopeMarker();
1272 ASSERT(isTableBodyContextTag(m_tree.currentElement()->localName()));
1273 processFakeEndTag(m_tree.currentElement()->tagQName());
1274 reprocessStartTag(token);
1277 processStartTagForInTable(token);
1280 ASSERT(insertionMode() == InRowMode);
1281 if (isTableCellContextTag(token.name())) {
1282 m_tree.openElements()->popUntilTableRowScopeMarker();
1283 m_tree.insertHTMLElement(token);
1284 setInsertionMode(InCellMode);
1285 m_tree.activeFormattingElements()->appendMarker();
1288 if (token.name() == trTag
1289 || isCaptionColOrColgroupTag(token.name())
1290 || isTableBodyContextTag(token.name())) {
1291 if (!processTrEndTagForInRow()) {
1292 ASSERT(isParsingFragment());
1295 ASSERT(insertionMode() == InTableBodyMode);
1296 reprocessStartTag(token);
1299 processStartTagForInTable(token);
1302 ASSERT(insertionMode() == InCellMode);
1303 if (isCaptionColOrColgroupTag(token.name())
1304 || isTableCellContextTag(token.name())
1305 || token.name() == trTag
1306 || isTableBodyContextTag(token.name())) {
1307 // FIXME: This could be more efficient.
1308 if (!m_tree.openElements()->inTableScope(tdTag) && !m_tree.openElements()->inTableScope(thTag)) {
1309 ASSERT(isParsingFragment());
1314 reprocessStartTag(token);
1317 processStartTagForInBody(token);
1320 case AfterAfterBodyMode:
1321 ASSERT(insertionMode() == AfterBodyMode || insertionMode() == AfterAfterBodyMode);
1322 if (token.name() == htmlTag) {
1323 m_tree.insertHTMLHtmlStartTagInBody(token);
1326 setInsertionMode(InBodyMode);
1327 reprocessStartTag(token);
1329 case InHeadNoscriptMode:
1330 ASSERT(insertionMode() == InHeadNoscriptMode);
1331 if (token.name() == htmlTag) {
1332 m_tree.insertHTMLHtmlStartTagInBody(token);
1335 if (token.name() == basefontTag
1336 || token.name() == bgsoundTag
1337 || token.name() == linkTag
1338 || token.name() == metaTag
1339 || token.name() == noframesTag
1340 || token.name() == styleTag) {
1341 bool didProcess = processStartTagForInHead(token);
1342 ASSERT_UNUSED(didProcess, didProcess);
1345 if (token.name() == htmlTag || token.name() == noscriptTag) {
1349 defaultForInHeadNoscript();
1350 processToken(token);
1352 case InFramesetMode:
1353 ASSERT(insertionMode() == InFramesetMode);
1354 if (token.name() == htmlTag) {
1355 m_tree.insertHTMLHtmlStartTagInBody(token);
1358 if (token.name() == framesetTag) {
1359 m_tree.insertHTMLElement(token);
1362 if (token.name() == frameTag) {
1363 m_tree.insertSelfClosingHTMLElement(token);
1366 if (token.name() == noframesTag) {
1367 processStartTagForInHead(token);
1372 case AfterFramesetMode:
1373 case AfterAfterFramesetMode:
1374 ASSERT(insertionMode() == AfterFramesetMode || insertionMode() == AfterAfterFramesetMode);
1375 if (token.name() == htmlTag) {
1376 m_tree.insertHTMLHtmlStartTagInBody(token);
1379 if (token.name() == noframesTag) {
1380 processStartTagForInHead(token);
1385 case InSelectInTableMode:
1386 ASSERT(insertionMode() == InSelectInTableMode);
1387 if (token.name() == captionTag
1388 || token.name() == tableTag
1389 || isTableBodyContextTag(token.name())
1390 || token.name() == trTag
1391 || isTableCellContextTag(token.name())) {
1393 AtomicHTMLToken endSelect(HTMLToken::EndTag, selectTag.localName());
1394 processEndTag(endSelect);
1395 reprocessStartTag(token);
1400 ASSERT(insertionMode() == InSelectMode || insertionMode() == InSelectInTableMode);
1401 if (token.name() == htmlTag) {
1402 m_tree.insertHTMLHtmlStartTagInBody(token);
1405 if (token.name() == optionTag) {
1406 if (m_tree.currentElement()->hasTagName(optionTag)) {
1407 AtomicHTMLToken endOption(HTMLToken::EndTag, optionTag.localName());
1408 processEndTag(endOption);
1410 m_tree.insertHTMLElement(token);
1413 if (token.name() == optgroupTag) {
1414 if (m_tree.currentElement()->hasTagName(optionTag)) {
1415 AtomicHTMLToken endOption(HTMLToken::EndTag, optionTag.localName());
1416 processEndTag(endOption);
1418 if (m_tree.currentElement()->hasTagName(optgroupTag)) {
1419 AtomicHTMLToken endOptgroup(HTMLToken::EndTag, optgroupTag.localName());
1420 processEndTag(endOptgroup);
1422 m_tree.insertHTMLElement(token);
1425 if (token.name() == selectTag) {
1427 AtomicHTMLToken endSelect(HTMLToken::EndTag, selectTag.localName());
1428 processEndTag(endSelect);
1431 if (token.name() == inputTag
1432 || token.name() == keygenTag
1433 || token.name() == textareaTag) {
1435 if (!m_tree.openElements()->inSelectScope(selectTag)) {
1436 ASSERT(isParsingFragment());
1439 AtomicHTMLToken endSelect(HTMLToken::EndTag, selectTag.localName());
1440 processEndTag(endSelect);
1441 reprocessStartTag(token);
1444 if (token.name() == scriptTag) {
1445 bool didProcess = processStartTagForInHead(token);
1446 ASSERT_UNUSED(didProcess, didProcess);
1450 case InTableTextMode:
1451 defaultForInTableText();
1452 processStartTag(token);
1454 case InForeignContentMode: {
1455 if (shouldProcessForeignContentUsingInBodyInsertionMode(token, m_tree.currentElement())) {
1456 processForeignContentUsingInBodyModeAndResetMode(token);
1459 if (token.name() == bTag
1460 || token.name() == bigTag
1461 || token.name() == blockquoteTag
1462 || token.name() == bodyTag
1463 || token.name() == brTag
1464 || token.name() == centerTag
1465 || token.name() == codeTag
1466 || token.name() == ddTag
1467 || token.name() == divTag
1468 || token.name() == dlTag
1469 || token.name() == dtTag
1470 || token.name() == emTag
1471 || token.name() == embedTag
1472 || isNumberedHeaderTag(token.name())
1473 || token.name() == headTag
1474 || token.name() == hrTag
1475 || token.name() == iTag
1476 || token.name() == imgTag
1477 || token.name() == liTag
1478 || token.name() == listingTag
1479 || token.name() == menuTag
1480 || token.name() == metaTag
1481 || token.name() == nobrTag
1482 || token.name() == olTag
1483 || token.name() == pTag
1484 || token.name() == preTag
1485 || token.name() == rubyTag
1486 || token.name() == sTag
1487 || token.name() == smallTag
1488 || token.name() == spanTag
1489 || token.name() == strongTag
1490 || token.name() == strikeTag
1491 || token.name() == subTag
1492 || token.name() == supTag
1493 || token.name() == tableTag
1494 || token.name() == ttTag
1495 || token.name() == uTag
1496 || token.name() == ulTag
1497 || token.name() == varTag
1498 || (token.name() == fontTag && (token.getAttributeItem(colorAttr) || token.getAttributeItem(faceAttr) || token.getAttributeItem(sizeAttr)))) {
1500 m_tree.openElements()->popUntilForeignContentScopeMarker();
1501 resetInsertionModeAppropriately();
1502 reprocessStartTag(token);
1505 const AtomicString& currentNamespace = m_tree.currentElement()->namespaceURI();
1506 if (currentNamespace == MathMLNames::mathmlNamespaceURI)
1507 adjustMathMLAttributes(token);
1508 if (currentNamespace == SVGNames::svgNamespaceURI) {
1509 adjustSVGTagNameCase(token);
1510 adjustSVGAttributes(token);
1512 adjustForeignAttributes(token);
1513 m_tree.insertForeignElement(token, currentNamespace);
1517 ASSERT_NOT_REACHED();
1522 bool HTMLTreeBuilder::processBodyEndTagForInBody(AtomicHTMLToken& token)
1524 ASSERT(token.type() == HTMLToken::EndTag);
1525 ASSERT(token.name() == bodyTag);
1526 if (!m_tree.openElements()->inScope(bodyTag.localName())) {
1530 notImplemented(); // Emit a more specific parse error based on stack contents.
1531 setInsertionMode(AfterBodyMode);
1535 void HTMLTreeBuilder::processAnyOtherEndTagForInBody(AtomicHTMLToken& token)
1537 ASSERT(token.type() == HTMLToken::EndTag);
1538 HTMLElementStack::ElementRecord* record = m_tree.openElements()->topRecord();
1540 Element* node = record->element();
1541 if (node->hasLocalName(token.name())) {
1542 m_tree.generateImpliedEndTags();
1543 if (!m_tree.currentElement()->hasLocalName(token.name())) {
1545 // FIXME: This is either a bug in the spec, or a bug in our
1546 // implementation. Filed a bug with HTML5:
1547 // http://www.w3.org/Bugs/Public/show_bug.cgi?id=10080
1548 // We might have already popped the node for the token in
1549 // generateImpliedEndTags, just abort.
1550 if (!m_tree.openElements()->contains(node))
1553 m_tree.openElements()->popUntilPopped(node);
1556 if (isSpecialNode(node)) {
1560 record = record->next();
1564 // FIXME: This probably belongs on HTMLElementStack.
1565 HTMLElementStack::ElementRecord* HTMLTreeBuilder::furthestBlockForFormattingElement(Element* formattingElement)
1567 HTMLElementStack::ElementRecord* furthestBlock = 0;
1568 HTMLElementStack::ElementRecord* record = m_tree.openElements()->topRecord();
1569 for (; record; record = record->next()) {
1570 if (record->element() == formattingElement)
1571 return furthestBlock;
1572 if (isSpecialNode(record->element()))
1573 furthestBlock = record;
1575 ASSERT_NOT_REACHED();
1579 // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#parsing-main-inbody
1580 void HTMLTreeBuilder::callTheAdoptionAgency(AtomicHTMLToken& token)
1582 // The adoption agency algorithm is N^2. We limit the number of iterations
1583 // to stop from hanging the whole browser. This limit is copied from the
1584 // legacy tree builder and might need to be tweaked in the future.
1585 static const int adoptionAgencyIterationLimit = 10;
1587 for (int i = 0; i < adoptionAgencyIterationLimit; ++i) {
1589 Element* formattingElement = m_tree.activeFormattingElements()->closestElementInScopeWithName(token.name());
1590 if (!formattingElement || ((m_tree.openElements()->contains(formattingElement)) && !m_tree.openElements()->inScope(formattingElement))) {
1592 notImplemented(); // Check the stack of open elements for a more specific parse error.
1595 HTMLElementStack::ElementRecord* formattingElementRecord = m_tree.openElements()->find(formattingElement);
1596 if (!formattingElementRecord) {
1598 m_tree.activeFormattingElements()->remove(formattingElement);
1601 if (formattingElement != m_tree.currentElement())
1604 HTMLElementStack::ElementRecord* furthestBlock = furthestBlockForFormattingElement(formattingElement);
1606 if (!furthestBlock) {
1607 m_tree.openElements()->popUntilPopped(formattingElement);
1608 m_tree.activeFormattingElements()->remove(formattingElement);
1612 ASSERT(furthestBlock->isAbove(formattingElementRecord));
1613 Element* commonAncestor = formattingElementRecord->next()->element();
1615 HTMLFormattingElementList::Bookmark bookmark = m_tree.activeFormattingElements()->bookmarkFor(formattingElement);
1617 HTMLElementStack::ElementRecord* node = furthestBlock;
1618 HTMLElementStack::ElementRecord* nextNode = node->next();
1619 HTMLElementStack::ElementRecord* lastNode = furthestBlock;
1620 for (int i = 0; i < adoptionAgencyIterationLimit; ++i) {
1624 nextNode = node->next(); // Save node->next() for the next iteration in case node is deleted in 6.2.
1626 if (!m_tree.activeFormattingElements()->contains(node->element())) {
1627 m_tree.openElements()->remove(node->element());
1632 if (node == formattingElementRecord)
1635 RefPtr<Element> newElement = m_tree.createHTMLElementFromElementRecord(node);
1636 HTMLFormattingElementList::Entry* nodeEntry = m_tree.activeFormattingElements()->find(node->element());
1637 nodeEntry->replaceElement(newElement.get());
1638 node->replaceElement(newElement.release());
1639 // 6.4 -- Intentionally out of order to handle the case where node
1640 // was replaced in 6.5.
1641 // http://www.w3.org/Bugs/Public/show_bug.cgi?id=10096
1642 if (lastNode == furthestBlock)
1643 bookmark.moveToAfter(nodeEntry);
1645 if (Element* parent = lastNode->element()->parentElement())
1646 parent->parserRemoveChild(lastNode->element());
1647 node->element()->parserAddChild(lastNode->element());
1648 if (lastNode->element()->parentElement()->attached() && !lastNode->element()->attached())
1649 lastNode->element()->lazyAttach();
1654 const AtomicString& commonAncestorTag = commonAncestor->localName();
1655 if (Element* parent = lastNode->element()->parentElement())
1656 parent->parserRemoveChild(lastNode->element());
1657 // FIXME: If this moves to HTMLConstructionSite, this check should use
1658 // causesFosterParenting(tagName) instead.
1659 if (commonAncestorTag == tableTag
1660 || commonAncestorTag == trTag
1661 || isTableBodyContextTag(commonAncestorTag))
1662 m_tree.fosterParent(lastNode->element());
1664 commonAncestor->parserAddChild(lastNode->element());
1665 if (lastNode->element()->parentElement()->attached() && !lastNode->element()->attached())
1666 lastNode->element()->lazyAttach();
1669 RefPtr<Element> newElement = m_tree.createHTMLElementFromElementRecord(formattingElementRecord);
1671 newElement->takeAllChildrenFrom(furthestBlock->element());
1673 Element* furthestBlockElement = furthestBlock->element();
1674 // FIXME: All this creation / parserAddChild / attach business should
1675 // be in HTMLConstructionSite. My guess is that steps 8--12
1676 // should all be in some HTMLConstructionSite function.
1677 furthestBlockElement->parserAddChild(newElement);
1678 if (furthestBlockElement->attached() && !newElement->attached()) {
1679 // Notice that newElement might already be attached if, for example, one of the reparented
1680 // children is a style element, which attaches itself automatically.
1681 newElement->attach();
1684 m_tree.activeFormattingElements()->swapTo(formattingElement, newElement.get(), bookmark);
1686 m_tree.openElements()->remove(formattingElement);
1687 m_tree.openElements()->insertAbove(newElement, furthestBlock);
1691 void HTMLTreeBuilder::resetInsertionModeAppropriately()
1693 // http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#reset-the-insertion-mode-appropriately
1695 HTMLElementStack::ElementRecord* nodeRecord = m_tree.openElements()->topRecord();
1697 Element* node = nodeRecord->element();
1698 if (node == m_tree.openElements()->bottom()) {
1699 ASSERT(isParsingFragment());
1701 node = m_fragmentContext.contextElement();
1703 if (node->hasTagName(selectTag)) {
1704 ASSERT(isParsingFragment());
1705 return setInsertionMode(InSelectMode);
1707 if (node->hasTagName(tdTag) || node->hasTagName(thTag))
1708 return setInsertionMode(InCellMode);
1709 if (node->hasTagName(trTag))
1710 return setInsertionMode(InRowMode);
1711 if (node->hasTagName(tbodyTag) || node->hasTagName(theadTag) || node->hasTagName(tfootTag))
1712 return setInsertionMode(InTableBodyMode);
1713 if (node->hasTagName(captionTag))
1714 return setInsertionMode(InCaptionMode);
1715 if (node->hasTagName(colgroupTag)) {
1716 ASSERT(isParsingFragment());
1717 return setInsertionMode(InColumnGroupMode);
1719 if (node->hasTagName(tableTag))
1720 return setInsertionMode(InTableMode);
1721 if (node->hasTagName(headTag)) {
1722 ASSERT(isParsingFragment());
1723 return setInsertionMode(InBodyMode);
1725 if (node->hasTagName(bodyTag))
1726 return setInsertionMode(InBodyMode);
1727 if (node->hasTagName(framesetTag)) {
1728 ASSERT(isParsingFragment());
1729 return setInsertionMode(InFramesetMode);
1731 if (node->hasTagName(htmlTag)) {
1732 ASSERT(isParsingFragment());
1733 return setInsertionMode(BeforeHeadMode);
1735 if (node->namespaceURI() == SVGNames::svgNamespaceURI
1736 || node->namespaceURI() == MathMLNames::mathmlNamespaceURI)
1737 return setInsertionMode(InForeignContentMode);
1739 ASSERT(isParsingFragment());
1740 return setInsertionMode(InBodyMode);
1742 nodeRecord = nodeRecord->next();
1746 void HTMLTreeBuilder::processEndTagForInTableBody(AtomicHTMLToken& token)
1748 ASSERT(token.type() == HTMLToken::EndTag);
1749 if (isTableBodyContextTag(token.name())) {
1750 if (!m_tree.openElements()->inTableScope(token.name())) {
1754 m_tree.openElements()->popUntilTableBodyScopeMarker();
1755 m_tree.openElements()->pop();
1756 setInsertionMode(InTableMode);
1759 if (token.name() == tableTag) {
1760 // FIXME: This is slow.
1761 if (!m_tree.openElements()->inTableScope(tbodyTag.localName()) && !m_tree.openElements()->inTableScope(theadTag.localName()) && !m_tree.openElements()->inTableScope(tfootTag.localName())) {
1762 ASSERT(isParsingFragment());
1766 m_tree.openElements()->popUntilTableBodyScopeMarker();
1767 ASSERT(isTableBodyContextTag(m_tree.currentElement()->localName()));
1768 processFakeEndTag(m_tree.currentElement()->tagQName());
1769 reprocessEndTag(token);
1772 if (token.name() == bodyTag
1773 || isCaptionColOrColgroupTag(token.name())
1774 || token.name() == htmlTag
1775 || isTableCellContextTag(token.name())
1776 || token.name() == trTag) {
1780 processEndTagForInTable(token);
1783 void HTMLTreeBuilder::processEndTagForInRow(AtomicHTMLToken& token)
1785 ASSERT(token.type() == HTMLToken::EndTag);
1786 if (token.name() == trTag) {
1787 processTrEndTagForInRow();
1790 if (token.name() == tableTag) {
1791 if (!processTrEndTagForInRow()) {
1792 ASSERT(isParsingFragment());
1795 ASSERT(insertionMode() == InTableBodyMode);
1796 reprocessEndTag(token);
1799 if (isTableBodyContextTag(token.name())) {
1800 if (!m_tree.openElements()->inTableScope(token.name())) {
1804 processFakeEndTag(trTag);
1805 ASSERT(insertionMode() == InTableBodyMode);
1806 reprocessEndTag(token);
1809 if (token.name() == bodyTag
1810 || isCaptionColOrColgroupTag(token.name())
1811 || token.name() == htmlTag
1812 || isTableCellContextTag(token.name())) {
1816 processEndTagForInTable(token);
1819 void HTMLTreeBuilder::processEndTagForInCell(AtomicHTMLToken& token)
1821 ASSERT(token.type() == HTMLToken::EndTag);
1822 if (isTableCellContextTag(token.name())) {
1823 if (!m_tree.openElements()->inTableScope(token.name())) {
1827 m_tree.generateImpliedEndTags();
1828 if (!m_tree.currentElement()->hasLocalName(token.name()))
1830 m_tree.openElements()->popUntilPopped(token.name());
1831 m_tree.activeFormattingElements()->clearToLastMarker();
1832 setInsertionMode(InRowMode);
1835 if (token.name() == bodyTag
1836 || isCaptionColOrColgroupTag(token.name())
1837 || token.name() == htmlTag) {
1841 if (token.name() == tableTag
1842 || token.name() == trTag
1843 || isTableBodyContextTag(token.name())) {
1844 if (!m_tree.openElements()->inTableScope(token.name())) {
1845 ASSERT(isTableBodyContextTag(token.name()) || isParsingFragment());
1850 reprocessEndTag(token);
1853 processEndTagForInBody(token);
1856 void HTMLTreeBuilder::processEndTagForInBody(AtomicHTMLToken& token)
1858 ASSERT(token.type() == HTMLToken::EndTag);
1859 if (token.name() == bodyTag) {
1860 processBodyEndTagForInBody(token);
1863 if (token.name() == htmlTag) {
1864 AtomicHTMLToken endBody(HTMLToken::EndTag, bodyTag.localName());
1865 if (processBodyEndTagForInBody(endBody))
1866 reprocessEndTag(token);
1869 if (token.name() == addressTag
1870 || token.name() == articleTag
1871 || token.name() == asideTag
1872 || token.name() == blockquoteTag
1873 || token.name() == buttonTag
1874 || token.name() == centerTag
1875 || token.name() == detailsTag
1876 || token.name() == dirTag
1877 || token.name() == divTag
1878 || token.name() == dlTag
1879 || token.name() == fieldsetTag
1880 || token.name() == figcaptionTag
1881 || token.name() == figureTag
1882 || token.name() == footerTag
1883 || token.name() == headerTag
1884 || token.name() == hgroupTag
1885 || token.name() == listingTag
1886 || token.name() == menuTag
1887 || token.name() == navTag
1888 || token.name() == olTag
1889 || token.name() == preTag
1890 || token.name() == sectionTag
1891 || token.name() == summaryTag
1892 || token.name() == ulTag) {
1893 if (!m_tree.openElements()->inScope(token.name())) {
1897 m_tree.generateImpliedEndTags();
1898 if (!m_tree.currentElement()->hasLocalName(token.name()))
1900 m_tree.openElements()->popUntilPopped(token.name());
1903 if (token.name() == formTag) {
1904 RefPtr<Element> node = m_tree.takeForm();
1905 if (!node || !m_tree.openElements()->inScope(node.get())) {
1909 m_tree.generateImpliedEndTags();
1910 if (m_tree.currentElement() != node.get())
1912 m_tree.openElements()->remove(node.get());
1914 if (token.name() == pTag) {
1915 if (!m_tree.openElements()->inButtonScope(token.name())) {
1917 processFakeStartTag(pTag);
1918 ASSERT(m_tree.openElements()->inScope(token.name()));
1919 reprocessEndTag(token);
1922 m_tree.generateImpliedEndTagsWithExclusion(token.name());
1923 if (!m_tree.currentElement()->hasLocalName(token.name()))
1925 m_tree.openElements()->popUntilPopped(token.name());
1928 if (token.name() == liTag) {
1929 if (!m_tree.openElements()->inListItemScope(token.name())) {
1933 m_tree.generateImpliedEndTagsWithExclusion(token.name());
1934 if (!m_tree.currentElement()->hasLocalName(token.name()))
1936 m_tree.openElements()->popUntilPopped(token.name());
1939 if (token.name() == ddTag
1940 || token.name() == dtTag) {
1941 if (!m_tree.openElements()->inScope(token.name())) {
1945 m_tree.generateImpliedEndTagsWithExclusion(token.name());
1946 if (!m_tree.currentElement()->hasLocalName(token.name()))
1948 m_tree.openElements()->popUntilPopped(token.name());
1951 if (isNumberedHeaderTag(token.name())) {
1952 if (!m_tree.openElements()->hasNumberedHeaderElementInScope()) {
1956 m_tree.generateImpliedEndTags();
1957 if (!m_tree.currentElement()->hasLocalName(token.name()))
1959 m_tree.openElements()->popUntilNumberedHeaderElementPopped();
1962 if (isFormattingTag(token.name())) {
1963 callTheAdoptionAgency(token);
1966 if (token.name() == appletTag
1967 || token.name() == marqueeTag
1968 || token.name() == objectTag) {
1969 if (!m_tree.openElements()->inScope(token.name())) {
1973 m_tree.generateImpliedEndTags();
1974 if (!m_tree.currentElement()->hasLocalName(token.name()))
1976 m_tree.openElements()->popUntilPopped(token.name());
1977 m_tree.activeFormattingElements()->clearToLastMarker();
1980 if (token.name() == brTag) {
1982 processFakeStartTag(brTag);
1985 processAnyOtherEndTagForInBody(token);
1988 bool HTMLTreeBuilder::processCaptionEndTagForInCaption()
1990 if (!m_tree.openElements()->inTableScope(captionTag.localName())) {
1991 ASSERT(isParsingFragment());
1992 // FIXME: parse error
1995 m_tree.generateImpliedEndTags();
1996 // FIXME: parse error if (!m_tree.currentElement()->hasTagName(captionTag))
1997 m_tree.openElements()->popUntilPopped(captionTag.localName());
1998 m_tree.activeFormattingElements()->clearToLastMarker();
1999 setInsertionMode(InTableMode);
2003 bool HTMLTreeBuilder::processTrEndTagForInRow()
2005 if (!m_tree.openElements()->inTableScope(trTag.localName())) {
2006 ASSERT(isParsingFragment());
2007 // FIXME: parse error
2010 m_tree.openElements()->popUntilTableRowScopeMarker();
2011 ASSERT(m_tree.currentElement()->hasTagName(trTag));
2012 m_tree.openElements()->pop();
2013 setInsertionMode(InTableBodyMode);
2017 bool HTMLTreeBuilder::processTableEndTagForInTable()
2019 if (!m_tree.openElements()->inTableScope(tableTag)) {
2020 ASSERT(isParsingFragment());
2021 // FIXME: parse error.
2024 m_tree.openElements()->popUntilPopped(tableTag.localName());
2025 resetInsertionModeAppropriately();
2029 void HTMLTreeBuilder::processEndTagForInTable(AtomicHTMLToken& token)
2031 ASSERT(token.type() == HTMLToken::EndTag);
2032 if (token.name() == tableTag) {
2033 processTableEndTagForInTable();
2036 if (token.name() == bodyTag
2037 || isCaptionColOrColgroupTag(token.name())
2038 || token.name() == htmlTag
2039 || isTableBodyContextTag(token.name())
2040 || isTableCellContextTag(token.name())
2041 || token.name() == trTag) {
2045 // Is this redirection necessary here?
2046 HTMLConstructionSite::RedirectToFosterParentGuard redirecter(m_tree);
2047 processEndTagForInBody(token);
2050 void HTMLTreeBuilder::processEndTag(AtomicHTMLToken& token)
2052 ASSERT(token.type() == HTMLToken::EndTag);
2053 switch (insertionMode()) {
2055 ASSERT(insertionMode() == InitialMode);
2056 defaultForInitial();
2058 case BeforeHTMLMode:
2059 ASSERT(insertionMode() == BeforeHTMLMode);
2060 if (token.name() != headTag && token.name() != bodyTag && token.name() != htmlTag && token.name() != brTag) {
2064 defaultForBeforeHTML();
2066 case BeforeHeadMode:
2067 ASSERT(insertionMode() == BeforeHeadMode);
2068 if (token.name() != headTag && token.name() != bodyTag && token.name() != htmlTag && token.name() != brTag) {
2072 defaultForBeforeHead();
2075 ASSERT(insertionMode() == InHeadMode);
2076 if (token.name() == headTag) {
2077 m_tree.openElements()->popHTMLHeadElement();
2078 setInsertionMode(AfterHeadMode);
2081 if (token.name() != bodyTag && token.name() != htmlTag && token.name() != brTag) {
2088 ASSERT(insertionMode() == AfterHeadMode);
2089 if (token.name() != bodyTag && token.name() != htmlTag && token.name() != brTag) {
2093 defaultForAfterHead();
2096 ASSERT(insertionMode() == InBodyMode);
2097 processEndTagForInBody(token);
2100 ASSERT(insertionMode() == InTableMode);
2101 processEndTagForInTable(token);
2104 ASSERT(insertionMode() == InCaptionMode);
2105 if (token.name() == captionTag) {
2106 processCaptionEndTagForInCaption();
2109 if (token.name() == tableTag) {
2111 if (!processCaptionEndTagForInCaption()) {
2112 ASSERT(isParsingFragment());
2115 reprocessEndTag(token);
2118 if (token.name() == bodyTag
2119 || token.name() == colTag
2120 || token.name() == colgroupTag
2121 || token.name() == htmlTag
2122 || isTableBodyContextTag(token.name())
2123 || isTableCellContextTag(token.name())
2124 || token.name() == trTag) {
2128 processEndTagForInBody(token);
2130 case InColumnGroupMode:
2131 ASSERT(insertionMode() == InColumnGroupMode);
2132 if (token.name() == colgroupTag) {
2133 processColgroupEndTagForInColumnGroup();
2136 if (token.name() == colTag) {
2140 if (!processColgroupEndTagForInColumnGroup()) {
2141 ASSERT(isParsingFragment());
2144 reprocessEndTag(token);
2147 ASSERT(insertionMode() == InRowMode);
2148 processEndTagForInRow(token);
2151 ASSERT(insertionMode() == InCellMode);
2152 processEndTagForInCell(token);
2154 case InTableBodyMode:
2155 ASSERT(insertionMode() == InTableBodyMode);
2156 processEndTagForInTableBody(token);
2159 ASSERT(insertionMode() == AfterBodyMode);
2160 if (token.name() == htmlTag) {
2161 if (isParsingFragment()) {
2165 setInsertionMode(AfterAfterBodyMode);
2168 prepareToReprocessToken();
2170 case AfterAfterBodyMode:
2171 ASSERT(insertionMode() == AfterBodyMode || insertionMode() == AfterAfterBodyMode);
2173 setInsertionMode(InBodyMode);
2174 reprocessEndTag(token);
2176 case InHeadNoscriptMode:
2177 ASSERT(insertionMode() == InHeadNoscriptMode);
2178 if (token.name() == noscriptTag) {
2179 ASSERT(m_tree.currentElement()->hasTagName(noscriptTag));
2180 m_tree.openElements()->pop();
2181 ASSERT(m_tree.currentElement()->hasTagName(headTag));
2182 setInsertionMode(InHeadMode);
2185 if (token.name() != brTag) {
2189 defaultForInHeadNoscript();
2190 processToken(token);
2193 if (token.name() == scriptTag) {
2194 // Pause ourselves so that parsing stops until the script can be processed by the caller.
2196 ASSERT(m_tree.currentElement()->hasTagName(scriptTag));
2197 m_scriptToProcess = m_tree.currentElement();
2198 m_scriptToProcessStartPosition = WTF::toOneBasedTextPosition(m_lastScriptElementStartPosition);
2199 m_tree.openElements()->pop();
2200 if (isParsingFragment() && m_fragmentContext.scriptingPermission() == FragmentScriptingNotAllowed)
2201 m_scriptToProcess->removeAllChildren();
2202 setInsertionMode(m_originalInsertionMode);
2204 // This token will not have been created by the tokenizer if a
2205 // self-closing script tag was encountered and pre-HTML5 parser
2206 // quirks are enabled. We must set the tokenizer's state to
2207 // DataState explicitly if the tokenizer didn't have a chance to.
2208 ASSERT(m_tokenizer->state() == HTMLTokenizer::DataState || m_usePreHTML5ParserQuirks);
2209 m_tokenizer->setState(HTMLTokenizer::DataState);
2212 m_tree.openElements()->pop();
2213 setInsertionMode(m_originalInsertionMode);
2215 case InFramesetMode:
2216 ASSERT(insertionMode() == InFramesetMode);
2217 if (token.name() == framesetTag) {
2218 if (m_tree.currentElement() == m_tree.openElements()->htmlElement()) {
2222 m_tree.openElements()->pop();
2223 if (!isParsingFragment() && !m_tree.currentElement()->hasTagName(framesetTag))
2224 setInsertionMode(AfterFramesetMode);
2228 case AfterFramesetMode:
2229 ASSERT(insertionMode() == AfterFramesetMode);
2230 if (token.name() == htmlTag) {
2231 setInsertionMode(AfterAfterFramesetMode);
2235 case AfterAfterFramesetMode:
2236 ASSERT(insertionMode() == AfterFramesetMode || insertionMode() == AfterAfterFramesetMode);
2239 case InSelectInTableMode:
2240 ASSERT(insertionMode() == InSelectInTableMode);
2241 if (token.name() == captionTag
2242 || token.name() == tableTag
2243 || isTableBodyContextTag(token.name())
2244 || token.name() == trTag
2245 || isTableCellContextTag(token.name())) {
2247 if (m_tree.openElements()->inTableScope(token.name())) {
2248 AtomicHTMLToken endSelect(HTMLToken::EndTag, selectTag.localName());
2249 processEndTag(endSelect);
2250 reprocessEndTag(token);
2256 ASSERT(insertionMode() == InSelectMode || insertionMode() == InSelectInTableMode);
2257 if (token.name() == optgroupTag) {
2258 if (m_tree.currentElement()->hasTagName(optionTag) && m_tree.oneBelowTop()->hasTagName(optgroupTag))
2259 processFakeEndTag(optionTag);
2260 if (m_tree.currentElement()->hasTagName(optgroupTag)) {
2261 m_tree.openElements()->pop();
2267 if (token.name() == optionTag) {
2268 if (m_tree.currentElement()->hasTagName(optionTag)) {
2269 m_tree.openElements()->pop();
2275 if (token.name() == selectTag) {
2276 if (!m_tree.openElements()->inSelectScope(token.name())) {
2277 ASSERT(isParsingFragment());
2281 m_tree.openElements()->popUntilPopped(selectTag.localName());
2282 resetInsertionModeAppropriately();
2286 case InTableTextMode:
2287 defaultForInTableText();
2288 processEndTag(token);
2290 case InForeignContentMode:
2291 if (token.name() == SVGNames::scriptTag && m_tree.currentElement()->hasTagName(SVGNames::scriptTag)) {
2295 if (m_tree.currentElement()->namespaceURI() != xhtmlNamespaceURI) {
2296 // FIXME: This code just wants an Element* iterator, instead of an ElementRecord*
2297 HTMLElementStack::ElementRecord* nodeRecord = m_tree.openElements()->topRecord();
2298 if (!nodeRecord->element()->hasLocalName(token.name()))
2301 if (nodeRecord->element()->hasLocalName(token.name())) {
2302 m_tree.openElements()->popUntilPopped(nodeRecord->element());
2305 nodeRecord = nodeRecord->next();
2306 if (nodeRecord->element()->namespaceURI() == xhtmlNamespaceURI)
2310 // 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."
2311 processForeignContentUsingInBodyModeAndResetMode(token);
2316 void HTMLTreeBuilder::prepareToReprocessToken()
2318 if (m_hasPendingForeignInsertionModeSteps) {
2319 resetForeignInsertionMode();
2320 m_hasPendingForeignInsertionModeSteps = false;
2324 void HTMLTreeBuilder::reprocessStartTag(AtomicHTMLToken& token)
2326 prepareToReprocessToken();
2327 processStartTag(token);
2330 void HTMLTreeBuilder::reprocessEndTag(AtomicHTMLToken& token)
2332 prepareToReprocessToken();
2333 processEndTag(token);
2336 class HTMLTreeBuilder::FakeInsertionMode : public Noncopyable {
2338 FakeInsertionMode(HTMLTreeBuilder* treeBuilder, InsertionMode mode)
2339 : m_treeBuilder(treeBuilder)
2340 , m_originalMode(treeBuilder->insertionMode())
2342 m_treeBuilder->setFakeInsertionMode(mode);
2345 ~FakeInsertionMode()
2347 if (m_treeBuilder->isFakeInsertionMode())
2348 m_treeBuilder->setInsertionMode(m_originalMode);
2352 HTMLTreeBuilder* m_treeBuilder;
2353 InsertionMode m_originalMode;
2356 void HTMLTreeBuilder::processForeignContentUsingInBodyModeAndResetMode(AtomicHTMLToken& token)
2358 m_hasPendingForeignInsertionModeSteps = true;
2360 FakeInsertionMode fakeMode(this, InBodyMode);
2361 processToken(token);
2363 if (m_hasPendingForeignInsertionModeSteps)
2364 resetForeignInsertionMode();
2367 void HTMLTreeBuilder::resetForeignInsertionMode()
2369 if (insertionMode() == InForeignContentMode)
2370 resetInsertionModeAppropriately();
2373 void HTMLTreeBuilder::processComment(AtomicHTMLToken& token)
2375 ASSERT(token.type() == HTMLToken::Comment);
2376 if (m_insertionMode == InitialMode
2377 || m_insertionMode == BeforeHTMLMode
2378 || m_insertionMode == AfterAfterBodyMode
2379 || m_insertionMode == AfterAfterFramesetMode) {
2380 m_tree.insertCommentOnDocument(token);
2383 if (m_insertionMode == AfterBodyMode) {
2384 m_tree.insertCommentOnHTMLHtmlElement(token);
2387 if (m_insertionMode == InTableTextMode) {
2388 defaultForInTableText();
2389 processComment(token);
2392 m_tree.insertComment(token);
2395 void HTMLTreeBuilder::processCharacter(AtomicHTMLToken& token)
2397 ASSERT(token.type() == HTMLToken::Character);
2398 ExternalCharacterTokenBuffer buffer(token);
2399 processCharacterBuffer(buffer);
2402 void HTMLTreeBuilder::processCharacterBuffer(ExternalCharacterTokenBuffer& buffer)
2405 switch (insertionMode()) {
2407 ASSERT(insertionMode() == InitialMode);
2408 buffer.skipLeadingWhitespace();
2409 if (buffer.isEmpty())
2411 defaultForInitial();
2414 case BeforeHTMLMode: {
2415 ASSERT(insertionMode() == BeforeHTMLMode);
2416 buffer.skipLeadingWhitespace();
2417 if (buffer.isEmpty())
2419 defaultForBeforeHTML();
2422 case BeforeHeadMode: {
2423 ASSERT(insertionMode() == BeforeHeadMode);
2424 buffer.skipLeadingWhitespace();
2425 if (buffer.isEmpty())
2427 defaultForBeforeHead();
2431 ASSERT(insertionMode() == InHeadMode);
2432 String leadingWhitespace = buffer.takeLeadingWhitespace();
2433 if (!leadingWhitespace.isEmpty())
2434 m_tree.insertTextNode(leadingWhitespace);
2435 if (buffer.isEmpty())
2440 case AfterHeadMode: {
2441 ASSERT(insertionMode() == AfterHeadMode);
2442 String leadingWhitespace = buffer.takeLeadingWhitespace();
2443 if (!leadingWhitespace.isEmpty())
2444 m_tree.insertTextNode(leadingWhitespace);
2445 if (buffer.isEmpty())
2447 defaultForAfterHead();
2453 ASSERT(insertionMode() == InBodyMode || insertionMode() == InCaptionMode || insertionMode() == InCellMode);
2454 m_tree.reconstructTheActiveFormattingElements();
2455 String characters = buffer.takeRemaining();
2456 m_tree.insertTextNode(characters);
2457 if (m_framesetOk && !isAllWhitespaceOrReplacementCharacters(characters))
2458 m_framesetOk = false;
2462 case InTableBodyMode:
2464 ASSERT(insertionMode() == InTableMode || insertionMode() == InTableBodyMode || insertionMode() == InRowMode);
2465 ASSERT(m_pendingTableCharacters.isEmpty());
2466 m_originalInsertionMode = m_insertionMode;
2467 setInsertionMode(InTableTextMode);
2468 prepareToReprocessToken();
2471 case InTableTextMode: {
2472 buffer.giveRemainingTo(m_pendingTableCharacters);
2475 case InColumnGroupMode: {
2476 ASSERT(insertionMode() == InColumnGroupMode);
2477 String leadingWhitespace = buffer.takeLeadingWhitespace();
2478 if (!leadingWhitespace.isEmpty())
2479 m_tree.insertTextNode(leadingWhitespace);
2480 if (buffer.isEmpty())
2482 if (!processColgroupEndTagForInColumnGroup()) {
2483 ASSERT(isParsingFragment());
2484 // The spec tells us to drop these characters on the floor.
2485 buffer.takeLeadingNonWhitespace();
2486 if (buffer.isEmpty())
2489 prepareToReprocessToken();
2490 goto ReprocessBuffer;
2493 case AfterAfterBodyMode: {
2494 ASSERT(insertionMode() == AfterBodyMode || insertionMode() == AfterAfterBodyMode);
2495 // FIXME: parse error
2496 setInsertionMode(InBodyMode);
2497 prepareToReprocessToken();
2498 goto ReprocessBuffer;
2502 ASSERT(insertionMode() == TextMode);
2503 m_tree.insertTextNode(buffer.takeRemaining());
2506 case InHeadNoscriptMode: {
2507 ASSERT(insertionMode() == InHeadNoscriptMode);
2508 String leadingWhitespace = buffer.takeLeadingWhitespace();
2509 if (!leadingWhitespace.isEmpty())
2510 m_tree.insertTextNode(leadingWhitespace);
2511 if (buffer.isEmpty())
2513 defaultForInHeadNoscript();
2514 goto ReprocessBuffer;
2517 case InFramesetMode:
2518 case AfterFramesetMode: {
2519 ASSERT(insertionMode() == InFramesetMode || insertionMode() == AfterFramesetMode || insertionMode() == AfterAfterFramesetMode);
2520 String leadingWhitespace = buffer.takeRemainingWhitespace();
2521 if (!leadingWhitespace.isEmpty())
2522 m_tree.insertTextNode(leadingWhitespace);
2523 // FIXME: We should generate a parse error if we skipped over any
2524 // non-whitespace characters.
2527 case InSelectInTableMode:
2528 case InSelectMode: {
2529 ASSERT(insertionMode() == InSelectMode || insertionMode() == InSelectInTableMode);
2530 m_tree.insertTextNode(buffer.takeRemaining());
2533 case InForeignContentMode: {
2534 ASSERT(insertionMode() == InForeignContentMode);
2535 String characters = buffer.takeRemaining();
2536 m_tree.insertTextNode(characters);
2537 if (m_framesetOk && !isAllWhitespace(characters))
2538 m_framesetOk = false;
2541 case AfterAfterFramesetMode: {
2542 String leadingWhitespace = buffer.takeRemainingWhitespace();
2543 if (!leadingWhitespace.isEmpty()) {
2544 m_tree.reconstructTheActiveFormattingElements();
2545 m_tree.insertTextNode(leadingWhitespace);
2547 // FIXME: We should generate a parse error if we skipped over any
2548 // non-whitespace characters.
2554 void HTMLTreeBuilder::processEndOfFile(AtomicHTMLToken& token)
2556 ASSERT(token.type() == HTMLToken::EndOfFile);
2557 switch (insertionMode()) {
2559 ASSERT(insertionMode() == InitialMode);
2560 defaultForInitial();
2562 case BeforeHTMLMode:
2563 ASSERT(insertionMode() == BeforeHTMLMode);
2564 defaultForBeforeHTML();
2566 case BeforeHeadMode:
2567 ASSERT(insertionMode() == BeforeHeadMode);
2568 defaultForBeforeHead();
2571 ASSERT(insertionMode() == InHeadMode);
2575 ASSERT(insertionMode() == AfterHeadMode);
2576 defaultForAfterHead();
2582 ASSERT(insertionMode() == InBodyMode || insertionMode() == InCellMode || insertionMode() == InCaptionMode || insertionMode() == InRowMode);
2583 notImplemented(); // Emit parse error based on what elements are still open.
2586 case AfterAfterBodyMode:
2587 ASSERT(insertionMode() == AfterBodyMode || insertionMode() == AfterAfterBodyMode);
2589 case InHeadNoscriptMode:
2590 ASSERT(insertionMode() == InHeadNoscriptMode);
2591 defaultForInHeadNoscript();
2592 processEndOfFile(token);
2594 case AfterFramesetMode:
2595 case AfterAfterFramesetMode:
2596 ASSERT(insertionMode() == AfterFramesetMode || insertionMode() == AfterAfterFramesetMode);
2598 case InFramesetMode:
2600 case InTableBodyMode:
2601 case InSelectInTableMode:
2603 ASSERT(insertionMode() == InSelectMode || insertionMode() == InSelectInTableMode || insertionMode() == InTableMode || insertionMode() == InFramesetMode || insertionMode() == InTableBodyMode);
2604 if (m_tree.currentElement() != m_tree.openElements()->htmlElement())
2607 case InColumnGroupMode:
2608 if (m_tree.currentElement() == m_tree.openElements()->htmlElement()) {
2609 ASSERT(isParsingFragment());
2612 if (!processColgroupEndTagForInColumnGroup()) {
2613 ASSERT(isParsingFragment());
2616 prepareToReprocessToken();
2617 processEndOfFile(token);
2619 case InForeignContentMode:
2620 setInsertionMode(InBodyMode);
2621 processEndOfFile(token);
2623 case InTableTextMode:
2624 defaultForInTableText();
2625 processEndOfFile(token);
2629 if (m_tree.currentElement()->hasTagName(scriptTag))
2630 notImplemented(); // mark the script element as "already started".
2631 m_tree.openElements()->pop();
2632 setInsertionMode(m_originalInsertionMode);
2633 prepareToReprocessToken();
2634 processEndOfFile(token);
2637 ASSERT(m_tree.openElements()->top());
2638 m_tree.openElements()->popAll();
2641 void HTMLTreeBuilder::defaultForInitial()
2644 if (!m_fragmentContext.fragment())
2645 m_document->setCompatibilityMode(Document::QuirksMode);
2646 // FIXME: parse error
2647 setInsertionMode(BeforeHTMLMode);
2648 prepareToReprocessToken();
2651 void HTMLTreeBuilder::defaultForBeforeHTML()
2653 AtomicHTMLToken startHTML(HTMLToken::StartTag, htmlTag.localName());
2654 m_tree.insertHTMLHtmlStartTagBeforeHTML(startHTML);
2655 setInsertionMode(BeforeHeadMode);
2656 prepareToReprocessToken();
2659 void HTMLTreeBuilder::defaultForBeforeHead()
2661 AtomicHTMLToken startHead(HTMLToken::StartTag, headTag.localName());
2662 processStartTag(startHead);
2663 prepareToReprocessToken();
2666 void HTMLTreeBuilder::defaultForInHead()
2668 AtomicHTMLToken endHead(HTMLToken::EndTag, headTag.localName());
2669 processEndTag(endHead);
2670 prepareToReprocessToken();
2673 void HTMLTreeBuilder::defaultForInHeadNoscript()
2675 AtomicHTMLToken endNoscript(HTMLToken::EndTag, noscriptTag.localName());
2676 processEndTag(endNoscript);
2677 prepareToReprocessToken();
2680 void HTMLTreeBuilder::defaultForAfterHead()
2682 AtomicHTMLToken startBody(HTMLToken::StartTag, bodyTag.localName());
2683 processStartTag(startBody);
2684 m_framesetOk = true;
2685 prepareToReprocessToken();
2688 void HTMLTreeBuilder::defaultForInTableText()
2690 String characters = String::adopt(m_pendingTableCharacters);
2691 if (!isAllWhitespace(characters)) {
2692 // FIXME: parse error
2693 HTMLConstructionSite::RedirectToFosterParentGuard redirecter(m_tree);
2694 m_tree.reconstructTheActiveFormattingElements();
2695 m_tree.insertTextNode(characters);
2696 m_framesetOk = false;
2697 setInsertionMode(m_originalInsertionMode);
2698 prepareToReprocessToken();
2701 m_tree.insertTextNode(characters);
2702 setInsertionMode(m_originalInsertionMode);
2703 prepareToReprocessToken();
2706 bool HTMLTreeBuilder::processStartTagForInHead(AtomicHTMLToken& token)
2708 ASSERT(token.type() == HTMLToken::StartTag);
2709 if (token.name() == htmlTag) {
2710 m_tree.insertHTMLHtmlStartTagInBody(token);
2713 if (token.name() == baseTag
2714 || token.name() == basefontTag
2715 || token.name() == bgsoundTag
2716 || token.name() == commandTag
2717 || token.name() == linkTag
2718 || token.name() == metaTag) {
2719 m_tree.insertSelfClosingHTMLElement(token);
2720 // Note: The custom processing for the <meta> tag is done in HTMLMetaElement::process().
2723 if (token.name() == titleTag) {
2724 processGenericRCDATAStartTag(token);
2727 if (token.name() == noscriptTag) {
2728 if (scriptEnabled(m_document->frame())) {
2729 processGenericRawTextStartTag(token);
2732 m_tree.insertHTMLElement(token);
2733 setInsertionMode(InHeadNoscriptMode);
2736 if (token.name() == noframesTag || token.name() == styleTag) {
2737 processGenericRawTextStartTag(token);
2740 if (token.name() == scriptTag) {
2741 processScriptStartTag(token);
2742 if (m_usePreHTML5ParserQuirks && token.selfClosing())
2743 processFakeEndTag(scriptTag);
2746 if (token.name() == headTag) {
2753 void HTMLTreeBuilder::processGenericRCDATAStartTag(AtomicHTMLToken& token)
2755 ASSERT(token.type() == HTMLToken::StartTag);
2756 m_tree.insertHTMLElement(token);
2757 m_tokenizer->setState(HTMLTokenizer::RCDATAState);
2758 m_originalInsertionMode = m_insertionMode;
2759 setInsertionMode(TextMode);
2762 void HTMLTreeBuilder::processGenericRawTextStartTag(AtomicHTMLToken& token)
2764 ASSERT(token.type() == HTMLToken::StartTag);
2765 m_tree.insertHTMLElement(token);
2766 m_tokenizer->setState(HTMLTokenizer::RAWTEXTState);
2767 m_originalInsertionMode = m_insertionMode;
2768 setInsertionMode(TextMode);
2771 void HTMLTreeBuilder::processScriptStartTag(AtomicHTMLToken& token)
2773 ASSERT(token.type() == HTMLToken::StartTag);
2774 m_tree.insertScriptElement(token);
2775 m_tokenizer->setState(HTMLTokenizer::ScriptDataState);
2776 m_originalInsertionMode = m_insertionMode;
2778 TextPosition0 position = TextPosition0(WTF::ZeroBasedNumber::fromZeroBasedInt(m_tokenizer->lineNumber()), WTF::ZeroBasedNumber::base());
2779 m_lastScriptElementStartPosition = position;
2781 setInsertionMode(TextMode);
2784 void HTMLTreeBuilder::finished()
2787 if (isParsingFragment()) {
2788 m_fragmentContext.finished();
2792 // Warning, this may detach the parser. Do not do anything else after this.
2793 m_document->finishedParsing();
2796 bool HTMLTreeBuilder::scriptEnabled(Frame* frame)
2800 return frame->script()->canExecuteScripts(NotAboutToExecuteScript);
2803 bool HTMLTreeBuilder::pluginsEnabled(Frame* frame)
2807 return frame->loader()->subframeLoader()->allowPlugins(NotAboutToInstantiatePlugin);