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.
DeleteGroupAndRelatedRecords(AppCacheDatabase * database,int64 group_id,std::vector<int64> * deletable_response_ids)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.
ClearSessionOnlyOrigins(AppCacheDatabase * database,scoped_refptr<quota::SpecialStoragePolicy> special_storage_policy,bool force_keep_session_state)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:
DatabaseTask(AppCacheStorageImpl * storage)135 explicit DatabaseTask(AppCacheStorageImpl* storage)
136 : storage_(storage), database_(storage->database_),
137 io_thread_(base::MessageLoopProxy::current()) {
138 DCHECK(io_thread_.get());
139 }
140
AddDelegate(DelegateReference * delegate_reference)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.
RunCompleted()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>;
~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
Schedule()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
CancelCompletion()196 void AppCacheStorageImpl::DatabaseTask::CancelCompletion() {
197 DCHECK(io_thread_->BelongsToCurrentThread());
198 delegates_.clear();
199 storage_ = NULL;
200 }
201
CallRun(base::TimeTicks schedule_time)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
CallRunCompleted(base::TimeTicks schedule_time)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
CallDisableStorage()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:
InitTask(AppCacheStorageImpl * storage)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:
~InitTask()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
Run()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
RunCompleted()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:
CloseConnectionTask(AppCacheStorageImpl * storage)301 explicit CloseConnectionTask(AppCacheStorageImpl* storage)
302 : DatabaseTask(storage) {}
303
304 // DatabaseTask:
Run()305 virtual void Run() OVERRIDE { database_->CloseConnection(); }
306
307 protected:
~CloseConnectionTask()308 virtual ~CloseConnectionTask() {}
309 };
310
311 // DisableDatabaseTask -------
312
313 class AppCacheStorageImpl::DisableDatabaseTask : public DatabaseTask {
314 public:
DisableDatabaseTask(AppCacheStorageImpl * storage)315 explicit DisableDatabaseTask(AppCacheStorageImpl* storage)
316 : DatabaseTask(storage) {}
317
318 // DatabaseTask:
Run()319 virtual void Run() OVERRIDE { database_->Disable(); }
320
321 protected:
~DisableDatabaseTask()322 virtual ~DisableDatabaseTask() {}
323 };
324
325 // GetAllInfoTask -------
326
327 class AppCacheStorageImpl::GetAllInfoTask : public DatabaseTask {
328 public:
GetAllInfoTask(AppCacheStorageImpl * storage)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:
~GetAllInfoTask()339 virtual ~GetAllInfoTask() {}
340
341 private:
342 scoped_refptr<AppCacheInfoCollection> info_collection_;
343 };
344
Run()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
RunCompleted()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:
StoreOrLoadTask(AppCacheStorageImpl * storage)382 explicit StoreOrLoadTask(AppCacheStorageImpl* storage)
383 : DatabaseTask(storage) {}
~StoreOrLoadTask()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
FindRelatedCacheRecords(int64 cache_id)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
CreateCacheAndGroupFromRecords(scoped_refptr<AppCache> * cache,scoped_refptr<AppCacheGroup> * group)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:
CacheLoadTask(int64 cache_id,AppCacheStorageImpl * storage)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:
~CacheLoadTask()493 virtual ~CacheLoadTask() {}
494
495 private:
496 int64 cache_id_;
497 bool success_;
498 };
499
Run()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
RunCompleted()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:
GroupLoadTask(GURL manifest_url,AppCacheStorageImpl * storage)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:
~GroupLoadTask()535 virtual ~GroupLoadTask() {}
536
537 private:
538 GURL manifest_url_;
539 bool success_;
540 };
541
Run()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
RunCompleted()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:
~StoreGroupAndCacheTask()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
StoreGroupAndCacheTask(AppCacheStorageImpl * storage,AppCacheGroup * group,AppCache * newest_cache)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
GetQuotaThenSchedule()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
OnQuotaCallback(quota::QuotaStatusCode status,int64 usage,int64 quota)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
Run()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
RunCompleted()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
CancelCompletion()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:
SortByCachePreference(int64 preferred_id,const std::set<int64> & in_use_ids)795 SortByCachePreference(int64 preferred_id, const std::set<int64>& in_use_ids)
796 : preferred_id_(preferred_id), in_use_ids_(in_use_ids) {
797 }
operator ()(const AppCacheDatabase::EntryRecord & lhs,const AppCacheDatabase::EntryRecord & rhs)798 bool operator()(
799 const AppCacheDatabase::EntryRecord& lhs,
800 const AppCacheDatabase::EntryRecord& rhs) {
801 return compute_value(lhs) > compute_value(rhs);
802 }
803 private:
compute_value(const AppCacheDatabase::EntryRecord & entry)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
SortByLength(const AppCacheDatabase::NamespaceRecord & lhs,const AppCacheDatabase::NamespaceRecord & rhs)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:
NetworkNamespaceHelper(AppCacheDatabase * database)824 explicit NetworkNamespaceHelper(AppCacheDatabase* database)
825 : database_(database) {
826 }
827
IsInNetworkNamespace(const GURL & url,int64 cache_id)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:
GetOnlineWhiteListForCache(int64 cache_id,NamespaceVector * namespaces)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:
FindMainResponseTask(AppCacheStorageImpl * storage,const GURL & url,const GURL & preferred_manifest_url,const AppCacheWorkingSet::GroupMap * groups_in_use)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:
~FindMainResponseTask()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
Run()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::
FindExactMatch(int64 preferred_cache_id)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::
FindNamespaceMatch(int64 preferred_cache_id)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::
FindNamespaceHelper(int64 preferred_cache_id,AppCacheDatabase::NamespaceRecordVector * namespaces,NetworkNamespaceHelper * network_namespace_helper)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::
FindFirstValidNamespace(const NamespaceRecordPtrVector & namespaces)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
RunCompleted()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:
MarkEntryAsForeignTask(AppCacheStorageImpl * storage,const GURL & url,int64 cache_id)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:
~MarkEntryAsForeignTask()1093 virtual ~MarkEntryAsForeignTask() {}
1094
1095 private:
1096 int64 cache_id_;
1097 GURL entry_url_;
1098 };
1099
Run()1100 void AppCacheStorageImpl::MarkEntryAsForeignTask::Run() {
1101 database_->AddEntryFlags(entry_url_, cache_id_, AppCacheEntry::FOREIGN);
1102 }
1103
RunCompleted()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:
~MakeGroupObsoleteTask()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
MakeGroupObsoleteTask(AppCacheStorageImpl * storage,AppCacheGroup * group)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
Run()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
RunCompleted()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
CancelCompletion()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:
GetDeletableResponseIdsTask(AppCacheStorageImpl * storage,int64 max_rowid)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:
~GetDeletableResponseIdsTask()1203 virtual ~GetDeletableResponseIdsTask() {}
1204
1205 private:
1206 int64 max_rowid_;
1207 std::vector<int64> response_ids_;
1208 };
1209
Run()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
RunCompleted()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:
InsertDeletableResponseIdsTask(AppCacheStorageImpl * storage)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:
~InsertDeletableResponseIdsTask()1235 virtual ~InsertDeletableResponseIdsTask() {}
1236 };
1237
Run()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:
DeleteDeletableResponseIdsTask(AppCacheStorageImpl * storage)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:
~DeleteDeletableResponseIdsTask()1257 virtual ~DeleteDeletableResponseIdsTask() {}
1258 };
1259
Run()1260 void AppCacheStorageImpl::DeleteDeletableResponseIdsTask::Run() {
1261 database_->DeleteDeletableResponseIds(response_ids_);
1262 }
1263
1264 // UpdateGroupLastAccessTimeTask -------
1265
1266 class AppCacheStorageImpl::UpdateGroupLastAccessTimeTask
1267 : public DatabaseTask {
1268 public:
UpdateGroupLastAccessTimeTask(AppCacheStorageImpl * storage,AppCacheGroup * group,base::Time time)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:
~UpdateGroupLastAccessTimeTask()1280 virtual ~UpdateGroupLastAccessTimeTask() {}
1281
1282 private:
1283 int64 group_id_;
1284 base::Time last_access_time_;
1285 };
1286
Run()1287 void AppCacheStorageImpl::UpdateGroupLastAccessTimeTask::Run() {
1288 database_->UpdateGroupLastAccessTime(group_id_, last_access_time_);
1289 }
1290
1291
1292 // AppCacheStorageImpl ---------------------------------------------------
1293
AppCacheStorageImpl(AppCacheService * service)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
~AppCacheStorageImpl()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
Initialize(const base::FilePath & cache_directory,base::MessageLoopProxy * db_thread,base::MessageLoopProxy * cache_thread)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
Disable()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
GetAllInfo(Delegate * delegate)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
LoadCache(int64 id,Delegate * delegate)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
LoadOrCreateGroup(const GURL & manifest_url,Delegate * delegate)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
StoreGroupAndNewestCache(AppCacheGroup * group,AppCache * newest_cache,Delegate * delegate)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
FindResponseForMainRequest(const GURL & url,const GURL & preferred_manifest_url,Delegate * delegate)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
FindResponseForMainRequestInGroup(AppCacheGroup * group,const GURL & url,Delegate * delegate)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
DeliverShortCircuitedFindMainResponse(const GURL & url,const AppCacheEntry & found_entry,scoped_refptr<AppCacheGroup> group,scoped_refptr<AppCache> cache,scoped_refptr<DelegateReference> delegate_ref)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
CallOnMainResponseFound(DelegateReferenceVector * delegates,const GURL & url,const AppCacheEntry & entry,const GURL & namespace_entry_url,const AppCacheEntry & fallback_entry,int64 cache_id,int64 group_id,const GURL & manifest_url)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
FindResponseForSubRequest(AppCache * cache,const GURL & url,AppCacheEntry * found_entry,AppCacheEntry * found_fallback_entry,bool * found_network_namespace)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
MarkEntryAsForeign(const GURL & entry_url,int64 cache_id)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
MakeGroupObsolete(AppCacheGroup * group,Delegate * delegate)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
CreateResponseReader(const GURL & manifest_url,int64 group_id,int64 response_id)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
CreateResponseWriter(const GURL & manifest_url,int64 group_id)1613 AppCacheResponseWriter* AppCacheStorageImpl::CreateResponseWriter(
1614 const GURL& manifest_url, int64 group_id) {
1615 return new AppCacheResponseWriter(NewResponseId(), group_id, disk_cache());
1616 }
1617
DoomResponses(const GURL & manifest_url,const std::vector<int64> & response_ids)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
DeleteResponses(const GURL & manifest_url,const std::vector<int64> & response_ids)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
PurgeMemory()1644 void AppCacheStorageImpl::PurgeMemory() {
1645 scoped_refptr<CloseConnectionTask> task(new CloseConnectionTask(this));
1646 task->Schedule();
1647 }
1648
DelayedStartDeletingUnusedResponses()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
StartDeletingResponses(const std::vector<int64> & response_ids)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
ScheduleDeleteOneResponse()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
DeleteOneResponse()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
OnDeletedOneResponse(int rv)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*
GetPendingCacheLoadTask(int64 cache_id)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*
GetPendingGroupLoadTask(const GURL & manifest_url)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
GetPendingForeignMarkingsForCache(int64 cache_id,std::vector<GURL> * urls)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
ScheduleSimpleTask(const base::Closure & task)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
RunOnePendingSimpleTask()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
disk_cache()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
OnDiskCacheInitialized(int rv)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
CallReinitialize()1825 void AppCacheStorageImpl::CallReinitialize() {
1826 service_->Reinitialize();
1827 // note: 'this' may be deleted during reinit.
1828 }
1829
1830 } // namespace appcache
1831