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