blob: 67ea649583d9f8617c20398b26b85212a08ce2fe [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 "modules/serviceworkers/ServiceWorkerClients.h"
#include <memory>
#include <utility>
#include "base/memory/scoped_refptr.h"
#include "bindings/core/v8/CallbackPromiseAdapter.h"
#include "bindings/core/v8/ScriptPromiseResolver.h"
#include "core/dom/DOMException.h"
#include "core/dom/ExceptionCode.h"
#include "core/dom/ExecutionContext.h"
#include "core/workers/WorkerGlobalScope.h"
#include "core/workers/WorkerLocation.h"
#include "modules/serviceworkers/ServiceWorkerError.h"
#include "modules/serviceworkers/ServiceWorkerGlobalScopeClient.h"
#include "modules/serviceworkers/ServiceWorkerWindowClient.h"
#include "modules/serviceworkers/ServiceWorkerWindowClientCallback.h"
#include "platform/bindings/V8ThrowException.h"
#include "platform/wtf/PtrUtil.h"
#include "platform/wtf/Vector.h"
#include "public/platform/modules/serviceworker/WebServiceWorkerClientQueryOptions.h"
#include "public/platform/modules/serviceworker/WebServiceWorkerClientsInfo.h"
#include "third_party/WebKit/common/service_worker/service_worker_client.mojom-blink.h"
namespace blink {
namespace {
class ClientArray {
public:
using WebType = const WebServiceWorkerClientsInfo&;
static HeapVector<Member<ServiceWorkerClient>> Take(
ScriptPromiseResolver*,
const WebServiceWorkerClientsInfo& web_clients) {
HeapVector<Member<ServiceWorkerClient>> clients;
for (size_t i = 0; i < web_clients.clients.size(); ++i) {
const WebServiceWorkerClientInfo& client = web_clients.clients[i];
if (client.client_type == mojom::ServiceWorkerClientType::kWindow)
clients.push_back(ServiceWorkerWindowClient::Create(client));
else
clients.push_back(ServiceWorkerClient::Create(client));
}
return clients;
}
private:
WTF_MAKE_NONCOPYABLE(ClientArray);
ClientArray() = delete;
};
mojom::ServiceWorkerClientType GetClientType(const String& type) {
if (type == "window")
return mojom::ServiceWorkerClientType::kWindow;
if (type == "sharedworker")
return mojom::ServiceWorkerClientType::kSharedWorker;
if (type == "all")
return mojom::ServiceWorkerClientType::kAll;
NOTREACHED();
return mojom::ServiceWorkerClientType::kWindow;
}
class GetCallback : public WebServiceWorkerClientCallbacks {
public:
explicit GetCallback(ScriptPromiseResolver* resolver) : resolver_(resolver) {}
~GetCallback() override {}
void OnSuccess(
std::unique_ptr<WebServiceWorkerClientInfo> web_client) override {
std::unique_ptr<WebServiceWorkerClientInfo> client =
WTF::WrapUnique(web_client.release());
if (!resolver_->GetExecutionContext() ||
resolver_->GetExecutionContext()->IsContextDestroyed())
return;
if (!client) {
// Resolve the promise with undefined.
resolver_->Resolve();
return;
}
resolver_->Resolve(ServiceWorkerClient::Take(resolver_, std::move(client)));
}
void OnError(const WebServiceWorkerError& error) override {
if (!resolver_->GetExecutionContext() ||
resolver_->GetExecutionContext()->IsContextDestroyed())
return;
resolver_->Reject(ServiceWorkerError::Take(resolver_.Get(), error));
}
private:
Persistent<ScriptPromiseResolver> resolver_;
WTF_MAKE_NONCOPYABLE(GetCallback);
};
} // namespace
ServiceWorkerClients* ServiceWorkerClients::Create() {
return new ServiceWorkerClients();
}
ServiceWorkerClients::ServiceWorkerClients() {}
ScriptPromise ServiceWorkerClients::get(ScriptState* script_state,
const String& id) {
ExecutionContext* execution_context = ExecutionContext::From(script_state);
// TODO(jungkees): May be null due to worker termination:
// http://crbug.com/413518.
if (!execution_context)
return ScriptPromise();
ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state);
ScriptPromise promise = resolver->Promise();
ServiceWorkerGlobalScopeClient::From(execution_context)
->GetClient(id, std::make_unique<GetCallback>(resolver));
return promise;
}
ScriptPromise ServiceWorkerClients::matchAll(
ScriptState* script_state,
const ClientQueryOptions& options) {
ExecutionContext* execution_context = ExecutionContext::From(script_state);
// FIXME: May be null due to worker termination: http://crbug.com/413518.
if (!execution_context)
return ScriptPromise();
ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state);
ScriptPromise promise = resolver->Promise();
WebServiceWorkerClientQueryOptions web_options;
web_options.client_type = GetClientType(options.type());
web_options.include_uncontrolled = options.includeUncontrolled();
ServiceWorkerGlobalScopeClient::From(execution_context)
->GetClients(web_options,
std::make_unique<
CallbackPromiseAdapter<ClientArray, ServiceWorkerError>>(
resolver));
return promise;
}
ScriptPromise ServiceWorkerClients::claim(ScriptState* script_state) {
ExecutionContext* execution_context = ExecutionContext::From(script_state);
// FIXME: May be null due to worker termination: http://crbug.com/413518.
if (!execution_context)
return ScriptPromise();
ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state);
ScriptPromise promise = resolver->Promise();
auto callbacks =
std::make_unique<CallbackPromiseAdapter<void, ServiceWorkerError>>(
resolver);
ServiceWorkerGlobalScopeClient::From(execution_context)
->Claim(std::move(callbacks));
return promise;
}
ScriptPromise ServiceWorkerClients::openWindow(ScriptState* script_state,
const String& url) {
ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state);
ScriptPromise promise = resolver->Promise();
ExecutionContext* context = ExecutionContext::From(script_state);
KURL parsed_url = KURL(ToWorkerGlobalScope(context)->location()->Url(), url);
if (!parsed_url.IsValid()) {
resolver->Reject(V8ThrowException::CreateTypeError(
script_state->GetIsolate(), "'" + url + "' is not a valid URL."));
return promise;
}
if (!context->GetSecurityOrigin()->CanDisplay(parsed_url)) {
resolver->Reject(V8ThrowException::CreateTypeError(
script_state->GetIsolate(),
"'" + parsed_url.ElidedString() + "' cannot be opened."));
return promise;
}
if (!context->IsWindowInteractionAllowed()) {
resolver->Reject(DOMException::Create(kInvalidAccessError,
"Not allowed to open a window."));
return promise;
}
context->ConsumeWindowInteraction();
ServiceWorkerGlobalScopeClient::From(context)->OpenWindowForClients(
parsed_url, std::make_unique<NavigateClientCallback>(resolver));
return promise;
}
} // namespace blink