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;
61 inline bool isHTMLSpaceOrReplacementCharacter(UChar character)
63 return isHTMLSpace(character) || character == replacementCharacter;
66 inline bool isAllWhitespace(const String& string)
68 return string.isAllSpecialCharacters<isHTMLSpace>();
71 inline bool isAllWhitespaceOrReplacementCharacters(const String& string)
73 return string.isAllSpecialCharacters<isHTMLSpaceOrReplacementCharacter>();
76 bool isNumberedHeaderTag(const AtomicString& tagName)
78 return tagName == h1Tag
86 bool isCaptionColOrColgroupTag(const AtomicString& tagName)
88 return tagName == captionTag
90 || tagName == colgroupTag;
93 bool isTableCellContextTag(const AtomicString& tagName)
95 return tagName == thTag || tagName == tdTag;
98 bool isTableBodyContextTag(const AtomicString& tagName)
100 return tagName == tbodyTag
101 || tagName == tfootTag
102 || tagName == theadTag;
105 // http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#special
106 bool isSpecialNode(Node* node)
108 if (node->hasTagName(SVGNames::foreignObjectTag))
110 if (node->namespaceURI() != xhtmlNamespaceURI)
112 const AtomicString& tagName = node->localName();
113 return tagName == addressTag
114 || tagName == appletTag
115 || tagName == areaTag
116 || tagName == articleTag
117 || tagName == asideTag
118 || tagName == baseTag
119 || tagName == basefontTag
120 || tagName == bgsoundTag
121 || tagName == blockquoteTag
122 || tagName == bodyTag
124 || tagName == buttonTag
125 || tagName == captionTag
126 || tagName == centerTag
128 || tagName == colgroupTag
129 || tagName == commandTag
131 || tagName == detailsTag
136 || tagName == embedTag
137 || tagName == fieldsetTag
138 || tagName == figcaptionTag
139 || tagName == figureTag
140 || tagName == footerTag
141 || tagName == formTag
142 || tagName == frameTag
143 || tagName == framesetTag
144 || isNumberedHeaderTag(tagName)
145 || tagName == headTag
146 || tagName == headerTag
147 || tagName == hgroupTag
149 || tagName == htmlTag
150 || tagName == iframeTag
152 || tagName == inputTag
153 || tagName == isindexTag
155 || tagName == linkTag
156 || tagName == listingTag
157 || tagName == marqueeTag
158 || tagName == menuTag
159 || tagName == metaTag
161 || tagName == noembedTag
162 || tagName == noframesTag
163 || tagName == noscriptTag
164 || tagName == objectTag
167 || tagName == paramTag
168 || tagName == plaintextTag
170 || tagName == scriptTag
171 || tagName == sectionTag
172 || tagName == selectTag
173 || tagName == styleTag
174 || tagName == summaryTag
175 || tagName == tableTag
176 || isTableBodyContextTag(tagName)
178 || tagName == textareaTag
180 || tagName == titleTag
184 || tagName == xmpTag;
187 bool isNonAnchorNonNobrFormattingTag(const AtomicString& tagName)
189 return tagName == bTag
191 || tagName == codeTag
193 || tagName == fontTag
196 || tagName == smallTag
197 || tagName == strikeTag
198 || tagName == strongTag
203 bool isNonAnchorFormattingTag(const AtomicString& tagName)
205 return tagName == nobrTag
206 || isNonAnchorNonNobrFormattingTag(tagName);
209 // http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#formatting
210 bool isFormattingTag(const AtomicString& tagName)
212 return tagName == aTag || isNonAnchorFormattingTag(tagName);
215 HTMLFormElement* closestFormAncestor(Element* element)
218 if (element->hasTagName(formTag))
219 return static_cast<HTMLFormElement*>(element);
220 Node* parent = element->parent();
221 if (!parent || !parent->isElementNode())
223 element = static_cast<Element*>(parent);
230 class HTMLTreeBuilder::ExternalCharacterTokenBuffer : public Noncopyable {
232 explicit ExternalCharacterTokenBuffer(AtomicHTMLToken& token)
233 : m_current(token.characters().data())
234 , m_end(m_current + token.characters().size())
239 explicit ExternalCharacterTokenBuffer(const String& string)
240 : m_current(string.characters())
241 , m_end(m_current + string.length())
246 ~ExternalCharacterTokenBuffer()
251 bool isEmpty() const { return m_current == m_end; }
253 void skipLeadingWhitespace()
255 skipLeading<isHTMLSpace>();
258 String takeLeadingWhitespace()
260 return takeLeading<isHTMLSpace>();
263 String takeLeadingNonWhitespace()
265 return takeLeading<isNotHTMLSpace>();
268 String takeRemaining()
271 const UChar* start = m_current;
273 return String(start, m_current - start);
276 void giveRemainingTo(Vector<UChar>& recipient)
278 recipient.append(m_current, m_end - m_current);
282 String takeRemainingWhitespace()
285 Vector<UChar> whitespace;
287 UChar cc = *m_current++;
289 whitespace.append(cc);
290 } while (m_current < m_end);
291 // Returning the null string when there aren't any whitespace
292 // characters is slightly cleaner semantically because we don't want
293 // to insert a text node (as opposed to inserting an empty text node).
294 if (whitespace.isEmpty())
296 return String::adopt(whitespace);
300 template<bool characterPredicate(UChar)>
304 while (characterPredicate(*m_current)) {
305 if (++m_current == m_end)
310 template<bool characterPredicate(UChar)>
314 const UChar* start = m_current;
315 skipLeading<characterPredicate>();
316 if (start == m_current)
318 return String(start, m_current - start);
321 const UChar* m_current;
326 HTMLTreeBuilder::HTMLTreeBuilder(HTMLTokenizer* tokenizer, HTMLDocument* document, bool reportErrors, bool usePreHTML5ParserQuirks)
328 , m_document(document)
329 , m_tree(document, FragmentScriptingAllowed, false)
330 , m_reportErrors(reportErrors)
332 , m_insertionMode(InitialMode)
333 , m_originalInsertionMode(InitialMode)
334 , m_secondaryInsertionMode(InitialMode)
335 , m_tokenizer(tokenizer)
336 , m_scriptToProcessStartLine(uninitializedLineNumberValue)
337 , m_lastScriptElementStartLine(uninitializedLineNumberValue)
338 , m_usePreHTML5ParserQuirks(usePreHTML5ParserQuirks)
342 // FIXME: Member variables should be grouped into self-initializing structs to
343 // minimize code duplication between these constructors.
344 HTMLTreeBuilder::HTMLTreeBuilder(HTMLTokenizer* tokenizer, DocumentFragment* fragment, Element* contextElement, FragmentScriptingPermission scriptingPermission, bool usePreHTML5ParserQuirks)
346 , m_fragmentContext(fragment, contextElement, scriptingPermission)
347 , m_document(m_fragmentContext.document())
348 , m_tree(m_document, scriptingPermission, true)
349 , m_reportErrors(false) // FIXME: Why not report errors in fragments?
351 , m_insertionMode(InitialMode)
352 , m_originalInsertionMode(InitialMode)
353 , m_secondaryInsertionMode(InitialMode)
354 , m_tokenizer(tokenizer)
355 , m_scriptToProcessStartLine(uninitializedLineNumberValue)
356 , m_lastScriptElementStartLine(uninitializedLineNumberValue)
357 , m_usePreHTML5ParserQuirks(usePreHTML5ParserQuirks)
359 if (contextElement) {
360 // Steps 4.2-4.6 of the HTML5 Fragment Case parsing algorithm:
361 // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-end.html#fragment-case
362 m_document->setCompatibilityMode(contextElement->document()->compatibilityMode());
363 processFakeStartTag(htmlTag);
364 resetInsertionModeAppropriately();
365 m_tree.setForm(closestFormAncestor(contextElement));
369 HTMLTreeBuilder::~HTMLTreeBuilder()
373 void HTMLTreeBuilder::detach()
375 // This call makes little sense in fragment mode, but for consistency
376 // DocumentParser expects detach() to always be called before it's destroyed.
378 // HTMLConstructionSite might be on the callstack when detach() is called
379 // otherwise we'd just call m_tree.clear() here instead.
383 HTMLTreeBuilder::FragmentParsingContext::FragmentParsingContext()
385 , m_contextElement(0)
386 , m_scriptingPermission(FragmentScriptingAllowed)
390 HTMLTreeBuilder::FragmentParsingContext::FragmentParsingContext(DocumentFragment* fragment, Element* contextElement, FragmentScriptingPermission scriptingPermission)
391 : m_dummyDocumentForFragmentParsing(HTMLDocument::create(0, KURL(), fragment->document()->baseURI()))
392 , m_fragment(fragment)
393 , m_contextElement(contextElement)
394 , m_scriptingPermission(scriptingPermission)
396 m_dummyDocumentForFragmentParsing->setCompatibilityMode(fragment->document()->compatibilityMode());
399 Document* HTMLTreeBuilder::FragmentParsingContext::document() const
402 return m_dummyDocumentForFragmentParsing.get();
405 void HTMLTreeBuilder::FragmentParsingContext::finished()
407 // Populate the DocumentFragment with the parsed content now that we're done.
408 ContainerNode* root = m_dummyDocumentForFragmentParsing.get();
409 if (m_contextElement)
410 root = m_dummyDocumentForFragmentParsing->documentElement();
411 m_fragment->takeAllChildrenFrom(root);
414 HTMLTreeBuilder::FragmentParsingContext::~FragmentParsingContext()
418 PassRefPtr<Element> HTMLTreeBuilder::takeScriptToProcess(int& scriptStartLine)
420 // Unpause ourselves, callers may pause us again when processing the script.
421 // The HTML5 spec is written as though scripts are executed inside the tree
422 // builder. We pause the parser to exit the tree builder, and then resume
423 // before running scripts.
425 scriptStartLine = m_scriptToProcessStartLine;
426 m_scriptToProcessStartLine = uninitializedLineNumberValue;
427 return m_scriptToProcess.release();
430 void HTMLTreeBuilder::constructTreeFromToken(HTMLToken& rawToken)
432 AtomicHTMLToken token(rawToken);
433 constructTreeFromAtomicToken(token);
436 void HTMLTreeBuilder::constructTreeFromAtomicToken(AtomicHTMLToken& token)
440 // Swallowing U+0000 characters isn't in the HTML5 spec, but turning all
441 // the U+0000 characters into replacement characters has compatibility
443 m_tokenizer->setForceNullCharacterReplacement(m_insertionMode == TextMode || m_insertionMode == InForeignContentMode);
444 m_tokenizer->setShouldAllowCDATA(m_insertionMode == InForeignContentMode && m_tree.currentElement()->namespaceURI() != xhtmlNamespaceURI);
447 void HTMLTreeBuilder::processToken(AtomicHTMLToken& token)
449 switch (token.type()) {
450 case HTMLToken::Uninitialized:
451 ASSERT_NOT_REACHED();
453 case HTMLToken::DOCTYPE:
454 processDoctypeToken(token);
456 case HTMLToken::StartTag:
457 processStartTag(token);
459 case HTMLToken::EndTag:
460 processEndTag(token);
462 case HTMLToken::Comment:
463 processComment(token);
465 case HTMLToken::Character:
466 processCharacter(token);
468 case HTMLToken::EndOfFile:
469 processEndOfFile(token);
474 void HTMLTreeBuilder::processDoctypeToken(AtomicHTMLToken& token)
476 ASSERT(token.type() == HTMLToken::DOCTYPE);
477 if (m_insertionMode == InitialMode) {
478 m_tree.insertDoctype(token);
479 setInsertionMode(BeforeHTMLMode);
482 if (m_insertionMode == InTableTextMode) {
483 defaultForInTableText();
484 processDoctypeToken(token);
490 void HTMLTreeBuilder::processFakeStartTag(const QualifiedName& tagName, PassRefPtr<NamedNodeMap> attributes)
492 // FIXME: We'll need a fancier conversion than just "localName" for SVG/MathML tags.
493 AtomicHTMLToken fakeToken(HTMLToken::StartTag, tagName.localName(), attributes);
494 processStartTag(fakeToken);
497 void HTMLTreeBuilder::processFakeEndTag(const QualifiedName& tagName)
499 // FIXME: We'll need a fancier conversion than just "localName" for SVG/MathML tags.
500 AtomicHTMLToken fakeToken(HTMLToken::EndTag, tagName.localName());
501 processEndTag(fakeToken);
504 void HTMLTreeBuilder::processFakeCharacters(const String& characters)
506 ASSERT(!characters.isEmpty());
507 ExternalCharacterTokenBuffer buffer(characters);
508 processCharacterBuffer(buffer);
511 void HTMLTreeBuilder::processFakePEndTagIfPInButtonScope()
513 if (!m_tree.openElements()->inButtonScope(pTag.localName()))
515 AtomicHTMLToken endP(HTMLToken::EndTag, pTag.localName());
519 PassRefPtr<NamedNodeMap> HTMLTreeBuilder::attributesForIsindexInput(AtomicHTMLToken& token)
521 RefPtr<NamedNodeMap> attributes = token.takeAtributes();
523 attributes = NamedNodeMap::create();
525 attributes->removeAttribute(nameAttr);
526 attributes->removeAttribute(actionAttr);
527 attributes->removeAttribute(promptAttr);
530 RefPtr<Attribute> mappedAttribute = Attribute::createMapped(nameAttr, isindexTag.localName());
531 attributes->insertAttribute(mappedAttribute.release(), false);
532 return attributes.release();
535 void HTMLTreeBuilder::processIsindexStartTagForInBody(AtomicHTMLToken& token)
537 ASSERT(token.type() == HTMLToken::StartTag);
538 ASSERT(token.name() == isindexTag);
542 notImplemented(); // Acknowledge self-closing flag
543 processFakeStartTag(formTag);
544 Attribute* actionAttribute = token.getAttributeItem(actionAttr);
545 if (actionAttribute) {
546 ASSERT(m_tree.currentElement()->hasTagName(formTag));
547 m_tree.currentElement()->setAttribute(actionAttr, actionAttribute->value());
549 processFakeStartTag(hrTag);
550 processFakeStartTag(labelTag);
551 Attribute* promptAttribute = token.getAttributeItem(promptAttr);
553 processFakeCharacters(promptAttribute->value());
555 processFakeCharacters(searchableIndexIntroduction());
556 processFakeStartTag(inputTag, attributesForIsindexInput(token));
557 notImplemented(); // This second set of characters may be needed by non-english locales.
558 processFakeEndTag(labelTag);
559 processFakeStartTag(hrTag);
560 processFakeEndTag(formTag);
565 bool isLi(const Element* element)
567 return element->hasTagName(liTag);
570 bool isDdOrDt(const Element* element)
572 return element->hasTagName(ddTag)
573 || element->hasTagName(dtTag);
578 template <bool shouldClose(const Element*)>
579 void HTMLTreeBuilder::processCloseWhenNestedTag(AtomicHTMLToken& token)
581 m_framesetOk = false;
582 HTMLElementStack::ElementRecord* nodeRecord = m_tree.openElements()->topRecord();
584 Element* node = nodeRecord->element();
585 if (shouldClose(node)) {
586 processFakeEndTag(node->tagQName());
589 if (isSpecialNode(node) && !node->hasTagName(addressTag) && !node->hasTagName(divTag) && !node->hasTagName(pTag))
591 nodeRecord = nodeRecord->next();
593 processFakePEndTagIfPInButtonScope();
594 m_tree.insertHTMLElement(token);
599 typedef HashMap<AtomicString, QualifiedName> PrefixedNameToQualifiedNameMap;
601 void mapLoweredLocalNameToName(PrefixedNameToQualifiedNameMap* map, QualifiedName** names, size_t length)
603 for (size_t i = 0; i < length; ++i) {
604 const QualifiedName& name = *names[i];
605 const AtomicString& localName = name.localName();
606 AtomicString loweredLocalName = localName.lower();
607 if (loweredLocalName != localName)
608 map->add(loweredLocalName, name);
612 void adjustSVGTagNameCase(AtomicHTMLToken& token)
614 static PrefixedNameToQualifiedNameMap* caseMap = 0;
616 caseMap = new PrefixedNameToQualifiedNameMap;
618 QualifiedName** svgTags = SVGNames::getSVGTags(&length);
619 mapLoweredLocalNameToName(caseMap, svgTags, length);
622 const QualifiedName& casedName = caseMap->get(token.name());
623 if (casedName.localName().isNull())
625 token.setName(casedName.localName());
628 template<QualifiedName** getAttrs(size_t* length)>
629 void adjustAttributes(AtomicHTMLToken& token)
631 static PrefixedNameToQualifiedNameMap* caseMap = 0;
633 caseMap = new PrefixedNameToQualifiedNameMap;
635 QualifiedName** attrs = getAttrs(&length);
636 mapLoweredLocalNameToName(caseMap, attrs, length);
639 NamedNodeMap* attributes = token.attributes();
643 for (unsigned x = 0; x < attributes->length(); ++x) {
644 Attribute* attribute = attributes->attributeItem(x);
645 const QualifiedName& casedName = caseMap->get(attribute->localName());
646 if (!casedName.localName().isNull())
647 attribute->parserSetName(casedName);
651 void adjustSVGAttributes(AtomicHTMLToken& token)
653 adjustAttributes<SVGNames::getSVGAttrs>(token);
656 void adjustMathMLAttributes(AtomicHTMLToken& token)
658 adjustAttributes<MathMLNames::getMathMLAttrs>(token);
661 void addNamesWithPrefix(PrefixedNameToQualifiedNameMap* map, const AtomicString& prefix, QualifiedName** names, size_t length)
663 for (size_t i = 0; i < length; ++i) {
664 QualifiedName* name = names[i];
665 const AtomicString& localName = name->localName();
666 AtomicString prefixColonLocalName(prefix + ":" + localName);
667 QualifiedName nameWithPrefix(prefix, localName, name->namespaceURI());
668 map->add(prefixColonLocalName, nameWithPrefix);
672 void adjustForeignAttributes(AtomicHTMLToken& token)
674 static PrefixedNameToQualifiedNameMap* map = 0;
676 map = new PrefixedNameToQualifiedNameMap;
678 QualifiedName** attrs = XLinkNames::getXLinkAttrs(&length);
679 addNamesWithPrefix(map, "xlink", attrs, length);
681 attrs = XMLNames::getXMLAttrs(&length);
682 addNamesWithPrefix(map, "xml", attrs, length);
684 map->add("xmlns", XMLNSNames::xmlnsAttr);
685 map->add("xmlns:xlink", QualifiedName("xmlns", "xlink", XMLNSNames::xmlnsNamespaceURI));
688 NamedNodeMap* attributes = token.attributes();
692 for (unsigned x = 0; x < attributes->length(); ++x) {
693 Attribute* attribute = attributes->attributeItem(x);
694 const QualifiedName& name = map->get(attribute->localName());
695 if (!name.localName().isNull())
696 attribute->parserSetName(name);
702 void HTMLTreeBuilder::processStartTagForInBody(AtomicHTMLToken& token)
704 ASSERT(token.type() == HTMLToken::StartTag);
705 if (token.name() == htmlTag) {
706 m_tree.insertHTMLHtmlStartTagInBody(token);
709 if (token.name() == baseTag
710 || token.name() == basefontTag
711 || token.name() == bgsoundTag
712 || token.name() == commandTag
713 || token.name() == linkTag
714 || token.name() == metaTag
715 || token.name() == noframesTag
716 || token.name() == scriptTag
717 || token.name() == styleTag
718 || token.name() == titleTag) {
719 bool didProcess = processStartTagForInHead(token);
720 ASSERT_UNUSED(didProcess, didProcess);
723 if (token.name() == bodyTag) {
724 if (!m_tree.openElements()->secondElementIsHTMLBodyElement() || m_tree.openElements()->hasOnlyOneElement()) {
725 ASSERT(isParsingFragment());
728 m_tree.insertHTMLBodyStartTagInBody(token);
731 if (token.name() == framesetTag) {
733 if (!m_tree.openElements()->secondElementIsHTMLBodyElement() || m_tree.openElements()->hasOnlyOneElement()) {
734 ASSERT(isParsingFragment());
739 ExceptionCode ec = 0;
740 m_tree.openElements()->bodyElement()->remove(ec);
742 m_tree.openElements()->popUntil(m_tree.openElements()->bodyElement());
743 m_tree.openElements()->popHTMLBodyElement();
744 ASSERT(m_tree.openElements()->top() == m_tree.openElements()->htmlElement());
745 m_tree.insertHTMLElement(token);
746 setInsertionMode(InFramesetMode);
749 if (token.name() == addressTag
750 || token.name() == articleTag
751 || token.name() == asideTag
752 || token.name() == blockquoteTag
753 || token.name() == centerTag
754 || token.name() == detailsTag
755 || token.name() == dirTag
756 || token.name() == divTag
757 || token.name() == dlTag
758 || token.name() == fieldsetTag
759 || token.name() == figcaptionTag
760 || token.name() == figureTag
761 || token.name() == footerTag
762 || token.name() == headerTag
763 || token.name() == hgroupTag
764 || token.name() == menuTag
765 || token.name() == navTag
766 || token.name() == olTag
767 || token.name() == pTag
768 || token.name() == sectionTag
769 || token.name() == summaryTag
770 || token.name() == ulTag) {
771 processFakePEndTagIfPInButtonScope();
772 m_tree.insertHTMLElement(token);
775 if (isNumberedHeaderTag(token.name())) {
776 processFakePEndTagIfPInButtonScope();
777 if (isNumberedHeaderTag(m_tree.currentElement()->localName())) {
779 m_tree.openElements()->pop();
781 m_tree.insertHTMLElement(token);
784 if (token.name() == preTag || token.name() == listingTag) {
785 processFakePEndTagIfPInButtonScope();
786 m_tree.insertHTMLElement(token);
787 m_tokenizer->setSkipLeadingNewLineForListing(true);
788 m_framesetOk = false;
791 if (token.name() == formTag) {
796 processFakePEndTagIfPInButtonScope();
797 m_tree.insertHTMLFormElement(token);
800 if (token.name() == liTag) {
801 processCloseWhenNestedTag<isLi>(token);
804 if (token.name() == ddTag || token.name() == dtTag) {
805 processCloseWhenNestedTag<isDdOrDt>(token);
808 if (token.name() == plaintextTag) {
809 processFakePEndTagIfPInButtonScope();
810 m_tree.insertHTMLElement(token);
811 m_tokenizer->setState(HTMLTokenizer::PLAINTEXTState);
814 if (token.name() == buttonTag) {
815 if (m_tree.openElements()->inScope(buttonTag)) {
817 processFakeEndTag(buttonTag);
818 processStartTag(token); // FIXME: Could we just fall through here?
821 m_tree.reconstructTheActiveFormattingElements();
822 m_tree.insertHTMLElement(token);
823 m_framesetOk = false;
826 if (token.name() == aTag) {
827 Element* activeATag = m_tree.activeFormattingElements()->closestElementInScopeWithName(aTag.localName());
830 processFakeEndTag(aTag);
831 m_tree.activeFormattingElements()->remove(activeATag);
832 if (m_tree.openElements()->contains(activeATag))
833 m_tree.openElements()->remove(activeATag);
835 m_tree.reconstructTheActiveFormattingElements();
836 m_tree.insertFormattingElement(token);
839 if (isNonAnchorNonNobrFormattingTag(token.name())) {
840 m_tree.reconstructTheActiveFormattingElements();
841 m_tree.insertFormattingElement(token);
844 if (token.name() == nobrTag) {
845 m_tree.reconstructTheActiveFormattingElements();
846 if (m_tree.openElements()->inScope(nobrTag)) {
848 processFakeEndTag(nobrTag);
849 m_tree.reconstructTheActiveFormattingElements();
851 m_tree.insertFormattingElement(token);
854 if (token.name() == appletTag
855 || token.name() == marqueeTag
856 || token.name() == objectTag) {
857 m_tree.reconstructTheActiveFormattingElements();
858 m_tree.insertHTMLElement(token);
859 m_tree.activeFormattingElements()->appendMarker();
860 m_framesetOk = false;
863 if (token.name() == tableTag) {
864 if (!m_document->inQuirksMode() && m_tree.openElements()->inButtonScope(pTag))
865 processFakeEndTag(pTag);
866 m_tree.insertHTMLElement(token);
867 m_framesetOk = false;
868 setInsertionMode(InTableMode);
871 if (token.name() == imageTag) {
873 // Apparently we're not supposed to ask.
874 token.setName(imgTag.localName());
875 // Note the fall through to the imgTag handling below!
877 if (token.name() == areaTag
878 || token.name() == brTag
879 || token.name() == embedTag
880 || token.name() == imgTag
881 || token.name() == inputTag
882 || token.name() == keygenTag
883 || token.name() == wbrTag) {
884 m_tree.reconstructTheActiveFormattingElements();
885 m_tree.insertSelfClosingHTMLElement(token);
886 m_framesetOk = false;
889 if (token.name() == paramTag
890 || token.name() == sourceTag
891 || token.name() == trackTag) {
892 m_tree.insertSelfClosingHTMLElement(token);
895 if (token.name() == hrTag) {
896 processFakePEndTagIfPInButtonScope();
897 m_tree.insertSelfClosingHTMLElement(token);
898 m_framesetOk = false;
901 if (token.name() == isindexTag) {
902 processIsindexStartTagForInBody(token);
905 if (token.name() == textareaTag) {
906 m_tree.insertHTMLElement(token);
907 m_tokenizer->setSkipLeadingNewLineForListing(true);
908 m_tokenizer->setState(HTMLTokenizer::RCDATAState);
909 m_originalInsertionMode = m_insertionMode;
910 m_framesetOk = false;
911 setInsertionMode(TextMode);
914 if (token.name() == xmpTag) {
915 processFakePEndTagIfPInButtonScope();
916 m_tree.reconstructTheActiveFormattingElements();
917 m_framesetOk = false;
918 processGenericRawTextStartTag(token);
921 if (token.name() == iframeTag) {
922 m_framesetOk = false;
923 processGenericRawTextStartTag(token);
926 if (token.name() == noembedTag && pluginsEnabled(m_document->frame())) {
927 processGenericRawTextStartTag(token);
930 if (token.name() == noscriptTag && scriptEnabled(m_document->frame())) {
931 processGenericRawTextStartTag(token);
934 if (token.name() == selectTag) {
935 m_tree.reconstructTheActiveFormattingElements();
936 m_tree.insertHTMLElement(token);
937 m_framesetOk = false;
938 if (m_insertionMode == InTableMode
939 || m_insertionMode == InCaptionMode
940 || m_insertionMode == InColumnGroupMode
941 || m_insertionMode == InTableBodyMode
942 || m_insertionMode == InRowMode
943 || m_insertionMode == InCellMode)
944 setInsertionMode(InSelectInTableMode);
946 setInsertionMode(InSelectMode);
949 if (token.name() == optgroupTag || token.name() == optionTag) {
950 if (m_tree.openElements()->inScope(optionTag.localName())) {
951 AtomicHTMLToken endOption(HTMLToken::EndTag, optionTag.localName());
952 processEndTag(endOption);
954 m_tree.reconstructTheActiveFormattingElements();
955 m_tree.insertHTMLElement(token);
958 if (token.name() == rpTag || token.name() == rtTag) {
959 if (m_tree.openElements()->inScope(rubyTag.localName())) {
960 m_tree.generateImpliedEndTags();
961 if (!m_tree.currentElement()->hasTagName(rubyTag)) {
963 m_tree.openElements()->popUntil(rubyTag.localName());
966 m_tree.insertHTMLElement(token);
969 if (token.name() == MathMLNames::mathTag.localName()) {
970 m_tree.reconstructTheActiveFormattingElements();
971 adjustMathMLAttributes(token);
972 adjustForeignAttributes(token);
973 m_tree.insertForeignElement(token, MathMLNames::mathmlNamespaceURI);
974 if (m_insertionMode != InForeignContentMode) {
975 setSecondaryInsertionMode(m_insertionMode);
976 setInsertionMode(InForeignContentMode);
980 if (token.name() == SVGNames::svgTag.localName()) {
981 m_tree.reconstructTheActiveFormattingElements();
982 adjustSVGAttributes(token);
983 adjustForeignAttributes(token);
984 m_tree.insertForeignElement(token, SVGNames::svgNamespaceURI);
985 if (m_insertionMode != InForeignContentMode) {
986 setSecondaryInsertionMode(m_insertionMode);
987 setInsertionMode(InForeignContentMode);
991 if (isCaptionColOrColgroupTag(token.name())
992 || token.name() == frameTag
993 || token.name() == headTag
994 || isTableBodyContextTag(token.name())
995 || isTableCellContextTag(token.name())
996 || token.name() == trTag) {
1000 m_tree.reconstructTheActiveFormattingElements();
1001 m_tree.insertHTMLElement(token);
1004 bool HTMLTreeBuilder::processColgroupEndTagForInColumnGroup()
1006 if (m_tree.currentElement() == m_tree.openElements()->htmlElement()) {
1007 ASSERT(isParsingFragment());
1008 // FIXME: parse error
1011 m_tree.openElements()->pop();
1012 setInsertionMode(InTableMode);
1016 // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#close-the-cell
1017 void HTMLTreeBuilder::closeTheCell()
1019 ASSERT(insertionMode() == InCellMode);
1020 if (m_tree.openElements()->inTableScope(tdTag)) {
1021 ASSERT(!m_tree.openElements()->inTableScope(thTag));
1022 processFakeEndTag(tdTag);
1025 ASSERT(m_tree.openElements()->inTableScope(thTag));
1026 processFakeEndTag(thTag);
1027 ASSERT(insertionMode() == InRowMode);
1030 void HTMLTreeBuilder::processStartTagForInTable(AtomicHTMLToken& token)
1032 ASSERT(token.type() == HTMLToken::StartTag);
1033 if (token.name() == captionTag) {
1034 m_tree.openElements()->popUntilTableScopeMarker();
1035 m_tree.activeFormattingElements()->appendMarker();
1036 m_tree.insertHTMLElement(token);
1037 setInsertionMode(InCaptionMode);
1040 if (token.name() == colgroupTag) {
1041 m_tree.openElements()->popUntilTableScopeMarker();
1042 m_tree.insertHTMLElement(token);
1043 setInsertionMode(InColumnGroupMode);
1046 if (token.name() == colTag) {
1047 processFakeStartTag(colgroupTag);
1048 ASSERT(InColumnGroupMode);
1049 processStartTag(token);
1052 if (isTableBodyContextTag(token.name())) {
1053 m_tree.openElements()->popUntilTableScopeMarker();
1054 m_tree.insertHTMLElement(token);
1055 setInsertionMode(InTableBodyMode);
1058 if (isTableCellContextTag(token.name())
1059 || token.name() == trTag) {
1060 processFakeStartTag(tbodyTag);
1061 ASSERT(insertionMode() == InTableBodyMode);
1062 processStartTag(token);
1065 if (token.name() == tableTag) {
1067 if (!processTableEndTagForInTable()) {
1068 ASSERT(isParsingFragment());
1071 processStartTag(token);
1074 if (token.name() == styleTag || token.name() == scriptTag) {
1075 processStartTagForInHead(token);
1078 if (token.name() == inputTag) {
1079 Attribute* typeAttribute = token.getAttributeItem(typeAttr);
1080 if (typeAttribute && equalIgnoringCase(typeAttribute->value(), "hidden")) {
1082 m_tree.insertSelfClosingHTMLElement(token);
1085 // Fall through to "anything else" case.
1087 if (token.name() == formTag) {
1091 m_tree.insertHTMLFormElement(token, true);
1092 m_tree.openElements()->pop();
1096 HTMLConstructionSite::RedirectToFosterParentGuard redirecter(m_tree);
1097 processStartTagForInBody(token);
1102 bool shouldProcessUsingSecondaryInsertionMode(AtomicHTMLToken& token, Element* currentElement)
1104 ASSERT(token.type() == HTMLToken::StartTag);
1105 if (currentElement->hasTagName(MathMLNames::miTag)
1106 || currentElement->hasTagName(MathMLNames::moTag)
1107 || currentElement->hasTagName(MathMLNames::mnTag)
1108 || currentElement->hasTagName(MathMLNames::msTag)
1109 || currentElement->hasTagName(MathMLNames::mtextTag)) {
1110 return token.name() != MathMLNames::mglyphTag
1111 && token.name() != MathMLNames::malignmarkTag;
1113 if (currentElement->hasTagName(MathMLNames::annotation_xmlTag))
1114 return token.name() == SVGNames::svgTag;
1115 if (currentElement->hasTagName(SVGNames::foreignObjectTag)
1116 || currentElement->hasTagName(SVGNames::descTag)
1117 || currentElement->hasTagName(SVGNames::titleTag))
1119 return currentElement->namespaceURI() == HTMLNames::xhtmlNamespaceURI;
1124 void HTMLTreeBuilder::processStartTag(AtomicHTMLToken& token)
1126 ASSERT(token.type() == HTMLToken::StartTag);
1127 switch (insertionMode()) {
1129 ASSERT(insertionMode() == InitialMode);
1130 defaultForInitial();
1132 case BeforeHTMLMode:
1133 ASSERT(insertionMode() == BeforeHTMLMode);
1134 if (token.name() == htmlTag) {
1135 m_tree.insertHTMLHtmlStartTagBeforeHTML(token);
1136 setInsertionMode(BeforeHeadMode);
1139 defaultForBeforeHTML();
1141 case BeforeHeadMode:
1142 ASSERT(insertionMode() == BeforeHeadMode);
1143 if (token.name() == htmlTag) {
1144 m_tree.insertHTMLHtmlStartTagInBody(token);
1147 if (token.name() == headTag) {
1148 m_tree.insertHTMLHeadElement(token);
1149 setInsertionMode(InHeadMode);
1152 defaultForBeforeHead();
1155 ASSERT(insertionMode() == InHeadMode);
1156 if (processStartTagForInHead(token))
1161 ASSERT(insertionMode() == AfterHeadMode);
1162 if (token.name() == htmlTag) {
1163 m_tree.insertHTMLHtmlStartTagInBody(token);
1166 if (token.name() == bodyTag) {
1167 m_framesetOk = false;
1168 m_tree.insertHTMLBodyElement(token);
1169 setInsertionMode(InBodyMode);
1172 if (token.name() == framesetTag) {
1173 m_tree.insertHTMLElement(token);
1174 setInsertionMode(InFramesetMode);
1177 if (token.name() == baseTag
1178 || token.name() == basefontTag
1179 || token.name() == bgsoundTag
1180 || token.name() == linkTag
1181 || token.name() == metaTag
1182 || token.name() == noframesTag
1183 || token.name() == scriptTag
1184 || token.name() == styleTag
1185 || token.name() == titleTag) {
1187 ASSERT(m_tree.head());
1188 m_tree.openElements()->pushHTMLHeadElement(m_tree.head());
1189 processStartTagForInHead(token);
1190 m_tree.openElements()->removeHTMLHeadElement(m_tree.head());
1193 if (token.name() == headTag) {
1197 defaultForAfterHead();
1200 ASSERT(insertionMode() == InBodyMode);
1201 processStartTagForInBody(token);
1204 ASSERT(insertionMode() == InTableMode);
1205 processStartTagForInTable(token);
1208 ASSERT(insertionMode() == InCaptionMode);
1209 if (isCaptionColOrColgroupTag(token.name())
1210 || isTableBodyContextTag(token.name())
1211 || isTableCellContextTag(token.name())
1212 || token.name() == trTag) {
1214 if (!processCaptionEndTagForInCaption()) {
1215 ASSERT(isParsingFragment());
1218 processStartTag(token);
1221 processStartTagForInBody(token);
1223 case InColumnGroupMode:
1224 ASSERT(insertionMode() == InColumnGroupMode);
1225 if (token.name() == htmlTag) {
1226 m_tree.insertHTMLHtmlStartTagInBody(token);
1229 if (token.name() == colTag) {
1230 m_tree.insertSelfClosingHTMLElement(token);
1233 if (!processColgroupEndTagForInColumnGroup()) {
1234 ASSERT(isParsingFragment());
1237 processStartTag(token);
1239 case InTableBodyMode:
1240 ASSERT(insertionMode() == InTableBodyMode);
1241 if (token.name() == trTag) {
1242 m_tree.openElements()->popUntilTableBodyScopeMarker(); // How is there ever anything to pop?
1243 m_tree.insertHTMLElement(token);
1244 setInsertionMode(InRowMode);
1247 if (isTableCellContextTag(token.name())) {
1249 processFakeStartTag(trTag);
1250 ASSERT(insertionMode() == InRowMode);
1251 processStartTag(token);
1254 if (isCaptionColOrColgroupTag(token.name()) || isTableBodyContextTag(token.name())) {
1255 // FIXME: This is slow.
1256 if (!m_tree.openElements()->inTableScope(tbodyTag.localName()) && !m_tree.openElements()->inTableScope(theadTag.localName()) && !m_tree.openElements()->inTableScope(tfootTag.localName())) {
1257 ASSERT(isParsingFragment());
1261 m_tree.openElements()->popUntilTableBodyScopeMarker();
1262 ASSERT(isTableBodyContextTag(m_tree.currentElement()->localName()));
1263 processFakeEndTag(m_tree.currentElement()->tagQName());
1264 processStartTag(token);
1267 processStartTagForInTable(token);
1270 ASSERT(insertionMode() == InRowMode);
1271 if (isTableCellContextTag(token.name())) {
1272 m_tree.openElements()->popUntilTableRowScopeMarker();
1273 m_tree.insertHTMLElement(token);
1274 setInsertionMode(InCellMode);
1275 m_tree.activeFormattingElements()->appendMarker();
1278 if (token.name() == trTag
1279 || isCaptionColOrColgroupTag(token.name())
1280 || isTableBodyContextTag(token.name())) {
1281 if (!processTrEndTagForInRow()) {
1282 ASSERT(isParsingFragment());
1285 ASSERT(insertionMode() == InTableBodyMode);
1286 processStartTag(token);
1289 processStartTagForInTable(token);
1292 ASSERT(insertionMode() == InCellMode);
1293 if (isCaptionColOrColgroupTag(token.name())
1294 || isTableCellContextTag(token.name())
1295 || token.name() == trTag
1296 || isTableBodyContextTag(token.name())) {
1297 // FIXME: This could be more efficient.
1298 if (!m_tree.openElements()->inTableScope(tdTag) && !m_tree.openElements()->inTableScope(thTag)) {
1299 ASSERT(isParsingFragment());
1304 processStartTag(token);
1307 processStartTagForInBody(token);
1310 case AfterAfterBodyMode:
1311 ASSERT(insertionMode() == AfterBodyMode || insertionMode() == AfterAfterBodyMode);
1312 if (token.name() == htmlTag) {
1313 m_tree.insertHTMLHtmlStartTagInBody(token);
1316 setInsertionMode(InBodyMode);
1317 processStartTag(token);
1319 case InHeadNoscriptMode:
1320 ASSERT(insertionMode() == InHeadNoscriptMode);
1321 if (token.name() == htmlTag) {
1322 m_tree.insertHTMLHtmlStartTagInBody(token);
1325 if (token.name() == basefontTag
1326 || token.name() == bgsoundTag
1327 || token.name() == linkTag
1328 || token.name() == metaTag
1329 || token.name() == noframesTag
1330 || token.name() == styleTag) {
1331 bool didProcess = processStartTagForInHead(token);
1332 ASSERT_UNUSED(didProcess, didProcess);
1335 if (token.name() == htmlTag || token.name() == noscriptTag) {
1339 defaultForInHeadNoscript();
1340 processToken(token);
1342 case InFramesetMode:
1343 ASSERT(insertionMode() == InFramesetMode);
1344 if (token.name() == htmlTag) {
1345 m_tree.insertHTMLHtmlStartTagInBody(token);
1348 if (token.name() == framesetTag) {
1349 m_tree.insertHTMLElement(token);
1352 if (token.name() == frameTag) {
1353 m_tree.insertSelfClosingHTMLElement(token);
1356 if (token.name() == noframesTag) {
1357 processStartTagForInHead(token);
1362 case AfterFramesetMode:
1363 case AfterAfterFramesetMode:
1364 ASSERT(insertionMode() == AfterFramesetMode || insertionMode() == AfterAfterFramesetMode);
1365 if (token.name() == htmlTag) {
1366 m_tree.insertHTMLHtmlStartTagInBody(token);
1369 if (token.name() == noframesTag) {
1370 processStartTagForInHead(token);
1375 case InSelectInTableMode:
1376 ASSERT(insertionMode() == InSelectInTableMode);
1377 if (token.name() == captionTag
1378 || token.name() == tableTag
1379 || isTableBodyContextTag(token.name())
1380 || token.name() == trTag
1381 || isTableCellContextTag(token.name())) {
1383 AtomicHTMLToken endSelect(HTMLToken::EndTag, selectTag.localName());
1384 processEndTag(endSelect);
1385 processStartTag(token);
1390 ASSERT(insertionMode() == InSelectMode || insertionMode() == InSelectInTableMode);
1391 if (token.name() == htmlTag) {
1392 m_tree.insertHTMLHtmlStartTagInBody(token);
1395 if (token.name() == optionTag) {
1396 if (m_tree.currentElement()->hasTagName(optionTag)) {
1397 AtomicHTMLToken endOption(HTMLToken::EndTag, optionTag.localName());
1398 processEndTag(endOption);
1400 m_tree.insertHTMLElement(token);
1403 if (token.name() == optgroupTag) {
1404 if (m_tree.currentElement()->hasTagName(optionTag)) {
1405 AtomicHTMLToken endOption(HTMLToken::EndTag, optionTag.localName());
1406 processEndTag(endOption);
1408 if (m_tree.currentElement()->hasTagName(optgroupTag)) {
1409 AtomicHTMLToken endOptgroup(HTMLToken::EndTag, optgroupTag.localName());
1410 processEndTag(endOptgroup);
1412 m_tree.insertHTMLElement(token);
1415 if (token.name() == selectTag) {
1417 AtomicHTMLToken endSelect(HTMLToken::EndTag, selectTag.localName());
1418 processEndTag(endSelect);
1421 if (token.name() == inputTag
1422 || token.name() == keygenTag
1423 || token.name() == textareaTag) {
1425 if (!m_tree.openElements()->inSelectScope(selectTag)) {
1426 ASSERT(isParsingFragment());
1429 AtomicHTMLToken endSelect(HTMLToken::EndTag, selectTag.localName());
1430 processEndTag(endSelect);
1431 processStartTag(token);
1434 if (token.name() == scriptTag) {
1435 bool didProcess = processStartTagForInHead(token);
1436 ASSERT_UNUSED(didProcess, didProcess);
1440 case InTableTextMode:
1441 defaultForInTableText();
1442 processStartTag(token);
1444 case InForeignContentMode: {
1445 if (shouldProcessUsingSecondaryInsertionMode(token, m_tree.currentElement())) {
1446 processUsingSecondaryInsertionModeAndAdjustInsertionMode(token);
1449 if (token.name() == bTag
1450 || token.name() == bigTag
1451 || token.name() == blockquoteTag
1452 || token.name() == bodyTag
1453 || token.name() == brTag
1454 || token.name() == centerTag
1455 || token.name() == codeTag
1456 || token.name() == ddTag
1457 || token.name() == divTag
1458 || token.name() == dlTag
1459 || token.name() == dtTag
1460 || token.name() == emTag
1461 || token.name() == embedTag
1462 || isNumberedHeaderTag(token.name())
1463 || token.name() == headTag
1464 || token.name() == hrTag
1465 || token.name() == iTag
1466 || token.name() == imgTag
1467 || token.name() == liTag
1468 || token.name() == listingTag
1469 || token.name() == menuTag
1470 || token.name() == metaTag
1471 || token.name() == nobrTag
1472 || token.name() == olTag
1473 || token.name() == pTag
1474 || token.name() == preTag
1475 || token.name() == rubyTag
1476 || token.name() == sTag
1477 || token.name() == smallTag
1478 || token.name() == spanTag
1479 || token.name() == strongTag
1480 || token.name() == strikeTag
1481 || token.name() == subTag
1482 || token.name() == supTag
1483 || token.name() == tableTag
1484 || token.name() == ttTag
1485 || token.name() == uTag
1486 || token.name() == ulTag
1487 || token.name() == varTag
1488 || (token.name() == fontTag && (token.getAttributeItem(colorAttr) || token.getAttributeItem(faceAttr) || token.getAttributeItem(sizeAttr)))) {
1490 m_tree.openElements()->popUntilForeignContentScopeMarker();
1491 if (insertionMode() == InForeignContentMode && m_tree.openElements()->hasOnlyHTMLElementsInScope())
1492 setInsertionMode(m_secondaryInsertionMode);
1493 processStartTag(token);
1496 const AtomicString& currentNamespace = m_tree.currentElement()->namespaceURI();
1497 if (currentNamespace == MathMLNames::mathmlNamespaceURI)
1498 adjustMathMLAttributes(token);
1499 if (currentNamespace == SVGNames::svgNamespaceURI) {
1500 adjustSVGTagNameCase(token);
1501 adjustSVGAttributes(token);
1503 adjustForeignAttributes(token);
1504 m_tree.insertForeignElement(token, currentNamespace);
1508 ASSERT_NOT_REACHED();
1513 bool HTMLTreeBuilder::processBodyEndTagForInBody(AtomicHTMLToken& token)
1515 ASSERT(token.type() == HTMLToken::EndTag);
1516 ASSERT(token.name() == bodyTag);
1517 if (!m_tree.openElements()->inScope(bodyTag.localName())) {
1521 notImplemented(); // Emit a more specific parse error based on stack contents.
1522 setInsertionMode(AfterBodyMode);
1526 void HTMLTreeBuilder::processAnyOtherEndTagForInBody(AtomicHTMLToken& token)
1528 ASSERT(token.type() == HTMLToken::EndTag);
1529 HTMLElementStack::ElementRecord* record = m_tree.openElements()->topRecord();
1531 Element* node = record->element();
1532 if (node->hasLocalName(token.name())) {
1533 m_tree.generateImpliedEndTags();
1534 if (!m_tree.currentElement()->hasLocalName(token.name())) {
1536 // FIXME: This is either a bug in the spec, or a bug in our
1537 // implementation. Filed a bug with HTML5:
1538 // http://www.w3.org/Bugs/Public/show_bug.cgi?id=10080
1539 // We might have already popped the node for the token in
1540 // generateImpliedEndTags, just abort.
1541 if (!m_tree.openElements()->contains(node))
1544 m_tree.openElements()->popUntilPopped(node);
1547 if (isSpecialNode(node)) {
1551 record = record->next();
1555 // FIXME: This probably belongs on HTMLElementStack.
1556 HTMLElementStack::ElementRecord* HTMLTreeBuilder::furthestBlockForFormattingElement(Element* formattingElement)
1558 HTMLElementStack::ElementRecord* furthestBlock = 0;
1559 HTMLElementStack::ElementRecord* record = m_tree.openElements()->topRecord();
1560 for (; record; record = record->next()) {
1561 if (record->element() == formattingElement)
1562 return furthestBlock;
1563 if (isSpecialNode(record->element()))
1564 furthestBlock = record;
1566 ASSERT_NOT_REACHED();
1570 // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#parsing-main-inbody
1571 void HTMLTreeBuilder::callTheAdoptionAgency(AtomicHTMLToken& token)
1573 // The adoption agency algorithm is N^2. We limit the number of iterations
1574 // to stop from hanging the whole browser. This limit is copied from the
1575 // legacy tree builder and might need to be tweaked in the future.
1576 static const int adoptionAgencyIterationLimit = 10;
1578 for (int i = 0; i < adoptionAgencyIterationLimit; ++i) {
1580 Element* formattingElement = m_tree.activeFormattingElements()->closestElementInScopeWithName(token.name());
1581 if (!formattingElement || ((m_tree.openElements()->contains(formattingElement)) && !m_tree.openElements()->inScope(formattingElement))) {
1583 notImplemented(); // Check the stack of open elements for a more specific parse error.
1586 HTMLElementStack::ElementRecord* formattingElementRecord = m_tree.openElements()->find(formattingElement);
1587 if (!formattingElementRecord) {
1589 m_tree.activeFormattingElements()->remove(formattingElement);
1592 if (formattingElement != m_tree.currentElement())
1595 HTMLElementStack::ElementRecord* furthestBlock = furthestBlockForFormattingElement(formattingElement);
1597 if (!furthestBlock) {
1598 m_tree.openElements()->popUntilPopped(formattingElement);
1599 m_tree.activeFormattingElements()->remove(formattingElement);
1603 ASSERT(furthestBlock->isAbove(formattingElementRecord));
1604 Element* commonAncestor = formattingElementRecord->next()->element();
1606 HTMLFormattingElementList::Bookmark bookmark = m_tree.activeFormattingElements()->bookmarkFor(formattingElement);
1608 HTMLElementStack::ElementRecord* node = furthestBlock;
1609 HTMLElementStack::ElementRecord* nextNode = node->next();
1610 HTMLElementStack::ElementRecord* lastNode = furthestBlock;
1611 for (int i = 0; i < adoptionAgencyIterationLimit; ++i) {
1615 nextNode = node->next(); // Save node->next() for the next iteration in case node is deleted in 6.2.
1617 if (!m_tree.activeFormattingElements()->contains(node->element())) {
1618 m_tree.openElements()->remove(node->element());
1623 if (node == formattingElementRecord)
1626 RefPtr<Element> newElement = m_tree.createHTMLElementFromElementRecord(node);
1627 HTMLFormattingElementList::Entry* nodeEntry = m_tree.activeFormattingElements()->find(node->element());
1628 nodeEntry->replaceElement(newElement.get());
1629 node->replaceElement(newElement.release());
1630 // 6.4 -- Intentionally out of order to handle the case where node
1631 // was replaced in 6.5.
1632 // http://www.w3.org/Bugs/Public/show_bug.cgi?id=10096
1633 if (lastNode == furthestBlock)
1634 bookmark.moveToAfter(nodeEntry);
1636 if (Element* parent = lastNode->element()->parentElement())
1637 parent->parserRemoveChild(lastNode->element());
1638 node->element()->parserAddChild(lastNode->element());
1639 if (lastNode->element()->parentElement()->attached() && !lastNode->element()->attached())
1640 lastNode->element()->lazyAttach();
1645 const AtomicString& commonAncestorTag = commonAncestor->localName();
1646 if (Element* parent = lastNode->element()->parentElement())
1647 parent->parserRemoveChild(lastNode->element());
1648 // FIXME: If this moves to HTMLConstructionSite, this check should use
1649 // causesFosterParenting(tagName) instead.
1650 if (commonAncestorTag == tableTag
1651 || commonAncestorTag == trTag
1652 || isTableBodyContextTag(commonAncestorTag))
1653 m_tree.fosterParent(lastNode->element());
1655 commonAncestor->parserAddChild(lastNode->element());
1656 if (lastNode->element()->parentElement()->attached() && !lastNode->element()->attached())
1657 lastNode->element()->lazyAttach();
1660 RefPtr<Element> newElement = m_tree.createHTMLElementFromElementRecord(formattingElementRecord);
1662 newElement->takeAllChildrenFrom(furthestBlock->element());
1664 Element* furthestBlockElement = furthestBlock->element();
1665 // FIXME: All this creation / parserAddChild / attach business should
1666 // be in HTMLConstructionSite. My guess is that steps 8--12
1667 // should all be in some HTMLConstructionSite function.
1668 furthestBlockElement->parserAddChild(newElement);
1669 if (furthestBlockElement->attached() && !newElement->attached()) {
1670 // Notice that newElement might already be attached if, for example, one of the reparented
1671 // children is a style element, which attaches itself automatically.
1672 newElement->attach();
1675 m_tree.activeFormattingElements()->swapTo(formattingElement, newElement.get(), bookmark);
1677 m_tree.openElements()->remove(formattingElement);
1678 m_tree.openElements()->insertAbove(newElement, furthestBlock);
1682 void HTMLTreeBuilder::setSecondaryInsertionMode(InsertionMode mode)
1684 ASSERT(mode != InForeignContentMode);
1685 m_secondaryInsertionMode = mode;
1688 void HTMLTreeBuilder::setInsertionModeAndEnd(InsertionMode newInsertionMode, bool foreign)
1690 setInsertionMode(newInsertionMode);
1692 setSecondaryInsertionMode(m_insertionMode);
1693 setInsertionMode(InForeignContentMode);
1697 void HTMLTreeBuilder::resetInsertionModeAppropriately()
1699 // http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#reset-the-insertion-mode-appropriately
1701 bool foreign = false;
1702 HTMLElementStack::ElementRecord* nodeRecord = m_tree.openElements()->topRecord();
1704 Element* node = nodeRecord->element();
1705 if (node == m_tree.openElements()->bottom()) {
1706 ASSERT(isParsingFragment());
1708 node = m_fragmentContext.contextElement();
1710 if (node->hasTagName(selectTag)) {
1711 ASSERT(isParsingFragment());
1712 return setInsertionModeAndEnd(InSelectMode, foreign);
1714 if (node->hasTagName(tdTag) || node->hasTagName(thTag))
1715 return setInsertionModeAndEnd(InCellMode, foreign);
1716 if (node->hasTagName(trTag))
1717 return setInsertionModeAndEnd(InRowMode, foreign);
1718 if (isTableBodyContextTag(node->localName()))
1719 return setInsertionModeAndEnd(InTableBodyMode, foreign);
1720 if (node->hasTagName(captionTag))
1721 return setInsertionModeAndEnd(InCaptionMode, foreign);
1722 if (node->hasTagName(colgroupTag)) {
1723 ASSERT(isParsingFragment());
1724 return setInsertionModeAndEnd(InColumnGroupMode, foreign);
1726 if (node->hasTagName(tableTag))
1727 return setInsertionModeAndEnd(InTableMode, foreign);
1728 if (node->hasTagName(headTag)) {
1729 ASSERT(isParsingFragment());
1730 return setInsertionModeAndEnd(InBodyMode, foreign);
1732 if (node->hasTagName(bodyTag))
1733 return setInsertionModeAndEnd(InBodyMode, foreign);
1734 if (node->hasTagName(framesetTag)) {
1735 ASSERT(isParsingFragment());
1736 return setInsertionModeAndEnd(InFramesetMode, foreign);
1738 if (node->hasTagName(htmlTag)) {
1739 ASSERT(isParsingFragment());
1740 return setInsertionModeAndEnd(BeforeHeadMode, foreign);
1742 if (node->namespaceURI() == SVGNames::svgNamespaceURI
1743 || node->namespaceURI() == MathMLNames::mathmlNamespaceURI)
1746 ASSERT(isParsingFragment());
1747 return setInsertionModeAndEnd(InBodyMode, foreign);
1749 nodeRecord = nodeRecord->next();
1753 void HTMLTreeBuilder::processEndTagForInTableBody(AtomicHTMLToken& token)
1755 ASSERT(token.type() == HTMLToken::EndTag);
1756 if (isTableBodyContextTag(token.name())) {
1757 if (!m_tree.openElements()->inTableScope(token.name())) {
1761 m_tree.openElements()->popUntilTableBodyScopeMarker();
1762 m_tree.openElements()->pop();
1763 setInsertionMode(InTableMode);
1766 if (token.name() == tableTag) {
1767 // FIXME: This is slow.
1768 if (!m_tree.openElements()->inTableScope(tbodyTag.localName()) && !m_tree.openElements()->inTableScope(theadTag.localName()) && !m_tree.openElements()->inTableScope(tfootTag.localName())) {
1769 ASSERT(isParsingFragment());
1773 m_tree.openElements()->popUntilTableBodyScopeMarker();
1774 ASSERT(isTableBodyContextTag(m_tree.currentElement()->localName()));
1775 processFakeEndTag(m_tree.currentElement()->tagQName());
1776 processEndTag(token);
1779 if (token.name() == bodyTag
1780 || isCaptionColOrColgroupTag(token.name())
1781 || token.name() == htmlTag
1782 || isTableCellContextTag(token.name())
1783 || token.name() == trTag) {
1787 processEndTagForInTable(token);
1790 void HTMLTreeBuilder::processEndTagForInRow(AtomicHTMLToken& token)
1792 ASSERT(token.type() == HTMLToken::EndTag);
1793 if (token.name() == trTag) {
1794 processTrEndTagForInRow();
1797 if (token.name() == tableTag) {
1798 if (!processTrEndTagForInRow()) {
1799 ASSERT(isParsingFragment());
1802 ASSERT(insertionMode() == InTableBodyMode);
1803 processEndTag(token);
1806 if (isTableBodyContextTag(token.name())) {
1807 if (!m_tree.openElements()->inTableScope(token.name())) {
1811 processFakeEndTag(trTag);
1812 ASSERT(insertionMode() == InTableBodyMode);
1813 processEndTag(token);
1816 if (token.name() == bodyTag
1817 || isCaptionColOrColgroupTag(token.name())
1818 || token.name() == htmlTag
1819 || isTableCellContextTag(token.name())) {
1823 processEndTagForInTable(token);
1826 void HTMLTreeBuilder::processEndTagForInCell(AtomicHTMLToken& token)
1828 ASSERT(token.type() == HTMLToken::EndTag);
1829 if (isTableCellContextTag(token.name())) {
1830 if (!m_tree.openElements()->inTableScope(token.name())) {
1834 m_tree.generateImpliedEndTags();
1835 if (!m_tree.currentElement()->hasLocalName(token.name()))
1837 m_tree.openElements()->popUntilPopped(token.name());
1838 m_tree.activeFormattingElements()->clearToLastMarker();
1839 setInsertionMode(InRowMode);
1842 if (token.name() == bodyTag
1843 || isCaptionColOrColgroupTag(token.name())
1844 || token.name() == htmlTag) {
1848 if (token.name() == tableTag
1849 || token.name() == trTag
1850 || isTableBodyContextTag(token.name())) {
1851 if (!m_tree.openElements()->inTableScope(token.name())) {
1852 ASSERT(isTableBodyContextTag(token.name()) || isParsingFragment());
1857 processEndTag(token);
1860 processEndTagForInBody(token);
1863 void HTMLTreeBuilder::processEndTagForInBody(AtomicHTMLToken& token)
1865 ASSERT(token.type() == HTMLToken::EndTag);
1866 if (token.name() == bodyTag) {
1867 processBodyEndTagForInBody(token);
1870 if (token.name() == htmlTag) {
1871 AtomicHTMLToken endBody(HTMLToken::EndTag, bodyTag.localName());
1872 if (processBodyEndTagForInBody(endBody))
1873 processEndTag(token);
1876 if (token.name() == addressTag
1877 || token.name() == articleTag
1878 || token.name() == asideTag
1879 || token.name() == blockquoteTag
1880 || token.name() == buttonTag
1881 || token.name() == centerTag
1882 || token.name() == detailsTag
1883 || token.name() == dirTag
1884 || token.name() == divTag
1885 || token.name() == dlTag
1886 || token.name() == fieldsetTag
1887 || token.name() == figcaptionTag
1888 || token.name() == figureTag
1889 || token.name() == footerTag
1890 || token.name() == headerTag
1891 || token.name() == hgroupTag
1892 || token.name() == listingTag
1893 || token.name() == menuTag
1894 || token.name() == navTag
1895 || token.name() == olTag
1896 || token.name() == preTag
1897 || token.name() == sectionTag
1898 || token.name() == summaryTag
1899 || token.name() == ulTag) {
1900 if (!m_tree.openElements()->inScope(token.name())) {
1904 m_tree.generateImpliedEndTags();
1905 if (!m_tree.currentElement()->hasLocalName(token.name()))
1907 m_tree.openElements()->popUntilPopped(token.name());
1910 if (token.name() == formTag) {
1911 RefPtr<Element> node = m_tree.takeForm();
1912 if (!node || !m_tree.openElements()->inScope(node.get())) {
1916 m_tree.generateImpliedEndTags();
1917 if (m_tree.currentElement() != node.get())
1919 m_tree.openElements()->remove(node.get());
1921 if (token.name() == pTag) {
1922 if (!m_tree.openElements()->inButtonScope(token.name())) {
1924 processFakeStartTag(pTag);
1925 ASSERT(m_tree.openElements()->inScope(token.name()));
1926 processEndTag(token);
1929 m_tree.generateImpliedEndTagsWithExclusion(token.name());
1930 if (!m_tree.currentElement()->hasLocalName(token.name()))
1932 m_tree.openElements()->popUntilPopped(token.name());
1935 if (token.name() == liTag) {
1936 if (!m_tree.openElements()->inListItemScope(token.name())) {
1940 m_tree.generateImpliedEndTagsWithExclusion(token.name());
1941 if (!m_tree.currentElement()->hasLocalName(token.name()))
1943 m_tree.openElements()->popUntilPopped(token.name());
1946 if (token.name() == ddTag
1947 || token.name() == dtTag) {
1948 if (!m_tree.openElements()->inScope(token.name())) {
1952 m_tree.generateImpliedEndTagsWithExclusion(token.name());
1953 if (!m_tree.currentElement()->hasLocalName(token.name()))
1955 m_tree.openElements()->popUntilPopped(token.name());
1958 if (isNumberedHeaderTag(token.name())) {
1959 if (!m_tree.openElements()->hasNumberedHeaderElementInScope()) {
1963 m_tree.generateImpliedEndTags();
1964 if (!m_tree.currentElement()->hasLocalName(token.name()))
1966 m_tree.openElements()->popUntilNumberedHeaderElementPopped();
1969 if (isFormattingTag(token.name())) {
1970 callTheAdoptionAgency(token);
1973 if (token.name() == appletTag
1974 || token.name() == marqueeTag
1975 || token.name() == objectTag) {
1976 if (!m_tree.openElements()->inScope(token.name())) {
1980 m_tree.generateImpliedEndTags();
1981 if (!m_tree.currentElement()->hasLocalName(token.name()))
1983 m_tree.openElements()->popUntilPopped(token.name());
1984 m_tree.activeFormattingElements()->clearToLastMarker();
1987 if (token.name() == brTag) {
1989 processFakeStartTag(brTag);
1992 processAnyOtherEndTagForInBody(token);
1995 bool HTMLTreeBuilder::processCaptionEndTagForInCaption()
1997 if (!m_tree.openElements()->inTableScope(captionTag.localName())) {
1998 ASSERT(isParsingFragment());
1999 // FIXME: parse error
2002 m_tree.generateImpliedEndTags();
2003 // FIXME: parse error if (!m_tree.currentElement()->hasTagName(captionTag))
2004 m_tree.openElements()->popUntilPopped(captionTag.localName());
2005 m_tree.activeFormattingElements()->clearToLastMarker();
2006 setInsertionMode(InTableMode);
2010 bool HTMLTreeBuilder::processTrEndTagForInRow()
2012 if (!m_tree.openElements()->inTableScope(trTag.localName())) {
2013 ASSERT(isParsingFragment());
2014 // FIXME: parse error
2017 m_tree.openElements()->popUntilTableRowScopeMarker();
2018 ASSERT(m_tree.currentElement()->hasTagName(trTag));
2019 m_tree.openElements()->pop();
2020 setInsertionMode(InTableBodyMode);
2024 bool HTMLTreeBuilder::processTableEndTagForInTable()
2026 if (!m_tree.openElements()->inTableScope(tableTag)) {
2027 ASSERT(isParsingFragment());
2028 // FIXME: parse error.
2031 m_tree.openElements()->popUntilPopped(tableTag.localName());
2032 resetInsertionModeAppropriately();
2036 void HTMLTreeBuilder::processEndTagForInTable(AtomicHTMLToken& token)
2038 ASSERT(token.type() == HTMLToken::EndTag);
2039 if (token.name() == tableTag) {
2040 processTableEndTagForInTable();
2043 if (token.name() == bodyTag
2044 || isCaptionColOrColgroupTag(token.name())
2045 || token.name() == htmlTag
2046 || isTableBodyContextTag(token.name())
2047 || isTableCellContextTag(token.name())
2048 || token.name() == trTag) {
2052 // Is this redirection necessary here?
2053 HTMLConstructionSite::RedirectToFosterParentGuard redirecter(m_tree);
2054 processEndTagForInBody(token);
2057 void HTMLTreeBuilder::processEndTag(AtomicHTMLToken& token)
2059 ASSERT(token.type() == HTMLToken::EndTag);
2060 switch (insertionMode()) {
2062 ASSERT(insertionMode() == InitialMode);
2063 defaultForInitial();
2065 case BeforeHTMLMode:
2066 ASSERT(insertionMode() == BeforeHTMLMode);
2067 if (token.name() != headTag && token.name() != bodyTag && token.name() != htmlTag && token.name() != brTag) {
2071 defaultForBeforeHTML();
2073 case BeforeHeadMode:
2074 ASSERT(insertionMode() == BeforeHeadMode);
2075 if (token.name() != headTag && token.name() != bodyTag && token.name() != htmlTag && token.name() != brTag) {
2079 defaultForBeforeHead();
2082 ASSERT(insertionMode() == InHeadMode);
2083 if (token.name() == headTag) {
2084 m_tree.openElements()->popHTMLHeadElement();
2085 setInsertionMode(AfterHeadMode);
2088 if (token.name() != bodyTag && token.name() != htmlTag && token.name() != brTag) {
2095 ASSERT(insertionMode() == AfterHeadMode);
2096 if (token.name() != bodyTag && token.name() != htmlTag && token.name() != brTag) {
2100 defaultForAfterHead();
2103 ASSERT(insertionMode() == InBodyMode);
2104 processEndTagForInBody(token);
2107 ASSERT(insertionMode() == InTableMode);
2108 processEndTagForInTable(token);
2111 ASSERT(insertionMode() == InCaptionMode);
2112 if (token.name() == captionTag) {
2113 processCaptionEndTagForInCaption();
2116 if (token.name() == tableTag) {
2118 if (!processCaptionEndTagForInCaption()) {
2119 ASSERT(isParsingFragment());
2122 processEndTag(token);
2125 if (token.name() == bodyTag
2126 || token.name() == colTag
2127 || token.name() == colgroupTag
2128 || token.name() == htmlTag
2129 || isTableBodyContextTag(token.name())
2130 || isTableCellContextTag(token.name())
2131 || token.name() == trTag) {
2135 processEndTagForInBody(token);
2137 case InColumnGroupMode:
2138 ASSERT(insertionMode() == InColumnGroupMode);
2139 if (token.name() == colgroupTag) {
2140 processColgroupEndTagForInColumnGroup();
2143 if (token.name() == colTag) {
2147 if (!processColgroupEndTagForInColumnGroup()) {
2148 ASSERT(isParsingFragment());
2151 processEndTag(token);
2154 ASSERT(insertionMode() == InRowMode);
2155 processEndTagForInRow(token);
2158 ASSERT(insertionMode() == InCellMode);
2159 processEndTagForInCell(token);
2161 case InTableBodyMode:
2162 ASSERT(insertionMode() == InTableBodyMode);
2163 processEndTagForInTableBody(token);
2166 ASSERT(insertionMode() == AfterBodyMode);
2167 if (token.name() == htmlTag) {
2168 if (isParsingFragment()) {
2172 setInsertionMode(AfterAfterBodyMode);
2176 case AfterAfterBodyMode:
2177 ASSERT(insertionMode() == AfterBodyMode || insertionMode() == AfterAfterBodyMode);
2179 setInsertionMode(InBodyMode);
2180 processEndTag(token);
2182 case InHeadNoscriptMode:
2183 ASSERT(insertionMode() == InHeadNoscriptMode);
2184 if (token.name() == noscriptTag) {
2185 ASSERT(m_tree.currentElement()->hasTagName(noscriptTag));
2186 m_tree.openElements()->pop();
2187 ASSERT(m_tree.currentElement()->hasTagName(headTag));
2188 setInsertionMode(InHeadMode);
2191 if (token.name() != brTag) {
2195 defaultForInHeadNoscript();
2196 processToken(token);
2199 if (token.name() == scriptTag) {
2200 // Pause ourselves so that parsing stops until the script can be processed by the caller.
2202 ASSERT(m_tree.currentElement()->hasTagName(scriptTag));
2203 m_scriptToProcess = m_tree.currentElement();
2204 m_scriptToProcessStartLine = m_lastScriptElementStartLine + 1;
2205 m_tree.openElements()->pop();
2206 if (isParsingFragment() && m_fragmentContext.scriptingPermission() == FragmentScriptingNotAllowed)
2207 m_scriptToProcess->removeAllChildren();
2208 setInsertionMode(m_originalInsertionMode);
2210 // This token will not have been created by the tokenizer if a
2211 // self-closing script tag was encountered and pre-HTML5 parser
2212 // quirks are enabled. We must set the tokenizer's state to
2213 // DataState explicitly if the tokenizer didn't have a chance to.
2214 ASSERT(m_tokenizer->state() == HTMLTokenizer::DataState || m_usePreHTML5ParserQuirks);
2215 m_tokenizer->setState(HTMLTokenizer::DataState);
2218 m_tree.openElements()->pop();
2219 setInsertionMode(m_originalInsertionMode);
2221 case InFramesetMode:
2222 ASSERT(insertionMode() == InFramesetMode);
2223 if (token.name() == framesetTag) {
2224 if (m_tree.currentElement() == m_tree.openElements()->htmlElement()) {
2228 m_tree.openElements()->pop();
2229 if (!isParsingFragment() && !m_tree.currentElement()->hasTagName(framesetTag))
2230 setInsertionMode(AfterFramesetMode);
2234 case AfterFramesetMode:
2235 ASSERT(insertionMode() == AfterFramesetMode);
2236 if (token.name() == htmlTag) {
2237 setInsertionMode(AfterAfterFramesetMode);
2241 case AfterAfterFramesetMode:
2242 ASSERT(insertionMode() == AfterFramesetMode || insertionMode() == AfterAfterFramesetMode);
2245 case InSelectInTableMode:
2246 ASSERT(insertionMode() == InSelectInTableMode);
2247 if (token.name() == captionTag
2248 || token.name() == tableTag
2249 || isTableBodyContextTag(token.name())
2250 || token.name() == trTag
2251 || isTableCellContextTag(token.name())) {
2253 if (m_tree.openElements()->inTableScope(token.name())) {
2254 AtomicHTMLToken endSelect(HTMLToken::EndTag, selectTag.localName());
2255 processEndTag(endSelect);
2256 processEndTag(token);
2262 ASSERT(insertionMode() == InSelectMode || insertionMode() == InSelectInTableMode);
2263 if (token.name() == optgroupTag) {
2264 if (m_tree.currentElement()->hasTagName(optionTag) && m_tree.oneBelowTop()->hasTagName(optgroupTag))
2265 processFakeEndTag(optionTag);
2266 if (m_tree.currentElement()->hasTagName(optgroupTag)) {
2267 m_tree.openElements()->pop();
2273 if (token.name() == optionTag) {
2274 if (m_tree.currentElement()->hasTagName(optionTag)) {
2275 m_tree.openElements()->pop();
2281 if (token.name() == selectTag) {
2282 if (!m_tree.openElements()->inSelectScope(token.name())) {
2283 ASSERT(isParsingFragment());
2287 m_tree.openElements()->popUntilPopped(selectTag.localName());
2288 resetInsertionModeAppropriately();
2292 case InTableTextMode:
2293 defaultForInTableText();
2294 processEndTag(token);
2296 case InForeignContentMode:
2297 if (token.name() == SVGNames::scriptTag && m_tree.currentElement()->hasTagName(SVGNames::scriptTag)) {
2301 if (m_tree.currentElement()->namespaceURI() != xhtmlNamespaceURI) {
2302 // FIXME: This code just wants an Element* iterator, instead of an ElementRecord*
2303 HTMLElementStack::ElementRecord* nodeRecord = m_tree.openElements()->topRecord();
2304 if (!nodeRecord->element()->hasLocalName(token.name()))
2307 if (nodeRecord->element()->hasLocalName(token.name())) {
2308 m_tree.openElements()->popUntilPopped(nodeRecord->element());
2311 nodeRecord = nodeRecord->next();
2312 if (nodeRecord->element()->namespaceURI() == xhtmlNamespaceURI)
2316 // 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."
2317 processUsingSecondaryInsertionModeAndAdjustInsertionMode(token);
2322 class HTMLTreeBuilder::FakeInsertionMode : public Noncopyable {
2324 FakeInsertionMode(HTMLTreeBuilder* treeBuilder, InsertionMode mode)
2325 : m_treeBuilder(treeBuilder)
2326 , m_originalMode(treeBuilder->insertionMode())
2328 m_treeBuilder->setFakeInsertionMode(mode);
2331 ~FakeInsertionMode()
2333 if (m_treeBuilder->isFakeInsertionMode())
2334 m_treeBuilder->setInsertionMode(m_originalMode);
2338 HTMLTreeBuilder* m_treeBuilder;
2339 InsertionMode m_originalMode;
2342 // This handles both secondary insertion mode processing, as well as updating
2343 // the insertion mode. These are separate steps in the spec, but always occur
2344 // right after one another.
2345 void HTMLTreeBuilder::processUsingSecondaryInsertionModeAndAdjustInsertionMode(AtomicHTMLToken& token)
2347 ASSERT(token.type() == HTMLToken::StartTag || token.type() == HTMLToken::EndTag);
2349 FakeInsertionMode fakeMode(this, m_secondaryInsertionMode);
2350 processToken(token);
2352 if (insertionMode() == InForeignContentMode && m_tree.openElements()->hasOnlyHTMLElementsInScope())
2353 setInsertionMode(m_secondaryInsertionMode);
2356 void HTMLTreeBuilder::processComment(AtomicHTMLToken& token)
2358 ASSERT(token.type() == HTMLToken::Comment);
2359 if (m_insertionMode == InitialMode
2360 || m_insertionMode == BeforeHTMLMode
2361 || m_insertionMode == AfterAfterBodyMode
2362 || m_insertionMode == AfterAfterFramesetMode) {
2363 m_tree.insertCommentOnDocument(token);
2366 if (m_insertionMode == AfterBodyMode) {
2367 m_tree.insertCommentOnHTMLHtmlElement(token);
2370 if (m_insertionMode == InTableTextMode) {
2371 defaultForInTableText();
2372 processComment(token);
2375 m_tree.insertComment(token);
2378 void HTMLTreeBuilder::processCharacter(AtomicHTMLToken& token)
2380 ASSERT(token.type() == HTMLToken::Character);
2381 ExternalCharacterTokenBuffer buffer(token);
2382 processCharacterBuffer(buffer);
2385 void HTMLTreeBuilder::processCharacterBuffer(ExternalCharacterTokenBuffer& buffer)
2388 switch (insertionMode()) {
2390 ASSERT(insertionMode() == InitialMode);
2391 buffer.skipLeadingWhitespace();
2392 if (buffer.isEmpty())
2394 defaultForInitial();
2397 case BeforeHTMLMode: {
2398 ASSERT(insertionMode() == BeforeHTMLMode);
2399 buffer.skipLeadingWhitespace();
2400 if (buffer.isEmpty())
2402 defaultForBeforeHTML();
2405 case BeforeHeadMode: {
2406 ASSERT(insertionMode() == BeforeHeadMode);
2407 buffer.skipLeadingWhitespace();
2408 if (buffer.isEmpty())
2410 defaultForBeforeHead();
2414 ASSERT(insertionMode() == InHeadMode);
2415 String leadingWhitespace = buffer.takeLeadingWhitespace();
2416 if (!leadingWhitespace.isEmpty())
2417 m_tree.insertTextNode(leadingWhitespace);
2418 if (buffer.isEmpty())
2423 case AfterHeadMode: {
2424 ASSERT(insertionMode() == AfterHeadMode);
2425 String leadingWhitespace = buffer.takeLeadingWhitespace();
2426 if (!leadingWhitespace.isEmpty())
2427 m_tree.insertTextNode(leadingWhitespace);
2428 if (buffer.isEmpty())
2430 defaultForAfterHead();
2436 ASSERT(insertionMode() == InBodyMode || insertionMode() == InCaptionMode || insertionMode() == InCellMode);
2437 m_tree.reconstructTheActiveFormattingElements();
2438 String characters = buffer.takeRemaining();
2439 m_tree.insertTextNode(characters);
2440 if (m_framesetOk && !isAllWhitespaceOrReplacementCharacters(characters))
2441 m_framesetOk = false;
2445 case InTableBodyMode:
2447 ASSERT(insertionMode() == InTableMode || insertionMode() == InTableBodyMode || insertionMode() == InRowMode);
2448 ASSERT(m_pendingTableCharacters.isEmpty());
2449 m_originalInsertionMode = m_insertionMode;
2450 setInsertionMode(InTableTextMode);
2453 case InTableTextMode: {
2454 buffer.giveRemainingTo(m_pendingTableCharacters);
2457 case InColumnGroupMode: {
2458 ASSERT(insertionMode() == InColumnGroupMode);
2459 String leadingWhitespace = buffer.takeLeadingWhitespace();
2460 if (!leadingWhitespace.isEmpty())
2461 m_tree.insertTextNode(leadingWhitespace);
2462 if (buffer.isEmpty())
2464 if (!processColgroupEndTagForInColumnGroup()) {
2465 ASSERT(isParsingFragment());
2466 // The spec tells us to drop these characters on the floor.
2467 buffer.takeLeadingNonWhitespace();
2468 if (buffer.isEmpty())
2471 goto ReprocessBuffer;
2474 case AfterAfterBodyMode: {
2475 ASSERT(insertionMode() == AfterBodyMode || insertionMode() == AfterAfterBodyMode);
2476 // FIXME: parse error
2477 setInsertionMode(InBodyMode);
2478 goto ReprocessBuffer;
2482 ASSERT(insertionMode() == TextMode);
2483 m_tree.insertTextNode(buffer.takeRemaining());
2486 case InHeadNoscriptMode: {
2487 ASSERT(insertionMode() == InHeadNoscriptMode);
2488 String leadingWhitespace = buffer.takeLeadingWhitespace();
2489 if (!leadingWhitespace.isEmpty())
2490 m_tree.insertTextNode(leadingWhitespace);
2491 if (buffer.isEmpty())
2493 defaultForInHeadNoscript();
2494 goto ReprocessBuffer;
2497 case InFramesetMode:
2498 case AfterFramesetMode: {
2499 ASSERT(insertionMode() == InFramesetMode || insertionMode() == AfterFramesetMode || insertionMode() == AfterAfterFramesetMode);
2500 String leadingWhitespace = buffer.takeRemainingWhitespace();
2501 if (!leadingWhitespace.isEmpty())
2502 m_tree.insertTextNode(leadingWhitespace);
2503 // FIXME: We should generate a parse error if we skipped over any
2504 // non-whitespace characters.
2507 case InSelectInTableMode:
2508 case InSelectMode: {
2509 ASSERT(insertionMode() == InSelectMode || insertionMode() == InSelectInTableMode);
2510 m_tree.insertTextNode(buffer.takeRemaining());
2513 case InForeignContentMode: {
2514 ASSERT(insertionMode() == InForeignContentMode);
2515 String characters = buffer.takeRemaining();
2516 m_tree.insertTextNode(characters);
2517 if (m_framesetOk && !isAllWhitespace(characters))
2518 m_framesetOk = false;
2521 case AfterAfterFramesetMode: {
2522 String leadingWhitespace = buffer.takeRemainingWhitespace();
2523 if (!leadingWhitespace.isEmpty()) {
2524 m_tree.reconstructTheActiveFormattingElements();
2525 m_tree.insertTextNode(leadingWhitespace);
2527 // FIXME: We should generate a parse error if we skipped over any
2528 // non-whitespace characters.
2534 void HTMLTreeBuilder::processEndOfFile(AtomicHTMLToken& token)
2536 ASSERT(token.type() == HTMLToken::EndOfFile);
2537 switch (insertionMode()) {
2539 ASSERT(insertionMode() == InitialMode);
2540 defaultForInitial();
2542 case BeforeHTMLMode:
2543 ASSERT(insertionMode() == BeforeHTMLMode);
2544 defaultForBeforeHTML();
2546 case BeforeHeadMode:
2547 ASSERT(insertionMode() == BeforeHeadMode);
2548 defaultForBeforeHead();
2551 ASSERT(insertionMode() == InHeadMode);
2555 ASSERT(insertionMode() == AfterHeadMode);
2556 defaultForAfterHead();
2562 ASSERT(insertionMode() == InBodyMode || insertionMode() == InCellMode || insertionMode() == InCaptionMode || insertionMode() == InRowMode);
2563 notImplemented(); // Emit parse error based on what elements are still open.
2566 case AfterAfterBodyMode:
2567 ASSERT(insertionMode() == AfterBodyMode || insertionMode() == AfterAfterBodyMode);
2569 case InHeadNoscriptMode:
2570 ASSERT(insertionMode() == InHeadNoscriptMode);
2571 defaultForInHeadNoscript();
2572 processEndOfFile(token);
2574 case AfterFramesetMode:
2575 case AfterAfterFramesetMode:
2576 ASSERT(insertionMode() == AfterFramesetMode || insertionMode() == AfterAfterFramesetMode);
2578 case InFramesetMode:
2580 case InTableBodyMode:
2581 case InSelectInTableMode:
2583 ASSERT(insertionMode() == InSelectMode || insertionMode() == InSelectInTableMode || insertionMode() == InTableMode || insertionMode() == InFramesetMode || insertionMode() == InTableBodyMode);
2584 if (m_tree.currentElement() != m_tree.openElements()->htmlElement())
2587 case InColumnGroupMode:
2588 if (m_tree.currentElement() == m_tree.openElements()->htmlElement()) {
2589 ASSERT(isParsingFragment());
2592 if (!processColgroupEndTagForInColumnGroup()) {
2593 ASSERT(isParsingFragment());
2596 processEndOfFile(token);
2598 case InForeignContentMode:
2600 m_tree.openElements()->popUntilForeignContentScopeMarker();
2601 // FIXME: The spec adds the following condition before setting the
2602 // insertion mode. However, this condition causes an infinite loop.
2603 // See http://www.w3.org/Bugs/Public/show_bug.cgi?id=10621
2604 // if (insertionMode() == InForeignContentMode && m_tree.openElements()->hasOnlyHTMLElementsInScope())
2605 setInsertionMode(m_secondaryInsertionMode);
2606 processEndOfFile(token);
2608 case InTableTextMode:
2609 defaultForInTableText();
2610 processEndOfFile(token);
2614 if (m_tree.currentElement()->hasTagName(scriptTag))
2615 notImplemented(); // mark the script element as "already started".
2616 m_tree.openElements()->pop();
2617 setInsertionMode(m_originalInsertionMode);
2618 processEndOfFile(token);
2621 ASSERT(m_tree.openElements()->top());
2622 m_tree.openElements()->popAll();
2625 void HTMLTreeBuilder::defaultForInitial()
2628 if (!m_fragmentContext.fragment())
2629 m_document->setCompatibilityMode(Document::QuirksMode);
2630 // FIXME: parse error
2631 setInsertionMode(BeforeHTMLMode);
2634 void HTMLTreeBuilder::defaultForBeforeHTML()
2636 AtomicHTMLToken startHTML(HTMLToken::StartTag, htmlTag.localName());
2637 m_tree.insertHTMLHtmlStartTagBeforeHTML(startHTML);
2638 setInsertionMode(BeforeHeadMode);
2641 void HTMLTreeBuilder::defaultForBeforeHead()
2643 AtomicHTMLToken startHead(HTMLToken::StartTag, headTag.localName());
2644 processStartTag(startHead);
2647 void HTMLTreeBuilder::defaultForInHead()
2649 AtomicHTMLToken endHead(HTMLToken::EndTag, headTag.localName());
2650 processEndTag(endHead);
2653 void HTMLTreeBuilder::defaultForInHeadNoscript()
2655 AtomicHTMLToken endNoscript(HTMLToken::EndTag, noscriptTag.localName());
2656 processEndTag(endNoscript);
2659 void HTMLTreeBuilder::defaultForAfterHead()
2661 AtomicHTMLToken startBody(HTMLToken::StartTag, bodyTag.localName());
2662 processStartTag(startBody);
2663 m_framesetOk = true;
2666 void HTMLTreeBuilder::defaultForInTableText()
2668 String characters = String::adopt(m_pendingTableCharacters);
2669 if (!isAllWhitespace(characters)) {
2670 // FIXME: parse error
2671 HTMLConstructionSite::RedirectToFosterParentGuard redirecter(m_tree);
2672 m_tree.reconstructTheActiveFormattingElements();
2673 m_tree.insertTextNode(characters);
2674 m_framesetOk = false;
2675 setInsertionMode(m_originalInsertionMode);
2678 m_tree.insertTextNode(characters);
2679 setInsertionMode(m_originalInsertionMode);
2682 bool HTMLTreeBuilder::processStartTagForInHead(AtomicHTMLToken& token)
2684 ASSERT(token.type() == HTMLToken::StartTag);
2685 if (token.name() == htmlTag) {
2686 m_tree.insertHTMLHtmlStartTagInBody(token);
2689 if (token.name() == baseTag
2690 || token.name() == basefontTag
2691 || token.name() == bgsoundTag
2692 || token.name() == commandTag
2693 || token.name() == linkTag
2694 || token.name() == metaTag) {
2695 m_tree.insertSelfClosingHTMLElement(token);
2696 // Note: The custom processing for the <meta> tag is done in HTMLMetaElement::process().
2699 if (token.name() == titleTag) {
2700 processGenericRCDATAStartTag(token);
2703 if (token.name() == noscriptTag) {
2704 if (scriptEnabled(m_document->frame())) {
2705 processGenericRawTextStartTag(token);
2708 m_tree.insertHTMLElement(token);
2709 setInsertionMode(InHeadNoscriptMode);
2712 if (token.name() == noframesTag || token.name() == styleTag) {
2713 processGenericRawTextStartTag(token);
2716 if (token.name() == scriptTag) {
2717 processScriptStartTag(token);
2718 if (m_usePreHTML5ParserQuirks && token.selfClosing())
2719 processFakeEndTag(scriptTag);
2722 if (token.name() == headTag) {
2729 void HTMLTreeBuilder::processGenericRCDATAStartTag(AtomicHTMLToken& token)
2731 ASSERT(token.type() == HTMLToken::StartTag);
2732 m_tree.insertHTMLElement(token);
2733 m_tokenizer->setState(HTMLTokenizer::RCDATAState);
2734 m_originalInsertionMode = m_insertionMode;
2735 setInsertionMode(TextMode);
2738 void HTMLTreeBuilder::processGenericRawTextStartTag(AtomicHTMLToken& token)
2740 ASSERT(token.type() == HTMLToken::StartTag);
2741 m_tree.insertHTMLElement(token);
2742 m_tokenizer->setState(HTMLTokenizer::RAWTEXTState);
2743 m_originalInsertionMode = m_insertionMode;
2744 setInsertionMode(TextMode);
2747 void HTMLTreeBuilder::processScriptStartTag(AtomicHTMLToken& token)
2749 ASSERT(token.type() == HTMLToken::StartTag);
2750 m_tree.insertScriptElement(token);
2751 m_tokenizer->setState(HTMLTokenizer::ScriptDataState);
2752 m_originalInsertionMode = m_insertionMode;
2753 m_lastScriptElementStartLine = m_tokenizer->lineNumber();
2754 setInsertionMode(TextMode);
2757 void HTMLTreeBuilder::finished()
2760 if (isParsingFragment()) {
2761 m_fragmentContext.finished();
2765 // Warning, this may detach the parser. Do not do anything else after this.
2766 m_document->finishedParsing();
2769 bool HTMLTreeBuilder::scriptEnabled(Frame* frame)
2773 return frame->script()->canExecuteScripts(NotAboutToExecuteScript);
2776 bool HTMLTreeBuilder::pluginsEnabled(Frame* frame)
2780 return frame->loader()->subframeLoader()->allowPlugins(NotAboutToInstantiatePlugin);