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"
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"
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
{
31 UsageTracker
* tracker
,
36 weak_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
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()) {
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()) {
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(
85 base::Bind(&GatherUsageTaskBase::DidGetUsage
,
86 weak_factory_
.GetWeakPtr()));
90 virtual void Aborted() OVERRIDE
{
94 UsageTracker
* tracker() const { return tracker_
; }
95 ClientUsageTracker
* client_tracker() const { return client_tracker_
.get(); }
98 void DidGetUsage(int64 usage
) {
99 if (!client_tracker()) {
104 DCHECK(original_task_runner()->BelongsToCurrentThread());
105 DCHECK(!pending_origins_
.empty());
107 // Defend against confusing inputs from QuotaClients.
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()) {
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
{
146 GatherGlobalUsageTask(
147 UsageTracker
* tracker
,
149 : GatherUsageTaskBase(tracker
, client
),
151 weak_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
155 virtual ~GatherGlobalUsageTask() {}
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();
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
{
181 UsageTracker
* tracker
,
183 const std::string
& host
)
184 : GatherUsageTaskBase(tracker
, client
),
187 weak_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
190 virtual ~GatherHostUsageTask() {}
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_
);
204 QuotaClient
* client_
;
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
)
216 weak_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
217 for (QuotaClientList::const_iterator iter
= clients
.begin();
218 iter
!= clients
.end();
220 client_tracker_map_
.insert(std::make_pair(
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
;
237 void UsageTracker::GetGlobalUsage(const GlobalUsageCallback
& callback
) {
238 if (client_tracker_map_
.size() == 0) {
239 // No clients registered.
240 callback
.Run(type_
, 0, 0);
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();
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.
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();
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 {
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 {
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
,
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
,
333 DCHECK_EQ(type
, type_
);
334 TrackingInfo
& info
= outstanding_host_usage_
[host
];
336 if (--info
.pending_clients
== 0) {
337 // Defend against confusing inputs from clients.
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
)
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
) {
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());
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
));
391 if (!host_usage_callbacks_
.Add(host
, callback
) || global_usage_task_
)
393 GatherHostUsageTask
* task
= new GatherHostUsageTask(tracker_
, client_
, host
);
394 host_usage_tasks_
[host
] = task
;
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);
411 // We don't know about this host yet, so populate our cache for it.
413 base::Bind(&ClientUsageTracker::NoopHostUsageCallback
,
414 base::Unretained(this)));
417 void ClientUsageTracker::GetCachedHostsUsage(
418 std::map
<std::string
, int64
>* host_usage
) const {
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 {
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
;
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())
489 const UsageMap
& map
= found
->second
;
490 for (UsageMap::const_iterator iter
= map
.begin();
491 iter
!= map
.end(); ++iter
) {
492 usage
+= iter
->second
;
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
)
524 return special_storage_policy_
.get() &&
525 special_storage_policy_
->IsStorageUnlimited(origin
);