Revert "Merged all Chromoting Host code into remoting_core.dll (Windows)."
[chromium-blink-merge.git] / webkit / quota / usage_tracker.cc
blob4d8bb2704a04ef3c69e3ee8c30afee843f25f475
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/quota/usage_tracker.h"
7 #include <algorithm>
8 #include <deque>
9 #include <set>
10 #include <string>
11 #include <vector>
13 #include "base/bind.h"
14 #include "base/message_loop_proxy.h"
15 #include "base/stl_util.h"
16 #include "net/base/net_util.h"
18 namespace quota {
20 namespace {
21 bool SortByHost(const GURL& lhs, const GURL& rhs) {
22 return net::GetHostOrSpecFromURL(lhs) > net::GetHostOrSpecFromURL(rhs);
26 // A task class for getting the total amount of data used for a collection of
27 // origins. This class is self-destructed.
28 class ClientUsageTracker::GatherUsageTaskBase : public QuotaTask {
29 public:
30 GatherUsageTaskBase(
31 UsageTracker* tracker,
32 QuotaClient* client)
33 : QuotaTask(tracker),
34 client_(client),
35 tracker_(tracker),
36 weak_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
37 DCHECK(tracker_);
38 DCHECK(client_);
39 client_tracker_ = base::AsWeakPtr(
40 tracker_->GetClientTracker(client_->id()));
41 DCHECK(client_tracker_.get());
43 virtual ~GatherUsageTaskBase() {}
45 // Get total usage for the given |origins|.
46 void GetUsageForOrigins(const std::set<GURL>& origins, StorageType type) {
47 DCHECK(original_task_runner()->BelongsToCurrentThread());
48 if (!client_tracker()) {
49 DeleteSoon();
50 return;
52 // We do not get usage for origins for which we have valid usage cache.
53 std::vector<GURL> origins_to_gather;
54 std::set<GURL> cached_origins;
55 client_tracker()->GetCachedOrigins(&cached_origins);
56 std::set<GURL> already_added;
57 for (std::set<GURL>::const_iterator iter = origins.begin();
58 iter != origins.end(); ++iter) {
59 if (cached_origins.find(*iter) == cached_origins.end() &&
60 already_added.insert(*iter).second) {
61 origins_to_gather.push_back(*iter);
64 if (origins_to_gather.empty()) {
65 CallCompleted();
66 DeleteSoon();
67 return;
70 // Sort them so we can detect when we've gathered all info for a particular
71 // host in DidGetUsage.
72 std::sort(origins_to_gather.begin(), origins_to_gather.end(), SortByHost);
74 // First, fully populate the pending queue because GetOriginUsage may call
75 // the completion callback immediately.
76 for (std::vector<GURL>::const_iterator iter = origins_to_gather.begin();
77 iter != origins_to_gather.end(); iter++)
78 pending_origins_.push_back(*iter);
80 for (std::vector<GURL>::const_iterator iter = origins_to_gather.begin();
81 iter != origins_to_gather.end(); iter++)
82 client_->GetOriginUsage(
83 *iter,
84 tracker_->type(),
85 base::Bind(&GatherUsageTaskBase::DidGetUsage,
86 weak_factory_.GetWeakPtr()));
89 protected:
90 virtual void Aborted() OVERRIDE {
91 DeleteSoon();
94 UsageTracker* tracker() const { return tracker_; }
95 ClientUsageTracker* client_tracker() const { return client_tracker_.get(); }
97 private:
98 void DidGetUsage(int64 usage) {
99 if (!client_tracker()) {
100 DeleteSoon();
101 return;
104 DCHECK(original_task_runner()->BelongsToCurrentThread());
105 DCHECK(!pending_origins_.empty());
107 // Defend against confusing inputs from QuotaClients.
108 DCHECK_GE(usage, 0);
109 if (usage < 0)
110 usage = 0;
112 // This code assumes DidGetUsage callbacks are called in the same
113 // order as we dispatched GetOriginUsage calls.
114 const GURL& origin = pending_origins_.front();
115 std::string host = net::GetHostOrSpecFromURL(origin);
116 client_tracker_->AddCachedOrigin(origin, usage);
118 pending_origins_.pop_front();
119 if (pending_origins_.empty() ||
120 host != net::GetHostOrSpecFromURL(pending_origins_.front())) {
121 client_tracker_->AddCachedHost(host);
124 if (pending_origins_.empty()) {
125 // We're done.
126 CallCompleted();
127 DeleteSoon();
131 QuotaClient* client_;
132 UsageTracker* tracker_;
133 base::WeakPtr<ClientUsageTracker> client_tracker_;
134 std::deque<GURL> pending_origins_;
135 std::map<GURL, int64> origin_usage_map_;
136 base::WeakPtrFactory<GatherUsageTaskBase> weak_factory_;
138 DISALLOW_COPY_AND_ASSIGN(GatherUsageTaskBase);
141 // A task class for getting the total amount of data used for a given storage
142 // type. This class is self-destructed.
143 class ClientUsageTracker::GatherGlobalUsageTask
144 : public GatherUsageTaskBase {
145 public:
146 GatherGlobalUsageTask(
147 UsageTracker* tracker,
148 QuotaClient* client)
149 : GatherUsageTaskBase(tracker, client),
150 client_(client),
151 weak_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
152 DCHECK(tracker);
153 DCHECK(client);
155 virtual ~GatherGlobalUsageTask() {}
157 protected:
158 virtual void Run() OVERRIDE {
159 client_->GetOriginsForType(tracker()->type(),
160 base::Bind(&GatherUsageTaskBase::GetUsageForOrigins,
161 weak_factory_.GetWeakPtr()));
164 virtual void Completed() OVERRIDE {
165 client_tracker()->GatherGlobalUsageComplete();
168 private:
169 QuotaClient* client_;
170 base::WeakPtrFactory<GatherUsageTaskBase> weak_factory_;
172 DISALLOW_COPY_AND_ASSIGN(GatherGlobalUsageTask);
175 // A task class for getting the total amount of data used for a given host.
176 // This class is self-destructed.
177 class ClientUsageTracker::GatherHostUsageTask
178 : public GatherUsageTaskBase {
179 public:
180 GatherHostUsageTask(
181 UsageTracker* tracker,
182 QuotaClient* client,
183 const std::string& host)
184 : GatherUsageTaskBase(tracker, client),
185 client_(client),
186 host_(host),
187 weak_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
188 DCHECK(client_);
190 virtual ~GatherHostUsageTask() {}
192 protected:
193 virtual void Run() OVERRIDE {
194 client_->GetOriginsForHost(tracker()->type(), host_,
195 base::Bind(&GatherUsageTaskBase::GetUsageForOrigins,
196 weak_factory_.GetWeakPtr()));
199 virtual void Completed() OVERRIDE {
200 client_tracker()->GatherHostUsageComplete(host_);
203 private:
204 QuotaClient* client_;
205 std::string host_;
206 base::WeakPtrFactory<GatherUsageTaskBase> weak_factory_;
208 DISALLOW_COPY_AND_ASSIGN(GatherHostUsageTask);
211 // UsageTracker ----------------------------------------------------------
213 UsageTracker::UsageTracker(const QuotaClientList& clients, StorageType type,
214 SpecialStoragePolicy* special_storage_policy)
215 : type_(type),
216 weak_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
217 for (QuotaClientList::const_iterator iter = clients.begin();
218 iter != clients.end();
219 ++iter) {
220 client_tracker_map_.insert(std::make_pair(
221 (*iter)->id(),
222 new ClientUsageTracker(this, *iter, type, special_storage_policy)));
226 UsageTracker::~UsageTracker() {
227 STLDeleteValues(&client_tracker_map_);
230 ClientUsageTracker* UsageTracker::GetClientTracker(QuotaClient::ID client_id) {
231 ClientTrackerMap::iterator found = client_tracker_map_.find(client_id);
232 if (found != client_tracker_map_.end())
233 return found->second;
234 return NULL;
237 void UsageTracker::GetGlobalUsage(const GlobalUsageCallback& callback) {
238 if (client_tracker_map_.size() == 0) {
239 // No clients registered.
240 callback.Run(type_, 0, 0);
241 return;
243 if (global_usage_callbacks_.Add(callback)) {
244 // This is the first call. Asks each ClientUsageTracker to collect
245 // usage information.
246 global_usage_.pending_clients = client_tracker_map_.size();
247 global_usage_.usage = 0;
248 global_usage_.unlimited_usage = 0;
249 for (ClientTrackerMap::iterator iter = client_tracker_map_.begin();
250 iter != client_tracker_map_.end();
251 ++iter) {
252 iter->second->GetGlobalUsage(
253 base::Bind(&UsageTracker::DidGetClientGlobalUsage,
254 weak_factory_.GetWeakPtr()));
259 void UsageTracker::GetHostUsage(
260 const std::string& host, const UsageCallback& callback) {
261 if (client_tracker_map_.size() == 0) {
262 // No clients registered.
263 callback.Run(0);
264 return;
266 if (host_usage_callbacks_.Add(host, callback)) {
267 // This is the first call for the given host.
268 DCHECK(outstanding_host_usage_.find(host) == outstanding_host_usage_.end());
269 outstanding_host_usage_[host].pending_clients = client_tracker_map_.size();
270 for (ClientTrackerMap::iterator iter = client_tracker_map_.begin();
271 iter != client_tracker_map_.end();
272 ++iter) {
273 iter->second->GetHostUsage(host,
274 base::Bind(&UsageTracker::DidGetClientHostUsage,
275 weak_factory_.GetWeakPtr(), host, type_));
280 void UsageTracker::UpdateUsageCache(
281 QuotaClient::ID client_id, const GURL& origin, int64 delta) {
282 ClientUsageTracker* client_tracker = GetClientTracker(client_id);
283 DCHECK(client_tracker);
284 client_tracker->UpdateUsageCache(origin, delta);
287 void UsageTracker::GetCachedHostsUsage(
288 std::map<std::string, int64>* host_usage) const {
289 DCHECK(host_usage);
290 host_usage->clear();
291 for (ClientTrackerMap::const_iterator iter = client_tracker_map_.begin();
292 iter != client_tracker_map_.end(); ++iter) {
293 iter->second->GetCachedHostsUsage(host_usage);
297 void UsageTracker::GetCachedOrigins(std::set<GURL>* origins) const {
298 DCHECK(origins);
299 origins->clear();
300 for (ClientTrackerMap::const_iterator iter = client_tracker_map_.begin();
301 iter != client_tracker_map_.end(); ++iter) {
302 iter->second->GetCachedOrigins(origins);
306 void UsageTracker::DidGetClientGlobalUsage(StorageType type,
307 int64 usage,
308 int64 unlimited_usage) {
309 DCHECK_EQ(type, type_);
310 global_usage_.usage += usage;
311 global_usage_.unlimited_usage += unlimited_usage;
312 if (--global_usage_.pending_clients == 0) {
313 // Defend against confusing inputs from clients.
314 if (global_usage_.usage < 0)
315 global_usage_.usage = 0;
316 // TODO(michaeln): The unlimited number is not trustworthy, it
317 // can get out of whack when apps are installed or uninstalled.
318 if (global_usage_.unlimited_usage > global_usage_.usage)
319 global_usage_.unlimited_usage = global_usage_.usage;
320 else if (global_usage_.unlimited_usage < 0)
321 global_usage_.unlimited_usage = 0;
323 // All the clients have returned their usage data. Dispatches the
324 // pending callbacks.
325 global_usage_callbacks_.Run(type, global_usage_.usage,
326 global_usage_.unlimited_usage);
330 void UsageTracker::DidGetClientHostUsage(const std::string& host,
331 StorageType type,
332 int64 usage) {
333 DCHECK_EQ(type, type_);
334 TrackingInfo& info = outstanding_host_usage_[host];
335 info.usage += usage;
336 if (--info.pending_clients == 0) {
337 // Defend against confusing inputs from clients.
338 if (info.usage < 0)
339 info.usage = 0;
340 // All the clients have returned their usage data. Dispatches the
341 // pending callbacks.
342 host_usage_callbacks_.Run(host, info.usage);
343 outstanding_host_usage_.erase(host);
347 // ClientUsageTracker ----------------------------------------------------
349 ClientUsageTracker::ClientUsageTracker(
350 UsageTracker* tracker, QuotaClient* client, StorageType type,
351 SpecialStoragePolicy* special_storage_policy)
352 : tracker_(tracker),
353 client_(client),
354 type_(type),
355 global_usage_(0),
356 global_unlimited_usage_(0),
357 global_usage_retrieved_(false),
358 global_unlimited_usage_is_valid_(true),
359 global_usage_task_(NULL),
360 special_storage_policy_(special_storage_policy) {
361 DCHECK(tracker_);
362 DCHECK(client_);
363 if (special_storage_policy_)
364 special_storage_policy_->AddObserver(this);
367 ClientUsageTracker::~ClientUsageTracker() {
368 if (special_storage_policy_)
369 special_storage_policy_->RemoveObserver(this);
372 void ClientUsageTracker::GetGlobalUsage(const GlobalUsageCallback& callback) {
373 if (global_usage_retrieved_) {
374 callback.Run(type_, global_usage_, GetCachedGlobalUnlimitedUsage());
375 return;
377 DCHECK(!global_usage_callback_.HasCallbacks());
378 global_usage_callback_.Add(callback);
379 global_usage_task_ = new GatherGlobalUsageTask(tracker_, client_);
380 global_usage_task_->Start();
383 void ClientUsageTracker::GetHostUsage(
384 const std::string& host, const UsageCallback& callback) {
385 HostSet::const_iterator found = cached_hosts_.find(host);
386 if (found != cached_hosts_.end()) {
387 // TODO(kinuko): Drop host_usage_map_ cache periodically.
388 callback.Run(GetCachedHostUsage(host));
389 return;
391 if (!host_usage_callbacks_.Add(host, callback) || global_usage_task_)
392 return;
393 GatherHostUsageTask* task = new GatherHostUsageTask(tracker_, client_, host);
394 host_usage_tasks_[host] = task;
395 task->Start();
398 void ClientUsageTracker::UpdateUsageCache(
399 const GURL& origin, int64 delta) {
400 std::string host = net::GetHostOrSpecFromURL(origin);
401 if (cached_hosts_.find(host) != cached_hosts_.end()) {
402 cached_usage_[host][origin] += delta;
403 global_usage_ += delta;
404 if (global_unlimited_usage_is_valid_ && IsStorageUnlimited(origin))
405 global_unlimited_usage_ += delta;
406 DCHECK_GE(cached_usage_[host][origin], 0);
407 DCHECK_GE(global_usage_, 0);
408 return;
411 // We don't know about this host yet, so populate our cache for it.
412 GetHostUsage(host,
413 base::Bind(&ClientUsageTracker::NoopHostUsageCallback,
414 base::Unretained(this)));
417 void ClientUsageTracker::GetCachedHostsUsage(
418 std::map<std::string, int64>* host_usage) const {
419 DCHECK(host_usage);
420 for (HostUsageMap::const_iterator host_iter = cached_usage_.begin();
421 host_iter != cached_usage_.end(); host_iter++) {
422 host_usage->operator[](host_iter->first) +=
423 GetCachedHostUsage(host_iter->first);
427 void ClientUsageTracker::GetCachedOrigins(std::set<GURL>* origins) const {
428 DCHECK(origins);
429 for (HostUsageMap::const_iterator host_iter = cached_usage_.begin();
430 host_iter != cached_usage_.end(); host_iter++) {
431 const UsageMap& origin_map = host_iter->second;
432 for (UsageMap::const_iterator origin_iter = origin_map.begin();
433 origin_iter != origin_map.end(); origin_iter++) {
434 origins->insert(origin_iter->first);
439 void ClientUsageTracker::AddCachedOrigin(
440 const GURL& origin, int64 usage) {
441 std::string host = net::GetHostOrSpecFromURL(origin);
442 UsageMap::iterator iter = cached_usage_[host].
443 insert(UsageMap::value_type(origin, 0)).first;
444 int64 old_usage = iter->second;
445 iter->second = usage;
446 int64 delta = usage - old_usage;
447 if (delta) {
448 global_usage_ += delta;
449 if (global_unlimited_usage_is_valid_ && IsStorageUnlimited(origin))
450 global_unlimited_usage_ += delta;
452 DCHECK_GE(iter->second, 0);
453 DCHECK_GE(global_usage_, 0);
456 void ClientUsageTracker::AddCachedHost(const std::string& host) {
457 cached_hosts_.insert(host);
460 void ClientUsageTracker::GatherGlobalUsageComplete() {
461 DCHECK(global_usage_task_ != NULL);
462 global_usage_task_ = NULL;
463 // TODO(kinuko): Record when it has retrieved the global usage.
464 global_usage_retrieved_ = true;
466 DCHECK(global_usage_callback_.HasCallbacks());
467 global_usage_callback_.Run(type_, global_usage_,
468 GetCachedGlobalUnlimitedUsage());
470 for (HostUsageCallbackMap::iterator iter = host_usage_callbacks_.Begin();
471 iter != host_usage_callbacks_.End(); ++iter) {
472 iter->second.Run(GetCachedHostUsage(iter->first));
474 host_usage_callbacks_.Clear();
477 void ClientUsageTracker::GatherHostUsageComplete(const std::string& host) {
478 DCHECK(host_usage_tasks_.find(host) != host_usage_tasks_.end());
479 host_usage_tasks_.erase(host);
480 host_usage_callbacks_.Run(host, GetCachedHostUsage(host));
483 int64 ClientUsageTracker::GetCachedHostUsage(const std::string& host) const {
484 HostUsageMap::const_iterator found = cached_usage_.find(host);
485 if (found == cached_usage_.end())
486 return 0;
488 int64 usage = 0;
489 const UsageMap& map = found->second;
490 for (UsageMap::const_iterator iter = map.begin();
491 iter != map.end(); ++iter) {
492 usage += iter->second;
494 return usage;
497 int64 ClientUsageTracker::GetCachedGlobalUnlimitedUsage() {
498 if (!global_unlimited_usage_is_valid_) {
499 global_unlimited_usage_ = 0;
500 for (HostUsageMap::const_iterator host_iter = cached_usage_.begin();
501 host_iter != cached_usage_.end(); host_iter++) {
502 const UsageMap& origin_map = host_iter->second;
503 for (UsageMap::const_iterator origin_iter = origin_map.begin();
504 origin_iter != origin_map.end(); origin_iter++) {
505 if (IsStorageUnlimited(origin_iter->first))
506 global_unlimited_usage_ += origin_iter->second;
509 global_unlimited_usage_is_valid_ = true;
511 return global_unlimited_usage_;
514 void ClientUsageTracker::OnSpecialStoragePolicyChanged() {
515 DCHECK(CalledOnValidThread());
516 global_unlimited_usage_is_valid_ = false;
519 void ClientUsageTracker::NoopHostUsageCallback(int64 usage) {}
521 bool ClientUsageTracker::IsStorageUnlimited(const GURL& origin) const {
522 if (type_ == kStorageTypeSyncable)
523 return false;
524 return special_storage_policy_.get() &&
525 special_storage_policy_->IsStorageUnlimited(origin);
528 } // namespace quota