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