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_storage_impl.h"
      6 
      7 #include <algorithm>
      8 #include <functional>
      9 #include <set>
     10 #include <vector>
     11 
     12 #include "base/bind.h"
     13 #include "base/bind_helpers.h"
     14 #include "base/file_util.h"
     15 #include "base/logging.h"
     16 #include "base/message_loop/message_loop.h"
     17 #include "base/stl_util.h"
     18 #include "base/strings/string_util.h"
     19 #include "net/base/cache_type.h"
     20 #include "net/base/net_errors.h"
     21 #include "sql/connection.h"
     22 #include "sql/transaction.h"
     23 #include "webkit/browser/appcache/appcache.h"
     24 #include "webkit/browser/appcache/appcache_database.h"
     25 #include "webkit/browser/appcache/appcache_entry.h"
     26 #include "webkit/browser/appcache/appcache_group.h"
     27 #include "webkit/browser/appcache/appcache_histograms.h"
     28 #include "webkit/browser/appcache/appcache_quota_client.h"
     29 #include "webkit/browser/appcache/appcache_response.h"
     30 #include "webkit/browser/appcache/appcache_service.h"
     31 #include "webkit/browser/quota/quota_client.h"
     32 #include "webkit/browser/quota/quota_manager.h"
     33 #include "webkit/browser/quota/special_storage_policy.h"
     34 
     35 namespace appcache {
     36 
     37 // Hard coded default when not using quota management.
     38 static const int kDefaultQuota = 5 * 1024 * 1024;
     39 
     40 static const int kMaxDiskCacheSize = 250 * 1024 * 1024;
     41 static const int kMaxMemDiskCacheSize = 10 * 1024 * 1024;
     42 static const base::FilePath::CharType kDiskCacheDirectoryName[] =
     43     FILE_PATH_LITERAL("Cache");
     44 
     45 namespace {
     46 
     47 // Helpers for clearing data from the AppCacheDatabase.
     48 bool DeleteGroupAndRelatedRecords(AppCacheDatabase* database,
     49                                   int64 group_id,
     50                                   std::vector<int64>* deletable_response_ids) {
     51   AppCacheDatabase::CacheRecord cache_record;
     52   bool success = false;
     53   if (database->FindCacheForGroup(group_id, &cache_record)) {
     54     database->FindResponseIdsForCacheAsVector(cache_record.cache_id,
     55                                               deletable_response_ids);
     56     success =
     57         database->DeleteGroup(group_id) &&
     58         database->DeleteCache(cache_record.cache_id) &&
     59         database->DeleteEntriesForCache(cache_record.cache_id) &&
     60         database->DeleteNamespacesForCache(cache_record.cache_id) &&
     61         database->DeleteOnlineWhiteListForCache(cache_record.cache_id) &&
     62         database->InsertDeletableResponseIds(*deletable_response_ids);
     63   } else {
     64     NOTREACHED() << "A existing group without a cache is unexpected";
     65     success = database->DeleteGroup(group_id);
     66   }
     67   return success;
     68 }
     69 
     70 // Destroys |database|. If there is appcache data to be deleted
     71 // (|force_keep_session_state| is false), deletes session-only appcache data.
     72 void ClearSessionOnlyOrigins(
     73     AppCacheDatabase* database,
     74     scoped_refptr<quota::SpecialStoragePolicy> special_storage_policy,
     75     bool force_keep_session_state) {
     76   scoped_ptr<AppCacheDatabase> database_to_delete(database);
     77 
     78   // If saving session state, only delete the database.
     79   if (force_keep_session_state)
     80     return;
     81 
     82   bool has_session_only_appcaches =
     83       special_storage_policy.get() &&
     84       special_storage_policy->HasSessionOnlyOrigins();
     85 
     86   // Clearning only session-only databases, and there are none.
     87   if (!has_session_only_appcaches)
     88     return;
     89 
     90   std::set<GURL> origins;
     91   database->FindOriginsWithGroups(&origins);
     92   if (origins.empty())
     93     return;  // nothing to delete
     94 
     95   sql::Connection* connection = database->db_connection();
     96   if (!connection) {
     97     NOTREACHED() << "Missing database connection.";
     98     return;
     99   }
    100 
    101   std::set<GURL>::const_iterator origin;
    102   for (origin = origins.begin(); origin != origins.end(); ++origin) {
    103     if (!special_storage_policy->IsStorageSessionOnly(*origin))
    104       continue;
    105     if (special_storage_policy.get() &&
    106         special_storage_policy->IsStorageProtected(*origin))
    107       continue;
    108 
    109     std::vector<AppCacheDatabase::GroupRecord> groups;
    110     database->FindGroupsForOrigin(*origin, &groups);
    111     std::vector<AppCacheDatabase::GroupRecord>::const_iterator group;
    112     for (group = groups.begin(); group != groups.end(); ++group) {
    113       sql::Transaction transaction(connection);
    114       if (!transaction.Begin()) {
    115         NOTREACHED() << "Failed to start transaction";
    116         return;
    117       }
    118       std::vector<int64> deletable_response_ids;
    119       bool success = DeleteGroupAndRelatedRecords(database,
    120                                                   group->group_id,
    121                                                   &deletable_response_ids);
    122       success = success && transaction.Commit();
    123       DCHECK(success);
    124     }  // for each group
    125   }  // for each origin
    126 }
    127 
    128 }  // namespace
    129 
    130 // DatabaseTask -----------------------------------------
    131 
    132 class AppCacheStorageImpl::DatabaseTask
    133     : public base::RefCountedThreadSafe<DatabaseTask> {
    134  public:
    135   explicit DatabaseTask(AppCacheStorageImpl* storage)
    136       : storage_(storage), database_(storage->database_),
    137         io_thread_(base::MessageLoopProxy::current()) {
    138     DCHECK(io_thread_.get());
    139   }
    140 
    141   void AddDelegate(DelegateReference* delegate_reference) {
    142     delegates_.push_back(make_scoped_refptr(delegate_reference));
    143   }
    144 
    145   // Schedules a task to be Run() on the DB thread. Tasks
    146   // are run in the order in which they are scheduled.
    147   void Schedule();
    148 
    149   // Called on the DB thread.
    150   virtual void Run() = 0;
    151 
    152   // Called on the IO thread after Run() has completed.
    153   virtual void RunCompleted() {}
    154 
    155   // Once scheduled a task cannot be cancelled, but the
    156   // call to RunCompleted may be. This method should only be
    157   // called on the IO thread. This is used by AppCacheStorageImpl
    158   // to cancel the completion calls when AppCacheStorageImpl is
    159   // destructed. This method may be overriden to release or delete
    160   // additional data associated with the task that is not DB thread
    161   // safe. If overriden, this base class method must be called from
    162   // within the override.
    163   virtual void CancelCompletion();
    164 
    165  protected:
    166   friend class base::RefCountedThreadSafe<DatabaseTask>;
    167   virtual ~DatabaseTask() {}
    168 
    169   AppCacheStorageImpl* storage_;
    170   AppCacheDatabase* database_;
    171   DelegateReferenceVector delegates_;
    172 
    173  private:
    174   void CallRun(base::TimeTicks schedule_time);
    175   void CallRunCompleted(base::TimeTicks schedule_time);
    176   void CallDisableStorage();
    177 
    178   scoped_refptr<base::MessageLoopProxy> io_thread_;
    179 };
    180 
    181 void AppCacheStorageImpl::DatabaseTask::Schedule() {
    182   DCHECK(storage_);
    183   DCHECK(io_thread_->BelongsToCurrentThread());
    184   if (!storage_->database_)
    185     return;
    186 
    187   if (storage_->db_thread_->PostTask(
    188       FROM_HERE,
    189       base::Bind(&DatabaseTask::CallRun, this, base::TimeTicks::Now()))) {
    190     storage_->scheduled_database_tasks_.push_back(this);
    191   } else {
    192     NOTREACHED() << "Thread for database tasks is not running.";
    193   }
    194 }
    195 
    196 void AppCacheStorageImpl::DatabaseTask::CancelCompletion() {
    197   DCHECK(io_thread_->BelongsToCurrentThread());
    198   delegates_.clear();
    199   storage_ = NULL;
    200 }
    201 
    202 void AppCacheStorageImpl::DatabaseTask::CallRun(
    203     base::TimeTicks schedule_time) {
    204   AppCacheHistograms::AddTaskQueueTimeSample(
    205       base::TimeTicks::Now() - schedule_time);
    206   if (!database_->is_disabled()) {
    207     base::TimeTicks run_time = base::TimeTicks::Now();
    208     Run();
    209     AppCacheHistograms::AddTaskRunTimeSample(
    210         base::TimeTicks::Now() - run_time);
    211     if (database_->is_disabled()) {
    212       io_thread_->PostTask(
    213           FROM_HERE,
    214           base::Bind(&DatabaseTask::CallDisableStorage, this));
    215     }
    216   }
    217   io_thread_->PostTask(
    218       FROM_HERE,
    219       base::Bind(&DatabaseTask::CallRunCompleted, this,
    220                  base::TimeTicks::Now()));
    221 }
    222 
    223 void AppCacheStorageImpl::DatabaseTask::CallRunCompleted(
    224     base::TimeTicks schedule_time) {
    225   AppCacheHistograms::AddCompletionQueueTimeSample(
    226       base::TimeTicks::Now() - schedule_time);
    227   if (storage_) {
    228     DCHECK(io_thread_->BelongsToCurrentThread());
    229     DCHECK(storage_->scheduled_database_tasks_.front() == this);
    230     storage_->scheduled_database_tasks_.pop_front();
    231     base::TimeTicks run_time = base::TimeTicks::Now();
    232     RunCompleted();
    233     AppCacheHistograms::AddCompletionRunTimeSample(
    234         base::TimeTicks::Now() - run_time);
    235     delegates_.clear();
    236   }
    237 }
    238 
    239 void AppCacheStorageImpl::DatabaseTask::CallDisableStorage() {
    240   if (storage_) {
    241     DCHECK(io_thread_->BelongsToCurrentThread());
    242     storage_->Disable();
    243   }
    244 }
    245 
    246 // InitTask -------
    247 
    248 class AppCacheStorageImpl::InitTask : public DatabaseTask {
    249  public:
    250   explicit InitTask(AppCacheStorageImpl* storage)
    251       : DatabaseTask(storage), last_group_id_(0),
    252         last_cache_id_(0), last_response_id_(0),
    253         last_deletable_response_rowid_(0) {}
    254 
    255   // DatabaseTask:
    256   virtual void Run() OVERRIDE;
    257   virtual void RunCompleted() OVERRIDE;
    258 
    259  protected:
    260   virtual ~InitTask() {}
    261 
    262  private:
    263   int64 last_group_id_;
    264   int64 last_cache_id_;
    265   int64 last_response_id_;
    266   int64 last_deletable_response_rowid_;
    267   std::map<GURL, int64> usage_map_;
    268 };
    269 
    270 void AppCacheStorageImpl::InitTask::Run() {
    271   database_->FindLastStorageIds(
    272       &last_group_id_, &last_cache_id_, &last_response_id_,
    273       &last_deletable_response_rowid_);
    274   database_->GetAllOriginUsage(&usage_map_);
    275 }
    276 
    277 void AppCacheStorageImpl::InitTask::RunCompleted() {
    278   storage_->last_group_id_ = last_group_id_;
    279   storage_->last_cache_id_ = last_cache_id_;
    280   storage_->last_response_id_ = last_response_id_;
    281   storage_->last_deletable_response_rowid_ = last_deletable_response_rowid_;
    282 
    283   if (!storage_->is_disabled()) {
    284     storage_->usage_map_.swap(usage_map_);
    285     const base::TimeDelta kDelay = base::TimeDelta::FromMinutes(5);
    286     base::MessageLoop::current()->PostDelayedTask(
    287         FROM_HERE,
    288         base::Bind(&AppCacheStorageImpl::DelayedStartDeletingUnusedResponses,
    289                    storage_->weak_factory_.GetWeakPtr()),
    290         kDelay);
    291   }
    292 
    293   if (storage_->service()->quota_client())
    294     storage_->service()->quota_client()->NotifyAppCacheReady();
    295 }
    296 
    297 // CloseConnectionTask -------
    298 
    299 class AppCacheStorageImpl::CloseConnectionTask : public DatabaseTask {
    300  public:
    301   explicit CloseConnectionTask(AppCacheStorageImpl* storage)
    302       : DatabaseTask(storage) {}
    303 
    304   // DatabaseTask:
    305   virtual void Run() OVERRIDE { database_->CloseConnection(); }
    306 
    307  protected:
    308   virtual ~CloseConnectionTask() {}
    309 };
    310 
    311 // DisableDatabaseTask -------
    312 
    313 class AppCacheStorageImpl::DisableDatabaseTask : public DatabaseTask {
    314  public:
    315   explicit DisableDatabaseTask(AppCacheStorageImpl* storage)
    316       : DatabaseTask(storage) {}
    317 
    318   // DatabaseTask:
    319   virtual void Run() OVERRIDE { database_->Disable(); }
    320 
    321  protected:
    322   virtual ~DisableDatabaseTask() {}
    323 };
    324 
    325 // GetAllInfoTask -------
    326 
    327 class AppCacheStorageImpl::GetAllInfoTask : public DatabaseTask {
    328  public:
    329   explicit GetAllInfoTask(AppCacheStorageImpl* storage)
    330       : DatabaseTask(storage),
    331         info_collection_(new AppCacheInfoCollection()) {
    332   }
    333 
    334   // DatabaseTask:
    335   virtual void Run() OVERRIDE;
    336   virtual void RunCompleted() OVERRIDE;
    337 
    338  protected:
    339   virtual ~GetAllInfoTask() {}
    340 
    341  private:
    342   scoped_refptr<AppCacheInfoCollection> info_collection_;
    343 };
    344 
    345 void AppCacheStorageImpl::GetAllInfoTask::Run() {
    346   std::set<GURL> origins;
    347   database_->FindOriginsWithGroups(&origins);
    348   for (std::set<GURL>::const_iterator origin = origins.begin();
    349        origin != origins.end(); ++origin) {
    350     AppCacheInfoVector& infos =
    351         info_collection_->infos_by_origin[*origin];
    352     std::vector<AppCacheDatabase::GroupRecord> groups;
    353     database_->FindGroupsForOrigin(*origin, &groups);
    354     for (std::vector<AppCacheDatabase::GroupRecord>::const_iterator
    355          group = groups.begin();
    356          group != groups.end(); ++group) {
    357       AppCacheDatabase::CacheRecord cache_record;
    358       database_->FindCacheForGroup(group->group_id, &cache_record);
    359       AppCacheInfo info;
    360       info.manifest_url = group->manifest_url;
    361       info.creation_time = group->creation_time;
    362       info.size = cache_record.cache_size;
    363       info.last_access_time = group->last_access_time;
    364       info.last_update_time = cache_record.update_time;
    365       info.cache_id = cache_record.cache_id;
    366       info.group_id = group->group_id;
    367       info.is_complete = true;
    368       infos.push_back(info);
    369     }
    370   }
    371 }
    372 
    373 void AppCacheStorageImpl::GetAllInfoTask::RunCompleted() {
    374   DCHECK(delegates_.size() == 1);
    375   FOR_EACH_DELEGATE(delegates_, OnAllInfo(info_collection_.get()));
    376 }
    377 
    378 // StoreOrLoadTask -------
    379 
    380 class AppCacheStorageImpl::StoreOrLoadTask : public DatabaseTask {
    381  protected:
    382   explicit StoreOrLoadTask(AppCacheStorageImpl* storage)
    383       : DatabaseTask(storage) {}
    384   virtual ~StoreOrLoadTask() {}
    385 
    386   bool FindRelatedCacheRecords(int64 cache_id);
    387   void CreateCacheAndGroupFromRecords(
    388       scoped_refptr<AppCache>* cache, scoped_refptr<AppCacheGroup>* group);
    389 
    390   AppCacheDatabase::GroupRecord group_record_;
    391   AppCacheDatabase::CacheRecord cache_record_;
    392   std::vector<AppCacheDatabase::EntryRecord> entry_records_;
    393   std::vector<AppCacheDatabase::NamespaceRecord>
    394       intercept_namespace_records_;
    395   std::vector<AppCacheDatabase::NamespaceRecord>
    396       fallback_namespace_records_;
    397   std::vector<AppCacheDatabase::OnlineWhiteListRecord>
    398       online_whitelist_records_;
    399 };
    400 
    401 bool AppCacheStorageImpl::StoreOrLoadTask::FindRelatedCacheRecords(
    402     int64 cache_id) {
    403   return database_->FindEntriesForCache(cache_id, &entry_records_) &&
    404          database_->FindNamespacesForCache(
    405              cache_id, &intercept_namespace_records_,
    406              &fallback_namespace_records_) &&
    407          database_->FindOnlineWhiteListForCache(
    408              cache_id, &online_whitelist_records_);
    409 }
    410 
    411 void AppCacheStorageImpl::StoreOrLoadTask::CreateCacheAndGroupFromRecords(
    412     scoped_refptr<AppCache>* cache, scoped_refptr<AppCacheGroup>* group) {
    413   DCHECK(storage_ && cache && group);
    414 
    415   (*cache) = storage_->working_set_.GetCache(cache_record_.cache_id);
    416   if (cache->get()) {
    417     (*group) = cache->get()->owning_group();
    418     DCHECK(group->get());
    419     DCHECK_EQ(group_record_.group_id, group->get()->group_id());
    420 
    421     // TODO(michaeln): histogram is fishing for clues to crbug/95101
    422     if (!cache->get()->GetEntry(group_record_.manifest_url)) {
    423       AppCacheHistograms::AddMissingManifestDetectedAtCallsite(
    424           AppCacheHistograms::CALLSITE_0);
    425     }
    426 
    427     storage_->NotifyStorageAccessed(group_record_.origin);
    428     return;
    429   }
    430 
    431   (*cache) = new AppCache(storage_, cache_record_.cache_id);
    432   cache->get()->InitializeWithDatabaseRecords(
    433       cache_record_, entry_records_,
    434       intercept_namespace_records_,
    435       fallback_namespace_records_,
    436       online_whitelist_records_);
    437   cache->get()->set_complete(true);
    438 
    439   (*group) = storage_->working_set_.GetGroup(group_record_.manifest_url);
    440   if (group->get()) {
    441     DCHECK(group_record_.group_id == group->get()->group_id());
    442     group->get()->AddCache(cache->get());
    443 
    444     // TODO(michaeln): histogram is fishing for clues to crbug/95101
    445     if (!cache->get()->GetEntry(group_record_.manifest_url)) {
    446       AppCacheHistograms::AddMissingManifestDetectedAtCallsite(
    447           AppCacheHistograms::CALLSITE_1);
    448     }
    449   } else {
    450     (*group) = new AppCacheGroup(
    451         storage_, group_record_.manifest_url,
    452         group_record_.group_id);
    453     group->get()->set_creation_time(group_record_.creation_time);
    454     group->get()->AddCache(cache->get());
    455 
    456     // TODO(michaeln): histogram is fishing for clues to crbug/95101
    457     if (!cache->get()->GetEntry(group_record_.manifest_url)) {
    458       AppCacheHistograms::AddMissingManifestDetectedAtCallsite(
    459           AppCacheHistograms::CALLSITE_2);
    460     }
    461   }
    462   DCHECK(group->get()->newest_complete_cache() == cache->get());
    463 
    464   // We have to update foriegn entries if MarkEntryAsForeignTasks
    465   // are in flight.
    466   std::vector<GURL> urls;
    467   storage_->GetPendingForeignMarkingsForCache(cache->get()->cache_id(), &urls);
    468   for (std::vector<GURL>::iterator iter = urls.begin();
    469        iter != urls.end(); ++iter) {
    470     DCHECK(cache->get()->GetEntry(*iter));
    471     cache->get()->GetEntry(*iter)->add_types(AppCacheEntry::FOREIGN);
    472   }
    473 
    474   storage_->NotifyStorageAccessed(group_record_.origin);
    475 
    476   // TODO(michaeln): Maybe verify that the responses we expect to exist
    477   // do actually exist in the disk_cache (and if not then what?)
    478 }
    479 
    480 // CacheLoadTask -------
    481 
    482 class AppCacheStorageImpl::CacheLoadTask : public StoreOrLoadTask {
    483  public:
    484   CacheLoadTask(int64 cache_id, AppCacheStorageImpl* storage)
    485       : StoreOrLoadTask(storage), cache_id_(cache_id),
    486         success_(false) {}
    487 
    488   // DatabaseTask:
    489   virtual void Run() OVERRIDE;
    490   virtual void RunCompleted() OVERRIDE;
    491 
    492  protected:
    493   virtual ~CacheLoadTask() {}
    494 
    495  private:
    496   int64 cache_id_;
    497   bool success_;
    498 };
    499 
    500 void AppCacheStorageImpl::CacheLoadTask::Run() {
    501   success_ =
    502       database_->FindCache(cache_id_, &cache_record_) &&
    503       database_->FindGroup(cache_record_.group_id, &group_record_) &&
    504       FindRelatedCacheRecords(cache_id_);
    505 
    506   if (success_)
    507     database_->UpdateGroupLastAccessTime(group_record_.group_id,
    508                                          base::Time::Now());
    509 }
    510 
    511 void AppCacheStorageImpl::CacheLoadTask::RunCompleted() {
    512   storage_->pending_cache_loads_.erase(cache_id_);
    513   scoped_refptr<AppCache> cache;
    514   scoped_refptr<AppCacheGroup> group;
    515   if (success_ && !storage_->is_disabled()) {
    516     DCHECK(cache_record_.cache_id == cache_id_);
    517     CreateCacheAndGroupFromRecords(&cache, &group);
    518   }
    519   FOR_EACH_DELEGATE(delegates_, OnCacheLoaded(cache.get(), cache_id_));
    520 }
    521 
    522 // GroupLoadTask -------
    523 
    524 class AppCacheStorageImpl::GroupLoadTask : public StoreOrLoadTask {
    525  public:
    526   GroupLoadTask(GURL manifest_url, AppCacheStorageImpl* storage)
    527       : StoreOrLoadTask(storage), manifest_url_(manifest_url),
    528         success_(false) {}
    529 
    530   // DatabaseTask:
    531   virtual void Run() OVERRIDE;
    532   virtual void RunCompleted() OVERRIDE;
    533 
    534  protected:
    535   virtual ~GroupLoadTask() {}
    536 
    537  private:
    538   GURL manifest_url_;
    539   bool success_;
    540 };
    541 
    542 void AppCacheStorageImpl::GroupLoadTask::Run() {
    543   success_ =
    544       database_->FindGroupForManifestUrl(manifest_url_, &group_record_) &&
    545       database_->FindCacheForGroup(group_record_.group_id, &cache_record_) &&
    546       FindRelatedCacheRecords(cache_record_.cache_id);
    547 
    548   if (success_)
    549     database_->UpdateGroupLastAccessTime(group_record_.group_id,
    550                                          base::Time::Now());
    551 }
    552 
    553 void AppCacheStorageImpl::GroupLoadTask::RunCompleted() {
    554   storage_->pending_group_loads_.erase(manifest_url_);
    555   scoped_refptr<AppCacheGroup> group;
    556   scoped_refptr<AppCache> cache;
    557   if (!storage_->is_disabled()) {
    558     if (success_) {
    559       DCHECK(group_record_.manifest_url == manifest_url_);
    560       CreateCacheAndGroupFromRecords(&cache, &group);
    561     } else {
    562       group = storage_->working_set_.GetGroup(manifest_url_);
    563       if (!group.get()) {
    564         group =
    565             new AppCacheGroup(storage_, manifest_url_, storage_->NewGroupId());
    566       }
    567     }
    568   }
    569   FOR_EACH_DELEGATE(delegates_, OnGroupLoaded(group.get(), manifest_url_));
    570 }
    571 
    572 // StoreGroupAndCacheTask -------
    573 
    574 class AppCacheStorageImpl::StoreGroupAndCacheTask : public StoreOrLoadTask {
    575  public:
    576   StoreGroupAndCacheTask(AppCacheStorageImpl* storage, AppCacheGroup* group,
    577                          AppCache* newest_cache);
    578 
    579   void GetQuotaThenSchedule();
    580   void OnQuotaCallback(
    581       quota::QuotaStatusCode status, int64 usage, int64 quota);
    582 
    583   // DatabaseTask:
    584   virtual void Run() OVERRIDE;
    585   virtual void RunCompleted() OVERRIDE;
    586   virtual void CancelCompletion() OVERRIDE;
    587 
    588  protected:
    589   virtual ~StoreGroupAndCacheTask() {}
    590 
    591  private:
    592   scoped_refptr<AppCacheGroup> group_;
    593   scoped_refptr<AppCache> cache_;
    594   bool success_;
    595   bool would_exceed_quota_;
    596   int64 space_available_;
    597   int64 new_origin_usage_;
    598   std::vector<int64> newly_deletable_response_ids_;
    599 };
    600 
    601 AppCacheStorageImpl::StoreGroupAndCacheTask::StoreGroupAndCacheTask(
    602     AppCacheStorageImpl* storage, AppCacheGroup* group, AppCache* newest_cache)
    603     : StoreOrLoadTask(storage), group_(group), cache_(newest_cache),
    604       success_(false), would_exceed_quota_(false),
    605       space_available_(-1), new_origin_usage_(-1) {
    606   group_record_.group_id = group->group_id();
    607   group_record_.manifest_url = group->manifest_url();
    608   group_record_.origin = group_record_.manifest_url.GetOrigin();
    609   newest_cache->ToDatabaseRecords(
    610       group,
    611       &cache_record_, &entry_records_,
    612       &intercept_namespace_records_,
    613       &fallback_namespace_records_,
    614       &online_whitelist_records_);
    615 }
    616 
    617 void AppCacheStorageImpl::StoreGroupAndCacheTask::GetQuotaThenSchedule() {
    618   quota::QuotaManager* quota_manager = NULL;
    619   if (storage_->service()->quota_manager_proxy()) {
    620     quota_manager =
    621         storage_->service()->quota_manager_proxy()->quota_manager();
    622   }
    623 
    624   if (!quota_manager) {
    625     if (storage_->service()->special_storage_policy() &&
    626         storage_->service()->special_storage_policy()->IsStorageUnlimited(
    627             group_record_.origin))
    628       space_available_ = kint64max;
    629     Schedule();
    630     return;
    631   }
    632 
    633   // We have to ask the quota manager for the value.
    634   storage_->pending_quota_queries_.insert(this);
    635   quota_manager->GetUsageAndQuota(
    636       group_record_.origin, quota::kStorageTypeTemporary,
    637       base::Bind(&StoreGroupAndCacheTask::OnQuotaCallback, this));
    638 }
    639 
    640 void AppCacheStorageImpl::StoreGroupAndCacheTask::OnQuotaCallback(
    641     quota::QuotaStatusCode status, int64 usage, int64 quota) {
    642   if (storage_) {
    643     if (status == quota::kQuotaStatusOk)
    644       space_available_ = std::max(static_cast<int64>(0), quota - usage);
    645     else
    646       space_available_ = 0;
    647     storage_->pending_quota_queries_.erase(this);
    648     Schedule();
    649   }
    650 }
    651 
    652 void AppCacheStorageImpl::StoreGroupAndCacheTask::Run() {
    653   DCHECK(!success_);
    654   sql::Connection* connection = database_->db_connection();
    655   if (!connection)
    656     return;
    657 
    658   sql::Transaction transaction(connection);
    659   if (!transaction.Begin())
    660     return;
    661 
    662   int64 old_origin_usage = database_->GetOriginUsage(group_record_.origin);
    663 
    664   AppCacheDatabase::GroupRecord existing_group;
    665   success_ = database_->FindGroup(group_record_.group_id, &existing_group);
    666   if (!success_) {
    667     group_record_.creation_time = base::Time::Now();
    668     group_record_.last_access_time = base::Time::Now();
    669     success_ = database_->InsertGroup(&group_record_);
    670   } else {
    671     DCHECK(group_record_.group_id == existing_group.group_id);
    672     DCHECK(group_record_.manifest_url == existing_group.manifest_url);
    673     DCHECK(group_record_.origin == existing_group.origin);
    674 
    675     database_->UpdateGroupLastAccessTime(group_record_.group_id,
    676                                          base::Time::Now());
    677 
    678     AppCacheDatabase::CacheRecord cache;
    679     if (database_->FindCacheForGroup(group_record_.group_id, &cache)) {
    680       // Get the set of response ids in the old cache.
    681       std::set<int64> existing_response_ids;
    682       database_->FindResponseIdsForCacheAsSet(cache.cache_id,
    683                                               &existing_response_ids);
    684 
    685       // Remove those that remain in the new cache.
    686       std::vector<AppCacheDatabase::EntryRecord>::const_iterator entry_iter =
    687           entry_records_.begin();
    688       while (entry_iter != entry_records_.end()) {
    689         existing_response_ids.erase(entry_iter->response_id);
    690         ++entry_iter;
    691       }
    692 
    693       // The rest are deletable.
    694       std::set<int64>::const_iterator id_iter = existing_response_ids.begin();
    695       while (id_iter != existing_response_ids.end()) {
    696         newly_deletable_response_ids_.push_back(*id_iter);
    697         ++id_iter;
    698       }
    699 
    700       success_ =
    701           database_->DeleteCache(cache.cache_id) &&
    702           database_->DeleteEntriesForCache(cache.cache_id) &&
    703           database_->DeleteNamespacesForCache(cache.cache_id) &&
    704           database_->DeleteOnlineWhiteListForCache(cache.cache_id) &&
    705           database_->InsertDeletableResponseIds(newly_deletable_response_ids_);
    706           // TODO(michaeln): store group_id too with deletable ids
    707     } else {
    708       NOTREACHED() << "A existing group without a cache is unexpected";
    709     }
    710   }
    711 
    712   success_ =
    713       success_ &&
    714       database_->InsertCache(&cache_record_) &&
    715       database_->InsertEntryRecords(entry_records_) &&
    716       database_->InsertNamespaceRecords(intercept_namespace_records_) &&
    717       database_->InsertNamespaceRecords(fallback_namespace_records_) &&
    718       database_->InsertOnlineWhiteListRecords(online_whitelist_records_);
    719 
    720   if (!success_)
    721     return;
    722 
    723   new_origin_usage_ = database_->GetOriginUsage(group_record_.origin);
    724 
    725   // Only check quota when the new usage exceeds the old usage.
    726   if (new_origin_usage_ <= old_origin_usage) {
    727     success_ = transaction.Commit();
    728     return;
    729   }
    730 
    731   // Use a simple hard-coded value when not using quota management.
    732   if (space_available_ == -1) {
    733     if (new_origin_usage_ > kDefaultQuota) {
    734       would_exceed_quota_ = true;
    735       success_ = false;
    736       return;
    737     }
    738     success_ = transaction.Commit();
    739     return;
    740   }
    741 
    742   // Check limits based on the space availbable given to us via the
    743   // quota system.
    744   int64 delta = new_origin_usage_ - old_origin_usage;
    745   if (delta > space_available_) {
    746     would_exceed_quota_ = true;
    747     success_ = false;
    748     return;
    749   }
    750 
    751   success_ = transaction.Commit();
    752 }
    753 
    754 void AppCacheStorageImpl::StoreGroupAndCacheTask::RunCompleted() {
    755   if (success_) {
    756     storage_->UpdateUsageMapAndNotify(
    757         group_->manifest_url().GetOrigin(), new_origin_usage_);
    758     if (cache_.get() != group_->newest_complete_cache()) {
    759       cache_->set_complete(true);
    760       group_->AddCache(cache_.get());
    761     }
    762     if (group_->creation_time().is_null())
    763       group_->set_creation_time(group_record_.creation_time);
    764     group_->AddNewlyDeletableResponseIds(&newly_deletable_response_ids_);
    765   }
    766   FOR_EACH_DELEGATE(
    767       delegates_,
    768       OnGroupAndNewestCacheStored(
    769           group_.get(), cache_.get(), success_, would_exceed_quota_));
    770   group_ = NULL;
    771   cache_ = NULL;
    772 
    773   // TODO(michaeln): if (would_exceed_quota_) what if the current usage
    774   // also exceeds the quota? http://crbug.com/83968
    775 }
    776 
    777 void AppCacheStorageImpl::StoreGroupAndCacheTask::CancelCompletion() {
    778   // Overriden to safely drop our reference to the group and cache
    779   // which are not thread safe refcounted.
    780   DatabaseTask::CancelCompletion();
    781   group_ = NULL;
    782   cache_ = NULL;
    783 }
    784 
    785 // FindMainResponseTask -------
    786 
    787 // Helpers for FindMainResponseTask::Run()
    788 namespace {
    789 class SortByCachePreference
    790     : public std::binary_function<
    791         AppCacheDatabase::EntryRecord,
    792         AppCacheDatabase::EntryRecord,
    793         bool> {
    794  public:
    795   SortByCachePreference(int64 preferred_id, const std::set<int64>& in_use_ids)
    796       : preferred_id_(preferred_id), in_use_ids_(in_use_ids) {
    797   }
    798   bool operator()(
    799       const AppCacheDatabase::EntryRecord& lhs,
    800       const AppCacheDatabase::EntryRecord& rhs) {
    801     return compute_value(lhs) > compute_value(rhs);
    802   }
    803  private:
    804   int compute_value(const AppCacheDatabase::EntryRecord& entry) {
    805     if (entry.cache_id == preferred_id_)
    806       return 100;
    807     else if (in_use_ids_.find(entry.cache_id) != in_use_ids_.end())
    808       return 50;
    809     return 0;
    810   }
    811   int64 preferred_id_;
    812   const std::set<int64>& in_use_ids_;
    813 };
    814 
    815 bool SortByLength(
    816     const AppCacheDatabase::NamespaceRecord& lhs,
    817     const AppCacheDatabase::NamespaceRecord& rhs) {
    818   return lhs.namespace_.namespace_url.spec().length() >
    819          rhs.namespace_.namespace_url.spec().length();
    820 }
    821 
    822 class NetworkNamespaceHelper {
    823  public:
    824   explicit NetworkNamespaceHelper(AppCacheDatabase* database)
    825       : database_(database) {
    826   }
    827 
    828   bool IsInNetworkNamespace(const GURL& url, int64 cache_id) {
    829     typedef std::pair<WhiteListMap::iterator, bool> InsertResult;
    830     InsertResult result = namespaces_map_.insert(
    831         WhiteListMap::value_type(cache_id, NamespaceVector()));
    832     if (result.second)
    833       GetOnlineWhiteListForCache(cache_id, &result.first->second);
    834     return AppCache::FindNamespace(result.first->second, url) != NULL;
    835   }
    836 
    837  private:
    838   void GetOnlineWhiteListForCache(
    839       int64 cache_id, NamespaceVector* namespaces) {
    840     DCHECK(namespaces && namespaces->empty());
    841     typedef std::vector<AppCacheDatabase::OnlineWhiteListRecord>
    842         WhiteListVector;
    843     WhiteListVector records;
    844     if (!database_->FindOnlineWhiteListForCache(cache_id, &records))
    845       return;
    846     WhiteListVector::const_iterator iter = records.begin();
    847     while (iter != records.end()) {
    848       namespaces->push_back(
    849             Namespace(NETWORK_NAMESPACE, iter->namespace_url, GURL(),
    850                       iter->is_pattern));
    851       ++iter;
    852     }
    853   }
    854 
    855   // Key is cache id
    856   typedef std::map<int64, NamespaceVector> WhiteListMap;
    857   WhiteListMap namespaces_map_;
    858   AppCacheDatabase* database_;
    859 };
    860 
    861 }  // namespace
    862 
    863 class AppCacheStorageImpl::FindMainResponseTask : public DatabaseTask {
    864  public:
    865   FindMainResponseTask(AppCacheStorageImpl* storage,
    866                        const GURL& url,
    867                        const GURL& preferred_manifest_url,
    868                        const AppCacheWorkingSet::GroupMap* groups_in_use)
    869       : DatabaseTask(storage), url_(url),
    870         preferred_manifest_url_(preferred_manifest_url),
    871         cache_id_(kNoCacheId), group_id_(0) {
    872     if (groups_in_use) {
    873       for (AppCacheWorkingSet::GroupMap::const_iterator it =
    874                groups_in_use->begin();
    875            it != groups_in_use->end(); ++it) {
    876         AppCacheGroup* group = it->second;
    877         AppCache* cache = group->newest_complete_cache();
    878         if (group->is_obsolete() || !cache)
    879           continue;
    880         cache_ids_in_use_.insert(cache->cache_id());
    881       }
    882     }
    883   }
    884 
    885   // DatabaseTask:
    886   virtual void Run() OVERRIDE;
    887   virtual void RunCompleted() OVERRIDE;
    888 
    889  protected:
    890   virtual ~FindMainResponseTask() {}
    891 
    892  private:
    893   typedef std::vector<AppCacheDatabase::NamespaceRecord*>
    894       NamespaceRecordPtrVector;
    895 
    896   bool FindExactMatch(int64 preferred_id);
    897   bool FindNamespaceMatch(int64 preferred_id);
    898   bool FindNamespaceHelper(
    899       int64 preferred_cache_id,
    900       AppCacheDatabase::NamespaceRecordVector* namespaces,
    901       NetworkNamespaceHelper* network_namespace_helper);
    902   bool FindFirstValidNamespace(const NamespaceRecordPtrVector& namespaces);
    903 
    904   GURL url_;
    905   GURL preferred_manifest_url_;
    906   std::set<int64> cache_ids_in_use_;
    907   AppCacheEntry entry_;
    908   AppCacheEntry fallback_entry_;
    909   GURL namespace_entry_url_;
    910   int64 cache_id_;
    911   int64 group_id_;
    912   GURL manifest_url_;
    913 };
    914 
    915 
    916 
    917 void AppCacheStorageImpl::FindMainResponseTask::Run() {
    918   // NOTE: The heuristics around choosing amoungst multiple candidates
    919   // is underspecified, and just plain not fully understood. This needs
    920   // to be refined.
    921 
    922   // The 'preferred_manifest_url' is the url of the manifest associated
    923   // with the page that opened or embedded the page being loaded now.
    924   // We have a strong preference to use resources from that cache.
    925   // We also have a lesser bias to use resources from caches that are currently
    926   // being used by other unrelated pages.
    927   // TODO(michaeln): come up with a 'preferred_manifest_url' in more cases
    928   // - when navigating a frame whose current contents are from an appcache
    929   // - when clicking an href in a frame that is appcached
    930   int64 preferred_cache_id = kNoCacheId;
    931   if (!preferred_manifest_url_.is_empty()) {
    932     AppCacheDatabase::GroupRecord preferred_group;
    933     AppCacheDatabase::CacheRecord preferred_cache;
    934     if (database_->FindGroupForManifestUrl(
    935             preferred_manifest_url_, &preferred_group) &&
    936         database_->FindCacheForGroup(
    937             preferred_group.group_id, &preferred_cache)) {
    938       preferred_cache_id = preferred_cache.cache_id;
    939     }
    940   }
    941 
    942   if (FindExactMatch(preferred_cache_id) ||
    943       FindNamespaceMatch(preferred_cache_id)) {
    944     // We found something.
    945     DCHECK(cache_id_ != kNoCacheId && !manifest_url_.is_empty() &&
    946            group_id_ != 0);
    947     return;
    948   }
    949 
    950   // We didn't find anything.
    951   DCHECK(cache_id_ == kNoCacheId && manifest_url_.is_empty() &&
    952          group_id_ == 0);
    953 }
    954 
    955 bool AppCacheStorageImpl::
    956 FindMainResponseTask::FindExactMatch(int64 preferred_cache_id) {
    957   std::vector<AppCacheDatabase::EntryRecord> entries;
    958   if (database_->FindEntriesForUrl(url_, &entries) && !entries.empty()) {
    959     // Sort them in order of preference, from the preferred_cache first,
    960     // followed by hits from caches that are 'in use', then the rest.
    961     std::sort(entries.begin(), entries.end(),
    962               SortByCachePreference(preferred_cache_id, cache_ids_in_use_));
    963 
    964     // Take the first with a valid, non-foreign entry.
    965     std::vector<AppCacheDatabase::EntryRecord>::iterator iter;
    966     for (iter = entries.begin(); iter < entries.end(); ++iter) {
    967       AppCacheDatabase::GroupRecord group_record;
    968       if ((iter->flags & AppCacheEntry::FOREIGN) ||
    969           !database_->FindGroupForCache(iter->cache_id, &group_record)) {
    970         continue;
    971       }
    972       manifest_url_ = group_record.manifest_url;
    973       group_id_ = group_record.group_id;
    974       entry_ = AppCacheEntry(iter->flags, iter->response_id);
    975       cache_id_ = iter->cache_id;
    976       return true;  // We found an exact match.
    977     }
    978   }
    979   return false;
    980 }
    981 
    982 bool AppCacheStorageImpl::
    983 FindMainResponseTask::FindNamespaceMatch(int64 preferred_cache_id) {
    984   AppCacheDatabase::NamespaceRecordVector all_intercepts;
    985   AppCacheDatabase::NamespaceRecordVector all_fallbacks;
    986   if (!database_->FindNamespacesForOrigin(
    987           url_.GetOrigin(), &all_intercepts, &all_fallbacks)
    988       || (all_intercepts.empty() && all_fallbacks.empty())) {
    989     return false;
    990   }
    991 
    992   NetworkNamespaceHelper network_namespace_helper(database_);
    993   if (FindNamespaceHelper(preferred_cache_id,
    994                           &all_intercepts,
    995                           &network_namespace_helper) ||
    996       FindNamespaceHelper(preferred_cache_id,
    997                           &all_fallbacks,
    998                           &network_namespace_helper)) {
    999     return true;
   1000   }
   1001   return false;
   1002 }
   1003 
   1004 bool AppCacheStorageImpl::
   1005 FindMainResponseTask::FindNamespaceHelper(
   1006     int64 preferred_cache_id,
   1007     AppCacheDatabase::NamespaceRecordVector* namespaces,
   1008     NetworkNamespaceHelper* network_namespace_helper) {
   1009   // Sort them by length, longer matches within the same cache/bucket take
   1010   // precedence.
   1011   std::sort(namespaces->begin(), namespaces->end(), SortByLength);
   1012 
   1013   NamespaceRecordPtrVector preferred_namespaces;
   1014   NamespaceRecordPtrVector inuse_namespaces;
   1015   NamespaceRecordPtrVector other_namespaces;
   1016   std::vector<AppCacheDatabase::NamespaceRecord>::iterator iter;
   1017   for (iter = namespaces->begin(); iter < namespaces->end(); ++iter) {
   1018     // Skip those that aren't a match.
   1019     if (!iter->namespace_.IsMatch(url_))
   1020       continue;
   1021 
   1022     // Skip namespaces where the requested url falls into a network
   1023     // namespace of its containing appcache.
   1024     if (network_namespace_helper->IsInNetworkNamespace(url_, iter->cache_id))
   1025       continue;
   1026 
   1027     // Bin them into one of our three buckets.
   1028     if (iter->cache_id == preferred_cache_id)
   1029       preferred_namespaces.push_back(&(*iter));
   1030     else if (cache_ids_in_use_.find(iter->cache_id) != cache_ids_in_use_.end())
   1031       inuse_namespaces.push_back(&(*iter));
   1032     else
   1033       other_namespaces.push_back(&(*iter));
   1034   }
   1035 
   1036   if (FindFirstValidNamespace(preferred_namespaces) ||
   1037       FindFirstValidNamespace(inuse_namespaces) ||
   1038       FindFirstValidNamespace(other_namespaces))
   1039     return true;  // We found one.
   1040 
   1041   // We didn't find anything.
   1042   return false;
   1043 }
   1044 
   1045 bool AppCacheStorageImpl::
   1046 FindMainResponseTask::FindFirstValidNamespace(
   1047     const NamespaceRecordPtrVector& namespaces) {
   1048   // Take the first with a valid, non-foreign entry.
   1049   NamespaceRecordPtrVector::const_iterator iter;
   1050   for (iter = namespaces.begin(); iter < namespaces.end();  ++iter) {
   1051     AppCacheDatabase::EntryRecord entry_record;
   1052     if (database_->FindEntry((*iter)->cache_id, (*iter)->namespace_.target_url,
   1053                              &entry_record)) {
   1054       AppCacheDatabase::GroupRecord group_record;
   1055       if ((entry_record.flags & AppCacheEntry::FOREIGN) ||
   1056           !database_->FindGroupForCache(entry_record.cache_id, &group_record)) {
   1057         continue;
   1058       }
   1059       manifest_url_ = group_record.manifest_url;
   1060       group_id_ = group_record.group_id;
   1061       cache_id_ = (*iter)->cache_id;
   1062       namespace_entry_url_ = (*iter)->namespace_.target_url;
   1063       if ((*iter)->namespace_.type == FALLBACK_NAMESPACE)
   1064         fallback_entry_ = AppCacheEntry(entry_record.flags,
   1065                                         entry_record.response_id);
   1066       else
   1067         entry_ = AppCacheEntry(entry_record.flags, entry_record.response_id);
   1068       return true;  // We found one.
   1069     }
   1070   }
   1071   return false;  // We didn't find a match.
   1072 }
   1073 
   1074 void AppCacheStorageImpl::FindMainResponseTask::RunCompleted() {
   1075   storage_->CallOnMainResponseFound(
   1076       &delegates_, url_, entry_, namespace_entry_url_, fallback_entry_,
   1077       cache_id_, group_id_, manifest_url_);
   1078 }
   1079 
   1080 // MarkEntryAsForeignTask -------
   1081 
   1082 class AppCacheStorageImpl::MarkEntryAsForeignTask : public DatabaseTask {
   1083  public:
   1084   MarkEntryAsForeignTask(
   1085       AppCacheStorageImpl* storage, const GURL& url, int64 cache_id)
   1086       : DatabaseTask(storage), cache_id_(cache_id), entry_url_(url) {}
   1087 
   1088   // DatabaseTask:
   1089   virtual void Run() OVERRIDE;
   1090   virtual void RunCompleted() OVERRIDE;
   1091 
   1092  protected:
   1093   virtual ~MarkEntryAsForeignTask() {}
   1094 
   1095  private:
   1096   int64 cache_id_;
   1097   GURL entry_url_;
   1098 };
   1099 
   1100 void AppCacheStorageImpl::MarkEntryAsForeignTask::Run() {
   1101   database_->AddEntryFlags(entry_url_, cache_id_, AppCacheEntry::FOREIGN);
   1102 }
   1103 
   1104 void AppCacheStorageImpl::MarkEntryAsForeignTask::RunCompleted() {
   1105   DCHECK(storage_->pending_foreign_markings_.front().first == entry_url_ &&
   1106          storage_->pending_foreign_markings_.front().second == cache_id_);
   1107   storage_->pending_foreign_markings_.pop_front();
   1108 }
   1109 
   1110 // MakeGroupObsoleteTask -------
   1111 
   1112 class AppCacheStorageImpl::MakeGroupObsoleteTask : public DatabaseTask {
   1113  public:
   1114   MakeGroupObsoleteTask(AppCacheStorageImpl* storage, AppCacheGroup* group);
   1115 
   1116   // DatabaseTask:
   1117   virtual void Run() OVERRIDE;
   1118   virtual void RunCompleted() OVERRIDE;
   1119   virtual void CancelCompletion() OVERRIDE;
   1120 
   1121  protected:
   1122   virtual ~MakeGroupObsoleteTask() {}
   1123 
   1124  private:
   1125   scoped_refptr<AppCacheGroup> group_;
   1126   int64 group_id_;
   1127   GURL origin_;
   1128   bool success_;
   1129   int64 new_origin_usage_;
   1130   std::vector<int64> newly_deletable_response_ids_;
   1131 };
   1132 
   1133 AppCacheStorageImpl::MakeGroupObsoleteTask::MakeGroupObsoleteTask(
   1134     AppCacheStorageImpl* storage, AppCacheGroup* group)
   1135     : DatabaseTask(storage), group_(group), group_id_(group->group_id()),
   1136       origin_(group->manifest_url().GetOrigin()),
   1137       success_(false), new_origin_usage_(-1) {
   1138 }
   1139 
   1140 void AppCacheStorageImpl::MakeGroupObsoleteTask::Run() {
   1141   DCHECK(!success_);
   1142   sql::Connection* connection = database_->db_connection();
   1143   if (!connection)
   1144     return;
   1145 
   1146   sql::Transaction transaction(connection);
   1147   if (!transaction.Begin())
   1148     return;
   1149 
   1150   AppCacheDatabase::GroupRecord group_record;
   1151   if (!database_->FindGroup(group_id_, &group_record)) {
   1152     // This group doesn't exists in the database, nothing todo here.
   1153     new_origin_usage_ = database_->GetOriginUsage(origin_);
   1154     success_ = true;
   1155     return;
   1156   }
   1157 
   1158   DCHECK_EQ(group_record.origin, origin_);
   1159   success_ = DeleteGroupAndRelatedRecords(database_,
   1160                                           group_id_,
   1161                                           &newly_deletable_response_ids_);
   1162 
   1163   new_origin_usage_ = database_->GetOriginUsage(origin_);
   1164   success_ = success_ && transaction.Commit();
   1165 }
   1166 
   1167 void AppCacheStorageImpl::MakeGroupObsoleteTask::RunCompleted() {
   1168   if (success_) {
   1169     group_->set_obsolete(true);
   1170     if (!storage_->is_disabled()) {
   1171       storage_->UpdateUsageMapAndNotify(origin_, new_origin_usage_);
   1172       group_->AddNewlyDeletableResponseIds(&newly_deletable_response_ids_);
   1173 
   1174       // Also remove from the working set, caches for an 'obsolete' group
   1175       // may linger in use, but the group itself cannot be looked up by
   1176       // 'manifest_url' in the working set any longer.
   1177       storage_->working_set()->RemoveGroup(group_.get());
   1178     }
   1179   }
   1180   FOR_EACH_DELEGATE(delegates_, OnGroupMadeObsolete(group_.get(), success_));
   1181   group_ = NULL;
   1182 }
   1183 
   1184 void AppCacheStorageImpl::MakeGroupObsoleteTask::CancelCompletion() {
   1185   // Overriden to safely drop our reference to the group
   1186   // which is not thread safe refcounted.
   1187   DatabaseTask::CancelCompletion();
   1188   group_ = NULL;
   1189 }
   1190 
   1191 // GetDeletableResponseIdsTask -------
   1192 
   1193 class AppCacheStorageImpl::GetDeletableResponseIdsTask : public DatabaseTask {
   1194  public:
   1195   GetDeletableResponseIdsTask(AppCacheStorageImpl* storage, int64 max_rowid)
   1196       : DatabaseTask(storage), max_rowid_(max_rowid) {}
   1197 
   1198   // DatabaseTask:
   1199   virtual void Run() OVERRIDE;
   1200   virtual void RunCompleted() OVERRIDE;
   1201 
   1202  protected:
   1203   virtual ~GetDeletableResponseIdsTask() {}
   1204 
   1205  private:
   1206   int64 max_rowid_;
   1207   std::vector<int64> response_ids_;
   1208 };
   1209 
   1210 void AppCacheStorageImpl::GetDeletableResponseIdsTask::Run() {
   1211   const int kSqlLimit = 1000;
   1212   database_->GetDeletableResponseIds(&response_ids_, max_rowid_, kSqlLimit);
   1213   // TODO(michaeln): retrieve group_ids too
   1214 }
   1215 
   1216 void AppCacheStorageImpl::GetDeletableResponseIdsTask::RunCompleted() {
   1217   if (!response_ids_.empty())
   1218     storage_->StartDeletingResponses(response_ids_);
   1219 }
   1220 
   1221 // InsertDeletableResponseIdsTask -------
   1222 
   1223 class AppCacheStorageImpl::InsertDeletableResponseIdsTask
   1224     : public DatabaseTask {
   1225  public:
   1226   explicit InsertDeletableResponseIdsTask(AppCacheStorageImpl* storage)
   1227       : DatabaseTask(storage) {}
   1228 
   1229   // DatabaseTask:
   1230   virtual void Run() OVERRIDE;
   1231 
   1232   std::vector<int64> response_ids_;
   1233 
   1234  protected:
   1235   virtual ~InsertDeletableResponseIdsTask() {}
   1236 };
   1237 
   1238 void AppCacheStorageImpl::InsertDeletableResponseIdsTask::Run() {
   1239   database_->InsertDeletableResponseIds(response_ids_);
   1240   // TODO(michaeln): store group_ids too
   1241 }
   1242 
   1243 // DeleteDeletableResponseIdsTask -------
   1244 
   1245 class AppCacheStorageImpl::DeleteDeletableResponseIdsTask
   1246     : public DatabaseTask {
   1247  public:
   1248   explicit DeleteDeletableResponseIdsTask(AppCacheStorageImpl* storage)
   1249       : DatabaseTask(storage) {}
   1250 
   1251   // DatabaseTask:
   1252   virtual void Run() OVERRIDE;
   1253 
   1254   std::vector<int64> response_ids_;
   1255 
   1256  protected:
   1257   virtual ~DeleteDeletableResponseIdsTask() {}
   1258 };
   1259 
   1260 void AppCacheStorageImpl::DeleteDeletableResponseIdsTask::Run() {
   1261   database_->DeleteDeletableResponseIds(response_ids_);
   1262 }
   1263 
   1264 // UpdateGroupLastAccessTimeTask -------
   1265 
   1266 class AppCacheStorageImpl::UpdateGroupLastAccessTimeTask
   1267     : public DatabaseTask {
   1268  public:
   1269   UpdateGroupLastAccessTimeTask(
   1270       AppCacheStorageImpl* storage, AppCacheGroup* group, base::Time time)
   1271       : DatabaseTask(storage), group_id_(group->group_id()),
   1272         last_access_time_(time) {
   1273     storage->NotifyStorageAccessed(group->manifest_url().GetOrigin());
   1274   }
   1275 
   1276   // DatabaseTask:
   1277   virtual void Run() OVERRIDE;
   1278 
   1279  protected:
   1280   virtual ~UpdateGroupLastAccessTimeTask() {}
   1281 
   1282  private:
   1283   int64 group_id_;
   1284   base::Time last_access_time_;
   1285 };
   1286 
   1287 void AppCacheStorageImpl::UpdateGroupLastAccessTimeTask::Run() {
   1288   database_->UpdateGroupLastAccessTime(group_id_, last_access_time_);
   1289 }
   1290 
   1291 
   1292 // AppCacheStorageImpl ---------------------------------------------------
   1293 
   1294 AppCacheStorageImpl::AppCacheStorageImpl(AppCacheService* service)
   1295     : AppCacheStorage(service),
   1296       is_incognito_(false),
   1297       is_response_deletion_scheduled_(false),
   1298       did_start_deleting_responses_(false),
   1299       last_deletable_response_rowid_(0),
   1300       database_(NULL),
   1301       is_disabled_(false),
   1302       weak_factory_(this) {
   1303 }
   1304 
   1305 AppCacheStorageImpl::~AppCacheStorageImpl() {
   1306   std::for_each(pending_quota_queries_.begin(),
   1307                 pending_quota_queries_.end(),
   1308                 std::mem_fun(&DatabaseTask::CancelCompletion));
   1309   std::for_each(scheduled_database_tasks_.begin(),
   1310                 scheduled_database_tasks_.end(),
   1311                 std::mem_fun(&DatabaseTask::CancelCompletion));
   1312 
   1313   if (database_ &&
   1314       !db_thread_->PostTask(
   1315           FROM_HERE,
   1316           base::Bind(&ClearSessionOnlyOrigins, database_,
   1317                      make_scoped_refptr(service_->special_storage_policy()),
   1318                      service()->force_keep_session_state()))) {
   1319     delete database_;
   1320   }
   1321   database_ = NULL;  // So no further database tasks can be scheduled.
   1322 }
   1323 
   1324 void AppCacheStorageImpl::Initialize(const base::FilePath& cache_directory,
   1325                                      base::MessageLoopProxy* db_thread,
   1326                                      base::MessageLoopProxy* cache_thread) {
   1327   DCHECK(db_thread);
   1328 
   1329   cache_directory_ = cache_directory;
   1330   is_incognito_ = cache_directory_.empty();
   1331 
   1332   base::FilePath db_file_path;
   1333   if (!is_incognito_)
   1334     db_file_path = cache_directory_.Append(kAppCacheDatabaseName);
   1335   database_ = new AppCacheDatabase(db_file_path);
   1336 
   1337   db_thread_ = db_thread;
   1338   cache_thread_ = cache_thread;
   1339 
   1340   scoped_refptr<InitTask> task(new InitTask(this));
   1341   task->Schedule();
   1342 }
   1343 
   1344 void AppCacheStorageImpl::Disable() {
   1345   if (is_disabled_)
   1346     return;
   1347   VLOG(1) << "Disabling appcache storage.";
   1348   is_disabled_ = true;
   1349   ClearUsageMapAndNotify();
   1350   working_set()->Disable();
   1351   if (disk_cache_)
   1352     disk_cache_->Disable();
   1353   scoped_refptr<DisableDatabaseTask> task(new DisableDatabaseTask(this));
   1354   task->Schedule();
   1355 }
   1356 
   1357 void AppCacheStorageImpl::GetAllInfo(Delegate* delegate) {
   1358   DCHECK(delegate);
   1359   scoped_refptr<GetAllInfoTask> task(new GetAllInfoTask(this));
   1360   task->AddDelegate(GetOrCreateDelegateReference(delegate));
   1361   task->Schedule();
   1362 }
   1363 
   1364 void AppCacheStorageImpl::LoadCache(int64 id, Delegate* delegate) {
   1365   DCHECK(delegate);
   1366   if (is_disabled_) {
   1367     delegate->OnCacheLoaded(NULL, id);
   1368     return;
   1369   }
   1370 
   1371   AppCache* cache = working_set_.GetCache(id);
   1372   if (cache) {
   1373     delegate->OnCacheLoaded(cache, id);
   1374     if (cache->owning_group()) {
   1375       scoped_refptr<DatabaseTask> update_task(
   1376           new UpdateGroupLastAccessTimeTask(
   1377               this, cache->owning_group(), base::Time::Now()));
   1378       update_task->Schedule();
   1379     }
   1380     return;
   1381   }
   1382   scoped_refptr<CacheLoadTask> task(GetPendingCacheLoadTask(id));
   1383   if (task.get()) {
   1384     task->AddDelegate(GetOrCreateDelegateReference(delegate));
   1385     return;
   1386   }
   1387   task = new CacheLoadTask(id, this);
   1388   task->AddDelegate(GetOrCreateDelegateReference(delegate));
   1389   task->Schedule();
   1390   pending_cache_loads_[id] = task.get();
   1391 }
   1392 
   1393 void AppCacheStorageImpl::LoadOrCreateGroup(
   1394     const GURL& manifest_url, Delegate* delegate) {
   1395   DCHECK(delegate);
   1396   if (is_disabled_) {
   1397     delegate->OnGroupLoaded(NULL, manifest_url);
   1398     return;
   1399   }
   1400 
   1401   AppCacheGroup* group = working_set_.GetGroup(manifest_url);
   1402   if (group) {
   1403     delegate->OnGroupLoaded(group, manifest_url);
   1404     scoped_refptr<DatabaseTask> update_task(
   1405         new UpdateGroupLastAccessTimeTask(
   1406             this, group, base::Time::Now()));
   1407     update_task->Schedule();
   1408     return;
   1409   }
   1410 
   1411   scoped_refptr<GroupLoadTask> task(GetPendingGroupLoadTask(manifest_url));
   1412   if (task.get()) {
   1413     task->AddDelegate(GetOrCreateDelegateReference(delegate));
   1414     return;
   1415   }
   1416 
   1417   if (usage_map_.find(manifest_url.GetOrigin()) == usage_map_.end()) {
   1418     // No need to query the database, return a new group immediately.
   1419     scoped_refptr<AppCacheGroup> group(new AppCacheGroup(
   1420         this, manifest_url, NewGroupId()));
   1421     delegate->OnGroupLoaded(group.get(), manifest_url);
   1422     return;
   1423   }
   1424 
   1425   task = new GroupLoadTask(manifest_url, this);
   1426   task->AddDelegate(GetOrCreateDelegateReference(delegate));
   1427   task->Schedule();
   1428   pending_group_loads_[manifest_url] = task.get();
   1429 }
   1430 
   1431 void AppCacheStorageImpl::StoreGroupAndNewestCache(
   1432     AppCacheGroup* group, AppCache* newest_cache, Delegate* delegate) {
   1433   // TODO(michaeln): distinguish between a simple update of an existing
   1434   // cache that just adds new master entry(s), and the insertion of a
   1435   // whole new cache. The StoreGroupAndCacheTask as written will handle
   1436   // the simple update case in a very heavy weight way (delete all and
   1437   // the reinsert all over again).
   1438   DCHECK(group && delegate && newest_cache);
   1439   scoped_refptr<StoreGroupAndCacheTask> task(
   1440       new StoreGroupAndCacheTask(this, group, newest_cache));
   1441   task->AddDelegate(GetOrCreateDelegateReference(delegate));
   1442   task->GetQuotaThenSchedule();
   1443 
   1444   // TODO(michaeln): histogram is fishing for clues to crbug/95101
   1445   if (!newest_cache->GetEntry(group->manifest_url())) {
   1446     AppCacheHistograms::AddMissingManifestDetectedAtCallsite(
   1447         AppCacheHistograms::CALLSITE_3);
   1448   }
   1449 }
   1450 
   1451 void AppCacheStorageImpl::FindResponseForMainRequest(
   1452     const GURL& url, const GURL& preferred_manifest_url,
   1453     Delegate* delegate) {
   1454   DCHECK(delegate);
   1455 
   1456   const GURL* url_ptr = &url;
   1457   GURL url_no_ref;
   1458   if (url.has_ref()) {
   1459     GURL::Replacements replacements;
   1460     replacements.ClearRef();
   1461     url_no_ref = url.ReplaceComponents(replacements);
   1462     url_ptr = &url_no_ref;
   1463   }
   1464 
   1465   const GURL origin = url.GetOrigin();
   1466 
   1467   // First look in our working set for a direct hit without having to query
   1468   // the database.
   1469   const AppCacheWorkingSet::GroupMap* groups_in_use =
   1470       working_set()->GetGroupsInOrigin(origin);
   1471   if (groups_in_use) {
   1472     if (!preferred_manifest_url.is_empty()) {
   1473       AppCacheWorkingSet::GroupMap::const_iterator found =
   1474           groups_in_use->find(preferred_manifest_url);
   1475       if (found != groups_in_use->end() &&
   1476           FindResponseForMainRequestInGroup(
   1477               found->second, *url_ptr, delegate)) {
   1478           return;
   1479       }
   1480     } else {
   1481       for (AppCacheWorkingSet::GroupMap::const_iterator it =
   1482               groups_in_use->begin();
   1483            it != groups_in_use->end(); ++it) {
   1484         if (FindResponseForMainRequestInGroup(
   1485                 it->second, *url_ptr, delegate)) {
   1486           return;
   1487         }
   1488       }
   1489     }
   1490   }
   1491 
   1492   if (IsInitTaskComplete() &&  usage_map_.find(origin) == usage_map_.end()) {
   1493     // No need to query the database, return async'ly but without going thru
   1494     // the DB thread.
   1495     scoped_refptr<AppCacheGroup> no_group;
   1496     scoped_refptr<AppCache> no_cache;
   1497     ScheduleSimpleTask(
   1498         base::Bind(&AppCacheStorageImpl::DeliverShortCircuitedFindMainResponse,
   1499                    weak_factory_.GetWeakPtr(), url, AppCacheEntry(), no_group,
   1500                    no_cache,
   1501                    make_scoped_refptr(GetOrCreateDelegateReference(delegate))));
   1502     return;
   1503   }
   1504 
   1505   // We have to query the database, schedule a database task to do so.
   1506   scoped_refptr<FindMainResponseTask> task(
   1507       new FindMainResponseTask(this, *url_ptr, preferred_manifest_url,
   1508                                groups_in_use));
   1509   task->AddDelegate(GetOrCreateDelegateReference(delegate));
   1510   task->Schedule();
   1511 }
   1512 
   1513 bool AppCacheStorageImpl::FindResponseForMainRequestInGroup(
   1514     AppCacheGroup* group,  const GURL& url, Delegate* delegate) {
   1515   AppCache* cache = group->newest_complete_cache();
   1516   if (group->is_obsolete() || !cache)
   1517     return false;
   1518 
   1519   AppCacheEntry* entry = cache->GetEntry(url);
   1520   if (!entry || entry->IsForeign())
   1521     return false;
   1522 
   1523   ScheduleSimpleTask(
   1524       base::Bind(&AppCacheStorageImpl::DeliverShortCircuitedFindMainResponse,
   1525                  weak_factory_.GetWeakPtr(), url, *entry,
   1526                  make_scoped_refptr(group), make_scoped_refptr(cache),
   1527                  make_scoped_refptr(GetOrCreateDelegateReference(delegate))));
   1528   return true;
   1529 }
   1530 
   1531 void AppCacheStorageImpl::DeliverShortCircuitedFindMainResponse(
   1532     const GURL& url,
   1533     const AppCacheEntry& found_entry,
   1534     scoped_refptr<AppCacheGroup> group,
   1535     scoped_refptr<AppCache> cache,
   1536     scoped_refptr<DelegateReference> delegate_ref) {
   1537   if (delegate_ref->delegate) {
   1538     DelegateReferenceVector delegates(1, delegate_ref);
   1539     CallOnMainResponseFound(
   1540         &delegates, url, found_entry,
   1541         GURL(), AppCacheEntry(),
   1542         cache.get() ? cache->cache_id() : kNoCacheId,
   1543         group.get() ? group->group_id() : kNoCacheId,
   1544         group.get() ? group->manifest_url() : GURL());
   1545   }
   1546 }
   1547 
   1548 void AppCacheStorageImpl::CallOnMainResponseFound(
   1549     DelegateReferenceVector* delegates,
   1550     const GURL& url, const AppCacheEntry& entry,
   1551     const GURL& namespace_entry_url, const AppCacheEntry& fallback_entry,
   1552     int64 cache_id, int64 group_id, const GURL& manifest_url) {
   1553   FOR_EACH_DELEGATE(
   1554       (*delegates),
   1555       OnMainResponseFound(url, entry,
   1556                           namespace_entry_url, fallback_entry,
   1557                           cache_id, group_id, manifest_url));
   1558 }
   1559 
   1560 void AppCacheStorageImpl::FindResponseForSubRequest(
   1561     AppCache* cache, const GURL& url,
   1562     AppCacheEntry* found_entry, AppCacheEntry* found_fallback_entry,
   1563     bool* found_network_namespace) {
   1564   DCHECK(cache && cache->is_complete());
   1565 
   1566   // When a group is forcibly deleted, all subresource loads for pages
   1567   // using caches in the group will result in a synthesized network errors.
   1568   // Forcible deletion is not a function that is covered by the HTML5 spec.
   1569   if (cache->owning_group()->is_being_deleted()) {
   1570     *found_entry = AppCacheEntry();
   1571     *found_fallback_entry = AppCacheEntry();
   1572     *found_network_namespace = false;
   1573     return;
   1574   }
   1575 
   1576   GURL fallback_namespace_not_used;
   1577   GURL intercept_namespace_not_used;
   1578   cache->FindResponseForRequest(
   1579       url, found_entry, &intercept_namespace_not_used,
   1580       found_fallback_entry, &fallback_namespace_not_used,
   1581       found_network_namespace);
   1582 }
   1583 
   1584 void AppCacheStorageImpl::MarkEntryAsForeign(
   1585     const GURL& entry_url, int64 cache_id) {
   1586   AppCache* cache = working_set_.GetCache(cache_id);
   1587   if (cache) {
   1588     AppCacheEntry* entry = cache->GetEntry(entry_url);
   1589     DCHECK(entry);
   1590     if (entry)
   1591       entry->add_types(AppCacheEntry::FOREIGN);
   1592   }
   1593   scoped_refptr<MarkEntryAsForeignTask> task(
   1594       new MarkEntryAsForeignTask(this, entry_url, cache_id));
   1595   task->Schedule();
   1596   pending_foreign_markings_.push_back(std::make_pair(entry_url, cache_id));
   1597 }
   1598 
   1599 void AppCacheStorageImpl::MakeGroupObsolete(
   1600     AppCacheGroup* group, Delegate* delegate) {
   1601   DCHECK(group && delegate);
   1602   scoped_refptr<MakeGroupObsoleteTask> task(
   1603       new MakeGroupObsoleteTask(this, group));
   1604   task->AddDelegate(GetOrCreateDelegateReference(delegate));
   1605   task->Schedule();
   1606 }
   1607 
   1608 AppCacheResponseReader* AppCacheStorageImpl::CreateResponseReader(
   1609     const GURL& manifest_url, int64 group_id, int64 response_id) {
   1610   return new AppCacheResponseReader(response_id, group_id, disk_cache());
   1611 }
   1612 
   1613 AppCacheResponseWriter* AppCacheStorageImpl::CreateResponseWriter(
   1614     const GURL& manifest_url, int64 group_id) {
   1615   return new AppCacheResponseWriter(NewResponseId(), group_id, disk_cache());
   1616 }
   1617 
   1618 void AppCacheStorageImpl::DoomResponses(
   1619     const GURL& manifest_url, const std::vector<int64>& response_ids) {
   1620   if (response_ids.empty())
   1621     return;
   1622 
   1623   // Start deleting them from the disk cache lazily.
   1624   StartDeletingResponses(response_ids);
   1625 
   1626   // Also schedule a database task to record these ids in the
   1627   // deletable responses table.
   1628   // TODO(michaeln): There is a race here. If the browser crashes
   1629   // prior to committing these rows to the database and prior to us
   1630   // having deleted them from the disk cache, we'll never delete them.
   1631   scoped_refptr<InsertDeletableResponseIdsTask> task(
   1632       new InsertDeletableResponseIdsTask(this));
   1633   task->response_ids_ = response_ids;
   1634   task->Schedule();
   1635 }
   1636 
   1637 void AppCacheStorageImpl::DeleteResponses(
   1638     const GURL& manifest_url, const std::vector<int64>& response_ids) {
   1639   if (response_ids.empty())
   1640     return;
   1641   StartDeletingResponses(response_ids);
   1642 }
   1643 
   1644 void AppCacheStorageImpl::PurgeMemory() {
   1645   scoped_refptr<CloseConnectionTask> task(new CloseConnectionTask(this));
   1646   task->Schedule();
   1647 }
   1648 
   1649 void AppCacheStorageImpl::DelayedStartDeletingUnusedResponses() {
   1650   // Only if we haven't already begun.
   1651   if (!did_start_deleting_responses_) {
   1652     scoped_refptr<GetDeletableResponseIdsTask> task(
   1653         new GetDeletableResponseIdsTask(this, last_deletable_response_rowid_));
   1654     task->Schedule();
   1655   }
   1656 }
   1657 
   1658 void AppCacheStorageImpl::StartDeletingResponses(
   1659     const std::vector<int64>& response_ids) {
   1660   DCHECK(!response_ids.empty());
   1661   did_start_deleting_responses_ = true;
   1662   deletable_response_ids_.insert(
   1663       deletable_response_ids_.end(),
   1664       response_ids.begin(), response_ids.end());
   1665   if (!is_response_deletion_scheduled_)
   1666     ScheduleDeleteOneResponse();
   1667 }
   1668 
   1669 void AppCacheStorageImpl::ScheduleDeleteOneResponse() {
   1670   DCHECK(!is_response_deletion_scheduled_);
   1671   const base::TimeDelta kDelay = base::TimeDelta::FromMilliseconds(10);
   1672   base::MessageLoop::current()->PostDelayedTask(
   1673       FROM_HERE,
   1674       base::Bind(&AppCacheStorageImpl::DeleteOneResponse,
   1675                  weak_factory_.GetWeakPtr()),
   1676       kDelay);
   1677   is_response_deletion_scheduled_ = true;
   1678 }
   1679 
   1680 void AppCacheStorageImpl::DeleteOneResponse() {
   1681   DCHECK(is_response_deletion_scheduled_);
   1682   DCHECK(!deletable_response_ids_.empty());
   1683 
   1684   if (!disk_cache()) {
   1685     DCHECK(is_disabled_);
   1686     deletable_response_ids_.clear();
   1687     deleted_response_ids_.clear();
   1688     is_response_deletion_scheduled_ = false;
   1689     return;
   1690   }
   1691 
   1692   // TODO(michaeln): add group_id to DoomEntry args
   1693   int64 id = deletable_response_ids_.front();
   1694   int rv = disk_cache_->DoomEntry(
   1695       id, base::Bind(&AppCacheStorageImpl::OnDeletedOneResponse,
   1696                      base::Unretained(this)));
   1697   if (rv != net::ERR_IO_PENDING)
   1698     OnDeletedOneResponse(rv);
   1699 }
   1700 
   1701 void AppCacheStorageImpl::OnDeletedOneResponse(int rv) {
   1702   is_response_deletion_scheduled_ = false;
   1703   if (is_disabled_)
   1704     return;
   1705 
   1706   int64 id = deletable_response_ids_.front();
   1707   deletable_response_ids_.pop_front();
   1708   if (rv != net::ERR_ABORTED)
   1709     deleted_response_ids_.push_back(id);
   1710 
   1711   const size_t kBatchSize = 50U;
   1712   if (deleted_response_ids_.size() >= kBatchSize ||
   1713       deletable_response_ids_.empty()) {
   1714     scoped_refptr<DeleteDeletableResponseIdsTask> task(
   1715         new DeleteDeletableResponseIdsTask(this));
   1716     task->response_ids_.swap(deleted_response_ids_);
   1717     task->Schedule();
   1718   }
   1719 
   1720   if (deletable_response_ids_.empty()) {
   1721     scoped_refptr<GetDeletableResponseIdsTask> task(
   1722         new GetDeletableResponseIdsTask(this, last_deletable_response_rowid_));
   1723     task->Schedule();
   1724     return;
   1725   }
   1726 
   1727   ScheduleDeleteOneResponse();
   1728 }
   1729 
   1730 AppCacheStorageImpl::CacheLoadTask*
   1731 AppCacheStorageImpl::GetPendingCacheLoadTask(int64 cache_id) {
   1732   PendingCacheLoads::iterator found = pending_cache_loads_.find(cache_id);
   1733   if (found != pending_cache_loads_.end())
   1734     return found->second;
   1735   return NULL;
   1736 }
   1737 
   1738 AppCacheStorageImpl::GroupLoadTask*
   1739 AppCacheStorageImpl::GetPendingGroupLoadTask(const GURL& manifest_url) {
   1740   PendingGroupLoads::iterator found = pending_group_loads_.find(manifest_url);
   1741   if (found != pending_group_loads_.end())
   1742     return found->second;
   1743   return NULL;
   1744 }
   1745 
   1746 void AppCacheStorageImpl::GetPendingForeignMarkingsForCache(
   1747     int64 cache_id, std::vector<GURL>* urls) {
   1748   PendingForeignMarkings::iterator iter = pending_foreign_markings_.begin();
   1749   while (iter != pending_foreign_markings_.end()) {
   1750     if (iter->second == cache_id)
   1751       urls->push_back(iter->first);
   1752     ++iter;
   1753   }
   1754 }
   1755 
   1756 void AppCacheStorageImpl::ScheduleSimpleTask(const base::Closure& task) {
   1757   pending_simple_tasks_.push_back(task);
   1758   base::MessageLoop::current()->PostTask(
   1759       FROM_HERE,
   1760       base::Bind(&AppCacheStorageImpl::RunOnePendingSimpleTask,
   1761                  weak_factory_.GetWeakPtr()));
   1762 }
   1763 
   1764 void AppCacheStorageImpl::RunOnePendingSimpleTask() {
   1765   DCHECK(!pending_simple_tasks_.empty());
   1766   base::Closure task = pending_simple_tasks_.front();
   1767   pending_simple_tasks_.pop_front();
   1768   task.Run();
   1769 }
   1770 
   1771 AppCacheDiskCache* AppCacheStorageImpl::disk_cache() {
   1772   DCHECK(IsInitTaskComplete());
   1773 
   1774   if (is_disabled_)
   1775     return NULL;
   1776 
   1777   if (!disk_cache_) {
   1778     int rv = net::OK;
   1779     disk_cache_.reset(new AppCacheDiskCache);
   1780     if (is_incognito_) {
   1781       rv = disk_cache_->InitWithMemBackend(
   1782           kMaxMemDiskCacheSize,
   1783           base::Bind(&AppCacheStorageImpl::OnDiskCacheInitialized,
   1784                      base::Unretained(this)));
   1785     } else {
   1786       rv = disk_cache_->InitWithDiskBackend(
   1787           cache_directory_.Append(kDiskCacheDirectoryName),
   1788           kMaxDiskCacheSize,
   1789           false,
   1790           cache_thread_.get(),
   1791           base::Bind(&AppCacheStorageImpl::OnDiskCacheInitialized,
   1792                      base::Unretained(this)));
   1793     }
   1794 
   1795     // We should not keep this reference around.
   1796     cache_thread_ = NULL;
   1797 
   1798     if (rv != net::ERR_IO_PENDING)
   1799       OnDiskCacheInitialized(rv);
   1800   }
   1801   return disk_cache_.get();
   1802 }
   1803 
   1804 void AppCacheStorageImpl::OnDiskCacheInitialized(int rv) {
   1805   if (rv != net::OK) {
   1806     LOG(ERROR) << "Failed to open the appcache diskcache.";
   1807     AppCacheHistograms::CountInitResult(AppCacheHistograms::DISK_CACHE_ERROR);
   1808 
   1809     // We're unable to open the disk cache, this is a fatal error that we can't
   1810     // really recover from. We handle it by temporarily disabling the appcache
   1811     // deleting the directory on disk and reinitializing the appcache system.
   1812     Disable();
   1813     if (!is_incognito_ && rv != net::ERR_ABORTED) {
   1814       VLOG(1) << "Deleting existing appcache data and starting over.";
   1815       db_thread_->PostTaskAndReply(
   1816           FROM_HERE,
   1817           base::Bind(base::IgnoreResult(&base::DeleteFile),
   1818                      cache_directory_, true),
   1819           base::Bind(&AppCacheStorageImpl::CallReinitialize,
   1820                      weak_factory_.GetWeakPtr()));
   1821     }
   1822   }
   1823 }
   1824 
   1825 void AppCacheStorageImpl::CallReinitialize() {
   1826   service_->Reinitialize();
   1827   // note: 'this' may be deleted during reinit.
   1828 }
   1829 
   1830 }  // namespace appcache
   1831