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