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 "webkit/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/stl_util.h"
14 #include "net/base/completion_callback.h"
15 #include "net/base/io_buffer.h"
16 #include "webkit/browser/appcache/appcache.h"
17 #include "webkit/browser/appcache/appcache_backend_impl.h"
18 #include "webkit/browser/appcache/appcache_entry.h"
19 #include "webkit/browser/appcache/appcache_executable_handler.h"
20 #include "webkit/browser/appcache/appcache_histograms.h"
21 #include "webkit/browser/appcache/appcache_policy.h"
22 #include "webkit/browser/appcache/appcache_quota_client.h"
23 #include "webkit/browser/appcache/appcache_response.h"
24 #include "webkit/browser/appcache/appcache_service_impl.h"
25 #include "webkit/browser/appcache/appcache_storage_impl.h"
26 #include "webkit/browser/quota/special_storage_policy.h"
32 void DeferredCallback(const net::CompletionCallback
& callback
, int rv
) {
38 AppCacheInfoCollection::AppCacheInfoCollection() {}
40 AppCacheInfoCollection::~AppCacheInfoCollection() {}
42 // AsyncHelper -------
44 class AppCacheServiceImpl::AsyncHelper
45 : public AppCacheStorage::Delegate
{
47 AsyncHelper(AppCacheServiceImpl
* service
,
48 const net::CompletionCallback
& callback
)
49 : service_(service
), callback_(callback
) {
50 service_
->pending_helpers_
.insert(this);
53 virtual ~AsyncHelper() {
55 service_
->pending_helpers_
.erase(this);
58 virtual void Start() = 0;
59 virtual void Cancel();
62 void CallCallback(int rv
) {
63 if (!callback_
.is_null()) {
64 // Defer to guarantee async completion.
65 base::MessageLoop::current()->PostTask(
66 FROM_HERE
, base::Bind(&DeferredCallback
, callback_
, rv
));
71 AppCacheServiceImpl
* service_
;
72 net::CompletionCallback callback_
;
75 void AppCacheServiceImpl::AsyncHelper::Cancel() {
76 if (!callback_
.is_null()) {
77 callback_
.Run(net::ERR_ABORTED
);
80 service_
->storage()->CancelDelegateCallbacks(this);
84 // CanHandleOfflineHelper -------
86 class AppCacheServiceImpl::CanHandleOfflineHelper
: AsyncHelper
{
88 CanHandleOfflineHelper(
89 AppCacheServiceImpl
* service
, const GURL
& url
,
90 const GURL
& first_party
, const net::CompletionCallback
& callback
)
91 : AsyncHelper(service
, callback
),
93 first_party_(first_party
) {
96 virtual void Start() OVERRIDE
{
97 AppCachePolicy
* policy
= service_
->appcache_policy();
98 if (policy
&& !policy
->CanLoadAppCache(url_
, first_party_
)) {
99 CallCallback(net::ERR_FAILED
);
104 service_
->storage()->FindResponseForMainRequest(url_
, GURL(), this);
108 // AppCacheStorage::Delegate implementation.
109 virtual void OnMainResponseFound(
110 const GURL
& url
, const AppCacheEntry
& entry
,
111 const GURL
& fallback_url
, const AppCacheEntry
& fallback_entry
,
112 int64 cache_id
, int64 group_id
, const GURL
& mainfest_url
) OVERRIDE
;
117 DISALLOW_COPY_AND_ASSIGN(CanHandleOfflineHelper
);
120 void AppCacheServiceImpl::CanHandleOfflineHelper::OnMainResponseFound(
121 const GURL
& url
, const AppCacheEntry
& entry
,
122 const GURL
& fallback_url
, const AppCacheEntry
& fallback_entry
,
123 int64 cache_id
, int64 group_id
, const GURL
& manifest_url
) {
124 bool can
= (entry
.has_response_id() || fallback_entry
.has_response_id());
125 CallCallback(can
? net::OK
: net::ERR_FAILED
);
129 // DeleteHelper -------
131 class AppCacheServiceImpl::DeleteHelper
: public AsyncHelper
{
134 AppCacheServiceImpl
* service
, const GURL
& manifest_url
,
135 const net::CompletionCallback
& callback
)
136 : AsyncHelper(service
, callback
), manifest_url_(manifest_url
) {
139 virtual void Start() OVERRIDE
{
140 service_
->storage()->LoadOrCreateGroup(manifest_url_
, this);
144 // AppCacheStorage::Delegate implementation.
145 virtual void OnGroupLoaded(
146 appcache::AppCacheGroup
* group
, const GURL
& manifest_url
) OVERRIDE
;
147 virtual void OnGroupMadeObsolete(appcache::AppCacheGroup
* group
,
149 int response_code
) OVERRIDE
;
152 DISALLOW_COPY_AND_ASSIGN(DeleteHelper
);
155 void AppCacheServiceImpl::DeleteHelper::OnGroupLoaded(
156 appcache::AppCacheGroup
* group
, const GURL
& manifest_url
) {
158 group
->set_being_deleted(true);
159 group
->CancelUpdate();
160 service_
->storage()->MakeGroupObsolete(group
, this, 0);
162 CallCallback(net::ERR_FAILED
);
167 void AppCacheServiceImpl::DeleteHelper::OnGroupMadeObsolete(
168 appcache::AppCacheGroup
* group
,
171 CallCallback(success
? net::OK
: net::ERR_FAILED
);
175 // DeleteOriginHelper -------
177 class AppCacheServiceImpl::DeleteOriginHelper
: public AsyncHelper
{
180 AppCacheServiceImpl
* service
, const GURL
& origin
,
181 const net::CompletionCallback
& callback
)
182 : AsyncHelper(service
, callback
), origin_(origin
),
183 num_caches_to_delete_(0), successes_(0), failures_(0) {
186 virtual void Start() OVERRIDE
{
187 // We start by listing all caches, continues in OnAllInfo().
188 service_
->storage()->GetAllInfo(this);
192 // AppCacheStorage::Delegate implementation.
193 virtual void OnAllInfo(AppCacheInfoCollection
* collection
) OVERRIDE
;
194 virtual void OnGroupLoaded(
195 appcache::AppCacheGroup
* group
, const GURL
& manifest_url
) OVERRIDE
;
196 virtual void OnGroupMadeObsolete(appcache::AppCacheGroup
* group
,
198 int response_code
) OVERRIDE
;
200 void CacheCompleted(bool success
);
203 int num_caches_to_delete_
;
207 DISALLOW_COPY_AND_ASSIGN(DeleteOriginHelper
);
210 void AppCacheServiceImpl::DeleteOriginHelper::OnAllInfo(
211 AppCacheInfoCollection
* collection
) {
213 // Failed to get a listing.
214 CallCallback(net::ERR_FAILED
);
219 std::map
<GURL
, AppCacheInfoVector
>::iterator found
=
220 collection
->infos_by_origin
.find(origin_
);
221 if (found
== collection
->infos_by_origin
.end() || found
->second
.empty()) {
222 // No caches for this origin.
223 CallCallback(net::OK
);
228 // We have some caches to delete.
229 const AppCacheInfoVector
& caches_to_delete
= found
->second
;
232 num_caches_to_delete_
= static_cast<int>(caches_to_delete
.size());
233 for (AppCacheInfoVector::const_iterator iter
= caches_to_delete
.begin();
234 iter
!= caches_to_delete
.end(); ++iter
) {
235 service_
->storage()->LoadOrCreateGroup(iter
->manifest_url
, this);
239 void AppCacheServiceImpl::DeleteOriginHelper::OnGroupLoaded(
240 appcache::AppCacheGroup
* group
, const GURL
& manifest_url
) {
242 group
->set_being_deleted(true);
243 group
->CancelUpdate();
244 service_
->storage()->MakeGroupObsolete(group
, this, 0);
246 CacheCompleted(false);
250 void AppCacheServiceImpl::DeleteOriginHelper::OnGroupMadeObsolete(
251 appcache::AppCacheGroup
* group
,
254 CacheCompleted(success
);
257 void AppCacheServiceImpl::DeleteOriginHelper::CacheCompleted(bool success
) {
262 if ((successes_
+ failures_
) < num_caches_to_delete_
)
265 CallCallback(!failures_
? net::OK
: net::ERR_FAILED
);
270 // GetInfoHelper -------
272 class AppCacheServiceImpl::GetInfoHelper
: AsyncHelper
{
275 AppCacheServiceImpl
* service
, AppCacheInfoCollection
* collection
,
276 const net::CompletionCallback
& callback
)
277 : AsyncHelper(service
, callback
), collection_(collection
) {
280 virtual void Start() OVERRIDE
{
281 service_
->storage()->GetAllInfo(this);
285 // AppCacheStorage::Delegate implementation.
286 virtual 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 virtual void Start() OVERRIDE
{
319 service_
->storage()->LoadOrCreateGroup(manifest_url_
, this);
322 virtual void Cancel() OVERRIDE
{
323 AppCacheHistograms::CountCheckResponseResult(
324 AppCacheHistograms::CHECK_CANCELED
);
325 response_reader_
.reset();
326 AsyncHelper::Cancel();
330 virtual void OnGroupLoaded(AppCacheGroup
* group
,
331 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(quota::QuotaManagerProxy
*
446 : appcache_policy_(NULL
), quota_client_(NULL
), handler_factory_(NULL
),
447 quota_manager_proxy_(quota_manager_proxy
),
448 request_context_(NULL
),
449 force_keep_session_state_(false) {
450 if (quota_manager_proxy_
.get()) {
451 quota_client_
= new AppCacheQuotaClient(this);
452 quota_manager_proxy_
->RegisterClient(quota_client_
);
456 AppCacheServiceImpl::~AppCacheServiceImpl() {
457 DCHECK(backends_
.empty());
458 std::for_each(pending_helpers_
.begin(),
459 pending_helpers_
.end(),
460 std::mem_fun(&AsyncHelper::Cancel
));
461 STLDeleteElements(&pending_helpers_
);
463 quota_client_
->NotifyAppCacheDestroyed();
465 // Destroy storage_ first; ~AppCacheStorageImpl accesses other data members
466 // (special_storage_policy_).
470 void AppCacheServiceImpl::Initialize(const base::FilePath
& cache_directory
,
471 base::MessageLoopProxy
* db_thread
,
472 base::MessageLoopProxy
* cache_thread
) {
473 DCHECK(!storage_
.get());
474 cache_directory_
= cache_directory
;
475 db_thread_
= db_thread
;
476 cache_thread_
= cache_thread
;
477 AppCacheStorageImpl
* storage
= new AppCacheStorageImpl(this);
478 storage
->Initialize(cache_directory
, db_thread
, cache_thread
);
479 storage_
.reset(storage
);
482 void AppCacheServiceImpl::ScheduleReinitialize() {
483 if (reinit_timer_
.IsRunning())
486 // Reinitialization only happens when corruption has been noticed.
487 // We don't want to thrash the disk but we also don't want to
488 // leave the appcache disabled for an indefinite period of time. Some
489 // users never shutdown the browser.
491 const base::TimeDelta kZeroDelta
;
492 const base::TimeDelta
kOneHour(base::TimeDelta::FromHours(1));
493 const base::TimeDelta
k30Seconds(base::TimeDelta::FromSeconds(30));
495 // If the system managed to stay up for long enough, reset the
496 // delay so a new failure won't incur a long wait to get going again.
497 base::TimeDelta up_time
= base::Time::Now() - last_reinit_time_
;
498 if (next_reinit_delay_
!= kZeroDelta
&& up_time
> kOneHour
)
499 next_reinit_delay_
= kZeroDelta
;
501 reinit_timer_
.Start(FROM_HERE
, next_reinit_delay_
,
502 this, &AppCacheServiceImpl::Reinitialize
);
504 // Adjust the delay for next time.
505 base::TimeDelta increment
= std::max(k30Seconds
, next_reinit_delay_
);
506 next_reinit_delay_
= std::min(next_reinit_delay_
+ increment
, kOneHour
);
509 void AppCacheServiceImpl::Reinitialize() {
510 AppCacheHistograms::CountReinitAttempt(!last_reinit_time_
.is_null());
511 last_reinit_time_
= base::Time::Now();
513 // Inform observers of about this and give them a chance to
514 // defer deletion of the old storage object.
515 scoped_refptr
<AppCacheStorageReference
>
516 old_storage_ref(new AppCacheStorageReference(storage_
.Pass()));
517 FOR_EACH_OBSERVER(Observer
, observers_
,
518 OnServiceReinitialized(old_storage_ref
.get()));
520 Initialize(cache_directory_
, db_thread_
, cache_thread_
);
523 void AppCacheServiceImpl::CanHandleMainResourceOffline(
525 const GURL
& first_party
,
526 const net::CompletionCallback
& callback
) {
527 CanHandleOfflineHelper
* helper
=
528 new CanHandleOfflineHelper(this, url
, first_party
, callback
);
532 void AppCacheServiceImpl::GetAllAppCacheInfo(
533 AppCacheInfoCollection
* collection
,
534 const net::CompletionCallback
& callback
) {
536 GetInfoHelper
* helper
= new GetInfoHelper(this, collection
, callback
);
540 void AppCacheServiceImpl::DeleteAppCacheGroup(
541 const GURL
& manifest_url
,
542 const net::CompletionCallback
& callback
) {
543 DeleteHelper
* helper
= new DeleteHelper(this, manifest_url
, callback
);
547 void AppCacheServiceImpl::DeleteAppCachesForOrigin(
548 const GURL
& origin
, const net::CompletionCallback
& callback
) {
549 DeleteOriginHelper
* helper
= new DeleteOriginHelper(this, origin
, callback
);
553 void AppCacheServiceImpl::CheckAppCacheResponse(const GURL
& manifest_url
,
556 CheckResponseHelper
* helper
= new CheckResponseHelper(
557 this, manifest_url
, cache_id
, response_id
);
561 void AppCacheServiceImpl::set_special_storage_policy(
562 quota::SpecialStoragePolicy
* policy
) {
563 special_storage_policy_
= policy
;
566 void AppCacheServiceImpl::RegisterBackend(
567 AppCacheBackendImpl
* backend_impl
) {
568 DCHECK(backends_
.find(backend_impl
->process_id()) == backends_
.end());
570 BackendMap::value_type(backend_impl
->process_id(), backend_impl
));
573 void AppCacheServiceImpl::UnregisterBackend(
574 AppCacheBackendImpl
* backend_impl
) {
575 backends_
.erase(backend_impl
->process_id());
578 } // namespace appcache