Unreviewed complementary fix for r61818. It added StaticHashSetListNode.cpp|h to...
[WebKit.git] / WebCore / html / HTMLDocumentParser.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 APPLE 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 APPLE 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 "HTMLDocumentParser.h"
28
29 #include "DocumentFragment.h"
30 #include "Element.h"
31 #include "Frame.h"
32 #include "HTMLParserScheduler.h"
33 #include "HTMLTokenizer.h"
34 #include "HTMLPreloadScanner.h"
35 #include "HTMLScriptRunner.h"
36 #include "HTMLTreeBuilder.h"
37 #include "HTMLDocument.h"
38 #include "XSSAuditor.h"
39 #include <wtf/CurrentTime.h>
40
41 #if ENABLE(INSPECTOR)
42 #include "InspectorTimelineAgent.h"
43 #endif
44
45 namespace WebCore {
46
47 namespace {
48
49 class NestingLevelIncrementer : public Noncopyable {
50 public:
51     explicit NestingLevelIncrementer(int& counter)
52         : m_counter(&counter)
53     {
54         ++(*m_counter);
55     }
56
57     ~NestingLevelIncrementer()
58     {
59         --(*m_counter);
60     }
61
62 private:
63     int* m_counter;
64 };
65
66 } // namespace
67
68 HTMLDocumentParser::HTMLDocumentParser(HTMLDocument* document, bool reportErrors)
69     : DocumentParser(document)
70     , m_tokenizer(new HTMLTokenizer)
71     , m_scriptRunner(new HTMLScriptRunner(document, this))
72     , m_treeBuilder(new HTMLTreeBuilder(m_tokenizer.get(), document, reportErrors))
73     , m_parserScheduler(new HTMLParserScheduler(this))
74     , m_endWasDelayed(false)
75     , m_writeNestingLevel(0)
76 {
77     begin();
78 }
79
80 // FIXME: Member variables should be grouped into self-initializing structs to
81 // minimize code duplication between these constructors.
82 HTMLDocumentParser::HTMLDocumentParser(DocumentFragment* fragment, FragmentScriptingPermission scriptingPermission)
83     : DocumentParser(fragment->document())
84     , m_tokenizer(new HTMLTokenizer)
85     , m_treeBuilder(new HTMLTreeBuilder(m_tokenizer.get(), fragment, scriptingPermission))
86     , m_endWasDelayed(false)
87     , m_writeNestingLevel(0)
88 {
89     begin();
90 }
91
92 HTMLDocumentParser::~HTMLDocumentParser()
93 {
94     // FIXME: We'd like to ASSERT that normal operation of this class clears
95     // out any delayed actions, but we can't because we're unceremoniously
96     // deleted.  If there were a required call to some sort of cancel function,
97     // then we could ASSERT some invariants here.
98 }
99
100 void HTMLDocumentParser::begin()
101 {
102     // FIXME: Should we reset the tokenizer?
103 }
104
105 void HTMLDocumentParser::stopParsing()
106 {
107     DocumentParser::stopParsing();
108     m_parserScheduler.clear(); // Deleting the scheduler will clear any timers.
109 }
110
111 bool HTMLDocumentParser::processingData() const
112 {
113     return isScheduledForResume() || inWrite();
114 }
115
116 void HTMLDocumentParser::pumpTokenizerIfPossible(SynchronousMode mode)
117 {
118     if (m_parserStopped || m_treeBuilder->isPaused())
119         return;
120
121     // Once a resume is scheduled, HTMLParserScheduler controls when we next pump.
122     if (isScheduledForResume()) {
123         ASSERT(mode == AllowYield);
124         return;
125     }
126
127     pumpTokenizer(mode);
128 }
129
130 bool HTMLDocumentParser::isScheduledForResume() const
131 {
132     return m_parserScheduler && m_parserScheduler->isScheduledForResume();
133 }
134
135 // Used by HTMLParserScheduler
136 void HTMLDocumentParser::resumeParsingAfterYield()
137 {
138     // We should never be here unless we can pump immediately.  Call pumpTokenizer()
139     // directly so that ASSERTS will fire if we're wrong.
140     pumpTokenizer(AllowYield);
141 }
142
143 bool HTMLDocumentParser::runScriptsForPausedTreeBuilder()
144 {
145     ASSERT(m_treeBuilder->isPaused());
146
147     int scriptStartLine = 0;
148     RefPtr<Element> scriptElement = m_treeBuilder->takeScriptToProcess(scriptStartLine);
149     // We will not have a scriptRunner when parsing a DocumentFragment.
150     if (!m_scriptRunner)
151         return true;
152     return m_scriptRunner->execute(scriptElement.release(), scriptStartLine);
153 }
154
155 void HTMLDocumentParser::pumpTokenizer(SynchronousMode mode)
156 {
157     ASSERT(!m_parserStopped);
158     ASSERT(!m_treeBuilder->isPaused());
159     ASSERT(!isScheduledForResume());
160
161     // We tell the InspectorTimelineAgent about every pump, even if we
162     // end up pumping nothing.  It can filter out empty pumps itself.
163     willPumpLexer();
164
165     HTMLParserScheduler::PumpSession session;
166     // FIXME: This loop body has is now too long and needs cleanup.
167     while (mode == ForceSynchronous || (!m_parserStopped && m_parserScheduler->shouldContinueParsing(session))) {
168         if (!m_tokenizer->nextToken(m_input.current(), m_token))
169             break;
170
171         m_treeBuilder->constructTreeFromToken(m_token);
172         m_token.clear();
173
174         // The parser will pause itself when waiting on a script to load or run.
175         if (!m_treeBuilder->isPaused())
176             continue;
177
178         // If we're paused waiting for a script, we try to execute scripts before continuing.
179         bool shouldContinueParsing = runScriptsForPausedTreeBuilder();
180         m_treeBuilder->setPaused(!shouldContinueParsing);
181         if (!shouldContinueParsing)
182             break;
183     }
184
185     if (isWaitingForScripts()) {
186         ASSERT(m_tokenizer->state() == HTMLTokenizer::DataState);
187         if (!m_preloadScanner) {
188             m_preloadScanner.set(new HTMLPreloadScanner(m_document));
189             m_preloadScanner->appendToEnd(m_input.current());
190         }
191         m_preloadScanner->scan();
192     }
193
194     didPumpLexer();
195 }
196
197 void HTMLDocumentParser::willPumpLexer()
198 {
199 #if ENABLE(INSPECTOR)
200     // FIXME: m_input.current().length() is only accurate if we
201     // end up parsing the whole buffer in this pump.  We should pass how
202     // much we parsed as part of didWriteHTML instead of willWriteHTML.
203     if (InspectorTimelineAgent* timelineAgent = m_document->inspectorTimelineAgent())
204         timelineAgent->willWriteHTML(m_input.current().length(), m_tokenizer->lineNumber());
205 #endif
206 }
207
208 void HTMLDocumentParser::didPumpLexer()
209 {
210 #if ENABLE(INSPECTOR)
211     if (InspectorTimelineAgent* timelineAgent = m_document->inspectorTimelineAgent())
212         timelineAgent->didWriteHTML(m_tokenizer->lineNumber());
213 #endif
214 }
215
216 void HTMLDocumentParser::insert(const SegmentedString& source)
217 {
218     if (m_parserStopped)
219         return;
220
221     NestingLevelIncrementer nestingLevelIncrementer(m_writeNestingLevel);
222     m_input.insertAtCurrentInsertionPoint(source);
223     pumpTokenizerIfPossible(ForceSynchronous);
224     endIfDelayed();
225 }
226
227 void HTMLDocumentParser::append(const SegmentedString& source)
228 {
229     if (m_parserStopped)
230         return;
231
232     NestingLevelIncrementer nestingLevelIncrementer(m_writeNestingLevel);
233
234     m_input.appendToEnd(source);
235     if (m_preloadScanner)
236         m_preloadScanner->appendToEnd(source);
237
238     if (m_writeNestingLevel > 1) {
239         // We've gotten data off the network in a nested write.
240         // We don't want to consume any more of the input stream now.  Do
241         // not worry.  We'll consume this data in a less-nested write().
242         return;
243     }
244
245     pumpTokenizerIfPossible(AllowYield);
246     endIfDelayed();
247 }
248
249 void HTMLDocumentParser::end()
250 {
251     ASSERT(!isScheduledForResume());
252     // NOTE: This pump should only ever emit buffered character tokens,
253     // so ForceSynchronous vs. AllowYield should be meaningless.
254     pumpTokenizerIfPossible(ForceSynchronous);
255
256     // Informs the the rest of WebCore that parsing is really finished (and deletes this).
257     m_treeBuilder->finished();
258 }
259
260 void HTMLDocumentParser::attemptToEnd()
261 {
262     // finish() indicates we will not receive any more data. If we are waiting on
263     // an external script to load, we can't finish parsing quite yet.
264
265     if (inWrite() || isWaitingForScripts() || inScriptExecution() || isScheduledForResume()) {
266         m_endWasDelayed = true;
267         return;
268     }
269     end();
270 }
271
272 void HTMLDocumentParser::endIfDelayed()
273 {
274     // We don't check inWrite() here since inWrite() will be true if this was
275     // called from write().
276     if (!m_endWasDelayed || isWaitingForScripts() || inScriptExecution() || isScheduledForResume())
277         return;
278
279     m_endWasDelayed = false;
280     end();
281 }
282
283 void HTMLDocumentParser::finish()
284 {
285     // We're not going to get any more data off the network, so we close the
286     // input stream to indicate EOF.
287     m_input.close();
288     attemptToEnd();
289 }
290
291 bool HTMLDocumentParser::finishWasCalled()
292 {
293     return m_input.isClosed();
294 }
295
296 // This function is virtual and just for the DocumentParser interface.
297 bool HTMLDocumentParser::isExecutingScript() const
298 {
299     return inScriptExecution();
300 }
301
302 // This function is non-virtual and used throughout the implementation.
303 bool HTMLDocumentParser::inScriptExecution() const
304 {
305     if (!m_scriptRunner)
306         return false;
307     return m_scriptRunner->inScriptExecution();
308 }
309
310 int HTMLDocumentParser::lineNumber() const
311 {
312     return m_tokenizer->lineNumber();
313 }
314
315 int HTMLDocumentParser::columnNumber() const
316 {
317     return m_tokenizer->columnNumber();
318 }
319
320 LegacyHTMLTreeBuilder* HTMLDocumentParser::htmlTreeBuilder() const
321 {
322     return m_treeBuilder->legacyTreeBuilder();
323 }
324
325 bool HTMLDocumentParser::isWaitingForScripts() const
326 {
327     return m_treeBuilder->isPaused();
328 }
329
330 void HTMLDocumentParser::resumeParsingAfterScriptExecution()
331 {
332     ASSERT(!inScriptExecution());
333     ASSERT(!m_treeBuilder->isPaused());
334
335     pumpTokenizerIfPossible(AllowYield);
336
337     // The document already finished parsing we were just waiting on scripts when finished() was called.
338     endIfDelayed();
339 }
340
341 void HTMLDocumentParser::watchForLoad(CachedResource* cachedScript)
342 {
343     ASSERT(!cachedScript->isLoaded());
344     // addClient would call notifyFinished if the load were complete.
345     // Callers do not expect to be re-entered from this call, so they should
346     // not an already-loaded CachedResource.
347     cachedScript->addClient(this);
348 }
349
350 void HTMLDocumentParser::stopWatchingForLoad(CachedResource* cachedScript)
351 {
352     cachedScript->removeClient(this);
353 }
354
355 bool HTMLDocumentParser::shouldLoadExternalScriptFromSrc(const AtomicString& srcValue)
356 {
357     if (!m_XSSAuditor)
358         return true;
359     return m_XSSAuditor->canLoadExternalScriptFromSrc(srcValue);
360 }
361
362 void HTMLDocumentParser::notifyFinished(CachedResource* cachedResource)
363 {
364     ASSERT(m_scriptRunner);
365     ASSERT(!inScriptExecution());
366     ASSERT(m_treeBuilder->isPaused());
367     // Note: We only ever wait on one script at a time, so we always know this
368     // is the one we were waiting on and can un-pause the tree builder.
369     m_treeBuilder->setPaused(false);
370     bool shouldContinueParsing = m_scriptRunner->executeScriptsWaitingForLoad(cachedResource);
371     m_treeBuilder->setPaused(!shouldContinueParsing);
372     if (shouldContinueParsing)
373         resumeParsingAfterScriptExecution();
374 }
375
376 void HTMLDocumentParser::executeScriptsWaitingForStylesheets()
377 {
378     // Document only calls this when the Document owns the DocumentParser
379     // so this will not be called in the DocumentFragment case.
380     ASSERT(m_scriptRunner);
381     // Ignore calls unless we have a script blocking the parser waiting on a
382     // stylesheet load.  Otherwise we are currently parsing and this
383     // is a re-entrant call from encountering a </ style> tag.
384     if (!m_scriptRunner->hasScriptsWaitingForStylesheets())
385         return;
386     ASSERT(!m_scriptRunner->inScriptExecution());
387     ASSERT(m_treeBuilder->isPaused());
388     // Note: We only ever wait on one script at a time, so we always know this
389     // is the one we were waiting on and can un-pause the tree builder.
390     m_treeBuilder->setPaused(false);
391     bool shouldContinueParsing = m_scriptRunner->executeScriptsWaitingForStylesheets();
392     m_treeBuilder->setPaused(!shouldContinueParsing);
393     if (shouldContinueParsing)
394         resumeParsingAfterScriptExecution();
395 }
396
397 ScriptController* HTMLDocumentParser::script() const
398 {
399     return m_document->frame() ? m_document->frame()->script() : 0;
400 }
401
402 void HTMLDocumentParser::parseDocumentFragment(const String& source, DocumentFragment* fragment, FragmentScriptingPermission scriptingPermission)
403 {
404     HTMLDocumentParser parser(fragment, scriptingPermission);
405     parser.insert(source); // Use insert() so that the parser will not yield.
406     parser.finish();
407     ASSERT(!parser.processingData()); // Make sure we're done. <rdar://problem/3963151>
408 }
409
410 }