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