Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(172)

Side by Side Diff: webkit/browser/appcache/appcache_storage_impl.cc

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

Powered by Google App Engine
This is Rietveld 408576698