a9e9fe5b14640e6f7e6e2321d7d2f0cdcc6284a7
[WebKit-https.git] / Source / WebCore / html / parser / 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 "ContentSecurityPolicy.h"
30 #include "DocumentFragment.h"
31 #include "Element.h"
32 #include "Frame.h"
33 #include "HTMLNames.h"
34 #include "HTMLParserScheduler.h"
35 #include "HTMLTokenizer.h"
36 #include "HTMLPreloadScanner.h"
37 #include "HTMLScriptRunner.h"
38 #include "HTMLTreeBuilder.h"
39 #include "HTMLDocument.h"
40 #include "InspectorInstrumentation.h"
41 #include "NestingLevelIncrementer.h"
42 #include "Settings.h"
43 #include <wtf/CurrentTime.h>
44
45 namespace WebCore {
46
47 using namespace HTMLNames;
48
49 namespace {
50
51 // This is a direct transcription of step 4 from:
52 // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-end.html#fragment-case
53 HTMLTokenizer::State tokenizerStateForContextElement(Element* contextElement, bool reportErrors)
54 {
55     if (!contextElement)
56         return HTMLTokenizer::DataState;
57
58     const QualifiedName& contextTag = contextElement->tagQName();
59
60     if (contextTag.matches(titleTag) || contextTag.matches(textareaTag))
61         return HTMLTokenizer::RCDATAState;
62     if (contextTag.matches(styleTag)
63         || contextTag.matches(xmpTag)
64         || contextTag.matches(iframeTag)
65         || (contextTag.matches(noembedTag) && HTMLTreeBuilder::pluginsEnabled(contextElement->document()->frame()))
66         || (contextTag.matches(noscriptTag) && HTMLTreeBuilder::scriptEnabled(contextElement->document()->frame()))
67         || contextTag.matches(noframesTag))
68         return reportErrors ? HTMLTokenizer::RAWTEXTState : HTMLTokenizer::PLAINTEXTState;
69     if (contextTag.matches(scriptTag))
70         return reportErrors ? HTMLTokenizer::ScriptDataState : HTMLTokenizer::PLAINTEXTState;
71     if (contextTag.matches(plaintextTag))
72         return HTMLTokenizer::PLAINTEXTState;
73     return HTMLTokenizer::DataState;
74 }
75
76 } // namespace
77
78 HTMLDocumentParser::HTMLDocumentParser(HTMLDocument* document, bool reportErrors)
79     : ScriptableDocumentParser(document)
80     , m_tokenizer(HTMLTokenizer::create(usePreHTML5ParserQuirks(document)))
81     , m_scriptRunner(HTMLScriptRunner::create(document, this))
82     , m_treeBuilder(HTMLTreeBuilder::create(this, document, reportErrors, usePreHTML5ParserQuirks(document)))
83     , m_parserScheduler(HTMLParserScheduler::create(this))
84     , m_xssFilter(this)
85     , m_endWasDelayed(false)
86     , m_pumpSessionNestingLevel(0)
87 {
88 }
89
90 // FIXME: Member variables should be grouped into self-initializing structs to
91 // minimize code duplication between these constructors.
92 HTMLDocumentParser::HTMLDocumentParser(DocumentFragment* fragment, Element* contextElement, FragmentScriptingPermission scriptingPermission)
93     : ScriptableDocumentParser(fragment->document())
94     , m_tokenizer(HTMLTokenizer::create(usePreHTML5ParserQuirks(fragment->document())))
95     , m_treeBuilder(HTMLTreeBuilder::create(this, fragment, contextElement, scriptingPermission, usePreHTML5ParserQuirks(fragment->document())))
96     , m_xssFilter(this)
97     , m_endWasDelayed(false)
98     , m_pumpSessionNestingLevel(0)
99 {
100     bool reportErrors = false; // For now document fragment parsing never reports errors.
101     m_tokenizer->setState(tokenizerStateForContextElement(contextElement, reportErrors));
102 }
103
104 HTMLDocumentParser::~HTMLDocumentParser()
105 {
106     ASSERT(!m_parserScheduler);
107     ASSERT(!m_pumpSessionNestingLevel);
108     ASSERT(!m_preloadScanner);
109 }
110
111 void HTMLDocumentParser::detach()
112 {
113     DocumentParser::detach();
114     if (m_scriptRunner)
115         m_scriptRunner->detach();
116     m_treeBuilder->detach();
117     // FIXME: It seems wrong that we would have a preload scanner here.
118     // Yet during fast/dom/HTMLScriptElement/script-load-events.html we do.
119     m_preloadScanner.clear();
120     m_parserScheduler.clear(); // Deleting the scheduler will clear any timers.
121 }
122
123 void HTMLDocumentParser::stopParsing()
124 {
125     DocumentParser::stopParsing();
126     m_parserScheduler.clear(); // Deleting the scheduler will clear any timers.
127 }
128
129 // This kicks off "Once the user agent stops parsing" as described by:
130 // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-end.html#the-end
131 void HTMLDocumentParser::prepareToStopParsing()
132 {
133     ASSERT(!hasInsertionPoint());
134
135     // pumpTokenizer can cause this parser to be detached from the Document,
136     // but we need to ensure it isn't deleted yet.
137     RefPtr<HTMLDocumentParser> protect(this);
138
139     // NOTE: This pump should only ever emit buffered character tokens,
140     // so ForceSynchronous vs. AllowYield should be meaningless.
141     pumpTokenizerIfPossible(ForceSynchronous);
142     
143     if (isStopped())
144         return;
145
146     DocumentParser::prepareToStopParsing();
147
148     // We will not have a scriptRunner when parsing a DocumentFragment.
149     if (m_scriptRunner)
150         document()->setReadyState(Document::Interactive);
151
152     attemptToRunDeferredScriptsAndEnd();
153 }
154
155 bool HTMLDocumentParser::isParsingFragment() const
156 {
157     return m_treeBuilder->isParsingFragment();
158 }
159
160 bool HTMLDocumentParser::processingData() const
161 {
162     return isScheduledForResume() || inPumpSession();
163 }
164
165 void HTMLDocumentParser::pumpTokenizerIfPossible(SynchronousMode mode)
166 {
167     if (isStopped() || m_treeBuilder->isPaused())
168         return;
169
170     // Once a resume is scheduled, HTMLParserScheduler controls when we next pump.
171     if (isScheduledForResume()) {
172         ASSERT(mode == AllowYield);
173         return;
174     }
175
176     pumpTokenizer(mode);
177 }
178
179 bool HTMLDocumentParser::isScheduledForResume() const
180 {
181     return m_parserScheduler && m_parserScheduler->isScheduledForResume();
182 }
183
184 // Used by HTMLParserScheduler
185 void HTMLDocumentParser::resumeParsingAfterYield()
186 {
187     // pumpTokenizer can cause this parser to be detached from the Document,
188     // but we need to ensure it isn't deleted yet.
189     RefPtr<HTMLDocumentParser> protect(this);
190
191     // We should never be here unless we can pump immediately.  Call pumpTokenizer()
192     // directly so that ASSERTS will fire if we're wrong.
193     pumpTokenizer(AllowYield);
194     endIfDelayed();
195 }
196
197 bool HTMLDocumentParser::runScriptsForPausedTreeBuilder()
198 {
199     ASSERT(m_treeBuilder->isPaused());
200
201     TextPosition1 scriptStartPosition = TextPosition1::belowRangePosition();
202     RefPtr<Element> scriptElement = m_treeBuilder->takeScriptToProcess(scriptStartPosition);
203     // We will not have a scriptRunner when parsing a DocumentFragment.
204     if (!m_scriptRunner)
205         return true;
206     return m_scriptRunner->execute(scriptElement.release(), scriptStartPosition);
207 }
208
209 bool HTMLDocumentParser::canTakeNextToken(SynchronousMode mode, PumpSession& session)
210 {
211     if (isStopped())
212         return false;
213
214     // The parser will pause itself when waiting on a script to load or run.
215     if (m_treeBuilder->isPaused()) {
216         if (mode == AllowYield)
217             m_parserScheduler->checkForYieldBeforeScript(session);
218
219         // If we don't run the script, we cannot allow the next token to be taken.
220         if (session.needsYield)
221             return false;
222
223         // If we're paused waiting for a script, we try to execute scripts before continuing.
224         bool shouldContinueParsing = runScriptsForPausedTreeBuilder();
225         m_treeBuilder->setPaused(!shouldContinueParsing);
226         if (!shouldContinueParsing || isStopped())
227             return false;
228     }
229
230     // FIXME: It's wrong for the HTMLDocumentParser to reach back to the
231     //        Frame, but this approach is how the old parser handled
232     //        stopping when the page assigns window.location.  What really
233     //        should happen is that assigning window.location causes the
234     //        parser to stop parsing cleanly.  The problem is we're not
235     //        perpared to do that at every point where we run JavaScript.
236     if (!isParsingFragment()
237         && document()->frame() && document()->frame()->navigationScheduler()->locationChangePending())
238         return false;
239
240     if (mode == AllowYield)
241         m_parserScheduler->checkForYieldBeforeToken(session);
242
243     return true;
244 }
245
246 void HTMLDocumentParser::pumpTokenizer(SynchronousMode mode)
247 {
248     ASSERT(!isStopped());
249     ASSERT(!isScheduledForResume());
250     // ASSERT that this object is both attached to the Document and protected.
251     ASSERT(refCount() >= 2);
252
253     PumpSession session(m_pumpSessionNestingLevel);
254
255     // We tell the InspectorInstrumentation about every pump, even if we
256     // end up pumping nothing.  It can filter out empty pumps itself.
257     // FIXME: m_input.current().length() is only accurate if we
258     // end up parsing the whole buffer in this pump.  We should pass how
259     // much we parsed as part of didWriteHTML instead of willWriteHTML.
260     InspectorInstrumentationCookie cookie = InspectorInstrumentation::willWriteHTML(document(), m_input.current().length(), m_tokenizer->lineNumber());
261
262     while (canTakeNextToken(mode, session) && !session.needsYield) {
263         if (!isParsingFragment())
264             m_sourceTracker.start(m_input, m_token);
265
266         if (!m_tokenizer->nextToken(m_input.current(), m_token))
267             break;
268
269         if (!isParsingFragment()) {
270             m_sourceTracker.end(m_input, m_token);
271
272             // We do not XSS filter innerHTML, which means we (intentionally) fail
273             // http/tests/security/xssAuditor/dom-write-innerHTML.html
274             m_xssFilter.filterToken(m_token);
275         }
276
277         m_treeBuilder->constructTreeFromToken(m_token);
278         m_token.clear();
279     }
280
281     // Ensure we haven't been totally deref'ed after pumping. Any caller of this
282     // function should be holding a RefPtr to this to ensure we weren't deleted.
283     ASSERT(refCount() >= 1);
284
285     if (isStopped())
286         return;
287
288     if (session.needsYield)
289         m_parserScheduler->scheduleForResume();
290
291     if (isWaitingForScripts()) {
292         ASSERT(m_tokenizer->state() == HTMLTokenizer::DataState);
293         if (!m_preloadScanner) {
294             m_preloadScanner.set(new HTMLPreloadScanner(document()));
295             m_preloadScanner->appendToEnd(m_input.current());
296         }
297         m_preloadScanner->scan();
298     }
299
300     InspectorInstrumentation::didWriteHTML(cookie, m_tokenizer->lineNumber());
301 }
302
303 bool HTMLDocumentParser::hasInsertionPoint()
304 {
305     // FIXME: The wasCreatedByScript() branch here might not be fully correct.
306     //        Our model of the EOF character differs slightly from the one in
307     //        the spec because our treatment is uniform between network-sourced
308     //        and script-sourced input streams whereas the spec treats them
309     //        differently.
310     return m_input.hasInsertionPoint() || (wasCreatedByScript() && !m_input.haveSeenEndOfFile());
311 }
312
313 void HTMLDocumentParser::insert(const SegmentedString& source)
314 {
315     if (isStopped())
316         return;
317
318     // pumpTokenizer can cause this parser to be detached from the Document,
319     // but we need to ensure it isn't deleted yet.
320     RefPtr<HTMLDocumentParser> protect(this);
321
322     SegmentedString excludedLineNumberSource(source);
323     excludedLineNumberSource.setExcludeLineNumbers();
324     m_input.insertAtCurrentInsertionPoint(excludedLineNumberSource);
325     pumpTokenizerIfPossible(ForceSynchronous);
326     
327     if (m_treeBuilder->isPaused()) {
328         // Check the document.write() output with a separate preload scanner as
329         // the main scanner can't deal with insertions.
330         HTMLPreloadScanner preloadScanner(document());
331         preloadScanner.appendToEnd(source);
332         preloadScanner.scan();
333     }
334
335     endIfDelayed();
336 }
337
338 void HTMLDocumentParser::append(const SegmentedString& source)
339 {
340     if (isStopped())
341         return;
342
343     // pumpTokenizer can cause this parser to be detached from the Document,
344     // but we need to ensure it isn't deleted yet.
345     RefPtr<HTMLDocumentParser> protect(this);
346
347     m_input.appendToEnd(source);
348     if (m_preloadScanner) {
349         m_preloadScanner->appendToEnd(source);
350         if (m_treeBuilder->isPaused())
351             m_preloadScanner->scan();
352     }
353
354     if (inPumpSession()) {
355         // We've gotten data off the network in a nested write.
356         // We don't want to consume any more of the input stream now.  Do
357         // not worry.  We'll consume this data in a less-nested write().
358         return;
359     }
360
361     pumpTokenizerIfPossible(AllowYield);
362
363     endIfDelayed();
364 }
365
366 void HTMLDocumentParser::end()
367 {
368     ASSERT(!isDetached());
369     ASSERT(!isScheduledForResume());
370
371     // Informs the the rest of WebCore that parsing is really finished (and deletes this).
372     m_treeBuilder->finished();
373 }
374
375 void HTMLDocumentParser::attemptToRunDeferredScriptsAndEnd()
376 {
377     ASSERT(isStopping());
378     ASSERT(!hasInsertionPoint());
379     if (m_scriptRunner && !m_scriptRunner->executeScriptsWaitingForParsing())
380         return;
381     end();
382 }
383
384 void HTMLDocumentParser::attemptToEnd()
385 {
386     // finish() indicates we will not receive any more data. If we are waiting on
387     // an external script to load, we can't finish parsing quite yet.
388
389     if (shouldDelayEnd()) {
390         m_endWasDelayed = true;
391         return;
392     }
393     prepareToStopParsing();
394 }
395
396 void HTMLDocumentParser::endIfDelayed()
397 {
398     // If we've already been detached, don't bother ending.
399     if (isDetached())
400         return;
401
402     if (!m_endWasDelayed || shouldDelayEnd())
403         return;
404
405     m_endWasDelayed = false;
406     prepareToStopParsing();
407 }
408
409 void HTMLDocumentParser::finish()
410 {
411     // FIXME: We should ASSERT(!m_parserStopped) here, since it does not
412     // makes sense to call any methods on DocumentParser once it's been stopped.
413     // However, FrameLoader::stop calls Document::finishParsing unconditionally
414     // which in turn calls m_parser->finish().
415
416     // We're not going to get any more data off the network, so we tell the
417     // input stream we've reached the end of file.  finish() can be called more
418     // than once, if the first time does not call end().
419     if (!m_input.haveSeenEndOfFile())
420         m_input.markEndOfFile();
421     attemptToEnd();
422 }
423
424 bool HTMLDocumentParser::finishWasCalled()
425 {
426     return m_input.haveSeenEndOfFile();
427 }
428
429 // This function is virtual and just for the DocumentParser interface.
430 bool HTMLDocumentParser::isExecutingScript() const
431 {
432     return inScriptExecution();
433 }
434
435 // This function is non-virtual and used throughout the implementation.
436 bool HTMLDocumentParser::inScriptExecution() const
437 {
438     if (!m_scriptRunner)
439         return false;
440     return m_scriptRunner->isExecutingScript();
441 }
442
443 String HTMLDocumentParser::sourceForToken(const HTMLToken& token)
444 {
445     return m_sourceTracker.sourceForToken(token);
446 }
447
448 int HTMLDocumentParser::lineNumber() const
449 {
450     return m_tokenizer->lineNumber();
451 }
452
453 TextPosition0 HTMLDocumentParser::textPosition() const
454 {
455     const SegmentedString& currentString = m_input.current();
456     WTF::ZeroBasedNumber line = currentString.currentLine();
457     WTF::ZeroBasedNumber column = currentString.currentColumn();
458     ASSERT(m_tokenizer->lineNumber() == line.zeroBasedInt());
459
460     return TextPosition0(line, column);
461 }
462
463 bool HTMLDocumentParser::isWaitingForScripts() const
464 {
465     return m_treeBuilder->isPaused();
466 }
467
468 void HTMLDocumentParser::resumeParsingAfterScriptExecution()
469 {
470     ASSERT(!inScriptExecution());
471     ASSERT(!m_treeBuilder->isPaused());
472
473     m_preloadScanner.clear();
474     pumpTokenizerIfPossible(AllowYield);
475     endIfDelayed();
476 }
477
478 void HTMLDocumentParser::watchForLoad(CachedResource* cachedScript)
479 {
480     ASSERT(!cachedScript->isLoaded());
481     // addClient would call notifyFinished if the load were complete.
482     // Callers do not expect to be re-entered from this call, so they should
483     // not an already-loaded CachedResource.
484     cachedScript->addClient(this);
485 }
486
487 void HTMLDocumentParser::stopWatchingForLoad(CachedResource* cachedScript)
488 {
489     cachedScript->removeClient(this);
490 }
491     
492 void HTMLDocumentParser::appendCurrentInputStreamToPreloadScannerAndScan()
493 {
494     ASSERT(m_preloadScanner);
495     m_preloadScanner->appendToEnd(m_input.current());
496     m_preloadScanner->scan();
497 }
498
499 void HTMLDocumentParser::notifyFinished(CachedResource* cachedResource)
500 {
501     // pumpTokenizer can cause this parser to be detached from the Document,
502     // but we need to ensure it isn't deleted yet.
503     RefPtr<HTMLDocumentParser> protect(this);
504
505     ASSERT(m_scriptRunner);
506     ASSERT(!inScriptExecution());
507     if (isStopping()) {
508         attemptToRunDeferredScriptsAndEnd();
509         return;
510     }
511
512     ASSERT(m_treeBuilder->isPaused());
513     // Note: We only ever wait on one script at a time, so we always know this
514     // is the one we were waiting on and can un-pause the tree builder.
515     m_treeBuilder->setPaused(false);
516     bool shouldContinueParsing = m_scriptRunner->executeScriptsWaitingForLoad(cachedResource);
517     m_treeBuilder->setPaused(!shouldContinueParsing);
518     if (shouldContinueParsing)
519         resumeParsingAfterScriptExecution();
520 }
521
522 void HTMLDocumentParser::executeScriptsWaitingForStylesheets()
523 {
524     // Document only calls this when the Document owns the DocumentParser
525     // so this will not be called in the DocumentFragment case.
526     ASSERT(m_scriptRunner);
527     // Ignore calls unless we have a script blocking the parser waiting on a
528     // stylesheet load.  Otherwise we are currently parsing and this
529     // is a re-entrant call from encountering a </ style> tag.
530     if (!m_scriptRunner->hasScriptsWaitingForStylesheets())
531         return;
532
533     // pumpTokenizer can cause this parser to be detached from the Document,
534     // but we need to ensure it isn't deleted yet.
535     RefPtr<HTMLDocumentParser> protect(this);
536
537     ASSERT(!m_scriptRunner->isExecutingScript());
538     ASSERT(m_treeBuilder->isPaused());
539     // Note: We only ever wait on one script at a time, so we always know this
540     // is the one we were waiting on and can un-pause the tree builder.
541     m_treeBuilder->setPaused(false);
542     bool shouldContinueParsing = m_scriptRunner->executeScriptsWaitingForStylesheets();
543     m_treeBuilder->setPaused(!shouldContinueParsing);
544     if (shouldContinueParsing)
545         resumeParsingAfterScriptExecution();
546 }
547
548 ScriptController* HTMLDocumentParser::script() const
549 {
550     return document()->frame() ? document()->frame()->script() : 0;
551 }
552
553 void HTMLDocumentParser::parseDocumentFragment(const String& source, DocumentFragment* fragment, Element* contextElement, FragmentScriptingPermission scriptingPermission)
554 {
555     RefPtr<HTMLDocumentParser> parser = HTMLDocumentParser::create(fragment, contextElement, scriptingPermission);
556     parser->insert(source); // Use insert() so that the parser will not yield.
557     parser->finish();
558     ASSERT(!parser->processingData()); // Make sure we're done. <rdar://problem/3963151>
559     parser->detach(); // Allows ~DocumentParser to assert it was detached before destruction.
560 }
561     
562 bool HTMLDocumentParser::usePreHTML5ParserQuirks(Document* document)
563 {
564     ASSERT(document);
565     return document->settings() && document->settings()->usePreHTML5ParserQuirks();
566 }
567
568 void HTMLDocumentParser::suspendScheduledTasks()
569 {
570     if (m_parserScheduler)
571         m_parserScheduler->suspend();
572 }
573
574 void HTMLDocumentParser::resumeScheduledTasks()
575 {
576     if (m_parserScheduler)
577         m_parserScheduler->resume();
578 }
579
580 }