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
15using storage::QuotaClient;
16
17namespace {
18storage::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
27void RunFront(content::AppCacheQuotaClient::RequestQueue* queue) {
28  base::Closure request = queue->front();
29  queue->pop_front();
30  request.Run();
31}
32}  // namespace
33
34namespace content {
35
36AppCacheQuotaClient::AppCacheQuotaClient(AppCacheServiceImpl* service)
37    : service_(service),
38      appcache_is_ready_(false),
39      quota_manager_is_destroyed_(false) {
40}
41
42AppCacheQuotaClient::~AppCacheQuotaClient() {
43  DCHECK(pending_batch_requests_.empty());
44  DCHECK(pending_serial_requests_.empty());
45  DCHECK(current_delete_request_callback_.is_null());
46}
47
48QuotaClient::ID AppCacheQuotaClient::id() const {
49  return kAppcache;
50}
51
52void 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
64void 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
96void AppCacheQuotaClient::GetOriginsForType(
97    storage::StorageType type,
98    const GetOriginsCallback& callback) {
99  GetOriginsHelper(type, std::string(), callback);
100}
101
102void 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
114void 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
141bool AppCacheQuotaClient::DoesSupport(storage::StorageType type) const {
142  return type == storage::kStorageTypeTemporary;
143}
144
145void 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
160void 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
193void 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
202void AppCacheQuotaClient::DeletePendingRequests() {
203  pending_batch_requests_.clear();
204  pending_serial_requests_.clear();
205}
206
207const AppCacheStorage::UsageMap* AppCacheQuotaClient::GetUsageMap() {
208  DCHECK(service_);
209  return service_->storage()->usage_map();
210}
211
212net::CancelableCompletionCallback*
213AppCacheQuotaClient::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
225void AppCacheQuotaClient::NotifyAppCacheReady() {
226  // Can reoccur during reinitialization.
227  if (!appcache_is_ready_) {
228    appcache_is_ready_ = true;
229    ProcessPendingRequests();
230  }
231}
232
233void 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