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