Index: webkit/browser/appcache/appcache_storage_impl.cc |
diff --git a/webkit/browser/appcache/appcache_storage_impl.cc b/webkit/browser/appcache/appcache_storage_impl.cc |
deleted file mode 100644 |
index 62563d7ff1b5f2bb637f2f3fc294f6c6922d6a9a..0000000000000000000000000000000000000000 |
--- a/webkit/browser/appcache/appcache_storage_impl.cc |
+++ /dev/null |
@@ -1,1860 +0,0 @@ |
-// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
-// Use of this source code is governed by a BSD-style license that can be |
-// found in the LICENSE file. |
- |
-#include "webkit/browser/appcache/appcache_storage_impl.h" |
- |
-#include <algorithm> |
-#include <functional> |
-#include <set> |
-#include <vector> |
- |
-#include "base/bind.h" |
-#include "base/bind_helpers.h" |
-#include "base/file_util.h" |
-#include "base/logging.h" |
-#include "base/message_loop/message_loop.h" |
-#include "base/stl_util.h" |
-#include "base/strings/string_util.h" |
-#include "net/base/cache_type.h" |
-#include "net/base/net_errors.h" |
-#include "sql/connection.h" |
-#include "sql/transaction.h" |
-#include "webkit/browser/appcache/appcache.h" |
-#include "webkit/browser/appcache/appcache_database.h" |
-#include "webkit/browser/appcache/appcache_entry.h" |
-#include "webkit/browser/appcache/appcache_group.h" |
-#include "webkit/browser/appcache/appcache_histograms.h" |
-#include "webkit/browser/appcache/appcache_quota_client.h" |
-#include "webkit/browser/appcache/appcache_response.h" |
-#include "webkit/browser/appcache/appcache_service_impl.h" |
-#include "webkit/browser/quota/quota_client.h" |
-#include "webkit/browser/quota/quota_manager.h" |
-#include "webkit/browser/quota/quota_manager_proxy.h" |
-#include "webkit/browser/quota/special_storage_policy.h" |
- |
-namespace appcache { |
- |
-// Hard coded default when not using quota management. |
-static const int kDefaultQuota = 5 * 1024 * 1024; |
- |
-static const int kMaxDiskCacheSize = 250 * 1024 * 1024; |
-static const int kMaxMemDiskCacheSize = 10 * 1024 * 1024; |
-static const base::FilePath::CharType kDiskCacheDirectoryName[] = |
- FILE_PATH_LITERAL("Cache"); |
- |
-namespace { |
- |
-// Helpers for clearing data from the AppCacheDatabase. |
-bool DeleteGroupAndRelatedRecords(AppCacheDatabase* database, |
- int64 group_id, |
- std::vector<int64>* deletable_response_ids) { |
- AppCacheDatabase::CacheRecord cache_record; |
- bool success = false; |
- if (database->FindCacheForGroup(group_id, &cache_record)) { |
- database->FindResponseIdsForCacheAsVector(cache_record.cache_id, |
- deletable_response_ids); |
- success = |
- database->DeleteGroup(group_id) && |
- database->DeleteCache(cache_record.cache_id) && |
- database->DeleteEntriesForCache(cache_record.cache_id) && |
- database->DeleteNamespacesForCache(cache_record.cache_id) && |
- database->DeleteOnlineWhiteListForCache(cache_record.cache_id) && |
- database->InsertDeletableResponseIds(*deletable_response_ids); |
- } else { |
- NOTREACHED() << "A existing group without a cache is unexpected"; |
- success = database->DeleteGroup(group_id); |
- } |
- return success; |
-} |
- |
-// Destroys |database|. If there is appcache data to be deleted |
-// (|force_keep_session_state| is false), deletes session-only appcache data. |
-void ClearSessionOnlyOrigins( |
- AppCacheDatabase* database, |
- scoped_refptr<quota::SpecialStoragePolicy> special_storage_policy, |
- bool force_keep_session_state) { |
- scoped_ptr<AppCacheDatabase> database_to_delete(database); |
- |
- // If saving session state, only delete the database. |
- if (force_keep_session_state) |
- return; |
- |
- bool has_session_only_appcaches = |
- special_storage_policy.get() && |
- special_storage_policy->HasSessionOnlyOrigins(); |
- |
- // Clearning only session-only databases, and there are none. |
- if (!has_session_only_appcaches) |
- return; |
- |
- std::set<GURL> origins; |
- database->FindOriginsWithGroups(&origins); |
- if (origins.empty()) |
- return; // nothing to delete |
- |
- sql::Connection* connection = database->db_connection(); |
- if (!connection) { |
- NOTREACHED() << "Missing database connection."; |
- return; |
- } |
- |
- std::set<GURL>::const_iterator origin; |
- for (origin = origins.begin(); origin != origins.end(); ++origin) { |
- if (!special_storage_policy->IsStorageSessionOnly(*origin)) |
- continue; |
- if (special_storage_policy.get() && |
- special_storage_policy->IsStorageProtected(*origin)) |
- continue; |
- |
- std::vector<AppCacheDatabase::GroupRecord> groups; |
- database->FindGroupsForOrigin(*origin, &groups); |
- std::vector<AppCacheDatabase::GroupRecord>::const_iterator group; |
- for (group = groups.begin(); group != groups.end(); ++group) { |
- sql::Transaction transaction(connection); |
- if (!transaction.Begin()) { |
- NOTREACHED() << "Failed to start transaction"; |
- return; |
- } |
- std::vector<int64> deletable_response_ids; |
- bool success = DeleteGroupAndRelatedRecords(database, |
- group->group_id, |
- &deletable_response_ids); |
- success = success && transaction.Commit(); |
- DCHECK(success); |
- } // for each group |
- } // for each origin |
-} |
- |
-} // namespace |
- |
-// DatabaseTask ----------------------------------------- |
- |
-class AppCacheStorageImpl::DatabaseTask |
- : public base::RefCountedThreadSafe<DatabaseTask> { |
- public: |
- explicit DatabaseTask(AppCacheStorageImpl* storage) |
- : storage_(storage), database_(storage->database_), |
- io_thread_(base::MessageLoopProxy::current()) { |
- DCHECK(io_thread_.get()); |
- } |
- |
- void AddDelegate(DelegateReference* delegate_reference) { |
- delegates_.push_back(make_scoped_refptr(delegate_reference)); |
- } |
- |
- // Schedules a task to be Run() on the DB thread. Tasks |
- // are run in the order in which they are scheduled. |
- void Schedule(); |
- |
- // Called on the DB thread. |
- virtual void Run() = 0; |
- |
- // Called on the IO thread after Run() has completed. |
- virtual void RunCompleted() {} |
- |
- // Once scheduled a task cannot be cancelled, but the |
- // call to RunCompleted may be. This method should only be |
- // called on the IO thread. This is used by AppCacheStorageImpl |
- // to cancel the completion calls when AppCacheStorageImpl is |
- // destructed. This method may be overriden to release or delete |
- // additional data associated with the task that is not DB thread |
- // safe. If overriden, this base class method must be called from |
- // within the override. |
- virtual void CancelCompletion(); |
- |
- protected: |
- friend class base::RefCountedThreadSafe<DatabaseTask>; |
- virtual ~DatabaseTask() {} |
- |
- AppCacheStorageImpl* storage_; |
- AppCacheDatabase* database_; |
- DelegateReferenceVector delegates_; |
- |
- private: |
- void CallRun(base::TimeTicks schedule_time); |
- void CallRunCompleted(base::TimeTicks schedule_time); |
- void OnFatalError(); |
- |
- scoped_refptr<base::MessageLoopProxy> io_thread_; |
-}; |
- |
-void AppCacheStorageImpl::DatabaseTask::Schedule() { |
- DCHECK(storage_); |
- DCHECK(io_thread_->BelongsToCurrentThread()); |
- if (!storage_->database_) |
- return; |
- |
- if (storage_->db_thread_->PostTask( |
- FROM_HERE, |
- base::Bind(&DatabaseTask::CallRun, this, base::TimeTicks::Now()))) { |
- storage_->scheduled_database_tasks_.push_back(this); |
- } else { |
- NOTREACHED() << "Thread for database tasks is not running."; |
- } |
-} |
- |
-void AppCacheStorageImpl::DatabaseTask::CancelCompletion() { |
- DCHECK(io_thread_->BelongsToCurrentThread()); |
- delegates_.clear(); |
- storage_ = NULL; |
-} |
- |
-void AppCacheStorageImpl::DatabaseTask::CallRun( |
- base::TimeTicks schedule_time) { |
- AppCacheHistograms::AddTaskQueueTimeSample( |
- base::TimeTicks::Now() - schedule_time); |
- if (!database_->is_disabled()) { |
- base::TimeTicks run_time = base::TimeTicks::Now(); |
- Run(); |
- AppCacheHistograms::AddTaskRunTimeSample( |
- base::TimeTicks::Now() - run_time); |
- |
- if (database_->was_corruption_detected()) { |
- AppCacheHistograms::CountCorruptionDetected(); |
- database_->Disable(); |
- } |
- if (database_->is_disabled()) { |
- io_thread_->PostTask( |
- FROM_HERE, |
- base::Bind(&DatabaseTask::OnFatalError, this)); |
- } |
- } |
- io_thread_->PostTask( |
- FROM_HERE, |
- base::Bind(&DatabaseTask::CallRunCompleted, this, |
- base::TimeTicks::Now())); |
-} |
- |
-void AppCacheStorageImpl::DatabaseTask::CallRunCompleted( |
- base::TimeTicks schedule_time) { |
- AppCacheHistograms::AddCompletionQueueTimeSample( |
- base::TimeTicks::Now() - schedule_time); |
- if (storage_) { |
- DCHECK(io_thread_->BelongsToCurrentThread()); |
- DCHECK(storage_->scheduled_database_tasks_.front() == this); |
- storage_->scheduled_database_tasks_.pop_front(); |
- base::TimeTicks run_time = base::TimeTicks::Now(); |
- RunCompleted(); |
- AppCacheHistograms::AddCompletionRunTimeSample( |
- base::TimeTicks::Now() - run_time); |
- delegates_.clear(); |
- } |
-} |
- |
-void AppCacheStorageImpl::DatabaseTask::OnFatalError() { |
- if (storage_) { |
- DCHECK(io_thread_->BelongsToCurrentThread()); |
- storage_->Disable(); |
- storage_->DeleteAndStartOver(); |
- } |
-} |
- |
-// InitTask ------- |
- |
-class AppCacheStorageImpl::InitTask : public DatabaseTask { |
- public: |
- explicit InitTask(AppCacheStorageImpl* storage) |
- : DatabaseTask(storage), last_group_id_(0), |
- last_cache_id_(0), last_response_id_(0), |
- last_deletable_response_rowid_(0) { |
- if (!storage->is_incognito_) { |
- db_file_path_ = |
- storage->cache_directory_.Append(kAppCacheDatabaseName); |
- disk_cache_directory_ = |
- storage->cache_directory_.Append(kDiskCacheDirectoryName); |
- } |
- } |
- |
- // DatabaseTask: |
- virtual void Run() OVERRIDE; |
- virtual void RunCompleted() OVERRIDE; |
- |
- protected: |
- virtual ~InitTask() {} |
- |
- private: |
- base::FilePath db_file_path_; |
- base::FilePath disk_cache_directory_; |
- int64 last_group_id_; |
- int64 last_cache_id_; |
- int64 last_response_id_; |
- int64 last_deletable_response_rowid_; |
- std::map<GURL, int64> usage_map_; |
-}; |
- |
-void AppCacheStorageImpl::InitTask::Run() { |
- // If there is no sql database, ensure there is no disk cache either. |
- if (!db_file_path_.empty() && |
- !base::PathExists(db_file_path_) && |
- base::DirectoryExists(disk_cache_directory_)) { |
- base::DeleteFile(disk_cache_directory_, true); |
- if (base::DirectoryExists(disk_cache_directory_)) { |
- database_->Disable(); // This triggers OnFatalError handling. |
- return; |
- } |
- } |
- |
- database_->FindLastStorageIds( |
- &last_group_id_, &last_cache_id_, &last_response_id_, |
- &last_deletable_response_rowid_); |
- database_->GetAllOriginUsage(&usage_map_); |
-} |
- |
-void AppCacheStorageImpl::InitTask::RunCompleted() { |
- storage_->last_group_id_ = last_group_id_; |
- storage_->last_cache_id_ = last_cache_id_; |
- storage_->last_response_id_ = last_response_id_; |
- storage_->last_deletable_response_rowid_ = last_deletable_response_rowid_; |
- |
- if (!storage_->is_disabled()) { |
- storage_->usage_map_.swap(usage_map_); |
- const base::TimeDelta kDelay = base::TimeDelta::FromMinutes(5); |
- base::MessageLoop::current()->PostDelayedTask( |
- FROM_HERE, |
- base::Bind(&AppCacheStorageImpl::DelayedStartDeletingUnusedResponses, |
- storage_->weak_factory_.GetWeakPtr()), |
- kDelay); |
- } |
- |
- if (storage_->service()->quota_client()) |
- storage_->service()->quota_client()->NotifyAppCacheReady(); |
-} |
- |
-// DisableDatabaseTask ------- |
- |
-class AppCacheStorageImpl::DisableDatabaseTask : public DatabaseTask { |
- public: |
- explicit DisableDatabaseTask(AppCacheStorageImpl* storage) |
- : DatabaseTask(storage) {} |
- |
- // DatabaseTask: |
- virtual void Run() OVERRIDE { database_->Disable(); } |
- |
- protected: |
- virtual ~DisableDatabaseTask() {} |
-}; |
- |
-// GetAllInfoTask ------- |
- |
-class AppCacheStorageImpl::GetAllInfoTask : public DatabaseTask { |
- public: |
- explicit GetAllInfoTask(AppCacheStorageImpl* storage) |
- : DatabaseTask(storage), |
- info_collection_(new AppCacheInfoCollection()) { |
- } |
- |
- // DatabaseTask: |
- virtual void Run() OVERRIDE; |
- virtual void RunCompleted() OVERRIDE; |
- |
- protected: |
- virtual ~GetAllInfoTask() {} |
- |
- private: |
- scoped_refptr<AppCacheInfoCollection> info_collection_; |
-}; |
- |
-void AppCacheStorageImpl::GetAllInfoTask::Run() { |
- std::set<GURL> origins; |
- database_->FindOriginsWithGroups(&origins); |
- for (std::set<GURL>::const_iterator origin = origins.begin(); |
- origin != origins.end(); ++origin) { |
- AppCacheInfoVector& infos = |
- info_collection_->infos_by_origin[*origin]; |
- std::vector<AppCacheDatabase::GroupRecord> groups; |
- database_->FindGroupsForOrigin(*origin, &groups); |
- for (std::vector<AppCacheDatabase::GroupRecord>::const_iterator |
- group = groups.begin(); |
- group != groups.end(); ++group) { |
- AppCacheDatabase::CacheRecord cache_record; |
- database_->FindCacheForGroup(group->group_id, &cache_record); |
- AppCacheInfo info; |
- info.manifest_url = group->manifest_url; |
- info.creation_time = group->creation_time; |
- info.size = cache_record.cache_size; |
- info.last_access_time = group->last_access_time; |
- info.last_update_time = cache_record.update_time; |
- info.cache_id = cache_record.cache_id; |
- info.group_id = group->group_id; |
- info.is_complete = true; |
- infos.push_back(info); |
- } |
- } |
-} |
- |
-void AppCacheStorageImpl::GetAllInfoTask::RunCompleted() { |
- DCHECK(delegates_.size() == 1); |
- FOR_EACH_DELEGATE(delegates_, OnAllInfo(info_collection_.get())); |
-} |
- |
-// StoreOrLoadTask ------- |
- |
-class AppCacheStorageImpl::StoreOrLoadTask : public DatabaseTask { |
- protected: |
- explicit StoreOrLoadTask(AppCacheStorageImpl* storage) |
- : DatabaseTask(storage) {} |
- virtual ~StoreOrLoadTask() {} |
- |
- bool FindRelatedCacheRecords(int64 cache_id); |
- void CreateCacheAndGroupFromRecords( |
- scoped_refptr<AppCache>* cache, scoped_refptr<AppCacheGroup>* group); |
- |
- AppCacheDatabase::GroupRecord group_record_; |
- AppCacheDatabase::CacheRecord cache_record_; |
- std::vector<AppCacheDatabase::EntryRecord> entry_records_; |
- std::vector<AppCacheDatabase::NamespaceRecord> |
- intercept_namespace_records_; |
- std::vector<AppCacheDatabase::NamespaceRecord> |
- fallback_namespace_records_; |
- std::vector<AppCacheDatabase::OnlineWhiteListRecord> |
- online_whitelist_records_; |
-}; |
- |
-bool AppCacheStorageImpl::StoreOrLoadTask::FindRelatedCacheRecords( |
- int64 cache_id) { |
- return database_->FindEntriesForCache(cache_id, &entry_records_) && |
- database_->FindNamespacesForCache( |
- cache_id, &intercept_namespace_records_, |
- &fallback_namespace_records_) && |
- database_->FindOnlineWhiteListForCache( |
- cache_id, &online_whitelist_records_); |
-} |
- |
-void AppCacheStorageImpl::StoreOrLoadTask::CreateCacheAndGroupFromRecords( |
- scoped_refptr<AppCache>* cache, scoped_refptr<AppCacheGroup>* group) { |
- DCHECK(storage_ && cache && group); |
- |
- (*cache) = storage_->working_set_.GetCache(cache_record_.cache_id); |
- if (cache->get()) { |
- (*group) = cache->get()->owning_group(); |
- DCHECK(group->get()); |
- DCHECK_EQ(group_record_.group_id, group->get()->group_id()); |
- |
- // TODO(michaeln): histogram is fishing for clues to crbug/95101 |
- if (!cache->get()->GetEntry(group_record_.manifest_url)) { |
- AppCacheHistograms::AddMissingManifestDetectedAtCallsite( |
- AppCacheHistograms::CALLSITE_0); |
- } |
- |
- storage_->NotifyStorageAccessed(group_record_.origin); |
- return; |
- } |
- |
- (*cache) = new AppCache(storage_, cache_record_.cache_id); |
- cache->get()->InitializeWithDatabaseRecords( |
- cache_record_, entry_records_, |
- intercept_namespace_records_, |
- fallback_namespace_records_, |
- online_whitelist_records_); |
- cache->get()->set_complete(true); |
- |
- (*group) = storage_->working_set_.GetGroup(group_record_.manifest_url); |
- if (group->get()) { |
- DCHECK(group_record_.group_id == group->get()->group_id()); |
- group->get()->AddCache(cache->get()); |
- |
- // TODO(michaeln): histogram is fishing for clues to crbug/95101 |
- if (!cache->get()->GetEntry(group_record_.manifest_url)) { |
- AppCacheHistograms::AddMissingManifestDetectedAtCallsite( |
- AppCacheHistograms::CALLSITE_1); |
- } |
- } else { |
- (*group) = new AppCacheGroup( |
- storage_, group_record_.manifest_url, |
- group_record_.group_id); |
- group->get()->set_creation_time(group_record_.creation_time); |
- group->get()->AddCache(cache->get()); |
- |
- // TODO(michaeln): histogram is fishing for clues to crbug/95101 |
- if (!cache->get()->GetEntry(group_record_.manifest_url)) { |
- AppCacheHistograms::AddMissingManifestDetectedAtCallsite( |
- AppCacheHistograms::CALLSITE_2); |
- } |
- } |
- DCHECK(group->get()->newest_complete_cache() == cache->get()); |
- |
- // We have to update foriegn entries if MarkEntryAsForeignTasks |
- // are in flight. |
- std::vector<GURL> urls; |
- storage_->GetPendingForeignMarkingsForCache(cache->get()->cache_id(), &urls); |
- for (std::vector<GURL>::iterator iter = urls.begin(); |
- iter != urls.end(); ++iter) { |
- DCHECK(cache->get()->GetEntry(*iter)); |
- cache->get()->GetEntry(*iter)->add_types(AppCacheEntry::FOREIGN); |
- } |
- |
- storage_->NotifyStorageAccessed(group_record_.origin); |
- |
- // TODO(michaeln): Maybe verify that the responses we expect to exist |
- // do actually exist in the disk_cache (and if not then what?) |
-} |
- |
-// CacheLoadTask ------- |
- |
-class AppCacheStorageImpl::CacheLoadTask : public StoreOrLoadTask { |
- public: |
- CacheLoadTask(int64 cache_id, AppCacheStorageImpl* storage) |
- : StoreOrLoadTask(storage), cache_id_(cache_id), |
- success_(false) {} |
- |
- // DatabaseTask: |
- virtual void Run() OVERRIDE; |
- virtual void RunCompleted() OVERRIDE; |
- |
- protected: |
- virtual ~CacheLoadTask() {} |
- |
- private: |
- int64 cache_id_; |
- bool success_; |
-}; |
- |
-void AppCacheStorageImpl::CacheLoadTask::Run() { |
- success_ = |
- database_->FindCache(cache_id_, &cache_record_) && |
- database_->FindGroup(cache_record_.group_id, &group_record_) && |
- FindRelatedCacheRecords(cache_id_); |
- |
- if (success_) |
- database_->UpdateGroupLastAccessTime(group_record_.group_id, |
- base::Time::Now()); |
-} |
- |
-void AppCacheStorageImpl::CacheLoadTask::RunCompleted() { |
- storage_->pending_cache_loads_.erase(cache_id_); |
- scoped_refptr<AppCache> cache; |
- scoped_refptr<AppCacheGroup> group; |
- if (success_ && !storage_->is_disabled()) { |
- DCHECK(cache_record_.cache_id == cache_id_); |
- CreateCacheAndGroupFromRecords(&cache, &group); |
- } |
- FOR_EACH_DELEGATE(delegates_, OnCacheLoaded(cache.get(), cache_id_)); |
-} |
- |
-// GroupLoadTask ------- |
- |
-class AppCacheStorageImpl::GroupLoadTask : public StoreOrLoadTask { |
- public: |
- GroupLoadTask(GURL manifest_url, AppCacheStorageImpl* storage) |
- : StoreOrLoadTask(storage), manifest_url_(manifest_url), |
- success_(false) {} |
- |
- // DatabaseTask: |
- virtual void Run() OVERRIDE; |
- virtual void RunCompleted() OVERRIDE; |
- |
- protected: |
- virtual ~GroupLoadTask() {} |
- |
- private: |
- GURL manifest_url_; |
- bool success_; |
-}; |
- |
-void AppCacheStorageImpl::GroupLoadTask::Run() { |
- success_ = |
- database_->FindGroupForManifestUrl(manifest_url_, &group_record_) && |
- database_->FindCacheForGroup(group_record_.group_id, &cache_record_) && |
- FindRelatedCacheRecords(cache_record_.cache_id); |
- |
- if (success_) |
- database_->UpdateGroupLastAccessTime(group_record_.group_id, |
- base::Time::Now()); |
-} |
- |
-void AppCacheStorageImpl::GroupLoadTask::RunCompleted() { |
- storage_->pending_group_loads_.erase(manifest_url_); |
- scoped_refptr<AppCacheGroup> group; |
- scoped_refptr<AppCache> cache; |
- if (!storage_->is_disabled()) { |
- if (success_) { |
- DCHECK(group_record_.manifest_url == manifest_url_); |
- CreateCacheAndGroupFromRecords(&cache, &group); |
- } else { |
- group = storage_->working_set_.GetGroup(manifest_url_); |
- if (!group.get()) { |
- group = |
- new AppCacheGroup(storage_, manifest_url_, storage_->NewGroupId()); |
- } |
- } |
- } |
- FOR_EACH_DELEGATE(delegates_, OnGroupLoaded(group.get(), manifest_url_)); |
-} |
- |
-// StoreGroupAndCacheTask ------- |
- |
-class AppCacheStorageImpl::StoreGroupAndCacheTask : public StoreOrLoadTask { |
- public: |
- StoreGroupAndCacheTask(AppCacheStorageImpl* storage, AppCacheGroup* group, |
- AppCache* newest_cache); |
- |
- void GetQuotaThenSchedule(); |
- void OnQuotaCallback( |
- quota::QuotaStatusCode status, int64 usage, int64 quota); |
- |
- // DatabaseTask: |
- virtual void Run() OVERRIDE; |
- virtual void RunCompleted() OVERRIDE; |
- virtual void CancelCompletion() OVERRIDE; |
- |
- protected: |
- virtual ~StoreGroupAndCacheTask() {} |
- |
- private: |
- scoped_refptr<AppCacheGroup> group_; |
- scoped_refptr<AppCache> cache_; |
- bool success_; |
- bool would_exceed_quota_; |
- int64 space_available_; |
- int64 new_origin_usage_; |
- std::vector<int64> newly_deletable_response_ids_; |
-}; |
- |
-AppCacheStorageImpl::StoreGroupAndCacheTask::StoreGroupAndCacheTask( |
- AppCacheStorageImpl* storage, AppCacheGroup* group, AppCache* newest_cache) |
- : StoreOrLoadTask(storage), group_(group), cache_(newest_cache), |
- success_(false), would_exceed_quota_(false), |
- space_available_(-1), new_origin_usage_(-1) { |
- group_record_.group_id = group->group_id(); |
- group_record_.manifest_url = group->manifest_url(); |
- group_record_.origin = group_record_.manifest_url.GetOrigin(); |
- newest_cache->ToDatabaseRecords( |
- group, |
- &cache_record_, &entry_records_, |
- &intercept_namespace_records_, |
- &fallback_namespace_records_, |
- &online_whitelist_records_); |
-} |
- |
-void AppCacheStorageImpl::StoreGroupAndCacheTask::GetQuotaThenSchedule() { |
- quota::QuotaManager* quota_manager = NULL; |
- if (storage_->service()->quota_manager_proxy()) { |
- quota_manager = |
- storage_->service()->quota_manager_proxy()->quota_manager(); |
- } |
- |
- if (!quota_manager) { |
- if (storage_->service()->special_storage_policy() && |
- storage_->service()->special_storage_policy()->IsStorageUnlimited( |
- group_record_.origin)) |
- space_available_ = kint64max; |
- Schedule(); |
- return; |
- } |
- |
- // We have to ask the quota manager for the value. |
- storage_->pending_quota_queries_.insert(this); |
- quota_manager->GetUsageAndQuota( |
- group_record_.origin, quota::kStorageTypeTemporary, |
- base::Bind(&StoreGroupAndCacheTask::OnQuotaCallback, this)); |
-} |
- |
-void AppCacheStorageImpl::StoreGroupAndCacheTask::OnQuotaCallback( |
- quota::QuotaStatusCode status, int64 usage, int64 quota) { |
- if (storage_) { |
- if (status == quota::kQuotaStatusOk) |
- space_available_ = std::max(static_cast<int64>(0), quota - usage); |
- else |
- space_available_ = 0; |
- storage_->pending_quota_queries_.erase(this); |
- Schedule(); |
- } |
-} |
- |
-void AppCacheStorageImpl::StoreGroupAndCacheTask::Run() { |
- DCHECK(!success_); |
- sql::Connection* connection = database_->db_connection(); |
- if (!connection) |
- return; |
- |
- sql::Transaction transaction(connection); |
- if (!transaction.Begin()) |
- return; |
- |
- int64 old_origin_usage = database_->GetOriginUsage(group_record_.origin); |
- |
- AppCacheDatabase::GroupRecord existing_group; |
- success_ = database_->FindGroup(group_record_.group_id, &existing_group); |
- if (!success_) { |
- group_record_.creation_time = base::Time::Now(); |
- group_record_.last_access_time = base::Time::Now(); |
- success_ = database_->InsertGroup(&group_record_); |
- } else { |
- DCHECK(group_record_.group_id == existing_group.group_id); |
- DCHECK(group_record_.manifest_url == existing_group.manifest_url); |
- DCHECK(group_record_.origin == existing_group.origin); |
- |
- database_->UpdateGroupLastAccessTime(group_record_.group_id, |
- base::Time::Now()); |
- |
- AppCacheDatabase::CacheRecord cache; |
- if (database_->FindCacheForGroup(group_record_.group_id, &cache)) { |
- // Get the set of response ids in the old cache. |
- std::set<int64> existing_response_ids; |
- database_->FindResponseIdsForCacheAsSet(cache.cache_id, |
- &existing_response_ids); |
- |
- // Remove those that remain in the new cache. |
- std::vector<AppCacheDatabase::EntryRecord>::const_iterator entry_iter = |
- entry_records_.begin(); |
- while (entry_iter != entry_records_.end()) { |
- existing_response_ids.erase(entry_iter->response_id); |
- ++entry_iter; |
- } |
- |
- // The rest are deletable. |
- std::set<int64>::const_iterator id_iter = existing_response_ids.begin(); |
- while (id_iter != existing_response_ids.end()) { |
- newly_deletable_response_ids_.push_back(*id_iter); |
- ++id_iter; |
- } |
- |
- success_ = |
- database_->DeleteCache(cache.cache_id) && |
- database_->DeleteEntriesForCache(cache.cache_id) && |
- database_->DeleteNamespacesForCache(cache.cache_id) && |
- database_->DeleteOnlineWhiteListForCache(cache.cache_id) && |
- database_->InsertDeletableResponseIds(newly_deletable_response_ids_); |
- // TODO(michaeln): store group_id too with deletable ids |
- } else { |
- NOTREACHED() << "A existing group without a cache is unexpected"; |
- } |
- } |
- |
- success_ = |
- success_ && |
- database_->InsertCache(&cache_record_) && |
- database_->InsertEntryRecords(entry_records_) && |
- database_->InsertNamespaceRecords(intercept_namespace_records_) && |
- database_->InsertNamespaceRecords(fallback_namespace_records_) && |
- database_->InsertOnlineWhiteListRecords(online_whitelist_records_); |
- |
- if (!success_) |
- return; |
- |
- new_origin_usage_ = database_->GetOriginUsage(group_record_.origin); |
- |
- // Only check quota when the new usage exceeds the old usage. |
- if (new_origin_usage_ <= old_origin_usage) { |
- success_ = transaction.Commit(); |
- return; |
- } |
- |
- // Use a simple hard-coded value when not using quota management. |
- if (space_available_ == -1) { |
- if (new_origin_usage_ > kDefaultQuota) { |
- would_exceed_quota_ = true; |
- success_ = false; |
- return; |
- } |
- success_ = transaction.Commit(); |
- return; |
- } |
- |
- // Check limits based on the space availbable given to us via the |
- // quota system. |
- int64 delta = new_origin_usage_ - old_origin_usage; |
- if (delta > space_available_) { |
- would_exceed_quota_ = true; |
- success_ = false; |
- return; |
- } |
- |
- success_ = transaction.Commit(); |
-} |
- |
-void AppCacheStorageImpl::StoreGroupAndCacheTask::RunCompleted() { |
- if (success_) { |
- storage_->UpdateUsageMapAndNotify( |
- group_->manifest_url().GetOrigin(), new_origin_usage_); |
- if (cache_.get() != group_->newest_complete_cache()) { |
- cache_->set_complete(true); |
- group_->AddCache(cache_.get()); |
- } |
- if (group_->creation_time().is_null()) |
- group_->set_creation_time(group_record_.creation_time); |
- group_->AddNewlyDeletableResponseIds(&newly_deletable_response_ids_); |
- } |
- FOR_EACH_DELEGATE( |
- delegates_, |
- OnGroupAndNewestCacheStored( |
- group_.get(), cache_.get(), success_, would_exceed_quota_)); |
- group_ = NULL; |
- cache_ = NULL; |
- |
- // TODO(michaeln): if (would_exceed_quota_) what if the current usage |
- // also exceeds the quota? http://crbug.com/83968 |
-} |
- |
-void AppCacheStorageImpl::StoreGroupAndCacheTask::CancelCompletion() { |
- // Overriden to safely drop our reference to the group and cache |
- // which are not thread safe refcounted. |
- DatabaseTask::CancelCompletion(); |
- group_ = NULL; |
- cache_ = NULL; |
-} |
- |
-// FindMainResponseTask ------- |
- |
-// Helpers for FindMainResponseTask::Run() |
-namespace { |
-class SortByCachePreference |
- : public std::binary_function< |
- AppCacheDatabase::EntryRecord, |
- AppCacheDatabase::EntryRecord, |
- bool> { |
- public: |
- SortByCachePreference(int64 preferred_id, const std::set<int64>& in_use_ids) |
- : preferred_id_(preferred_id), in_use_ids_(in_use_ids) { |
- } |
- bool operator()( |
- const AppCacheDatabase::EntryRecord& lhs, |
- const AppCacheDatabase::EntryRecord& rhs) { |
- return compute_value(lhs) > compute_value(rhs); |
- } |
- private: |
- int compute_value(const AppCacheDatabase::EntryRecord& entry) { |
- if (entry.cache_id == preferred_id_) |
- return 100; |
- else if (in_use_ids_.find(entry.cache_id) != in_use_ids_.end()) |
- return 50; |
- return 0; |
- } |
- int64 preferred_id_; |
- const std::set<int64>& in_use_ids_; |
-}; |
- |
-bool SortByLength( |
- const AppCacheDatabase::NamespaceRecord& lhs, |
- const AppCacheDatabase::NamespaceRecord& rhs) { |
- return lhs.namespace_.namespace_url.spec().length() > |
- rhs.namespace_.namespace_url.spec().length(); |
-} |
- |
-class NetworkNamespaceHelper { |
- public: |
- explicit NetworkNamespaceHelper(AppCacheDatabase* database) |
- : database_(database) { |
- } |
- |
- bool IsInNetworkNamespace(const GURL& url, int64 cache_id) { |
- typedef std::pair<WhiteListMap::iterator, bool> InsertResult; |
- InsertResult result = namespaces_map_.insert( |
- WhiteListMap::value_type(cache_id, NamespaceVector())); |
- if (result.second) |
- GetOnlineWhiteListForCache(cache_id, &result.first->second); |
- return AppCache::FindNamespace(result.first->second, url) != NULL; |
- } |
- |
- private: |
- void GetOnlineWhiteListForCache( |
- int64 cache_id, NamespaceVector* namespaces) { |
- DCHECK(namespaces && namespaces->empty()); |
- typedef std::vector<AppCacheDatabase::OnlineWhiteListRecord> |
- WhiteListVector; |
- WhiteListVector records; |
- if (!database_->FindOnlineWhiteListForCache(cache_id, &records)) |
- return; |
- WhiteListVector::const_iterator iter = records.begin(); |
- while (iter != records.end()) { |
- namespaces->push_back( |
- Namespace(APPCACHE_NETWORK_NAMESPACE, iter->namespace_url, GURL(), |
- iter->is_pattern)); |
- ++iter; |
- } |
- } |
- |
- // Key is cache id |
- typedef std::map<int64, NamespaceVector> WhiteListMap; |
- WhiteListMap namespaces_map_; |
- AppCacheDatabase* database_; |
-}; |
- |
-} // namespace |
- |
-class AppCacheStorageImpl::FindMainResponseTask : public DatabaseTask { |
- public: |
- FindMainResponseTask(AppCacheStorageImpl* storage, |
- const GURL& url, |
- const GURL& preferred_manifest_url, |
- const AppCacheWorkingSet::GroupMap* groups_in_use) |
- : DatabaseTask(storage), url_(url), |
- preferred_manifest_url_(preferred_manifest_url), |
- cache_id_(kAppCacheNoCacheId), group_id_(0) { |
- if (groups_in_use) { |
- for (AppCacheWorkingSet::GroupMap::const_iterator it = |
- groups_in_use->begin(); |
- it != groups_in_use->end(); ++it) { |
- AppCacheGroup* group = it->second; |
- AppCache* cache = group->newest_complete_cache(); |
- if (group->is_obsolete() || !cache) |
- continue; |
- cache_ids_in_use_.insert(cache->cache_id()); |
- } |
- } |
- } |
- |
- // DatabaseTask: |
- virtual void Run() OVERRIDE; |
- virtual void RunCompleted() OVERRIDE; |
- |
- protected: |
- virtual ~FindMainResponseTask() {} |
- |
- private: |
- typedef std::vector<AppCacheDatabase::NamespaceRecord*> |
- NamespaceRecordPtrVector; |
- |
- bool FindExactMatch(int64 preferred_id); |
- bool FindNamespaceMatch(int64 preferred_id); |
- bool FindNamespaceHelper( |
- int64 preferred_cache_id, |
- AppCacheDatabase::NamespaceRecordVector* namespaces, |
- NetworkNamespaceHelper* network_namespace_helper); |
- bool FindFirstValidNamespace(const NamespaceRecordPtrVector& namespaces); |
- |
- GURL url_; |
- GURL preferred_manifest_url_; |
- std::set<int64> cache_ids_in_use_; |
- AppCacheEntry entry_; |
- AppCacheEntry fallback_entry_; |
- GURL namespace_entry_url_; |
- int64 cache_id_; |
- int64 group_id_; |
- GURL manifest_url_; |
-}; |
- |
-void AppCacheStorageImpl::FindMainResponseTask::Run() { |
- // NOTE: The heuristics around choosing amoungst multiple candidates |
- // is underspecified, and just plain not fully understood. This needs |
- // to be refined. |
- |
- // The 'preferred_manifest_url' is the url of the manifest associated |
- // with the page that opened or embedded the page being loaded now. |
- // We have a strong preference to use resources from that cache. |
- // We also have a lesser bias to use resources from caches that are currently |
- // being used by other unrelated pages. |
- // TODO(michaeln): come up with a 'preferred_manifest_url' in more cases |
- // - when navigating a frame whose current contents are from an appcache |
- // - when clicking an href in a frame that is appcached |
- int64 preferred_cache_id = kAppCacheNoCacheId; |
- if (!preferred_manifest_url_.is_empty()) { |
- AppCacheDatabase::GroupRecord preferred_group; |
- AppCacheDatabase::CacheRecord preferred_cache; |
- if (database_->FindGroupForManifestUrl( |
- preferred_manifest_url_, &preferred_group) && |
- database_->FindCacheForGroup( |
- preferred_group.group_id, &preferred_cache)) { |
- preferred_cache_id = preferred_cache.cache_id; |
- } |
- } |
- |
- if (FindExactMatch(preferred_cache_id) || |
- FindNamespaceMatch(preferred_cache_id)) { |
- // We found something. |
- DCHECK(cache_id_ != kAppCacheNoCacheId && !manifest_url_.is_empty() && |
- group_id_ != 0); |
- return; |
- } |
- |
- // We didn't find anything. |
- DCHECK(cache_id_ == kAppCacheNoCacheId && manifest_url_.is_empty() && |
- group_id_ == 0); |
-} |
- |
-bool AppCacheStorageImpl:: |
-FindMainResponseTask::FindExactMatch(int64 preferred_cache_id) { |
- std::vector<AppCacheDatabase::EntryRecord> entries; |
- if (database_->FindEntriesForUrl(url_, &entries) && !entries.empty()) { |
- // Sort them in order of preference, from the preferred_cache first, |
- // followed by hits from caches that are 'in use', then the rest. |
- std::sort(entries.begin(), entries.end(), |
- SortByCachePreference(preferred_cache_id, cache_ids_in_use_)); |
- |
- // Take the first with a valid, non-foreign entry. |
- std::vector<AppCacheDatabase::EntryRecord>::iterator iter; |
- for (iter = entries.begin(); iter < entries.end(); ++iter) { |
- AppCacheDatabase::GroupRecord group_record; |
- if ((iter->flags & AppCacheEntry::FOREIGN) || |
- !database_->FindGroupForCache(iter->cache_id, &group_record)) { |
- continue; |
- } |
- manifest_url_ = group_record.manifest_url; |
- group_id_ = group_record.group_id; |
- entry_ = AppCacheEntry(iter->flags, iter->response_id); |
- cache_id_ = iter->cache_id; |
- return true; // We found an exact match. |
- } |
- } |
- return false; |
-} |
- |
-bool AppCacheStorageImpl:: |
-FindMainResponseTask::FindNamespaceMatch(int64 preferred_cache_id) { |
- AppCacheDatabase::NamespaceRecordVector all_intercepts; |
- AppCacheDatabase::NamespaceRecordVector all_fallbacks; |
- if (!database_->FindNamespacesForOrigin( |
- url_.GetOrigin(), &all_intercepts, &all_fallbacks) |
- || (all_intercepts.empty() && all_fallbacks.empty())) { |
- return false; |
- } |
- |
- NetworkNamespaceHelper network_namespace_helper(database_); |
- if (FindNamespaceHelper(preferred_cache_id, |
- &all_intercepts, |
- &network_namespace_helper) || |
- FindNamespaceHelper(preferred_cache_id, |
- &all_fallbacks, |
- &network_namespace_helper)) { |
- return true; |
- } |
- return false; |
-} |
- |
-bool AppCacheStorageImpl:: |
-FindMainResponseTask::FindNamespaceHelper( |
- int64 preferred_cache_id, |
- AppCacheDatabase::NamespaceRecordVector* namespaces, |
- NetworkNamespaceHelper* network_namespace_helper) { |
- // Sort them by length, longer matches within the same cache/bucket take |
- // precedence. |
- std::sort(namespaces->begin(), namespaces->end(), SortByLength); |
- |
- NamespaceRecordPtrVector preferred_namespaces; |
- NamespaceRecordPtrVector inuse_namespaces; |
- NamespaceRecordPtrVector other_namespaces; |
- std::vector<AppCacheDatabase::NamespaceRecord>::iterator iter; |
- for (iter = namespaces->begin(); iter < namespaces->end(); ++iter) { |
- // Skip those that aren't a match. |
- if (!iter->namespace_.IsMatch(url_)) |
- continue; |
- |
- // Skip namespaces where the requested url falls into a network |
- // namespace of its containing appcache. |
- if (network_namespace_helper->IsInNetworkNamespace(url_, iter->cache_id)) |
- continue; |
- |
- // Bin them into one of our three buckets. |
- if (iter->cache_id == preferred_cache_id) |
- preferred_namespaces.push_back(&(*iter)); |
- else if (cache_ids_in_use_.find(iter->cache_id) != cache_ids_in_use_.end()) |
- inuse_namespaces.push_back(&(*iter)); |
- else |
- other_namespaces.push_back(&(*iter)); |
- } |
- |
- if (FindFirstValidNamespace(preferred_namespaces) || |
- FindFirstValidNamespace(inuse_namespaces) || |
- FindFirstValidNamespace(other_namespaces)) |
- return true; // We found one. |
- |
- // We didn't find anything. |
- return false; |
-} |
- |
-bool AppCacheStorageImpl:: |
-FindMainResponseTask::FindFirstValidNamespace( |
- const NamespaceRecordPtrVector& namespaces) { |
- // Take the first with a valid, non-foreign entry. |
- NamespaceRecordPtrVector::const_iterator iter; |
- for (iter = namespaces.begin(); iter < namespaces.end(); ++iter) { |
- AppCacheDatabase::EntryRecord entry_record; |
- if (database_->FindEntry((*iter)->cache_id, (*iter)->namespace_.target_url, |
- &entry_record)) { |
- AppCacheDatabase::GroupRecord group_record; |
- if ((entry_record.flags & AppCacheEntry::FOREIGN) || |
- !database_->FindGroupForCache(entry_record.cache_id, &group_record)) { |
- continue; |
- } |
- manifest_url_ = group_record.manifest_url; |
- group_id_ = group_record.group_id; |
- cache_id_ = (*iter)->cache_id; |
- namespace_entry_url_ = (*iter)->namespace_.target_url; |
- if ((*iter)->namespace_.type == APPCACHE_FALLBACK_NAMESPACE) |
- fallback_entry_ = AppCacheEntry(entry_record.flags, |
- entry_record.response_id); |
- else |
- entry_ = AppCacheEntry(entry_record.flags, entry_record.response_id); |
- return true; // We found one. |
- } |
- } |
- return false; // We didn't find a match. |
-} |
- |
-void AppCacheStorageImpl::FindMainResponseTask::RunCompleted() { |
- storage_->CallOnMainResponseFound( |
- &delegates_, url_, entry_, namespace_entry_url_, fallback_entry_, |
- cache_id_, group_id_, manifest_url_); |
-} |
- |
-// MarkEntryAsForeignTask ------- |
- |
-class AppCacheStorageImpl::MarkEntryAsForeignTask : public DatabaseTask { |
- public: |
- MarkEntryAsForeignTask( |
- AppCacheStorageImpl* storage, const GURL& url, int64 cache_id) |
- : DatabaseTask(storage), cache_id_(cache_id), entry_url_(url) {} |
- |
- // DatabaseTask: |
- virtual void Run() OVERRIDE; |
- virtual void RunCompleted() OVERRIDE; |
- |
- protected: |
- virtual ~MarkEntryAsForeignTask() {} |
- |
- private: |
- int64 cache_id_; |
- GURL entry_url_; |
-}; |
- |
-void AppCacheStorageImpl::MarkEntryAsForeignTask::Run() { |
- database_->AddEntryFlags(entry_url_, cache_id_, AppCacheEntry::FOREIGN); |
-} |
- |
-void AppCacheStorageImpl::MarkEntryAsForeignTask::RunCompleted() { |
- DCHECK(storage_->pending_foreign_markings_.front().first == entry_url_ && |
- storage_->pending_foreign_markings_.front().second == cache_id_); |
- storage_->pending_foreign_markings_.pop_front(); |
-} |
- |
-// MakeGroupObsoleteTask ------- |
- |
-class AppCacheStorageImpl::MakeGroupObsoleteTask : public DatabaseTask { |
- public: |
- MakeGroupObsoleteTask(AppCacheStorageImpl* storage, |
- AppCacheGroup* group, |
- int response_code); |
- |
- // DatabaseTask: |
- virtual void Run() OVERRIDE; |
- virtual void RunCompleted() OVERRIDE; |
- virtual void CancelCompletion() OVERRIDE; |
- |
- protected: |
- virtual ~MakeGroupObsoleteTask() {} |
- |
- private: |
- scoped_refptr<AppCacheGroup> group_; |
- int64 group_id_; |
- GURL origin_; |
- bool success_; |
- int response_code_; |
- int64 new_origin_usage_; |
- std::vector<int64> newly_deletable_response_ids_; |
-}; |
- |
-AppCacheStorageImpl::MakeGroupObsoleteTask::MakeGroupObsoleteTask( |
- AppCacheStorageImpl* storage, |
- AppCacheGroup* group, |
- int response_code) |
- : DatabaseTask(storage), |
- group_(group), |
- group_id_(group->group_id()), |
- origin_(group->manifest_url().GetOrigin()), |
- success_(false), |
- response_code_(response_code), |
- new_origin_usage_(-1) {} |
- |
-void AppCacheStorageImpl::MakeGroupObsoleteTask::Run() { |
- DCHECK(!success_); |
- sql::Connection* connection = database_->db_connection(); |
- if (!connection) |
- return; |
- |
- sql::Transaction transaction(connection); |
- if (!transaction.Begin()) |
- return; |
- |
- AppCacheDatabase::GroupRecord group_record; |
- if (!database_->FindGroup(group_id_, &group_record)) { |
- // This group doesn't exists in the database, nothing todo here. |
- new_origin_usage_ = database_->GetOriginUsage(origin_); |
- success_ = true; |
- return; |
- } |
- |
- DCHECK_EQ(group_record.origin, origin_); |
- success_ = DeleteGroupAndRelatedRecords(database_, |
- group_id_, |
- &newly_deletable_response_ids_); |
- |
- new_origin_usage_ = database_->GetOriginUsage(origin_); |
- success_ = success_ && transaction.Commit(); |
-} |
- |
-void AppCacheStorageImpl::MakeGroupObsoleteTask::RunCompleted() { |
- if (success_) { |
- group_->set_obsolete(true); |
- if (!storage_->is_disabled()) { |
- storage_->UpdateUsageMapAndNotify(origin_, new_origin_usage_); |
- group_->AddNewlyDeletableResponseIds(&newly_deletable_response_ids_); |
- |
- // Also remove from the working set, caches for an 'obsolete' group |
- // may linger in use, but the group itself cannot be looked up by |
- // 'manifest_url' in the working set any longer. |
- storage_->working_set()->RemoveGroup(group_.get()); |
- } |
- } |
- FOR_EACH_DELEGATE( |
- delegates_, OnGroupMadeObsolete(group_.get(), success_, response_code_)); |
- group_ = NULL; |
-} |
- |
-void AppCacheStorageImpl::MakeGroupObsoleteTask::CancelCompletion() { |
- // Overriden to safely drop our reference to the group |
- // which is not thread safe refcounted. |
- DatabaseTask::CancelCompletion(); |
- group_ = NULL; |
-} |
- |
-// GetDeletableResponseIdsTask ------- |
- |
-class AppCacheStorageImpl::GetDeletableResponseIdsTask : public DatabaseTask { |
- public: |
- GetDeletableResponseIdsTask(AppCacheStorageImpl* storage, int64 max_rowid) |
- : DatabaseTask(storage), max_rowid_(max_rowid) {} |
- |
- // DatabaseTask: |
- virtual void Run() OVERRIDE; |
- virtual void RunCompleted() OVERRIDE; |
- |
- protected: |
- virtual ~GetDeletableResponseIdsTask() {} |
- |
- private: |
- int64 max_rowid_; |
- std::vector<int64> response_ids_; |
-}; |
- |
-void AppCacheStorageImpl::GetDeletableResponseIdsTask::Run() { |
- const int kSqlLimit = 1000; |
- database_->GetDeletableResponseIds(&response_ids_, max_rowid_, kSqlLimit); |
- // TODO(michaeln): retrieve group_ids too |
-} |
- |
-void AppCacheStorageImpl::GetDeletableResponseIdsTask::RunCompleted() { |
- if (!response_ids_.empty()) |
- storage_->StartDeletingResponses(response_ids_); |
-} |
- |
-// InsertDeletableResponseIdsTask ------- |
- |
-class AppCacheStorageImpl::InsertDeletableResponseIdsTask |
- : public DatabaseTask { |
- public: |
- explicit InsertDeletableResponseIdsTask(AppCacheStorageImpl* storage) |
- : DatabaseTask(storage) {} |
- |
- // DatabaseTask: |
- virtual void Run() OVERRIDE; |
- |
- std::vector<int64> response_ids_; |
- |
- protected: |
- virtual ~InsertDeletableResponseIdsTask() {} |
-}; |
- |
-void AppCacheStorageImpl::InsertDeletableResponseIdsTask::Run() { |
- database_->InsertDeletableResponseIds(response_ids_); |
- // TODO(michaeln): store group_ids too |
-} |
- |
-// DeleteDeletableResponseIdsTask ------- |
- |
-class AppCacheStorageImpl::DeleteDeletableResponseIdsTask |
- : public DatabaseTask { |
- public: |
- explicit DeleteDeletableResponseIdsTask(AppCacheStorageImpl* storage) |
- : DatabaseTask(storage) {} |
- |
- // DatabaseTask: |
- virtual void Run() OVERRIDE; |
- |
- std::vector<int64> response_ids_; |
- |
- protected: |
- virtual ~DeleteDeletableResponseIdsTask() {} |
-}; |
- |
-void AppCacheStorageImpl::DeleteDeletableResponseIdsTask::Run() { |
- database_->DeleteDeletableResponseIds(response_ids_); |
-} |
- |
-// UpdateGroupLastAccessTimeTask ------- |
- |
-class AppCacheStorageImpl::UpdateGroupLastAccessTimeTask |
- : public DatabaseTask { |
- public: |
- UpdateGroupLastAccessTimeTask( |
- AppCacheStorageImpl* storage, AppCacheGroup* group, base::Time time) |
- : DatabaseTask(storage), group_id_(group->group_id()), |
- last_access_time_(time) { |
- storage->NotifyStorageAccessed(group->manifest_url().GetOrigin()); |
- } |
- |
- // DatabaseTask: |
- virtual void Run() OVERRIDE; |
- |
- protected: |
- virtual ~UpdateGroupLastAccessTimeTask() {} |
- |
- private: |
- int64 group_id_; |
- base::Time last_access_time_; |
-}; |
- |
-void AppCacheStorageImpl::UpdateGroupLastAccessTimeTask::Run() { |
- database_->UpdateGroupLastAccessTime(group_id_, last_access_time_); |
-} |
- |
- |
-// AppCacheStorageImpl --------------------------------------------------- |
- |
-AppCacheStorageImpl::AppCacheStorageImpl(AppCacheServiceImpl* service) |
- : AppCacheStorage(service), |
- is_incognito_(false), |
- is_response_deletion_scheduled_(false), |
- did_start_deleting_responses_(false), |
- last_deletable_response_rowid_(0), |
- database_(NULL), |
- is_disabled_(false), |
- weak_factory_(this) { |
-} |
- |
-AppCacheStorageImpl::~AppCacheStorageImpl() { |
- std::for_each(pending_quota_queries_.begin(), |
- pending_quota_queries_.end(), |
- std::mem_fun(&DatabaseTask::CancelCompletion)); |
- std::for_each(scheduled_database_tasks_.begin(), |
- scheduled_database_tasks_.end(), |
- std::mem_fun(&DatabaseTask::CancelCompletion)); |
- |
- if (database_ && |
- !db_thread_->PostTask( |
- FROM_HERE, |
- base::Bind(&ClearSessionOnlyOrigins, database_, |
- make_scoped_refptr(service_->special_storage_policy()), |
- service()->force_keep_session_state()))) { |
- delete database_; |
- } |
- database_ = NULL; // So no further database tasks can be scheduled. |
-} |
- |
-void AppCacheStorageImpl::Initialize(const base::FilePath& cache_directory, |
- base::MessageLoopProxy* db_thread, |
- base::MessageLoopProxy* cache_thread) { |
- DCHECK(db_thread); |
- |
- cache_directory_ = cache_directory; |
- is_incognito_ = cache_directory_.empty(); |
- |
- base::FilePath db_file_path; |
- if (!is_incognito_) |
- db_file_path = cache_directory_.Append(kAppCacheDatabaseName); |
- database_ = new AppCacheDatabase(db_file_path); |
- |
- db_thread_ = db_thread; |
- cache_thread_ = cache_thread; |
- |
- scoped_refptr<InitTask> task(new InitTask(this)); |
- task->Schedule(); |
-} |
- |
-void AppCacheStorageImpl::Disable() { |
- if (is_disabled_) |
- return; |
- VLOG(1) << "Disabling appcache storage."; |
- is_disabled_ = true; |
- ClearUsageMapAndNotify(); |
- working_set()->Disable(); |
- if (disk_cache_) |
- disk_cache_->Disable(); |
- scoped_refptr<DisableDatabaseTask> task(new DisableDatabaseTask(this)); |
- task->Schedule(); |
-} |
- |
-void AppCacheStorageImpl::GetAllInfo(Delegate* delegate) { |
- DCHECK(delegate); |
- scoped_refptr<GetAllInfoTask> task(new GetAllInfoTask(this)); |
- task->AddDelegate(GetOrCreateDelegateReference(delegate)); |
- task->Schedule(); |
-} |
- |
-void AppCacheStorageImpl::LoadCache(int64 id, Delegate* delegate) { |
- DCHECK(delegate); |
- if (is_disabled_) { |
- delegate->OnCacheLoaded(NULL, id); |
- return; |
- } |
- |
- AppCache* cache = working_set_.GetCache(id); |
- if (cache) { |
- delegate->OnCacheLoaded(cache, id); |
- if (cache->owning_group()) { |
- scoped_refptr<DatabaseTask> update_task( |
- new UpdateGroupLastAccessTimeTask( |
- this, cache->owning_group(), base::Time::Now())); |
- update_task->Schedule(); |
- } |
- return; |
- } |
- scoped_refptr<CacheLoadTask> task(GetPendingCacheLoadTask(id)); |
- if (task.get()) { |
- task->AddDelegate(GetOrCreateDelegateReference(delegate)); |
- return; |
- } |
- task = new CacheLoadTask(id, this); |
- task->AddDelegate(GetOrCreateDelegateReference(delegate)); |
- task->Schedule(); |
- pending_cache_loads_[id] = task.get(); |
-} |
- |
-void AppCacheStorageImpl::LoadOrCreateGroup( |
- const GURL& manifest_url, Delegate* delegate) { |
- DCHECK(delegate); |
- if (is_disabled_) { |
- delegate->OnGroupLoaded(NULL, manifest_url); |
- return; |
- } |
- |
- AppCacheGroup* group = working_set_.GetGroup(manifest_url); |
- if (group) { |
- delegate->OnGroupLoaded(group, manifest_url); |
- scoped_refptr<DatabaseTask> update_task( |
- new UpdateGroupLastAccessTimeTask( |
- this, group, base::Time::Now())); |
- update_task->Schedule(); |
- return; |
- } |
- |
- scoped_refptr<GroupLoadTask> task(GetPendingGroupLoadTask(manifest_url)); |
- if (task.get()) { |
- task->AddDelegate(GetOrCreateDelegateReference(delegate)); |
- return; |
- } |
- |
- if (usage_map_.find(manifest_url.GetOrigin()) == usage_map_.end()) { |
- // No need to query the database, return a new group immediately. |
- scoped_refptr<AppCacheGroup> group(new AppCacheGroup( |
- this, manifest_url, NewGroupId())); |
- delegate->OnGroupLoaded(group.get(), manifest_url); |
- return; |
- } |
- |
- task = new GroupLoadTask(manifest_url, this); |
- task->AddDelegate(GetOrCreateDelegateReference(delegate)); |
- task->Schedule(); |
- pending_group_loads_[manifest_url] = task.get(); |
-} |
- |
-void AppCacheStorageImpl::StoreGroupAndNewestCache( |
- AppCacheGroup* group, AppCache* newest_cache, Delegate* delegate) { |
- // TODO(michaeln): distinguish between a simple update of an existing |
- // cache that just adds new master entry(s), and the insertion of a |
- // whole new cache. The StoreGroupAndCacheTask as written will handle |
- // the simple update case in a very heavy weight way (delete all and |
- // the reinsert all over again). |
- DCHECK(group && delegate && newest_cache); |
- scoped_refptr<StoreGroupAndCacheTask> task( |
- new StoreGroupAndCacheTask(this, group, newest_cache)); |
- task->AddDelegate(GetOrCreateDelegateReference(delegate)); |
- task->GetQuotaThenSchedule(); |
- |
- // TODO(michaeln): histogram is fishing for clues to crbug/95101 |
- if (!newest_cache->GetEntry(group->manifest_url())) { |
- AppCacheHistograms::AddMissingManifestDetectedAtCallsite( |
- AppCacheHistograms::CALLSITE_3); |
- } |
-} |
- |
-void AppCacheStorageImpl::FindResponseForMainRequest( |
- const GURL& url, const GURL& preferred_manifest_url, |
- Delegate* delegate) { |
- DCHECK(delegate); |
- |
- const GURL* url_ptr = &url; |
- GURL url_no_ref; |
- if (url.has_ref()) { |
- GURL::Replacements replacements; |
- replacements.ClearRef(); |
- url_no_ref = url.ReplaceComponents(replacements); |
- url_ptr = &url_no_ref; |
- } |
- |
- const GURL origin = url.GetOrigin(); |
- |
- // First look in our working set for a direct hit without having to query |
- // the database. |
- const AppCacheWorkingSet::GroupMap* groups_in_use = |
- working_set()->GetGroupsInOrigin(origin); |
- if (groups_in_use) { |
- if (!preferred_manifest_url.is_empty()) { |
- AppCacheWorkingSet::GroupMap::const_iterator found = |
- groups_in_use->find(preferred_manifest_url); |
- if (found != groups_in_use->end() && |
- FindResponseForMainRequestInGroup( |
- found->second, *url_ptr, delegate)) { |
- return; |
- } |
- } else { |
- for (AppCacheWorkingSet::GroupMap::const_iterator it = |
- groups_in_use->begin(); |
- it != groups_in_use->end(); ++it) { |
- if (FindResponseForMainRequestInGroup( |
- it->second, *url_ptr, delegate)) { |
- return; |
- } |
- } |
- } |
- } |
- |
- if (IsInitTaskComplete() && usage_map_.find(origin) == usage_map_.end()) { |
- // No need to query the database, return async'ly but without going thru |
- // the DB thread. |
- scoped_refptr<AppCacheGroup> no_group; |
- scoped_refptr<AppCache> no_cache; |
- ScheduleSimpleTask( |
- base::Bind(&AppCacheStorageImpl::DeliverShortCircuitedFindMainResponse, |
- weak_factory_.GetWeakPtr(), url, AppCacheEntry(), no_group, |
- no_cache, |
- make_scoped_refptr(GetOrCreateDelegateReference(delegate)))); |
- return; |
- } |
- |
- // We have to query the database, schedule a database task to do so. |
- scoped_refptr<FindMainResponseTask> task( |
- new FindMainResponseTask(this, *url_ptr, preferred_manifest_url, |
- groups_in_use)); |
- task->AddDelegate(GetOrCreateDelegateReference(delegate)); |
- task->Schedule(); |
-} |
- |
-bool AppCacheStorageImpl::FindResponseForMainRequestInGroup( |
- AppCacheGroup* group, const GURL& url, Delegate* delegate) { |
- AppCache* cache = group->newest_complete_cache(); |
- if (group->is_obsolete() || !cache) |
- return false; |
- |
- AppCacheEntry* entry = cache->GetEntry(url); |
- if (!entry || entry->IsForeign()) |
- return false; |
- |
- ScheduleSimpleTask( |
- base::Bind(&AppCacheStorageImpl::DeliverShortCircuitedFindMainResponse, |
- weak_factory_.GetWeakPtr(), url, *entry, |
- make_scoped_refptr(group), make_scoped_refptr(cache), |
- make_scoped_refptr(GetOrCreateDelegateReference(delegate)))); |
- return true; |
-} |
- |
-void AppCacheStorageImpl::DeliverShortCircuitedFindMainResponse( |
- const GURL& url, |
- const AppCacheEntry& found_entry, |
- scoped_refptr<AppCacheGroup> group, |
- scoped_refptr<AppCache> cache, |
- scoped_refptr<DelegateReference> delegate_ref) { |
- if (delegate_ref->delegate) { |
- DelegateReferenceVector delegates(1, delegate_ref); |
- CallOnMainResponseFound( |
- &delegates, url, found_entry, |
- GURL(), AppCacheEntry(), |
- cache.get() ? cache->cache_id() : kAppCacheNoCacheId, |
- group.get() ? group->group_id() : kAppCacheNoCacheId, |
- group.get() ? group->manifest_url() : GURL()); |
- } |
-} |
- |
-void AppCacheStorageImpl::CallOnMainResponseFound( |
- DelegateReferenceVector* delegates, |
- const GURL& url, const AppCacheEntry& entry, |
- const GURL& namespace_entry_url, const AppCacheEntry& fallback_entry, |
- int64 cache_id, int64 group_id, const GURL& manifest_url) { |
- FOR_EACH_DELEGATE( |
- (*delegates), |
- OnMainResponseFound(url, entry, |
- namespace_entry_url, fallback_entry, |
- cache_id, group_id, manifest_url)); |
-} |
- |
-void AppCacheStorageImpl::FindResponseForSubRequest( |
- AppCache* cache, const GURL& url, |
- AppCacheEntry* found_entry, AppCacheEntry* found_fallback_entry, |
- bool* found_network_namespace) { |
- DCHECK(cache && cache->is_complete()); |
- |
- // When a group is forcibly deleted, all subresource loads for pages |
- // using caches in the group will result in a synthesized network errors. |
- // Forcible deletion is not a function that is covered by the HTML5 spec. |
- if (cache->owning_group()->is_being_deleted()) { |
- *found_entry = AppCacheEntry(); |
- *found_fallback_entry = AppCacheEntry(); |
- *found_network_namespace = false; |
- return; |
- } |
- |
- GURL fallback_namespace_not_used; |
- GURL intercept_namespace_not_used; |
- cache->FindResponseForRequest( |
- url, found_entry, &intercept_namespace_not_used, |
- found_fallback_entry, &fallback_namespace_not_used, |
- found_network_namespace); |
-} |
- |
-void AppCacheStorageImpl::MarkEntryAsForeign( |
- const GURL& entry_url, int64 cache_id) { |
- AppCache* cache = working_set_.GetCache(cache_id); |
- if (cache) { |
- AppCacheEntry* entry = cache->GetEntry(entry_url); |
- DCHECK(entry); |
- if (entry) |
- entry->add_types(AppCacheEntry::FOREIGN); |
- } |
- scoped_refptr<MarkEntryAsForeignTask> task( |
- new MarkEntryAsForeignTask(this, entry_url, cache_id)); |
- task->Schedule(); |
- pending_foreign_markings_.push_back(std::make_pair(entry_url, cache_id)); |
-} |
- |
-void AppCacheStorageImpl::MakeGroupObsolete(AppCacheGroup* group, |
- Delegate* delegate, |
- int response_code) { |
- DCHECK(group && delegate); |
- scoped_refptr<MakeGroupObsoleteTask> task( |
- new MakeGroupObsoleteTask(this, group, response_code)); |
- task->AddDelegate(GetOrCreateDelegateReference(delegate)); |
- task->Schedule(); |
-} |
- |
-AppCacheResponseReader* AppCacheStorageImpl::CreateResponseReader( |
- const GURL& manifest_url, int64 group_id, int64 response_id) { |
- return new AppCacheResponseReader(response_id, group_id, disk_cache()); |
-} |
- |
-AppCacheResponseWriter* AppCacheStorageImpl::CreateResponseWriter( |
- const GURL& manifest_url, int64 group_id) { |
- return new AppCacheResponseWriter(NewResponseId(), group_id, disk_cache()); |
-} |
- |
-void AppCacheStorageImpl::DoomResponses( |
- const GURL& manifest_url, const std::vector<int64>& response_ids) { |
- if (response_ids.empty()) |
- return; |
- |
- // Start deleting them from the disk cache lazily. |
- StartDeletingResponses(response_ids); |
- |
- // Also schedule a database task to record these ids in the |
- // deletable responses table. |
- // TODO(michaeln): There is a race here. If the browser crashes |
- // prior to committing these rows to the database and prior to us |
- // having deleted them from the disk cache, we'll never delete them. |
- scoped_refptr<InsertDeletableResponseIdsTask> task( |
- new InsertDeletableResponseIdsTask(this)); |
- task->response_ids_ = response_ids; |
- task->Schedule(); |
-} |
- |
-void AppCacheStorageImpl::DeleteResponses( |
- const GURL& manifest_url, const std::vector<int64>& response_ids) { |
- if (response_ids.empty()) |
- return; |
- StartDeletingResponses(response_ids); |
-} |
- |
-void AppCacheStorageImpl::DelayedStartDeletingUnusedResponses() { |
- // Only if we haven't already begun. |
- if (!did_start_deleting_responses_) { |
- scoped_refptr<GetDeletableResponseIdsTask> task( |
- new GetDeletableResponseIdsTask(this, last_deletable_response_rowid_)); |
- task->Schedule(); |
- } |
-} |
- |
-void AppCacheStorageImpl::StartDeletingResponses( |
- const std::vector<int64>& response_ids) { |
- DCHECK(!response_ids.empty()); |
- did_start_deleting_responses_ = true; |
- deletable_response_ids_.insert( |
- deletable_response_ids_.end(), |
- response_ids.begin(), response_ids.end()); |
- if (!is_response_deletion_scheduled_) |
- ScheduleDeleteOneResponse(); |
-} |
- |
-void AppCacheStorageImpl::ScheduleDeleteOneResponse() { |
- DCHECK(!is_response_deletion_scheduled_); |
- const base::TimeDelta kDelay = base::TimeDelta::FromMilliseconds(10); |
- base::MessageLoop::current()->PostDelayedTask( |
- FROM_HERE, |
- base::Bind(&AppCacheStorageImpl::DeleteOneResponse, |
- weak_factory_.GetWeakPtr()), |
- kDelay); |
- is_response_deletion_scheduled_ = true; |
-} |
- |
-void AppCacheStorageImpl::DeleteOneResponse() { |
- DCHECK(is_response_deletion_scheduled_); |
- DCHECK(!deletable_response_ids_.empty()); |
- |
- if (!disk_cache()) { |
- DCHECK(is_disabled_); |
- deletable_response_ids_.clear(); |
- deleted_response_ids_.clear(); |
- is_response_deletion_scheduled_ = false; |
- return; |
- } |
- |
- // TODO(michaeln): add group_id to DoomEntry args |
- int64 id = deletable_response_ids_.front(); |
- int rv = disk_cache_->DoomEntry( |
- id, base::Bind(&AppCacheStorageImpl::OnDeletedOneResponse, |
- base::Unretained(this))); |
- if (rv != net::ERR_IO_PENDING) |
- OnDeletedOneResponse(rv); |
-} |
- |
-void AppCacheStorageImpl::OnDeletedOneResponse(int rv) { |
- is_response_deletion_scheduled_ = false; |
- if (is_disabled_) |
- return; |
- |
- int64 id = deletable_response_ids_.front(); |
- deletable_response_ids_.pop_front(); |
- if (rv != net::ERR_ABORTED) |
- deleted_response_ids_.push_back(id); |
- |
- const size_t kBatchSize = 50U; |
- if (deleted_response_ids_.size() >= kBatchSize || |
- deletable_response_ids_.empty()) { |
- scoped_refptr<DeleteDeletableResponseIdsTask> task( |
- new DeleteDeletableResponseIdsTask(this)); |
- task->response_ids_.swap(deleted_response_ids_); |
- task->Schedule(); |
- } |
- |
- if (deletable_response_ids_.empty()) { |
- scoped_refptr<GetDeletableResponseIdsTask> task( |
- new GetDeletableResponseIdsTask(this, last_deletable_response_rowid_)); |
- task->Schedule(); |
- return; |
- } |
- |
- ScheduleDeleteOneResponse(); |
-} |
- |
-AppCacheStorageImpl::CacheLoadTask* |
-AppCacheStorageImpl::GetPendingCacheLoadTask(int64 cache_id) { |
- PendingCacheLoads::iterator found = pending_cache_loads_.find(cache_id); |
- if (found != pending_cache_loads_.end()) |
- return found->second; |
- return NULL; |
-} |
- |
-AppCacheStorageImpl::GroupLoadTask* |
-AppCacheStorageImpl::GetPendingGroupLoadTask(const GURL& manifest_url) { |
- PendingGroupLoads::iterator found = pending_group_loads_.find(manifest_url); |
- if (found != pending_group_loads_.end()) |
- return found->second; |
- return NULL; |
-} |
- |
-void AppCacheStorageImpl::GetPendingForeignMarkingsForCache( |
- int64 cache_id, std::vector<GURL>* urls) { |
- PendingForeignMarkings::iterator iter = pending_foreign_markings_.begin(); |
- while (iter != pending_foreign_markings_.end()) { |
- if (iter->second == cache_id) |
- urls->push_back(iter->first); |
- ++iter; |
- } |
-} |
- |
-void AppCacheStorageImpl::ScheduleSimpleTask(const base::Closure& task) { |
- pending_simple_tasks_.push_back(task); |
- base::MessageLoop::current()->PostTask( |
- FROM_HERE, |
- base::Bind(&AppCacheStorageImpl::RunOnePendingSimpleTask, |
- weak_factory_.GetWeakPtr())); |
-} |
- |
-void AppCacheStorageImpl::RunOnePendingSimpleTask() { |
- DCHECK(!pending_simple_tasks_.empty()); |
- base::Closure task = pending_simple_tasks_.front(); |
- pending_simple_tasks_.pop_front(); |
- task.Run(); |
-} |
- |
-AppCacheDiskCache* AppCacheStorageImpl::disk_cache() { |
- DCHECK(IsInitTaskComplete()); |
- |
- if (is_disabled_) |
- return NULL; |
- |
- if (!disk_cache_) { |
- int rv = net::OK; |
- disk_cache_.reset(new AppCacheDiskCache); |
- if (is_incognito_) { |
- rv = disk_cache_->InitWithMemBackend( |
- kMaxMemDiskCacheSize, |
- base::Bind(&AppCacheStorageImpl::OnDiskCacheInitialized, |
- base::Unretained(this))); |
- } else { |
- rv = disk_cache_->InitWithDiskBackend( |
- cache_directory_.Append(kDiskCacheDirectoryName), |
- kMaxDiskCacheSize, |
- false, |
- cache_thread_.get(), |
- base::Bind(&AppCacheStorageImpl::OnDiskCacheInitialized, |
- base::Unretained(this))); |
- } |
- |
- if (rv != net::ERR_IO_PENDING) |
- OnDiskCacheInitialized(rv); |
- } |
- return disk_cache_.get(); |
-} |
- |
-void AppCacheStorageImpl::OnDiskCacheInitialized(int rv) { |
- if (rv != net::OK) { |
- LOG(ERROR) << "Failed to open the appcache diskcache."; |
- AppCacheHistograms::CountInitResult(AppCacheHistograms::DISK_CACHE_ERROR); |
- |
- // We're unable to open the disk cache, this is a fatal error that we can't |
- // really recover from. We handle it by temporarily disabling the appcache |
- // deleting the directory on disk and reinitializing the appcache system. |
- Disable(); |
- if (rv != net::ERR_ABORTED) |
- DeleteAndStartOver(); |
- } |
-} |
- |
-void AppCacheStorageImpl::DeleteAndStartOver() { |
- DCHECK(is_disabled_); |
- if (!is_incognito_) { |
- VLOG(1) << "Deleting existing appcache data and starting over."; |
- // We can have tasks in flight to close file handles on both the db |
- // and cache threads, we need to allow those tasks to cycle thru |
- // prior to deleting the files and calling reinit. |
- cache_thread_->PostTaskAndReply( |
- FROM_HERE, |
- base::Bind(&base::DoNothing), |
- base::Bind(&AppCacheStorageImpl::DeleteAndStartOverPart2, |
- weak_factory_.GetWeakPtr())); |
- } |
-} |
- |
-void AppCacheStorageImpl::DeleteAndStartOverPart2() { |
- db_thread_->PostTaskAndReply( |
- FROM_HERE, |
- base::Bind(base::IgnoreResult(&base::DeleteFile), |
- cache_directory_, true), |
- base::Bind(&AppCacheStorageImpl::CallScheduleReinitialize, |
- weak_factory_.GetWeakPtr())); |
-} |
- |
-void AppCacheStorageImpl::CallScheduleReinitialize() { |
- service_->ScheduleReinitialize(); |
- // note: 'this' may be deleted at this point. |
-} |
- |
-} // namespace appcache |