Re-subimission of https://codereview.chromium.org/1041213003/
[chromium-blink-merge.git] / content / browser / appcache / appcache_service_impl.cc
blob0c784dfd3dcc5572e36825f9d0880d1357c04ae2
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/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"
29 namespace content {
31 namespace {
33 void DeferredCallback(const net::CompletionCallback& callback, int rv) {
34 callback.Run(rv);
37 } // namespace
39 AppCacheInfoCollection::AppCacheInfoCollection() {}
41 AppCacheInfoCollection::~AppCacheInfoCollection() {}
43 // AsyncHelper -------
45 class AppCacheServiceImpl::AsyncHelper
46 : public AppCacheStorage::Delegate {
47 public:
48 AsyncHelper(AppCacheServiceImpl* service,
49 const net::CompletionCallback& callback)
50 : service_(service), callback_(callback) {
51 service_->pending_helpers_.insert(this);
54 ~AsyncHelper() override {
55 if (service_)
56 service_->pending_helpers_.erase(this);
59 virtual void Start() = 0;
60 virtual void Cancel();
62 protected:
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));
69 callback_.Reset();
72 AppCacheServiceImpl* service_;
73 net::CompletionCallback callback_;
76 void AppCacheServiceImpl::AsyncHelper::Cancel() {
77 if (!callback_.is_null()) {
78 callback_.Run(net::ERR_ABORTED);
79 callback_.Reset();
81 service_->storage()->CancelDelegateCallbacks(this);
82 service_ = NULL;
85 // CanHandleOfflineHelper -------
87 class AppCacheServiceImpl::CanHandleOfflineHelper : AsyncHelper {
88 public:
89 CanHandleOfflineHelper(
90 AppCacheServiceImpl* service, const GURL& url,
91 const GURL& first_party, const net::CompletionCallback& callback)
92 : AsyncHelper(service, callback),
93 url_(url),
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);
101 delete this;
102 return;
105 service_->storage()->FindResponseForMainRequest(url_, GURL(), this);
108 private:
109 // AppCacheStorage::Delegate implementation.
110 void OnMainResponseFound(const GURL& url,
111 const AppCacheEntry& entry,
112 const GURL& fallback_url,
113 const AppCacheEntry& fallback_entry,
114 int64 cache_id,
115 int64 group_id,
116 const GURL& mainfest_url) override;
118 GURL url_;
119 GURL first_party_;
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);
130 delete this;
133 // DeleteHelper -------
135 class AppCacheServiceImpl::DeleteHelper : public AsyncHelper {
136 public:
137 DeleteHelper(
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);
147 private:
148 // AppCacheStorage::Delegate implementation.
149 void OnGroupLoaded(AppCacheGroup* group, const GURL& manifest_url) override;
150 void OnGroupMadeObsolete(AppCacheGroup* group,
151 bool success,
152 int response_code) override;
154 GURL manifest_url_;
155 DISALLOW_COPY_AND_ASSIGN(DeleteHelper);
158 void AppCacheServiceImpl::DeleteHelper::OnGroupLoaded(
159 AppCacheGroup* group, const GURL& manifest_url) {
160 if (group) {
161 group->set_being_deleted(true);
162 group->CancelUpdate();
163 service_->storage()->MakeGroupObsolete(group, this, 0);
164 } else {
165 CallCallback(net::ERR_FAILED);
166 delete this;
170 void AppCacheServiceImpl::DeleteHelper::OnGroupMadeObsolete(
171 AppCacheGroup* group,
172 bool success,
173 int response_code) {
174 CallCallback(success ? net::OK : net::ERR_FAILED);
175 delete this;
178 // DeleteOriginHelper -------
180 class AppCacheServiceImpl::DeleteOriginHelper : public AsyncHelper {
181 public:
182 DeleteOriginHelper(
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);
194 private:
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,
199 bool success,
200 int response_code) override;
202 void CacheCompleted(bool success);
204 GURL origin_;
205 int num_caches_to_delete_;
206 int successes_;
207 int failures_;
209 DISALLOW_COPY_AND_ASSIGN(DeleteOriginHelper);
212 void AppCacheServiceImpl::DeleteOriginHelper::OnAllInfo(
213 AppCacheInfoCollection* collection) {
214 if (!collection) {
215 // Failed to get a listing.
216 CallCallback(net::ERR_FAILED);
217 delete this;
218 return;
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);
226 delete this;
227 return;
230 // We have some caches to delete.
231 const AppCacheInfoVector& caches_to_delete = found->second;
232 successes_ = 0;
233 failures_ = 0;
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) {
243 if (group) {
244 group->set_being_deleted(true);
245 group->CancelUpdate();
246 service_->storage()->MakeGroupObsolete(group, this, 0);
247 } else {
248 CacheCompleted(false);
252 void AppCacheServiceImpl::DeleteOriginHelper::OnGroupMadeObsolete(
253 AppCacheGroup* group,
254 bool success,
255 int response_code) {
256 CacheCompleted(success);
259 void AppCacheServiceImpl::DeleteOriginHelper::CacheCompleted(bool success) {
260 if (success)
261 ++successes_;
262 else
263 ++failures_;
264 if ((successes_ + failures_) < num_caches_to_delete_)
265 return;
267 CallCallback(!failures_ ? net::OK : net::ERR_FAILED);
268 delete this;
272 // GetInfoHelper -------
274 class AppCacheServiceImpl::GetInfoHelper : AsyncHelper {
275 public:
276 GetInfoHelper(
277 AppCacheServiceImpl* service, AppCacheInfoCollection* collection,
278 const net::CompletionCallback& callback)
279 : AsyncHelper(service, callback), collection_(collection) {
282 void Start() override { service_->storage()->GetAllInfo(this); }
284 private:
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) {
295 if (collection)
296 collection->infos_by_origin.swap(collection_->infos_by_origin);
297 CallCallback(collection ? net::OK : net::ERR_FAILED);
298 delete this;
301 // CheckResponseHelper -------
303 class AppCacheServiceImpl::CheckResponseHelper : AsyncHelper {
304 public:
305 CheckResponseHelper(
306 AppCacheServiceImpl* service, const GURL& manifest_url, int64 cache_id,
307 int64 response_id)
308 : AsyncHelper(service, net::CompletionCallback()),
309 manifest_url_(manifest_url),
310 cache_id_(cache_id),
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();
329 private:
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.
335 GURL manifest_url_;
336 int64 cache_id_;
337 int64 response_id_;
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);
358 delete this;
359 return;
362 cache_ = group->newest_complete_cache();
363 const AppCacheEntry* entry = cache_->GetEntryWithResponseId(response_id_);
364 if (!entry) {
365 if (cache_->cache_id() == cache_id_) {
366 AppCacheHistograms::CountCheckResponseResult(
367 AppCacheHistograms::ENTRY_NOT_FOUND);
368 service_->DeleteAppCacheGroup(manifest_url_, net::CompletionCallback());
369 } else {
370 AppCacheHistograms::CountCheckResponseResult(
371 AppCacheHistograms::RESPONSE_OUT_OF_DATE);
373 delete this;
374 return;
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(
383 info_buffer_.get(),
384 base::Bind(&CheckResponseHelper::OnReadInfoComplete,
385 base::Unretained(this)));
388 void AppCacheServiceImpl::CheckResponseHelper::OnReadInfoComplete(int result) {
389 if (result < 0) {
390 AppCacheHistograms::CountCheckResponseResult(
391 AppCacheHistograms::READ_HEADERS_ERROR);
392 service_->DeleteAppCacheGroup(manifest_url_, net::CompletionCallback());
393 delete this;
394 return;
396 amount_headers_read_ = result;
398 // Start reading the data.
399 data_buffer_ = new net::IOBuffer(kIOBufferSize);
400 response_reader_->ReadData(
401 data_buffer_.get(),
402 kIOBufferSize,
403 base::Bind(&CheckResponseHelper::OnReadDataComplete,
404 base::Unretained(this)));
407 void AppCacheServiceImpl::CheckResponseHelper::OnReadDataComplete(int result) {
408 if (result > 0) {
409 // Keep reading until we've read thru everything or failed to read.
410 amount_data_read_ += result;
411 response_reader_->ReadData(
412 data_buffer_.get(),
413 kIOBufferSize,
414 base::Bind(&CheckResponseHelper::OnReadDataComplete,
415 base::Unretained(this)));
416 return;
419 AppCacheHistograms::CheckResponseResultType check_result;
420 if (result < 0)
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;
425 else
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());
431 delete this;
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),
446 quota_client_(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_);
463 if (quota_client_)
464 quota_client_->NotifyAppCacheDestroyed();
466 // Destroy storage_ first; ~AppCacheStorageImpl accesses other data members
467 // (special_storage_policy_).
468 storage_.reset();
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())
486 return;
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(
526 const GURL& url,
527 const GURL& first_party,
528 const net::CompletionCallback& callback) {
529 CanHandleOfflineHelper* helper =
530 new CanHandleOfflineHelper(this, url, first_party, callback);
531 helper->Start();
534 void AppCacheServiceImpl::GetAllAppCacheInfo(
535 AppCacheInfoCollection* collection,
536 const net::CompletionCallback& callback) {
537 DCHECK(collection);
538 GetInfoHelper* helper = new GetInfoHelper(this, collection, callback);
539 helper->Start();
542 void AppCacheServiceImpl::DeleteAppCacheGroup(
543 const GURL& manifest_url,
544 const net::CompletionCallback& callback) {
545 DeleteHelper* helper = new DeleteHelper(this, manifest_url, callback);
546 helper->Start();
549 void AppCacheServiceImpl::DeleteAppCachesForOrigin(
550 const GURL& origin, const net::CompletionCallback& callback) {
551 DeleteOriginHelper* helper = new DeleteOriginHelper(this, origin, callback);
552 helper->Start();
555 void AppCacheServiceImpl::CheckAppCacheResponse(const GURL& manifest_url,
556 int64 cache_id,
557 int64 response_id) {
558 CheckResponseHelper* helper = new CheckResponseHelper(
559 this, manifest_url, cache_id, response_id);
560 helper->Start();
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());
571 backends_.insert(
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