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/location.h"
11 #include "base/logging.h"
12 #include "base/single_thread_task_runner.h"
13 #include "base/thread_task_runner_handle.h"
14 #include "content/browser/appcache/appcache.h"
15 #include "content/browser/appcache/appcache_host.h"
16 #include "content/browser/appcache/appcache_service_impl.h"
17 #include "content/browser/appcache/appcache_storage.h"
18 #include "content/browser/appcache/appcache_update_job.h"
24 // Use this helper class because we cannot make AppCacheGroup a derived class
25 // of AppCacheHost::Observer as it would create a circular dependency between
26 // AppCacheHost and AppCacheGroup.
27 class AppCacheGroup::HostObserver
: public AppCacheHost::Observer
{
29 explicit HostObserver(AppCacheGroup
* group
) : group_(group
) {}
31 // Methods for AppCacheHost::Observer.
32 void OnCacheSelectionComplete(AppCacheHost
* host
) override
{} // N/A
33 void OnDestructionImminent(AppCacheHost
* host
) override
{
34 group_
->HostDestructionImminent(host
);
37 AppCacheGroup
* group_
;
40 AppCacheGroup::AppCacheGroup(AppCacheStorage
* storage
,
41 const GURL
& manifest_url
,
43 : group_id_(group_id
),
44 manifest_url_(manifest_url
),
47 is_being_deleted_(false),
48 newest_complete_cache_(NULL
),
52 storage_
->working_set()->AddGroup(this);
53 host_observer_
.reset(new HostObserver(this));
56 AppCacheGroup::~AppCacheGroup() {
57 DCHECK(old_caches_
.empty());
58 DCHECK(!newest_complete_cache_
);
59 DCHECK(restart_update_task_
.IsCancelled());
60 DCHECK(queued_updates_
.empty());
66 DCHECK_EQ(IDLE
, update_status_
);
68 storage_
->working_set()->RemoveGroup(this);
69 storage_
->DeleteResponses(manifest_url_
, newly_deletable_response_ids_
);
72 void AppCacheGroup::AddUpdateObserver(UpdateObserver
* observer
) {
73 // If observer being added is a host that has been queued for later update,
74 // add observer to a different observer list.
75 AppCacheHost
* host
= static_cast<AppCacheHost
*>(observer
);
76 if (queued_updates_
.find(host
) != 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(host
, new_master_resource
));
188 // Need to know when host is destroyed.
189 host
->AddObserver(host_observer_
.get());
191 // If host is already observing for updates, move host to queued observers
192 // list so that host is not notified when the current update completes.
193 if (FindObserver(host
, observers_
)) {
194 observers_
.RemoveObserver(host
);
195 queued_observers_
.AddObserver(host
);
199 void AppCacheGroup::RunQueuedUpdates() {
200 if (!restart_update_task_
.IsCancelled())
201 restart_update_task_
.Cancel();
203 if (queued_updates_
.empty())
206 QueuedUpdates updates_to_run
;
207 queued_updates_
.swap(updates_to_run
);
208 DCHECK(queued_updates_
.empty());
210 for (QueuedUpdates::iterator it
= updates_to_run
.begin();
211 it
!= updates_to_run
.end(); ++it
) {
212 AppCacheHost
* host
= it
->first
;
213 host
->RemoveObserver(host_observer_
.get());
214 if (FindObserver(host
, queued_observers_
)) {
215 queued_observers_
.RemoveObserver(host
);
216 observers_
.AddObserver(host
);
219 if (!is_obsolete() && !is_being_deleted())
220 StartUpdateWithNewMasterEntry(host
, it
->second
);
225 bool AppCacheGroup::FindObserver(
226 const UpdateObserver
* find_me
,
227 const base::ObserverList
<UpdateObserver
>& observer_list
) {
228 return observer_list
.HasObserver(find_me
);
231 void AppCacheGroup::ScheduleUpdateRestart(int delay_ms
) {
232 DCHECK(restart_update_task_
.IsCancelled());
233 restart_update_task_
.Reset(
234 base::Bind(&AppCacheGroup::RunQueuedUpdates
, this));
235 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
236 FROM_HERE
, restart_update_task_
.callback(),
237 base::TimeDelta::FromMilliseconds(delay_ms
));
240 void AppCacheGroup::HostDestructionImminent(AppCacheHost
* host
) {
241 queued_updates_
.erase(host
);
242 if (queued_updates_
.empty() && !restart_update_task_
.IsCancelled())
243 restart_update_task_
.Cancel();
246 void AppCacheGroup::SetUpdateAppCacheStatus(UpdateAppCacheStatus status
) {
247 if (status
== update_status_
)
250 update_status_
= status
;
252 if (status
!= IDLE
) {
257 // Observers may release us in these callbacks, so we protect against
258 // deletion by adding an extra ref in this scope (but only if we're not
259 // in our destructor).
260 scoped_refptr
<AppCacheGroup
> protect(is_in_dtor_
? NULL
: this);
261 FOR_EACH_OBSERVER(UpdateObserver
, observers_
, OnUpdateComplete(this));
262 if (!queued_updates_
.empty())
263 ScheduleUpdateRestart(kUpdateRestartDelayMs
);
267 } // namespace content