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_service.h" 6 7 #include <functional> 8 9 #include "base/bind.h" 10 #include "base/bind_helpers.h" 11 #include "base/logging.h" 12 #include "base/message_loop/message_loop.h" 13 #include "base/stl_util.h" 14 #include "net/base/completion_callback.h" 15 #include "net/base/io_buffer.h" 16 #include "webkit/browser/appcache/appcache.h" 17 #include "webkit/browser/appcache/appcache_backend_impl.h" 18 #include "webkit/browser/appcache/appcache_entry.h" 19 #include "webkit/browser/appcache/appcache_executable_handler.h" 20 #include "webkit/browser/appcache/appcache_histograms.h" 21 #include "webkit/browser/appcache/appcache_policy.h" 22 #include "webkit/browser/appcache/appcache_quota_client.h" 23 #include "webkit/browser/appcache/appcache_response.h" 24 #include "webkit/browser/appcache/appcache_storage_impl.h" 25 #include "webkit/browser/quota/quota_manager.h" 26 #include "webkit/browser/quota/special_storage_policy.h" 27 28 namespace appcache { 29 30 namespace { 31 32 void DeferredCallback(const net::CompletionCallback& callback, int rv) { 33 callback.Run(rv); 34 } 35 36 } // namespace 37 38 AppCacheInfoCollection::AppCacheInfoCollection() {} 39 40 AppCacheInfoCollection::~AppCacheInfoCollection() {} 41 42 // AsyncHelper ------- 43 44 class AppCacheService::AsyncHelper 45 : public AppCacheStorage::Delegate { 46 public: 47 AsyncHelper(AppCacheService* service, 48 const net::CompletionCallback& callback) 49 : service_(service), callback_(callback) { 50 service_->pending_helpers_.insert(this); 51 } 52 53 virtual ~AsyncHelper() { 54 if (service_) 55 service_->pending_helpers_.erase(this); 56 } 57 58 virtual void Start() = 0; 59 virtual void Cancel(); 60 61 protected: 62 void CallCallback(int rv) { 63 if (!callback_.is_null()) { 64 // Defer to guarantee async completion. 65 base::MessageLoop::current()->PostTask( 66 FROM_HERE, base::Bind(&DeferredCallback, callback_, rv)); 67 } 68 callback_.Reset(); 69 } 70 71 AppCacheService* service_; 72 net::CompletionCallback callback_; 73 }; 74 75 void AppCacheService::AsyncHelper::Cancel() { 76 if (!callback_.is_null()) { 77 callback_.Run(net::ERR_ABORTED); 78 callback_.Reset(); 79 } 80 service_->storage()->CancelDelegateCallbacks(this); 81 service_ = NULL; 82 } 83 84 // CanHandleOfflineHelper ------- 85 86 class AppCacheService::CanHandleOfflineHelper : AsyncHelper { 87 public: 88 CanHandleOfflineHelper( 89 AppCacheService* service, const GURL& url, 90 const GURL& first_party, const net::CompletionCallback& callback) 91 : AsyncHelper(service, callback), 92 url_(url), 93 first_party_(first_party) { 94 } 95 96 virtual void Start() OVERRIDE { 97 AppCachePolicy* policy = service_->appcache_policy(); 98 if (policy && !policy->CanLoadAppCache(url_, first_party_)) { 99 CallCallback(net::ERR_FAILED); 100 delete this; 101 return; 102 } 103 104 service_->storage()->FindResponseForMainRequest(url_, GURL(), this); 105 } 106 107 private: 108 // AppCacheStorage::Delegate implementation. 109 virtual void OnMainResponseFound( 110 const GURL& url, const AppCacheEntry& entry, 111 const GURL& fallback_url, const AppCacheEntry& fallback_entry, 112 int64 cache_id, int64 group_id, const GURL& mainfest_url) OVERRIDE; 113 114 GURL url_; 115 GURL first_party_; 116 117 DISALLOW_COPY_AND_ASSIGN(CanHandleOfflineHelper); 118 }; 119 120 void AppCacheService::CanHandleOfflineHelper::OnMainResponseFound( 121 const GURL& url, const AppCacheEntry& entry, 122 const GURL& fallback_url, const AppCacheEntry& fallback_entry, 123 int64 cache_id, int64 group_id, const GURL& manifest_url) { 124 bool can = (entry.has_response_id() || fallback_entry.has_response_id()); 125 CallCallback(can ? net::OK : net::ERR_FAILED); 126 delete this; 127 } 128 129 // DeleteHelper ------- 130 131 class AppCacheService::DeleteHelper : public AsyncHelper { 132 public: 133 DeleteHelper( 134 AppCacheService* service, const GURL& manifest_url, 135 const net::CompletionCallback& callback) 136 : AsyncHelper(service, callback), manifest_url_(manifest_url) { 137 } 138 139 virtual void Start() OVERRIDE { 140 service_->storage()->LoadOrCreateGroup(manifest_url_, this); 141 } 142 143 private: 144 // AppCacheStorage::Delegate implementation. 145 virtual void OnGroupLoaded( 146 appcache::AppCacheGroup* group, const GURL& manifest_url) OVERRIDE; 147 virtual void OnGroupMadeObsolete( 148 appcache::AppCacheGroup* group, bool success) OVERRIDE; 149 150 GURL manifest_url_; 151 DISALLOW_COPY_AND_ASSIGN(DeleteHelper); 152 }; 153 154 void AppCacheService::DeleteHelper::OnGroupLoaded( 155 appcache::AppCacheGroup* group, const GURL& manifest_url) { 156 if (group) { 157 group->set_being_deleted(true); 158 group->CancelUpdate(); 159 service_->storage()->MakeGroupObsolete(group, this); 160 } else { 161 CallCallback(net::ERR_FAILED); 162 delete this; 163 } 164 } 165 166 void AppCacheService::DeleteHelper::OnGroupMadeObsolete( 167 appcache::AppCacheGroup* group, bool success) { 168 CallCallback(success ? net::OK : net::ERR_FAILED); 169 delete this; 170 } 171 172 // DeleteOriginHelper ------- 173 174 class AppCacheService::DeleteOriginHelper : public AsyncHelper { 175 public: 176 DeleteOriginHelper( 177 AppCacheService* service, const GURL& origin, 178 const net::CompletionCallback& callback) 179 : AsyncHelper(service, callback), origin_(origin), 180 num_caches_to_delete_(0), successes_(0), failures_(0) { 181 } 182 183 virtual void Start() OVERRIDE { 184 // We start by listing all caches, continues in OnAllInfo(). 185 service_->storage()->GetAllInfo(this); 186 } 187 188 private: 189 // AppCacheStorage::Delegate implementation. 190 virtual void OnAllInfo(AppCacheInfoCollection* collection) OVERRIDE; 191 virtual void OnGroupLoaded( 192 appcache::AppCacheGroup* group, const GURL& manifest_url) OVERRIDE; 193 virtual void OnGroupMadeObsolete( 194 appcache::AppCacheGroup* group, bool success) OVERRIDE; 195 196 void CacheCompleted(bool success); 197 198 GURL origin_; 199 int num_caches_to_delete_; 200 int successes_; 201 int failures_; 202 203 DISALLOW_COPY_AND_ASSIGN(DeleteOriginHelper); 204 }; 205 206 void AppCacheService::DeleteOriginHelper::OnAllInfo( 207 AppCacheInfoCollection* collection) { 208 if (!collection) { 209 // Failed to get a listing. 210 CallCallback(net::ERR_FAILED); 211 delete this; 212 return; 213 } 214 215 std::map<GURL, AppCacheInfoVector>::iterator found = 216 collection->infos_by_origin.find(origin_); 217 if (found == collection->infos_by_origin.end() || found->second.empty()) { 218 // No caches for this origin. 219 CallCallback(net::OK); 220 delete this; 221 return; 222 } 223 224 // We have some caches to delete. 225 const AppCacheInfoVector& caches_to_delete = found->second; 226 successes_ = 0; 227 failures_ = 0; 228 num_caches_to_delete_ = static_cast<int>(caches_to_delete.size()); 229 for (AppCacheInfoVector::const_iterator iter = caches_to_delete.begin(); 230 iter != caches_to_delete.end(); ++iter) { 231 service_->storage()->LoadOrCreateGroup(iter->manifest_url, this); 232 } 233 } 234 235 void AppCacheService::DeleteOriginHelper::OnGroupLoaded( 236 appcache::AppCacheGroup* group, const GURL& manifest_url) { 237 if (group) { 238 group->set_being_deleted(true); 239 group->CancelUpdate(); 240 service_->storage()->MakeGroupObsolete(group, this); 241 } else { 242 CacheCompleted(false); 243 } 244 } 245 246 void AppCacheService::DeleteOriginHelper::OnGroupMadeObsolete( 247 appcache::AppCacheGroup* group, bool success) { 248 CacheCompleted(success); 249 } 250 251 void AppCacheService::DeleteOriginHelper::CacheCompleted(bool success) { 252 if (success) 253 ++successes_; 254 else 255 ++failures_; 256 if ((successes_ + failures_) < num_caches_to_delete_) 257 return; 258 259 CallCallback(!failures_ ? net::OK : net::ERR_FAILED); 260 delete this; 261 } 262 263 264 // GetInfoHelper ------- 265 266 class AppCacheService::GetInfoHelper : AsyncHelper { 267 public: 268 GetInfoHelper( 269 AppCacheService* service, AppCacheInfoCollection* collection, 270 const net::CompletionCallback& callback) 271 : AsyncHelper(service, callback), collection_(collection) { 272 } 273 274 virtual void Start() OVERRIDE { 275 service_->storage()->GetAllInfo(this); 276 } 277 278 private: 279 // AppCacheStorage::Delegate implementation. 280 virtual void OnAllInfo(AppCacheInfoCollection* collection) OVERRIDE; 281 282 scoped_refptr<AppCacheInfoCollection> collection_; 283 284 DISALLOW_COPY_AND_ASSIGN(GetInfoHelper); 285 }; 286 287 void AppCacheService::GetInfoHelper::OnAllInfo( 288 AppCacheInfoCollection* collection) { 289 if (collection) 290 collection->infos_by_origin.swap(collection_->infos_by_origin); 291 CallCallback(collection ? net::OK : net::ERR_FAILED); 292 delete this; 293 } 294 295 // CheckResponseHelper ------- 296 297 class AppCacheService::CheckResponseHelper : AsyncHelper { 298 public: 299 CheckResponseHelper( 300 AppCacheService* service, const GURL& manifest_url, int64 cache_id, 301 int64 response_id) 302 : AsyncHelper(service, net::CompletionCallback()), 303 manifest_url_(manifest_url), 304 cache_id_(cache_id), 305 response_id_(response_id), 306 kIOBufferSize(32 * 1024), 307 expected_total_size_(0), 308 amount_headers_read_(0), 309 amount_data_read_(0) { 310 } 311 312 virtual void Start() OVERRIDE { 313 service_->storage()->LoadOrCreateGroup(manifest_url_, this); 314 } 315 316 virtual void Cancel() OVERRIDE { 317 AppCacheHistograms::CountCheckResponseResult( 318 AppCacheHistograms::CHECK_CANCELED); 319 response_reader_.reset(); 320 AsyncHelper::Cancel(); 321 } 322 323 private: 324 virtual void OnGroupLoaded(AppCacheGroup* group, 325 const GURL& manifest_url) OVERRIDE; 326 void OnReadInfoComplete(int result); 327 void OnReadDataComplete(int result); 328 329 // Inputs describing what to check. 330 GURL manifest_url_; 331 int64 cache_id_; 332 int64 response_id_; 333 334 // Internals used to perform the checks. 335 const int kIOBufferSize; 336 scoped_refptr<AppCache> cache_; 337 scoped_ptr<AppCacheResponseReader> response_reader_; 338 scoped_refptr<HttpResponseInfoIOBuffer> info_buffer_; 339 scoped_refptr<net::IOBuffer> data_buffer_; 340 int64 expected_total_size_; 341 int amount_headers_read_; 342 int amount_data_read_; 343 DISALLOW_COPY_AND_ASSIGN(CheckResponseHelper); 344 }; 345 346 void AppCacheService::CheckResponseHelper::OnGroupLoaded( 347 AppCacheGroup* group, const GURL& manifest_url) { 348 DCHECK_EQ(manifest_url_, manifest_url); 349 if (!group || !group->newest_complete_cache() || group->is_being_deleted() || 350 group->is_obsolete()) { 351 AppCacheHistograms::CountCheckResponseResult( 352 AppCacheHistograms::MANIFEST_OUT_OF_DATE); 353 delete this; 354 return; 355 } 356 357 cache_ = group->newest_complete_cache(); 358 const AppCacheEntry* entry = cache_->GetEntryWithResponseId(response_id_); 359 if (!entry) { 360 if (cache_->cache_id() == cache_id_) { 361 AppCacheHistograms::CountCheckResponseResult( 362 AppCacheHistograms::ENTRY_NOT_FOUND); 363 service_->DeleteAppCacheGroup(manifest_url_, net::CompletionCallback()); 364 } else { 365 AppCacheHistograms::CountCheckResponseResult( 366 AppCacheHistograms::RESPONSE_OUT_OF_DATE); 367 } 368 delete this; 369 return; 370 } 371 372 // Verify that we can read the response info and data. 373 expected_total_size_ = entry->response_size(); 374 response_reader_.reset(service_->storage()->CreateResponseReader( 375 manifest_url_, group->group_id(), response_id_)); 376 info_buffer_ = new HttpResponseInfoIOBuffer(); 377 response_reader_->ReadInfo( 378 info_buffer_.get(), 379 base::Bind(&CheckResponseHelper::OnReadInfoComplete, 380 base::Unretained(this))); 381 } 382 383 void AppCacheService::CheckResponseHelper::OnReadInfoComplete(int result) { 384 if (result < 0) { 385 AppCacheHistograms::CountCheckResponseResult( 386 AppCacheHistograms::READ_HEADERS_ERROR); 387 service_->DeleteAppCacheGroup(manifest_url_, net::CompletionCallback()); 388 delete this; 389 return; 390 } 391 amount_headers_read_ = result; 392 393 // Start reading the data. 394 data_buffer_ = new net::IOBuffer(kIOBufferSize); 395 response_reader_->ReadData( 396 data_buffer_.get(), 397 kIOBufferSize, 398 base::Bind(&CheckResponseHelper::OnReadDataComplete, 399 base::Unretained(this))); 400 } 401 402 void AppCacheService::CheckResponseHelper::OnReadDataComplete(int result) { 403 if (result > 0) { 404 // Keep reading until we've read thru everything or failed to read. 405 amount_data_read_ += result; 406 response_reader_->ReadData( 407 data_buffer_.get(), 408 kIOBufferSize, 409 base::Bind(&CheckResponseHelper::OnReadDataComplete, 410 base::Unretained(this))); 411 return; 412 } 413 414 AppCacheHistograms::CheckResponseResultType check_result; 415 if (result < 0) 416 check_result = AppCacheHistograms::READ_DATA_ERROR; 417 else if (info_buffer_->response_data_size != amount_data_read_ || 418 expected_total_size_ != amount_data_read_ + amount_headers_read_) 419 check_result = AppCacheHistograms::UNEXPECTED_DATA_SIZE; 420 else 421 check_result = AppCacheHistograms::RESPONSE_OK; 422 AppCacheHistograms::CountCheckResponseResult(check_result); 423 424 if (check_result != AppCacheHistograms::RESPONSE_OK) 425 service_->DeleteAppCacheGroup(manifest_url_, net::CompletionCallback()); 426 delete this; 427 } 428 429 430 // AppCacheStorageReference ------ 431 432 AppCacheStorageReference::AppCacheStorageReference( 433 scoped_ptr<AppCacheStorage> storage) 434 : storage_(storage.Pass()) {} 435 AppCacheStorageReference::~AppCacheStorageReference() {} 436 437 // AppCacheService ------- 438 439 AppCacheService::AppCacheService(quota::QuotaManagerProxy* quota_manager_proxy) 440 : appcache_policy_(NULL), quota_client_(NULL), handler_factory_(NULL), 441 quota_manager_proxy_(quota_manager_proxy), 442 request_context_(NULL), 443 force_keep_session_state_(false), 444 was_reinitialized_(false) { 445 if (quota_manager_proxy_.get()) { 446 quota_client_ = new AppCacheQuotaClient(this); 447 quota_manager_proxy_->RegisterClient(quota_client_); 448 } 449 } 450 451 AppCacheService::~AppCacheService() { 452 DCHECK(backends_.empty()); 453 std::for_each(pending_helpers_.begin(), 454 pending_helpers_.end(), 455 std::mem_fun(&AsyncHelper::Cancel)); 456 STLDeleteElements(&pending_helpers_); 457 if (quota_client_) 458 quota_client_->NotifyAppCacheDestroyed(); 459 460 // Destroy storage_ first; ~AppCacheStorageImpl accesses other data members 461 // (special_storage_policy_). 462 storage_.reset(); 463 } 464 465 void AppCacheService::Initialize(const base::FilePath& cache_directory, 466 base::MessageLoopProxy* db_thread, 467 base::MessageLoopProxy* cache_thread) { 468 DCHECK(!storage_.get()); 469 cache_directory_ = cache_directory; 470 db_thread_ = db_thread; 471 cache_thread_ = cache_thread; 472 AppCacheStorageImpl* storage = new AppCacheStorageImpl(this); 473 storage->Initialize(cache_directory, db_thread, cache_thread); 474 storage_.reset(storage); 475 } 476 477 void AppCacheService::Reinitialize() { 478 AppCacheHistograms::CountReinitAttempt(was_reinitialized_); 479 480 // To avoid thrashing, we only do this once. 481 if (was_reinitialized_) 482 return; 483 was_reinitialized_ = true; 484 485 // Inform observers of about this and give them a chance to 486 // defer deletion of the old storage object. 487 scoped_refptr<AppCacheStorageReference> 488 old_storage_ref(new AppCacheStorageReference(storage_.Pass())); 489 FOR_EACH_OBSERVER(Observer, observers_, 490 OnServiceReinitialized(old_storage_ref.get())); 491 492 Initialize(cache_directory_, db_thread_, cache_thread_); 493 } 494 495 void AppCacheService::CanHandleMainResourceOffline( 496 const GURL& url, 497 const GURL& first_party, 498 const net::CompletionCallback& callback) { 499 CanHandleOfflineHelper* helper = 500 new CanHandleOfflineHelper(this, url, first_party, callback); 501 helper->Start(); 502 } 503 504 void AppCacheService::GetAllAppCacheInfo( 505 AppCacheInfoCollection* collection, 506 const net::CompletionCallback& callback) { 507 DCHECK(collection); 508 GetInfoHelper* helper = new GetInfoHelper(this, collection, callback); 509 helper->Start(); 510 } 511 512 void AppCacheService::DeleteAppCacheGroup( 513 const GURL& manifest_url, 514 const net::CompletionCallback& callback) { 515 DeleteHelper* helper = new DeleteHelper(this, manifest_url, callback); 516 helper->Start(); 517 } 518 519 void AppCacheService::DeleteAppCachesForOrigin( 520 const GURL& origin, const net::CompletionCallback& callback) { 521 DeleteOriginHelper* helper = new DeleteOriginHelper(this, origin, callback); 522 helper->Start(); 523 } 524 525 void AppCacheService::CheckAppCacheResponse(const GURL& manifest_url, 526 int64 cache_id, 527 int64 response_id) { 528 CheckResponseHelper* helper = new CheckResponseHelper( 529 this, manifest_url, cache_id, response_id); 530 helper->Start(); 531 } 532 533 void AppCacheService::set_special_storage_policy( 534 quota::SpecialStoragePolicy* policy) { 535 special_storage_policy_ = policy; 536 } 537 538 void AppCacheService::RegisterBackend( 539 AppCacheBackendImpl* backend_impl) { 540 DCHECK(backends_.find(backend_impl->process_id()) == backends_.end()); 541 backends_.insert( 542 BackendMap::value_type(backend_impl->process_id(), backend_impl)); 543 } 544 545 void AppCacheService::UnregisterBackend( 546 AppCacheBackendImpl* backend_impl) { 547 backends_.erase(backend_impl->process_id()); 548 } 549 550 } // namespace appcache 551