1 // Copyright 2013 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 "webkit/browser/quota/usage_tracker.h"
6
7 #include <algorithm>
8 #include <deque>
9 #include <set>
10 #include <string>
11 #include <vector>
12
13 #include "base/bind.h"
14 #include "base/message_loop/message_loop_proxy.h"
15 #include "base/stl_util.h"
16 #include "net/base/net_util.h"
17
18 namespace quota {
19
20 namespace {
21
22 typedef ClientUsageTracker::OriginUsageAccumulator OriginUsageAccumulator;
23 typedef ClientUsageTracker::OriginSetByHost OriginSetByHost;
24
DidGetOriginUsage(const OriginUsageAccumulator & accumulator,const GURL & origin,int64 usage)25 void DidGetOriginUsage(const OriginUsageAccumulator& accumulator,
26 const GURL& origin,
27 int64 usage) {
28 accumulator.Run(origin, usage);
29 }
30
DidGetHostUsage(const UsageCallback & callback,int64 limited_usage,int64 unlimited_usage)31 void DidGetHostUsage(const UsageCallback& callback,
32 int64 limited_usage,
33 int64 unlimited_usage) {
34 DCHECK_GE(limited_usage, 0);
35 DCHECK_GE(unlimited_usage, 0);
36 callback.Run(limited_usage + unlimited_usage);
37 }
38
NoopHostUsageCallback(int64 usage)39 void NoopHostUsageCallback(int64 usage) {}
40
EraseOriginFromOriginSet(OriginSetByHost * origins_by_host,const std::string & host,const GURL & origin)41 bool EraseOriginFromOriginSet(OriginSetByHost* origins_by_host,
42 const std::string& host,
43 const GURL& origin) {
44 OriginSetByHost::iterator found = origins_by_host->find(host);
45 if (found == origins_by_host->end())
46 return false;
47
48 if (!found->second.erase(origin))
49 return false;
50
51 if (found->second.empty())
52 origins_by_host->erase(host);
53 return true;
54 }
55
OriginSetContainsOrigin(const OriginSetByHost & origins,const std::string & host,const GURL & origin)56 bool OriginSetContainsOrigin(const OriginSetByHost& origins,
57 const std::string& host,
58 const GURL& origin) {
59 OriginSetByHost::const_iterator itr = origins.find(host);
60 return itr != origins.end() && ContainsKey(itr->second, origin);
61 }
62
DidGetGlobalUsageForLimitedGlobalUsage(const UsageCallback & callback,int64 total_global_usage,int64 global_unlimited_usage)63 void DidGetGlobalUsageForLimitedGlobalUsage(const UsageCallback& callback,
64 int64 total_global_usage,
65 int64 global_unlimited_usage) {
66 callback.Run(total_global_usage - global_unlimited_usage);
67 }
68
69 } // namespace
70
71 // UsageTracker ----------------------------------------------------------
72
UsageTracker(const QuotaClientList & clients,StorageType type,SpecialStoragePolicy * special_storage_policy)73 UsageTracker::UsageTracker(const QuotaClientList& clients,
74 StorageType type,
75 SpecialStoragePolicy* special_storage_policy)
76 : type_(type),
77 weak_factory_(this) {
78 for (QuotaClientList::const_iterator iter = clients.begin();
79 iter != clients.end();
80 ++iter) {
81 if ((*iter)->DoesSupport(type)) {
82 client_tracker_map_[(*iter)->id()] =
83 new ClientUsageTracker(this, *iter, type, special_storage_policy);
84 }
85 }
86 }
87
~UsageTracker()88 UsageTracker::~UsageTracker() {
89 STLDeleteValues(&client_tracker_map_);
90 }
91
GetClientTracker(QuotaClient::ID client_id)92 ClientUsageTracker* UsageTracker::GetClientTracker(QuotaClient::ID client_id) {
93 ClientTrackerMap::iterator found = client_tracker_map_.find(client_id);
94 if (found != client_tracker_map_.end())
95 return found->second;
96 return NULL;
97 }
98
GetGlobalLimitedUsage(const UsageCallback & callback)99 void UsageTracker::GetGlobalLimitedUsage(const UsageCallback& callback) {
100 if (global_usage_callbacks_.HasCallbacks()) {
101 global_usage_callbacks_.Add(base::Bind(
102 &DidGetGlobalUsageForLimitedGlobalUsage, callback));
103 return;
104 }
105
106 if (!global_limited_usage_callbacks_.Add(callback))
107 return;
108
109 AccumulateInfo* info = new AccumulateInfo;
110 // Calling GetGlobalLimitedUsage(accumulator) may synchronously
111 // return if the usage is cached, which may in turn dispatch
112 // the completion callback before we finish looping over
113 // all clients (because info->pending_clients may reach 0
114 // during the loop).
115 // To avoid this, we add one more pending client as a sentinel
116 // and fire the sentinel callback at the end.
117 info->pending_clients = client_tracker_map_.size() + 1;
118 UsageCallback accumulator = base::Bind(
119 &UsageTracker::AccumulateClientGlobalLimitedUsage,
120 weak_factory_.GetWeakPtr(), base::Owned(info));
121
122 for (ClientTrackerMap::iterator iter = client_tracker_map_.begin();
123 iter != client_tracker_map_.end();
124 ++iter)
125 iter->second->GetGlobalLimitedUsage(accumulator);
126
127 // Fire the sentinel as we've now called GetGlobalUsage for all clients.
128 accumulator.Run(0);
129 }
130
GetGlobalUsage(const GlobalUsageCallback & callback)131 void UsageTracker::GetGlobalUsage(const GlobalUsageCallback& callback) {
132 if (!global_usage_callbacks_.Add(callback))
133 return;
134
135 AccumulateInfo* info = new AccumulateInfo;
136 // Calling GetGlobalUsage(accumulator) may synchronously
137 // return if the usage is cached, which may in turn dispatch
138 // the completion callback before we finish looping over
139 // all clients (because info->pending_clients may reach 0
140 // during the loop).
141 // To avoid this, we add one more pending client as a sentinel
142 // and fire the sentinel callback at the end.
143 info->pending_clients = client_tracker_map_.size() + 1;
144 GlobalUsageCallback accumulator = base::Bind(
145 &UsageTracker::AccumulateClientGlobalUsage, weak_factory_.GetWeakPtr(),
146 base::Owned(info));
147
148 for (ClientTrackerMap::iterator iter = client_tracker_map_.begin();
149 iter != client_tracker_map_.end();
150 ++iter)
151 iter->second->GetGlobalUsage(accumulator);
152
153 // Fire the sentinel as we've now called GetGlobalUsage for all clients.
154 accumulator.Run(0, 0);
155 }
156
GetHostUsage(const std::string & host,const UsageCallback & callback)157 void UsageTracker::GetHostUsage(const std::string& host,
158 const UsageCallback& callback) {
159 if (!host_usage_callbacks_.Add(host, callback))
160 return;
161
162 AccumulateInfo* info = new AccumulateInfo;
163 // Calling GetHostUsage(accumulator) may synchronously
164 // return if the usage is cached, which may in turn dispatch
165 // the completion callback before we finish looping over
166 // all clients (because info->pending_clients may reach 0
167 // during the loop).
168 // To avoid this, we add one more pending client as a sentinel
169 // and fire the sentinel callback at the end.
170 info->pending_clients = client_tracker_map_.size() + 1;
171 UsageCallback accumulator = base::Bind(
172 &UsageTracker::AccumulateClientHostUsage, weak_factory_.GetWeakPtr(),
173 base::Owned(info), host);
174
175 for (ClientTrackerMap::iterator iter = client_tracker_map_.begin();
176 iter != client_tracker_map_.end();
177 ++iter)
178 iter->second->GetHostUsage(host, accumulator);
179
180 // Fire the sentinel as we've now called GetHostUsage for all clients.
181 accumulator.Run(0);
182 }
183
UpdateUsageCache(QuotaClient::ID client_id,const GURL & origin,int64 delta)184 void UsageTracker::UpdateUsageCache(
185 QuotaClient::ID client_id, const GURL& origin, int64 delta) {
186 ClientUsageTracker* client_tracker = GetClientTracker(client_id);
187 DCHECK(client_tracker);
188 client_tracker->UpdateUsageCache(origin, delta);
189 }
190
GetCachedHostsUsage(std::map<std::string,int64> * host_usage) const191 void UsageTracker::GetCachedHostsUsage(
192 std::map<std::string, int64>* host_usage) const {
193 DCHECK(host_usage);
194 host_usage->clear();
195 for (ClientTrackerMap::const_iterator iter = client_tracker_map_.begin();
196 iter != client_tracker_map_.end(); ++iter) {
197 iter->second->GetCachedHostsUsage(host_usage);
198 }
199 }
200
GetCachedOrigins(std::set<GURL> * origins) const201 void UsageTracker::GetCachedOrigins(std::set<GURL>* origins) const {
202 DCHECK(origins);
203 origins->clear();
204 for (ClientTrackerMap::const_iterator iter = client_tracker_map_.begin();
205 iter != client_tracker_map_.end(); ++iter) {
206 iter->second->GetCachedOrigins(origins);
207 }
208 }
209
SetUsageCacheEnabled(QuotaClient::ID client_id,const GURL & origin,bool enabled)210 void UsageTracker::SetUsageCacheEnabled(QuotaClient::ID client_id,
211 const GURL& origin,
212 bool enabled) {
213 ClientUsageTracker* client_tracker = GetClientTracker(client_id);
214 DCHECK(client_tracker);
215
216 client_tracker->SetUsageCacheEnabled(origin, enabled);
217 }
218
AccumulateClientGlobalLimitedUsage(AccumulateInfo * info,int64 limited_usage)219 void UsageTracker::AccumulateClientGlobalLimitedUsage(AccumulateInfo* info,
220 int64 limited_usage) {
221 info->usage += limited_usage;
222 if (--info->pending_clients)
223 return;
224
225 // All the clients have returned their usage data. Dispatch the
226 // pending callbacks.
227 global_limited_usage_callbacks_.Run(MakeTuple(info->usage));
228 }
229
AccumulateClientGlobalUsage(AccumulateInfo * info,int64 usage,int64 unlimited_usage)230 void UsageTracker::AccumulateClientGlobalUsage(AccumulateInfo* info,
231 int64 usage,
232 int64 unlimited_usage) {
233 info->usage += usage;
234 info->unlimited_usage += unlimited_usage;
235 if (--info->pending_clients)
236 return;
237
238 // Defend against confusing inputs from clients.
239 if (info->usage < 0)
240 info->usage = 0;
241
242 // TODO(michaeln): The unlimited number is not trustworthy, it
243 // can get out of whack when apps are installed or uninstalled.
244 if (info->unlimited_usage > info->usage)
245 info->unlimited_usage = info->usage;
246 else if (info->unlimited_usage < 0)
247 info->unlimited_usage = 0;
248
249 // All the clients have returned their usage data. Dispatch the
250 // pending callbacks.
251 global_usage_callbacks_.Run(MakeTuple(info->usage, info->unlimited_usage));
252 }
253
AccumulateClientHostUsage(AccumulateInfo * info,const std::string & host,int64 usage)254 void UsageTracker::AccumulateClientHostUsage(AccumulateInfo* info,
255 const std::string& host,
256 int64 usage) {
257 info->usage += usage;
258 if (--info->pending_clients)
259 return;
260
261 // Defend against confusing inputs from clients.
262 if (info->usage < 0)
263 info->usage = 0;
264
265 // All the clients have returned their usage data. Dispatch the
266 // pending callbacks.
267 host_usage_callbacks_.Run(host, MakeTuple(info->usage));
268 }
269
270 // ClientUsageTracker ----------------------------------------------------
271
ClientUsageTracker(UsageTracker * tracker,QuotaClient * client,StorageType type,SpecialStoragePolicy * special_storage_policy)272 ClientUsageTracker::ClientUsageTracker(
273 UsageTracker* tracker, QuotaClient* client, StorageType type,
274 SpecialStoragePolicy* special_storage_policy)
275 : tracker_(tracker),
276 client_(client),
277 type_(type),
278 global_limited_usage_(0),
279 global_unlimited_usage_(0),
280 global_usage_retrieved_(false),
281 special_storage_policy_(special_storage_policy) {
282 DCHECK(tracker_);
283 DCHECK(client_);
284 if (special_storage_policy_.get())
285 special_storage_policy_->AddObserver(this);
286 }
287
~ClientUsageTracker()288 ClientUsageTracker::~ClientUsageTracker() {
289 if (special_storage_policy_.get())
290 special_storage_policy_->RemoveObserver(this);
291 }
292
GetGlobalLimitedUsage(const UsageCallback & callback)293 void ClientUsageTracker::GetGlobalLimitedUsage(const UsageCallback& callback) {
294 if (!global_usage_retrieved_) {
295 GetGlobalUsage(base::Bind(&DidGetGlobalUsageForLimitedGlobalUsage,
296 callback));
297 return;
298 }
299
300 if (non_cached_limited_origins_by_host_.empty()) {
301 callback.Run(global_limited_usage_);
302 return;
303 }
304
305 AccumulateInfo* info = new AccumulateInfo;
306 info->pending_jobs = non_cached_limited_origins_by_host_.size() + 1;
307 UsageCallback accumulator = base::Bind(
308 &ClientUsageTracker::AccumulateLimitedOriginUsage, AsWeakPtr(),
309 base::Owned(info), callback);
310
311 for (OriginSetByHost::iterator host_itr =
312 non_cached_limited_origins_by_host_.begin();
313 host_itr != non_cached_limited_origins_by_host_.end(); ++host_itr) {
314 for (std::set<GURL>::iterator origin_itr = host_itr->second.begin();
315 origin_itr != host_itr->second.end(); ++origin_itr)
316 client_->GetOriginUsage(*origin_itr, type_, accumulator);
317 }
318
319 accumulator.Run(global_limited_usage_);
320 }
321
GetGlobalUsage(const GlobalUsageCallback & callback)322 void ClientUsageTracker::GetGlobalUsage(const GlobalUsageCallback& callback) {
323 if (global_usage_retrieved_ &&
324 non_cached_limited_origins_by_host_.empty() &&
325 non_cached_unlimited_origins_by_host_.empty()) {
326 callback.Run(global_limited_usage_ + global_unlimited_usage_,
327 global_unlimited_usage_);
328 return;
329 }
330
331 client_->GetOriginsForType(type_, base::Bind(
332 &ClientUsageTracker::DidGetOriginsForGlobalUsage, AsWeakPtr(),
333 callback));
334 }
335
GetHostUsage(const std::string & host,const UsageCallback & callback)336 void ClientUsageTracker::GetHostUsage(
337 const std::string& host, const UsageCallback& callback) {
338 if (ContainsKey(cached_hosts_, host) &&
339 !ContainsKey(non_cached_limited_origins_by_host_, host) &&
340 !ContainsKey(non_cached_unlimited_origins_by_host_, host)) {
341 // TODO(kinuko): Drop host_usage_map_ cache periodically.
342 callback.Run(GetCachedHostUsage(host));
343 return;
344 }
345
346 if (!host_usage_accumulators_.Add(
347 host, base::Bind(&DidGetHostUsage, callback)))
348 return;
349 client_->GetOriginsForHost(type_, host, base::Bind(
350 &ClientUsageTracker::DidGetOriginsForHostUsage, AsWeakPtr(), host));
351 }
352
UpdateUsageCache(const GURL & origin,int64 delta)353 void ClientUsageTracker::UpdateUsageCache(
354 const GURL& origin, int64 delta) {
355 std::string host = net::GetHostOrSpecFromURL(origin);
356 if (cached_hosts_.find(host) != cached_hosts_.end()) {
357 if (!IsUsageCacheEnabledForOrigin(origin))
358 return;
359
360 cached_usage_by_host_[host][origin] += delta;
361 if (IsStorageUnlimited(origin))
362 global_unlimited_usage_ += delta;
363 else
364 global_limited_usage_ += delta;
365 DCHECK_GE(cached_usage_by_host_[host][origin], 0);
366 DCHECK_GE(global_limited_usage_, 0);
367 return;
368 }
369
370 // We don't know about this host yet, so populate our cache for it.
371 GetHostUsage(host, base::Bind(&NoopHostUsageCallback));
372 }
373
GetCachedHostsUsage(std::map<std::string,int64> * host_usage) const374 void ClientUsageTracker::GetCachedHostsUsage(
375 std::map<std::string, int64>* host_usage) const {
376 DCHECK(host_usage);
377 for (HostUsageMap::const_iterator host_iter = cached_usage_by_host_.begin();
378 host_iter != cached_usage_by_host_.end(); host_iter++) {
379 const std::string& host = host_iter->first;
380 (*host_usage)[host] += GetCachedHostUsage(host);
381 }
382 }
383
GetCachedOrigins(std::set<GURL> * origins) const384 void ClientUsageTracker::GetCachedOrigins(std::set<GURL>* origins) const {
385 DCHECK(origins);
386 for (HostUsageMap::const_iterator host_iter = cached_usage_by_host_.begin();
387 host_iter != cached_usage_by_host_.end(); host_iter++) {
388 const UsageMap& origin_map = host_iter->second;
389 for (UsageMap::const_iterator origin_iter = origin_map.begin();
390 origin_iter != origin_map.end(); origin_iter++) {
391 origins->insert(origin_iter->first);
392 }
393 }
394 }
395
SetUsageCacheEnabled(const GURL & origin,bool enabled)396 void ClientUsageTracker::SetUsageCacheEnabled(const GURL& origin,
397 bool enabled) {
398 std::string host = net::GetHostOrSpecFromURL(origin);
399 if (!enabled) {
400 // Erase |origin| from cache and subtract its usage.
401 HostUsageMap::iterator found_host = cached_usage_by_host_.find(host);
402 if (found_host != cached_usage_by_host_.end()) {
403 UsageMap& cached_usage_for_host = found_host->second;
404
405 UsageMap::iterator found = cached_usage_for_host.find(origin);
406 if (found != cached_usage_for_host.end()) {
407 int64 usage = found->second;
408 UpdateUsageCache(origin, -usage);
409 cached_usage_for_host.erase(found);
410 if (cached_usage_for_host.empty()) {
411 cached_usage_by_host_.erase(found_host);
412 cached_hosts_.erase(host);
413 }
414 }
415 }
416
417 if (IsStorageUnlimited(origin))
418 non_cached_unlimited_origins_by_host_[host].insert(origin);
419 else
420 non_cached_limited_origins_by_host_[host].insert(origin);
421 } else {
422 // Erase |origin| from |non_cached_origins_| and invalidate the usage cache
423 // for the host.
424 if (EraseOriginFromOriginSet(&non_cached_limited_origins_by_host_,
425 host, origin) ||
426 EraseOriginFromOriginSet(&non_cached_unlimited_origins_by_host_,
427 host, origin)) {
428 cached_hosts_.erase(host);
429 global_usage_retrieved_ = false;
430 }
431 }
432 }
433
AccumulateLimitedOriginUsage(AccumulateInfo * info,const UsageCallback & callback,int64 usage)434 void ClientUsageTracker::AccumulateLimitedOriginUsage(
435 AccumulateInfo* info,
436 const UsageCallback& callback,
437 int64 usage) {
438 info->limited_usage += usage;
439 if (--info->pending_jobs)
440 return;
441
442 callback.Run(info->limited_usage);
443 }
444
DidGetOriginsForGlobalUsage(const GlobalUsageCallback & callback,const std::set<GURL> & origins)445 void ClientUsageTracker::DidGetOriginsForGlobalUsage(
446 const GlobalUsageCallback& callback,
447 const std::set<GURL>& origins) {
448 OriginSetByHost origins_by_host;
449 for (std::set<GURL>::const_iterator itr = origins.begin();
450 itr != origins.end(); ++itr)
451 origins_by_host[net::GetHostOrSpecFromURL(*itr)].insert(*itr);
452
453 AccumulateInfo* info = new AccumulateInfo;
454 // Getting host usage may synchronously return the result if the usage is
455 // cached, which may in turn dispatch the completion callback before we finish
456 // looping over all hosts (because info->pending_jobs may reach 0 during the
457 // loop). To avoid this, we add one more pending host as a sentinel and
458 // fire the sentinel callback at the end.
459 info->pending_jobs = origins_by_host.size() + 1;
460 HostUsageAccumulator accumulator =
461 base::Bind(&ClientUsageTracker::AccumulateHostUsage, AsWeakPtr(),
462 base::Owned(info), callback);
463
464 for (OriginSetByHost::iterator itr = origins_by_host.begin();
465 itr != origins_by_host.end(); ++itr) {
466 if (host_usage_accumulators_.Add(itr->first, accumulator))
467 GetUsageForOrigins(itr->first, itr->second);
468 }
469
470 // Fire the sentinel as we've now called GetUsageForOrigins for all clients.
471 accumulator.Run(0, 0);
472 }
473
AccumulateHostUsage(AccumulateInfo * info,const GlobalUsageCallback & callback,int64 limited_usage,int64 unlimited_usage)474 void ClientUsageTracker::AccumulateHostUsage(
475 AccumulateInfo* info,
476 const GlobalUsageCallback& callback,
477 int64 limited_usage,
478 int64 unlimited_usage) {
479 info->limited_usage += limited_usage;
480 info->unlimited_usage += unlimited_usage;
481 if (--info->pending_jobs)
482 return;
483
484 DCHECK_GE(info->limited_usage, 0);
485 DCHECK_GE(info->unlimited_usage, 0);
486
487 global_usage_retrieved_ = true;
488 callback.Run(info->limited_usage + info->unlimited_usage,
489 info->unlimited_usage);
490 }
491
DidGetOriginsForHostUsage(const std::string & host,const std::set<GURL> & origins)492 void ClientUsageTracker::DidGetOriginsForHostUsage(
493 const std::string& host,
494 const std::set<GURL>& origins) {
495 GetUsageForOrigins(host, origins);
496 }
497
GetUsageForOrigins(const std::string & host,const std::set<GURL> & origins)498 void ClientUsageTracker::GetUsageForOrigins(
499 const std::string& host,
500 const std::set<GURL>& origins) {
501 AccumulateInfo* info = new AccumulateInfo;
502 // Getting origin usage may synchronously return the result if the usage is
503 // cached, which may in turn dispatch the completion callback before we finish
504 // looping over all origins (because info->pending_jobs may reach 0 during the
505 // loop). To avoid this, we add one more pending origin as a sentinel and
506 // fire the sentinel callback at the end.
507 info->pending_jobs = origins.size() + 1;
508 OriginUsageAccumulator accumulator =
509 base::Bind(&ClientUsageTracker::AccumulateOriginUsage, AsWeakPtr(),
510 base::Owned(info), host);
511
512 for (std::set<GURL>::const_iterator itr = origins.begin();
513 itr != origins.end(); ++itr) {
514 DCHECK_EQ(host, net::GetHostOrSpecFromURL(*itr));
515
516 int64 origin_usage = 0;
517 if (GetCachedOriginUsage(*itr, &origin_usage)) {
518 accumulator.Run(*itr, origin_usage);
519 } else {
520 client_->GetOriginUsage(*itr, type_, base::Bind(
521 &DidGetOriginUsage, accumulator, *itr));
522 }
523 }
524
525 // Fire the sentinel as we've now called GetOriginUsage for all clients.
526 accumulator.Run(GURL(), 0);
527 }
528
AccumulateOriginUsage(AccumulateInfo * info,const std::string & host,const GURL & origin,int64 usage)529 void ClientUsageTracker::AccumulateOriginUsage(AccumulateInfo* info,
530 const std::string& host,
531 const GURL& origin,
532 int64 usage) {
533 if (!origin.is_empty()) {
534 if (usage < 0)
535 usage = 0;
536
537 if (IsStorageUnlimited(origin))
538 info->unlimited_usage += usage;
539 else
540 info->limited_usage += usage;
541 if (IsUsageCacheEnabledForOrigin(origin))
542 AddCachedOrigin(origin, usage);
543 }
544 if (--info->pending_jobs)
545 return;
546
547 AddCachedHost(host);
548 host_usage_accumulators_.Run(
549 host, MakeTuple(info->limited_usage, info->unlimited_usage));
550 }
551
AddCachedOrigin(const GURL & origin,int64 new_usage)552 void ClientUsageTracker::AddCachedOrigin(
553 const GURL& origin, int64 new_usage) {
554 DCHECK(IsUsageCacheEnabledForOrigin(origin));
555
556 std::string host = net::GetHostOrSpecFromURL(origin);
557 int64* usage = &cached_usage_by_host_[host][origin];
558 int64 delta = new_usage - *usage;
559 *usage = new_usage;
560 if (delta) {
561 if (IsStorageUnlimited(origin))
562 global_unlimited_usage_ += delta;
563 else
564 global_limited_usage_ += delta;
565 }
566 DCHECK_GE(*usage, 0);
567 DCHECK_GE(global_limited_usage_, 0);
568 }
569
AddCachedHost(const std::string & host)570 void ClientUsageTracker::AddCachedHost(const std::string& host) {
571 cached_hosts_.insert(host);
572 }
573
GetCachedHostUsage(const std::string & host) const574 int64 ClientUsageTracker::GetCachedHostUsage(const std::string& host) const {
575 HostUsageMap::const_iterator found = cached_usage_by_host_.find(host);
576 if (found == cached_usage_by_host_.end())
577 return 0;
578
579 int64 usage = 0;
580 const UsageMap& map = found->second;
581 for (UsageMap::const_iterator iter = map.begin();
582 iter != map.end(); ++iter) {
583 usage += iter->second;
584 }
585 return usage;
586 }
587
GetCachedOriginUsage(const GURL & origin,int64 * usage) const588 bool ClientUsageTracker::GetCachedOriginUsage(
589 const GURL& origin,
590 int64* usage) const {
591 std::string host = net::GetHostOrSpecFromURL(origin);
592 HostUsageMap::const_iterator found_host = cached_usage_by_host_.find(host);
593 if (found_host == cached_usage_by_host_.end())
594 return false;
595
596 UsageMap::const_iterator found = found_host->second.find(origin);
597 if (found == found_host->second.end())
598 return false;
599
600 DCHECK(IsUsageCacheEnabledForOrigin(origin));
601 *usage = found->second;
602 return true;
603 }
604
IsUsageCacheEnabledForOrigin(const GURL & origin) const605 bool ClientUsageTracker::IsUsageCacheEnabledForOrigin(
606 const GURL& origin) const {
607 std::string host = net::GetHostOrSpecFromURL(origin);
608 return !OriginSetContainsOrigin(non_cached_limited_origins_by_host_,
609 host, origin) &&
610 !OriginSetContainsOrigin(non_cached_unlimited_origins_by_host_,
611 host, origin);
612 }
613
OnGranted(const GURL & origin,int change_flags)614 void ClientUsageTracker::OnGranted(const GURL& origin,
615 int change_flags) {
616 DCHECK(CalledOnValidThread());
617 if (change_flags & SpecialStoragePolicy::STORAGE_UNLIMITED) {
618 int64 usage = 0;
619 if (GetCachedOriginUsage(origin, &usage)) {
620 global_unlimited_usage_ += usage;
621 global_limited_usage_ -= usage;
622 }
623
624 std::string host = net::GetHostOrSpecFromURL(origin);
625 if (EraseOriginFromOriginSet(&non_cached_limited_origins_by_host_,
626 host, origin))
627 non_cached_unlimited_origins_by_host_[host].insert(origin);
628 }
629 }
630
OnRevoked(const GURL & origin,int change_flags)631 void ClientUsageTracker::OnRevoked(const GURL& origin,
632 int change_flags) {
633 DCHECK(CalledOnValidThread());
634 if (change_flags & SpecialStoragePolicy::STORAGE_UNLIMITED) {
635 int64 usage = 0;
636 if (GetCachedOriginUsage(origin, &usage)) {
637 global_unlimited_usage_ -= usage;
638 global_limited_usage_ += usage;
639 }
640
641 std::string host = net::GetHostOrSpecFromURL(origin);
642 if (EraseOriginFromOriginSet(&non_cached_unlimited_origins_by_host_,
643 host, origin))
644 non_cached_limited_origins_by_host_[host].insert(origin);
645 }
646 }
647
OnCleared()648 void ClientUsageTracker::OnCleared() {
649 DCHECK(CalledOnValidThread());
650 global_limited_usage_ += global_unlimited_usage_;
651 global_unlimited_usage_ = 0;
652
653 for (OriginSetByHost::const_iterator host_itr =
654 non_cached_unlimited_origins_by_host_.begin();
655 host_itr != non_cached_unlimited_origins_by_host_.end();
656 ++host_itr) {
657 for (std::set<GURL>::const_iterator origin_itr = host_itr->second.begin();
658 origin_itr != host_itr->second.end();
659 ++origin_itr)
660 non_cached_limited_origins_by_host_[host_itr->first].insert(*origin_itr);
661 }
662 non_cached_unlimited_origins_by_host_.clear();
663 }
664
IsStorageUnlimited(const GURL & origin) const665 bool ClientUsageTracker::IsStorageUnlimited(const GURL& origin) const {
666 if (type_ == kStorageTypeSyncable)
667 return false;
668 return special_storage_policy_.get() &&
669 special_storage_policy_->IsStorageUnlimited(origin);
670 }
671
672 } // namespace quota
673