960615660738ed7d2408bb1fe5696a3b55a1f6b5
[WebKit-https.git] / WebCore / html / HTMLTreeBuilder.cpp
1 /*
2  * Copyright (C) 2010 Google, Inc. All Rights Reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL GOOGLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #include "config.h"
27 #include "HTMLTreeBuilder.h"
28
29 #include "Comment.h"
30 #include "DocumentFragment.h"
31 #include "DocumentType.h"
32 #include "Element.h"
33 #include "Frame.h"
34 #include "HTMLElementFactory.h"
35 #include "HTMLScriptElement.h"
36 #include "HTMLTokenizer.h"
37 #include "HTMLToken.h"
38 #include "HTMLDocument.h"
39 #include "HTMLHtmlElement.h"
40 #include "LegacyHTMLDocumentParser.h"
41 #include "HTMLNames.h"
42 #include "LegacyHTMLTreeBuilder.h"
43 #include "NotImplemented.h"
44 #include "Settings.h"
45 #include "ScriptController.h"
46 #include "Text.h"
47 #include <wtf/UnusedParam.h>
48
49 namespace WebCore {
50
51 using namespace HTMLNames;
52
53 static const int uninitializedLineNumberValue = -1;
54
55 namespace {
56
57 inline bool isTreeBuilderWhiteSpace(UChar cc)
58 {
59     return cc == '\t' || cc == '\x0A' || cc == '\x0C' || cc == '\x0D' || cc == ' ';
60 }
61
62 bool shouldUseLegacyTreeBuilder(Document* document)
63 {
64     return !document->settings() || !document->settings()->html5TreeBuilderEnabled();
65 }
66
67 } // namespace
68
69 HTMLTreeBuilder::HTMLTreeBuilder(HTMLTokenizer* tokenizer, HTMLDocument* document, bool reportErrors)
70     : m_framesetOk(true)
71     , m_document(document)
72     , m_reportErrors(reportErrors)
73     , m_isPaused(false)
74     , m_insertionMode(InitialMode)
75     , m_originalInsertionMode(InitialMode)
76     , m_tokenizer(tokenizer)
77     , m_legacyTreeBuilder(shouldUseLegacyTreeBuilder(document) ? new LegacyHTMLTreeBuilder(document, reportErrors) : 0)
78     , m_lastScriptElementStartLine(uninitializedLineNumberValue)
79     , m_scriptToProcessStartLine(uninitializedLineNumberValue)
80     , m_fragmentScriptingPermission(FragmentScriptingAllowed)
81     , m_isParsingFragment(false)
82 {
83 }
84
85 // FIXME: Member variables should be grouped into self-initializing structs to
86 // minimize code duplication between these constructors.
87 HTMLTreeBuilder::HTMLTreeBuilder(HTMLTokenizer* tokenizer, DocumentFragment* fragment, FragmentScriptingPermission scriptingPermission)
88     : m_framesetOk(true)
89     , m_document(fragment->document())
90     , m_reportErrors(false) // FIXME: Why not report errors in fragments?
91     , m_isPaused(false)
92     , m_insertionMode(InitialMode)
93     , m_originalInsertionMode(InitialMode)
94     , m_tokenizer(tokenizer)
95     , m_legacyTreeBuilder(new LegacyHTMLTreeBuilder(fragment, scriptingPermission))
96     , m_lastScriptElementStartLine(uninitializedLineNumberValue)
97     , m_scriptToProcessStartLine(uninitializedLineNumberValue)
98     , m_fragmentScriptingPermission(scriptingPermission)
99     , m_isParsingFragment(true)
100 {
101 }
102
103 HTMLTreeBuilder::~HTMLTreeBuilder()
104 {
105 }
106
107 static void convertToOldStyle(const AtomicHTMLToken& token, Token& oldStyleToken)
108 {
109     switch (token.type()) {
110     case HTMLToken::Uninitialized:
111     case HTMLToken::DOCTYPE:
112         ASSERT_NOT_REACHED();
113         break;
114     case HTMLToken::EndOfFile:
115         ASSERT_NOT_REACHED();
116         notImplemented();
117         break;
118     case HTMLToken::StartTag:
119     case HTMLToken::EndTag: {
120         oldStyleToken.beginTag = (token.type() == HTMLToken::StartTag);
121         oldStyleToken.selfClosingTag = token.selfClosing();
122         oldStyleToken.tagName = token.name();
123         oldStyleToken.attrs = token.attributes();
124         break;
125     }
126     case HTMLToken::Comment:
127         oldStyleToken.tagName = commentAtom;
128         oldStyleToken.text = token.comment().impl();
129         break;
130     case HTMLToken::Character:
131         oldStyleToken.tagName = textAtom;
132         oldStyleToken.text = token.characters().impl();
133         break;
134     }
135 }
136
137 void HTMLTreeBuilder::handleScriptStartTag()
138 {
139     notImplemented(); // The HTML frgment case?
140     m_tokenizer->setState(HTMLTokenizer::ScriptDataState);
141     notImplemented(); // Save insertion mode.
142 }
143
144 void HTMLTreeBuilder::handleScriptEndTag(Element* scriptElement, int scriptStartLine)
145 {
146     ASSERT(!m_scriptToProcess); // Caller never called takeScriptToProcess!
147     ASSERT(m_scriptToProcessStartLine == uninitializedLineNumberValue); // Caller never called takeScriptToProcess!
148     notImplemented(); // Save insertion mode and insertion point?
149
150     // Pause ourselves so that parsing stops until the script can be processed by the caller.
151     m_isPaused = true;
152     m_scriptToProcess = scriptElement;
153     // Lexer line numbers are 0-based, ScriptSourceCode expects 1-based lines,
154     // so we convert here before passing the line number off to HTMLScriptRunner.
155     m_scriptToProcessStartLine = scriptStartLine + 1;
156 }
157
158 PassRefPtr<Element> HTMLTreeBuilder::takeScriptToProcess(int& scriptStartLine)
159 {
160     // Unpause ourselves, callers may pause us again when processing the script.
161     // The HTML5 spec is written as though scripts are executed inside the tree
162     // builder.  We pause the parser to exit the tree builder, and then resume
163     // before running scripts.
164     m_isPaused = false;
165     scriptStartLine = m_scriptToProcessStartLine;
166     m_scriptToProcessStartLine = uninitializedLineNumberValue;
167     return m_scriptToProcess.release();
168 }
169
170 HTMLTokenizer::State HTMLTreeBuilder::adjustedLexerState(HTMLTokenizer::State state, const AtomicString& tagName, Frame* frame)
171 {
172     if (tagName == textareaTag || tagName == titleTag)
173         return HTMLTokenizer::RCDATAState;
174
175     if (tagName == styleTag || tagName == iframeTag || tagName == xmpTag || tagName == noembedTag
176         || tagName == noframesTag || (tagName == noscriptTag && isScriptingFlagEnabled(frame)))
177         return HTMLTokenizer::RAWTEXTState;
178
179     if (tagName == plaintextTag)
180         return HTMLTokenizer::PLAINTEXTState;
181
182     return state;
183 }
184
185 void HTMLTreeBuilder::passTokenToLegacyParser(HTMLToken& token)
186 {
187     if (token.type() == HTMLToken::DOCTYPE) {
188         DoctypeToken doctypeToken;
189         doctypeToken.m_name.append(token.name().data(), token.name().size());
190         doctypeToken.m_publicID = token.publicIdentifier();
191         doctypeToken.m_systemID = token.systemIdentifier();
192         doctypeToken.m_forceQuirks = token.forceQuirks();
193
194         m_legacyTreeBuilder->parseDoctypeToken(&doctypeToken);
195         return;
196     }
197
198     if (token.type() == HTMLToken::EndOfFile)
199         return;
200
201     // For now, we translate into an old-style token for testing.
202     Token oldStyleToken;
203     AtomicHTMLToken atomicToken(token);
204     convertToOldStyle(atomicToken, oldStyleToken);
205
206     RefPtr<Node> result =  m_legacyTreeBuilder->parseToken(&oldStyleToken);
207     if (token.type() == HTMLToken::StartTag) {
208         // This work is supposed to be done by the parser, but
209         // when using the old parser for we have to do this manually.
210         if (oldStyleToken.tagName == scriptTag) {
211             handleScriptStartTag();
212             m_lastScriptElement = static_pointer_cast<Element>(result);
213             m_lastScriptElementStartLine = m_tokenizer->lineNumber();
214         } else if (oldStyleToken.tagName == preTag || oldStyleToken.tagName == listingTag)
215             m_tokenizer->skipLeadingNewLineForListing();
216         else
217             m_tokenizer->setState(adjustedLexerState(m_tokenizer->state(), oldStyleToken.tagName, m_document->frame()));
218     } else if (token.type() == HTMLToken::EndTag) {
219         if (oldStyleToken.tagName == scriptTag) {
220             if (m_lastScriptElement) {
221                 ASSERT(m_lastScriptElementStartLine != uninitializedLineNumberValue);
222                 if (m_fragmentScriptingPermission == FragmentScriptingNotAllowed) {
223                     // FIXME: This is a horrible hack for platform/Pasteboard.
224                     // Clear the <script> tag when using the Parser to create
225                     // a DocumentFragment for pasting so that javascript content
226                     // does not show up in pasted HTML.
227                     m_lastScriptElement->removeChildren();
228                 } else if (insertionMode() != AfterFramesetMode)
229                     handleScriptEndTag(m_lastScriptElement.get(), m_lastScriptElementStartLine);
230                 m_lastScriptElement = 0;
231                 m_lastScriptElementStartLine = uninitializedLineNumberValue;
232             }
233         } else if (oldStyleToken.tagName == framesetTag)
234             setInsertionMode(AfterFramesetMode);
235     }
236 }
237
238 void HTMLTreeBuilder::constructTreeFromToken(HTMLToken& rawToken)
239 {
240     if (m_legacyTreeBuilder) {
241         passTokenToLegacyParser(rawToken);
242         return;
243     }
244
245     AtomicHTMLToken token(rawToken);
246     processToken(token);
247 }
248
249 void HTMLTreeBuilder::processToken(AtomicHTMLToken& token)
250 {
251     switch (token.type()) {
252     case HTMLToken::Uninitialized:
253         ASSERT_NOT_REACHED();
254         break;
255     case HTMLToken::DOCTYPE:
256         processDoctypeToken(token);
257         break;
258     case HTMLToken::StartTag:
259         processStartTag(token);
260         break;
261     case HTMLToken::EndTag:
262         processEndTag(token);
263         break;
264     case HTMLToken::Comment:
265         processComment(token);
266         return;
267     case HTMLToken::Character:
268         processCharacter(token);
269         break;
270     case HTMLToken::EndOfFile:
271         processEndOfFile(token);
272         break;
273     }
274 }
275
276 void HTMLTreeBuilder::processDoctypeToken(AtomicHTMLToken& token)
277 {
278     if (insertionMode() == InitialMode) {
279         insertDoctype(token);
280         return;
281     }
282     parseError(token);
283 }
284
285 void HTMLTreeBuilder::insertHTMLStartTagBeforeHTML(AtomicHTMLToken& token)
286 {
287     RefPtr<Element> element = HTMLHtmlElement::create(m_document);
288     element->setAttributeMap(token.attributes(), m_fragmentScriptingPermission);
289     m_openElements.pushHTMLHtmlElement(attach(m_document, element.release()));
290 }
291
292 void HTMLTreeBuilder::mergeAttributesFromTokenIntoElement(AtomicHTMLToken& token, Element* element)
293 {
294     if (!token.attributes())
295         return;
296
297     NamedNodeMap* attributes = element->attributes(false);
298     for (unsigned i = 0; i < token.attributes()->length(); ++i) {
299         Attribute* attribute = token.attributes()->attributeItem(i);
300         if (!attributes->getAttributeItem(attribute->name()))
301             element->setAttribute(attribute->name(), attribute->value());
302     }
303 }
304
305 void HTMLTreeBuilder::insertHTMLStartTagInBody(AtomicHTMLToken& token)
306 {
307     parseError(token);
308     mergeAttributesFromTokenIntoElement(token, m_openElements.htmlElement());
309 }
310
311 void HTMLTreeBuilder::processFakePEndTagIfPInScope()
312 {
313     if (!m_openElements.inScope(pTag.localName()))
314         return;
315     AtomicHTMLToken endP(HTMLToken::EndTag, pTag.localName());
316     processEndTag(endP);
317 }
318
319 void HTMLTreeBuilder::processStartTag(AtomicHTMLToken& token)
320 {
321     switch (insertionMode()) {
322     case InitialMode:
323         ASSERT(insertionMode() == InitialMode);
324         processDefaultForInitialMode(token);
325         // Fall through.
326     case BeforeHTMLMode:
327         ASSERT(insertionMode() == BeforeHTMLMode);
328         if (token.name() == htmlTag) {
329             insertHTMLStartTagBeforeHTML(token);
330             setInsertionMode(BeforeHeadMode);
331             return;
332         }
333         processDefaultForBeforeHTMLMode(token);
334         // Fall through.
335     case BeforeHeadMode:
336         ASSERT(insertionMode() == BeforeHeadMode);
337         if (token.name() == htmlTag) {
338             insertHTMLStartTagInBody(token);
339             return;
340         }
341         if (token.name() == headTag) {
342             insertHTMLHeadElement(token);
343             setInsertionMode(InHeadMode);
344             return;
345         }
346         processDefaultForBeforeHeadMode(token);
347         // Fall through.
348     case InHeadMode:
349         ASSERT(insertionMode() == InHeadMode);
350         if (processStartTagForInHead(token))
351             return;
352         processDefaultForInHeadMode(token);
353         // Fall through.
354     case AfterHeadMode:
355         ASSERT(insertionMode() == AfterHeadMode);
356         if (token.name() == htmlTag) {
357             insertHTMLStartTagInBody(token);
358             return;
359         }
360         if (token.name() == bodyTag) {
361             m_framesetOk = false;
362             insertHTMLBodyElement(token);
363             m_insertionMode = InBodyMode;
364             return;
365         }
366         if (token.name() == framesetTag) {
367             insertElement(token);
368             setInsertionMode(InFramesetMode);
369             return;
370         }
371         if (token.name() == baseTag || token.name() == linkTag || token.name() == metaTag || token.name() == noframesTag || token.name() == scriptTag || token.name() == styleTag || token.name() == titleTag) {
372             parseError(token);
373             ASSERT(m_headElement);
374             m_openElements.pushHTMLHeadElement(m_headElement);
375             processStartTagForInHead(token);
376             m_openElements.removeHTMLHeadElement(m_headElement.get());
377             return;
378         }
379         if (token.name() == headTag) {
380             parseError(token);
381             return;
382         }
383         processDefaultForAfterHeadMode(token);
384         // Fall through
385     case InBodyMode:
386         ASSERT(insertionMode() == InBodyMode);
387         if (token.name() == htmlTag) {
388             insertHTMLStartTagInBody(token);
389             return;
390         }
391         if (token.name() == baseTag || token.name() == "command" || token.name() == linkTag || token.name() == metaTag || token.name() == noframesTag || token.name() == scriptTag || token.name() == styleTag || token.name() == titleTag) {
392             bool didProcess = processStartTagForInHead(token);
393             ASSERT_UNUSED(didProcess, didProcess);
394             return;
395         }
396         if (token.name() == bodyTag) {
397             parseError(token);
398             notImplemented(); // fragment case
399             mergeAttributesFromTokenIntoElement(token, m_openElements.bodyElement());
400             return;
401         }
402         if (token.name() == framesetTag) {
403             parseError(token);
404             notImplemented(); // fragment case
405             if (!m_framesetOk)
406                 return;
407             ExceptionCode ec = 0;
408             m_openElements.bodyElement()->remove(ec);
409             ASSERT(!ec);
410             m_openElements.popUntil(m_openElements.bodyElement());
411             m_openElements.popHTMLBodyElement();
412             ASSERT(m_openElements.top() == m_openElements.htmlElement());
413             insertElement(token);
414             m_insertionMode = InFramesetMode;
415             return;
416         }
417         if (token.name() == addressTag || token.name() == articleTag || token.name() == asideTag || token.name() == blockquoteTag || token.name() == centerTag || token.name() == "details" || token.name() == dirTag || token.name() == divTag || token.name() == dlTag || token.name() == fieldsetTag || token.name() == "figure" || token.name() == footerTag || token.name() == headerTag || token.name() == hgroupTag || token.name() == menuTag || token.name() == navTag || token.name() == olTag || token.name() == pTag || token.name() == sectionTag || token.name() == ulTag) {
418             processFakePEndTagIfPInScope();
419             insertElement(token);
420             return;
421         }
422         if (token.name() == h1Tag || token.name() == h2Tag || token.name() == h3Tag || token.name() == h4Tag || token.name() == h5Tag || token.name() == h6Tag) {
423             processFakePEndTagIfPInScope();
424             notImplemented();
425             insertElement(token);
426             return;
427         }
428         if (token.name() == preTag || token.name() == listingTag) {
429             processFakePEndTagIfPInScope();
430             insertElement(token);
431             m_tokenizer->skipLeadingNewLineForListing();
432             m_framesetOk = false;
433             return;
434         }
435         if (token.name() == formTag) {
436             notImplemented();
437             processFakePEndTagIfPInScope();
438             insertElement(token);
439             m_formElement = currentElement();
440             return;
441         }
442         if (token.name() == liTag) {
443             notImplemented();
444             processFakePEndTagIfPInScope();
445             insertElement(token);
446             return;
447         }
448         if (token.name() == ddTag || token.name() == dtTag) {
449             notImplemented();
450             processFakePEndTagIfPInScope();
451             insertElement(token);
452             return;
453         }
454         if (token.name() == plaintextTag) {
455             processFakePEndTagIfPInScope();
456             insertElement(token);
457             m_tokenizer->setState(HTMLTokenizer::PLAINTEXTState);
458             return;
459         }
460         if (token.name() == buttonTag) {
461             notImplemented();
462             reconstructTheActiveFormattingElements();
463             insertElement(token);
464             m_framesetOk = false;
465             return;
466         }
467         if (token.name() == aTag) {
468             notImplemented();
469             reconstructTheActiveFormattingElements();
470             insertFormattingElement(token);
471             return;
472         }
473         if (token.name() == bTag || token.name() == bigTag || token.name() == codeTag || token.name() == emTag || token.name() == fontTag || token.name() == iTag || token.name() == sTag || token.name() == smallTag || token.name() == strikeTag || token.name() == strongTag || token.name() == ttTag || token.name() == uTag) {
474             reconstructTheActiveFormattingElements();
475             insertFormattingElement(token);
476             return;
477         }
478         if (token.name() == nobrTag) {
479             reconstructTheActiveFormattingElements();
480             notImplemented();
481             insertFormattingElement(token);
482             return;
483         }
484         if (token.name() == appletTag || token.name() == marqueeTag || token.name() == objectTag) {
485             reconstructTheActiveFormattingElements();
486             insertElement(token);
487             notImplemented();
488             m_framesetOk = false;
489             return;
490         }
491         if (token.name() == tableTag) {
492             notImplemented();
493             insertElement(token);
494             m_framesetOk = false;
495             m_insertionMode = InTableMode;
496             return;
497         }
498         if (token.name() == imageTag) {
499             parseError(token);
500             // Apparently we're not supposed to ask.
501             token.setName(imgTag.localName());
502             // Note the fall through to the imgTag handling below!
503         }
504         if (token.name() == areaTag || token.name() == basefontTag || token.name() == "bgsound" || token.name() == brTag || token.name() == embedTag || token.name() == imgTag || token.name() == inputTag || token.name() == keygenTag || token.name() == wbrTag) {
505             reconstructTheActiveFormattingElements();
506             insertSelfClosingElement(token);
507             m_framesetOk = false;
508             return;
509         }
510         if (token.name() == paramTag || token.name() == sourceTag || token.name() == "track") {
511             insertSelfClosingElement(token);
512             return;
513         }
514         if (token.name() == hrTag) {
515             processFakePEndTagIfPInScope();
516             insertSelfClosingElement(token);
517             m_framesetOk = false;
518             return;
519         }
520         if (token.name() == isindexTag) {
521             parseError(token);
522             notImplemented();
523             return;
524         }
525         if (token.name() == textareaTag) {
526             insertElement(token);
527             m_tokenizer->skipLeadingNewLineForListing();
528             m_tokenizer->setState(HTMLTokenizer::RCDATAState);
529             m_originalInsertionMode = m_insertionMode;
530             m_framesetOk = false;
531             m_insertionMode = TextMode;
532             return;
533         }
534         if (token.name() == xmpTag) {
535             processFakePEndTagIfPInScope();
536             reconstructTheActiveFormattingElements();
537             m_framesetOk = false;
538             insertGenericRawTextElement(token);
539             return;
540         }
541         if (token.name() == iframeTag) {
542             m_framesetOk = false;
543             insertGenericRawTextElement(token);
544             return;
545         }
546         if (token.name() == noembedTag) {
547             insertGenericRawTextElement(token);
548             return;
549         }
550         if (token.name() == noscriptTag && isScriptingFlagEnabled(m_document->frame())) {
551             insertGenericRawTextElement(token);
552             return;
553         }
554         if (token.name() == selectTag) {
555             reconstructTheActiveFormattingElements();
556             insertElement(token);
557             m_framesetOk = false;
558             if (m_insertionMode == InTableMode || m_insertionMode == InCaptionMode || m_insertionMode == InColumnGroupMode || m_insertionMode == InTableBodyMode || m_insertionMode == InRowMode || m_insertionMode == InCellMode)
559                 m_insertionMode = InSelectInTableMode;
560             else
561                 m_insertionMode = InSelectMode;
562             return;
563         }
564         if (token.name() == optgroupTag || token.name() == optionTag) {
565             if (m_openElements.inScope(optionTag.localName())) {
566                 AtomicHTMLToken endOption(HTMLToken::EndTag, optionTag.localName());
567                 processEndTag(endOption);
568             }
569             reconstructTheActiveFormattingElements();
570             insertElement(token);
571             return;
572         }
573         if (token.name() == rpTag || token.name() == rtTag) {
574             if (m_openElements.inScope(rubyTag.localName())) {
575                 generateImpliedEndTags();
576                 if (!currentElement()->hasTagName(rubyTag)) {
577                     parseError(token);
578                     m_openElements.popUntil(rubyTag.localName());
579                 }
580             }
581             insertElement(token);
582             return;
583         }
584         if (token.name() == "math") {
585             // This is the MathML foreign content branch point.
586             notImplemented();
587         }
588         if (token.name() == "svg") {
589             // This is the SVG foreign content branch point.
590             notImplemented();
591         }
592         if (token.name() == captionTag || token.name() == colTag || token.name() == colgroupTag || token.name() == frameTag || token.name() == headTag || token.name() == tbodyTag || token.name() == tdTag || token.name() == tfootTag || token.name() == thTag || token.name() == theadTag || token.name() == trTag) {
593             parseError(token);
594             return;
595         }
596         reconstructTheActiveFormattingElements();
597         insertElement(token);
598         break;
599     case AfterBodyMode:
600     case AfterAfterBodyMode:
601         ASSERT(insertionMode() == AfterBodyMode || insertionMode() == AfterAfterBodyMode);
602         if (token.name() == htmlTag) {
603             insertHTMLStartTagInBody(token);
604             return;
605         }
606         m_insertionMode = InBodyMode;
607         processStartTag(token);
608         break;
609     case InHeadNoscriptMode:
610         ASSERT(insertionMode() == InHeadNoscriptMode);
611         if (token.name() == htmlTag) {
612             insertHTMLStartTagInBody(token);
613             return;
614         }
615         if (token.name() == linkTag || token.name() == metaTag || token.name() == noframesTag || token.name() == styleTag) {
616             bool didProcess = processStartTagForInHead(token);
617             ASSERT_UNUSED(didProcess, didProcess);
618             return;
619         }
620         if (token.name() == htmlTag || token.name() == noscriptTag) {
621             parseError(token);
622             return;
623         }
624         processDefaultForInHeadNoscriptMode(token);
625         processToken(token);
626         break;
627     case InFramesetMode:
628         ASSERT(insertionMode() == InFramesetMode);
629         if (token.name() == htmlTag) {
630             insertHTMLStartTagInBody(token);
631             return;
632         }
633         if (token.name() == framesetTag) {
634             insertElement(token);
635             return;
636         }
637         if (token.name() == frameTag) {
638             insertSelfClosingElement(token);
639             return;
640         }
641         if (token.name() == noframesTag) {
642             processStartTagForInHead(token);
643             return;
644         }
645         parseError(token);
646         break;
647     case AfterFramesetMode:
648         ASSERT(insertionMode() == AfterFramesetMode);
649         if (token.name() == htmlTag) {
650             insertHTMLStartTagInBody(token);
651             return;
652         }
653         if (token.name() == noframesTag) {
654             processStartTagForInHead(token);
655             return;
656         }
657         parseError(token);
658         break;
659     default:
660         notImplemented();
661     }
662 }
663
664 bool HTMLTreeBuilder::processBodyEndTagForInBody(AtomicHTMLToken& token)
665 {
666     if (!m_openElements.inScope(bodyTag.localName())) {
667         parseError(token);
668         return false;
669     }
670     notImplemented();
671     m_insertionMode = AfterBodyMode;
672     return true;
673 }
674
675 void HTMLTreeBuilder::processEndTag(AtomicHTMLToken& token)
676 {
677     switch (insertionMode()) {
678     case InitialMode:
679         ASSERT(insertionMode() == InitialMode);
680         processDefaultForInitialMode(token);
681         // Fall through.
682     case BeforeHTMLMode:
683         ASSERT(insertionMode() == BeforeHTMLMode);
684         if (token.name() != headTag && token.name() != bodyTag && token.name() != htmlTag && token.name() != brTag) {
685             parseError(token);
686             return;
687         }
688         processDefaultForBeforeHTMLMode(token);
689         // Fall through.
690     case BeforeHeadMode:
691         ASSERT(insertionMode() == BeforeHeadMode);
692         if (token.name() != headTag && token.name() != bodyTag && token.name() != brTag) {
693             parseError(token);
694             return;
695         }
696         processDefaultForBeforeHeadMode(token);
697         // Fall through.
698     case InHeadMode:
699         ASSERT(insertionMode() == InHeadMode);
700         if (token.name() == headTag) {
701             m_openElements.popHTMLHeadElement();
702             setInsertionMode(AfterHeadMode);
703             return;
704         }
705         if (token.name() != bodyTag && token.name() != htmlTag && token.name() != brTag) {
706             parseError(token);
707             return;
708         }
709         processDefaultForInHeadMode(token);
710         // Fall through.
711     case AfterHeadMode:
712         ASSERT(insertionMode() == AfterHeadMode);
713         if (token.name() != bodyTag && token.name() != htmlTag && token.name() != brTag) {
714             parseError(token);
715             return;
716         }
717         processDefaultForAfterHeadMode(token);
718         // Fall through
719     case InBodyMode:
720         ASSERT(insertionMode() == InBodyMode);
721         if (token.name() == bodyTag) {
722             processBodyEndTagForInBody(token);
723             return;
724         }
725         if (token.name() == htmlTag) {
726             if (processBodyEndTagForInBody(token))
727                 processEndTag(token);
728             return;
729         }
730         if (token.name() == addressTag || token.name() == articleTag || token.name() == asideTag || token.name() == blockquoteTag || token.name() == buttonTag || token.name() == centerTag || token.name() == "details" || token.name() == dirTag || token.name() == divTag || token.name() == dlTag || token.name() == fieldsetTag || token.name() == "figure" || token.name() == footerTag || token.name() == headerTag || token.name() == hgroupTag || token.name() == listingTag || token.name() == menuTag || token.name() == navTag || token.name() == olTag || token.name() == preTag || token.name() == sectionTag || token.name() == ulTag) {
731             if (!m_openElements.inScope(token.name())) {
732                 parseError(token);
733                 return;
734             }
735             generateImpliedEndTags();
736             if (currentElement()->tagQName() != token.name())
737                 parseError(token);
738             m_openElements.popUntil(token.name());
739             m_openElements.pop();
740         }
741         if (token.name() == formTag) {
742             RefPtr<Element> node = m_formElement.release();
743             if (!node || !m_openElements.inScope(node.get())) {
744                 parseError(token);
745                 return;
746             }
747             generateImpliedEndTags();
748             if (currentElement() != node.get())
749                 parseError(token);
750             m_openElements.remove(node.get());
751         }
752         if (token.name() == pTag) {
753             if (!m_openElements.inScope(token.name())) {
754                 parseError(token);
755                 notImplemented();
756                 return;
757             }
758             generateImpliedEndTagsWithExclusion(token.name());
759             if (!currentElement()->hasLocalName(token.name()))
760                 parseError(token);
761             m_openElements.popUntil(token.name());
762             m_openElements.pop();
763             return;
764         }
765         if (token.name() == liTag) {
766             if (!m_openElements.inListItemScope(token.name())) {
767                 parseError(token);
768                 return;
769             }
770             generateImpliedEndTagsWithExclusion(token.name());
771             if (!currentElement()->hasLocalName(token.name()))
772                 parseError(token);
773             m_openElements.popUntil(token.name());
774             m_openElements.pop();
775             return;
776         }
777         if (token.name() == ddTag || token.name() == dtTag) {
778             if (!m_openElements.inScope(token.name())) {
779                 parseError(token);
780                 return;
781             }
782             generateImpliedEndTagsWithExclusion(token.name());
783             if (!currentElement()->hasLocalName(token.name()))
784                 parseError(token);
785             m_openElements.popUntil(token.name());
786             m_openElements.pop();
787             return;
788         }
789         if (token.name() == h1Tag || token.name() == h2Tag || token.name() == h3Tag || token.name() == h4Tag || token.name() == h5Tag || token.name() == h6Tag) {
790             if (!m_openElements.inScope(token.name())) {
791                 parseError(token);
792                 return;
793             }
794             generateImpliedEndTags();
795             if (!currentElement()->hasLocalName(token.name()))
796                 parseError(token);
797             m_openElements.popUntil(token.name());
798             m_openElements.pop();
799             return;
800         }
801         if (token.name() == "sarcasm") {
802             notImplemented(); // Take a deep breath.
803             return;
804         }
805         if (token.name() == aTag || token.name() == bTag || token.name() == bigTag || token.name() == codeTag || token.name() == emTag || token.name() == fontTag || token.name() == iTag || token.name() == nobrTag || token.name() == sTag || token.name() == smallTag || token.name() == strikeTag || token.name() == strongTag || token.name() == ttTag || token.name() == uTag) {
806             notImplemented();
807             // FIXME: There's a complicated algorithm that goes here.
808             return;
809         }
810         if (token.name() == appletTag || token.name() == marqueeTag || token.name() == objectTag) {
811             if (!m_openElements.inScope(token.name())) {
812                 parseError(token);
813                 return;
814             }
815             generateImpliedEndTags();
816             if (currentElement()->tagQName() != token.name())
817                 parseError(token);
818             m_openElements.popUntil(token.name());
819             m_openElements.pop();
820             m_activeFormattingElements.clearToLastMarker();
821             return;
822         }
823         if (token.name() == brTag) {
824             parseError(token);
825             reconstructTheActiveFormattingElements();
826             // Notice that we lose the attributes.
827             AtomicHTMLToken startBr(HTMLToken::StartTag, token.name());
828             insertSelfClosingElement(startBr);
829             m_framesetOk = false;
830             return;
831         }
832         // FIXME: We need an iterator over m_openElements to implement this
833         // correctly.
834         notImplemented();
835         if (!m_openElements.inScope(token.name()))
836             return;
837         m_openElements.popUntil(token.name());
838         m_openElements.pop();
839         break;
840     case AfterBodyMode:
841         ASSERT(insertionMode() == AfterBodyMode);
842         if (token.name() == htmlTag) {
843             if (m_isParsingFragment) {
844                 parseError(token);
845                 return;
846             }
847             m_insertionMode = AfterAfterBodyMode;
848             return;
849         }
850         // Fall through.
851     case AfterAfterBodyMode:
852         ASSERT(insertionMode() == AfterBodyMode || insertionMode() == AfterAfterBodyMode);
853         parseError(token);
854         m_insertionMode = InBodyMode;
855         processEndTag(token);
856         break;
857     case InHeadNoscriptMode:
858         ASSERT(insertionMode() == InHeadNoscriptMode);
859         if (token.name() == noscriptTag) {
860             ASSERT(currentElement()->tagQName() == noscriptTag);
861             m_openElements.pop();
862             ASSERT(currentElement()->tagQName() == headTag);
863             setInsertionMode(InHeadMode);
864             return;
865         }
866         if (token.name() != brTag) {
867             parseError(token);
868             return;
869         }
870         processDefaultForInHeadNoscriptMode(token);
871         processToken(token);
872         break;
873     case TextMode:
874         if (token.name() == scriptTag) {
875             // Pause ourselves so that parsing stops until the script can be processed by the caller.
876             m_isPaused = true;
877             ASSERT(currentElement()->tagQName() == scriptTag);
878             m_scriptToProcess = currentElement();
879             m_openElements.pop();
880             m_insertionMode = m_originalInsertionMode;
881             return;
882         }
883         m_openElements.pop();
884         m_insertionMode = m_originalInsertionMode;
885         break;
886     case InFramesetMode:
887         ASSERT(insertionMode() == InFramesetMode);
888         if (token.name() == framesetTag) {
889             if (currentElement() == m_openElements.htmlElement()) {
890                 parseError(token);
891                 return;
892             }
893             m_openElements.pop();
894             if (!m_isParsingFragment && !currentElement()->hasTagName(framesetTag))
895                 m_insertionMode = AfterFramesetMode;
896             return;
897         }
898         break;
899     case AfterFramesetMode:
900         ASSERT(insertionMode() == AfterFramesetMode);
901         if (token.name() == htmlTag) {
902             m_insertionMode = AfterAfterFramesetMode;
903             return;
904         }
905         parseError(token);
906         break;
907     default:
908         notImplemented();
909     }
910 }
911
912 void HTMLTreeBuilder::processComment(AtomicHTMLToken& token)
913 {
914     if (m_insertionMode == InitialMode || m_insertionMode == BeforeHTMLMode || m_insertionMode == AfterAfterBodyMode) {
915         insertCommentOnDocument(token);
916         return;
917     }
918     if (m_insertionMode == AfterBodyMode) {
919         insertCommentOnHTMLHtmlElement(token);
920         return;
921     }
922     insertComment(token);
923 }
924
925 void HTMLTreeBuilder::processCharacter(AtomicHTMLToken& token)
926 {
927     // FIXME: We need to figure out how to handle each character individually.
928     switch (insertionMode()) {
929     case InitialMode:
930         ASSERT(insertionMode() == InitialMode);
931         notImplemented();
932         processDefaultForInitialMode(token);
933         // Fall through.
934     case BeforeHTMLMode:
935         ASSERT(insertionMode() == BeforeHTMLMode);
936         notImplemented();
937         processDefaultForBeforeHTMLMode(token);
938         // Fall through.
939     case BeforeHeadMode:
940         ASSERT(insertionMode() == BeforeHeadMode);
941         notImplemented();
942         processDefaultForBeforeHeadMode(token);
943         // Fall through.
944     case InHeadMode:
945         ASSERT(insertionMode() == InHeadMode);
946         notImplemented();
947         processDefaultForInHeadMode(token);
948         // Fall through.
949     case AfterHeadMode:
950         ASSERT(insertionMode() == AfterHeadMode);
951         notImplemented();
952         processDefaultForAfterHeadMode(token);
953         // Fall through
954     case InBodyMode:
955         ASSERT(insertionMode() == InBodyMode);
956         notImplemented();
957         insertTextNode(token);
958         break;
959     case AfterBodyMode:
960     case AfterAfterBodyMode:
961         ASSERT(insertionMode() == AfterBodyMode || insertionMode() == AfterAfterBodyMode);
962         parseError(token);
963         m_insertionMode = InBodyMode;
964         processCharacter(token);
965         break;
966     case TextMode:
967         notImplemented();
968         insertTextNode(token);
969         break;
970     case InHeadNoscriptMode:
971         ASSERT(insertionMode() == InHeadNoscriptMode);
972         processDefaultForInHeadNoscriptMode(token);
973         processToken(token);
974         break;
975     case InFramesetMode:
976     case AfterFramesetMode:
977         ASSERT(insertionMode() == InFramesetMode || insertionMode() == AfterFramesetMode);
978         parseError(token);
979         break;
980     default:
981         notImplemented();
982     }
983 }
984
985 void HTMLTreeBuilder::processEndOfFile(AtomicHTMLToken& token)
986 {
987     switch (insertionMode()) {
988     case InitialMode:
989         ASSERT(insertionMode() == InitialMode);
990         processDefaultForInitialMode(token);
991         // Fall through.
992     case BeforeHTMLMode:
993         ASSERT(insertionMode() == BeforeHTMLMode);
994         processDefaultForBeforeHTMLMode(token);
995         // Fall through.
996     case BeforeHeadMode:
997         ASSERT(insertionMode() == BeforeHeadMode);
998         processDefaultForBeforeHeadMode(token);
999         // Fall through.
1000     case InHeadMode:
1001         ASSERT(insertionMode() == InHeadMode);
1002         processDefaultForInHeadMode(token);
1003         // Fall through.
1004     case AfterHeadMode:
1005         ASSERT(insertionMode() == AfterHeadMode);
1006         processDefaultForAfterHeadMode(token);
1007         // Fall through
1008     case InBodyMode:
1009         ASSERT(insertionMode() == InBodyMode);
1010         notImplemented();
1011         break;
1012     case AfterBodyMode:
1013     case AfterAfterBodyMode:
1014         ASSERT(insertionMode() == AfterBodyMode || insertionMode() == AfterAfterBodyMode);
1015         notImplemented();
1016         break;
1017     case InHeadNoscriptMode:
1018         ASSERT(insertionMode() == InHeadNoscriptMode);
1019         processDefaultForInHeadNoscriptMode(token);
1020         processToken(token);
1021         break;
1022     case InFramesetMode:
1023         ASSERT(insertionMode() == InFramesetMode);
1024         if (currentElement() != m_openElements.htmlElement())
1025             parseError(token);
1026         break;
1027     case AfterFramesetMode:
1028         ASSERT(insertionMode() == AfterFramesetMode);
1029         break;
1030     default:
1031         notImplemented();
1032     }
1033 }
1034
1035 void HTMLTreeBuilder::processDefaultForInitialMode(AtomicHTMLToken& token)
1036 {
1037     notImplemented();
1038     parseError(token);
1039     setInsertionMode(BeforeHTMLMode);
1040 }
1041
1042 void HTMLTreeBuilder::processDefaultForBeforeHTMLMode(AtomicHTMLToken&)
1043 {
1044     AtomicHTMLToken startHTML(HTMLToken::StartTag, htmlTag.localName());
1045     insertHTMLStartTagBeforeHTML(startHTML);
1046     setInsertionMode(BeforeHeadMode);
1047 }
1048
1049 void HTMLTreeBuilder::processDefaultForBeforeHeadMode(AtomicHTMLToken&)
1050 {
1051     AtomicHTMLToken startHead(HTMLToken::StartTag, headTag.localName());
1052     processStartTag(startHead);
1053 }
1054
1055 void HTMLTreeBuilder::processDefaultForInHeadMode(AtomicHTMLToken&)
1056 {
1057     AtomicHTMLToken endHead(HTMLToken::EndTag, headTag.localName());
1058     processEndTag(endHead);
1059 }
1060
1061 void HTMLTreeBuilder::processDefaultForInHeadNoscriptMode(AtomicHTMLToken&)
1062 {
1063     AtomicHTMLToken endNoscript(HTMLToken::EndTag, noscriptTag.localName());
1064     processEndTag(endNoscript);
1065 }
1066
1067 void HTMLTreeBuilder::processDefaultForAfterHeadMode(AtomicHTMLToken&)
1068 {
1069     AtomicHTMLToken startBody(HTMLToken::StartTag, bodyTag.localName());
1070     processStartTag(startBody);
1071     m_framesetOk = true;
1072 }
1073
1074 bool HTMLTreeBuilder::processStartTagForInHead(AtomicHTMLToken& token)
1075 {
1076     if (token.name() == htmlTag) {
1077         insertHTMLStartTagInBody(token);
1078         return true;
1079     }
1080     // FIXME: Atomize "command".
1081     if (token.name() == baseTag || token.name() == "command" || token.name() == linkTag || token.name() == metaTag) {
1082         insertSelfClosingElement(token);
1083         // Note: The custom processing for the <meta> tag is done in HTMLMetaElement::process().
1084         return true;
1085     }
1086     if (token.name() == titleTag) {
1087         insertGenericRCDATAElement(token);
1088         return true;
1089     }
1090     if (token.name() == noscriptTag) {
1091         if (isScriptingFlagEnabled(m_document->frame())) {
1092             insertGenericRawTextElement(token);
1093             return true;
1094         }
1095         insertElement(token);
1096         setInsertionMode(InHeadNoscriptMode);
1097         return true;
1098     }
1099     if (token.name() == noframesTag || token.name() == styleTag) {
1100         insertGenericRawTextElement(token);
1101         return true;
1102     }
1103     if (token.name() == scriptTag) {
1104         insertScriptElement(token);
1105         return true;
1106     }
1107     if (token.name() == headTag) {
1108         parseError(token);
1109         return true;
1110     }
1111     return false;
1112 }
1113
1114 void HTMLTreeBuilder::insertDoctype(AtomicHTMLToken& token)
1115 {
1116     ASSERT(token.type() == HTMLToken::DOCTYPE);
1117     attach(m_document, DocumentType::create(m_document, token.name(), String::adopt(token.publicIdentifier()), String::adopt(token.systemIdentifier())));
1118     // FIXME: Move quirks mode detection from DocumentType element to here.
1119     notImplemented();
1120     if (token.forceQuirks())
1121         m_document->setParseMode(Document::Compat);
1122 }
1123
1124 void HTMLTreeBuilder::insertComment(AtomicHTMLToken& token)
1125 {
1126     ASSERT(token.type() == HTMLToken::Comment);
1127     attach(currentElement(), Comment::create(m_document, token.comment()));
1128 }
1129
1130 void HTMLTreeBuilder::insertCommentOnDocument(AtomicHTMLToken& token)
1131 {
1132     ASSERT(token.type() == HTMLToken::Comment);
1133     attach(m_document, Comment::create(m_document, token.comment()));
1134 }
1135
1136 void HTMLTreeBuilder::insertCommentOnHTMLHtmlElement(AtomicHTMLToken& token)
1137 {
1138     ASSERT(token.type() == HTMLToken::Comment);
1139     attach(m_openElements.htmlElement(), Comment::create(m_document, token.comment()));
1140 }
1141
1142 PassRefPtr<Element> HTMLTreeBuilder::createElementAndAttachToCurrent(AtomicHTMLToken& token)
1143 {
1144     ASSERT(token.type() == HTMLToken::StartTag);
1145     return attach(currentElement(), createElement(token));
1146 }
1147
1148 void HTMLTreeBuilder::insertHTMLHtmlElement(AtomicHTMLToken& token)
1149 {
1150     m_openElements.pushHTMLHtmlElement(createElementAndAttachToCurrent(token));
1151 }
1152
1153 void HTMLTreeBuilder::insertHTMLHeadElement(AtomicHTMLToken& token)
1154 {
1155     m_headElement = createElementAndAttachToCurrent(token);
1156     m_openElements.pushHTMLHeadElement(m_headElement);
1157 }
1158
1159 void HTMLTreeBuilder::insertHTMLBodyElement(AtomicHTMLToken& token)
1160 {
1161     m_openElements.pushHTMLBodyElement(createElementAndAttachToCurrent(token));
1162 }
1163
1164 void HTMLTreeBuilder::insertElement(AtomicHTMLToken& token)
1165 {
1166     m_openElements.push(createElementAndAttachToCurrent(token));
1167 }
1168
1169 void HTMLTreeBuilder::insertSelfClosingElement(AtomicHTMLToken& token)
1170 {
1171     ASSERT(token.type() == HTMLToken::StartTag);
1172     attach(currentElement(), createElement(token));
1173     // FIXME: Do we want to acknowledge the token's self-closing flag?
1174     // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#acknowledge-self-closing-flag
1175 }
1176
1177 void HTMLTreeBuilder::insertFormattingElement(AtomicHTMLToken& token)
1178 {
1179     // http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#the-stack-of-open-elements
1180     // Possible active formatting elements include:
1181     // a, b, big, code, em, font, i, nobr, s, small, strike, strong, tt, and u.
1182     insertElement(token);
1183     m_activeFormattingElements.append(currentElement());
1184 }
1185
1186 void HTMLTreeBuilder::insertGenericRCDATAElement(AtomicHTMLToken& token)
1187 {
1188     insertElement(token);
1189     m_tokenizer->setState(HTMLTokenizer::RCDATAState);
1190     m_originalInsertionMode = m_insertionMode;
1191     m_insertionMode = TextMode;
1192 }
1193
1194 void HTMLTreeBuilder::insertGenericRawTextElement(AtomicHTMLToken& token)
1195 {
1196     insertElement(token);
1197     m_tokenizer->setState(HTMLTokenizer::RAWTEXTState);
1198     m_originalInsertionMode = m_insertionMode;
1199     m_insertionMode = TextMode;
1200 }
1201
1202 void HTMLTreeBuilder::insertScriptElement(AtomicHTMLToken& token)
1203 {
1204     ASSERT_UNUSED(token, token.type() == HTMLToken::StartTag);
1205     RefPtr<HTMLScriptElement> element = HTMLScriptElement::create(scriptTag, m_document, true);
1206     element->setAttributeMap(token.attributes(), m_fragmentScriptingPermission);
1207     m_openElements.push(attach(currentElement(), element.release()));
1208     m_tokenizer->setState(HTMLTokenizer::ScriptDataState);
1209     m_originalInsertionMode = m_insertionMode;
1210     m_insertionMode = TextMode;
1211 }
1212
1213 void HTMLTreeBuilder::insertTextNode(AtomicHTMLToken& token)
1214 {
1215     attach(currentElement(), Text::create(m_document, token.characters()));
1216 }
1217     
1218 PassRefPtr<Element> HTMLTreeBuilder::createElement(AtomicHTMLToken& token)
1219 {
1220     RefPtr<Element> element = HTMLElementFactory::createHTMLElement(QualifiedName(nullAtom, token.name(), xhtmlNamespaceURI), m_document, 0);
1221     element->setAttributeMap(token.attributes(), m_fragmentScriptingPermission);
1222     return element.release();
1223 }
1224
1225 bool HTMLTreeBuilder::indexOfFirstUnopenFormattingElement(unsigned& firstUnopenElementIndex) const
1226 {
1227     if (m_activeFormattingElements.isEmpty())
1228         return false;
1229     unsigned index = m_activeFormattingElements.size();
1230     do {
1231         --index;
1232         const HTMLFormattingElementList::Entry& entry = m_activeFormattingElements[index];
1233         if (entry.isMarker() || m_openElements.contains(entry.element())) {
1234             firstUnopenElementIndex = index;
1235             return true;
1236         }
1237     } while (index);
1238     return false;
1239 }
1240
1241 void HTMLTreeBuilder::reconstructTheActiveFormattingElements()
1242 {
1243     unsigned firstUnopenElementIndex;
1244     if (!indexOfFirstUnopenFormattingElement(firstUnopenElementIndex))
1245         return;
1246
1247     unsigned unopenEntryIndex = firstUnopenElementIndex;
1248     ASSERT(unopenEntryIndex < m_activeFormattingElements.size());
1249     for (; unopenEntryIndex < m_activeFormattingElements.size(); ++unopenEntryIndex) {
1250         HTMLFormattingElementList::Entry& unopenedEntry = m_activeFormattingElements[unopenEntryIndex];
1251         // FIXME: We're supposed to save the original token in the entry.
1252         AtomicHTMLToken fakeToken(HTMLToken::StartTag, unopenedEntry.element()->localName());
1253         insertElement(fakeToken);
1254         unopenedEntry.replaceElement(currentElement());
1255     }
1256 }
1257
1258 namespace {
1259
1260 bool hasImpliedEndTag(Element* element)
1261 {
1262     return element->hasTagName(ddTag)
1263         || element->hasTagName(dtTag)
1264         || element->hasTagName(liTag)
1265         || element->hasTagName(optionTag)
1266         || element->hasTagName(optgroupTag)
1267         || element->hasTagName(pTag)
1268         || element->hasTagName(rpTag)
1269         || element->hasTagName(rtTag);
1270 }
1271
1272 }
1273
1274 void HTMLTreeBuilder::generateImpliedEndTagsWithExclusion(const AtomicString& tagName)
1275 {
1276     while (hasImpliedEndTag(currentElement()) && !currentElement()->hasLocalName(tagName))
1277         m_openElements.pop();
1278 }
1279
1280 void HTMLTreeBuilder::generateImpliedEndTags()
1281 {
1282     while (hasImpliedEndTag(currentElement()))
1283         m_openElements.pop();
1284 }
1285
1286 void HTMLTreeBuilder::finished()
1287 {
1288     // We should call m_document->finishedParsing() here, except
1289     // m_legacyTreeBuilder->finished() does it for us.
1290     if (m_legacyTreeBuilder) {
1291         m_legacyTreeBuilder->finished();
1292         return;
1293     }
1294
1295     AtomicHTMLToken eofToken(HTMLToken::EndOfFile, nullAtom);
1296     processToken(eofToken);
1297
1298     // Warning, this may delete the parser, so don't try to do anything else after this.
1299     if (!m_isParsingFragment)
1300         m_document->finishedParsing();
1301 }
1302
1303 bool HTMLTreeBuilder::isScriptingFlagEnabled(Frame* frame)
1304 {
1305     if (!frame)
1306         return false;
1307     if (ScriptController* scriptController = frame->script())
1308         return scriptController->canExecuteScripts(NotAboutToExecuteScript);
1309     return false;
1310 }
1311
1312 }