• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
DeferredCallback(const net::CompletionCallback & callback,int rv)32 void DeferredCallback(const net::CompletionCallback& callback, int rv) {
33   callback.Run(rv);
34 }
35 
36 }  // namespace
37 
AppCacheInfoCollection()38 AppCacheInfoCollection::AppCacheInfoCollection() {}
39 
~AppCacheInfoCollection()40 AppCacheInfoCollection::~AppCacheInfoCollection() {}
41 
42 // AsyncHelper -------
43 
44 class AppCacheService::AsyncHelper
45     : public AppCacheStorage::Delegate {
46  public:
AsyncHelper(AppCacheService * service,const net::CompletionCallback & callback)47   AsyncHelper(AppCacheService* service,
48               const net::CompletionCallback& callback)
49       : service_(service), callback_(callback) {
50     service_->pending_helpers_.insert(this);
51   }
52 
~AsyncHelper()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:
CallCallback(int rv)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 
Cancel()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:
CanHandleOfflineHelper(AppCacheService * service,const GURL & url,const GURL & first_party,const net::CompletionCallback & callback)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 
Start()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 
OnMainResponseFound(const GURL & url,const AppCacheEntry & entry,const GURL & fallback_url,const AppCacheEntry & fallback_entry,int64 cache_id,int64 group_id,const GURL & manifest_url)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:
DeleteHelper(AppCacheService * service,const GURL & manifest_url,const net::CompletionCallback & callback)133   DeleteHelper(
134       AppCacheService* service, const GURL& manifest_url,
135       const net::CompletionCallback& callback)
136       : AsyncHelper(service, callback), manifest_url_(manifest_url) {
137   }
138 
Start()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 
OnGroupLoaded(appcache::AppCacheGroup * group,const GURL & manifest_url)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 
OnGroupMadeObsolete(appcache::AppCacheGroup * group,bool success)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:
DeleteOriginHelper(AppCacheService * service,const GURL & origin,const net::CompletionCallback & callback)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 
Start()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 
OnAllInfo(AppCacheInfoCollection * collection)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 
OnGroupLoaded(appcache::AppCacheGroup * group,const GURL & manifest_url)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 
OnGroupMadeObsolete(appcache::AppCacheGroup * group,bool success)246 void AppCacheService::DeleteOriginHelper::OnGroupMadeObsolete(
247       appcache::AppCacheGroup* group, bool success) {
248   CacheCompleted(success);
249 }
250 
CacheCompleted(bool success)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:
GetInfoHelper(AppCacheService * service,AppCacheInfoCollection * collection,const net::CompletionCallback & callback)268   GetInfoHelper(
269       AppCacheService* service, AppCacheInfoCollection* collection,
270       const net::CompletionCallback& callback)
271       : AsyncHelper(service, callback), collection_(collection) {
272   }
273 
Start()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 
OnAllInfo(AppCacheInfoCollection * collection)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:
CheckResponseHelper(AppCacheService * service,const GURL & manifest_url,int64 cache_id,int64 response_id)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 
Start()312   virtual void Start() OVERRIDE {
313     service_->storage()->LoadOrCreateGroup(manifest_url_, this);
314   }
315 
Cancel()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 
OnGroupLoaded(AppCacheGroup * group,const GURL & manifest_url)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 
OnReadInfoComplete(int result)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 
OnReadDataComplete(int result)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 
AppCacheStorageReference(scoped_ptr<AppCacheStorage> storage)432 AppCacheStorageReference::AppCacheStorageReference(
433     scoped_ptr<AppCacheStorage> storage)
434     : storage_(storage.Pass()) {}
~AppCacheStorageReference()435 AppCacheStorageReference::~AppCacheStorageReference() {}
436 
437 // AppCacheService -------
438 
AppCacheService(quota::QuotaManagerProxy * quota_manager_proxy)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 
~AppCacheService()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 
Initialize(const base::FilePath & cache_directory,base::MessageLoopProxy * db_thread,base::MessageLoopProxy * cache_thread)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 
Reinitialize()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 
CanHandleMainResourceOffline(const GURL & url,const GURL & first_party,const net::CompletionCallback & callback)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 
GetAllAppCacheInfo(AppCacheInfoCollection * collection,const net::CompletionCallback & callback)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 
DeleteAppCacheGroup(const GURL & manifest_url,const net::CompletionCallback & callback)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 
DeleteAppCachesForOrigin(const GURL & origin,const net::CompletionCallback & callback)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 
CheckAppCacheResponse(const GURL & manifest_url,int64 cache_id,int64 response_id)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 
set_special_storage_policy(quota::SpecialStoragePolicy * policy)533 void AppCacheService::set_special_storage_policy(
534     quota::SpecialStoragePolicy* policy) {
535   special_storage_policy_ = policy;
536 }
537 
RegisterBackend(AppCacheBackendImpl * backend_impl)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 
UnregisterBackend(AppCacheBackendImpl * backend_impl)545 void AppCacheService::UnregisterBackend(
546     AppCacheBackendImpl* backend_impl) {
547   backends_.erase(backend_impl->process_id());
548 }
549 
550 }  // namespace appcache
551