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