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