OLD | NEW |
| (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 | |
OLD | NEW |