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