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/logging.h"
12 #include "base/message_loop/message_loop.h"
13 #include "base/single_thread_task_runner.h"
14 #include "base/stl_util.h"
15 #include "content/browser/appcache/appcache.h"
16 #include "content/browser/appcache/appcache_backend_impl.h"
17 #include "content/browser/appcache/appcache_entry.h"
18 #include "content/browser/appcache/appcache_executable_handler.h"
19 #include "content/browser/appcache/appcache_histograms.h"
20 #include "content/browser/appcache/appcache_policy.h"
21 #include "content/browser/appcache/appcache_quota_client.h"
22 #include "content/browser/appcache/appcache_response.h"
23 #include "content/browser/appcache/appcache_service_impl.h"
24 #include "content/browser/appcache/appcache_storage_impl.h"
25 #include "net/base/completion_callback.h"
26 #include "net/base/io_buffer.h"
27 #include "storage/browser/quota/special_storage_policy.h"
33 void DeferredCallback(const net::CompletionCallback
& callback
, int rv
) {
39 AppCacheInfoCollection::AppCacheInfoCollection() {}
41 AppCacheInfoCollection::~AppCacheInfoCollection() {}
43 // AsyncHelper -------
45 class AppCacheServiceImpl::AsyncHelper
46 : public AppCacheStorage::Delegate
{
48 AsyncHelper(AppCacheServiceImpl
* service
,
49 const net::CompletionCallback
& callback
)
50 : service_(service
), callback_(callback
) {
51 service_
->pending_helpers_
.insert(this);
54 ~AsyncHelper() override
{
56 service_
->pending_helpers_
.erase(this);
59 virtual void Start() = 0;
60 virtual void Cancel();
63 void CallCallback(int rv
) {
64 if (!callback_
.is_null()) {
65 // Defer to guarantee async completion.
66 base::MessageLoop::current()->PostTask(
67 FROM_HERE
, base::Bind(&DeferredCallback
, callback_
, rv
));
72 AppCacheServiceImpl
* service_
;
73 net::CompletionCallback callback_
;
76 void AppCacheServiceImpl::AsyncHelper::Cancel() {
77 if (!callback_
.is_null()) {
78 callback_
.Run(net::ERR_ABORTED
);
81 service_
->storage()->CancelDelegateCallbacks(this);
85 // CanHandleOfflineHelper -------
87 class AppCacheServiceImpl::CanHandleOfflineHelper
: AsyncHelper
{
89 CanHandleOfflineHelper(
90 AppCacheServiceImpl
* service
, const GURL
& url
,
91 const GURL
& first_party
, const net::CompletionCallback
& callback
)
92 : AsyncHelper(service
, callback
),
94 first_party_(first_party
) {
97 void Start() override
{
98 AppCachePolicy
* policy
= service_
->appcache_policy();
99 if (policy
&& !policy
->CanLoadAppCache(url_
, first_party_
)) {
100 CallCallback(net::ERR_FAILED
);
105 service_
->storage()->FindResponseForMainRequest(url_
, GURL(), this);
109 // AppCacheStorage::Delegate implementation.
110 void OnMainResponseFound(const GURL
& url
,
111 const AppCacheEntry
& entry
,
112 const GURL
& fallback_url
,
113 const AppCacheEntry
& fallback_entry
,
116 const GURL
& mainfest_url
) override
;
121 DISALLOW_COPY_AND_ASSIGN(CanHandleOfflineHelper
);
124 void AppCacheServiceImpl::CanHandleOfflineHelper::OnMainResponseFound(
125 const GURL
& url
, const AppCacheEntry
& entry
,
126 const GURL
& fallback_url
, const AppCacheEntry
& fallback_entry
,
127 int64 cache_id
, int64 group_id
, const GURL
& manifest_url
) {
128 bool can
= (entry
.has_response_id() || fallback_entry
.has_response_id());
129 CallCallback(can
? net::OK
: net::ERR_FAILED
);
133 // DeleteHelper -------
135 class AppCacheServiceImpl::DeleteHelper
: public AsyncHelper
{
138 AppCacheServiceImpl
* service
, const GURL
& manifest_url
,
139 const net::CompletionCallback
& callback
)
140 : AsyncHelper(service
, callback
), manifest_url_(manifest_url
) {
143 void Start() override
{
144 service_
->storage()->LoadOrCreateGroup(manifest_url_
, this);
148 // AppCacheStorage::Delegate implementation.
149 void OnGroupLoaded(AppCacheGroup
* group
, const GURL
& manifest_url
) override
;
150 void OnGroupMadeObsolete(AppCacheGroup
* group
,
152 int response_code
) override
;
155 DISALLOW_COPY_AND_ASSIGN(DeleteHelper
);
158 void AppCacheServiceImpl::DeleteHelper::OnGroupLoaded(
159 AppCacheGroup
* group
, const GURL
& manifest_url
) {
161 group
->set_being_deleted(true);
162 group
->CancelUpdate();
163 service_
->storage()->MakeGroupObsolete(group
, this, 0);
165 CallCallback(net::ERR_FAILED
);
170 void AppCacheServiceImpl::DeleteHelper::OnGroupMadeObsolete(
171 AppCacheGroup
* group
,
174 CallCallback(success
? net::OK
: net::ERR_FAILED
);
178 // DeleteOriginHelper -------
180 class AppCacheServiceImpl::DeleteOriginHelper
: public AsyncHelper
{
183 AppCacheServiceImpl
* service
, const GURL
& origin
,
184 const net::CompletionCallback
& callback
)
185 : AsyncHelper(service
, callback
), origin_(origin
),
186 num_caches_to_delete_(0), successes_(0), failures_(0) {
189 void Start() override
{
190 // We start by listing all caches, continues in OnAllInfo().
191 service_
->storage()->GetAllInfo(this);
195 // AppCacheStorage::Delegate implementation.
196 void OnAllInfo(AppCacheInfoCollection
* collection
) override
;
197 void OnGroupLoaded(AppCacheGroup
* group
, const GURL
& manifest_url
) override
;
198 void OnGroupMadeObsolete(AppCacheGroup
* group
,
200 int response_code
) override
;
202 void CacheCompleted(bool success
);
205 int num_caches_to_delete_
;
209 DISALLOW_COPY_AND_ASSIGN(DeleteOriginHelper
);
212 void AppCacheServiceImpl::DeleteOriginHelper::OnAllInfo(
213 AppCacheInfoCollection
* collection
) {
215 // Failed to get a listing.
216 CallCallback(net::ERR_FAILED
);
221 std::map
<GURL
, AppCacheInfoVector
>::iterator found
=
222 collection
->infos_by_origin
.find(origin_
);
223 if (found
== collection
->infos_by_origin
.end() || found
->second
.empty()) {
224 // No caches for this origin.
225 CallCallback(net::OK
);
230 // We have some caches to delete.
231 const AppCacheInfoVector
& caches_to_delete
= found
->second
;
234 num_caches_to_delete_
= static_cast<int>(caches_to_delete
.size());
235 for (AppCacheInfoVector::const_iterator iter
= caches_to_delete
.begin();
236 iter
!= caches_to_delete
.end(); ++iter
) {
237 service_
->storage()->LoadOrCreateGroup(iter
->manifest_url
, this);
241 void AppCacheServiceImpl::DeleteOriginHelper::OnGroupLoaded(
242 AppCacheGroup
* group
, const GURL
& manifest_url
) {
244 group
->set_being_deleted(true);
245 group
->CancelUpdate();
246 service_
->storage()->MakeGroupObsolete(group
, this, 0);
248 CacheCompleted(false);
252 void AppCacheServiceImpl::DeleteOriginHelper::OnGroupMadeObsolete(
253 AppCacheGroup
* group
,
256 CacheCompleted(success
);
259 void AppCacheServiceImpl::DeleteOriginHelper::CacheCompleted(bool success
) {
264 if ((successes_
+ failures_
) < num_caches_to_delete_
)
267 CallCallback(!failures_
? net::OK
: net::ERR_FAILED
);
272 // GetInfoHelper -------
274 class AppCacheServiceImpl::GetInfoHelper
: AsyncHelper
{
277 AppCacheServiceImpl
* service
, AppCacheInfoCollection
* collection
,
278 const net::CompletionCallback
& callback
)
279 : AsyncHelper(service
, callback
), collection_(collection
) {
282 void Start() override
{ service_
->storage()->GetAllInfo(this); }
285 // AppCacheStorage::Delegate implementation.
286 void OnAllInfo(AppCacheInfoCollection
* collection
) override
;
288 scoped_refptr
<AppCacheInfoCollection
> collection_
;
290 DISALLOW_COPY_AND_ASSIGN(GetInfoHelper
);
293 void AppCacheServiceImpl::GetInfoHelper::OnAllInfo(
294 AppCacheInfoCollection
* collection
) {
296 collection
->infos_by_origin
.swap(collection_
->infos_by_origin
);
297 CallCallback(collection
? net::OK
: net::ERR_FAILED
);
301 // CheckResponseHelper -------
303 class AppCacheServiceImpl::CheckResponseHelper
: AsyncHelper
{
306 AppCacheServiceImpl
* service
, const GURL
& manifest_url
, int64 cache_id
,
308 : AsyncHelper(service
, net::CompletionCallback()),
309 manifest_url_(manifest_url
),
311 response_id_(response_id
),
312 kIOBufferSize(32 * 1024),
313 expected_total_size_(0),
314 amount_headers_read_(0),
315 amount_data_read_(0) {
318 void Start() override
{
319 service_
->storage()->LoadOrCreateGroup(manifest_url_
, this);
322 void Cancel() override
{
323 AppCacheHistograms::CountCheckResponseResult(
324 AppCacheHistograms::CHECK_CANCELED
);
325 response_reader_
.reset();
326 AsyncHelper::Cancel();
330 void OnGroupLoaded(AppCacheGroup
* group
, const GURL
& manifest_url
) override
;
331 void OnReadInfoComplete(int result
);
332 void OnReadDataComplete(int result
);
334 // Inputs describing what to check.
339 // Internals used to perform the checks.
340 const int kIOBufferSize
;
341 scoped_refptr
<AppCache
> cache_
;
342 scoped_ptr
<AppCacheResponseReader
> response_reader_
;
343 scoped_refptr
<HttpResponseInfoIOBuffer
> info_buffer_
;
344 scoped_refptr
<net::IOBuffer
> data_buffer_
;
345 int64 expected_total_size_
;
346 int amount_headers_read_
;
347 int amount_data_read_
;
348 DISALLOW_COPY_AND_ASSIGN(CheckResponseHelper
);
351 void AppCacheServiceImpl::CheckResponseHelper::OnGroupLoaded(
352 AppCacheGroup
* group
, const GURL
& manifest_url
) {
353 DCHECK_EQ(manifest_url_
, manifest_url
);
354 if (!group
|| !group
->newest_complete_cache() || group
->is_being_deleted() ||
355 group
->is_obsolete()) {
356 AppCacheHistograms::CountCheckResponseResult(
357 AppCacheHistograms::MANIFEST_OUT_OF_DATE
);
362 cache_
= group
->newest_complete_cache();
363 const AppCacheEntry
* entry
= cache_
->GetEntryWithResponseId(response_id_
);
365 if (cache_
->cache_id() == cache_id_
) {
366 AppCacheHistograms::CountCheckResponseResult(
367 AppCacheHistograms::ENTRY_NOT_FOUND
);
368 service_
->DeleteAppCacheGroup(manifest_url_
, net::CompletionCallback());
370 AppCacheHistograms::CountCheckResponseResult(
371 AppCacheHistograms::RESPONSE_OUT_OF_DATE
);
377 // Verify that we can read the response info and data.
378 expected_total_size_
= entry
->response_size();
379 response_reader_
.reset(service_
->storage()->CreateResponseReader(
380 manifest_url_
, group
->group_id(), response_id_
));
381 info_buffer_
= new HttpResponseInfoIOBuffer();
382 response_reader_
->ReadInfo(
384 base::Bind(&CheckResponseHelper::OnReadInfoComplete
,
385 base::Unretained(this)));
388 void AppCacheServiceImpl::CheckResponseHelper::OnReadInfoComplete(int result
) {
390 AppCacheHistograms::CountCheckResponseResult(
391 AppCacheHistograms::READ_HEADERS_ERROR
);
392 service_
->DeleteAppCacheGroup(manifest_url_
, net::CompletionCallback());
396 amount_headers_read_
= result
;
398 // Start reading the data.
399 data_buffer_
= new net::IOBuffer(kIOBufferSize
);
400 response_reader_
->ReadData(
403 base::Bind(&CheckResponseHelper::OnReadDataComplete
,
404 base::Unretained(this)));
407 void AppCacheServiceImpl::CheckResponseHelper::OnReadDataComplete(int result
) {
409 // Keep reading until we've read thru everything or failed to read.
410 amount_data_read_
+= result
;
411 response_reader_
->ReadData(
414 base::Bind(&CheckResponseHelper::OnReadDataComplete
,
415 base::Unretained(this)));
419 AppCacheHistograms::CheckResponseResultType check_result
;
421 check_result
= AppCacheHistograms::READ_DATA_ERROR
;
422 else if (info_buffer_
->response_data_size
!= amount_data_read_
||
423 expected_total_size_
!= amount_data_read_
+ amount_headers_read_
)
424 check_result
= AppCacheHistograms::UNEXPECTED_DATA_SIZE
;
426 check_result
= AppCacheHistograms::RESPONSE_OK
;
427 AppCacheHistograms::CountCheckResponseResult(check_result
);
429 if (check_result
!= AppCacheHistograms::RESPONSE_OK
)
430 service_
->DeleteAppCacheGroup(manifest_url_
, net::CompletionCallback());
434 // AppCacheStorageReference ------
436 AppCacheStorageReference::AppCacheStorageReference(
437 scoped_ptr
<AppCacheStorage
> storage
)
438 : storage_(storage
.Pass()) {}
439 AppCacheStorageReference::~AppCacheStorageReference() {}
441 // AppCacheServiceImpl -------
443 AppCacheServiceImpl::AppCacheServiceImpl(
444 storage::QuotaManagerProxy
* quota_manager_proxy
)
445 : appcache_policy_(NULL
),
447 handler_factory_(NULL
),
448 quota_manager_proxy_(quota_manager_proxy
),
449 request_context_(NULL
),
450 force_keep_session_state_(false) {
451 if (quota_manager_proxy_
.get()) {
452 quota_client_
= new AppCacheQuotaClient(this);
453 quota_manager_proxy_
->RegisterClient(quota_client_
);
457 AppCacheServiceImpl::~AppCacheServiceImpl() {
458 DCHECK(backends_
.empty());
459 std::for_each(pending_helpers_
.begin(),
460 pending_helpers_
.end(),
461 std::mem_fun(&AsyncHelper::Cancel
));
462 STLDeleteElements(&pending_helpers_
);
464 quota_client_
->NotifyAppCacheDestroyed();
466 // Destroy storage_ first; ~AppCacheStorageImpl accesses other data members
467 // (special_storage_policy_).
471 void AppCacheServiceImpl::Initialize(
472 const base::FilePath
& cache_directory
,
473 const scoped_refptr
<base::SingleThreadTaskRunner
>& db_thread
,
474 const scoped_refptr
<base::SingleThreadTaskRunner
>& cache_thread
) {
475 DCHECK(!storage_
.get());
476 cache_directory_
= cache_directory
;
477 db_thread_
= db_thread
;
478 cache_thread_
= cache_thread
;
479 AppCacheStorageImpl
* storage
= new AppCacheStorageImpl(this);
480 storage
->Initialize(cache_directory
, db_thread
, cache_thread
);
481 storage_
.reset(storage
);
484 void AppCacheServiceImpl::ScheduleReinitialize() {
485 if (reinit_timer_
.IsRunning())
488 // Reinitialization only happens when corruption has been noticed.
489 // We don't want to thrash the disk but we also don't want to
490 // leave the appcache disabled for an indefinite period of time. Some
491 // users never shutdown the browser.
493 const base::TimeDelta kZeroDelta
;
494 const base::TimeDelta
kOneHour(base::TimeDelta::FromHours(1));
495 const base::TimeDelta
k30Seconds(base::TimeDelta::FromSeconds(30));
497 // If the system managed to stay up for long enough, reset the
498 // delay so a new failure won't incur a long wait to get going again.
499 base::TimeDelta up_time
= base::Time::Now() - last_reinit_time_
;
500 if (next_reinit_delay_
!= kZeroDelta
&& up_time
> kOneHour
)
501 next_reinit_delay_
= kZeroDelta
;
503 reinit_timer_
.Start(FROM_HERE
, next_reinit_delay_
,
504 this, &AppCacheServiceImpl::Reinitialize
);
506 // Adjust the delay for next time.
507 base::TimeDelta increment
= std::max(k30Seconds
, next_reinit_delay_
);
508 next_reinit_delay_
= std::min(next_reinit_delay_
+ increment
, kOneHour
);
511 void AppCacheServiceImpl::Reinitialize() {
512 AppCacheHistograms::CountReinitAttempt(!last_reinit_time_
.is_null());
513 last_reinit_time_
= base::Time::Now();
515 // Inform observers of about this and give them a chance to
516 // defer deletion of the old storage object.
517 scoped_refptr
<AppCacheStorageReference
>
518 old_storage_ref(new AppCacheStorageReference(storage_
.Pass()));
519 FOR_EACH_OBSERVER(Observer
, observers_
,
520 OnServiceReinitialized(old_storage_ref
.get()));
522 Initialize(cache_directory_
, db_thread_
, cache_thread_
);
525 void AppCacheServiceImpl::CanHandleMainResourceOffline(
527 const GURL
& first_party
,
528 const net::CompletionCallback
& callback
) {
529 CanHandleOfflineHelper
* helper
=
530 new CanHandleOfflineHelper(this, url
, first_party
, callback
);
534 void AppCacheServiceImpl::GetAllAppCacheInfo(
535 AppCacheInfoCollection
* collection
,
536 const net::CompletionCallback
& callback
) {
538 GetInfoHelper
* helper
= new GetInfoHelper(this, collection
, callback
);
542 void AppCacheServiceImpl::DeleteAppCacheGroup(
543 const GURL
& manifest_url
,
544 const net::CompletionCallback
& callback
) {
545 DeleteHelper
* helper
= new DeleteHelper(this, manifest_url
, callback
);
549 void AppCacheServiceImpl::DeleteAppCachesForOrigin(
550 const GURL
& origin
, const net::CompletionCallback
& callback
) {
551 DeleteOriginHelper
* helper
= new DeleteOriginHelper(this, origin
, callback
);
555 void AppCacheServiceImpl::CheckAppCacheResponse(const GURL
& manifest_url
,
558 CheckResponseHelper
* helper
= new CheckResponseHelper(
559 this, manifest_url
, cache_id
, response_id
);
563 void AppCacheServiceImpl::set_special_storage_policy(
564 storage::SpecialStoragePolicy
* policy
) {
565 special_storage_policy_
= policy
;
568 void AppCacheServiceImpl::RegisterBackend(
569 AppCacheBackendImpl
* backend_impl
) {
570 DCHECK(backends_
.find(backend_impl
->process_id()) == backends_
.end());
572 BackendMap::value_type(backend_impl
->process_id(), backend_impl
));
575 void AppCacheServiceImpl::UnregisterBackend(
576 AppCacheBackendImpl
* backend_impl
) {
577 backends_
.erase(backend_impl
->process_id());
580 } // namespace content