Separate Simple Backend creation from initialization.
[chromium-blink-merge.git] / webkit / quota / quota_manager.cc
blob02b5b19c5c81705ebfbd895ce2127bf4f8e3ca04
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "webkit/quota/quota_manager.h"
7 #include <algorithm>
8 #include <deque>
9 #include <functional>
10 #include <set>
12 #include "base/bind.h"
13 #include "base/bind_helpers.h"
14 #include "base/callback.h"
15 #include "base/command_line.h"
16 #include "base/file_util.h"
17 #include "base/files/file_path.h"
18 #include "base/metrics/histogram.h"
19 #include "base/sequenced_task_runner.h"
20 #include "base/single_thread_task_runner.h"
21 #include "base/strings/string_number_conversions.h"
22 #include "base/sys_info.h"
23 #include "base/task_runner_util.h"
24 #include "base/time.h"
25 #include "net/base/net_util.h"
26 #include "webkit/quota/quota_database.h"
27 #include "webkit/quota/quota_temporary_storage_evictor.h"
28 #include "webkit/quota/quota_types.h"
29 #include "webkit/quota/usage_tracker.h"
31 #define UMA_HISTOGRAM_MBYTES(name, sample) \
32 UMA_HISTOGRAM_CUSTOM_COUNTS( \
33 (name), static_cast<int>((sample) / kMBytes), \
34 1, 10 * 1024 * 1024 /* 10TB */, 100)
36 namespace quota {
38 namespace {
40 const int64 kMBytes = 1024 * 1024;
41 const int kMinutesInMilliSeconds = 60 * 1000;
43 const int64 kIncognitoDefaultTemporaryQuota = 50 * kMBytes;
44 const int64 kReportHistogramInterval = 60 * 60 * 1000; // 1 hour
45 const double kTemporaryQuotaRatioToAvail = 0.5; // 50%
47 void CountOriginType(const std::set<GURL>& origins,
48 SpecialStoragePolicy* policy,
49 size_t* protected_origins,
50 size_t* unlimited_origins) {
51 DCHECK(protected_origins);
52 DCHECK(unlimited_origins);
53 *protected_origins = 0;
54 *unlimited_origins = 0;
55 if (!policy)
56 return;
57 for (std::set<GURL>::const_iterator itr = origins.begin();
58 itr != origins.end();
59 ++itr) {
60 if (policy->IsStorageProtected(*itr))
61 ++*protected_origins;
62 if (policy->IsStorageUnlimited(*itr))
63 ++*unlimited_origins;
67 bool SetTemporaryGlobalOverrideQuotaOnDBThread(int64* new_quota,
68 QuotaDatabase* database) {
69 DCHECK(database);
70 if (!database->SetQuotaConfigValue(
71 QuotaDatabase::kTemporaryQuotaOverrideKey, *new_quota)) {
72 *new_quota = -1;
73 return false;
75 return true;
78 bool GetPersistentHostQuotaOnDBThread(const std::string& host,
79 int64* quota,
80 QuotaDatabase* database) {
81 DCHECK(database);
82 database->GetHostQuota(host, kStorageTypePersistent, quota);
83 return true;
86 bool SetPersistentHostQuotaOnDBThread(const std::string& host,
87 int64* new_quota,
88 QuotaDatabase* database) {
89 DCHECK(database);
90 if (database->SetHostQuota(host, kStorageTypePersistent, *new_quota))
91 return true;
92 *new_quota = 0;
93 return false;
96 bool InitializeOnDBThread(int64* temporary_quota_override,
97 int64* desired_available_space,
98 QuotaDatabase* database) {
99 DCHECK(database);
100 database->GetQuotaConfigValue(QuotaDatabase::kTemporaryQuotaOverrideKey,
101 temporary_quota_override);
102 database->GetQuotaConfigValue(QuotaDatabase::kDesiredAvailableSpaceKey,
103 desired_available_space);
104 return true;
107 bool GetLRUOriginOnDBThread(StorageType type,
108 std::set<GURL>* exceptions,
109 SpecialStoragePolicy* policy,
110 GURL* url,
111 QuotaDatabase* database) {
112 DCHECK(database);
113 database->GetLRUOrigin(type, *exceptions, policy, url);
114 return true;
117 bool DeleteOriginInfoOnDBThread(const GURL& origin,
118 StorageType type,
119 QuotaDatabase* database) {
120 DCHECK(database);
121 return database->DeleteOriginInfo(origin, type);
124 bool InitializeTemporaryOriginsInfoOnDBThread(const std::set<GURL>* origins,
125 QuotaDatabase* database) {
126 DCHECK(database);
127 if (database->IsOriginDatabaseBootstrapped())
128 return true;
130 // Register existing origins with 0 last time access.
131 if (database->RegisterInitialOriginInfo(*origins, kStorageTypeTemporary)) {
132 database->SetOriginDatabaseBootstrapped(true);
133 return true;
135 return false;
138 bool UpdateAccessTimeOnDBThread(const GURL& origin,
139 StorageType type,
140 base::Time accessed_time,
141 QuotaDatabase* database) {
142 DCHECK(database);
143 return database->SetOriginLastAccessTime(origin, type, accessed_time);
146 bool UpdateModifiedTimeOnDBThread(const GURL& origin,
147 StorageType type,
148 base::Time modified_time,
149 QuotaDatabase* database) {
150 DCHECK(database);
151 return database->SetOriginLastModifiedTime(origin, type, modified_time);
154 int64 CallSystemGetAmountOfFreeDiskSpace(const base::FilePath& profile_path) {
155 // Ensure the profile path exists.
156 if(!file_util::CreateDirectory(profile_path)) {
157 LOG(WARNING) << "Create directory failed for path" << profile_path.value();
158 return 0;
160 return base::SysInfo::AmountOfFreeDiskSpace(profile_path);
163 } // anonymous namespace
165 const int64 QuotaManager::kNoLimit = kint64max;
167 const int QuotaManager::kPerHostTemporaryPortion = 5; // 20%
169 const char QuotaManager::kDatabaseName[] = "QuotaManager";
171 // Preserve kMinimumPreserveForSystem disk space for system book-keeping
172 // when returning the quota to unlimited apps/extensions.
173 // TODO(kinuko): This should be like 10% of the actual disk space.
174 // For now we simply use a constant as getting the disk size needs
175 // platform-dependent code. (http://crbug.com/178976)
176 const int64 QuotaManager::kMinimumPreserveForSystem = 1024 * kMBytes;
178 const int QuotaManager::kThresholdOfErrorsToBeBlacklisted = 3;
180 const int QuotaManager::kEvictionIntervalInMilliSeconds =
181 30 * kMinutesInMilliSeconds;
183 // Heuristics: assuming average cloud server allows a few Gigs storage
184 // on the server side and the storage needs to be shared for user data
185 // and by multiple apps.
186 int64 QuotaManager::kSyncableStorageDefaultHostQuota = 500 * kMBytes;
188 int64 CalculateQuotaForInstalledApp(
189 int64 available_disk_space, int64 usage, int64 quota) {
190 if (available_disk_space < QuotaManager::kMinimumPreserveForSystem) {
191 // No more space; cap the quota to the current usage.
192 return usage;
195 available_disk_space -= QuotaManager::kMinimumPreserveForSystem;
196 if (available_disk_space < quota - usage)
197 return available_disk_space + usage;
199 return quota;
202 // Callback translators.
203 void CallGetUsageAndQuotaCallback(
204 const QuotaManager::GetUsageAndQuotaCallback& callback,
205 bool unlimited,
206 bool is_installed_app,
207 QuotaStatusCode status,
208 const QuotaAndUsage& quota_and_usage) {
209 // Regular limited case.
210 if (!unlimited) {
211 if (is_installed_app) {
212 // Cap the quota by the available disk space.
213 callback.Run(status, quota_and_usage.usage,
214 CalculateQuotaForInstalledApp(
215 quota_and_usage.available_disk_space,
216 quota_and_usage.usage,
217 quota_and_usage.quota));
218 return;
220 callback.Run(status, quota_and_usage.usage, quota_and_usage.quota);
221 return;
224 int64 usage = quota_and_usage.unlimited_usage;
226 // Unlimited case: for non-installed apps just return unlimited quota.
227 // TODO(kinuko): We should probably always return the capped disk space
228 // for internal quota clients (while we still would not want to expose
229 // the actual usage to webapps). http://crbug.com/179040
230 if (!is_installed_app) {
231 callback.Run(status, usage, QuotaManager::kNoLimit);
232 return;
235 // For installed unlimited apps.
236 callback.Run(status, usage,
237 CalculateQuotaForInstalledApp(
238 quota_and_usage.available_disk_space,
239 usage, QuotaManager::kNoLimit));
242 void CallQuotaCallback(
243 const QuotaCallback& callback,
244 QuotaStatusCode status,
245 const QuotaAndUsage& quota_and_usage) {
246 callback.Run(status, quota_and_usage.quota);
249 // This class is for posting GetUsage/GetQuota tasks, gathering
250 // results and dispatching GetAndQuota callbacks.
251 // This class is self-destructed.
252 class QuotaManager::UsageAndQuotaDispatcherTask : public QuotaTask {
253 public:
254 typedef UsageAndQuotaDispatcherCallback Callback;
255 typedef std::deque<Callback> CallbackList;
257 static UsageAndQuotaDispatcherTask* Create(
258 QuotaManager* manager,
259 bool global,
260 const HostAndType& host_and_type);
262 // Returns true if it is the first call for this task; which means
263 // the caller needs to call Start().
264 bool AddCallback(const Callback& callback) {
265 callbacks_.push_back(callback);
266 return (callbacks_.size() == 1);
269 void DidGetGlobalUsage(StorageType type, int64 usage, int64 unlimited_usage) {
270 DCHECK_EQ(this->type(), type);
271 DCHECK_GE(usage, unlimited_usage);
272 if (quota_status_ == kQuotaStatusUnknown)
273 quota_status_ = kQuotaStatusOk;
274 global_usage_ = usage;
275 global_unlimited_usage_ = unlimited_usage;
276 CheckCompleted();
279 void DidGetHostUsage(const std::string& host, StorageType type, int64 usage) {
280 DCHECK_EQ(this->host(), host);
281 DCHECK_EQ(this->type(), type);
282 if (quota_status_ == kQuotaStatusUnknown)
283 quota_status_ = kQuotaStatusOk;
284 host_usage_ = usage;
285 CheckCompleted();
288 void DidGetHostQuota(const std::string& host,
289 StorageType type,
290 QuotaStatusCode status,
291 int64 host_quota) {
292 DCHECK_EQ(this->host(), host);
293 DCHECK_EQ(this->type(), type);
294 if (quota_status_ == kQuotaStatusUnknown || quota_status_ == kQuotaStatusOk)
295 quota_status_ = status;
296 host_quota_ = host_quota;
297 CheckCompleted();
300 void DidGetAvailableSpace(QuotaStatusCode status, int64 space) {
301 DCHECK_GE(space, 0);
302 if (quota_status_ == kQuotaStatusUnknown || quota_status_ == kQuotaStatusOk)
303 quota_status_ = status;
304 available_space_ = space;
305 CheckCompleted();
308 bool IsStartable() const {
309 return !started_ && !callbacks_.empty();
312 protected:
313 UsageAndQuotaDispatcherTask(
314 QuotaManager* manager,
315 const HostAndType& host_and_type)
316 : QuotaTask(manager),
317 host_and_type_(host_and_type),
318 started_(false),
319 host_quota_(-1),
320 global_usage_(-1),
321 global_unlimited_usage_(-1),
322 host_usage_(-1),
323 available_space_(-1),
324 quota_status_(kQuotaStatusUnknown),
325 waiting_callbacks_(1),
326 weak_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {}
328 virtual ~UsageAndQuotaDispatcherTask() {}
330 // Subclasses must implement them.
331 virtual void RunBody() = 0;
332 virtual void DispatchCallbacks() = 0;
334 virtual void Run() OVERRIDE {
335 DCHECK(!started_);
336 started_ = true;
337 RunBody();
338 // We initialize waiting_callbacks to 1 so that we won't run
339 // the completion callback until here even some of the callbacks
340 // are dispatched synchronously.
341 CheckCompleted();
344 virtual void Aborted() OVERRIDE {
345 CallCallbacksAndClear(kQuotaErrorAbort, 0, 0, 0, 0);
346 DeleteSoon();
349 virtual void Completed() OVERRIDE {
350 DeleteSoon();
353 void CallCallbacksAndClear(
354 QuotaStatusCode status,
355 int64 usage, int64 unlimited_usage, int64 quota,
356 int64 available_space) {
357 QuotaAndUsage qau = { usage, unlimited_usage, quota, available_space };
358 for (CallbackList::iterator iter = callbacks_.begin();
359 iter != callbacks_.end(); ++iter) {
360 (*iter).Run(status, qau);
362 callbacks_.clear();
365 QuotaManager* manager() const {
366 return static_cast<QuotaManager*>(observer());
369 std::string host() const { return host_and_type_.first; }
370 virtual StorageType type() const { return host_and_type_.second; }
371 int64 host_quota() const { return host_quota_; }
372 int64 global_usage() const { return global_usage_; }
373 int64 global_unlimited_usage() const { return global_unlimited_usage_; }
374 int64 host_usage() const { return host_usage_; }
375 int64 available_space() const { return available_space_; }
376 QuotaStatusCode quota_status() const { return quota_status_; }
377 CallbackList& callbacks() { return callbacks_; }
379 // The main logic that determines the temporary global quota.
380 int64 temporary_global_quota() const {
381 DCHECK_EQ(type(), kStorageTypeTemporary);
382 DCHECK(manager());
383 DCHECK_GE(global_usage(), global_unlimited_usage());
384 if (manager()->temporary_quota_override_ > 0) {
385 // If the user has specified an explicit temporary quota, use the value.
386 return manager()->temporary_quota_override_;
388 int64 limited_usage = global_usage() - global_unlimited_usage();
389 int64 avail_space = available_space();
390 if (avail_space < kint64max - limited_usage) {
391 // We basically calculate the temporary quota by
392 // [available_space + space_used_for_temp] * kTempQuotaRatio,
393 // but make sure we'll have no overflow.
394 avail_space += limited_usage;
396 return avail_space * kTemporaryQuotaRatioToAvail;
399 // Subclasses must call following methods to create a new 'waitable'
400 // callback, which decrements waiting_callbacks when it is called.
401 GlobalUsageCallback NewWaitableGlobalUsageCallback() {
402 ++waiting_callbacks_;
403 return base::Bind(&UsageAndQuotaDispatcherTask::DidGetGlobalUsage,
404 weak_factory_.GetWeakPtr());
406 UsageCallback NewWaitableHostUsageCallback() {
407 ++waiting_callbacks_;
408 return base::Bind(&UsageAndQuotaDispatcherTask::DidGetHostUsage,
409 weak_factory_.GetWeakPtr(), host(), type());
411 QuotaCallback NewWaitableHostQuotaCallback() {
412 ++waiting_callbacks_;
413 return base::Bind(&UsageAndQuotaDispatcherTask::DidGetHostQuota,
414 weak_factory_.GetWeakPtr(), host(), type());
416 AvailableSpaceCallback NewWaitableAvailableSpaceCallback() {
417 ++waiting_callbacks_;
418 return base::Bind(&UsageAndQuotaDispatcherTask::DidGetAvailableSpace,
419 weak_factory_.GetWeakPtr());
423 private:
424 void CheckCompleted() {
425 if (--waiting_callbacks_ <= 0) {
426 DispatchCallbacks();
427 DCHECK(callbacks_.empty());
429 UsageAndQuotaDispatcherTaskMap& dispatcher_map =
430 manager()->usage_and_quota_dispatchers_;
431 DCHECK(dispatcher_map.find(host_and_type_) != dispatcher_map.end());
432 dispatcher_map.erase(host_and_type_);
433 CallCompleted();
437 const std::string host_;
438 const HostAndType host_and_type_;
439 bool started_;
440 int64 host_quota_;
441 int64 global_usage_;
442 int64 global_unlimited_usage_;
443 int64 host_usage_;
444 int64 available_space_;
445 QuotaStatusCode quota_status_;
446 CallbackList callbacks_;
447 int waiting_callbacks_;
448 base::WeakPtrFactory<UsageAndQuotaDispatcherTask> weak_factory_;
450 DISALLOW_COPY_AND_ASSIGN(UsageAndQuotaDispatcherTask);
453 class QuotaManager::GetUsageInfoTask : public QuotaTask {
454 private:
455 typedef QuotaManager::GetUsageInfoTask self_type;
457 public:
458 GetUsageInfoTask(
459 QuotaManager* manager,
460 const GetUsageInfoCallback& callback)
461 : QuotaTask(manager),
462 callback_(callback),
463 weak_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
466 protected:
467 virtual void Run() OVERRIDE {
468 remaining_trackers_ = 3;
469 // This will populate cached hosts and usage info.
470 manager()->GetUsageTracker(kStorageTypeTemporary)->GetGlobalUsage(
471 base::Bind(&GetUsageInfoTask::DidGetGlobalUsage,
472 weak_factory_.GetWeakPtr()));
473 manager()->GetUsageTracker(kStorageTypePersistent)->GetGlobalUsage(
474 base::Bind(&GetUsageInfoTask::DidGetGlobalUsage,
475 weak_factory_.GetWeakPtr()));
476 manager()->GetUsageTracker(kStorageTypeSyncable)->GetGlobalUsage(
477 base::Bind(&GetUsageInfoTask::DidGetGlobalUsage,
478 weak_factory_.GetWeakPtr()));
481 virtual void Completed() OVERRIDE {
482 callback_.Run(entries_);
483 DeleteSoon();
486 virtual void Aborted() OVERRIDE {
487 callback_.Run(UsageInfoEntries());
488 DeleteSoon();
491 private:
492 void AddEntries(StorageType type, UsageTracker* tracker) {
493 std::map<std::string, int64> host_usage;
494 tracker->GetCachedHostsUsage(&host_usage);
495 for (std::map<std::string, int64>::const_iterator iter = host_usage.begin();
496 iter != host_usage.end();
497 ++iter) {
498 entries_.push_back(UsageInfo(iter->first, type, iter->second));
500 if (--remaining_trackers_ == 0)
501 CallCompleted();
504 void DidGetGlobalUsage(StorageType type, int64, int64) {
505 AddEntries(type, manager()->GetUsageTracker(type));
508 QuotaManager* manager() const {
509 return static_cast<QuotaManager*>(observer());
512 GetUsageInfoCallback callback_;
513 UsageInfoEntries entries_;
514 base::WeakPtrFactory<GetUsageInfoTask> weak_factory_;
515 int remaining_trackers_;
517 DISALLOW_COPY_AND_ASSIGN(GetUsageInfoTask);
520 class QuotaManager::UsageAndQuotaDispatcherTaskForTemporary
521 : public QuotaManager::UsageAndQuotaDispatcherTask {
522 public:
523 UsageAndQuotaDispatcherTaskForTemporary(
524 QuotaManager* manager, const HostAndType& host_and_type)
525 : UsageAndQuotaDispatcherTask(manager, host_and_type) {}
527 protected:
528 virtual void RunBody() OVERRIDE {
529 manager()->GetUsageTracker(type())->GetGlobalUsage(
530 NewWaitableGlobalUsageCallback());
531 manager()->GetUsageTracker(type())->GetHostUsage(
532 host(), NewWaitableHostUsageCallback());
533 manager()->GetAvailableSpace(NewWaitableAvailableSpaceCallback());
536 virtual void DispatchCallbacks() OVERRIDE {
537 // Allow an individual host to utilize a fraction of the total
538 // pool available for temp storage.
539 int64 host_quota = temporary_global_quota() / kPerHostTemporaryPortion;
541 // But if total temp usage is over-budget, stop letting new data in
542 // until we reclaim space.
543 DCHECK_GE(global_usage(), global_unlimited_usage());
544 int64 limited_global_usage = global_usage() - global_unlimited_usage();
545 if (limited_global_usage > temporary_global_quota())
546 host_quota = std::min(host_quota, host_usage());
548 CallCallbacksAndClear(quota_status(),
549 host_usage(), host_usage(), host_quota,
550 available_space());
554 class QuotaManager::UsageAndQuotaDispatcherTaskForPersistent
555 : public QuotaManager::UsageAndQuotaDispatcherTask {
556 public:
557 UsageAndQuotaDispatcherTaskForPersistent(
558 QuotaManager* manager, const HostAndType& host_and_type)
559 : UsageAndQuotaDispatcherTask(manager, host_and_type) {}
561 protected:
562 virtual void RunBody() OVERRIDE {
563 manager()->GetUsageTracker(type())->GetHostUsage(
564 host(), NewWaitableHostUsageCallback());
565 manager()->GetPersistentHostQuota(
566 host(), NewWaitableHostQuotaCallback());
567 manager()->GetAvailableSpace(NewWaitableAvailableSpaceCallback());
570 virtual void DispatchCallbacks() OVERRIDE {
571 CallCallbacksAndClear(quota_status(),
572 host_usage(), host_usage(), host_quota(),
573 available_space());
577 class QuotaManager::UsageAndQuotaDispatcherTaskForSyncable
578 : public QuotaManager::UsageAndQuotaDispatcherTask {
579 public:
580 UsageAndQuotaDispatcherTaskForSyncable(
581 QuotaManager* manager, const HostAndType& host_and_type)
582 : UsageAndQuotaDispatcherTask(manager, host_and_type) {}
584 protected:
585 virtual void RunBody() OVERRIDE {
586 manager()->GetUsageTracker(type())->GetHostUsage(
587 host(), NewWaitableHostUsageCallback());
590 virtual void DispatchCallbacks() OVERRIDE {
591 // TODO(kinuko): We should reflect the backend's actual quota instead
592 // of returning a fixed default value.
593 CallCallbacksAndClear(quota_status(),
594 host_usage(), host_usage(),
595 kSyncableStorageDefaultHostQuota,
596 available_space());
600 class QuotaManager::UsageAndQuotaDispatcherTaskForTemporaryGlobal
601 : public QuotaManager::UsageAndQuotaDispatcherTask {
602 public:
603 UsageAndQuotaDispatcherTaskForTemporaryGlobal(
604 QuotaManager* manager, const HostAndType& host_and_type)
605 : UsageAndQuotaDispatcherTask(manager, host_and_type) {}
607 protected:
608 virtual void RunBody() OVERRIDE {
609 manager()->GetUsageTracker(type())->GetGlobalUsage(
610 NewWaitableGlobalUsageCallback());
611 manager()->GetAvailableSpace(NewWaitableAvailableSpaceCallback());
614 virtual void DispatchCallbacks() OVERRIDE {
615 CallCallbacksAndClear(quota_status(),
616 global_usage(), global_unlimited_usage(),
617 temporary_global_quota(),
618 available_space());
621 virtual StorageType type() const OVERRIDE { return kStorageTypeTemporary; }
624 // static
625 QuotaManager::UsageAndQuotaDispatcherTask*
626 QuotaManager::UsageAndQuotaDispatcherTask::Create(
627 QuotaManager* manager, bool global,
628 const QuotaManager::HostAndType& host_and_type) {
629 if (global)
630 return new UsageAndQuotaDispatcherTaskForTemporaryGlobal(
631 manager, host_and_type);
632 switch (host_and_type.second) {
633 case kStorageTypeTemporary:
634 return new UsageAndQuotaDispatcherTaskForTemporary(
635 manager, host_and_type);
636 case kStorageTypePersistent:
637 return new UsageAndQuotaDispatcherTaskForPersistent(
638 manager, host_and_type);
639 case kStorageTypeSyncable:
640 return new UsageAndQuotaDispatcherTaskForSyncable(
641 manager, host_and_type);
642 default:
643 NOTREACHED();
645 return NULL;
648 class QuotaManager::OriginDataDeleter : public QuotaTask {
649 public:
650 OriginDataDeleter(QuotaManager* manager,
651 const GURL& origin,
652 StorageType type,
653 int quota_client_mask,
654 const StatusCallback& callback)
655 : QuotaTask(manager),
656 origin_(origin),
657 type_(type),
658 quota_client_mask_(quota_client_mask),
659 error_count_(0),
660 remaining_clients_(-1),
661 skipped_clients_(0),
662 callback_(callback),
663 weak_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {}
665 protected:
666 virtual void Run() OVERRIDE {
667 error_count_ = 0;
668 remaining_clients_ = manager()->clients_.size();
669 for (QuotaClientList::iterator iter = manager()->clients_.begin();
670 iter != manager()->clients_.end(); ++iter) {
671 if (quota_client_mask_ & (*iter)->id()) {
672 (*iter)->DeleteOriginData(
673 origin_, type_,
674 base::Bind(&OriginDataDeleter::DidDeleteOriginData,
675 weak_factory_.GetWeakPtr()));
676 } else {
677 ++skipped_clients_;
678 if (--remaining_clients_ == 0)
679 CallCompleted();
684 virtual void Completed() OVERRIDE {
685 if (error_count_ == 0) {
686 // Only remove the entire origin if we didn't skip any client types.
687 if (skipped_clients_ == 0)
688 manager()->DeleteOriginFromDatabase(origin_, type_);
689 callback_.Run(kQuotaStatusOk);
690 } else {
691 callback_.Run(kQuotaErrorInvalidModification);
693 DeleteSoon();
696 virtual void Aborted() OVERRIDE {
697 callback_.Run(kQuotaErrorAbort);
698 DeleteSoon();
701 void DidDeleteOriginData(QuotaStatusCode status) {
702 DCHECK_GT(remaining_clients_, 0);
704 if (status != kQuotaStatusOk)
705 ++error_count_;
707 if (--remaining_clients_ == 0)
708 CallCompleted();
711 QuotaManager* manager() const {
712 return static_cast<QuotaManager*>(observer());
715 GURL origin_;
716 StorageType type_;
717 int quota_client_mask_;
718 int error_count_;
719 int remaining_clients_;
720 int skipped_clients_;
721 StatusCallback callback_;
723 base::WeakPtrFactory<OriginDataDeleter> weak_factory_;
724 DISALLOW_COPY_AND_ASSIGN(OriginDataDeleter);
727 class QuotaManager::HostDataDeleter : public QuotaTask {
728 public:
729 HostDataDeleter(QuotaManager* manager,
730 const std::string& host,
731 StorageType type,
732 int quota_client_mask,
733 const StatusCallback& callback)
734 : QuotaTask(manager),
735 host_(host),
736 type_(type),
737 quota_client_mask_(quota_client_mask),
738 error_count_(0),
739 remaining_clients_(-1),
740 remaining_deleters_(-1),
741 callback_(callback),
742 weak_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {}
744 protected:
745 virtual void Run() OVERRIDE {
746 error_count_ = 0;
747 remaining_clients_ = manager()->clients_.size();
748 for (QuotaClientList::iterator iter = manager()->clients_.begin();
749 iter != manager()->clients_.end(); ++iter) {
750 (*iter)->GetOriginsForHost(
751 type_, host_,
752 base::Bind(&HostDataDeleter::DidGetOriginsForHost,
753 weak_factory_.GetWeakPtr()));
757 virtual void Completed() OVERRIDE {
758 if (error_count_ == 0) {
759 callback_.Run(kQuotaStatusOk);
760 } else {
761 callback_.Run(kQuotaErrorInvalidModification);
763 DeleteSoon();
766 virtual void Aborted() OVERRIDE {
767 callback_.Run(kQuotaErrorAbort);
768 DeleteSoon();
771 void DidGetOriginsForHost(const std::set<GURL>& origins, StorageType type) {
772 DCHECK_GT(remaining_clients_, 0);
774 origins_.insert(origins.begin(), origins.end());
776 if (--remaining_clients_ == 0) {
777 if (!origins_.empty())
778 ScheduleOriginsDeletion();
779 else
780 CallCompleted();
784 void ScheduleOriginsDeletion() {
785 remaining_deleters_ = origins_.size();
786 for (std::set<GURL>::const_iterator p = origins_.begin();
787 p != origins_.end();
788 ++p) {
789 OriginDataDeleter* deleter =
790 new OriginDataDeleter(
791 manager(), *p, type_, quota_client_mask_,
792 base::Bind(&HostDataDeleter::DidDeleteOriginData,
793 weak_factory_.GetWeakPtr()));
794 deleter->Start();
798 void DidDeleteOriginData(QuotaStatusCode status) {
799 DCHECK_GT(remaining_deleters_, 0);
801 if (status != kQuotaStatusOk)
802 ++error_count_;
804 if (--remaining_deleters_ == 0)
805 CallCompleted();
808 QuotaManager* manager() const {
809 return static_cast<QuotaManager*>(observer());
812 std::string host_;
813 StorageType type_;
814 int quota_client_mask_;
815 std::set<GURL> origins_;
816 int error_count_;
817 int remaining_clients_;
818 int remaining_deleters_;
819 StatusCallback callback_;
821 base::WeakPtrFactory<HostDataDeleter> weak_factory_;
822 DISALLOW_COPY_AND_ASSIGN(HostDataDeleter);
825 class QuotaManager::GetModifiedSinceHelper {
826 public:
827 bool GetModifiedSinceOnDBThread(StorageType type,
828 base::Time modified_since,
829 QuotaDatabase* database) {
830 DCHECK(database);
831 return database->GetOriginsModifiedSince(type, &origins_, modified_since);
834 void DidGetModifiedSince(QuotaManager* manager,
835 const GetOriginsCallback& callback,
836 StorageType type,
837 bool success) {
838 if (!manager) {
839 // The operation was aborted.
840 callback.Run(std::set<GURL>(), type);
841 return;
843 manager->DidDatabaseWork(success);
844 callback.Run(origins_, type);
847 private:
848 std::set<GURL> origins_;
851 class QuotaManager::DumpQuotaTableHelper {
852 public:
853 bool DumpQuotaTableOnDBThread(QuotaDatabase* database) {
854 DCHECK(database);
855 return database->DumpQuotaTable(
856 new TableCallback(base::Bind(&DumpQuotaTableHelper::AppendEntry,
857 base::Unretained(this))));
860 void DidDumpQuotaTable(QuotaManager* manager,
861 const DumpQuotaTableCallback& callback,
862 bool success) {
863 if (!manager) {
864 // The operation was aborted.
865 callback.Run(QuotaTableEntries());
866 return;
868 manager->DidDatabaseWork(success);
869 callback.Run(entries_);
872 private:
873 typedef QuotaDatabase::QuotaTableCallback TableCallback;
875 bool AppendEntry(const QuotaTableEntry& entry) {
876 entries_.push_back(entry);
877 return true;
880 QuotaTableEntries entries_;
883 class QuotaManager::DumpOriginInfoTableHelper {
884 public:
885 bool DumpOriginInfoTableOnDBThread(QuotaDatabase* database) {
886 DCHECK(database);
887 return database->DumpOriginInfoTable(
888 new TableCallback(base::Bind(&DumpOriginInfoTableHelper::AppendEntry,
889 base::Unretained(this))));
892 void DidDumpOriginInfoTable(QuotaManager* manager,
893 const DumpOriginInfoTableCallback& callback,
894 bool success) {
895 if (!manager) {
896 // The operation was aborted.
897 callback.Run(OriginInfoTableEntries());
898 return;
900 manager->DidDatabaseWork(success);
901 callback.Run(entries_);
904 private:
905 typedef QuotaDatabase::OriginInfoTableCallback TableCallback;
907 bool AppendEntry(const OriginInfoTableEntry& entry) {
908 entries_.push_back(entry);
909 return true;
912 OriginInfoTableEntries entries_;
915 // QuotaManager ---------------------------------------------------------------
917 QuotaManager::QuotaManager(bool is_incognito,
918 const base::FilePath& profile_path,
919 base::SingleThreadTaskRunner* io_thread,
920 base::SequencedTaskRunner* db_thread,
921 SpecialStoragePolicy* special_storage_policy)
922 : is_incognito_(is_incognito),
923 profile_path_(profile_path),
924 proxy_(new QuotaManagerProxy(
925 ALLOW_THIS_IN_INITIALIZER_LIST(this), io_thread)),
926 db_disabled_(false),
927 eviction_disabled_(false),
928 io_thread_(io_thread),
929 db_thread_(db_thread),
930 temporary_quota_initialized_(false),
931 temporary_quota_override_(-1),
932 desired_available_space_(-1),
933 special_storage_policy_(special_storage_policy),
934 weak_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)),
935 get_disk_space_fn_(&CallSystemGetAmountOfFreeDiskSpace) {
938 void QuotaManager::GetUsageInfo(const GetUsageInfoCallback& callback) {
939 LazyInitialize();
940 GetUsageInfoTask* get_usage_info = new GetUsageInfoTask(this, callback);
941 get_usage_info->Start();
944 void QuotaManager::GetUsageAndQuota(
945 const GURL& origin, StorageType type,
946 const GetUsageAndQuotaCallback& callback) {
947 DCHECK(origin == origin.GetOrigin());
948 GetUsageAndQuotaInternal(
949 origin, type, false /* global */,
950 base::Bind(&CallGetUsageAndQuotaCallback, callback,
951 IsStorageUnlimited(origin, type), IsInstalledApp(origin)));
954 void QuotaManager::NotifyStorageAccessed(
955 QuotaClient::ID client_id,
956 const GURL& origin, StorageType type) {
957 DCHECK(origin == origin.GetOrigin());
958 NotifyStorageAccessedInternal(client_id, origin, type, base::Time::Now());
961 void QuotaManager::NotifyStorageModified(
962 QuotaClient::ID client_id,
963 const GURL& origin, StorageType type, int64 delta) {
964 DCHECK(origin == origin.GetOrigin());
965 NotifyStorageModifiedInternal(client_id, origin, type, delta,
966 base::Time::Now());
969 void QuotaManager::NotifyOriginInUse(const GURL& origin) {
970 DCHECK(io_thread_->BelongsToCurrentThread());
971 origins_in_use_[origin]++;
974 void QuotaManager::NotifyOriginNoLongerInUse(const GURL& origin) {
975 DCHECK(io_thread_->BelongsToCurrentThread());
976 DCHECK(IsOriginInUse(origin));
977 int& count = origins_in_use_[origin];
978 if (--count == 0)
979 origins_in_use_.erase(origin);
982 void QuotaManager::DeleteOriginData(
983 const GURL& origin, StorageType type, int quota_client_mask,
984 const StatusCallback& callback) {
985 LazyInitialize();
987 if (origin.is_empty() || clients_.empty()) {
988 callback.Run(kQuotaStatusOk);
989 return;
992 DCHECK(origin == origin.GetOrigin());
993 OriginDataDeleter* deleter =
994 new OriginDataDeleter(this, origin, type, quota_client_mask, callback);
995 deleter->Start();
998 void QuotaManager::DeleteHostData(const std::string& host,
999 StorageType type,
1000 int quota_client_mask,
1001 const StatusCallback& callback) {
1002 LazyInitialize();
1004 if (host.empty() || clients_.empty()) {
1005 callback.Run(kQuotaStatusOk);
1006 return;
1009 HostDataDeleter* deleter =
1010 new HostDataDeleter(this, host, type, quota_client_mask, callback);
1011 deleter->Start();
1014 void QuotaManager::GetAvailableSpace(const AvailableSpaceCallback& callback) {
1015 if (is_incognito_) {
1016 callback.Run(kQuotaStatusOk, kIncognitoDefaultTemporaryQuota);
1017 return;
1020 PostTaskAndReplyWithResult(
1021 db_thread_,
1022 FROM_HERE,
1023 base::Bind(get_disk_space_fn_, profile_path_),
1024 base::Bind(&QuotaManager::DidGetAvailableSpace,
1025 weak_factory_.GetWeakPtr(),
1026 callback));
1029 void QuotaManager::GetTemporaryGlobalQuota(const QuotaCallback& callback) {
1030 if (temporary_quota_override_ > 0) {
1031 callback.Run(kQuotaStatusOk, temporary_quota_override_);
1032 return;
1034 GetUsageAndQuotaInternal(
1035 GURL(), kStorageTypeTemporary, true /* global */,
1036 base::Bind(&CallQuotaCallback, callback));
1039 void QuotaManager::SetTemporaryGlobalOverrideQuota(
1040 int64 new_quota, const QuotaCallback& callback) {
1041 LazyInitialize();
1043 if (new_quota < 0) {
1044 if (!callback.is_null())
1045 callback.Run(kQuotaErrorInvalidModification, -1);
1046 return;
1049 if (db_disabled_) {
1050 if (callback.is_null())
1051 callback.Run(kQuotaErrorInvalidAccess, -1);
1052 return;
1055 int64* new_quota_ptr = new int64(new_quota);
1056 PostTaskAndReplyWithResultForDBThread(
1057 FROM_HERE,
1058 base::Bind(&SetTemporaryGlobalOverrideQuotaOnDBThread,
1059 base::Unretained(new_quota_ptr)),
1060 base::Bind(&QuotaManager::DidSetTemporaryGlobalOverrideQuota,
1061 weak_factory_.GetWeakPtr(),
1062 callback,
1063 base::Owned(new_quota_ptr)));
1066 void QuotaManager::GetPersistentHostQuota(const std::string& host,
1067 const QuotaCallback& callback) {
1068 LazyInitialize();
1069 if (host.empty()) {
1070 // This could happen if we are called on file:///.
1071 // TODO(kinuko) We may want to respect --allow-file-access-from-files
1072 // command line switch.
1073 callback.Run(kQuotaStatusOk, 0);
1074 return;
1077 int64* quota_ptr = new int64(0);
1078 PostTaskAndReplyWithResultForDBThread(
1079 FROM_HERE,
1080 base::Bind(&GetPersistentHostQuotaOnDBThread,
1081 host,
1082 base::Unretained(quota_ptr)),
1083 base::Bind(&QuotaManager::DidGetPersistentHostQuota,
1084 weak_factory_.GetWeakPtr(),
1085 callback,
1086 host,
1087 base::Owned(quota_ptr)));
1090 void QuotaManager::SetPersistentHostQuota(const std::string& host,
1091 int64 new_quota,
1092 const QuotaCallback& callback) {
1093 LazyInitialize();
1094 if (host.empty()) {
1095 // This could happen if we are called on file:///.
1096 callback.Run(kQuotaErrorNotSupported, 0);
1097 return;
1099 if (new_quota < 0) {
1100 callback.Run(kQuotaErrorInvalidModification, -1);
1101 return;
1104 if (db_disabled_) {
1105 callback.Run(kQuotaErrorInvalidAccess, -1);
1106 return;
1109 int64* new_quota_ptr = new int64(new_quota);
1110 PostTaskAndReplyWithResultForDBThread(
1111 FROM_HERE,
1112 base::Bind(&SetPersistentHostQuotaOnDBThread,
1113 host,
1114 base::Unretained(new_quota_ptr)),
1115 base::Bind(&QuotaManager::DidSetPersistentHostQuota,
1116 weak_factory_.GetWeakPtr(),
1117 host,
1118 callback,
1119 base::Owned(new_quota_ptr)));
1122 void QuotaManager::GetGlobalUsage(StorageType type,
1123 const GlobalUsageCallback& callback) {
1124 LazyInitialize();
1125 GetUsageTracker(type)->GetGlobalUsage(callback);
1128 void QuotaManager::GetHostUsage(const std::string& host,
1129 StorageType type,
1130 const UsageCallback& callback) {
1131 LazyInitialize();
1132 GetUsageTracker(type)->GetHostUsage(host, callback);
1135 void QuotaManager::GetStatistics(
1136 std::map<std::string, std::string>* statistics) {
1137 DCHECK(statistics);
1138 if (temporary_storage_evictor_.get()) {
1139 std::map<std::string, int64> stats;
1140 temporary_storage_evictor_->GetStatistics(&stats);
1141 for (std::map<std::string, int64>::iterator p = stats.begin();
1142 p != stats.end();
1143 ++p)
1144 (*statistics)[p->first] = base::Int64ToString(p->second);
1148 bool QuotaManager::IsStorageUnlimited(const GURL& origin,
1149 StorageType type) const {
1150 // For syncable storage we should always enforce quota (since the
1151 // quota must be capped by the server limit).
1152 if (type == kStorageTypeSyncable)
1153 return false;
1154 return special_storage_policy_.get() &&
1155 special_storage_policy_->IsStorageUnlimited(origin);
1158 void QuotaManager::GetOriginsModifiedSince(StorageType type,
1159 base::Time modified_since,
1160 const GetOriginsCallback& callback) {
1161 LazyInitialize();
1162 GetModifiedSinceHelper* helper = new GetModifiedSinceHelper;
1163 PostTaskAndReplyWithResultForDBThread(
1164 FROM_HERE,
1165 base::Bind(&GetModifiedSinceHelper::GetModifiedSinceOnDBThread,
1166 base::Unretained(helper),
1167 type,
1168 modified_since),
1169 base::Bind(&GetModifiedSinceHelper::DidGetModifiedSince,
1170 base::Owned(helper),
1171 weak_factory_.GetWeakPtr(),
1172 callback,
1173 type));
1176 bool QuotaManager::ResetUsageTracker(StorageType type) {
1177 DCHECK(GetUsageTracker(type));
1178 if (GetUsageTracker(type)->IsWorking())
1179 return false;
1180 switch (type) {
1181 case kStorageTypeTemporary:
1182 temporary_usage_tracker_.reset(
1183 new UsageTracker(clients_, kStorageTypeTemporary,
1184 special_storage_policy_));
1185 return true;
1186 case kStorageTypePersistent:
1187 persistent_usage_tracker_.reset(
1188 new UsageTracker(clients_, kStorageTypePersistent,
1189 special_storage_policy_));
1190 return true;
1191 case kStorageTypeSyncable:
1192 syncable_usage_tracker_.reset(
1193 new UsageTracker(clients_, kStorageTypeSyncable,
1194 special_storage_policy_));
1195 return true;
1196 default:
1197 NOTREACHED();
1199 return true;
1202 QuotaManager::~QuotaManager() {
1203 proxy_->manager_ = NULL;
1204 std::for_each(clients_.begin(), clients_.end(),
1205 std::mem_fun(&QuotaClient::OnQuotaManagerDestroyed));
1206 if (database_.get())
1207 db_thread_->DeleteSoon(FROM_HERE, database_.release());
1210 QuotaManager::EvictionContext::EvictionContext()
1211 : evicted_type(kStorageTypeUnknown) {
1214 QuotaManager::EvictionContext::~EvictionContext() {
1217 void QuotaManager::LazyInitialize() {
1218 DCHECK(io_thread_->BelongsToCurrentThread());
1219 if (database_.get()) {
1220 // Initialization seems to be done already.
1221 return;
1224 // Use an empty path to open an in-memory only databse for incognito.
1225 database_.reset(new QuotaDatabase(is_incognito_ ? base::FilePath() :
1226 profile_path_.AppendASCII(kDatabaseName)));
1228 temporary_usage_tracker_.reset(
1229 new UsageTracker(clients_, kStorageTypeTemporary,
1230 special_storage_policy_));
1231 persistent_usage_tracker_.reset(
1232 new UsageTracker(clients_, kStorageTypePersistent,
1233 special_storage_policy_));
1234 syncable_usage_tracker_.reset(
1235 new UsageTracker(clients_, kStorageTypeSyncable,
1236 special_storage_policy_));
1238 int64* temporary_quota_override = new int64(-1);
1239 int64* desired_available_space = new int64(-1);
1240 PostTaskAndReplyWithResultForDBThread(
1241 FROM_HERE,
1242 base::Bind(&InitializeOnDBThread,
1243 base::Unretained(temporary_quota_override),
1244 base::Unretained(desired_available_space)),
1245 base::Bind(&QuotaManager::DidInitialize,
1246 weak_factory_.GetWeakPtr(),
1247 base::Owned(temporary_quota_override),
1248 base::Owned(desired_available_space)));
1251 void QuotaManager::RegisterClient(QuotaClient* client) {
1252 DCHECK(!database_.get());
1253 clients_.push_back(client);
1256 UsageTracker* QuotaManager::GetUsageTracker(StorageType type) const {
1257 switch (type) {
1258 case kStorageTypeTemporary:
1259 return temporary_usage_tracker_.get();
1260 case kStorageTypePersistent:
1261 return persistent_usage_tracker_.get();
1262 case kStorageTypeSyncable:
1263 return syncable_usage_tracker_.get();
1264 default:
1265 NOTREACHED();
1267 return NULL;
1270 void QuotaManager::GetCachedOrigins(
1271 StorageType type, std::set<GURL>* origins) {
1272 DCHECK(origins);
1273 LazyInitialize();
1274 DCHECK(GetUsageTracker(type));
1275 GetUsageTracker(type)->GetCachedOrigins(origins);
1278 void QuotaManager::NotifyStorageAccessedInternal(
1279 QuotaClient::ID client_id,
1280 const GURL& origin, StorageType type,
1281 base::Time accessed_time) {
1282 LazyInitialize();
1283 if (type == kStorageTypeTemporary && !lru_origin_callback_.is_null()) {
1284 // Record the accessed origins while GetLRUOrigin task is runing
1285 // to filter out them from eviction.
1286 access_notified_origins_.insert(origin);
1289 if (db_disabled_)
1290 return;
1291 PostTaskAndReplyWithResultForDBThread(
1292 FROM_HERE,
1293 base::Bind(&UpdateAccessTimeOnDBThread, origin, type, accessed_time),
1294 base::Bind(&QuotaManager::DidDatabaseWork,
1295 weak_factory_.GetWeakPtr()));
1298 void QuotaManager::NotifyStorageModifiedInternal(
1299 QuotaClient::ID client_id,
1300 const GURL& origin,
1301 StorageType type,
1302 int64 delta,
1303 base::Time modified_time) {
1304 LazyInitialize();
1305 GetUsageTracker(type)->UpdateUsageCache(client_id, origin, delta);
1307 PostTaskAndReplyWithResultForDBThread(
1308 FROM_HERE,
1309 base::Bind(&UpdateModifiedTimeOnDBThread, origin, type, modified_time),
1310 base::Bind(&QuotaManager::DidDatabaseWork,
1311 weak_factory_.GetWeakPtr()));
1314 void QuotaManager::GetUsageAndQuotaInternal(
1315 const GURL& origin, StorageType type, bool global,
1316 const UsageAndQuotaDispatcherCallback& callback) {
1317 LazyInitialize();
1319 StorageType requested_type = type;
1320 if (type == kStorageTypeUnknown) {
1321 // Quota only supports temporary/persistent types.
1322 callback.Run(kQuotaErrorNotSupported, QuotaAndUsage());
1323 return;
1326 // Special internal type for querying global usage and quota.
1327 const int kStorageTypeTemporaryGlobal = kStorageTypeTemporary + 100;
1328 if (global) {
1329 DCHECK_EQ(kStorageTypeTemporary, type);
1330 type = static_cast<StorageType>(kStorageTypeTemporaryGlobal);
1333 std::string host = net::GetHostOrSpecFromURL(origin);
1334 HostAndType host_and_type = std::make_pair(host, type);
1335 UsageAndQuotaDispatcherTaskMap::iterator found =
1336 usage_and_quota_dispatchers_.find(host_and_type);
1337 if (found == usage_and_quota_dispatchers_.end()) {
1338 UsageAndQuotaDispatcherTask* dispatcher =
1339 UsageAndQuotaDispatcherTask::Create(this, global, host_and_type);
1340 found = usage_and_quota_dispatchers_.insert(
1341 std::make_pair(host_and_type, dispatcher)).first;
1343 // Start the dispatcher if it is the first one and temporary_quota_override
1344 // is already initialized iff the requested type is temporary.
1345 // (The first dispatcher task for temporary will be kicked in
1346 // DidInitialize if temporary_quota_initialized_ is false here.)
1347 if (found->second->AddCallback(callback) &&
1348 (requested_type != kStorageTypeTemporary ||
1349 temporary_quota_initialized_)) {
1350 found->second->Start();
1354 void QuotaManager::DumpQuotaTable(const DumpQuotaTableCallback& callback) {
1355 DumpQuotaTableHelper* helper = new DumpQuotaTableHelper;
1356 PostTaskAndReplyWithResultForDBThread(
1357 FROM_HERE,
1358 base::Bind(&DumpQuotaTableHelper::DumpQuotaTableOnDBThread,
1359 base::Unretained(helper)),
1360 base::Bind(&DumpQuotaTableHelper::DidDumpQuotaTable,
1361 base::Owned(helper),
1362 weak_factory_.GetWeakPtr(),
1363 callback));
1366 void QuotaManager::DumpOriginInfoTable(
1367 const DumpOriginInfoTableCallback& callback) {
1368 DumpOriginInfoTableHelper* helper = new DumpOriginInfoTableHelper;
1369 PostTaskAndReplyWithResultForDBThread(
1370 FROM_HERE,
1371 base::Bind(&DumpOriginInfoTableHelper::DumpOriginInfoTableOnDBThread,
1372 base::Unretained(helper)),
1373 base::Bind(&DumpOriginInfoTableHelper::DidDumpOriginInfoTable,
1374 base::Owned(helper),
1375 weak_factory_.GetWeakPtr(),
1376 callback));
1379 void QuotaManager::StartEviction() {
1380 DCHECK(!temporary_storage_evictor_.get());
1381 temporary_storage_evictor_.reset(new QuotaTemporaryStorageEvictor(
1382 this, kEvictionIntervalInMilliSeconds));
1383 if (desired_available_space_ >= 0)
1384 temporary_storage_evictor_->set_min_available_disk_space_to_start_eviction(
1385 desired_available_space_);
1386 temporary_storage_evictor_->Start();
1389 void QuotaManager::DeleteOriginFromDatabase(
1390 const GURL& origin, StorageType type) {
1391 LazyInitialize();
1392 if (db_disabled_)
1393 return;
1395 PostTaskAndReplyWithResultForDBThread(
1396 FROM_HERE,
1397 base::Bind(&DeleteOriginInfoOnDBThread, origin, type),
1398 base::Bind(&QuotaManager::DidDatabaseWork,
1399 weak_factory_.GetWeakPtr()));
1402 void QuotaManager::DidOriginDataEvicted(QuotaStatusCode status) {
1403 DCHECK(io_thread_->BelongsToCurrentThread());
1405 // We only try evict origins that are not in use, so basically
1406 // deletion attempt for eviction should not fail. Let's record
1407 // the origin if we get error and exclude it from future eviction
1408 // if the error happens consistently (> kThresholdOfErrorsToBeBlacklisted).
1409 if (status != kQuotaStatusOk)
1410 origins_in_error_[eviction_context_.evicted_origin]++;
1412 eviction_context_.evict_origin_data_callback.Run(status);
1413 eviction_context_.evict_origin_data_callback.Reset();
1416 void QuotaManager::ReportHistogram() {
1417 GetGlobalUsage(kStorageTypeTemporary,
1418 base::Bind(
1419 &QuotaManager::DidGetTemporaryGlobalUsageForHistogram,
1420 weak_factory_.GetWeakPtr()));
1421 GetGlobalUsage(kStorageTypePersistent,
1422 base::Bind(
1423 &QuotaManager::DidGetPersistentGlobalUsageForHistogram,
1424 weak_factory_.GetWeakPtr()));
1427 void QuotaManager::DidGetTemporaryGlobalUsageForHistogram(
1428 StorageType type,
1429 int64 usage,
1430 int64 unlimited_usage) {
1431 UMA_HISTOGRAM_MBYTES("Quota.GlobalUsageOfTemporaryStorage", usage);
1433 std::set<GURL> origins;
1434 GetCachedOrigins(type, &origins);
1436 size_t num_origins = origins.size();
1437 size_t protected_origins = 0;
1438 size_t unlimited_origins = 0;
1439 CountOriginType(origins, special_storage_policy_,
1440 &protected_origins, &unlimited_origins);
1442 UMA_HISTOGRAM_COUNTS("Quota.NumberOfTemporaryStorageOrigins",
1443 num_origins);
1444 UMA_HISTOGRAM_COUNTS("Quota.NumberOfProtectedTemporaryStorageOrigins",
1445 protected_origins);
1446 UMA_HISTOGRAM_COUNTS("Quota.NumberOfUnlimitedTemporaryStorageOrigins",
1447 unlimited_origins);
1450 void QuotaManager::DidGetPersistentGlobalUsageForHistogram(
1451 StorageType type,
1452 int64 usage,
1453 int64 unlimited_usage) {
1454 UMA_HISTOGRAM_MBYTES("Quota.GlobalUsageOfPersistentStorage", usage);
1456 std::set<GURL> origins;
1457 GetCachedOrigins(type, &origins);
1459 size_t num_origins = origins.size();
1460 size_t protected_origins = 0;
1461 size_t unlimited_origins = 0;
1462 CountOriginType(origins, special_storage_policy_,
1463 &protected_origins, &unlimited_origins);
1465 UMA_HISTOGRAM_COUNTS("Quota.NumberOfPersistentStorageOrigins",
1466 num_origins);
1467 UMA_HISTOGRAM_COUNTS("Quota.NumberOfProtectedPersistentStorageOrigins",
1468 protected_origins);
1469 UMA_HISTOGRAM_COUNTS("Quota.NumberOfUnlimitedPersistentStorageOrigins",
1470 unlimited_origins);
1473 void QuotaManager::GetLRUOrigin(
1474 StorageType type,
1475 const GetLRUOriginCallback& callback) {
1476 LazyInitialize();
1477 // This must not be called while there's an in-flight task.
1478 DCHECK(lru_origin_callback_.is_null());
1479 lru_origin_callback_ = callback;
1480 if (db_disabled_) {
1481 lru_origin_callback_.Run(GURL());
1482 lru_origin_callback_.Reset();
1483 return;
1486 std::set<GURL>* exceptions = new std::set<GURL>;
1487 for (std::map<GURL, int>::const_iterator p = origins_in_use_.begin();
1488 p != origins_in_use_.end();
1489 ++p) {
1490 if (p->second > 0)
1491 exceptions->insert(p->first);
1493 for (std::map<GURL, int>::const_iterator p = origins_in_error_.begin();
1494 p != origins_in_error_.end();
1495 ++p) {
1496 if (p->second > QuotaManager::kThresholdOfErrorsToBeBlacklisted)
1497 exceptions->insert(p->first);
1500 GURL* url = new GURL;
1501 PostTaskAndReplyWithResultForDBThread(
1502 FROM_HERE,
1503 base::Bind(&GetLRUOriginOnDBThread,
1504 type,
1505 base::Owned(exceptions),
1506 special_storage_policy_,
1507 base::Unretained(url)),
1508 base::Bind(&QuotaManager::DidGetLRUOrigin,
1509 weak_factory_.GetWeakPtr(),
1510 base::Owned(url)));
1513 void QuotaManager::EvictOriginData(
1514 const GURL& origin,
1515 StorageType type,
1516 const EvictOriginDataCallback& callback) {
1517 DCHECK(io_thread_->BelongsToCurrentThread());
1518 DCHECK_EQ(type, kStorageTypeTemporary);
1520 eviction_context_.evicted_origin = origin;
1521 eviction_context_.evicted_type = type;
1522 eviction_context_.evict_origin_data_callback = callback;
1524 DeleteOriginData(origin, type, QuotaClient::kAllClientsMask,
1525 base::Bind(&QuotaManager::DidOriginDataEvicted,
1526 weak_factory_.GetWeakPtr()));
1529 void QuotaManager::GetUsageAndQuotaForEviction(
1530 const GetUsageAndQuotaForEvictionCallback& callback) {
1531 DCHECK(io_thread_->BelongsToCurrentThread());
1532 GetUsageAndQuotaInternal(
1533 GURL(), kStorageTypeTemporary, true /* global */, callback);
1536 void QuotaManager::DidSetTemporaryGlobalOverrideQuota(
1537 const QuotaCallback& callback,
1538 const int64* new_quota,
1539 bool success) {
1540 QuotaStatusCode status = kQuotaErrorInvalidAccess;
1541 DidDatabaseWork(success);
1542 if (success) {
1543 temporary_quota_override_ = *new_quota;
1544 status = kQuotaStatusOk;
1547 if (callback.is_null())
1548 return;
1550 callback.Run(status, *new_quota);
1553 void QuotaManager::DidGetPersistentHostQuota(const QuotaCallback& callback,
1554 const std::string& host,
1555 const int64* quota,
1556 bool success) {
1557 DidDatabaseWork(success);
1558 callback.Run(kQuotaStatusOk, *quota);
1561 void QuotaManager::DidSetPersistentHostQuota(const std::string& host,
1562 const QuotaCallback& callback,
1563 const int64* new_quota,
1564 bool success) {
1565 DidDatabaseWork(success);
1566 callback.Run(success ? kQuotaStatusOk : kQuotaErrorInvalidAccess, *new_quota);
1569 void QuotaManager::DidInitialize(int64* temporary_quota_override,
1570 int64* desired_available_space,
1571 bool success) {
1572 temporary_quota_override_ = *temporary_quota_override;
1573 desired_available_space_ = *desired_available_space;
1574 temporary_quota_initialized_ = true;
1575 DidDatabaseWork(success);
1577 histogram_timer_.Start(FROM_HERE,
1578 base::TimeDelta::FromMilliseconds(
1579 kReportHistogramInterval),
1580 this, &QuotaManager::ReportHistogram);
1582 DCHECK(temporary_quota_initialized_);
1584 // Kick the tasks that have been waiting for the
1585 // temporary_quota_initialized_ to be initialized (if there're any).
1586 for (UsageAndQuotaDispatcherTaskMap::iterator iter =
1587 usage_and_quota_dispatchers_.begin();
1588 iter != usage_and_quota_dispatchers_.end(); ++iter) {
1589 if (iter->second->IsStartable())
1590 iter->second->Start();
1593 // Kick the first GetTemporaryGlobalQuota. This internally fetches (and
1594 // caches) the usage of all origins and checks the available disk space.
1595 GetTemporaryGlobalQuota(
1596 base::Bind(&QuotaManager::DidGetInitialTemporaryGlobalQuota,
1597 weak_factory_.GetWeakPtr()));
1600 void QuotaManager::DidGetLRUOrigin(const GURL* origin,
1601 bool success) {
1602 DidDatabaseWork(success);
1603 // Make sure the returned origin is (still) not in the origin_in_use_ set
1604 // and has not been accessed since we posted the task.
1605 if (origins_in_use_.find(*origin) != origins_in_use_.end() ||
1606 access_notified_origins_.find(*origin) != access_notified_origins_.end())
1607 lru_origin_callback_.Run(GURL());
1608 else
1609 lru_origin_callback_.Run(*origin);
1610 access_notified_origins_.clear();
1611 lru_origin_callback_.Reset();
1614 void QuotaManager::DidGetInitialTemporaryGlobalQuota(
1615 QuotaStatusCode status, int64 quota_unused) {
1616 if (eviction_disabled_)
1617 return;
1619 std::set<GURL>* origins = new std::set<GURL>;
1620 temporary_usage_tracker_->GetCachedOrigins(origins);
1621 // This will call the StartEviction() when initial origin registration
1622 // is completed.
1623 PostTaskAndReplyWithResultForDBThread(
1624 FROM_HERE,
1625 base::Bind(&InitializeTemporaryOriginsInfoOnDBThread,
1626 base::Owned(origins)),
1627 base::Bind(&QuotaManager::DidInitializeTemporaryOriginsInfo,
1628 weak_factory_.GetWeakPtr()));
1631 void QuotaManager::DidInitializeTemporaryOriginsInfo(bool success) {
1632 DidDatabaseWork(success);
1633 if (success)
1634 StartEviction();
1637 void QuotaManager::DidGetAvailableSpace(const AvailableSpaceCallback& callback,
1638 int64 space) {
1639 callback.Run(kQuotaStatusOk, space);
1642 void QuotaManager::DidDatabaseWork(bool success) {
1643 db_disabled_ = !success;
1646 void QuotaManager::DeleteOnCorrectThread() const {
1647 if (!io_thread_->BelongsToCurrentThread() &&
1648 io_thread_->DeleteSoon(FROM_HERE, this)) {
1649 return;
1651 delete this;
1654 void QuotaManager::PostTaskAndReplyWithResultForDBThread(
1655 const tracked_objects::Location& from_here,
1656 const base::Callback<bool(QuotaDatabase*)>& task,
1657 const base::Callback<void(bool)>& reply) {
1658 // Deleting manager will post another task to DB thread to delete
1659 // |database_|, therefore we can be sure that database_ is alive when this
1660 // task runs.
1661 base::PostTaskAndReplyWithResult(
1662 db_thread_,
1663 from_here,
1664 base::Bind(task, base::Unretained(database_.get())),
1665 reply);
1668 // QuotaManagerProxy ----------------------------------------------------------
1670 void QuotaManagerProxy::RegisterClient(QuotaClient* client) {
1671 if (!io_thread_->BelongsToCurrentThread() &&
1672 io_thread_->PostTask(
1673 FROM_HERE,
1674 base::Bind(&QuotaManagerProxy::RegisterClient, this, client))) {
1675 return;
1678 if (manager_)
1679 manager_->RegisterClient(client);
1680 else
1681 client->OnQuotaManagerDestroyed();
1684 void QuotaManagerProxy::NotifyStorageAccessed(
1685 QuotaClient::ID client_id,
1686 const GURL& origin,
1687 StorageType type) {
1688 if (!io_thread_->BelongsToCurrentThread()) {
1689 io_thread_->PostTask(
1690 FROM_HERE,
1691 base::Bind(&QuotaManagerProxy::NotifyStorageAccessed, this, client_id,
1692 origin, type));
1693 return;
1696 if (manager_)
1697 manager_->NotifyStorageAccessed(client_id, origin, type);
1700 void QuotaManagerProxy::NotifyStorageModified(
1701 QuotaClient::ID client_id,
1702 const GURL& origin,
1703 StorageType type,
1704 int64 delta) {
1705 if (!io_thread_->BelongsToCurrentThread()) {
1706 io_thread_->PostTask(
1707 FROM_HERE,
1708 base::Bind(&QuotaManagerProxy::NotifyStorageModified, this, client_id,
1709 origin, type, delta));
1710 return;
1713 if (manager_)
1714 manager_->NotifyStorageModified(client_id, origin, type, delta);
1717 void QuotaManagerProxy::NotifyOriginInUse(
1718 const GURL& origin) {
1719 if (!io_thread_->BelongsToCurrentThread()) {
1720 io_thread_->PostTask(
1721 FROM_HERE,
1722 base::Bind(&QuotaManagerProxy::NotifyOriginInUse, this, origin));
1723 return;
1726 if (manager_)
1727 manager_->NotifyOriginInUse(origin);
1730 void QuotaManagerProxy::NotifyOriginNoLongerInUse(
1731 const GURL& origin) {
1732 if (!io_thread_->BelongsToCurrentThread()) {
1733 io_thread_->PostTask(
1734 FROM_HERE,
1735 base::Bind(&QuotaManagerProxy::NotifyOriginNoLongerInUse, this,
1736 origin));
1737 return;
1739 if (manager_)
1740 manager_->NotifyOriginNoLongerInUse(origin);
1743 QuotaManager* QuotaManagerProxy::quota_manager() const {
1744 DCHECK(!io_thread_ || io_thread_->BelongsToCurrentThread());
1745 return manager_;
1748 QuotaManagerProxy::QuotaManagerProxy(
1749 QuotaManager* manager, base::SingleThreadTaskRunner* io_thread)
1750 : manager_(manager), io_thread_(io_thread) {
1753 QuotaManagerProxy::~QuotaManagerProxy() {
1756 } // namespace quota