diff options
Diffstat (limited to 'Source/WebCore/html/parser/HTMLDocumentParser.cpp')
-rw-r--r-- | Source/WebCore/html/parser/HTMLDocumentParser.cpp | 358 |
1 files changed, 165 insertions, 193 deletions
diff --git a/Source/WebCore/html/parser/HTMLDocumentParser.cpp b/Source/WebCore/html/parser/HTMLDocumentParser.cpp index 488862049..c3846db05 100644 --- a/Source/WebCore/html/parser/HTMLDocumentParser.cpp +++ b/Source/WebCore/html/parser/HTMLDocumentParser.cpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2010 Google, Inc. All Rights Reserved. + * Copyright (C) 2015-2017 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,99 +27,70 @@ #include "config.h" #include "HTMLDocumentParser.h" -#include "ContentSecurityPolicy.h" #include "DocumentFragment.h" -#include "DocumentLoader.h" #include "Frame.h" +#include "HTMLDocument.h" #include "HTMLParserScheduler.h" +#include "HTMLPreloadScanner.h" #include "HTMLScriptRunner.h" #include "HTMLTreeBuilder.h" -#include "HTMLDocument.h" -#include "InspectorInstrumentation.h" -#include "Settings.h" -#include <wtf/Ref.h> +#include "HTMLUnknownElement.h" +#include "JSCustomElementInterface.h" +#include "ScriptElement.h" namespace WebCore { using namespace HTMLNames; -// This is a direct transcription of step 4 from: -// http://www.whatwg.org/specs/web-apps/current-work/multipage/the-end.html#fragment-case -static HTMLTokenizer::State tokenizerStateForContextElement(Element* contextElement, bool reportErrors, const HTMLParserOptions& options) -{ - if (!contextElement) - return HTMLTokenizer::DataState; - - const QualifiedName& contextTag = contextElement->tagQName(); - - if (contextTag.matches(titleTag) || contextTag.matches(textareaTag)) - return HTMLTokenizer::RCDATAState; - if (contextTag.matches(styleTag) - || contextTag.matches(xmpTag) - || contextTag.matches(iframeTag) - || (contextTag.matches(noembedTag) && options.pluginsEnabled) - || (contextTag.matches(noscriptTag) && options.scriptEnabled) - || contextTag.matches(noframesTag)) - return reportErrors ? HTMLTokenizer::RAWTEXTState : HTMLTokenizer::PLAINTEXTState; - if (contextTag.matches(scriptTag)) - return reportErrors ? HTMLTokenizer::ScriptDataState : HTMLTokenizer::PLAINTEXTState; - if (contextTag.matches(plaintextTag)) - return HTMLTokenizer::PLAINTEXTState; - return HTMLTokenizer::DataState; -} - HTMLDocumentParser::HTMLDocumentParser(HTMLDocument& document) : ScriptableDocumentParser(document) , m_options(document) - , m_token(std::make_unique<HTMLToken>()) - , m_tokenizer(std::make_unique<HTMLTokenizer>(m_options)) + , m_tokenizer(m_options) , m_scriptRunner(std::make_unique<HTMLScriptRunner>(document, static_cast<HTMLScriptRunnerHost&>(*this))) , m_treeBuilder(std::make_unique<HTMLTreeBuilder>(*this, document, parserContentPolicy(), m_options)) , m_parserScheduler(std::make_unique<HTMLParserScheduler>(*this)) , m_xssAuditorDelegate(document) , m_preloader(std::make_unique<HTMLResourcePreloader>(document)) - , m_endWasDelayed(false) - , m_haveBackgroundParser(false) - , m_pumpSessionNestingLevel(0) { - ASSERT(m_token); - ASSERT(m_tokenizer); } -// FIXME: Member variables should be grouped into self-initializing structs to -// minimize code duplication between these constructors. -HTMLDocumentParser::HTMLDocumentParser(DocumentFragment& fragment, Element* contextElement, ParserContentPolicy parserContentPolicy) - : ScriptableDocumentParser(fragment.document(), parserContentPolicy) +Ref<HTMLDocumentParser> HTMLDocumentParser::create(HTMLDocument& document) +{ + return adoptRef(*new HTMLDocumentParser(document)); +} + +inline HTMLDocumentParser::HTMLDocumentParser(DocumentFragment& fragment, Element& contextElement, ParserContentPolicy rawPolicy) + : ScriptableDocumentParser(fragment.document(), rawPolicy) , m_options(fragment.document()) - , m_token(std::make_unique<HTMLToken>()) - , m_tokenizer(std::make_unique<HTMLTokenizer>(m_options)) - , m_treeBuilder(std::make_unique<HTMLTreeBuilder>(*this, fragment, contextElement, this->parserContentPolicy(), m_options)) + , m_tokenizer(m_options) + , m_treeBuilder(std::make_unique<HTMLTreeBuilder>(*this, fragment, contextElement, parserContentPolicy(), m_options)) , m_xssAuditorDelegate(fragment.document()) - , m_endWasDelayed(false) - , m_haveBackgroundParser(false) - , m_pumpSessionNestingLevel(0) { - bool reportErrors = false; // For now document fragment parsing never reports errors. - m_tokenizer->setState(tokenizerStateForContextElement(contextElement, reportErrors, m_options)); + // https://html.spec.whatwg.org/multipage/syntax.html#parsing-html-fragments + if (contextElement.isHTMLElement()) + m_tokenizer.updateStateFor(contextElement.tagQName().localName()); m_xssAuditor.initForFragment(); } +inline Ref<HTMLDocumentParser> HTMLDocumentParser::create(DocumentFragment& fragment, Element& contextElement, ParserContentPolicy parserContentPolicy) +{ + return adoptRef(*new HTMLDocumentParser(fragment, contextElement, parserContentPolicy)); +} + HTMLDocumentParser::~HTMLDocumentParser() { ASSERT(!m_parserScheduler); ASSERT(!m_pumpSessionNestingLevel); ASSERT(!m_preloadScanner); ASSERT(!m_insertionPreloadScanner); - ASSERT(!m_haveBackgroundParser); } void HTMLDocumentParser::detach() { - DocumentParser::detach(); + ScriptableDocumentParser::detach(); if (m_scriptRunner) m_scriptRunner->detach(); - m_treeBuilder->detach(); // FIXME: It seems wrong that we would have a preload scanner here. // Yet during fast/dom/HTMLScriptElement/script-load-events.html we do. m_preloadScanner = nullptr; @@ -133,16 +105,14 @@ void HTMLDocumentParser::stopParsing() } // This kicks off "Once the user agent stops parsing" as described by: -// http://www.whatwg.org/specs/web-apps/current-work/multipage/the-end.html#the-end +// https://html.spec.whatwg.org/multipage/syntax.html#the-end void HTMLDocumentParser::prepareToStopParsing() { - // FIXME: It may not be correct to disable this for the background parser. - // That means hasInsertionPoint() may not be correct in some cases. - ASSERT(!hasInsertionPoint() || m_haveBackgroundParser); + ASSERT(!hasInsertionPoint()); // pumpTokenizer can cause this parser to be detached from the Document, // but we need to ensure it isn't deleted yet. - Ref<HTMLDocumentParser> protect(*this); + Ref<HTMLDocumentParser> protectedThis(*this); // NOTE: This pump should only ever emit buffered character tokens, // so ForceSynchronous vs. AllowYield should be meaningless. @@ -165,6 +135,16 @@ void HTMLDocumentParser::prepareToStopParsing() attemptToRunDeferredScriptsAndEnd(); } +inline bool HTMLDocumentParser::inPumpSession() const +{ + return m_pumpSessionNestingLevel > 0; +} + +inline bool HTMLDocumentParser::shouldDelayEnd() const +{ + return inPumpSession() || isWaitingForScripts() || isScheduledForResume() || isExecutingScript(); +} + bool HTMLDocumentParser::isParsingFragment() const { return m_treeBuilder->isParsingFragment(); @@ -172,7 +152,7 @@ bool HTMLDocumentParser::isParsingFragment() const bool HTMLDocumentParser::processingData() const { - return isScheduledForResume() || inPumpSession() || m_haveBackgroundParser; + return isScheduledForResume() || inPumpSession(); } void HTMLDocumentParser::pumpTokenizerIfPossible(SynchronousMode mode) @@ -199,10 +179,10 @@ void HTMLDocumentParser::resumeParsingAfterYield() { // pumpTokenizer can cause this parser to be detached from the Document, // but we need to ensure it isn't deleted yet. - Ref<HTMLDocumentParser> protect(*this); + Ref<HTMLDocumentParser> protectedThis(*this); - // We should never be here unless we can pump immediately. Call pumpTokenizer() - // directly so that ASSERTS will fire if we're wrong. + // We should never be here unless we can pump immediately. + // Call pumpTokenizer() directly so that ASSERTS will fire if we're wrong. pumpTokenizer(AllowYield); endIfDelayed(); } @@ -211,53 +191,23 @@ void HTMLDocumentParser::runScriptsForPausedTreeBuilder() { ASSERT(scriptingContentIsAllowed(parserContentPolicy())); - TextPosition scriptStartPosition = TextPosition::belowRangePosition(); - RefPtr<Element> scriptElement = m_treeBuilder->takeScriptToProcess(scriptStartPosition); - // We will not have a scriptRunner when parsing a DocumentFragment. - if (m_scriptRunner) - m_scriptRunner->execute(scriptElement.release(), scriptStartPosition); -} - -bool HTMLDocumentParser::canTakeNextToken(SynchronousMode mode, PumpSession& session) -{ - if (isStopped()) - return false; - - ASSERT(!m_haveBackgroundParser || mode == ForceSynchronous); + if (std::unique_ptr<CustomElementConstructionData> constructionData = m_treeBuilder->takeCustomElementConstructionData()) { + ASSERT(!m_treeBuilder->hasParserBlockingScriptWork()); - if (isWaitingForScripts()) { - if (mode == AllowYield) - m_parserScheduler->checkForYieldBeforeScript(session); - - // If we don't run the script, we cannot allow the next token to be taken. - if (session.needsYield) - return false; - - // If we're paused waiting for a script, we try to execute scripts before continuing. - runScriptsForPausedTreeBuilder(); - if (isWaitingForScripts() || isStopped()) - return false; + // https://html.spec.whatwg.org/#create-an-element-for-the-token + auto& elementInterface = constructionData->elementInterface.get(); + auto newElement = elementInterface.constructElementWithFallback(*document(), constructionData->name); + m_treeBuilder->didCreateCustomOrCallbackElement(WTFMove(newElement), *constructionData); + return; } - // FIXME: It's wrong for the HTMLDocumentParser to reach back to the - // Frame, but this approach is how the old parser handled - // stopping when the page assigns window.location. What really - // should happen is that assigning window.location causes the - // parser to stop parsing cleanly. The problem is we're not - // perpared to do that at every point where we run JavaScript. - if (!isParsingFragment() - && document()->frame() && document()->frame()->navigationScheduler().locationChangePending()) - return false; - - if (mode == AllowYield) - m_parserScheduler->checkForYieldBeforeToken(session); - - return true; -} - -void HTMLDocumentParser::forcePlaintextForTextDocument() -{ - m_tokenizer->setState(HTMLTokenizer::PLAINTEXTState); + TextPosition scriptStartPosition = TextPosition::belowRangePosition(); + if (auto scriptElement = m_treeBuilder->takeScriptToProcess(scriptStartPosition)) { + ASSERT(!m_treeBuilder->hasParserBlockingScriptWork()); + // We will not have a scriptRunner when parsing a DocumentFragment. + if (m_scriptRunner) + m_scriptRunner->execute(scriptElement.releaseNonNull(), scriptStartPosition); + } } Document* HTMLDocumentParser::contextForParsingSession() @@ -265,50 +215,67 @@ Document* HTMLDocumentParser::contextForParsingSession() // The parsing session should interact with the document only when parsing // non-fragments. Otherwise, we might delay the load event mistakenly. if (isParsingFragment()) - return 0; + return nullptr; return document(); } -void HTMLDocumentParser::pumpTokenizer(SynchronousMode mode) +bool HTMLDocumentParser::pumpTokenizerLoop(SynchronousMode mode, bool parsingFragment, PumpSession& session) { - ASSERT(!isStopped()); - ASSERT(!isScheduledForResume()); - // ASSERT that this object is both attached to the Document and protected. - ASSERT(refCount() >= 2); - ASSERT(m_tokenizer); - ASSERT(m_token); - ASSERT(!m_haveBackgroundParser || mode == ForceSynchronous); - - PumpSession session(m_pumpSessionNestingLevel, contextForParsingSession()); + do { + if (UNLIKELY(isWaitingForScripts())) { + if (mode == AllowYield && m_parserScheduler->shouldYieldBeforeExecutingScript(session)) + return true; + runScriptsForPausedTreeBuilder(); + // If we're paused waiting for a script, we try to execute scripts before continuing. + if (isWaitingForScripts() || isStopped()) + return false; + } - // We tell the InspectorInstrumentation about every pump, even if we - // end up pumping nothing. It can filter out empty pumps itself. - // FIXME: m_input.current().length() is only accurate if we - // end up parsing the whole buffer in this pump. We should pass how - // much we parsed as part of didWriteHTML instead of willWriteHTML. - InspectorInstrumentationCookie cookie = InspectorInstrumentation::willWriteHTML(document(), m_input.current().currentLine().zeroBasedInt()); + // FIXME: It's wrong for the HTMLDocumentParser to reach back to the Frame, but this approach is + // how the parser has always handled stopping when the page assigns window.location. What should + // happen instead is that assigning window.location causes the parser to stop parsing cleanly. + // The problem is we're not prepared to do that at every point where we run JavaScript. + if (UNLIKELY(!parsingFragment && document()->frame() && document()->frame()->navigationScheduler().locationChangePending())) + return false; - m_xssAuditor.init(document(), &m_xssAuditorDelegate); + if (UNLIKELY(mode == AllowYield && m_parserScheduler->shouldYieldBeforeToken(session))) + return true; - while (canTakeNextToken(mode, session) && !session.needsYield) { - if (!isParsingFragment()) - m_sourceTracker.start(m_input.current(), m_tokenizer.get(), token()); + if (!parsingFragment) + m_sourceTracker.startToken(m_input.current(), m_tokenizer); - if (!m_tokenizer->nextToken(m_input.current(), token())) - break; + auto token = m_tokenizer.nextToken(m_input.current()); + if (!token) + return false; - if (!isParsingFragment()) { - m_sourceTracker.end(m_input.current(), m_tokenizer.get(), token()); + if (!parsingFragment) { + m_sourceTracker.endToken(m_input.current(), m_tokenizer); // We do not XSS filter innerHTML, which means we (intentionally) fail // http/tests/security/xssAuditor/dom-write-innerHTML.html - if (auto xssInfo = m_xssAuditor.filterToken(FilterTokenRequest(token(), m_sourceTracker, m_tokenizer->shouldAllowCDATA()))) + if (auto xssInfo = m_xssAuditor.filterToken(FilterTokenRequest(*token, m_sourceTracker, m_tokenizer.shouldAllowCDATA()))) m_xssAuditorDelegate.didBlockScript(*xssInfo); } - constructTreeFromHTMLToken(token()); - ASSERT(token().isUninitialized()); - } + constructTreeFromHTMLToken(token); + } while (!isStopped()); + + return false; +} + +void HTMLDocumentParser::pumpTokenizer(SynchronousMode mode) +{ + ASSERT(!isStopped()); + ASSERT(!isScheduledForResume()); + + // This is an attempt to check that this object is both attached to the Document and protected by something. + ASSERT(refCount() >= 2); + + PumpSession session(m_pumpSessionNestingLevel, contextForParsingSession()); + + m_xssAuditor.init(document(), &m_xssAuditorDelegate); + + bool shouldResume = pumpTokenizerLoop(mode, isParsingFragment(), session); // Ensure we haven't been totally deref'ed after pumping. Any caller of this // function should be holding a RefPtr to this to ensure we weren't deleted. @@ -317,24 +284,22 @@ void HTMLDocumentParser::pumpTokenizer(SynchronousMode mode) if (isStopped()) return; - if (session.needsYield) + if (shouldResume) m_parserScheduler->scheduleForResume(); if (isWaitingForScripts()) { - ASSERT(m_tokenizer->state() == HTMLTokenizer::DataState); + ASSERT(m_tokenizer.isInDataState()); if (!m_preloadScanner) { m_preloadScanner = std::make_unique<HTMLPreloadScanner>(m_options, document()->url(), document()->deviceScaleFactor()); m_preloadScanner->appendToEnd(m_input.current()); } - m_preloadScanner->scan(m_preloader.get(), document()->baseElementURL()); + m_preloadScanner->scan(*m_preloader, *document()); } - - InspectorInstrumentation::didWriteHTML(cookie, m_input.current().currentLine().zeroBasedInt()); } -void HTMLDocumentParser::constructTreeFromHTMLToken(HTMLToken& rawToken) +void HTMLDocumentParser::constructTreeFromHTMLToken(HTMLTokenizer::TokenPtr& rawToken) { - AtomicHTMLToken token(rawToken); + AtomicHTMLToken token(*rawToken); // We clear the rawToken in case constructTreeFromAtomicToken // synchronously re-enters the parser. We don't clear the token immedately @@ -346,63 +311,59 @@ void HTMLDocumentParser::constructTreeFromHTMLToken(HTMLToken& rawToken) // FIXME: Stop clearing the rawToken once we start running the parser off // the main thread or once we stop allowing synchronous JavaScript // execution from parseAttribute. - if (rawToken.type() != HTMLToken::Character) - rawToken.clear(); - - m_treeBuilder->constructTree(&token); - - if (!rawToken.isUninitialized()) { - ASSERT(rawToken.type() == HTMLToken::Character); + if (rawToken->type() != HTMLToken::Character) { + // Clearing the TokenPtr makes sure we don't clear the HTMLToken a second time + // later when the TokenPtr is destroyed. rawToken.clear(); } + + m_treeBuilder->constructTree(WTFMove(token)); } bool HTMLDocumentParser::hasInsertionPoint() { // FIXME: The wasCreatedByScript() branch here might not be fully correct. - // Our model of the EOF character differs slightly from the one in - // the spec because our treatment is uniform between network-sourced - // and script-sourced input streams whereas the spec treats them - // differently. + // Our model of the EOF character differs slightly from the one in the spec + // because our treatment is uniform between network-sourced and script-sourced + // input streams whereas the spec treats them differently. return m_input.hasInsertionPoint() || (wasCreatedByScript() && !m_input.haveSeenEndOfFile()); } -void HTMLDocumentParser::insert(const SegmentedString& source) +void HTMLDocumentParser::insert(SegmentedString&& source) { if (isStopped()) return; // pumpTokenizer can cause this parser to be detached from the Document, // but we need to ensure it isn't deleted yet. - Ref<HTMLDocumentParser> protect(*this); + Ref<HTMLDocumentParser> protectedThis(*this); - SegmentedString excludedLineNumberSource(source); - excludedLineNumberSource.setExcludeLineNumbers(); - m_input.insertAtCurrentInsertionPoint(excludedLineNumberSource); + source.setExcludeLineNumbers(); + m_input.insertAtCurrentInsertionPoint(WTFMove(source)); pumpTokenizerIfPossible(ForceSynchronous); if (isWaitingForScripts()) { // Check the document.write() output with a separate preload scanner as // the main scanner can't deal with insertions. - if (!m_insertionPreloadScanner) { + if (!m_insertionPreloadScanner) m_insertionPreloadScanner = std::make_unique<HTMLPreloadScanner>(m_options, document()->url(), document()->deviceScaleFactor()); - } m_insertionPreloadScanner->appendToEnd(source); - m_insertionPreloadScanner->scan(m_preloader.get(), document()->baseElementURL()); + m_insertionPreloadScanner->scan(*m_preloader, *document()); } endIfDelayed(); } -void HTMLDocumentParser::append(PassRefPtr<StringImpl> inputSource) +void HTMLDocumentParser::append(RefPtr<StringImpl>&& inputSource) { if (isStopped()) return; // pumpTokenizer can cause this parser to be detached from the Document, // but we need to ensure it isn't deleted yet. - Ref<HTMLDocumentParser> protect(*this); - String source(inputSource); + Ref<HTMLDocumentParser> protectedThis(*this); + + String source { WTFMove(inputSource) }; if (m_preloadScanner) { if (m_input.current().isEmpty() && !isWaitingForScripts()) { @@ -412,7 +373,7 @@ void HTMLDocumentParser::append(PassRefPtr<StringImpl> inputSource) } else { m_preloadScanner->appendToEnd(source); if (isWaitingForScripts()) - m_preloadScanner->scan(m_preloader.get(), document()->baseElementURL()); + m_preloadScanner->scan(*m_preloader, *document()); } } @@ -442,9 +403,7 @@ void HTMLDocumentParser::end() void HTMLDocumentParser::attemptToRunDeferredScriptsAndEnd() { ASSERT(isStopping()); - // FIXME: It may not be correct to disable this for the background parser. - // That means hasInsertionPoint() may not be correct in some cases. - ASSERT(!hasInsertionPoint() || m_haveBackgroundParser); + ASSERT(!hasInsertionPoint()); if (m_scriptRunner && !m_scriptRunner->executeScriptsWaitingForParsing()) return; end(); @@ -492,18 +451,18 @@ void HTMLDocumentParser::finish() bool HTMLDocumentParser::isExecutingScript() const { - if (!m_scriptRunner) - return false; - return m_scriptRunner->isExecutingScript(); + return m_scriptRunner && m_scriptRunner->isExecutingScript(); } TextPosition HTMLDocumentParser::textPosition() const { - const SegmentedString& currentString = m_input.current(); - OrdinalNumber line = currentString.currentLine(); - OrdinalNumber column = currentString.currentColumn(); + auto& currentString = m_input.current(); + return TextPosition(currentString.currentLine(), currentString.currentColumn()); +} - return TextPosition(line, column); +bool HTMLDocumentParser::shouldAssociateConsoleMessagesWithTextPosition() const +{ + return inPumpSession() && !isExecutingScript(); } bool HTMLDocumentParser::isWaitingForScripts() const @@ -513,7 +472,7 @@ bool HTMLDocumentParser::isWaitingForScripts() const // The script runner will hold the script until its loaded and run. During // any of this time, we want to count ourselves as "waiting for a script" and thus // run the preload scanner, as well as delay completion of parsing. - bool treeBuilderHasBlockingScript = m_treeBuilder->hasParserBlockingScript(); + bool treeBuilderHasBlockingScript = m_treeBuilder->hasParserBlockingScriptWork(); bool scriptRunnerHasBlockingScript = m_scriptRunner && m_scriptRunner->hasParserBlockingScript(); // Since the parser is paused while a script runner has a blocking script, it should // never be possible to end up with both objects holding a blocking script. @@ -527,37 +486,45 @@ void HTMLDocumentParser::resumeParsingAfterScriptExecution() ASSERT(!isExecutingScript()); ASSERT(!isWaitingForScripts()); + // pumpTokenizer can cause this parser to be detached from the Document, + // but we need to ensure it isn't deleted yet. + Ref<HTMLDocumentParser> protectedThis(*this); + m_insertionPreloadScanner = nullptr; pumpTokenizerIfPossible(AllowYield); endIfDelayed(); } -void HTMLDocumentParser::watchForLoad(CachedResource* cachedScript) +void HTMLDocumentParser::watchForLoad(PendingScript& pendingScript) { - ASSERT(!cachedScript->isLoaded()); - // addClient would call notifyFinished if the load were complete. + ASSERT(!pendingScript.isLoaded()); + // setClient would call notifyFinished if the load were complete. // Callers do not expect to be re-entered from this call, so they should - // not an already-loaded CachedResource. - cachedScript->addClient(this); + // not an already-loaded PendingScript. + pendingScript.setClient(*this); } -void HTMLDocumentParser::stopWatchingForLoad(CachedResource* cachedScript) +void HTMLDocumentParser::stopWatchingForLoad(PendingScript& pendingScript) { - cachedScript->removeClient(this); + pendingScript.clearClient(); } - + void HTMLDocumentParser::appendCurrentInputStreamToPreloadScannerAndScan() { ASSERT(m_preloadScanner); m_preloadScanner->appendToEnd(m_input.current()); - m_preloadScanner->scan(m_preloader.get(), document()->baseElementURL()); + m_preloadScanner->scan(*m_preloader, *document()); } -void HTMLDocumentParser::notifyFinished(CachedResource* cachedResource) +void HTMLDocumentParser::notifyFinished(PendingScript& pendingScript) { // pumpTokenizer can cause this parser to be detached from the Document, // but we need to ensure it isn't deleted yet. - Ref<HTMLDocumentParser> protect(*this); + Ref<HTMLDocumentParser> protectedThis(*this); + + // After Document parser is stopped or detached, the parser-inserted deferred script execution should be ignored. + if (isStopped()) + return; ASSERT(m_scriptRunner); ASSERT(!isExecutingScript()); @@ -566,11 +533,16 @@ void HTMLDocumentParser::notifyFinished(CachedResource* cachedResource) return; } - m_scriptRunner->executeScriptsWaitingForLoad(cachedResource); + m_scriptRunner->executeScriptsWaitingForLoad(pendingScript); if (!isWaitingForScripts()) resumeParsingAfterScriptExecution(); } +bool HTMLDocumentParser::hasScriptsWaitingForStylesheets() const +{ + return m_scriptRunner && m_scriptRunner->hasScriptsWaitingForStylesheets(); +} + void HTMLDocumentParser::executeScriptsWaitingForStylesheets() { // Document only calls this when the Document owns the DocumentParser @@ -584,19 +556,19 @@ void HTMLDocumentParser::executeScriptsWaitingForStylesheets() // pumpTokenizer can cause this parser to be detached from the Document, // but we need to ensure it isn't deleted yet. - Ref<HTMLDocumentParser> protect(*this); + Ref<HTMLDocumentParser> protectedThis(*this); m_scriptRunner->executeScriptsWaitingForStylesheets(); if (!isWaitingForScripts()) resumeParsingAfterScriptExecution(); } -void HTMLDocumentParser::parseDocumentFragment(const String& source, DocumentFragment& fragment, Element* contextElement, ParserContentPolicy parserContentPolicy) +void HTMLDocumentParser::parseDocumentFragment(const String& source, DocumentFragment& fragment, Element& contextElement, ParserContentPolicy parserContentPolicy) { - RefPtr<HTMLDocumentParser> parser = HTMLDocumentParser::create(fragment, contextElement, parserContentPolicy); + auto parser = create(fragment, contextElement, parserContentPolicy); parser->insert(source); // Use insert() so that the parser will not yield. parser->finish(); - ASSERT(!parser->processingData()); // Make sure we're done. <rdar://problem/3963151> - parser->detach(); // Allows ~DocumentParser to assert it was detached before destruction. + ASSERT(!parser->processingData()); + parser->detach(); } void HTMLDocumentParser::suspendScheduledTasks() |