blob: 06e44d2831cfb432036cde92ae45a3c28bcf2005 [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "core/loader/modulescript/ModuleScriptLoader.h"
#include "core/dom/ExecutionContext.h"
#include "core/dom/Modulator.h"
#include "core/dom/ModuleScript.h"
#include "core/inspector/ConsoleMessage.h"
#include "core/loader/modulescript/DocumentModuleScriptFetcher.h"
#include "core/loader/modulescript/ModuleScriptFetcher.h"
#include "core/loader/modulescript/ModuleScriptLoaderClient.h"
#include "core/loader/modulescript/ModuleScriptLoaderRegistry.h"
#include "core/loader/modulescript/WorkletModuleScriptFetcher.h"
#include "core/workers/MainThreadWorkletGlobalScope.h"
#include "platform/loader/fetch/Resource.h"
#include "platform/loader/fetch/ResourceLoaderOptions.h"
#include "platform/loader/fetch/ResourceLoadingLog.h"
#include "platform/weborigin/SecurityPolicy.h"
#include "platform/wtf/text/AtomicString.h"
namespace blink {
ModuleScriptLoader::ModuleScriptLoader(Modulator* modulator,
const ScriptFetchOptions& options,
ModuleScriptLoaderRegistry* registry,
ModuleScriptLoaderClient* client)
: modulator_(modulator),
options_(options),
registry_(registry),
client_(client) {
DCHECK(modulator);
DCHECK(registry);
DCHECK(client);
}
ModuleScriptLoader::~ModuleScriptLoader() {}
#if DCHECK_IS_ON()
const char* ModuleScriptLoader::StateToString(ModuleScriptLoader::State state) {
switch (state) {
case State::kInitial:
return "Initial";
case State::kFetching:
return "Fetching";
case State::kFinished:
return "Finished";
}
NOTREACHED();
return "";
}
#endif
void ModuleScriptLoader::AdvanceState(ModuleScriptLoader::State new_state) {
switch (state_) {
case State::kInitial:
DCHECK_EQ(new_state, State::kFetching);
break;
case State::kFetching:
DCHECK_EQ(new_state, State::kFinished);
break;
case State::kFinished:
NOTREACHED();
break;
}
#if DCHECK_IS_ON()
RESOURCE_LOADING_DVLOG(1)
<< "ModuleLoader[" << url_.GetString() << "]::advanceState("
<< StateToString(state_) << " -> " << StateToString(new_state) << ")";
#endif
state_ = new_state;
if (state_ == State::kFinished) {
registry_->ReleaseFinishedLoader(this);
client_->NotifyNewSingleModuleFinished(module_script_);
}
}
void ModuleScriptLoader::Fetch(const ModuleScriptFetchRequest& module_request,
ModuleGraphLevel level) {
// https://html.spec.whatwg.org/#fetch-a-single-module-script
// Step 4. "Set moduleMap[url] to "fetching"." [spec text]
AdvanceState(State::kFetching);
// Step 5. "Let request be a new request whose url is url, ..." [spec text]
ResourceRequest resource_request(module_request.Url());
#if DCHECK_IS_ON()
url_ = module_request.Url();
#endif
ResourceLoaderOptions options;
// TODO(kouhei): handle "destination is destination,"
// Step 6. "Set up the module script request given request and options."
// [spec text]
// [SMSR]
// https://html.spec.whatwg.org/multipage/webappapis.html#set-up-the-module-script-request
// [SMSR] "... its parser metadata to options's parser metadata, ..."
// [spec text]
options.parser_disposition = options_.ParserState();
// As initiator for module script fetch is not specified in HTML spec,
// we specity "" as initiator per:
// https://fetch.spec.whatwg.org/#concept-request-initiator
options.initiator_info.name = g_empty_atom;
if (level == ModuleGraphLevel::kDependentModuleFetch) {
options.initiator_info.imported_module_referrer =
module_request.GetReferrer();
options.initiator_info.position = module_request.GetReferrerPosition();
}
// Note: |options| should not be modified after here.
FetchParameters fetch_params(resource_request, options);
// [SMSR] "... its integrity metadata to options's integrity metadata, ..."
// [spec text]
fetch_params.SetIntegrityMetadata(options_.GetIntegrityMetadata());
fetch_params.MutableResourceRequest().SetFetchIntegrity(
options_.GetIntegrityAttributeValue());
// [SMSR] "Set request's cryptographic nonce metadata to options's
// cryptographic nonce, ..." [spec text]
fetch_params.SetContentSecurityPolicyNonce(options_.Nonce());
// Step 5. "... mode is "cors", ..."
// [SMSR] "... and its credentials mode to options's credentials mode."
// [spec text]
fetch_params.SetCrossOriginAccessControl(
modulator_->GetSecurityOriginForFetch(), options_.CredentialsMode());
// Step 5. "... referrer is referrer, ..." [spec text]
if (!module_request.GetReferrer().IsNull()) {
resource_request.SetHTTPReferrer(SecurityPolicy::GenerateReferrer(
module_request.GetReferrerPolicy(), module_request.Url(),
module_request.GetReferrer()));
}
// Step 5. "... and client is fetch client settings object." [spec text]
// -> set by ResourceFetcher
// Note: The fetch request's "origin" isn't specified in
// https://html.spec.whatwg.org/#fetch-a-single-module-script
// Thus, the "origin" is "client" per
// https://fetch.spec.whatwg.org/#concept-request-origin
// Module scripts are always defer.
fetch_params.SetDefer(FetchParameters::kLazyLoad);
// [nospec] Unlike defer/async classic scripts, module scripts are fetched at
// High priority.
fetch_params.MutableResourceRequest().SetPriority(kResourceLoadPriorityHigh);
// Use UTF-8, according to Step 9:
// "Let source text be the result of UTF-8 decoding response's body."
// [spec text]
fetch_params.SetDecoderOptions(
TextResourceDecoderOptions::CreateAlwaysUseUTF8ForText());
// Step 7. "If the caller specified custom steps to perform the fetch,
// perform them on request, setting the is top-level flag if the top-level
// module fetch flag is set. Return from this algorithm, and when the custom
// perform the fetch steps complete with response response, run the remaining
// steps.
// Otherwise, fetch request. Return from this algorithm, and run the remaining
// steps as part of the fetch's process response for the response response."
// [spec text]
module_fetcher_ = modulator_->CreateModuleScriptFetcher();
module_fetcher_->Fetch(fetch_params, this);
}
void ModuleScriptLoader::NotifyFetchFinished(
const WTF::Optional<ModuleScriptCreationParams>& params,
const HeapVector<Member<ConsoleMessage>>& error_messages) {
// [nospec] Abort the steps if the browsing context is discarded.
if (!modulator_->HasValidContext()) {
AdvanceState(State::kFinished);
return;
}
// Note: "conditions" referred in Step 8 is implemented in
// WasModuleLoadSuccessful() in ModuleScriptFetcher.cpp.
// Step 8. "If any of the following conditions are met, set moduleMap[url] to
// null, asynchronously complete this algorithm with null, and abort these
// steps." [spec text]
if (!params.has_value()) {
for (ConsoleMessage* error_message : error_messages) {
ExecutionContext::From(modulator_->GetScriptState())
->AddConsoleMessage(error_message);
}
AdvanceState(State::kFinished);
return;
}
// Step 9. "Let source text be the result of UTF-8 decoding response's body."
// [spec text]
// Step 10. "Let module script be the result of creating a module script given
// source text, module map settings object, response's url, and options."
// [spec text]
module_script_ = ModuleScript::Create(params->GetSourceText(), modulator_,
params->GetResponseUrl(), options_,
params->GetAccessControlStatus());
AdvanceState(State::kFinished);
}
void ModuleScriptLoader::Trace(blink::Visitor* visitor) {
visitor->Trace(modulator_);
visitor->Trace(module_script_);
visitor->Trace(registry_);
visitor->Trace(client_);
visitor->Trace(module_fetcher_);
}
} // namespace blink