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.
5 #include "webkit/browser/appcache/appcache_group.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"
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
{
27 explicit HostObserver(AppCacheGroup
* group
) : group_(group
) {}
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
);
35 AppCacheGroup
* group_
;
38 AppCacheGroup::AppCacheGroup(AppCacheStorage
* storage
,
39 const GURL
& manifest_url
,
41 : group_id_(group_id
),
42 manifest_url_(manifest_url
),
45 is_being_deleted_(false),
46 newest_complete_cache_(NULL
),
50 storage_
->working_set()->AddGroup(this);
51 host_observer_
.reset(new HostObserver(this));
54 AppCacheGroup::~AppCacheGroup() {
55 DCHECK(old_caches_
.empty());
56 DCHECK(!newest_complete_cache_
);
57 DCHECK(restart_update_task_
.IsCancelled());
58 DCHECK(queued_updates_
.empty());
64 DCHECK_EQ(IDLE
, update_status_
);
66 storage_
->working_set()->RemoveGroup(this);
67 storage_
->DeleteResponses(manifest_url_
, newly_deletable_response_ids_
);
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
);
77 observers_
.AddObserver(observer
);
80 void AppCacheGroup::RemoveUpdateObserver(UpdateObserver
* observer
) {
81 observers_
.RemoveObserver(observer
);
82 queued_observers_
.RemoveObserver(observer
);
85 void AppCacheGroup::AddCache(AppCache
* complete_cache
) {
86 DCHECK(complete_cache
->is_complete());
87 complete_cache
->set_owning_group(this);
89 if (!newest_complete_cache_
) {
90 newest_complete_cache_
= complete_cache
;
94 if (complete_cache
->IsNewerThan(newest_complete_cache_
)) {
95 old_caches_
.push_back(newest_complete_cache_
);
96 newest_complete_cache_
= complete_cache
;
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);
108 old_caches_
.push_back(complete_cache
);
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
119 scoped_refptr
<AppCacheGroup
> protect(this);
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
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();
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();
145 if (newly_deletable_response_ids_
.empty()) {
146 newly_deletable_response_ids_
.swap(*response_ids
);
149 newly_deletable_response_ids_
.insert(
150 newly_deletable_response_ids_
.end(),
151 response_ids
->begin(), response_ids
->end());
152 response_ids
->clear();
155 void AppCacheGroup::StartUpdateWithNewMasterEntry(
156 AppCacheHost
* host
, const GURL
& new_master_resource
) {
157 DCHECK(!is_obsolete() && !is_being_deleted());
162 update_job_
= new AppCacheUpdateJob(storage_
->service(), this);
164 update_job_
->StartUpdate(host
, new_master_resource
);
166 // Run queued update immediately as an update has been started manually.
167 if (!restart_update_task_
.IsCancelled()) {
168 restart_update_task_
.Cancel();
173 void AppCacheGroup::CancelUpdate() {
176 DCHECK(!update_job_
);
177 DCHECK_EQ(IDLE
, update_status_
);
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
));
186 // Need to know when host is destroyed.
187 host
->AddObserver(host_observer_
.get());
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
);
197 void AppCacheGroup::RunQueuedUpdates() {
198 if (!restart_update_task_
.IsCancelled())
199 restart_update_task_
.Cancel();
201 if (queued_updates_
.empty())
204 QueuedUpdates updates_to_run
;
205 queued_updates_
.swap(updates_to_run
);
206 DCHECK(queued_updates_
.empty());
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
);
217 if (!is_obsolete() && !is_being_deleted())
218 StartUpdateWithNewMasterEntry(host
, it
->second
);
222 bool AppCacheGroup::FindObserver(UpdateObserver
* find_me
,
223 const ObserverList
<UpdateObserver
>& observer_list
) {
224 return observer_list
.HasObserver(find_me
);
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(
233 restart_update_task_
.callback(),
234 base::TimeDelta::FromMilliseconds(delay_ms
));
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();
243 void AppCacheGroup::SetUpdateStatus(UpdateStatus status
) {
244 if (status
== update_status_
)
247 update_status_
= status
;
249 if (status
!= IDLE
) {
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
);
264 } // namespace appcache