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