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