blob: cb192069fad2140bbeab3d2914006dc6f8200dcd [file] [log] [blame]
/*
* Copyright (C) 2009 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "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 THE COPYRIGHT
* OWNER 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 "third_party/blink/renderer/core/exported/web_shared_worker_impl.h"
#include <memory>
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "services/network/public/mojom/fetch_api.mojom-blink.h"
#include "third_party/blink/public/mojom/devtools/devtools_agent.mojom-blink.h"
#include "third_party/blink/public/mojom/script/script_type.mojom-blink.h"
#include "third_party/blink/public/platform/modules/service_worker/web_service_worker_network_provider.h"
#include "third_party/blink/public/platform/task_type.h"
#include "third_party/blink/public/platform/web_content_settings_client.h"
#include "third_party/blink/public/platform/web_string.h"
#include "third_party/blink/public/platform/web_url.h"
#include "third_party/blink/public/platform/web_url_request.h"
#include "third_party/blink/public/platform/web_worker_fetch_context.h"
#include "third_party/blink/public/web/web_settings.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_cache_options.h"
#include "third_party/blink/renderer/core/core_initializer.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/events/message_event.h"
#include "third_party/blink/renderer/core/inspector/console_message.h"
#include "third_party/blink/renderer/core/inspector/worker_devtools_params.h"
#include "third_party/blink/renderer/core/loader/appcache/application_cache_host.h"
#include "third_party/blink/renderer/core/loader/document_loader.h"
#include "third_party/blink/renderer/core/loader/frame_load_request.h"
#include "third_party/blink/renderer/core/loader/frame_loader.h"
#include "third_party/blink/renderer/core/loader/worker_fetch_context.h"
#include "third_party/blink/renderer/core/probe/core_probes.h"
#include "third_party/blink/renderer/core/script/script.h"
#include "third_party/blink/renderer/core/workers/global_scope_creation_params.h"
#include "third_party/blink/renderer/core/workers/parent_execution_context_task_runners.h"
#include "third_party/blink/renderer/core/workers/shared_worker_content_settings_proxy.h"
#include "third_party/blink/renderer/core/workers/shared_worker_global_scope.h"
#include "third_party/blink/renderer/core/workers/shared_worker_thread.h"
#include "third_party/blink/renderer/core/workers/worker_classic_script_loader.h"
#include "third_party/blink/renderer/core/workers/worker_content_settings_client.h"
#include "third_party/blink/renderer/core/workers/worker_global_scope.h"
#include "third_party/blink/renderer/platform/cross_thread_functional.h"
#include "third_party/blink/renderer/platform/heap/handle.h"
#include "third_party/blink/renderer/platform/heap/persistent.h"
#include "third_party/blink/renderer/platform/loader/fetch/fetch_client_settings_object_snapshot.h"
#include "third_party/blink/renderer/platform/loader/fetch/resource_fetcher.h"
#include "third_party/blink/renderer/platform/loader/fetch/resource_response.h"
#include "third_party/blink/renderer/platform/network/content_security_policy_parsers.h"
#include "third_party/blink/renderer/platform/weborigin/kurl.h"
#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
#include "third_party/blink/renderer/platform/weborigin/security_policy.h"
#include "third_party/blink/renderer/platform/wtf/functional.h"
namespace blink {
WebSharedWorkerImpl::WebSharedWorkerImpl(WebSharedWorkerClient* client)
: client_(client),
creation_address_space_(mojom::IPAddressSpace::kPublic),
parent_execution_context_task_runners_(
ParentExecutionContextTaskRunners::Create()),
weak_ptr_factory_(this) {
DCHECK(IsMainThread());
}
WebSharedWorkerImpl::~WebSharedWorkerImpl() {
DCHECK(IsMainThread());
}
void WebSharedWorkerImpl::TerminateWorkerThread() {
DCHECK(IsMainThread());
if (asked_to_terminate_)
return;
asked_to_terminate_ = true;
if (shadow_page_ && !shadow_page_->WasInitialized()) {
client_->WorkerScriptLoadFailed();
// |this| is deleted at this point.
return;
}
if (main_script_loader_) {
main_script_loader_->Cancel();
main_script_loader_ = nullptr;
client_->WorkerScriptLoadFailed();
// |this| is deleted at this point.
return;
}
if (worker_thread_) {
worker_thread_->Terminate();
DevToolsAgent::WorkerThreadTerminated(shadow_page_->GetDocument(),
worker_thread_.get());
}
}
std::unique_ptr<WebApplicationCacheHost>
WebSharedWorkerImpl::CreateApplicationCacheHost(
WebApplicationCacheHostClient* appcache_host_client) {
DCHECK(IsMainThread());
return client_->CreateApplicationCacheHost(appcache_host_client);
}
void WebSharedWorkerImpl::OnShadowPageInitialized() {
DCHECK(IsMainThread());
DCHECK(!main_script_loader_);
shadow_page_->DocumentLoader()->SetServiceWorkerNetworkProvider(
client_->CreateServiceWorkerNetworkProvider());
main_script_loader_ = MakeGarbageCollected<WorkerClassicScriptLoader>();
main_script_loader_->LoadTopLevelScriptAsynchronously(
*shadow_page_->GetDocument(), shadow_page_->GetDocument()->Fetcher(),
script_request_url_, mojom::RequestContextType::SHARED_WORKER,
network::mojom::FetchRequestMode::kSameOrigin,
network::mojom::FetchCredentialsMode::kSameOrigin,
creation_address_space_,
Bind(&WebSharedWorkerImpl::DidReceiveScriptLoaderResponse,
WTF::Unretained(this)),
Bind(&WebSharedWorkerImpl::OnScriptLoaderFinished,
WTF::Unretained(this)));
// Do nothing here since OnScriptLoaderFinished() might have been already
// invoked and |this| might have been deleted at this point.
}
void WebSharedWorkerImpl::ResumeStartup() {
DCHECK(IsMainThread());
bool is_paused_on_start = is_paused_on_start_;
is_paused_on_start_ = false;
if (is_paused_on_start) {
// We'll continue in OnShadowPageInitialized().
shadow_page_->Initialize(script_request_url_);
}
}
const base::UnguessableToken& WebSharedWorkerImpl::GetDevToolsWorkerToken() {
return devtools_worker_token_;
}
void WebSharedWorkerImpl::CountFeature(WebFeature feature) {
DCHECK(IsMainThread());
client_->CountFeature(feature);
}
void WebSharedWorkerImpl::DidCloseWorkerGlobalScope() {
DCHECK(IsMainThread());
client_->WorkerContextClosed();
TerminateWorkerThread();
}
void WebSharedWorkerImpl::DidTerminateWorkerThread() {
DCHECK(IsMainThread());
client_->WorkerContextDestroyed();
// |this| is deleted at this point.
}
void WebSharedWorkerImpl::Connect(MessagePortChannel web_channel) {
DCHECK(IsMainThread());
// The HTML spec requires to queue a connect event using the DOM manipulation
// task source.
// https://html.spec.whatwg.org/multipage/workers.html#shared-workers-and-the-sharedworker-interface
PostCrossThreadTask(
*GetWorkerThread()->GetTaskRunner(TaskType::kDOMManipulation), FROM_HERE,
CrossThreadBind(&WebSharedWorkerImpl::ConnectTaskOnWorkerThread,
WTF::CrossThreadUnretained(this),
WTF::Passed(std::move(web_channel))));
}
void WebSharedWorkerImpl::ConnectTaskOnWorkerThread(
MessagePortChannel channel) {
// Wrap the passed-in channel in a MessagePort, and send it off via a connect
// event.
DCHECK(worker_thread_->IsCurrentThread());
auto* scope = To<SharedWorkerGlobalScope>(worker_thread_->GlobalScope());
scope->Connect(std::move(channel));
}
void WebSharedWorkerImpl::StartWorkerContext(
const WebURL& script_request_url,
const WebString& name,
const WebString& content_security_policy,
mojom::ContentSecurityPolicyType policy_type,
mojom::IPAddressSpace creation_address_space,
const base::UnguessableToken& devtools_worker_token,
PrivacyPreferences privacy_preferences,
scoped_refptr<network::SharedURLLoaderFactory> loader_factory,
mojo::ScopedMessagePipeHandle content_settings_handle,
mojo::ScopedMessagePipeHandle interface_provider) {
DCHECK(IsMainThread());
script_request_url_ = script_request_url;
name_ = name;
creation_address_space_ = creation_address_space;
// Chrome doesn't use interface versioning.
content_settings_info_ = mojom::blink::WorkerContentSettingsProxyPtrInfo(
std::move(content_settings_handle), 0u);
pending_interface_provider_.set_handle(std::move(interface_provider));
devtools_worker_token_ = devtools_worker_token;
// |shadow_page_| must be created after |devtools_worker_token_| because it
// triggers creation of a InspectorNetworkAgent that tries to access the
// token.
shadow_page_ =
std::make_unique<WorkerShadowPage>(this, std::move(loader_factory),
std::move(privacy_preferences));
// If we were asked to pause worker context on start and wait for debugger
// then now is a good time to do that.
client_->WorkerReadyForInspection();
if (pause_worker_context_on_start_) {
is_paused_on_start_ = true;
return;
}
// We'll continue in OnShadowPageInitialized().
shadow_page_->Initialize(script_request_url_);
}
void WebSharedWorkerImpl::DidReceiveScriptLoaderResponse() {
DCHECK(IsMainThread());
probe::didReceiveScriptResponse(shadow_page_->GetDocument(),
main_script_loader_->Identifier());
client_->SelectAppCacheID(main_script_loader_->AppCacheID());
}
void WebSharedWorkerImpl::OnScriptLoaderFinished() {
DCHECK(IsMainThread());
DCHECK(main_script_loader_);
if (asked_to_terminate_)
return;
if (main_script_loader_->Failed()) {
main_script_loader_->Cancel();
client_->WorkerScriptLoadFailed();
// |this| is deleted at this point.
return;
}
// S13nServiceWorker: The browser process is expected to send a
// SetController IPC before sending the script response, but there is no
// guarantee of the ordering as the messages arrive on different message
// pipes. Wait for the SetController IPC to be received before starting the
// worker; otherwise fetches from the worker might not go through the
// appropriate controller.
//
// (For non-S13nServiceWorker, we don't need to do this step as the controller
// service worker isn't used directly by the renderer, but to minimize code
// differences between the flags just do it anyway.)
client_->WaitForServiceWorkerControllerInfo(
shadow_page_->DocumentLoader()->GetServiceWorkerNetworkProvider(),
WTF::Bind(&WebSharedWorkerImpl::ContinueOnScriptLoaderFinished,
weak_ptr_factory_.GetWeakPtr()));
}
void WebSharedWorkerImpl::ContinueOnScriptLoaderFinished() {
DCHECK(IsMainThread());
DCHECK(main_script_loader_);
DCHECK(!main_script_loader_->Failed());
if (asked_to_terminate_)
return;
// FIXME: this document's origin is pristine and without any extra privileges
// (e.g. GrantUniversalAccess) that can be overriden in regular documents
// via WebPreference by embedders. (crbug.com/254993)
Document* document = shadow_page_->GetDocument();
// Creates 'outside settings' used in the "Processing model" algorithm in the
// HTML spec:
// https://html.spec.whatwg.org/multipage/workers.html#worker-processing-model
//
// TODO(nhiroki): According to the spec, the 'outside settings' should
// correspond to the Document that called 'new SharedWorker()'. However,
// for now there is no way to pass the settings object over mojo IPCs, so as
// a stopgap the shadow page's Document is used here.
auto* outside_settings_object =
MakeGarbageCollected<FetchClientSettingsObjectSnapshot>(
*document->Fetcher()->Context().GetFetchClientSettingsObject());
scoped_refptr<WebWorkerFetchContext> web_worker_fetch_context =
client_->CreateWorkerFetchContext(
shadow_page_->DocumentLoader()->GetServiceWorkerNetworkProvider());
DCHECK(web_worker_fetch_context);
web_worker_fetch_context->SetApplicationCacheHostID(
document->Loader()->GetApplicationCacheHost()->GetHostID());
ContentSecurityPolicy* content_security_policy =
main_script_loader_->GetContentSecurityPolicy();
auto referrer_policy = network::mojom::ReferrerPolicy::kDefault;
if (!main_script_loader_->GetReferrerPolicy().IsNull()) {
SecurityPolicy::ReferrerPolicyFromHeaderValue(
main_script_loader_->GetReferrerPolicy(),
kDoNotSupportReferrerPolicyLegacyKeywords, &referrer_policy);
}
// TODO(nhiroki); Set |script_type| to mojom::ScriptType::kModule for module
// fetch (https://crbug.com/824646).
mojom::ScriptType script_type = mojom::ScriptType::kClassic;
const KURL script_response_url = main_script_loader_->ResponseURL();
DCHECK(static_cast<KURL>(script_request_url_) == script_response_url ||
SecurityOrigin::AreSameSchemeHostPort(script_request_url_,
script_response_url));
auto global_scope_creation_params =
std::make_unique<GlobalScopeCreationParams>(
script_response_url, script_type, document->UserAgent(),
std::move(web_worker_fetch_context),
content_security_policy ? content_security_policy->Headers()
: Vector<CSPHeaderAndType>(),
referrer_policy, outside_settings_object->GetSecurityOrigin(),
document->IsSecureContext(), outside_settings_object->GetHttpsState(),
CreateWorkerClients(), main_script_loader_->ResponseAddressSpace(),
main_script_loader_->OriginTrialTokens(), devtools_worker_token_,
std::make_unique<WorkerSettings>(document->GetFrame()->GetSettings()),
kV8CacheOptionsDefault, nullptr /* worklet_module_response_map */,
std::move(pending_interface_provider_));
StartWorkerThread(std::move(global_scope_creation_params),
script_response_url, main_script_loader_->SourceText());
probe::scriptImported(document, main_script_loader_->Identifier(),
main_script_loader_->SourceText());
main_script_loader_ = nullptr;
}
void WebSharedWorkerImpl::StartWorkerThread(
std::unique_ptr<GlobalScopeCreationParams> global_scope_creation_params,
const KURL& script_response_url,
const String& source_code) {
DCHECK(IsMainThread());
reporting_proxy_ = MakeGarbageCollected<SharedWorkerReportingProxy>(
this, parent_execution_context_task_runners_);
worker_thread_ =
std::make_unique<SharedWorkerThread>(name_, *reporting_proxy_);
auto thread_startup_data = WorkerBackingThreadStartupData::CreateDefault();
thread_startup_data.atomics_wait_mode =
WorkerBackingThreadStartupData::AtomicsWaitMode::kAllow;
auto devtools_params = DevToolsAgent::WorkerThreadCreated(
shadow_page_->GetDocument(), GetWorkerThread(), script_response_url);
GetWorkerThread()->Start(std::move(global_scope_creation_params),
thread_startup_data, std::move(devtools_params),
parent_execution_context_task_runners_);
// TODO(nhiroki): Support module workers (https://crbug.com/680046).
GetWorkerThread()->EvaluateClassicScript(script_response_url, source_code,
nullptr /* cached_meta_data */,
v8_inspector::V8StackTraceId());
client_->WorkerScriptLoaded();
}
WorkerClients* WebSharedWorkerImpl::CreateWorkerClients() {
WorkerClients* worker_clients = WorkerClients::Create();
CoreInitializer::GetInstance().ProvideLocalFileSystemToWorker(
*worker_clients);
CoreInitializer::GetInstance().ProvideIndexedDBClientToWorker(
*worker_clients);
ProvideContentSettingsClientToWorker(
worker_clients, std::make_unique<SharedWorkerContentSettingsProxy>(
std::move(content_settings_info_)));
return worker_clients;
}
void WebSharedWorkerImpl::TerminateWorkerContext() {
DCHECK(IsMainThread());
TerminateWorkerThread();
}
void WebSharedWorkerImpl::PauseWorkerContextOnStart() {
pause_worker_context_on_start_ = true;
}
void WebSharedWorkerImpl::BindDevToolsAgent(
mojo::ScopedInterfaceEndpointHandle devtools_agent_host_ptr_info,
mojo::ScopedInterfaceEndpointHandle devtools_agent_request) {
shadow_page_->DevToolsAgent()->BindRequest(
mojom::blink::DevToolsAgentHostAssociatedPtrInfo(
std::move(devtools_agent_host_ptr_info),
mojom::blink::DevToolsAgentHost::Version_),
mojom::blink::DevToolsAgentAssociatedRequest(
std::move(devtools_agent_request)));
}
scoped_refptr<base::SingleThreadTaskRunner> WebSharedWorkerImpl::GetTaskRunner(
TaskType task_type) {
return parent_execution_context_task_runners_->Get(task_type);
}
std::unique_ptr<WebSharedWorker> WebSharedWorker::Create(
WebSharedWorkerClient* client) {
return base::WrapUnique(new WebSharedWorkerImpl(client));
}
} // namespace blink