1 // Copyright 2014 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 "storage/browser/quota/client_usage_tracker.h"
8 #include "base/stl_util.h"
9 #include "net/base/net_util.h"
10 #include "storage/browser/quota/storage_monitor.h"
11 #include "storage/browser/quota/storage_observer.h"
17 typedef ClientUsageTracker::OriginUsageAccumulator OriginUsageAccumulator
;
18 typedef ClientUsageTracker::OriginSetByHost OriginSetByHost
;
20 void DidGetHostUsage(const UsageCallback
& callback
,
22 int64 unlimited_usage
) {
23 DCHECK_GE(limited_usage
, 0);
24 DCHECK_GE(unlimited_usage
, 0);
25 callback
.Run(limited_usage
+ unlimited_usage
);
28 bool EraseOriginFromOriginSet(OriginSetByHost
* origins_by_host
,
29 const std::string
& host
,
31 OriginSetByHost::iterator found
= origins_by_host
->find(host
);
32 if (found
== origins_by_host
->end())
35 if (!found
->second
.erase(origin
))
38 if (found
->second
.empty())
39 origins_by_host
->erase(host
);
43 bool OriginSetContainsOrigin(const OriginSetByHost
& origins
,
44 const std::string
& host
,
46 OriginSetByHost::const_iterator itr
= origins
.find(host
);
47 return itr
!= origins
.end() && ContainsKey(itr
->second
, origin
);
50 void DidGetGlobalUsageForLimitedGlobalUsage(const UsageCallback
& callback
,
51 int64 total_global_usage
,
52 int64 global_unlimited_usage
) {
53 callback
.Run(total_global_usage
- global_unlimited_usage
);
58 ClientUsageTracker::ClientUsageTracker(
59 UsageTracker
* tracker
, QuotaClient
* client
, StorageType type
,
60 SpecialStoragePolicy
* special_storage_policy
,
61 StorageMonitor
* storage_monitor
)
65 storage_monitor_(storage_monitor
),
66 global_limited_usage_(0),
67 global_unlimited_usage_(0),
68 global_usage_retrieved_(false),
69 special_storage_policy_(special_storage_policy
) {
72 if (special_storage_policy_
.get())
73 special_storage_policy_
->AddObserver(this);
76 ClientUsageTracker::~ClientUsageTracker() {
77 if (special_storage_policy_
.get())
78 special_storage_policy_
->RemoveObserver(this);
81 void ClientUsageTracker::GetGlobalLimitedUsage(const UsageCallback
& callback
) {
82 if (!global_usage_retrieved_
) {
83 GetGlobalUsage(base::Bind(&DidGetGlobalUsageForLimitedGlobalUsage
,
88 if (non_cached_limited_origins_by_host_
.empty()) {
89 callback
.Run(global_limited_usage_
);
93 AccumulateInfo
* info
= new AccumulateInfo
;
94 info
->pending_jobs
= non_cached_limited_origins_by_host_
.size() + 1;
95 UsageCallback accumulator
= base::Bind(
96 &ClientUsageTracker::AccumulateLimitedOriginUsage
, AsWeakPtr(),
97 base::Owned(info
), callback
);
99 for (const auto& host_and_origins
: non_cached_limited_origins_by_host_
) {
100 for (const auto& origin
: host_and_origins
.second
)
101 client_
->GetOriginUsage(origin
, type_
, accumulator
);
104 accumulator
.Run(global_limited_usage_
);
107 void ClientUsageTracker::GetGlobalUsage(const GlobalUsageCallback
& callback
) {
108 if (global_usage_retrieved_
&&
109 non_cached_limited_origins_by_host_
.empty() &&
110 non_cached_unlimited_origins_by_host_
.empty()) {
111 callback
.Run(global_limited_usage_
+ global_unlimited_usage_
,
112 global_unlimited_usage_
);
116 client_
->GetOriginsForType(type_
, base::Bind(
117 &ClientUsageTracker::DidGetOriginsForGlobalUsage
, AsWeakPtr(),
121 void ClientUsageTracker::GetHostUsage(
122 const std::string
& host
, const UsageCallback
& callback
) {
123 if (ContainsKey(cached_hosts_
, host
) &&
124 !ContainsKey(non_cached_limited_origins_by_host_
, host
) &&
125 !ContainsKey(non_cached_unlimited_origins_by_host_
, host
)) {
126 // TODO(kinuko): Drop host_usage_map_ cache periodically.
127 callback
.Run(GetCachedHostUsage(host
));
131 if (!host_usage_accumulators_
.Add(
132 host
, base::Bind(&DidGetHostUsage
, callback
)))
134 client_
->GetOriginsForHost(type_
, host
, base::Bind(
135 &ClientUsageTracker::DidGetOriginsForHostUsage
, AsWeakPtr(), host
));
138 void ClientUsageTracker::UpdateUsageCache(
139 const GURL
& origin
, int64 delta
) {
140 std::string host
= net::GetHostOrSpecFromURL(origin
);
141 if (cached_hosts_
.find(host
) != cached_hosts_
.end()) {
142 if (!IsUsageCacheEnabledForOrigin(origin
))
145 cached_usage_by_host_
[host
][origin
] += delta
;
146 if (IsStorageUnlimited(origin
))
147 global_unlimited_usage_
+= delta
;
149 global_limited_usage_
+= delta
;
150 DCHECK_GE(cached_usage_by_host_
[host
][origin
], 0);
151 DCHECK_GE(global_limited_usage_
, 0);
153 // Notify the usage monitor that usage has changed. The storage monitor may
154 // be NULL during tests.
155 if (storage_monitor_
) {
156 StorageObserver::Filter
filter(type_
, origin
);
157 storage_monitor_
->NotifyUsageChange(filter
, delta
);
162 // We don't know about this host yet, so populate our cache for it.
163 GetHostUsage(host
, base::Bind(&ClientUsageTracker::DidGetHostUsageAfterUpdate
,
164 AsWeakPtr(), origin
));
167 void ClientUsageTracker::GetCachedHostsUsage(
168 std::map
<std::string
, int64
>* host_usage
) const {
170 for (const auto& host_and_usage_map
: cached_usage_by_host_
) {
171 const std::string
& host
= host_and_usage_map
.first
;
172 (*host_usage
)[host
] += GetCachedHostUsage(host
);
176 void ClientUsageTracker::GetCachedOrigins(std::set
<GURL
>* origins
) const {
178 for (const auto& host_and_usage_map
: cached_usage_by_host_
) {
179 for (const auto& origin_and_usage
: host_and_usage_map
.second
)
180 origins
->insert(origin_and_usage
.first
);
184 void ClientUsageTracker::SetUsageCacheEnabled(const GURL
& origin
,
186 std::string host
= net::GetHostOrSpecFromURL(origin
);
188 // Erase |origin| from cache and subtract its usage.
189 HostUsageMap::iterator found_host
= cached_usage_by_host_
.find(host
);
190 if (found_host
!= cached_usage_by_host_
.end()) {
191 UsageMap
& cached_usage_for_host
= found_host
->second
;
193 UsageMap::iterator found
= cached_usage_for_host
.find(origin
);
194 if (found
!= cached_usage_for_host
.end()) {
195 int64 usage
= found
->second
;
196 UpdateUsageCache(origin
, -usage
);
197 cached_usage_for_host
.erase(found
);
198 if (cached_usage_for_host
.empty()) {
199 cached_usage_by_host_
.erase(found_host
);
200 cached_hosts_
.erase(host
);
205 if (IsStorageUnlimited(origin
))
206 non_cached_unlimited_origins_by_host_
[host
].insert(origin
);
208 non_cached_limited_origins_by_host_
[host
].insert(origin
);
210 // Erase |origin| from |non_cached_origins_| and invalidate the usage cache
212 if (EraseOriginFromOriginSet(&non_cached_limited_origins_by_host_
,
214 EraseOriginFromOriginSet(&non_cached_unlimited_origins_by_host_
,
216 cached_hosts_
.erase(host
);
217 global_usage_retrieved_
= false;
222 void ClientUsageTracker::AccumulateLimitedOriginUsage(
223 AccumulateInfo
* info
,
224 const UsageCallback
& callback
,
226 info
->limited_usage
+= usage
;
227 if (--info
->pending_jobs
)
230 callback
.Run(info
->limited_usage
);
233 void ClientUsageTracker::DidGetOriginsForGlobalUsage(
234 const GlobalUsageCallback
& callback
,
235 const std::set
<GURL
>& origins
) {
236 OriginSetByHost origins_by_host
;
237 for (const auto& origin
: origins
)
238 origins_by_host
[net::GetHostOrSpecFromURL(origin
)].insert(origin
);
240 AccumulateInfo
* info
= new AccumulateInfo
;
241 // Getting host usage may synchronously return the result if the usage is
242 // cached, which may in turn dispatch the completion callback before we finish
243 // looping over all hosts (because info->pending_jobs may reach 0 during the
244 // loop). To avoid this, we add one more pending host as a sentinel and
245 // fire the sentinel callback at the end.
246 info
->pending_jobs
= origins_by_host
.size() + 1;
247 HostUsageAccumulator accumulator
=
248 base::Bind(&ClientUsageTracker::AccumulateHostUsage
, AsWeakPtr(),
249 base::Owned(info
), callback
);
251 for (const auto& host_and_origins
: origins_by_host
) {
252 const std::string
& host
= host_and_origins
.first
;
253 const std::set
<GURL
>& origins
= host_and_origins
.second
;
254 if (host_usage_accumulators_
.Add(host
, accumulator
))
255 GetUsageForOrigins(host
, origins
);
258 // Fire the sentinel as we've now called GetUsageForOrigins for all clients.
259 accumulator
.Run(0, 0);
262 void ClientUsageTracker::AccumulateHostUsage(
263 AccumulateInfo
* info
,
264 const GlobalUsageCallback
& callback
,
266 int64 unlimited_usage
) {
267 info
->limited_usage
+= limited_usage
;
268 info
->unlimited_usage
+= unlimited_usage
;
269 if (--info
->pending_jobs
)
272 DCHECK_GE(info
->limited_usage
, 0);
273 DCHECK_GE(info
->unlimited_usage
, 0);
275 global_usage_retrieved_
= true;
276 callback
.Run(info
->limited_usage
+ info
->unlimited_usage
,
277 info
->unlimited_usage
);
280 void ClientUsageTracker::DidGetOriginsForHostUsage(
281 const std::string
& host
,
282 const std::set
<GURL
>& origins
) {
283 GetUsageForOrigins(host
, origins
);
286 void ClientUsageTracker::GetUsageForOrigins(
287 const std::string
& host
,
288 const std::set
<GURL
>& origins
) {
289 AccumulateInfo
* info
= new AccumulateInfo
;
290 // Getting origin usage may synchronously return the result if the usage is
291 // cached, which may in turn dispatch the completion callback before we finish
292 // looping over all origins (because info->pending_jobs may reach 0 during the
293 // loop). To avoid this, we add one more pending origin as a sentinel and
294 // fire the sentinel callback at the end.
295 info
->pending_jobs
= origins
.size() + 1;
296 OriginUsageAccumulator accumulator
=
297 base::Bind(&ClientUsageTracker::AccumulateOriginUsage
, AsWeakPtr(),
298 base::Owned(info
), host
);
300 for (const auto& origin
: origins
) {
301 DCHECK_EQ(host
, net::GetHostOrSpecFromURL(origin
));
303 int64 origin_usage
= 0;
304 if (GetCachedOriginUsage(origin
, &origin_usage
))
305 accumulator
.Run(origin
, origin_usage
);
307 client_
->GetOriginUsage(origin
, type_
, base::Bind(accumulator
, origin
));
310 // Fire the sentinel as we've now called GetOriginUsage for all clients.
311 accumulator
.Run(GURL(), 0);
314 void ClientUsageTracker::AccumulateOriginUsage(AccumulateInfo
* info
,
315 const std::string
& host
,
318 if (!origin
.is_empty()) {
322 if (IsStorageUnlimited(origin
))
323 info
->unlimited_usage
+= usage
;
325 info
->limited_usage
+= usage
;
326 if (IsUsageCacheEnabledForOrigin(origin
))
327 AddCachedOrigin(origin
, usage
);
329 if (--info
->pending_jobs
)
333 host_usage_accumulators_
.Run(
334 host
, info
->limited_usage
, info
->unlimited_usage
);
337 void ClientUsageTracker::DidGetHostUsageAfterUpdate(
338 const GURL
& origin
, int64 usage
) {
339 if (!storage_monitor_
)
342 StorageObserver::Filter
filter(type_
, origin
);
343 storage_monitor_
->NotifyUsageChange(filter
, 0);
346 void ClientUsageTracker::AddCachedOrigin(
347 const GURL
& origin
, int64 new_usage
) {
348 DCHECK(IsUsageCacheEnabledForOrigin(origin
));
350 std::string host
= net::GetHostOrSpecFromURL(origin
);
351 int64
* usage
= &cached_usage_by_host_
[host
][origin
];
352 int64 delta
= new_usage
- *usage
;
355 if (IsStorageUnlimited(origin
))
356 global_unlimited_usage_
+= delta
;
358 global_limited_usage_
+= delta
;
360 DCHECK_GE(*usage
, 0);
361 DCHECK_GE(global_limited_usage_
, 0);
364 void ClientUsageTracker::AddCachedHost(const std::string
& host
) {
365 cached_hosts_
.insert(host
);
368 int64
ClientUsageTracker::GetCachedHostUsage(const std::string
& host
) const {
369 HostUsageMap::const_iterator found
= cached_usage_by_host_
.find(host
);
370 if (found
== cached_usage_by_host_
.end())
374 const UsageMap
& usage_map
= found
->second
;
375 for (const auto& origin_and_usage
: usage_map
)
376 usage
+= origin_and_usage
.second
;
380 bool ClientUsageTracker::GetCachedOriginUsage(
382 int64
* usage
) const {
383 std::string host
= net::GetHostOrSpecFromURL(origin
);
384 HostUsageMap::const_iterator found_host
= cached_usage_by_host_
.find(host
);
385 if (found_host
== cached_usage_by_host_
.end())
388 UsageMap::const_iterator found
= found_host
->second
.find(origin
);
389 if (found
== found_host
->second
.end())
392 DCHECK(IsUsageCacheEnabledForOrigin(origin
));
393 *usage
= found
->second
;
397 bool ClientUsageTracker::IsUsageCacheEnabledForOrigin(
398 const GURL
& origin
) const {
399 std::string host
= net::GetHostOrSpecFromURL(origin
);
400 return !OriginSetContainsOrigin(non_cached_limited_origins_by_host_
,
402 !OriginSetContainsOrigin(non_cached_unlimited_origins_by_host_
,
406 void ClientUsageTracker::OnGranted(const GURL
& origin
,
408 DCHECK(CalledOnValidThread());
409 if (change_flags
& SpecialStoragePolicy::STORAGE_UNLIMITED
) {
411 if (GetCachedOriginUsage(origin
, &usage
)) {
412 global_unlimited_usage_
+= usage
;
413 global_limited_usage_
-= usage
;
416 std::string host
= net::GetHostOrSpecFromURL(origin
);
417 if (EraseOriginFromOriginSet(&non_cached_limited_origins_by_host_
,
419 non_cached_unlimited_origins_by_host_
[host
].insert(origin
);
423 void ClientUsageTracker::OnRevoked(const GURL
& origin
,
425 DCHECK(CalledOnValidThread());
426 if (change_flags
& SpecialStoragePolicy::STORAGE_UNLIMITED
) {
428 if (GetCachedOriginUsage(origin
, &usage
)) {
429 global_unlimited_usage_
-= usage
;
430 global_limited_usage_
+= usage
;
433 std::string host
= net::GetHostOrSpecFromURL(origin
);
434 if (EraseOriginFromOriginSet(&non_cached_unlimited_origins_by_host_
,
436 non_cached_limited_origins_by_host_
[host
].insert(origin
);
440 void ClientUsageTracker::OnCleared() {
441 DCHECK(CalledOnValidThread());
442 global_limited_usage_
+= global_unlimited_usage_
;
443 global_unlimited_usage_
= 0;
445 for (const auto& host_and_origins
: non_cached_unlimited_origins_by_host_
) {
446 const auto& host
= host_and_origins
.first
;
447 for (const auto& origin
: host_and_origins
.second
)
448 non_cached_limited_origins_by_host_
[host
].insert(origin
);
450 non_cached_unlimited_origins_by_host_
.clear();
453 bool ClientUsageTracker::IsStorageUnlimited(const GURL
& origin
) const {
454 if (type_
== kStorageTypeSyncable
)
456 return special_storage_policy_
.get() &&
457 special_storage_policy_
->IsStorageUnlimited(origin
);
460 } // namespace storage