blob: fdc6f8b8ea1f4d870aa228e79c5b607226bf1540 [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 "core/exported/WebSharedWorkerImpl.h"
#include <memory>
#include "core/dom/Document.h"
#include "core/dom/TaskRunnerHelper.h"
#include "core/events/MessageEvent.h"
#include "core/exported/WebDataSourceImpl.h"
#include "core/exported/WebFactory.h"
#include "core/exported/WebViewBase.h"
#include "core/frame/WebLocalFrameBase.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/ParentFrameTaskRunners.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 "core/workers/WorkerThreadStartupData.h"
#include "platform/CrossThreadFunctional.h"
#include "platform/RuntimeEnabledFeatures.h"
#include "platform/heap/Handle.h"
#include "platform/heap/Persistent.h"
#include "platform/loader/fetch/ResourceResponse.h"
#include "platform/network/ContentSecurityPolicyParsers.h"
#include "platform/weborigin/KURL.h"
#include "platform/weborigin/SecurityOrigin.h"
#include "platform/wtf/Functional.h"
#include "platform/wtf/PtrUtil.h"
#include "public/platform/Platform.h"
#include "public/platform/WebContentSettingsClient.h"
#include "public/platform/WebMessagePortChannel.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/WebFrame.h"
#include "public/web/WebSettings.h"
#include "public/web/WebView.h"
namespace blink {
// TODO(toyoshim): Share implementation with WebEmbeddedWorkerImpl as much as
// possible.
template class CORE_TEMPLATE_EXPORT
WorkerClientsInitializer<WebSharedWorkerImpl>;
WebSharedWorkerImpl::WebSharedWorkerImpl(WebSharedWorkerClient* client)
: web_view_(nullptr),
main_frame_(nullptr),
asked_to_terminate_(false),
worker_inspector_proxy_(WorkerInspectorProxy::Create()),
client_(client),
pause_worker_context_on_start_(false),
is_paused_on_start_(false),
creation_address_space_(kWebAddressSpacePublic) {
DCHECK(IsMainThread());
}
WebSharedWorkerImpl::~WebSharedWorkerImpl() {
DCHECK(IsMainThread());
DCHECK(web_view_);
// Detach the client before closing the view to avoid getting called back.
main_frame_->SetClient(0);
web_view_->Close();
main_frame_->Close();
}
void WebSharedWorkerImpl::TerminateWorkerThread() {
DCHECK(IsMainThread());
if (asked_to_terminate_)
return;
asked_to_terminate_ = true;
if (main_script_loader_) {
main_script_loader_->Cancel();
main_script_loader_.Clear();
client_->WorkerScriptLoadFailed();
delete this;
return;
}
if (worker_thread_)
worker_thread_->Terminate();
worker_inspector_proxy_->WorkerThreadTerminated();
}
void WebSharedWorkerImpl::InitializeLoader(bool data_saver_enabled) {
DCHECK(IsMainThread());
// Create 'shadow page'. This page is never displayed, it is used to proxy the
// loading requests from the worker context to the rest of WebKit and Chromium
// infrastructure.
DCHECK(!web_view_);
web_view_ = WebFactory::GetInstance().CreateWebViewBase(
nullptr, kWebPageVisibilityStateVisible);
// FIXME: http://crbug.com/363843. This needs to find a better way to
// not create graphics layers.
web_view_->GetSettings()->SetAcceleratedCompositingEnabled(false);
web_view_->GetSettings()->SetDataSaverEnabled(data_saver_enabled);
// FIXME: Settings information should be passed to the Worker process from
// Browser process when the worker is created (similar to
// RenderThread::OnCreateNewView).
main_frame_ = WebFactory::GetInstance().CreateWebLocalFrameBase(
WebTreeScopeType::kDocument, this,
Platform::Current()->GetInterfaceProvider(), nullptr);
web_view_->SetMainFrame(main_frame_.Get());
main_frame_->SetDevToolsAgentClient(this);
// If we were asked to pause worker context on start and wait for debugger
// then it is the good time to do that.
client_->WorkerReadyForInspection();
if (pause_worker_context_on_start_) {
is_paused_on_start_ = true;
return;
}
LoadShadowPage();
}
std::unique_ptr<WebApplicationCacheHost>
WebSharedWorkerImpl::CreateApplicationCacheHost(
WebApplicationCacheHostClient* appcache_host_client) {
DCHECK(IsMainThread());
return client_->CreateApplicationCacheHost(appcache_host_client);
}
void WebSharedWorkerImpl::LoadShadowPage() {
DCHECK(IsMainThread());
// Construct substitute data source for the 'shadow page'. We only need it
// to have same origin as the worker so the loading checks work correctly.
CString content("");
RefPtr<SharedBuffer> buffer(
SharedBuffer::Create(content.data(), content.length()));
main_frame_->GetFrame()->Loader().Load(
FrameLoadRequest(0, ResourceRequest(url_),
SubstituteData(buffer, "text/html", "UTF-8", KURL())));
}
void WebSharedWorkerImpl::FrameDetached(WebLocalFrame* frame, DetachType type) {
DCHECK(type == DetachType::kRemove && frame->Parent());
DCHECK(frame->FrameWidget());
frame->Close();
}
void WebSharedWorkerImpl::DidFinishDocumentLoad() {
DCHECK(IsMainThread());
DCHECK(!loading_document_);
DCHECK(!main_script_loader_);
main_frame_->DataSource()->SetServiceWorkerNetworkProvider(
client_->CreateServiceWorkerNetworkProvider());
main_script_loader_ = WorkerScriptLoader::Create();
main_script_loader_->SetRequestContext(
WebURLRequest::kRequestContextSharedWorker);
loading_document_ = main_frame_->GetFrame()->GetDocument();
WebURLRequest::FetchRequestMode fetch_request_mode =
WebURLRequest::kFetchRequestModeSameOrigin;
WebURLRequest::FetchCredentialsMode fetch_credentials_mode =
WebURLRequest::kFetchCredentialsModeSameOrigin;
if ((static_cast<KURL>(url_)).ProtocolIsData()) {
fetch_request_mode = WebURLRequest::kFetchRequestModeNoCORS;
fetch_credentials_mode = WebURLRequest::kFetchCredentialsModeInclude;
}
main_script_loader_->LoadAsynchronously(
*loading_document_.Get(), url_, 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)
LoadShadowPage();
}
WebDevToolsAgentClient::WebKitClientMessageLoop*
WebSharedWorkerImpl::CreateClientMessageLoop() {
DCHECK(IsMainThread());
return client_->CreateDevToolsMessageLoop();
}
void WebSharedWorkerImpl::CountFeature(WebFeature feature) {
DCHECK(IsMainThread());
client_->CountFeature(static_cast<uint32_t>(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(
std::unique_ptr<WebMessagePortChannel> 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
TaskRunnerHelper::Get(TaskType::kDOMManipulation, GetWorkerThread())
->PostTask(
BLINK_FROM_HERE,
CrossThreadBind(&WebSharedWorkerImpl::ConnectTaskOnWorkerThread,
WTF::CrossThreadUnretained(this),
WTF::Passed(std::move(web_channel))));
}
void WebSharedWorkerImpl::ConnectTaskOnWorkerThread(
std::unique_ptr<WebMessagePortChannel> 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,
bool data_saver_enabled) {
DCHECK(IsMainThread());
url_ = url;
name_ = name;
creation_address_space_ = creation_address_space;
InitializeLoader(data_saver_enabled);
}
void WebSharedWorkerImpl::DidReceiveScriptLoaderResponse() {
DCHECK(IsMainThread());
probe::didReceiveScriptResponse(loading_document_,
main_script_loader_->Identifier());
client_->SelectAppCacheID(main_script_loader_->AppCacheID());
}
void WebSharedWorkerImpl::OnScriptLoaderFinished() {
DCHECK(IsMainThread());
DCHECK(loading_document_);
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)
SecurityOrigin* starter_origin = loading_document_->GetSecurityOrigin();
WorkerClients* worker_clients = WorkerClients::Create();
WorkerClientsInitializer<WebSharedWorkerImpl>::Run(worker_clients);
WebSecurityOrigin web_security_origin(loading_document_->GetSecurityOrigin());
ProvideContentSettingsClientToWorker(
worker_clients,
client_->CreateWorkerContentSettingsClient(web_security_origin));
if (RuntimeEnabledFeatures::OffMainThreadFetchEnabled()) {
std::unique_ptr<WebWorkerFetchContext> web_worker_fetch_context =
client_->CreateWorkerFetchContext(
WebLocalFrameBase::FromFrame(main_frame_->GetFrame())
->DataSource()
->GetServiceWorkerNetworkProvider());
DCHECK(web_worker_fetch_context);
// TODO(horo): Set more information about the context (ex: AppCacheHostID)
// to |web_worker_fetch_context|.
web_worker_fetch_context->SetDataSaverEnabled(
main_frame_->GetFrame()->GetSettings()->GetDataSaverEnabled());
ProvideWorkerFetchContextToWorker(worker_clients,
std::move(web_worker_fetch_context));
}
ContentSecurityPolicy* content_security_policy =
main_script_loader_->ReleaseContentSecurityPolicy();
WorkerThreadStartMode start_mode =
worker_inspector_proxy_->WorkerStartMode(loading_document_);
std::unique_ptr<WorkerSettings> worker_settings = WTF::WrapUnique(
new WorkerSettings(main_frame_->GetFrame()->GetSettings()));
WorkerV8Settings worker_v8_settings = WorkerV8Settings::Default();
worker_v8_settings.atomics_wait_mode_ =
WorkerV8Settings::AtomicsWaitMode::kAllow;
std::unique_ptr<WorkerThreadStartupData> startup_data =
WorkerThreadStartupData::Create(
url_, loading_document_->UserAgent(),
main_script_loader_->SourceText(), nullptr, start_mode,
content_security_policy ? content_security_policy->Headers().get()
: nullptr,
main_script_loader_->GetReferrerPolicy(), starter_origin,
worker_clients, main_script_loader_->ResponseAddressSpace(),
main_script_loader_->OriginTrialTokens(), std::move(worker_settings),
worker_v8_settings);
// 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 null document in order to
// use the thread's default task runner. 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(nullptr);
reporting_proxy_ = new SharedWorkerReportingProxy(this, task_runners);
worker_thread_ = WTF::MakeUnique<SharedWorkerThread>(
name_,
ThreadableLoadingContext::Create(*ToDocument(loading_document_.Get())),
*reporting_proxy_);
probe::scriptImported(loading_document_, main_script_loader_->Identifier(),
main_script_loader_->SourceText());
main_script_loader_.Clear();
GetWorkerThread()->Start(std::move(startup_data), task_runners);
worker_inspector_proxy_->WorkerThreadCreated(ToDocument(loading_document_),
GetWorkerThread(), url_);
client_->WorkerScriptLoaded();
}
void WebSharedWorkerImpl::TerminateWorkerContext() {
DCHECK(IsMainThread());
TerminateWorkerThread();
}
void WebSharedWorkerImpl::PauseWorkerContextOnStart() {
pause_worker_context_on_start_ = true;
}
void WebSharedWorkerImpl::AttachDevTools(const WebString& host_id,
int session_id) {
WebDevToolsAgent* devtools_agent = main_frame_->DevToolsAgent();
if (devtools_agent)
devtools_agent->Attach(host_id, session_id);
}
void WebSharedWorkerImpl::ReattachDevTools(const WebString& host_id,
int session_id,
const WebString& saved_state) {
WebDevToolsAgent* devtools_agent = main_frame_->DevToolsAgent();
if (devtools_agent)
devtools_agent->Reattach(host_id, session_id, saved_state);
ResumeStartup();
}
void WebSharedWorkerImpl::DetachDevTools(int session_id) {
WebDevToolsAgent* devtools_agent = main_frame_->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 = main_frame_->DevToolsAgent();
if (devtools_agent) {
devtools_agent->DispatchOnInspectorBackend(session_id, call_id, method,
message);
}
}
WebSharedWorker* WebSharedWorker::Create(WebSharedWorkerClient* client) {
return new WebSharedWorkerImpl(client);
}
} // namespace blink