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 "content/browser/appcache/appcache_group.h"
10 #include "base/bind.h"
11 #include "base/location.h"
12 #include "base/logging.h"
13 #include "base/single_thread_task_runner.h"
14 #include "base/thread_task_runner_handle.h"
15 #include "content/browser/appcache/appcache.h"
16 #include "content/browser/appcache/appcache_host.h"
17 #include "content/browser/appcache/appcache_service_impl.h"
18 #include "content/browser/appcache/appcache_storage.h"
19 #include "content/browser/appcache/appcache_update_job.h"
25 // Use this helper class because we cannot make AppCacheGroup a derived class
26 // of AppCacheHost::Observer as it would create a circular dependency between
27 // AppCacheHost and AppCacheGroup.
28 class AppCacheGroup::HostObserver
: public AppCacheHost::Observer
{
30 explicit HostObserver(AppCacheGroup
* group
) : group_(group
) {}
32 // Methods for AppCacheHost::Observer.
33 void OnCacheSelectionComplete(AppCacheHost
* host
) override
{} // N/A
34 void OnDestructionImminent(AppCacheHost
* host
) override
{
35 group_
->HostDestructionImminent(host
);
38 AppCacheGroup
* group_
;
41 AppCacheGroup::AppCacheGroup(AppCacheStorage
* storage
,
42 const GURL
& manifest_url
,
44 : group_id_(group_id
),
45 manifest_url_(manifest_url
),
48 is_being_deleted_(false),
49 newest_complete_cache_(NULL
),
53 storage_
->working_set()->AddGroup(this);
54 host_observer_
.reset(new HostObserver(this));
57 AppCacheGroup::~AppCacheGroup() {
58 DCHECK(old_caches_
.empty());
59 DCHECK(!newest_complete_cache_
);
60 DCHECK(restart_update_task_
.IsCancelled());
61 DCHECK(queued_updates_
.empty());
67 DCHECK_EQ(IDLE
, update_status_
);
69 storage_
->working_set()->RemoveGroup(this);
70 storage_
->DeleteResponses(manifest_url_
, newly_deletable_response_ids_
);
73 void AppCacheGroup::AddUpdateObserver(UpdateObserver
* observer
) {
74 // If observer being added is a host that has been queued for later update,
75 // add observer to a different observer list.
76 if (queued_updates_
.find(observer
) != queued_updates_
.end())
77 queued_observers_
.AddObserver(observer
);
79 observers_
.AddObserver(observer
);
82 void AppCacheGroup::RemoveUpdateObserver(UpdateObserver
* observer
) {
83 observers_
.RemoveObserver(observer
);
84 queued_observers_
.RemoveObserver(observer
);
87 void AppCacheGroup::AddCache(AppCache
* complete_cache
) {
88 DCHECK(complete_cache
->is_complete());
89 complete_cache
->set_owning_group(this);
91 if (!newest_complete_cache_
) {
92 newest_complete_cache_
= complete_cache
;
96 if (complete_cache
->IsNewerThan(newest_complete_cache_
)) {
97 old_caches_
.push_back(newest_complete_cache_
);
98 newest_complete_cache_
= complete_cache
;
100 // Update hosts of older caches to add a reference to the newest cache.
101 for (Caches::iterator it
= old_caches_
.begin();
102 it
!= old_caches_
.end(); ++it
) {
103 AppCache::AppCacheHosts
& hosts
= (*it
)->associated_hosts();
104 for (AppCache::AppCacheHosts::iterator host_it
= hosts
.begin();
105 host_it
!= hosts
.end(); ++host_it
) {
106 (*host_it
)->SetSwappableCache(this);
110 old_caches_
.push_back(complete_cache
);
114 void AppCacheGroup::RemoveCache(AppCache
* cache
) {
115 DCHECK(cache
->associated_hosts().empty());
116 if (cache
== newest_complete_cache_
) {
117 AppCache
* tmp_cache
= newest_complete_cache_
;
118 newest_complete_cache_
= NULL
;
119 tmp_cache
->set_owning_group(NULL
); // may cause this group to be deleted
121 scoped_refptr
<AppCacheGroup
> protect(this);
123 Caches::iterator it
=
124 std::find(old_caches_
.begin(), old_caches_
.end(), cache
);
125 if (it
!= old_caches_
.end()) {
126 AppCache
* tmp_cache
= *it
;
127 old_caches_
.erase(it
);
128 tmp_cache
->set_owning_group(NULL
); // may cause group to be released
131 if (!is_obsolete() && old_caches_
.empty() &&
132 !newly_deletable_response_ids_
.empty()) {
133 storage_
->DeleteResponses(manifest_url_
, newly_deletable_response_ids_
);
134 newly_deletable_response_ids_
.clear();
139 void AppCacheGroup::AddNewlyDeletableResponseIds(
140 std::vector
<int64
>* response_ids
) {
141 if (is_being_deleted() || (!is_obsolete() && old_caches_
.empty())) {
142 storage_
->DeleteResponses(manifest_url_
, *response_ids
);
143 response_ids
->clear();
147 if (newly_deletable_response_ids_
.empty()) {
148 newly_deletable_response_ids_
.swap(*response_ids
);
151 newly_deletable_response_ids_
.insert(
152 newly_deletable_response_ids_
.end(),
153 response_ids
->begin(), response_ids
->end());
154 response_ids
->clear();
157 void AppCacheGroup::StartUpdateWithNewMasterEntry(
158 AppCacheHost
* host
, const GURL
& new_master_resource
) {
159 DCHECK(!is_obsolete() && !is_being_deleted());
164 update_job_
= new AppCacheUpdateJob(storage_
->service(), this);
166 update_job_
->StartUpdate(host
, new_master_resource
);
168 // Run queued update immediately as an update has been started manually.
169 if (!restart_update_task_
.IsCancelled()) {
170 restart_update_task_
.Cancel();
175 void AppCacheGroup::CancelUpdate() {
178 DCHECK(!update_job_
);
179 DCHECK_EQ(IDLE
, update_status_
);
183 void AppCacheGroup::QueueUpdate(AppCacheHost
* host
,
184 const GURL
& new_master_resource
) {
185 DCHECK(update_job_
&& host
&& !new_master_resource
.is_empty());
186 queued_updates_
.insert(QueuedUpdates::value_type(
187 host
, std::make_pair(host
, new_master_resource
)));
189 // Need to know when host is destroyed.
190 host
->AddObserver(host_observer_
.get());
192 // If host is already observing for updates, move host to queued observers
193 // list so that host is not notified when the current update completes.
194 if (FindObserver(host
, observers_
)) {
195 observers_
.RemoveObserver(host
);
196 queued_observers_
.AddObserver(host
);
200 void AppCacheGroup::RunQueuedUpdates() {
201 if (!restart_update_task_
.IsCancelled())
202 restart_update_task_
.Cancel();
204 if (queued_updates_
.empty())
207 QueuedUpdates updates_to_run
;
208 queued_updates_
.swap(updates_to_run
);
209 DCHECK(queued_updates_
.empty());
211 for (QueuedUpdates::iterator it
= updates_to_run
.begin();
212 it
!= updates_to_run
.end(); ++it
) {
213 AppCacheHost
* host
= it
->second
.first
;
214 host
->RemoveObserver(host_observer_
.get());
215 if (FindObserver(host
, queued_observers_
)) {
216 queued_observers_
.RemoveObserver(host
);
217 observers_
.AddObserver(host
);
220 if (!is_obsolete() && !is_being_deleted())
221 StartUpdateWithNewMasterEntry(host
, it
->second
.second
);
226 bool AppCacheGroup::FindObserver(
227 const UpdateObserver
* find_me
,
228 const base::ObserverList
<UpdateObserver
>& observer_list
) {
229 return observer_list
.HasObserver(find_me
);
232 void AppCacheGroup::ScheduleUpdateRestart(int delay_ms
) {
233 DCHECK(restart_update_task_
.IsCancelled());
234 restart_update_task_
.Reset(
235 base::Bind(&AppCacheGroup::RunQueuedUpdates
, this));
236 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
237 FROM_HERE
, restart_update_task_
.callback(),
238 base::TimeDelta::FromMilliseconds(delay_ms
));
241 void AppCacheGroup::HostDestructionImminent(AppCacheHost
* host
) {
242 queued_updates_
.erase(host
);
243 if (queued_updates_
.empty() && !restart_update_task_
.IsCancelled())
244 restart_update_task_
.Cancel();
247 void AppCacheGroup::SetUpdateAppCacheStatus(UpdateAppCacheStatus status
) {
248 if (status
== update_status_
)
251 update_status_
= status
;
253 if (status
!= IDLE
) {
258 // Observers may release us in these callbacks, so we protect against
259 // deletion by adding an extra ref in this scope (but only if we're not
260 // in our destructor).
261 scoped_refptr
<AppCacheGroup
> protect(is_in_dtor_
? NULL
: this);
262 FOR_EACH_OBSERVER(UpdateObserver
, observers_
, OnUpdateComplete(this));
263 if (!queued_updates_
.empty())
264 ScheduleUpdateRestart(kUpdateRestartDelayMs
);
268 } // namespace content