blob: fd9dc6b0af93b06af321a14edadf65b9338a2079 [file] [log] [blame]
/*
* Copyright (C) 2010 Google, Inc. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "core/html/parser/HTMLParserScriptRunner.h"
#include <inttypes.h>
#include <memory>
#include "bindings/core/v8/Microtask.h"
#include "bindings/core/v8/ScriptSourceCode.h"
#include "bindings/core/v8/V8Binding.h"
#include "bindings/core/v8/V8PerIsolateData.h"
#include "core/dom/DocumentParserTiming.h"
#include "core/dom/Element.h"
#include "core/dom/IgnoreDestructiveWriteCountIncrementer.h"
#include "core/dom/ScriptLoader.h"
#include "core/dom/TaskRunnerHelper.h"
#include "core/events/Event.h"
#include "core/frame/LocalFrame.h"
#include "core/html/parser/HTMLInputStream.h"
#include "core/html/parser/HTMLParserScriptRunnerHost.h"
#include "core/html/parser/NestingLevelIncrementer.h"
#include "core/inspector/ConsoleMessage.h"
#include "core/loader/resource/ScriptResource.h"
#include "platform/Histogram.h"
#include "platform/WebFrameScheduler.h"
#include "platform/instrumentation/tracing/TraceEvent.h"
#include "platform/instrumentation/tracing/TracedValue.h"
#include "platform/loader/fetch/MemoryCache.h"
#include "public/platform/Platform.h"
namespace blink {
namespace {
// TODO(bmcquade): move this to a shared location if we find ourselves wanting
// to trace similar data elsewhere in the codebase.
std::unique_ptr<TracedValue> getTraceArgsForScriptElement(
ScriptElementBase* element,
const TextPosition& textPosition) {
std::unique_ptr<TracedValue> value = TracedValue::create();
ScriptLoader* scriptLoader = element->loader();
if (scriptLoader && scriptLoader->resource())
value->setString("url", scriptLoader->resource()->url().getString());
if (element->document().frame()) {
value->setString(
"frame",
String::format("0x%" PRIx64,
static_cast<uint64_t>(reinterpret_cast<intptr_t>(
element->document().frame()))));
}
if (textPosition.m_line.zeroBasedInt() > 0 ||
textPosition.m_column.zeroBasedInt() > 0) {
value->setInteger("lineNumber", textPosition.m_line.oneBasedInt());
value->setInteger("columnNumber", textPosition.m_column.oneBasedInt());
}
return value;
}
bool doExecuteScript(ScriptElementBase* element,
const ScriptSourceCode& sourceCode,
const TextPosition& textPosition) {
ScriptLoader* scriptLoader = element->loader();
DCHECK(scriptLoader);
TRACE_EVENT_WITH_FLOW1("blink", "HTMLParserScriptRunner ExecuteScript",
element, TRACE_EVENT_FLAG_FLOW_IN, "data",
getTraceArgsForScriptElement(element, textPosition));
return scriptLoader->executeScript(sourceCode);
}
void traceParserBlockingScript(const PendingScript* pendingScript,
bool waitingForResources) {
// The HTML parser must yield before executing script in the following
// cases:
// * the script's execution is blocked on the completed load of the script
// resource
// (https://html.spec.whatwg.org/multipage/scripting.html#pending-parsing-blocking-script)
// * the script's execution is blocked on the load of a style sheet or other
// resources that are blocking scripts
// (https://html.spec.whatwg.org/multipage/semantics.html#a-style-sheet-that-is-blocking-scripts)
//
// Both of these cases can introduce significant latency when loading a
// web page, especially for users on slow connections, since the HTML parser
// must yield until the blocking resources finish loading.
//
// We trace these parser yields here using flow events, so we can track
// both when these yields occur, as well as how long the parser had
// to yield. The connecting flow events are traced once the parser becomes
// unblocked when the script actually executes, in doExecuteScript.
ScriptElementBase* element = pendingScript->element();
if (!element)
return;
TextPosition scriptStartPosition = pendingScript->startingPosition();
if (!pendingScript->isReady()) {
if (waitingForResources) {
TRACE_EVENT_WITH_FLOW1(
"blink", "YieldParserForScriptLoadAndBlockingResources", element,
TRACE_EVENT_FLAG_FLOW_OUT, "data",
getTraceArgsForScriptElement(element, scriptStartPosition));
} else {
TRACE_EVENT_WITH_FLOW1(
"blink", "YieldParserForScriptLoad", element,
TRACE_EVENT_FLAG_FLOW_OUT, "data",
getTraceArgsForScriptElement(element, scriptStartPosition));
}
} else if (waitingForResources) {
TRACE_EVENT_WITH_FLOW1(
"blink", "YieldParserForScriptBlockingResources", element,
TRACE_EVENT_FLAG_FLOW_OUT, "data",
getTraceArgsForScriptElement(element, scriptStartPosition));
}
}
static KURL documentURLForScriptExecution(Document* document) {
if (!document)
return KURL();
if (!document->frame()) {
if (document->importsController())
return document->url();
return KURL();
}
// Use the URL of the currently active document for this frame.
return document->frame()->document()->url();
}
} // namespace
using namespace HTMLNames;
HTMLParserScriptRunner::HTMLParserScriptRunner(
HTMLParserReentryPermit* reentryPermit,
Document* document,
HTMLParserScriptRunnerHost* host)
: m_reentryPermit(reentryPermit),
m_document(document),
m_host(host),
m_parserBlockingScript(nullptr) {
DCHECK(m_host);
}
HTMLParserScriptRunner::~HTMLParserScriptRunner() {
// Verify that detach() has been called.
DCHECK(!m_document);
}
void HTMLParserScriptRunner::detach() {
if (!m_document)
return;
if (m_parserBlockingScript)
m_parserBlockingScript->dispose();
m_parserBlockingScript = nullptr;
while (!m_scriptsToExecuteAfterParsing.isEmpty()) {
PendingScript* pendingScript = m_scriptsToExecuteAfterParsing.takeFirst();
pendingScript->dispose();
}
m_document = nullptr;
// m_reentryPermit is not cleared here, because the script runner
// may continue to run pending scripts after the parser has
// detached.
}
bool HTMLParserScriptRunner::isParserBlockingScriptReady() {
DCHECK(parserBlockingScript());
if (!m_document->isScriptExecutionReady())
return false;
return parserBlockingScript()->isReady();
}
// This has two callers and corresponds to different concepts in the spec:
// - When called from executeParsingBlockingScripts(), this corresponds to some
// steps of the "Otherwise" Clause of 'An end tag whose tag name is "script"'
// https://html.spec.whatwg.org/#scriptEndTag
// - When called from executeScriptsWaitingForParsing(), this corresponds
// https://html.spec.whatwg.org/#execute-the-script-block
// and thus currently this function does more than specced.
// TODO(hiroshige): Make the spec and implementation consistent.
void HTMLParserScriptRunner::executePendingScriptAndDispatchEvent(
PendingScript* pendingScript,
ScriptStreamer::Type pendingScriptType) {
bool errorOccurred = false;
ScriptSourceCode sourceCode = pendingScript->getSource(
documentURLForScriptExecution(m_document), errorOccurred);
// Stop watching loads before executeScript to prevent recursion if the script
// reloads itself.
// TODO(kouhei): Consider merging this w/ pendingScript->dispose() after the
// if block.
pendingScript->stopWatchingForLoad();
if (!isExecutingScript()) {
Microtask::performCheckpoint(V8PerIsolateData::mainThreadIsolate());
if (pendingScriptType == ScriptStreamer::ParsingBlocking) {
// The parser cannot be unblocked as a microtask requested another
// resource
if (!m_document->isScriptExecutionReady())
return;
}
}
TextPosition scriptStartPosition = pendingScript->startingPosition();
double scriptParserBlockingTime =
pendingScript->parserBlockingLoadStartTime();
ScriptElementBase* element = pendingScript->element();
// 1. "Let the script be the pending parsing-blocking script.
// There is no longer a pending parsing-blocking script."
// Clear the pending script before possible re-entrancy from executeScript()
pendingScript->dispose();
pendingScript = nullptr;
if (pendingScriptType == ScriptStreamer::ParsingBlocking) {
m_parserBlockingScript = nullptr;
}
if (ScriptLoader* scriptLoader = element->loader()) {
// 7. "Increment the parser's script nesting level by one (it should be
// zero before this step, so this sets it to one)."
HTMLParserReentryPermit::ScriptNestingLevelIncrementer
nestingLevelIncrementer =
m_reentryPermit->incrementScriptNestingLevel();
IgnoreDestructiveWriteCountIncrementer
ignoreDestructiveWriteCountIncrementer(m_document);
// 8. "Execute the script."
if (errorOccurred) {
TRACE_EVENT_WITH_FLOW1(
"blink", "HTMLParserScriptRunner ExecuteScriptFailed", element,
TRACE_EVENT_FLAG_FLOW_IN, "data",
getTraceArgsForScriptElement(element, scriptStartPosition));
scriptLoader->dispatchErrorEvent();
} else {
DCHECK(isExecutingScript());
if (scriptParserBlockingTime > 0.0) {
DocumentParserTiming::from(*m_document)
.recordParserBlockedOnScriptLoadDuration(
monotonicallyIncreasingTime() - scriptParserBlockingTime,
scriptLoader->wasCreatedDuringDocumentWrite());
}
if (!doExecuteScript(element, sourceCode, scriptStartPosition)) {
scriptLoader->dispatchErrorEvent();
} else {
element->dispatchLoadEvent();
}
}
// 9. "Decrement the parser's script nesting level by one.
// If the parser's script nesting level is zero
// (which it always should be at this point),
// then set the parser pause flag to false."
// This is implemented by ~ScriptNestingLevelIncrementer().
}
DCHECK(!isExecutingScript());
}
void fetchBlockedDocWriteScript(ScriptElementBase* element,
bool isParserInserted,
const TextPosition& scriptStartPosition) {
DCHECK(element);
ScriptLoader* scriptLoader =
ScriptLoader::create(element, isParserInserted, false, false);
DCHECK(scriptLoader);
scriptLoader->setFetchDocWrittenScriptDeferIdle();
scriptLoader->prepareScript(scriptStartPosition);
}
void emitWarningForDocWriteScripts(const String& url, Document& document) {
String message =
"The Parser-blocking, cross site (i.e. different eTLD+1) "
"script, " +
url +
", invoked via document.write was NOT BLOCKED on this page load, but MAY "
"be blocked by the browser in future page loads with poor network "
"connectivity.";
document.addConsoleMessage(
ConsoleMessage::create(JSMessageSource, WarningMessageLevel, message));
WTFLogAlways("%s", message.utf8().data());
}
void emitErrorForDocWriteScripts(const String& url, Document& document) {
String message =
"Network request for the parser-blocking, cross site "
"(i.e. different eTLD+1) script, " +
url +
", invoked via document.write was BLOCKED by the browser due to poor "
"network connectivity. ";
document.addConsoleMessage(
ConsoleMessage::create(JSMessageSource, ErrorMessageLevel, message));
WTFLogAlways("%s", message.utf8().data());
}
void HTMLParserScriptRunner::possiblyFetchBlockedDocWriteScript(
PendingScript* pendingScript) {
// If the script was blocked as part of document.write intervention,
// then send an asynchronous GET request with an interventions header.
if (!parserBlockingScript())
return;
if (parserBlockingScript() != pendingScript)
return;
ScriptElementBase* element = parserBlockingScript()->element();
ScriptLoader* scriptLoader = element->loader();
if (!scriptLoader || !scriptLoader->disallowedFetchForDocWrittenScript())
return;
if (!pendingScript->errorOccurred()) {
emitWarningForDocWriteScripts(pendingScript->resource()->url().getString(),
*m_document);
return;
}
// Due to dependency violation, not able to check the exact error to be
// ERR_CACHE_MISS but other errors are rare with
// WebCachePolicy::ReturnCacheDataDontLoad.
emitErrorForDocWriteScripts(pendingScript->resource()->url().getString(),
*m_document);
TextPosition startingPosition = parserBlockingScript()->startingPosition();
bool isParserInserted = scriptLoader->isParserInserted();
// Remove this resource entry from memory cache as the new request
// should not join onto this existing entry.
memoryCache()->remove(pendingScript->resource());
fetchBlockedDocWriteScript(element, isParserInserted, startingPosition);
}
void HTMLParserScriptRunner::pendingScriptFinished(
PendingScript* pendingScript) {
// Handle cancellations of parser-blocking script loads without
// notifying the host (i.e., parser) if these were initiated by nested
// document.write()s. The cancellation may have been triggered by
// script execution to signal an abrupt stop (e.g., window.close().)
//
// The parser is unprepared to be told, and doesn't need to be.
if (isExecutingScript() && pendingScript->resource()->wasCanceled()) {
pendingScript->dispose();
if (pendingScript == parserBlockingScript()) {
m_parserBlockingScript = nullptr;
} else {
CHECK_EQ(pendingScript, m_scriptsToExecuteAfterParsing.front());
// TODO(hiroshige): Remove this CHECK() before going to beta.
// This is only to make clusterfuzz to find a test case that executes
// this code path.
CHECK(false);
m_scriptsToExecuteAfterParsing.pop_front();
// TODO(hiroshige): executeScriptsWaitingForParsing() should be
// called later at the appropriate time. https://crbug.com/696775
}
return;
}
// If the script was blocked as part of document.write intervention,
// then send an asynchronous GET request with an interventions header.
possiblyFetchBlockedDocWriteScript(pendingScript);
m_host->notifyScriptLoaded(pendingScript);
}
// 'An end tag whose tag name is "script"'
// https://html.spec.whatwg.org/#scriptEndTag
//
// Script handling lives outside the tree builder to keep each class simple.
void HTMLParserScriptRunner::processScriptElement(
Element* scriptElement,
const TextPosition& scriptStartPosition) {
DCHECK(scriptElement);
// FIXME: If scripting is disabled, always just return.
bool hadPreloadScanner = m_host->hasPreloadScanner();
// Initial steps of 'An end tag whose tag name is "script"'.
// Try to execute the script given to us.
processScriptElementInternal(scriptElement, scriptStartPosition);
// "At this stage, if there is a pending parsing-blocking script, then:"
if (hasParserBlockingScript()) {
// - "If the script nesting level is not zero:"
if (isExecutingScript()) {
// "Set the parser pause flag to true, and abort the processing of any
// nested invocations of the tokenizer, yielding control back to the
// caller. (Tokenization will resume when the caller returns to the
// "outer" tree construction stage.)"
// TODO(hiroshige): set the parser pause flag to true here.
// Unwind to the outermost HTMLParserScriptRunner::processScriptElement
// before continuing parsing.
return;
}
// - "Otherwise":
traceParserBlockingScript(parserBlockingScript(),
!m_document->isScriptExecutionReady());
m_parserBlockingScript->markParserBlockingLoadStartTime();
// If preload scanner got created, it is missing the source after the
// current insertion point. Append it and scan.
if (!hadPreloadScanner && m_host->hasPreloadScanner())
m_host->appendCurrentInputStreamToPreloadScannerAndScan();
executeParsingBlockingScripts();
}
}
bool HTMLParserScriptRunner::hasParserBlockingScript() const {
return parserBlockingScript();
}
// The "Otherwise" Clause of 'An end tag whose tag name is "script"'
// https://html.spec.whatwg.org/#scriptEndTag
void HTMLParserScriptRunner::executeParsingBlockingScripts() {
// 3. "If (1) the parser's Document has a style sheet that is blocking scripts
// or (2) the script's "ready to be parser-executed" flag is not set:
// spin the event loop
// until the parser's Document has no style sheet that is blocking scripts
// and the script's "ready to be parser-executed" flag is set."
//
// These conditions correspond to isParserBlockingScriptReady() and
// if it is false, executeParsingBlockingScripts() will be called later
// when isParserBlockingScriptReady() becomes true:
// (1) from HTMLParserScriptRunner::executeScriptsWaitingForResources(), or
// (2) from HTMLParserScriptRunner::executeScriptsWaitingForLoad().
while (hasParserBlockingScript() && isParserBlockingScriptReady()) {
DCHECK(m_document);
DCHECK(!isExecutingScript());
DCHECK(m_document->isScriptExecutionReady());
// 6. "Let the insertion point be just before the next input character."
InsertionPointRecord insertionPointRecord(m_host->inputStream());
// 1., 7.--9.
executePendingScriptAndDispatchEvent(m_parserBlockingScript,
ScriptStreamer::ParsingBlocking);
// 10. "Let the insertion point be undefined again."
// Implemented as ~InsertionPointRecord().
// 11. "If there is once again a pending parsing-blocking script, then
// repeat these steps from step 1."
}
}
void HTMLParserScriptRunner::executeScriptsWaitingForLoad(
PendingScript* pendingScript) {
TRACE_EVENT0("blink", "HTMLParserScriptRunner::executeScriptsWaitingForLoad");
DCHECK(!isExecutingScript());
DCHECK(hasParserBlockingScript());
DCHECK_EQ(pendingScript, parserBlockingScript());
DCHECK(parserBlockingScript()->isReady());
executeParsingBlockingScripts();
}
void HTMLParserScriptRunner::executeScriptsWaitingForResources() {
TRACE_EVENT0("blink",
"HTMLParserScriptRunner::executeScriptsWaitingForResources");
DCHECK(m_document);
DCHECK(!isExecutingScript());
DCHECK(m_document->isScriptExecutionReady());
executeParsingBlockingScripts();
}
// Step 3 of https://html.spec.whatwg.org/#the-end:
// "If the list of scripts that will execute when the document has
// finished parsing is not empty, run these substeps:"
bool HTMLParserScriptRunner::executeScriptsWaitingForParsing() {
TRACE_EVENT0("blink",
"HTMLParserScriptRunner::executeScriptsWaitingForParsing");
while (!m_scriptsToExecuteAfterParsing.isEmpty()) {
DCHECK(!isExecutingScript());
DCHECK(!hasParserBlockingScript());
DCHECK(m_scriptsToExecuteAfterParsing.front()->resource());
// 1. "Spin the event loop until the first script in the list of scripts
// that will execute when the document has finished parsing
// has its "ready to be parser-executed" flag set and
// the parser's Document has no style sheet that is blocking scripts."
// TODO(hiroshige): Is the latter part checked anywhere?
if (!m_scriptsToExecuteAfterParsing.front()->isReady()) {
m_scriptsToExecuteAfterParsing.front()->watchForLoad(this);
traceParserBlockingScript(m_scriptsToExecuteAfterParsing.front().get(),
!m_document->isScriptExecutionReady());
m_scriptsToExecuteAfterParsing.front()->markParserBlockingLoadStartTime();
return false;
}
// 3. "Remove the first script element from the list of scripts that will
// execute when the document has finished parsing (i.e. shift out the
// first entry in the list)."
PendingScript* first = m_scriptsToExecuteAfterParsing.takeFirst();
// 2. "Execute the first script in the list of scripts that will execute
// when the document has finished parsing."
executePendingScriptAndDispatchEvent(first, ScriptStreamer::Deferred);
// FIXME: What is this m_document check for?
if (!m_document)
return false;
// 4. "If the list of scripts that will execute when the document has
// finished parsing is still not empty, repeat these substeps again
// from substep 1."
}
return true;
}
// 2nd Clause, Step 23 of https://html.spec.whatwg.org/#prepare-a-script
void HTMLParserScriptRunner::requestParsingBlockingScript(Element* element) {
// "The element is the pending parsing-blocking script of the Document of
// the parser that created the element.
// (There can only be one such script per Document at a time.)"
CHECK(!parserBlockingScript());
m_parserBlockingScript = requestPendingScript(element);
if (!parserBlockingScript())
return;
DCHECK(parserBlockingScript()->resource());
// We only care about a load callback if resource is not already in the cache.
// Callers will attempt to run the m_parserBlockingScript if possible before
// returning control to the parser.
if (!parserBlockingScript()->isReady()) {
m_parserBlockingScript->startStreamingIfPossible(
m_document, ScriptStreamer::ParsingBlocking);
m_parserBlockingScript->watchForLoad(this);
}
}
// 1st Clause, Step 23 of https://html.spec.whatwg.org/#prepare-a-script
void HTMLParserScriptRunner::requestDeferredScript(Element* element) {
PendingScript* pendingScript = requestPendingScript(element);
if (!pendingScript)
return;
if (!pendingScript->isReady()) {
pendingScript->startStreamingIfPossible(m_document,
ScriptStreamer::Deferred);
}
DCHECK(pendingScript->resource());
// "Add the element to the end of the list of scripts that will execute
// when the document has finished parsing associated with the Document
// of the parser that created the element."
m_scriptsToExecuteAfterParsing.push_back(pendingScript);
}
PendingScript* HTMLParserScriptRunner::requestPendingScript(
Element* element) const {
ScriptElementBase* scriptElement =
ScriptElementBase::fromElementIfPossible(element);
ScriptResource* resource = scriptElement->loader()->resource();
// Here |resource| should be non-null. If it were nullptr,
// ScriptLoader::fetchScript() should have returned false and
// thus the control shouldn't have reached here.
CHECK(resource);
return PendingScript::create(scriptElement, resource);
}
// The initial steps for 'An end tag whose tag name is "script"'
// https://html.spec.whatwg.org/#scriptEndTag
void HTMLParserScriptRunner::processScriptElementInternal(
Element* script,
const TextPosition& scriptStartPosition) {
DCHECK(m_document);
DCHECK(!hasParserBlockingScript());
{
ScriptElementBase* element =
ScriptElementBase::fromElementIfPossible(script);
DCHECK(element);
ScriptLoader* scriptLoader = element->loader();
DCHECK(scriptLoader);
// FIXME: Align trace event name and function name.
TRACE_EVENT1("blink", "HTMLParserScriptRunner::execute", "data",
getTraceArgsForScriptElement(element, scriptStartPosition));
DCHECK(scriptLoader->isParserInserted());
if (!isExecutingScript())
Microtask::performCheckpoint(V8PerIsolateData::mainThreadIsolate());
// "Let the old insertion point have the same value as the current
// insertion point.
// Let the insertion point be just before the next input character."
InsertionPointRecord insertionPointRecord(m_host->inputStream());
// "Increment the parser's script nesting level by one."
HTMLParserReentryPermit::ScriptNestingLevelIncrementer
nestingLevelIncrementer =
m_reentryPermit->incrementScriptNestingLevel();
// "Prepare the script. This might cause some script to execute, which
// might cause new characters to be inserted into the tokenizer, and
// might cause the tokenizer to output more tokens, resulting in a
// reentrant invocation of the parser."
scriptLoader->prepareScript(scriptStartPosition);
// A part of Step 23 of https://html.spec.whatwg.org/#prepare-a-script:
if (!scriptLoader->willBeParserExecuted())
return;
if (scriptLoader->willExecuteWhenDocumentFinishedParsing()) {
// 1st Clause of Step 23.
requestDeferredScript(script);
} else if (scriptLoader->readyToBeParserExecuted()) {
// 5th Clause of Step 23.
// "If ... it's an HTML parser
// whose script nesting level is not greater than one"
if (m_reentryPermit->scriptNestingLevel() == 1u) {
// "The element is the pending parsing-blocking script of the
// Document of the parser that created the element.
// (There can only be one such script per Document at a time.)"
CHECK(!m_parserBlockingScript);
m_parserBlockingScript =
PendingScript::create(element, scriptStartPosition);
} else {
// 6th Clause of Step 23.
// "Immediately execute the script block,
// even if other scripts are already executing."
// TODO(hiroshige): Merge the block into ScriptLoader::prepareScript().
DCHECK_GT(m_reentryPermit->scriptNestingLevel(), 1u);
if (m_parserBlockingScript)
m_parserBlockingScript->dispose();
m_parserBlockingScript = nullptr;
ScriptSourceCode sourceCode(script->textContent(),
documentURLForScriptExecution(m_document),
scriptStartPosition);
doExecuteScript(element, sourceCode, scriptStartPosition);
}
} else {
// 2nd Clause of Step 23.
requestParsingBlockingScript(script);
}
// "Decrement the parser's script nesting level by one.
// If the parser's script nesting level is zero, then set the parser
// pause flag to false."
// Implemented by ~ScriptNestingLevelIncrementer().
// "Let the insertion point have the value of the old insertion point."
// Implemented by ~InsertionPointRecord().
}
}
DEFINE_TRACE(HTMLParserScriptRunner) {
visitor->trace(m_document);
visitor->trace(m_host);
visitor->trace(m_parserBlockingScript);
visitor->trace(m_scriptsToExecuteAfterParsing);
PendingScriptClient::trace(visitor);
}
} // namespace blink