| OLD | NEW |
| (Empty) |
| 1 // Copyright 2014 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 "content/browser/service_worker/service_worker_cache_storage_manager.h" | |
| 6 | |
| 7 #include <map> | |
| 8 #include <string> | |
| 9 | |
| 10 #include "base/bind.h" | |
| 11 #include "base/files/file_enumerator.h" | |
| 12 #include "base/files/file_util.h" | |
| 13 #include "base/id_map.h" | |
| 14 #include "base/sha1.h" | |
| 15 #include "base/strings/string_number_conversions.h" | |
| 16 #include "base/strings/string_util.h" | |
| 17 #include "content/browser/service_worker/service_worker_cache.pb.h" | |
| 18 #include "content/browser/service_worker/service_worker_cache_quota_client.h" | |
| 19 #include "content/browser/service_worker/service_worker_cache_storage.h" | |
| 20 #include "content/browser/service_worker/service_worker_context_core.h" | |
| 21 #include "content/public/browser/browser_thread.h" | |
| 22 #include "net/base/net_util.h" | |
| 23 #include "storage/browser/quota/quota_manager_proxy.h" | |
| 24 #include "storage/common/database/database_identifier.h" | |
| 25 #include "storage/common/quota/quota_status_code.h" | |
| 26 #include "url/gurl.h" | |
| 27 | |
| 28 namespace content { | |
| 29 | |
| 30 namespace { | |
| 31 | |
| 32 bool DeleteDir(const base::FilePath& path) { | |
| 33 return base::DeleteFile(path, true /* recursive */); | |
| 34 } | |
| 35 | |
| 36 void DeleteOriginDidDeleteDir( | |
| 37 const storage::QuotaClient::DeletionCallback& callback, | |
| 38 bool rv) { | |
| 39 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 40 | |
| 41 callback.Run(rv ? storage::kQuotaStatusOk : storage::kQuotaErrorAbort); | |
| 42 } | |
| 43 | |
| 44 std::set<GURL> ListOriginsOnDisk(base::FilePath root_path_) { | |
| 45 std::set<GURL> origins; | |
| 46 base::FileEnumerator file_enum( | |
| 47 root_path_, false /* recursive */, base::FileEnumerator::DIRECTORIES); | |
| 48 | |
| 49 base::FilePath path; | |
| 50 while (!(path = file_enum.Next()).empty()) { | |
| 51 std::string protobuf; | |
| 52 base::ReadFileToString( | |
| 53 path.AppendASCII(ServiceWorkerCacheStorage::kIndexFileName), &protobuf); | |
| 54 | |
| 55 ServiceWorkerCacheStorageIndex index; | |
| 56 if (index.ParseFromString(protobuf)) { | |
| 57 if (index.has_origin()) | |
| 58 origins.insert(GURL(index.origin())); | |
| 59 } | |
| 60 } | |
| 61 | |
| 62 return origins; | |
| 63 } | |
| 64 | |
| 65 void GetOriginsForHostDidListOrigins( | |
| 66 const std::string& host, | |
| 67 const storage::QuotaClient::GetOriginsCallback& callback, | |
| 68 const std::set<GURL>& origins) { | |
| 69 std::set<GURL> out_origins; | |
| 70 for (const GURL& origin : origins) { | |
| 71 if (host == net::GetHostOrSpecFromURL(origin)) | |
| 72 out_origins.insert(origin); | |
| 73 } | |
| 74 callback.Run(out_origins); | |
| 75 } | |
| 76 | |
| 77 } // namespace | |
| 78 | |
| 79 // static | |
| 80 scoped_ptr<ServiceWorkerCacheStorageManager> | |
| 81 ServiceWorkerCacheStorageManager::Create( | |
| 82 const base::FilePath& path, | |
| 83 const scoped_refptr<base::SequencedTaskRunner>& cache_task_runner, | |
| 84 const scoped_refptr<storage::QuotaManagerProxy>& quota_manager_proxy) { | |
| 85 base::FilePath root_path = path; | |
| 86 if (!path.empty()) { | |
| 87 root_path = path.Append(ServiceWorkerContextCore::kServiceWorkerDirectory) | |
| 88 .AppendASCII("CacheStorage"); | |
| 89 } | |
| 90 | |
| 91 return make_scoped_ptr(new ServiceWorkerCacheStorageManager( | |
| 92 root_path, cache_task_runner, quota_manager_proxy)); | |
| 93 } | |
| 94 | |
| 95 // static | |
| 96 scoped_ptr<ServiceWorkerCacheStorageManager> | |
| 97 ServiceWorkerCacheStorageManager::Create( | |
| 98 ServiceWorkerCacheStorageManager* old_manager) { | |
| 99 scoped_ptr<ServiceWorkerCacheStorageManager> manager( | |
| 100 new ServiceWorkerCacheStorageManager( | |
| 101 old_manager->root_path(), | |
| 102 old_manager->cache_task_runner(), | |
| 103 old_manager->quota_manager_proxy_.get())); | |
| 104 // These values may be NULL, in which case this will be called again later by | |
| 105 // the dispatcher host per usual. | |
| 106 manager->SetBlobParametersForCache(old_manager->url_request_context(), | |
| 107 old_manager->blob_storage_context()); | |
| 108 return manager.Pass(); | |
| 109 } | |
| 110 | |
| 111 ServiceWorkerCacheStorageManager::~ServiceWorkerCacheStorageManager() { | |
| 112 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 113 for (ServiceWorkerCacheStorageMap::iterator it = cache_storage_map_.begin(); | |
| 114 it != cache_storage_map_.end(); | |
| 115 ++it) { | |
| 116 delete it->second; | |
| 117 } | |
| 118 } | |
| 119 | |
| 120 void ServiceWorkerCacheStorageManager::OpenCache( | |
| 121 const GURL& origin, | |
| 122 const std::string& cache_name, | |
| 123 const ServiceWorkerCacheStorage::CacheAndErrorCallback& callback) { | |
| 124 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 125 | |
| 126 ServiceWorkerCacheStorage* cache_storage = | |
| 127 FindOrCreateServiceWorkerCacheManager(origin); | |
| 128 | |
| 129 cache_storage->OpenCache(cache_name, callback); | |
| 130 } | |
| 131 | |
| 132 void ServiceWorkerCacheStorageManager::HasCache( | |
| 133 const GURL& origin, | |
| 134 const std::string& cache_name, | |
| 135 const ServiceWorkerCacheStorage::BoolAndErrorCallback& callback) { | |
| 136 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 137 | |
| 138 ServiceWorkerCacheStorage* cache_storage = | |
| 139 FindOrCreateServiceWorkerCacheManager(origin); | |
| 140 cache_storage->HasCache(cache_name, callback); | |
| 141 } | |
| 142 | |
| 143 void ServiceWorkerCacheStorageManager::DeleteCache( | |
| 144 const GURL& origin, | |
| 145 const std::string& cache_name, | |
| 146 const ServiceWorkerCacheStorage::BoolAndErrorCallback& callback) { | |
| 147 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 148 | |
| 149 ServiceWorkerCacheStorage* cache_storage = | |
| 150 FindOrCreateServiceWorkerCacheManager(origin); | |
| 151 cache_storage->DeleteCache(cache_name, callback); | |
| 152 } | |
| 153 | |
| 154 void ServiceWorkerCacheStorageManager::EnumerateCaches( | |
| 155 const GURL& origin, | |
| 156 const ServiceWorkerCacheStorage::StringsAndErrorCallback& callback) { | |
| 157 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 158 | |
| 159 ServiceWorkerCacheStorage* cache_storage = | |
| 160 FindOrCreateServiceWorkerCacheManager(origin); | |
| 161 | |
| 162 cache_storage->EnumerateCaches(callback); | |
| 163 } | |
| 164 | |
| 165 void ServiceWorkerCacheStorageManager::MatchCache( | |
| 166 const GURL& origin, | |
| 167 const std::string& cache_name, | |
| 168 scoped_ptr<ServiceWorkerFetchRequest> request, | |
| 169 const ServiceWorkerCache::ResponseCallback& callback) { | |
| 170 ServiceWorkerCacheStorage* cache_storage = | |
| 171 FindOrCreateServiceWorkerCacheManager(origin); | |
| 172 | |
| 173 cache_storage->MatchCache(cache_name, request.Pass(), callback); | |
| 174 } | |
| 175 | |
| 176 void ServiceWorkerCacheStorageManager::MatchAllCaches( | |
| 177 const GURL& origin, | |
| 178 scoped_ptr<ServiceWorkerFetchRequest> request, | |
| 179 const ServiceWorkerCache::ResponseCallback& callback) { | |
| 180 ServiceWorkerCacheStorage* cache_storage = | |
| 181 FindOrCreateServiceWorkerCacheManager(origin); | |
| 182 | |
| 183 cache_storage->MatchAllCaches(request.Pass(), callback); | |
| 184 } | |
| 185 | |
| 186 void ServiceWorkerCacheStorageManager::SetBlobParametersForCache( | |
| 187 net::URLRequestContext* request_context, | |
| 188 base::WeakPtr<storage::BlobStorageContext> blob_storage_context) { | |
| 189 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 190 DCHECK(cache_storage_map_.empty()); | |
| 191 DCHECK(!request_context_ || request_context_ == request_context); | |
| 192 DCHECK(!blob_context_ || blob_context_.get() == blob_storage_context.get()); | |
| 193 request_context_ = request_context; | |
| 194 blob_context_ = blob_storage_context; | |
| 195 } | |
| 196 | |
| 197 void ServiceWorkerCacheStorageManager::GetOriginUsage( | |
| 198 const GURL& origin_url, | |
| 199 const storage::QuotaClient::GetUsageCallback& callback) { | |
| 200 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 201 | |
| 202 if (IsMemoryBacked()) { | |
| 203 int64 sum = 0; | |
| 204 for (const auto& key_value : cache_storage_map_) | |
| 205 sum += key_value.second->MemoryBackedSize(); | |
| 206 callback.Run(sum); | |
| 207 return; | |
| 208 } | |
| 209 | |
| 210 MigrateOrigin(origin_url); | |
| 211 PostTaskAndReplyWithResult( | |
| 212 cache_task_runner_.get(), | |
| 213 FROM_HERE, | |
| 214 base::Bind(base::ComputeDirectorySize, | |
| 215 ConstructOriginPath(root_path_, origin_url)), | |
| 216 base::Bind(callback)); | |
| 217 } | |
| 218 | |
| 219 void ServiceWorkerCacheStorageManager::GetOrigins( | |
| 220 const storage::QuotaClient::GetOriginsCallback& callback) { | |
| 221 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 222 | |
| 223 if (IsMemoryBacked()) { | |
| 224 std::set<GURL> origins; | |
| 225 for (const auto& key_value : cache_storage_map_) | |
| 226 origins.insert(key_value.first); | |
| 227 | |
| 228 callback.Run(origins); | |
| 229 return; | |
| 230 } | |
| 231 | |
| 232 PostTaskAndReplyWithResult(cache_task_runner_.get(), | |
| 233 FROM_HERE, | |
| 234 base::Bind(&ListOriginsOnDisk, root_path_), | |
| 235 base::Bind(callback)); | |
| 236 } | |
| 237 | |
| 238 void ServiceWorkerCacheStorageManager::GetOriginsForHost( | |
| 239 const std::string& host, | |
| 240 const storage::QuotaClient::GetOriginsCallback& callback) { | |
| 241 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 242 | |
| 243 if (IsMemoryBacked()) { | |
| 244 std::set<GURL> origins; | |
| 245 for (const auto& key_value : cache_storage_map_) { | |
| 246 if (host == net::GetHostOrSpecFromURL(key_value.first)) | |
| 247 origins.insert(key_value.first); | |
| 248 } | |
| 249 callback.Run(origins); | |
| 250 return; | |
| 251 } | |
| 252 | |
| 253 PostTaskAndReplyWithResult( | |
| 254 cache_task_runner_.get(), | |
| 255 FROM_HERE, | |
| 256 base::Bind(&ListOriginsOnDisk, root_path_), | |
| 257 base::Bind(&GetOriginsForHostDidListOrigins, host, callback)); | |
| 258 } | |
| 259 | |
| 260 void ServiceWorkerCacheStorageManager::DeleteOriginData( | |
| 261 const GURL& origin, | |
| 262 const storage::QuotaClient::DeletionCallback& callback) { | |
| 263 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 264 | |
| 265 ServiceWorkerCacheStorage* cache_storage = | |
| 266 FindOrCreateServiceWorkerCacheManager(origin); | |
| 267 cache_storage_map_.erase(origin); | |
| 268 cache_storage->CloseAllCaches( | |
| 269 base::Bind(&ServiceWorkerCacheStorageManager::DeleteOriginDidClose, | |
| 270 origin, callback, base::Passed(make_scoped_ptr(cache_storage)), | |
| 271 weak_ptr_factory_.GetWeakPtr())); | |
| 272 } | |
| 273 | |
| 274 // static | |
| 275 void ServiceWorkerCacheStorageManager::DeleteOriginDidClose( | |
| 276 const GURL& origin, | |
| 277 const storage::QuotaClient::DeletionCallback& callback, | |
| 278 scoped_ptr<ServiceWorkerCacheStorage> cache_storage, | |
| 279 base::WeakPtr<ServiceWorkerCacheStorageManager> cache_manager) { | |
| 280 // TODO(jkarlin): Deleting the storage leaves any unfinished operations | |
| 281 // hanging, resulting in unresolved promises. Fix this by guaranteeing that | |
| 282 // callbacks are called in ServiceWorkerStorage. | |
| 283 cache_storage.reset(); | |
| 284 | |
| 285 if (!cache_manager) { | |
| 286 callback.Run(storage::kQuotaErrorAbort); | |
| 287 return; | |
| 288 } | |
| 289 | |
| 290 if (cache_manager->IsMemoryBacked()) { | |
| 291 callback.Run(storage::kQuotaStatusOk); | |
| 292 return; | |
| 293 } | |
| 294 | |
| 295 cache_manager->MigrateOrigin(origin); | |
| 296 PostTaskAndReplyWithResult( | |
| 297 cache_manager->cache_task_runner_.get(), FROM_HERE, | |
| 298 base::Bind(&DeleteDir, | |
| 299 ConstructOriginPath(cache_manager->root_path_, origin)), | |
| 300 base::Bind(&DeleteOriginDidDeleteDir, callback)); | |
| 301 } | |
| 302 | |
| 303 ServiceWorkerCacheStorageManager::ServiceWorkerCacheStorageManager( | |
| 304 const base::FilePath& path, | |
| 305 const scoped_refptr<base::SequencedTaskRunner>& cache_task_runner, | |
| 306 const scoped_refptr<storage::QuotaManagerProxy>& quota_manager_proxy) | |
| 307 : root_path_(path), | |
| 308 cache_task_runner_(cache_task_runner), | |
| 309 quota_manager_proxy_(quota_manager_proxy), | |
| 310 request_context_(NULL), | |
| 311 weak_ptr_factory_(this) { | |
| 312 if (quota_manager_proxy_.get()) { | |
| 313 quota_manager_proxy_->RegisterClient( | |
| 314 new ServiceWorkerCacheQuotaClient(weak_ptr_factory_.GetWeakPtr())); | |
| 315 } | |
| 316 } | |
| 317 | |
| 318 ServiceWorkerCacheStorage* | |
| 319 ServiceWorkerCacheStorageManager::FindOrCreateServiceWorkerCacheManager( | |
| 320 const GURL& origin) { | |
| 321 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 322 DCHECK(request_context_); | |
| 323 ServiceWorkerCacheStorageMap::const_iterator it = | |
| 324 cache_storage_map_.find(origin); | |
| 325 if (it == cache_storage_map_.end()) { | |
| 326 MigrateOrigin(origin); | |
| 327 ServiceWorkerCacheStorage* cache_storage = | |
| 328 new ServiceWorkerCacheStorage(ConstructOriginPath(root_path_, origin), | |
| 329 IsMemoryBacked(), | |
| 330 cache_task_runner_.get(), | |
| 331 request_context_, | |
| 332 quota_manager_proxy_, | |
| 333 blob_context_, | |
| 334 origin); | |
| 335 // The map owns fetch_stores. | |
| 336 cache_storage_map_.insert(std::make_pair(origin, cache_storage)); | |
| 337 return cache_storage; | |
| 338 } | |
| 339 return it->second; | |
| 340 } | |
| 341 | |
| 342 // static | |
| 343 base::FilePath ServiceWorkerCacheStorageManager::ConstructLegacyOriginPath( | |
| 344 const base::FilePath& root_path, | |
| 345 const GURL& origin) { | |
| 346 const std::string origin_hash = base::SHA1HashString(origin.spec()); | |
| 347 const std::string origin_hash_hex = base::StringToLowerASCII( | |
| 348 base::HexEncode(origin_hash.c_str(), origin_hash.length())); | |
| 349 return root_path.AppendASCII(origin_hash_hex); | |
| 350 } | |
| 351 | |
| 352 // static | |
| 353 base::FilePath ServiceWorkerCacheStorageManager::ConstructOriginPath( | |
| 354 const base::FilePath& root_path, | |
| 355 const GURL& origin) { | |
| 356 const std::string identifier = storage::GetIdentifierFromOrigin(origin); | |
| 357 const std::string origin_hash = base::SHA1HashString(identifier); | |
| 358 const std::string origin_hash_hex = base::StringToLowerASCII( | |
| 359 base::HexEncode(origin_hash.c_str(), origin_hash.length())); | |
| 360 return root_path.AppendASCII(origin_hash_hex); | |
| 361 } | |
| 362 | |
| 363 // Migrate from old origin-based path to storage identifier-based path. | |
| 364 // TODO(jsbell); Remove after a few releases. | |
| 365 void ServiceWorkerCacheStorageManager::MigrateOrigin(const GURL& origin) { | |
| 366 if (IsMemoryBacked()) | |
| 367 return; | |
| 368 base::FilePath old_path = ConstructLegacyOriginPath(root_path_, origin); | |
| 369 base::FilePath new_path = ConstructOriginPath(root_path_, origin); | |
| 370 cache_task_runner_->PostTask( | |
| 371 FROM_HERE, base::Bind(&MigrateOriginOnTaskRunner, old_path, new_path)); | |
| 372 } | |
| 373 | |
| 374 // static | |
| 375 void ServiceWorkerCacheStorageManager::MigrateOriginOnTaskRunner( | |
| 376 const base::FilePath& old_path, | |
| 377 const base::FilePath& new_path) { | |
| 378 if (base::PathExists(old_path)) { | |
| 379 if (!base::PathExists(new_path)) | |
| 380 base::Move(old_path, new_path); | |
| 381 base::DeleteFile(old_path, /*recursive*/ true); | |
| 382 } | |
| 383 } | |
| 384 | |
| 385 } // namespace content | |
| OLD | NEW |