blob: e9bc8dde07c383e07e44d2878b5f6665701355b4 [file] [log] [blame]
// Copyright 2018 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 "third_party/blink/renderer/core/loader/modulescript/worker_module_script_fetcher.h"
#include <memory>
#include "services/network/public/mojom/ip_address_space.mojom-blink.h"
#include "services/network/public/mojom/referrer_policy.mojom-blink.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/common/loader/network_utils.h"
#include "third_party/blink/renderer/bindings/core/v8/script_source_location_type.h"
#include "third_party/blink/renderer/core/inspector/console_message.h"
#include "third_party/blink/renderer/core/origin_trials/origin_trial_context.h"
#include "third_party/blink/renderer/core/workers/worker_global_scope.h"
#include "third_party/blink/renderer/platform/heap/heap.h"
#include "third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object.h"
#include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h"
#include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher_properties.h"
#include "third_party/blink/renderer/platform/loader/fetch/unique_identifier.h"
#include "third_party/blink/renderer/platform/network/content_security_policy_response_headers.h"
#include "third_party/blink/renderer/platform/network/http_names.h"
#include "third_party/blink/renderer/platform/network/mime/mime_type_registry.h"
#include "third_party/blink/renderer/platform/network/network_utils.h"
#include "third_party/blink/renderer/platform/weborigin/security_policy.h"
namespace blink {
WorkerModuleScriptFetcher::WorkerModuleScriptFetcher(
WorkerGlobalScope* global_scope,
base::PassKey<ModuleScriptLoader> pass_key)
: ModuleScriptFetcher(pass_key), global_scope_(global_scope) {}
// <specdef href="https://html.spec.whatwg.org/C/#run-a-worker">
void WorkerModuleScriptFetcher::Fetch(
FetchParameters& fetch_params,
ModuleType expected_module_type,
ResourceFetcher* fetch_client_settings_object_fetcher,
ModuleGraphLevel level,
ModuleScriptFetcher::Client* client) {
DCHECK_EQ(fetch_params.GetScriptType(), mojom::blink::ScriptType::kModule);
DCHECK(global_scope_->IsContextThread());
DCHECK(!fetch_client_settings_object_fetcher_);
fetch_client_settings_object_fetcher_ = fetch_client_settings_object_fetcher;
client_ = client;
level_ = level;
expected_module_type_ = expected_module_type;
// Use WorkerMainScriptLoader to load the main script when
// dedicated workers (PlzDedicatedWorker) and shared workers.
std::unique_ptr<WorkerMainScriptLoadParameters>
worker_main_script_load_params =
global_scope_->TakeWorkerMainScriptLoadingParametersForModules();
if (worker_main_script_load_params) {
DCHECK_EQ(level_, ModuleGraphLevel::kTopLevelModuleFetch);
fetch_params.MutableResourceRequest().SetInspectorId(
CreateUniqueIdentifier());
worker_main_script_loader_ = MakeGarbageCollected<WorkerMainScriptLoader>();
worker_main_script_loader_->Start(
fetch_params, std::move(worker_main_script_load_params),
&fetch_client_settings_object_fetcher->Context(),
fetch_client_settings_object_fetcher->GetResourceLoadObserver(), this);
return;
}
// <spec step="12">In both cases, to perform the fetch given request, perform
// the following steps if the is top-level flag is set:</spec>
//
// <spec step="12.1">Set request's reserved client to inside settings.</spec>
//
// This is implemented in the browser process.
// <spec step="12.2">Fetch request, and asynchronously wait to run the
// remaining steps as part of fetch's process response for the response
// response.</spec>
ScriptResource::Fetch(fetch_params, fetch_client_settings_object_fetcher,
this, ScriptResource::kNoStreaming);
}
void WorkerModuleScriptFetcher::Trace(Visitor* visitor) const {
ModuleScriptFetcher::Trace(visitor);
visitor->Trace(client_);
visitor->Trace(global_scope_);
visitor->Trace(fetch_client_settings_object_fetcher_);
visitor->Trace(worker_main_script_loader_);
}
// https://html.spec.whatwg.org/C/#worker-processing-model
void WorkerModuleScriptFetcher::NotifyFinished(Resource* resource) {
DCHECK(global_scope_->IsContextThread());
ClearResource();
auto* script_resource = To<ScriptResource>(resource);
{
HeapVector<Member<ConsoleMessage>> error_messages;
if (!WasModuleLoadSuccessful(script_resource, expected_module_type_,
&error_messages)) {
client_->NotifyFetchFinishedError(error_messages);
return;
}
}
NotifyClient(resource->Url(), expected_module_type_,
script_resource->SourceText(), resource->GetResponse(),
script_resource->CacheHandler());
}
void WorkerModuleScriptFetcher::NotifyClient(
const KURL& request_url,
ModuleType module_type,
const ParkableString& source_text,
const ResourceResponse& response,
SingleCachedMetadataHandler* cache_handler) {
HeapVector<Member<ConsoleMessage>> error_messages;
if (level_ == ModuleGraphLevel::kTopLevelModuleFetch) {
// TODO(nhiroki, hiroshige): Access to WorkerGlobalScope in module loaders
// is a layering violation. Also, updating WorkerGlobalScope ('module map
// settigns object') in flight can be dangerous because module loaders may
// refer to it. We should move these steps out of core/loader/modulescript/
// and run them after module loading. This may require the spec change.
// (https://crbug.com/845285)
// Ensure redirects don't affect SecurityOrigin.
const KURL response_url = response.CurrentRequestUrl();
DCHECK(fetch_client_settings_object_fetcher_->GetProperties()
.GetFetchClientSettingsObject()
.GetSecurityOrigin()
->CanReadContent(request_url))
<< "Top-level worker script request url must be same-origin with "
"outside settings constructor origin or permitted by the parent "
"chrome-extension.";
// |response_url| must be same-origin with request origin or its url's
// scheme must be "data".
//
// https://fetch.spec.whatwg.org/#concept-main-fetch
// Step 5:
// - request’s current URL’s origin is same origin with request’s
// origin (request's current URL indicates |response_url|)
// - request’s current URL’s scheme is "data"
// ---> Return the result of performing a scheme fetch using request.
// - request’s mode is "same-origin"
// ---> Return a network error. [spec text]
if (!SecurityOrigin::AreSameOrigin(request_url, response_url) &&
!response_url.ProtocolIsData()) {
error_messages.push_back(MakeGarbageCollected<ConsoleMessage>(
mojom::ConsoleMessageSource::kSecurity,
mojom::ConsoleMessageLevel::kError,
"Refused to cross-origin redirects of the top-level worker script."));
client_->NotifyFetchFinishedError(error_messages);
return;
}
auto response_referrer_policy = network::mojom::ReferrerPolicy::kDefault;
const String response_referrer_policy_header =
response.HttpHeaderField(http_names::kReferrerPolicy);
if (!response_referrer_policy_header.IsNull()) {
SecurityPolicy::ReferrerPolicyFromHeaderValue(
response_referrer_policy_header,
kDoNotSupportReferrerPolicyLegacyKeywords, &response_referrer_policy);
}
std::unique_ptr<Vector<String>> response_origin_trial_tokens =
OriginTrialContext::ParseHeaderValue(
response.HttpHeaderField(http_names::kOriginTrial));
// Step 12.3-12.6 are implemented in Initialize().
global_scope_->Initialize(
response_url, response_referrer_policy, response.AddressSpace(),
ContentSecurityPolicy::ParseHeaders(
ContentSecurityPolicyResponseHeaders(response)),
response_origin_trial_tokens.get(), response.AppCacheID());
}
// <spec step="12.7">Asynchronously complete the perform the fetch steps with
// response.</spec>
const KURL& url = response.CurrentRequestUrl();
// Create an external module script where base_url == source_url.
// https://html.spec.whatwg.org/multipage/webappapis.html#concept-script-base-url
client_->NotifyFetchFinishedSuccess(ModuleScriptCreationParams(
/*source_url=*/url, /*base_url=*/url,
ScriptSourceLocationType::kExternalFile, module_type, source_text,
cache_handler));
}
void WorkerModuleScriptFetcher::DidReceiveData(base::span<const char> span) {
if (!decoder_) {
decoder_ = std::make_unique<TextResourceDecoder>(TextResourceDecoderOptions(
TextResourceDecoderOptions::kPlainTextContent,
worker_main_script_loader_->GetScriptEncoding()));
}
if (!span.size())
return;
source_text_.Append(decoder_->Decode(span.data(), span.size()));
}
void WorkerModuleScriptFetcher::OnStartLoadingBody(
const ResourceResponse& resource_response) {
if (!MIMETypeRegistry::IsSupportedJavaScriptMIMEType(
resource_response.HttpContentType())) {
HeapVector<Member<ConsoleMessage>> error_messages;
String message =
"Failed to load module script: The server responded with a "
"non-JavaScript MIME type of \"" +
resource_response.HttpContentType() +
"\". Strict MIME type checking is enforced for module scripts per HTML "
"spec.";
error_messages.push_back(MakeGarbageCollected<ConsoleMessage>(
mojom::ConsoleMessageSource::kJavaScript,
mojom::ConsoleMessageLevel::kError, message,
resource_response.CurrentRequestUrl().GetString(), /*loader=*/nullptr,
-1));
worker_main_script_loader_->Cancel();
client_->NotifyFetchFinishedError(error_messages);
return;
}
}
void WorkerModuleScriptFetcher::OnFinishedLoadingWorkerMainScript() {
const ResourceResponse& response = worker_main_script_loader_->GetResponse();
if (decoder_)
source_text_.Append(decoder_->Flush());
NotifyClient(worker_main_script_loader_->GetRequestURL(),
ModuleType::kJavaScript,
ParkableString(source_text_.ToString().ReleaseImpl()), response,
worker_main_script_loader_->CreateCachedMetadataHandler());
}
void WorkerModuleScriptFetcher::OnFailedLoadingWorkerMainScript() {
client_->NotifyFetchFinishedError(HeapVector<Member<ConsoleMessage>>());
}
} // namespace blink