1 // Copyright (c) 2012 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.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_storage_impl.h"
25 #include "webkit/browser/quota/quota_manager.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 AppCacheService::AsyncHelper
45 : public AppCacheStorage::Delegate
{
47 AsyncHelper(AppCacheService
* 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 AppCacheService
* service_
;
72 net::CompletionCallback callback_
;
75 void AppCacheService::AsyncHelper::Cancel() {
76 if (!callback_
.is_null()) {
77 callback_
.Run(net::ERR_ABORTED
);
80 service_
->storage()->CancelDelegateCallbacks(this);
84 // CanHandleOfflineHelper -------
86 class AppCacheService::CanHandleOfflineHelper
: AsyncHelper
{
88 CanHandleOfflineHelper(
89 AppCacheService
* 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 AppCacheService::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 AppCacheService::DeleteHelper
: public AsyncHelper
{
134 AppCacheService
* 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(
148 appcache::AppCacheGroup
* group
, bool success
) OVERRIDE
;
151 DISALLOW_COPY_AND_ASSIGN(DeleteHelper
);
154 void AppCacheService::DeleteHelper::OnGroupLoaded(
155 appcache::AppCacheGroup
* group
, const GURL
& manifest_url
) {
157 group
->set_being_deleted(true);
158 group
->CancelUpdate();
159 service_
->storage()->MakeGroupObsolete(group
, this);
161 CallCallback(net::ERR_FAILED
);
166 void AppCacheService::DeleteHelper::OnGroupMadeObsolete(
167 appcache::AppCacheGroup
* group
, bool success
) {
168 CallCallback(success
? net::OK
: net::ERR_FAILED
);
172 // DeleteOriginHelper -------
174 class AppCacheService::DeleteOriginHelper
: public AsyncHelper
{
177 AppCacheService
* service
, const GURL
& origin
,
178 const net::CompletionCallback
& callback
)
179 : AsyncHelper(service
, callback
), origin_(origin
),
180 num_caches_to_delete_(0), successes_(0), failures_(0) {
183 virtual void Start() OVERRIDE
{
184 // We start by listing all caches, continues in OnAllInfo().
185 service_
->storage()->GetAllInfo(this);
189 // AppCacheStorage::Delegate implementation.
190 virtual void OnAllInfo(AppCacheInfoCollection
* collection
) OVERRIDE
;
191 virtual void OnGroupLoaded(
192 appcache::AppCacheGroup
* group
, const GURL
& manifest_url
) OVERRIDE
;
193 virtual void OnGroupMadeObsolete(
194 appcache::AppCacheGroup
* group
, bool success
) OVERRIDE
;
196 void CacheCompleted(bool success
);
199 int num_caches_to_delete_
;
203 DISALLOW_COPY_AND_ASSIGN(DeleteOriginHelper
);
206 void AppCacheService::DeleteOriginHelper::OnAllInfo(
207 AppCacheInfoCollection
* collection
) {
209 // Failed to get a listing.
210 CallCallback(net::ERR_FAILED
);
215 std::map
<GURL
, AppCacheInfoVector
>::iterator found
=
216 collection
->infos_by_origin
.find(origin_
);
217 if (found
== collection
->infos_by_origin
.end() || found
->second
.empty()) {
218 // No caches for this origin.
219 CallCallback(net::OK
);
224 // We have some caches to delete.
225 const AppCacheInfoVector
& caches_to_delete
= found
->second
;
228 num_caches_to_delete_
= static_cast<int>(caches_to_delete
.size());
229 for (AppCacheInfoVector::const_iterator iter
= caches_to_delete
.begin();
230 iter
!= caches_to_delete
.end(); ++iter
) {
231 service_
->storage()->LoadOrCreateGroup(iter
->manifest_url
, this);
235 void AppCacheService::DeleteOriginHelper::OnGroupLoaded(
236 appcache::AppCacheGroup
* group
, const GURL
& manifest_url
) {
238 group
->set_being_deleted(true);
239 group
->CancelUpdate();
240 service_
->storage()->MakeGroupObsolete(group
, this);
242 CacheCompleted(false);
246 void AppCacheService::DeleteOriginHelper::OnGroupMadeObsolete(
247 appcache::AppCacheGroup
* group
, bool success
) {
248 CacheCompleted(success
);
251 void AppCacheService::DeleteOriginHelper::CacheCompleted(bool success
) {
256 if ((successes_
+ failures_
) < num_caches_to_delete_
)
259 CallCallback(!failures_
? net::OK
: net::ERR_FAILED
);
264 // GetInfoHelper -------
266 class AppCacheService::GetInfoHelper
: AsyncHelper
{
269 AppCacheService
* service
, AppCacheInfoCollection
* collection
,
270 const net::CompletionCallback
& callback
)
271 : AsyncHelper(service
, callback
), collection_(collection
) {
274 virtual void Start() OVERRIDE
{
275 service_
->storage()->GetAllInfo(this);
279 // AppCacheStorage::Delegate implementation.
280 virtual void OnAllInfo(AppCacheInfoCollection
* collection
) OVERRIDE
;
282 scoped_refptr
<AppCacheInfoCollection
> collection_
;
284 DISALLOW_COPY_AND_ASSIGN(GetInfoHelper
);
287 void AppCacheService::GetInfoHelper::OnAllInfo(
288 AppCacheInfoCollection
* collection
) {
290 collection
->infos_by_origin
.swap(collection_
->infos_by_origin
);
291 CallCallback(collection
? net::OK
: net::ERR_FAILED
);
295 // CheckResponseHelper -------
297 class AppCacheService::CheckResponseHelper
: AsyncHelper
{
300 AppCacheService
* service
, const GURL
& manifest_url
, int64 cache_id
,
302 : AsyncHelper(service
, net::CompletionCallback()),
303 manifest_url_(manifest_url
),
305 response_id_(response_id
),
306 kIOBufferSize(32 * 1024),
307 expected_total_size_(0),
308 amount_headers_read_(0),
309 amount_data_read_(0) {
312 virtual void Start() OVERRIDE
{
313 service_
->storage()->LoadOrCreateGroup(manifest_url_
, this);
316 virtual void Cancel() OVERRIDE
{
317 AppCacheHistograms::CountCheckResponseResult(
318 AppCacheHistograms::CHECK_CANCELED
);
319 response_reader_
.reset();
320 AsyncHelper::Cancel();
324 virtual void OnGroupLoaded(AppCacheGroup
* group
,
325 const GURL
& manifest_url
) OVERRIDE
;
326 void OnReadInfoComplete(int result
);
327 void OnReadDataComplete(int result
);
329 // Inputs describing what to check.
334 // Internals used to perform the checks.
335 const int kIOBufferSize
;
336 scoped_refptr
<AppCache
> cache_
;
337 scoped_ptr
<AppCacheResponseReader
> response_reader_
;
338 scoped_refptr
<HttpResponseInfoIOBuffer
> info_buffer_
;
339 scoped_refptr
<net::IOBuffer
> data_buffer_
;
340 int64 expected_total_size_
;
341 int amount_headers_read_
;
342 int amount_data_read_
;
343 DISALLOW_COPY_AND_ASSIGN(CheckResponseHelper
);
346 void AppCacheService::CheckResponseHelper::OnGroupLoaded(
347 AppCacheGroup
* group
, const GURL
& manifest_url
) {
348 DCHECK_EQ(manifest_url_
, manifest_url
);
349 if (!group
|| !group
->newest_complete_cache() || group
->is_being_deleted() ||
350 group
->is_obsolete()) {
351 AppCacheHistograms::CountCheckResponseResult(
352 AppCacheHistograms::MANIFEST_OUT_OF_DATE
);
357 cache_
= group
->newest_complete_cache();
358 const AppCacheEntry
* entry
= cache_
->GetEntryWithResponseId(response_id_
);
360 if (cache_
->cache_id() == cache_id_
) {
361 AppCacheHistograms::CountCheckResponseResult(
362 AppCacheHistograms::ENTRY_NOT_FOUND
);
363 service_
->DeleteAppCacheGroup(manifest_url_
, net::CompletionCallback());
365 AppCacheHistograms::CountCheckResponseResult(
366 AppCacheHistograms::RESPONSE_OUT_OF_DATE
);
372 // Verify that we can read the response info and data.
373 expected_total_size_
= entry
->response_size();
374 response_reader_
.reset(service_
->storage()->CreateResponseReader(
375 manifest_url_
, group
->group_id(), response_id_
));
376 info_buffer_
= new HttpResponseInfoIOBuffer();
377 response_reader_
->ReadInfo(
379 base::Bind(&CheckResponseHelper::OnReadInfoComplete
,
380 base::Unretained(this)));
383 void AppCacheService::CheckResponseHelper::OnReadInfoComplete(int result
) {
385 AppCacheHistograms::CountCheckResponseResult(
386 AppCacheHistograms::READ_HEADERS_ERROR
);
387 service_
->DeleteAppCacheGroup(manifest_url_
, net::CompletionCallback());
391 amount_headers_read_
= result
;
393 // Start reading the data.
394 data_buffer_
= new net::IOBuffer(kIOBufferSize
);
395 response_reader_
->ReadData(
398 base::Bind(&CheckResponseHelper::OnReadDataComplete
,
399 base::Unretained(this)));
402 void AppCacheService::CheckResponseHelper::OnReadDataComplete(int result
) {
404 // Keep reading until we've read thru everything or failed to read.
405 amount_data_read_
+= result
;
406 response_reader_
->ReadData(
409 base::Bind(&CheckResponseHelper::OnReadDataComplete
,
410 base::Unretained(this)));
414 AppCacheHistograms::CheckResponseResultType check_result
;
416 check_result
= AppCacheHistograms::READ_DATA_ERROR
;
417 else if (info_buffer_
->response_data_size
!= amount_data_read_
||
418 expected_total_size_
!= amount_data_read_
+ amount_headers_read_
)
419 check_result
= AppCacheHistograms::UNEXPECTED_DATA_SIZE
;
421 check_result
= AppCacheHistograms::RESPONSE_OK
;
422 AppCacheHistograms::CountCheckResponseResult(check_result
);
424 if (check_result
!= AppCacheHistograms::RESPONSE_OK
)
425 service_
->DeleteAppCacheGroup(manifest_url_
, net::CompletionCallback());
430 // AppCacheService -------
432 AppCacheService::AppCacheService(quota::QuotaManagerProxy
* quota_manager_proxy
)
433 : appcache_policy_(NULL
), quota_client_(NULL
), handler_factory_(NULL
),
434 quota_manager_proxy_(quota_manager_proxy
),
435 request_context_(NULL
),
436 force_keep_session_state_(false) {
437 if (quota_manager_proxy_
.get()) {
438 quota_client_
= new AppCacheQuotaClient(this);
439 quota_manager_proxy_
->RegisterClient(quota_client_
);
443 AppCacheService::~AppCacheService() {
444 DCHECK(backends_
.empty());
445 std::for_each(pending_helpers_
.begin(),
446 pending_helpers_
.end(),
447 std::mem_fun(&AsyncHelper::Cancel
));
448 STLDeleteElements(&pending_helpers_
);
450 quota_client_
->NotifyAppCacheDestroyed();
452 // Destroy storage_ first; ~AppCacheStorageImpl accesses other data members
453 // (special_storage_policy_).
457 void AppCacheService::Initialize(const base::FilePath
& cache_directory
,
458 base::MessageLoopProxy
* db_thread
,
459 base::MessageLoopProxy
* cache_thread
) {
460 DCHECK(!storage_
.get());
461 AppCacheStorageImpl
* storage
= new AppCacheStorageImpl(this);
462 storage
->Initialize(cache_directory
, db_thread
, cache_thread
);
463 storage_
.reset(storage
);
466 void AppCacheService::CanHandleMainResourceOffline(
468 const GURL
& first_party
,
469 const net::CompletionCallback
& callback
) {
470 CanHandleOfflineHelper
* helper
=
471 new CanHandleOfflineHelper(this, url
, first_party
, callback
);
475 void AppCacheService::GetAllAppCacheInfo(
476 AppCacheInfoCollection
* collection
,
477 const net::CompletionCallback
& callback
) {
479 GetInfoHelper
* helper
= new GetInfoHelper(this, collection
, callback
);
483 void AppCacheService::DeleteAppCacheGroup(
484 const GURL
& manifest_url
,
485 const net::CompletionCallback
& callback
) {
486 DeleteHelper
* helper
= new DeleteHelper(this, manifest_url
, callback
);
490 void AppCacheService::DeleteAppCachesForOrigin(
491 const GURL
& origin
, const net::CompletionCallback
& callback
) {
492 DeleteOriginHelper
* helper
= new DeleteOriginHelper(this, origin
, callback
);
496 void AppCacheService::CheckAppCacheResponse(const GURL
& manifest_url
,
499 CheckResponseHelper
* helper
= new CheckResponseHelper(
500 this, manifest_url
, cache_id
, response_id
);
504 void AppCacheService::set_special_storage_policy(
505 quota::SpecialStoragePolicy
* policy
) {
506 special_storage_policy_
= policy
;
509 void AppCacheService::RegisterBackend(
510 AppCacheBackendImpl
* backend_impl
) {
511 DCHECK(backends_
.find(backend_impl
->process_id()) == backends_
.end());
513 BackendMap::value_type(backend_impl
->process_id(), backend_impl
));
516 void AppCacheService::UnregisterBackend(
517 AppCacheBackendImpl
* backend_impl
) {
518 backends_
.erase(backend_impl
->process_id());
521 } // namespace appcache