2 * Copyright (C) 2010 Google, Inc. All Rights Reserved.
3 * Copyright (C) 2011, 2014 Apple Inc. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. ``AS IS'' AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE INC. OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 #include "core/html/parser/HTMLTreeBuilder.h"
30 #include "bindings/core/v8/ExceptionStatePlaceholder.h"
31 #include "core/HTMLNames.h"
32 #include "core/MathMLNames.h"
33 #include "core/SVGNames.h"
34 #include "core/XLinkNames.h"
35 #include "core/XMLNSNames.h"
36 #include "core/XMLNames.h"
37 #include "core/dom/DocumentFragment.h"
38 #include "core/dom/ElementTraversal.h"
39 #include "core/html/HTMLDocument.h"
40 #include "core/html/HTMLFormElement.h"
41 #include "core/html/parser/AtomicHTMLToken.h"
42 #include "core/html/parser/HTMLDocumentParser.h"
43 #include "core/html/parser/HTMLParserIdioms.h"
44 #include "core/html/parser/HTMLStackItem.h"
45 #include "core/html/parser/HTMLToken.h"
46 #include "core/html/parser/HTMLTokenizer.h"
47 #include "platform/NotImplemented.h"
48 #include "platform/text/PlatformLocale.h"
49 #include "wtf/MainThread.h"
50 #include "wtf/unicode/CharacterNames.h"
54 using namespace HTMLNames;
58 inline bool isHTMLSpaceOrReplacementCharacter(UChar character)
60 return isHTMLSpace<UChar>(character) || character == replacementCharacter;
65 static TextPosition uninitializedPositionValue1()
67 return TextPosition(OrdinalNumber::fromOneBasedInt(-1), OrdinalNumber::first());
70 static inline bool isAllWhitespace(const String& string)
72 return string.isAllSpecialCharacters<isHTMLSpace<UChar> >();
75 static inline bool isAllWhitespaceOrReplacementCharacters(const String& string)
77 return string.isAllSpecialCharacters<isHTMLSpaceOrReplacementCharacter>();
80 static bool isNumberedHeaderTag(const AtomicString& tagName)
82 return tagName == h1Tag
90 static bool isCaptionColOrColgroupTag(const AtomicString& tagName)
92 return tagName == captionTag
94 || tagName == colgroupTag;
97 static bool isTableCellContextTag(const AtomicString& tagName)
99 return tagName == thTag || tagName == tdTag;
102 static bool isTableBodyContextTag(const AtomicString& tagName)
104 return tagName == tbodyTag
105 || tagName == tfootTag
106 || tagName == theadTag;
109 static bool isNonAnchorNonNobrFormattingTag(const AtomicString& tagName)
111 return tagName == bTag
113 || tagName == codeTag
115 || tagName == fontTag
118 || tagName == smallTag
119 || tagName == strikeTag
120 || tagName == strongTag
125 static bool isNonAnchorFormattingTag(const AtomicString& tagName)
127 return tagName == nobrTag
128 || isNonAnchorNonNobrFormattingTag(tagName);
131 // http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#formatting
132 static bool isFormattingTag(const AtomicString& tagName)
134 return tagName == aTag || isNonAnchorFormattingTag(tagName);
137 static HTMLFormElement* closestFormAncestor(Element& element)
139 ASSERT(isMainThread());
140 return Traversal<HTMLFormElement>::firstAncestorOrSelf(element);
143 class HTMLTreeBuilder::CharacterTokenBuffer {
144 WTF_MAKE_NONCOPYABLE(CharacterTokenBuffer);
146 explicit CharacterTokenBuffer(AtomicHTMLToken* token)
147 : m_characters(token->characters().impl())
149 , m_end(token->characters().length())
154 explicit CharacterTokenBuffer(const String& characters)
155 : m_characters(characters.impl())
157 , m_end(characters.length())
162 ~CharacterTokenBuffer()
167 bool isEmpty() const { return m_current == m_end; }
169 void skipAtMostOneLeadingNewline()
172 if ((*m_characters)[m_current] == '\n')
176 void skipLeadingWhitespace()
178 skipLeading<isHTMLSpace<UChar> >();
181 String takeLeadingWhitespace()
183 return takeLeading<isHTMLSpace<UChar> >();
186 void skipLeadingNonWhitespace()
188 skipLeading<isNotHTMLSpace<UChar> >();
191 String takeRemaining()
194 unsigned start = m_current;
196 // Notice that substring is smart enough to return *this when start == 0.
197 return String(m_characters->substring(start, m_end - start));
200 void giveRemainingTo(StringBuilder& recipient)
202 if (m_characters->is8Bit())
203 recipient.append(m_characters->characters8() + m_current, m_end - m_current);
205 recipient.append(m_characters->characters16() + m_current, m_end - m_current);
209 String takeRemainingWhitespace()
212 const unsigned start = m_current;
213 m_current = m_end; // One way or another, we're taking everything!
216 for (unsigned i = start; i < m_end; ++i) {
217 if (isHTMLSpace<UChar>((*m_characters)[i]))
220 // Returning the null string when there aren't any whitespace
221 // characters is slightly cleaner semantically because we don't want
222 // to insert a text node (as opposed to inserting an empty text node).
225 if (length == start - m_end) // It's all whitespace.
226 return String(m_characters->substring(start, start - m_end));
228 StringBuilder result;
229 result.reserveCapacity(length);
230 for (unsigned i = start; i < m_end; ++i) {
231 UChar c = (*m_characters)[i];
232 if (isHTMLSpace<UChar>(c))
236 return result.toString();
240 template<bool characterPredicate(UChar)>
244 while (characterPredicate((*m_characters)[m_current])) {
245 if (++m_current == m_end)
250 template<bool characterPredicate(UChar)>
254 const unsigned start = m_current;
255 skipLeading<characterPredicate>();
256 if (start == m_current)
258 return String(m_characters->substring(start, m_current - start));
261 RefPtr<StringImpl> m_characters;
266 HTMLTreeBuilder::HTMLTreeBuilder(HTMLDocumentParser* parser, HTMLDocument* document, ParserContentPolicy parserContentPolicy, bool, const HTMLParserOptions& options)
271 , m_tree(document, parserContentPolicy)
272 , m_insertionMode(InitialMode)
273 , m_originalInsertionMode(InitialMode)
274 , m_shouldSkipLeadingNewline(false)
276 , m_scriptToProcessStartPosition(uninitializedPositionValue1())
281 // FIXME: Member variables should be grouped into self-initializing structs to
282 // minimize code duplication between these constructors.
283 HTMLTreeBuilder::HTMLTreeBuilder(HTMLDocumentParser* parser, DocumentFragment* fragment, Element* contextElement, ParserContentPolicy parserContentPolicy, const HTMLParserOptions& options)
288 , m_fragmentContext(fragment, contextElement)
289 , m_tree(fragment, parserContentPolicy)
290 , m_insertionMode(InitialMode)
291 , m_originalInsertionMode(InitialMode)
292 , m_shouldSkipLeadingNewline(false)
294 , m_scriptToProcessStartPosition(uninitializedPositionValue1())
297 ASSERT(isMainThread());
298 ASSERT(contextElement);
300 // Steps 4.2-4.6 of the HTML5 Fragment Case parsing algorithm:
301 // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-end.html#fragment-case
302 // For efficiency, we skip step 4.2 ("Let root be a new html element with no attributes")
303 // and instead use the DocumentFragment as a root node.
304 m_tree.openElements()->pushRootNode(HTMLStackItem::create(fragment, HTMLStackItem::ItemForDocumentFragmentNode));
306 if (isHTMLTemplateElement(*contextElement))
307 m_templateInsertionModes.append(TemplateContentsMode);
309 resetInsertionModeAppropriately();
310 m_tree.setForm(closestFormAncestor(*contextElement));
313 HTMLTreeBuilder::~HTMLTreeBuilder()
317 void HTMLTreeBuilder::trace(Visitor* visitor)
319 visitor->trace(m_fragmentContext);
320 visitor->trace(m_tree);
321 visitor->trace(m_parser);
322 visitor->trace(m_scriptToProcess);
325 void HTMLTreeBuilder::detach()
328 // This call makes little sense in fragment mode, but for consistency
329 // DocumentParser expects detach() to always be called before it's destroyed.
330 m_isAttached = false;
332 // HTMLConstructionSite might be on the callstack when detach() is called
333 // otherwise we'd just call m_tree.clear() here instead.
337 HTMLTreeBuilder::FragmentParsingContext::FragmentParsingContext()
338 : m_fragment(nullptr)
342 HTMLTreeBuilder::FragmentParsingContext::FragmentParsingContext(DocumentFragment* fragment, Element* contextElement)
343 : m_fragment(fragment)
345 ASSERT(!fragment->hasChildren());
346 m_contextElementStackItem = HTMLStackItem::create(contextElement, HTMLStackItem::ItemForContextElement);
349 HTMLTreeBuilder::FragmentParsingContext::~FragmentParsingContext()
353 void HTMLTreeBuilder::FragmentParsingContext::trace(Visitor* visitor)
355 visitor->trace(m_fragment);
356 visitor->trace(m_contextElementStackItem);
359 PassRefPtrWillBeRawPtr<Element> HTMLTreeBuilder::takeScriptToProcess(TextPosition& scriptStartPosition)
361 ASSERT(m_scriptToProcess);
362 ASSERT(!m_tree.hasPendingTasks());
363 // Unpause ourselves, callers may pause us again when processing the script.
364 // The HTML5 spec is written as though scripts are executed inside the tree
365 // builder. We pause the parser to exit the tree builder, and then resume
366 // before running scripts.
367 scriptStartPosition = m_scriptToProcessStartPosition;
368 m_scriptToProcessStartPosition = uninitializedPositionValue1();
369 return m_scriptToProcess.release();
372 void HTMLTreeBuilder::constructTree(AtomicHTMLToken* token)
374 if (shouldProcessTokenInForeignContent(token))
375 processTokenInForeignContent(token);
379 if (m_parser->tokenizer()) {
380 bool inForeignContent = false;
381 if (!m_tree.isEmpty()) {
382 HTMLStackItem* adjustedCurrentNode = adjustedCurrentStackItem();
383 inForeignContent = !adjustedCurrentNode->isInHTMLNamespace()
384 && !HTMLElementStack::isHTMLIntegrationPoint(adjustedCurrentNode)
385 && !HTMLElementStack::isMathMLTextIntegrationPoint(adjustedCurrentNode);
388 m_parser->tokenizer()->setForceNullCharacterReplacement(m_insertionMode == TextMode || inForeignContent);
389 m_parser->tokenizer()->setShouldAllowCDATA(inForeignContent);
392 m_tree.executeQueuedTasks();
393 // We might be detached now.
396 void HTMLTreeBuilder::processToken(AtomicHTMLToken* token)
398 if (token->type() == HTMLToken::Character) {
399 processCharacter(token);
403 // Any non-character token needs to cause us to flush any pending text immediately.
404 // NOTE: flush() can cause any queued tasks to execute, possibly re-entering the parser.
406 m_shouldSkipLeadingNewline = false;
408 switch (token->type()) {
409 case HTMLToken::Uninitialized:
410 case HTMLToken::Character:
411 ASSERT_NOT_REACHED();
413 case HTMLToken::DOCTYPE:
414 processDoctypeToken(token);
416 case HTMLToken::StartTag:
417 processStartTag(token);
419 case HTMLToken::EndTag:
420 processEndTag(token);
422 case HTMLToken::Comment:
423 processComment(token);
425 case HTMLToken::EndOfFile:
426 processEndOfFile(token);
431 void HTMLTreeBuilder::processDoctypeToken(AtomicHTMLToken* token)
433 ASSERT(token->type() == HTMLToken::DOCTYPE);
434 if (m_insertionMode == InitialMode) {
435 m_tree.insertDoctype(token);
436 setInsertionMode(BeforeHTMLMode);
439 if (m_insertionMode == InTableTextMode) {
440 defaultForInTableText();
441 processDoctypeToken(token);
447 void HTMLTreeBuilder::processFakeStartTag(const QualifiedName& tagName, const Vector<Attribute>& attributes)
449 // FIXME: We'll need a fancier conversion than just "localName" for SVG/MathML tags.
450 AtomicHTMLToken fakeToken(HTMLToken::StartTag, tagName.localName(), attributes);
451 processStartTag(&fakeToken);
454 void HTMLTreeBuilder::processFakeEndTag(const AtomicString& tagName)
456 AtomicHTMLToken fakeToken(HTMLToken::EndTag, tagName);
457 processEndTag(&fakeToken);
460 void HTMLTreeBuilder::processFakeEndTag(const QualifiedName& tagName)
462 // FIXME: We'll need a fancier conversion than just "localName" for SVG/MathML tags.
463 processFakeEndTag(tagName.localName());
466 void HTMLTreeBuilder::processFakePEndTagIfPInButtonScope()
468 if (!m_tree.openElements()->inButtonScope(pTag.localName()))
470 AtomicHTMLToken endP(HTMLToken::EndTag, pTag.localName());
471 processEndTag(&endP);
476 bool isLi(const HTMLStackItem* item)
478 return item->hasTagName(liTag);
481 bool isDdOrDt(const HTMLStackItem* item)
483 return item->hasTagName(ddTag)
484 || item->hasTagName(dtTag);
489 template <bool shouldClose(const HTMLStackItem*)>
490 void HTMLTreeBuilder::processCloseWhenNestedTag(AtomicHTMLToken* token)
492 m_framesetOk = false;
493 HTMLElementStack::ElementRecord* nodeRecord = m_tree.openElements()->topRecord();
495 RefPtrWillBeRawPtr<HTMLStackItem> item = nodeRecord->stackItem();
496 if (shouldClose(item.get())) {
497 ASSERT(item->isElementNode());
498 processFakeEndTag(item->localName());
501 if (item->isSpecialNode() && !item->hasTagName(addressTag) && !item->hasTagName(divTag) && !item->hasTagName(pTag))
503 nodeRecord = nodeRecord->next();
505 processFakePEndTagIfPInButtonScope();
506 m_tree.insertHTMLElement(token);
509 typedef HashMap<AtomicString, QualifiedName> PrefixedNameToQualifiedNameMap;
511 template <typename TableQualifiedName>
512 static void mapLoweredLocalNameToName(PrefixedNameToQualifiedNameMap* map, const TableQualifiedName* const* names, size_t length)
514 for (size_t i = 0; i < length; ++i) {
515 const QualifiedName& name = *names[i];
516 const AtomicString& localName = name.localName();
517 AtomicString loweredLocalName = localName.lower();
518 if (loweredLocalName != localName)
519 map->add(loweredLocalName, name);
523 static void adjustSVGTagNameCase(AtomicHTMLToken* token)
525 static PrefixedNameToQualifiedNameMap* caseMap = 0;
527 caseMap = new PrefixedNameToQualifiedNameMap;
528 OwnPtr<const SVGQualifiedName*[]> svgTags = SVGNames::getSVGTags();
529 mapLoweredLocalNameToName(caseMap, svgTags.get(), SVGNames::SVGTagsCount);
532 const QualifiedName& casedName = caseMap->get(token->name());
533 if (casedName.localName().isNull())
535 token->setName(casedName.localName());
538 template<PassOwnPtr<const QualifiedName*[]> getAttrs(), unsigned length>
539 static void adjustAttributes(AtomicHTMLToken* token)
541 static PrefixedNameToQualifiedNameMap* caseMap = 0;
543 caseMap = new PrefixedNameToQualifiedNameMap;
544 OwnPtr<const QualifiedName*[]> attrs = getAttrs();
545 mapLoweredLocalNameToName(caseMap, attrs.get(), length);
548 for (unsigned i = 0; i < token->attributes().size(); ++i) {
549 Attribute& tokenAttribute = token->attributes().at(i);
550 const QualifiedName& casedName = caseMap->get(tokenAttribute.localName());
551 if (!casedName.localName().isNull())
552 tokenAttribute.parserSetName(casedName);
556 static void adjustSVGAttributes(AtomicHTMLToken* token)
558 adjustAttributes<SVGNames::getSVGAttrs, SVGNames::SVGAttrsCount>(token);
561 static void adjustMathMLAttributes(AtomicHTMLToken* token)
563 adjustAttributes<MathMLNames::getMathMLAttrs, MathMLNames::MathMLAttrsCount>(token);
566 static void addNamesWithPrefix(PrefixedNameToQualifiedNameMap* map, const AtomicString& prefix, const QualifiedName* const* names, size_t length)
568 for (size_t i = 0; i < length; ++i) {
569 const QualifiedName* name = names[i];
570 const AtomicString& localName = name->localName();
571 AtomicString prefixColonLocalName = prefix + ':' + localName;
572 QualifiedName nameWithPrefix(prefix, localName, name->namespaceURI());
573 map->add(prefixColonLocalName, nameWithPrefix);
577 static void adjustForeignAttributes(AtomicHTMLToken* token)
579 static PrefixedNameToQualifiedNameMap* map = 0;
581 map = new PrefixedNameToQualifiedNameMap;
583 OwnPtr<const QualifiedName*[]> attrs = XLinkNames::getXLinkAttrs();
584 addNamesWithPrefix(map, xlinkAtom, attrs.get(), XLinkNames::XLinkAttrsCount);
586 OwnPtr<const QualifiedName*[]> xmlAttrs = XMLNames::getXMLAttrs();
587 addNamesWithPrefix(map, xmlAtom, xmlAttrs.get(), XMLNames::XMLAttrsCount);
589 map->add(WTF::xmlnsAtom, XMLNSNames::xmlnsAttr);
590 map->add("xmlns:xlink", QualifiedName(xmlnsAtom, xlinkAtom, XMLNSNames::xmlnsNamespaceURI));
593 for (unsigned i = 0; i < token->attributes().size(); ++i) {
594 Attribute& tokenAttribute = token->attributes().at(i);
595 const QualifiedName& name = map->get(tokenAttribute.localName());
596 if (!name.localName().isNull())
597 tokenAttribute.parserSetName(name);
601 void HTMLTreeBuilder::processStartTagForInBody(AtomicHTMLToken* token)
603 ASSERT(token->type() == HTMLToken::StartTag);
604 if (token->name() == htmlTag) {
605 processHtmlStartTagForInBody(token);
608 if (token->name() == baseTag
609 || token->name() == basefontTag
610 || token->name() == bgsoundTag
611 || token->name() == commandTag
612 || token->name() == linkTag
613 || token->name() == metaTag
614 || token->name() == noframesTag
615 || token->name() == scriptTag
616 || token->name() == styleTag
617 || token->name() == titleTag) {
618 bool didProcess = processStartTagForInHead(token);
619 ASSERT_UNUSED(didProcess, didProcess);
622 if (token->name() == bodyTag) {
624 if (!m_tree.openElements()->secondElementIsHTMLBodyElement() || m_tree.openElements()->hasOnlyOneElement() || m_tree.openElements()->hasTemplateInHTMLScope()) {
625 ASSERT(isParsingFragmentOrTemplateContents());
628 m_framesetOk = false;
629 m_tree.insertHTMLBodyStartTagInBody(token);
632 if (token->name() == framesetTag) {
634 if (!m_tree.openElements()->secondElementIsHTMLBodyElement() || m_tree.openElements()->hasOnlyOneElement()) {
635 ASSERT(isParsingFragmentOrTemplateContents());
640 m_tree.openElements()->bodyElement()->remove(ASSERT_NO_EXCEPTION);
641 m_tree.openElements()->popUntil(m_tree.openElements()->bodyElement());
642 m_tree.openElements()->popHTMLBodyElement();
643 ASSERT(m_tree.openElements()->top() == m_tree.openElements()->htmlElement());
644 m_tree.insertHTMLElement(token);
645 setInsertionMode(InFramesetMode);
648 if (token->name() == addressTag
649 || token->name() == articleTag
650 || token->name() == asideTag
651 || token->name() == blockquoteTag
652 || token->name() == centerTag
653 || token->name() == detailsTag
654 || token->name() == dirTag
655 || token->name() == divTag
656 || token->name() == dlTag
657 || token->name() == fieldsetTag
658 || token->name() == figcaptionTag
659 || token->name() == figureTag
660 || token->name() == footerTag
661 || token->name() == headerTag
662 || token->name() == hgroupTag
663 || token->name() == mainTag
664 || token->name() == menuTag
665 || token->name() == navTag
666 || token->name() == olTag
667 || token->name() == pTag
668 || token->name() == sectionTag
669 || token->name() == summaryTag
670 || token->name() == ulTag) {
671 processFakePEndTagIfPInButtonScope();
672 m_tree.insertHTMLElement(token);
675 if (isNumberedHeaderTag(token->name())) {
676 processFakePEndTagIfPInButtonScope();
677 if (m_tree.currentStackItem()->isNumberedHeaderElement()) {
679 m_tree.openElements()->pop();
681 m_tree.insertHTMLElement(token);
684 if (token->name() == preTag || token->name() == listingTag) {
685 processFakePEndTagIfPInButtonScope();
686 m_tree.insertHTMLElement(token);
687 m_shouldSkipLeadingNewline = true;
688 m_framesetOk = false;
691 if (token->name() == formTag) {
696 processFakePEndTagIfPInButtonScope();
697 m_tree.insertHTMLFormElement(token);
700 if (token->name() == liTag) {
701 processCloseWhenNestedTag<isLi>(token);
704 if (token->name() == ddTag || token->name() == dtTag) {
705 processCloseWhenNestedTag<isDdOrDt>(token);
708 if (token->name() == plaintextTag) {
709 processFakePEndTagIfPInButtonScope();
710 m_tree.insertHTMLElement(token);
711 if (m_parser->tokenizer())
712 m_parser->tokenizer()->setState(HTMLTokenizer::PLAINTEXTState);
715 if (token->name() == buttonTag) {
716 if (m_tree.openElements()->inScope(buttonTag)) {
718 processFakeEndTag(buttonTag);
719 processStartTag(token); // FIXME: Could we just fall through here?
722 m_tree.reconstructTheActiveFormattingElements();
723 m_tree.insertHTMLElement(token);
724 m_framesetOk = false;
727 if (token->name() == aTag) {
728 Element* activeATag = m_tree.activeFormattingElements()->closestElementInScopeWithName(aTag.localName());
731 processFakeEndTag(aTag);
732 m_tree.activeFormattingElements()->remove(activeATag);
733 if (m_tree.openElements()->contains(activeATag))
734 m_tree.openElements()->remove(activeATag);
736 m_tree.reconstructTheActiveFormattingElements();
737 m_tree.insertFormattingElement(token);
740 if (isNonAnchorNonNobrFormattingTag(token->name())) {
741 m_tree.reconstructTheActiveFormattingElements();
742 m_tree.insertFormattingElement(token);
745 if (token->name() == nobrTag) {
746 m_tree.reconstructTheActiveFormattingElements();
747 if (m_tree.openElements()->inScope(nobrTag)) {
749 processFakeEndTag(nobrTag);
750 m_tree.reconstructTheActiveFormattingElements();
752 m_tree.insertFormattingElement(token);
755 if (token->name() == appletTag
756 || token->name() == embedTag
757 || token->name() == objectTag) {
758 if (!pluginContentIsAllowed(m_tree.parserContentPolicy()))
761 if (token->name() == appletTag
762 || token->name() == marqueeTag
763 || token->name() == objectTag) {
764 m_tree.reconstructTheActiveFormattingElements();
765 m_tree.insertHTMLElement(token);
766 m_tree.activeFormattingElements()->appendMarker();
767 m_framesetOk = false;
770 if (token->name() == tableTag) {
771 if (!m_tree.inQuirksMode() && m_tree.openElements()->inButtonScope(pTag))
772 processFakeEndTag(pTag);
773 m_tree.insertHTMLElement(token);
774 m_framesetOk = false;
775 setInsertionMode(InTableMode);
778 if (token->name() == imageTag) {
780 // Apparently we're not supposed to ask.
781 token->setName(imgTag.localName());
782 // Note the fall through to the imgTag handling below!
784 if (token->name() == areaTag
785 || token->name() == brTag
786 || token->name() == embedTag
787 || token->name() == imgTag
788 || token->name() == keygenTag
789 || token->name() == wbrTag) {
790 m_tree.reconstructTheActiveFormattingElements();
791 m_tree.insertSelfClosingHTMLElement(token);
792 m_framesetOk = false;
795 if (token->name() == inputTag) {
796 Attribute* typeAttribute = token->getAttributeItem(typeAttr);
797 m_tree.reconstructTheActiveFormattingElements();
798 m_tree.insertSelfClosingHTMLElement(token);
799 if (!typeAttribute || !equalIgnoringCase(typeAttribute->value(), "hidden"))
800 m_framesetOk = false;
803 if ((RuntimeEnabledFeatures::contextMenuEnabled() && token->name() == menuitemTag)
804 || token->name() == paramTag
805 || token->name() == sourceTag
806 || token->name() == trackTag) {
807 m_tree.insertSelfClosingHTMLElement(token);
810 if (token->name() == hrTag) {
811 processFakePEndTagIfPInButtonScope();
812 m_tree.insertSelfClosingHTMLElement(token);
813 m_framesetOk = false;
816 if (token->name() == textareaTag) {
817 m_tree.insertHTMLElement(token);
818 m_shouldSkipLeadingNewline = true;
819 if (m_parser->tokenizer())
820 m_parser->tokenizer()->setState(HTMLTokenizer::RCDATAState);
821 m_originalInsertionMode = m_insertionMode;
822 m_framesetOk = false;
823 setInsertionMode(TextMode);
826 if (token->name() == xmpTag) {
827 processFakePEndTagIfPInButtonScope();
828 m_tree.reconstructTheActiveFormattingElements();
829 m_framesetOk = false;
830 processGenericRawTextStartTag(token);
833 if (token->name() == iframeTag) {
834 m_framesetOk = false;
835 processGenericRawTextStartTag(token);
838 if (token->name() == noembedTag && m_options.pluginsEnabled) {
839 processGenericRawTextStartTag(token);
842 if (token->name() == noscriptTag && m_options.scriptEnabled) {
843 processGenericRawTextStartTag(token);
846 if (token->name() == selectTag) {
847 m_tree.reconstructTheActiveFormattingElements();
848 m_tree.insertHTMLElement(token);
849 m_framesetOk = false;
850 if (m_insertionMode == InTableMode
851 || m_insertionMode == InCaptionMode
852 || m_insertionMode == InColumnGroupMode
853 || m_insertionMode == InTableBodyMode
854 || m_insertionMode == InRowMode
855 || m_insertionMode == InCellMode)
856 setInsertionMode(InSelectInTableMode);
858 setInsertionMode(InSelectMode);
861 if (token->name() == optgroupTag || token->name() == optionTag) {
862 if (m_tree.currentStackItem()->hasTagName(optionTag)) {
863 AtomicHTMLToken endOption(HTMLToken::EndTag, optionTag.localName());
864 processEndTag(&endOption);
866 m_tree.reconstructTheActiveFormattingElements();
867 m_tree.insertHTMLElement(token);
870 if (token->name() == rpTag || token->name() == rtTag) {
871 if (m_tree.openElements()->inScope(rubyTag.localName())) {
872 m_tree.generateImpliedEndTags();
873 if (!m_tree.currentStackItem()->hasTagName(rubyTag))
876 m_tree.insertHTMLElement(token);
879 if (token->name() == MathMLNames::mathTag.localName()) {
880 m_tree.reconstructTheActiveFormattingElements();
881 adjustMathMLAttributes(token);
882 adjustForeignAttributes(token);
883 m_tree.insertForeignElement(token, MathMLNames::mathmlNamespaceURI);
886 if (token->name() == SVGNames::svgTag.localName()) {
887 m_tree.reconstructTheActiveFormattingElements();
888 adjustSVGAttributes(token);
889 adjustForeignAttributes(token);
890 m_tree.insertForeignElement(token, SVGNames::svgNamespaceURI);
893 if (isCaptionColOrColgroupTag(token->name())
894 || token->name() == frameTag
895 || token->name() == headTag
896 || isTableBodyContextTag(token->name())
897 || isTableCellContextTag(token->name())
898 || token->name() == trTag) {
902 if (token->name() == templateTag) {
903 processTemplateStartTag(token);
906 m_tree.reconstructTheActiveFormattingElements();
907 m_tree.insertHTMLElement(token);
910 void HTMLTreeBuilder::processTemplateStartTag(AtomicHTMLToken* token)
912 m_tree.activeFormattingElements()->appendMarker();
913 m_tree.insertHTMLElement(token);
914 m_templateInsertionModes.append(TemplateContentsMode);
915 setInsertionMode(TemplateContentsMode);
918 bool HTMLTreeBuilder::processTemplateEndTag(AtomicHTMLToken* token)
920 ASSERT(token->name() == templateTag.localName());
921 if (!m_tree.openElements()->hasTemplateInHTMLScope()) {
922 ASSERT(m_templateInsertionModes.isEmpty() || (m_templateInsertionModes.size() == 1 && isHTMLTemplateElement(m_fragmentContext.contextElement())));
926 m_tree.generateImpliedEndTags();
927 if (!m_tree.currentStackItem()->hasTagName(templateTag))
929 m_tree.openElements()->popUntilPopped(templateTag);
930 m_tree.activeFormattingElements()->clearToLastMarker();
931 m_templateInsertionModes.removeLast();
932 resetInsertionModeAppropriately();
936 bool HTMLTreeBuilder::processEndOfFileForInTemplateContents(AtomicHTMLToken* token)
938 AtomicHTMLToken endTemplate(HTMLToken::EndTag, templateTag.localName());
939 if (!processTemplateEndTag(&endTemplate))
942 processEndOfFile(token);
946 bool HTMLTreeBuilder::processColgroupEndTagForInColumnGroup()
948 if (m_tree.currentIsRootNode() || isHTMLTemplateElement(*m_tree.currentNode())) {
949 ASSERT(isParsingFragmentOrTemplateContents());
950 // FIXME: parse error
953 m_tree.openElements()->pop();
954 setInsertionMode(InTableMode);
958 // http://www.whatwg.org/specs/web-apps/current-work/#adjusted-current-node
959 HTMLStackItem* HTMLTreeBuilder::adjustedCurrentStackItem() const
961 ASSERT(!m_tree.isEmpty());
962 if (isParsingFragment() && m_tree.openElements()->hasOnlyOneElement())
963 return m_fragmentContext.contextElementStackItem();
965 return m_tree.currentStackItem();
968 // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#close-the-cell
969 void HTMLTreeBuilder::closeTheCell()
971 ASSERT(insertionMode() == InCellMode);
972 if (m_tree.openElements()->inTableScope(tdTag)) {
973 ASSERT(!m_tree.openElements()->inTableScope(thTag));
974 processFakeEndTag(tdTag);
977 ASSERT(m_tree.openElements()->inTableScope(thTag));
978 processFakeEndTag(thTag);
979 ASSERT(insertionMode() == InRowMode);
982 void HTMLTreeBuilder::processStartTagForInTable(AtomicHTMLToken* token)
984 ASSERT(token->type() == HTMLToken::StartTag);
985 if (token->name() == captionTag) {
986 m_tree.openElements()->popUntilTableScopeMarker();
987 m_tree.activeFormattingElements()->appendMarker();
988 m_tree.insertHTMLElement(token);
989 setInsertionMode(InCaptionMode);
992 if (token->name() == colgroupTag) {
993 m_tree.openElements()->popUntilTableScopeMarker();
994 m_tree.insertHTMLElement(token);
995 setInsertionMode(InColumnGroupMode);
998 if (token->name() == colTag) {
999 processFakeStartTag(colgroupTag);
1000 ASSERT(InColumnGroupMode);
1001 processStartTag(token);
1004 if (isTableBodyContextTag(token->name())) {
1005 m_tree.openElements()->popUntilTableScopeMarker();
1006 m_tree.insertHTMLElement(token);
1007 setInsertionMode(InTableBodyMode);
1010 if (isTableCellContextTag(token->name())
1011 || token->name() == trTag) {
1012 processFakeStartTag(tbodyTag);
1013 ASSERT(insertionMode() == InTableBodyMode);
1014 processStartTag(token);
1017 if (token->name() == tableTag) {
1019 if (!processTableEndTagForInTable()) {
1020 ASSERT(isParsingFragmentOrTemplateContents());
1023 processStartTag(token);
1026 if (token->name() == styleTag || token->name() == scriptTag) {
1027 processStartTagForInHead(token);
1030 if (token->name() == inputTag) {
1031 Attribute* typeAttribute = token->getAttributeItem(typeAttr);
1032 if (typeAttribute && equalIgnoringCase(typeAttribute->value(), "hidden")) {
1034 m_tree.insertSelfClosingHTMLElement(token);
1037 // Fall through to "anything else" case.
1039 if (token->name() == formTag) {
1043 m_tree.insertHTMLFormElement(token, true);
1044 m_tree.openElements()->pop();
1047 if (token->name() == templateTag) {
1048 processTemplateStartTag(token);
1052 HTMLConstructionSite::RedirectToFosterParentGuard redirecter(m_tree);
1053 processStartTagForInBody(token);
1056 void HTMLTreeBuilder::processStartTag(AtomicHTMLToken* token)
1058 ASSERT(token->type() == HTMLToken::StartTag);
1059 switch (insertionMode()) {
1061 ASSERT(insertionMode() == InitialMode);
1062 defaultForInitial();
1064 case BeforeHTMLMode:
1065 ASSERT(insertionMode() == BeforeHTMLMode);
1066 if (token->name() == htmlTag) {
1067 m_tree.insertHTMLHtmlStartTagBeforeHTML(token);
1068 setInsertionMode(BeforeHeadMode);
1071 defaultForBeforeHTML();
1073 case BeforeHeadMode:
1074 ASSERT(insertionMode() == BeforeHeadMode);
1075 if (token->name() == htmlTag) {
1076 processHtmlStartTagForInBody(token);
1079 if (token->name() == headTag) {
1080 m_tree.insertHTMLHeadElement(token);
1081 setInsertionMode(InHeadMode);
1084 defaultForBeforeHead();
1087 ASSERT(insertionMode() == InHeadMode);
1088 if (processStartTagForInHead(token))
1093 ASSERT(insertionMode() == AfterHeadMode);
1094 if (token->name() == htmlTag) {
1095 processHtmlStartTagForInBody(token);
1098 if (token->name() == bodyTag) {
1099 m_framesetOk = false;
1100 m_tree.insertHTMLBodyElement(token);
1101 setInsertionMode(InBodyMode);
1104 if (token->name() == framesetTag) {
1105 m_tree.insertHTMLElement(token);
1106 setInsertionMode(InFramesetMode);
1109 if (token->name() == baseTag
1110 || token->name() == basefontTag
1111 || token->name() == bgsoundTag
1112 || token->name() == linkTag
1113 || token->name() == metaTag
1114 || token->name() == noframesTag
1115 || token->name() == scriptTag
1116 || token->name() == styleTag
1117 || token->name() == templateTag
1118 || token->name() == titleTag) {
1120 ASSERT(m_tree.head());
1121 m_tree.openElements()->pushHTMLHeadElement(m_tree.headStackItem());
1122 processStartTagForInHead(token);
1123 m_tree.openElements()->removeHTMLHeadElement(m_tree.head());
1126 if (token->name() == headTag) {
1130 defaultForAfterHead();
1133 ASSERT(insertionMode() == InBodyMode);
1134 processStartTagForInBody(token);
1137 ASSERT(insertionMode() == InTableMode);
1138 processStartTagForInTable(token);
1141 ASSERT(insertionMode() == InCaptionMode);
1142 if (isCaptionColOrColgroupTag(token->name())
1143 || isTableBodyContextTag(token->name())
1144 || isTableCellContextTag(token->name())
1145 || token->name() == trTag) {
1147 if (!processCaptionEndTagForInCaption()) {
1148 ASSERT(isParsingFragment());
1151 processStartTag(token);
1154 processStartTagForInBody(token);
1156 case InColumnGroupMode:
1157 ASSERT(insertionMode() == InColumnGroupMode);
1158 if (token->name() == htmlTag) {
1159 processHtmlStartTagForInBody(token);
1162 if (token->name() == colTag) {
1163 m_tree.insertSelfClosingHTMLElement(token);
1166 if (token->name() == templateTag) {
1167 processTemplateStartTag(token);
1170 if (!processColgroupEndTagForInColumnGroup()) {
1171 ASSERT(isParsingFragmentOrTemplateContents());
1174 processStartTag(token);
1176 case InTableBodyMode:
1177 ASSERT(insertionMode() == InTableBodyMode);
1178 if (token->name() == trTag) {
1179 m_tree.openElements()->popUntilTableBodyScopeMarker(); // How is there ever anything to pop?
1180 m_tree.insertHTMLElement(token);
1181 setInsertionMode(InRowMode);
1184 if (isTableCellContextTag(token->name())) {
1186 processFakeStartTag(trTag);
1187 ASSERT(insertionMode() == InRowMode);
1188 processStartTag(token);
1191 if (isCaptionColOrColgroupTag(token->name()) || isTableBodyContextTag(token->name())) {
1192 // FIXME: This is slow.
1193 if (!m_tree.openElements()->inTableScope(tbodyTag) && !m_tree.openElements()->inTableScope(theadTag) && !m_tree.openElements()->inTableScope(tfootTag)) {
1194 ASSERT(isParsingFragmentOrTemplateContents());
1198 m_tree.openElements()->popUntilTableBodyScopeMarker();
1199 ASSERT(isTableBodyContextTag(m_tree.currentStackItem()->localName()));
1200 processFakeEndTag(m_tree.currentStackItem()->localName());
1201 processStartTag(token);
1204 processStartTagForInTable(token);
1207 ASSERT(insertionMode() == InRowMode);
1208 if (isTableCellContextTag(token->name())) {
1209 m_tree.openElements()->popUntilTableRowScopeMarker();
1210 m_tree.insertHTMLElement(token);
1211 setInsertionMode(InCellMode);
1212 m_tree.activeFormattingElements()->appendMarker();
1215 if (token->name() == trTag
1216 || isCaptionColOrColgroupTag(token->name())
1217 || isTableBodyContextTag(token->name())) {
1218 if (!processTrEndTagForInRow()) {
1219 ASSERT(isParsingFragmentOrTemplateContents());
1222 ASSERT(insertionMode() == InTableBodyMode);
1223 processStartTag(token);
1226 processStartTagForInTable(token);
1229 ASSERT(insertionMode() == InCellMode);
1230 if (isCaptionColOrColgroupTag(token->name())
1231 || isTableCellContextTag(token->name())
1232 || token->name() == trTag
1233 || isTableBodyContextTag(token->name())) {
1234 // FIXME: This could be more efficient.
1235 if (!m_tree.openElements()->inTableScope(tdTag) && !m_tree.openElements()->inTableScope(thTag)) {
1236 ASSERT(isParsingFragment());
1241 processStartTag(token);
1244 processStartTagForInBody(token);
1247 case AfterAfterBodyMode:
1248 ASSERT(insertionMode() == AfterBodyMode || insertionMode() == AfterAfterBodyMode);
1249 if (token->name() == htmlTag) {
1250 processHtmlStartTagForInBody(token);
1253 setInsertionMode(InBodyMode);
1254 processStartTag(token);
1256 case InHeadNoscriptMode:
1257 ASSERT(insertionMode() == InHeadNoscriptMode);
1258 if (token->name() == htmlTag) {
1259 processHtmlStartTagForInBody(token);
1262 if (token->name() == basefontTag
1263 || token->name() == bgsoundTag
1264 || token->name() == linkTag
1265 || token->name() == metaTag
1266 || token->name() == noframesTag
1267 || token->name() == styleTag) {
1268 bool didProcess = processStartTagForInHead(token);
1269 ASSERT_UNUSED(didProcess, didProcess);
1272 if (token->name() == htmlTag || token->name() == noscriptTag) {
1276 defaultForInHeadNoscript();
1277 processToken(token);
1279 case InFramesetMode:
1280 ASSERT(insertionMode() == InFramesetMode);
1281 if (token->name() == htmlTag) {
1282 processHtmlStartTagForInBody(token);
1285 if (token->name() == framesetTag) {
1286 m_tree.insertHTMLElement(token);
1289 if (token->name() == frameTag) {
1290 m_tree.insertSelfClosingHTMLElement(token);
1293 if (token->name() == noframesTag) {
1294 processStartTagForInHead(token);
1297 if (token->name() == templateTag) {
1298 processTemplateStartTag(token);
1303 case AfterFramesetMode:
1304 case AfterAfterFramesetMode:
1305 ASSERT(insertionMode() == AfterFramesetMode || insertionMode() == AfterAfterFramesetMode);
1306 if (token->name() == htmlTag) {
1307 processHtmlStartTagForInBody(token);
1310 if (token->name() == noframesTag) {
1311 processStartTagForInHead(token);
1316 case InSelectInTableMode:
1317 ASSERT(insertionMode() == InSelectInTableMode);
1318 if (token->name() == captionTag
1319 || token->name() == tableTag
1320 || isTableBodyContextTag(token->name())
1321 || token->name() == trTag
1322 || isTableCellContextTag(token->name())) {
1324 AtomicHTMLToken endSelect(HTMLToken::EndTag, selectTag.localName());
1325 processEndTag(&endSelect);
1326 processStartTag(token);
1331 ASSERT(insertionMode() == InSelectMode || insertionMode() == InSelectInTableMode);
1332 if (token->name() == htmlTag) {
1333 processHtmlStartTagForInBody(token);
1336 if (token->name() == optionTag) {
1337 if (m_tree.currentStackItem()->hasTagName(optionTag)) {
1338 AtomicHTMLToken endOption(HTMLToken::EndTag, optionTag.localName());
1339 processEndTag(&endOption);
1341 m_tree.insertHTMLElement(token);
1344 if (token->name() == optgroupTag) {
1345 if (m_tree.currentStackItem()->hasTagName(optionTag)) {
1346 AtomicHTMLToken endOption(HTMLToken::EndTag, optionTag.localName());
1347 processEndTag(&endOption);
1349 if (m_tree.currentStackItem()->hasTagName(optgroupTag)) {
1350 AtomicHTMLToken endOptgroup(HTMLToken::EndTag, optgroupTag.localName());
1351 processEndTag(&endOptgroup);
1353 m_tree.insertHTMLElement(token);
1356 if (token->name() == selectTag) {
1358 AtomicHTMLToken endSelect(HTMLToken::EndTag, selectTag.localName());
1359 processEndTag(&endSelect);
1362 if (token->name() == inputTag
1363 || token->name() == keygenTag
1364 || token->name() == textareaTag) {
1366 if (!m_tree.openElements()->inSelectScope(selectTag)) {
1367 ASSERT(isParsingFragment());
1370 AtomicHTMLToken endSelect(HTMLToken::EndTag, selectTag.localName());
1371 processEndTag(&endSelect);
1372 processStartTag(token);
1375 if (token->name() == scriptTag) {
1376 bool didProcess = processStartTagForInHead(token);
1377 ASSERT_UNUSED(didProcess, didProcess);
1380 if (token->name() == templateTag) {
1381 processTemplateStartTag(token);
1385 case InTableTextMode:
1386 defaultForInTableText();
1387 processStartTag(token);
1390 ASSERT_NOT_REACHED();
1392 case TemplateContentsMode:
1393 if (token->name() == templateTag) {
1394 processTemplateStartTag(token);
1398 if (token->name() == linkTag
1399 || token->name() == scriptTag
1400 || token->name() == styleTag
1401 || token->name() == metaTag) {
1402 processStartTagForInHead(token);
1406 InsertionMode insertionMode = TemplateContentsMode;
1407 if (token->name() == frameTag)
1408 insertionMode = InFramesetMode;
1409 else if (token->name() == colTag)
1410 insertionMode = InColumnGroupMode;
1411 else if (isCaptionColOrColgroupTag(token->name()) || isTableBodyContextTag(token->name()))
1412 insertionMode = InTableMode;
1413 else if (token->name() == trTag)
1414 insertionMode = InTableBodyMode;
1415 else if (isTableCellContextTag(token->name()))
1416 insertionMode = InRowMode;
1418 insertionMode = InBodyMode;
1420 ASSERT(insertionMode != TemplateContentsMode);
1421 ASSERT(m_templateInsertionModes.last() == TemplateContentsMode);
1422 m_templateInsertionModes.last() = insertionMode;
1423 setInsertionMode(insertionMode);
1425 processStartTag(token);
1430 void HTMLTreeBuilder::processHtmlStartTagForInBody(AtomicHTMLToken* token)
1433 if (m_tree.openElements()->hasTemplateInHTMLScope()) {
1434 ASSERT(isParsingTemplateContents());
1437 m_tree.insertHTMLHtmlStartTagInBody(token);
1440 bool HTMLTreeBuilder::processBodyEndTagForInBody(AtomicHTMLToken* token)
1442 ASSERT(token->type() == HTMLToken::EndTag);
1443 ASSERT(token->name() == bodyTag);
1444 if (!m_tree.openElements()->inScope(bodyTag.localName())) {
1448 notImplemented(); // Emit a more specific parse error based on stack contents.
1449 setInsertionMode(AfterBodyMode);
1453 void HTMLTreeBuilder::processAnyOtherEndTagForInBody(AtomicHTMLToken* token)
1455 ASSERT(token->type() == HTMLToken::EndTag);
1456 HTMLElementStack::ElementRecord* record = m_tree.openElements()->topRecord();
1458 RefPtrWillBeRawPtr<HTMLStackItem> item = record->stackItem();
1459 if (item->matchesHTMLTag(token->name())) {
1460 m_tree.generateImpliedEndTagsWithExclusion(token->name());
1461 if (!m_tree.currentStackItem()->matchesHTMLTag(token->name()))
1463 m_tree.openElements()->popUntilPopped(item->element());
1466 if (item->isSpecialNode()) {
1470 record = record->next();
1474 // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#parsing-main-inbody
1475 void HTMLTreeBuilder::callTheAdoptionAgency(AtomicHTMLToken* token)
1477 // The adoption agency algorithm is N^2. We limit the number of iterations
1478 // to stop from hanging the whole browser. This limit is specified in the
1479 // adoption agency algorithm:
1480 // http://www.whatwg.org/specs/web-apps/current-work/multipage/tree-construction.html#parsing-main-inbody
1481 static const int outerIterationLimit = 8;
1482 static const int innerIterationLimit = 3;
1484 // 1, 2, 3 and 16 are covered by the for() loop.
1485 for (int i = 0; i < outerIterationLimit; ++i) {
1487 Element* formattingElement = m_tree.activeFormattingElements()->closestElementInScopeWithName(token->name());
1489 if (!formattingElement)
1490 return processAnyOtherEndTagForInBody(token);
1492 if ((m_tree.openElements()->contains(formattingElement)) && !m_tree.openElements()->inScope(formattingElement)) {
1494 notImplemented(); // Check the stack of open elements for a more specific parse error.
1498 HTMLElementStack::ElementRecord* formattingElementRecord = m_tree.openElements()->find(formattingElement);
1499 if (!formattingElementRecord) {
1501 m_tree.activeFormattingElements()->remove(formattingElement);
1505 if (formattingElement != m_tree.currentElement())
1508 HTMLElementStack::ElementRecord* furthestBlock = m_tree.openElements()->furthestBlockForFormattingElement(formattingElement);
1510 if (!furthestBlock) {
1511 m_tree.openElements()->popUntilPopped(formattingElement);
1512 m_tree.activeFormattingElements()->remove(formattingElement);
1516 ASSERT(furthestBlock->isAbove(formattingElementRecord));
1517 RefPtrWillBeRawPtr<HTMLStackItem> commonAncestor = formattingElementRecord->next()->stackItem();
1519 HTMLFormattingElementList::Bookmark bookmark = m_tree.activeFormattingElements()->bookmarkFor(formattingElement);
1521 HTMLElementStack::ElementRecord* node = furthestBlock;
1522 HTMLElementStack::ElementRecord* nextNode = node->next();
1523 HTMLElementStack::ElementRecord* lastNode = furthestBlock;
1524 // 9.1, 9.2, 9.3 and 9.11 are covered by the for() loop.
1525 for (int i = 0; i < innerIterationLimit; ++i) {
1529 nextNode = node->next(); // Save node->next() for the next iteration in case node is deleted in 9.5.
1531 if (!m_tree.activeFormattingElements()->contains(node->element())) {
1532 m_tree.openElements()->remove(node->element());
1537 if (node == formattingElementRecord)
1540 RefPtrWillBeRawPtr<HTMLStackItem> newItem = m_tree.createElementFromSavedToken(node->stackItem().get());
1542 HTMLFormattingElementList::Entry* nodeEntry = m_tree.activeFormattingElements()->find(node->element());
1543 nodeEntry->replaceElement(newItem);
1544 node->replaceElement(newItem.release());
1547 if (lastNode == furthestBlock)
1548 bookmark.moveToAfter(nodeEntry);
1550 m_tree.reparent(node, lastNode);
1555 m_tree.insertAlreadyParsedChild(commonAncestor.get(), lastNode);
1557 RefPtrWillBeRawPtr<HTMLStackItem> newItem = m_tree.createElementFromSavedToken(formattingElementRecord->stackItem().get());
1559 m_tree.takeAllChildren(newItem.get(), furthestBlock);
1561 m_tree.reparent(furthestBlock, newItem.get());
1563 m_tree.activeFormattingElements()->swapTo(formattingElement, newItem, bookmark);
1565 m_tree.openElements()->remove(formattingElement);
1566 m_tree.openElements()->insertAbove(newItem, furthestBlock);
1570 void HTMLTreeBuilder::resetInsertionModeAppropriately()
1572 // http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#reset-the-insertion-mode-appropriately
1574 HTMLElementStack::ElementRecord* nodeRecord = m_tree.openElements()->topRecord();
1576 RefPtrWillBeRawPtr<HTMLStackItem> item = nodeRecord->stackItem();
1577 if (item->node() == m_tree.openElements()->rootNode()) {
1579 if (isParsingFragment())
1580 item = m_fragmentContext.contextElementStackItem();
1582 if (item->hasTagName(templateTag))
1583 return setInsertionMode(m_templateInsertionModes.last());
1584 if (item->hasTagName(selectTag)) {
1586 while (item->node() != m_tree.openElements()->rootNode() && !item->hasTagName(templateTag)) {
1587 nodeRecord = nodeRecord->next();
1588 item = nodeRecord->stackItem();
1589 if (item->hasTagName(tableTag))
1590 return setInsertionMode(InSelectInTableMode);
1593 return setInsertionMode(InSelectMode);
1595 if (item->hasTagName(tdTag) || item->hasTagName(thTag))
1596 return setInsertionMode(InCellMode);
1597 if (item->hasTagName(trTag))
1598 return setInsertionMode(InRowMode);
1599 if (item->hasTagName(tbodyTag) || item->hasTagName(theadTag) || item->hasTagName(tfootTag))
1600 return setInsertionMode(InTableBodyMode);
1601 if (item->hasTagName(captionTag))
1602 return setInsertionMode(InCaptionMode);
1603 if (item->hasTagName(colgroupTag)) {
1604 return setInsertionMode(InColumnGroupMode);
1606 if (item->hasTagName(tableTag))
1607 return setInsertionMode(InTableMode);
1608 if (item->hasTagName(headTag)) {
1609 if (!m_fragmentContext.fragment() || m_fragmentContext.contextElement() != item->node())
1610 return setInsertionMode(InHeadMode);
1611 return setInsertionMode(InBodyMode);
1613 if (item->hasTagName(bodyTag))
1614 return setInsertionMode(InBodyMode);
1615 if (item->hasTagName(framesetTag)) {
1616 return setInsertionMode(InFramesetMode);
1618 if (item->hasTagName(htmlTag)) {
1619 if (m_tree.headStackItem())
1620 return setInsertionMode(AfterHeadMode);
1622 ASSERT(isParsingFragment());
1623 return setInsertionMode(BeforeHeadMode);
1626 ASSERT(isParsingFragment());
1627 return setInsertionMode(InBodyMode);
1629 nodeRecord = nodeRecord->next();
1633 void HTMLTreeBuilder::processEndTagForInTableBody(AtomicHTMLToken* token)
1635 ASSERT(token->type() == HTMLToken::EndTag);
1636 if (isTableBodyContextTag(token->name())) {
1637 if (!m_tree.openElements()->inTableScope(token->name())) {
1641 m_tree.openElements()->popUntilTableBodyScopeMarker();
1642 m_tree.openElements()->pop();
1643 setInsertionMode(InTableMode);
1646 if (token->name() == tableTag) {
1647 // FIXME: This is slow.
1648 if (!m_tree.openElements()->inTableScope(tbodyTag) && !m_tree.openElements()->inTableScope(theadTag) && !m_tree.openElements()->inTableScope(tfootTag)) {
1649 ASSERT(isParsingFragmentOrTemplateContents());
1653 m_tree.openElements()->popUntilTableBodyScopeMarker();
1654 ASSERT(isTableBodyContextTag(m_tree.currentStackItem()->localName()));
1655 processFakeEndTag(m_tree.currentStackItem()->localName());
1656 processEndTag(token);
1659 if (token->name() == bodyTag
1660 || isCaptionColOrColgroupTag(token->name())
1661 || token->name() == htmlTag
1662 || isTableCellContextTag(token->name())
1663 || token->name() == trTag) {
1667 processEndTagForInTable(token);
1670 void HTMLTreeBuilder::processEndTagForInRow(AtomicHTMLToken* token)
1672 ASSERT(token->type() == HTMLToken::EndTag);
1673 if (token->name() == trTag) {
1674 processTrEndTagForInRow();
1677 if (token->name() == tableTag) {
1678 if (!processTrEndTagForInRow()) {
1679 ASSERT(isParsingFragmentOrTemplateContents());
1682 ASSERT(insertionMode() == InTableBodyMode);
1683 processEndTag(token);
1686 if (isTableBodyContextTag(token->name())) {
1687 if (!m_tree.openElements()->inTableScope(token->name())) {
1691 processFakeEndTag(trTag);
1692 ASSERT(insertionMode() == InTableBodyMode);
1693 processEndTag(token);
1696 if (token->name() == bodyTag
1697 || isCaptionColOrColgroupTag(token->name())
1698 || token->name() == htmlTag
1699 || isTableCellContextTag(token->name())) {
1703 processEndTagForInTable(token);
1706 void HTMLTreeBuilder::processEndTagForInCell(AtomicHTMLToken* token)
1708 ASSERT(token->type() == HTMLToken::EndTag);
1709 if (isTableCellContextTag(token->name())) {
1710 if (!m_tree.openElements()->inTableScope(token->name())) {
1714 m_tree.generateImpliedEndTags();
1715 if (!m_tree.currentStackItem()->matchesHTMLTag(token->name()))
1717 m_tree.openElements()->popUntilPopped(token->name());
1718 m_tree.activeFormattingElements()->clearToLastMarker();
1719 setInsertionMode(InRowMode);
1722 if (token->name() == bodyTag
1723 || isCaptionColOrColgroupTag(token->name())
1724 || token->name() == htmlTag) {
1728 if (token->name() == tableTag
1729 || token->name() == trTag
1730 || isTableBodyContextTag(token->name())) {
1731 if (!m_tree.openElements()->inTableScope(token->name())) {
1732 ASSERT(isTableBodyContextTag(token->name()) || m_tree.openElements()->inTableScope(templateTag) || isParsingFragment());
1737 processEndTag(token);
1740 processEndTagForInBody(token);
1743 void HTMLTreeBuilder::processEndTagForInBody(AtomicHTMLToken* token)
1745 ASSERT(token->type() == HTMLToken::EndTag);
1746 if (token->name() == bodyTag) {
1747 processBodyEndTagForInBody(token);
1750 if (token->name() == htmlTag) {
1751 AtomicHTMLToken endBody(HTMLToken::EndTag, bodyTag.localName());
1752 if (processBodyEndTagForInBody(&endBody))
1753 processEndTag(token);
1756 if (token->name() == addressTag
1757 || token->name() == articleTag
1758 || token->name() == asideTag
1759 || token->name() == blockquoteTag
1760 || token->name() == buttonTag
1761 || token->name() == centerTag
1762 || token->name() == detailsTag
1763 || token->name() == dirTag
1764 || token->name() == divTag
1765 || token->name() == dlTag
1766 || token->name() == fieldsetTag
1767 || token->name() == figcaptionTag
1768 || token->name() == figureTag
1769 || token->name() == footerTag
1770 || token->name() == headerTag
1771 || token->name() == hgroupTag
1772 || token->name() == listingTag
1773 || token->name() == mainTag
1774 || token->name() == menuTag
1775 || token->name() == navTag
1776 || token->name() == olTag
1777 || token->name() == preTag
1778 || token->name() == sectionTag
1779 || token->name() == summaryTag
1780 || token->name() == ulTag) {
1781 if (!m_tree.openElements()->inScope(token->name())) {
1785 m_tree.generateImpliedEndTags();
1786 if (!m_tree.currentStackItem()->matchesHTMLTag(token->name()))
1788 m_tree.openElements()->popUntilPopped(token->name());
1791 if (token->name() == formTag) {
1792 RefPtrWillBeRawPtr<Element> node = m_tree.takeForm();
1793 if (!node || !m_tree.openElements()->inScope(node.get())) {
1797 m_tree.generateImpliedEndTags();
1798 if (m_tree.currentElement() != node.get())
1800 m_tree.openElements()->remove(node.get());
1802 if (token->name() == pTag) {
1803 if (!m_tree.openElements()->inButtonScope(token->name())) {
1805 processFakeStartTag(pTag);
1806 ASSERT(m_tree.openElements()->inScope(token->name()));
1807 processEndTag(token);
1810 m_tree.generateImpliedEndTagsWithExclusion(token->name());
1811 if (!m_tree.currentStackItem()->matchesHTMLTag(token->name()))
1813 m_tree.openElements()->popUntilPopped(token->name());
1816 if (token->name() == liTag) {
1817 if (!m_tree.openElements()->inListItemScope(token->name())) {
1821 m_tree.generateImpliedEndTagsWithExclusion(token->name());
1822 if (!m_tree.currentStackItem()->matchesHTMLTag(token->name()))
1824 m_tree.openElements()->popUntilPopped(token->name());
1827 if (token->name() == ddTag
1828 || token->name() == dtTag) {
1829 if (!m_tree.openElements()->inScope(token->name())) {
1833 m_tree.generateImpliedEndTagsWithExclusion(token->name());
1834 if (!m_tree.currentStackItem()->matchesHTMLTag(token->name()))
1836 m_tree.openElements()->popUntilPopped(token->name());
1839 if (isNumberedHeaderTag(token->name())) {
1840 if (!m_tree.openElements()->hasNumberedHeaderElementInScope()) {
1844 m_tree.generateImpliedEndTags();
1845 if (!m_tree.currentStackItem()->matchesHTMLTag(token->name()))
1847 m_tree.openElements()->popUntilNumberedHeaderElementPopped();
1850 if (isFormattingTag(token->name())) {
1851 callTheAdoptionAgency(token);
1854 if (token->name() == appletTag
1855 || token->name() == marqueeTag
1856 || token->name() == objectTag) {
1857 if (!m_tree.openElements()->inScope(token->name())) {
1861 m_tree.generateImpliedEndTags();
1862 if (!m_tree.currentStackItem()->matchesHTMLTag(token->name()))
1864 m_tree.openElements()->popUntilPopped(token->name());
1865 m_tree.activeFormattingElements()->clearToLastMarker();
1868 if (token->name() == brTag) {
1870 processFakeStartTag(brTag);
1873 if (token->name() == templateTag) {
1874 processTemplateEndTag(token);
1877 processAnyOtherEndTagForInBody(token);
1880 bool HTMLTreeBuilder::processCaptionEndTagForInCaption()
1882 if (!m_tree.openElements()->inTableScope(captionTag.localName())) {
1883 ASSERT(isParsingFragment());
1884 // FIXME: parse error
1887 m_tree.generateImpliedEndTags();
1888 // FIXME: parse error if (!m_tree.currentStackItem()->hasTagName(captionTag))
1889 m_tree.openElements()->popUntilPopped(captionTag.localName());
1890 m_tree.activeFormattingElements()->clearToLastMarker();
1891 setInsertionMode(InTableMode);
1895 bool HTMLTreeBuilder::processTrEndTagForInRow()
1897 if (!m_tree.openElements()->inTableScope(trTag)) {
1898 ASSERT(isParsingFragmentOrTemplateContents());
1899 // FIXME: parse error
1902 m_tree.openElements()->popUntilTableRowScopeMarker();
1903 ASSERT(m_tree.currentStackItem()->hasTagName(trTag));
1904 m_tree.openElements()->pop();
1905 setInsertionMode(InTableBodyMode);
1909 bool HTMLTreeBuilder::processTableEndTagForInTable()
1911 if (!m_tree.openElements()->inTableScope(tableTag)) {
1912 ASSERT(isParsingFragmentOrTemplateContents());
1913 // FIXME: parse error.
1916 m_tree.openElements()->popUntilPopped(tableTag.localName());
1917 resetInsertionModeAppropriately();
1921 void HTMLTreeBuilder::processEndTagForInTable(AtomicHTMLToken* token)
1923 ASSERT(token->type() == HTMLToken::EndTag);
1924 if (token->name() == tableTag) {
1925 processTableEndTagForInTable();
1928 if (token->name() == bodyTag
1929 || isCaptionColOrColgroupTag(token->name())
1930 || token->name() == htmlTag
1931 || isTableBodyContextTag(token->name())
1932 || isTableCellContextTag(token->name())
1933 || token->name() == trTag) {
1938 // Is this redirection necessary here?
1939 HTMLConstructionSite::RedirectToFosterParentGuard redirecter(m_tree);
1940 processEndTagForInBody(token);
1943 void HTMLTreeBuilder::processEndTag(AtomicHTMLToken* token)
1945 ASSERT(token->type() == HTMLToken::EndTag);
1946 switch (insertionMode()) {
1948 ASSERT(insertionMode() == InitialMode);
1949 defaultForInitial();
1951 case BeforeHTMLMode:
1952 ASSERT(insertionMode() == BeforeHTMLMode);
1953 if (token->name() != headTag && token->name() != bodyTag && token->name() != htmlTag && token->name() != brTag) {
1957 defaultForBeforeHTML();
1959 case BeforeHeadMode:
1960 ASSERT(insertionMode() == BeforeHeadMode);
1961 if (token->name() != headTag && token->name() != bodyTag && token->name() != htmlTag && token->name() != brTag) {
1965 defaultForBeforeHead();
1968 ASSERT(insertionMode() == InHeadMode);
1969 // FIXME: This case should be broken out into processEndTagForInHead,
1970 // because other end tag cases now refer to it ("process the token for using the rules of the "in head" insertion mode").
1971 // but because the logic falls through to AfterHeadMode, that gets a little messy.
1972 if (token->name() == templateTag) {
1973 processTemplateEndTag(token);
1976 if (token->name() == headTag) {
1977 m_tree.openElements()->popHTMLHeadElement();
1978 setInsertionMode(AfterHeadMode);
1981 if (token->name() != bodyTag && token->name() != htmlTag && token->name() != brTag) {
1988 ASSERT(insertionMode() == AfterHeadMode);
1989 if (token->name() != bodyTag && token->name() != htmlTag && token->name() != brTag) {
1993 defaultForAfterHead();
1996 ASSERT(insertionMode() == InBodyMode);
1997 processEndTagForInBody(token);
2000 ASSERT(insertionMode() == InTableMode);
2001 processEndTagForInTable(token);
2004 ASSERT(insertionMode() == InCaptionMode);
2005 if (token->name() == captionTag) {
2006 processCaptionEndTagForInCaption();
2009 if (token->name() == tableTag) {
2011 if (!processCaptionEndTagForInCaption()) {
2012 ASSERT(isParsingFragment());
2015 processEndTag(token);
2018 if (token->name() == bodyTag
2019 || token->name() == colTag
2020 || token->name() == colgroupTag
2021 || token->name() == htmlTag
2022 || isTableBodyContextTag(token->name())
2023 || isTableCellContextTag(token->name())
2024 || token->name() == trTag) {
2028 processEndTagForInBody(token);
2030 case InColumnGroupMode:
2031 ASSERT(insertionMode() == InColumnGroupMode);
2032 if (token->name() == colgroupTag) {
2033 processColgroupEndTagForInColumnGroup();
2036 if (token->name() == colTag) {
2040 if (token->name() == templateTag) {
2041 processTemplateEndTag(token);
2044 if (!processColgroupEndTagForInColumnGroup()) {
2045 ASSERT(isParsingFragmentOrTemplateContents());
2048 processEndTag(token);
2051 ASSERT(insertionMode() == InRowMode);
2052 processEndTagForInRow(token);
2055 ASSERT(insertionMode() == InCellMode);
2056 processEndTagForInCell(token);
2058 case InTableBodyMode:
2059 ASSERT(insertionMode() == InTableBodyMode);
2060 processEndTagForInTableBody(token);
2063 ASSERT(insertionMode() == AfterBodyMode);
2064 if (token->name() == htmlTag) {
2065 if (isParsingFragment()) {
2069 setInsertionMode(AfterAfterBodyMode);
2073 case AfterAfterBodyMode:
2074 ASSERT(insertionMode() == AfterBodyMode || insertionMode() == AfterAfterBodyMode);
2076 setInsertionMode(InBodyMode);
2077 processEndTag(token);
2079 case InHeadNoscriptMode:
2080 ASSERT(insertionMode() == InHeadNoscriptMode);
2081 if (token->name() == noscriptTag) {
2082 ASSERT(m_tree.currentStackItem()->hasTagName(noscriptTag));
2083 m_tree.openElements()->pop();
2084 ASSERT(m_tree.currentStackItem()->hasTagName(headTag));
2085 setInsertionMode(InHeadMode);
2088 if (token->name() != brTag) {
2092 defaultForInHeadNoscript();
2093 processToken(token);
2096 if (token->name() == scriptTag) {
2097 // Pause ourselves so that parsing stops until the script can be processed by the caller.
2098 ASSERT(m_tree.currentStackItem()->hasTagName(scriptTag));
2099 if (scriptingContentIsAllowed(m_tree.parserContentPolicy()))
2100 m_scriptToProcess = m_tree.currentElement();
2101 m_tree.openElements()->pop();
2102 setInsertionMode(m_originalInsertionMode);
2104 if (m_parser->tokenizer()) {
2105 // We must set the tokenizer's state to
2106 // DataState explicitly if the tokenizer didn't have a chance to.
2107 ASSERT(m_parser->tokenizer()->state() == HTMLTokenizer::DataState || m_options.useThreading);
2108 m_parser->tokenizer()->setState(HTMLTokenizer::DataState);
2112 m_tree.openElements()->pop();
2113 setInsertionMode(m_originalInsertionMode);
2115 case InFramesetMode:
2116 ASSERT(insertionMode() == InFramesetMode);
2117 if (token->name() == framesetTag) {
2118 bool ignoreFramesetForFragmentParsing = m_tree.currentIsRootNode();
2119 ignoreFramesetForFragmentParsing = ignoreFramesetForFragmentParsing || m_tree.openElements()->hasTemplateInHTMLScope();
2120 if (ignoreFramesetForFragmentParsing) {
2121 ASSERT(isParsingFragmentOrTemplateContents());
2125 m_tree.openElements()->pop();
2126 if (!isParsingFragment() && !m_tree.currentStackItem()->hasTagName(framesetTag))
2127 setInsertionMode(AfterFramesetMode);
2130 if (token->name() == templateTag) {
2131 processTemplateEndTag(token);
2135 case AfterFramesetMode:
2136 ASSERT(insertionMode() == AfterFramesetMode);
2137 if (token->name() == htmlTag) {
2138 setInsertionMode(AfterAfterFramesetMode);
2142 case AfterAfterFramesetMode:
2143 ASSERT(insertionMode() == AfterFramesetMode || insertionMode() == AfterAfterFramesetMode);
2146 case InSelectInTableMode:
2147 ASSERT(insertionMode() == InSelectInTableMode);
2148 if (token->name() == captionTag
2149 || token->name() == tableTag
2150 || isTableBodyContextTag(token->name())
2151 || token->name() == trTag
2152 || isTableCellContextTag(token->name())) {
2154 if (m_tree.openElements()->inTableScope(token->name())) {
2155 AtomicHTMLToken endSelect(HTMLToken::EndTag, selectTag.localName());
2156 processEndTag(&endSelect);
2157 processEndTag(token);
2163 ASSERT(insertionMode() == InSelectMode || insertionMode() == InSelectInTableMode);
2164 if (token->name() == optgroupTag) {
2165 if (m_tree.currentStackItem()->hasTagName(optionTag) && m_tree.oneBelowTop() && m_tree.oneBelowTop()->hasTagName(optgroupTag))
2166 processFakeEndTag(optionTag);
2167 if (m_tree.currentStackItem()->hasTagName(optgroupTag)) {
2168 m_tree.openElements()->pop();
2174 if (token->name() == optionTag) {
2175 if (m_tree.currentStackItem()->hasTagName(optionTag)) {
2176 m_tree.openElements()->pop();
2182 if (token->name() == selectTag) {
2183 if (!m_tree.openElements()->inSelectScope(token->name())) {
2184 ASSERT(isParsingFragment());
2188 m_tree.openElements()->popUntilPopped(selectTag.localName());
2189 resetInsertionModeAppropriately();
2192 if (token->name() == templateTag) {
2193 processTemplateEndTag(token);
2197 case InTableTextMode:
2198 defaultForInTableText();
2199 processEndTag(token);
2201 case TemplateContentsMode:
2202 if (token->name() == templateTag) {
2203 processTemplateEndTag(token);
2210 void HTMLTreeBuilder::processComment(AtomicHTMLToken* token)
2212 ASSERT(token->type() == HTMLToken::Comment);
2213 if (m_insertionMode == InitialMode
2214 || m_insertionMode == BeforeHTMLMode
2215 || m_insertionMode == AfterAfterBodyMode
2216 || m_insertionMode == AfterAfterFramesetMode) {
2217 m_tree.insertCommentOnDocument(token);
2220 if (m_insertionMode == AfterBodyMode) {
2221 m_tree.insertCommentOnHTMLHtmlElement(token);
2224 if (m_insertionMode == InTableTextMode) {
2225 defaultForInTableText();
2226 processComment(token);
2229 m_tree.insertComment(token);
2232 void HTMLTreeBuilder::processCharacter(AtomicHTMLToken* token)
2234 ASSERT(token->type() == HTMLToken::Character);
2235 CharacterTokenBuffer buffer(token);
2236 processCharacterBuffer(buffer);
2239 void HTMLTreeBuilder::processCharacterBuffer(CharacterTokenBuffer& buffer)
2242 // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#parsing-main-inbody
2243 // Note that this logic is different than the generic \r\n collapsing
2244 // handled in the input stream preprocessor. This logic is here as an
2245 // "authoring convenience" so folks can write:
2252 // without getting an extra newline at the start of their <pre> element.
2253 if (m_shouldSkipLeadingNewline) {
2254 m_shouldSkipLeadingNewline = false;
2255 buffer.skipAtMostOneLeadingNewline();
2256 if (buffer.isEmpty())
2260 switch (insertionMode()) {
2262 ASSERT(insertionMode() == InitialMode);
2263 buffer.skipLeadingWhitespace();
2264 if (buffer.isEmpty())
2266 defaultForInitial();
2269 case BeforeHTMLMode: {
2270 ASSERT(insertionMode() == BeforeHTMLMode);
2271 buffer.skipLeadingWhitespace();
2272 if (buffer.isEmpty())
2274 defaultForBeforeHTML();
2277 case BeforeHeadMode: {
2278 ASSERT(insertionMode() == BeforeHeadMode);
2279 buffer.skipLeadingWhitespace();
2280 if (buffer.isEmpty())
2282 defaultForBeforeHead();
2286 ASSERT(insertionMode() == InHeadMode);
2287 String leadingWhitespace = buffer.takeLeadingWhitespace();
2288 if (!leadingWhitespace.isEmpty())
2289 m_tree.insertTextNode(leadingWhitespace, AllWhitespace);
2290 if (buffer.isEmpty())
2295 case AfterHeadMode: {
2296 ASSERT(insertionMode() == AfterHeadMode);
2297 String leadingWhitespace = buffer.takeLeadingWhitespace();
2298 if (!leadingWhitespace.isEmpty())
2299 m_tree.insertTextNode(leadingWhitespace, AllWhitespace);
2300 if (buffer.isEmpty())
2302 defaultForAfterHead();
2307 case TemplateContentsMode:
2309 ASSERT(insertionMode() == InBodyMode || insertionMode() == InCaptionMode || insertionMode() == InCellMode || insertionMode() == TemplateContentsMode);
2310 processCharacterBufferForInBody(buffer);
2314 case InTableBodyMode:
2316 ASSERT(insertionMode() == InTableMode || insertionMode() == InTableBodyMode || insertionMode() == InRowMode);
2317 ASSERT(m_pendingTableCharacters.isEmpty());
2318 if (m_tree.currentStackItem()->isElementNode()
2319 && (m_tree.currentStackItem()->hasTagName(tableTag)
2320 || m_tree.currentStackItem()->hasTagName(tbodyTag)
2321 || m_tree.currentStackItem()->hasTagName(tfootTag)
2322 || m_tree.currentStackItem()->hasTagName(theadTag)
2323 || m_tree.currentStackItem()->hasTagName(trTag))) {
2324 m_originalInsertionMode = m_insertionMode;
2325 setInsertionMode(InTableTextMode);
2326 // Note that we fall through to the InTableTextMode case below.
2328 HTMLConstructionSite::RedirectToFosterParentGuard redirecter(m_tree);
2329 processCharacterBufferForInBody(buffer);
2334 case InTableTextMode: {
2335 buffer.giveRemainingTo(m_pendingTableCharacters);
2338 case InColumnGroupMode: {
2339 ASSERT(insertionMode() == InColumnGroupMode);
2340 String leadingWhitespace = buffer.takeLeadingWhitespace();
2341 if (!leadingWhitespace.isEmpty())
2342 m_tree.insertTextNode(leadingWhitespace, AllWhitespace);
2343 if (buffer.isEmpty())
2345 if (!processColgroupEndTagForInColumnGroup()) {
2346 ASSERT(isParsingFragmentOrTemplateContents());
2347 // The spec tells us to drop these characters on the floor.
2348 buffer.skipLeadingNonWhitespace();
2349 if (buffer.isEmpty())
2352 goto ReprocessBuffer;
2355 case AfterAfterBodyMode: {
2356 ASSERT(insertionMode() == AfterBodyMode || insertionMode() == AfterAfterBodyMode);
2357 // FIXME: parse error
2358 setInsertionMode(InBodyMode);
2359 goto ReprocessBuffer;
2362 ASSERT(insertionMode() == TextMode);
2363 m_tree.insertTextNode(buffer.takeRemaining());
2366 case InHeadNoscriptMode: {
2367 ASSERT(insertionMode() == InHeadNoscriptMode);
2368 String leadingWhitespace = buffer.takeLeadingWhitespace();
2369 if (!leadingWhitespace.isEmpty())
2370 m_tree.insertTextNode(leadingWhitespace, AllWhitespace);
2371 if (buffer.isEmpty())
2373 defaultForInHeadNoscript();
2374 goto ReprocessBuffer;
2376 case InFramesetMode:
2377 case AfterFramesetMode: {
2378 ASSERT(insertionMode() == InFramesetMode || insertionMode() == AfterFramesetMode || insertionMode() == AfterAfterFramesetMode);
2379 String leadingWhitespace = buffer.takeRemainingWhitespace();
2380 if (!leadingWhitespace.isEmpty())
2381 m_tree.insertTextNode(leadingWhitespace, AllWhitespace);
2382 // FIXME: We should generate a parse error if we skipped over any
2383 // non-whitespace characters.
2386 case InSelectInTableMode:
2387 case InSelectMode: {
2388 ASSERT(insertionMode() == InSelectMode || insertionMode() == InSelectInTableMode);
2389 m_tree.insertTextNode(buffer.takeRemaining());
2392 case AfterAfterFramesetMode: {
2393 String leadingWhitespace = buffer.takeRemainingWhitespace();
2394 if (!leadingWhitespace.isEmpty()) {
2395 m_tree.reconstructTheActiveFormattingElements();
2396 m_tree.insertTextNode(leadingWhitespace, AllWhitespace);
2398 // FIXME: We should generate a parse error if we skipped over any
2399 // non-whitespace characters.
2405 void HTMLTreeBuilder::processCharacterBufferForInBody(CharacterTokenBuffer& buffer)
2407 m_tree.reconstructTheActiveFormattingElements();
2408 const String& characters = buffer.takeRemaining();
2409 m_tree.insertTextNode(characters);
2410 if (m_framesetOk && !isAllWhitespaceOrReplacementCharacters(characters))
2411 m_framesetOk = false;
2414 void HTMLTreeBuilder::processEndOfFile(AtomicHTMLToken* token)
2416 ASSERT(token->type() == HTMLToken::EndOfFile);
2417 switch (insertionMode()) {
2419 ASSERT(insertionMode() == InitialMode);
2420 defaultForInitial();
2422 case BeforeHTMLMode:
2423 ASSERT(insertionMode() == BeforeHTMLMode);
2424 defaultForBeforeHTML();
2426 case BeforeHeadMode:
2427 ASSERT(insertionMode() == BeforeHeadMode);
2428 defaultForBeforeHead();
2431 ASSERT(insertionMode() == InHeadMode);
2435 ASSERT(insertionMode() == AfterHeadMode);
2436 defaultForAfterHead();
2442 ASSERT(insertionMode() == InBodyMode || insertionMode() == InCellMode || insertionMode() == InCaptionMode || insertionMode() == InRowMode || insertionMode() == TemplateContentsMode);
2443 notImplemented(); // Emit parse error based on what elements are still open.
2444 if (!m_templateInsertionModes.isEmpty() && processEndOfFileForInTemplateContents(token))
2448 case AfterAfterBodyMode:
2449 ASSERT(insertionMode() == AfterBodyMode || insertionMode() == AfterAfterBodyMode);
2451 case InHeadNoscriptMode:
2452 ASSERT(insertionMode() == InHeadNoscriptMode);
2453 defaultForInHeadNoscript();
2454 processEndOfFile(token);
2456 case AfterFramesetMode:
2457 case AfterAfterFramesetMode:
2458 ASSERT(insertionMode() == AfterFramesetMode || insertionMode() == AfterAfterFramesetMode);
2460 case InColumnGroupMode:
2461 if (m_tree.currentIsRootNode()) {
2462 ASSERT(isParsingFragment());
2463 return; // FIXME: Should we break here instead of returning?
2465 ASSERT(m_tree.currentNode()->hasTagName(colgroupTag) || isHTMLTemplateElement(m_tree.currentNode()));
2466 processColgroupEndTagForInColumnGroup();
2468 case InFramesetMode:
2470 case InTableBodyMode:
2471 case InSelectInTableMode:
2473 ASSERT(insertionMode() == InSelectMode || insertionMode() == InSelectInTableMode || insertionMode() == InTableMode || insertionMode() == InFramesetMode || insertionMode() == InTableBodyMode || insertionMode() == InColumnGroupMode);
2474 if (m_tree.currentNode() != m_tree.openElements()->rootNode())
2476 if (!m_templateInsertionModes.isEmpty() && processEndOfFileForInTemplateContents(token))
2479 case InTableTextMode:
2480 defaultForInTableText();
2481 processEndOfFile(token);
2485 if (m_tree.currentStackItem()->hasTagName(scriptTag))
2486 notImplemented(); // mark the script element as "already started".
2487 m_tree.openElements()->pop();
2488 ASSERT(m_originalInsertionMode != TextMode);
2489 setInsertionMode(m_originalInsertionMode);
2490 processEndOfFile(token);
2492 case TemplateContentsMode:
2493 if (processEndOfFileForInTemplateContents(token))
2497 m_tree.processEndOfFile();
2500 void HTMLTreeBuilder::defaultForInitial()
2503 m_tree.setDefaultCompatibilityMode();
2504 // FIXME: parse error
2505 setInsertionMode(BeforeHTMLMode);
2508 void HTMLTreeBuilder::defaultForBeforeHTML()
2510 AtomicHTMLToken startHTML(HTMLToken::StartTag, htmlTag.localName());
2511 m_tree.insertHTMLHtmlStartTagBeforeHTML(&startHTML);
2512 setInsertionMode(BeforeHeadMode);
2515 void HTMLTreeBuilder::defaultForBeforeHead()
2517 AtomicHTMLToken startHead(HTMLToken::StartTag, headTag.localName());
2518 processStartTag(&startHead);
2521 void HTMLTreeBuilder::defaultForInHead()
2523 AtomicHTMLToken endHead(HTMLToken::EndTag, headTag.localName());
2524 processEndTag(&endHead);
2527 void HTMLTreeBuilder::defaultForInHeadNoscript()
2529 AtomicHTMLToken endNoscript(HTMLToken::EndTag, noscriptTag.localName());
2530 processEndTag(&endNoscript);
2533 void HTMLTreeBuilder::defaultForAfterHead()
2535 AtomicHTMLToken startBody(HTMLToken::StartTag, bodyTag.localName());
2536 processStartTag(&startBody);
2537 m_framesetOk = true;
2540 void HTMLTreeBuilder::defaultForInTableText()
2542 String characters = m_pendingTableCharacters.toString();
2543 m_pendingTableCharacters.clear();
2544 if (!isAllWhitespace(characters)) {
2545 // FIXME: parse error
2546 HTMLConstructionSite::RedirectToFosterParentGuard redirecter(m_tree);
2547 m_tree.reconstructTheActiveFormattingElements();
2548 m_tree.insertTextNode(characters, NotAllWhitespace);
2549 m_framesetOk = false;
2550 setInsertionMode(m_originalInsertionMode);
2553 m_tree.insertTextNode(characters);
2554 setInsertionMode(m_originalInsertionMode);
2557 bool HTMLTreeBuilder::processStartTagForInHead(AtomicHTMLToken* token)
2559 ASSERT(token->type() == HTMLToken::StartTag);
2560 if (token->name() == htmlTag) {
2561 processHtmlStartTagForInBody(token);
2564 if (token->name() == baseTag
2565 || token->name() == basefontTag
2566 || token->name() == bgsoundTag
2567 || token->name() == commandTag
2568 || token->name() == linkTag
2569 || token->name() == metaTag) {
2570 m_tree.insertSelfClosingHTMLElement(token);
2571 // Note: The custom processing for the <meta> tag is done in HTMLMetaElement::process().
2574 if (token->name() == titleTag) {
2575 processGenericRCDATAStartTag(token);
2578 if (token->name() == noscriptTag) {
2579 if (m_options.scriptEnabled) {
2580 processGenericRawTextStartTag(token);
2583 m_tree.insertHTMLElement(token);
2584 setInsertionMode(InHeadNoscriptMode);
2587 if (token->name() == noframesTag || token->name() == styleTag) {
2588 processGenericRawTextStartTag(token);
2591 if (token->name() == scriptTag) {
2592 processScriptStartTag(token);
2595 if (token->name() == templateTag) {
2596 processTemplateStartTag(token);
2599 if (token->name() == headTag) {
2606 void HTMLTreeBuilder::processGenericRCDATAStartTag(AtomicHTMLToken* token)
2608 ASSERT(token->type() == HTMLToken::StartTag);
2609 m_tree.insertHTMLElement(token);
2610 if (m_parser->tokenizer())
2611 m_parser->tokenizer()->setState(HTMLTokenizer::RCDATAState);
2612 m_originalInsertionMode = m_insertionMode;
2613 setInsertionMode(TextMode);
2616 void HTMLTreeBuilder::processGenericRawTextStartTag(AtomicHTMLToken* token)
2618 ASSERT(token->type() == HTMLToken::StartTag);
2619 m_tree.insertHTMLElement(token);
2620 if (m_parser->tokenizer())
2621 m_parser->tokenizer()->setState(HTMLTokenizer::RAWTEXTState);
2622 m_originalInsertionMode = m_insertionMode;
2623 setInsertionMode(TextMode);
2626 void HTMLTreeBuilder::processScriptStartTag(AtomicHTMLToken* token)
2628 ASSERT(token->type() == HTMLToken::StartTag);
2629 m_tree.insertScriptElement(token);
2630 if (m_parser->tokenizer())
2631 m_parser->tokenizer()->setState(HTMLTokenizer::ScriptDataState);
2632 m_originalInsertionMode = m_insertionMode;
2634 TextPosition position = m_parser->textPosition();
2636 m_scriptToProcessStartPosition = position;
2638 setInsertionMode(TextMode);
2641 // http://www.whatwg.org/specs/web-apps/current-work/multipage/tree-construction.html#tree-construction
2642 bool HTMLTreeBuilder::shouldProcessTokenInForeignContent(AtomicHTMLToken* token)
2644 if (m_tree.isEmpty())
2646 HTMLStackItem* adjustedCurrentNode = adjustedCurrentStackItem();
2648 if (adjustedCurrentNode->isInHTMLNamespace())
2650 if (HTMLElementStack::isMathMLTextIntegrationPoint(adjustedCurrentNode)) {
2651 if (token->type() == HTMLToken::StartTag
2652 && token->name() != MathMLNames::mglyphTag
2653 && token->name() != MathMLNames::malignmarkTag)
2655 if (token->type() == HTMLToken::Character)
2658 if (adjustedCurrentNode->hasTagName(MathMLNames::annotation_xmlTag)
2659 && token->type() == HTMLToken::StartTag
2660 && token->name() == SVGNames::svgTag)
2662 if (HTMLElementStack::isHTMLIntegrationPoint(adjustedCurrentNode)) {
2663 if (token->type() == HTMLToken::StartTag)
2665 if (token->type() == HTMLToken::Character)
2668 if (token->type() == HTMLToken::EndOfFile)
2673 void HTMLTreeBuilder::processTokenInForeignContent(AtomicHTMLToken* token)
2675 if (token->type() == HTMLToken::Character) {
2676 const String& characters = token->characters();
2677 m_tree.insertTextNode(characters);
2678 if (m_framesetOk && !isAllWhitespaceOrReplacementCharacters(characters))
2679 m_framesetOk = false;
2684 HTMLStackItem* adjustedCurrentNode = adjustedCurrentStackItem();
2686 switch (token->type()) {
2687 case HTMLToken::Uninitialized:
2688 ASSERT_NOT_REACHED();
2690 case HTMLToken::DOCTYPE:
2693 case HTMLToken::StartTag: {
2694 if (token->name() == bTag
2695 || token->name() == bigTag
2696 || token->name() == blockquoteTag
2697 || token->name() == bodyTag
2698 || token->name() == brTag
2699 || token->name() == centerTag
2700 || token->name() == codeTag
2701 || token->name() == ddTag
2702 || token->name() == divTag
2703 || token->name() == dlTag
2704 || token->name() == dtTag
2705 || token->name() == emTag
2706 || token->name() == embedTag
2707 || isNumberedHeaderTag(token->name())
2708 || token->name() == headTag
2709 || token->name() == hrTag
2710 || token->name() == iTag
2711 || token->name() == imgTag
2712 || token->name() == liTag
2713 || token->name() == listingTag
2714 || token->name() == menuTag
2715 || token->name() == metaTag
2716 || token->name() == nobrTag
2717 || token->name() == olTag
2718 || token->name() == pTag
2719 || token->name() == preTag
2720 || token->name() == rubyTag
2721 || token->name() == sTag
2722 || token->name() == smallTag
2723 || token->name() == spanTag
2724 || token->name() == strongTag
2725 || token->name() == strikeTag
2726 || token->name() == subTag
2727 || token->name() == supTag
2728 || token->name() == tableTag
2729 || token->name() == ttTag
2730 || token->name() == uTag
2731 || token->name() == ulTag
2732 || token->name() == varTag
2733 || (token->name() == fontTag && (token->getAttributeItem(colorAttr) || token->getAttributeItem(faceAttr) || token->getAttributeItem(sizeAttr)))) {
2735 m_tree.openElements()->popUntilForeignContentScopeMarker();
2736 processStartTag(token);
2739 const AtomicString& currentNamespace = adjustedCurrentNode->namespaceURI();
2740 if (currentNamespace == MathMLNames::mathmlNamespaceURI)
2741 adjustMathMLAttributes(token);
2742 if (currentNamespace == SVGNames::svgNamespaceURI) {
2743 adjustSVGTagNameCase(token);
2744 adjustSVGAttributes(token);
2746 adjustForeignAttributes(token);
2747 m_tree.insertForeignElement(token, currentNamespace);
2750 case HTMLToken::EndTag: {
2751 if (adjustedCurrentNode->namespaceURI() == SVGNames::svgNamespaceURI)
2752 adjustSVGTagNameCase(token);
2754 if (token->name() == SVGNames::scriptTag && m_tree.currentStackItem()->hasTagName(SVGNames::scriptTag)) {
2755 if (scriptingContentIsAllowed(m_tree.parserContentPolicy()))
2756 m_scriptToProcess = m_tree.currentElement();
2757 m_tree.openElements()->pop();
2760 if (!m_tree.currentStackItem()->isInHTMLNamespace()) {
2761 // FIXME: This code just wants an Element* iterator, instead of an ElementRecord*
2762 HTMLElementStack::ElementRecord* nodeRecord = m_tree.openElements()->topRecord();
2763 if (!nodeRecord->stackItem()->hasLocalName(token->name()))
2766 if (nodeRecord->stackItem()->hasLocalName(token->name())) {
2767 m_tree.openElements()->popUntilPopped(nodeRecord->element());
2770 nodeRecord = nodeRecord->next();
2772 if (nodeRecord->stackItem()->isInHTMLNamespace())
2776 // Otherwise, process the token according to the rules given in the section corresponding to the current insertion mode in HTML content.
2777 processEndTag(token);
2780 case HTMLToken::Comment:
2781 m_tree.insertComment(token);
2783 case HTMLToken::Character:
2784 case HTMLToken::EndOfFile:
2785 ASSERT_NOT_REACHED();
2790 void HTMLTreeBuilder::finished()
2792 if (isParsingFragment())
2795 ASSERT(m_templateInsertionModes.isEmpty());
2796 ASSERT(m_isAttached);
2797 // Warning, this may detach the parser. Do not do anything else after this.
2798 m_tree.finishedParsing();
2801 void HTMLTreeBuilder::parseError(AtomicHTMLToken*)
2805 } // namespace blink