blob: 1b94d33abc3e4bea927f444a3324cf0fb77a475e [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/ClassicPendingScript.h"
#include "core/dom/ClassicScript.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 "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& text_position) {
std::unique_ptr<TracedValue> value = TracedValue::Create();
ScriptLoader* script_loader = element->Loader();
if (script_loader && script_loader->GetResource())
value->SetString("url", script_loader->GetResource()->Url().GetString());
if (element->GetDocument().GetFrame()) {
value->SetString(
"frame",
String::Format("0x%" PRIx64,
static_cast<uint64_t>(reinterpret_cast<intptr_t>(
element->GetDocument().GetFrame()))));
}
if (text_position.line_.ZeroBasedInt() > 0 ||
text_position.column_.ZeroBasedInt() > 0) {
value->SetInteger("lineNumber", text_position.line_.OneBasedInt());
value->SetInteger("columnNumber", text_position.column_.OneBasedInt());
}
return value;
}
bool DoExecuteScript(ScriptElementBase* element,
const Script* script,
const TextPosition& text_position) {
ScriptLoader* script_loader = element->Loader();
DCHECK(script_loader);
TRACE_EVENT_WITH_FLOW1("blink", "HTMLParserScriptRunner ExecuteScript",
element, TRACE_EVENT_FLAG_FLOW_IN, "data",
GetTraceArgsForScriptElement(element, text_position));
return script_loader->ExecuteScript(script);
}
void TraceParserBlockingScript(const PendingScript* pending_script,
bool waiting_for_resources) {
// 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 = pending_script->GetElement();
if (!element)
return;
TextPosition script_start_position = pending_script->StartingPosition();
if (!pending_script->IsReady()) {
if (waiting_for_resources) {
TRACE_EVENT_WITH_FLOW1(
"blink", "YieldParserForScriptLoadAndBlockingResources", element,
TRACE_EVENT_FLAG_FLOW_OUT, "data",
GetTraceArgsForScriptElement(element, script_start_position));
} else {
TRACE_EVENT_WITH_FLOW1(
"blink", "YieldParserForScriptLoad", element,
TRACE_EVENT_FLAG_FLOW_OUT, "data",
GetTraceArgsForScriptElement(element, script_start_position));
}
} else if (waiting_for_resources) {
TRACE_EVENT_WITH_FLOW1(
"blink", "YieldParserForScriptBlockingResources", element,
TRACE_EVENT_FLAG_FLOW_OUT, "data",
GetTraceArgsForScriptElement(element, script_start_position));
}
}
static KURL DocumentURLForScriptExecution(Document* document) {
if (!document)
return KURL();
if (!document->GetFrame()) {
if (document->ImportsController())
return document->Url();
return KURL();
}
// Use the URL of the currently active document for this frame.
return document->GetFrame()->GetDocument()->Url();
}
} // namespace
using namespace HTMLNames;
HTMLParserScriptRunner::HTMLParserScriptRunner(
HTMLParserReentryPermit* reentry_permit,
Document* document,
HTMLParserScriptRunnerHost* host)
: reentry_permit_(reentry_permit),
document_(document),
host_(host),
parser_blocking_script_(nullptr) {
DCHECK(host_);
}
HTMLParserScriptRunner::~HTMLParserScriptRunner() {
// Verify that detach() has been called.
DCHECK(!document_);
}
void HTMLParserScriptRunner::Detach() {
if (!document_)
return;
if (parser_blocking_script_)
parser_blocking_script_->Dispose();
parser_blocking_script_ = nullptr;
while (!scripts_to_execute_after_parsing_.IsEmpty()) {
PendingScript* pending_script =
scripts_to_execute_after_parsing_.TakeFirst();
pending_script->Dispose();
}
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 (!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* pending_script,
ScriptStreamer::Type pending_script_type) {
bool error_occurred = false;
Script* script = pending_script->GetSource(
DocumentURLForScriptExecution(document_), error_occurred);
// 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.
pending_script->StopWatchingForLoad();
if (!IsExecutingScript()) {
Microtask::PerformCheckpoint(V8PerIsolateData::MainThreadIsolate());
if (pending_script_type == ScriptStreamer::kParsingBlocking) {
// The parser cannot be unblocked as a microtask requested another
// resource
if (!document_->IsScriptExecutionReady())
return;
}
}
TextPosition script_start_position = pending_script->StartingPosition();
double script_parser_blocking_time =
pending_script->ParserBlockingLoadStartTime();
ScriptElementBase* element = pending_script->GetElement();
// 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()
pending_script->Dispose();
pending_script = nullptr;
if (pending_script_type == ScriptStreamer::kParsingBlocking) {
parser_blocking_script_ = nullptr;
}
if (ScriptLoader* script_loader = 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
nesting_level_incrementer =
reentry_permit_->IncrementScriptNestingLevel();
IgnoreDestructiveWriteCountIncrementer
ignore_destructive_write_count_incrementer(document_);
// 8. "Execute the script."
if (error_occurred) {
TRACE_EVENT_WITH_FLOW1(
"blink", "HTMLParserScriptRunner ExecuteScriptFailed", element,
TRACE_EVENT_FLAG_FLOW_IN, "data",
GetTraceArgsForScriptElement(element, script_start_position));
script_loader->DispatchErrorEvent();
} else {
DCHECK(IsExecutingScript());
if (script_parser_blocking_time > 0.0) {
DocumentParserTiming::From(*document_)
.RecordParserBlockedOnScriptLoadDuration(
MonotonicallyIncreasingTime() - script_parser_blocking_time,
script_loader->WasCreatedDuringDocumentWrite());
}
if (!DoExecuteScript(element, script, script_start_position)) {
script_loader->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 is_parser_inserted,
const TextPosition& script_start_position) {
DCHECK(element);
ScriptLoader* script_loader =
ScriptLoader::Create(element, is_parser_inserted, false, false);
DCHECK(script_loader);
script_loader->SetFetchDocWrittenScriptDeferIdle();
script_loader->PrepareScript(script_start_position);
}
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(kJSMessageSource, kWarningMessageLevel, 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(kJSMessageSource, kErrorMessageLevel, message));
WTFLogAlways("%s", message.Utf8().Data());
}
void HTMLParserScriptRunner::PossiblyFetchBlockedDocWriteScript(
PendingScript* pending_script) {
// 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() != pending_script)
return;
ScriptElementBase* element = ParserBlockingScript()->GetElement();
ScriptLoader* script_loader = element->Loader();
if (!script_loader || !script_loader->DisallowedFetchForDocWrittenScript())
return;
// We don't allow document.write() and its intervention with module scripts.
CHECK_EQ(pending_script->GetScriptType(), ScriptType::kClassic);
if (!pending_script->ErrorOccurred()) {
EmitWarningForDocWriteScripts(pending_script->Url().GetString(),
*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(pending_script->Url().GetString(), *document_);
TextPosition starting_position = ParserBlockingScript()->StartingPosition();
bool is_parser_inserted = script_loader->IsParserInserted();
// Remove this resource entry from memory cache as the new request
// should not join onto this existing entry.
pending_script->RemoveFromMemoryCache();
FetchBlockedDocWriteScript(element, is_parser_inserted, starting_position);
}
void HTMLParserScriptRunner::PendingScriptFinished(
PendingScript* pending_script) {
// 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() && pending_script->WasCanceled()) {
pending_script->Dispose();
if (pending_script == ParserBlockingScript()) {
parser_blocking_script_ = nullptr;
} else {
CHECK_EQ(pending_script, scripts_to_execute_after_parsing_.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);
scripts_to_execute_after_parsing_.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(pending_script);
host_->NotifyScriptLoaded(pending_script);
}
// '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* script_element,
const TextPosition& script_start_position) {
DCHECK(script_element);
// FIXME: If scripting is disabled, always just return.
bool had_preload_scanner = host_->HasPreloadScanner();
// Initial steps of 'An end tag whose tag name is "script"'.
// Try to execute the script given to us.
ProcessScriptElementInternal(script_element, script_start_position);
// "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(),
!document_->IsScriptExecutionReady());
parser_blocking_script_->MarkParserBlockingLoadStartTime();
// If preload scanner got created, it is missing the source after the
// current insertion point. Append it and scan.
if (!had_preload_scanner && host_->HasPreloadScanner())
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(document_);
DCHECK(!IsExecutingScript());
DCHECK(document_->IsScriptExecutionReady());
// 6. "Let the insertion point be just before the next input character."
InsertionPointRecord insertion_point_record(host_->InputStream());
// 1., 7.--9.
ExecutePendingScriptAndDispatchEvent(parser_blocking_script_,
ScriptStreamer::kParsingBlocking);
// 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* pending_script) {
TRACE_EVENT0("blink", "HTMLParserScriptRunner::executeScriptsWaitingForLoad");
DCHECK(!IsExecutingScript());
DCHECK(HasParserBlockingScript());
DCHECK_EQ(pending_script, ParserBlockingScript());
DCHECK(ParserBlockingScript()->IsReady());
ExecuteParsingBlockingScripts();
}
void HTMLParserScriptRunner::ExecuteScriptsWaitingForResources() {
TRACE_EVENT0("blink",
"HTMLParserScriptRunner::executeScriptsWaitingForResources");
DCHECK(document_);
DCHECK(!IsExecutingScript());
DCHECK(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 (!scripts_to_execute_after_parsing_.IsEmpty()) {
DCHECK(!IsExecutingScript());
DCHECK(!HasParserBlockingScript());
DCHECK(scripts_to_execute_after_parsing_.front()->IsExternal());
// 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 (!scripts_to_execute_after_parsing_.front()->IsReady()) {
scripts_to_execute_after_parsing_.front()->WatchForLoad(this);
TraceParserBlockingScript(scripts_to_execute_after_parsing_.front().Get(),
!document_->IsScriptExecutionReady());
scripts_to_execute_after_parsing_.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 = scripts_to_execute_after_parsing_.TakeFirst();
// 2. "Execute the first script in the list of scripts that will execute
// when the document has finished parsing."
ExecutePendingScriptAndDispatchEvent(first, ScriptStreamer::kDeferred);
// FIXME: What is this m_document check for?
if (!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());
parser_blocking_script_ = RequestPendingScript(element);
if (!ParserBlockingScript())
return;
DCHECK(ParserBlockingScript()->IsExternal());
// 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()) {
parser_blocking_script_->StartStreamingIfPossible(
document_, ScriptStreamer::kParsingBlocking);
parser_blocking_script_->WatchForLoad(this);
}
}
// 1st Clause, Step 23 of https://html.spec.whatwg.org/#prepare-a-script
void HTMLParserScriptRunner::RequestDeferredScript(Element* element) {
PendingScript* pending_script = RequestPendingScript(element);
if (!pending_script)
return;
if (!pending_script->IsReady()) {
pending_script->StartStreamingIfPossible(document_,
ScriptStreamer::kDeferred);
}
DCHECK(pending_script->IsExternal());
// "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."
scripts_to_execute_after_parsing_.push_back(pending_script);
}
PendingScript* HTMLParserScriptRunner::RequestPendingScript(
Element* element) const {
ScriptElementBase* script_element =
ScriptElementBase::FromElementIfPossible(element);
return script_element->Loader()->CreatePendingScript();
}
// 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& script_start_position) {
DCHECK(document_);
DCHECK(!HasParserBlockingScript());
{
ScriptElementBase* element =
ScriptElementBase::FromElementIfPossible(script);
DCHECK(element);
ScriptLoader* script_loader = element->Loader();
DCHECK(script_loader);
// FIXME: Align trace event name and function name.
TRACE_EVENT1("blink", "HTMLParserScriptRunner::execute", "data",
GetTraceArgsForScriptElement(element, script_start_position));
DCHECK(script_loader->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 insertion_point_record(host_->InputStream());
// "Increment the parser's script nesting level by one."
HTMLParserReentryPermit::ScriptNestingLevelIncrementer
nesting_level_incrementer =
reentry_permit_->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."
script_loader->PrepareScript(script_start_position);
// A part of Step 23 of https://html.spec.whatwg.org/#prepare-a-script:
if (!script_loader->WillBeParserExecuted())
return;
if (script_loader->WillExecuteWhenDocumentFinishedParsing()) {
// 1st Clause of Step 23.
RequestDeferredScript(script);
} else if (script_loader->ReadyToBeParserExecuted()) {
// 5th Clause of Step 23.
// "If ... it's an HTML parser
// whose script nesting level is not greater than one"
if (reentry_permit_->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(!parser_blocking_script_);
parser_blocking_script_ =
ClassicPendingScript::Create(element, script_start_position);
} 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(reentry_permit_->ScriptNestingLevel(), 1u);
if (parser_blocking_script_)
parser_blocking_script_->Dispose();
parser_blocking_script_ = nullptr;
ScriptSourceCode source_code(script->textContent(),
DocumentURLForScriptExecution(document_),
script_start_position);
DoExecuteScript(element, ClassicScript::Create(source_code),
script_start_position);
}
} 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(document_);
visitor->Trace(host_);
visitor->Trace(parser_blocking_script_);
visitor->Trace(scripts_to_execute_after_parsing_);
PendingScriptClient::Trace(visitor);
}
} // namespace blink