1// Copyright 2013 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_context_wrapper.h"
6
7#include <map>
8
9#include "base/files/file_path.h"
10#include "base/logging.h"
11#include "base/threading/sequenced_worker_pool.h"
12#include "content/browser/fileapi/chrome_blob_storage_context.h"
13#include "content/browser/service_worker/service_worker_context_core.h"
14#include "content/browser/service_worker/service_worker_context_observer.h"
15#include "content/browser/service_worker/service_worker_process_manager.h"
16#include "content/public/browser/browser_context.h"
17#include "content/public/browser/browser_thread.h"
18#include "net/url_request/url_request_context_getter.h"
19#include "storage/browser/blob/blob_storage_context.h"
20#include "storage/browser/quota/quota_manager_proxy.h"
21
22namespace content {
23
24ServiceWorkerContextWrapper::ServiceWorkerContextWrapper(
25    BrowserContext* browser_context)
26    : observer_list_(
27          new ObserverListThreadSafe<ServiceWorkerContextObserver>()),
28      process_manager_(new ServiceWorkerProcessManager(browser_context)),
29      is_incognito_(false) {
30}
31
32ServiceWorkerContextWrapper::~ServiceWorkerContextWrapper() {
33}
34
35void ServiceWorkerContextWrapper::Init(
36    const base::FilePath& user_data_directory,
37    storage::QuotaManagerProxy* quota_manager_proxy) {
38  is_incognito_ = user_data_directory.empty();
39  scoped_refptr<base::SequencedTaskRunner> database_task_runner =
40      BrowserThread::GetBlockingPool()->
41          GetSequencedTaskRunnerWithShutdownBehavior(
42              BrowserThread::GetBlockingPool()->GetSequenceToken(),
43              base::SequencedWorkerPool::SKIP_ON_SHUTDOWN);
44  scoped_refptr<base::SingleThreadTaskRunner> disk_cache_thread =
45      BrowserThread::GetMessageLoopProxyForThread(BrowserThread::CACHE);
46  scoped_refptr<base::SequencedTaskRunner> cache_task_runner =
47      BrowserThread::GetBlockingPool()
48          ->GetSequencedTaskRunnerWithShutdownBehavior(
49              BrowserThread::GetBlockingPool()->GetSequenceToken(),
50              base::SequencedWorkerPool::SKIP_ON_SHUTDOWN);
51  InitInternal(user_data_directory,
52               cache_task_runner,
53               database_task_runner,
54               disk_cache_thread,
55               quota_manager_proxy);
56}
57
58void ServiceWorkerContextWrapper::Shutdown() {
59  DCHECK_CURRENTLY_ON(BrowserThread::UI);
60  process_manager_->Shutdown();
61  BrowserThread::PostTask(
62      BrowserThread::IO,
63      FROM_HERE,
64      base::Bind(&ServiceWorkerContextWrapper::ShutdownOnIO, this));
65}
66
67void ServiceWorkerContextWrapper::DeleteAndStartOver() {
68  DCHECK_CURRENTLY_ON(BrowserThread::IO);
69  context_core_->DeleteAndStartOver(
70      base::Bind(&ServiceWorkerContextWrapper::DidDeleteAndStartOver, this));
71}
72
73ServiceWorkerContextCore* ServiceWorkerContextWrapper::context() {
74  DCHECK_CURRENTLY_ON(BrowserThread::IO);
75  return context_core_.get();
76}
77
78static void FinishRegistrationOnIO(
79    const ServiceWorkerContext::ResultCallback& continuation,
80    ServiceWorkerStatusCode status,
81    int64 registration_id,
82    int64 version_id) {
83  DCHECK_CURRENTLY_ON(BrowserThread::IO);
84  BrowserThread::PostTask(
85      BrowserThread::UI,
86      FROM_HERE,
87      base::Bind(continuation, status == SERVICE_WORKER_OK));
88}
89
90void ServiceWorkerContextWrapper::RegisterServiceWorker(
91    const GURL& pattern,
92    const GURL& script_url,
93    const ResultCallback& continuation) {
94  if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
95    BrowserThread::PostTask(
96        BrowserThread::IO,
97        FROM_HERE,
98        base::Bind(&ServiceWorkerContextWrapper::RegisterServiceWorker,
99                   this,
100                   pattern,
101                   script_url,
102                   continuation));
103    return;
104  }
105
106  context()->RegisterServiceWorker(
107      pattern,
108      script_url,
109      NULL /* provider_host */,
110      base::Bind(&FinishRegistrationOnIO, continuation));
111}
112
113static void FinishUnregistrationOnIO(
114    const ServiceWorkerContext::ResultCallback& continuation,
115    ServiceWorkerStatusCode status) {
116  DCHECK_CURRENTLY_ON(BrowserThread::IO);
117  BrowserThread::PostTask(
118      BrowserThread::UI,
119      FROM_HERE,
120      base::Bind(continuation, status == SERVICE_WORKER_OK));
121}
122
123void ServiceWorkerContextWrapper::UnregisterServiceWorker(
124    const GURL& pattern,
125    const ResultCallback& continuation) {
126  if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
127    BrowserThread::PostTask(
128        BrowserThread::IO,
129        FROM_HERE,
130        base::Bind(&ServiceWorkerContextWrapper::UnregisterServiceWorker,
131                   this,
132                   pattern,
133                   continuation));
134    return;
135  }
136
137  context()->UnregisterServiceWorker(
138      pattern,
139      base::Bind(&FinishUnregistrationOnIO, continuation));
140}
141
142void ServiceWorkerContextWrapper::Terminate() {
143  DCHECK_CURRENTLY_ON(BrowserThread::UI);
144  process_manager_->Shutdown();
145}
146
147void ServiceWorkerContextWrapper::GetAllOriginsInfo(
148    const GetUsageInfoCallback& callback) {
149  DCHECK_CURRENTLY_ON(BrowserThread::IO);
150  context_core_->storage()->GetAllRegistrations(base::Bind(
151      &ServiceWorkerContextWrapper::DidGetAllRegistrationsForGetAllOrigins,
152      this,
153      callback));
154}
155
156void ServiceWorkerContextWrapper::DidGetAllRegistrationsForGetAllOrigins(
157    const GetUsageInfoCallback& callback,
158    const std::vector<ServiceWorkerRegistrationInfo>& registrations) {
159  DCHECK_CURRENTLY_ON(BrowserThread::IO);
160  std::vector<ServiceWorkerUsageInfo> usage_infos;
161
162  std::map<GURL, ServiceWorkerUsageInfo> origins;
163  for (std::vector<ServiceWorkerRegistrationInfo>::const_iterator it =
164           registrations.begin();
165       it != registrations.end();
166       ++it) {
167    const ServiceWorkerRegistrationInfo& registration_info = *it;
168    GURL origin = registration_info.pattern.GetOrigin();
169
170    ServiceWorkerUsageInfo& usage_info = origins[origin];
171    if (usage_info.origin.is_empty())
172      usage_info.origin = origin;
173    usage_info.scopes.push_back(registration_info.pattern);
174  }
175
176  for (std::map<GURL, ServiceWorkerUsageInfo>::const_iterator it =
177           origins.begin();
178       it != origins.end();
179       ++it) {
180    usage_infos.push_back(it->second);
181  }
182
183  callback.Run(usage_infos);
184}
185
186namespace {
187
188void EmptySuccessCallback(bool success) {
189}
190
191}  // namespace
192
193void ServiceWorkerContextWrapper::DeleteForOrigin(const GURL& origin_url) {
194  DCHECK_CURRENTLY_ON(BrowserThread::IO);
195  context_core_->storage()->GetAllRegistrations(base::Bind(
196      &ServiceWorkerContextWrapper::DidGetAllRegistrationsForDeleteForOrigin,
197      this,
198      origin_url));
199}
200
201void ServiceWorkerContextWrapper::DidGetAllRegistrationsForDeleteForOrigin(
202    const GURL& origin,
203    const std::vector<ServiceWorkerRegistrationInfo>& registrations) {
204  DCHECK_CURRENTLY_ON(BrowserThread::IO);
205
206  for (std::vector<ServiceWorkerRegistrationInfo>::const_iterator it =
207           registrations.begin();
208       it != registrations.end();
209       ++it) {
210    const ServiceWorkerRegistrationInfo& registration_info = *it;
211    if (origin == registration_info.pattern.GetOrigin()) {
212      UnregisterServiceWorker(registration_info.pattern,
213                              base::Bind(&EmptySuccessCallback));
214    }
215  }
216}
217
218void ServiceWorkerContextWrapper::AddObserver(
219    ServiceWorkerContextObserver* observer) {
220  observer_list_->AddObserver(observer);
221}
222
223void ServiceWorkerContextWrapper::RemoveObserver(
224    ServiceWorkerContextObserver* observer) {
225  observer_list_->RemoveObserver(observer);
226}
227
228void ServiceWorkerContextWrapper::SetBlobParametersForCache(
229    net::URLRequestContextGetter* request_context,
230    ChromeBlobStorageContext* blob_storage_context) {
231  DCHECK_CURRENTLY_ON(BrowserThread::IO);
232
233  if (context_core_ && request_context && blob_storage_context) {
234    context_core_->SetBlobParametersForCache(
235        request_context->GetURLRequestContext(),
236        blob_storage_context->context()->AsWeakPtr());
237  }
238}
239
240void ServiceWorkerContextWrapper::InitInternal(
241    const base::FilePath& user_data_directory,
242    const scoped_refptr<base::SequencedTaskRunner>& stores_task_runner,
243    const scoped_refptr<base::SequencedTaskRunner>& database_task_runner,
244    const scoped_refptr<base::SingleThreadTaskRunner>& disk_cache_thread,
245    storage::QuotaManagerProxy* quota_manager_proxy) {
246  if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
247    BrowserThread::PostTask(
248        BrowserThread::IO,
249        FROM_HERE,
250        base::Bind(&ServiceWorkerContextWrapper::InitInternal,
251                   this,
252                   user_data_directory,
253                   stores_task_runner,
254                   database_task_runner,
255                   disk_cache_thread,
256                   make_scoped_refptr(quota_manager_proxy)));
257    return;
258  }
259  DCHECK(!context_core_);
260  context_core_.reset(new ServiceWorkerContextCore(user_data_directory,
261                                                   stores_task_runner,
262                                                   database_task_runner,
263                                                   disk_cache_thread,
264                                                   quota_manager_proxy,
265                                                   observer_list_.get(),
266                                                   this));
267}
268
269void ServiceWorkerContextWrapper::ShutdownOnIO() {
270  DCHECK_CURRENTLY_ON(BrowserThread::IO);
271  context_core_.reset();
272}
273
274void ServiceWorkerContextWrapper::DidDeleteAndStartOver(
275    ServiceWorkerStatusCode status) {
276  DCHECK_CURRENTLY_ON(BrowserThread::IO);
277  if (status != SERVICE_WORKER_OK) {
278    context_core_.reset();
279    return;
280  }
281  context_core_.reset(new ServiceWorkerContextCore(context_core_.get(), this));
282  DVLOG(1) << "Restarted ServiceWorkerContextCore successfully.";
283}
284
285}  // namespace content
286