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