2 * Copyright (C) 2010 Google, Inc. All Rights Reserved.
3 * Copyright (C) 2011, 2015 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 "HTMLTreeBuilder.h"
30 #include "DocumentFragment.h"
31 #include "HTMLDocument.h"
32 #include "HTMLDocumentParser.h"
33 #include "HTMLFormControlElement.h"
34 #include "HTMLFormElement.h"
35 #include "HTMLOptGroupElement.h"
36 #include "HTMLParserIdioms.h"
37 #include "LocalizedStrings.h"
38 #include "NotImplemented.h"
39 #include "XLinkNames.h"
40 #include "XMLNSNames.h"
42 #include <wtf/NeverDestroyed.h>
43 #include <wtf/unicode/CharacterNames.h>
45 #if ENABLE(TELEPHONE_NUMBER_DETECTION) && PLATFORM(IOS)
46 #include "TelephoneNumberDetector.h"
51 using namespace HTMLNames;
55 inline bool isHTMLSpaceOrReplacementCharacter(UChar character)
57 return isHTMLSpace(character) || character == replacementCharacter;
62 static inline TextPosition uninitializedPositionValue1()
64 return TextPosition(OrdinalNumber::fromOneBasedInt(-1), OrdinalNumber::first());
67 static inline bool isAllWhitespace(const String& string)
69 return string.isAllSpecialCharacters<isHTMLSpace>();
72 static inline bool isAllWhitespaceOrReplacementCharacters(const String& string)
74 return string.isAllSpecialCharacters<isHTMLSpaceOrReplacementCharacter>();
77 static bool isNumberedHeaderTag(const AtomicString& tagName)
79 return tagName == h1Tag
87 static bool isCaptionColOrColgroupTag(const AtomicString& tagName)
89 return tagName == captionTag || tagName == colTag || tagName == colgroupTag;
92 static bool isTableCellContextTag(const AtomicString& tagName)
94 return tagName == thTag || tagName == tdTag;
97 static bool isTableBodyContextTag(const AtomicString& tagName)
99 return tagName == tbodyTag || tagName == tfootTag || tagName == theadTag;
102 static bool isNonAnchorNonNobrFormattingTag(const AtomicString& tagName)
104 return tagName == bTag
106 || tagName == codeTag
108 || tagName == fontTag
111 || tagName == smallTag
112 || tagName == strikeTag
113 || tagName == strongTag
118 static bool isNonAnchorFormattingTag(const AtomicString& tagName)
120 return tagName == nobrTag || isNonAnchorNonNobrFormattingTag(tagName);
123 // https://html.spec.whatwg.org/multipage/syntax.html#formatting
124 bool HTMLConstructionSite::isFormattingTag(const AtomicString& tagName)
126 return tagName == aTag || isNonAnchorFormattingTag(tagName);
129 class HTMLTreeBuilder::ExternalCharacterTokenBuffer {
131 explicit ExternalCharacterTokenBuffer(AtomicHTMLToken& token)
132 : m_text(token.characters(), token.charactersLength())
133 , m_isAll8BitData(token.charactersIsAll8BitData())
138 explicit ExternalCharacterTokenBuffer(const String& string)
140 , m_isAll8BitData(m_text.is8Bit())
145 ~ExternalCharacterTokenBuffer()
150 bool isEmpty() const { return m_text.isEmpty(); }
152 bool isAll8BitData() const { return m_isAll8BitData; }
154 void skipAtMostOneLeadingNewline()
157 if (m_text[0] == '\n')
158 m_text = m_text.substring(1);
161 void skipLeadingWhitespace()
163 skipLeading<isHTMLSpace>();
166 String takeLeadingWhitespace()
168 return takeLeading<isHTMLSpace>();
171 void skipLeadingNonWhitespace()
173 skipLeading<isNotHTMLSpace>();
176 String takeRemaining()
178 String result = makeString(m_text);
179 m_text = StringView();
183 void giveRemainingTo(StringBuilder& recipient)
185 recipient.append(m_text);
186 m_text = StringView();
189 String takeRemainingWhitespace()
192 Vector<LChar, 8> whitespace;
194 UChar character = m_text[0];
195 if (isHTMLSpace(character))
196 whitespace.append(character);
197 m_text = m_text.substring(1);
198 } while (!m_text.isEmpty());
200 // Returning the null string when there aren't any whitespace
201 // characters is slightly cleaner semantically because we don't want
202 // to insert a text node (as opposed to inserting an empty text node).
203 if (whitespace.isEmpty())
206 return String::adopt(whitespace);
210 template<bool characterPredicate(UChar)> void skipLeading()
213 while (characterPredicate(m_text[0])) {
214 m_text = m_text.substring(1);
215 if (m_text.isEmpty())
220 template<bool characterPredicate(UChar)> String takeLeading()
223 StringView start = m_text;
224 skipLeading<characterPredicate>();
225 if (start.length() == m_text.length())
227 return makeString(start.substring(0, start.length() - m_text.length()));
230 String makeString(StringView stringView) const
232 if (stringView.is8Bit() || !isAll8BitData())
233 return stringView.toString();
234 return String::make8BitFrom16BitSource(stringView.characters16(), stringView.length());
238 bool m_isAll8BitData;
241 inline bool HTMLTreeBuilder::isParsingTemplateContents() const
243 #if ENABLE(TEMPLATE_ELEMENT)
244 return m_tree.openElements().hasTemplateInHTMLScope();
250 inline bool HTMLTreeBuilder::isParsingFragmentOrTemplateContents() const
252 return isParsingFragment() || isParsingTemplateContents();
255 HTMLTreeBuilder::HTMLTreeBuilder(HTMLDocumentParser& parser, HTMLDocument& document, ParserContentPolicy parserContentPolicy, const HTMLParserOptions& options)
258 , m_tree(document, parserContentPolicy, options.maximumDOMTreeDepth)
259 , m_scriptToProcessStartPosition(uninitializedPositionValue1())
262 m_destructionProhibited = false;
266 HTMLTreeBuilder::HTMLTreeBuilder(HTMLDocumentParser& parser, DocumentFragment& fragment, Element& contextElement, ParserContentPolicy parserContentPolicy, const HTMLParserOptions& options)
269 , m_fragmentContext(fragment, contextElement)
270 , m_tree(fragment, parserContentPolicy, options.maximumDOMTreeDepth)
271 , m_scriptToProcessStartPosition(uninitializedPositionValue1())
273 ASSERT(isMainThread());
275 // https://html.spec.whatwg.org/multipage/syntax.html#parsing-html-fragments
276 // For efficiency, we skip step 5 ("Let root be a new html element with no attributes") and instead use the DocumentFragment as a root node.
277 m_tree.openElements().pushRootNode(HTMLStackItem::create(fragment));
279 #if ENABLE(TEMPLATE_ELEMENT)
280 if (contextElement.hasTagName(templateTag))
281 m_templateInsertionModes.append(InsertionMode::TemplateContents);
284 resetInsertionModeAppropriately();
286 m_tree.setForm(is<HTMLFormElement>(contextElement) ? &downcast<HTMLFormElement>(contextElement) : HTMLFormElement::findClosestFormAncestor(contextElement));
289 m_destructionProhibited = false;
293 HTMLTreeBuilder::FragmentParsingContext::FragmentParsingContext()
297 HTMLTreeBuilder::FragmentParsingContext::FragmentParsingContext(DocumentFragment& fragment, Element& contextElement)
298 : m_fragment(&fragment)
300 ASSERT(!fragment.hasChildNodes());
301 m_contextElementStackItem = HTMLStackItem::create(contextElement);
304 inline Element& HTMLTreeBuilder::FragmentParsingContext::contextElement() const
306 return contextElementStackItem().element();
309 inline HTMLStackItem& HTMLTreeBuilder::FragmentParsingContext::contextElementStackItem() const
312 return *m_contextElementStackItem;
315 RefPtr<Element> HTMLTreeBuilder::takeScriptToProcess(TextPosition& scriptStartPosition)
317 ASSERT(!m_destroyed);
319 if (!m_scriptToProcess)
322 // Unpause ourselves, callers may pause us again when processing the script.
323 // The HTML5 spec is written as though scripts are executed inside the tree builder.
324 // We pause the parser to exit the tree builder, and then resume before running scripts.
325 scriptStartPosition = m_scriptToProcessStartPosition;
326 m_scriptToProcessStartPosition = uninitializedPositionValue1();
327 return WTFMove(m_scriptToProcess);
330 void HTMLTreeBuilder::constructTree(AtomicHTMLToken& token)
333 ASSERT(!m_destroyed);
334 ASSERT(!m_destructionProhibited);
335 m_destructionProhibited = true;
338 if (shouldProcessTokenInForeignContent(token))
339 processTokenInForeignContent(token);
343 bool inForeignContent = !m_tree.isEmpty()
344 && !isInHTMLNamespace(adjustedCurrentStackItem())
345 && !HTMLElementStack::isHTMLIntegrationPoint(m_tree.currentStackItem())
346 && !HTMLElementStack::isMathMLTextIntegrationPoint(m_tree.currentStackItem());
348 m_parser.tokenizer().setForceNullCharacterReplacement(m_insertionMode == InsertionMode::Text || inForeignContent);
349 m_parser.tokenizer().setShouldAllowCDATA(inForeignContent);
352 m_destructionProhibited = false;
355 m_tree.executeQueuedTasks();
356 // The tree builder might have been destroyed as an indirect result of executing the queued tasks.
359 void HTMLTreeBuilder::processToken(AtomicHTMLToken& token)
361 switch (token.type()) {
362 case HTMLToken::Uninitialized:
363 ASSERT_NOT_REACHED();
365 case HTMLToken::DOCTYPE:
366 m_shouldSkipLeadingNewline = false;
367 processDoctypeToken(token);
369 case HTMLToken::StartTag:
370 m_shouldSkipLeadingNewline = false;
371 processStartTag(token);
373 case HTMLToken::EndTag:
374 m_shouldSkipLeadingNewline = false;
375 processEndTag(token);
377 case HTMLToken::Comment:
378 m_shouldSkipLeadingNewline = false;
379 processComment(token);
381 case HTMLToken::Character:
382 processCharacter(token);
384 case HTMLToken::EndOfFile:
385 m_shouldSkipLeadingNewline = false;
386 processEndOfFile(token);
391 void HTMLTreeBuilder::processDoctypeToken(AtomicHTMLToken& token)
393 ASSERT(token.type() == HTMLToken::DOCTYPE);
394 if (m_insertionMode == InsertionMode::Initial) {
395 m_tree.insertDoctype(&token);
396 m_insertionMode = InsertionMode::BeforeHTML;
399 if (m_insertionMode == InsertionMode::InTableText) {
400 defaultForInTableText();
401 processDoctypeToken(token);
407 void HTMLTreeBuilder::processFakeStartTag(const QualifiedName& tagName, Vector<Attribute>&& attributes)
409 // FIXME: We'll need a fancier conversion than just "localName" for SVG/MathML tags.
410 AtomicHTMLToken fakeToken(HTMLToken::StartTag, tagName.localName(), WTFMove(attributes));
411 processStartTag(fakeToken);
414 void HTMLTreeBuilder::processFakeEndTag(const AtomicString& tagName)
416 AtomicHTMLToken fakeToken(HTMLToken::EndTag, tagName);
417 processEndTag(fakeToken);
420 void HTMLTreeBuilder::processFakeEndTag(const QualifiedName& tagName)
422 // FIXME: We'll need a fancier conversion than just "localName" for SVG/MathML tags.
423 processFakeEndTag(tagName.localName());
426 void HTMLTreeBuilder::processFakeCharacters(const String& characters)
428 ASSERT(!characters.isEmpty());
429 ExternalCharacterTokenBuffer buffer(characters);
430 processCharacterBuffer(buffer);
433 void HTMLTreeBuilder::processFakePEndTagIfPInButtonScope()
435 if (!m_tree.openElements().inButtonScope(pTag.localName()))
437 AtomicHTMLToken endP(HTMLToken::EndTag, pTag.localName());
441 Vector<Attribute> HTMLTreeBuilder::attributesForIsindexInput(AtomicHTMLToken& token)
443 Vector<Attribute> attributes = token.attributes();
444 attributes.removeAllMatching([] (const Attribute& attribute) {
445 const QualifiedName& name = attribute.name();
446 return name.matches(nameAttr) || name.matches(actionAttr) || name.matches(promptAttr);
449 attributes.append(Attribute(nameAttr, isindexTag.localName()));
453 void HTMLTreeBuilder::processIsindexStartTagForInBody(AtomicHTMLToken& token)
455 ASSERT(token.type() == HTMLToken::StartTag);
456 ASSERT(token.name() == isindexTag);
458 if (m_tree.form() && !isParsingTemplateContents())
460 notImplemented(); // Acknowledge self-closing flag
461 processFakeStartTag(formTag);
462 if (Attribute* actionAttribute = findAttribute(token.attributes(), actionAttr))
463 m_tree.form()->setAttribute(actionAttr, actionAttribute->value());
464 processFakeStartTag(hrTag);
465 processFakeStartTag(labelTag);
466 if (Attribute* promptAttribute = findAttribute(token.attributes(), promptAttr))
467 processFakeCharacters(promptAttribute->value());
469 processFakeCharacters(searchableIndexIntroduction());
470 processFakeStartTag(inputTag, attributesForIsindexInput(token));
471 notImplemented(); // This second set of characters may be needed by non-english locales.
472 processFakeEndTag(labelTag);
473 processFakeStartTag(hrTag);
474 processFakeEndTag(formTag);
479 bool isLi(const HTMLStackItem& item)
481 return item.hasTagName(liTag);
484 bool isDdOrDt(const HTMLStackItem& item)
486 return item.hasTagName(ddTag) || item.hasTagName(dtTag);
491 template <bool shouldClose(const HTMLStackItem&)> void HTMLTreeBuilder::processCloseWhenNestedTag(AtomicHTMLToken& token)
493 m_framesetOk = false;
494 for (auto* nodeRecord = &m_tree.openElements().topRecord(); ; nodeRecord = nodeRecord->next()) {
495 HTMLStackItem& item = nodeRecord->stackItem();
496 if (shouldClose(item)) {
497 ASSERT(item.isElement());
498 processFakeEndTag(item.localName());
501 if (isSpecialNode(item) && !item.hasTagName(addressTag) && !item.hasTagName(divTag) && !item.hasTagName(pTag))
504 processFakePEndTagIfPInButtonScope();
505 m_tree.insertHTMLElement(&token);
508 template <typename TableQualifiedName> static HashMap<AtomicString, QualifiedName> createCaseMap(const TableQualifiedName* const names[], unsigned length)
510 HashMap<AtomicString, QualifiedName> map;
511 for (unsigned i = 0; i < length; ++i) {
512 const QualifiedName& name = *names[i];
513 const AtomicString& localName = name.localName();
514 AtomicString loweredLocalName = localName.lower();
515 if (loweredLocalName != localName)
516 map.add(loweredLocalName, name);
521 static void adjustSVGTagNameCase(AtomicHTMLToken& token)
523 static NeverDestroyed<HashMap<AtomicString, QualifiedName>> map = createCaseMap(SVGNames::getSVGTags(), SVGNames::SVGTagsCount);
524 const QualifiedName& casedName = map.get().get(token.name());
525 if (casedName.localName().isNull())
527 token.setName(casedName.localName());
530 static inline void adjustAttributes(HashMap<AtomicString, QualifiedName>& map, AtomicHTMLToken& token)
532 for (auto& attribute : token.attributes()) {
533 const QualifiedName& casedName = map.get(attribute.localName());
534 if (!casedName.localName().isNull())
535 attribute.parserSetName(casedName);
539 template<const QualifiedName* const* attributesTable(), unsigned attributesTableLength> static void adjustAttributes(AtomicHTMLToken& token)
541 static NeverDestroyed<HashMap<AtomicString, QualifiedName>> map = createCaseMap(attributesTable(), attributesTableLength);
542 adjustAttributes(map, token);
545 static inline void adjustSVGAttributes(AtomicHTMLToken& token)
547 adjustAttributes<SVGNames::getSVGAttrs, SVGNames::SVGAttrsCount>(token);
550 static inline void adjustMathMLAttributes(AtomicHTMLToken& token)
552 adjustAttributes<MathMLNames::getMathMLAttrs, MathMLNames::MathMLAttrsCount>(token);
555 static void addNamesWithPrefix(HashMap<AtomicString, QualifiedName>& map, const AtomicString& prefix, const QualifiedName* const names[], unsigned length)
557 for (unsigned i = 0; i < length; ++i) {
558 const QualifiedName& name = *names[i];
559 const AtomicString& localName = name.localName();
560 map.add(prefix + ':' + localName, QualifiedName(prefix, localName, name.namespaceURI()));
564 static HashMap<AtomicString, QualifiedName> createForeignAttributesMap()
566 HashMap<AtomicString, QualifiedName> map;
568 addNamesWithPrefix(map, xlinkAtom, XLinkNames::getXLinkAttrs(), XLinkNames::XLinkAttrsCount);
569 addNamesWithPrefix(map, xmlAtom, XMLNames::getXMLAttrs(), XMLNames::XMLAttrsCount);
571 map.add(WTF::xmlnsAtom, XMLNSNames::xmlnsAttr);
572 map.add("xmlns:xlink", QualifiedName(xmlnsAtom, xlinkAtom, XMLNSNames::xmlnsNamespaceURI));
577 static void adjustForeignAttributes(AtomicHTMLToken& token)
579 static NeverDestroyed<HashMap<AtomicString, QualifiedName>> map = createForeignAttributesMap();
580 adjustAttributes(map, token);
583 void HTMLTreeBuilder::processStartTagForInBody(AtomicHTMLToken& token)
585 ASSERT(token.type() == HTMLToken::StartTag);
586 if (token.name() == htmlTag) {
587 processHtmlStartTagForInBody(token);
590 if (token.name() == baseTag
591 || token.name() == basefontTag
592 || token.name() == bgsoundTag
593 || token.name() == commandTag
594 || token.name() == linkTag
595 || token.name() == metaTag
596 || token.name() == noframesTag
597 || token.name() == scriptTag
598 || token.name() == styleTag
599 || token.name() == titleTag) {
600 bool didProcess = processStartTagForInHead(token);
601 ASSERT_UNUSED(didProcess, didProcess);
604 if (token.name() == bodyTag) {
606 bool fragmentOrTemplateCase = !m_tree.openElements().secondElementIsHTMLBodyElement() || m_tree.openElements().hasOnlyOneElement();
607 #if ENABLE(TEMPLATE_ELEMENT)
608 fragmentOrTemplateCase = fragmentOrTemplateCase || m_tree.openElements().hasTemplateInHTMLScope();
610 if (fragmentOrTemplateCase) {
611 ASSERT(isParsingFragmentOrTemplateContents());
614 m_framesetOk = false;
615 m_tree.insertHTMLBodyStartTagInBody(&token);
618 if (token.name() == framesetTag) {
620 if (!m_tree.openElements().secondElementIsHTMLBodyElement() || m_tree.openElements().hasOnlyOneElement()) {
621 ASSERT(isParsingFragmentOrTemplateContents());
626 m_tree.openElements().bodyElement().remove(ASSERT_NO_EXCEPTION);
627 m_tree.openElements().popUntil(&m_tree.openElements().bodyElement());
628 m_tree.openElements().popHTMLBodyElement();
629 ASSERT(&m_tree.openElements().top() == &m_tree.openElements().htmlElement());
630 m_tree.insertHTMLElement(&token);
631 m_insertionMode = InsertionMode::InFrameset;
634 if (token.name() == addressTag
635 || token.name() == articleTag
636 || token.name() == asideTag
637 || token.name() == blockquoteTag
638 || token.name() == centerTag
639 || token.name() == detailsTag
640 || token.name() == dirTag
641 || token.name() == divTag
642 || token.name() == dlTag
643 || token.name() == fieldsetTag
644 || token.name() == figcaptionTag
645 || token.name() == figureTag
646 || token.name() == footerTag
647 || token.name() == headerTag
648 || token.name() == hgroupTag
649 || token.name() == mainTag
650 || token.name() == menuTag
651 || token.name() == navTag
652 || token.name() == olTag
653 || token.name() == pTag
654 || token.name() == sectionTag
655 || token.name() == summaryTag
656 || token.name() == ulTag) {
657 processFakePEndTagIfPInButtonScope();
658 m_tree.insertHTMLElement(&token);
661 if (isNumberedHeaderTag(token.name())) {
662 processFakePEndTagIfPInButtonScope();
663 if (isNumberedHeaderElement(m_tree.currentStackItem())) {
665 m_tree.openElements().pop();
667 m_tree.insertHTMLElement(&token);
670 if (token.name() == preTag || token.name() == listingTag) {
671 processFakePEndTagIfPInButtonScope();
672 m_tree.insertHTMLElement(&token);
673 m_shouldSkipLeadingNewline = true;
674 m_framesetOk = false;
677 if (token.name() == formTag) {
678 if (m_tree.form() && !isParsingTemplateContents()) {
682 processFakePEndTagIfPInButtonScope();
683 m_tree.insertHTMLFormElement(&token);
686 if (token.name() == liTag) {
687 processCloseWhenNestedTag<isLi>(token);
690 if (token.name() == ddTag || token.name() == dtTag) {
691 processCloseWhenNestedTag<isDdOrDt>(token);
694 if (token.name() == plaintextTag) {
695 processFakePEndTagIfPInButtonScope();
696 m_tree.insertHTMLElement(&token);
697 m_parser.tokenizer().setPLAINTEXTState();
700 if (token.name() == buttonTag) {
701 if (m_tree.openElements().inScope(buttonTag)) {
703 processFakeEndTag(buttonTag);
704 processStartTag(token); // FIXME: Could we just fall through here?
707 m_tree.reconstructTheActiveFormattingElements();
708 m_tree.insertHTMLElement(&token);
709 m_framesetOk = false;
712 if (token.name() == aTag) {
713 Element* activeATag = m_tree.activeFormattingElements().closestElementInScopeWithName(aTag.localName());
716 processFakeEndTag(aTag);
717 m_tree.activeFormattingElements().remove(activeATag);
718 if (m_tree.openElements().contains(activeATag))
719 m_tree.openElements().remove(activeATag);
721 m_tree.reconstructTheActiveFormattingElements();
722 m_tree.insertFormattingElement(&token);
725 if (isNonAnchorNonNobrFormattingTag(token.name())) {
726 m_tree.reconstructTheActiveFormattingElements();
727 m_tree.insertFormattingElement(&token);
730 if (token.name() == nobrTag) {
731 m_tree.reconstructTheActiveFormattingElements();
732 if (m_tree.openElements().inScope(nobrTag)) {
734 processFakeEndTag(nobrTag);
735 m_tree.reconstructTheActiveFormattingElements();
737 m_tree.insertFormattingElement(&token);
740 if (token.name() == appletTag || token.name() == embedTag || token.name() == objectTag) {
741 if (!pluginContentIsAllowed(m_tree.parserContentPolicy()))
744 if (token.name() == appletTag || token.name() == marqueeTag || token.name() == objectTag) {
745 m_tree.reconstructTheActiveFormattingElements();
746 m_tree.insertHTMLElement(&token);
747 m_tree.activeFormattingElements().appendMarker();
748 m_framesetOk = false;
751 if (token.name() == tableTag) {
752 if (!m_tree.inQuirksMode() && m_tree.openElements().inButtonScope(pTag))
753 processFakeEndTag(pTag);
754 m_tree.insertHTMLElement(&token);
755 m_framesetOk = false;
756 m_insertionMode = InsertionMode::InTable;
759 if (token.name() == imageTag) {
761 // Apparently we're not supposed to ask.
762 token.setName(imgTag.localName());
763 // Note the fall through to the imgTag handling below!
765 if (token.name() == areaTag
766 || token.name() == brTag
767 || token.name() == embedTag
768 || token.name() == imgTag
769 || token.name() == keygenTag
770 || token.name() == wbrTag) {
771 m_tree.reconstructTheActiveFormattingElements();
772 m_tree.insertSelfClosingHTMLElement(&token);
773 m_framesetOk = false;
776 if (token.name() == inputTag) {
777 m_tree.reconstructTheActiveFormattingElements();
778 m_tree.insertSelfClosingHTMLElement(&token);
779 Attribute* typeAttribute = findAttribute(token.attributes(), typeAttr);
780 if (!typeAttribute || !equalIgnoringCase(typeAttribute->value(), "hidden"))
781 m_framesetOk = false;
784 if (token.name() == paramTag || token.name() == sourceTag || token.name() == trackTag) {
785 m_tree.insertSelfClosingHTMLElement(&token);
788 if (token.name() == hrTag) {
789 processFakePEndTagIfPInButtonScope();
790 m_tree.insertSelfClosingHTMLElement(&token);
791 m_framesetOk = false;
794 if (token.name() == isindexTag) {
795 processIsindexStartTagForInBody(token);
798 if (token.name() == textareaTag) {
799 m_tree.insertHTMLElement(&token);
800 m_shouldSkipLeadingNewline = true;
801 m_parser.tokenizer().setRCDATAState();
802 m_originalInsertionMode = m_insertionMode;
803 m_framesetOk = false;
804 m_insertionMode = InsertionMode::Text;
807 if (token.name() == xmpTag) {
808 processFakePEndTagIfPInButtonScope();
809 m_tree.reconstructTheActiveFormattingElements();
810 m_framesetOk = false;
811 processGenericRawTextStartTag(token);
814 if (token.name() == iframeTag) {
815 m_framesetOk = false;
816 processGenericRawTextStartTag(token);
819 if (token.name() == noembedTag && m_options.pluginsEnabled) {
820 processGenericRawTextStartTag(token);
823 if (token.name() == noscriptTag && m_options.scriptEnabled) {
824 processGenericRawTextStartTag(token);
827 if (token.name() == selectTag) {
828 m_tree.reconstructTheActiveFormattingElements();
829 m_tree.insertHTMLElement(&token);
830 m_framesetOk = false;
831 if (m_insertionMode == InsertionMode::InTable
832 || m_insertionMode == InsertionMode::InCaption
833 || m_insertionMode == InsertionMode::InColumnGroup
834 || m_insertionMode == InsertionMode::InTableBody
835 || m_insertionMode == InsertionMode::InRow
836 || m_insertionMode == InsertionMode::InCell)
837 m_insertionMode = InsertionMode::InSelectInTable;
839 m_insertionMode = InsertionMode::InSelect;
842 if (token.name() == optgroupTag || token.name() == optionTag) {
843 if (is<HTMLOptionElement>(m_tree.currentStackItem().node())) {
844 AtomicHTMLToken endOption(HTMLToken::EndTag, optionTag.localName());
845 processEndTag(endOption);
847 m_tree.reconstructTheActiveFormattingElements();
848 m_tree.insertHTMLElement(&token);
851 if (token.name() == rbTag || token.name() == rtcTag) {
852 if (m_tree.openElements().inScope(rubyTag.localName())) {
853 m_tree.generateImpliedEndTags();
854 if (!m_tree.currentStackItem().hasTagName(rubyTag))
857 m_tree.insertHTMLElement(&token);
860 if (token.name() == rtTag || token.name() == rpTag) {
861 if (m_tree.openElements().inScope(rubyTag.localName())) {
862 m_tree.generateImpliedEndTagsWithExclusion(rtcTag.localName());
863 if (!m_tree.currentStackItem().hasTagName(rubyTag) && !m_tree.currentStackItem().hasTagName(rtcTag))
866 m_tree.insertHTMLElement(&token);
869 if (token.name() == MathMLNames::mathTag.localName()) {
870 m_tree.reconstructTheActiveFormattingElements();
871 adjustMathMLAttributes(token);
872 adjustForeignAttributes(token);
873 m_tree.insertForeignElement(&token, MathMLNames::mathmlNamespaceURI);
876 if (token.name() == SVGNames::svgTag.localName()) {
877 m_tree.reconstructTheActiveFormattingElements();
878 adjustSVGAttributes(token);
879 adjustForeignAttributes(token);
880 m_tree.insertForeignElement(&token, SVGNames::svgNamespaceURI);
883 if (isCaptionColOrColgroupTag(token.name())
884 || token.name() == frameTag
885 || token.name() == headTag
886 || isTableBodyContextTag(token.name())
887 || isTableCellContextTag(token.name())
888 || token.name() == trTag) {
892 #if ENABLE(TEMPLATE_ELEMENT)
893 if (token.name() == templateTag) {
894 processTemplateStartTag(token);
898 m_tree.reconstructTheActiveFormattingElements();
899 m_tree.insertHTMLElement(&token);
902 #if ENABLE(TEMPLATE_ELEMENT)
904 void HTMLTreeBuilder::processTemplateStartTag(AtomicHTMLToken& token)
906 m_tree.activeFormattingElements().appendMarker();
907 m_tree.insertHTMLElement(&token);
908 m_templateInsertionModes.append(InsertionMode::TemplateContents);
909 m_insertionMode = InsertionMode::TemplateContents;
912 bool HTMLTreeBuilder::processTemplateEndTag(AtomicHTMLToken& token)
914 ASSERT(token.name() == templateTag.localName());
915 if (!m_tree.openElements().hasTemplateInHTMLScope()) {
916 ASSERT(m_templateInsertionModes.isEmpty() || (m_templateInsertionModes.size() == 1 && m_fragmentContext.contextElement().hasTagName(templateTag)));
920 m_tree.generateImpliedEndTags();
921 if (!m_tree.currentStackItem().hasTagName(templateTag))
923 m_tree.openElements().popUntilPopped(templateTag);
924 m_tree.activeFormattingElements().clearToLastMarker();
925 m_templateInsertionModes.removeLast();
926 resetInsertionModeAppropriately();
930 bool HTMLTreeBuilder::processEndOfFileForInTemplateContents(AtomicHTMLToken& token)
932 AtomicHTMLToken endTemplate(HTMLToken::EndTag, templateTag.localName());
933 if (!processTemplateEndTag(endTemplate))
936 processEndOfFile(token);
942 bool HTMLTreeBuilder::processColgroupEndTagForInColumnGroup()
944 bool ignoreFakeEndTag = m_tree.currentIsRootNode();
945 #if ENABLE(TEMPLATE_ELEMENT)
946 ignoreFakeEndTag = ignoreFakeEndTag || m_tree.currentNode().hasTagName(templateTag);
949 if (ignoreFakeEndTag) {
950 ASSERT(isParsingFragmentOrTemplateContents());
951 // FIXME: parse error
954 m_tree.openElements().pop();
955 m_insertionMode = InsertionMode::InTable;
959 // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#close-the-cell
960 void HTMLTreeBuilder::closeTheCell()
962 ASSERT(m_insertionMode == InsertionMode::InCell);
963 if (m_tree.openElements().inTableScope(tdTag)) {
964 ASSERT(!m_tree.openElements().inTableScope(thTag));
965 processFakeEndTag(tdTag);
968 ASSERT(m_tree.openElements().inTableScope(thTag));
969 processFakeEndTag(thTag);
970 ASSERT(m_insertionMode == InsertionMode::InRow);
973 void HTMLTreeBuilder::processStartTagForInTable(AtomicHTMLToken& token)
975 ASSERT(token.type() == HTMLToken::StartTag);
976 if (token.name() == captionTag) {
977 m_tree.openElements().popUntilTableScopeMarker();
978 m_tree.activeFormattingElements().appendMarker();
979 m_tree.insertHTMLElement(&token);
980 m_insertionMode = InsertionMode::InCaption;
983 if (token.name() == colgroupTag) {
984 m_tree.openElements().popUntilTableScopeMarker();
985 m_tree.insertHTMLElement(&token);
986 m_insertionMode = InsertionMode::InColumnGroup;
989 if (token.name() == colTag) {
990 processFakeStartTag(colgroupTag);
991 ASSERT(m_insertionMode == InsertionMode::InColumnGroup);
992 processStartTag(token);
995 if (isTableBodyContextTag(token.name())) {
996 m_tree.openElements().popUntilTableScopeMarker();
997 m_tree.insertHTMLElement(&token);
998 m_insertionMode = InsertionMode::InTableBody;
1001 if (isTableCellContextTag(token.name()) || token.name() == trTag) {
1002 processFakeStartTag(tbodyTag);
1003 ASSERT(m_insertionMode == InsertionMode::InTableBody);
1004 processStartTag(token);
1007 if (token.name() == tableTag) {
1009 if (!processTableEndTagForInTable()) {
1010 ASSERT(isParsingFragmentOrTemplateContents());
1013 processStartTag(token);
1016 if (token.name() == styleTag || token.name() == scriptTag) {
1017 processStartTagForInHead(token);
1020 if (token.name() == inputTag) {
1021 Attribute* typeAttribute = findAttribute(token.attributes(), typeAttr);
1022 if (typeAttribute && equalIgnoringCase(typeAttribute->value(), "hidden")) {
1024 m_tree.insertSelfClosingHTMLElement(&token);
1027 // Fall through to "anything else" case.
1029 if (token.name() == formTag) {
1031 if (m_tree.form() && !isParsingTemplateContents())
1033 m_tree.insertHTMLFormElement(&token, true);
1034 m_tree.openElements().pop();
1037 #if ENABLE(TEMPLATE_ELEMENT)
1038 if (token.name() == templateTag) {
1039 processTemplateStartTag(token);
1044 HTMLConstructionSite::RedirectToFosterParentGuard redirecter(m_tree);
1045 processStartTagForInBody(token);
1048 void HTMLTreeBuilder::processStartTag(AtomicHTMLToken& token)
1050 ASSERT(token.type() == HTMLToken::StartTag);
1051 switch (m_insertionMode) {
1052 case InsertionMode::Initial:
1053 defaultForInitial();
1054 ASSERT(m_insertionMode == InsertionMode::BeforeHTML);
1056 case InsertionMode::BeforeHTML:
1057 if (token.name() == htmlTag) {
1058 m_tree.insertHTMLHtmlStartTagBeforeHTML(&token);
1059 m_insertionMode = InsertionMode::BeforeHead;
1062 defaultForBeforeHTML();
1063 ASSERT(m_insertionMode == InsertionMode::BeforeHead);
1065 case InsertionMode::BeforeHead:
1066 if (token.name() == htmlTag) {
1067 processHtmlStartTagForInBody(token);
1070 if (token.name() == headTag) {
1071 m_tree.insertHTMLHeadElement(&token);
1072 m_insertionMode = InsertionMode::InHead;
1075 defaultForBeforeHead();
1076 ASSERT(m_insertionMode == InsertionMode::InHead);
1078 case InsertionMode::InHead:
1079 if (processStartTagForInHead(token))
1082 ASSERT(m_insertionMode == InsertionMode::AfterHead);
1084 case InsertionMode::AfterHead:
1085 if (token.name() == htmlTag) {
1086 processHtmlStartTagForInBody(token);
1089 if (token.name() == bodyTag) {
1090 m_framesetOk = false;
1091 m_tree.insertHTMLBodyElement(&token);
1092 m_insertionMode = InsertionMode::InBody;
1095 if (token.name() == framesetTag) {
1096 m_tree.insertHTMLElement(&token);
1097 m_insertionMode = InsertionMode::InFrameset;
1100 if (token.name() == baseTag
1101 || token.name() == basefontTag
1102 || token.name() == bgsoundTag
1103 || token.name() == linkTag
1104 || token.name() == metaTag
1105 || token.name() == noframesTag
1106 || token.name() == scriptTag
1107 || token.name() == styleTag
1108 #if ENABLE(TEMPLATE_ELEMENT)
1109 || token.name() == templateTag
1111 || token.name() == titleTag) {
1113 m_tree.openElements().pushHTMLHeadElement(m_tree.headStackItem());
1114 processStartTagForInHead(token);
1115 m_tree.openElements().removeHTMLHeadElement(&m_tree.head());
1118 if (token.name() == headTag) {
1122 defaultForAfterHead();
1123 ASSERT(m_insertionMode == InsertionMode::InBody);
1125 case InsertionMode::InBody:
1126 processStartTagForInBody(token);
1128 case InsertionMode::InTable:
1129 processStartTagForInTable(token);
1131 case InsertionMode::InCaption:
1132 if (isCaptionColOrColgroupTag(token.name())
1133 || isTableBodyContextTag(token.name())
1134 || isTableCellContextTag(token.name())
1135 || token.name() == trTag) {
1137 if (!processCaptionEndTagForInCaption()) {
1138 ASSERT(isParsingFragment());
1141 processStartTag(token);
1144 processStartTagForInBody(token);
1146 case InsertionMode::InColumnGroup:
1147 if (token.name() == htmlTag) {
1148 processHtmlStartTagForInBody(token);
1151 if (token.name() == colTag) {
1152 m_tree.insertSelfClosingHTMLElement(&token);
1155 #if ENABLE(TEMPLATE_ELEMENT)
1156 if (token.name() == templateTag) {
1157 processTemplateStartTag(token);
1161 if (!processColgroupEndTagForInColumnGroup()) {
1162 ASSERT(isParsingFragmentOrTemplateContents());
1165 processStartTag(token);
1167 case InsertionMode::InTableBody:
1168 if (token.name() == trTag) {
1169 m_tree.openElements().popUntilTableBodyScopeMarker(); // How is there ever anything to pop?
1170 m_tree.insertHTMLElement(&token);
1171 m_insertionMode = InsertionMode::InRow;
1174 if (isTableCellContextTag(token.name())) {
1176 processFakeStartTag(trTag);
1177 ASSERT(m_insertionMode == InsertionMode::InRow);
1178 processStartTag(token);
1181 if (isCaptionColOrColgroupTag(token.name()) || isTableBodyContextTag(token.name())) {
1182 // FIXME: This is slow.
1183 if (!m_tree.openElements().inTableScope(tbodyTag) && !m_tree.openElements().inTableScope(theadTag) && !m_tree.openElements().inTableScope(tfootTag)) {
1184 ASSERT(isParsingFragmentOrTemplateContents());
1188 m_tree.openElements().popUntilTableBodyScopeMarker();
1189 ASSERT(isTableBodyContextTag(m_tree.currentStackItem().localName()));
1190 processFakeEndTag(m_tree.currentStackItem().localName());
1191 processStartTag(token);
1194 processStartTagForInTable(token);
1196 case InsertionMode::InRow:
1197 if (isTableCellContextTag(token.name())) {
1198 m_tree.openElements().popUntilTableRowScopeMarker();
1199 m_tree.insertHTMLElement(&token);
1200 m_insertionMode = InsertionMode::InCell;
1201 m_tree.activeFormattingElements().appendMarker();
1204 if (token.name() == trTag
1205 || isCaptionColOrColgroupTag(token.name())
1206 || isTableBodyContextTag(token.name())) {
1207 if (!processTrEndTagForInRow()) {
1208 ASSERT(isParsingFragmentOrTemplateContents());
1211 ASSERT(m_insertionMode == InsertionMode::InTableBody);
1212 processStartTag(token);
1215 processStartTagForInTable(token);
1217 case InsertionMode::InCell:
1218 if (isCaptionColOrColgroupTag(token.name())
1219 || isTableCellContextTag(token.name())
1220 || token.name() == trTag
1221 || isTableBodyContextTag(token.name())) {
1222 // FIXME: This could be more efficient.
1223 if (!m_tree.openElements().inTableScope(tdTag) && !m_tree.openElements().inTableScope(thTag)) {
1224 ASSERT(isParsingFragment());
1229 processStartTag(token);
1232 processStartTagForInBody(token);
1234 case InsertionMode::AfterBody:
1235 case InsertionMode::AfterAfterBody:
1236 if (token.name() == htmlTag) {
1237 processHtmlStartTagForInBody(token);
1240 m_insertionMode = InsertionMode::InBody;
1241 processStartTag(token);
1243 case InsertionMode::InHeadNoscript:
1244 if (token.name() == htmlTag) {
1245 processHtmlStartTagForInBody(token);
1248 if (token.name() == basefontTag
1249 || token.name() == bgsoundTag
1250 || token.name() == linkTag
1251 || token.name() == metaTag
1252 || token.name() == noframesTag
1253 || token.name() == styleTag) {
1254 bool didProcess = processStartTagForInHead(token);
1255 ASSERT_UNUSED(didProcess, didProcess);
1258 if (token.name() == htmlTag || token.name() == noscriptTag) {
1262 defaultForInHeadNoscript();
1263 processToken(token);
1265 case InsertionMode::InFrameset:
1266 if (token.name() == htmlTag) {
1267 processHtmlStartTagForInBody(token);
1270 if (token.name() == framesetTag) {
1271 m_tree.insertHTMLElement(&token);
1274 if (token.name() == frameTag) {
1275 m_tree.insertSelfClosingHTMLElement(&token);
1278 if (token.name() == noframesTag) {
1279 processStartTagForInHead(token);
1282 #if ENABLE(TEMPLATE_ELEMENT)
1283 if (token.name() == templateTag) {
1284 processTemplateStartTag(token);
1290 case InsertionMode::AfterFrameset:
1291 case InsertionMode::AfterAfterFrameset:
1292 if (token.name() == htmlTag) {
1293 processHtmlStartTagForInBody(token);
1296 if (token.name() == noframesTag) {
1297 processStartTagForInHead(token);
1302 case InsertionMode::InSelectInTable:
1303 if (token.name() == captionTag
1304 || token.name() == tableTag
1305 || isTableBodyContextTag(token.name())
1306 || token.name() == trTag
1307 || isTableCellContextTag(token.name())) {
1309 AtomicHTMLToken endSelect(HTMLToken::EndTag, selectTag.localName());
1310 processEndTag(endSelect);
1311 processStartTag(token);
1315 case InsertionMode::InSelect:
1316 if (token.name() == htmlTag) {
1317 processHtmlStartTagForInBody(token);
1320 if (token.name() == optionTag) {
1321 if (is<HTMLOptionElement>(m_tree.currentStackItem().node())) {
1322 AtomicHTMLToken endOption(HTMLToken::EndTag, optionTag.localName());
1323 processEndTag(endOption);
1325 m_tree.insertHTMLElement(&token);
1328 if (token.name() == optgroupTag) {
1329 if (is<HTMLOptionElement>(m_tree.currentStackItem().node())) {
1330 AtomicHTMLToken endOption(HTMLToken::EndTag, optionTag.localName());
1331 processEndTag(endOption);
1333 if (is<HTMLOptGroupElement>(m_tree.currentStackItem().node())) {
1334 AtomicHTMLToken endOptgroup(HTMLToken::EndTag, optgroupTag.localName());
1335 processEndTag(endOptgroup);
1337 m_tree.insertHTMLElement(&token);
1340 if (token.name() == selectTag) {
1342 AtomicHTMLToken endSelect(HTMLToken::EndTag, selectTag.localName());
1343 processEndTag(endSelect);
1346 if (token.name() == inputTag || token.name() == keygenTag || token.name() == textareaTag) {
1348 if (!m_tree.openElements().inSelectScope(selectTag)) {
1349 ASSERT(isParsingFragment());
1352 AtomicHTMLToken endSelect(HTMLToken::EndTag, selectTag.localName());
1353 processEndTag(endSelect);
1354 processStartTag(token);
1357 if (token.name() == scriptTag) {
1358 bool didProcess = processStartTagForInHead(token);
1359 ASSERT_UNUSED(didProcess, didProcess);
1362 #if ENABLE(TEMPLATE_ELEMENT)
1363 if (token.name() == templateTag) {
1364 processTemplateStartTag(token);
1369 case InsertionMode::InTableText:
1370 defaultForInTableText();
1371 processStartTag(token);
1373 case InsertionMode::Text:
1374 ASSERT_NOT_REACHED();
1376 #if ENABLE(TEMPLATE_ELEMENT)
1377 case InsertionMode::TemplateContents:
1378 if (token.name() == templateTag) {
1379 processTemplateStartTag(token);
1383 if (token.name() == linkTag
1384 || token.name() == scriptTag
1385 || token.name() == styleTag
1386 || token.name() == metaTag) {
1387 processStartTagForInHead(token);
1391 InsertionMode insertionMode = InsertionMode::TemplateContents;
1392 if (token.name() == frameTag)
1393 insertionMode = InsertionMode::InFrameset;
1394 else if (token.name() == colTag)
1395 insertionMode = InsertionMode::InColumnGroup;
1396 else if (isCaptionColOrColgroupTag(token.name()) || isTableBodyContextTag(token.name()))
1397 insertionMode = InsertionMode::InTable;
1398 else if (token.name() == trTag)
1399 insertionMode = InsertionMode::InTableBody;
1400 else if (isTableCellContextTag(token.name()))
1401 insertionMode = InsertionMode::InRow;
1403 insertionMode = InsertionMode::InBody;
1405 ASSERT(insertionMode != InsertionMode::TemplateContents);
1406 ASSERT(m_templateInsertionModes.last() == InsertionMode::TemplateContents);
1407 m_templateInsertionModes.last() = insertionMode;
1408 m_insertionMode = insertionMode;
1410 processStartTag(token);
1416 void HTMLTreeBuilder::processHtmlStartTagForInBody(AtomicHTMLToken& token)
1419 #if ENABLE(TEMPLATE_ELEMENT)
1420 if (m_tree.openElements().hasTemplateInHTMLScope()) {
1421 ASSERT(isParsingTemplateContents());
1425 m_tree.insertHTMLHtmlStartTagInBody(&token);
1428 bool HTMLTreeBuilder::processBodyEndTagForInBody(AtomicHTMLToken& token)
1430 ASSERT(token.type() == HTMLToken::EndTag);
1431 ASSERT(token.name() == bodyTag);
1432 if (!m_tree.openElements().inScope(bodyTag.localName())) {
1436 notImplemented(); // Emit a more specific parse error based on stack contents.
1437 m_insertionMode = InsertionMode::AfterBody;
1441 void HTMLTreeBuilder::processAnyOtherEndTagForInBody(AtomicHTMLToken& token)
1443 ASSERT(token.type() == HTMLToken::EndTag);
1444 for (auto* record = &m_tree.openElements().topRecord(); ; record = record->next()) {
1445 HTMLStackItem& item = record->stackItem();
1446 if (item.matchesHTMLTag(token.name())) {
1447 m_tree.generateImpliedEndTagsWithExclusion(token.name());
1448 if (!m_tree.currentStackItem().matchesHTMLTag(token.name()))
1450 m_tree.openElements().popUntilPopped(&item.element());
1453 if (isSpecialNode(item)) {
1460 // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#parsing-main-inbody
1461 void HTMLTreeBuilder::callTheAdoptionAgency(AtomicHTMLToken& token)
1463 // The adoption agency algorithm is N^2. We limit the number of iterations
1464 // to stop from hanging the whole browser. This limit is specified in the
1465 // adoption agency algorithm:
1466 // http://www.whatwg.org/specs/web-apps/current-work/multipage/tree-construction.html#parsing-main-inbody
1467 static const int outerIterationLimit = 8;
1468 static const int innerIterationLimit = 3;
1470 // 1, 2, 3 and 16 are covered by the for() loop.
1471 for (int i = 0; i < outerIterationLimit; ++i) {
1473 Element* formattingElement = m_tree.activeFormattingElements().closestElementInScopeWithName(token.name());
1475 if (!formattingElement)
1476 return processAnyOtherEndTagForInBody(token);
1478 if ((m_tree.openElements().contains(formattingElement)) && !m_tree.openElements().inScope(formattingElement)) {
1480 notImplemented(); // Check the stack of open elements for a more specific parse error.
1484 auto* formattingElementRecord = m_tree.openElements().find(formattingElement);
1485 if (!formattingElementRecord) {
1487 m_tree.activeFormattingElements().remove(formattingElement);
1491 if (formattingElement != &m_tree.currentElement())
1494 auto* furthestBlock = m_tree.openElements().furthestBlockForFormattingElement(formattingElement);
1496 if (!furthestBlock) {
1497 m_tree.openElements().popUntilPopped(formattingElement);
1498 m_tree.activeFormattingElements().remove(formattingElement);
1502 ASSERT(furthestBlock->isAbove(formattingElementRecord));
1503 Ref<HTMLStackItem> commonAncestor = formattingElementRecord->next()->stackItem();
1505 HTMLFormattingElementList::Bookmark bookmark = m_tree.activeFormattingElements().bookmarkFor(formattingElement);
1507 auto* node = furthestBlock;
1508 auto* nextNode = node->next();
1509 auto* lastNode = furthestBlock;
1510 // 9.1, 9.2, 9.3 and 9.11 are covered by the for() loop.
1511 for (int i = 0; i < innerIterationLimit; ++i) {
1515 nextNode = node->next(); // Save node->next() for the next iteration in case node is deleted in 9.5.
1517 if (!m_tree.activeFormattingElements().contains(&node->element())) {
1518 m_tree.openElements().remove(&node->element());
1523 if (node == formattingElementRecord)
1526 RefPtr<HTMLStackItem> newItem = m_tree.createElementFromSavedToken(&node->stackItem());
1528 HTMLFormattingElementList::Entry* nodeEntry = m_tree.activeFormattingElements().find(&node->element());
1529 nodeEntry->replaceElement(newItem.copyRef());
1530 node->replaceElement(newItem.release());
1533 if (lastNode == furthestBlock)
1534 bookmark.moveToAfter(nodeEntry);
1536 m_tree.reparent(*node, *lastNode);
1541 m_tree.insertAlreadyParsedChild(commonAncestor.get(), *lastNode);
1543 RefPtr<HTMLStackItem> newItem = m_tree.createElementFromSavedToken(&formattingElementRecord->stackItem());
1545 m_tree.takeAllChildren(*newItem, *furthestBlock);
1547 m_tree.reparent(*furthestBlock, *newItem);
1549 m_tree.activeFormattingElements().swapTo(formattingElement, newItem, bookmark);
1551 m_tree.openElements().remove(formattingElement);
1552 m_tree.openElements().insertAbove(newItem, furthestBlock);
1556 void HTMLTreeBuilder::resetInsertionModeAppropriately()
1558 // http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#reset-the-insertion-mode-appropriately
1560 for (auto* record = &m_tree.openElements().topRecord(); ; record = record->next()) {
1561 HTMLStackItem* item = &record->stackItem();
1562 if (&item->node() == &m_tree.openElements().rootNode()) {
1564 #if ENABLE(TEMPLATE_ELEMENT)
1565 bool shouldCreateItem = isParsingFragment();
1567 ASSERT(isParsingFragment());
1568 bool shouldCreateItem = true;
1570 if (shouldCreateItem)
1571 item = &m_fragmentContext.contextElementStackItem();
1574 #if ENABLE(TEMPLATE_ELEMENT)
1575 if (item->hasTagName(templateTag)) {
1576 m_insertionMode = m_templateInsertionModes.last();
1580 if (item->hasTagName(selectTag)) {
1581 #if ENABLE(TEMPLATE_ELEMENT)
1583 while (&item->node() != &m_tree.openElements().rootNode() && !item->hasTagName(templateTag)) {
1584 record = record->next();
1585 item = &record->stackItem();
1586 if (is<HTMLTableElement>(item->node())) {
1587 m_insertionMode = InsertionMode::InSelectInTable;
1593 m_insertionMode = InsertionMode::InSelect;
1596 if (item->hasTagName(tdTag) || item->hasTagName(thTag)) {
1597 m_insertionMode = InsertionMode::InCell;
1600 if (item->hasTagName(trTag)) {
1601 m_insertionMode = InsertionMode::InRow;
1604 if (item->hasTagName(tbodyTag) || item->hasTagName(theadTag) || item->hasTagName(tfootTag)) {
1605 m_insertionMode = InsertionMode::InTableBody;
1608 if (item->hasTagName(captionTag)) {
1609 m_insertionMode = InsertionMode::InCaption;
1612 if (item->hasTagName(colgroupTag)) {
1613 m_insertionMode = InsertionMode::InColumnGroup;
1616 if (is<HTMLTableElement>(item->node())) {
1617 m_insertionMode = InsertionMode::InTable;
1620 if (item->hasTagName(headTag)) {
1621 #if ENABLE(TEMPLATE_ELEMENT)
1622 if (!m_fragmentContext.fragment() || &m_fragmentContext.contextElement() != &item->node()) {
1623 m_insertionMode = InsertionMode::InHead;
1627 m_insertionMode = InsertionMode::InBody;
1630 if (item->hasTagName(bodyTag)) {
1631 m_insertionMode = InsertionMode::InBody;
1634 if (item->hasTagName(framesetTag)) {
1635 m_insertionMode = InsertionMode::InFrameset;
1638 if (item->hasTagName(htmlTag)) {
1639 if (m_tree.headStackItem()) {
1640 m_insertionMode = InsertionMode::AfterHead;
1643 ASSERT(isParsingFragment());
1644 m_insertionMode = InsertionMode::BeforeHead;
1648 ASSERT(isParsingFragment());
1649 m_insertionMode = InsertionMode::InBody;
1655 void HTMLTreeBuilder::processEndTagForInTableBody(AtomicHTMLToken& token)
1657 ASSERT(token.type() == HTMLToken::EndTag);
1658 if (isTableBodyContextTag(token.name())) {
1659 if (!m_tree.openElements().inTableScope(token.name())) {
1663 m_tree.openElements().popUntilTableBodyScopeMarker();
1664 m_tree.openElements().pop();
1665 m_insertionMode = InsertionMode::InTable;
1668 if (token.name() == tableTag) {
1669 // FIXME: This is slow.
1670 if (!m_tree.openElements().inTableScope(tbodyTag) && !m_tree.openElements().inTableScope(theadTag) && !m_tree.openElements().inTableScope(tfootTag)) {
1671 ASSERT(isParsingFragmentOrTemplateContents());
1675 m_tree.openElements().popUntilTableBodyScopeMarker();
1676 ASSERT(isTableBodyContextTag(m_tree.currentStackItem().localName()));
1677 processFakeEndTag(m_tree.currentStackItem().localName());
1678 processEndTag(token);
1681 if (token.name() == bodyTag
1682 || isCaptionColOrColgroupTag(token.name())
1683 || token.name() == htmlTag
1684 || isTableCellContextTag(token.name())
1685 || token.name() == trTag) {
1689 processEndTagForInTable(token);
1692 void HTMLTreeBuilder::processEndTagForInRow(AtomicHTMLToken& token)
1694 ASSERT(token.type() == HTMLToken::EndTag);
1695 if (token.name() == trTag) {
1696 processTrEndTagForInRow();
1699 if (token.name() == tableTag) {
1700 if (!processTrEndTagForInRow()) {
1701 ASSERT(isParsingFragmentOrTemplateContents());
1704 ASSERT(m_insertionMode == InsertionMode::InTableBody);
1705 processEndTag(token);
1708 if (isTableBodyContextTag(token.name())) {
1709 if (!m_tree.openElements().inTableScope(token.name())) {
1713 processFakeEndTag(trTag);
1714 ASSERT(m_insertionMode == InsertionMode::InTableBody);
1715 processEndTag(token);
1718 if (token.name() == bodyTag
1719 || isCaptionColOrColgroupTag(token.name())
1720 || token.name() == htmlTag
1721 || isTableCellContextTag(token.name())) {
1725 processEndTagForInTable(token);
1728 void HTMLTreeBuilder::processEndTagForInCell(AtomicHTMLToken& token)
1730 ASSERT(token.type() == HTMLToken::EndTag);
1731 if (isTableCellContextTag(token.name())) {
1732 if (!m_tree.openElements().inTableScope(token.name())) {
1736 m_tree.generateImpliedEndTags();
1737 if (!m_tree.currentStackItem().matchesHTMLTag(token.name()))
1739 m_tree.openElements().popUntilPopped(token.name());
1740 m_tree.activeFormattingElements().clearToLastMarker();
1741 m_insertionMode = InsertionMode::InRow;
1744 if (token.name() == bodyTag
1745 || isCaptionColOrColgroupTag(token.name())
1746 || token.name() == htmlTag) {
1750 if (token.name() == tableTag
1751 || token.name() == trTag
1752 || isTableBodyContextTag(token.name())) {
1753 if (!m_tree.openElements().inTableScope(token.name())) {
1754 #if ENABLE(TEMPLATE_ELEMENT)
1755 ASSERT(isTableBodyContextTag(token.name()) || m_tree.openElements().inTableScope(templateTag) || isParsingFragment());
1757 ASSERT(isTableBodyContextTag(token.name()) || isParsingFragment());
1763 processEndTag(token);
1766 processEndTagForInBody(token);
1769 void HTMLTreeBuilder::processEndTagForInBody(AtomicHTMLToken& token)
1771 ASSERT(token.type() == HTMLToken::EndTag);
1772 if (token.name() == bodyTag) {
1773 processBodyEndTagForInBody(token);
1776 if (token.name() == htmlTag) {
1777 AtomicHTMLToken endBody(HTMLToken::EndTag, bodyTag.localName());
1778 if (processBodyEndTagForInBody(endBody))
1779 processEndTag(token);
1782 if (token.name() == addressTag
1783 || token.name() == articleTag
1784 || token.name() == asideTag
1785 || token.name() == blockquoteTag
1786 || token.name() == buttonTag
1787 || token.name() == centerTag
1788 || token.name() == detailsTag
1789 || token.name() == dirTag
1790 || token.name() == divTag
1791 || token.name() == dlTag
1792 || token.name() == fieldsetTag
1793 || token.name() == figcaptionTag
1794 || token.name() == figureTag
1795 || token.name() == footerTag
1796 || token.name() == headerTag
1797 || token.name() == hgroupTag
1798 || token.name() == listingTag
1799 || token.name() == mainTag
1800 || token.name() == menuTag
1801 || token.name() == navTag
1802 || token.name() == olTag
1803 || token.name() == preTag
1804 || token.name() == sectionTag
1805 || token.name() == summaryTag
1806 || token.name() == ulTag) {
1807 if (!m_tree.openElements().inScope(token.name())) {
1811 m_tree.generateImpliedEndTags();
1812 if (!m_tree.currentStackItem().matchesHTMLTag(token.name()))
1814 m_tree.openElements().popUntilPopped(token.name());
1817 if (token.name() == formTag) {
1818 if (!isParsingTemplateContents()) {
1819 RefPtr<Element> node = m_tree.takeForm();
1820 if (!node || !m_tree.openElements().inScope(node.get())) {
1824 m_tree.generateImpliedEndTags();
1825 if (&m_tree.currentNode() != node.get())
1827 m_tree.openElements().remove(node.get());
1829 if (!m_tree.openElements().inScope(token.name())) {
1833 m_tree.generateImpliedEndTags();
1834 if (!m_tree.currentNode().hasTagName(formTag))
1836 m_tree.openElements().popUntilPopped(token.name());
1839 if (token.name() == pTag) {
1840 if (!m_tree.openElements().inButtonScope(token.name())) {
1842 processFakeStartTag(pTag);
1843 ASSERT(m_tree.openElements().inScope(token.name()));
1844 processEndTag(token);
1847 m_tree.generateImpliedEndTagsWithExclusion(token.name());
1848 if (!m_tree.currentStackItem().matchesHTMLTag(token.name()))
1850 m_tree.openElements().popUntilPopped(token.name());
1853 if (token.name() == liTag) {
1854 if (!m_tree.openElements().inListItemScope(token.name())) {
1858 m_tree.generateImpliedEndTagsWithExclusion(token.name());
1859 if (!m_tree.currentStackItem().matchesHTMLTag(token.name()))
1861 m_tree.openElements().popUntilPopped(token.name());
1864 if (token.name() == ddTag || token.name() == dtTag) {
1865 if (!m_tree.openElements().inScope(token.name())) {
1869 m_tree.generateImpliedEndTagsWithExclusion(token.name());
1870 if (!m_tree.currentStackItem().matchesHTMLTag(token.name()))
1872 m_tree.openElements().popUntilPopped(token.name());
1875 if (isNumberedHeaderTag(token.name())) {
1876 if (!m_tree.openElements().hasNumberedHeaderElementInScope()) {
1880 m_tree.generateImpliedEndTags();
1881 if (!m_tree.currentStackItem().matchesHTMLTag(token.name()))
1883 m_tree.openElements().popUntilNumberedHeaderElementPopped();
1886 if (HTMLConstructionSite::isFormattingTag(token.name())) {
1887 callTheAdoptionAgency(token);
1890 if (token.name() == appletTag || token.name() == marqueeTag || token.name() == objectTag) {
1891 if (!m_tree.openElements().inScope(token.name())) {
1895 m_tree.generateImpliedEndTags();
1896 if (!m_tree.currentStackItem().matchesHTMLTag(token.name()))
1898 m_tree.openElements().popUntilPopped(token.name());
1899 m_tree.activeFormattingElements().clearToLastMarker();
1902 if (token.name() == brTag) {
1904 processFakeStartTag(brTag);
1907 #if ENABLE(TEMPLATE_ELEMENT)
1908 if (token.name() == templateTag) {
1909 processTemplateEndTag(token);
1913 processAnyOtherEndTagForInBody(token);
1916 bool HTMLTreeBuilder::processCaptionEndTagForInCaption()
1918 if (!m_tree.openElements().inTableScope(captionTag.localName())) {
1919 ASSERT(isParsingFragment());
1920 // FIXME: parse error
1923 m_tree.generateImpliedEndTags();
1924 // FIXME: parse error if (!m_tree.currentStackItem().hasTagName(captionTag))
1925 m_tree.openElements().popUntilPopped(captionTag.localName());
1926 m_tree.activeFormattingElements().clearToLastMarker();
1927 m_insertionMode = InsertionMode::InTable;
1931 bool HTMLTreeBuilder::processTrEndTagForInRow()
1933 if (!m_tree.openElements().inTableScope(trTag)) {
1934 ASSERT(isParsingFragmentOrTemplateContents());
1935 // FIXME: parse error
1938 m_tree.openElements().popUntilTableRowScopeMarker();
1939 ASSERT(m_tree.currentStackItem().hasTagName(trTag));
1940 m_tree.openElements().pop();
1941 m_insertionMode = InsertionMode::InTableBody;
1945 bool HTMLTreeBuilder::processTableEndTagForInTable()
1947 if (!m_tree.openElements().inTableScope(tableTag)) {
1948 ASSERT(isParsingFragmentOrTemplateContents());
1949 // FIXME: parse error.
1952 m_tree.openElements().popUntilPopped(tableTag.localName());
1953 resetInsertionModeAppropriately();
1957 void HTMLTreeBuilder::processEndTagForInTable(AtomicHTMLToken& token)
1959 ASSERT(token.type() == HTMLToken::EndTag);
1960 if (token.name() == tableTag) {
1961 processTableEndTagForInTable();
1964 if (token.name() == bodyTag
1965 || isCaptionColOrColgroupTag(token.name())
1966 || token.name() == htmlTag
1967 || isTableBodyContextTag(token.name())
1968 || isTableCellContextTag(token.name())
1969 || token.name() == trTag) {
1974 // Is this redirection necessary here?
1975 HTMLConstructionSite::RedirectToFosterParentGuard redirecter(m_tree);
1976 processEndTagForInBody(token);
1979 void HTMLTreeBuilder::processEndTag(AtomicHTMLToken& token)
1981 ASSERT(token.type() == HTMLToken::EndTag);
1982 switch (m_insertionMode) {
1983 case InsertionMode::Initial:
1984 defaultForInitial();
1985 ASSERT(m_insertionMode == InsertionMode::BeforeHTML);
1987 case InsertionMode::BeforeHTML:
1988 if (token.name() != headTag && token.name() != bodyTag && token.name() != htmlTag && token.name() != brTag) {
1992 defaultForBeforeHTML();
1993 ASSERT(m_insertionMode == InsertionMode::BeforeHead);
1995 case InsertionMode::BeforeHead:
1996 if (token.name() != headTag && token.name() != bodyTag && token.name() != htmlTag && token.name() != brTag) {
2000 defaultForBeforeHead();
2001 ASSERT(m_insertionMode == InsertionMode::InHead);
2003 case InsertionMode::InHead:
2004 // FIXME: This case should be broken out into processEndTagForInHead,
2005 // because other end tag cases now refer to it ("process the token for using the rules of the "in head" insertion mode").
2006 // but because the logic falls through to InsertionMode::AfterHead, that gets a little messy.
2007 #if ENABLE(TEMPLATE_ELEMENT)
2008 if (token.name() == templateTag) {
2009 processTemplateEndTag(token);
2013 if (token.name() == headTag) {
2014 m_tree.openElements().popHTMLHeadElement();
2015 m_insertionMode = InsertionMode::AfterHead;
2018 if (token.name() != bodyTag && token.name() != htmlTag && token.name() != brTag) {
2023 ASSERT(m_insertionMode == InsertionMode::AfterHead);
2025 case InsertionMode::AfterHead:
2026 if (token.name() != bodyTag && token.name() != htmlTag && token.name() != brTag) {
2030 defaultForAfterHead();
2031 ASSERT(m_insertionMode == InsertionMode::InBody);
2033 case InsertionMode::InBody:
2034 processEndTagForInBody(token);
2036 case InsertionMode::InTable:
2037 processEndTagForInTable(token);
2039 case InsertionMode::InCaption:
2040 if (token.name() == captionTag) {
2041 processCaptionEndTagForInCaption();
2044 if (token.name() == tableTag) {
2046 if (!processCaptionEndTagForInCaption()) {
2047 ASSERT(isParsingFragment());
2050 processEndTag(token);
2053 if (token.name() == bodyTag
2054 || token.name() == colTag
2055 || token.name() == colgroupTag
2056 || token.name() == htmlTag
2057 || isTableBodyContextTag(token.name())
2058 || isTableCellContextTag(token.name())
2059 || token.name() == trTag) {
2063 processEndTagForInBody(token);
2065 case InsertionMode::InColumnGroup:
2066 if (token.name() == colgroupTag) {
2067 processColgroupEndTagForInColumnGroup();
2070 if (token.name() == colTag) {
2074 #if ENABLE(TEMPLATE_ELEMENT)
2075 if (token.name() == templateTag) {
2076 processTemplateEndTag(token);
2080 if (!processColgroupEndTagForInColumnGroup()) {
2081 ASSERT(isParsingFragmentOrTemplateContents());
2084 processEndTag(token);
2086 case InsertionMode::InRow:
2087 processEndTagForInRow(token);
2089 case InsertionMode::InCell:
2090 processEndTagForInCell(token);
2092 case InsertionMode::InTableBody:
2093 processEndTagForInTableBody(token);
2095 case InsertionMode::AfterBody:
2096 if (token.name() == htmlTag) {
2097 if (isParsingFragment()) {
2101 m_insertionMode = InsertionMode::AfterAfterBody;
2105 case InsertionMode::AfterAfterBody:
2106 ASSERT(m_insertionMode == InsertionMode::AfterBody || m_insertionMode == InsertionMode::AfterAfterBody);
2108 m_insertionMode = InsertionMode::InBody;
2109 processEndTag(token);
2111 case InsertionMode::InHeadNoscript:
2112 if (token.name() == noscriptTag) {
2113 ASSERT(m_tree.currentStackItem().hasTagName(noscriptTag));
2114 m_tree.openElements().pop();
2115 ASSERT(m_tree.currentStackItem().hasTagName(headTag));
2116 m_insertionMode = InsertionMode::InHead;
2119 if (token.name() != brTag) {
2123 defaultForInHeadNoscript();
2124 processToken(token);
2126 case InsertionMode::Text:
2127 if (token.name() == scriptTag) {
2128 // Pause ourselves so that parsing stops until the script can be processed by the caller.
2129 ASSERT(m_tree.currentStackItem().hasTagName(scriptTag));
2130 if (scriptingContentIsAllowed(m_tree.parserContentPolicy()))
2131 m_scriptToProcess = &m_tree.currentElement();
2132 m_tree.openElements().pop();
2133 m_insertionMode = m_originalInsertionMode;
2135 // This token will not have been created by the tokenizer if a
2136 // self-closing script tag was encountered and pre-HTML5 parser
2137 // quirks are enabled. We must set the tokenizer's state to
2138 // DataState explicitly if the tokenizer didn't have a chance to.
2139 ASSERT(m_parser.tokenizer().isInDataState() || m_options.usePreHTML5ParserQuirks);
2140 m_parser.tokenizer().setDataState();
2143 m_tree.openElements().pop();
2144 m_insertionMode = m_originalInsertionMode;
2146 case InsertionMode::InFrameset:
2147 if (token.name() == framesetTag) {
2148 bool ignoreFramesetForFragmentParsing = m_tree.currentIsRootNode();
2149 #if ENABLE(TEMPLATE_ELEMENT)
2150 ignoreFramesetForFragmentParsing = ignoreFramesetForFragmentParsing || m_tree.openElements().hasTemplateInHTMLScope();
2152 if (ignoreFramesetForFragmentParsing) {
2153 ASSERT(isParsingFragmentOrTemplateContents());
2157 m_tree.openElements().pop();
2158 if (!isParsingFragment() && !m_tree.currentStackItem().hasTagName(framesetTag))
2159 m_insertionMode = InsertionMode::AfterFrameset;
2162 #if ENABLE(TEMPLATE_ELEMENT)
2163 if (token.name() == templateTag) {
2164 processTemplateEndTag(token);
2169 case InsertionMode::AfterFrameset:
2170 if (token.name() == htmlTag) {
2171 m_insertionMode = InsertionMode::AfterAfterFrameset;
2175 case InsertionMode::AfterAfterFrameset:
2176 ASSERT(m_insertionMode == InsertionMode::AfterFrameset || m_insertionMode == InsertionMode::AfterAfterFrameset);
2179 case InsertionMode::InSelectInTable:
2180 if (token.name() == captionTag
2181 || token.name() == tableTag
2182 || isTableBodyContextTag(token.name())
2183 || token.name() == trTag
2184 || isTableCellContextTag(token.name())) {
2186 if (m_tree.openElements().inTableScope(token.name())) {
2187 AtomicHTMLToken endSelect(HTMLToken::EndTag, selectTag.localName());
2188 processEndTag(endSelect);
2189 processEndTag(token);
2194 case InsertionMode::InSelect:
2195 ASSERT(m_insertionMode == InsertionMode::InSelect || m_insertionMode == InsertionMode::InSelectInTable);
2196 if (token.name() == optgroupTag) {
2197 if (is<HTMLOptionElement>(m_tree.currentStackItem().node()) && m_tree.oneBelowTop() && is<HTMLOptGroupElement>(m_tree.oneBelowTop()->node()))
2198 processFakeEndTag(optionTag);
2199 if (is<HTMLOptGroupElement>(m_tree.currentStackItem().node())) {
2200 m_tree.openElements().pop();
2206 if (token.name() == optionTag) {
2207 if (is<HTMLOptionElement>(m_tree.currentStackItem().node())) {
2208 m_tree.openElements().pop();
2214 if (token.name() == selectTag) {
2215 if (!m_tree.openElements().inSelectScope(token.name())) {
2216 ASSERT(isParsingFragment());
2220 m_tree.openElements().popUntilPopped(selectTag.localName());
2221 resetInsertionModeAppropriately();
2224 #if ENABLE(TEMPLATE_ELEMENT)
2225 if (token.name() == templateTag) {
2226 processTemplateEndTag(token);
2231 case InsertionMode::InTableText:
2232 defaultForInTableText();
2233 processEndTag(token);
2235 #if ENABLE(TEMPLATE_ELEMENT)
2236 case InsertionMode::TemplateContents:
2237 if (token.name() == templateTag) {
2238 processTemplateEndTag(token);
2246 void HTMLTreeBuilder::processComment(AtomicHTMLToken& token)
2248 ASSERT(token.type() == HTMLToken::Comment);
2249 if (m_insertionMode == InsertionMode::Initial
2250 || m_insertionMode == InsertionMode::BeforeHTML
2251 || m_insertionMode == InsertionMode::AfterAfterBody
2252 || m_insertionMode == InsertionMode::AfterAfterFrameset) {
2253 m_tree.insertCommentOnDocument(&token);
2256 if (m_insertionMode == InsertionMode::AfterBody) {
2257 m_tree.insertCommentOnHTMLHtmlElement(&token);
2260 if (m_insertionMode == InsertionMode::InTableText) {
2261 defaultForInTableText();
2262 processComment(token);
2265 m_tree.insertComment(&token);
2268 void HTMLTreeBuilder::processCharacter(AtomicHTMLToken& token)
2270 ASSERT(token.type() == HTMLToken::Character);
2271 ExternalCharacterTokenBuffer buffer(token);
2272 processCharacterBuffer(buffer);
2275 #if ENABLE(TELEPHONE_NUMBER_DETECTION) && PLATFORM(IOS)
2277 // FIXME: Extract the following iOS-specific code into a separate file.
2278 // From the string 4089961010, creates a link of the form <a href="tel:4089961010">4089961010</a> and inserts it.
2279 void HTMLTreeBuilder::insertPhoneNumberLink(const String& string)
2281 Vector<Attribute> attributes;
2282 attributes.append(Attribute(HTMLNames::hrefAttr, ASCIILiteral("tel:") + string));
2284 const AtomicString& aTagLocalName = aTag.localName();
2285 AtomicHTMLToken aStartToken(HTMLToken::StartTag, aTagLocalName, WTFMove(attributes));
2286 AtomicHTMLToken aEndToken(HTMLToken::EndTag, aTagLocalName);
2288 processStartTag(aStartToken);
2289 m_tree.executeQueuedTasks();
2290 m_tree.insertTextNode(string);
2291 processEndTag(aEndToken);
2294 // Locates the phone numbers in the string and deals with it
2295 // 1. Appends the text before the phone number as a text node.
2296 // 2. Wraps the phone number in a tel: link.
2297 // 3. Goes back to step 1 if a phone number is found in the rest of the string.
2298 // 4. Appends the rest of the string as a text node.
2299 void HTMLTreeBuilder::linkifyPhoneNumbers(const String& string)
2301 ASSERT(TelephoneNumberDetector::isSupported());
2303 // relativeStartPosition and relativeEndPosition are the endpoints of the phone number range,
2304 // relative to the scannerPosition
2305 unsigned length = string.length();
2306 unsigned scannerPosition = 0;
2307 int relativeStartPosition = 0;
2308 int relativeEndPosition = 0;
2310 auto characters = StringView(string).upconvertedCharacters();
2312 // While there's a phone number in the rest of the string...
2313 while (scannerPosition < length && TelephoneNumberDetector::find(&characters[scannerPosition], length - scannerPosition, &relativeStartPosition, &relativeEndPosition)) {
2314 // The convention in the Data Detectors framework is that the end position is the first character NOT in the phone number
2315 // (that is, the length of the range is relativeEndPosition - relativeStartPosition). So substract 1 to get the same
2316 // convention as the old WebCore phone number parser (so that the rest of the code is still valid if we want to go back
2317 // to the old parser).
2318 --relativeEndPosition;
2320 ASSERT(scannerPosition + relativeEndPosition < length);
2322 m_tree.insertTextNode(string.substring(scannerPosition, relativeStartPosition));
2323 insertPhoneNumberLink(string.substring(scannerPosition + relativeStartPosition, relativeEndPosition - relativeStartPosition + 1));
2325 scannerPosition += relativeEndPosition + 1;
2328 // Append the rest as a text node.
2329 if (scannerPosition > 0) {
2330 if (scannerPosition < length) {
2331 String after = string.substring(scannerPosition, length - scannerPosition);
2332 m_tree.insertTextNode(after);
2335 m_tree.insertTextNode(string);
2338 // Looks at the ancestors of the element to determine whether we're inside an element which disallows parsing phone numbers.
2339 static inline bool disallowTelephoneNumberParsing(const ContainerNode& node)
2341 return node.isLink()
2342 || node.hasTagName(scriptTag)
2343 || is<HTMLFormControlElement>(node)
2344 || node.hasTagName(styleTag)
2345 || node.hasTagName(ttTag)
2346 || node.hasTagName(preTag)
2347 || node.hasTagName(codeTag);
2350 static inline bool shouldParseTelephoneNumbersInNode(const ContainerNode& node)
2352 for (const ContainerNode* ancestor = &node; ancestor; ancestor = ancestor->parentNode()) {
2353 if (disallowTelephoneNumberParsing(*ancestor))
2359 #endif // ENABLE(TELEPHONE_NUMBER_DETECTION) && PLATFORM(IOS)
2361 void HTMLTreeBuilder::processCharacterBuffer(ExternalCharacterTokenBuffer& buffer)
2364 // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#parsing-main-inbody
2365 // Note that this logic is different than the generic \r\n collapsing
2366 // handled in the input stream preprocessor. This logic is here as an
2367 // "authoring convenience" so folks can write:
2374 // without getting an extra newline at the start of their <pre> element.
2375 if (m_shouldSkipLeadingNewline) {
2376 m_shouldSkipLeadingNewline = false;
2377 buffer.skipAtMostOneLeadingNewline();
2378 if (buffer.isEmpty())
2382 switch (m_insertionMode) {
2383 case InsertionMode::Initial:
2384 buffer.skipLeadingWhitespace();
2385 if (buffer.isEmpty())
2387 defaultForInitial();
2388 ASSERT(m_insertionMode == InsertionMode::BeforeHTML);
2390 case InsertionMode::BeforeHTML:
2391 buffer.skipLeadingWhitespace();
2392 if (buffer.isEmpty())
2394 defaultForBeforeHTML();
2395 ASSERT(m_insertionMode == InsertionMode::BeforeHead);
2397 case InsertionMode::BeforeHead:
2398 buffer.skipLeadingWhitespace();
2399 if (buffer.isEmpty())
2401 defaultForBeforeHead();
2402 ASSERT(m_insertionMode == InsertionMode::InHead);
2404 case InsertionMode::InHead: {
2405 String leadingWhitespace = buffer.takeLeadingWhitespace();
2406 if (!leadingWhitespace.isEmpty())
2407 m_tree.insertTextNode(leadingWhitespace, AllWhitespace);
2408 if (buffer.isEmpty())
2411 ASSERT(m_insertionMode == InsertionMode::AfterHead);
2414 case InsertionMode::AfterHead: {
2415 String leadingWhitespace = buffer.takeLeadingWhitespace();
2416 if (!leadingWhitespace.isEmpty())
2417 m_tree.insertTextNode(leadingWhitespace, AllWhitespace);
2418 if (buffer.isEmpty())
2420 defaultForAfterHead();
2421 ASSERT(m_insertionMode == InsertionMode::InBody);
2424 case InsertionMode::InBody:
2425 case InsertionMode::InCaption:
2426 case InsertionMode::InCell:
2427 #if ENABLE(TEMPLATE_ELEMENT)
2428 case InsertionMode::TemplateContents:
2430 processCharacterBufferForInBody(buffer);
2432 case InsertionMode::InTable:
2433 case InsertionMode::InTableBody:
2434 case InsertionMode::InRow:
2435 ASSERT(m_pendingTableCharacters.isEmpty());
2436 if (is<HTMLTableElement>(m_tree.currentStackItem().node())
2437 || m_tree.currentStackItem().hasTagName(HTMLNames::tbodyTag)
2438 || m_tree.currentStackItem().hasTagName(HTMLNames::tfootTag)
2439 || m_tree.currentStackItem().hasTagName(HTMLNames::theadTag)
2440 || m_tree.currentStackItem().hasTagName(HTMLNames::trTag)) {
2442 m_originalInsertionMode = m_insertionMode;
2443 m_insertionMode = InsertionMode::InTableText;
2444 // Note that we fall through to the InsertionMode::InTableText case below.
2446 HTMLConstructionSite::RedirectToFosterParentGuard redirecter(m_tree);
2447 processCharacterBufferForInBody(buffer);
2451 case InsertionMode::InTableText:
2452 buffer.giveRemainingTo(m_pendingTableCharacters);
2454 case InsertionMode::InColumnGroup: {
2455 String leadingWhitespace = buffer.takeLeadingWhitespace();
2456 if (!leadingWhitespace.isEmpty())
2457 m_tree.insertTextNode(leadingWhitespace, AllWhitespace);
2458 if (buffer.isEmpty())
2460 if (!processColgroupEndTagForInColumnGroup()) {
2461 ASSERT(isParsingFragmentOrTemplateContents());
2462 // The spec tells us to drop these characters on the floor.
2463 buffer.skipLeadingNonWhitespace();
2464 if (buffer.isEmpty())
2467 goto ReprocessBuffer;
2469 case InsertionMode::AfterBody:
2470 case InsertionMode::AfterAfterBody:
2471 // FIXME: parse error
2472 m_insertionMode = InsertionMode::InBody;
2473 goto ReprocessBuffer;
2474 case InsertionMode::Text:
2475 m_tree.insertTextNode(buffer.takeRemaining());
2477 case InsertionMode::InHeadNoscript: {
2478 String leadingWhitespace = buffer.takeLeadingWhitespace();
2479 if (!leadingWhitespace.isEmpty())
2480 m_tree.insertTextNode(leadingWhitespace, AllWhitespace);
2481 if (buffer.isEmpty())
2483 defaultForInHeadNoscript();
2484 goto ReprocessBuffer;
2486 case InsertionMode::InFrameset:
2487 case InsertionMode::AfterFrameset: {
2488 String leadingWhitespace = buffer.takeRemainingWhitespace();
2489 if (!leadingWhitespace.isEmpty())
2490 m_tree.insertTextNode(leadingWhitespace, AllWhitespace);
2491 // FIXME: We should generate a parse error if we skipped over any
2492 // non-whitespace characters.
2495 case InsertionMode::InSelectInTable:
2496 case InsertionMode::InSelect:
2497 m_tree.insertTextNode(buffer.takeRemaining());
2499 case InsertionMode::AfterAfterFrameset: {
2500 String leadingWhitespace = buffer.takeRemainingWhitespace();
2501 if (!leadingWhitespace.isEmpty()) {
2502 m_tree.reconstructTheActiveFormattingElements();
2503 m_tree.insertTextNode(leadingWhitespace, AllWhitespace);
2505 // FIXME: We should generate a parse error if we skipped over any
2506 // non-whitespace characters.
2512 void HTMLTreeBuilder::processCharacterBufferForInBody(ExternalCharacterTokenBuffer& buffer)
2514 m_tree.reconstructTheActiveFormattingElements();
2515 String characters = buffer.takeRemaining();
2516 #if ENABLE(TELEPHONE_NUMBER_DETECTION) && PLATFORM(IOS)
2517 if (!isParsingFragment() && m_tree.isTelephoneNumberParsingEnabled() && shouldParseTelephoneNumbersInNode(m_tree.currentNode()) && TelephoneNumberDetector::isSupported())
2518 linkifyPhoneNumbers(characters);
2520 m_tree.insertTextNode(characters);
2522 m_tree.insertTextNode(characters);
2524 if (m_framesetOk && !isAllWhitespaceOrReplacementCharacters(characters))
2525 m_framesetOk = false;
2528 void HTMLTreeBuilder::processEndOfFile(AtomicHTMLToken& token)
2530 ASSERT(token.type() == HTMLToken::EndOfFile);
2531 switch (m_insertionMode) {
2532 case InsertionMode::Initial:
2533 defaultForInitial();
2534 ASSERT(m_insertionMode == InsertionMode::BeforeHTML);
2536 case InsertionMode::BeforeHTML:
2537 defaultForBeforeHTML();
2538 ASSERT(m_insertionMode == InsertionMode::BeforeHead);
2540 case InsertionMode::BeforeHead:
2541 defaultForBeforeHead();
2542 ASSERT(m_insertionMode == InsertionMode::InHead);
2544 case InsertionMode::InHead:
2546 ASSERT(m_insertionMode == InsertionMode::AfterHead);
2548 case InsertionMode::AfterHead:
2549 defaultForAfterHead();
2550 ASSERT(m_insertionMode == InsertionMode::InBody);
2552 case InsertionMode::InBody:
2553 case InsertionMode::InCell:
2554 case InsertionMode::InCaption:
2555 case InsertionMode::InRow:
2556 notImplemented(); // Emit parse error based on what elements are still open.
2557 #if ENABLE(TEMPLATE_ELEMENT)
2558 if (!m_templateInsertionModes.isEmpty()) {
2559 if (processEndOfFileForInTemplateContents(token))
2564 case InsertionMode::AfterBody:
2565 case InsertionMode::AfterAfterBody:
2567 case InsertionMode::InHeadNoscript:
2568 defaultForInHeadNoscript();
2569 processEndOfFile(token);
2571 case InsertionMode::AfterFrameset:
2572 case InsertionMode::AfterAfterFrameset:
2574 case InsertionMode::InColumnGroup:
2575 if (m_tree.currentIsRootNode()) {
2576 ASSERT(isParsingFragment());
2577 return; // FIXME: Should we break here instead of returning?
2579 #if ENABLE(TEMPLATE_ELEMENT)
2580 ASSERT(m_tree.currentNode().hasTagName(colgroupTag) || m_tree.currentNode().hasTagName(templateTag));
2582 ASSERT(m_tree.currentNode().hasTagName(colgroupTag));
2584 processColgroupEndTagForInColumnGroup();
2586 case InsertionMode::InFrameset:
2587 case InsertionMode::InTable:
2588 case InsertionMode::InTableBody:
2589 case InsertionMode::InSelectInTable:
2590 case InsertionMode::InSelect:
2591 ASSERT(m_insertionMode == InsertionMode::InSelect || m_insertionMode == InsertionMode::InSelectInTable || m_insertionMode == InsertionMode::InTable || m_insertionMode == InsertionMode::InFrameset || m_insertionMode == InsertionMode::InTableBody || m_insertionMode == InsertionMode::InColumnGroup);
2592 if (&m_tree.currentNode() != &m_tree.openElements().rootNode())
2594 #if ENABLE(TEMPLATE_ELEMENT)
2595 if (!m_templateInsertionModes.isEmpty()) {
2596 if (processEndOfFileForInTemplateContents(token))
2601 case InsertionMode::InTableText:
2602 defaultForInTableText();
2603 processEndOfFile(token);
2605 case InsertionMode::Text:
2607 if (m_tree.currentStackItem().hasTagName(scriptTag))
2608 notImplemented(); // mark the script element as "already started".
2609 m_tree.openElements().pop();
2610 ASSERT(m_originalInsertionMode != InsertionMode::Text);
2611 m_insertionMode = m_originalInsertionMode;
2612 processEndOfFile(token);
2614 #if ENABLE(TEMPLATE_ELEMENT)
2615 case InsertionMode::TemplateContents:
2616 if (processEndOfFileForInTemplateContents(token))
2621 m_tree.openElements().popAll();
2624 void HTMLTreeBuilder::defaultForInitial()
2627 m_tree.setDefaultCompatibilityMode();
2628 // FIXME: parse error
2629 m_insertionMode = InsertionMode::BeforeHTML;
2632 void HTMLTreeBuilder::defaultForBeforeHTML()
2634 AtomicHTMLToken startHTML(HTMLToken::StartTag, htmlTag.localName());
2635 m_tree.insertHTMLHtmlStartTagBeforeHTML(&startHTML);
2636 m_insertionMode = InsertionMode::BeforeHead;
2639 void HTMLTreeBuilder::defaultForBeforeHead()
2641 AtomicHTMLToken startHead(HTMLToken::StartTag, headTag.localName());
2642 processStartTag(startHead);
2645 void HTMLTreeBuilder::defaultForInHead()
2647 AtomicHTMLToken endHead(HTMLToken::EndTag, headTag.localName());
2648 processEndTag(endHead);
2651 void HTMLTreeBuilder::defaultForInHeadNoscript()
2653 AtomicHTMLToken endNoscript(HTMLToken::EndTag, noscriptTag.localName());
2654 processEndTag(endNoscript);
2657 void HTMLTreeBuilder::defaultForAfterHead()
2659 AtomicHTMLToken startBody(HTMLToken::StartTag, bodyTag.localName());
2660 processStartTag(startBody);
2661 m_framesetOk = true;
2664 void HTMLTreeBuilder::defaultForInTableText()
2666 String characters = m_pendingTableCharacters.toString();
2667 m_pendingTableCharacters.clear();
2668 if (!isAllWhitespace(characters)) {
2669 // FIXME: parse error
2670 HTMLConstructionSite::RedirectToFosterParentGuard redirecter(m_tree);
2671 m_tree.reconstructTheActiveFormattingElements();
2672 m_tree.insertTextNode(characters, NotAllWhitespace);
2673 m_framesetOk = false;
2674 m_insertionMode = m_originalInsertionMode;
2677 m_tree.insertTextNode(characters);
2678 m_insertionMode = m_originalInsertionMode;
2681 bool HTMLTreeBuilder::processStartTagForInHead(AtomicHTMLToken& token)
2683 ASSERT(token.type() == HTMLToken::StartTag);
2684 if (token.name() == htmlTag) {
2685 processHtmlStartTagForInBody(token);
2688 if (token.name() == baseTag
2689 || token.name() == basefontTag
2690 || token.name() == bgsoundTag
2691 || token.name() == commandTag
2692 || token.name() == linkTag
2693 || token.name() == metaTag) {
2694 m_tree.insertSelfClosingHTMLElement(&token);
2695 // Note: The custom processing for the <meta> tag is done in HTMLMetaElement::process().
2698 if (token.name() == titleTag) {
2699 processGenericRCDATAStartTag(token);
2702 if (token.name() == noscriptTag) {
2703 if (m_options.scriptEnabled) {
2704 processGenericRawTextStartTag(token);
2707 m_tree.insertHTMLElement(&token);
2708 m_insertionMode = InsertionMode::InHeadNoscript;
2711 if (token.name() == noframesTag || token.name() == styleTag) {
2712 processGenericRawTextStartTag(token);
2715 if (token.name() == scriptTag) {
2716 processScriptStartTag(token);
2717 if (m_options.usePreHTML5ParserQuirks && token.selfClosing())
2718 processFakeEndTag(scriptTag);
2721 #if ENABLE(TEMPLATE_ELEMENT)
2722 if (token.name() == templateTag) {
2723 processTemplateStartTag(token);
2727 if (token.name() == headTag) {
2734 void HTMLTreeBuilder::processGenericRCDATAStartTag(AtomicHTMLToken& token)
2736 ASSERT(token.type() == HTMLToken::StartTag);
2737 m_tree.insertHTMLElement(&token);
2738 m_parser.tokenizer().setRCDATAState();
2739 m_originalInsertionMode = m_insertionMode;
2740 m_insertionMode = InsertionMode::Text;
2743 void HTMLTreeBuilder::processGenericRawTextStartTag(AtomicHTMLToken& token)
2745 ASSERT(token.type() == HTMLToken::StartTag);
2746 m_tree.insertHTMLElement(&token);
2747 m_parser.tokenizer().setRAWTEXTState();
2748 m_originalInsertionMode = m_insertionMode;
2749 m_insertionMode = InsertionMode::Text;
2752 void HTMLTreeBuilder::processScriptStartTag(AtomicHTMLToken& token)
2754 ASSERT(token.type() == HTMLToken::StartTag);
2755 m_tree.insertScriptElement(&token);
2756 m_parser.tokenizer().setScriptDataState();
2757 m_originalInsertionMode = m_insertionMode;
2759 TextPosition position = m_parser.textPosition();
2761 m_scriptToProcessStartPosition = position;
2763 m_insertionMode = InsertionMode::Text;
2766 // http://www.whatwg.org/specs/web-apps/current-work/#adjusted-current-node
2767 HTMLStackItem& HTMLTreeBuilder::adjustedCurrentStackItem() const
2769 ASSERT(!m_tree.isEmpty());
2770 if (isParsingFragment() && m_tree.openElements().hasOnlyOneElement())
2771 return m_fragmentContext.contextElementStackItem();
2773 return m_tree.currentStackItem();
2776 // http://www.whatwg.org/specs/web-apps/current-work/multipage/tree-construction.html#tree-construction
2777 bool HTMLTreeBuilder::shouldProcessTokenInForeignContent(AtomicHTMLToken& token)
2779 if (m_tree.isEmpty())
2781 HTMLStackItem& adjustedCurrentNode = adjustedCurrentStackItem();
2782 if (isInHTMLNamespace(adjustedCurrentNode))
2784 if (HTMLElementStack::isMathMLTextIntegrationPoint(adjustedCurrentNode)) {
2785 if (token.type() == HTMLToken::StartTag
2786 && token.name() != MathMLNames::mglyphTag
2787 && token.name() != MathMLNames::malignmarkTag)
2789 if (token.type() == HTMLToken::Character)
2792 if (adjustedCurrentNode.hasTagName(MathMLNames::annotation_xmlTag)
2793 && token.type() == HTMLToken::StartTag
2794 && token.name() == SVGNames::svgTag)
2796 if (HTMLElementStack::isHTMLIntegrationPoint(adjustedCurrentNode)) {
2797 if (token.type() == HTMLToken::StartTag)
2799 if (token.type() == HTMLToken::Character)
2802 if (token.type() == HTMLToken::EndOfFile)
2807 static bool hasAttribute(AtomicHTMLToken& token, const QualifiedName& name)
2809 return findAttribute(token.attributes(), name);
2812 void HTMLTreeBuilder::processTokenInForeignContent(AtomicHTMLToken& token)
2814 HTMLStackItem& adjustedCurrentNode = adjustedCurrentStackItem();
2816 switch (token.type()) {
2817 case HTMLToken::Uninitialized:
2818 ASSERT_NOT_REACHED();
2820 case HTMLToken::DOCTYPE:
2823 case HTMLToken::StartTag: {
2824 if (token.name() == bTag
2825 || token.name() == bigTag
2826 || token.name() == blockquoteTag
2827 || token.name() == bodyTag
2828 || token.name() == brTag
2829 || token.name() == centerTag
2830 || token.name() == codeTag
2831 || token.name() == ddTag
2832 || token.name() == divTag
2833 || token.name() == dlTag
2834 || token.name() == dtTag
2835 || token.name() == emTag
2836 || token.name() == embedTag
2837 || isNumberedHeaderTag(token.name())
2838 || token.name() == headTag
2839 || token.name() == hrTag
2840 || token.name() == iTag
2841 || token.name() == imgTag
2842 || token.name() == liTag
2843 || token.name() == listingTag
2844 || token.name() == menuTag
2845 || token.name() == metaTag
2846 || token.name() == nobrTag
2847 || token.name() == olTag
2848 || token.name() == pTag
2849 || token.name() == preTag
2850 || token.name() == rubyTag
2851 || token.name() == sTag
2852 || token.name() == smallTag
2853 || token.name() == spanTag
2854 || token.name() == strongTag
2855 || token.name() == strikeTag
2856 || token.name() == subTag
2857 || token.name() == supTag
2858 || token.name() == tableTag
2859 || token.name() == ttTag
2860 || token.name() == uTag
2861 || token.name() == ulTag
2862 || token.name() == varTag
2863 || (token.name() == fontTag && (hasAttribute(token, colorAttr) || hasAttribute(token, faceAttr) || hasAttribute(token, sizeAttr)))) {
2865 m_tree.openElements().popUntilForeignContentScopeMarker();
2866 processStartTag(token);
2869 const AtomicString& currentNamespace = adjustedCurrentNode.namespaceURI();
2870 if (currentNamespace == MathMLNames::mathmlNamespaceURI)
2871 adjustMathMLAttributes(token);
2872 if (currentNamespace == SVGNames::svgNamespaceURI) {
2873 adjustSVGTagNameCase(token);
2874 adjustSVGAttributes(token);
2876 adjustForeignAttributes(token);
2877 m_tree.insertForeignElement(&token, currentNamespace);
2880 case HTMLToken::EndTag: {
2881 if (adjustedCurrentNode.namespaceURI() == SVGNames::svgNamespaceURI)
2882 adjustSVGTagNameCase(token);
2884 if (token.name() == SVGNames::scriptTag && m_tree.currentStackItem().hasTagName(SVGNames::scriptTag)) {
2885 if (scriptingContentIsAllowed(m_tree.parserContentPolicy()))
2886 m_scriptToProcess = &m_tree.currentElement();
2887 m_tree.openElements().pop();
2890 if (!isInHTMLNamespace(m_tree.currentStackItem())) {
2891 // FIXME: This code just wants an Element* iterator, instead of an ElementRecord*
2892 auto* nodeRecord = &m_tree.openElements().topRecord();
2893 if (nodeRecord->stackItem().localName() != token.name())
2896 if (nodeRecord->stackItem().localName() == token.name()) {
2897 m_tree.openElements().popUntilPopped(&nodeRecord->element());
2900 nodeRecord = nodeRecord->next();
2902 if (isInHTMLNamespace(nodeRecord->stackItem()))
2906 // Otherwise, process the token according to the rules given in the section corresponding to the current insertion mode in HTML content.
2907 processEndTag(token);
2910 case HTMLToken::Comment:
2911 m_tree.insertComment(&token);
2913 case HTMLToken::Character: {
2914 String characters = String(token.characters(), token.charactersLength());
2915 m_tree.insertTextNode(characters);
2916 if (m_framesetOk && !isAllWhitespaceOrReplacementCharacters(characters))
2917 m_framesetOk = false;
2920 case HTMLToken::EndOfFile:
2921 ASSERT_NOT_REACHED();
2926 void HTMLTreeBuilder::finished()
2928 ASSERT(!m_destroyed);
2930 if (isParsingFragment())
2933 #if ENABLE(TEMPLATE_ELEMENT)
2934 ASSERT(m_templateInsertionModes.isEmpty());
2937 m_tree.finishedParsing();
2938 // The tree builder might have been destroyed as an indirect result of finishing the parsing.
2941 inline void HTMLTreeBuilder::parseError(AtomicHTMLToken&)