Bug 1606652 - Speculatively off thread parse external scripts as soon as they are fetched. r=smaug

The changes proposed here will speculatively parse all external scripts off thread as soon as they are fetched, except for async, link preload, and non parser inserted scripts.  This should save us some time since currently all scripts are parsed right before execution or while they are blocking the dom parser.

Differential Revision: https://phabricator.services.mozilla.com/D76644
This commit is contained in:
Denis Palmeiro
2020-06-10 00:45:26 +00:00
parent bc40e8720a
commit 6fbd659595
5 changed files with 151 additions and 35 deletions

View File

@@ -170,6 +170,7 @@ ScriptLoader::ScriptLoader(Document* aDocument)
mNumberOfProcessors(0),
mEnabled(true),
mDeferEnabled(false),
mSpeculativeOMTParsingEnabled(false),
mDeferCheckpointReached(false),
mBlockingDOMContentLoaded(false),
mLoadEventFired(false),
@@ -177,6 +178,9 @@ ScriptLoader::ScriptLoader(Document* aDocument)
mReporter(new ConsoleReportCollector()) {
LOG(("ScriptLoader::ScriptLoader %p", this));
EnsureModuleHooksInitialized();
mSpeculativeOMTParsingEnabled = StaticPrefs::
dom_script_loader_external_scripts_speculative_omt_parse_enabled();
}
ScriptLoader::~ScriptLoader() {
@@ -1151,9 +1155,7 @@ void ScriptLoader::ProcessLoadedModuleTree(ModuleLoadRequest* aRequest) {
}
}
if (aRequest->mWasCompiledOMT) {
mDocument->UnblockOnload(false);
}
aRequest->MaybeUnblockOnload();
}
JS::Value ScriptLoader::FindFirstParseError(ModuleLoadRequest* aRequest) {
@@ -1741,8 +1743,9 @@ bool ScriptLoader::ProcessExternalScript(nsIScriptElement* aElement,
}
// We should still be in loading stage of script unless we're loading a
// module.
NS_ASSERTION(!request->InCompilingStage() || request->IsModuleRequest(),
// module or speculatively off-main-thread parsing a script.
NS_ASSERTION(SpeculativeOMTParsingEnabled() || !request->InCompilingStage() ||
request->IsModuleRequest(),
"Request should not yet be in compiling stage.");
if (request->IsAsyncScript()) {
@@ -1943,6 +1946,11 @@ ScriptLoadRequest* ScriptLoader::LookupPreloadRequest(
// preload!
RefPtr<ScriptLoadRequest> request = mPreloads[i].mRequest;
request->SetIsLoadRequest(aElement);
if (request->mWasCompiledOMT && !request->IsModuleRequest()) {
request->SetReady();
}
nsString preloadCharset(mPreloads[i].mCharset);
mPreloads.RemoveElementAt(i);
@@ -2066,6 +2074,15 @@ nsresult ScriptLoader::ProcessOffThreadRequest(ScriptLoadRequest* aRequest) {
return ProcessFetchedModuleSource(request);
}
// Element may not be ready yet if speculatively compiling, so process the
// request in ProcessPendingRequests when it is available.
MOZ_ASSERT_IF(!SpeculativeOMTParsingEnabled(), aRequest->GetScriptElement());
if (!aRequest->GetScriptElement()) {
// Unblock onload here in case this request never gets executed.
aRequest->MaybeUnblockOnload();
return NS_OK;
}
aRequest->SetReady();
if (aRequest == mParserBlockingRequest) {
@@ -2080,14 +2097,19 @@ nsresult ScriptLoader::ProcessOffThreadRequest(ScriptLoadRequest* aRequest) {
mParserBlockingRequest = nullptr;
UnblockParser(aRequest);
ProcessRequest(aRequest);
mDocument->UnblockOnload(false);
ContinueParserAsync(aRequest);
return NS_OK;
}
nsresult rv = ProcessRequest(aRequest);
mDocument->UnblockOnload(false);
return rv;
// Async scripts and blocking scripts can be executed right away.
if (aRequest->IsAsyncScript() || aRequest->IsBlockingScript()) {
nsresult rv = ProcessRequest(aRequest);
return rv;
}
// Process other scripts in the proper order.
ProcessPendingRequests();
return NS_OK;
}
NotifyOffThreadScriptLoadCompletedRunnable::
@@ -2198,7 +2220,10 @@ static void OffThreadScriptLoaderCallback(JS::OffThreadToken* aToken,
nsresult ScriptLoader::AttemptAsyncScriptCompile(ScriptLoadRequest* aRequest,
bool* aCouldCompileOut) {
MOZ_ASSERT_IF(!aRequest->IsModuleRequest(), aRequest->IsReadyToRun());
// If speculative parsing is enabled, the request may not be ready to run if
// the element is not yet available.
MOZ_ASSERT_IF(!SpeculativeOMTParsingEnabled() && !aRequest->IsModuleRequest(),
aRequest->IsReadyToRun());
MOZ_ASSERT(!aRequest->mWasCompiledOMT);
MOZ_ASSERT(aCouldCompileOut && !*aCouldCompileOut);
@@ -2303,7 +2328,7 @@ nsresult ScriptLoader::AttemptAsyncScriptCompile(ScriptLoadRequest* aRequest,
}
}
mDocument->BlockOnload();
aRequest->BlockOnload(mDocument);
// Once the compilation is finished, an event would be added to the event loop
// to call ScriptLoader::ProcessOffThreadRequest with the same request.
@@ -2318,21 +2343,18 @@ nsresult ScriptLoader::CompileOffThreadOrProcessRequest(
ScriptLoadRequest* aRequest) {
NS_ASSERTION(nsContentUtils::IsSafeToRunScript(),
"Processing requests when running scripts is unsafe.");
NS_ASSERTION(!aRequest->mOffThreadToken,
"Candidate for off-thread compile is already parsed off-thread");
NS_ASSERTION(
!aRequest->InCompilingStage(),
"Candidate for off-thread compile is already in compiling stage.");
bool couldCompile = false;
nsresult rv = AttemptAsyncScriptCompile(aRequest, &couldCompile);
if (NS_FAILED(rv)) {
HandleLoadError(aRequest, rv);
return rv;
}
if (!aRequest->mOffThreadToken && !aRequest->mWasCompiledOMT) {
bool couldCompile = false;
nsresult rv = AttemptAsyncScriptCompile(aRequest, &couldCompile);
if (NS_FAILED(rv)) {
HandleLoadError(aRequest, rv);
return rv;
}
if (couldCompile) {
return NS_OK;
if (couldCompile) {
return NS_OK;
}
}
return ProcessRequest(aRequest);
@@ -2409,6 +2431,8 @@ nsresult ScriptLoader::ProcessRequest(ScriptLoadRequest* aRequest) {
NS_ENSURE_ARG(aRequest);
auto unblockOnload = MakeScopeExit([&] { aRequest->MaybeUnblockOnload(); });
if (aRequest->IsModuleRequest()) {
ModuleLoadRequest* request = aRequest->AsModuleRequest();
if (request->mModuleScript) {
@@ -3211,9 +3235,6 @@ void ScriptLoader::ProcessPendingRequests() {
request.swap(mParserBlockingRequest);
UnblockParser(request);
ProcessRequest(request);
if (request->mWasCompiledOMT) {
mDocument->UnblockOnload(false);
}
ContinueParserAsync(request);
}
@@ -3683,6 +3704,40 @@ static bool IsInternalURIScheme(nsIURI* uri) {
uri->SchemeIs("chrome");
}
bool ScriptLoader::ShouldCompileOffThread(ScriptLoadRequest* aRequest) {
if (NumberOfProcessors() <= 1) {
return false;
}
if (aRequest == mParserBlockingRequest) {
return true;
}
if (SpeculativeOMTParsingEnabled()) {
// Processing non async inserted scripts too early can potentially delay the
// load event from firing so focus on other scripts instead.
if (aRequest->mIsNonAsyncScriptInserted &&
!StaticPrefs::
dom_script_loader_external_scripts_speculate_non_parser_inserted_enabled()) {
return false;
}
// Async and link preload scripts do not need to be parsed right away.
if (aRequest->IsAsyncScript() &&
!StaticPrefs::
dom_script_loader_external_scripts_speculate_async_enabled()) {
return false;
}
if (aRequest->IsLinkPreloadScript() &&
!StaticPrefs::
dom_script_loader_external_scripts_speculate_link_preload_enabled()) {
return false;
}
return true;
}
return false;
}
nsresult ScriptLoader::PrepareLoadedRequest(ScriptLoadRequest* aRequest,
nsIIncrementalStreamLoader* aLoader,
nsresult aStatus) {
@@ -3796,9 +3851,10 @@ nsresult ScriptLoader::PrepareLoadedRequest(ScriptLoadRequest* aRequest,
// The script is now loaded and ready to run.
aRequest->SetReady();
// If this is currently blocking the parser, attempt to compile it
// off-main-thread.
if (aRequest == mParserBlockingRequest && NumberOfProcessors() > 1) {
// If speculative parsing is enabled attempt to compile all
// external scripts off-main-thread. Otherwise, only omt compile scripts
// blocking the parser.
if (ShouldCompileOffThread(aRequest)) {
MOZ_ASSERT(!aRequest->IsModuleRequest());
bool couldCompile = false;
nsresult rv = AttemptAsyncScriptCompile(aRequest, &couldCompile);