| // 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 |