blob: 5df114d5bae06b9a1ecb90474177cc3bacc223a3 [file] [log] [blame]
// Copyright 2022 The Cobalt Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "cobalt/worker/clients.h"
#include <memory>
#include <string>
#include <utility>
#include "base/bind.h"
#include "base/memory/ref_counted.h"
#include "base/message_loop/message_loop.h"
#include "base/task_runner.h"
#include "base/trace_event/trace_event.h"
#include "cobalt/base/polymorphic_downcast.h"
#include "cobalt/script/environment_settings.h"
#include "cobalt/script/global_environment.h"
#include "cobalt/script/promise.h"
#include "cobalt/script/script_value_factory.h"
#include "cobalt/web/context.h"
#include "cobalt/web/dom_exception.h"
#include "cobalt/web/environment_settings.h"
#include "cobalt/worker/client.h"
#include "cobalt/worker/service_worker_global_scope.h"
#include "cobalt/worker/service_worker_jobs.h"
#include "cobalt/worker/service_worker_object.h"
namespace cobalt {
namespace worker {
namespace {
ServiceWorkerObject* GetAssociatedServiceWorker(
web::EnvironmentSettings* settings) {
// Ensure this runs in the right thread to dereferences the WeakPtr.
DCHECK_EQ(settings->context()->message_loop(), base::MessageLoop::current());
auto* global_scope = settings->context()->GetWindowOrWorkerGlobalScope();
DCHECK(global_scope->IsServiceWorker());
return global_scope->AsServiceWorker()->service_worker_object();
}
} // namespace
Clients::Clients(script::EnvironmentSettings* settings)
: settings_(
base::polymorphic_downcast<web::EnvironmentSettings*>(settings)) {}
script::HandlePromiseWrappable Clients::Get(const std::string& id) {
TRACE_EVENT0("cobalt::worker", "Clients::Get()");
DCHECK_EQ(base::MessageLoop::current(), settings_->context()->message_loop());
// Algorithm for get(id):
// https://w3c.github.io/ServiceWorker/#clients-get
// 1. Let promise be a new promise.
script::HandlePromiseWrappable promise =
settings_->context()
->global_environment()
->script_value_factory()
->CreateInterfacePromise<scoped_refptr<Client>>();
std::unique_ptr<script::ValuePromiseWrappable::Reference> promise_reference(
new script::ValuePromiseWrappable::Reference(this, promise));
// 2. Run these substeps in parallel:
ServiceWorkerJobs* jobs = settings_->context()->service_worker_jobs();
DCHECK(jobs);
jobs->message_loop()->task_runner()->PostTask(
FROM_HERE,
base::BindOnce(&ServiceWorkerJobs::ClientsGetSubSteps,
base::Unretained(jobs), base::Unretained(settings_),
base::Unretained(GetAssociatedServiceWorker(settings_)),
std::move(promise_reference), id));
// 3. Return promise.
return promise;
}
script::HandlePromiseSequenceWrappable Clients::MatchAll(
const ClientQueryOptions& options) {
TRACE_EVENT0("cobalt::worker", "Clients::MatchAll()");
DCHECK_EQ(base::MessageLoop::current(), settings_->context()->message_loop());
// Algorithm for matchAll():
// https://w3c.github.io/ServiceWorker/#clients-matchall
// 1. Let promise be a new promise.
auto promise = settings_->context()
->global_environment()
->script_value_factory()
->CreateBasicPromise<script::SequenceWrappable>();
std::unique_ptr<script::ValuePromiseSequenceWrappable::Reference>
promise_reference(
new script::ValuePromiseSequenceWrappable::Reference(this, promise));
// 2. Run the following steps in parallel:
ServiceWorkerJobs* jobs = settings_->context()->service_worker_jobs();
DCHECK(jobs);
jobs->message_loop()->task_runner()->PostTask(
FROM_HERE,
base::BindOnce(&ServiceWorkerJobs::ClientsMatchAllSubSteps,
base::Unretained(jobs), base::Unretained(settings_),
base::Unretained(GetAssociatedServiceWorker(settings_)),
std::move(promise_reference),
options.include_uncontrolled(), options.type()));
// 3. Return promise.
return promise;
}
script::HandlePromiseVoid Clients::Claim() {
TRACE_EVENT0("cobalt::worker", "Clients::Claim()");
DCHECK_EQ(base::MessageLoop::current(), settings_->context()->message_loop());
// Algorithm for claim():
// https://w3c.github.io/ServiceWorker/#clients-claim
// 2. Let promise be a new promise.
// Note: Done first because it's needed for rejecting in step 1.
auto promise = settings_->context()
->global_environment()
->script_value_factory()
->CreateBasicPromise<void>();
std::unique_ptr<script::ValuePromiseVoid::Reference> promise_reference(
new script::ValuePromiseVoid::Reference(this, promise));
// 1. If the service worker is not an active worker, return a promise rejected
// with an "InvalidStateError" DOMException.
ServiceWorkerObject* service_worker = GetAssociatedServiceWorker(settings_);
DCHECK(service_worker->containing_service_worker_registration());
if (service_worker != service_worker->containing_service_worker_registration()
->active_worker()) {
DCHECK(service_worker->state() != kServiceWorkerStateActivated &&
service_worker->state() != kServiceWorkerStateActivating);
// Perform the rest of the steps in a task, because the promise has to be
// returned before we can safely reject it.
base::MessageLoop::current()->task_runner()->PostTask(
FROM_HERE,
base::BindOnce(
[](std::unique_ptr<script::ValuePromiseVoid::Reference>
promise_reference) {
promise_reference->value().Reject(
new web::DOMException(web::DOMException::kInvalidStateErr));
},
std::move(promise_reference)));
return promise;
}
DCHECK(service_worker->state() == kServiceWorkerStateActivated ||
service_worker->state() == kServiceWorkerStateActivating);
// 3. Run the following substeps in parallel:
ServiceWorkerJobs* jobs = settings_->context()->service_worker_jobs();
DCHECK(jobs);
jobs->message_loop()->task_runner()->PostTask(
FROM_HERE,
base::BindOnce(&ServiceWorkerJobs::ClaimSubSteps, base::Unretained(jobs),
base::Unretained(settings_),
base::Unretained(GetAssociatedServiceWorker(settings_)),
std::move(promise_reference)));
// 4. Return promise.
return promise;
}
} // namespace worker
} // namespace cobalt