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/appcache/appcache_service.h"
10 #include "base/bind_helpers.h"
11 #include "base/logging.h"
12 #include "base/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/appcache/appcache.h"
17 #include "webkit/appcache/appcache_backend_impl.h"
18 #include "webkit/appcache/appcache_entry.h"
19 #include "webkit/appcache/appcache_histograms.h"
20 #include "webkit/appcache/appcache_policy.h"
21 #include "webkit/appcache/appcache_quota_client.h"
22 #include "webkit/appcache/appcache_response.h"
23 #include "webkit/appcache/appcache_storage_impl.h"
24 #include "webkit/quota/quota_manager.h"
25 #include "webkit/quota/special_storage_policy.h"
31 void DeferredCallback(const net::CompletionCallback
& callback
, int rv
) {
37 AppCacheInfoCollection::AppCacheInfoCollection() {}
39 AppCacheInfoCollection::~AppCacheInfoCollection() {}
41 // AsyncHelper -------
43 class AppCacheService::AsyncHelper
44 : public AppCacheStorage::Delegate
{
46 AsyncHelper(AppCacheService
* service
,
47 const net::CompletionCallback
& callback
)
48 : service_(service
), callback_(callback
) {
49 service_
->pending_helpers_
.insert(this);
52 virtual ~AsyncHelper() {
54 service_
->pending_helpers_
.erase(this);
57 virtual void Start() = 0;
58 virtual void Cancel();
61 void CallCallback(int rv
) {
62 if (!callback_
.is_null()) {
63 // Defer to guarantee async completion.
64 base::MessageLoop::current()->PostTask(
65 FROM_HERE
, base::Bind(&DeferredCallback
, callback_
, rv
));
70 AppCacheService
* service_
;
71 net::CompletionCallback callback_
;
74 void AppCacheService::AsyncHelper::Cancel() {
75 if (!callback_
.is_null()) {
76 callback_
.Run(net::ERR_ABORTED
);
79 service_
->storage()->CancelDelegateCallbacks(this);
83 // CanHandleOfflineHelper -------
85 class AppCacheService::CanHandleOfflineHelper
: AsyncHelper
{
87 CanHandleOfflineHelper(
88 AppCacheService
* service
, const GURL
& url
,
89 const GURL
& first_party
, const net::CompletionCallback
& callback
)
90 : AsyncHelper(service
, callback
),
92 first_party_(first_party
) {
95 virtual void Start() OVERRIDE
{
96 AppCachePolicy
* policy
= service_
->appcache_policy();
97 if (policy
&& !policy
->CanLoadAppCache(url_
, first_party_
)) {
98 CallCallback(net::ERR_FAILED
);
103 service_
->storage()->FindResponseForMainRequest(url_
, GURL(), this);
107 // AppCacheStorage::Delegate implementation.
108 virtual void OnMainResponseFound(
109 const GURL
& url
, const AppCacheEntry
& entry
,
110 const GURL
& fallback_url
, const AppCacheEntry
& fallback_entry
,
111 int64 cache_id
, int64 group_id
, const GURL
& mainfest_url
) OVERRIDE
;
116 DISALLOW_COPY_AND_ASSIGN(CanHandleOfflineHelper
);
119 void AppCacheService::CanHandleOfflineHelper::OnMainResponseFound(
120 const GURL
& url
, const AppCacheEntry
& entry
,
121 const GURL
& fallback_url
, const AppCacheEntry
& fallback_entry
,
122 int64 cache_id
, int64 group_id
, const GURL
& manifest_url
) {
123 bool can
= (entry
.has_response_id() || fallback_entry
.has_response_id());
124 CallCallback(can
? net::OK
: net::ERR_FAILED
);
128 // DeleteHelper -------
130 class AppCacheService::DeleteHelper
: public AsyncHelper
{
133 AppCacheService
* service
, const GURL
& manifest_url
,
134 const net::CompletionCallback
& callback
)
135 : AsyncHelper(service
, callback
), manifest_url_(manifest_url
) {
138 virtual void Start() OVERRIDE
{
139 service_
->storage()->LoadOrCreateGroup(manifest_url_
, this);
143 // AppCacheStorage::Delegate implementation.
144 virtual void OnGroupLoaded(
145 appcache::AppCacheGroup
* group
, const GURL
& manifest_url
) OVERRIDE
;
146 virtual void OnGroupMadeObsolete(
147 appcache::AppCacheGroup
* group
, bool success
) OVERRIDE
;
150 DISALLOW_COPY_AND_ASSIGN(DeleteHelper
);
153 void AppCacheService::DeleteHelper::OnGroupLoaded(
154 appcache::AppCacheGroup
* group
, const GURL
& manifest_url
) {
156 group
->set_being_deleted(true);
157 group
->CancelUpdate();
158 service_
->storage()->MakeGroupObsolete(group
, this);
160 CallCallback(net::ERR_FAILED
);
165 void AppCacheService::DeleteHelper::OnGroupMadeObsolete(
166 appcache::AppCacheGroup
* group
, bool success
) {
167 CallCallback(success
? net::OK
: net::ERR_FAILED
);
171 // DeleteOriginHelper -------
173 class AppCacheService::DeleteOriginHelper
: public AsyncHelper
{
176 AppCacheService
* service
, const GURL
& origin
,
177 const net::CompletionCallback
& callback
)
178 : AsyncHelper(service
, callback
), origin_(origin
),
179 num_caches_to_delete_(0), successes_(0), failures_(0) {
182 virtual void Start() OVERRIDE
{
183 // We start by listing all caches, continues in OnAllInfo().
184 service_
->storage()->GetAllInfo(this);
188 // AppCacheStorage::Delegate implementation.
189 virtual void OnAllInfo(AppCacheInfoCollection
* collection
) OVERRIDE
;
190 virtual void OnGroupLoaded(
191 appcache::AppCacheGroup
* group
, const GURL
& manifest_url
) OVERRIDE
;
192 virtual void OnGroupMadeObsolete(
193 appcache::AppCacheGroup
* group
, bool success
) OVERRIDE
;
195 void CacheCompleted(bool success
);
198 int num_caches_to_delete_
;
202 DISALLOW_COPY_AND_ASSIGN(DeleteOriginHelper
);
205 void AppCacheService::DeleteOriginHelper::OnAllInfo(
206 AppCacheInfoCollection
* collection
) {
208 // Failed to get a listing.
209 CallCallback(net::ERR_FAILED
);
214 std::map
<GURL
, AppCacheInfoVector
>::iterator found
=
215 collection
->infos_by_origin
.find(origin_
);
216 if (found
== collection
->infos_by_origin
.end() || found
->second
.empty()) {
217 // No caches for this origin.
218 CallCallback(net::OK
);
223 // We have some caches to delete.
224 const AppCacheInfoVector
& caches_to_delete
= found
->second
;
227 num_caches_to_delete_
= static_cast<int>(caches_to_delete
.size());
228 for (AppCacheInfoVector::const_iterator iter
= caches_to_delete
.begin();
229 iter
!= caches_to_delete
.end(); ++iter
) {
230 service_
->storage()->LoadOrCreateGroup(iter
->manifest_url
, this);
234 void AppCacheService::DeleteOriginHelper::OnGroupLoaded(
235 appcache::AppCacheGroup
* group
, const GURL
& manifest_url
) {
237 group
->set_being_deleted(true);
238 group
->CancelUpdate();
239 service_
->storage()->MakeGroupObsolete(group
, this);
241 CacheCompleted(false);
245 void AppCacheService::DeleteOriginHelper::OnGroupMadeObsolete(
246 appcache::AppCacheGroup
* group
, bool success
) {
247 CacheCompleted(success
);
250 void AppCacheService::DeleteOriginHelper::CacheCompleted(bool success
) {
255 if ((successes_
+ failures_
) < num_caches_to_delete_
)
258 CallCallback(!failures_
? net::OK
: net::ERR_FAILED
);
263 // GetInfoHelper -------
265 class AppCacheService::GetInfoHelper
: AsyncHelper
{
268 AppCacheService
* service
, AppCacheInfoCollection
* collection
,
269 const net::CompletionCallback
& callback
)
270 : AsyncHelper(service
, callback
), collection_(collection
) {
273 virtual void Start() OVERRIDE
{
274 service_
->storage()->GetAllInfo(this);
278 // AppCacheStorage::Delegate implementation.
279 virtual void OnAllInfo(AppCacheInfoCollection
* collection
) OVERRIDE
;
281 scoped_refptr
<AppCacheInfoCollection
> collection_
;
283 DISALLOW_COPY_AND_ASSIGN(GetInfoHelper
);
286 void AppCacheService::GetInfoHelper::OnAllInfo(
287 AppCacheInfoCollection
* collection
) {
289 collection
->infos_by_origin
.swap(collection_
->infos_by_origin
);
290 CallCallback(collection
? net::OK
: net::ERR_FAILED
);
294 // CheckResponseHelper -------
296 class AppCacheService::CheckResponseHelper
: AsyncHelper
{
299 AppCacheService
* service
, const GURL
& manifest_url
, int64 cache_id
,
301 : AsyncHelper(service
, net::CompletionCallback()),
302 manifest_url_(manifest_url
),
304 response_id_(response_id
),
305 kIOBufferSize(32 * 1024),
306 expected_total_size_(0),
307 amount_headers_read_(0),
308 amount_data_read_(0) {
311 virtual void Start() OVERRIDE
{
312 service_
->storage()->LoadOrCreateGroup(manifest_url_
, this);
315 virtual void Cancel() OVERRIDE
{
316 AppCacheHistograms::CountCheckResponseResult(
317 AppCacheHistograms::CHECK_CANCELED
);
318 response_reader_
.reset();
319 AsyncHelper::Cancel();
323 virtual void OnGroupLoaded(AppCacheGroup
* group
,
324 const GURL
& manifest_url
) OVERRIDE
;
325 void OnReadInfoComplete(int result
);
326 void OnReadDataComplete(int result
);
328 // Inputs describing what to check.
333 // Internals used to perform the checks.
334 const int kIOBufferSize
;
335 scoped_refptr
<AppCache
> cache_
;
336 scoped_ptr
<AppCacheResponseReader
> response_reader_
;
337 scoped_refptr
<HttpResponseInfoIOBuffer
> info_buffer_
;
338 scoped_refptr
<net::IOBuffer
> data_buffer_
;
339 int64 expected_total_size_
;
340 int amount_headers_read_
;
341 int amount_data_read_
;
342 DISALLOW_COPY_AND_ASSIGN(CheckResponseHelper
);
345 void AppCacheService::CheckResponseHelper::OnGroupLoaded(
346 AppCacheGroup
* group
, const GURL
& manifest_url
) {
347 DCHECK_EQ(manifest_url_
, manifest_url
);
348 if (!group
|| !group
->newest_complete_cache() || group
->is_being_deleted() ||
349 group
->is_obsolete()) {
350 AppCacheHistograms::CountCheckResponseResult(
351 AppCacheHistograms::MANIFEST_OUT_OF_DATE
);
356 cache_
= group
->newest_complete_cache();
357 const AppCacheEntry
* entry
= cache_
->GetEntryWithResponseId(response_id_
);
359 if (cache_
->cache_id() == cache_id_
) {
360 AppCacheHistograms::CountCheckResponseResult(
361 AppCacheHistograms::ENTRY_NOT_FOUND
);
362 service_
->DeleteAppCacheGroup(manifest_url_
, net::CompletionCallback());
364 AppCacheHistograms::CountCheckResponseResult(
365 AppCacheHistograms::RESPONSE_OUT_OF_DATE
);
371 // Verify that we can read the response info and data.
372 expected_total_size_
= entry
->response_size();
373 response_reader_
.reset(service_
->storage()->CreateResponseReader(
374 manifest_url_
, group
->group_id(), response_id_
));
375 info_buffer_
= new HttpResponseInfoIOBuffer();
376 response_reader_
->ReadInfo(
377 info_buffer_
, base::Bind(&CheckResponseHelper::OnReadInfoComplete
,
378 base::Unretained(this)));
381 void AppCacheService::CheckResponseHelper::OnReadInfoComplete(int result
) {
383 AppCacheHistograms::CountCheckResponseResult(
384 AppCacheHistograms::READ_HEADERS_ERROR
);
385 service_
->DeleteAppCacheGroup(manifest_url_
, net::CompletionCallback());
389 amount_headers_read_
= result
;
391 // Start reading the data.
392 data_buffer_
= new net::IOBuffer(kIOBufferSize
);
393 response_reader_
->ReadData(
394 data_buffer_
, kIOBufferSize
,
395 base::Bind(&CheckResponseHelper::OnReadDataComplete
,
396 base::Unretained(this)));
399 void AppCacheService::CheckResponseHelper::OnReadDataComplete(int result
) {
401 // Keep reading until we've read thru everything or failed to read.
402 amount_data_read_
+= result
;
403 response_reader_
->ReadData(
404 data_buffer_
, kIOBufferSize
,
405 base::Bind(&CheckResponseHelper::OnReadDataComplete
,
406 base::Unretained(this)));
410 AppCacheHistograms::CheckResponseResultType check_result
;
412 check_result
= AppCacheHistograms::READ_DATA_ERROR
;
413 else if (info_buffer_
->response_data_size
!= amount_data_read_
||
414 expected_total_size_
!= amount_data_read_
+ amount_headers_read_
)
415 check_result
= AppCacheHistograms::UNEXPECTED_DATA_SIZE
;
417 check_result
= AppCacheHistograms::RESPONSE_OK
;
418 AppCacheHistograms::CountCheckResponseResult(check_result
);
420 if (check_result
!= AppCacheHistograms::RESPONSE_OK
)
421 service_
->DeleteAppCacheGroup(manifest_url_
, net::CompletionCallback());
426 // AppCacheService -------
428 AppCacheService::AppCacheService(quota::QuotaManagerProxy
* quota_manager_proxy
)
429 : appcache_policy_(NULL
), quota_client_(NULL
),
430 quota_manager_proxy_(quota_manager_proxy
),
431 request_context_(NULL
),
432 force_keep_session_state_(false) {
433 if (quota_manager_proxy_
) {
434 quota_client_
= new AppCacheQuotaClient(this);
435 quota_manager_proxy_
->RegisterClient(quota_client_
);
439 AppCacheService::~AppCacheService() {
440 DCHECK(backends_
.empty());
441 std::for_each(pending_helpers_
.begin(),
442 pending_helpers_
.end(),
443 std::mem_fun(&AsyncHelper::Cancel
));
444 STLDeleteElements(&pending_helpers_
);
446 quota_client_
->NotifyAppCacheDestroyed();
448 // Destroy storage_ first; ~AppCacheStorageImpl accesses other data members
449 // (special_storage_policy_).
453 void AppCacheService::Initialize(const base::FilePath
& cache_directory
,
454 base::MessageLoopProxy
* db_thread
,
455 base::MessageLoopProxy
* cache_thread
) {
456 DCHECK(!storage_
.get());
457 AppCacheStorageImpl
* storage
= new AppCacheStorageImpl(this);
458 storage
->Initialize(cache_directory
, db_thread
, cache_thread
);
459 storage_
.reset(storage
);
462 void AppCacheService::CanHandleMainResourceOffline(
464 const GURL
& first_party
,
465 const net::CompletionCallback
& callback
) {
466 CanHandleOfflineHelper
* helper
=
467 new CanHandleOfflineHelper(this, url
, first_party
, callback
);
471 void AppCacheService::GetAllAppCacheInfo(
472 AppCacheInfoCollection
* collection
,
473 const net::CompletionCallback
& callback
) {
475 GetInfoHelper
* helper
= new GetInfoHelper(this, collection
, callback
);
479 void AppCacheService::DeleteAppCacheGroup(
480 const GURL
& manifest_url
,
481 const net::CompletionCallback
& callback
) {
482 DeleteHelper
* helper
= new DeleteHelper(this, manifest_url
, callback
);
486 void AppCacheService::DeleteAppCachesForOrigin(
487 const GURL
& origin
, const net::CompletionCallback
& callback
) {
488 DeleteOriginHelper
* helper
= new DeleteOriginHelper(this, origin
, callback
);
492 void AppCacheService::CheckAppCacheResponse(const GURL
& manifest_url
,
495 CheckResponseHelper
* helper
= new CheckResponseHelper(
496 this, manifest_url
, cache_id
, response_id
);
500 void AppCacheService::set_special_storage_policy(
501 quota::SpecialStoragePolicy
* policy
) {
502 special_storage_policy_
= policy
;
505 void AppCacheService::RegisterBackend(
506 AppCacheBackendImpl
* backend_impl
) {
507 DCHECK(backends_
.find(backend_impl
->process_id()) == backends_
.end());
509 BackendMap::value_type(backend_impl
->process_id(), backend_impl
));
512 void AppCacheService::UnregisterBackend(
513 AppCacheBackendImpl
* backend_impl
) {
514 backends_
.erase(backend_impl
->process_id());
517 } // namespace appcache