Home | History | Annotate | Download | only in appcache
      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.
      4 
      5 #include "webkit/browser/appcache/appcache_service.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/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"
     27 
     28 namespace appcache {
     29 
     30 namespace {
     31 
     32 void DeferredCallback(const net::CompletionCallback& callback, int rv) {
     33   callback.Run(rv);
     34 }
     35 
     36 }  // namespace
     37 
     38 AppCacheInfoCollection::AppCacheInfoCollection() {}
     39 
     40 AppCacheInfoCollection::~AppCacheInfoCollection() {}
     41 
     42 // AsyncHelper -------
     43 
     44 class AppCacheService::AsyncHelper
     45     : public AppCacheStorage::Delegate {
     46  public:
     47   AsyncHelper(AppCacheService* service,
     48               const net::CompletionCallback& callback)
     49       : service_(service), callback_(callback) {
     50     service_->pending_helpers_.insert(this);
     51   }
     52 
     53   virtual ~AsyncHelper() {
     54     if (service_)
     55       service_->pending_helpers_.erase(this);
     56   }
     57 
     58   virtual void Start() = 0;
     59   virtual void Cancel();
     60 
     61  protected:
     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));
     67     }
     68     callback_.Reset();
     69   }
     70 
     71   AppCacheService* service_;
     72   net::CompletionCallback callback_;
     73 };
     74 
     75 void AppCacheService::AsyncHelper::Cancel() {
     76   if (!callback_.is_null()) {
     77     callback_.Run(net::ERR_ABORTED);
     78     callback_.Reset();
     79   }
     80   service_->storage()->CancelDelegateCallbacks(this);
     81   service_ = NULL;
     82 }
     83 
     84 // CanHandleOfflineHelper -------
     85 
     86 class AppCacheService::CanHandleOfflineHelper : AsyncHelper {
     87  public:
     88   CanHandleOfflineHelper(
     89       AppCacheService* service, const GURL& url,
     90       const GURL& first_party, const net::CompletionCallback& callback)
     91       : AsyncHelper(service, callback),
     92         url_(url),
     93         first_party_(first_party) {
     94   }
     95 
     96   virtual void Start() OVERRIDE {
     97     AppCachePolicy* policy = service_->appcache_policy();
     98     if (policy && !policy->CanLoadAppCache(url_, first_party_)) {
     99       CallCallback(net::ERR_FAILED);
    100       delete this;
    101       return;
    102     }
    103 
    104     service_->storage()->FindResponseForMainRequest(url_, GURL(), this);
    105   }
    106 
    107  private:
    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;
    113 
    114   GURL url_;
    115   GURL first_party_;
    116 
    117   DISALLOW_COPY_AND_ASSIGN(CanHandleOfflineHelper);
    118 };
    119 
    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);
    126   delete this;
    127 }
    128 
    129 // DeleteHelper -------
    130 
    131 class AppCacheService::DeleteHelper : public AsyncHelper {
    132  public:
    133   DeleteHelper(
    134       AppCacheService* service, const GURL& manifest_url,
    135       const net::CompletionCallback& callback)
    136       : AsyncHelper(service, callback), manifest_url_(manifest_url) {
    137   }
    138 
    139   virtual void Start() OVERRIDE {
    140     service_->storage()->LoadOrCreateGroup(manifest_url_, this);
    141   }
    142 
    143  private:
    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;
    149 
    150   GURL manifest_url_;
    151   DISALLOW_COPY_AND_ASSIGN(DeleteHelper);
    152 };
    153 
    154 void AppCacheService::DeleteHelper::OnGroupLoaded(
    155       appcache::AppCacheGroup* group, const GURL& manifest_url) {
    156   if (group) {
    157     group->set_being_deleted(true);
    158     group->CancelUpdate();
    159     service_->storage()->MakeGroupObsolete(group, this);
    160   } else {
    161     CallCallback(net::ERR_FAILED);
    162     delete this;
    163   }
    164 }
    165 
    166 void AppCacheService::DeleteHelper::OnGroupMadeObsolete(
    167       appcache::AppCacheGroup* group, bool success) {
    168   CallCallback(success ? net::OK : net::ERR_FAILED);
    169   delete this;
    170 }
    171 
    172 // DeleteOriginHelper -------
    173 
    174 class AppCacheService::DeleteOriginHelper : public AsyncHelper {
    175  public:
    176   DeleteOriginHelper(
    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) {
    181   }
    182 
    183   virtual void Start() OVERRIDE {
    184     // We start by listing all caches, continues in OnAllInfo().
    185     service_->storage()->GetAllInfo(this);
    186   }
    187 
    188  private:
    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;
    195 
    196   void CacheCompleted(bool success);
    197 
    198   GURL origin_;
    199   int num_caches_to_delete_;
    200   int successes_;
    201   int failures_;
    202 
    203   DISALLOW_COPY_AND_ASSIGN(DeleteOriginHelper);
    204 };
    205 
    206 void AppCacheService::DeleteOriginHelper::OnAllInfo(
    207     AppCacheInfoCollection* collection) {
    208   if (!collection) {
    209     // Failed to get a listing.
    210     CallCallback(net::ERR_FAILED);
    211     delete this;
    212     return;
    213   }
    214 
    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);
    220     delete this;
    221     return;
    222   }
    223 
    224   // We have some caches to delete.
    225   const AppCacheInfoVector& caches_to_delete = found->second;
    226   successes_ = 0;
    227   failures_ = 0;
    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);
    232   }
    233 }
    234 
    235 void AppCacheService::DeleteOriginHelper::OnGroupLoaded(
    236       appcache::AppCacheGroup* group, const GURL& manifest_url) {
    237   if (group) {
    238     group->set_being_deleted(true);
    239     group->CancelUpdate();
    240     service_->storage()->MakeGroupObsolete(group, this);
    241   } else {
    242     CacheCompleted(false);
    243   }
    244 }
    245 
    246 void AppCacheService::DeleteOriginHelper::OnGroupMadeObsolete(
    247       appcache::AppCacheGroup* group, bool success) {
    248   CacheCompleted(success);
    249 }
    250 
    251 void AppCacheService::DeleteOriginHelper::CacheCompleted(bool success) {
    252   if (success)
    253     ++successes_;
    254   else
    255     ++failures_;
    256   if ((successes_ + failures_) < num_caches_to_delete_)
    257     return;
    258 
    259   CallCallback(!failures_ ? net::OK : net::ERR_FAILED);
    260   delete this;
    261 }
    262 
    263 
    264 // GetInfoHelper -------
    265 
    266 class AppCacheService::GetInfoHelper : AsyncHelper {
    267  public:
    268   GetInfoHelper(
    269       AppCacheService* service, AppCacheInfoCollection* collection,
    270       const net::CompletionCallback& callback)
    271       : AsyncHelper(service, callback), collection_(collection) {
    272   }
    273 
    274   virtual void Start() OVERRIDE {
    275     service_->storage()->GetAllInfo(this);
    276   }
    277 
    278  private:
    279   // AppCacheStorage::Delegate implementation.
    280   virtual void OnAllInfo(AppCacheInfoCollection* collection) OVERRIDE;
    281 
    282   scoped_refptr<AppCacheInfoCollection> collection_;
    283 
    284   DISALLOW_COPY_AND_ASSIGN(GetInfoHelper);
    285 };
    286 
    287 void AppCacheService::GetInfoHelper::OnAllInfo(
    288       AppCacheInfoCollection* collection) {
    289   if (collection)
    290     collection->infos_by_origin.swap(collection_->infos_by_origin);
    291   CallCallback(collection ? net::OK : net::ERR_FAILED);
    292   delete this;
    293 }
    294 
    295 // CheckResponseHelper -------
    296 
    297 class AppCacheService::CheckResponseHelper : AsyncHelper {
    298  public:
    299   CheckResponseHelper(
    300       AppCacheService* service, const GURL& manifest_url, int64 cache_id,
    301       int64 response_id)
    302       : AsyncHelper(service, net::CompletionCallback()),
    303         manifest_url_(manifest_url),
    304         cache_id_(cache_id),
    305         response_id_(response_id),
    306         kIOBufferSize(32 * 1024),
    307         expected_total_size_(0),
    308         amount_headers_read_(0),
    309         amount_data_read_(0) {
    310   }
    311 
    312   virtual void Start() OVERRIDE {
    313     service_->storage()->LoadOrCreateGroup(manifest_url_, this);
    314   }
    315 
    316   virtual void Cancel() OVERRIDE {
    317     AppCacheHistograms::CountCheckResponseResult(
    318         AppCacheHistograms::CHECK_CANCELED);
    319     response_reader_.reset();
    320     AsyncHelper::Cancel();
    321   }
    322 
    323  private:
    324   virtual void OnGroupLoaded(AppCacheGroup* group,
    325                              const GURL& manifest_url) OVERRIDE;
    326   void OnReadInfoComplete(int result);
    327   void OnReadDataComplete(int result);
    328 
    329   // Inputs describing what to check.
    330   GURL manifest_url_;
    331   int64 cache_id_;
    332   int64 response_id_;
    333 
    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);
    344 };
    345 
    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);
    353     delete this;
    354     return;
    355   }
    356 
    357   cache_ = group->newest_complete_cache();
    358   const AppCacheEntry* entry = cache_->GetEntryWithResponseId(response_id_);
    359   if (!entry) {
    360     if (cache_->cache_id() == cache_id_) {
    361       AppCacheHistograms::CountCheckResponseResult(
    362           AppCacheHistograms::ENTRY_NOT_FOUND);
    363       service_->DeleteAppCacheGroup(manifest_url_, net::CompletionCallback());
    364     } else {
    365       AppCacheHistograms::CountCheckResponseResult(
    366           AppCacheHistograms::RESPONSE_OUT_OF_DATE);
    367     }
    368     delete this;
    369     return;
    370   }
    371 
    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(
    378       info_buffer_.get(),
    379       base::Bind(&CheckResponseHelper::OnReadInfoComplete,
    380                  base::Unretained(this)));
    381 }
    382 
    383 void AppCacheService::CheckResponseHelper::OnReadInfoComplete(int result) {
    384   if (result < 0) {
    385     AppCacheHistograms::CountCheckResponseResult(
    386         AppCacheHistograms::READ_HEADERS_ERROR);
    387     service_->DeleteAppCacheGroup(manifest_url_, net::CompletionCallback());
    388     delete this;
    389     return;
    390   }
    391   amount_headers_read_ = result;
    392 
    393   // Start reading the data.
    394   data_buffer_ = new net::IOBuffer(kIOBufferSize);
    395   response_reader_->ReadData(
    396       data_buffer_.get(),
    397       kIOBufferSize,
    398       base::Bind(&CheckResponseHelper::OnReadDataComplete,
    399                  base::Unretained(this)));
    400 }
    401 
    402 void AppCacheService::CheckResponseHelper::OnReadDataComplete(int result) {
    403   if (result > 0) {
    404     // Keep reading until we've read thru everything or failed to read.
    405     amount_data_read_ += result;
    406     response_reader_->ReadData(
    407         data_buffer_.get(),
    408         kIOBufferSize,
    409         base::Bind(&CheckResponseHelper::OnReadDataComplete,
    410                    base::Unretained(this)));
    411     return;
    412   }
    413 
    414   AppCacheHistograms::CheckResponseResultType check_result;
    415   if (result < 0)
    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;
    420   else
    421     check_result = AppCacheHistograms::RESPONSE_OK;
    422   AppCacheHistograms::CountCheckResponseResult(check_result);
    423 
    424   if (check_result != AppCacheHistograms::RESPONSE_OK)
    425     service_->DeleteAppCacheGroup(manifest_url_, net::CompletionCallback());
    426   delete this;
    427 }
    428 
    429 
    430 // AppCacheService -------
    431 
    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_);
    440   }
    441 }
    442 
    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_);
    449   if (quota_client_)
    450     quota_client_->NotifyAppCacheDestroyed();
    451 
    452   // Destroy storage_ first; ~AppCacheStorageImpl accesses other data members
    453   // (special_storage_policy_).
    454   storage_.reset();
    455 }
    456 
    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);
    464 }
    465 
    466 void AppCacheService::CanHandleMainResourceOffline(
    467     const GURL& url,
    468     const GURL& first_party,
    469     const net::CompletionCallback& callback) {
    470   CanHandleOfflineHelper* helper =
    471       new CanHandleOfflineHelper(this, url, first_party, callback);
    472   helper->Start();
    473 }
    474 
    475 void AppCacheService::GetAllAppCacheInfo(
    476     AppCacheInfoCollection* collection,
    477     const net::CompletionCallback& callback) {
    478   DCHECK(collection);
    479   GetInfoHelper* helper = new GetInfoHelper(this, collection, callback);
    480   helper->Start();
    481 }
    482 
    483 void AppCacheService::DeleteAppCacheGroup(
    484     const GURL& manifest_url,
    485     const net::CompletionCallback& callback) {
    486   DeleteHelper* helper = new DeleteHelper(this, manifest_url, callback);
    487   helper->Start();
    488 }
    489 
    490 void AppCacheService::DeleteAppCachesForOrigin(
    491     const GURL& origin,  const net::CompletionCallback& callback) {
    492   DeleteOriginHelper* helper = new DeleteOriginHelper(this, origin, callback);
    493   helper->Start();
    494 }
    495 
    496 void AppCacheService::CheckAppCacheResponse(const GURL& manifest_url,
    497                                             int64 cache_id,
    498                                             int64 response_id) {
    499   CheckResponseHelper* helper = new CheckResponseHelper(
    500       this, manifest_url, cache_id, response_id);
    501   helper->Start();
    502 }
    503 
    504 void AppCacheService::set_special_storage_policy(
    505     quota::SpecialStoragePolicy* policy) {
    506   special_storage_policy_ = policy;
    507 }
    508 
    509 void AppCacheService::RegisterBackend(
    510     AppCacheBackendImpl* backend_impl) {
    511   DCHECK(backends_.find(backend_impl->process_id()) == backends_.end());
    512   backends_.insert(
    513       BackendMap::value_type(backend_impl->process_id(), backend_impl));
    514 }
    515 
    516 void AppCacheService::UnregisterBackend(
    517     AppCacheBackendImpl* backend_impl) {
    518   backends_.erase(backend_impl->process_id());
    519 }
    520 
    521 }  // namespace appcache
    522