| /* |
| * 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 "core/exported/WebSharedWorkerImpl.h" |
| |
| #include <memory> |
| #include "bindings/core/v8/V8CacheOptions.h" |
| #include "core/CoreInitializer.h" |
| #include "core/dom/Document.h" |
| #include "core/events/MessageEvent.h" |
| #include "core/inspector/ConsoleMessage.h" |
| #include "core/loader/FrameLoadRequest.h" |
| #include "core/loader/FrameLoader.h" |
| #include "core/loader/ThreadableLoadingContext.h" |
| #include "core/loader/WorkerFetchContext.h" |
| #include "core/probe/CoreProbes.h" |
| #include "core/workers/GlobalScopeCreationParams.h" |
| #include "core/workers/ParentFrameTaskRunners.h" |
| #include "core/workers/SharedWorkerContentSettingsProxy.h" |
| #include "core/workers/SharedWorkerGlobalScope.h" |
| #include "core/workers/SharedWorkerThread.h" |
| #include "core/workers/WorkerContentSettingsClient.h" |
| #include "core/workers/WorkerGlobalScope.h" |
| #include "core/workers/WorkerInspectorProxy.h" |
| #include "core/workers/WorkerScriptLoader.h" |
| #include "platform/CrossThreadFunctional.h" |
| #include "platform/heap/Handle.h" |
| #include "platform/heap/Persistent.h" |
| #include "platform/loader/fetch/ResourceFetcher.h" |
| #include "platform/loader/fetch/ResourceResponse.h" |
| #include "platform/network/ContentSecurityPolicyParsers.h" |
| #include "platform/runtime_enabled_features.h" |
| #include "platform/weborigin/KURL.h" |
| #include "platform/weborigin/SecurityOrigin.h" |
| #include "platform/weborigin/SecurityPolicy.h" |
| #include "platform/wtf/Functional.h" |
| #include "platform/wtf/PtrUtil.h" |
| #include "public/platform/TaskType.h" |
| #include "public/platform/WebContentSettingsClient.h" |
| #include "public/platform/WebString.h" |
| #include "public/platform/WebURL.h" |
| #include "public/platform/WebURLRequest.h" |
| #include "public/platform/WebWorkerFetchContext.h" |
| #include "public/platform/modules/serviceworker/WebServiceWorkerNetworkProvider.h" |
| #include "public/web/WebDevToolsAgent.h" |
| #include "public/web/WebSettings.h" |
| #include "services/network/public/interfaces/fetch_api.mojom-blink.h" |
| |
| namespace blink { |
| |
| WebSharedWorkerImpl::WebSharedWorkerImpl(WebSharedWorkerClient* client) |
| : worker_inspector_proxy_(WorkerInspectorProxy::Create()), |
| client_(client), |
| creation_address_space_(kWebAddressSpacePublic) { |
| 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(); |
| delete this; |
| return; |
| } |
| if (main_script_loader_) { |
| main_script_loader_->Cancel(); |
| main_script_loader_ = nullptr; |
| client_->WorkerScriptLoadFailed(); |
| delete this; |
| return; |
| } |
| if (worker_thread_) |
| worker_thread_->Terminate(); |
| worker_inspector_proxy_->WorkerThreadTerminated(); |
| } |
| |
| 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_ = WorkerScriptLoader::Create(); |
| |
| network::mojom::FetchRequestMode fetch_request_mode = |
| network::mojom::FetchRequestMode::kSameOrigin; |
| network::mojom::FetchCredentialsMode fetch_credentials_mode = |
| network::mojom::FetchCredentialsMode::kSameOrigin; |
| if ((static_cast<KURL>(url_)).ProtocolIsData()) { |
| fetch_request_mode = network::mojom::FetchRequestMode::kNoCORS; |
| fetch_credentials_mode = network::mojom::FetchCredentialsMode::kInclude; |
| } |
| |
| main_script_loader_->LoadAsynchronously( |
| *shadow_page_->GetDocument(), url_, |
| WebURLRequest::kRequestContextSharedWorker, fetch_request_mode, |
| fetch_credentials_mode, 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::SendProtocolMessage(int session_id, |
| int call_id, |
| const WebString& message, |
| const WebString& state) { |
| DCHECK(IsMainThread()); |
| client_->SendDevToolsMessage(session_id, call_id, message, state); |
| } |
| |
| 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(url_); |
| } |
| } |
| |
| WebDevToolsAgentClient::WebKitClientMessageLoop* |
| WebSharedWorkerImpl::CreateClientMessageLoop() { |
| DCHECK(IsMainThread()); |
| return client_->CreateDevToolsMessageLoop(); |
| } |
| |
| const WebString& WebSharedWorkerImpl::GetInstrumentationToken() { |
| return instrumentation_token_; |
| } |
| |
| void WebSharedWorkerImpl::CountFeature(WebFeature feature) { |
| DCHECK(IsMainThread()); |
| client_->CountFeature(feature); |
| } |
| |
| void WebSharedWorkerImpl::PostMessageToPageInspector(int session_id, |
| const String& message) { |
| DCHECK(IsMainThread()); |
| worker_inspector_proxy_->DispatchMessageFromWorker(session_id, message); |
| } |
| |
| void WebSharedWorkerImpl::DidCloseWorkerGlobalScope() { |
| DCHECK(IsMainThread()); |
| client_->WorkerContextClosed(); |
| TerminateWorkerThread(); |
| } |
| |
| void WebSharedWorkerImpl::DidTerminateWorkerThread() { |
| DCHECK(IsMainThread()); |
| client_->WorkerContextDestroyed(); |
| // The lifetime of this proxy is controlled by the worker context. |
| delete this; |
| } |
| |
| 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 |
| GetWorkerThread() |
| ->GetTaskRunner(TaskType::kDOMManipulation) |
| ->PostTask( |
| BLINK_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()); |
| WorkerGlobalScope* worker_global_scope = |
| ToWorkerGlobalScope(worker_thread_->GlobalScope()); |
| MessagePort* port = MessagePort::Create(*worker_global_scope); |
| port->Entangle(std::move(channel)); |
| SECURITY_DCHECK(worker_global_scope->IsSharedWorkerGlobalScope()); |
| worker_global_scope->DispatchEvent(CreateConnectEvent(port)); |
| } |
| |
| void WebSharedWorkerImpl::StartWorkerContext( |
| const WebURL& url, |
| const WebString& name, |
| const WebString& content_security_policy, |
| WebContentSecurityPolicyType policy_type, |
| WebAddressSpace creation_address_space, |
| const WebString& instrumentation_token, |
| mojo::ScopedMessagePipeHandle content_settings_handle, |
| mojo::ScopedMessagePipeHandle interface_provider) { |
| DCHECK(IsMainThread()); |
| url_ = 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)); |
| |
| instrumentation_token_ = instrumentation_token; |
| shadow_page_ = std::make_unique<WorkerShadowPage>(this); |
| |
| // 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(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(); |
| |
| // The SharedWorker was unable to load the initial script, so |
| // shut it down right here. |
| delete this; |
| 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(); |
| const SecurityOrigin* starter_origin = document->GetSecurityOrigin(); |
| |
| 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_))); |
| |
| if (RuntimeEnabledFeatures::OffMainThreadFetchEnabled()) { |
| std::unique_ptr<WebWorkerFetchContext> web_worker_fetch_context = |
| client_->CreateWorkerFetchContext( |
| shadow_page_->DocumentLoader()->GetServiceWorkerNetworkProvider()); |
| DCHECK(web_worker_fetch_context); |
| web_worker_fetch_context->SetApplicationCacheHostID( |
| shadow_page_->GetDocument() |
| ->Fetcher() |
| ->Context() |
| .ApplicationCacheHostID()); |
| ProvideWorkerFetchContextToWorker(worker_clients, |
| std::move(web_worker_fetch_context)); |
| } |
| |
| ContentSecurityPolicy* content_security_policy = |
| main_script_loader_->ReleaseContentSecurityPolicy(); |
| ReferrerPolicy referrer_policy = kReferrerPolicyDefault; |
| if (!main_script_loader_->GetReferrerPolicy().IsNull()) { |
| SecurityPolicy::ReferrerPolicyFromHeaderValue( |
| main_script_loader_->GetReferrerPolicy(), |
| kDoNotSupportReferrerPolicyLegacyKeywords, &referrer_policy); |
| } |
| auto worker_settings = std::make_unique<WorkerSettings>( |
| shadow_page_->GetDocument()->GetFrame()->GetSettings()); |
| auto global_scope_creation_params = |
| std::make_unique<GlobalScopeCreationParams>( |
| url_, document->UserAgent(), |
| content_security_policy ? content_security_policy->Headers().get() |
| : nullptr, |
| referrer_policy, starter_origin, worker_clients, |
| main_script_loader_->ResponseAddressSpace(), |
| main_script_loader_->OriginTrialTokens(), std::move(worker_settings), |
| kV8CacheOptionsDefault, std::move(pending_interface_provider_)); |
| String source_code = main_script_loader_->SourceText(); |
| |
| // SharedWorker can sometimes run tasks that are initiated by/associated with |
| // a document's frame but these documents can be from a different process. So |
| // we intentionally populate the task runners with default task runners of the |
| // main thread. Note that |m_document| should not be used as it's a dummy |
| // document for loading that doesn't represent the frame of any associated |
| // document. |
| ParentFrameTaskRunners* task_runners = ParentFrameTaskRunners::Create(); |
| |
| reporting_proxy_ = new SharedWorkerReportingProxy(this, task_runners); |
| worker_thread_ = std::make_unique<SharedWorkerThread>( |
| name_, ThreadableLoadingContext::Create(*document), *reporting_proxy_); |
| probe::scriptImported(document, main_script_loader_->Identifier(), |
| main_script_loader_->SourceText()); |
| main_script_loader_ = nullptr; |
| |
| auto thread_startup_data = WorkerBackingThreadStartupData::CreateDefault(); |
| thread_startup_data.atomics_wait_mode = |
| WorkerBackingThreadStartupData::AtomicsWaitMode::kAllow; |
| |
| GetWorkerThread()->Start( |
| std::move(global_scope_creation_params), thread_startup_data, |
| std::make_unique<GlobalScopeInspectorCreationParams>( |
| worker_inspector_proxy_->ShouldPauseOnWorkerStart(document)), |
| task_runners, source_code); |
| worker_inspector_proxy_->WorkerThreadCreated(document, GetWorkerThread(), |
| url_); |
| client_->WorkerScriptLoaded(); |
| } |
| |
| void WebSharedWorkerImpl::TerminateWorkerContext() { |
| DCHECK(IsMainThread()); |
| TerminateWorkerThread(); |
| } |
| |
| void WebSharedWorkerImpl::PauseWorkerContextOnStart() { |
| pause_worker_context_on_start_ = true; |
| } |
| |
| void WebSharedWorkerImpl::AttachDevTools(int session_id) { |
| WebDevToolsAgent* devtools_agent = shadow_page_->DevToolsAgent(); |
| if (devtools_agent) |
| devtools_agent->Attach(session_id); |
| } |
| |
| void WebSharedWorkerImpl::ReattachDevTools(int session_id, |
| const WebString& saved_state) { |
| WebDevToolsAgent* devtools_agent = shadow_page_->DevToolsAgent(); |
| if (devtools_agent) |
| devtools_agent->Reattach(session_id, saved_state); |
| ResumeStartup(); |
| } |
| |
| void WebSharedWorkerImpl::DetachDevTools(int session_id) { |
| WebDevToolsAgent* devtools_agent = shadow_page_->DevToolsAgent(); |
| if (devtools_agent) |
| devtools_agent->Detach(session_id); |
| } |
| |
| void WebSharedWorkerImpl::DispatchDevToolsMessage(int session_id, |
| int call_id, |
| const WebString& method, |
| const WebString& message) { |
| if (asked_to_terminate_) |
| return; |
| WebDevToolsAgent* devtools_agent = shadow_page_->DevToolsAgent(); |
| if (devtools_agent) { |
| devtools_agent->DispatchOnInspectorBackend(session_id, call_id, method, |
| message); |
| } |
| } |
| |
| WebSharedWorker* WebSharedWorker::Create(WebSharedWorkerClient* client) { |
| return new WebSharedWorkerImpl(client); |
| } |
| |
| } // namespace blink |