Home | History | Annotate | Download | only in appcache
      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_group.h"
      6 
      7 #include <algorithm>
      8 
      9 #include "base/bind.h"
     10 #include "base/logging.h"
     11 #include "base/message_loop/message_loop.h"
     12 #include "webkit/browser/appcache/appcache.h"
     13 #include "webkit/browser/appcache/appcache_host.h"
     14 #include "webkit/browser/appcache/appcache_service.h"
     15 #include "webkit/browser/appcache/appcache_storage.h"
     16 #include "webkit/browser/appcache/appcache_update_job.h"
     17 
     18 namespace appcache {
     19 
     20 class AppCacheGroup;
     21 
     22 // Use this helper class because we cannot make AppCacheGroup a derived class
     23 // of AppCacheHost::Observer as it would create a circular dependency between
     24 // AppCacheHost and AppCacheGroup.
     25 class AppCacheGroup::HostObserver : public AppCacheHost::Observer {
     26  public:
     27   explicit HostObserver(AppCacheGroup* group) : group_(group) {}
     28 
     29   // Methods for AppCacheHost::Observer.
     30   virtual void OnCacheSelectionComplete(AppCacheHost* host) OVERRIDE {}  // N/A
     31   virtual void OnDestructionImminent(AppCacheHost* host) OVERRIDE {
     32     group_->HostDestructionImminent(host);
     33   }
     34  private:
     35   AppCacheGroup* group_;
     36 };
     37 
     38 AppCacheGroup::AppCacheGroup(AppCacheStorage* storage,
     39                              const GURL& manifest_url,
     40                              int64 group_id)
     41     : group_id_(group_id),
     42       manifest_url_(manifest_url),
     43       update_status_(IDLE),
     44       is_obsolete_(false),
     45       is_being_deleted_(false),
     46       newest_complete_cache_(NULL),
     47       update_job_(NULL),
     48       storage_(storage),
     49       is_in_dtor_(false) {
     50   storage_->working_set()->AddGroup(this);
     51   host_observer_.reset(new HostObserver(this));
     52 }
     53 
     54 AppCacheGroup::~AppCacheGroup() {
     55   DCHECK(old_caches_.empty());
     56   DCHECK(!newest_complete_cache_);
     57   DCHECK(restart_update_task_.IsCancelled());
     58   DCHECK(queued_updates_.empty());
     59 
     60   is_in_dtor_ = true;
     61 
     62   if (update_job_)
     63     delete update_job_;
     64   DCHECK_EQ(IDLE, update_status_);
     65 
     66   storage_->working_set()->RemoveGroup(this);
     67   storage_->DeleteResponses(manifest_url_, newly_deletable_response_ids_);
     68 }
     69 
     70 void AppCacheGroup::AddUpdateObserver(UpdateObserver* observer) {
     71   // If observer being added is a host that has been queued for later update,
     72   // add observer to a different observer list.
     73   AppCacheHost* host = static_cast<AppCacheHost*>(observer);
     74   if (queued_updates_.find(host) != queued_updates_.end())
     75     queued_observers_.AddObserver(observer);
     76   else
     77     observers_.AddObserver(observer);
     78 }
     79 
     80 void AppCacheGroup::RemoveUpdateObserver(UpdateObserver* observer) {
     81   observers_.RemoveObserver(observer);
     82   queued_observers_.RemoveObserver(observer);
     83 }
     84 
     85 void AppCacheGroup::AddCache(AppCache* complete_cache) {
     86   DCHECK(complete_cache->is_complete());
     87   complete_cache->set_owning_group(this);
     88 
     89   if (!newest_complete_cache_) {
     90     newest_complete_cache_ = complete_cache;
     91     return;
     92   }
     93 
     94   if (complete_cache->IsNewerThan(newest_complete_cache_)) {
     95     old_caches_.push_back(newest_complete_cache_);
     96     newest_complete_cache_ = complete_cache;
     97 
     98     // Update hosts of older caches to add a reference to the newest cache.
     99     for (Caches::iterator it = old_caches_.begin();
    100          it != old_caches_.end(); ++it) {
    101       AppCache::AppCacheHosts& hosts = (*it)->associated_hosts();
    102       for (AppCache::AppCacheHosts::iterator host_it = hosts.begin();
    103            host_it != hosts.end(); ++host_it) {
    104         (*host_it)->SetSwappableCache(this);
    105       }
    106     }
    107   } else {
    108     old_caches_.push_back(complete_cache);
    109   }
    110 }
    111 
    112 void AppCacheGroup::RemoveCache(AppCache* cache) {
    113   DCHECK(cache->associated_hosts().empty());
    114   if (cache == newest_complete_cache_) {
    115     AppCache* tmp_cache = newest_complete_cache_;
    116     newest_complete_cache_ = NULL;
    117     tmp_cache->set_owning_group(NULL);  // may cause this group to be deleted
    118   } else {
    119     scoped_refptr<AppCacheGroup> protect(this);
    120 
    121     Caches::iterator it =
    122         std::find(old_caches_.begin(), old_caches_.end(), cache);
    123     if (it != old_caches_.end()) {
    124       AppCache* tmp_cache = *it;
    125       old_caches_.erase(it);
    126       tmp_cache->set_owning_group(NULL);  // may cause group to be released
    127     }
    128 
    129     if (!is_obsolete() && old_caches_.empty() &&
    130         !newly_deletable_response_ids_.empty()) {
    131       storage_->DeleteResponses(manifest_url_, newly_deletable_response_ids_);
    132       newly_deletable_response_ids_.clear();
    133     }
    134   }
    135 }
    136 
    137 void AppCacheGroup::AddNewlyDeletableResponseIds(
    138     std::vector<int64>* response_ids) {
    139   if (is_being_deleted() || (!is_obsolete() && old_caches_.empty())) {
    140     storage_->DeleteResponses(manifest_url_, *response_ids);
    141     response_ids->clear();
    142     return;
    143   }
    144 
    145   if (newly_deletable_response_ids_.empty()) {
    146     newly_deletable_response_ids_.swap(*response_ids);
    147     return;
    148   }
    149   newly_deletable_response_ids_.insert(
    150       newly_deletable_response_ids_.end(),
    151       response_ids->begin(), response_ids->end());
    152   response_ids->clear();
    153 }
    154 
    155 void AppCacheGroup::StartUpdateWithNewMasterEntry(
    156     AppCacheHost* host, const GURL& new_master_resource) {
    157   DCHECK(!is_obsolete() && !is_being_deleted());
    158   if (is_in_dtor_)
    159     return;
    160 
    161   if (!update_job_)
    162     update_job_ = new AppCacheUpdateJob(storage_->service(), this);
    163 
    164   update_job_->StartUpdate(host, new_master_resource);
    165 
    166   // Run queued update immediately as an update has been started manually.
    167   if (!restart_update_task_.IsCancelled()) {
    168     restart_update_task_.Cancel();
    169     RunQueuedUpdates();
    170   }
    171 }
    172 
    173 void AppCacheGroup::CancelUpdate() {
    174   if (update_job_) {
    175     delete update_job_;
    176     DCHECK(!update_job_);
    177     DCHECK_EQ(IDLE, update_status_);
    178   }
    179 }
    180 
    181 void AppCacheGroup::QueueUpdate(AppCacheHost* host,
    182                                 const GURL& new_master_resource) {
    183   DCHECK(update_job_ && host && !new_master_resource.is_empty());
    184   queued_updates_.insert(QueuedUpdates::value_type(host, new_master_resource));
    185 
    186   // Need to know when host is destroyed.
    187   host->AddObserver(host_observer_.get());
    188 
    189   // If host is already observing for updates, move host to queued observers
    190   // list so that host is not notified when the current update completes.
    191   if (FindObserver(host, observers_)) {
    192     observers_.RemoveObserver(host);
    193     queued_observers_.AddObserver(host);
    194   }
    195 }
    196 
    197 void AppCacheGroup::RunQueuedUpdates() {
    198   if (!restart_update_task_.IsCancelled())
    199     restart_update_task_.Cancel();
    200 
    201   if (queued_updates_.empty())
    202     return;
    203 
    204   QueuedUpdates updates_to_run;
    205   queued_updates_.swap(updates_to_run);
    206   DCHECK(queued_updates_.empty());
    207 
    208   for (QueuedUpdates::iterator it = updates_to_run.begin();
    209        it != updates_to_run.end(); ++it) {
    210     AppCacheHost* host = it->first;
    211     host->RemoveObserver(host_observer_.get());
    212     if (FindObserver(host, queued_observers_)) {
    213       queued_observers_.RemoveObserver(host);
    214       observers_.AddObserver(host);
    215     }
    216 
    217     if (!is_obsolete() && !is_being_deleted())
    218       StartUpdateWithNewMasterEntry(host, it->second);
    219   }
    220 }
    221 
    222 bool AppCacheGroup::FindObserver(UpdateObserver* find_me,
    223     const ObserverList<UpdateObserver>& observer_list) {
    224   return observer_list.HasObserver(find_me);
    225 }
    226 
    227 void AppCacheGroup::ScheduleUpdateRestart(int delay_ms) {
    228   DCHECK(restart_update_task_.IsCancelled());
    229   restart_update_task_.Reset(
    230       base::Bind(&AppCacheGroup::RunQueuedUpdates, this));
    231   base::MessageLoop::current()->PostDelayedTask(
    232       FROM_HERE,
    233       restart_update_task_.callback(),
    234       base::TimeDelta::FromMilliseconds(delay_ms));
    235 }
    236 
    237 void AppCacheGroup::HostDestructionImminent(AppCacheHost* host) {
    238   queued_updates_.erase(host);
    239   if (queued_updates_.empty() && !restart_update_task_.IsCancelled())
    240     restart_update_task_.Cancel();
    241 }
    242 
    243 void AppCacheGroup::SetUpdateStatus(UpdateStatus status) {
    244   if (status == update_status_)
    245     return;
    246 
    247   update_status_ = status;
    248 
    249   if (status != IDLE) {
    250     DCHECK(update_job_);
    251   } else {
    252     update_job_ = NULL;
    253 
    254     // Observers may release us in these callbacks, so we protect against
    255     // deletion by adding an extra ref in this scope (but only if we're not
    256     // in our destructor).
    257     scoped_refptr<AppCacheGroup> protect(is_in_dtor_ ? NULL : this);
    258     FOR_EACH_OBSERVER(UpdateObserver, observers_, OnUpdateComplete(this));
    259     if (!queued_updates_.empty())
    260       ScheduleUpdateRestart(kUpdateRestartDelayMs);
    261   }
    262 }
    263 
    264 }  // namespace appcache
    265