| 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 |