d9643cd2ad29db4a99652cc3ae3c1db6fb18668f
[WebKit-https.git] / Source / WebCore / html / parser / HTMLDocumentParser.cpp
1 /*
2  * Copyright (C) 2010 Google, Inc. All Rights Reserved.
3  * Copyright (C) 2015 Apple Inc. All Rights Reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 #include "config.h"
28 #include "HTMLDocumentParser.h"
29
30 #include "DocumentFragment.h"
31 #include "HTMLParserScheduler.h"
32 #include "HTMLPreloadScanner.h"
33 #include "HTMLScriptRunner.h"
34 #include "HTMLTreeBuilder.h"
35 #include "HTMLDocument.h"
36 #include "InspectorInstrumentation.h"
37
38 namespace WebCore {
39
40 using namespace HTMLNames;
41
42 // This is a direct transcription of step 4 from:
43 // https://html.spec.whatwg.org/multipage/syntax.html#parsing-html-fragments
44 static HTMLTokenizer::State tokenizerStateForContextElement(Element& contextElement, bool reportErrors, const HTMLParserOptions& options)
45 {
46     const QualifiedName& contextTag = contextElement.tagQName();
47
48     if (contextTag.matches(titleTag) || contextTag.matches(textareaTag))
49         return HTMLTokenizer::RCDATAState;
50     if (contextTag.matches(styleTag)
51         || contextTag.matches(xmpTag)
52         || contextTag.matches(iframeTag)
53         || (contextTag.matches(noembedTag) && options.pluginsEnabled)
54         || (contextTag.matches(noscriptTag) && options.scriptEnabled)
55         || contextTag.matches(noframesTag))
56         return reportErrors ? HTMLTokenizer::RAWTEXTState : HTMLTokenizer::PLAINTEXTState;
57     if (contextTag.matches(scriptTag))
58         return reportErrors ? HTMLTokenizer::ScriptDataState : HTMLTokenizer::PLAINTEXTState;
59     if (contextTag.matches(plaintextTag))
60         return HTMLTokenizer::PLAINTEXTState;
61     return HTMLTokenizer::DataState;
62 }
63
64 HTMLDocumentParser::HTMLDocumentParser(HTMLDocument& document)
65     : ScriptableDocumentParser(document)
66     , m_options(document)
67     , m_tokenizer(m_options)
68     , m_scriptRunner(std::make_unique<HTMLScriptRunner>(document, static_cast<HTMLScriptRunnerHost&>(*this)))
69     , m_treeBuilder(std::make_unique<HTMLTreeBuilder>(*this, document, parserContentPolicy(), m_options))
70     , m_parserScheduler(std::make_unique<HTMLParserScheduler>(*this))
71     , m_xssAuditorDelegate(document)
72     , m_preloader(std::make_unique<HTMLResourcePreloader>(document))
73 {
74 }
75
76 Ref<HTMLDocumentParser> HTMLDocumentParser::create(HTMLDocument& document)
77 {
78     return adoptRef(*new HTMLDocumentParser(document));
79 }
80
81 inline HTMLDocumentParser::HTMLDocumentParser(DocumentFragment& fragment, Element& contextElement, ParserContentPolicy rawPolicy)
82     : ScriptableDocumentParser(fragment.document(), rawPolicy)
83     , m_options(fragment.document())
84     , m_tokenizer(m_options)
85     , m_treeBuilder(std::make_unique<HTMLTreeBuilder>(*this, fragment, contextElement, parserContentPolicy(), m_options))
86     , m_xssAuditorDelegate(fragment.document())
87 {
88     bool reportErrors = false; // For now document fragment parsing never reports errors.
89     m_tokenizer.setState(tokenizerStateForContextElement(contextElement, reportErrors, m_options));
90     m_xssAuditor.initForFragment();
91 }
92
93 inline Ref<HTMLDocumentParser> HTMLDocumentParser::create(DocumentFragment& fragment, Element& contextElement, ParserContentPolicy parserContentPolicy)
94 {
95     return adoptRef(*new HTMLDocumentParser(fragment, contextElement, parserContentPolicy));
96 }
97
98 HTMLDocumentParser::~HTMLDocumentParser()
99 {
100     ASSERT(!m_parserScheduler);
101     ASSERT(!m_pumpSessionNestingLevel);
102     ASSERT(!m_preloadScanner);
103     ASSERT(!m_insertionPreloadScanner);
104 }
105
106 void HTMLDocumentParser::detach()
107 {
108     DocumentParser::detach();
109
110     if (m_scriptRunner)
111         m_scriptRunner->detach();
112     // FIXME: It seems wrong that we would have a preload scanner here.
113     // Yet during fast/dom/HTMLScriptElement/script-load-events.html we do.
114     m_preloadScanner = nullptr;
115     m_insertionPreloadScanner = nullptr;
116     m_parserScheduler = nullptr; // Deleting the scheduler will clear any timers.
117 }
118
119 void HTMLDocumentParser::stopParsing()
120 {
121     DocumentParser::stopParsing();
122     m_parserScheduler = nullptr; // Deleting the scheduler will clear any timers.
123 }
124
125 // This kicks off "Once the user agent stops parsing" as described by:
126 // https://html.spec.whatwg.org/multipage/syntax.html#the-end
127 void HTMLDocumentParser::prepareToStopParsing()
128 {
129     ASSERT(!hasInsertionPoint());
130
131     // pumpTokenizer can cause this parser to be detached from the Document,
132     // but we need to ensure it isn't deleted yet.
133     Ref<HTMLDocumentParser> protect(*this);
134
135     // NOTE: This pump should only ever emit buffered character tokens,
136     // so ForceSynchronous vs. AllowYield should be meaningless.
137     pumpTokenizerIfPossible(ForceSynchronous);
138
139     if (isStopped())
140         return;
141
142     DocumentParser::prepareToStopParsing();
143
144     // We will not have a scriptRunner when parsing a DocumentFragment.
145     if (m_scriptRunner)
146         document()->setReadyState(Document::Interactive);
147
148     // Setting the ready state above can fire mutation event and detach us
149     // from underneath. In that case, just bail out.
150     if (isDetached())
151         return;
152
153     attemptToRunDeferredScriptsAndEnd();
154 }
155
156 inline bool HTMLDocumentParser::inPumpSession() const
157 {
158     return m_pumpSessionNestingLevel > 0;
159 }
160
161 inline bool HTMLDocumentParser::shouldDelayEnd() const
162 {
163     return inPumpSession() || isWaitingForScripts() || isScheduledForResume() || isExecutingScript();
164 }
165
166 bool HTMLDocumentParser::isParsingFragment() const
167 {
168     return m_treeBuilder->isParsingFragment();
169 }
170
171 bool HTMLDocumentParser::processingData() const
172 {
173     return isScheduledForResume() || inPumpSession();
174 }
175
176 void HTMLDocumentParser::pumpTokenizerIfPossible(SynchronousMode mode)
177 {
178     if (isStopped() || isWaitingForScripts())
179         return;
180
181     // Once a resume is scheduled, HTMLParserScheduler controls when we next pump.
182     if (isScheduledForResume()) {
183         ASSERT(mode == AllowYield);
184         return;
185     }
186
187     pumpTokenizer(mode);
188 }
189
190 bool HTMLDocumentParser::isScheduledForResume() const
191 {
192     return m_parserScheduler && m_parserScheduler->isScheduledForResume();
193 }
194
195 // Used by HTMLParserScheduler
196 void HTMLDocumentParser::resumeParsingAfterYield()
197 {
198     // pumpTokenizer can cause this parser to be detached from the Document,
199     // but we need to ensure it isn't deleted yet.
200     Ref<HTMLDocumentParser> protect(*this);
201
202     // We should never be here unless we can pump immediately.
203     // Call pumpTokenizer() directly so that ASSERTS will fire if we're wrong.
204     pumpTokenizer(AllowYield);
205     endIfDelayed();
206 }
207
208 void HTMLDocumentParser::runScriptsForPausedTreeBuilder()
209 {
210     ASSERT(scriptingContentIsAllowed(parserContentPolicy()));
211
212     TextPosition scriptStartPosition = TextPosition::belowRangePosition();
213     if (auto scriptElement = m_treeBuilder->takeScriptToProcess(scriptStartPosition)) {
214         // We will not have a scriptRunner when parsing a DocumentFragment.
215         if (m_scriptRunner)
216             m_scriptRunner->execute(scriptElement.release(), scriptStartPosition);
217     }
218 }
219
220 bool HTMLDocumentParser::canTakeNextToken(SynchronousMode mode, PumpSession& session)
221 {
222     if (isStopped())
223         return false;
224
225     if (isWaitingForScripts()) {
226         if (mode == AllowYield)
227             m_parserScheduler->checkForYieldBeforeScript(session);
228
229         // If we don't run the script, we cannot allow the next token to be taken.
230         if (session.needsYield)
231             return false;
232
233         // If we're paused waiting for a script, we try to execute scripts before continuing.
234         runScriptsForPausedTreeBuilder();
235         if (isWaitingForScripts() || isStopped())
236             return false;
237     }
238
239     // FIXME: It's wrong for the HTMLDocumentParser to reach back to the Frame, but this approach is
240     // how the parser has always handled stopping when the page assigns window.location. What should
241     // happen instead  is that assigning window.location causes the parser to stop parsing cleanly.
242     // The problem is we're not prepared to do that at every point where we run JavaScript.
243     if (!isParsingFragment() && document()->frame() && document()->frame()->navigationScheduler().locationChangePending())
244         return false;
245
246     if (mode == AllowYield)
247         m_parserScheduler->checkForYieldBeforeToken(session);
248
249     return true;
250 }
251
252 Document* HTMLDocumentParser::contextForParsingSession()
253 {
254     // The parsing session should interact with the document only when parsing
255     // non-fragments. Otherwise, we might delay the load event mistakenly.
256     if (isParsingFragment())
257         return nullptr;
258     return document();
259 }
260
261 void HTMLDocumentParser::pumpTokenizer(SynchronousMode mode)
262 {
263     ASSERT(!isStopped());
264     ASSERT(!isScheduledForResume());
265
266     // This is an attempt to check that this object is both attached to the Document and protected by something.
267     ASSERT(refCount() >= 2);
268
269     PumpSession session(m_pumpSessionNestingLevel, contextForParsingSession());
270
271     // We tell the InspectorInstrumentation about every pump, even if we
272     // end up pumping nothing.  It can filter out empty pumps itself.
273     // FIXME: m_input.current().length() is only accurate if we
274     // end up parsing the whole buffer in this pump.  We should pass how
275     // much we parsed as part of didWriteHTML instead of willWriteHTML.
276     auto cookie = InspectorInstrumentation::willWriteHTML(document(), m_input.current().currentLine().zeroBasedInt());
277
278     m_xssAuditor.init(document(), &m_xssAuditorDelegate);
279
280     while (canTakeNextToken(mode, session) && !session.needsYield) {
281         if (!isParsingFragment())
282             m_sourceTracker.start(m_input.current(), &m_tokenizer, m_token);
283
284         if (!m_tokenizer.nextToken(m_input.current(), m_token))
285             break;
286
287         if (!isParsingFragment()) {
288             m_sourceTracker.end(m_input.current(), &m_tokenizer, m_token);
289
290             // We do not XSS filter innerHTML, which means we (intentionally) fail
291             // http/tests/security/xssAuditor/dom-write-innerHTML.html
292             if (auto xssInfo = m_xssAuditor.filterToken(FilterTokenRequest(m_token, m_sourceTracker, m_tokenizer.shouldAllowCDATA())))
293                 m_xssAuditorDelegate.didBlockScript(*xssInfo);
294         }
295
296         constructTreeFromHTMLToken(m_token);
297         ASSERT(m_token.type() == HTMLToken::Uninitialized);
298     }
299
300     // Ensure we haven't been totally deref'ed after pumping. Any caller of this
301     // function should be holding a RefPtr to this to ensure we weren't deleted.
302     ASSERT(refCount() >= 1);
303
304     if (isStopped())
305         return;
306
307     if (session.needsYield)
308         m_parserScheduler->scheduleForResume();
309
310     if (isWaitingForScripts()) {
311         ASSERT(m_tokenizer.state() == HTMLTokenizer::DataState);
312         if (!m_preloadScanner) {
313             m_preloadScanner = std::make_unique<HTMLPreloadScanner>(m_options, document()->url(), document()->deviceScaleFactor());
314             m_preloadScanner->appendToEnd(m_input.current());
315         }
316         m_preloadScanner->scan(m_preloader.get(), *document());
317     }
318
319     InspectorInstrumentation::didWriteHTML(cookie, m_input.current().currentLine().zeroBasedInt());
320 }
321
322 void HTMLDocumentParser::constructTreeFromHTMLToken(HTMLToken& rawToken)
323 {
324     AtomicHTMLToken token(rawToken);
325
326     // We clear the rawToken in case constructTreeFromAtomicToken
327     // synchronously re-enters the parser. We don't clear the token immedately
328     // for Character tokens because the AtomicHTMLToken avoids copying the
329     // characters by keeping a pointer to the underlying buffer in the
330     // HTMLToken. Fortunately, Character tokens can't cause us to re-enter
331     // the parser.
332     //
333     // FIXME: Stop clearing the rawToken once we start running the parser off
334     // the main thread or once we stop allowing synchronous JavaScript
335     // execution from parseAttribute.
336     if (rawToken.type() != HTMLToken::Character)
337         rawToken.clear();
338
339     m_treeBuilder->constructTree(token);
340
341     if (rawToken.type() != HTMLToken::Uninitialized) {
342         ASSERT(rawToken.type() == HTMLToken::Character);
343         rawToken.clear();
344     }
345 }
346
347 bool HTMLDocumentParser::hasInsertionPoint()
348 {
349     // FIXME: The wasCreatedByScript() branch here might not be fully correct.
350     // Our model of the EOF character differs slightly from the one in the spec
351     // because our treatment is uniform between network-sourced and script-sourced
352     // input streams whereas the spec treats them differently.
353     return m_input.hasInsertionPoint() || (wasCreatedByScript() && !m_input.haveSeenEndOfFile());
354 }
355
356 void HTMLDocumentParser::insert(const SegmentedString& source)
357 {
358     if (isStopped())
359         return;
360
361     // pumpTokenizer can cause this parser to be detached from the Document,
362     // but we need to ensure it isn't deleted yet.
363     Ref<HTMLDocumentParser> protect(*this);
364
365     SegmentedString excludedLineNumberSource(source);
366     excludedLineNumberSource.setExcludeLineNumbers();
367     m_input.insertAtCurrentInsertionPoint(excludedLineNumberSource);
368     pumpTokenizerIfPossible(ForceSynchronous);
369
370     if (isWaitingForScripts()) {
371         // Check the document.write() output with a separate preload scanner as
372         // the main scanner can't deal with insertions.
373         if (!m_insertionPreloadScanner)
374             m_insertionPreloadScanner = std::make_unique<HTMLPreloadScanner>(m_options, document()->url(), document()->deviceScaleFactor());
375         m_insertionPreloadScanner->appendToEnd(source);
376         m_insertionPreloadScanner->scan(m_preloader.get(), *document());
377     }
378
379     endIfDelayed();
380 }
381
382 void HTMLDocumentParser::append(PassRefPtr<StringImpl> inputSource)
383 {
384     if (isStopped())
385         return;
386
387     // pumpTokenizer can cause this parser to be detached from the Document,
388     // but we need to ensure it isn't deleted yet.
389     Ref<HTMLDocumentParser> protect(*this);
390
391     String source(inputSource);
392
393     if (m_preloadScanner) {
394         if (m_input.current().isEmpty() && !isWaitingForScripts()) {
395             // We have parsed until the end of the current input and so are now moving ahead of the preload scanner.
396             // Clear the scanner so we know to scan starting from the current input point if we block again.
397             m_preloadScanner = nullptr;
398         } else {
399             m_preloadScanner->appendToEnd(source);
400             if (isWaitingForScripts())
401                 m_preloadScanner->scan(m_preloader.get(), *document());
402         }
403     }
404
405     m_input.appendToEnd(source);
406
407     if (inPumpSession()) {
408         // We've gotten data off the network in a nested write.
409         // We don't want to consume any more of the input stream now.  Do
410         // not worry.  We'll consume this data in a less-nested write().
411         return;
412     }
413
414     pumpTokenizerIfPossible(AllowYield);
415
416     endIfDelayed();
417 }
418
419 void HTMLDocumentParser::end()
420 {
421     ASSERT(!isDetached());
422     ASSERT(!isScheduledForResume());
423
424     // Informs the the rest of WebCore that parsing is really finished (and deletes this).
425     m_treeBuilder->finished();
426 }
427
428 void HTMLDocumentParser::attemptToRunDeferredScriptsAndEnd()
429 {
430     ASSERT(isStopping());
431     ASSERT(!hasInsertionPoint());
432     if (m_scriptRunner && !m_scriptRunner->executeScriptsWaitingForParsing())
433         return;
434     end();
435 }
436
437 void HTMLDocumentParser::attemptToEnd()
438 {
439     // finish() indicates we will not receive any more data. If we are waiting on
440     // an external script to load, we can't finish parsing quite yet.
441
442     if (shouldDelayEnd()) {
443         m_endWasDelayed = true;
444         return;
445     }
446     prepareToStopParsing();
447 }
448
449 void HTMLDocumentParser::endIfDelayed()
450 {
451     // If we've already been detached, don't bother ending.
452     if (isDetached())
453         return;
454
455     if (!m_endWasDelayed || shouldDelayEnd())
456         return;
457
458     m_endWasDelayed = false;
459     prepareToStopParsing();
460 }
461
462 void HTMLDocumentParser::finish()
463 {
464     // FIXME: We should ASSERT(!m_parserStopped) here, since it does not
465     // makes sense to call any methods on DocumentParser once it's been stopped.
466     // However, FrameLoader::stop calls DocumentParser::finish unconditionally.
467
468     // We're not going to get any more data off the network, so we tell the
469     // input stream we've reached the end of file. finish() can be called more
470     // than once, if the first time does not call end().
471     if (!m_input.haveSeenEndOfFile())
472         m_input.markEndOfFile();
473
474     attemptToEnd();
475 }
476
477 bool HTMLDocumentParser::isExecutingScript() const
478 {
479     return m_scriptRunner && m_scriptRunner->isExecutingScript();
480 }
481
482 TextPosition HTMLDocumentParser::textPosition() const
483 {
484     auto& currentString = m_input.current();
485     return TextPosition(currentString.currentLine(), currentString.currentColumn());
486 }
487
488 bool HTMLDocumentParser::isWaitingForScripts() const
489 {
490     // When the TreeBuilder encounters a </script> tag, it returns to the HTMLDocumentParser
491     // where the script is transfered from the treebuilder to the script runner.
492     // The script runner will hold the script until its loaded and run. During
493     // any of this time, we want to count ourselves as "waiting for a script" and thus
494     // run the preload scanner, as well as delay completion of parsing.
495     bool treeBuilderHasBlockingScript = m_treeBuilder->hasParserBlockingScript();
496     bool scriptRunnerHasBlockingScript = m_scriptRunner && m_scriptRunner->hasParserBlockingScript();
497     // Since the parser is paused while a script runner has a blocking script, it should
498     // never be possible to end up with both objects holding a blocking script.
499     ASSERT(!(treeBuilderHasBlockingScript && scriptRunnerHasBlockingScript));
500     // If either object has a blocking script, the parser should be paused.
501     return treeBuilderHasBlockingScript || scriptRunnerHasBlockingScript;
502 }
503
504 void HTMLDocumentParser::resumeParsingAfterScriptExecution()
505 {
506     ASSERT(!isExecutingScript());
507     ASSERT(!isWaitingForScripts());
508
509     // pumpTokenizer can cause this parser to be detached from the Document,
510     // but we need to ensure it isn't deleted yet.
511     Ref<HTMLDocumentParser> protect(*this);
512
513     m_insertionPreloadScanner = nullptr;
514     pumpTokenizerIfPossible(AllowYield);
515     endIfDelayed();
516 }
517
518 void HTMLDocumentParser::watchForLoad(CachedResource* cachedScript)
519 {
520     ASSERT(!cachedScript->isLoaded());
521     // addClient would call notifyFinished if the load were complete.
522     // Callers do not expect to be re-entered from this call, so they should
523     // not an already-loaded CachedResource.
524     cachedScript->addClient(this);
525 }
526
527 void HTMLDocumentParser::stopWatchingForLoad(CachedResource* cachedScript)
528 {
529     cachedScript->removeClient(this);
530 }
531
532 void HTMLDocumentParser::appendCurrentInputStreamToPreloadScannerAndScan()
533 {
534     ASSERT(m_preloadScanner);
535     m_preloadScanner->appendToEnd(m_input.current());
536     m_preloadScanner->scan(m_preloader.get(), *document());
537 }
538
539 void HTMLDocumentParser::notifyFinished(CachedResource* cachedResource)
540 {
541     // pumpTokenizer can cause this parser to be detached from the Document,
542     // but we need to ensure it isn't deleted yet.
543     Ref<HTMLDocumentParser> protect(*this);
544
545     ASSERT(m_scriptRunner);
546     ASSERT(!isExecutingScript());
547     if (isStopping()) {
548         attemptToRunDeferredScriptsAndEnd();
549         return;
550     }
551
552     m_scriptRunner->executeScriptsWaitingForLoad(cachedResource);
553     if (!isWaitingForScripts())
554         resumeParsingAfterScriptExecution();
555 }
556
557 void HTMLDocumentParser::executeScriptsWaitingForStylesheets()
558 {
559     // Document only calls this when the Document owns the DocumentParser
560     // so this will not be called in the DocumentFragment case.
561     ASSERT(m_scriptRunner);
562     // Ignore calls unless we have a script blocking the parser waiting on a
563     // stylesheet load.  Otherwise we are currently parsing and this
564     // is a re-entrant call from encountering a </ style> tag.
565     if (!m_scriptRunner->hasScriptsWaitingForStylesheets())
566         return;
567
568     // pumpTokenizer can cause this parser to be detached from the Document,
569     // but we need to ensure it isn't deleted yet.
570     Ref<HTMLDocumentParser> protect(*this);
571     m_scriptRunner->executeScriptsWaitingForStylesheets();
572     if (!isWaitingForScripts())
573         resumeParsingAfterScriptExecution();
574 }
575
576 void HTMLDocumentParser::parseDocumentFragment(const String& source, DocumentFragment& fragment, Element& contextElement, ParserContentPolicy parserContentPolicy)
577 {
578     auto parser = create(fragment, contextElement, parserContentPolicy);
579     parser->insert(source); // Use insert() so that the parser will not yield.
580     parser->finish();
581     ASSERT(!parser->processingData());
582     parser->detach();
583 }
584     
585 void HTMLDocumentParser::suspendScheduledTasks()
586 {
587     if (m_parserScheduler)
588         m_parserScheduler->suspend();
589 }
590
591 void HTMLDocumentParser::resumeScheduledTasks()
592 {
593     if (m_parserScheduler)
594         m_parserScheduler->resume();
595 }
596
597 }