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.
5 #include "content/browser/appcache/appcache_service_impl.h"
10 #include "base/bind_helpers.h"
11 #include "base/location.h"
12 #include "base/logging.h"
13 #include "base/single_thread_task_runner.h"
14 #include "base/stl_util.h"
15 #include "base/thread_task_runner_handle.h"
16 #include "content/browser/appcache/appcache.h"
17 #include "content/browser/appcache/appcache_backend_impl.h"
18 #include "content/browser/appcache/appcache_entry.h"
19 #include "content/browser/appcache/appcache_executable_handler.h"
20 #include "content/browser/appcache/appcache_histograms.h"
21 #include "content/browser/appcache/appcache_policy.h"
22 #include "content/browser/appcache/appcache_quota_client.h"
23 #include "content/browser/appcache/appcache_response.h"
24 #include "content/browser/appcache/appcache_service_impl.h"
25 #include "content/browser/appcache/appcache_storage_impl.h"
26 #include "net/base/completion_callback.h"
27 #include "net/base/io_buffer.h"
28 #include "storage/browser/quota/special_storage_policy.h"
34 void DeferredCallback(const net::CompletionCallback
& callback
, int rv
) {
40 AppCacheInfoCollection::AppCacheInfoCollection() {}
42 AppCacheInfoCollection::~AppCacheInfoCollection() {}
44 // AsyncHelper -------
46 class AppCacheServiceImpl::AsyncHelper
47 : public AppCacheStorage::Delegate
{
49 AsyncHelper(AppCacheServiceImpl
* service
,
50 const net::CompletionCallback
& callback
)
51 : service_(service
), callback_(callback
) {
52 service_
->pending_helpers_
.insert(this);
55 ~AsyncHelper() override
{
57 service_
->pending_helpers_
.erase(this);
60 virtual void Start() = 0;
61 virtual void Cancel();
64 void CallCallback(int rv
) {
65 if (!callback_
.is_null()) {
66 // Defer to guarantee async completion.
67 base::ThreadTaskRunnerHandle::Get()->PostTask(
68 FROM_HERE
, base::Bind(&DeferredCallback
, callback_
, rv
));
73 AppCacheServiceImpl
* service_
;
74 net::CompletionCallback callback_
;
77 void AppCacheServiceImpl::AsyncHelper::Cancel() {
78 if (!callback_
.is_null()) {
79 callback_
.Run(net::ERR_ABORTED
);
82 service_
->storage()->CancelDelegateCallbacks(this);
86 // CanHandleOfflineHelper -------
88 class AppCacheServiceImpl::CanHandleOfflineHelper
: AsyncHelper
{
90 CanHandleOfflineHelper(
91 AppCacheServiceImpl
* service
, const GURL
& url
,
92 const GURL
& first_party
, const net::CompletionCallback
& callback
)
93 : AsyncHelper(service
, callback
),
95 first_party_(first_party
) {
98 void Start() override
{
99 AppCachePolicy
* policy
= service_
->appcache_policy();
100 if (policy
&& !policy
->CanLoadAppCache(url_
, first_party_
)) {
101 CallCallback(net::ERR_FAILED
);
106 service_
->storage()->FindResponseForMainRequest(url_
, GURL(), this);
110 // AppCacheStorage::Delegate implementation.
111 void OnMainResponseFound(const GURL
& url
,
112 const AppCacheEntry
& entry
,
113 const GURL
& fallback_url
,
114 const AppCacheEntry
& fallback_entry
,
117 const GURL
& mainfest_url
) override
;
122 DISALLOW_COPY_AND_ASSIGN(CanHandleOfflineHelper
);
125 void AppCacheServiceImpl::CanHandleOfflineHelper::OnMainResponseFound(
126 const GURL
& url
, const AppCacheEntry
& entry
,
127 const GURL
& fallback_url
, const AppCacheEntry
& fallback_entry
,
128 int64 cache_id
, int64 group_id
, const GURL
& manifest_url
) {
129 bool can
= (entry
.has_response_id() || fallback_entry
.has_response_id());
130 CallCallback(can
? net::OK
: net::ERR_FAILED
);
134 // DeleteHelper -------
136 class AppCacheServiceImpl::DeleteHelper
: public AsyncHelper
{
139 AppCacheServiceImpl
* service
, const GURL
& manifest_url
,
140 const net::CompletionCallback
& callback
)
141 : AsyncHelper(service
, callback
), manifest_url_(manifest_url
) {
144 void Start() override
{
145 service_
->storage()->LoadOrCreateGroup(manifest_url_
, this);
149 // AppCacheStorage::Delegate implementation.
150 void OnGroupLoaded(AppCacheGroup
* group
, const GURL
& manifest_url
) override
;
151 void OnGroupMadeObsolete(AppCacheGroup
* group
,
153 int response_code
) override
;
156 DISALLOW_COPY_AND_ASSIGN(DeleteHelper
);
159 void AppCacheServiceImpl::DeleteHelper::OnGroupLoaded(
160 AppCacheGroup
* group
, const GURL
& manifest_url
) {
162 group
->set_being_deleted(true);
163 group
->CancelUpdate();
164 service_
->storage()->MakeGroupObsolete(group
, this, 0);
166 CallCallback(net::ERR_FAILED
);
171 void AppCacheServiceImpl::DeleteHelper::OnGroupMadeObsolete(
172 AppCacheGroup
* group
,
175 CallCallback(success
? net::OK
: net::ERR_FAILED
);
179 // DeleteOriginHelper -------
181 class AppCacheServiceImpl::DeleteOriginHelper
: public AsyncHelper
{
184 AppCacheServiceImpl
* service
, const GURL
& origin
,
185 const net::CompletionCallback
& callback
)
186 : AsyncHelper(service
, callback
), origin_(origin
),
187 num_caches_to_delete_(0), successes_(0), failures_(0) {
190 void Start() override
{
191 // We start by listing all caches, continues in OnAllInfo().
192 service_
->storage()->GetAllInfo(this);
196 // AppCacheStorage::Delegate implementation.
197 void OnAllInfo(AppCacheInfoCollection
* collection
) override
;
198 void OnGroupLoaded(AppCacheGroup
* group
, const GURL
& manifest_url
) override
;
199 void OnGroupMadeObsolete(AppCacheGroup
* group
,
201 int response_code
) override
;
203 void CacheCompleted(bool success
);
206 int num_caches_to_delete_
;
210 DISALLOW_COPY_AND_ASSIGN(DeleteOriginHelper
);
213 void AppCacheServiceImpl::DeleteOriginHelper::OnAllInfo(
214 AppCacheInfoCollection
* collection
) {
216 // Failed to get a listing.
217 CallCallback(net::ERR_FAILED
);
222 std::map
<GURL
, AppCacheInfoVector
>::iterator found
=
223 collection
->infos_by_origin
.find(origin_
);
224 if (found
== collection
->infos_by_origin
.end() || found
->second
.empty()) {
225 // No caches for this origin.
226 CallCallback(net::OK
);
231 // We have some caches to delete.
232 const AppCacheInfoVector
& caches_to_delete
= found
->second
;
235 num_caches_to_delete_
= static_cast<int>(caches_to_delete
.size());
236 for (AppCacheInfoVector::const_iterator iter
= caches_to_delete
.begin();
237 iter
!= caches_to_delete
.end(); ++iter
) {
238 service_
->storage()->LoadOrCreateGroup(iter
->manifest_url
, this);
242 void AppCacheServiceImpl::DeleteOriginHelper::OnGroupLoaded(
243 AppCacheGroup
* group
, const GURL
& manifest_url
) {
245 group
->set_being_deleted(true);
246 group
->CancelUpdate();
247 service_
->storage()->MakeGroupObsolete(group
, this, 0);
249 CacheCompleted(false);
253 void AppCacheServiceImpl::DeleteOriginHelper::OnGroupMadeObsolete(
254 AppCacheGroup
* group
,
257 CacheCompleted(success
);
260 void AppCacheServiceImpl::DeleteOriginHelper::CacheCompleted(bool success
) {
265 if ((successes_
+ failures_
) < num_caches_to_delete_
)
268 CallCallback(!failures_
? net::OK
: net::ERR_FAILED
);
273 // GetInfoHelper -------
275 class AppCacheServiceImpl::GetInfoHelper
: AsyncHelper
{
278 AppCacheServiceImpl
* service
, AppCacheInfoCollection
* collection
,
279 const net::CompletionCallback
& callback
)
280 : AsyncHelper(service
, callback
), collection_(collection
) {
283 void Start() override
{ service_
->storage()->GetAllInfo(this); }
286 // AppCacheStorage::Delegate implementation.
287 void OnAllInfo(AppCacheInfoCollection
* collection
) override
;
289 scoped_refptr
<AppCacheInfoCollection
> collection_
;
291 DISALLOW_COPY_AND_ASSIGN(GetInfoHelper
);
294 void AppCacheServiceImpl::GetInfoHelper::OnAllInfo(
295 AppCacheInfoCollection
* collection
) {
297 collection
->infos_by_origin
.swap(collection_
->infos_by_origin
);
298 CallCallback(collection
? net::OK
: net::ERR_FAILED
);
302 // CheckResponseHelper -------
304 class AppCacheServiceImpl::CheckResponseHelper
: AsyncHelper
{
307 AppCacheServiceImpl
* service
, const GURL
& manifest_url
, int64 cache_id
,
309 : AsyncHelper(service
, net::CompletionCallback()),
310 manifest_url_(manifest_url
),
312 response_id_(response_id
),
313 kIOBufferSize(32 * 1024),
314 expected_total_size_(0),
315 amount_headers_read_(0),
316 amount_data_read_(0) {
319 void Start() override
{
320 service_
->storage()->LoadOrCreateGroup(manifest_url_
, this);
323 void Cancel() override
{
324 AppCacheHistograms::CountCheckResponseResult(
325 AppCacheHistograms::CHECK_CANCELED
);
326 response_reader_
.reset();
327 AsyncHelper::Cancel();
331 void OnGroupLoaded(AppCacheGroup
* group
, const GURL
& manifest_url
) override
;
332 void OnReadInfoComplete(int result
);
333 void OnReadDataComplete(int result
);
335 // Inputs describing what to check.
340 // Internals used to perform the checks.
341 const int kIOBufferSize
;
342 scoped_refptr
<AppCache
> cache_
;
343 scoped_ptr
<AppCacheResponseReader
> response_reader_
;
344 scoped_refptr
<HttpResponseInfoIOBuffer
> info_buffer_
;
345 scoped_refptr
<net::IOBuffer
> data_buffer_
;
346 int64 expected_total_size_
;
347 int amount_headers_read_
;
348 int amount_data_read_
;
349 DISALLOW_COPY_AND_ASSIGN(CheckResponseHelper
);
352 void AppCacheServiceImpl::CheckResponseHelper::OnGroupLoaded(
353 AppCacheGroup
* group
, const GURL
& manifest_url
) {
354 DCHECK_EQ(manifest_url_
, manifest_url
);
355 if (!group
|| !group
->newest_complete_cache() || group
->is_being_deleted() ||
356 group
->is_obsolete()) {
357 AppCacheHistograms::CountCheckResponseResult(
358 AppCacheHistograms::MANIFEST_OUT_OF_DATE
);
363 cache_
= group
->newest_complete_cache();
364 const AppCacheEntry
* entry
= cache_
->GetEntryWithResponseId(response_id_
);
366 if (cache_
->cache_id() == cache_id_
) {
367 AppCacheHistograms::CountCheckResponseResult(
368 AppCacheHistograms::ENTRY_NOT_FOUND
);
369 service_
->DeleteAppCacheGroup(manifest_url_
, net::CompletionCallback());
371 AppCacheHistograms::CountCheckResponseResult(
372 AppCacheHistograms::RESPONSE_OUT_OF_DATE
);
378 // Verify that we can read the response info and data.
379 expected_total_size_
= entry
->response_size();
380 response_reader_
.reset(service_
->storage()->CreateResponseReader(
381 manifest_url_
, group
->group_id(), response_id_
));
382 info_buffer_
= new HttpResponseInfoIOBuffer();
383 response_reader_
->ReadInfo(
385 base::Bind(&CheckResponseHelper::OnReadInfoComplete
,
386 base::Unretained(this)));
389 void AppCacheServiceImpl::CheckResponseHelper::OnReadInfoComplete(int result
) {
391 AppCacheHistograms::CountCheckResponseResult(
392 AppCacheHistograms::READ_HEADERS_ERROR
);
393 service_
->DeleteAppCacheGroup(manifest_url_
, net::CompletionCallback());
397 amount_headers_read_
= result
;
399 // Start reading the data.
400 data_buffer_
= new net::IOBuffer(kIOBufferSize
);
401 response_reader_
->ReadData(
404 base::Bind(&CheckResponseHelper::OnReadDataComplete
,
405 base::Unretained(this)));
408 void AppCacheServiceImpl::CheckResponseHelper::OnReadDataComplete(int result
) {
410 // Keep reading until we've read thru everything or failed to read.
411 amount_data_read_
+= result
;
412 response_reader_
->ReadData(
415 base::Bind(&CheckResponseHelper::OnReadDataComplete
,
416 base::Unretained(this)));
420 AppCacheHistograms::CheckResponseResultType check_result
;
422 check_result
= AppCacheHistograms::READ_DATA_ERROR
;
423 else if (info_buffer_
->response_data_size
!= amount_data_read_
||
424 expected_total_size_
!= amount_data_read_
+ amount_headers_read_
)
425 check_result
= AppCacheHistograms::UNEXPECTED_DATA_SIZE
;
427 check_result
= AppCacheHistograms::RESPONSE_OK
;
428 AppCacheHistograms::CountCheckResponseResult(check_result
);
430 if (check_result
!= AppCacheHistograms::RESPONSE_OK
)
431 service_
->DeleteAppCacheGroup(manifest_url_
, net::CompletionCallback());
435 // AppCacheStorageReference ------
437 AppCacheStorageReference::AppCacheStorageReference(
438 scoped_ptr
<AppCacheStorage
> storage
)
439 : storage_(storage
.Pass()) {}
440 AppCacheStorageReference::~AppCacheStorageReference() {}
442 // AppCacheServiceImpl -------
444 AppCacheServiceImpl::AppCacheServiceImpl(
445 storage::QuotaManagerProxy
* quota_manager_proxy
)
446 : appcache_policy_(NULL
),
448 handler_factory_(NULL
),
449 quota_manager_proxy_(quota_manager_proxy
),
450 request_context_(NULL
),
451 force_keep_session_state_(false),
452 weak_factory_(this) {
453 if (quota_manager_proxy_
.get()) {
454 quota_client_
= new AppCacheQuotaClient(this);
455 quota_manager_proxy_
->RegisterClient(quota_client_
);
459 AppCacheServiceImpl::~AppCacheServiceImpl() {
460 DCHECK(backends_
.empty());
461 std::for_each(pending_helpers_
.begin(),
462 pending_helpers_
.end(),
463 std::mem_fun(&AsyncHelper::Cancel
));
464 STLDeleteElements(&pending_helpers_
);
466 quota_client_
->NotifyAppCacheDestroyed();
468 // Destroy storage_ first; ~AppCacheStorageImpl accesses other data members
469 // (special_storage_policy_).
473 void AppCacheServiceImpl::Initialize(
474 const base::FilePath
& cache_directory
,
475 const scoped_refptr
<base::SingleThreadTaskRunner
>& db_thread
,
476 const scoped_refptr
<base::SingleThreadTaskRunner
>& cache_thread
) {
477 DCHECK(!storage_
.get());
478 cache_directory_
= cache_directory
;
479 db_thread_
= db_thread
;
480 cache_thread_
= cache_thread
;
481 AppCacheStorageImpl
* storage
= new AppCacheStorageImpl(this);
482 storage
->Initialize(cache_directory
, db_thread
, cache_thread
);
483 storage_
.reset(storage
);
486 void AppCacheServiceImpl::ScheduleReinitialize() {
487 if (reinit_timer_
.IsRunning())
490 // Reinitialization only happens when corruption has been noticed.
491 // We don't want to thrash the disk but we also don't want to
492 // leave the appcache disabled for an indefinite period of time. Some
493 // users never shutdown the browser.
495 const base::TimeDelta kZeroDelta
;
496 const base::TimeDelta
kOneHour(base::TimeDelta::FromHours(1));
497 const base::TimeDelta
k30Seconds(base::TimeDelta::FromSeconds(30));
499 // If the system managed to stay up for long enough, reset the
500 // delay so a new failure won't incur a long wait to get going again.
501 base::TimeDelta up_time
= base::Time::Now() - last_reinit_time_
;
502 if (next_reinit_delay_
!= kZeroDelta
&& up_time
> kOneHour
)
503 next_reinit_delay_
= kZeroDelta
;
505 reinit_timer_
.Start(FROM_HERE
, next_reinit_delay_
,
506 this, &AppCacheServiceImpl::Reinitialize
);
508 // Adjust the delay for next time.
509 base::TimeDelta increment
= std::max(k30Seconds
, next_reinit_delay_
);
510 next_reinit_delay_
= std::min(next_reinit_delay_
+ increment
, kOneHour
);
513 void AppCacheServiceImpl::Reinitialize() {
514 AppCacheHistograms::CountReinitAttempt(!last_reinit_time_
.is_null());
515 last_reinit_time_
= base::Time::Now();
517 // Inform observers of about this and give them a chance to
518 // defer deletion of the old storage object.
519 scoped_refptr
<AppCacheStorageReference
>
520 old_storage_ref(new AppCacheStorageReference(storage_
.Pass()));
521 FOR_EACH_OBSERVER(Observer
, observers_
,
522 OnServiceReinitialized(old_storage_ref
.get()));
524 Initialize(cache_directory_
, db_thread_
, cache_thread_
);
527 void AppCacheServiceImpl::CanHandleMainResourceOffline(
529 const GURL
& first_party
,
530 const net::CompletionCallback
& callback
) {
531 CanHandleOfflineHelper
* helper
=
532 new CanHandleOfflineHelper(this, url
, first_party
, callback
);
536 void AppCacheServiceImpl::GetAllAppCacheInfo(
537 AppCacheInfoCollection
* collection
,
538 const net::CompletionCallback
& callback
) {
540 GetInfoHelper
* helper
= new GetInfoHelper(this, collection
, callback
);
544 void AppCacheServiceImpl::DeleteAppCacheGroup(
545 const GURL
& manifest_url
,
546 const net::CompletionCallback
& callback
) {
547 DeleteHelper
* helper
= new DeleteHelper(this, manifest_url
, callback
);
551 void AppCacheServiceImpl::DeleteAppCachesForOrigin(
552 const GURL
& origin
, const net::CompletionCallback
& callback
) {
553 DeleteOriginHelper
* helper
= new DeleteOriginHelper(this, origin
, callback
);
557 void AppCacheServiceImpl::CheckAppCacheResponse(const GURL
& manifest_url
,
560 CheckResponseHelper
* helper
= new CheckResponseHelper(
561 this, manifest_url
, cache_id
, response_id
);
565 void AppCacheServiceImpl::set_special_storage_policy(
566 storage::SpecialStoragePolicy
* policy
) {
567 special_storage_policy_
= policy
;
570 void AppCacheServiceImpl::RegisterBackend(
571 AppCacheBackendImpl
* backend_impl
) {
572 DCHECK(backends_
.find(backend_impl
->process_id()) == backends_
.end());
574 BackendMap::value_type(backend_impl
->process_id(), backend_impl
));
577 void AppCacheServiceImpl::UnregisterBackend(
578 AppCacheBackendImpl
* backend_impl
) {
579 backends_
.erase(backend_impl
->process_id());
582 } // namespace content