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 "content/browser/appcache/appcache_quota_client.h"
6
7 #include <algorithm>
8 #include <map>
9 #include <set>
10
11 #include "base/bind.h"
12 #include "base/bind_helpers.h"
13 #include "content/browser/appcache/appcache_service_impl.h"
14
15 using storage::QuotaClient;
16
17 namespace {
NetErrorCodeToQuotaStatus(int code)18 storage::QuotaStatusCode NetErrorCodeToQuotaStatus(int code) {
19 if (code == net::OK)
20 return storage::kQuotaStatusOk;
21 else if (code == net::ERR_ABORTED)
22 return storage::kQuotaErrorAbort;
23 else
24 return storage::kQuotaStatusUnknown;
25 }
26
RunFront(content::AppCacheQuotaClient::RequestQueue * queue)27 void RunFront(content::AppCacheQuotaClient::RequestQueue* queue) {
28 base::Closure request = queue->front();
29 queue->pop_front();
30 request.Run();
31 }
32 } // namespace
33
34 namespace content {
35
AppCacheQuotaClient(AppCacheServiceImpl * service)36 AppCacheQuotaClient::AppCacheQuotaClient(AppCacheServiceImpl* service)
37 : service_(service),
38 appcache_is_ready_(false),
39 quota_manager_is_destroyed_(false) {
40 }
41
~AppCacheQuotaClient()42 AppCacheQuotaClient::~AppCacheQuotaClient() {
43 DCHECK(pending_batch_requests_.empty());
44 DCHECK(pending_serial_requests_.empty());
45 DCHECK(current_delete_request_callback_.is_null());
46 }
47
id() const48 QuotaClient::ID AppCacheQuotaClient::id() const {
49 return kAppcache;
50 }
51
OnQuotaManagerDestroyed()52 void AppCacheQuotaClient::OnQuotaManagerDestroyed() {
53 DeletePendingRequests();
54 if (!current_delete_request_callback_.is_null()) {
55 current_delete_request_callback_.Reset();
56 GetServiceDeleteCallback()->Cancel();
57 }
58
59 quota_manager_is_destroyed_ = true;
60 if (!service_)
61 delete this;
62 }
63
GetOriginUsage(const GURL & origin,storage::StorageType type,const GetUsageCallback & callback)64 void AppCacheQuotaClient::GetOriginUsage(const GURL& origin,
65 storage::StorageType type,
66 const GetUsageCallback& callback) {
67 DCHECK(!callback.is_null());
68 DCHECK(!quota_manager_is_destroyed_);
69
70 if (!service_) {
71 callback.Run(0);
72 return;
73 }
74
75 if (!appcache_is_ready_) {
76 pending_batch_requests_.push_back(
77 base::Bind(&AppCacheQuotaClient::GetOriginUsage,
78 base::Unretained(this), origin, type, callback));
79 return;
80 }
81
82 if (type != storage::kStorageTypeTemporary) {
83 callback.Run(0);
84 return;
85 }
86
87 const AppCacheStorage::UsageMap* map = GetUsageMap();
88 AppCacheStorage::UsageMap::const_iterator found = map->find(origin);
89 if (found == map->end()) {
90 callback.Run(0);
91 return;
92 }
93 callback.Run(found->second);
94 }
95
GetOriginsForType(storage::StorageType type,const GetOriginsCallback & callback)96 void AppCacheQuotaClient::GetOriginsForType(
97 storage::StorageType type,
98 const GetOriginsCallback& callback) {
99 GetOriginsHelper(type, std::string(), callback);
100 }
101
GetOriginsForHost(storage::StorageType type,const std::string & host,const GetOriginsCallback & callback)102 void AppCacheQuotaClient::GetOriginsForHost(
103 storage::StorageType type,
104 const std::string& host,
105 const GetOriginsCallback& callback) {
106 DCHECK(!callback.is_null());
107 if (host.empty()) {
108 callback.Run(std::set<GURL>());
109 return;
110 }
111 GetOriginsHelper(type, host, callback);
112 }
113
DeleteOriginData(const GURL & origin,storage::StorageType type,const DeletionCallback & callback)114 void AppCacheQuotaClient::DeleteOriginData(const GURL& origin,
115 storage::StorageType type,
116 const DeletionCallback& callback) {
117 DCHECK(!quota_manager_is_destroyed_);
118
119 if (!service_) {
120 callback.Run(storage::kQuotaErrorAbort);
121 return;
122 }
123
124 if (!appcache_is_ready_ || !current_delete_request_callback_.is_null()) {
125 pending_serial_requests_.push_back(
126 base::Bind(&AppCacheQuotaClient::DeleteOriginData,
127 base::Unretained(this), origin, type, callback));
128 return;
129 }
130
131 current_delete_request_callback_ = callback;
132 if (type != storage::kStorageTypeTemporary) {
133 DidDeleteAppCachesForOrigin(net::OK);
134 return;
135 }
136
137 service_->DeleteAppCachesForOrigin(
138 origin, GetServiceDeleteCallback()->callback());
139 }
140
DoesSupport(storage::StorageType type) const141 bool AppCacheQuotaClient::DoesSupport(storage::StorageType type) const {
142 return type == storage::kStorageTypeTemporary;
143 }
144
DidDeleteAppCachesForOrigin(int rv)145 void AppCacheQuotaClient::DidDeleteAppCachesForOrigin(int rv) {
146 DCHECK(service_);
147 if (quota_manager_is_destroyed_)
148 return;
149
150 // Finish the request by calling our callers callback.
151 current_delete_request_callback_.Run(NetErrorCodeToQuotaStatus(rv));
152 current_delete_request_callback_.Reset();
153 if (pending_serial_requests_.empty())
154 return;
155
156 // Start the next in the queue.
157 RunFront(&pending_serial_requests_);
158 }
159
GetOriginsHelper(storage::StorageType type,const std::string & opt_host,const GetOriginsCallback & callback)160 void AppCacheQuotaClient::GetOriginsHelper(storage::StorageType type,
161 const std::string& opt_host,
162 const GetOriginsCallback& callback) {
163 DCHECK(!callback.is_null());
164 DCHECK(!quota_manager_is_destroyed_);
165
166 if (!service_) {
167 callback.Run(std::set<GURL>());
168 return;
169 }
170
171 if (!appcache_is_ready_) {
172 pending_batch_requests_.push_back(
173 base::Bind(&AppCacheQuotaClient::GetOriginsHelper,
174 base::Unretained(this), type, opt_host, callback));
175 return;
176 }
177
178 if (type != storage::kStorageTypeTemporary) {
179 callback.Run(std::set<GURL>());
180 return;
181 }
182
183 const AppCacheStorage::UsageMap* map = GetUsageMap();
184 std::set<GURL> origins;
185 for (AppCacheStorage::UsageMap::const_iterator iter = map->begin();
186 iter != map->end(); ++iter) {
187 if (opt_host.empty() || iter->first.host() == opt_host)
188 origins.insert(iter->first);
189 }
190 callback.Run(origins);
191 }
192
ProcessPendingRequests()193 void AppCacheQuotaClient::ProcessPendingRequests() {
194 DCHECK(appcache_is_ready_);
195 while (!pending_batch_requests_.empty())
196 RunFront(&pending_batch_requests_);
197
198 if (!pending_serial_requests_.empty())
199 RunFront(&pending_serial_requests_);
200 }
201
DeletePendingRequests()202 void AppCacheQuotaClient::DeletePendingRequests() {
203 pending_batch_requests_.clear();
204 pending_serial_requests_.clear();
205 }
206
GetUsageMap()207 const AppCacheStorage::UsageMap* AppCacheQuotaClient::GetUsageMap() {
208 DCHECK(service_);
209 return service_->storage()->usage_map();
210 }
211
212 net::CancelableCompletionCallback*
GetServiceDeleteCallback()213 AppCacheQuotaClient::GetServiceDeleteCallback() {
214 // Lazily created due to CancelableCompletionCallback's threading
215 // restrictions, there is no way to detach from the thread created on.
216 if (!service_delete_callback_) {
217 service_delete_callback_.reset(
218 new net::CancelableCompletionCallback(
219 base::Bind(&AppCacheQuotaClient::DidDeleteAppCachesForOrigin,
220 base::Unretained(this))));
221 }
222 return service_delete_callback_.get();
223 }
224
NotifyAppCacheReady()225 void AppCacheQuotaClient::NotifyAppCacheReady() {
226 // Can reoccur during reinitialization.
227 if (!appcache_is_ready_) {
228 appcache_is_ready_ = true;
229 ProcessPendingRequests();
230 }
231 }
232
NotifyAppCacheDestroyed()233 void AppCacheQuotaClient::NotifyAppCacheDestroyed() {
234 service_ = NULL;
235 while (!pending_batch_requests_.empty())
236 RunFront(&pending_batch_requests_);
237
238 while (!pending_serial_requests_.empty())
239 RunFront(&pending_serial_requests_);
240
241 if (!current_delete_request_callback_.is_null()) {
242 current_delete_request_callback_.Run(storage::kQuotaErrorAbort);
243 current_delete_request_callback_.Reset();
244 GetServiceDeleteCallback()->Cancel();
245 }
246
247 if (quota_manager_is_destroyed_)
248 delete this;
249 }
250
251 } // namespace content
252