| /* |
| * 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 |