Include all dupe types (event when value is zero) in scan stats.
[chromium-blink-merge.git] / storage / browser / quota / quota_manager.cc
blob13b84c74106b217e77f4d68b99c9842808339a9e
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.
5 #include "storage/browser/quota/quota_manager.h"
7 #include <algorithm>
8 #include <functional>
10 #include "base/bind.h"
11 #include "base/bind_helpers.h"
12 #include "base/command_line.h"
13 #include "base/files/file_util.h"
14 #include "base/metrics/histogram.h"
15 #include "base/profiler/scoped_tracker.h"
16 #include "base/sequenced_task_runner.h"
17 #include "base/single_thread_task_runner.h"
18 #include "base/strings/string_number_conversions.h"
19 #include "base/sys_info.h"
20 #include "base/task_runner_util.h"
21 #include "base/time/time.h"
22 #include "net/base/net_util.h"
23 #include "storage/browser/quota/client_usage_tracker.h"
24 #include "storage/browser/quota/quota_manager_proxy.h"
25 #include "storage/browser/quota/quota_temporary_storage_evictor.h"
26 #include "storage/browser/quota/storage_monitor.h"
27 #include "storage/browser/quota/usage_tracker.h"
28 #include "storage/common/quota/quota_types.h"
30 #define UMA_HISTOGRAM_MBYTES(name, sample) \
31 UMA_HISTOGRAM_CUSTOM_COUNTS( \
32 (name), static_cast<int>((sample) / kMBytes), \
33 1, 10 * 1024 * 1024 /* 10TB */, 100)
35 namespace storage {
37 namespace {
39 const int64 kMBytes = 1024 * 1024;
40 const int kMinutesInMilliSeconds = 60 * 1000;
42 const int64 kReportHistogramInterval = 60 * 60 * 1000; // 1 hour
43 const double kTemporaryQuotaRatioToAvail = 1.0 / 3.0; // 33%
45 } // namespace
47 // Arbitrary for now, but must be reasonably small so that
48 // in-memory databases can fit.
49 // TODO(kinuko): Refer SysInfo::AmountOfPhysicalMemory() to determine this.
50 const int64 QuotaManager::kIncognitoDefaultQuotaLimit = 100 * kMBytes;
52 const int64 QuotaManager::kNoLimit = kint64max;
54 const int QuotaManager::kPerHostTemporaryPortion = 5; // 20%
56 // Cap size for per-host persistent quota determined by the histogram.
57 // This is a bit lax value because the histogram says nothing about per-host
58 // persistent storage usage and we determined by global persistent storage
59 // usage that is less than 10GB for almost all users.
60 const int64 QuotaManager::kPerHostPersistentQuotaLimit = 10 * 1024 * kMBytes;
62 const char QuotaManager::kDatabaseName[] = "QuotaManager";
64 const int QuotaManager::kThresholdOfErrorsToBeBlacklisted = 3;
66 // Preserve kMinimumPreserveForSystem disk space for system book-keeping
67 // when returning the quota to unlimited apps/extensions.
68 // TODO(kinuko): This should be like 10% of the actual disk space.
69 // For now we simply use a constant as getting the disk size needs
70 // platform-dependent code. (http://crbug.com/178976)
71 int64 QuotaManager::kMinimumPreserveForSystem = 1024 * kMBytes;
73 const int QuotaManager::kEvictionIntervalInMilliSeconds =
74 30 * kMinutesInMilliSeconds;
76 // Heuristics: assuming average cloud server allows a few Gigs storage
77 // on the server side and the storage needs to be shared for user data
78 // and by multiple apps.
79 int64 QuotaManager::kSyncableStorageDefaultHostQuota = 500 * kMBytes;
81 namespace {
83 void CountOriginType(const std::set<GURL>& origins,
84 SpecialStoragePolicy* policy,
85 size_t* protected_origins,
86 size_t* unlimited_origins) {
87 DCHECK(protected_origins);
88 DCHECK(unlimited_origins);
89 *protected_origins = 0;
90 *unlimited_origins = 0;
91 if (!policy)
92 return;
93 for (std::set<GURL>::const_iterator itr = origins.begin();
94 itr != origins.end();
95 ++itr) {
96 if (policy->IsStorageProtected(*itr))
97 ++*protected_origins;
98 if (policy->IsStorageUnlimited(*itr))
99 ++*unlimited_origins;
103 bool SetTemporaryGlobalOverrideQuotaOnDBThread(int64* new_quota,
104 QuotaDatabase* database) {
105 DCHECK(database);
106 if (!database->SetQuotaConfigValue(
107 QuotaDatabase::kTemporaryQuotaOverrideKey, *new_quota)) {
108 *new_quota = -1;
109 return false;
111 return true;
114 bool GetPersistentHostQuotaOnDBThread(const std::string& host,
115 int64* quota,
116 QuotaDatabase* database) {
117 DCHECK(database);
118 database->GetHostQuota(host, kStorageTypePersistent, quota);
119 return true;
122 bool SetPersistentHostQuotaOnDBThread(const std::string& host,
123 int64* new_quota,
124 QuotaDatabase* database) {
125 DCHECK(database);
126 if (database->SetHostQuota(host, kStorageTypePersistent, *new_quota))
127 return true;
128 *new_quota = 0;
129 return false;
132 bool InitializeOnDBThread(int64* temporary_quota_override,
133 int64* desired_available_space,
134 QuotaDatabase* database) {
135 DCHECK(database);
136 database->GetQuotaConfigValue(QuotaDatabase::kTemporaryQuotaOverrideKey,
137 temporary_quota_override);
138 database->GetQuotaConfigValue(QuotaDatabase::kDesiredAvailableSpaceKey,
139 desired_available_space);
140 return true;
143 bool GetLRUOriginOnDBThread(StorageType type,
144 std::set<GURL>* exceptions,
145 SpecialStoragePolicy* policy,
146 GURL* url,
147 QuotaDatabase* database) {
148 DCHECK(database);
149 database->GetLRUOrigin(type, *exceptions, policy, url);
150 return true;
153 bool DeleteOriginInfoOnDBThread(const GURL& origin,
154 StorageType type,
155 QuotaDatabase* database) {
156 DCHECK(database);
157 return database->DeleteOriginInfo(origin, type);
160 bool InitializeTemporaryOriginsInfoOnDBThread(const std::set<GURL>* origins,
161 QuotaDatabase* database) {
162 DCHECK(database);
163 if (database->IsOriginDatabaseBootstrapped())
164 return true;
166 // Register existing origins with 0 last time access.
167 if (database->RegisterInitialOriginInfo(*origins, kStorageTypeTemporary)) {
168 database->SetOriginDatabaseBootstrapped(true);
169 return true;
171 return false;
174 bool UpdateAccessTimeOnDBThread(const GURL& origin,
175 StorageType type,
176 base::Time accessed_time,
177 QuotaDatabase* database) {
178 DCHECK(database);
179 return database->SetOriginLastAccessTime(origin, type, accessed_time);
182 bool UpdateModifiedTimeOnDBThread(const GURL& origin,
183 StorageType type,
184 base::Time modified_time,
185 QuotaDatabase* database) {
186 DCHECK(database);
187 return database->SetOriginLastModifiedTime(origin, type, modified_time);
190 int64 CallSystemGetAmountOfFreeDiskSpace(const base::FilePath& profile_path) {
191 // Ensure the profile path exists.
192 if (!base::CreateDirectory(profile_path)) {
193 LOG(WARNING) << "Create directory failed for path" << profile_path.value();
194 return 0;
196 return base::SysInfo::AmountOfFreeDiskSpace(profile_path);
199 int64 CalculateTemporaryGlobalQuota(int64 global_limited_usage,
200 int64 available_space) {
201 DCHECK_GE(global_limited_usage, 0);
202 int64 avail_space = available_space;
203 if (avail_space < kint64max - global_limited_usage) {
204 // We basically calculate the temporary quota by
205 // [available_space + space_used_for_temp] * kTempQuotaRatio,
206 // but make sure we'll have no overflow.
207 avail_space += global_limited_usage;
209 return avail_space * kTemporaryQuotaRatioToAvail;
212 void DispatchTemporaryGlobalQuotaCallback(
213 const QuotaCallback& callback,
214 QuotaStatusCode status,
215 const UsageAndQuota& usage_and_quota) {
216 if (status != kQuotaStatusOk) {
217 callback.Run(status, 0);
218 return;
221 callback.Run(status, CalculateTemporaryGlobalQuota(
222 usage_and_quota.global_limited_usage,
223 usage_and_quota.available_disk_space));
226 int64 CalculateQuotaWithDiskSpace(
227 int64 available_disk_space, int64 usage, int64 quota) {
228 if (available_disk_space < QuotaManager::kMinimumPreserveForSystem) {
229 LOG(WARNING)
230 << "Running out of disk space for profile."
231 << " QuotaManager starts forbidding further quota consumption.";
232 return usage;
235 if (quota < usage) {
236 // No more space; cap the quota to the current usage.
237 return usage;
240 available_disk_space -= QuotaManager::kMinimumPreserveForSystem;
241 if (available_disk_space < quota - usage)
242 return available_disk_space + usage;
244 return quota;
247 int64 CalculateTemporaryHostQuota(int64 host_usage,
248 int64 global_quota,
249 int64 global_limited_usage) {
250 DCHECK_GE(global_limited_usage, 0);
251 int64 host_quota = global_quota / QuotaManager::kPerHostTemporaryPortion;
252 if (global_limited_usage > global_quota)
253 host_quota = std::min(host_quota, host_usage);
254 return host_quota;
257 void DispatchUsageAndQuotaForWebApps(
258 StorageType type,
259 bool is_incognito,
260 bool is_unlimited,
261 bool can_query_disk_size,
262 const QuotaManager::GetUsageAndQuotaCallback& callback,
263 QuotaStatusCode status,
264 const UsageAndQuota& usage_and_quota) {
265 if (status != kQuotaStatusOk) {
266 callback.Run(status, 0, 0);
267 return;
270 int64 usage = usage_and_quota.usage;
271 int64 quota = usage_and_quota.quota;
273 if (type == kStorageTypeTemporary && !is_unlimited) {
274 quota = CalculateTemporaryHostQuota(
275 usage, quota, usage_and_quota.global_limited_usage);
278 if (is_incognito) {
279 quota = std::min(quota, QuotaManager::kIncognitoDefaultQuotaLimit);
280 callback.Run(status, usage, quota);
281 return;
284 // For apps with unlimited permission or can_query_disk_size is true (and not
285 // in incognito mode).
286 // We assume we can expose the actual disk size for them and cap the quota by
287 // the available disk space.
288 if (is_unlimited || can_query_disk_size) {
289 callback.Run(
290 status, usage,
291 CalculateQuotaWithDiskSpace(
292 usage_and_quota.available_disk_space,
293 usage, quota));
294 return;
297 callback.Run(status, usage, quota);
300 } // namespace
302 UsageAndQuota::UsageAndQuota()
303 : usage(0),
304 global_limited_usage(0),
305 quota(0),
306 available_disk_space(0) {
309 UsageAndQuota::UsageAndQuota(
310 int64 usage,
311 int64 global_limited_usage,
312 int64 quota,
313 int64 available_disk_space)
314 : usage(usage),
315 global_limited_usage(global_limited_usage),
316 quota(quota),
317 available_disk_space(available_disk_space) {
320 class UsageAndQuotaCallbackDispatcher
321 : public QuotaTask,
322 public base::SupportsWeakPtr<UsageAndQuotaCallbackDispatcher> {
323 public:
324 explicit UsageAndQuotaCallbackDispatcher(QuotaManager* manager)
325 : QuotaTask(manager),
326 has_usage_(false),
327 has_global_limited_usage_(false),
328 has_quota_(false),
329 has_available_disk_space_(false),
330 status_(kQuotaStatusUnknown),
331 usage_and_quota_(-1, -1, -1, -1),
332 waiting_callbacks_(1) {}
334 ~UsageAndQuotaCallbackDispatcher() override {}
336 void WaitForResults(const QuotaManager::UsageAndQuotaCallback& callback) {
337 callback_ = callback;
338 Start();
341 void set_usage(int64 usage) {
342 usage_and_quota_.usage = usage;
343 has_usage_ = true;
346 void set_global_limited_usage(int64 global_limited_usage) {
347 usage_and_quota_.global_limited_usage = global_limited_usage;
348 has_global_limited_usage_ = true;
351 void set_quota(int64 quota) {
352 usage_and_quota_.quota = quota;
353 has_quota_ = true;
356 void set_available_disk_space(int64 available_disk_space) {
357 usage_and_quota_.available_disk_space = available_disk_space;
358 has_available_disk_space_ = true;
361 UsageCallback GetHostUsageCallback() {
362 ++waiting_callbacks_;
363 has_usage_ = true;
364 return base::Bind(&UsageAndQuotaCallbackDispatcher::DidGetHostUsage,
365 AsWeakPtr());
368 UsageCallback GetGlobalLimitedUsageCallback() {
369 ++waiting_callbacks_;
370 has_global_limited_usage_ = true;
371 return base::Bind(
372 &UsageAndQuotaCallbackDispatcher::DidGetGlobalLimitedUsage,
373 AsWeakPtr());
376 QuotaCallback GetQuotaCallback() {
377 ++waiting_callbacks_;
378 has_quota_ = true;
379 return base::Bind(&UsageAndQuotaCallbackDispatcher::DidGetQuota,
380 AsWeakPtr());
383 QuotaCallback GetAvailableSpaceCallback() {
384 ++waiting_callbacks_;
385 has_available_disk_space_ = true;
386 return base::Bind(&UsageAndQuotaCallbackDispatcher::DidGetAvailableSpace,
387 AsWeakPtr());
390 private:
391 void DidGetHostUsage(int64 usage) {
392 if (status_ == kQuotaStatusUnknown)
393 status_ = kQuotaStatusOk;
394 usage_and_quota_.usage = usage;
395 CheckCompleted();
398 void DidGetGlobalLimitedUsage(int64 limited_usage) {
399 if (status_ == kQuotaStatusUnknown)
400 status_ = kQuotaStatusOk;
401 usage_and_quota_.global_limited_usage = limited_usage;
402 CheckCompleted();
405 void DidGetQuota(QuotaStatusCode status, int64 quota) {
406 if (status_ == kQuotaStatusUnknown || status_ == kQuotaStatusOk)
407 status_ = status;
408 usage_and_quota_.quota = quota;
409 CheckCompleted();
412 void DidGetAvailableSpace(QuotaStatusCode status, int64 space) {
413 DCHECK_GE(space, 0);
414 if (status_ == kQuotaStatusUnknown || status_ == kQuotaStatusOk)
415 status_ = status;
416 usage_and_quota_.available_disk_space = space;
417 CheckCompleted();
420 void Run() override {
421 // We initialize waiting_callbacks to 1 so that we won't run
422 // the completion callback until here even some of the callbacks
423 // are dispatched synchronously.
424 CheckCompleted();
427 void Aborted() override {
428 callback_.Run(kQuotaErrorAbort, UsageAndQuota());
429 DeleteSoon();
432 void Completed() override {
433 DCHECK(!has_usage_ || usage_and_quota_.usage >= 0);
434 DCHECK(!has_global_limited_usage_ ||
435 usage_and_quota_.global_limited_usage >= 0);
436 DCHECK(!has_quota_ || usage_and_quota_.quota >= 0);
437 DCHECK(!has_available_disk_space_ ||
438 usage_and_quota_.available_disk_space >= 0);
440 callback_.Run(status_, usage_and_quota_);
441 DeleteSoon();
444 void CheckCompleted() {
445 if (--waiting_callbacks_ <= 0)
446 CallCompleted();
449 // For sanity checks, they're checked only when DCHECK is on.
450 bool has_usage_;
451 bool has_global_limited_usage_;
452 bool has_quota_;
453 bool has_available_disk_space_;
455 QuotaStatusCode status_;
456 UsageAndQuota usage_and_quota_;
457 QuotaManager::UsageAndQuotaCallback callback_;
458 int waiting_callbacks_;
460 DISALLOW_COPY_AND_ASSIGN(UsageAndQuotaCallbackDispatcher);
463 class QuotaManager::GetUsageInfoTask : public QuotaTask {
464 public:
465 GetUsageInfoTask(
466 QuotaManager* manager,
467 const GetUsageInfoCallback& callback)
468 : QuotaTask(manager),
469 callback_(callback),
470 weak_factory_(this) {
473 protected:
474 void Run() override {
475 remaining_trackers_ = 3;
476 // This will populate cached hosts and usage info.
477 manager()->GetUsageTracker(kStorageTypeTemporary)->GetGlobalUsage(
478 base::Bind(&GetUsageInfoTask::DidGetGlobalUsage,
479 weak_factory_.GetWeakPtr(),
480 kStorageTypeTemporary));
481 manager()->GetUsageTracker(kStorageTypePersistent)->GetGlobalUsage(
482 base::Bind(&GetUsageInfoTask::DidGetGlobalUsage,
483 weak_factory_.GetWeakPtr(),
484 kStorageTypePersistent));
485 manager()->GetUsageTracker(kStorageTypeSyncable)->GetGlobalUsage(
486 base::Bind(&GetUsageInfoTask::DidGetGlobalUsage,
487 weak_factory_.GetWeakPtr(),
488 kStorageTypeSyncable));
491 void Completed() override {
492 callback_.Run(entries_);
493 DeleteSoon();
496 void Aborted() override {
497 callback_.Run(UsageInfoEntries());
498 DeleteSoon();
501 private:
502 void AddEntries(StorageType type, UsageTracker* tracker) {
503 std::map<std::string, int64> host_usage;
504 tracker->GetCachedHostsUsage(&host_usage);
505 for (std::map<std::string, int64>::const_iterator iter = host_usage.begin();
506 iter != host_usage.end();
507 ++iter) {
508 entries_.push_back(UsageInfo(iter->first, type, iter->second));
510 if (--remaining_trackers_ == 0)
511 CallCompleted();
514 void DidGetGlobalUsage(StorageType type, int64, int64) {
515 DCHECK(manager()->GetUsageTracker(type));
516 AddEntries(type, manager()->GetUsageTracker(type));
519 QuotaManager* manager() const {
520 return static_cast<QuotaManager*>(observer());
523 GetUsageInfoCallback callback_;
524 UsageInfoEntries entries_;
525 int remaining_trackers_;
526 base::WeakPtrFactory<GetUsageInfoTask> weak_factory_;
528 DISALLOW_COPY_AND_ASSIGN(GetUsageInfoTask);
531 class QuotaManager::OriginDataDeleter : public QuotaTask {
532 public:
533 OriginDataDeleter(QuotaManager* manager,
534 const GURL& origin,
535 StorageType type,
536 int quota_client_mask,
537 const StatusCallback& callback)
538 : QuotaTask(manager),
539 origin_(origin),
540 type_(type),
541 quota_client_mask_(quota_client_mask),
542 error_count_(0),
543 remaining_clients_(-1),
544 skipped_clients_(0),
545 callback_(callback),
546 weak_factory_(this) {}
548 protected:
549 void Run() override {
550 error_count_ = 0;
551 remaining_clients_ = manager()->clients_.size();
552 for (QuotaClientList::iterator iter = manager()->clients_.begin();
553 iter != manager()->clients_.end(); ++iter) {
554 if (quota_client_mask_ & (*iter)->id()) {
555 (*iter)->DeleteOriginData(
556 origin_, type_,
557 base::Bind(&OriginDataDeleter::DidDeleteOriginData,
558 weak_factory_.GetWeakPtr()));
559 } else {
560 ++skipped_clients_;
561 if (--remaining_clients_ == 0)
562 CallCompleted();
567 void Completed() override {
568 if (error_count_ == 0) {
569 // Only remove the entire origin if we didn't skip any client types.
570 if (skipped_clients_ == 0)
571 manager()->DeleteOriginFromDatabase(origin_, type_);
572 callback_.Run(kQuotaStatusOk);
573 } else {
574 callback_.Run(kQuotaErrorInvalidModification);
576 DeleteSoon();
579 void Aborted() override {
580 callback_.Run(kQuotaErrorAbort);
581 DeleteSoon();
584 private:
585 void DidDeleteOriginData(QuotaStatusCode status) {
586 DCHECK_GT(remaining_clients_, 0);
588 if (status != kQuotaStatusOk)
589 ++error_count_;
591 if (--remaining_clients_ == 0)
592 CallCompleted();
595 QuotaManager* manager() const {
596 return static_cast<QuotaManager*>(observer());
599 GURL origin_;
600 StorageType type_;
601 int quota_client_mask_;
602 int error_count_;
603 int remaining_clients_;
604 int skipped_clients_;
605 StatusCallback callback_;
607 base::WeakPtrFactory<OriginDataDeleter> weak_factory_;
608 DISALLOW_COPY_AND_ASSIGN(OriginDataDeleter);
611 class QuotaManager::HostDataDeleter : public QuotaTask {
612 public:
613 HostDataDeleter(QuotaManager* manager,
614 const std::string& host,
615 StorageType type,
616 int quota_client_mask,
617 const StatusCallback& callback)
618 : QuotaTask(manager),
619 host_(host),
620 type_(type),
621 quota_client_mask_(quota_client_mask),
622 error_count_(0),
623 remaining_clients_(-1),
624 remaining_deleters_(-1),
625 callback_(callback),
626 weak_factory_(this) {}
628 protected:
629 void Run() override {
630 error_count_ = 0;
631 remaining_clients_ = manager()->clients_.size();
632 for (QuotaClientList::iterator iter = manager()->clients_.begin();
633 iter != manager()->clients_.end(); ++iter) {
634 (*iter)->GetOriginsForHost(
635 type_, host_,
636 base::Bind(&HostDataDeleter::DidGetOriginsForHost,
637 weak_factory_.GetWeakPtr()));
641 void Completed() override {
642 if (error_count_ == 0) {
643 callback_.Run(kQuotaStatusOk);
644 } else {
645 callback_.Run(kQuotaErrorInvalidModification);
647 DeleteSoon();
650 void Aborted() override {
651 callback_.Run(kQuotaErrorAbort);
652 DeleteSoon();
655 private:
656 void DidGetOriginsForHost(const std::set<GURL>& origins) {
657 DCHECK_GT(remaining_clients_, 0);
659 origins_.insert(origins.begin(), origins.end());
661 if (--remaining_clients_ == 0) {
662 if (!origins_.empty())
663 ScheduleOriginsDeletion();
664 else
665 CallCompleted();
669 void ScheduleOriginsDeletion() {
670 remaining_deleters_ = origins_.size();
671 for (std::set<GURL>::const_iterator p = origins_.begin();
672 p != origins_.end();
673 ++p) {
674 OriginDataDeleter* deleter =
675 new OriginDataDeleter(
676 manager(), *p, type_, quota_client_mask_,
677 base::Bind(&HostDataDeleter::DidDeleteOriginData,
678 weak_factory_.GetWeakPtr()));
679 deleter->Start();
683 void DidDeleteOriginData(QuotaStatusCode status) {
684 DCHECK_GT(remaining_deleters_, 0);
686 if (status != kQuotaStatusOk)
687 ++error_count_;
689 if (--remaining_deleters_ == 0)
690 CallCompleted();
693 QuotaManager* manager() const {
694 return static_cast<QuotaManager*>(observer());
697 std::string host_;
698 StorageType type_;
699 int quota_client_mask_;
700 std::set<GURL> origins_;
701 int error_count_;
702 int remaining_clients_;
703 int remaining_deleters_;
704 StatusCallback callback_;
706 base::WeakPtrFactory<HostDataDeleter> weak_factory_;
707 DISALLOW_COPY_AND_ASSIGN(HostDataDeleter);
710 class QuotaManager::GetModifiedSinceHelper {
711 public:
712 bool GetModifiedSinceOnDBThread(StorageType type,
713 base::Time modified_since,
714 QuotaDatabase* database) {
715 DCHECK(database);
716 return database->GetOriginsModifiedSince(type, &origins_, modified_since);
719 void DidGetModifiedSince(const base::WeakPtr<QuotaManager>& manager,
720 const GetOriginsCallback& callback,
721 StorageType type,
722 bool success) {
723 if (!manager) {
724 // The operation was aborted.
725 callback.Run(std::set<GURL>(), type);
726 return;
728 manager->DidDatabaseWork(success);
729 callback.Run(origins_, type);
732 private:
733 std::set<GURL> origins_;
736 class QuotaManager::DumpQuotaTableHelper {
737 public:
738 bool DumpQuotaTableOnDBThread(QuotaDatabase* database) {
739 DCHECK(database);
740 return database->DumpQuotaTable(
741 base::Bind(&DumpQuotaTableHelper::AppendEntry, base::Unretained(this)));
744 void DidDumpQuotaTable(const base::WeakPtr<QuotaManager>& manager,
745 const DumpQuotaTableCallback& callback,
746 bool success) {
747 if (!manager) {
748 // The operation was aborted.
749 callback.Run(QuotaTableEntries());
750 return;
752 manager->DidDatabaseWork(success);
753 callback.Run(entries_);
756 private:
757 bool AppendEntry(const QuotaTableEntry& entry) {
758 entries_.push_back(entry);
759 return true;
762 QuotaTableEntries entries_;
765 class QuotaManager::DumpOriginInfoTableHelper {
766 public:
767 bool DumpOriginInfoTableOnDBThread(QuotaDatabase* database) {
768 DCHECK(database);
769 return database->DumpOriginInfoTable(
770 base::Bind(&DumpOriginInfoTableHelper::AppendEntry,
771 base::Unretained(this)));
774 void DidDumpOriginInfoTable(const base::WeakPtr<QuotaManager>& manager,
775 const DumpOriginInfoTableCallback& callback,
776 bool success) {
777 if (!manager) {
778 // The operation was aborted.
779 callback.Run(OriginInfoTableEntries());
780 return;
782 manager->DidDatabaseWork(success);
783 callback.Run(entries_);
786 private:
787 bool AppendEntry(const OriginInfoTableEntry& entry) {
788 entries_.push_back(entry);
789 return true;
792 OriginInfoTableEntries entries_;
795 // QuotaManager ---------------------------------------------------------------
797 QuotaManager::QuotaManager(
798 bool is_incognito,
799 const base::FilePath& profile_path,
800 const scoped_refptr<base::SingleThreadTaskRunner>& io_thread,
801 const scoped_refptr<base::SequencedTaskRunner>& db_thread,
802 const scoped_refptr<SpecialStoragePolicy>& special_storage_policy)
803 : is_incognito_(is_incognito),
804 profile_path_(profile_path),
805 proxy_(new QuotaManagerProxy(this, io_thread)),
806 db_disabled_(false),
807 eviction_disabled_(false),
808 io_thread_(io_thread),
809 db_thread_(db_thread),
810 temporary_quota_initialized_(false),
811 temporary_quota_override_(-1),
812 desired_available_space_(-1),
813 special_storage_policy_(special_storage_policy),
814 get_disk_space_fn_(&CallSystemGetAmountOfFreeDiskSpace),
815 storage_monitor_(new StorageMonitor(this)),
816 weak_factory_(this) {
819 void QuotaManager::GetUsageInfo(const GetUsageInfoCallback& callback) {
820 LazyInitialize();
821 GetUsageInfoTask* get_usage_info = new GetUsageInfoTask(this, callback);
822 get_usage_info->Start();
825 void QuotaManager::GetUsageAndQuotaForWebApps(
826 const GURL& origin,
827 StorageType type,
828 const GetUsageAndQuotaCallback& callback) {
829 // TODO(pkasting): Remove ScopedTracker below once crbug.com/477117 is fixed.
830 tracked_objects::ScopedTracker tracking_profile(
831 FROM_HERE_WITH_EXPLICIT_FUNCTION(
832 "477117 QuotaManager::GetUsageAndQuotaForWebApps"));
833 if (type != kStorageTypeTemporary &&
834 type != kStorageTypePersistent &&
835 type != kStorageTypeSyncable) {
836 callback.Run(kQuotaErrorNotSupported, 0, 0);
837 return;
840 DCHECK(origin == origin.GetOrigin());
841 LazyInitialize();
843 bool unlimited = IsStorageUnlimited(origin, type);
844 bool can_query_disk_size = CanQueryDiskSize(origin);
846 UsageAndQuotaCallbackDispatcher* dispatcher =
847 new UsageAndQuotaCallbackDispatcher(this);
849 UsageAndQuota usage_and_quota;
850 if (unlimited) {
851 dispatcher->set_quota(kNoLimit);
852 } else {
853 if (type == kStorageTypeTemporary) {
854 GetUsageTracker(type)->GetGlobalLimitedUsage(
855 dispatcher->GetGlobalLimitedUsageCallback());
856 GetTemporaryGlobalQuota(dispatcher->GetQuotaCallback());
857 } else if (type == kStorageTypePersistent) {
858 GetPersistentHostQuota(net::GetHostOrSpecFromURL(origin),
859 dispatcher->GetQuotaCallback());
860 } else {
861 dispatcher->set_quota(kSyncableStorageDefaultHostQuota);
865 DCHECK(GetUsageTracker(type));
866 GetUsageTracker(type)->GetHostUsage(net::GetHostOrSpecFromURL(origin),
867 dispatcher->GetHostUsageCallback());
869 if (!is_incognito_ && (unlimited || can_query_disk_size))
870 GetAvailableSpace(dispatcher->GetAvailableSpaceCallback());
872 dispatcher->WaitForResults(base::Bind(
873 &DispatchUsageAndQuotaForWebApps,
874 type, is_incognito_, unlimited, can_query_disk_size,
875 callback));
878 void QuotaManager::GetUsageAndQuota(
879 const GURL& origin, StorageType type,
880 const GetUsageAndQuotaCallback& callback) {
881 DCHECK(origin == origin.GetOrigin());
883 if (IsStorageUnlimited(origin, type)) {
884 callback.Run(kQuotaStatusOk, 0, kNoLimit);
885 return;
888 GetUsageAndQuotaForWebApps(origin, type, callback);
891 void QuotaManager::NotifyStorageAccessed(
892 QuotaClient::ID client_id,
893 const GURL& origin, StorageType type) {
894 DCHECK(origin == origin.GetOrigin());
895 NotifyStorageAccessedInternal(client_id, origin, type, base::Time::Now());
898 void QuotaManager::NotifyStorageModified(
899 QuotaClient::ID client_id,
900 const GURL& origin, StorageType type, int64 delta) {
901 DCHECK(origin == origin.GetOrigin());
902 NotifyStorageModifiedInternal(client_id, origin, type, delta,
903 base::Time::Now());
906 void QuotaManager::NotifyOriginInUse(const GURL& origin) {
907 DCHECK(io_thread_->BelongsToCurrentThread());
908 origins_in_use_[origin]++;
911 void QuotaManager::NotifyOriginNoLongerInUse(const GURL& origin) {
912 DCHECK(io_thread_->BelongsToCurrentThread());
913 DCHECK(IsOriginInUse(origin));
914 int& count = origins_in_use_[origin];
915 if (--count == 0)
916 origins_in_use_.erase(origin);
919 void QuotaManager::SetUsageCacheEnabled(QuotaClient::ID client_id,
920 const GURL& origin,
921 StorageType type,
922 bool enabled) {
923 LazyInitialize();
924 DCHECK(GetUsageTracker(type));
925 GetUsageTracker(type)->SetUsageCacheEnabled(client_id, origin, enabled);
928 void QuotaManager::DeleteOriginData(
929 const GURL& origin, StorageType type, int quota_client_mask,
930 const StatusCallback& callback) {
931 LazyInitialize();
933 if (origin.is_empty() || clients_.empty()) {
934 callback.Run(kQuotaStatusOk);
935 return;
938 DCHECK(origin == origin.GetOrigin());
939 OriginDataDeleter* deleter =
940 new OriginDataDeleter(this, origin, type, quota_client_mask, callback);
941 deleter->Start();
944 void QuotaManager::DeleteHostData(const std::string& host,
945 StorageType type,
946 int quota_client_mask,
947 const StatusCallback& callback) {
948 LazyInitialize();
950 if (host.empty() || clients_.empty()) {
951 callback.Run(kQuotaStatusOk);
952 return;
955 HostDataDeleter* deleter =
956 new HostDataDeleter(this, host, type, quota_client_mask, callback);
957 deleter->Start();
960 void QuotaManager::GetAvailableSpace(const AvailableSpaceCallback& callback) {
961 if (!available_space_callbacks_.Add(callback))
962 return;
964 PostTaskAndReplyWithResult(db_thread_.get(),
965 FROM_HERE,
966 base::Bind(get_disk_space_fn_, profile_path_),
967 base::Bind(&QuotaManager::DidGetAvailableSpace,
968 weak_factory_.GetWeakPtr()));
971 void QuotaManager::GetTemporaryGlobalQuota(const QuotaCallback& callback) {
972 LazyInitialize();
973 if (!temporary_quota_initialized_) {
974 db_initialization_callbacks_.Add(base::Bind(
975 &QuotaManager::GetTemporaryGlobalQuota,
976 weak_factory_.GetWeakPtr(), callback));
977 return;
980 if (temporary_quota_override_ > 0) {
981 callback.Run(kQuotaStatusOk, temporary_quota_override_);
982 return;
985 UsageAndQuotaCallbackDispatcher* dispatcher =
986 new UsageAndQuotaCallbackDispatcher(this);
987 GetUsageTracker(kStorageTypeTemporary)->
988 GetGlobalLimitedUsage(dispatcher->GetGlobalLimitedUsageCallback());
989 GetAvailableSpace(dispatcher->GetAvailableSpaceCallback());
990 dispatcher->WaitForResults(
991 base::Bind(&DispatchTemporaryGlobalQuotaCallback, callback));
994 void QuotaManager::SetTemporaryGlobalOverrideQuota(
995 int64 new_quota, const QuotaCallback& callback) {
996 LazyInitialize();
998 if (new_quota < 0) {
999 if (!callback.is_null())
1000 callback.Run(kQuotaErrorInvalidModification, -1);
1001 return;
1004 if (db_disabled_) {
1005 if (!callback.is_null())
1006 callback.Run(kQuotaErrorInvalidAccess, -1);
1007 return;
1010 int64* new_quota_ptr = new int64(new_quota);
1011 PostTaskAndReplyWithResultForDBThread(
1012 FROM_HERE,
1013 base::Bind(&SetTemporaryGlobalOverrideQuotaOnDBThread,
1014 base::Unretained(new_quota_ptr)),
1015 base::Bind(&QuotaManager::DidSetTemporaryGlobalOverrideQuota,
1016 weak_factory_.GetWeakPtr(),
1017 callback,
1018 base::Owned(new_quota_ptr)));
1021 void QuotaManager::GetPersistentHostQuota(const std::string& host,
1022 const QuotaCallback& callback) {
1023 LazyInitialize();
1024 if (host.empty()) {
1025 // This could happen if we are called on file:///.
1026 // TODO(kinuko) We may want to respect --allow-file-access-from-files
1027 // command line switch.
1028 callback.Run(kQuotaStatusOk, 0);
1029 return;
1032 if (!persistent_host_quota_callbacks_.Add(host, callback))
1033 return;
1035 int64* quota_ptr = new int64(0);
1036 PostTaskAndReplyWithResultForDBThread(
1037 FROM_HERE,
1038 base::Bind(&GetPersistentHostQuotaOnDBThread,
1039 host,
1040 base::Unretained(quota_ptr)),
1041 base::Bind(&QuotaManager::DidGetPersistentHostQuota,
1042 weak_factory_.GetWeakPtr(),
1043 host,
1044 base::Owned(quota_ptr)));
1047 void QuotaManager::SetPersistentHostQuota(const std::string& host,
1048 int64 new_quota,
1049 const QuotaCallback& callback) {
1050 LazyInitialize();
1051 if (host.empty()) {
1052 // This could happen if we are called on file:///.
1053 callback.Run(kQuotaErrorNotSupported, 0);
1054 return;
1057 if (new_quota < 0) {
1058 callback.Run(kQuotaErrorInvalidModification, -1);
1059 return;
1062 if (kPerHostPersistentQuotaLimit < new_quota) {
1063 // Cap the requested size at the per-host quota limit.
1064 new_quota = kPerHostPersistentQuotaLimit;
1067 if (db_disabled_) {
1068 callback.Run(kQuotaErrorInvalidAccess, -1);
1069 return;
1072 int64* new_quota_ptr = new int64(new_quota);
1073 PostTaskAndReplyWithResultForDBThread(
1074 FROM_HERE,
1075 base::Bind(&SetPersistentHostQuotaOnDBThread,
1076 host,
1077 base::Unretained(new_quota_ptr)),
1078 base::Bind(&QuotaManager::DidSetPersistentHostQuota,
1079 weak_factory_.GetWeakPtr(),
1080 host,
1081 callback,
1082 base::Owned(new_quota_ptr)));
1085 void QuotaManager::GetGlobalUsage(StorageType type,
1086 const GlobalUsageCallback& callback) {
1087 LazyInitialize();
1088 DCHECK(GetUsageTracker(type));
1089 GetUsageTracker(type)->GetGlobalUsage(callback);
1092 void QuotaManager::GetHostUsage(const std::string& host,
1093 StorageType type,
1094 const UsageCallback& callback) {
1095 LazyInitialize();
1096 DCHECK(GetUsageTracker(type));
1097 GetUsageTracker(type)->GetHostUsage(host, callback);
1100 void QuotaManager::GetHostUsage(const std::string& host,
1101 StorageType type,
1102 QuotaClient::ID client_id,
1103 const UsageCallback& callback) {
1104 LazyInitialize();
1105 DCHECK(GetUsageTracker(type));
1106 ClientUsageTracker* tracker =
1107 GetUsageTracker(type)->GetClientTracker(client_id);
1108 if (!tracker) {
1109 callback.Run(0);
1110 return;
1112 tracker->GetHostUsage(host, callback);
1115 bool QuotaManager::IsTrackingHostUsage(StorageType type,
1116 QuotaClient::ID client_id) const {
1117 UsageTracker* tracker = GetUsageTracker(type);
1118 return tracker && tracker->GetClientTracker(client_id);
1121 void QuotaManager::GetStatistics(
1122 std::map<std::string, std::string>* statistics) {
1123 DCHECK(statistics);
1124 if (temporary_storage_evictor_) {
1125 std::map<std::string, int64> stats;
1126 temporary_storage_evictor_->GetStatistics(&stats);
1127 for (std::map<std::string, int64>::iterator p = stats.begin();
1128 p != stats.end();
1129 ++p)
1130 (*statistics)[p->first] = base::Int64ToString(p->second);
1134 bool QuotaManager::IsStorageUnlimited(const GURL& origin,
1135 StorageType type) const {
1136 // For syncable storage we should always enforce quota (since the
1137 // quota must be capped by the server limit).
1138 if (type == kStorageTypeSyncable)
1139 return false;
1140 if (type == kStorageTypeQuotaNotManaged)
1141 return true;
1142 return special_storage_policy_.get() &&
1143 special_storage_policy_->IsStorageUnlimited(origin);
1146 void QuotaManager::GetOriginsModifiedSince(StorageType type,
1147 base::Time modified_since,
1148 const GetOriginsCallback& callback) {
1149 LazyInitialize();
1150 GetModifiedSinceHelper* helper = new GetModifiedSinceHelper;
1151 PostTaskAndReplyWithResultForDBThread(
1152 FROM_HERE,
1153 base::Bind(&GetModifiedSinceHelper::GetModifiedSinceOnDBThread,
1154 base::Unretained(helper),
1155 type,
1156 modified_since),
1157 base::Bind(&GetModifiedSinceHelper::DidGetModifiedSince,
1158 base::Owned(helper),
1159 weak_factory_.GetWeakPtr(),
1160 callback,
1161 type));
1164 bool QuotaManager::ResetUsageTracker(StorageType type) {
1165 DCHECK(GetUsageTracker(type));
1166 if (GetUsageTracker(type)->IsWorking())
1167 return false;
1168 switch (type) {
1169 case kStorageTypeTemporary:
1170 temporary_usage_tracker_.reset(new UsageTracker(
1171 clients_, kStorageTypeTemporary, special_storage_policy_.get(),
1172 storage_monitor_.get()));
1173 return true;
1174 case kStorageTypePersistent:
1175 persistent_usage_tracker_.reset(new UsageTracker(
1176 clients_, kStorageTypePersistent, special_storage_policy_.get(),
1177 storage_monitor_.get()));
1178 return true;
1179 case kStorageTypeSyncable:
1180 syncable_usage_tracker_.reset(new UsageTracker(
1181 clients_, kStorageTypeSyncable, special_storage_policy_.get(),
1182 storage_monitor_.get()));
1183 return true;
1184 default:
1185 NOTREACHED();
1187 return true;
1190 void QuotaManager::AddStorageObserver(
1191 StorageObserver* observer, const StorageObserver::MonitorParams& params) {
1192 DCHECK(observer);
1193 storage_monitor_->AddObserver(observer, params);
1196 void QuotaManager::RemoveStorageObserver(StorageObserver* observer) {
1197 DCHECK(observer);
1198 storage_monitor_->RemoveObserver(observer);
1201 void QuotaManager::RemoveStorageObserverForFilter(
1202 StorageObserver* observer, const StorageObserver::Filter& filter) {
1203 DCHECK(observer);
1204 storage_monitor_->RemoveObserverForFilter(observer, filter);
1207 QuotaManager::~QuotaManager() {
1208 proxy_->manager_ = NULL;
1209 std::for_each(clients_.begin(), clients_.end(),
1210 std::mem_fun(&QuotaClient::OnQuotaManagerDestroyed));
1211 if (database_)
1212 db_thread_->DeleteSoon(FROM_HERE, database_.release());
1215 QuotaManager::EvictionContext::EvictionContext()
1216 : evicted_type(kStorageTypeUnknown) {
1219 QuotaManager::EvictionContext::~EvictionContext() {
1222 void QuotaManager::LazyInitialize() {
1223 DCHECK(io_thread_->BelongsToCurrentThread());
1224 if (database_) {
1225 // Initialization seems to be done already.
1226 return;
1229 // Use an empty path to open an in-memory only databse for incognito.
1230 database_.reset(new QuotaDatabase(is_incognito_ ? base::FilePath() :
1231 profile_path_.AppendASCII(kDatabaseName)));
1233 temporary_usage_tracker_.reset(new UsageTracker(
1234 clients_, kStorageTypeTemporary, special_storage_policy_.get(),
1235 storage_monitor_.get()));
1236 persistent_usage_tracker_.reset(new UsageTracker(
1237 clients_, kStorageTypePersistent, special_storage_policy_.get(),
1238 storage_monitor_.get()));
1239 syncable_usage_tracker_.reset(new UsageTracker(
1240 clients_, kStorageTypeSyncable, special_storage_policy_.get(),
1241 storage_monitor_.get()));
1243 int64* temporary_quota_override = new int64(-1);
1244 int64* desired_available_space = new int64(-1);
1245 PostTaskAndReplyWithResultForDBThread(
1246 FROM_HERE,
1247 base::Bind(&InitializeOnDBThread,
1248 base::Unretained(temporary_quota_override),
1249 base::Unretained(desired_available_space)),
1250 base::Bind(&QuotaManager::DidInitialize,
1251 weak_factory_.GetWeakPtr(),
1252 base::Owned(temporary_quota_override),
1253 base::Owned(desired_available_space)));
1256 void QuotaManager::RegisterClient(QuotaClient* client) {
1257 DCHECK(!database_.get());
1258 clients_.push_back(client);
1261 UsageTracker* QuotaManager::GetUsageTracker(StorageType type) const {
1262 switch (type) {
1263 case kStorageTypeTemporary:
1264 return temporary_usage_tracker_.get();
1265 case kStorageTypePersistent:
1266 return persistent_usage_tracker_.get();
1267 case kStorageTypeSyncable:
1268 return syncable_usage_tracker_.get();
1269 case kStorageTypeQuotaNotManaged:
1270 return NULL;
1271 case kStorageTypeUnknown:
1272 NOTREACHED();
1274 return NULL;
1277 void QuotaManager::GetCachedOrigins(
1278 StorageType type, std::set<GURL>* origins) {
1279 DCHECK(origins);
1280 LazyInitialize();
1281 DCHECK(GetUsageTracker(type));
1282 GetUsageTracker(type)->GetCachedOrigins(origins);
1285 void QuotaManager::NotifyStorageAccessedInternal(
1286 QuotaClient::ID client_id,
1287 const GURL& origin, StorageType type,
1288 base::Time accessed_time) {
1289 LazyInitialize();
1290 if (type == kStorageTypeTemporary && !lru_origin_callback_.is_null()) {
1291 // Record the accessed origins while GetLRUOrigin task is runing
1292 // to filter out them from eviction.
1293 access_notified_origins_.insert(origin);
1296 if (db_disabled_)
1297 return;
1298 PostTaskAndReplyWithResultForDBThread(
1299 FROM_HERE,
1300 base::Bind(&UpdateAccessTimeOnDBThread, origin, type, accessed_time),
1301 base::Bind(&QuotaManager::DidDatabaseWork,
1302 weak_factory_.GetWeakPtr()));
1305 void QuotaManager::NotifyStorageModifiedInternal(
1306 QuotaClient::ID client_id,
1307 const GURL& origin,
1308 StorageType type,
1309 int64 delta,
1310 base::Time modified_time) {
1311 LazyInitialize();
1312 DCHECK(GetUsageTracker(type));
1313 GetUsageTracker(type)->UpdateUsageCache(client_id, origin, delta);
1315 PostTaskAndReplyWithResultForDBThread(
1316 FROM_HERE,
1317 base::Bind(&UpdateModifiedTimeOnDBThread, origin, type, modified_time),
1318 base::Bind(&QuotaManager::DidDatabaseWork,
1319 weak_factory_.GetWeakPtr()));
1322 void QuotaManager::DumpQuotaTable(const DumpQuotaTableCallback& callback) {
1323 DumpQuotaTableHelper* helper = new DumpQuotaTableHelper;
1324 PostTaskAndReplyWithResultForDBThread(
1325 FROM_HERE,
1326 base::Bind(&DumpQuotaTableHelper::DumpQuotaTableOnDBThread,
1327 base::Unretained(helper)),
1328 base::Bind(&DumpQuotaTableHelper::DidDumpQuotaTable,
1329 base::Owned(helper),
1330 weak_factory_.GetWeakPtr(),
1331 callback));
1334 void QuotaManager::DumpOriginInfoTable(
1335 const DumpOriginInfoTableCallback& callback) {
1336 DumpOriginInfoTableHelper* helper = new DumpOriginInfoTableHelper;
1337 PostTaskAndReplyWithResultForDBThread(
1338 FROM_HERE,
1339 base::Bind(&DumpOriginInfoTableHelper::DumpOriginInfoTableOnDBThread,
1340 base::Unretained(helper)),
1341 base::Bind(&DumpOriginInfoTableHelper::DidDumpOriginInfoTable,
1342 base::Owned(helper),
1343 weak_factory_.GetWeakPtr(),
1344 callback));
1347 void QuotaManager::StartEviction() {
1348 DCHECK(!temporary_storage_evictor_.get());
1349 temporary_storage_evictor_.reset(new QuotaTemporaryStorageEvictor(
1350 this, kEvictionIntervalInMilliSeconds));
1351 if (desired_available_space_ >= 0)
1352 temporary_storage_evictor_->set_min_available_disk_space_to_start_eviction(
1353 desired_available_space_);
1354 temporary_storage_evictor_->Start();
1357 void QuotaManager::DeleteOriginFromDatabase(
1358 const GURL& origin, StorageType type) {
1359 LazyInitialize();
1360 if (db_disabled_)
1361 return;
1363 PostTaskAndReplyWithResultForDBThread(
1364 FROM_HERE,
1365 base::Bind(&DeleteOriginInfoOnDBThread, origin, type),
1366 base::Bind(&QuotaManager::DidDatabaseWork,
1367 weak_factory_.GetWeakPtr()));
1370 void QuotaManager::DidOriginDataEvicted(QuotaStatusCode status) {
1371 DCHECK(io_thread_->BelongsToCurrentThread());
1373 // We only try evict origins that are not in use, so basically
1374 // deletion attempt for eviction should not fail. Let's record
1375 // the origin if we get error and exclude it from future eviction
1376 // if the error happens consistently (> kThresholdOfErrorsToBeBlacklisted).
1377 if (status != kQuotaStatusOk)
1378 origins_in_error_[eviction_context_.evicted_origin]++;
1380 eviction_context_.evict_origin_data_callback.Run(status);
1381 eviction_context_.evict_origin_data_callback.Reset();
1384 void QuotaManager::ReportHistogram() {
1385 GetGlobalUsage(kStorageTypeTemporary,
1386 base::Bind(
1387 &QuotaManager::DidGetTemporaryGlobalUsageForHistogram,
1388 weak_factory_.GetWeakPtr()));
1389 GetGlobalUsage(kStorageTypePersistent,
1390 base::Bind(
1391 &QuotaManager::DidGetPersistentGlobalUsageForHistogram,
1392 weak_factory_.GetWeakPtr()));
1395 void QuotaManager::DidGetTemporaryGlobalUsageForHistogram(
1396 int64 usage,
1397 int64 unlimited_usage) {
1398 UMA_HISTOGRAM_MBYTES("Quota.GlobalUsageOfTemporaryStorage", usage);
1400 std::set<GURL> origins;
1401 GetCachedOrigins(kStorageTypeTemporary, &origins);
1403 size_t num_origins = origins.size();
1404 size_t protected_origins = 0;
1405 size_t unlimited_origins = 0;
1406 CountOriginType(origins,
1407 special_storage_policy_.get(),
1408 &protected_origins,
1409 &unlimited_origins);
1411 UMA_HISTOGRAM_COUNTS("Quota.NumberOfTemporaryStorageOrigins",
1412 num_origins);
1413 UMA_HISTOGRAM_COUNTS("Quota.NumberOfProtectedTemporaryStorageOrigins",
1414 protected_origins);
1415 UMA_HISTOGRAM_COUNTS("Quota.NumberOfUnlimitedTemporaryStorageOrigins",
1416 unlimited_origins);
1419 void QuotaManager::DidGetPersistentGlobalUsageForHistogram(
1420 int64 usage,
1421 int64 unlimited_usage) {
1422 UMA_HISTOGRAM_MBYTES("Quota.GlobalUsageOfPersistentStorage", usage);
1424 std::set<GURL> origins;
1425 GetCachedOrigins(kStorageTypePersistent, &origins);
1427 size_t num_origins = origins.size();
1428 size_t protected_origins = 0;
1429 size_t unlimited_origins = 0;
1430 CountOriginType(origins,
1431 special_storage_policy_.get(),
1432 &protected_origins,
1433 &unlimited_origins);
1435 UMA_HISTOGRAM_COUNTS("Quota.NumberOfPersistentStorageOrigins",
1436 num_origins);
1437 UMA_HISTOGRAM_COUNTS("Quota.NumberOfProtectedPersistentStorageOrigins",
1438 protected_origins);
1439 UMA_HISTOGRAM_COUNTS("Quota.NumberOfUnlimitedPersistentStorageOrigins",
1440 unlimited_origins);
1443 void QuotaManager::GetLRUOrigin(
1444 StorageType type,
1445 const GetLRUOriginCallback& callback) {
1446 LazyInitialize();
1447 // This must not be called while there's an in-flight task.
1448 DCHECK(lru_origin_callback_.is_null());
1449 lru_origin_callback_ = callback;
1450 if (db_disabled_) {
1451 lru_origin_callback_.Run(GURL());
1452 lru_origin_callback_.Reset();
1453 return;
1456 std::set<GURL>* exceptions = new std::set<GURL>;
1457 for (std::map<GURL, int>::const_iterator p = origins_in_use_.begin();
1458 p != origins_in_use_.end();
1459 ++p) {
1460 if (p->second > 0)
1461 exceptions->insert(p->first);
1463 for (std::map<GURL, int>::const_iterator p = origins_in_error_.begin();
1464 p != origins_in_error_.end();
1465 ++p) {
1466 if (p->second > QuotaManager::kThresholdOfErrorsToBeBlacklisted)
1467 exceptions->insert(p->first);
1470 GURL* url = new GURL;
1471 PostTaskAndReplyWithResultForDBThread(
1472 FROM_HERE,
1473 base::Bind(&GetLRUOriginOnDBThread,
1474 type,
1475 base::Owned(exceptions),
1476 special_storage_policy_,
1477 base::Unretained(url)),
1478 base::Bind(&QuotaManager::DidGetLRUOrigin,
1479 weak_factory_.GetWeakPtr(),
1480 base::Owned(url)));
1483 void QuotaManager::EvictOriginData(
1484 const GURL& origin,
1485 StorageType type,
1486 const EvictOriginDataCallback& callback) {
1487 DCHECK(io_thread_->BelongsToCurrentThread());
1488 DCHECK_EQ(type, kStorageTypeTemporary);
1490 eviction_context_.evicted_origin = origin;
1491 eviction_context_.evicted_type = type;
1492 eviction_context_.evict_origin_data_callback = callback;
1494 DeleteOriginData(origin, type, QuotaClient::kAllClientsMask,
1495 base::Bind(&QuotaManager::DidOriginDataEvicted,
1496 weak_factory_.GetWeakPtr()));
1499 void QuotaManager::GetUsageAndQuotaForEviction(
1500 const UsageAndQuotaCallback& callback) {
1501 DCHECK(io_thread_->BelongsToCurrentThread());
1502 LazyInitialize();
1504 UsageAndQuotaCallbackDispatcher* dispatcher =
1505 new UsageAndQuotaCallbackDispatcher(this);
1506 GetUsageTracker(kStorageTypeTemporary)->
1507 GetGlobalLimitedUsage(dispatcher->GetGlobalLimitedUsageCallback());
1508 GetTemporaryGlobalQuota(dispatcher->GetQuotaCallback());
1509 GetAvailableSpace(dispatcher->GetAvailableSpaceCallback());
1510 dispatcher->WaitForResults(callback);
1513 void QuotaManager::DidSetTemporaryGlobalOverrideQuota(
1514 const QuotaCallback& callback,
1515 const int64* new_quota,
1516 bool success) {
1517 QuotaStatusCode status = kQuotaErrorInvalidAccess;
1518 DidDatabaseWork(success);
1519 if (success) {
1520 temporary_quota_override_ = *new_quota;
1521 status = kQuotaStatusOk;
1524 if (callback.is_null())
1525 return;
1527 callback.Run(status, *new_quota);
1530 void QuotaManager::DidGetPersistentHostQuota(const std::string& host,
1531 const int64* quota,
1532 bool success) {
1533 DidDatabaseWork(success);
1534 persistent_host_quota_callbacks_.Run(host, kQuotaStatusOk, *quota);
1537 void QuotaManager::DidSetPersistentHostQuota(const std::string& host,
1538 const QuotaCallback& callback,
1539 const int64* new_quota,
1540 bool success) {
1541 DidDatabaseWork(success);
1542 callback.Run(success ? kQuotaStatusOk : kQuotaErrorInvalidAccess, *new_quota);
1545 void QuotaManager::DidInitialize(int64* temporary_quota_override,
1546 int64* desired_available_space,
1547 bool success) {
1548 temporary_quota_override_ = *temporary_quota_override;
1549 desired_available_space_ = *desired_available_space;
1550 temporary_quota_initialized_ = true;
1551 DidDatabaseWork(success);
1553 histogram_timer_.Start(FROM_HERE,
1554 base::TimeDelta::FromMilliseconds(
1555 kReportHistogramInterval),
1556 this, &QuotaManager::ReportHistogram);
1558 db_initialization_callbacks_.Run();
1559 GetTemporaryGlobalQuota(
1560 base::Bind(&QuotaManager::DidGetInitialTemporaryGlobalQuota,
1561 weak_factory_.GetWeakPtr()));
1564 void QuotaManager::DidGetLRUOrigin(const GURL* origin,
1565 bool success) {
1566 DidDatabaseWork(success);
1567 // Make sure the returned origin is (still) not in the origin_in_use_ set
1568 // and has not been accessed since we posted the task.
1569 if (origins_in_use_.find(*origin) != origins_in_use_.end() ||
1570 access_notified_origins_.find(*origin) != access_notified_origins_.end())
1571 lru_origin_callback_.Run(GURL());
1572 else
1573 lru_origin_callback_.Run(*origin);
1574 access_notified_origins_.clear();
1575 lru_origin_callback_.Reset();
1578 void QuotaManager::DidGetInitialTemporaryGlobalQuota(
1579 QuotaStatusCode status, int64 quota_unused) {
1580 if (eviction_disabled_)
1581 return;
1583 std::set<GURL>* origins = new std::set<GURL>;
1584 temporary_usage_tracker_->GetCachedOrigins(origins);
1585 // This will call the StartEviction() when initial origin registration
1586 // is completed.
1587 PostTaskAndReplyWithResultForDBThread(
1588 FROM_HERE,
1589 base::Bind(&InitializeTemporaryOriginsInfoOnDBThread,
1590 base::Owned(origins)),
1591 base::Bind(&QuotaManager::DidInitializeTemporaryOriginsInfo,
1592 weak_factory_.GetWeakPtr()));
1595 void QuotaManager::DidInitializeTemporaryOriginsInfo(bool success) {
1596 DidDatabaseWork(success);
1597 if (success)
1598 StartEviction();
1601 void QuotaManager::DidGetAvailableSpace(int64 space) {
1602 available_space_callbacks_.Run(kQuotaStatusOk, space);
1605 void QuotaManager::DidDatabaseWork(bool success) {
1606 db_disabled_ = !success;
1609 void QuotaManager::DeleteOnCorrectThread() const {
1610 if (!io_thread_->BelongsToCurrentThread() &&
1611 io_thread_->DeleteSoon(FROM_HERE, this)) {
1612 return;
1614 delete this;
1617 void QuotaManager::PostTaskAndReplyWithResultForDBThread(
1618 const tracked_objects::Location& from_here,
1619 const base::Callback<bool(QuotaDatabase*)>& task,
1620 const base::Callback<void(bool)>& reply) {
1621 // Deleting manager will post another task to DB thread to delete
1622 // |database_|, therefore we can be sure that database_ is alive when this
1623 // task runs.
1624 base::PostTaskAndReplyWithResult(
1625 db_thread_.get(),
1626 from_here,
1627 base::Bind(task, base::Unretained(database_.get())),
1628 reply);
1631 } // namespace storage