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