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