blob: 1d246159a9d307c9bf4beceb4156bff067ebc5e8 [file] [log] [blame]
// Copyright 2014 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/modules/service_worker/service_worker_clients.h"
#include <utility>
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/memory/scoped_refptr.h"
#include "third_party/blink/public/mojom/service_worker/service_worker_client.mojom-blink.h"
#include "third_party/blink/renderer/bindings/core/v8/callback_promise_adapter.h"
#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
#include "third_party/blink/renderer/core/dom/dom_exception.h"
#include "third_party/blink/renderer/core/execution_context/execution_context.h"
#include "third_party/blink/renderer/core/workers/worker_location.h"
#include "third_party/blink/renderer/modules/service_worker/service_worker_error.h"
#include "third_party/blink/renderer/modules/service_worker/service_worker_global_scope.h"
#include "third_party/blink/renderer/modules/service_worker/service_worker_window_client.h"
#include "third_party/blink/renderer/platform/bindings/v8_throw_exception.h"
#include "third_party/blink/renderer/platform/heap/heap.h"
#include "third_party/blink/renderer/platform/wtf/vector.h"
namespace blink {
namespace {
mojom::ServiceWorkerClientType GetClientType(const String& type) {
if (type == "window")
return mojom::ServiceWorkerClientType::kWindow;
if (type == "worker")
return mojom::ServiceWorkerClientType::kDedicatedWorker;
if (type == "sharedworker")
return mojom::ServiceWorkerClientType::kSharedWorker;
if (type == "all")
return mojom::ServiceWorkerClientType::kAll;
NOTREACHED();
return mojom::ServiceWorkerClientType::kWindow;
}
void DidGetClient(ScriptPromiseResolver* resolver,
mojom::blink::ServiceWorkerClientInfoPtr info) {
if (!resolver->GetExecutionContext() ||
resolver->GetExecutionContext()->IsContextDestroyed()) {
return;
}
if (!info) {
// Resolve the promise with undefined.
resolver->Resolve();
return;
}
ServiceWorkerClient* client = nullptr;
switch (info->client_type) {
case mojom::ServiceWorkerClientType::kWindow:
client = MakeGarbageCollected<ServiceWorkerWindowClient>(*info);
break;
case mojom::ServiceWorkerClientType::kDedicatedWorker:
case mojom::ServiceWorkerClientType::kSharedWorker:
client = MakeGarbageCollected<ServiceWorkerClient>(*info);
break;
case mojom::ServiceWorkerClientType::kAll:
NOTREACHED();
return;
}
resolver->Resolve(client);
}
void DidClaim(ScriptPromiseResolver* resolver,
mojom::blink::ServiceWorkerErrorType error,
const String& error_msg) {
if (!resolver->GetExecutionContext() ||
resolver->GetExecutionContext()->IsContextDestroyed()) {
return;
}
if (error != mojom::blink::ServiceWorkerErrorType::kNone) {
DCHECK(!error_msg.IsNull());
resolver->Reject(
ServiceWorkerError::GetException(resolver, error, error_msg));
return;
}
DCHECK(error_msg.IsNull());
resolver->Resolve();
}
void DidGetClients(ScriptPromiseResolver* resolver,
Vector<mojom::blink::ServiceWorkerClientInfoPtr> infos) {
if (!resolver->GetExecutionContext() ||
resolver->GetExecutionContext()->IsContextDestroyed()) {
return;
}
HeapVector<Member<ServiceWorkerClient>> clients;
for (const auto& info : infos) {
if (info->client_type == mojom::blink::ServiceWorkerClientType::kWindow)
clients.push_back(MakeGarbageCollected<ServiceWorkerWindowClient>(*info));
else
clients.push_back(MakeGarbageCollected<ServiceWorkerClient>(*info));
}
resolver->Resolve(std::move(clients));
}
} // namespace
ServiceWorkerClients* ServiceWorkerClients::Create() {
return MakeGarbageCollected<ServiceWorkerClients>();
}
ServiceWorkerClients::ServiceWorkerClients() = default;
ScriptPromise ServiceWorkerClients::get(ScriptState* script_state,
const String& id) {
ServiceWorkerGlobalScope* global_scope =
To<ServiceWorkerGlobalScope>(ExecutionContext::From(script_state));
// TODO(jungkees): May be null due to worker termination:
// http://crbug.com/413518.
if (!global_scope)
return ScriptPromise();
auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
global_scope->GetServiceWorkerHost()->GetClient(
id, WTF::Bind(&DidGetClient, WrapPersistent(resolver)));
return resolver->Promise();
}
ScriptPromise ServiceWorkerClients::matchAll(
ScriptState* script_state,
const ClientQueryOptions* options) {
ServiceWorkerGlobalScope* global_scope =
To<ServiceWorkerGlobalScope>(ExecutionContext::From(script_state));
// FIXME: May be null due to worker termination: http://crbug.com/413518.
if (!global_scope)
return ScriptPromise();
auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
global_scope->GetServiceWorkerHost()->GetClients(
mojom::blink::ServiceWorkerClientQueryOptions::New(
options->includeUncontrolled(), GetClientType(options->type())),
WTF::Bind(&DidGetClients, WrapPersistent(resolver)));
return resolver->Promise();
}
ScriptPromise ServiceWorkerClients::claim(ScriptState* script_state) {
ServiceWorkerGlobalScope* global_scope =
To<ServiceWorkerGlobalScope>(ExecutionContext::From(script_state));
// FIXME: May be null due to worker termination: http://crbug.com/413518.
if (!global_scope)
return ScriptPromise();
auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
global_scope->GetServiceWorkerHost()->ClaimClients(
WTF::Bind(&DidClaim, WrapPersistent(resolver)));
return resolver->Promise();
}
ScriptPromise ServiceWorkerClients::openWindow(ScriptState* script_state,
const String& url) {
auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
ScriptPromise promise = resolver->Promise();
ServiceWorkerGlobalScope* global_scope =
To<ServiceWorkerGlobalScope>(ExecutionContext::From(script_state));
KURL parsed_url = KURL(global_scope->location()->Url(), url);
if (!parsed_url.IsValid()) {
resolver->Reject(V8ThrowException::CreateTypeError(
script_state->GetIsolate(), "'" + url + "' is not a valid URL."));
return promise;
}
if (!global_scope->GetSecurityOrigin()->CanDisplay(parsed_url)) {
resolver->Reject(V8ThrowException::CreateTypeError(
script_state->GetIsolate(),
"'" + parsed_url.ElidedString() + "' cannot be opened."));
return promise;
}
if (!global_scope->IsWindowInteractionAllowed()) {
resolver->Reject(MakeGarbageCollected<DOMException>(
DOMExceptionCode::kInvalidAccessError,
"Not allowed to open a window."));
return promise;
}
global_scope->ConsumeWindowInteraction();
global_scope->GetServiceWorkerHost()->OpenNewTab(
parsed_url,
ServiceWorkerWindowClient::CreateResolveWindowClientCallback(resolver));
return promise;
}
} // namespace blink