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