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