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