Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / content / browser / appcache / appcache_service_impl.cc
blob01cbb46add06ca9490036a440b9c9437ddf5d0b5
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"
7 #include <functional>
9 #include "base/bind.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"
30 namespace content {
32 namespace {
34 void DeferredCallback(const net::CompletionCallback& callback, int rv) {
35 callback.Run(rv);
38 } // namespace
40 AppCacheInfoCollection::AppCacheInfoCollection() {}
42 AppCacheInfoCollection::~AppCacheInfoCollection() {}
44 // AsyncHelper -------
46 class AppCacheServiceImpl::AsyncHelper
47 : public AppCacheStorage::Delegate {
48 public:
49 AsyncHelper(AppCacheServiceImpl* service,
50 const net::CompletionCallback& callback)
51 : service_(service), callback_(callback) {
52 service_->pending_helpers_.insert(this);
55 ~AsyncHelper() override {
56 if (service_)
57 service_->pending_helpers_.erase(this);
60 virtual void Start() = 0;
61 virtual void Cancel();
63 protected:
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));
70 callback_.Reset();
73 AppCacheServiceImpl* service_;
74 net::CompletionCallback callback_;
77 void AppCacheServiceImpl::AsyncHelper::Cancel() {
78 if (!callback_.is_null()) {
79 callback_.Run(net::ERR_ABORTED);
80 callback_.Reset();
82 service_->storage()->CancelDelegateCallbacks(this);
83 service_ = NULL;
86 // CanHandleOfflineHelper -------
88 class AppCacheServiceImpl::CanHandleOfflineHelper : AsyncHelper {
89 public:
90 CanHandleOfflineHelper(
91 AppCacheServiceImpl* service, const GURL& url,
92 const GURL& first_party, const net::CompletionCallback& callback)
93 : AsyncHelper(service, callback),
94 url_(url),
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);
102 delete this;
103 return;
106 service_->storage()->FindResponseForMainRequest(url_, GURL(), this);
109 private:
110 // AppCacheStorage::Delegate implementation.
111 void OnMainResponseFound(const GURL& url,
112 const AppCacheEntry& entry,
113 const GURL& fallback_url,
114 const AppCacheEntry& fallback_entry,
115 int64 cache_id,
116 int64 group_id,
117 const GURL& mainfest_url) override;
119 GURL url_;
120 GURL first_party_;
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);
131 delete this;
134 // DeleteHelper -------
136 class AppCacheServiceImpl::DeleteHelper : public AsyncHelper {
137 public:
138 DeleteHelper(
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);
148 private:
149 // AppCacheStorage::Delegate implementation.
150 void OnGroupLoaded(AppCacheGroup* group, const GURL& manifest_url) override;
151 void OnGroupMadeObsolete(AppCacheGroup* group,
152 bool success,
153 int response_code) override;
155 GURL manifest_url_;
156 DISALLOW_COPY_AND_ASSIGN(DeleteHelper);
159 void AppCacheServiceImpl::DeleteHelper::OnGroupLoaded(
160 AppCacheGroup* group, const GURL& manifest_url) {
161 if (group) {
162 group->set_being_deleted(true);
163 group->CancelUpdate();
164 service_->storage()->MakeGroupObsolete(group, this, 0);
165 } else {
166 CallCallback(net::ERR_FAILED);
167 delete this;
171 void AppCacheServiceImpl::DeleteHelper::OnGroupMadeObsolete(
172 AppCacheGroup* group,
173 bool success,
174 int response_code) {
175 CallCallback(success ? net::OK : net::ERR_FAILED);
176 delete this;
179 // DeleteOriginHelper -------
181 class AppCacheServiceImpl::DeleteOriginHelper : public AsyncHelper {
182 public:
183 DeleteOriginHelper(
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);
195 private:
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,
200 bool success,
201 int response_code) override;
203 void CacheCompleted(bool success);
205 GURL origin_;
206 int num_caches_to_delete_;
207 int successes_;
208 int failures_;
210 DISALLOW_COPY_AND_ASSIGN(DeleteOriginHelper);
213 void AppCacheServiceImpl::DeleteOriginHelper::OnAllInfo(
214 AppCacheInfoCollection* collection) {
215 if (!collection) {
216 // Failed to get a listing.
217 CallCallback(net::ERR_FAILED);
218 delete this;
219 return;
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);
227 delete this;
228 return;
231 // We have some caches to delete.
232 const AppCacheInfoVector& caches_to_delete = found->second;
233 successes_ = 0;
234 failures_ = 0;
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) {
244 if (group) {
245 group->set_being_deleted(true);
246 group->CancelUpdate();
247 service_->storage()->MakeGroupObsolete(group, this, 0);
248 } else {
249 CacheCompleted(false);
253 void AppCacheServiceImpl::DeleteOriginHelper::OnGroupMadeObsolete(
254 AppCacheGroup* group,
255 bool success,
256 int response_code) {
257 CacheCompleted(success);
260 void AppCacheServiceImpl::DeleteOriginHelper::CacheCompleted(bool success) {
261 if (success)
262 ++successes_;
263 else
264 ++failures_;
265 if ((successes_ + failures_) < num_caches_to_delete_)
266 return;
268 CallCallback(!failures_ ? net::OK : net::ERR_FAILED);
269 delete this;
273 // GetInfoHelper -------
275 class AppCacheServiceImpl::GetInfoHelper : AsyncHelper {
276 public:
277 GetInfoHelper(
278 AppCacheServiceImpl* service, AppCacheInfoCollection* collection,
279 const net::CompletionCallback& callback)
280 : AsyncHelper(service, callback), collection_(collection) {
283 void Start() override { service_->storage()->GetAllInfo(this); }
285 private:
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) {
296 if (collection)
297 collection->infos_by_origin.swap(collection_->infos_by_origin);
298 CallCallback(collection ? net::OK : net::ERR_FAILED);
299 delete this;
302 // CheckResponseHelper -------
304 class AppCacheServiceImpl::CheckResponseHelper : AsyncHelper {
305 public:
306 CheckResponseHelper(
307 AppCacheServiceImpl* service, const GURL& manifest_url, int64 cache_id,
308 int64 response_id)
309 : AsyncHelper(service, net::CompletionCallback()),
310 manifest_url_(manifest_url),
311 cache_id_(cache_id),
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();
330 private:
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.
336 GURL manifest_url_;
337 int64 cache_id_;
338 int64 response_id_;
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);
359 delete this;
360 return;
363 cache_ = group->newest_complete_cache();
364 const AppCacheEntry* entry = cache_->GetEntryWithResponseId(response_id_);
365 if (!entry) {
366 if (cache_->cache_id() == cache_id_) {
367 AppCacheHistograms::CountCheckResponseResult(
368 AppCacheHistograms::ENTRY_NOT_FOUND);
369 service_->DeleteAppCacheGroup(manifest_url_, net::CompletionCallback());
370 } else {
371 AppCacheHistograms::CountCheckResponseResult(
372 AppCacheHistograms::RESPONSE_OUT_OF_DATE);
374 delete this;
375 return;
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(
384 info_buffer_.get(),
385 base::Bind(&CheckResponseHelper::OnReadInfoComplete,
386 base::Unretained(this)));
389 void AppCacheServiceImpl::CheckResponseHelper::OnReadInfoComplete(int result) {
390 if (result < 0) {
391 AppCacheHistograms::CountCheckResponseResult(
392 AppCacheHistograms::READ_HEADERS_ERROR);
393 service_->DeleteAppCacheGroup(manifest_url_, net::CompletionCallback());
394 delete this;
395 return;
397 amount_headers_read_ = result;
399 // Start reading the data.
400 data_buffer_ = new net::IOBuffer(kIOBufferSize);
401 response_reader_->ReadData(
402 data_buffer_.get(),
403 kIOBufferSize,
404 base::Bind(&CheckResponseHelper::OnReadDataComplete,
405 base::Unretained(this)));
408 void AppCacheServiceImpl::CheckResponseHelper::OnReadDataComplete(int result) {
409 if (result > 0) {
410 // Keep reading until we've read thru everything or failed to read.
411 amount_data_read_ += result;
412 response_reader_->ReadData(
413 data_buffer_.get(),
414 kIOBufferSize,
415 base::Bind(&CheckResponseHelper::OnReadDataComplete,
416 base::Unretained(this)));
417 return;
420 AppCacheHistograms::CheckResponseResultType check_result;
421 if (result < 0)
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;
426 else
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());
432 delete this;
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),
447 quota_client_(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_);
465 if (quota_client_)
466 quota_client_->NotifyAppCacheDestroyed();
468 // Destroy storage_ first; ~AppCacheStorageImpl accesses other data members
469 // (special_storage_policy_).
470 storage_.reset();
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())
488 return;
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(
528 const GURL& url,
529 const GURL& first_party,
530 const net::CompletionCallback& callback) {
531 CanHandleOfflineHelper* helper =
532 new CanHandleOfflineHelper(this, url, first_party, callback);
533 helper->Start();
536 void AppCacheServiceImpl::GetAllAppCacheInfo(
537 AppCacheInfoCollection* collection,
538 const net::CompletionCallback& callback) {
539 DCHECK(collection);
540 GetInfoHelper* helper = new GetInfoHelper(this, collection, callback);
541 helper->Start();
544 void AppCacheServiceImpl::DeleteAppCacheGroup(
545 const GURL& manifest_url,
546 const net::CompletionCallback& callback) {
547 DeleteHelper* helper = new DeleteHelper(this, manifest_url, callback);
548 helper->Start();
551 void AppCacheServiceImpl::DeleteAppCachesForOrigin(
552 const GURL& origin, const net::CompletionCallback& callback) {
553 DeleteOriginHelper* helper = new DeleteOriginHelper(this, origin, callback);
554 helper->Start();
557 void AppCacheServiceImpl::CheckAppCacheResponse(const GURL& manifest_url,
558 int64 cache_id,
559 int64 response_id) {
560 CheckResponseHelper* helper = new CheckResponseHelper(
561 this, manifest_url, cache_id, response_id);
562 helper->Start();
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());
573 backends_.insert(
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