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.
5 #include "content/browser/service_worker/service_worker_context_wrapper.h"
12 #include "base/barrier_closure.h"
13 #include "base/bind.h"
14 #include "base/files/file_path.h"
15 #include "base/lazy_instance.h"
16 #include "base/logging.h"
17 #include "base/threading/sequenced_worker_pool.h"
18 #include "content/browser/service_worker/service_worker_context_core.h"
19 #include "content/browser/service_worker/service_worker_context_observer.h"
20 #include "content/browser/service_worker/service_worker_process_manager.h"
21 #include "content/browser/service_worker/service_worker_quota_client.h"
22 #include "content/browser/service_worker/service_worker_request_handler.h"
23 #include "content/browser/service_worker/service_worker_utils.h"
24 #include "content/browser/service_worker/service_worker_version.h"
25 #include "content/browser/storage_partition_impl.h"
26 #include "content/public/browser/browser_context.h"
27 #include "content/public/browser/browser_thread.h"
28 #include "content/public/browser/service_worker_context.h"
29 #include "net/base/net_errors.h"
30 #include "net/base/net_util.h"
31 #include "storage/browser/quota/quota_manager_proxy.h"
32 #include "storage/browser/quota/special_storage_policy.h"
38 typedef std::set
<std::string
> HeaderNameSet
;
39 base::LazyInstance
<HeaderNameSet
> g_excluded_header_name_set
=
40 LAZY_INSTANCE_INITIALIZER
;
42 void RunSoon(const base::Closure
& closure
) {
43 base::MessageLoop::current()->PostTask(FROM_HERE
, closure
);
46 void WorkerStarted(const ServiceWorkerContextWrapper::StatusCallback
& callback
,
47 ServiceWorkerStatusCode status
) {
48 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
49 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
,
50 base::Bind(callback
, status
));
53 void StartActiveWorkerOnIO(
54 const ServiceWorkerContextWrapper::StatusCallback
& callback
,
55 ServiceWorkerStatusCode status
,
56 const scoped_refptr
<ServiceWorkerRegistration
>& registration
) {
57 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
58 if (status
== SERVICE_WORKER_OK
) {
59 // Pass the reference of |registration| to WorkerStarted callback to prevent
60 // it from being deleted while starting the worker. If the refcount of
61 // |registration| is 1, it will be deleted after WorkerStarted is called.
62 registration
->active_version()->StartWorker(
63 base::Bind(WorkerStarted
, callback
));
66 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
,
67 base::Bind(callback
, SERVICE_WORKER_ERROR_NOT_FOUND
));
72 void ServiceWorkerContext::AddExcludedHeadersForFetchEvent(
73 const std::set
<std::string
>& header_names
) {
74 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
75 g_excluded_header_name_set
.Get().insert(header_names
.begin(),
79 bool ServiceWorkerContext::IsExcludedHeaderNameForFetchEvent(
80 const std::string
& header_name
) {
81 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
82 return g_excluded_header_name_set
.Get().find(header_name
) !=
83 g_excluded_header_name_set
.Get().end();
86 ServiceWorkerContext
* ServiceWorkerContext::GetServiceWorkerContext(
87 net::URLRequest
* request
) {
88 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
89 ServiceWorkerRequestHandler
* handler
=
90 ServiceWorkerRequestHandler::GetHandler(request
);
91 if (!handler
|| !handler
->context())
93 return handler
->context()->wrapper_
;
96 ServiceWorkerContextWrapper::ServiceWorkerContextWrapper(
97 BrowserContext
* browser_context
)
99 new ObserverListThreadSafe
<ServiceWorkerContextObserver
>()),
100 process_manager_(new ServiceWorkerProcessManager(browser_context
)),
101 is_incognito_(false),
102 storage_partition_(nullptr) {
103 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
106 ServiceWorkerContextWrapper::~ServiceWorkerContextWrapper() {
109 void ServiceWorkerContextWrapper::Init(
110 const base::FilePath
& user_data_directory
,
111 storage::QuotaManagerProxy
* quota_manager_proxy
,
112 storage::SpecialStoragePolicy
* special_storage_policy
) {
113 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
115 is_incognito_
= user_data_directory
.empty();
116 base::SequencedWorkerPool
* pool
= BrowserThread::GetBlockingPool();
117 scoped_ptr
<ServiceWorkerDatabaseTaskManager
> database_task_manager(
118 new ServiceWorkerDatabaseTaskManagerImpl(pool
));
119 scoped_refptr
<base::SingleThreadTaskRunner
> disk_cache_thread
=
120 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::CACHE
);
121 InitInternal(user_data_directory
,
122 database_task_manager
.Pass(),
125 special_storage_policy
);
128 void ServiceWorkerContextWrapper::Shutdown() {
129 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
131 storage_partition_
= nullptr;
132 process_manager_
->Shutdown();
133 BrowserThread::PostTask(
136 base::Bind(&ServiceWorkerContextWrapper::ShutdownOnIO
, this));
139 void ServiceWorkerContextWrapper::DeleteAndStartOver() {
140 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
141 context_core_
->DeleteAndStartOver(
142 base::Bind(&ServiceWorkerContextWrapper::DidDeleteAndStartOver
, this));
145 ServiceWorkerContextCore
* ServiceWorkerContextWrapper::context() {
146 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
147 return context_core_
.get();
150 StoragePartitionImpl
* ServiceWorkerContextWrapper::storage_partition() const {
151 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
152 return storage_partition_
;
155 void ServiceWorkerContextWrapper::set_storage_partition(
156 StoragePartitionImpl
* storage_partition
) {
157 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
158 storage_partition_
= storage_partition
;
161 static void FinishRegistrationOnIO(
162 const ServiceWorkerContext::ResultCallback
& continuation
,
163 ServiceWorkerStatusCode status
,
164 const std::string
& status_message
,
165 int64 registration_id
) {
166 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
167 BrowserThread::PostTask(
170 base::Bind(continuation
, status
== SERVICE_WORKER_OK
));
173 void ServiceWorkerContextWrapper::RegisterServiceWorker(
175 const GURL
& script_url
,
176 const ResultCallback
& continuation
) {
177 if (!BrowserThread::CurrentlyOn(BrowserThread::IO
)) {
178 BrowserThread::PostTask(
181 base::Bind(&ServiceWorkerContextWrapper::RegisterServiceWorker
,
188 if (!context_core_
.get()) {
189 LOG(ERROR
) << "ServiceWorkerContextCore is no longer alive.";
190 BrowserThread::PostTask(
193 base::Bind(continuation
, false));
196 context()->RegisterServiceWorker(
199 NULL
/* provider_host */,
200 base::Bind(&FinishRegistrationOnIO
, continuation
));
203 static void FinishUnregistrationOnIO(
204 const ServiceWorkerContext::ResultCallback
& continuation
,
205 ServiceWorkerStatusCode status
) {
206 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
207 BrowserThread::PostTask(
210 base::Bind(continuation
, status
== SERVICE_WORKER_OK
));
213 void ServiceWorkerContextWrapper::UnregisterServiceWorker(
215 const ResultCallback
& continuation
) {
216 if (!BrowserThread::CurrentlyOn(BrowserThread::IO
)) {
217 BrowserThread::PostTask(
220 base::Bind(&ServiceWorkerContextWrapper::UnregisterServiceWorker
,
226 if (!context_core_
.get()) {
227 LOG(ERROR
) << "ServiceWorkerContextCore is no longer alive.";
228 BrowserThread::PostTask(
231 base::Bind(continuation
, false));
235 context()->UnregisterServiceWorker(
237 base::Bind(&FinishUnregistrationOnIO
, continuation
));
240 void ServiceWorkerContextWrapper::StartServiceWorker(
242 const StatusCallback
& callback
) {
243 if (!BrowserThread::CurrentlyOn(BrowserThread::IO
)) {
244 BrowserThread::PostTask(
245 BrowserThread::IO
, FROM_HERE
,
246 base::Bind(&ServiceWorkerContextWrapper::StartServiceWorker
, this,
250 if (!context_core_
.get()) {
251 LOG(ERROR
) << "ServiceWorkerContextCore is no longer alive.";
252 BrowserThread::PostTask(
253 BrowserThread::UI
, FROM_HERE
,
254 base::Bind(callback
, SERVICE_WORKER_ERROR_START_WORKER_FAILED
));
257 context_core_
->storage()->FindRegistrationForPattern(
258 pattern
, base::Bind(&StartActiveWorkerOnIO
, callback
));
261 static void DidFindRegistrationForDocument(
262 const net::CompletionCallback
& callback
,
263 ServiceWorkerStatusCode status
,
264 const scoped_refptr
<ServiceWorkerRegistration
>& registration
) {
265 int rv
= registration
? net::OK
: net::ERR_CACHE_MISS
;
266 // Use RunSoon here because FindRegistrationForDocument can complete
267 // immediately but CanHandleMainResourceOffline must be async.
268 RunSoon(base::Bind(callback
, rv
));
271 void ServiceWorkerContextWrapper::CanHandleMainResourceOffline(
273 const GURL
& first_party
,
274 const net::CompletionCallback
& callback
) {
275 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
276 context()->storage()->FindRegistrationForDocument(
278 base::Bind(&DidFindRegistrationForDocument
, callback
));
281 void ServiceWorkerContextWrapper::GetAllOriginsInfo(
282 const GetUsageInfoCallback
& callback
) {
283 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
284 if (!context_core_
.get()) {
285 LOG(ERROR
) << "ServiceWorkerContextCore is no longer alive.";
286 BrowserThread::PostTask(
289 base::Bind(callback
, std::vector
<ServiceWorkerUsageInfo
>()));
292 context()->storage()->GetAllRegistrations(base::Bind(
293 &ServiceWorkerContextWrapper::DidGetAllRegistrationsForGetAllOrigins
,
298 void ServiceWorkerContextWrapper::DidGetAllRegistrationsForGetAllOrigins(
299 const GetUsageInfoCallback
& callback
,
300 const std::vector
<ServiceWorkerRegistrationInfo
>& registrations
) {
301 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
302 std::vector
<ServiceWorkerUsageInfo
> usage_infos
;
304 std::map
<GURL
, ServiceWorkerUsageInfo
> origins
;
305 for (const auto& registration_info
: registrations
) {
306 GURL origin
= registration_info
.pattern
.GetOrigin();
308 ServiceWorkerUsageInfo
& usage_info
= origins
[origin
];
309 if (usage_info
.origin
.is_empty())
310 usage_info
.origin
= origin
;
311 usage_info
.scopes
.push_back(registration_info
.pattern
);
312 usage_info
.total_size_bytes
+= registration_info
.stored_version_size_bytes
;
315 for (const auto& origin_info_pair
: origins
) {
316 usage_infos
.push_back(origin_info_pair
.second
);
318 callback
.Run(usage_infos
);
321 void ServiceWorkerContextWrapper::DidFindRegistrationForCheckHasServiceWorker(
322 const GURL
& other_url
,
323 const CheckHasServiceWorkerCallback
& callback
,
324 ServiceWorkerStatusCode status
,
325 const scoped_refptr
<ServiceWorkerRegistration
>& registration
) {
326 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
328 if (status
!= SERVICE_WORKER_OK
) {
329 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
,
330 base::Bind(callback
, false));
334 DCHECK(registration
);
335 BrowserThread::PostTask(
336 BrowserThread::UI
, FROM_HERE
,
337 base::Bind(callback
, registration
->active_version() &&
338 ServiceWorkerUtils::ScopeMatches(
339 registration
->pattern(), other_url
)));
343 void StatusCodeToBoolCallbackAdapter(
344 const ServiceWorkerContext::ResultCallback
& callback
,
345 ServiceWorkerStatusCode code
) {
346 callback
.Run(code
== ServiceWorkerStatusCode::SERVICE_WORKER_OK
);
349 void EmptySuccessCallback(bool success
) {
353 void ServiceWorkerContextWrapper::DeleteForOrigin(
354 const GURL
& origin_url
,
355 const ResultCallback
& result
) {
356 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
357 if (!context_core_
.get()) {
358 LOG(ERROR
) << "ServiceWorkerContextCore is no longer alive.";
359 BrowserThread::PostTask(
362 base::Bind(result
, false));
365 context()->UnregisterServiceWorkers(
366 origin_url
, base::Bind(&StatusCodeToBoolCallbackAdapter
, result
));
369 void ServiceWorkerContextWrapper::DeleteForOrigin(const GURL
& origin_url
) {
370 DeleteForOrigin(origin_url
, base::Bind(&EmptySuccessCallback
));
373 void ServiceWorkerContextWrapper::CheckHasServiceWorker(
375 const GURL
& other_url
,
376 const CheckHasServiceWorkerCallback
& callback
) {
377 if (!BrowserThread::CurrentlyOn(BrowserThread::IO
)) {
378 BrowserThread::PostTask(
379 BrowserThread::IO
, FROM_HERE
,
380 base::Bind(&ServiceWorkerContextWrapper::CheckHasServiceWorker
, this,
381 url
, other_url
, callback
));
384 if (!context_core_
.get()) {
385 LOG(ERROR
) << "ServiceWorkerContextCore is no longer alive.";
386 BrowserThread::PostTask(BrowserThread::IO
, FROM_HERE
,
387 base::Bind(callback
, false));
390 GURL stripped_url
= net::SimplifyUrlForRequest(url
);
391 context()->storage()->FindRegistrationForDocument(
392 stripped_url
, base::Bind(&ServiceWorkerContextWrapper::
393 DidFindRegistrationForCheckHasServiceWorker
,
394 this, other_url
, callback
));
397 void ServiceWorkerContextWrapper::AddObserver(
398 ServiceWorkerContextObserver
* observer
) {
399 observer_list_
->AddObserver(observer
);
402 void ServiceWorkerContextWrapper::RemoveObserver(
403 ServiceWorkerContextObserver
* observer
) {
404 observer_list_
->RemoveObserver(observer
);
407 void ServiceWorkerContextWrapper::InitInternal(
408 const base::FilePath
& user_data_directory
,
409 scoped_ptr
<ServiceWorkerDatabaseTaskManager
> database_task_manager
,
410 const scoped_refptr
<base::SingleThreadTaskRunner
>& disk_cache_thread
,
411 storage::QuotaManagerProxy
* quota_manager_proxy
,
412 storage::SpecialStoragePolicy
* special_storage_policy
) {
413 if (!BrowserThread::CurrentlyOn(BrowserThread::IO
)) {
414 BrowserThread::PostTask(
417 base::Bind(&ServiceWorkerContextWrapper::InitInternal
,
420 base::Passed(&database_task_manager
),
422 make_scoped_refptr(quota_manager_proxy
),
423 make_scoped_refptr(special_storage_policy
)));
426 DCHECK(!context_core_
);
427 if (quota_manager_proxy
) {
428 quota_manager_proxy
->RegisterClient(new ServiceWorkerQuotaClient(this));
430 context_core_
.reset(new ServiceWorkerContextCore(user_data_directory
,
431 database_task_manager
.Pass(),
434 special_storage_policy
,
435 observer_list_
.get(),
439 void ServiceWorkerContextWrapper::ShutdownOnIO() {
440 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
441 context_core_
.reset();
444 void ServiceWorkerContextWrapper::DidDeleteAndStartOver(
445 ServiceWorkerStatusCode status
) {
446 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
447 if (status
!= SERVICE_WORKER_OK
) {
448 context_core_
.reset();
451 context_core_
.reset(new ServiceWorkerContextCore(context_core_
.get(), this));
452 DVLOG(1) << "Restarted ServiceWorkerContextCore successfully.";
454 observer_list_
->Notify(FROM_HERE
,
455 &ServiceWorkerContextObserver::OnStorageWiped
);
458 } // namespace content