Home | History | Annotate | Download | only in appcache
      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.
      4 
      5 #include "content/browser/appcache/appcache_service_impl.h"
      6 
      7 #include <functional>
      8 
      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"
     28 
     29 namespace content {
     30 
     31 namespace {
     32 
     33 void DeferredCallback(const net::CompletionCallback& callback, int rv) {
     34   callback.Run(rv);
     35 }
     36 
     37 }  // namespace
     38 
     39 AppCacheInfoCollection::AppCacheInfoCollection() {}
     40 
     41 AppCacheInfoCollection::~AppCacheInfoCollection() {}
     42 
     43 // AsyncHelper -------
     44 
     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);
     52   }
     53 
     54   virtual ~AsyncHelper() {
     55     if (service_)
     56       service_->pending_helpers_.erase(this);
     57   }
     58 
     59   virtual void Start() = 0;
     60   virtual void Cancel();
     61 
     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));
     68     }
     69     callback_.Reset();
     70   }
     71 
     72   AppCacheServiceImpl* service_;
     73   net::CompletionCallback callback_;
     74 };
     75 
     76 void AppCacheServiceImpl::AsyncHelper::Cancel() {
     77   if (!callback_.is_null()) {
     78     callback_.Run(net::ERR_ABORTED);
     79     callback_.Reset();
     80   }
     81   service_->storage()->CancelDelegateCallbacks(this);
     82   service_ = NULL;
     83 }
     84 
     85 // CanHandleOfflineHelper -------
     86 
     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) {
     95   }
     96 
     97   virtual 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;
    103     }
    104 
    105     service_->storage()->FindResponseForMainRequest(url_, GURL(), this);
    106   }
    107 
    108  private:
    109   // AppCacheStorage::Delegate implementation.
    110   virtual void OnMainResponseFound(
    111       const GURL& url, const AppCacheEntry& entry,
    112       const GURL& fallback_url, const AppCacheEntry& fallback_entry,
    113       int64 cache_id, int64 group_id, const GURL& mainfest_url) OVERRIDE;
    114 
    115   GURL url_;
    116   GURL first_party_;
    117 
    118   DISALLOW_COPY_AND_ASSIGN(CanHandleOfflineHelper);
    119 };
    120 
    121 void AppCacheServiceImpl::CanHandleOfflineHelper::OnMainResponseFound(
    122       const GURL& url, const AppCacheEntry& entry,
    123       const GURL& fallback_url, const AppCacheEntry& fallback_entry,
    124       int64 cache_id, int64 group_id, const GURL& manifest_url) {
    125   bool can = (entry.has_response_id() || fallback_entry.has_response_id());
    126   CallCallback(can ? net::OK : net::ERR_FAILED);
    127   delete this;
    128 }
    129 
    130 // DeleteHelper -------
    131 
    132 class AppCacheServiceImpl::DeleteHelper : public AsyncHelper {
    133  public:
    134   DeleteHelper(
    135       AppCacheServiceImpl* service, const GURL& manifest_url,
    136       const net::CompletionCallback& callback)
    137       : AsyncHelper(service, callback), manifest_url_(manifest_url) {
    138   }
    139 
    140   virtual void Start() OVERRIDE {
    141     service_->storage()->LoadOrCreateGroup(manifest_url_, this);
    142   }
    143 
    144  private:
    145   // AppCacheStorage::Delegate implementation.
    146   virtual void OnGroupLoaded(
    147       AppCacheGroup* group, const GURL& manifest_url) OVERRIDE;
    148   virtual void OnGroupMadeObsolete(AppCacheGroup* group,
    149                                    bool success,
    150                                    int response_code) OVERRIDE;
    151 
    152   GURL manifest_url_;
    153   DISALLOW_COPY_AND_ASSIGN(DeleteHelper);
    154 };
    155 
    156 void AppCacheServiceImpl::DeleteHelper::OnGroupLoaded(
    157       AppCacheGroup* group, const GURL& manifest_url) {
    158   if (group) {
    159     group->set_being_deleted(true);
    160     group->CancelUpdate();
    161     service_->storage()->MakeGroupObsolete(group, this, 0);
    162   } else {
    163     CallCallback(net::ERR_FAILED);
    164     delete this;
    165   }
    166 }
    167 
    168 void AppCacheServiceImpl::DeleteHelper::OnGroupMadeObsolete(
    169     AppCacheGroup* group,
    170     bool success,
    171     int response_code) {
    172   CallCallback(success ? net::OK : net::ERR_FAILED);
    173   delete this;
    174 }
    175 
    176 // DeleteOriginHelper -------
    177 
    178 class AppCacheServiceImpl::DeleteOriginHelper : public AsyncHelper {
    179  public:
    180   DeleteOriginHelper(
    181       AppCacheServiceImpl* service, const GURL& origin,
    182       const net::CompletionCallback& callback)
    183       : AsyncHelper(service, callback), origin_(origin),
    184         num_caches_to_delete_(0), successes_(0), failures_(0) {
    185   }
    186 
    187   virtual void Start() OVERRIDE {
    188     // We start by listing all caches, continues in OnAllInfo().
    189     service_->storage()->GetAllInfo(this);
    190   }
    191 
    192  private:
    193   // AppCacheStorage::Delegate implementation.
    194   virtual void OnAllInfo(AppCacheInfoCollection* collection) OVERRIDE;
    195   virtual void OnGroupLoaded(
    196       AppCacheGroup* group, const GURL& manifest_url) OVERRIDE;
    197   virtual void OnGroupMadeObsolete(AppCacheGroup* group,
    198                                    bool success,
    199                                    int response_code) OVERRIDE;
    200 
    201   void CacheCompleted(bool success);
    202 
    203   GURL origin_;
    204   int num_caches_to_delete_;
    205   int successes_;
    206   int failures_;
    207 
    208   DISALLOW_COPY_AND_ASSIGN(DeleteOriginHelper);
    209 };
    210 
    211 void AppCacheServiceImpl::DeleteOriginHelper::OnAllInfo(
    212     AppCacheInfoCollection* collection) {
    213   if (!collection) {
    214     // Failed to get a listing.
    215     CallCallback(net::ERR_FAILED);
    216     delete this;
    217     return;
    218   }
    219 
    220   std::map<GURL, AppCacheInfoVector>::iterator found =
    221       collection->infos_by_origin.find(origin_);
    222   if (found == collection->infos_by_origin.end() || found->second.empty()) {
    223     // No caches for this origin.
    224     CallCallback(net::OK);
    225     delete this;
    226     return;
    227   }
    228 
    229   // We have some caches to delete.
    230   const AppCacheInfoVector& caches_to_delete = found->second;
    231   successes_ = 0;
    232   failures_ = 0;
    233   num_caches_to_delete_ = static_cast<int>(caches_to_delete.size());
    234   for (AppCacheInfoVector::const_iterator iter = caches_to_delete.begin();
    235        iter != caches_to_delete.end(); ++iter) {
    236     service_->storage()->LoadOrCreateGroup(iter->manifest_url, this);
    237   }
    238 }
    239 
    240 void AppCacheServiceImpl::DeleteOriginHelper::OnGroupLoaded(
    241       AppCacheGroup* group, const GURL& manifest_url) {
    242   if (group) {
    243     group->set_being_deleted(true);
    244     group->CancelUpdate();
    245     service_->storage()->MakeGroupObsolete(group, this, 0);
    246   } else {
    247     CacheCompleted(false);
    248   }
    249 }
    250 
    251 void AppCacheServiceImpl::DeleteOriginHelper::OnGroupMadeObsolete(
    252     AppCacheGroup* group,
    253     bool success,
    254     int response_code) {
    255   CacheCompleted(success);
    256 }
    257 
    258 void AppCacheServiceImpl::DeleteOriginHelper::CacheCompleted(bool success) {
    259   if (success)
    260     ++successes_;
    261   else
    262     ++failures_;
    263   if ((successes_ + failures_) < num_caches_to_delete_)
    264     return;
    265 
    266   CallCallback(!failures_ ? net::OK : net::ERR_FAILED);
    267   delete this;
    268 }
    269 
    270 
    271 // GetInfoHelper -------
    272 
    273 class AppCacheServiceImpl::GetInfoHelper : AsyncHelper {
    274  public:
    275   GetInfoHelper(
    276       AppCacheServiceImpl* service, AppCacheInfoCollection* collection,
    277       const net::CompletionCallback& callback)
    278       : AsyncHelper(service, callback), collection_(collection) {
    279   }
    280 
    281   virtual void Start() OVERRIDE {
    282     service_->storage()->GetAllInfo(this);
    283   }
    284 
    285  private:
    286   // AppCacheStorage::Delegate implementation.
    287   virtual void OnAllInfo(AppCacheInfoCollection* collection) OVERRIDE;
    288 
    289   scoped_refptr<AppCacheInfoCollection> collection_;
    290 
    291   DISALLOW_COPY_AND_ASSIGN(GetInfoHelper);
    292 };
    293 
    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;
    300 }
    301 
    302 // CheckResponseHelper -------
    303 
    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) {
    317   }
    318 
    319   virtual void Start() OVERRIDE {
    320     service_->storage()->LoadOrCreateGroup(manifest_url_, this);
    321   }
    322 
    323   virtual void Cancel() OVERRIDE {
    324     AppCacheHistograms::CountCheckResponseResult(
    325         AppCacheHistograms::CHECK_CANCELED);
    326     response_reader_.reset();
    327     AsyncHelper::Cancel();
    328   }
    329 
    330  private:
    331   virtual void OnGroupLoaded(AppCacheGroup* group,
    332                              const GURL& manifest_url) OVERRIDE;
    333   void OnReadInfoComplete(int result);
    334   void OnReadDataComplete(int result);
    335 
    336   // Inputs describing what to check.
    337   GURL manifest_url_;
    338   int64 cache_id_;
    339   int64 response_id_;
    340 
    341   // Internals used to perform the checks.
    342   const int kIOBufferSize;
    343   scoped_refptr<AppCache> cache_;
    344   scoped_ptr<AppCacheResponseReader> response_reader_;
    345   scoped_refptr<HttpResponseInfoIOBuffer> info_buffer_;
    346   scoped_refptr<net::IOBuffer> data_buffer_;
    347   int64 expected_total_size_;
    348   int amount_headers_read_;
    349   int amount_data_read_;
    350   DISALLOW_COPY_AND_ASSIGN(CheckResponseHelper);
    351 };
    352 
    353 void AppCacheServiceImpl::CheckResponseHelper::OnGroupLoaded(
    354     AppCacheGroup* group, const GURL& manifest_url) {
    355   DCHECK_EQ(manifest_url_, manifest_url);
    356   if (!group || !group->newest_complete_cache() || group->is_being_deleted() ||
    357       group->is_obsolete()) {
    358     AppCacheHistograms::CountCheckResponseResult(
    359         AppCacheHistograms::MANIFEST_OUT_OF_DATE);
    360     delete this;
    361     return;
    362   }
    363 
    364   cache_ = group->newest_complete_cache();
    365   const AppCacheEntry* entry = cache_->GetEntryWithResponseId(response_id_);
    366   if (!entry) {
    367     if (cache_->cache_id() == cache_id_) {
    368       AppCacheHistograms::CountCheckResponseResult(
    369           AppCacheHistograms::ENTRY_NOT_FOUND);
    370       service_->DeleteAppCacheGroup(manifest_url_, net::CompletionCallback());
    371     } else {
    372       AppCacheHistograms::CountCheckResponseResult(
    373           AppCacheHistograms::RESPONSE_OUT_OF_DATE);
    374     }
    375     delete this;
    376     return;
    377   }
    378 
    379   // Verify that we can read the response info and data.
    380   expected_total_size_ = entry->response_size();
    381   response_reader_.reset(service_->storage()->CreateResponseReader(
    382       manifest_url_, group->group_id(), response_id_));
    383   info_buffer_ = new HttpResponseInfoIOBuffer();
    384   response_reader_->ReadInfo(
    385       info_buffer_.get(),
    386       base::Bind(&CheckResponseHelper::OnReadInfoComplete,
    387                  base::Unretained(this)));
    388 }
    389 
    390 void AppCacheServiceImpl::CheckResponseHelper::OnReadInfoComplete(int result) {
    391   if (result < 0) {
    392     AppCacheHistograms::CountCheckResponseResult(
    393         AppCacheHistograms::READ_HEADERS_ERROR);
    394     service_->DeleteAppCacheGroup(manifest_url_, net::CompletionCallback());
    395     delete this;
    396     return;
    397   }
    398   amount_headers_read_ = result;
    399 
    400   // Start reading the data.
    401   data_buffer_ = new net::IOBuffer(kIOBufferSize);
    402   response_reader_->ReadData(
    403       data_buffer_.get(),
    404       kIOBufferSize,
    405       base::Bind(&CheckResponseHelper::OnReadDataComplete,
    406                  base::Unretained(this)));
    407 }
    408 
    409 void AppCacheServiceImpl::CheckResponseHelper::OnReadDataComplete(int result) {
    410   if (result > 0) {
    411     // Keep reading until we've read thru everything or failed to read.
    412     amount_data_read_ += result;
    413     response_reader_->ReadData(
    414         data_buffer_.get(),
    415         kIOBufferSize,
    416         base::Bind(&CheckResponseHelper::OnReadDataComplete,
    417                    base::Unretained(this)));
    418     return;
    419   }
    420 
    421   AppCacheHistograms::CheckResponseResultType check_result;
    422   if (result < 0)
    423     check_result = AppCacheHistograms::READ_DATA_ERROR;
    424   else if (info_buffer_->response_data_size != amount_data_read_ ||
    425            expected_total_size_ != amount_data_read_ + amount_headers_read_)
    426     check_result = AppCacheHistograms::UNEXPECTED_DATA_SIZE;
    427   else
    428     check_result = AppCacheHistograms::RESPONSE_OK;
    429   AppCacheHistograms::CountCheckResponseResult(check_result);
    430 
    431   if (check_result != AppCacheHistograms::RESPONSE_OK)
    432     service_->DeleteAppCacheGroup(manifest_url_, net::CompletionCallback());
    433   delete this;
    434 }
    435 
    436 // AppCacheStorageReference ------
    437 
    438 AppCacheStorageReference::AppCacheStorageReference(
    439     scoped_ptr<AppCacheStorage> storage)
    440     : storage_(storage.Pass()) {}
    441 AppCacheStorageReference::~AppCacheStorageReference() {}
    442 
    443 // AppCacheServiceImpl -------
    444 
    445 AppCacheServiceImpl::AppCacheServiceImpl(
    446     storage::QuotaManagerProxy* quota_manager_proxy)
    447     : appcache_policy_(NULL),
    448       quota_client_(NULL),
    449       handler_factory_(NULL),
    450       quota_manager_proxy_(quota_manager_proxy),
    451       request_context_(NULL),
    452       force_keep_session_state_(false) {
    453   if (quota_manager_proxy_.get()) {
    454     quota_client_ = new AppCacheQuotaClient(this);
    455     quota_manager_proxy_->RegisterClient(quota_client_);
    456   }
    457 }
    458 
    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();
    467 
    468   // Destroy storage_ first; ~AppCacheStorageImpl accesses other data members
    469   // (special_storage_policy_).
    470   storage_.reset();
    471 }
    472 
    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);
    484 }
    485 
    486 void AppCacheServiceImpl::ScheduleReinitialize() {
    487   if (reinit_timer_.IsRunning())
    488     return;
    489 
    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.
    494 
    495   const base::TimeDelta kZeroDelta;
    496   const base::TimeDelta kOneHour(base::TimeDelta::FromHours(1));
    497   const base::TimeDelta k30Seconds(base::TimeDelta::FromSeconds(30));
    498 
    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;
    504 
    505   reinit_timer_.Start(FROM_HERE, next_reinit_delay_,
    506                       this, &AppCacheServiceImpl::Reinitialize);
    507 
    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);
    511 }
    512 
    513 void AppCacheServiceImpl::Reinitialize() {
    514   AppCacheHistograms::CountReinitAttempt(!last_reinit_time_.is_null());
    515   last_reinit_time_ = base::Time::Now();
    516 
    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()));
    523 
    524   Initialize(cache_directory_, db_thread_, cache_thread_);
    525 }
    526 
    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();
    534 }
    535 
    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();
    542 }
    543 
    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();
    549 }
    550 
    551 void AppCacheServiceImpl::DeleteAppCachesForOrigin(
    552     const GURL& origin,  const net::CompletionCallback& callback) {
    553   DeleteOriginHelper* helper = new DeleteOriginHelper(this, origin, callback);
    554   helper->Start();
    555 }
    556 
    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();
    563 }
    564 
    565 void AppCacheServiceImpl::set_special_storage_policy(
    566     storage::SpecialStoragePolicy* policy) {
    567   special_storage_policy_ = policy;
    568 }
    569 
    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));
    575 }
    576 
    577 void AppCacheServiceImpl::UnregisterBackend(
    578     AppCacheBackendImpl* backend_impl) {
    579   backends_.erase(backend_impl->process_id());
    580 }
    581 
    582 }  // namespace content
    583