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 void HTMLTreeBuilder::processStartTag(AtomicHTMLToken& token)
1152 {
1153     ASSERT(token.type() == HTMLToken::StartTag);
1154     switch (insertionMode()) {
1155     case InitialMode:
1156         ASSERT(insertionMode() == InitialMode);
1157         processDefaultForInitialMode(token);
1158         // Fall through.
1159     case BeforeHTMLMode:
1160         ASSERT(insertionMode() == BeforeHTMLMode);
1161         if (token.name() == htmlTag) {
1162             m_tree.insertHTMLHtmlStartTagBeforeHTML(token);
1163             setInsertionMode(BeforeHeadMode);
1164             return;
1165         }
1166         processDefaultForBeforeHTMLMode(token);
1167         // Fall through.
1168     case BeforeHeadMode:
1169         ASSERT(insertionMode() == BeforeHeadMode);
1170         if (token.name() == htmlTag) {
1171             m_tree.insertHTMLHtmlStartTagInBody(token);
1172             return;
1173         }
1174         if (token.name() == headTag) {
1175             m_tree.insertHTMLHeadElement(token);
1176             setInsertionMode(InHeadMode);
1177             return;
1178         }
1179         processDefaultForBeforeHeadMode(token);
1180         // Fall through.
1181     case InHeadMode:
1182         ASSERT(insertionMode() == InHeadMode);
1183         if (processStartTagForInHead(token))
1184             return;
1185         processDefaultForInHeadMode(token);
1186         // Fall through.
1187     case AfterHeadMode:
1188         ASSERT(insertionMode() == AfterHeadMode);
1189         if (token.name() == htmlTag) {
1190             m_tree.insertHTMLHtmlStartTagInBody(token);
1191             return;
1192         }
1193         if (token.name() == bodyTag) {
1194             m_framesetOk = false;
1195             m_tree.insertHTMLBodyElement(token);
1196             setInsertionMode(InBodyMode);
1197             return;
1198         }
1199         if (token.name() == framesetTag) {
1200             m_tree.insertHTMLElement(token);
1201             setInsertionMode(InFramesetMode);
1202             return;
1203         }
1204         if (token.name() == baseTag
1205             || token.name() == linkTag
1206             || token.name() == metaTag
1207             || token.name() == noframesTag
1208             || token.name() == scriptTag
1209             || token.name() == styleTag
1210             || token.name() == titleTag) {
1211             parseError(token);
1212             ASSERT(m_tree.head());
1213             m_tree.openElements()->pushHTMLHeadElement(m_tree.head());
1214             processStartTagForInHead(token);
1215             m_tree.openElements()->removeHTMLHeadElement(m_tree.head());
1216             return;
1217         }
1218         if (token.name() == headTag) {
1219             parseError(token);
1220             return;
1221         }
1222         processDefaultForAfterHeadMode(token);
1223         // Fall through
1224     case InBodyMode:
1225         ASSERT(insertionMode() == InBodyMode);
1226         processStartTagForInBody(token);
1227         break;
1228     case InTableMode:
1229         ASSERT(insertionMode() == InTableMode);
1230         processStartTagForInTable(token);
1231         break;
1232     case InCaptionMode:
1233         ASSERT(insertionMode() == InCaptionMode);
1234         if (isCaptionColOrColgroupTag(token.name())
1235             || isTableBodyContextTag(token.name())
1236             || isTableCellContextTag(token.name())
1237             || token.name() == trTag) {
1238             parseError(token);
1239             if (!processCaptionEndTagForInCaption()) {
1240                 ASSERT(m_isParsingFragment);
1241                 return;
1242             }
1243             processStartTag(token);
1244             return;
1245         }
1246         processStartTagForInBody(token);
1247         break;
1248     case InColumnGroupMode:
1249         ASSERT(insertionMode() == InColumnGroupMode);
1250         if (token.name() == htmlTag) {
1251             m_tree.insertHTMLHtmlStartTagInBody(token);
1252             return;
1253         }
1254         if (token.name() == colTag) {
1255             m_tree.insertSelfClosingHTMLElement(token);
1256             return;
1257         }
1258         if (!processColgroupEndTagForInColumnGroup()) {
1259             ASSERT(m_isParsingFragment);
1260             return;
1261         }
1262         processStartTag(token);
1263         break;
1264     case InTableBodyMode:
1265         ASSERT(insertionMode() == InTableBodyMode);
1266         if (token.name() == trTag) {
1267             m_tree.openElements()->popUntilTableBodyScopeMarker(); // How is there ever anything to pop?
1268             m_tree.insertHTMLElement(token);
1269             setInsertionMode(InRowMode);
1270             return;
1271         }
1272         if (isTableCellContextTag(token.name())) {
1273             parseError(token);
1274             processFakeStartTag(trTag);
1275             ASSERT(insertionMode() == InRowMode);
1276             processStartTag(token);
1277             return;
1278         }
1279         if (isCaptionColOrColgroupTag(token.name()) || isTableBodyContextTag(token.name())) {
1280             // FIXME: This is slow.
1281             if (!m_tree.openElements()->inTableScope(tbodyTag.localName()) && !m_tree.openElements()->inTableScope(theadTag.localName()) && !m_tree.openElements()->inTableScope(tfootTag.localName())) {
1282                 ASSERT(m_isParsingFragment);
1283                 parseError(token);
1284                 return;
1285             }
1286             m_tree.openElements()->popUntilTableBodyScopeMarker();
1287             ASSERT(isTableBodyContextTag(m_tree.currentElement()->localName()));
1288             processFakeEndTag(m_tree.currentElement()->tagQName());
1289             processStartTag(token);
1290             return;
1291         }
1292         processStartTagForInTable(token);
1293         break;
1294     case InRowMode:
1295         ASSERT(insertionMode() == InRowMode);
1296         if (isTableCellContextTag(token.name())) {
1297             m_tree.openElements()->popUntilTableRowScopeMarker();
1298             m_tree.insertHTMLElement(token);
1299             setInsertionMode(InCellMode);
1300             m_tree.activeFormattingElements()->appendMarker();
1301             return;
1302         }
1303         if (token.name() == trTag
1304             || isCaptionColOrColgroupTag(token.name())
1305             || isTableBodyContextTag(token.name())) {
1306             if (!processTrEndTagForInRow()) {
1307                 ASSERT(m_isParsingFragment);
1308                 return;
1309             }
1310             ASSERT(insertionMode() == InTableBodyMode);
1311             processStartTag(token);
1312             return;
1313         }
1314         processStartTagForInTable(token);
1315         break;
1316     case InCellMode:
1317         ASSERT(insertionMode() == InCellMode);
1318         if (isCaptionColOrColgroupTag(token.name())
1319             || isTableCellContextTag(token.name())
1320             || token.name() == trTag
1321             || isTableBodyContextTag(token.name())) {
1322             // FIXME: This could be more efficient.
1323             if (!m_tree.openElements()->inTableScope(tdTag) && !m_tree.openElements()->inTableScope(thTag)) {
1324                 ASSERT(m_isParsingFragment);
1325                 parseError(token);
1326                 return;
1327             }
1328             closeTheCell();
1329             processStartTag(token);
1330             return;
1331         }
1332         processStartTagForInBody(token);
1333         break;
1334     case AfterBodyMode:
1335     case AfterAfterBodyMode:
1336         ASSERT(insertionMode() == AfterBodyMode || insertionMode() == AfterAfterBodyMode);
1337         if (token.name() == htmlTag) {
1338             m_tree.insertHTMLHtmlStartTagInBody(token);
1339             return;
1340         }
1341         setInsertionMode(InBodyMode);
1342         processStartTag(token);
1343         break;
1344     case InHeadNoscriptMode:
1345         ASSERT(insertionMode() == InHeadNoscriptMode);
1346         if (token.name() == htmlTag) {
1347             m_tree.insertHTMLHtmlStartTagInBody(token);
1348             return;
1349         }
1350         if (token.name() == linkTag
1351             || token.name() == metaTag
1352             || token.name() == noframesTag
1353             || token.name() == styleTag) {
1354             bool didProcess = processStartTagForInHead(token);
1355             ASSERT_UNUSED(didProcess, didProcess);
1356             return;
1357         }
1358         if (token.name() == htmlTag || token.name() == noscriptTag) {
1359             parseError(token);
1360             return;
1361         }
1362         processDefaultForInHeadNoscriptMode(token);
1363         processToken(token);
1364         break;
1365     case InFramesetMode:
1366         ASSERT(insertionMode() == InFramesetMode);
1367         if (token.name() == htmlTag) {
1368             m_tree.insertHTMLHtmlStartTagInBody(token);
1369             return;
1370         }
1371         if (token.name() == framesetTag) {
1372             m_tree.insertHTMLElement(token);
1373             return;
1374         }
1375         if (token.name() == frameTag) {
1376             m_tree.insertSelfClosingHTMLElement(token);
1377             return;
1378         }
1379         if (token.name() == noframesTag) {
1380             processStartTagForInHead(token);
1381             return;
1382         }
1383         parseError(token);
1384         break;
1385     case AfterFramesetMode:
1386     case AfterAfterFramesetMode:
1387         ASSERT(insertionMode() == AfterFramesetMode || insertionMode() == AfterAfterFramesetMode);
1388         if (token.name() == htmlTag) {
1389             m_tree.insertHTMLHtmlStartTagInBody(token);
1390             return;
1391         }
1392         if (token.name() == noframesTag) {
1393             processStartTagForInHead(token);
1394             return;
1395         }
1396         parseError(token);
1397         break;
1398     case InSelectInTableMode:
1399         ASSERT(insertionMode() == InSelectInTableMode);
1400         if (token.name() == captionTag
1401             || token.name() == tableTag
1402             || isTableBodyContextTag(token.name())
1403             || token.name() == trTag
1404             || isTableCellContextTag(token.name())) {
1405             parseError(token);
1406             AtomicHTMLToken endSelect(HTMLToken::EndTag, selectTag.localName());
1407             processEndTag(endSelect);
1408             processStartTag(token);
1409             return;
1410         }
1411         // Fall through
1412     case InSelectMode:
1413         ASSERT(insertionMode() == InSelectMode || insertionMode() == InSelectInTableMode);
1414         if (token.name() == htmlTag) {
1415             m_tree.insertHTMLHtmlStartTagInBody(token);
1416             return;
1417         }
1418         if (token.name() == optionTag) {
1419             if (m_tree.currentElement()->hasTagName(optionTag)) {
1420                 AtomicHTMLToken endOption(HTMLToken::EndTag, optionTag.localName());
1421                 processEndTag(endOption);
1422             }
1423             m_tree.insertHTMLElement(token);
1424             return;
1425         }
1426         if (token.name() == optgroupTag) {
1427             if (m_tree.currentElement()->hasTagName(optionTag)) {
1428                 AtomicHTMLToken endOption(HTMLToken::EndTag, optionTag.localName());
1429                 processEndTag(endOption);
1430             }
1431             if (m_tree.currentElement()->hasTagName(optgroupTag)) {
1432                 AtomicHTMLToken endOptgroup(HTMLToken::EndTag, optgroupTag.localName());
1433                 processEndTag(endOptgroup);
1434             }
1435             m_tree.insertHTMLElement(token);
1436             return;
1437         }
1438         if (token.name() == selectTag) {
1439             parseError(token);
1440             AtomicHTMLToken endSelect(HTMLToken::EndTag, selectTag.localName());
1441             processEndTag(endSelect);
1442             return;
1443         }
1444         if (token.name() == inputTag
1445             || token.name() == keygenTag
1446             || token.name() == textareaTag) {
1447             parseError(token);
1448             notImplemented(); // fragment case
1449             AtomicHTMLToken endSelect(HTMLToken::EndTag, selectTag.localName());
1450             processEndTag(endSelect);
1451             processStartTag(token);
1452             return;
1453         }
1454         if (token.name() == scriptTag) {
1455             bool didProcess = processStartTagForInHead(token);
1456             ASSERT_UNUSED(didProcess, didProcess);
1457             return;
1458         }
1459         break;
1460     case InTableTextMode:
1461         processDefaultForInTableTextMode(token);
1462         processStartTag(token);
1463         break;
1464     case InForeignContentMode: {
1465         // FIXME: We're missing a bunch of if branches here.
1466         notImplemented();
1467         const AtomicString& currentNamespace = m_tree.currentElement()->namespaceURI();
1468         if (currentNamespace == MathMLNames::mathmlNamespaceURI)
1469             adjustMathMLAttributes(token);
1470          if (currentNamespace == SVGNames::svgNamespaceURI) {
1471             adjustSVGTagNameCase(token);
1472             adjustSVGAttributes(token);
1473         }
1474         adjustForeignAttributes(token);
1475         m_tree.insertForeignElement(token, currentNamespace);
1476         break;
1477     }
1478     case TextMode:
1479         notImplemented();
1480         break;
1481     }
1482 }
1483
1484 bool HTMLTreeBuilder::processBodyEndTagForInBody(AtomicHTMLToken& token)
1485 {
1486     ASSERT(token.type() == HTMLToken::EndTag);
1487     ASSERT(token.name() == bodyTag);
1488     if (!m_tree.openElements()->inScope(bodyTag.localName())) {
1489         parseError(token);
1490         return false;
1491     }
1492     notImplemented();
1493     setInsertionMode(AfterBodyMode);
1494     return true;
1495 }
1496
1497 void HTMLTreeBuilder::processAnyOtherEndTagForInBody(AtomicHTMLToken& token)
1498 {
1499     ASSERT(token.type() == HTMLToken::EndTag);
1500     HTMLElementStack::ElementRecord* record = m_tree.openElements()->topRecord();
1501     while (1) {
1502         Element* node = record->element();
1503         if (node->hasLocalName(token.name())) {
1504             m_tree.generateImpliedEndTags();
1505             if (!m_tree.currentElement()->hasLocalName(token.name())) {
1506                 parseError(token);
1507                 // FIXME: This is either a bug in the spec, or a bug in our
1508                 // implementation.  Filed a bug with HTML5:
1509                 // http://www.w3.org/Bugs/Public/show_bug.cgi?id=10080
1510                 // We might have already popped the node for the token in
1511                 // generateImpliedEndTags, just abort.
1512                 if (!m_tree.openElements()->contains(node))
1513                     return;
1514             }
1515             m_tree.openElements()->popUntilPopped(node);
1516             return;
1517         }
1518         if (isNotFormattingAndNotPhrasing(node)) {
1519             parseError(token);
1520             return;
1521         }
1522         record = record->next();
1523     }
1524 }
1525
1526 // FIXME: This probably belongs on HTMLElementStack.
1527 HTMLElementStack::ElementRecord* HTMLTreeBuilder::furthestBlockForFormattingElement(Element* formattingElement)
1528 {
1529     HTMLElementStack::ElementRecord* furthestBlock = 0;
1530     HTMLElementStack::ElementRecord* record = m_tree.openElements()->topRecord();
1531     for (; record; record = record->next()) {
1532         if (record->element() == formattingElement)
1533             return furthestBlock;
1534         if (isNotFormattingAndNotPhrasing(record->element()))
1535             furthestBlock = record;
1536     }
1537     ASSERT_NOT_REACHED();
1538     return 0;
1539 }
1540
1541 // FIXME: This should have a whitty name.
1542 // FIXME: This must be implemented in many other places in WebCore.
1543 void HTMLTreeBuilder::reparentChildren(Element* oldParent, Element* newParent)
1544 {
1545     Node* child = oldParent->firstChild();
1546     while (child) {
1547         Node* nextChild = child->nextSibling();
1548         ExceptionCode ec;
1549         newParent->appendChild(child, ec);
1550         ASSERT(!ec);
1551         child = nextChild;
1552     }
1553 }
1554
1555 // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#parsing-main-inbody
1556 void HTMLTreeBuilder::callTheAdoptionAgency(AtomicHTMLToken& token)
1557 {
1558     while (1) {
1559         // 1.
1560         Element* formattingElement = m_tree.activeFormattingElements()->closestElementInScopeWithName(token.name());
1561         if (!formattingElement || !m_tree.openElements()->inScope(formattingElement)) {
1562             parseError(token);
1563             notImplemented(); // Check the stack of open elements for a more specific parse error.
1564             return;
1565         }
1566         HTMLElementStack::ElementRecord* formattingElementRecord = m_tree.openElements()->find(formattingElement);
1567         if (!formattingElementRecord) {
1568             parseError(token);
1569             m_tree.activeFormattingElements()->remove(formattingElement);
1570             return;
1571         }
1572         if (formattingElement != m_tree.currentElement())
1573             parseError(token);
1574         // 2.
1575         HTMLElementStack::ElementRecord* furthestBlock = furthestBlockForFormattingElement(formattingElement);
1576         // 3.
1577         if (!furthestBlock) {
1578             m_tree.openElements()->popUntilPopped(formattingElement);
1579             m_tree.activeFormattingElements()->remove(formattingElement);
1580             return;
1581         }
1582         // 4.
1583         ASSERT(furthestBlock->isAbove(formattingElementRecord));
1584         Element* commonAncestor = formattingElementRecord->next()->element();
1585         // 5.
1586         HTMLFormattingElementList::Bookmark bookmark = m_tree.activeFormattingElements()->bookmarkFor(formattingElement);
1587         // 6.
1588         HTMLElementStack::ElementRecord* node = furthestBlock;
1589         HTMLElementStack::ElementRecord* nextNode = node->next();
1590         HTMLElementStack::ElementRecord* lastNode = furthestBlock;
1591         while (1) {
1592             // 6.1
1593             node = nextNode;
1594             ASSERT(node);
1595             nextNode = node->next(); // Save node->next() for the next iteration in case node is deleted in 6.2.
1596             // 6.2
1597             if (!m_tree.activeFormattingElements()->contains(node->element())) {
1598                 m_tree.openElements()->remove(node->element());
1599                 node = 0;
1600                 continue;
1601             }
1602             // 6.3
1603             if (node == formattingElementRecord)
1604                 break;
1605             // 6.5
1606             // FIXME: We're supposed to save the original token in the entry.
1607             AtomicHTMLToken fakeToken(HTMLToken::StartTag, node->element()->localName());
1608             // Is createHTMLElement correct? (instead of insertHTMLElement)
1609             // Does this code ever leave newElement unattached?
1610             RefPtr<Element> newElement = m_tree.createHTMLElement(fakeToken);
1611             HTMLFormattingElementList::Entry* nodeEntry = m_tree.activeFormattingElements()->find(node->element());
1612             nodeEntry->replaceElement(newElement.get());
1613             node->replaceElement(newElement.release());
1614             // 6.4 -- Intentionally out of order to handle the case where node
1615             // was replaced in 6.5.
1616             // http://www.w3.org/Bugs/Public/show_bug.cgi?id=10096
1617             if (lastNode == furthestBlock)
1618                 bookmark.moveToAfter(node->element());
1619             // 6.6
1620             // Use appendChild instead of parserAddChild to handle possible reparenting.
1621             ExceptionCode ec;
1622             node->element()->appendChild(lastNode->element(), ec);
1623             ASSERT(!ec);
1624             // 6.7
1625             lastNode = node;
1626         }
1627         // 7
1628         const AtomicString& commonAncestorTag = commonAncestor->localName();
1629         if (commonAncestorTag == tableTag
1630             || commonAncestorTag == trTag
1631             || isTableBodyContextTag(commonAncestorTag))
1632             m_tree.fosterParent(lastNode->element());
1633         else {
1634             ExceptionCode ec;
1635             commonAncestor->appendChild(lastNode->element(), ec);
1636             ASSERT(!ec);
1637         }
1638         // 8
1639         // FIXME: We're supposed to save the original token in the entry.
1640         AtomicHTMLToken fakeToken(HTMLToken::StartTag, formattingElement->localName());
1641         RefPtr<Element> newElement = m_tree.createHTMLElement(fakeToken);
1642         // 9
1643         reparentChildren(furthestBlock->element(), newElement.get());
1644         // 10
1645         furthestBlock->element()->parserAddChild(newElement);
1646         // 11
1647         m_tree.activeFormattingElements()->remove(formattingElement);
1648         m_tree.activeFormattingElements()->insertAt(newElement.get(), bookmark);
1649         // 12
1650         m_tree.openElements()->remove(formattingElement);
1651         m_tree.openElements()->insertAbove(newElement, furthestBlock);
1652     }
1653 }
1654
1655 void HTMLTreeBuilder::setSecondaryInsertionMode(InsertionMode mode)
1656 {
1657     ASSERT(mode != InForeignContentMode);
1658     m_secondaryInsertionMode = mode;
1659 }
1660
1661 void HTMLTreeBuilder::setInsertionModeAndEnd(InsertionMode newInsertionMode, bool foreign)
1662 {
1663     setInsertionMode(newInsertionMode);
1664     if (foreign) {
1665         setSecondaryInsertionMode(m_insertionMode);
1666         setInsertionMode(InForeignContentMode);
1667     }
1668 }
1669
1670 void HTMLTreeBuilder::resetInsertionModeAppropriately()
1671 {
1672     // http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#reset-the-insertion-mode-appropriately
1673     bool last = false;
1674     bool foreign = false;
1675     HTMLElementStack::ElementRecord* nodeRecord = m_tree.openElements()->topRecord();
1676     while (1) {
1677         Element* node = nodeRecord->element();
1678         if (node == m_tree.openElements()->bottom()) {
1679             ASSERT(m_isParsingFragment);
1680             last = true;
1681             notImplemented(); // node = m_contextElement;
1682         }
1683         if (node->hasTagName(selectTag)) {
1684             ASSERT(m_isParsingFragment);
1685             return setInsertionModeAndEnd(InSelectMode, foreign);
1686         }
1687         if (node->hasTagName(tdTag) || node->hasTagName(thTag))
1688             return setInsertionModeAndEnd(InCellMode, foreign);
1689         if (node->hasTagName(trTag))
1690             return setInsertionModeAndEnd(InRowMode, foreign);
1691         if (isTableBodyContextTag(node->localName()))
1692             return setInsertionModeAndEnd(InTableBodyMode, foreign);
1693         if (node->hasTagName(captionTag))
1694             return setInsertionModeAndEnd(InCaptionMode, foreign);
1695         if (node->hasTagName(colgroupTag)) {
1696             ASSERT(m_isParsingFragment);
1697             return setInsertionModeAndEnd(InColumnGroupMode, foreign);
1698         }
1699         if (node->hasTagName(tableTag))
1700             return setInsertionModeAndEnd(InTableMode, foreign);
1701         if (node->hasTagName(headTag)) {
1702             ASSERT(m_isParsingFragment);
1703             return setInsertionModeAndEnd(InBodyMode, foreign);
1704         }
1705         if (node->hasTagName(bodyTag))
1706             return setInsertionModeAndEnd(InBodyMode, foreign);
1707         if (node->hasTagName(framesetTag)) {
1708             ASSERT(m_isParsingFragment);
1709             return setInsertionModeAndEnd(InFramesetMode, foreign);
1710         }
1711         if (node->hasTagName(htmlTag)) {
1712             ASSERT(m_isParsingFragment);
1713             return setInsertionModeAndEnd(BeforeHeadMode, foreign);
1714         }
1715         if (node->namespaceURI() == SVGNames::svgNamespaceURI
1716             || node->namespaceURI() == MathMLNames::mathmlNamespaceURI)
1717             foreign = true;
1718         if (last) {
1719             ASSERT(m_isParsingFragment);
1720             return setInsertionModeAndEnd(InBodyMode, foreign);
1721         }
1722         nodeRecord = nodeRecord->next();
1723     }
1724 }
1725
1726 void HTMLTreeBuilder::processEndTagForInTableBody(AtomicHTMLToken& token)
1727 {
1728     ASSERT(token.type() == HTMLToken::EndTag);
1729     if (isTableBodyContextTag(token.name())) {
1730         if (!m_tree.openElements()->inTableScope(token.name())) {
1731             parseError(token);
1732             return;
1733         }
1734         m_tree.openElements()->popUntilTableBodyScopeMarker();
1735         m_tree.openElements()->pop();
1736         setInsertionMode(InTableMode);
1737         return;
1738     }
1739     if (token.name() == tableTag) {
1740         // FIXME: This is slow.
1741         if (!m_tree.openElements()->inTableScope(tbodyTag.localName()) && !m_tree.openElements()->inTableScope(theadTag.localName()) && !m_tree.openElements()->inTableScope(tfootTag.localName())) {
1742             ASSERT(m_isParsingFragment);
1743             parseError(token);
1744             return;
1745         }
1746         m_tree.openElements()->popUntilTableBodyScopeMarker();
1747         ASSERT(isTableBodyContextTag(m_tree.currentElement()->localName()));
1748         processFakeEndTag(m_tree.currentElement()->tagQName());
1749         processEndTag(token);
1750         return;
1751     }
1752     if (token.name() == bodyTag
1753         || isCaptionColOrColgroupTag(token.name())
1754         || token.name() == htmlTag
1755         || isTableCellContextTag(token.name())
1756         || token.name() == trTag) {
1757         parseError(token);
1758         return;
1759     }
1760     processEndTagForInTable(token);
1761 }
1762
1763 void HTMLTreeBuilder::processEndTagForInRow(AtomicHTMLToken& token)
1764 {
1765     ASSERT(token.type() == HTMLToken::EndTag);
1766     if (token.name() == trTag) {
1767         processTrEndTagForInRow();
1768         return;
1769     }
1770     if (token.name() == tableTag) {
1771         if (!processTrEndTagForInRow()) {
1772             ASSERT(m_isParsingFragment);
1773             return;
1774         }
1775         ASSERT(insertionMode() == InTableBodyMode);
1776         processEndTag(token);
1777         return;
1778     }
1779     if (isTableBodyContextTag(token.name())) {
1780         if (!m_tree.openElements()->inTableScope(token.name())) {
1781             parseError(token);
1782             return;
1783         }
1784         processFakeEndTag(trTag);
1785         ASSERT(insertionMode() == InTableBodyMode);
1786         processEndTag(token);
1787         return;
1788     }
1789     if (token.name() == bodyTag
1790         || isCaptionColOrColgroupTag(token.name())
1791         || token.name() == htmlTag
1792         || isTableCellContextTag(token.name())) {
1793         parseError(token);
1794         return;
1795     }
1796     processEndTagForInTable(token);
1797 }
1798
1799 void HTMLTreeBuilder::processEndTagForInCell(AtomicHTMLToken& token)
1800 {
1801     ASSERT(token.type() == HTMLToken::EndTag);
1802     if (isTableCellContextTag(token.name())) {
1803         if (!m_tree.openElements()->inTableScope(token.name())) {
1804             parseError(token);
1805             return;
1806         }
1807         m_tree.generateImpliedEndTags();
1808         if (!m_tree.currentElement()->hasLocalName(token.name()))
1809             parseError(token);
1810         m_tree.openElements()->popUntilPopped(token.name());
1811         m_tree.activeFormattingElements()->clearToLastMarker();
1812         setInsertionMode(InRowMode);
1813         ASSERT(m_tree.currentElement()->hasTagName(trTag));
1814         return;
1815     }
1816     if (token.name() == bodyTag
1817         || isCaptionColOrColgroupTag(token.name())
1818         || token.name() == htmlTag) {
1819         parseError(token);
1820         return;
1821     }
1822     if (token.name() == tableTag
1823         || token.name() == trTag
1824         || isTableBodyContextTag(token.name())) {
1825         if (!m_tree.openElements()->inTableScope(token.name())) {
1826             ASSERT(m_isParsingFragment);
1827             // FIXME: It is unclear what the exact ASSERT should be.
1828             // http://www.w3.org/Bugs/Public/show_bug.cgi?id=10098
1829             parseError(token);
1830             return;
1831         }
1832         closeTheCell();
1833         processEndTag(token);
1834         return;
1835     }
1836     processEndTagForInBody(token);
1837 }
1838
1839 void HTMLTreeBuilder::processEndTagForInBody(AtomicHTMLToken& token)
1840 {
1841     ASSERT(token.type() == HTMLToken::EndTag);
1842     if (token.name() == bodyTag) {
1843         processBodyEndTagForInBody(token);
1844         return;
1845     }
1846     if (token.name() == htmlTag) {
1847         AtomicHTMLToken endBody(HTMLToken::EndTag, bodyTag.localName());
1848         if (processBodyEndTagForInBody(endBody))
1849             processEndTag(token);
1850         return;
1851     }
1852     if (token.name() == addressTag
1853         || token.name() == articleTag
1854         || token.name() == asideTag
1855         || token.name() == blockquoteTag
1856         || token.name() == buttonTag
1857         || token.name() == centerTag
1858         || token.name() == "details"
1859         || token.name() == dirTag
1860         || token.name() == divTag
1861         || token.name() == dlTag
1862         || token.name() == fieldsetTag
1863         || token.name() == "figure"
1864         || token.name() == footerTag
1865         || token.name() == headerTag
1866         || token.name() == hgroupTag
1867         || token.name() == listingTag
1868         || token.name() == menuTag
1869         || token.name() == navTag
1870         || token.name() == olTag
1871         || token.name() == preTag
1872         || token.name() == sectionTag
1873         || token.name() == ulTag) {
1874         if (!m_tree.openElements()->inScope(token.name())) {
1875             parseError(token);
1876             return;
1877         }
1878         m_tree.generateImpliedEndTags();
1879         if (!m_tree.currentElement()->hasLocalName(token.name()))
1880             parseError(token);
1881         m_tree.openElements()->popUntilPopped(token.name());
1882         return;
1883     }
1884     if (token.name() == formTag) {
1885         RefPtr<Element> node = m_tree.takeForm();
1886         if (!node || !m_tree.openElements()->inScope(node.get())) {
1887             parseError(token);
1888             return;
1889         }
1890         m_tree.generateImpliedEndTags();
1891         if (m_tree.currentElement() != node.get())
1892             parseError(token);
1893         m_tree.openElements()->remove(node.get());
1894     }
1895     if (token.name() == pTag) {
1896         if (!m_tree.openElements()->inScope(token.name())) {
1897             parseError(token);
1898             processFakeStartTag(pTag);
1899             ASSERT(m_tree.openElements()->inScope(token.name()));
1900             processEndTag(token);
1901             return;
1902         }
1903         m_tree.generateImpliedEndTagsWithExclusion(token.name());
1904         if (!m_tree.currentElement()->hasLocalName(token.name()))
1905             parseError(token);
1906         m_tree.openElements()->popUntilPopped(token.name());
1907         return;
1908     }
1909     if (token.name() == liTag) {
1910         if (!m_tree.openElements()->inListItemScope(token.name())) {
1911             parseError(token);
1912             return;
1913         }
1914         m_tree.generateImpliedEndTagsWithExclusion(token.name());
1915         if (!m_tree.currentElement()->hasLocalName(token.name()))
1916             parseError(token);
1917         m_tree.openElements()->popUntilPopped(token.name());
1918         return;
1919     }
1920     if (token.name() == ddTag
1921         || token.name() == dtTag) {
1922         if (!m_tree.openElements()->inScope(token.name())) {
1923             parseError(token);
1924             return;
1925         }
1926         m_tree.generateImpliedEndTagsWithExclusion(token.name());
1927         if (!m_tree.currentElement()->hasLocalName(token.name()))
1928             parseError(token);
1929         m_tree.openElements()->popUntilPopped(token.name());
1930         return;
1931     }
1932     if (isNumberedHeaderTag(token.name())) {
1933         if (!m_tree.openElements()->inScope(token.name())) {
1934             parseError(token);
1935             return;
1936         }
1937         m_tree.generateImpliedEndTags();
1938         if (!m_tree.currentElement()->hasLocalName(token.name()))
1939             parseError(token);
1940         m_tree.openElements()->popUntilPopped(token.name());
1941         return;
1942     }
1943     if (token.name() == "sarcasm") {
1944         notImplemented(); // Take a deep breath.
1945         return;
1946     }
1947     if (isFormattingTag(token.name())) {
1948         callTheAdoptionAgency(token);
1949         return;
1950     }
1951     if (token.name() == appletTag
1952         || token.name() == marqueeTag
1953         || token.name() == objectTag) {
1954         if (!m_tree.openElements()->inScope(token.name())) {
1955             parseError(token);
1956             return;
1957         }
1958         m_tree.generateImpliedEndTags();
1959         if (!m_tree.currentElement()->hasLocalName(token.name()))
1960             parseError(token);
1961         m_tree.openElements()->popUntilPopped(token.name());
1962         m_tree.activeFormattingElements()->clearToLastMarker();
1963         return;
1964     }
1965     if (token.name() == brTag) {
1966         parseError(token);
1967         processFakeStartTag(brTag);
1968         return;
1969     }
1970     processAnyOtherEndTagForInBody(token);
1971 }
1972
1973 bool HTMLTreeBuilder::processCaptionEndTagForInCaption()
1974 {
1975     if (!m_tree.openElements()->inTableScope(captionTag.localName())) {
1976         ASSERT(m_isParsingFragment);
1977         // FIXME: parse error
1978         return false;
1979     }
1980     m_tree.generateImpliedEndTags();
1981     // FIXME: parse error if (!m_tree.currentElement()->hasTagName(captionTag))
1982     m_tree.openElements()->popUntilPopped(captionTag.localName());
1983     m_tree.activeFormattingElements()->clearToLastMarker();
1984     setInsertionMode(InTableMode);
1985     return true;
1986 }
1987
1988 bool HTMLTreeBuilder::processTrEndTagForInRow()
1989 {
1990     if (!m_tree.openElements()->inTableScope(trTag.localName())) {
1991         ASSERT(m_isParsingFragment);
1992         // FIXME: parse error
1993         return false;
1994     }
1995     m_tree.openElements()->popUntilTableRowScopeMarker();
1996     ASSERT(m_tree.currentElement()->hasTagName(trTag));
1997     m_tree.openElements()->pop();
1998     setInsertionMode(InTableBodyMode);
1999     return true;
2000 }
2001
2002 bool HTMLTreeBuilder::processTableEndTagForInTable()
2003 {
2004     if (!m_tree.openElements()->inTableScope(tableTag)) {
2005         ASSERT(m_isParsingFragment);
2006         // FIXME: parse error.
2007         return false;
2008     }
2009     m_tree.openElements()->popUntilPopped(tableTag.localName());
2010     resetInsertionModeAppropriately();
2011     return true;
2012 }
2013
2014 void HTMLTreeBuilder::processEndTagForInTable(AtomicHTMLToken& token)
2015 {
2016     ASSERT(token.type() == HTMLToken::EndTag);
2017     if (token.name() == tableTag) {
2018         processTableEndTagForInTable();
2019         return;
2020     }
2021     if (token.name() == bodyTag
2022         || isCaptionColOrColgroupTag(token.name())
2023         || token.name() == htmlTag
2024         || isTableBodyContextTag(token.name())
2025         || isTableCellContextTag(token.name())
2026         || token.name() == trTag) {
2027         parseError(token);
2028         return;
2029     }
2030     // Is this redirection necessary here?
2031     HTMLConstructionSite::RedirectToFosterParentGuard redirecter(m_tree, requiresRedirectToFosterParent(m_tree.currentElement()));
2032     processEndTagForInBody(token);
2033 }
2034
2035 void HTMLTreeBuilder::processEndTag(AtomicHTMLToken& token)
2036 {
2037     ASSERT(token.type() == HTMLToken::EndTag);
2038     switch (insertionMode()) {
2039     case InitialMode:
2040         ASSERT(insertionMode() == InitialMode);
2041         processDefaultForInitialMode(token);
2042         // Fall through.
2043     case BeforeHTMLMode:
2044         ASSERT(insertionMode() == BeforeHTMLMode);
2045         if (token.name() != headTag && token.name() != bodyTag && token.name() != htmlTag && token.name() != brTag) {
2046             parseError(token);
2047             return;
2048         }
2049         processDefaultForBeforeHTMLMode(token);
2050         // Fall through.
2051     case BeforeHeadMode:
2052         ASSERT(insertionMode() == BeforeHeadMode);
2053         if (token.name() != headTag && token.name() != bodyTag && token.name() != htmlTag && token.name() != brTag) {
2054             parseError(token);
2055             return;
2056         }
2057         processDefaultForBeforeHeadMode(token);
2058         // Fall through.
2059     case InHeadMode:
2060         ASSERT(insertionMode() == InHeadMode);
2061         if (token.name() == headTag) {
2062             m_tree.openElements()->popHTMLHeadElement();
2063             setInsertionMode(AfterHeadMode);
2064             return;
2065         }
2066         if (token.name() != bodyTag && token.name() != htmlTag && token.name() != brTag) {
2067             parseError(token);
2068             return;
2069         }
2070         processDefaultForInHeadMode(token);
2071         // Fall through.
2072     case AfterHeadMode:
2073         ASSERT(insertionMode() == AfterHeadMode);
2074         if (token.name() != bodyTag && token.name() != htmlTag && token.name() != brTag) {
2075             parseError(token);
2076             return;
2077         }
2078         processDefaultForAfterHeadMode(token);
2079         // Fall through
2080     case InBodyMode:
2081         ASSERT(insertionMode() == InBodyMode);
2082         processEndTagForInBody(token);
2083         break;
2084     case InTableMode:
2085         ASSERT(insertionMode() == InTableMode);
2086         processEndTagForInTable(token);
2087         break;
2088     case InCaptionMode:
2089         ASSERT(insertionMode() == InCaptionMode);
2090         if (token.name() == captionTag) {
2091             processCaptionEndTagForInCaption();
2092             return;
2093         }
2094         if (token.name() == tableTag) {
2095             parseError(token);
2096             if (!processCaptionEndTagForInCaption()) {
2097                 ASSERT(m_isParsingFragment);
2098                 return;
2099             }
2100             processEndTag(token);
2101             return;
2102         }
2103         if (token.name() == bodyTag
2104             || token.name() == colTag
2105             || token.name() == colgroupTag
2106             || token.name() == htmlTag
2107             || isTableBodyContextTag(token.name())
2108             || isTableCellContextTag(token.name())
2109             || token.name() == trTag) {
2110             parseError(token);
2111             return;
2112         }
2113         processEndTagForInBody(token);
2114         break;
2115     case InColumnGroupMode:
2116         ASSERT(insertionMode() == InColumnGroupMode);
2117         if (token.name() == colgroupTag) {
2118             processColgroupEndTagForInColumnGroup();
2119             return;
2120         }
2121         if (token.name() == colTag) {
2122             parseError(token);
2123             return;
2124         }
2125         if (!processColgroupEndTagForInColumnGroup()) {
2126             ASSERT(m_isParsingFragment);
2127             return;
2128         }
2129         processEndTag(token);
2130         break;
2131     case InRowMode:
2132         ASSERT(insertionMode() == InRowMode);
2133         processEndTagForInRow(token);
2134         break;
2135     case InCellMode:
2136         ASSERT(insertionMode() == InCellMode);
2137         processEndTagForInCell(token);
2138         break;
2139     case InTableBodyMode:
2140         ASSERT(insertionMode() == InTableBodyMode);
2141         processEndTagForInTableBody(token);
2142         break;
2143     case AfterBodyMode:
2144         ASSERT(insertionMode() == AfterBodyMode);
2145         if (token.name() == htmlTag) {
2146             if (m_isParsingFragment) {
2147                 parseError(token);
2148                 return;
2149             }
2150             setInsertionMode(AfterAfterBodyMode);
2151             return;
2152         }
2153         // Fall through.
2154     case AfterAfterBodyMode:
2155         ASSERT(insertionMode() == AfterBodyMode || insertionMode() == AfterAfterBodyMode);
2156         parseError(token);
2157         setInsertionMode(InBodyMode);
2158         processEndTag(token);
2159         break;
2160     case InHeadNoscriptMode:
2161         ASSERT(insertionMode() == InHeadNoscriptMode);
2162         if (token.name() == noscriptTag) {
2163             ASSERT(m_tree.currentElement()->hasTagName(noscriptTag));
2164             m_tree.openElements()->pop();
2165             ASSERT(m_tree.currentElement()->hasTagName(headTag));
2166             setInsertionMode(InHeadMode);
2167             return;
2168         }
2169         if (token.name() != brTag) {
2170             parseError(token);
2171             return;
2172         }
2173         processDefaultForInHeadNoscriptMode(token);
2174         processToken(token);
2175         break;
2176     case TextMode:
2177         if (token.name() == scriptTag) {
2178             // Pause ourselves so that parsing stops until the script can be processed by the caller.
2179             m_isPaused = true;
2180             ASSERT(m_tree.currentElement()->hasTagName(scriptTag));
2181             m_scriptToProcess = m_tree.currentElement();
2182             m_tree.openElements()->pop();
2183             setInsertionMode(m_originalInsertionMode);
2184             return;
2185         }
2186         m_tree.openElements()->pop();
2187         setInsertionMode(m_originalInsertionMode);
2188         break;
2189     case InFramesetMode:
2190         ASSERT(insertionMode() == InFramesetMode);
2191         if (token.name() == framesetTag) {
2192             if (m_tree.currentElement() == m_tree.openElements()->htmlElement()) {
2193                 parseError(token);
2194                 return;
2195             }
2196             m_tree.openElements()->pop();
2197             if (!m_isParsingFragment && !m_tree.currentElement()->hasTagName(framesetTag))
2198                 setInsertionMode(AfterFramesetMode);
2199             return;
2200         }
2201         break;
2202     case AfterFramesetMode:
2203         ASSERT(insertionMode() == AfterFramesetMode);
2204         if (token.name() == htmlTag) {
2205             setInsertionMode(AfterAfterFramesetMode);
2206             return;
2207         }
2208         // Fall through.
2209     case AfterAfterFramesetMode:
2210         ASSERT(insertionMode() == AfterFramesetMode || insertionMode() == AfterAfterFramesetMode);
2211         parseError(token);
2212         break;
2213     case InSelectInTableMode:
2214         ASSERT(insertionMode() == InSelectInTableMode);
2215         if (token.name() == captionTag
2216             || token.name() == tableTag
2217             || isTableBodyContextTag(token.name())
2218             || token.name() == trTag
2219             || isTableCellContextTag(token.name())) {
2220             parseError(token);
2221             if (m_tree.openElements()->inTableScope(token.name())) {
2222                 AtomicHTMLToken endSelect(HTMLToken::EndTag, selectTag.localName());
2223                 processEndTag(endSelect);
2224                 processEndTag(token);
2225             }
2226             return;
2227         }
2228         // Fall through.
2229     case InSelectMode:
2230         ASSERT(insertionMode() == InSelectMode || insertionMode() == InSelectInTableMode);
2231         if (token.name() == optgroupTag) {
2232             if (m_tree.currentElement()->hasTagName(optionTag) && m_tree.oneBelowTop()->hasTagName(optgroupTag))
2233                 processFakeEndTag(optionTag);
2234             if (m_tree.currentElement()->hasTagName(optgroupTag)) {
2235                 m_tree.openElements()->pop();
2236                 return;
2237             }
2238             parseError(token);
2239             return;
2240         }
2241         if (token.name() == optionTag) {
2242             if (m_tree.currentElement()->hasTagName(optionTag)) {
2243                 m_tree.openElements()->pop();
2244                 return;
2245             }
2246             parseError(token);
2247             return;
2248         }
2249         if (token.name() == selectTag) {
2250             notImplemented(); // fragment case
2251             m_tree.openElements()->popUntilPopped(selectTag.localName());
2252             resetInsertionModeAppropriately();
2253             return;
2254         }
2255         break;
2256     case InTableTextMode:
2257         processDefaultForInTableTextMode(token);
2258         processEndTag(token);
2259         break;
2260     case InForeignContentMode:
2261         if (token.name() == SVGNames::scriptTag && m_tree.currentElement()->hasTagName(SVGNames::scriptTag)) {
2262             notImplemented();
2263             return;
2264         }
2265         if (m_tree.currentElement()->namespaceURI() != xhtmlNamespaceURI) {
2266             // FIXME: This code just wants an Element* iterator, instead of an ElementRecord*
2267             HTMLElementStack::ElementRecord* nodeRecord = m_tree.openElements()->topRecord();
2268             if (!nodeRecord->element()->hasLocalName(token.name())) {
2269                 parseError(token);
2270                 // FIXME: This return is not in the spec but appears to be needed.
2271                 // http://www.w3.org/Bugs/Public/show_bug.cgi?id=10118
2272                 return;
2273             }
2274             while (1) {
2275                 if (nodeRecord->element()->hasLocalName(token.name())) {
2276                     m_tree.openElements()->popUntilPopped(nodeRecord->element());
2277                     return;
2278                 }
2279                 nodeRecord = nodeRecord->next();
2280                 if (nodeRecord->element()->namespaceURI() == xhtmlNamespaceURI)
2281                     processEndTagUsingSecondaryInsertionModeAndAdjustInsertionMode(token);
2282             }
2283             return;
2284         }
2285         processEndTagUsingSecondaryInsertionModeAndAdjustInsertionMode(token);
2286         break;
2287     }
2288 }
2289
2290 class HTMLTreeBuilder::FakeInsertionMode : public Noncopyable {
2291 public:
2292     FakeInsertionMode(HTMLTreeBuilder* treeBuilder, InsertionMode mode)
2293         : m_treeBuilder(treeBuilder)
2294         , m_originalMode(treeBuilder->insertionMode())
2295     {
2296         m_treeBuilder->setFakeInsertionMode(mode);
2297     }
2298
2299     ~FakeInsertionMode()
2300     {
2301         if (m_treeBuilder->isFakeInsertionMode())
2302             m_treeBuilder->setInsertionMode(m_originalMode);
2303     }
2304
2305 private:
2306     HTMLTreeBuilder* m_treeBuilder;
2307     InsertionMode m_originalMode;
2308 };
2309
2310 // This handles both secondary insertion mode processing, as well as updating
2311 // the insertion mode.  These are separate steps in the spec, but always occur
2312 // right after one another.
2313 void HTMLTreeBuilder::processEndTagUsingSecondaryInsertionModeAndAdjustInsertionMode(AtomicHTMLToken& token)
2314 {
2315     {
2316         FakeInsertionMode fakeMode(this, m_secondaryInsertionMode);
2317         processEndTag(token);
2318     }
2319     if (insertionMode() == InForeignContentMode && m_tree.openElements()->hasOnlyHTMLElementsInScope())
2320         setInsertionMode(m_secondaryInsertionMode);
2321 }
2322
2323 void HTMLTreeBuilder::processComment(AtomicHTMLToken& token)
2324 {
2325     ASSERT(token.type() == HTMLToken::Comment);
2326     if (m_insertionMode == InitialMode
2327         || m_insertionMode == BeforeHTMLMode
2328         || m_insertionMode == AfterAfterBodyMode
2329         || m_insertionMode == AfterAfterFramesetMode) {
2330         m_tree.insertCommentOnDocument(token);
2331         return;
2332     }
2333     if (m_insertionMode == AfterBodyMode) {
2334         m_tree.insertCommentOnHTMLHtmlElement(token);
2335         return;
2336     }
2337     if (m_insertionMode == InTableTextMode) {
2338         processDefaultForInTableTextMode(token);
2339         processComment(token);
2340         return;
2341     }
2342     m_tree.insertComment(token);
2343 }
2344
2345 void HTMLTreeBuilder::processCharacter(AtomicHTMLToken& token)
2346 {
2347     ASSERT(token.type() == HTMLToken::Character);
2348
2349     // FIXME: Currently this design has an extra memcpy because we copy the
2350     // characters out of the HTMLTokenizer's buffer into the AtomicHTMLToken
2351     // and then into the text node.  What we'd really like is to copy directly
2352     // from the HTMLTokenizer's buffer into the text node.
2353     ExternalCharacterTokenBuffer buffer(token);
2354
2355 ReprocessBuffer:
2356     switch (insertionMode()) {
2357     case InitialMode: {
2358         ASSERT(insertionMode() == InitialMode);
2359         buffer.skipLeadingWhitespace();
2360         if (buffer.isEmpty())
2361             return;
2362         processDefaultForInitialMode(token);
2363         // Fall through.
2364     }
2365     case BeforeHTMLMode: {
2366         ASSERT(insertionMode() == BeforeHTMLMode);
2367         buffer.skipLeadingWhitespace();
2368         if (buffer.isEmpty())
2369             return;
2370         processDefaultForBeforeHTMLMode(token);
2371         // Fall through.
2372     }
2373     case BeforeHeadMode: {
2374         ASSERT(insertionMode() == BeforeHeadMode);
2375         buffer.skipLeadingWhitespace();
2376         if (buffer.isEmpty())
2377             return;
2378         processDefaultForBeforeHeadMode(token);
2379         // Fall through.
2380     }
2381     case InHeadMode: {
2382         ASSERT(insertionMode() == InHeadMode);
2383         String leadingWhitespace = buffer.takeLeadingWhitespace();
2384         if (!leadingWhitespace.isEmpty())
2385             m_tree.insertTextNode(leadingWhitespace);
2386         if (buffer.isEmpty())
2387             return;
2388         processDefaultForInHeadMode(token);
2389         // Fall through.
2390     }
2391     case AfterHeadMode: {
2392         ASSERT(insertionMode() == AfterHeadMode);
2393         String leadingWhitespace = buffer.takeLeadingWhitespace();
2394         if (!leadingWhitespace.isEmpty())
2395             m_tree.insertTextNode(leadingWhitespace);
2396         if (buffer.isEmpty())
2397             return;
2398         processDefaultForAfterHeadMode(token);
2399         // Fall through.
2400     }
2401     case InBodyMode:
2402     case InCaptionMode:
2403     case InCellMode: {
2404         ASSERT(insertionMode() == InBodyMode || insertionMode() == InCaptionMode || insertionMode() == InCellMode);
2405         m_tree.reconstructTheActiveFormattingElements();
2406         String characters = buffer.takeRemaining();
2407         m_tree.insertTextNode(characters);
2408         if (m_framesetOk && hasNonWhitespace(characters))
2409             m_framesetOk = false;
2410         break;
2411     }
2412     case InTableMode:
2413     case InTableBodyMode:
2414     case InRowMode: {
2415         ASSERT(insertionMode() == InTableMode || insertionMode() == InTableBodyMode || insertionMode() == InRowMode);
2416         ASSERT(m_pendingTableCharacters.isEmpty());
2417         m_originalInsertionMode = m_insertionMode;
2418         setInsertionMode(InTableTextMode);
2419         // Fall through.
2420     }
2421     case InTableTextMode: {
2422         buffer.giveRemainingTo(m_pendingTableCharacters);
2423         break;
2424     }
2425     case InColumnGroupMode: {
2426         ASSERT(insertionMode() == InColumnGroupMode);
2427         String leadingWhitespace = buffer.takeLeadingWhitespace();
2428         if (!leadingWhitespace.isEmpty())
2429             m_tree.insertTextNode(leadingWhitespace);
2430         if (buffer.isEmpty())
2431             return;
2432         if (!processColgroupEndTagForInColumnGroup()) {
2433             ASSERT(m_isParsingFragment);
2434             return;
2435         }
2436         goto ReprocessBuffer;
2437     }
2438     case AfterBodyMode:
2439     case AfterAfterBodyMode: {
2440         ASSERT(insertionMode() == AfterBodyMode || insertionMode() == AfterAfterBodyMode);
2441         parseError(token);
2442         setInsertionMode(InBodyMode);
2443         goto ReprocessBuffer;
2444         break;
2445     }
2446     case TextMode: {
2447         ASSERT(insertionMode() == TextMode);
2448         m_tree.insertTextNode(buffer.takeRemaining());
2449         break;
2450     }
2451     case InHeadNoscriptMode: {
2452         ASSERT(insertionMode() == InHeadNoscriptMode);
2453         String leadingWhitespace = buffer.takeLeadingWhitespace();
2454         if (!leadingWhitespace.isEmpty())
2455             m_tree.insertTextNode(leadingWhitespace);
2456         if (buffer.isEmpty())
2457             return;
2458         processDefaultForInHeadNoscriptMode(token);
2459         goto ReprocessBuffer;
2460         break;
2461     }
2462     case InFramesetMode:
2463     case AfterFramesetMode: {
2464         ASSERT(insertionMode() == InFramesetMode || insertionMode() == AfterFramesetMode || insertionMode() == AfterAfterFramesetMode);
2465         String leadingWhitespace = buffer.takeRemainingWhitespace();
2466         if (!leadingWhitespace.isEmpty())
2467             m_tree.insertTextNode(leadingWhitespace);
2468         // FIXME: We should generate a parse error if we skipped over any
2469         // non-whitespace characters.
2470         break;
2471     }
2472     case InSelectInTableMode:
2473     case InSelectMode: {
2474         ASSERT(insertionMode() == InSelectMode || insertionMode() == InSelectInTableMode);
2475         m_tree.insertTextNode(buffer.takeRemaining());
2476         break;
2477     }
2478     case InForeignContentMode: {
2479         ASSERT(insertionMode() == InForeignContentMode);
2480         String characters = buffer.takeRemaining();
2481         m_tree.insertTextNode(characters);
2482         if (m_framesetOk && hasNonWhitespace(characters))
2483             m_framesetOk = false;
2484         break;
2485     }
2486     case AfterAfterFramesetMode: {
2487         String leadingWhitespace = buffer.takeRemainingWhitespace();
2488         if (!leadingWhitespace.isEmpty()) {
2489             m_tree.reconstructTheActiveFormattingElements();
2490             m_tree.insertTextNode(leadingWhitespace);
2491         }
2492         // FIXME: We should generate a parse error if we skipped over any
2493         // non-whitespace characters.
2494         break;
2495     }
2496     }
2497 }
2498
2499 void HTMLTreeBuilder::processEndOfFile(AtomicHTMLToken& token)
2500 {
2501     ASSERT(token.type() == HTMLToken::EndOfFile);
2502     switch (insertionMode()) {
2503     case InitialMode:
2504         ASSERT(insertionMode() == InitialMode);
2505         processDefaultForInitialMode(token);
2506         // Fall through.
2507     case BeforeHTMLMode:
2508         ASSERT(insertionMode() == BeforeHTMLMode);
2509         processDefaultForBeforeHTMLMode(token);
2510         // Fall through.
2511     case BeforeHeadMode:
2512         ASSERT(insertionMode() == BeforeHeadMode);
2513         processDefaultForBeforeHeadMode(token);
2514         // Fall through.
2515     case InHeadMode:
2516         ASSERT(insertionMode() == InHeadMode);
2517         processDefaultForInHeadMode(token);
2518         // Fall through.
2519     case AfterHeadMode:
2520         ASSERT(insertionMode() == AfterHeadMode);
2521         processDefaultForAfterHeadMode(token);
2522         // Fall through
2523     case InBodyMode:
2524     case InCellMode:
2525         ASSERT(insertionMode() == InBodyMode || insertionMode() == InCellMode);
2526         notImplemented();
2527         break;
2528     case AfterBodyMode:
2529     case AfterAfterBodyMode:
2530         ASSERT(insertionMode() == AfterBodyMode || insertionMode() == AfterAfterBodyMode);
2531         notImplemented();
2532         break;
2533     case InHeadNoscriptMode:
2534         ASSERT(insertionMode() == InHeadNoscriptMode);
2535         processDefaultForInHeadNoscriptMode(token);
2536         processToken(token);
2537         break;
2538     case AfterFramesetMode:
2539     case AfterAfterFramesetMode:
2540         ASSERT(insertionMode() == AfterFramesetMode || insertionMode() == AfterAfterFramesetMode);
2541         break;
2542     case InFramesetMode:
2543     case InTableMode:
2544     case InTableBodyMode:
2545     case InSelectInTableMode:
2546     case InSelectMode:
2547         ASSERT(insertionMode() == InSelectMode || insertionMode() == InSelectInTableMode || insertionMode() == InTableMode || insertionMode() == InFramesetMode || insertionMode() == InTableBodyMode);
2548         if (m_tree.currentElement() != m_tree.openElements()->htmlElement())
2549             parseError(token);
2550         break;
2551     case InColumnGroupMode:
2552         if (m_tree.currentElement() == m_tree.openElements()->htmlElement()) {
2553             ASSERT(m_isParsingFragment);
2554             return;
2555         }
2556         if (!processColgroupEndTagForInColumnGroup()) {
2557             ASSERT(m_isParsingFragment);
2558             return;
2559         }
2560         processEndOfFile(token);
2561         break;
2562     case InForeignContentMode:
2563         parseError(token);
2564         // FIXME: Following the spec would infinitely recurse on <svg><svg>
2565         // http://www.w3.org/Bugs/Public/show_bug.cgi?id=10115
2566         while (m_tree.currentElement()) {
2567             if (m_tree.currentElement()->namespaceURI() == xhtmlNamespaceURI)
2568                 break;
2569             m_tree.openElements()->pop();
2570         }
2571         setInsertionMode(m_secondaryInsertionMode);
2572         processEndOfFile(token);
2573         break;
2574     case InTableTextMode:
2575         processDefaultForInTableTextMode(token);
2576         processEndOfFile(token);
2577         break;
2578     case TextMode:
2579     case InCaptionMode:
2580     case InRowMode:
2581         notImplemented();
2582         break;
2583     }
2584 }
2585
2586 void HTMLTreeBuilder::processDefaultForInitialMode(AtomicHTMLToken& token)
2587 {
2588     notImplemented();
2589     parseError(token);
2590     setInsertionMode(BeforeHTMLMode);
2591 }
2592
2593 void HTMLTreeBuilder::processDefaultForBeforeHTMLMode(AtomicHTMLToken&)
2594 {
2595     AtomicHTMLToken startHTML(HTMLToken::StartTag, htmlTag.localName());
2596     m_tree.insertHTMLHtmlStartTagBeforeHTML(startHTML);
2597     setInsertionMode(BeforeHeadMode);
2598 }
2599
2600 void HTMLTreeBuilder::processDefaultForBeforeHeadMode(AtomicHTMLToken&)
2601 {
2602     AtomicHTMLToken startHead(HTMLToken::StartTag, headTag.localName());
2603     processStartTag(startHead);
2604 }
2605
2606 void HTMLTreeBuilder::processDefaultForInHeadMode(AtomicHTMLToken&)
2607 {
2608     AtomicHTMLToken endHead(HTMLToken::EndTag, headTag.localName());
2609     processEndTag(endHead);
2610 }
2611
2612 void HTMLTreeBuilder::processDefaultForInHeadNoscriptMode(AtomicHTMLToken&)
2613 {
2614     AtomicHTMLToken endNoscript(HTMLToken::EndTag, noscriptTag.localName());
2615     processEndTag(endNoscript);
2616 }
2617
2618 void HTMLTreeBuilder::processDefaultForAfterHeadMode(AtomicHTMLToken&)
2619 {
2620     AtomicHTMLToken startBody(HTMLToken::StartTag, bodyTag.localName());
2621     processStartTag(startBody);
2622     m_framesetOk = true;
2623 }
2624
2625 void HTMLTreeBuilder::processDefaultForInTableTextMode(AtomicHTMLToken& token)
2626 {
2627     String characters = String::adopt(m_pendingTableCharacters);
2628     if (hasNonWhitespace(characters)) {
2629         parseError(token);
2630         HTMLConstructionSite::RedirectToFosterParentGuard redirecter(m_tree, requiresRedirectToFosterParent(m_tree.currentElement()));
2631         m_tree.reconstructTheActiveFormattingElements();
2632         m_tree.insertTextNode(characters);
2633         m_framesetOk = false;
2634         setInsertionMode(m_originalInsertionMode);
2635         return;
2636     }
2637     m_tree.insertTextNode(characters);
2638     setInsertionMode(m_originalInsertionMode);
2639 }
2640
2641 bool HTMLTreeBuilder::processStartTagForInHead(AtomicHTMLToken& token)
2642 {
2643     ASSERT(token.type() == HTMLToken::StartTag);
2644     if (token.name() == htmlTag) {
2645         m_tree.insertHTMLHtmlStartTagInBody(token);
2646         return true;
2647     }
2648     // FIXME: Atomize "command".
2649     if (token.name() == baseTag
2650         || token.name() == "command"
2651         || token.name() == linkTag
2652         || token.name() == metaTag) {
2653         m_tree.insertSelfClosingHTMLElement(token);
2654         // Note: The custom processing for the <meta> tag is done in HTMLMetaElement::process().
2655         return true;
2656     }
2657     if (token.name() == titleTag) {
2658         processGenericRCDATAStartTag(token);
2659         return true;
2660     }
2661     if (token.name() == noscriptTag) {
2662         if (isScriptingFlagEnabled(m_document->frame())) {
2663             processGenericRawTextStartTag(token);
2664             return true;
2665         }
2666         m_tree.insertHTMLElement(token);
2667         setInsertionMode(InHeadNoscriptMode);
2668         return true;
2669     }
2670     if (token.name() == noframesTag || token.name() == styleTag) {
2671         processGenericRawTextStartTag(token);
2672         return true;
2673     }
2674     if (token.name() == scriptTag) {
2675         processScriptStartTag(token);
2676         return true;
2677     }
2678     if (token.name() == headTag) {
2679         parseError(token);
2680         return true;
2681     }
2682     return false;
2683 }
2684
2685 void HTMLTreeBuilder::processGenericRCDATAStartTag(AtomicHTMLToken& token)
2686 {
2687     ASSERT(token.type() == HTMLToken::StartTag);
2688     m_tree.insertHTMLElement(token);
2689     m_tokenizer->setState(HTMLTokenizer::RCDATAState);
2690     m_originalInsertionMode = m_insertionMode;
2691     setInsertionMode(TextMode);
2692 }
2693
2694 void HTMLTreeBuilder::processGenericRawTextStartTag(AtomicHTMLToken& token)
2695 {
2696     ASSERT(token.type() == HTMLToken::StartTag);
2697     m_tree.insertHTMLElement(token);
2698     m_tokenizer->setState(HTMLTokenizer::RAWTEXTState);
2699     m_originalInsertionMode = m_insertionMode;
2700     setInsertionMode(TextMode);
2701 }
2702
2703 void HTMLTreeBuilder::processScriptStartTag(AtomicHTMLToken& token)
2704 {
2705     ASSERT(token.type() == HTMLToken::StartTag);
2706     m_tree.insertScriptElement(token);
2707     m_tokenizer->setState(HTMLTokenizer::ScriptDataState);
2708     m_originalInsertionMode = m_insertionMode;
2709     setInsertionMode(TextMode);
2710 }
2711
2712 void HTMLTreeBuilder::finished()
2713 {
2714     // We should call m_document->finishedParsing() here, except
2715     // m_legacyTreeBuilder->finished() does it for us.
2716     if (m_legacyTreeBuilder) {
2717         m_legacyTreeBuilder->finished();
2718         return;
2719     }
2720
2721     // Warning, this may delete the parser, so don't try to do anything else after this.
2722     if (!m_isParsingFragment)
2723         m_document->finishedParsing();
2724 }
2725
2726 bool HTMLTreeBuilder::isScriptingFlagEnabled(Frame* frame)
2727 {
2728     if (!frame)
2729         return false;
2730     if (ScriptController* scriptController = frame->script())
2731         return scriptController->canExecuteScripts(NotAboutToExecuteScript);
2732     return false;
2733 }
2734
2735 }