1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "content/browser/service_worker/service_worker_job_coordinator.h"
6 
7 #include <stddef.h>
8 
9 #include <memory>
10 #include <utility>
11 
12 #include "base/memory/ptr_util.h"
13 #include "content/browser/service_worker/service_worker_register_job_base.h"
14 #include "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom.h"
15 
16 namespace content {
17 
18 ServiceWorkerJobCoordinator::JobQueue::JobQueue() = default;
19 
20 ServiceWorkerJobCoordinator::JobQueue::JobQueue(JobQueue&&) = default;
21 
~JobQueue()22 ServiceWorkerJobCoordinator::JobQueue::~JobQueue() {
23   DCHECK(jobs_.empty()) << "Destroying JobQueue with " << jobs_.size()
24                         << " unfinished jobs";
25 }
26 
Push(std::unique_ptr<ServiceWorkerRegisterJobBase> job)27 ServiceWorkerRegisterJobBase* ServiceWorkerJobCoordinator::JobQueue::Push(
28     std::unique_ptr<ServiceWorkerRegisterJobBase> job) {
29   if (jobs_.empty()) {
30     jobs_.push_back(std::move(job));
31     StartOneJob();
32   } else if (!job->Equals(jobs_.back().get())) {
33     jobs_.push_back(std::move(job));
34   }
35   // Note we are releasing 'job' here in case neither of the two if() statements
36   // above were true.
37 
38   DCHECK(!jobs_.empty());
39   return jobs_.back().get();
40 }
41 
Pop(ServiceWorkerRegisterJobBase * job)42 void ServiceWorkerJobCoordinator::JobQueue::Pop(
43     ServiceWorkerRegisterJobBase* job) {
44   DCHECK(job == jobs_.front().get());
45   jobs_.pop_front();
46   if (!jobs_.empty())
47     StartOneJob();
48 }
49 
StartOneJob()50 void ServiceWorkerJobCoordinator::JobQueue::StartOneJob() {
51   DCHECK(!jobs_.empty());
52   jobs_.front()->Start();
53 }
54 
AbortAll()55 void ServiceWorkerJobCoordinator::JobQueue::AbortAll() {
56   for (const auto& job : jobs_)
57     job->Abort();
58   jobs_.clear();
59 }
60 
ClearForShutdown()61 void ServiceWorkerJobCoordinator::JobQueue::ClearForShutdown() {
62   for (const auto& job : jobs_)
63     job->WillShutDown();
64   jobs_.clear();
65 }
66 
ServiceWorkerJobCoordinator(ServiceWorkerContextCore * context)67 ServiceWorkerJobCoordinator::ServiceWorkerJobCoordinator(
68     ServiceWorkerContextCore* context)
69     : context_(context) {
70   DCHECK(context_);
71 }
72 
~ServiceWorkerJobCoordinator()73 ServiceWorkerJobCoordinator::~ServiceWorkerJobCoordinator() {
74   DCHECK(job_queues_.empty()) << "Destroying ServiceWorkerJobCoordinator with "
75                               << job_queues_.size() << " job queues";
76 }
77 
Register(const GURL & script_url,const blink::mojom::ServiceWorkerRegistrationOptions & options,blink::mojom::FetchClientSettingsObjectPtr outside_fetch_client_settings_object,ServiceWorkerRegisterJob::RegistrationCallback callback)78 void ServiceWorkerJobCoordinator::Register(
79     const GURL& script_url,
80     const blink::mojom::ServiceWorkerRegistrationOptions& options,
81     blink::mojom::FetchClientSettingsObjectPtr
82         outside_fetch_client_settings_object,
83     ServiceWorkerRegisterJob::RegistrationCallback callback) {
84   std::unique_ptr<ServiceWorkerRegisterJobBase> job(
85       new ServiceWorkerRegisterJob(
86           context_, script_url, options,
87           std::move(outside_fetch_client_settings_object)));
88   ServiceWorkerRegisterJob* queued_job = static_cast<ServiceWorkerRegisterJob*>(
89       job_queues_[options.scope].Push(std::move(job)));
90   queued_job->AddCallback(std::move(callback));
91 }
92 
Unregister(const GURL & scope,bool is_immediate,ServiceWorkerUnregisterJob::UnregistrationCallback callback)93 void ServiceWorkerJobCoordinator::Unregister(
94     const GURL& scope,
95     bool is_immediate,
96     ServiceWorkerUnregisterJob::UnregistrationCallback callback) {
97   std::unique_ptr<ServiceWorkerRegisterJobBase> job(
98       new ServiceWorkerUnregisterJob(context_, scope, is_immediate));
99   ServiceWorkerUnregisterJob* queued_job =
100       static_cast<ServiceWorkerUnregisterJob*>(
101           job_queues_[scope].Push(std::move(job)));
102   queued_job->AddCallback(std::move(callback));
103 }
104 
Update(ServiceWorkerRegistration * registration,bool force_bypass_cache)105 void ServiceWorkerJobCoordinator::Update(
106     ServiceWorkerRegistration* registration,
107     bool force_bypass_cache) {
108   DCHECK(registration);
109   // Use an empty fetch client settings object because this method is for
110   // browser-initiated update and there is no associated execution context.
111   job_queues_[registration->scope()].Push(
112       base::WrapUnique<ServiceWorkerRegisterJobBase>(
113           new ServiceWorkerRegisterJob(
114               context_, registration, force_bypass_cache,
115               false /* skip_script_comparison */,
116               blink::mojom::FetchClientSettingsObject::New())));
117 }
118 
Update(ServiceWorkerRegistration * registration,bool force_bypass_cache,bool skip_script_comparison,blink::mojom::FetchClientSettingsObjectPtr outside_fetch_client_settings_object,ServiceWorkerRegisterJob::RegistrationCallback callback)119 void ServiceWorkerJobCoordinator::Update(
120     ServiceWorkerRegistration* registration,
121     bool force_bypass_cache,
122     bool skip_script_comparison,
123     blink::mojom::FetchClientSettingsObjectPtr
124         outside_fetch_client_settings_object,
125     ServiceWorkerRegisterJob::RegistrationCallback callback) {
126   DCHECK(registration);
127   ServiceWorkerRegisterJob* queued_job = static_cast<ServiceWorkerRegisterJob*>(
128       job_queues_[registration->scope()].Push(
129           base::WrapUnique<ServiceWorkerRegisterJobBase>(
130               new ServiceWorkerRegisterJob(
131                   context_, registration, force_bypass_cache,
132                   skip_script_comparison,
133                   std::move(outside_fetch_client_settings_object)))));
134   queued_job->AddCallback(std::move(callback));
135 }
136 
Abort(const GURL & scope)137 void ServiceWorkerJobCoordinator::Abort(const GURL& scope) {
138   auto pending_jobs = job_queues_.find(scope);
139   if (pending_jobs == job_queues_.end())
140     return;
141   pending_jobs->second.AbortAll();
142   job_queues_.erase(pending_jobs);
143 }
144 
AbortAll()145 void ServiceWorkerJobCoordinator::AbortAll() {
146   for (auto& job_pair : job_queues_)
147     job_pair.second.AbortAll();
148   job_queues_.clear();
149 }
150 
ClearForShutdown()151 void ServiceWorkerJobCoordinator::ClearForShutdown() {
152   for (auto& job_pair : job_queues_)
153     job_pair.second.ClearForShutdown();
154   job_queues_.clear();
155 }
156 
FinishJob(const GURL & scope,ServiceWorkerRegisterJobBase * job)157 void ServiceWorkerJobCoordinator::FinishJob(const GURL& scope,
158                                             ServiceWorkerRegisterJobBase* job) {
159   auto pending_jobs = job_queues_.find(scope);
160   DCHECK(pending_jobs != job_queues_.end()) << "Deleting non-existent job.";
161   pending_jobs->second.Pop(job);
162   if (pending_jobs->second.empty())
163     job_queues_.erase(pending_jobs);
164 }
165 
166 }  // namespace content
167