HTMLTreeBuilder.cpp revision cad810f21b803229eb11403f9209855525a25d57
1/*
2 * Copyright (C) 2010 Google, Inc. All Rights Reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL GOOGLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "HTMLTreeBuilder.h"
28
29#include "CharacterNames.h"
30#include "Comment.h"
31#include "DocumentFragment.h"
32#include "DocumentType.h"
33#include "Frame.h"
34#include "HTMLDocument.h"
35#include "HTMLDocumentParser.h"
36#include "HTMLElementFactory.h"
37#include "HTMLFormElement.h"
38#include "HTMLHtmlElement.h"
39#include "HTMLNames.h"
40#include "HTMLParserIdioms.h"
41#include "HTMLScriptElement.h"
42#include "HTMLToken.h"
43#include "HTMLTokenizer.h"
44#include "LocalizedStrings.h"
45#include "MathMLNames.h"
46#include "NotImplemented.h"
47#include "SVGNames.h"
48#include "ScriptController.h"
49#include "Text.h"
50#include "XLinkNames.h"
51#include "XMLNSNames.h"
52#include "XMLNames.h"
53
54namespace WebCore {
55
56using namespace HTMLNames;
57
58static const int uninitializedLineNumberValue = -1;
59
60static TextPosition1 uninitializedPositionValue1()
61{
62    return TextPosition1(WTF::OneBasedNumber::fromOneBasedInt(-1), WTF::OneBasedNumber::base());
63}
64
65namespace {
66
67inline bool isHTMLSpaceOrReplacementCharacter(UChar character)
68{
69    return isHTMLSpace(character) || character == replacementCharacter;
70}
71
72inline bool isAllWhitespace(const String& string)
73{
74    return string.isAllSpecialCharacters<isHTMLSpace>();
75}
76
77inline bool isAllWhitespaceOrReplacementCharacters(const String& string)
78{
79    return string.isAllSpecialCharacters<isHTMLSpaceOrReplacementCharacter>();
80}
81
82bool isNumberedHeaderTag(const AtomicString& tagName)
83{
84    return tagName == h1Tag
85        || tagName == h2Tag
86        || tagName == h3Tag
87        || tagName == h4Tag
88        || tagName == h5Tag
89        || tagName == h6Tag;
90}
91
92bool isCaptionColOrColgroupTag(const AtomicString& tagName)
93{
94    return tagName == captionTag
95        || tagName == colTag
96        || tagName == colgroupTag;
97}
98
99bool isTableCellContextTag(const AtomicString& tagName)
100{
101    return tagName == thTag || tagName == tdTag;
102}
103
104bool isTableBodyContextTag(const AtomicString& tagName)
105{
106    return tagName == tbodyTag
107        || tagName == tfootTag
108        || tagName == theadTag;
109}
110
111// http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#special
112bool isSpecialNode(Node* node)
113{
114    if (node->hasTagName(MathMLNames::miTag)
115        || node->hasTagName(MathMLNames::moTag)
116        || node->hasTagName(MathMLNames::mnTag)
117        || node->hasTagName(MathMLNames::msTag)
118        || node->hasTagName(MathMLNames::mtextTag)
119        || node->hasTagName(MathMLNames::annotation_xmlTag)
120        || node->hasTagName(SVGNames::foreignObjectTag)
121        || node->hasTagName(SVGNames::descTag)
122        || node->hasTagName(SVGNames::titleTag))
123        return true;
124    if (node->namespaceURI() != xhtmlNamespaceURI)
125        return false;
126    const AtomicString& tagName = node->localName();
127    return tagName == addressTag
128        || tagName == appletTag
129        || tagName == areaTag
130        || tagName == articleTag
131        || tagName == asideTag
132        || tagName == baseTag
133        || tagName == basefontTag
134        || tagName == bgsoundTag
135        || tagName == blockquoteTag
136        || tagName == bodyTag
137        || tagName == brTag
138        || tagName == buttonTag
139        || tagName == captionTag
140        || tagName == centerTag
141        || tagName == colTag
142        || tagName == colgroupTag
143        || tagName == commandTag
144        || tagName == ddTag
145        || tagName == detailsTag
146        || tagName == dirTag
147        || tagName == divTag
148        || tagName == dlTag
149        || tagName == dtTag
150        || tagName == embedTag
151        || tagName == fieldsetTag
152        || tagName == figcaptionTag
153        || tagName == figureTag
154        || tagName == footerTag
155        || tagName == formTag
156        || tagName == frameTag
157        || tagName == framesetTag
158        || isNumberedHeaderTag(tagName)
159        || tagName == headTag
160        || tagName == headerTag
161        || tagName == hgroupTag
162        || tagName == hrTag
163        || tagName == htmlTag
164        || tagName == iframeTag
165        || tagName == imgTag
166        || tagName == inputTag
167        || tagName == isindexTag
168        || tagName == liTag
169        || tagName == linkTag
170        || tagName == listingTag
171        || tagName == marqueeTag
172        || tagName == menuTag
173        || tagName == metaTag
174        || tagName == navTag
175        || tagName == noembedTag
176        || tagName == noframesTag
177        || tagName == noscriptTag
178        || tagName == objectTag
179        || tagName == olTag
180        || tagName == pTag
181        || tagName == paramTag
182        || tagName == plaintextTag
183        || tagName == preTag
184        || tagName == scriptTag
185        || tagName == sectionTag
186        || tagName == selectTag
187        || tagName == styleTag
188        || tagName == summaryTag
189        || tagName == tableTag
190        || isTableBodyContextTag(tagName)
191        || tagName == tdTag
192        || tagName == textareaTag
193        || tagName == thTag
194        || tagName == titleTag
195        || tagName == trTag
196        || tagName == ulTag
197        || tagName == wbrTag
198        || tagName == xmpTag;
199}
200
201bool isNonAnchorNonNobrFormattingTag(const AtomicString& tagName)
202{
203    return tagName == bTag
204        || tagName == bigTag
205        || tagName == codeTag
206        || tagName == emTag
207        || tagName == fontTag
208        || tagName == iTag
209        || tagName == sTag
210        || tagName == smallTag
211        || tagName == strikeTag
212        || tagName == strongTag
213        || tagName == ttTag
214        || tagName == uTag;
215}
216
217bool isNonAnchorFormattingTag(const AtomicString& tagName)
218{
219    return tagName == nobrTag
220        || isNonAnchorNonNobrFormattingTag(tagName);
221}
222
223// http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#formatting
224bool isFormattingTag(const AtomicString& tagName)
225{
226    return tagName == aTag || isNonAnchorFormattingTag(tagName);
227}
228
229HTMLFormElement* closestFormAncestor(Element* element)
230{
231    while (element) {
232        if (element->hasTagName(formTag))
233            return static_cast<HTMLFormElement*>(element);
234        ContainerNode* parent = element->parentNode();
235        if (!parent || !parent->isElementNode())
236            return 0;
237        element = static_cast<Element*>(parent);
238    }
239    return 0;
240}
241
242} // namespace
243
244class HTMLTreeBuilder::ExternalCharacterTokenBuffer : public Noncopyable {
245public:
246    explicit ExternalCharacterTokenBuffer(AtomicHTMLToken& token)
247        : m_current(token.characters().data())
248        , m_end(m_current + token.characters().size())
249    {
250        ASSERT(!isEmpty());
251    }
252
253    explicit ExternalCharacterTokenBuffer(const String& string)
254        : m_current(string.characters())
255        , m_end(m_current + string.length())
256    {
257        ASSERT(!isEmpty());
258    }
259
260    ~ExternalCharacterTokenBuffer()
261    {
262        ASSERT(isEmpty());
263    }
264
265    bool isEmpty() const { return m_current == m_end; }
266
267    void skipLeadingWhitespace()
268    {
269        skipLeading<isHTMLSpace>();
270    }
271
272    String takeLeadingWhitespace()
273    {
274        return takeLeading<isHTMLSpace>();
275    }
276
277    String takeLeadingNonWhitespace()
278    {
279        return takeLeading<isNotHTMLSpace>();
280    }
281
282    String takeRemaining()
283    {
284        ASSERT(!isEmpty());
285        const UChar* start = m_current;
286        m_current = m_end;
287        return String(start, m_current - start);
288    }
289
290    void giveRemainingTo(Vector<UChar>& recipient)
291    {
292        recipient.append(m_current, m_end - m_current);
293        m_current = m_end;
294    }
295
296    String takeRemainingWhitespace()
297    {
298        ASSERT(!isEmpty());
299        Vector<UChar> whitespace;
300        do {
301            UChar cc = *m_current++;
302            if (isHTMLSpace(cc))
303                whitespace.append(cc);
304        } while (m_current < m_end);
305        // Returning the null string when there aren't any whitespace
306        // characters is slightly cleaner semantically because we don't want
307        // to insert a text node (as opposed to inserting an empty text node).
308        if (whitespace.isEmpty())
309            return String();
310        return String::adopt(whitespace);
311    }
312
313private:
314    template<bool characterPredicate(UChar)>
315    void skipLeading()
316    {
317        ASSERT(!isEmpty());
318        while (characterPredicate(*m_current)) {
319            if (++m_current == m_end)
320                return;
321        }
322    }
323
324    template<bool characterPredicate(UChar)>
325    String takeLeading()
326    {
327        ASSERT(!isEmpty());
328        const UChar* start = m_current;
329        skipLeading<characterPredicate>();
330        if (start == m_current)
331            return String();
332        return String(start, m_current - start);
333    }
334
335    const UChar* m_current;
336    const UChar* m_end;
337};
338
339
340HTMLTreeBuilder::HTMLTreeBuilder(HTMLDocumentParser* parser, HTMLDocument* document, bool reportErrors, bool usePreHTML5ParserQuirks)
341    : m_framesetOk(true)
342    , m_document(document)
343    , m_tree(document, FragmentScriptingAllowed, false)
344    , m_reportErrors(reportErrors)
345    , m_isPaused(false)
346    , m_insertionMode(InitialMode)
347    , m_originalInsertionMode(InitialMode)
348    , m_parser(parser)
349    , m_scriptToProcessStartPosition(uninitializedPositionValue1())
350    , m_lastScriptElementStartPosition(TextPosition0::belowRangePosition())
351    , m_usePreHTML5ParserQuirks(usePreHTML5ParserQuirks)
352    , m_hasPendingForeignInsertionModeSteps(false)
353{
354}
355
356// FIXME: Member variables should be grouped into self-initializing structs to
357// minimize code duplication between these constructors.
358HTMLTreeBuilder::HTMLTreeBuilder(HTMLDocumentParser* parser, DocumentFragment* fragment, Element* contextElement, FragmentScriptingPermission scriptingPermission, bool usePreHTML5ParserQuirks)
359    : m_framesetOk(true)
360    , m_fragmentContext(fragment, contextElement, scriptingPermission)
361    , m_document(m_fragmentContext.document())
362    , m_tree(m_document, scriptingPermission, true)
363    , m_reportErrors(false) // FIXME: Why not report errors in fragments?
364    , m_isPaused(false)
365    , m_insertionMode(InitialMode)
366    , m_originalInsertionMode(InitialMode)
367    , m_parser(parser)
368    , m_scriptToProcessStartPosition(uninitializedPositionValue1())
369    , m_lastScriptElementStartPosition(TextPosition0::belowRangePosition())
370    , m_usePreHTML5ParserQuirks(usePreHTML5ParserQuirks)
371    , m_hasPendingForeignInsertionModeSteps(false)
372{
373    if (contextElement) {
374        // Steps 4.2-4.6 of the HTML5 Fragment Case parsing algorithm:
375        // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-end.html#fragment-case
376        m_document->setCompatibilityMode(contextElement->document()->compatibilityMode());
377        processFakeStartTag(htmlTag);
378        resetInsertionModeAppropriately();
379        m_tree.setForm(closestFormAncestor(contextElement));
380    }
381}
382
383HTMLTreeBuilder::~HTMLTreeBuilder()
384{
385}
386
387void HTMLTreeBuilder::detach()
388{
389    // This call makes little sense in fragment mode, but for consistency
390    // DocumentParser expects detach() to always be called before it's destroyed.
391    m_document = 0;
392    // HTMLConstructionSite might be on the callstack when detach() is called
393    // otherwise we'd just call m_tree.clear() here instead.
394    m_tree.detach();
395}
396
397HTMLTreeBuilder::FragmentParsingContext::FragmentParsingContext()
398    : m_fragment(0)
399    , m_contextElement(0)
400    , m_scriptingPermission(FragmentScriptingAllowed)
401{
402}
403
404HTMLTreeBuilder::FragmentParsingContext::FragmentParsingContext(DocumentFragment* fragment, Element* contextElement, FragmentScriptingPermission scriptingPermission)
405    : m_dummyDocumentForFragmentParsing(HTMLDocument::create(0, KURL(), fragment->document()->baseURI()))
406    , m_fragment(fragment)
407    , m_contextElement(contextElement)
408    , m_scriptingPermission(scriptingPermission)
409{
410    m_dummyDocumentForFragmentParsing->setCompatibilityMode(fragment->document()->compatibilityMode());
411}
412
413Document* HTMLTreeBuilder::FragmentParsingContext::document() const
414{
415    ASSERT(m_fragment);
416    return m_dummyDocumentForFragmentParsing.get();
417}
418
419void HTMLTreeBuilder::FragmentParsingContext::finished()
420{
421    // Populate the DocumentFragment with the parsed content now that we're done.
422    ContainerNode* root = m_dummyDocumentForFragmentParsing.get();
423    if (m_contextElement)
424        root = m_dummyDocumentForFragmentParsing->documentElement();
425    m_fragment->takeAllChildrenFrom(root);
426}
427
428HTMLTreeBuilder::FragmentParsingContext::~FragmentParsingContext()
429{
430}
431
432PassRefPtr<Element> HTMLTreeBuilder::takeScriptToProcess(TextPosition1& scriptStartPosition)
433{
434    // Unpause ourselves, callers may pause us again when processing the script.
435    // The HTML5 spec is written as though scripts are executed inside the tree
436    // builder.  We pause the parser to exit the tree builder, and then resume
437    // before running scripts.
438    m_isPaused = false;
439    scriptStartPosition = m_scriptToProcessStartPosition;
440    m_scriptToProcessStartPosition = uninitializedPositionValue1();
441    return m_scriptToProcess.release();
442}
443
444void HTMLTreeBuilder::constructTreeFromToken(HTMLToken& rawToken)
445{
446    AtomicHTMLToken token(rawToken);
447    constructTreeFromAtomicToken(token);
448}
449
450void HTMLTreeBuilder::constructTreeFromAtomicToken(AtomicHTMLToken& token)
451{
452    processToken(token);
453
454    // Swallowing U+0000 characters isn't in the HTML5 spec, but turning all
455    // the U+0000 characters into replacement characters has compatibility
456    // problems.
457    m_parser->tokenizer()->setForceNullCharacterReplacement(m_insertionMode == TextMode || m_insertionMode == InForeignContentMode);
458    m_parser->tokenizer()->setShouldAllowCDATA(m_insertionMode == InForeignContentMode && m_tree.currentElement()->namespaceURI() != xhtmlNamespaceURI);
459}
460
461void HTMLTreeBuilder::processToken(AtomicHTMLToken& token)
462{
463    switch (token.type()) {
464    case HTMLToken::Uninitialized:
465        ASSERT_NOT_REACHED();
466        break;
467    case HTMLToken::DOCTYPE:
468        processDoctypeToken(token);
469        break;
470    case HTMLToken::StartTag:
471        processStartTag(token);
472        break;
473    case HTMLToken::EndTag:
474        processEndTag(token);
475        break;
476    case HTMLToken::Comment:
477        processComment(token);
478        return;
479    case HTMLToken::Character:
480        processCharacter(token);
481        break;
482    case HTMLToken::EndOfFile:
483        processEndOfFile(token);
484        break;
485    }
486}
487
488void HTMLTreeBuilder::processDoctypeToken(AtomicHTMLToken& token)
489{
490    ASSERT(token.type() == HTMLToken::DOCTYPE);
491    if (m_insertionMode == InitialMode) {
492        m_tree.insertDoctype(token);
493        setInsertionMode(BeforeHTMLMode);
494        return;
495    }
496    if (m_insertionMode == InTableTextMode) {
497        defaultForInTableText();
498        processDoctypeToken(token);
499        return;
500    }
501    parseError(token);
502}
503
504void HTMLTreeBuilder::processFakeStartTag(const QualifiedName& tagName, PassRefPtr<NamedNodeMap> attributes)
505{
506    // FIXME: We'll need a fancier conversion than just "localName" for SVG/MathML tags.
507    AtomicHTMLToken fakeToken(HTMLToken::StartTag, tagName.localName(), attributes);
508    processStartTag(fakeToken);
509}
510
511void HTMLTreeBuilder::processFakeEndTag(const QualifiedName& tagName)
512{
513    // FIXME: We'll need a fancier conversion than just "localName" for SVG/MathML tags.
514    AtomicHTMLToken fakeToken(HTMLToken::EndTag, tagName.localName());
515    processEndTag(fakeToken);
516}
517
518void HTMLTreeBuilder::processFakeCharacters(const String& characters)
519{
520    ASSERT(!characters.isEmpty());
521    ExternalCharacterTokenBuffer buffer(characters);
522    processCharacterBuffer(buffer);
523}
524
525void HTMLTreeBuilder::processFakePEndTagIfPInButtonScope()
526{
527    if (!m_tree.openElements()->inButtonScope(pTag.localName()))
528        return;
529    AtomicHTMLToken endP(HTMLToken::EndTag, pTag.localName());
530    processEndTag(endP);
531}
532
533PassRefPtr<NamedNodeMap> HTMLTreeBuilder::attributesForIsindexInput(AtomicHTMLToken& token)
534{
535    RefPtr<NamedNodeMap> attributes = token.takeAtributes();
536    if (!attributes)
537        attributes = NamedNodeMap::create();
538    else {
539        attributes->removeAttribute(nameAttr);
540        attributes->removeAttribute(actionAttr);
541        attributes->removeAttribute(promptAttr);
542    }
543
544    RefPtr<Attribute> mappedAttribute = Attribute::createMapped(nameAttr, isindexTag.localName());
545    attributes->insertAttribute(mappedAttribute.release(), false);
546    return attributes.release();
547}
548
549void HTMLTreeBuilder::processIsindexStartTagForInBody(AtomicHTMLToken& token)
550{
551    ASSERT(token.type() == HTMLToken::StartTag);
552    ASSERT(token.name() == isindexTag);
553    parseError(token);
554    if (m_tree.form())
555        return;
556    notImplemented(); // Acknowledge self-closing flag
557    processFakeStartTag(formTag);
558    RefPtr<Attribute> actionAttribute = token.getAttributeItem(actionAttr);
559    if (actionAttribute) {
560        ASSERT(m_tree.currentElement()->hasTagName(formTag));
561        m_tree.currentElement()->setAttribute(actionAttr, actionAttribute->value());
562    }
563    processFakeStartTag(hrTag);
564    processFakeStartTag(labelTag);
565    RefPtr<Attribute> promptAttribute = token.getAttributeItem(promptAttr);
566    if (promptAttribute)
567        processFakeCharacters(promptAttribute->value());
568    else
569        processFakeCharacters(searchableIndexIntroduction());
570    processFakeStartTag(inputTag, attributesForIsindexInput(token));
571    notImplemented(); // This second set of characters may be needed by non-english locales.
572    processFakeEndTag(labelTag);
573    processFakeStartTag(hrTag);
574    processFakeEndTag(formTag);
575}
576
577namespace {
578
579bool isLi(const Element* element)
580{
581    return element->hasTagName(liTag);
582}
583
584bool isDdOrDt(const Element* element)
585{
586    return element->hasTagName(ddTag)
587        || element->hasTagName(dtTag);
588}
589
590}
591
592template <bool shouldClose(const Element*)>
593void HTMLTreeBuilder::processCloseWhenNestedTag(AtomicHTMLToken& token)
594{
595    m_framesetOk = false;
596    HTMLElementStack::ElementRecord* nodeRecord = m_tree.openElements()->topRecord();
597    while (1) {
598        Element* node = nodeRecord->element();
599        if (shouldClose(node)) {
600            processFakeEndTag(node->tagQName());
601            break;
602        }
603        if (isSpecialNode(node) && !node->hasTagName(addressTag) && !node->hasTagName(divTag) && !node->hasTagName(pTag))
604            break;
605        nodeRecord = nodeRecord->next();
606    }
607    processFakePEndTagIfPInButtonScope();
608    m_tree.insertHTMLElement(token);
609}
610
611namespace {
612
613typedef HashMap<AtomicString, QualifiedName> PrefixedNameToQualifiedNameMap;
614
615void mapLoweredLocalNameToName(PrefixedNameToQualifiedNameMap* map, QualifiedName** names, size_t length)
616{
617    for (size_t i = 0; i < length; ++i) {
618        const QualifiedName& name = *names[i];
619        const AtomicString& localName = name.localName();
620        AtomicString loweredLocalName = localName.lower();
621        if (loweredLocalName != localName)
622            map->add(loweredLocalName, name);
623    }
624}
625
626void adjustSVGTagNameCase(AtomicHTMLToken& token)
627{
628    static PrefixedNameToQualifiedNameMap* caseMap = 0;
629    if (!caseMap) {
630        caseMap = new PrefixedNameToQualifiedNameMap;
631        size_t length = 0;
632        QualifiedName** svgTags = SVGNames::getSVGTags(&length);
633        mapLoweredLocalNameToName(caseMap, svgTags, length);
634    }
635
636    const QualifiedName& casedName = caseMap->get(token.name());
637    if (casedName.localName().isNull())
638        return;
639    token.setName(casedName.localName());
640}
641
642template<QualifiedName** getAttrs(size_t* length)>
643void adjustAttributes(AtomicHTMLToken& token)
644{
645    static PrefixedNameToQualifiedNameMap* caseMap = 0;
646    if (!caseMap) {
647        caseMap = new PrefixedNameToQualifiedNameMap;
648        size_t length = 0;
649        QualifiedName** attrs = getAttrs(&length);
650        mapLoweredLocalNameToName(caseMap, attrs, length);
651    }
652
653    NamedNodeMap* attributes = token.attributes();
654    if (!attributes)
655        return;
656
657    for (unsigned x = 0; x < attributes->length(); ++x) {
658        Attribute* attribute = attributes->attributeItem(x);
659        const QualifiedName& casedName = caseMap->get(attribute->localName());
660        if (!casedName.localName().isNull())
661            attribute->parserSetName(casedName);
662    }
663}
664
665void adjustSVGAttributes(AtomicHTMLToken& token)
666{
667    adjustAttributes<SVGNames::getSVGAttrs>(token);
668}
669
670void adjustMathMLAttributes(AtomicHTMLToken& token)
671{
672    adjustAttributes<MathMLNames::getMathMLAttrs>(token);
673}
674
675void addNamesWithPrefix(PrefixedNameToQualifiedNameMap* map, const AtomicString& prefix, QualifiedName** names, size_t length)
676{
677    for (size_t i = 0; i < length; ++i) {
678        QualifiedName* name = names[i];
679        const AtomicString& localName = name->localName();
680        AtomicString prefixColonLocalName(prefix + ":" + localName);
681        QualifiedName nameWithPrefix(prefix, localName, name->namespaceURI());
682        map->add(prefixColonLocalName, nameWithPrefix);
683    }
684}
685
686void adjustForeignAttributes(AtomicHTMLToken& token)
687{
688    static PrefixedNameToQualifiedNameMap* map = 0;
689    if (!map) {
690        map = new PrefixedNameToQualifiedNameMap;
691        size_t length = 0;
692        QualifiedName** attrs = XLinkNames::getXLinkAttrs(&length);
693        addNamesWithPrefix(map, "xlink", attrs, length);
694
695        attrs = XMLNames::getXMLAttrs(&length);
696        addNamesWithPrefix(map, "xml", attrs, length);
697
698        map->add("xmlns", XMLNSNames::xmlnsAttr);
699        map->add("xmlns:xlink", QualifiedName("xmlns", "xlink", XMLNSNames::xmlnsNamespaceURI));
700    }
701
702    NamedNodeMap* attributes = token.attributes();
703    if (!attributes)
704        return;
705
706    for (unsigned x = 0; x < attributes->length(); ++x) {
707        Attribute* attribute = attributes->attributeItem(x);
708        const QualifiedName& name = map->get(attribute->localName());
709        if (!name.localName().isNull())
710            attribute->parserSetName(name);
711    }
712}
713
714}
715
716void HTMLTreeBuilder::processStartTagForInBody(AtomicHTMLToken& token)
717{
718    ASSERT(token.type() == HTMLToken::StartTag);
719    if (token.name() == htmlTag) {
720        m_tree.insertHTMLHtmlStartTagInBody(token);
721        return;
722    }
723    if (token.name() == baseTag
724        || token.name() == basefontTag
725        || token.name() == bgsoundTag
726        || token.name() == commandTag
727        || token.name() == linkTag
728        || token.name() == metaTag
729        || token.name() == noframesTag
730        || token.name() == scriptTag
731        || token.name() == styleTag
732        || token.name() == titleTag) {
733        bool didProcess = processStartTagForInHead(token);
734        ASSERT_UNUSED(didProcess, didProcess);
735        return;
736    }
737    if (token.name() == bodyTag) {
738        if (!m_tree.openElements()->secondElementIsHTMLBodyElement() || m_tree.openElements()->hasOnlyOneElement()) {
739            ASSERT(isParsingFragment());
740            return;
741        }
742        m_tree.insertHTMLBodyStartTagInBody(token);
743        return;
744    }
745    if (token.name() == framesetTag) {
746        parseError(token);
747        if (!m_tree.openElements()->secondElementIsHTMLBodyElement() || m_tree.openElements()->hasOnlyOneElement()) {
748            ASSERT(isParsingFragment());
749            return;
750        }
751        if (!m_framesetOk)
752            return;
753        ExceptionCode ec = 0;
754        m_tree.openElements()->bodyElement()->remove(ec);
755        ASSERT(!ec);
756        m_tree.openElements()->popUntil(m_tree.openElements()->bodyElement());
757        m_tree.openElements()->popHTMLBodyElement();
758        ASSERT(m_tree.openElements()->top() == m_tree.openElements()->htmlElement());
759        m_tree.insertHTMLElement(token);
760        setInsertionMode(InFramesetMode);
761        return;
762    }
763    if (token.name() == addressTag
764        || token.name() == articleTag
765        || token.name() == asideTag
766        || token.name() == blockquoteTag
767        || token.name() == centerTag
768        || token.name() == detailsTag
769        || token.name() == dirTag
770        || token.name() == divTag
771        || token.name() == dlTag
772        || token.name() == fieldsetTag
773        || token.name() == figcaptionTag
774        || token.name() == figureTag
775        || token.name() == footerTag
776        || token.name() == headerTag
777        || token.name() == hgroupTag
778        || token.name() == menuTag
779        || token.name() == navTag
780        || token.name() == olTag
781        || token.name() == pTag
782        || token.name() == sectionTag
783        || token.name() == summaryTag
784        || token.name() == ulTag) {
785        processFakePEndTagIfPInButtonScope();
786        m_tree.insertHTMLElement(token);
787        return;
788    }
789    if (isNumberedHeaderTag(token.name())) {
790        processFakePEndTagIfPInButtonScope();
791        if (isNumberedHeaderTag(m_tree.currentElement()->localName())) {
792            parseError(token);
793            m_tree.openElements()->pop();
794        }
795        m_tree.insertHTMLElement(token);
796        return;
797    }
798    if (token.name() == preTag || token.name() == listingTag) {
799        processFakePEndTagIfPInButtonScope();
800        m_tree.insertHTMLElement(token);
801        m_parser->tokenizer()->setSkipLeadingNewLineForListing(true);
802        m_framesetOk = false;
803        return;
804    }
805    if (token.name() == formTag) {
806        if (m_tree.form()) {
807            parseError(token);
808            return;
809        }
810        processFakePEndTagIfPInButtonScope();
811        m_tree.insertHTMLFormElement(token);
812        return;
813    }
814    if (token.name() == liTag) {
815        processCloseWhenNestedTag<isLi>(token);
816        return;
817    }
818    if (token.name() == ddTag || token.name() == dtTag) {
819        processCloseWhenNestedTag<isDdOrDt>(token);
820        return;
821    }
822    if (token.name() == plaintextTag) {
823        processFakePEndTagIfPInButtonScope();
824        m_tree.insertHTMLElement(token);
825        m_parser->tokenizer()->setState(HTMLTokenizer::PLAINTEXTState);
826        return;
827    }
828    if (token.name() == buttonTag) {
829        if (m_tree.openElements()->inScope(buttonTag)) {
830            parseError(token);
831            processFakeEndTag(buttonTag);
832            reprocessStartTag(token); // FIXME: Could we just fall through here?
833            return;
834        }
835        m_tree.reconstructTheActiveFormattingElements();
836        m_tree.insertHTMLElement(token);
837        m_framesetOk = false;
838        return;
839    }
840    if (token.name() == aTag) {
841        Element* activeATag = m_tree.activeFormattingElements()->closestElementInScopeWithName(aTag.localName());
842        if (activeATag) {
843            parseError(token);
844            processFakeEndTag(aTag);
845            m_tree.activeFormattingElements()->remove(activeATag);
846            if (m_tree.openElements()->contains(activeATag))
847                m_tree.openElements()->remove(activeATag);
848        }
849        m_tree.reconstructTheActiveFormattingElements();
850        m_tree.insertFormattingElement(token);
851        return;
852    }
853    if (isNonAnchorNonNobrFormattingTag(token.name())) {
854        m_tree.reconstructTheActiveFormattingElements();
855        m_tree.insertFormattingElement(token);
856        return;
857    }
858    if (token.name() == nobrTag) {
859        m_tree.reconstructTheActiveFormattingElements();
860        if (m_tree.openElements()->inScope(nobrTag)) {
861            parseError(token);
862            processFakeEndTag(nobrTag);
863            m_tree.reconstructTheActiveFormattingElements();
864        }
865        m_tree.insertFormattingElement(token);
866        return;
867    }
868    if (token.name() == appletTag
869        || token.name() == marqueeTag
870        || token.name() == objectTag) {
871        m_tree.reconstructTheActiveFormattingElements();
872        m_tree.insertHTMLElement(token);
873        m_tree.activeFormattingElements()->appendMarker();
874        m_framesetOk = false;
875        return;
876    }
877    if (token.name() == tableTag) {
878        if (!m_document->inQuirksMode() && m_tree.openElements()->inButtonScope(pTag))
879            processFakeEndTag(pTag);
880        m_tree.insertHTMLElement(token);
881        m_framesetOk = false;
882        setInsertionMode(InTableMode);
883        return;
884    }
885    if (token.name() == imageTag) {
886        parseError(token);
887        // Apparently we're not supposed to ask.
888        token.setName(imgTag.localName());
889        prepareToReprocessToken();
890        // Note the fall through to the imgTag handling below!
891    }
892    if (token.name() == areaTag
893        || token.name() == brTag
894        || token.name() == embedTag
895        || token.name() == imgTag
896        || token.name() == keygenTag
897        || token.name() == wbrTag) {
898        m_tree.reconstructTheActiveFormattingElements();
899        m_tree.insertSelfClosingHTMLElement(token);
900        m_framesetOk = false;
901        return;
902    }
903    if (token.name() == inputTag) {
904        RefPtr<Attribute> typeAttribute = token.getAttributeItem(typeAttr);
905        m_tree.reconstructTheActiveFormattingElements();
906        m_tree.insertSelfClosingHTMLElement(token);
907        if (!typeAttribute || !equalIgnoringCase(typeAttribute->value(), "hidden"))
908            m_framesetOk = false;
909        return;
910    }
911    if (token.name() == paramTag
912        || token.name() == sourceTag
913        || token.name() == trackTag) {
914        m_tree.insertSelfClosingHTMLElement(token);
915        return;
916    }
917    if (token.name() == hrTag) {
918        processFakePEndTagIfPInButtonScope();
919        m_tree.insertSelfClosingHTMLElement(token);
920        m_framesetOk = false;
921        return;
922    }
923    if (token.name() == isindexTag) {
924        processIsindexStartTagForInBody(token);
925        return;
926    }
927    if (token.name() == textareaTag) {
928        m_tree.insertHTMLElement(token);
929        m_parser->tokenizer()->setSkipLeadingNewLineForListing(true);
930        m_parser->tokenizer()->setState(HTMLTokenizer::RCDATAState);
931        m_originalInsertionMode = m_insertionMode;
932        m_framesetOk = false;
933        setInsertionMode(TextMode);
934        return;
935    }
936    if (token.name() == xmpTag) {
937        processFakePEndTagIfPInButtonScope();
938        m_tree.reconstructTheActiveFormattingElements();
939        m_framesetOk = false;
940        processGenericRawTextStartTag(token);
941        return;
942    }
943    if (token.name() == iframeTag) {
944        m_framesetOk = false;
945        processGenericRawTextStartTag(token);
946        return;
947    }
948    if (token.name() == noembedTag && pluginsEnabled(m_document->frame())) {
949        processGenericRawTextStartTag(token);
950        return;
951    }
952    if (token.name() == noscriptTag && scriptEnabled(m_document->frame())) {
953        processGenericRawTextStartTag(token);
954        return;
955    }
956    if (token.name() == selectTag) {
957        m_tree.reconstructTheActiveFormattingElements();
958        m_tree.insertHTMLElement(token);
959        m_framesetOk = false;
960        if (m_insertionMode == InTableMode
961             || m_insertionMode == InCaptionMode
962             || m_insertionMode == InColumnGroupMode
963             || m_insertionMode == InTableBodyMode
964             || m_insertionMode == InRowMode
965             || m_insertionMode == InCellMode)
966            setInsertionMode(InSelectInTableMode);
967        else
968            setInsertionMode(InSelectMode);
969        return;
970    }
971    if (token.name() == optgroupTag || token.name() == optionTag) {
972        if (m_tree.openElements()->inScope(optionTag.localName())) {
973            AtomicHTMLToken endOption(HTMLToken::EndTag, optionTag.localName());
974            processEndTag(endOption);
975        }
976        m_tree.reconstructTheActiveFormattingElements();
977        m_tree.insertHTMLElement(token);
978        return;
979    }
980    if (token.name() == rpTag || token.name() == rtTag) {
981        if (m_tree.openElements()->inScope(rubyTag.localName())) {
982            m_tree.generateImpliedEndTags();
983            if (!m_tree.currentElement()->hasTagName(rubyTag)) {
984                parseError(token);
985                m_tree.openElements()->popUntil(rubyTag.localName());
986            }
987        }
988        m_tree.insertHTMLElement(token);
989        return;
990    }
991    if (token.name() == MathMLNames::mathTag.localName()) {
992        m_tree.reconstructTheActiveFormattingElements();
993        adjustMathMLAttributes(token);
994        adjustForeignAttributes(token);
995        m_tree.insertForeignElement(token, MathMLNames::mathmlNamespaceURI);
996        if (m_insertionMode != InForeignContentMode)
997            setInsertionMode(InForeignContentMode);
998        return;
999    }
1000    if (token.name() == SVGNames::svgTag.localName()) {
1001        m_tree.reconstructTheActiveFormattingElements();
1002        adjustSVGAttributes(token);
1003        adjustForeignAttributes(token);
1004        m_tree.insertForeignElement(token, SVGNames::svgNamespaceURI);
1005        if (m_insertionMode != InForeignContentMode)
1006            setInsertionMode(InForeignContentMode);
1007        return;
1008    }
1009    if (isCaptionColOrColgroupTag(token.name())
1010        || token.name() == frameTag
1011        || token.name() == headTag
1012        || isTableBodyContextTag(token.name())
1013        || isTableCellContextTag(token.name())
1014        || token.name() == trTag) {
1015        parseError(token);
1016        return;
1017    }
1018    m_tree.reconstructTheActiveFormattingElements();
1019    m_tree.insertHTMLElement(token);
1020}
1021
1022bool HTMLTreeBuilder::processColgroupEndTagForInColumnGroup()
1023{
1024    if (m_tree.currentElement() == m_tree.openElements()->htmlElement()) {
1025        ASSERT(isParsingFragment());
1026        // FIXME: parse error
1027        return false;
1028    }
1029    m_tree.openElements()->pop();
1030    setInsertionMode(InTableMode);
1031    return true;
1032}
1033
1034// http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#close-the-cell
1035void HTMLTreeBuilder::closeTheCell()
1036{
1037    ASSERT(insertionMode() == InCellMode);
1038    if (m_tree.openElements()->inTableScope(tdTag)) {
1039        ASSERT(!m_tree.openElements()->inTableScope(thTag));
1040        processFakeEndTag(tdTag);
1041        return;
1042    }
1043    ASSERT(m_tree.openElements()->inTableScope(thTag));
1044    processFakeEndTag(thTag);
1045    ASSERT(insertionMode() == InRowMode);
1046}
1047
1048void HTMLTreeBuilder::processStartTagForInTable(AtomicHTMLToken& token)
1049{
1050    ASSERT(token.type() == HTMLToken::StartTag);
1051    if (token.name() == captionTag) {
1052        m_tree.openElements()->popUntilTableScopeMarker();
1053        m_tree.activeFormattingElements()->appendMarker();
1054        m_tree.insertHTMLElement(token);
1055        setInsertionMode(InCaptionMode);
1056        return;
1057    }
1058    if (token.name() == colgroupTag) {
1059        m_tree.openElements()->popUntilTableScopeMarker();
1060        m_tree.insertHTMLElement(token);
1061        setInsertionMode(InColumnGroupMode);
1062        return;
1063    }
1064    if (token.name() == colTag) {
1065        processFakeStartTag(colgroupTag);
1066        ASSERT(InColumnGroupMode);
1067        reprocessStartTag(token);
1068        return;
1069    }
1070    if (isTableBodyContextTag(token.name())) {
1071        m_tree.openElements()->popUntilTableScopeMarker();
1072        m_tree.insertHTMLElement(token);
1073        setInsertionMode(InTableBodyMode);
1074        return;
1075    }
1076    if (isTableCellContextTag(token.name())
1077        || token.name() == trTag) {
1078        processFakeStartTag(tbodyTag);
1079        ASSERT(insertionMode() == InTableBodyMode);
1080        reprocessStartTag(token);
1081        return;
1082    }
1083    if (token.name() == tableTag) {
1084        parseError(token);
1085        if (!processTableEndTagForInTable()) {
1086            ASSERT(isParsingFragment());
1087            return;
1088        }
1089        reprocessStartTag(token);
1090        return;
1091    }
1092    if (token.name() == styleTag || token.name() == scriptTag) {
1093        processStartTagForInHead(token);
1094        return;
1095    }
1096    if (token.name() == inputTag) {
1097        Attribute* typeAttribute = token.getAttributeItem(typeAttr);
1098        if (typeAttribute && equalIgnoringCase(typeAttribute->value(), "hidden")) {
1099            parseError(token);
1100            m_tree.insertSelfClosingHTMLElement(token);
1101            return;
1102        }
1103        // Fall through to "anything else" case.
1104    }
1105    if (token.name() == formTag) {
1106        parseError(token);
1107        if (m_tree.form())
1108            return;
1109        m_tree.insertHTMLFormElement(token, true);
1110        m_tree.openElements()->pop();
1111        return;
1112    }
1113    parseError(token);
1114    HTMLConstructionSite::RedirectToFosterParentGuard redirecter(m_tree);
1115    processStartTagForInBody(token);
1116}
1117
1118namespace {
1119
1120bool shouldProcessForeignContentUsingInBodyInsertionMode(AtomicHTMLToken& token, Element* currentElement)
1121{
1122    ASSERT(token.type() == HTMLToken::StartTag);
1123    if (currentElement->hasTagName(MathMLNames::miTag)
1124        || currentElement->hasTagName(MathMLNames::moTag)
1125        || currentElement->hasTagName(MathMLNames::mnTag)
1126        || currentElement->hasTagName(MathMLNames::msTag)
1127        || currentElement->hasTagName(MathMLNames::mtextTag)) {
1128        return token.name() != MathMLNames::mglyphTag
1129            && token.name() != MathMLNames::malignmarkTag;
1130    }
1131    if (currentElement->hasTagName(MathMLNames::annotation_xmlTag))
1132        return token.name() == SVGNames::svgTag;
1133    if (currentElement->hasTagName(SVGNames::foreignObjectTag)
1134        || currentElement->hasTagName(SVGNames::descTag)
1135        || currentElement->hasTagName(SVGNames::titleTag))
1136        return true;
1137    return currentElement->namespaceURI() == HTMLNames::xhtmlNamespaceURI;
1138}
1139
1140}
1141
1142void HTMLTreeBuilder::processStartTag(AtomicHTMLToken& token)
1143{
1144    ASSERT(token.type() == HTMLToken::StartTag);
1145    switch (insertionMode()) {
1146    case InitialMode:
1147        ASSERT(insertionMode() == InitialMode);
1148        defaultForInitial();
1149        // Fall through.
1150    case BeforeHTMLMode:
1151        ASSERT(insertionMode() == BeforeHTMLMode);
1152        if (token.name() == htmlTag) {
1153            m_tree.insertHTMLHtmlStartTagBeforeHTML(token);
1154            setInsertionMode(BeforeHeadMode);
1155            return;
1156        }
1157        defaultForBeforeHTML();
1158        // Fall through.
1159    case BeforeHeadMode:
1160        ASSERT(insertionMode() == BeforeHeadMode);
1161        if (token.name() == htmlTag) {
1162            m_tree.insertHTMLHtmlStartTagInBody(token);
1163            return;
1164        }
1165        if (token.name() == headTag) {
1166            m_tree.insertHTMLHeadElement(token);
1167            setInsertionMode(InHeadMode);
1168            return;
1169        }
1170        defaultForBeforeHead();
1171        // Fall through.
1172    case InHeadMode:
1173        ASSERT(insertionMode() == InHeadMode);
1174        if (processStartTagForInHead(token))
1175            return;
1176        defaultForInHead();
1177        // Fall through.
1178    case AfterHeadMode:
1179        ASSERT(insertionMode() == AfterHeadMode);
1180        if (token.name() == htmlTag) {
1181            m_tree.insertHTMLHtmlStartTagInBody(token);
1182            return;
1183        }
1184        if (token.name() == bodyTag) {
1185            m_framesetOk = false;
1186            m_tree.insertHTMLBodyElement(token);
1187            setInsertionMode(InBodyMode);
1188            return;
1189        }
1190        if (token.name() == framesetTag) {
1191            m_tree.insertHTMLElement(token);
1192            setInsertionMode(InFramesetMode);
1193            return;
1194        }
1195        if (token.name() == baseTag
1196            || token.name() == basefontTag
1197            || token.name() == bgsoundTag
1198            || token.name() == linkTag
1199            || token.name() == metaTag
1200            || token.name() == noframesTag
1201            || token.name() == scriptTag
1202            || token.name() == styleTag
1203            || token.name() == titleTag) {
1204            parseError(token);
1205            ASSERT(m_tree.head());
1206            m_tree.openElements()->pushHTMLHeadElement(m_tree.head());
1207            processStartTagForInHead(token);
1208            m_tree.openElements()->removeHTMLHeadElement(m_tree.head());
1209            return;
1210        }
1211        if (token.name() == headTag) {
1212            parseError(token);
1213            return;
1214        }
1215        defaultForAfterHead();
1216        // Fall through
1217    case InBodyMode:
1218        ASSERT(insertionMode() == InBodyMode);
1219        processStartTagForInBody(token);
1220        break;
1221    case InTableMode:
1222        ASSERT(insertionMode() == InTableMode);
1223        processStartTagForInTable(token);
1224        break;
1225    case InCaptionMode:
1226        ASSERT(insertionMode() == InCaptionMode);
1227        if (isCaptionColOrColgroupTag(token.name())
1228            || isTableBodyContextTag(token.name())
1229            || isTableCellContextTag(token.name())
1230            || token.name() == trTag) {
1231            parseError(token);
1232            if (!processCaptionEndTagForInCaption()) {
1233                ASSERT(isParsingFragment());
1234                return;
1235            }
1236            reprocessStartTag(token);
1237            return;
1238        }
1239        processStartTagForInBody(token);
1240        break;
1241    case InColumnGroupMode:
1242        ASSERT(insertionMode() == InColumnGroupMode);
1243        if (token.name() == htmlTag) {
1244            m_tree.insertHTMLHtmlStartTagInBody(token);
1245            return;
1246        }
1247        if (token.name() == colTag) {
1248            m_tree.insertSelfClosingHTMLElement(token);
1249            return;
1250        }
1251        if (!processColgroupEndTagForInColumnGroup()) {
1252            ASSERT(isParsingFragment());
1253            return;
1254        }
1255        reprocessStartTag(token);
1256        break;
1257    case InTableBodyMode:
1258        ASSERT(insertionMode() == InTableBodyMode);
1259        if (token.name() == trTag) {
1260            m_tree.openElements()->popUntilTableBodyScopeMarker(); // How is there ever anything to pop?
1261            m_tree.insertHTMLElement(token);
1262            setInsertionMode(InRowMode);
1263            return;
1264        }
1265        if (isTableCellContextTag(token.name())) {
1266            parseError(token);
1267            processFakeStartTag(trTag);
1268            ASSERT(insertionMode() == InRowMode);
1269            reprocessStartTag(token);
1270            return;
1271        }
1272        if (isCaptionColOrColgroupTag(token.name()) || isTableBodyContextTag(token.name())) {
1273            // FIXME: This is slow.
1274            if (!m_tree.openElements()->inTableScope(tbodyTag.localName()) && !m_tree.openElements()->inTableScope(theadTag.localName()) && !m_tree.openElements()->inTableScope(tfootTag.localName())) {
1275                ASSERT(isParsingFragment());
1276                parseError(token);
1277                return;
1278            }
1279            m_tree.openElements()->popUntilTableBodyScopeMarker();
1280            ASSERT(isTableBodyContextTag(m_tree.currentElement()->localName()));
1281            processFakeEndTag(m_tree.currentElement()->tagQName());
1282            reprocessStartTag(token);
1283            return;
1284        }
1285        processStartTagForInTable(token);
1286        break;
1287    case InRowMode:
1288        ASSERT(insertionMode() == InRowMode);
1289        if (isTableCellContextTag(token.name())) {
1290            m_tree.openElements()->popUntilTableRowScopeMarker();
1291            m_tree.insertHTMLElement(token);
1292            setInsertionMode(InCellMode);
1293            m_tree.activeFormattingElements()->appendMarker();
1294            return;
1295        }
1296        if (token.name() == trTag
1297            || isCaptionColOrColgroupTag(token.name())
1298            || isTableBodyContextTag(token.name())) {
1299            if (!processTrEndTagForInRow()) {
1300                ASSERT(isParsingFragment());
1301                return;
1302            }
1303            ASSERT(insertionMode() == InTableBodyMode);
1304            reprocessStartTag(token);
1305            return;
1306        }
1307        processStartTagForInTable(token);
1308        break;
1309    case InCellMode:
1310        ASSERT(insertionMode() == InCellMode);
1311        if (isCaptionColOrColgroupTag(token.name())
1312            || isTableCellContextTag(token.name())
1313            || token.name() == trTag
1314            || isTableBodyContextTag(token.name())) {
1315            // FIXME: This could be more efficient.
1316            if (!m_tree.openElements()->inTableScope(tdTag) && !m_tree.openElements()->inTableScope(thTag)) {
1317                ASSERT(isParsingFragment());
1318                parseError(token);
1319                return;
1320            }
1321            closeTheCell();
1322            reprocessStartTag(token);
1323            return;
1324        }
1325        processStartTagForInBody(token);
1326        break;
1327    case AfterBodyMode:
1328    case AfterAfterBodyMode:
1329        ASSERT(insertionMode() == AfterBodyMode || insertionMode() == AfterAfterBodyMode);
1330        if (token.name() == htmlTag) {
1331            m_tree.insertHTMLHtmlStartTagInBody(token);
1332            return;
1333        }
1334        setInsertionMode(InBodyMode);
1335        reprocessStartTag(token);
1336        break;
1337    case InHeadNoscriptMode:
1338        ASSERT(insertionMode() == InHeadNoscriptMode);
1339        if (token.name() == htmlTag) {
1340            m_tree.insertHTMLHtmlStartTagInBody(token);
1341            return;
1342        }
1343        if (token.name() == basefontTag
1344            || token.name() == bgsoundTag
1345            || token.name() == linkTag
1346            || token.name() == metaTag
1347            || token.name() == noframesTag
1348            || token.name() == styleTag) {
1349            bool didProcess = processStartTagForInHead(token);
1350            ASSERT_UNUSED(didProcess, didProcess);
1351            return;
1352        }
1353        if (token.name() == htmlTag || token.name() == noscriptTag) {
1354            parseError(token);
1355            return;
1356        }
1357        defaultForInHeadNoscript();
1358        processToken(token);
1359        break;
1360    case InFramesetMode:
1361        ASSERT(insertionMode() == InFramesetMode);
1362        if (token.name() == htmlTag) {
1363            m_tree.insertHTMLHtmlStartTagInBody(token);
1364            return;
1365        }
1366        if (token.name() == framesetTag) {
1367            m_tree.insertHTMLElement(token);
1368            return;
1369        }
1370        if (token.name() == frameTag) {
1371            m_tree.insertSelfClosingHTMLElement(token);
1372            return;
1373        }
1374        if (token.name() == noframesTag) {
1375            processStartTagForInHead(token);
1376            return;
1377        }
1378        parseError(token);
1379        break;
1380    case AfterFramesetMode:
1381    case AfterAfterFramesetMode:
1382        ASSERT(insertionMode() == AfterFramesetMode || insertionMode() == AfterAfterFramesetMode);
1383        if (token.name() == htmlTag) {
1384            m_tree.insertHTMLHtmlStartTagInBody(token);
1385            return;
1386        }
1387        if (token.name() == noframesTag) {
1388            processStartTagForInHead(token);
1389            return;
1390        }
1391        parseError(token);
1392        break;
1393    case InSelectInTableMode:
1394        ASSERT(insertionMode() == InSelectInTableMode);
1395        if (token.name() == captionTag
1396            || token.name() == tableTag
1397            || isTableBodyContextTag(token.name())
1398            || token.name() == trTag
1399            || isTableCellContextTag(token.name())) {
1400            parseError(token);
1401            AtomicHTMLToken endSelect(HTMLToken::EndTag, selectTag.localName());
1402            processEndTag(endSelect);
1403            reprocessStartTag(token);
1404            return;
1405        }
1406        // Fall through
1407    case InSelectMode:
1408        ASSERT(insertionMode() == InSelectMode || insertionMode() == InSelectInTableMode);
1409        if (token.name() == htmlTag) {
1410            m_tree.insertHTMLHtmlStartTagInBody(token);
1411            return;
1412        }
1413        if (token.name() == optionTag) {
1414            if (m_tree.currentElement()->hasTagName(optionTag)) {
1415                AtomicHTMLToken endOption(HTMLToken::EndTag, optionTag.localName());
1416                processEndTag(endOption);
1417            }
1418            m_tree.insertHTMLElement(token);
1419            return;
1420        }
1421        if (token.name() == optgroupTag) {
1422            if (m_tree.currentElement()->hasTagName(optionTag)) {
1423                AtomicHTMLToken endOption(HTMLToken::EndTag, optionTag.localName());
1424                processEndTag(endOption);
1425            }
1426            if (m_tree.currentElement()->hasTagName(optgroupTag)) {
1427                AtomicHTMLToken endOptgroup(HTMLToken::EndTag, optgroupTag.localName());
1428                processEndTag(endOptgroup);
1429            }
1430            m_tree.insertHTMLElement(token);
1431            return;
1432        }
1433        if (token.name() == selectTag) {
1434            parseError(token);
1435            AtomicHTMLToken endSelect(HTMLToken::EndTag, selectTag.localName());
1436            processEndTag(endSelect);
1437            return;
1438        }
1439        if (token.name() == inputTag
1440            || token.name() == keygenTag
1441            || token.name() == textareaTag) {
1442            parseError(token);
1443            if (!m_tree.openElements()->inSelectScope(selectTag)) {
1444                ASSERT(isParsingFragment());
1445                return;
1446            }
1447            AtomicHTMLToken endSelect(HTMLToken::EndTag, selectTag.localName());
1448            processEndTag(endSelect);
1449            reprocessStartTag(token);
1450            return;
1451        }
1452        if (token.name() == scriptTag) {
1453            bool didProcess = processStartTagForInHead(token);
1454            ASSERT_UNUSED(didProcess, didProcess);
1455            return;
1456        }
1457        break;
1458    case InTableTextMode:
1459        defaultForInTableText();
1460        processStartTag(token);
1461        break;
1462    case InForeignContentMode: {
1463        if (shouldProcessForeignContentUsingInBodyInsertionMode(token, m_tree.currentElement())) {
1464            processForeignContentUsingInBodyModeAndResetMode(token);
1465            return;
1466        }
1467        if (token.name() == bTag
1468            || token.name() == bigTag
1469            || token.name() == blockquoteTag
1470            || token.name() == bodyTag
1471            || token.name() == brTag
1472            || token.name() == centerTag
1473            || token.name() == codeTag
1474            || token.name() == ddTag
1475            || token.name() == divTag
1476            || token.name() == dlTag
1477            || token.name() == dtTag
1478            || token.name() == emTag
1479            || token.name() == embedTag
1480            || isNumberedHeaderTag(token.name())
1481            || token.name() == headTag
1482            || token.name() == hrTag
1483            || token.name() == iTag
1484            || token.name() == imgTag
1485            || token.name() == liTag
1486            || token.name() == listingTag
1487            || token.name() == menuTag
1488            || token.name() == metaTag
1489            || token.name() == nobrTag
1490            || token.name() == olTag
1491            || token.name() == pTag
1492            || token.name() == preTag
1493            || token.name() == rubyTag
1494            || token.name() == sTag
1495            || token.name() == smallTag
1496            || token.name() == spanTag
1497            || token.name() == strongTag
1498            || token.name() == strikeTag
1499            || token.name() == subTag
1500            || token.name() == supTag
1501            || token.name() == tableTag
1502            || token.name() == ttTag
1503            || token.name() == uTag
1504            || token.name() == ulTag
1505            || token.name() == varTag
1506            || (token.name() == fontTag && (token.getAttributeItem(colorAttr) || token.getAttributeItem(faceAttr) || token.getAttributeItem(sizeAttr)))) {
1507            parseError(token);
1508            m_tree.openElements()->popUntilForeignContentScopeMarker();
1509            resetInsertionModeAppropriately();
1510            reprocessStartTag(token);
1511            return;
1512        }
1513        const AtomicString& currentNamespace = m_tree.currentElement()->namespaceURI();
1514        if (currentNamespace == MathMLNames::mathmlNamespaceURI)
1515            adjustMathMLAttributes(token);
1516        if (currentNamespace == SVGNames::svgNamespaceURI) {
1517            adjustSVGTagNameCase(token);
1518            adjustSVGAttributes(token);
1519        }
1520        adjustForeignAttributes(token);
1521        m_tree.insertForeignElement(token, currentNamespace);
1522        break;
1523    }
1524    case TextMode:
1525        ASSERT_NOT_REACHED();
1526        break;
1527    }
1528}
1529
1530bool HTMLTreeBuilder::processBodyEndTagForInBody(AtomicHTMLToken& token)
1531{
1532    ASSERT(token.type() == HTMLToken::EndTag);
1533    ASSERT(token.name() == bodyTag);
1534    if (!m_tree.openElements()->inScope(bodyTag.localName())) {
1535        parseError(token);
1536        return false;
1537    }
1538    notImplemented(); // Emit a more specific parse error based on stack contents.
1539    setInsertionMode(AfterBodyMode);
1540    return true;
1541}
1542
1543void HTMLTreeBuilder::processAnyOtherEndTagForInBody(AtomicHTMLToken& token)
1544{
1545    ASSERT(token.type() == HTMLToken::EndTag);
1546    HTMLElementStack::ElementRecord* record = m_tree.openElements()->topRecord();
1547    while (1) {
1548        Element* node = record->element();
1549        if (node->hasLocalName(token.name())) {
1550            m_tree.generateImpliedEndTags();
1551            if (!m_tree.currentElement()->hasLocalName(token.name())) {
1552                parseError(token);
1553                // FIXME: This is either a bug in the spec, or a bug in our
1554                // implementation.  Filed a bug with HTML5:
1555                // http://www.w3.org/Bugs/Public/show_bug.cgi?id=10080
1556                // We might have already popped the node for the token in
1557                // generateImpliedEndTags, just abort.
1558                if (!m_tree.openElements()->contains(node))
1559                    return;
1560            }
1561            m_tree.openElements()->popUntilPopped(node);
1562            return;
1563        }
1564        if (isSpecialNode(node)) {
1565            parseError(token);
1566            return;
1567        }
1568        record = record->next();
1569    }
1570}
1571
1572// FIXME: This probably belongs on HTMLElementStack.
1573HTMLElementStack::ElementRecord* HTMLTreeBuilder::furthestBlockForFormattingElement(Element* formattingElement)
1574{
1575    HTMLElementStack::ElementRecord* furthestBlock = 0;
1576    HTMLElementStack::ElementRecord* record = m_tree.openElements()->topRecord();
1577    for (; record; record = record->next()) {
1578        if (record->element() == formattingElement)
1579            return furthestBlock;
1580        if (isSpecialNode(record->element()))
1581            furthestBlock = record;
1582    }
1583    ASSERT_NOT_REACHED();
1584    return 0;
1585}
1586
1587// http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#parsing-main-inbody
1588void HTMLTreeBuilder::callTheAdoptionAgency(AtomicHTMLToken& token)
1589{
1590    // The adoption agency algorithm is N^2.  We limit the number of iterations
1591    // to stop from hanging the whole browser.  This limit is copied from the
1592    // legacy tree builder and might need to be tweaked in the future.
1593    static const int adoptionAgencyIterationLimit = 10;
1594
1595    for (int i = 0; i < adoptionAgencyIterationLimit; ++i) {
1596        // 1.
1597        Element* formattingElement = m_tree.activeFormattingElements()->closestElementInScopeWithName(token.name());
1598        if (!formattingElement || ((m_tree.openElements()->contains(formattingElement)) && !m_tree.openElements()->inScope(formattingElement))) {
1599            parseError(token);
1600            notImplemented(); // Check the stack of open elements for a more specific parse error.
1601            return;
1602        }
1603        HTMLElementStack::ElementRecord* formattingElementRecord = m_tree.openElements()->find(formattingElement);
1604        if (!formattingElementRecord) {
1605            parseError(token);
1606            m_tree.activeFormattingElements()->remove(formattingElement);
1607            return;
1608        }
1609        if (formattingElement != m_tree.currentElement())
1610            parseError(token);
1611        // 2.
1612        HTMLElementStack::ElementRecord* furthestBlock = furthestBlockForFormattingElement(formattingElement);
1613        // 3.
1614        if (!furthestBlock) {
1615            m_tree.openElements()->popUntilPopped(formattingElement);
1616            m_tree.activeFormattingElements()->remove(formattingElement);
1617            return;
1618        }
1619        // 4.
1620        ASSERT(furthestBlock->isAbove(formattingElementRecord));
1621        Element* commonAncestor = formattingElementRecord->next()->element();
1622        // 5.
1623        HTMLFormattingElementList::Bookmark bookmark = m_tree.activeFormattingElements()->bookmarkFor(formattingElement);
1624        // 6.
1625        HTMLElementStack::ElementRecord* node = furthestBlock;
1626        HTMLElementStack::ElementRecord* nextNode = node->next();
1627        HTMLElementStack::ElementRecord* lastNode = furthestBlock;
1628        for (int i = 0; i < adoptionAgencyIterationLimit; ++i) {
1629            // 6.1
1630            node = nextNode;
1631            ASSERT(node);
1632            nextNode = node->next(); // Save node->next() for the next iteration in case node is deleted in 6.2.
1633            // 6.2
1634            if (!m_tree.activeFormattingElements()->contains(node->element())) {
1635                m_tree.openElements()->remove(node->element());
1636                node = 0;
1637                continue;
1638            }
1639            // 6.3
1640            if (node == formattingElementRecord)
1641                break;
1642            // 6.5
1643            RefPtr<Element> newElement = m_tree.createHTMLElementFromElementRecord(node);
1644            HTMLFormattingElementList::Entry* nodeEntry = m_tree.activeFormattingElements()->find(node->element());
1645            nodeEntry->replaceElement(newElement.get());
1646            node->replaceElement(newElement.release());
1647            // 6.4 -- Intentionally out of order to handle the case where node
1648            // was replaced in 6.5.
1649            // http://www.w3.org/Bugs/Public/show_bug.cgi?id=10096
1650            if (lastNode == furthestBlock)
1651                bookmark.moveToAfter(nodeEntry);
1652            // 6.6
1653            if (Element* parent = lastNode->element()->parentElement())
1654                parent->parserRemoveChild(lastNode->element());
1655            node->element()->parserAddChild(lastNode->element());
1656            if (lastNode->element()->parentElement()->attached() && !lastNode->element()->attached())
1657                lastNode->element()->lazyAttach();
1658            // 6.7
1659            lastNode = node;
1660        }
1661        // 7
1662        const AtomicString& commonAncestorTag = commonAncestor->localName();
1663        if (Element* parent = lastNode->element()->parentElement())
1664            parent->parserRemoveChild(lastNode->element());
1665        // FIXME: If this moves to HTMLConstructionSite, this check should use
1666        // causesFosterParenting(tagName) instead.
1667        if (commonAncestorTag == tableTag
1668            || commonAncestorTag == trTag
1669            || isTableBodyContextTag(commonAncestorTag))
1670            m_tree.fosterParent(lastNode->element());
1671        else {
1672            commonAncestor->parserAddChild(lastNode->element());
1673            if (lastNode->element()->parentElement()->attached() && !lastNode->element()->attached())
1674                lastNode->element()->lazyAttach();
1675        }
1676        // 8
1677        RefPtr<Element> newElement = m_tree.createHTMLElementFromElementRecord(formattingElementRecord);
1678        // 9
1679        newElement->takeAllChildrenFrom(furthestBlock->element());
1680        // 10
1681        Element* furthestBlockElement = furthestBlock->element();
1682        // FIXME: All this creation / parserAddChild / attach business should
1683        //        be in HTMLConstructionSite.  My guess is that steps 8--12
1684        //        should all be in some HTMLConstructionSite function.
1685        furthestBlockElement->parserAddChild(newElement);
1686        if (furthestBlockElement->attached() && !newElement->attached()) {
1687            // Notice that newElement might already be attached if, for example, one of the reparented
1688            // children is a style element, which attaches itself automatically.
1689            newElement->attach();
1690        }
1691        // 11
1692        m_tree.activeFormattingElements()->swapTo(formattingElement, newElement.get(), bookmark);
1693        // 12
1694        m_tree.openElements()->remove(formattingElement);
1695        m_tree.openElements()->insertAbove(newElement, furthestBlock);
1696    }
1697}
1698
1699void HTMLTreeBuilder::resetInsertionModeAppropriately()
1700{
1701    // http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#reset-the-insertion-mode-appropriately
1702    bool last = false;
1703    HTMLElementStack::ElementRecord* nodeRecord = m_tree.openElements()->topRecord();
1704    while (1) {
1705        Element* node = nodeRecord->element();
1706        if (node == m_tree.openElements()->bottom()) {
1707            ASSERT(isParsingFragment());
1708            last = true;
1709            node = m_fragmentContext.contextElement();
1710        }
1711        if (node->hasTagName(selectTag)) {
1712            ASSERT(isParsingFragment());
1713            return setInsertionMode(InSelectMode);
1714        }
1715        if (node->hasTagName(tdTag) || node->hasTagName(thTag))
1716            return setInsertionMode(InCellMode);
1717        if (node->hasTagName(trTag))
1718            return setInsertionMode(InRowMode);
1719        if (node->hasTagName(tbodyTag) || node->hasTagName(theadTag) || node->hasTagName(tfootTag))
1720            return setInsertionMode(InTableBodyMode);
1721        if (node->hasTagName(captionTag))
1722            return setInsertionMode(InCaptionMode);
1723        if (node->hasTagName(colgroupTag)) {
1724            ASSERT(isParsingFragment());
1725            return setInsertionMode(InColumnGroupMode);
1726        }
1727        if (node->hasTagName(tableTag))
1728            return setInsertionMode(InTableMode);
1729        if (node->hasTagName(headTag)) {
1730            ASSERT(isParsingFragment());
1731            return setInsertionMode(InBodyMode);
1732        }
1733        if (node->hasTagName(bodyTag))
1734            return setInsertionMode(InBodyMode);
1735        if (node->hasTagName(framesetTag)) {
1736            ASSERT(isParsingFragment());
1737            return setInsertionMode(InFramesetMode);
1738        }
1739        if (node->hasTagName(htmlTag)) {
1740            ASSERT(isParsingFragment());
1741            return setInsertionMode(BeforeHeadMode);
1742        }
1743        if (node->namespaceURI() == SVGNames::svgNamespaceURI
1744            || node->namespaceURI() == MathMLNames::mathmlNamespaceURI)
1745            return setInsertionMode(InForeignContentMode);
1746        if (last) {
1747            ASSERT(isParsingFragment());
1748            return setInsertionMode(InBodyMode);
1749        }
1750        nodeRecord = nodeRecord->next();
1751    }
1752}
1753
1754void HTMLTreeBuilder::processEndTagForInTableBody(AtomicHTMLToken& token)
1755{
1756    ASSERT(token.type() == HTMLToken::EndTag);
1757    if (isTableBodyContextTag(token.name())) {
1758        if (!m_tree.openElements()->inTableScope(token.name())) {
1759            parseError(token);
1760            return;
1761        }
1762        m_tree.openElements()->popUntilTableBodyScopeMarker();
1763        m_tree.openElements()->pop();
1764        setInsertionMode(InTableMode);
1765        return;
1766    }
1767    if (token.name() == tableTag) {
1768        // FIXME: This is slow.
1769        if (!m_tree.openElements()->inTableScope(tbodyTag.localName()) && !m_tree.openElements()->inTableScope(theadTag.localName()) && !m_tree.openElements()->inTableScope(tfootTag.localName())) {
1770            ASSERT(isParsingFragment());
1771            parseError(token);
1772            return;
1773        }
1774        m_tree.openElements()->popUntilTableBodyScopeMarker();
1775        ASSERT(isTableBodyContextTag(m_tree.currentElement()->localName()));
1776        processFakeEndTag(m_tree.currentElement()->tagQName());
1777        reprocessEndTag(token);
1778        return;
1779    }
1780    if (token.name() == bodyTag
1781        || isCaptionColOrColgroupTag(token.name())
1782        || token.name() == htmlTag
1783        || isTableCellContextTag(token.name())
1784        || token.name() == trTag) {
1785        parseError(token);
1786        return;
1787    }
1788    processEndTagForInTable(token);
1789}
1790
1791void HTMLTreeBuilder::processEndTagForInRow(AtomicHTMLToken& token)
1792{
1793    ASSERT(token.type() == HTMLToken::EndTag);
1794    if (token.name() == trTag) {
1795        processTrEndTagForInRow();
1796        return;
1797    }
1798    if (token.name() == tableTag) {
1799        if (!processTrEndTagForInRow()) {
1800            ASSERT(isParsingFragment());
1801            return;
1802        }
1803        ASSERT(insertionMode() == InTableBodyMode);
1804        reprocessEndTag(token);
1805        return;
1806    }
1807    if (isTableBodyContextTag(token.name())) {
1808        if (!m_tree.openElements()->inTableScope(token.name())) {
1809            parseError(token);
1810            return;
1811        }
1812        processFakeEndTag(trTag);
1813        ASSERT(insertionMode() == InTableBodyMode);
1814        reprocessEndTag(token);
1815        return;
1816    }
1817    if (token.name() == bodyTag
1818        || isCaptionColOrColgroupTag(token.name())
1819        || token.name() == htmlTag
1820        || isTableCellContextTag(token.name())) {
1821        parseError(token);
1822        return;
1823    }
1824    processEndTagForInTable(token);
1825}
1826
1827void HTMLTreeBuilder::processEndTagForInCell(AtomicHTMLToken& token)
1828{
1829    ASSERT(token.type() == HTMLToken::EndTag);
1830    if (isTableCellContextTag(token.name())) {
1831        if (!m_tree.openElements()->inTableScope(token.name())) {
1832            parseError(token);
1833            return;
1834        }
1835        m_tree.generateImpliedEndTags();
1836        if (!m_tree.currentElement()->hasLocalName(token.name()))
1837            parseError(token);
1838        m_tree.openElements()->popUntilPopped(token.name());
1839        m_tree.activeFormattingElements()->clearToLastMarker();
1840        setInsertionMode(InRowMode);
1841        return;
1842    }
1843    if (token.name() == bodyTag
1844        || isCaptionColOrColgroupTag(token.name())
1845        || token.name() == htmlTag) {
1846        parseError(token);
1847        return;
1848    }
1849    if (token.name() == tableTag
1850        || token.name() == trTag
1851        || isTableBodyContextTag(token.name())) {
1852        if (!m_tree.openElements()->inTableScope(token.name())) {
1853            ASSERT(isTableBodyContextTag(token.name()) || isParsingFragment());
1854            parseError(token);
1855            return;
1856        }
1857        closeTheCell();
1858        reprocessEndTag(token);
1859        return;
1860    }
1861    processEndTagForInBody(token);
1862}
1863
1864void HTMLTreeBuilder::processEndTagForInBody(AtomicHTMLToken& token)
1865{
1866    ASSERT(token.type() == HTMLToken::EndTag);
1867    if (token.name() == bodyTag) {
1868        processBodyEndTagForInBody(token);
1869        return;
1870    }
1871    if (token.name() == htmlTag) {
1872        AtomicHTMLToken endBody(HTMLToken::EndTag, bodyTag.localName());
1873        if (processBodyEndTagForInBody(endBody))
1874            reprocessEndTag(token);
1875        return;
1876    }
1877    if (token.name() == addressTag
1878        || token.name() == articleTag
1879        || token.name() == asideTag
1880        || token.name() == blockquoteTag
1881        || token.name() == buttonTag
1882        || token.name() == centerTag
1883        || token.name() == detailsTag
1884        || token.name() == dirTag
1885        || token.name() == divTag
1886        || token.name() == dlTag
1887        || token.name() == fieldsetTag
1888        || token.name() == figcaptionTag
1889        || token.name() == figureTag
1890        || token.name() == footerTag
1891        || token.name() == headerTag
1892        || token.name() == hgroupTag
1893        || token.name() == listingTag
1894        || token.name() == menuTag
1895        || token.name() == navTag
1896        || token.name() == olTag
1897        || token.name() == preTag
1898        || token.name() == sectionTag
1899        || token.name() == summaryTag
1900        || token.name() == ulTag) {
1901        if (!m_tree.openElements()->inScope(token.name())) {
1902            parseError(token);
1903            return;
1904        }
1905        m_tree.generateImpliedEndTags();
1906        if (!m_tree.currentElement()->hasLocalName(token.name()))
1907            parseError(token);
1908        m_tree.openElements()->popUntilPopped(token.name());
1909        return;
1910    }
1911    if (token.name() == formTag) {
1912        RefPtr<Element> node = m_tree.takeForm();
1913        if (!node || !m_tree.openElements()->inScope(node.get())) {
1914            parseError(token);
1915            return;
1916        }
1917        m_tree.generateImpliedEndTags();
1918        if (m_tree.currentElement() != node.get())
1919            parseError(token);
1920        m_tree.openElements()->remove(node.get());
1921    }
1922    if (token.name() == pTag) {
1923        if (!m_tree.openElements()->inButtonScope(token.name())) {
1924            parseError(token);
1925            processFakeStartTag(pTag);
1926            ASSERT(m_tree.openElements()->inScope(token.name()));
1927            reprocessEndTag(token);
1928            return;
1929        }
1930        m_tree.generateImpliedEndTagsWithExclusion(token.name());
1931        if (!m_tree.currentElement()->hasLocalName(token.name()))
1932            parseError(token);
1933        m_tree.openElements()->popUntilPopped(token.name());
1934        return;
1935    }
1936    if (token.name() == liTag) {
1937        if (!m_tree.openElements()->inListItemScope(token.name())) {
1938            parseError(token);
1939            return;
1940        }
1941        m_tree.generateImpliedEndTagsWithExclusion(token.name());
1942        if (!m_tree.currentElement()->hasLocalName(token.name()))
1943            parseError(token);
1944        m_tree.openElements()->popUntilPopped(token.name());
1945        return;
1946    }
1947    if (token.name() == ddTag
1948        || token.name() == dtTag) {
1949        if (!m_tree.openElements()->inScope(token.name())) {
1950            parseError(token);
1951            return;
1952        }
1953        m_tree.generateImpliedEndTagsWithExclusion(token.name());
1954        if (!m_tree.currentElement()->hasLocalName(token.name()))
1955            parseError(token);
1956        m_tree.openElements()->popUntilPopped(token.name());
1957        return;
1958    }
1959    if (isNumberedHeaderTag(token.name())) {
1960        if (!m_tree.openElements()->hasNumberedHeaderElementInScope()) {
1961            parseError(token);
1962            return;
1963        }
1964        m_tree.generateImpliedEndTags();
1965        if (!m_tree.currentElement()->hasLocalName(token.name()))
1966            parseError(token);
1967        m_tree.openElements()->popUntilNumberedHeaderElementPopped();
1968        return;
1969    }
1970    if (isFormattingTag(token.name())) {
1971        callTheAdoptionAgency(token);
1972        return;
1973    }
1974    if (token.name() == appletTag
1975        || token.name() == marqueeTag
1976        || token.name() == objectTag) {
1977        if (!m_tree.openElements()->inScope(token.name())) {
1978            parseError(token);
1979            return;
1980        }
1981        m_tree.generateImpliedEndTags();
1982        if (!m_tree.currentElement()->hasLocalName(token.name()))
1983            parseError(token);
1984        m_tree.openElements()->popUntilPopped(token.name());
1985        m_tree.activeFormattingElements()->clearToLastMarker();
1986        return;
1987    }
1988    if (token.name() == brTag) {
1989        parseError(token);
1990        processFakeStartTag(brTag);
1991        return;
1992    }
1993    processAnyOtherEndTagForInBody(token);
1994}
1995
1996bool HTMLTreeBuilder::processCaptionEndTagForInCaption()
1997{
1998    if (!m_tree.openElements()->inTableScope(captionTag.localName())) {
1999        ASSERT(isParsingFragment());
2000        // FIXME: parse error
2001        return false;
2002    }
2003    m_tree.generateImpliedEndTags();
2004    // FIXME: parse error if (!m_tree.currentElement()->hasTagName(captionTag))
2005    m_tree.openElements()->popUntilPopped(captionTag.localName());
2006    m_tree.activeFormattingElements()->clearToLastMarker();
2007    setInsertionMode(InTableMode);
2008    return true;
2009}
2010
2011bool HTMLTreeBuilder::processTrEndTagForInRow()
2012{
2013    if (!m_tree.openElements()->inTableScope(trTag.localName())) {
2014        ASSERT(isParsingFragment());
2015        // FIXME: parse error
2016        return false;
2017    }
2018    m_tree.openElements()->popUntilTableRowScopeMarker();
2019    ASSERT(m_tree.currentElement()->hasTagName(trTag));
2020    m_tree.openElements()->pop();
2021    setInsertionMode(InTableBodyMode);
2022    return true;
2023}
2024
2025bool HTMLTreeBuilder::processTableEndTagForInTable()
2026{
2027    if (!m_tree.openElements()->inTableScope(tableTag)) {
2028        ASSERT(isParsingFragment());
2029        // FIXME: parse error.
2030        return false;
2031    }
2032    m_tree.openElements()->popUntilPopped(tableTag.localName());
2033    resetInsertionModeAppropriately();
2034    return true;
2035}
2036
2037void HTMLTreeBuilder::processEndTagForInTable(AtomicHTMLToken& token)
2038{
2039    ASSERT(token.type() == HTMLToken::EndTag);
2040    if (token.name() == tableTag) {
2041        processTableEndTagForInTable();
2042        return;
2043    }
2044    if (token.name() == bodyTag
2045        || isCaptionColOrColgroupTag(token.name())
2046        || token.name() == htmlTag
2047        || isTableBodyContextTag(token.name())
2048        || isTableCellContextTag(token.name())
2049        || token.name() == trTag) {
2050        parseError(token);
2051        return;
2052    }
2053    // Is this redirection necessary here?
2054    HTMLConstructionSite::RedirectToFosterParentGuard redirecter(m_tree);
2055    processEndTagForInBody(token);
2056}
2057
2058void HTMLTreeBuilder::processEndTag(AtomicHTMLToken& token)
2059{
2060    ASSERT(token.type() == HTMLToken::EndTag);
2061    switch (insertionMode()) {
2062    case InitialMode:
2063        ASSERT(insertionMode() == InitialMode);
2064        defaultForInitial();
2065        // Fall through.
2066    case BeforeHTMLMode:
2067        ASSERT(insertionMode() == BeforeHTMLMode);
2068        if (token.name() != headTag && token.name() != bodyTag && token.name() != htmlTag && token.name() != brTag) {
2069            parseError(token);
2070            return;
2071        }
2072        defaultForBeforeHTML();
2073        // Fall through.
2074    case BeforeHeadMode:
2075        ASSERT(insertionMode() == BeforeHeadMode);
2076        if (token.name() != headTag && token.name() != bodyTag && token.name() != htmlTag && token.name() != brTag) {
2077            parseError(token);
2078            return;
2079        }
2080        defaultForBeforeHead();
2081        // Fall through.
2082    case InHeadMode:
2083        ASSERT(insertionMode() == InHeadMode);
2084        if (token.name() == headTag) {
2085            m_tree.openElements()->popHTMLHeadElement();
2086            setInsertionMode(AfterHeadMode);
2087            return;
2088        }
2089        if (token.name() != bodyTag && token.name() != htmlTag && token.name() != brTag) {
2090            parseError(token);
2091            return;
2092        }
2093        defaultForInHead();
2094        // Fall through.
2095    case AfterHeadMode:
2096        ASSERT(insertionMode() == AfterHeadMode);
2097        if (token.name() != bodyTag && token.name() != htmlTag && token.name() != brTag) {
2098            parseError(token);
2099            return;
2100        }
2101        defaultForAfterHead();
2102        // Fall through
2103    case InBodyMode:
2104        ASSERT(insertionMode() == InBodyMode);
2105        processEndTagForInBody(token);
2106        break;
2107    case InTableMode:
2108        ASSERT(insertionMode() == InTableMode);
2109        processEndTagForInTable(token);
2110        break;
2111    case InCaptionMode:
2112        ASSERT(insertionMode() == InCaptionMode);
2113        if (token.name() == captionTag) {
2114            processCaptionEndTagForInCaption();
2115            return;
2116        }
2117        if (token.name() == tableTag) {
2118            parseError(token);
2119            if (!processCaptionEndTagForInCaption()) {
2120                ASSERT(isParsingFragment());
2121                return;
2122            }
2123            reprocessEndTag(token);
2124            return;
2125        }
2126        if (token.name() == bodyTag
2127            || token.name() == colTag
2128            || token.name() == colgroupTag
2129            || token.name() == htmlTag
2130            || isTableBodyContextTag(token.name())
2131            || isTableCellContextTag(token.name())
2132            || token.name() == trTag) {
2133            parseError(token);
2134            return;
2135        }
2136        processEndTagForInBody(token);
2137        break;
2138    case InColumnGroupMode:
2139        ASSERT(insertionMode() == InColumnGroupMode);
2140        if (token.name() == colgroupTag) {
2141            processColgroupEndTagForInColumnGroup();
2142            return;
2143        }
2144        if (token.name() == colTag) {
2145            parseError(token);
2146            return;
2147        }
2148        if (!processColgroupEndTagForInColumnGroup()) {
2149            ASSERT(isParsingFragment());
2150            return;
2151        }
2152        reprocessEndTag(token);
2153        break;
2154    case InRowMode:
2155        ASSERT(insertionMode() == InRowMode);
2156        processEndTagForInRow(token);
2157        break;
2158    case InCellMode:
2159        ASSERT(insertionMode() == InCellMode);
2160        processEndTagForInCell(token);
2161        break;
2162    case InTableBodyMode:
2163        ASSERT(insertionMode() == InTableBodyMode);
2164        processEndTagForInTableBody(token);
2165        break;
2166    case AfterBodyMode:
2167        ASSERT(insertionMode() == AfterBodyMode);
2168        if (token.name() == htmlTag) {
2169            if (isParsingFragment()) {
2170                parseError(token);
2171                return;
2172            }
2173            setInsertionMode(AfterAfterBodyMode);
2174            return;
2175        }
2176        prepareToReprocessToken();
2177        // Fall through.
2178    case AfterAfterBodyMode:
2179        ASSERT(insertionMode() == AfterBodyMode || insertionMode() == AfterAfterBodyMode);
2180        parseError(token);
2181        setInsertionMode(InBodyMode);
2182        reprocessEndTag(token);
2183        break;
2184    case InHeadNoscriptMode:
2185        ASSERT(insertionMode() == InHeadNoscriptMode);
2186        if (token.name() == noscriptTag) {
2187            ASSERT(m_tree.currentElement()->hasTagName(noscriptTag));
2188            m_tree.openElements()->pop();
2189            ASSERT(m_tree.currentElement()->hasTagName(headTag));
2190            setInsertionMode(InHeadMode);
2191            return;
2192        }
2193        if (token.name() != brTag) {
2194            parseError(token);
2195            return;
2196        }
2197        defaultForInHeadNoscript();
2198        processToken(token);
2199        break;
2200    case TextMode:
2201        if (token.name() == scriptTag) {
2202            // Pause ourselves so that parsing stops until the script can be processed by the caller.
2203            m_isPaused = true;
2204            ASSERT(m_tree.currentElement()->hasTagName(scriptTag));
2205            m_scriptToProcess = m_tree.currentElement();
2206            m_scriptToProcessStartPosition = WTF::toOneBasedTextPosition(m_lastScriptElementStartPosition);
2207            m_tree.openElements()->pop();
2208            if (isParsingFragment() && m_fragmentContext.scriptingPermission() == FragmentScriptingNotAllowed)
2209                m_scriptToProcess->removeAllChildren();
2210            setInsertionMode(m_originalInsertionMode);
2211
2212            // This token will not have been created by the tokenizer if a
2213            // self-closing script tag was encountered and pre-HTML5 parser
2214            // quirks are enabled. We must set the tokenizer's state to
2215            // DataState explicitly if the tokenizer didn't have a chance to.
2216            ASSERT(m_parser->tokenizer()->state() == HTMLTokenizer::DataState || m_usePreHTML5ParserQuirks);
2217            m_parser->tokenizer()->setState(HTMLTokenizer::DataState);
2218            return;
2219        }
2220        m_tree.openElements()->pop();
2221        setInsertionMode(m_originalInsertionMode);
2222        break;
2223    case InFramesetMode:
2224        ASSERT(insertionMode() == InFramesetMode);
2225        if (token.name() == framesetTag) {
2226            if (m_tree.currentElement() == m_tree.openElements()->htmlElement()) {
2227                parseError(token);
2228                return;
2229            }
2230            m_tree.openElements()->pop();
2231            if (!isParsingFragment() && !m_tree.currentElement()->hasTagName(framesetTag))
2232                setInsertionMode(AfterFramesetMode);
2233            return;
2234        }
2235        break;
2236    case AfterFramesetMode:
2237        ASSERT(insertionMode() == AfterFramesetMode);
2238        if (token.name() == htmlTag) {
2239            setInsertionMode(AfterAfterFramesetMode);
2240            return;
2241        }
2242        // Fall through.
2243    case AfterAfterFramesetMode:
2244        ASSERT(insertionMode() == AfterFramesetMode || insertionMode() == AfterAfterFramesetMode);
2245        parseError(token);
2246        break;
2247    case InSelectInTableMode:
2248        ASSERT(insertionMode() == InSelectInTableMode);
2249        if (token.name() == captionTag
2250            || token.name() == tableTag
2251            || isTableBodyContextTag(token.name())
2252            || token.name() == trTag
2253            || isTableCellContextTag(token.name())) {
2254            parseError(token);
2255            if (m_tree.openElements()->inTableScope(token.name())) {
2256                AtomicHTMLToken endSelect(HTMLToken::EndTag, selectTag.localName());
2257                processEndTag(endSelect);
2258                reprocessEndTag(token);
2259            }
2260            return;
2261        }
2262        // Fall through.
2263    case InSelectMode:
2264        ASSERT(insertionMode() == InSelectMode || insertionMode() == InSelectInTableMode);
2265        if (token.name() == optgroupTag) {
2266            if (m_tree.currentElement()->hasTagName(optionTag) && m_tree.oneBelowTop()->hasTagName(optgroupTag))
2267                processFakeEndTag(optionTag);
2268            if (m_tree.currentElement()->hasTagName(optgroupTag)) {
2269                m_tree.openElements()->pop();
2270                return;
2271            }
2272            parseError(token);
2273            return;
2274        }
2275        if (token.name() == optionTag) {
2276            if (m_tree.currentElement()->hasTagName(optionTag)) {
2277                m_tree.openElements()->pop();
2278                return;
2279            }
2280            parseError(token);
2281            return;
2282        }
2283        if (token.name() == selectTag) {
2284            if (!m_tree.openElements()->inSelectScope(token.name())) {
2285                ASSERT(isParsingFragment());
2286                parseError(token);
2287                return;
2288            }
2289            m_tree.openElements()->popUntilPopped(selectTag.localName());
2290            resetInsertionModeAppropriately();
2291            return;
2292        }
2293        break;
2294    case InTableTextMode:
2295        defaultForInTableText();
2296        processEndTag(token);
2297        break;
2298    case InForeignContentMode:
2299        if (token.name() == SVGNames::scriptTag && m_tree.currentElement()->hasTagName(SVGNames::scriptTag)) {
2300            notImplemented();
2301            return;
2302        }
2303        if (m_tree.currentElement()->namespaceURI() != xhtmlNamespaceURI) {
2304            // FIXME: This code just wants an Element* iterator, instead of an ElementRecord*
2305            HTMLElementStack::ElementRecord* nodeRecord = m_tree.openElements()->topRecord();
2306            if (!nodeRecord->element()->hasLocalName(token.name()))
2307                parseError(token);
2308            while (1) {
2309                if (nodeRecord->element()->hasLocalName(token.name())) {
2310                    m_tree.openElements()->popUntilPopped(nodeRecord->element());
2311                    resetForeignInsertionMode();
2312                    return;
2313                }
2314                nodeRecord = nodeRecord->next();
2315                if (nodeRecord->element()->namespaceURI() == xhtmlNamespaceURI)
2316                    break;
2317            }
2318        }
2319        // Any other end tag (also the last two steps of "An end tag, if the current node is not an element in the HTML namespace."
2320        processForeignContentUsingInBodyModeAndResetMode(token);
2321        break;
2322    }
2323}
2324
2325void HTMLTreeBuilder::prepareToReprocessToken()
2326{
2327    if (m_hasPendingForeignInsertionModeSteps) {
2328        resetForeignInsertionMode();
2329        m_hasPendingForeignInsertionModeSteps = false;
2330    }
2331}
2332
2333void HTMLTreeBuilder::reprocessStartTag(AtomicHTMLToken& token)
2334{
2335    prepareToReprocessToken();
2336    processStartTag(token);
2337}
2338
2339void HTMLTreeBuilder::reprocessEndTag(AtomicHTMLToken& token)
2340{
2341    prepareToReprocessToken();
2342    processEndTag(token);
2343}
2344
2345class HTMLTreeBuilder::FakeInsertionMode : public Noncopyable {
2346public:
2347    FakeInsertionMode(HTMLTreeBuilder* treeBuilder, InsertionMode mode)
2348        : m_treeBuilder(treeBuilder)
2349        , m_originalMode(treeBuilder->insertionMode())
2350    {
2351        m_treeBuilder->setFakeInsertionMode(mode);
2352    }
2353
2354    ~FakeInsertionMode()
2355    {
2356        if (m_treeBuilder->isFakeInsertionMode())
2357            m_treeBuilder->setInsertionMode(m_originalMode);
2358    }
2359
2360private:
2361    HTMLTreeBuilder* m_treeBuilder;
2362    InsertionMode m_originalMode;
2363};
2364
2365void HTMLTreeBuilder::processForeignContentUsingInBodyModeAndResetMode(AtomicHTMLToken& token)
2366{
2367    m_hasPendingForeignInsertionModeSteps = true;
2368    {
2369        FakeInsertionMode fakeMode(this, InBodyMode);
2370        processToken(token);
2371    }
2372    if (m_hasPendingForeignInsertionModeSteps)
2373        resetForeignInsertionMode();
2374}
2375
2376void HTMLTreeBuilder::resetForeignInsertionMode()
2377{
2378    if (insertionMode() == InForeignContentMode)
2379        resetInsertionModeAppropriately();
2380}
2381
2382void HTMLTreeBuilder::processComment(AtomicHTMLToken& token)
2383{
2384    ASSERT(token.type() == HTMLToken::Comment);
2385    if (m_insertionMode == InitialMode
2386        || m_insertionMode == BeforeHTMLMode
2387        || m_insertionMode == AfterAfterBodyMode
2388        || m_insertionMode == AfterAfterFramesetMode) {
2389        m_tree.insertCommentOnDocument(token);
2390        return;
2391    }
2392    if (m_insertionMode == AfterBodyMode) {
2393        m_tree.insertCommentOnHTMLHtmlElement(token);
2394        return;
2395    }
2396    if (m_insertionMode == InTableTextMode) {
2397        defaultForInTableText();
2398        processComment(token);
2399        return;
2400    }
2401    m_tree.insertComment(token);
2402}
2403
2404void HTMLTreeBuilder::processCharacter(AtomicHTMLToken& token)
2405{
2406    ASSERT(token.type() == HTMLToken::Character);
2407    ExternalCharacterTokenBuffer buffer(token);
2408    processCharacterBuffer(buffer);
2409}
2410
2411void HTMLTreeBuilder::processCharacterBuffer(ExternalCharacterTokenBuffer& buffer)
2412{
2413ReprocessBuffer:
2414    switch (insertionMode()) {
2415    case InitialMode: {
2416        ASSERT(insertionMode() == InitialMode);
2417        buffer.skipLeadingWhitespace();
2418        if (buffer.isEmpty())
2419            return;
2420        defaultForInitial();
2421        // Fall through.
2422    }
2423    case BeforeHTMLMode: {
2424        ASSERT(insertionMode() == BeforeHTMLMode);
2425        buffer.skipLeadingWhitespace();
2426        if (buffer.isEmpty())
2427            return;
2428        defaultForBeforeHTML();
2429        // Fall through.
2430    }
2431    case BeforeHeadMode: {
2432        ASSERT(insertionMode() == BeforeHeadMode);
2433        buffer.skipLeadingWhitespace();
2434        if (buffer.isEmpty())
2435            return;
2436        defaultForBeforeHead();
2437        // Fall through.
2438    }
2439    case InHeadMode: {
2440        ASSERT(insertionMode() == InHeadMode);
2441        String leadingWhitespace = buffer.takeLeadingWhitespace();
2442        if (!leadingWhitespace.isEmpty())
2443            m_tree.insertTextNode(leadingWhitespace);
2444        if (buffer.isEmpty())
2445            return;
2446        defaultForInHead();
2447        // Fall through.
2448    }
2449    case AfterHeadMode: {
2450        ASSERT(insertionMode() == AfterHeadMode);
2451        String leadingWhitespace = buffer.takeLeadingWhitespace();
2452        if (!leadingWhitespace.isEmpty())
2453            m_tree.insertTextNode(leadingWhitespace);
2454        if (buffer.isEmpty())
2455            return;
2456        defaultForAfterHead();
2457        // Fall through.
2458    }
2459    case InBodyMode:
2460    case InCaptionMode:
2461    case InCellMode: {
2462        ASSERT(insertionMode() == InBodyMode || insertionMode() == InCaptionMode || insertionMode() == InCellMode);
2463        m_tree.reconstructTheActiveFormattingElements();
2464        String characters = buffer.takeRemaining();
2465        m_tree.insertTextNode(characters);
2466        if (m_framesetOk && !isAllWhitespaceOrReplacementCharacters(characters))
2467            m_framesetOk = false;
2468        break;
2469    }
2470    case InTableMode:
2471    case InTableBodyMode:
2472    case InRowMode: {
2473        ASSERT(insertionMode() == InTableMode || insertionMode() == InTableBodyMode || insertionMode() == InRowMode);
2474        ASSERT(m_pendingTableCharacters.isEmpty());
2475        m_originalInsertionMode = m_insertionMode;
2476        setInsertionMode(InTableTextMode);
2477        prepareToReprocessToken();
2478        // Fall through.
2479    }
2480    case InTableTextMode: {
2481        buffer.giveRemainingTo(m_pendingTableCharacters);
2482        break;
2483    }
2484    case InColumnGroupMode: {
2485        ASSERT(insertionMode() == InColumnGroupMode);
2486        String leadingWhitespace = buffer.takeLeadingWhitespace();
2487        if (!leadingWhitespace.isEmpty())
2488            m_tree.insertTextNode(leadingWhitespace);
2489        if (buffer.isEmpty())
2490            return;
2491        if (!processColgroupEndTagForInColumnGroup()) {
2492            ASSERT(isParsingFragment());
2493            // The spec tells us to drop these characters on the floor.
2494            buffer.takeLeadingNonWhitespace();
2495            if (buffer.isEmpty())
2496                return;
2497        }
2498        prepareToReprocessToken();
2499        goto ReprocessBuffer;
2500    }
2501    case AfterBodyMode:
2502    case AfterAfterBodyMode: {
2503        ASSERT(insertionMode() == AfterBodyMode || insertionMode() == AfterAfterBodyMode);
2504        // FIXME: parse error
2505        setInsertionMode(InBodyMode);
2506        prepareToReprocessToken();
2507        goto ReprocessBuffer;
2508        break;
2509    }
2510    case TextMode: {
2511        ASSERT(insertionMode() == TextMode);
2512        m_tree.insertTextNode(buffer.takeRemaining());
2513        break;
2514    }
2515    case InHeadNoscriptMode: {
2516        ASSERT(insertionMode() == InHeadNoscriptMode);
2517        String leadingWhitespace = buffer.takeLeadingWhitespace();
2518        if (!leadingWhitespace.isEmpty())
2519            m_tree.insertTextNode(leadingWhitespace);
2520        if (buffer.isEmpty())
2521            return;
2522        defaultForInHeadNoscript();
2523        goto ReprocessBuffer;
2524        break;
2525    }
2526    case InFramesetMode:
2527    case AfterFramesetMode: {
2528        ASSERT(insertionMode() == InFramesetMode || insertionMode() == AfterFramesetMode || insertionMode() == AfterAfterFramesetMode);
2529        String leadingWhitespace = buffer.takeRemainingWhitespace();
2530        if (!leadingWhitespace.isEmpty())
2531            m_tree.insertTextNode(leadingWhitespace);
2532        // FIXME: We should generate a parse error if we skipped over any
2533        // non-whitespace characters.
2534        break;
2535    }
2536    case InSelectInTableMode:
2537    case InSelectMode: {
2538        ASSERT(insertionMode() == InSelectMode || insertionMode() == InSelectInTableMode);
2539        m_tree.insertTextNode(buffer.takeRemaining());
2540        break;
2541    }
2542    case InForeignContentMode: {
2543        ASSERT(insertionMode() == InForeignContentMode);
2544        String characters = buffer.takeRemaining();
2545        m_tree.insertTextNode(characters);
2546        if (m_framesetOk && !isAllWhitespace(characters))
2547            m_framesetOk = false;
2548        break;
2549    }
2550    case AfterAfterFramesetMode: {
2551        String leadingWhitespace = buffer.takeRemainingWhitespace();
2552        if (!leadingWhitespace.isEmpty()) {
2553            m_tree.reconstructTheActiveFormattingElements();
2554            m_tree.insertTextNode(leadingWhitespace);
2555        }
2556        // FIXME: We should generate a parse error if we skipped over any
2557        // non-whitespace characters.
2558        break;
2559    }
2560    }
2561}
2562
2563void HTMLTreeBuilder::processEndOfFile(AtomicHTMLToken& token)
2564{
2565    ASSERT(token.type() == HTMLToken::EndOfFile);
2566    switch (insertionMode()) {
2567    case InitialMode:
2568        ASSERT(insertionMode() == InitialMode);
2569        defaultForInitial();
2570        // Fall through.
2571    case BeforeHTMLMode:
2572        ASSERT(insertionMode() == BeforeHTMLMode);
2573        defaultForBeforeHTML();
2574        // Fall through.
2575    case BeforeHeadMode:
2576        ASSERT(insertionMode() == BeforeHeadMode);
2577        defaultForBeforeHead();
2578        // Fall through.
2579    case InHeadMode:
2580        ASSERT(insertionMode() == InHeadMode);
2581        defaultForInHead();
2582        // Fall through.
2583    case AfterHeadMode:
2584        ASSERT(insertionMode() == AfterHeadMode);
2585        defaultForAfterHead();
2586        // Fall through
2587    case InBodyMode:
2588    case InCellMode:
2589    case InCaptionMode:
2590    case InRowMode:
2591        ASSERT(insertionMode() == InBodyMode || insertionMode() == InCellMode || insertionMode() == InCaptionMode || insertionMode() == InRowMode);
2592        notImplemented(); // Emit parse error based on what elements are still open.
2593        break;
2594    case AfterBodyMode:
2595    case AfterAfterBodyMode:
2596        ASSERT(insertionMode() == AfterBodyMode || insertionMode() == AfterAfterBodyMode);
2597        break;
2598    case InHeadNoscriptMode:
2599        ASSERT(insertionMode() == InHeadNoscriptMode);
2600        defaultForInHeadNoscript();
2601        processEndOfFile(token);
2602        return;
2603    case AfterFramesetMode:
2604    case AfterAfterFramesetMode:
2605        ASSERT(insertionMode() == AfterFramesetMode || insertionMode() == AfterAfterFramesetMode);
2606        break;
2607    case InFramesetMode:
2608    case InTableMode:
2609    case InTableBodyMode:
2610    case InSelectInTableMode:
2611    case InSelectMode:
2612        ASSERT(insertionMode() == InSelectMode || insertionMode() == InSelectInTableMode || insertionMode() == InTableMode || insertionMode() == InFramesetMode || insertionMode() == InTableBodyMode);
2613        if (m_tree.currentElement() != m_tree.openElements()->htmlElement())
2614            parseError(token);
2615        break;
2616    case InColumnGroupMode:
2617        if (m_tree.currentElement() == m_tree.openElements()->htmlElement()) {
2618            ASSERT(isParsingFragment());
2619            return; // FIXME: Should we break here instead of returning?
2620        }
2621        if (!processColgroupEndTagForInColumnGroup()) {
2622            ASSERT(isParsingFragment());
2623            return; // FIXME: Should we break here instead of returning?
2624        }
2625        prepareToReprocessToken();
2626        processEndOfFile(token);
2627        return;
2628    case InForeignContentMode:
2629        setInsertionMode(InBodyMode);
2630        processEndOfFile(token);
2631        return;
2632    case InTableTextMode:
2633        defaultForInTableText();
2634        processEndOfFile(token);
2635        return;
2636    case TextMode:
2637        parseError(token);
2638        if (m_tree.currentElement()->hasTagName(scriptTag))
2639            notImplemented(); // mark the script element as "already started".
2640        m_tree.openElements()->pop();
2641        setInsertionMode(m_originalInsertionMode);
2642        prepareToReprocessToken();
2643        processEndOfFile(token);
2644        return;
2645    }
2646    ASSERT(m_tree.openElements()->top());
2647    m_tree.openElements()->popAll();
2648}
2649
2650void HTMLTreeBuilder::defaultForInitial()
2651{
2652    notImplemented();
2653    if (!m_fragmentContext.fragment())
2654        m_document->setCompatibilityMode(Document::QuirksMode);
2655    // FIXME: parse error
2656    setInsertionMode(BeforeHTMLMode);
2657    prepareToReprocessToken();
2658}
2659
2660void HTMLTreeBuilder::defaultForBeforeHTML()
2661{
2662    AtomicHTMLToken startHTML(HTMLToken::StartTag, htmlTag.localName());
2663    m_tree.insertHTMLHtmlStartTagBeforeHTML(startHTML);
2664    setInsertionMode(BeforeHeadMode);
2665    prepareToReprocessToken();
2666}
2667
2668void HTMLTreeBuilder::defaultForBeforeHead()
2669{
2670    AtomicHTMLToken startHead(HTMLToken::StartTag, headTag.localName());
2671    processStartTag(startHead);
2672    prepareToReprocessToken();
2673}
2674
2675void HTMLTreeBuilder::defaultForInHead()
2676{
2677    AtomicHTMLToken endHead(HTMLToken::EndTag, headTag.localName());
2678    processEndTag(endHead);
2679    prepareToReprocessToken();
2680}
2681
2682void HTMLTreeBuilder::defaultForInHeadNoscript()
2683{
2684    AtomicHTMLToken endNoscript(HTMLToken::EndTag, noscriptTag.localName());
2685    processEndTag(endNoscript);
2686    prepareToReprocessToken();
2687}
2688
2689void HTMLTreeBuilder::defaultForAfterHead()
2690{
2691    AtomicHTMLToken startBody(HTMLToken::StartTag, bodyTag.localName());
2692    processStartTag(startBody);
2693    m_framesetOk = true;
2694    prepareToReprocessToken();
2695}
2696
2697void HTMLTreeBuilder::defaultForInTableText()
2698{
2699    String characters = String::adopt(m_pendingTableCharacters);
2700    if (!isAllWhitespace(characters)) {
2701        // FIXME: parse error
2702        HTMLConstructionSite::RedirectToFosterParentGuard redirecter(m_tree);
2703        m_tree.reconstructTheActiveFormattingElements();
2704        m_tree.insertTextNode(characters);
2705        m_framesetOk = false;
2706        setInsertionMode(m_originalInsertionMode);
2707        prepareToReprocessToken();
2708        return;
2709    }
2710    m_tree.insertTextNode(characters);
2711    setInsertionMode(m_originalInsertionMode);
2712    prepareToReprocessToken();
2713}
2714
2715bool HTMLTreeBuilder::processStartTagForInHead(AtomicHTMLToken& token)
2716{
2717    ASSERT(token.type() == HTMLToken::StartTag);
2718    if (token.name() == htmlTag) {
2719        m_tree.insertHTMLHtmlStartTagInBody(token);
2720        return true;
2721    }
2722    if (token.name() == baseTag
2723        || token.name() == basefontTag
2724        || token.name() == bgsoundTag
2725        || token.name() == commandTag
2726        || token.name() == linkTag
2727        || token.name() == metaTag) {
2728        m_tree.insertSelfClosingHTMLElement(token);
2729        // Note: The custom processing for the <meta> tag is done in HTMLMetaElement::process().
2730        return true;
2731    }
2732    if (token.name() == titleTag) {
2733        processGenericRCDATAStartTag(token);
2734        return true;
2735    }
2736    if (token.name() == noscriptTag) {
2737        if (scriptEnabled(m_document->frame())) {
2738            processGenericRawTextStartTag(token);
2739            return true;
2740        }
2741        m_tree.insertHTMLElement(token);
2742        setInsertionMode(InHeadNoscriptMode);
2743        return true;
2744    }
2745    if (token.name() == noframesTag || token.name() == styleTag) {
2746        processGenericRawTextStartTag(token);
2747        return true;
2748    }
2749    if (token.name() == scriptTag) {
2750        processScriptStartTag(token);
2751        if (m_usePreHTML5ParserQuirks && token.selfClosing())
2752            processFakeEndTag(scriptTag);
2753        return true;
2754    }
2755    if (token.name() == headTag) {
2756        parseError(token);
2757        return true;
2758    }
2759    return false;
2760}
2761
2762void HTMLTreeBuilder::processGenericRCDATAStartTag(AtomicHTMLToken& token)
2763{
2764    ASSERT(token.type() == HTMLToken::StartTag);
2765    m_tree.insertHTMLElement(token);
2766    m_parser->tokenizer()->setState(HTMLTokenizer::RCDATAState);
2767    m_originalInsertionMode = m_insertionMode;
2768    setInsertionMode(TextMode);
2769}
2770
2771void HTMLTreeBuilder::processGenericRawTextStartTag(AtomicHTMLToken& token)
2772{
2773    ASSERT(token.type() == HTMLToken::StartTag);
2774    m_tree.insertHTMLElement(token);
2775    m_parser->tokenizer()->setState(HTMLTokenizer::RAWTEXTState);
2776    m_originalInsertionMode = m_insertionMode;
2777    setInsertionMode(TextMode);
2778}
2779
2780void HTMLTreeBuilder::processScriptStartTag(AtomicHTMLToken& token)
2781{
2782    ASSERT(token.type() == HTMLToken::StartTag);
2783    m_tree.insertScriptElement(token);
2784    m_parser->tokenizer()->setState(HTMLTokenizer::ScriptDataState);
2785    m_originalInsertionMode = m_insertionMode;
2786
2787    TextPosition0 position = m_parser->textPosition();
2788
2789    ASSERT(position.m_line.zeroBasedInt() == m_parser->tokenizer()->lineNumber());
2790
2791    m_lastScriptElementStartPosition = position;
2792
2793    setInsertionMode(TextMode);
2794}
2795
2796void HTMLTreeBuilder::finished()
2797{
2798    ASSERT(m_document);
2799    if (isParsingFragment()) {
2800        m_fragmentContext.finished();
2801        return;
2802    }
2803
2804    // Warning, this may detach the parser. Do not do anything else after this.
2805    m_document->finishedParsing();
2806}
2807
2808bool HTMLTreeBuilder::scriptEnabled(Frame* frame)
2809{
2810    if (!frame)
2811        return false;
2812    return frame->script()->canExecuteScripts(NotAboutToExecuteScript);
2813}
2814
2815bool HTMLTreeBuilder::pluginsEnabled(Frame* frame)
2816{
2817    if (!frame)
2818        return false;
2819    return frame->loader()->subframeLoader()->allowPlugins(NotAboutToInstantiatePlugin);
2820}
2821
2822}
2823