Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / storage / browser / quota / client_usage_tracker.cc
blob968b35181a18aaa0651d3dd75dbe46174e5c7541
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"
7 #include "base/bind.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"
13 namespace storage {
15 namespace {
17 typedef ClientUsageTracker::OriginUsageAccumulator OriginUsageAccumulator;
18 typedef ClientUsageTracker::OriginSetByHost OriginSetByHost;
20 void DidGetHostUsage(const UsageCallback& callback,
21 int64 limited_usage,
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,
30 const GURL& origin) {
31 OriginSetByHost::iterator found = origins_by_host->find(host);
32 if (found == origins_by_host->end())
33 return false;
35 if (!found->second.erase(origin))
36 return false;
38 if (found->second.empty())
39 origins_by_host->erase(host);
40 return true;
43 bool OriginSetContainsOrigin(const OriginSetByHost& origins,
44 const std::string& host,
45 const GURL& origin) {
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);
56 } // namespace
58 ClientUsageTracker::ClientUsageTracker(
59 UsageTracker* tracker, QuotaClient* client, StorageType type,
60 SpecialStoragePolicy* special_storage_policy,
61 StorageMonitor* storage_monitor)
62 : tracker_(tracker),
63 client_(client),
64 type_(type),
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) {
70 DCHECK(tracker_);
71 DCHECK(client_);
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,
84 callback));
85 return;
88 if (non_cached_limited_origins_by_host_.empty()) {
89 callback.Run(global_limited_usage_);
90 return;
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_);
113 return;
116 client_->GetOriginsForType(type_, base::Bind(
117 &ClientUsageTracker::DidGetOriginsForGlobalUsage, AsWeakPtr(),
118 callback));
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));
128 return;
131 if (!host_usage_accumulators_.Add(
132 host, base::Bind(&DidGetHostUsage, callback)))
133 return;
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))
143 return;
145 cached_usage_by_host_[host][origin] += delta;
146 if (IsStorageUnlimited(origin))
147 global_unlimited_usage_ += delta;
148 else
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);
159 return;
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 {
169 DCHECK(host_usage);
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 {
177 DCHECK(origins);
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,
185 bool enabled) {
186 std::string host = net::GetHostOrSpecFromURL(origin);
187 if (!enabled) {
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);
207 else
208 non_cached_limited_origins_by_host_[host].insert(origin);
209 } else {
210 // Erase |origin| from |non_cached_origins_| and invalidate the usage cache
211 // for the host.
212 if (EraseOriginFromOriginSet(&non_cached_limited_origins_by_host_,
213 host, origin) ||
214 EraseOriginFromOriginSet(&non_cached_unlimited_origins_by_host_,
215 host, origin)) {
216 cached_hosts_.erase(host);
217 global_usage_retrieved_ = false;
222 void ClientUsageTracker::AccumulateLimitedOriginUsage(
223 AccumulateInfo* info,
224 const UsageCallback& callback,
225 int64 usage) {
226 info->limited_usage += usage;
227 if (--info->pending_jobs)
228 return;
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,
265 int64 limited_usage,
266 int64 unlimited_usage) {
267 info->limited_usage += limited_usage;
268 info->unlimited_usage += unlimited_usage;
269 if (--info->pending_jobs)
270 return;
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);
306 else
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,
316 const GURL& origin,
317 int64 usage) {
318 if (!origin.is_empty()) {
319 if (usage < 0)
320 usage = 0;
322 if (IsStorageUnlimited(origin))
323 info->unlimited_usage += usage;
324 else
325 info->limited_usage += usage;
326 if (IsUsageCacheEnabledForOrigin(origin))
327 AddCachedOrigin(origin, usage);
329 if (--info->pending_jobs)
330 return;
332 AddCachedHost(host);
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_)
340 return;
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;
353 *usage = new_usage;
354 if (delta) {
355 if (IsStorageUnlimited(origin))
356 global_unlimited_usage_ += delta;
357 else
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())
371 return 0;
373 int64 usage = 0;
374 const UsageMap& usage_map = found->second;
375 for (const auto& origin_and_usage : usage_map)
376 usage += origin_and_usage.second;
377 return usage;
380 bool ClientUsageTracker::GetCachedOriginUsage(
381 const GURL& origin,
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())
386 return false;
388 UsageMap::const_iterator found = found_host->second.find(origin);
389 if (found == found_host->second.end())
390 return false;
392 DCHECK(IsUsageCacheEnabledForOrigin(origin));
393 *usage = found->second;
394 return true;
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_,
401 host, origin) &&
402 !OriginSetContainsOrigin(non_cached_unlimited_origins_by_host_,
403 host, origin);
406 void ClientUsageTracker::OnGranted(const GURL& origin,
407 int change_flags) {
408 DCHECK(CalledOnValidThread());
409 if (change_flags & SpecialStoragePolicy::STORAGE_UNLIMITED) {
410 int64 usage = 0;
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_,
418 host, origin))
419 non_cached_unlimited_origins_by_host_[host].insert(origin);
423 void ClientUsageTracker::OnRevoked(const GURL& origin,
424 int change_flags) {
425 DCHECK(CalledOnValidThread());
426 if (change_flags & SpecialStoragePolicy::STORAGE_UNLIMITED) {
427 int64 usage = 0;
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_,
435 host, origin))
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)
455 return false;
456 return special_storage_policy_.get() &&
457 special_storage_policy_->IsStorageUnlimited(origin);
460 } // namespace storage