blob: f849cbac7965da632177e1a91341bd0835f3c39c [file] [log] [blame]
// Copyright 2015 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/workers/dedicated_worker_messaging_proxy.h"
#include <memory>
#include "services/network/public/mojom/fetch_api.mojom-shared.h"
#include "third_party/blink/public/platform/task_type.h"
#include "third_party/blink/renderer/bindings/core/v8/sanitize_script_errors.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_cache_options.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/events/error_event.h"
#include "third_party/blink/renderer/core/events/message_event.h"
#include "third_party/blink/renderer/core/fetch/request.h"
#include "third_party/blink/renderer/core/frame/csp/content_security_policy.h"
#include "third_party/blink/renderer/core/inspector/thread_debugger.h"
#include "third_party/blink/renderer/core/messaging/blink_transferable_message.h"
#include "third_party/blink/renderer/core/workers/dedicated_worker.h"
#include "third_party/blink/renderer/core/workers/dedicated_worker_object_proxy.h"
#include "third_party/blink/renderer/core/workers/dedicated_worker_thread.h"
#include "third_party/blink/renderer/core/workers/worker_options.h"
#include "third_party/blink/renderer/platform/cross_thread_functional.h"
#include "third_party/blink/renderer/platform/scheduler/public/post_cross_thread_task.h"
#include "third_party/blink/renderer/platform/wtf/wtf.h"
namespace blink {
DedicatedWorkerMessagingProxy::DedicatedWorkerMessagingProxy(
ExecutionContext* execution_context,
DedicatedWorker* worker_object)
: ThreadedMessagingProxyBase(execution_context),
worker_object_(worker_object) {
worker_object_proxy_ = DedicatedWorkerObjectProxy::Create(
this, GetParentExecutionContextTaskRunners());
}
DedicatedWorkerMessagingProxy::~DedicatedWorkerMessagingProxy() = default;
void DedicatedWorkerMessagingProxy::StartWorkerGlobalScope(
std::unique_ptr<GlobalScopeCreationParams> creation_params,
const WorkerOptions* options,
const KURL& script_url,
FetchClientSettingsObjectSnapshot* outside_settings_object,
const v8_inspector::V8StackTraceId& stack_id,
const String& source_code) {
DCHECK(IsParentContextThread());
if (AskedToTerminate()) {
// Worker.terminate() could be called from JS before the thread was
// created.
return;
}
InitializeWorkerThread(
std::move(creation_params),
CreateBackingThreadStartupData(ToIsolate(GetExecutionContext())));
// Step 13: "Obtain script by switching on the value of options's type
// member:"
if (options->type() == "classic") {
// "classic: Fetch a classic worker script given url, outside settings,
// destination, and inside settings."
if (RuntimeEnabledFeatures::OffMainThreadWorkerScriptFetchEnabled()) {
GetWorkerThread()->ImportClassicScript(script_url,
outside_settings_object, stack_id);
} else {
// Legacy code path (to be deprecated, see https://crbug.com/835717):
GetWorkerThread()->EvaluateClassicScript(
script_url, source_code, nullptr /* cached_meta_data */, stack_id);
}
} else if (options->type() == "module") {
// "module: Fetch a module worker script graph given url, outside settings,
// destination, the value of the credentials member of options, and inside
// settings."
network::mojom::FetchCredentialsMode credentials_mode;
bool result = Request::ParseCredentialsMode(options->credentials(),
&credentials_mode);
DCHECK(result);
GetWorkerThread()->ImportModuleScript(script_url, outside_settings_object,
credentials_mode);
} else {
NOTREACHED();
}
}
void DedicatedWorkerMessagingProxy::PostMessageToWorkerGlobalScope(
BlinkTransferableMessage message) {
DCHECK(IsParentContextThread());
if (AskedToTerminate())
return;
if (!was_script_evaluated_) {
queued_early_tasks_.push_back(std::move(message));
return;
}
PostCrossThreadTask(
*GetWorkerThread()->GetTaskRunner(TaskType::kPostedMessage), FROM_HERE,
CrossThreadBind(
&DedicatedWorkerObjectProxy::ProcessMessageFromWorkerObject,
CrossThreadUnretained(&WorkerObjectProxy()),
WTF::Passed(std::move(message)),
CrossThreadUnretained(GetWorkerThread())));
}
bool DedicatedWorkerMessagingProxy::HasPendingActivity() const {
DCHECK(IsParentContextThread());
return !AskedToTerminate();
}
void DedicatedWorkerMessagingProxy::DidEvaluateScript(bool success) {
DCHECK(IsParentContextThread());
was_script_evaluated_ = true;
Vector<BlinkTransferableMessage> tasks;
queued_early_tasks_.swap(tasks);
// The worker thread can already be terminated.
if (!GetWorkerThread()) {
DCHECK(AskedToTerminate());
return;
}
// Post all queued tasks to the worker.
// TODO(nhiroki): Consider whether to post the queued tasks to the worker when
// |success| is false.
for (auto& task : tasks) {
PostCrossThreadTask(
*GetWorkerThread()->GetTaskRunner(TaskType::kPostedMessage), FROM_HERE,
CrossThreadBind(
&DedicatedWorkerObjectProxy::ProcessMessageFromWorkerObject,
CrossThreadUnretained(&WorkerObjectProxy()),
WTF::Passed(std::move(task)),
CrossThreadUnretained(GetWorkerThread())));
}
}
void DedicatedWorkerMessagingProxy::PostMessageToWorkerObject(
BlinkTransferableMessage message) {
DCHECK(IsParentContextThread());
if (!worker_object_ || AskedToTerminate())
return;
ThreadDebugger* debugger =
ThreadDebugger::From(ToIsolate(GetExecutionContext()));
MessagePortArray* ports = MessagePort::EntanglePorts(
*GetExecutionContext(), std::move(message.ports));
debugger->ExternalAsyncTaskStarted(message.sender_stack_trace_id);
worker_object_->DispatchEvent(
*MessageEvent::Create(ports, std::move(message.message)));
debugger->ExternalAsyncTaskFinished(message.sender_stack_trace_id);
}
void DedicatedWorkerMessagingProxy::DispatchErrorEvent(
const String& error_message,
std::unique_ptr<SourceLocation> location,
int exception_id) {
DCHECK(IsParentContextThread());
if (!worker_object_)
return;
// We don't bother checking the AskedToTerminate() flag for dispatching the
// event on the owner context, because exceptions should *always* be reported
// even if the thread is terminated as the spec says:
//
// "Thus, error reports propagate up to the chain of dedicated workers up to
// the original Document, even if some of the workers along this chain have
// been terminated and garbage collected."
// https://html.spec.whatwg.org/multipage/workers.html#runtime-script-errors-2
ErrorEvent* event =
ErrorEvent::Create(error_message, location->Clone(), nullptr);
if (worker_object_->DispatchEvent(*event) !=
DispatchEventResult::kNotCanceled)
return;
// The worker thread can already be terminated.
if (!GetWorkerThread()) {
DCHECK(AskedToTerminate());
return;
}
// The HTML spec requires to queue an error event using the DOM manipulation
// task source.
// https://html.spec.whatwg.org/multipage/workers.html#runtime-script-errors-2
PostCrossThreadTask(
*GetWorkerThread()->GetTaskRunner(TaskType::kDOMManipulation), FROM_HERE,
CrossThreadBind(&DedicatedWorkerObjectProxy::ProcessUnhandledException,
CrossThreadUnretained(worker_object_proxy_.get()),
exception_id, CrossThreadUnretained(GetWorkerThread())));
// Propagate an unhandled error to the parent context.
const auto mute_script_errors = SanitizeScriptErrors::kDoNotSanitize;
GetExecutionContext()->DispatchErrorEvent(event, mute_script_errors);
}
void DedicatedWorkerMessagingProxy::Trace(blink::Visitor* visitor) {
visitor->Trace(worker_object_);
ThreadedMessagingProxyBase::Trace(visitor);
}
base::Optional<WorkerBackingThreadStartupData>
DedicatedWorkerMessagingProxy::CreateBackingThreadStartupData(
v8::Isolate* isolate) {
using HeapLimitMode = WorkerBackingThreadStartupData::HeapLimitMode;
using AtomicsWaitMode = WorkerBackingThreadStartupData::AtomicsWaitMode;
return WorkerBackingThreadStartupData(
isolate->IsHeapLimitIncreasedForDebugging()
? HeapLimitMode::kIncreasedForDebugging
: HeapLimitMode::kDefault,
AtomicsWaitMode::kAllow);
}
std::unique_ptr<WorkerThread>
DedicatedWorkerMessagingProxy::CreateWorkerThread() {
return DedicatedWorkerThread::Create(
worker_object_->Name(), GetExecutionContext(), WorkerObjectProxy());
}
} // namespace blink