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 // AppCacheService ------- 431 432 AppCacheService::AppCacheService(quota::QuotaManagerProxy* quota_manager_proxy) 433 : appcache_policy_(NULL), quota_client_(NULL), handler_factory_(NULL), 434 quota_manager_proxy_(quota_manager_proxy), 435 request_context_(NULL), 436 force_keep_session_state_(false) { 437 if (quota_manager_proxy_.get()) { 438 quota_client_ = new AppCacheQuotaClient(this); 439 quota_manager_proxy_->RegisterClient(quota_client_); 440 } 441 } 442 443 AppCacheService::~AppCacheService() { 444 DCHECK(backends_.empty()); 445 std::for_each(pending_helpers_.begin(), 446 pending_helpers_.end(), 447 std::mem_fun(&AsyncHelper::Cancel)); 448 STLDeleteElements(&pending_helpers_); 449 if (quota_client_) 450 quota_client_->NotifyAppCacheDestroyed(); 451 452 // Destroy storage_ first; ~AppCacheStorageImpl accesses other data members 453 // (special_storage_policy_). 454 storage_.reset(); 455 } 456 457 void AppCacheService::Initialize(const base::FilePath& cache_directory, 458 base::MessageLoopProxy* db_thread, 459 base::MessageLoopProxy* cache_thread) { 460 DCHECK(!storage_.get()); 461 AppCacheStorageImpl* storage = new AppCacheStorageImpl(this); 462 storage->Initialize(cache_directory, db_thread, cache_thread); 463 storage_.reset(storage); 464 } 465 466 void AppCacheService::CanHandleMainResourceOffline( 467 const GURL& url, 468 const GURL& first_party, 469 const net::CompletionCallback& callback) { 470 CanHandleOfflineHelper* helper = 471 new CanHandleOfflineHelper(this, url, first_party, callback); 472 helper->Start(); 473 } 474 475 void AppCacheService::GetAllAppCacheInfo( 476 AppCacheInfoCollection* collection, 477 const net::CompletionCallback& callback) { 478 DCHECK(collection); 479 GetInfoHelper* helper = new GetInfoHelper(this, collection, callback); 480 helper->Start(); 481 } 482 483 void AppCacheService::DeleteAppCacheGroup( 484 const GURL& manifest_url, 485 const net::CompletionCallback& callback) { 486 DeleteHelper* helper = new DeleteHelper(this, manifest_url, callback); 487 helper->Start(); 488 } 489 490 void AppCacheService::DeleteAppCachesForOrigin( 491 const GURL& origin, const net::CompletionCallback& callback) { 492 DeleteOriginHelper* helper = new DeleteOriginHelper(this, origin, callback); 493 helper->Start(); 494 } 495 496 void AppCacheService::CheckAppCacheResponse(const GURL& manifest_url, 497 int64 cache_id, 498 int64 response_id) { 499 CheckResponseHelper* helper = new CheckResponseHelper( 500 this, manifest_url, cache_id, response_id); 501 helper->Start(); 502 } 503 504 void AppCacheService::set_special_storage_policy( 505 quota::SpecialStoragePolicy* policy) { 506 special_storage_policy_ = policy; 507 } 508 509 void AppCacheService::RegisterBackend( 510 AppCacheBackendImpl* backend_impl) { 511 DCHECK(backends_.find(backend_impl->process_id()) == backends_.end()); 512 backends_.insert( 513 BackendMap::value_type(backend_impl->process_id(), backend_impl)); 514 } 515 516 void AppCacheService::UnregisterBackend( 517 AppCacheBackendImpl* backend_impl) { 518 backends_.erase(backend_impl->process_id()); 519 } 520 521 } // namespace appcache 522