diff options
Diffstat (limited to 'webkit/browser/appcache/appcache_group.cc')
-rw-r--r-- | webkit/browser/appcache/appcache_group.cc | 264 |
1 files changed, 264 insertions, 0 deletions
diff --git a/webkit/browser/appcache/appcache_group.cc b/webkit/browser/appcache/appcache_group.cc new file mode 100644 index 0000000..6e93ea3 --- /dev/null +++ b/webkit/browser/appcache/appcache_group.cc @@ -0,0 +1,264 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "webkit/browser/appcache/appcache_group.h" + +#include <algorithm> + +#include "base/bind.h" +#include "base/logging.h" +#include "base/message_loop.h" +#include "webkit/browser/appcache/appcache.h" +#include "webkit/browser/appcache/appcache_host.h" +#include "webkit/browser/appcache/appcache_service.h" +#include "webkit/browser/appcache/appcache_storage.h" +#include "webkit/browser/appcache/appcache_update_job.h" + +namespace appcache { + +class AppCacheGroup; + +// Use this helper class because we cannot make AppCacheGroup a derived class +// of AppCacheHost::Observer as it would create a circular dependency between +// AppCacheHost and AppCacheGroup. +class AppCacheGroup::HostObserver : public AppCacheHost::Observer { + public: + explicit HostObserver(AppCacheGroup* group) : group_(group) {} + + // Methods for AppCacheHost::Observer. + virtual void OnCacheSelectionComplete(AppCacheHost* host) OVERRIDE {} // N/A + virtual void OnDestructionImminent(AppCacheHost* host) OVERRIDE { + group_->HostDestructionImminent(host); + } + private: + AppCacheGroup* group_; +}; + +AppCacheGroup::AppCacheGroup(AppCacheStorage* storage, + const GURL& manifest_url, + int64 group_id) + : group_id_(group_id), + manifest_url_(manifest_url), + update_status_(IDLE), + is_obsolete_(false), + is_being_deleted_(false), + newest_complete_cache_(NULL), + update_job_(NULL), + storage_(storage), + is_in_dtor_(false) { + storage_->working_set()->AddGroup(this); + host_observer_.reset(new HostObserver(this)); +} + +AppCacheGroup::~AppCacheGroup() { + DCHECK(old_caches_.empty()); + DCHECK(!newest_complete_cache_); + DCHECK(restart_update_task_.IsCancelled()); + DCHECK(queued_updates_.empty()); + + is_in_dtor_ = true; + + if (update_job_) + delete update_job_; + DCHECK_EQ(IDLE, update_status_); + + storage_->working_set()->RemoveGroup(this); + storage_->DeleteResponses(manifest_url_, newly_deletable_response_ids_); +} + +void AppCacheGroup::AddUpdateObserver(UpdateObserver* observer) { + // If observer being added is a host that has been queued for later update, + // add observer to a different observer list. + AppCacheHost* host = static_cast<AppCacheHost*>(observer); + if (queued_updates_.find(host) != queued_updates_.end()) + queued_observers_.AddObserver(observer); + else + observers_.AddObserver(observer); +} + +void AppCacheGroup::RemoveUpdateObserver(UpdateObserver* observer) { + observers_.RemoveObserver(observer); + queued_observers_.RemoveObserver(observer); +} + +void AppCacheGroup::AddCache(AppCache* complete_cache) { + DCHECK(complete_cache->is_complete()); + complete_cache->set_owning_group(this); + + if (!newest_complete_cache_) { + newest_complete_cache_ = complete_cache; + return; + } + + if (complete_cache->IsNewerThan(newest_complete_cache_)) { + old_caches_.push_back(newest_complete_cache_); + newest_complete_cache_ = complete_cache; + + // Update hosts of older caches to add a reference to the newest cache. + for (Caches::iterator it = old_caches_.begin(); + it != old_caches_.end(); ++it) { + AppCache::AppCacheHosts& hosts = (*it)->associated_hosts(); + for (AppCache::AppCacheHosts::iterator host_it = hosts.begin(); + host_it != hosts.end(); ++host_it) { + (*host_it)->SetSwappableCache(this); + } + } + } else { + old_caches_.push_back(complete_cache); + } +} + +void AppCacheGroup::RemoveCache(AppCache* cache) { + DCHECK(cache->associated_hosts().empty()); + if (cache == newest_complete_cache_) { + AppCache* tmp_cache = newest_complete_cache_; + newest_complete_cache_ = NULL; + tmp_cache->set_owning_group(NULL); // may cause this group to be deleted + } else { + scoped_refptr<AppCacheGroup> protect(this); + + Caches::iterator it = + std::find(old_caches_.begin(), old_caches_.end(), cache); + if (it != old_caches_.end()) { + AppCache* tmp_cache = *it; + old_caches_.erase(it); + tmp_cache->set_owning_group(NULL); // may cause group to be released + } + + if (!is_obsolete() && old_caches_.empty() && + !newly_deletable_response_ids_.empty()) { + storage_->DeleteResponses(manifest_url_, newly_deletable_response_ids_); + newly_deletable_response_ids_.clear(); + } + } +} + +void AppCacheGroup::AddNewlyDeletableResponseIds( + std::vector<int64>* response_ids) { + if (is_being_deleted() || (!is_obsolete() && old_caches_.empty())) { + storage_->DeleteResponses(manifest_url_, *response_ids); + response_ids->clear(); + return; + } + + if (newly_deletable_response_ids_.empty()) { + newly_deletable_response_ids_.swap(*response_ids); + return; + } + newly_deletable_response_ids_.insert( + newly_deletable_response_ids_.end(), + response_ids->begin(), response_ids->end()); + response_ids->clear(); +} + +void AppCacheGroup::StartUpdateWithNewMasterEntry( + AppCacheHost* host, const GURL& new_master_resource) { + DCHECK(!is_obsolete() && !is_being_deleted()); + if (is_in_dtor_) + return; + + if (!update_job_) + update_job_ = new AppCacheUpdateJob(storage_->service(), this); + + update_job_->StartUpdate(host, new_master_resource); + + // Run queued update immediately as an update has been started manually. + if (!restart_update_task_.IsCancelled()) { + restart_update_task_.Cancel(); + RunQueuedUpdates(); + } +} + +void AppCacheGroup::CancelUpdate() { + if (update_job_) { + delete update_job_; + DCHECK(!update_job_); + DCHECK_EQ(IDLE, update_status_); + } +} + +void AppCacheGroup::QueueUpdate(AppCacheHost* host, + const GURL& new_master_resource) { + DCHECK(update_job_ && host && !new_master_resource.is_empty()); + queued_updates_.insert(QueuedUpdates::value_type(host, new_master_resource)); + + // Need to know when host is destroyed. + host->AddObserver(host_observer_.get()); + + // If host is already observing for updates, move host to queued observers + // list so that host is not notified when the current update completes. + if (FindObserver(host, observers_)) { + observers_.RemoveObserver(host); + queued_observers_.AddObserver(host); + } +} + +void AppCacheGroup::RunQueuedUpdates() { + if (!restart_update_task_.IsCancelled()) + restart_update_task_.Cancel(); + + if (queued_updates_.empty()) + return; + + QueuedUpdates updates_to_run; + queued_updates_.swap(updates_to_run); + DCHECK(queued_updates_.empty()); + + for (QueuedUpdates::iterator it = updates_to_run.begin(); + it != updates_to_run.end(); ++it) { + AppCacheHost* host = it->first; + host->RemoveObserver(host_observer_.get()); + if (FindObserver(host, queued_observers_)) { + queued_observers_.RemoveObserver(host); + observers_.AddObserver(host); + } + + if (!is_obsolete() && !is_being_deleted()) + StartUpdateWithNewMasterEntry(host, it->second); + } +} + +bool AppCacheGroup::FindObserver(UpdateObserver* find_me, + const ObserverList<UpdateObserver>& observer_list) { + return observer_list.HasObserver(find_me); +} + +void AppCacheGroup::ScheduleUpdateRestart(int delay_ms) { + DCHECK(restart_update_task_.IsCancelled()); + restart_update_task_.Reset( + base::Bind(&AppCacheGroup::RunQueuedUpdates, this)); + base::MessageLoop::current()->PostDelayedTask( + FROM_HERE, + restart_update_task_.callback(), + base::TimeDelta::FromMilliseconds(delay_ms)); +} + +void AppCacheGroup::HostDestructionImminent(AppCacheHost* host) { + queued_updates_.erase(host); + if (queued_updates_.empty() && !restart_update_task_.IsCancelled()) + restart_update_task_.Cancel(); +} + +void AppCacheGroup::SetUpdateStatus(UpdateStatus status) { + if (status == update_status_) + return; + + update_status_ = status; + + if (status != IDLE) { + DCHECK(update_job_); + } else { + update_job_ = NULL; + + // Observers may release us in these callbacks, so we protect against + // deletion by adding an extra ref in this scope (but only if we're not + // in our destructor). + scoped_refptr<AppCacheGroup> protect(is_in_dtor_ ? NULL : this); + FOR_EACH_OBSERVER(UpdateObserver, observers_, OnUpdateComplete(this)); + if (!queued_updates_.empty()) + ScheduleUpdateRestart(kUpdateRestartDelayMs); + } +} + +} // namespace appcache |