root/webkit/browser/quota/quota_manager.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. CountOriginType
  2. SetTemporaryGlobalOverrideQuotaOnDBThread
  3. GetPersistentHostQuotaOnDBThread
  4. SetPersistentHostQuotaOnDBThread
  5. InitializeOnDBThread
  6. GetLRUOriginOnDBThread
  7. DeleteOriginInfoOnDBThread
  8. InitializeTemporaryOriginsInfoOnDBThread
  9. UpdateAccessTimeOnDBThread
  10. UpdateModifiedTimeOnDBThread
  11. CallSystemGetAmountOfFreeDiskSpace
  12. CalculateTemporaryGlobalQuota
  13. DispatchTemporaryGlobalQuotaCallback
  14. CalculateQuotaWithDiskSpace
  15. CalculateTemporaryHostQuota
  16. DispatchUsageAndQuotaForWebApps
  17. available_disk_space
  18. available_disk_space
  19. waiting_callbacks_
  20. WaitForResults
  21. set_usage
  22. set_global_limited_usage
  23. set_quota
  24. set_available_disk_space
  25. GetHostUsageCallback
  26. GetGlobalLimitedUsageCallback
  27. GetQuotaCallback
  28. GetAvailableSpaceCallback
  29. DidGetHostUsage
  30. DidGetGlobalLimitedUsage
  31. DidGetQuota
  32. DidGetAvailableSpace
  33. Run
  34. Aborted
  35. Completed
  36. CheckCompleted
  37. weak_factory_
  38. Run
  39. Completed
  40. Aborted
  41. AddEntries
  42. DidGetGlobalUsage
  43. manager
  44. weak_factory_
  45. Run
  46. Completed
  47. Aborted
  48. DidDeleteOriginData
  49. manager
  50. weak_factory_
  51. Run
  52. Completed
  53. Aborted
  54. DidGetOriginsForHost
  55. ScheduleOriginsDeletion
  56. DidDeleteOriginData
  57. manager
  58. GetModifiedSinceOnDBThread
  59. DidGetModifiedSince
  60. DumpQuotaTableOnDBThread
  61. DidDumpQuotaTable
  62. AppendEntry
  63. DumpOriginInfoTableOnDBThread
  64. DidDumpOriginInfoTable
  65. AppendEntry
  66. weak_factory_
  67. GetUsageInfo
  68. GetUsageAndQuotaForWebApps
  69. GetUsageAndQuota
  70. NotifyStorageAccessed
  71. NotifyStorageModified
  72. NotifyOriginInUse
  73. NotifyOriginNoLongerInUse
  74. SetUsageCacheEnabled
  75. DeleteOriginData
  76. DeleteHostData
  77. GetAvailableSpace
  78. GetTemporaryGlobalQuota
  79. SetTemporaryGlobalOverrideQuota
  80. GetPersistentHostQuota
  81. SetPersistentHostQuota
  82. GetGlobalUsage
  83. GetHostUsage
  84. GetHostUsage
  85. IsTrackingHostUsage
  86. GetStatistics
  87. IsStorageUnlimited
  88. GetOriginsModifiedSince
  89. ResetUsageTracker
  90. AddStorageObserver
  91. RemoveStorageObserver
  92. RemoveStorageObserverForFilter
  93. LazyInitialize
  94. RegisterClient
  95. GetUsageTracker
  96. GetCachedOrigins
  97. NotifyStorageAccessedInternal
  98. NotifyStorageModifiedInternal
  99. DumpQuotaTable
  100. DumpOriginInfoTable
  101. StartEviction
  102. DeleteOriginFromDatabase
  103. DidOriginDataEvicted
  104. ReportHistogram
  105. DidGetTemporaryGlobalUsageForHistogram
  106. DidGetPersistentGlobalUsageForHistogram
  107. GetLRUOrigin
  108. EvictOriginData
  109. GetUsageAndQuotaForEviction
  110. DidSetTemporaryGlobalOverrideQuota
  111. DidGetPersistentHostQuota
  112. DidSetPersistentHostQuota
  113. DidInitialize
  114. DidGetLRUOrigin
  115. DidGetInitialTemporaryGlobalQuota
  116. DidInitializeTemporaryOriginsInfo
  117. DidGetAvailableSpace
  118. DidDatabaseWork
  119. DeleteOnCorrectThread
  120. PostTaskAndReplyWithResultForDBThread

// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "webkit/browser/quota/quota_manager.h"

#include <algorithm>
#include <deque>
#include <functional>
#include <set>

#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/callback.h"
#include "base/command_line.h"
#include "base/file_util.h"
#include "base/files/file_path.h"
#include "base/metrics/histogram.h"
#include "base/sequenced_task_runner.h"
#include "base/single_thread_task_runner.h"
#include "base/strings/string_number_conversions.h"
#include "base/sys_info.h"
#include "base/task_runner_util.h"
#include "base/time/time.h"
#include "net/base/net_util.h"
#include "webkit/browser/quota/quota_database.h"
#include "webkit/browser/quota/quota_manager_proxy.h"
#include "webkit/browser/quota/quota_temporary_storage_evictor.h"
#include "webkit/browser/quota/storage_monitor.h"
#include "webkit/browser/quota/usage_tracker.h"
#include "webkit/common/quota/quota_types.h"

#define UMA_HISTOGRAM_MBYTES(name, sample)          \
  UMA_HISTOGRAM_CUSTOM_COUNTS(                      \
      (name), static_cast<int>((sample) / kMBytes), \
      1, 10 * 1024 * 1024 /* 10TB */, 100)

namespace quota {

namespace {

const int64 kMBytes = 1024 * 1024;
const int kMinutesInMilliSeconds = 60 * 1000;

const int64 kReportHistogramInterval = 60 * 60 * 1000;  // 1 hour
const double kTemporaryQuotaRatioToAvail = 1.0 / 3.0;  // 33%

}  // namespace

// Arbitrary for now, but must be reasonably small so that
// in-memory databases can fit.
// TODO(kinuko): Refer SysInfo::AmountOfPhysicalMemory() to determine this.
const int64 QuotaManager::kIncognitoDefaultQuotaLimit = 100 * kMBytes;

const int64 QuotaManager::kNoLimit = kint64max;

const int QuotaManager::kPerHostTemporaryPortion = 5;  // 20%

// Cap size for per-host persistent quota determined by the histogram.
// This is a bit lax value because the histogram says nothing about per-host
// persistent storage usage and we determined by global persistent storage
// usage that is less than 10GB for almost all users.
const int64 QuotaManager::kPerHostPersistentQuotaLimit = 10 * 1024 * kMBytes;

const char QuotaManager::kDatabaseName[] = "QuotaManager";

const int QuotaManager::kThresholdOfErrorsToBeBlacklisted = 3;

// Preserve kMinimumPreserveForSystem disk space for system book-keeping
// when returning the quota to unlimited apps/extensions.
// TODO(kinuko): This should be like 10% of the actual disk space.
// For now we simply use a constant as getting the disk size needs
// platform-dependent code. (http://crbug.com/178976)
int64 QuotaManager::kMinimumPreserveForSystem = 1024 * kMBytes;

const int QuotaManager::kEvictionIntervalInMilliSeconds =
    30 * kMinutesInMilliSeconds;

// Heuristics: assuming average cloud server allows a few Gigs storage
// on the server side and the storage needs to be shared for user data
// and by multiple apps.
int64 QuotaManager::kSyncableStorageDefaultHostQuota = 500 * kMBytes;

namespace {

void CountOriginType(const std::set<GURL>& origins,
                     SpecialStoragePolicy* policy,
                     size_t* protected_origins,
                     size_t* unlimited_origins) {
  DCHECK(protected_origins);
  DCHECK(unlimited_origins);
  *protected_origins = 0;
  *unlimited_origins = 0;
  if (!policy)
    return;
  for (std::set<GURL>::const_iterator itr = origins.begin();
       itr != origins.end();
       ++itr) {
    if (policy->IsStorageProtected(*itr))
      ++*protected_origins;
    if (policy->IsStorageUnlimited(*itr))
      ++*unlimited_origins;
  }
}

bool SetTemporaryGlobalOverrideQuotaOnDBThread(int64* new_quota,
                                               QuotaDatabase* database) {
  DCHECK(database);
  if (!database->SetQuotaConfigValue(
          QuotaDatabase::kTemporaryQuotaOverrideKey, *new_quota)) {
    *new_quota = -1;
    return false;
  }
  return true;
}

bool GetPersistentHostQuotaOnDBThread(const std::string& host,
                                      int64* quota,
                                      QuotaDatabase* database) {
  DCHECK(database);
  database->GetHostQuota(host, kStorageTypePersistent, quota);
  return true;
}

bool SetPersistentHostQuotaOnDBThread(const std::string& host,
                                      int64* new_quota,
                                      QuotaDatabase* database) {
  DCHECK(database);
  if (database->SetHostQuota(host, kStorageTypePersistent, *new_quota))
    return true;
  *new_quota = 0;
  return false;
}

bool InitializeOnDBThread(int64* temporary_quota_override,
                          int64* desired_available_space,
                          QuotaDatabase* database) {
  DCHECK(database);
  database->GetQuotaConfigValue(QuotaDatabase::kTemporaryQuotaOverrideKey,
                                temporary_quota_override);
  database->GetQuotaConfigValue(QuotaDatabase::kDesiredAvailableSpaceKey,
                                desired_available_space);
  return true;
}

bool GetLRUOriginOnDBThread(StorageType type,
                            std::set<GURL>* exceptions,
                            SpecialStoragePolicy* policy,
                            GURL* url,
                            QuotaDatabase* database) {
  DCHECK(database);
  database->GetLRUOrigin(type, *exceptions, policy, url);
  return true;
}

bool DeleteOriginInfoOnDBThread(const GURL& origin,
                                StorageType type,
                                QuotaDatabase* database) {
  DCHECK(database);
  return database->DeleteOriginInfo(origin, type);
}

bool InitializeTemporaryOriginsInfoOnDBThread(const std::set<GURL>* origins,
                                              QuotaDatabase* database) {
  DCHECK(database);
  if (database->IsOriginDatabaseBootstrapped())
    return true;

  // Register existing origins with 0 last time access.
  if (database->RegisterInitialOriginInfo(*origins, kStorageTypeTemporary)) {
    database->SetOriginDatabaseBootstrapped(true);
    return true;
  }
  return false;
}

bool UpdateAccessTimeOnDBThread(const GURL& origin,
                                StorageType type,
                                base::Time accessed_time,
                                QuotaDatabase* database) {
  DCHECK(database);
  return database->SetOriginLastAccessTime(origin, type, accessed_time);
}

bool UpdateModifiedTimeOnDBThread(const GURL& origin,
                                  StorageType type,
                                  base::Time modified_time,
                                  QuotaDatabase* database) {
  DCHECK(database);
  return database->SetOriginLastModifiedTime(origin, type, modified_time);
}

int64 CallSystemGetAmountOfFreeDiskSpace(const base::FilePath& profile_path) {
  // Ensure the profile path exists.
  if (!base::CreateDirectory(profile_path)) {
    LOG(WARNING) << "Create directory failed for path" << profile_path.value();
    return 0;
  }
  return base::SysInfo::AmountOfFreeDiskSpace(profile_path);
}

int64 CalculateTemporaryGlobalQuota(int64 global_limited_usage,
                                    int64 available_space) {
  DCHECK_GE(global_limited_usage, 0);
  int64 avail_space = available_space;
  if (avail_space < kint64max - global_limited_usage) {
    // We basically calculate the temporary quota by
    // [available_space + space_used_for_temp] * kTempQuotaRatio,
    // but make sure we'll have no overflow.
    avail_space += global_limited_usage;
  }
  return avail_space * kTemporaryQuotaRatioToAvail;
}

void DispatchTemporaryGlobalQuotaCallback(
    const QuotaCallback& callback,
    QuotaStatusCode status,
    const UsageAndQuota& usage_and_quota) {
  if (status != kQuotaStatusOk) {
    callback.Run(status, 0);
    return;
  }

  callback.Run(status, CalculateTemporaryGlobalQuota(
      usage_and_quota.global_limited_usage,
      usage_and_quota.available_disk_space));
}

int64 CalculateQuotaWithDiskSpace(
    int64 available_disk_space, int64 usage, int64 quota) {
  if (available_disk_space < QuotaManager::kMinimumPreserveForSystem) {
    LOG(WARNING)
        << "Running out of disk space for profile."
        << " QuotaManager starts forbidding further quota consumption.";
    return usage;
  }

  if (quota < usage) {
    // No more space; cap the quota to the current usage.
    return usage;
  }

  available_disk_space -= QuotaManager::kMinimumPreserveForSystem;
  if (available_disk_space < quota - usage)
    return available_disk_space + usage;

  return quota;
}

int64 CalculateTemporaryHostQuota(int64 host_usage,
                                  int64 global_quota,
                                  int64 global_limited_usage) {
  DCHECK_GE(global_limited_usage, 0);
  int64 host_quota = global_quota / QuotaManager::kPerHostTemporaryPortion;
  if (global_limited_usage > global_quota)
    host_quota = std::min(host_quota, host_usage);
  return host_quota;
}

void DispatchUsageAndQuotaForWebApps(
    StorageType type,
    bool is_incognito,
    bool is_unlimited,
    bool can_query_disk_size,
    const QuotaManager::GetUsageAndQuotaCallback& callback,
    QuotaStatusCode status,
    const UsageAndQuota& usage_and_quota) {
  if (status != kQuotaStatusOk) {
    callback.Run(status, 0, 0);
    return;
  }

  int64 usage = usage_and_quota.usage;
  int64 quota = usage_and_quota.quota;

  if (type == kStorageTypeTemporary && !is_unlimited) {
    quota = CalculateTemporaryHostQuota(
        usage, quota, usage_and_quota.global_limited_usage);
  }

  if (is_incognito) {
    quota = std::min(quota, QuotaManager::kIncognitoDefaultQuotaLimit);
    callback.Run(status, usage, quota);
    return;
  }

  // For apps with unlimited permission or can_query_disk_size is true (and not
  // in incognito mode).
  // We assume we can expose the actual disk size for them and cap the quota by
  // the available disk space.
  if (is_unlimited || can_query_disk_size) {
    callback.Run(
        status, usage,
        CalculateQuotaWithDiskSpace(
            usage_and_quota.available_disk_space,
            usage, quota));
    return;
  }

  callback.Run(status, usage, quota);
}

}  // namespace

UsageAndQuota::UsageAndQuota()
    : usage(0),
      global_limited_usage(0),
      quota(0),
      available_disk_space(0) {
}

UsageAndQuota::UsageAndQuota(
    int64 usage,
    int64 global_limited_usage,
    int64 quota,
    int64 available_disk_space)
    : usage(usage),
      global_limited_usage(global_limited_usage),
      quota(quota),
      available_disk_space(available_disk_space) {
}

class UsageAndQuotaCallbackDispatcher
    : public QuotaTask,
      public base::SupportsWeakPtr<UsageAndQuotaCallbackDispatcher> {
 public:
  explicit UsageAndQuotaCallbackDispatcher(QuotaManager* manager)
      : QuotaTask(manager),
        has_usage_(false),
        has_global_limited_usage_(false),
        has_quota_(false),
        has_available_disk_space_(false),
        status_(kQuotaStatusUnknown),
        usage_and_quota_(-1, -1, -1, -1),
        waiting_callbacks_(1) {}

  virtual ~UsageAndQuotaCallbackDispatcher() {}

  void WaitForResults(const QuotaManager::UsageAndQuotaCallback& callback) {
    callback_ = callback;
    Start();
  }

  void set_usage(int64 usage) {
    usage_and_quota_.usage = usage;
    has_usage_ = true;
  }

  void set_global_limited_usage(int64 global_limited_usage) {
    usage_and_quota_.global_limited_usage = global_limited_usage;
    has_global_limited_usage_ = true;
  }

  void set_quota(int64 quota) {
    usage_and_quota_.quota = quota;
    has_quota_ = true;
  }

  void set_available_disk_space(int64 available_disk_space) {
    usage_and_quota_.available_disk_space = available_disk_space;
    has_available_disk_space_ = true;
  }

  UsageCallback GetHostUsageCallback() {
    ++waiting_callbacks_;
    has_usage_ = true;
    return base::Bind(&UsageAndQuotaCallbackDispatcher::DidGetHostUsage,
                      AsWeakPtr());
  }

  UsageCallback GetGlobalLimitedUsageCallback() {
    ++waiting_callbacks_;
    has_global_limited_usage_ = true;
    return base::Bind(
        &UsageAndQuotaCallbackDispatcher::DidGetGlobalLimitedUsage,
        AsWeakPtr());
  }

  QuotaCallback GetQuotaCallback() {
    ++waiting_callbacks_;
    has_quota_ = true;
    return base::Bind(&UsageAndQuotaCallbackDispatcher::DidGetQuota,
                      AsWeakPtr());
  }

  QuotaCallback GetAvailableSpaceCallback() {
    ++waiting_callbacks_;
    has_available_disk_space_ = true;
    return base::Bind(&UsageAndQuotaCallbackDispatcher::DidGetAvailableSpace,
                      AsWeakPtr());
  }

 private:
  void DidGetHostUsage(int64 usage) {
    if (status_ == kQuotaStatusUnknown)
      status_ = kQuotaStatusOk;
    usage_and_quota_.usage = usage;
    CheckCompleted();
  }

  void DidGetGlobalLimitedUsage(int64 limited_usage) {
    if (status_ == kQuotaStatusUnknown)
      status_ = kQuotaStatusOk;
    usage_and_quota_.global_limited_usage = limited_usage;
    CheckCompleted();
  }

  void DidGetQuota(QuotaStatusCode status, int64 quota) {
    if (status_ == kQuotaStatusUnknown || status_ == kQuotaStatusOk)
      status_ = status;
    usage_and_quota_.quota = quota;
    CheckCompleted();
  }

  void DidGetAvailableSpace(QuotaStatusCode status, int64 space) {
    DCHECK_GE(space, 0);
    if (status_ == kQuotaStatusUnknown || status_ == kQuotaStatusOk)
      status_ = status;
    usage_and_quota_.available_disk_space = space;
    CheckCompleted();
  }

  virtual void Run() OVERRIDE {
    // We initialize waiting_callbacks to 1 so that we won't run
    // the completion callback until here even some of the callbacks
    // are dispatched synchronously.
    CheckCompleted();
  }

  virtual void Aborted() OVERRIDE {
    callback_.Run(kQuotaErrorAbort, UsageAndQuota());
    DeleteSoon();
  }

  virtual void Completed() OVERRIDE {
    DCHECK(!has_usage_ || usage_and_quota_.usage >= 0);
    DCHECK(!has_global_limited_usage_ ||
           usage_and_quota_.global_limited_usage >= 0);
    DCHECK(!has_quota_ || usage_and_quota_.quota >= 0);
    DCHECK(!has_available_disk_space_ ||
           usage_and_quota_.available_disk_space >= 0);

    callback_.Run(status_, usage_and_quota_);
    DeleteSoon();
  }

  void CheckCompleted() {
    if (--waiting_callbacks_ <= 0)
      CallCompleted();
  }

  // For sanity checks, they're checked only when DCHECK is on.
  bool has_usage_;
  bool has_global_limited_usage_;
  bool has_quota_;
  bool has_available_disk_space_;

  QuotaStatusCode status_;
  UsageAndQuota usage_and_quota_;
  QuotaManager::UsageAndQuotaCallback callback_;
  int waiting_callbacks_;

  DISALLOW_COPY_AND_ASSIGN(UsageAndQuotaCallbackDispatcher);
};

class QuotaManager::GetUsageInfoTask : public QuotaTask {
 public:
  GetUsageInfoTask(
      QuotaManager* manager,
      const GetUsageInfoCallback& callback)
      : QuotaTask(manager),
        callback_(callback),
        weak_factory_(this) {
  }

 protected:
  virtual void Run() OVERRIDE {
    remaining_trackers_ = 3;
    // This will populate cached hosts and usage info.
    manager()->GetUsageTracker(kStorageTypeTemporary)->GetGlobalUsage(
        base::Bind(&GetUsageInfoTask::DidGetGlobalUsage,
                   weak_factory_.GetWeakPtr(),
                   kStorageTypeTemporary));
    manager()->GetUsageTracker(kStorageTypePersistent)->GetGlobalUsage(
        base::Bind(&GetUsageInfoTask::DidGetGlobalUsage,
                   weak_factory_.GetWeakPtr(),
                   kStorageTypePersistent));
    manager()->GetUsageTracker(kStorageTypeSyncable)->GetGlobalUsage(
        base::Bind(&GetUsageInfoTask::DidGetGlobalUsage,
                   weak_factory_.GetWeakPtr(),
                   kStorageTypeSyncable));
  }

  virtual void Completed() OVERRIDE {
    callback_.Run(entries_);
    DeleteSoon();
  }

  virtual void Aborted() OVERRIDE {
    callback_.Run(UsageInfoEntries());
    DeleteSoon();
  }

 private:
  void AddEntries(StorageType type, UsageTracker* tracker) {
    std::map<std::string, int64> host_usage;
    tracker->GetCachedHostsUsage(&host_usage);
    for (std::map<std::string, int64>::const_iterator iter = host_usage.begin();
         iter != host_usage.end();
         ++iter) {
      entries_.push_back(UsageInfo(iter->first, type, iter->second));
    }
    if (--remaining_trackers_ == 0)
      CallCompleted();
  }

  void DidGetGlobalUsage(StorageType type, int64, int64) {
    DCHECK(manager()->GetUsageTracker(type));
    AddEntries(type, manager()->GetUsageTracker(type));
  }

  QuotaManager* manager() const {
    return static_cast<QuotaManager*>(observer());
  }

  GetUsageInfoCallback callback_;
  UsageInfoEntries entries_;
  int remaining_trackers_;
  base::WeakPtrFactory<GetUsageInfoTask> weak_factory_;

  DISALLOW_COPY_AND_ASSIGN(GetUsageInfoTask);
};

class QuotaManager::OriginDataDeleter : public QuotaTask {
 public:
  OriginDataDeleter(QuotaManager* manager,
                    const GURL& origin,
                    StorageType type,
                    int quota_client_mask,
                    const StatusCallback& callback)
      : QuotaTask(manager),
        origin_(origin),
        type_(type),
        quota_client_mask_(quota_client_mask),
        error_count_(0),
        remaining_clients_(-1),
        skipped_clients_(0),
        callback_(callback),
        weak_factory_(this) {}

 protected:
  virtual void Run() OVERRIDE {
    error_count_ = 0;
    remaining_clients_ = manager()->clients_.size();
    for (QuotaClientList::iterator iter = manager()->clients_.begin();
         iter != manager()->clients_.end(); ++iter) {
      if (quota_client_mask_ & (*iter)->id()) {
        (*iter)->DeleteOriginData(
            origin_, type_,
            base::Bind(&OriginDataDeleter::DidDeleteOriginData,
                       weak_factory_.GetWeakPtr()));
      } else {
        ++skipped_clients_;
        if (--remaining_clients_ == 0)
          CallCompleted();
      }
    }
  }

  virtual void Completed() OVERRIDE {
    if (error_count_ == 0) {
      // Only remove the entire origin if we didn't skip any client types.
      if (skipped_clients_ == 0)
        manager()->DeleteOriginFromDatabase(origin_, type_);
      callback_.Run(kQuotaStatusOk);
    } else {
      callback_.Run(kQuotaErrorInvalidModification);
    }
    DeleteSoon();
  }

  virtual void Aborted() OVERRIDE {
    callback_.Run(kQuotaErrorAbort);
    DeleteSoon();
  }

 private:
  void DidDeleteOriginData(QuotaStatusCode status) {
    DCHECK_GT(remaining_clients_, 0);

    if (status != kQuotaStatusOk)
      ++error_count_;

    if (--remaining_clients_ == 0)
      CallCompleted();
  }

  QuotaManager* manager() const {
    return static_cast<QuotaManager*>(observer());
  }

  GURL origin_;
  StorageType type_;
  int quota_client_mask_;
  int error_count_;
  int remaining_clients_;
  int skipped_clients_;
  StatusCallback callback_;

  base::WeakPtrFactory<OriginDataDeleter> weak_factory_;
  DISALLOW_COPY_AND_ASSIGN(OriginDataDeleter);
};

class QuotaManager::HostDataDeleter : public QuotaTask {
 public:
  HostDataDeleter(QuotaManager* manager,
                  const std::string& host,
                  StorageType type,
                  int quota_client_mask,
                  const StatusCallback& callback)
      : QuotaTask(manager),
        host_(host),
        type_(type),
        quota_client_mask_(quota_client_mask),
        error_count_(0),
        remaining_clients_(-1),
        remaining_deleters_(-1),
        callback_(callback),
        weak_factory_(this) {}

 protected:
  virtual void Run() OVERRIDE {
    error_count_ = 0;
    remaining_clients_ = manager()->clients_.size();
    for (QuotaClientList::iterator iter = manager()->clients_.begin();
         iter != manager()->clients_.end(); ++iter) {
      (*iter)->GetOriginsForHost(
          type_, host_,
          base::Bind(&HostDataDeleter::DidGetOriginsForHost,
                     weak_factory_.GetWeakPtr()));
    }
  }

  virtual void Completed() OVERRIDE {
    if (error_count_ == 0) {
      callback_.Run(kQuotaStatusOk);
    } else {
      callback_.Run(kQuotaErrorInvalidModification);
    }
    DeleteSoon();
  }

  virtual void Aborted() OVERRIDE {
    callback_.Run(kQuotaErrorAbort);
    DeleteSoon();
  }

 private:
  void DidGetOriginsForHost(const std::set<GURL>& origins) {
    DCHECK_GT(remaining_clients_, 0);

    origins_.insert(origins.begin(), origins.end());

    if (--remaining_clients_ == 0) {
      if (!origins_.empty())
        ScheduleOriginsDeletion();
      else
        CallCompleted();
    }
  }

  void ScheduleOriginsDeletion() {
    remaining_deleters_ = origins_.size();
    for (std::set<GURL>::const_iterator p = origins_.begin();
         p != origins_.end();
         ++p) {
      OriginDataDeleter* deleter =
          new OriginDataDeleter(
              manager(), *p, type_, quota_client_mask_,
              base::Bind(&HostDataDeleter::DidDeleteOriginData,
                         weak_factory_.GetWeakPtr()));
      deleter->Start();
    }
  }

  void DidDeleteOriginData(QuotaStatusCode status) {
    DCHECK_GT(remaining_deleters_, 0);

    if (status != kQuotaStatusOk)
      ++error_count_;

    if (--remaining_deleters_ == 0)
      CallCompleted();
  }

  QuotaManager* manager() const {
    return static_cast<QuotaManager*>(observer());
  }

  std::string host_;
  StorageType type_;
  int quota_client_mask_;
  std::set<GURL> origins_;
  int error_count_;
  int remaining_clients_;
  int remaining_deleters_;
  StatusCallback callback_;

  base::WeakPtrFactory<HostDataDeleter> weak_factory_;
  DISALLOW_COPY_AND_ASSIGN(HostDataDeleter);
};

class QuotaManager::GetModifiedSinceHelper {
 public:
  bool GetModifiedSinceOnDBThread(StorageType type,
                                  base::Time modified_since,
                                  QuotaDatabase* database) {
    DCHECK(database);
    return database->GetOriginsModifiedSince(type, &origins_, modified_since);
  }

  void DidGetModifiedSince(const base::WeakPtr<QuotaManager>& manager,
                           const GetOriginsCallback& callback,
                           StorageType type,
                           bool success) {
    if (!manager) {
      // The operation was aborted.
      callback.Run(std::set<GURL>(), type);
      return;
    }
    manager->DidDatabaseWork(success);
    callback.Run(origins_, type);
  }

 private:
  std::set<GURL> origins_;
};

class QuotaManager::DumpQuotaTableHelper {
 public:
  bool DumpQuotaTableOnDBThread(QuotaDatabase* database) {
    DCHECK(database);
    return database->DumpQuotaTable(
        base::Bind(&DumpQuotaTableHelper::AppendEntry, base::Unretained(this)));
  }

  void DidDumpQuotaTable(const base::WeakPtr<QuotaManager>& manager,
                         const DumpQuotaTableCallback& callback,
                         bool success) {
    if (!manager) {
      // The operation was aborted.
      callback.Run(QuotaTableEntries());
      return;
    }
    manager->DidDatabaseWork(success);
    callback.Run(entries_);
  }

 private:
  bool AppendEntry(const QuotaTableEntry& entry) {
    entries_.push_back(entry);
    return true;
  }

  QuotaTableEntries entries_;
};

class QuotaManager::DumpOriginInfoTableHelper {
 public:
  bool DumpOriginInfoTableOnDBThread(QuotaDatabase* database) {
    DCHECK(database);
    return database->DumpOriginInfoTable(
        base::Bind(&DumpOriginInfoTableHelper::AppendEntry,
                   base::Unretained(this)));
  }

  void DidDumpOriginInfoTable(const base::WeakPtr<QuotaManager>& manager,
                              const DumpOriginInfoTableCallback& callback,
                              bool success) {
    if (!manager) {
      // The operation was aborted.
      callback.Run(OriginInfoTableEntries());
      return;
    }
    manager->DidDatabaseWork(success);
    callback.Run(entries_);
  }

 private:
  bool AppendEntry(const OriginInfoTableEntry& entry) {
    entries_.push_back(entry);
    return true;
  }

  OriginInfoTableEntries entries_;
};

// QuotaManager ---------------------------------------------------------------

QuotaManager::QuotaManager(bool is_incognito,
                           const base::FilePath& profile_path,
                           base::SingleThreadTaskRunner* io_thread,
                           base::SequencedTaskRunner* db_thread,
                           SpecialStoragePolicy* special_storage_policy)
  : is_incognito_(is_incognito),
    profile_path_(profile_path),
    proxy_(new QuotaManagerProxy(
        this, io_thread)),
    db_disabled_(false),
    eviction_disabled_(false),
    io_thread_(io_thread),
    db_thread_(db_thread),
    temporary_quota_initialized_(false),
    temporary_quota_override_(-1),
    desired_available_space_(-1),
    special_storage_policy_(special_storage_policy),
    get_disk_space_fn_(&CallSystemGetAmountOfFreeDiskSpace),
    storage_monitor_(new StorageMonitor(this)),
    weak_factory_(this) {
}

void QuotaManager::GetUsageInfo(const GetUsageInfoCallback& callback) {
  LazyInitialize();
  GetUsageInfoTask* get_usage_info = new GetUsageInfoTask(this, callback);
  get_usage_info->Start();
}

void QuotaManager::GetUsageAndQuotaForWebApps(
    const GURL& origin,
    StorageType type,
    const GetUsageAndQuotaCallback& callback) {
  if (type != kStorageTypeTemporary &&
      type != kStorageTypePersistent &&
      type != kStorageTypeSyncable) {
    callback.Run(kQuotaErrorNotSupported, 0, 0);
    return;
  }

  DCHECK(origin == origin.GetOrigin());
  LazyInitialize();

  bool unlimited = IsStorageUnlimited(origin, type);
  bool can_query_disk_size = CanQueryDiskSize(origin);

  UsageAndQuotaCallbackDispatcher* dispatcher =
      new UsageAndQuotaCallbackDispatcher(this);

  UsageAndQuota usage_and_quota;
  if (unlimited) {
    dispatcher->set_quota(kNoLimit);
  } else {
    if (type == kStorageTypeTemporary) {
      GetUsageTracker(type)->GetGlobalLimitedUsage(
          dispatcher->GetGlobalLimitedUsageCallback());
      GetTemporaryGlobalQuota(dispatcher->GetQuotaCallback());
    } else if (type == kStorageTypePersistent) {
      GetPersistentHostQuota(net::GetHostOrSpecFromURL(origin),
                             dispatcher->GetQuotaCallback());
    } else {
      dispatcher->set_quota(kSyncableStorageDefaultHostQuota);
    }
  }

  DCHECK(GetUsageTracker(type));
  GetUsageTracker(type)->GetHostUsage(net::GetHostOrSpecFromURL(origin),
                                      dispatcher->GetHostUsageCallback());

  if (!is_incognito_ && (unlimited || can_query_disk_size))
    GetAvailableSpace(dispatcher->GetAvailableSpaceCallback());

  dispatcher->WaitForResults(base::Bind(
      &DispatchUsageAndQuotaForWebApps,
      type, is_incognito_, unlimited, can_query_disk_size,
      callback));
}

void QuotaManager::GetUsageAndQuota(
    const GURL& origin, StorageType type,
    const GetUsageAndQuotaCallback& callback) {
  DCHECK(origin == origin.GetOrigin());

  if (IsStorageUnlimited(origin, type)) {
    callback.Run(kQuotaStatusOk, 0, kNoLimit);
    return;
  }

  GetUsageAndQuotaForWebApps(origin, type, callback);
}

void QuotaManager::NotifyStorageAccessed(
    QuotaClient::ID client_id,
    const GURL& origin, StorageType type) {
  DCHECK(origin == origin.GetOrigin());
  NotifyStorageAccessedInternal(client_id, origin, type, base::Time::Now());
}

void QuotaManager::NotifyStorageModified(
    QuotaClient::ID client_id,
    const GURL& origin, StorageType type, int64 delta) {
  DCHECK(origin == origin.GetOrigin());
  NotifyStorageModifiedInternal(client_id, origin, type, delta,
                                base::Time::Now());
}

void QuotaManager::NotifyOriginInUse(const GURL& origin) {
  DCHECK(io_thread_->BelongsToCurrentThread());
  origins_in_use_[origin]++;
}

void QuotaManager::NotifyOriginNoLongerInUse(const GURL& origin) {
  DCHECK(io_thread_->BelongsToCurrentThread());
  DCHECK(IsOriginInUse(origin));
  int& count = origins_in_use_[origin];
  if (--count == 0)
    origins_in_use_.erase(origin);
}

void QuotaManager::SetUsageCacheEnabled(QuotaClient::ID client_id,
                                        const GURL& origin,
                                        StorageType type,
                                        bool enabled) {
  LazyInitialize();
  DCHECK(GetUsageTracker(type));
  GetUsageTracker(type)->SetUsageCacheEnabled(client_id, origin, enabled);
}

void QuotaManager::DeleteOriginData(
    const GURL& origin, StorageType type, int quota_client_mask,
    const StatusCallback& callback) {
  LazyInitialize();

  if (origin.is_empty() || clients_.empty()) {
    callback.Run(kQuotaStatusOk);
    return;
  }

  DCHECK(origin == origin.GetOrigin());
  OriginDataDeleter* deleter =
      new OriginDataDeleter(this, origin, type, quota_client_mask, callback);
  deleter->Start();
}

void QuotaManager::DeleteHostData(const std::string& host,
                                  StorageType type,
                                  int quota_client_mask,
                                  const StatusCallback& callback) {
  LazyInitialize();

  if (host.empty() || clients_.empty()) {
    callback.Run(kQuotaStatusOk);
    return;
  }

  HostDataDeleter* deleter =
      new HostDataDeleter(this, host, type, quota_client_mask, callback);
  deleter->Start();
}

void QuotaManager::GetAvailableSpace(const AvailableSpaceCallback& callback) {
  if (!available_space_callbacks_.Add(callback))
    return;

  PostTaskAndReplyWithResult(db_thread_.get(),
                             FROM_HERE,
                             base::Bind(get_disk_space_fn_, profile_path_),
                             base::Bind(&QuotaManager::DidGetAvailableSpace,
                                        weak_factory_.GetWeakPtr()));
}

void QuotaManager::GetTemporaryGlobalQuota(const QuotaCallback& callback) {
  LazyInitialize();
  if (!temporary_quota_initialized_) {
    db_initialization_callbacks_.Add(base::Bind(
        &QuotaManager::GetTemporaryGlobalQuota,
        weak_factory_.GetWeakPtr(), callback));
    return;
  }

  if (temporary_quota_override_ > 0) {
    callback.Run(kQuotaStatusOk, temporary_quota_override_);
    return;
  }

  UsageAndQuotaCallbackDispatcher* dispatcher =
      new UsageAndQuotaCallbackDispatcher(this);
  GetUsageTracker(kStorageTypeTemporary)->
      GetGlobalLimitedUsage(dispatcher->GetGlobalLimitedUsageCallback());
  GetAvailableSpace(dispatcher->GetAvailableSpaceCallback());
  dispatcher->WaitForResults(
      base::Bind(&DispatchTemporaryGlobalQuotaCallback, callback));
}

void QuotaManager::SetTemporaryGlobalOverrideQuota(
    int64 new_quota, const QuotaCallback& callback) {
  LazyInitialize();

  if (new_quota < 0) {
    if (!callback.is_null())
      callback.Run(kQuotaErrorInvalidModification, -1);
    return;
  }

  if (db_disabled_) {
    if (!callback.is_null())
      callback.Run(kQuotaErrorInvalidAccess, -1);
    return;
  }

  int64* new_quota_ptr = new int64(new_quota);
  PostTaskAndReplyWithResultForDBThread(
      FROM_HERE,
      base::Bind(&SetTemporaryGlobalOverrideQuotaOnDBThread,
                 base::Unretained(new_quota_ptr)),
      base::Bind(&QuotaManager::DidSetTemporaryGlobalOverrideQuota,
                 weak_factory_.GetWeakPtr(),
                 callback,
                 base::Owned(new_quota_ptr)));
}

void QuotaManager::GetPersistentHostQuota(const std::string& host,
                                          const QuotaCallback& callback) {
  LazyInitialize();
  if (host.empty()) {
    // This could happen if we are called on file:///.
    // TODO(kinuko) We may want to respect --allow-file-access-from-files
    // command line switch.
    callback.Run(kQuotaStatusOk, 0);
    return;
  }

  if (!persistent_host_quota_callbacks_.Add(host, callback))
    return;

  int64* quota_ptr = new int64(0);
  PostTaskAndReplyWithResultForDBThread(
      FROM_HERE,
      base::Bind(&GetPersistentHostQuotaOnDBThread,
                 host,
                 base::Unretained(quota_ptr)),
      base::Bind(&QuotaManager::DidGetPersistentHostQuota,
                 weak_factory_.GetWeakPtr(),
                 host,
                 base::Owned(quota_ptr)));
}

void QuotaManager::SetPersistentHostQuota(const std::string& host,
                                          int64 new_quota,
                                          const QuotaCallback& callback) {
  LazyInitialize();
  if (host.empty()) {
    // This could happen if we are called on file:///.
    callback.Run(kQuotaErrorNotSupported, 0);
    return;
  }

  if (new_quota < 0) {
    callback.Run(kQuotaErrorInvalidModification, -1);
    return;
  }

  if (kPerHostPersistentQuotaLimit < new_quota) {
    // Cap the requested size at the per-host quota limit.
    new_quota = kPerHostPersistentQuotaLimit;
  }

  if (db_disabled_) {
    callback.Run(kQuotaErrorInvalidAccess, -1);
    return;
  }

  int64* new_quota_ptr = new int64(new_quota);
  PostTaskAndReplyWithResultForDBThread(
      FROM_HERE,
      base::Bind(&SetPersistentHostQuotaOnDBThread,
                 host,
                 base::Unretained(new_quota_ptr)),
      base::Bind(&QuotaManager::DidSetPersistentHostQuota,
                 weak_factory_.GetWeakPtr(),
                 host,
                 callback,
                 base::Owned(new_quota_ptr)));
}

void QuotaManager::GetGlobalUsage(StorageType type,
                                  const GlobalUsageCallback& callback) {
  LazyInitialize();
  DCHECK(GetUsageTracker(type));
  GetUsageTracker(type)->GetGlobalUsage(callback);
}

void QuotaManager::GetHostUsage(const std::string& host,
                                StorageType type,
                                const UsageCallback& callback) {
  LazyInitialize();
  DCHECK(GetUsageTracker(type));
  GetUsageTracker(type)->GetHostUsage(host, callback);
}

void QuotaManager::GetHostUsage(const std::string& host,
                                StorageType type,
                                QuotaClient::ID client_id,
                                const UsageCallback& callback) {
  LazyInitialize();
  DCHECK(GetUsageTracker(type));
  ClientUsageTracker* tracker =
      GetUsageTracker(type)->GetClientTracker(client_id);
  if (!tracker) {
    callback.Run(0);
    return;
  }
  tracker->GetHostUsage(host, callback);
}

bool QuotaManager::IsTrackingHostUsage(StorageType type,
                                       QuotaClient::ID client_id) const {
  UsageTracker* tracker = GetUsageTracker(type);
  return tracker && tracker->GetClientTracker(client_id);
}

void QuotaManager::GetStatistics(
    std::map<std::string, std::string>* statistics) {
  DCHECK(statistics);
  if (temporary_storage_evictor_) {
    std::map<std::string, int64> stats;
    temporary_storage_evictor_->GetStatistics(&stats);
    for (std::map<std::string, int64>::iterator p = stats.begin();
         p != stats.end();
         ++p)
      (*statistics)[p->first] = base::Int64ToString(p->second);
  }
}

bool QuotaManager::IsStorageUnlimited(const GURL& origin,
                                      StorageType type) const {
  // For syncable storage we should always enforce quota (since the
  // quota must be capped by the server limit).
  if (type == kStorageTypeSyncable)
    return false;
  if (type == kStorageTypeQuotaNotManaged)
    return true;
  return special_storage_policy_.get() &&
         special_storage_policy_->IsStorageUnlimited(origin);
}

void QuotaManager::GetOriginsModifiedSince(StorageType type,
                                           base::Time modified_since,
                                           const GetOriginsCallback& callback) {
  LazyInitialize();
  GetModifiedSinceHelper* helper = new GetModifiedSinceHelper;
  PostTaskAndReplyWithResultForDBThread(
      FROM_HERE,
      base::Bind(&GetModifiedSinceHelper::GetModifiedSinceOnDBThread,
                 base::Unretained(helper),
                 type,
                 modified_since),
      base::Bind(&GetModifiedSinceHelper::DidGetModifiedSince,
                 base::Owned(helper),
                 weak_factory_.GetWeakPtr(),
                 callback,
                 type));
}

bool QuotaManager::ResetUsageTracker(StorageType type) {
  DCHECK(GetUsageTracker(type));
  if (GetUsageTracker(type)->IsWorking())
    return false;
  switch (type) {
    case kStorageTypeTemporary:
      temporary_usage_tracker_.reset(new UsageTracker(
          clients_, kStorageTypeTemporary, special_storage_policy_.get(),
          storage_monitor_.get()));
      return true;
    case kStorageTypePersistent:
      persistent_usage_tracker_.reset(new UsageTracker(
          clients_, kStorageTypePersistent, special_storage_policy_.get(),
          storage_monitor_.get()));
      return true;
    case kStorageTypeSyncable:
      syncable_usage_tracker_.reset(new UsageTracker(
          clients_, kStorageTypeSyncable, special_storage_policy_.get(),
          storage_monitor_.get()));
      return true;
    default:
      NOTREACHED();
  }
  return true;
}

void QuotaManager::AddStorageObserver(
    StorageObserver* observer, const StorageObserver::MonitorParams& params) {
  DCHECK(observer);
  storage_monitor_->AddObserver(observer, params);
}

void QuotaManager::RemoveStorageObserver(StorageObserver* observer) {
  DCHECK(observer);
  storage_monitor_->RemoveObserver(observer);
}

void QuotaManager::RemoveStorageObserverForFilter(
    StorageObserver* observer, const StorageObserver::Filter& filter) {
  DCHECK(observer);
  storage_monitor_->RemoveObserverForFilter(observer, filter);
}

QuotaManager::~QuotaManager() {
  proxy_->manager_ = NULL;
  std::for_each(clients_.begin(), clients_.end(),
                std::mem_fun(&QuotaClient::OnQuotaManagerDestroyed));
  if (database_)
    db_thread_->DeleteSoon(FROM_HERE, database_.release());
}

QuotaManager::EvictionContext::EvictionContext()
    : evicted_type(kStorageTypeUnknown) {
}

QuotaManager::EvictionContext::~EvictionContext() {
}

void QuotaManager::LazyInitialize() {
  DCHECK(io_thread_->BelongsToCurrentThread());
  if (database_) {
    // Initialization seems to be done already.
    return;
  }

  // Use an empty path to open an in-memory only databse for incognito.
  database_.reset(new QuotaDatabase(is_incognito_ ? base::FilePath() :
      profile_path_.AppendASCII(kDatabaseName)));

  temporary_usage_tracker_.reset(new UsageTracker(
      clients_, kStorageTypeTemporary, special_storage_policy_.get(),
      storage_monitor_.get()));
  persistent_usage_tracker_.reset(new UsageTracker(
      clients_, kStorageTypePersistent, special_storage_policy_.get(),
      storage_monitor_.get()));
  syncable_usage_tracker_.reset(new UsageTracker(
      clients_, kStorageTypeSyncable, special_storage_policy_.get(),
      storage_monitor_.get()));

  int64* temporary_quota_override = new int64(-1);
  int64* desired_available_space = new int64(-1);
  PostTaskAndReplyWithResultForDBThread(
      FROM_HERE,
      base::Bind(&InitializeOnDBThread,
                 base::Unretained(temporary_quota_override),
                 base::Unretained(desired_available_space)),
      base::Bind(&QuotaManager::DidInitialize,
                 weak_factory_.GetWeakPtr(),
                 base::Owned(temporary_quota_override),
                 base::Owned(desired_available_space)));
}

void QuotaManager::RegisterClient(QuotaClient* client) {
  DCHECK(!database_.get());
  clients_.push_back(client);
}

UsageTracker* QuotaManager::GetUsageTracker(StorageType type) const {
  switch (type) {
    case kStorageTypeTemporary:
      return temporary_usage_tracker_.get();
    case kStorageTypePersistent:
      return persistent_usage_tracker_.get();
    case kStorageTypeSyncable:
      return syncable_usage_tracker_.get();
    case kStorageTypeQuotaNotManaged:
      return NULL;
    case kStorageTypeUnknown:
      NOTREACHED();
  }
  return NULL;
}

void QuotaManager::GetCachedOrigins(
    StorageType type, std::set<GURL>* origins) {
  DCHECK(origins);
  LazyInitialize();
  DCHECK(GetUsageTracker(type));
  GetUsageTracker(type)->GetCachedOrigins(origins);
}

void QuotaManager::NotifyStorageAccessedInternal(
    QuotaClient::ID client_id,
    const GURL& origin, StorageType type,
    base::Time accessed_time) {
  LazyInitialize();
  if (type == kStorageTypeTemporary && !lru_origin_callback_.is_null()) {
    // Record the accessed origins while GetLRUOrigin task is runing
    // to filter out them from eviction.
    access_notified_origins_.insert(origin);
  }

  if (db_disabled_)
    return;
  PostTaskAndReplyWithResultForDBThread(
      FROM_HERE,
      base::Bind(&UpdateAccessTimeOnDBThread, origin, type, accessed_time),
      base::Bind(&QuotaManager::DidDatabaseWork,
                 weak_factory_.GetWeakPtr()));
}

void QuotaManager::NotifyStorageModifiedInternal(
    QuotaClient::ID client_id,
    const GURL& origin,
    StorageType type,
    int64 delta,
    base::Time modified_time) {
  LazyInitialize();
  DCHECK(GetUsageTracker(type));
  GetUsageTracker(type)->UpdateUsageCache(client_id, origin, delta);

  PostTaskAndReplyWithResultForDBThread(
      FROM_HERE,
      base::Bind(&UpdateModifiedTimeOnDBThread, origin, type, modified_time),
      base::Bind(&QuotaManager::DidDatabaseWork,
                 weak_factory_.GetWeakPtr()));
}

void QuotaManager::DumpQuotaTable(const DumpQuotaTableCallback& callback) {
  DumpQuotaTableHelper* helper = new DumpQuotaTableHelper;
  PostTaskAndReplyWithResultForDBThread(
      FROM_HERE,
      base::Bind(&DumpQuotaTableHelper::DumpQuotaTableOnDBThread,
                 base::Unretained(helper)),
      base::Bind(&DumpQuotaTableHelper::DidDumpQuotaTable,
                 base::Owned(helper),
                 weak_factory_.GetWeakPtr(),
                 callback));
}

void QuotaManager::DumpOriginInfoTable(
    const DumpOriginInfoTableCallback& callback) {
  DumpOriginInfoTableHelper* helper = new DumpOriginInfoTableHelper;
  PostTaskAndReplyWithResultForDBThread(
      FROM_HERE,
      base::Bind(&DumpOriginInfoTableHelper::DumpOriginInfoTableOnDBThread,
                 base::Unretained(helper)),
      base::Bind(&DumpOriginInfoTableHelper::DidDumpOriginInfoTable,
                 base::Owned(helper),
                 weak_factory_.GetWeakPtr(),
                 callback));
}

void QuotaManager::StartEviction() {
  DCHECK(!temporary_storage_evictor_.get());
  temporary_storage_evictor_.reset(new QuotaTemporaryStorageEvictor(
      this, kEvictionIntervalInMilliSeconds));
  if (desired_available_space_ >= 0)
    temporary_storage_evictor_->set_min_available_disk_space_to_start_eviction(
        desired_available_space_);
  temporary_storage_evictor_->Start();
}

void QuotaManager::DeleteOriginFromDatabase(
    const GURL& origin, StorageType type) {
  LazyInitialize();
  if (db_disabled_)
    return;

  PostTaskAndReplyWithResultForDBThread(
      FROM_HERE,
      base::Bind(&DeleteOriginInfoOnDBThread, origin, type),
      base::Bind(&QuotaManager::DidDatabaseWork,
                 weak_factory_.GetWeakPtr()));
}

void QuotaManager::DidOriginDataEvicted(QuotaStatusCode status) {
  DCHECK(io_thread_->BelongsToCurrentThread());

  // We only try evict origins that are not in use, so basically
  // deletion attempt for eviction should not fail.  Let's record
  // the origin if we get error and exclude it from future eviction
  // if the error happens consistently (> kThresholdOfErrorsToBeBlacklisted).
  if (status != kQuotaStatusOk)
    origins_in_error_[eviction_context_.evicted_origin]++;

  eviction_context_.evict_origin_data_callback.Run(status);
  eviction_context_.evict_origin_data_callback.Reset();
}

void QuotaManager::ReportHistogram() {
  GetGlobalUsage(kStorageTypeTemporary,
                 base::Bind(
                     &QuotaManager::DidGetTemporaryGlobalUsageForHistogram,
                     weak_factory_.GetWeakPtr()));
  GetGlobalUsage(kStorageTypePersistent,
                 base::Bind(
                     &QuotaManager::DidGetPersistentGlobalUsageForHistogram,
                     weak_factory_.GetWeakPtr()));
}

void QuotaManager::DidGetTemporaryGlobalUsageForHistogram(
    int64 usage,
    int64 unlimited_usage) {
  UMA_HISTOGRAM_MBYTES("Quota.GlobalUsageOfTemporaryStorage", usage);

  std::set<GURL> origins;
  GetCachedOrigins(kStorageTypeTemporary, &origins);

  size_t num_origins = origins.size();
  size_t protected_origins = 0;
  size_t unlimited_origins = 0;
  CountOriginType(origins,
                  special_storage_policy_.get(),
                  &protected_origins,
                  &unlimited_origins);

  UMA_HISTOGRAM_COUNTS("Quota.NumberOfTemporaryStorageOrigins",
                       num_origins);
  UMA_HISTOGRAM_COUNTS("Quota.NumberOfProtectedTemporaryStorageOrigins",
                       protected_origins);
  UMA_HISTOGRAM_COUNTS("Quota.NumberOfUnlimitedTemporaryStorageOrigins",
                       unlimited_origins);
}

void QuotaManager::DidGetPersistentGlobalUsageForHistogram(
    int64 usage,
    int64 unlimited_usage) {
  UMA_HISTOGRAM_MBYTES("Quota.GlobalUsageOfPersistentStorage", usage);

  std::set<GURL> origins;
  GetCachedOrigins(kStorageTypePersistent, &origins);

  size_t num_origins = origins.size();
  size_t protected_origins = 0;
  size_t unlimited_origins = 0;
  CountOriginType(origins,
                  special_storage_policy_.get(),
                  &protected_origins,
                  &unlimited_origins);

  UMA_HISTOGRAM_COUNTS("Quota.NumberOfPersistentStorageOrigins",
                       num_origins);
  UMA_HISTOGRAM_COUNTS("Quota.NumberOfProtectedPersistentStorageOrigins",
                       protected_origins);
  UMA_HISTOGRAM_COUNTS("Quota.NumberOfUnlimitedPersistentStorageOrigins",
                       unlimited_origins);
}

void QuotaManager::GetLRUOrigin(
    StorageType type,
    const GetLRUOriginCallback& callback) {
  LazyInitialize();
  // This must not be called while there's an in-flight task.
  DCHECK(lru_origin_callback_.is_null());
  lru_origin_callback_ = callback;
  if (db_disabled_) {
    lru_origin_callback_.Run(GURL());
    lru_origin_callback_.Reset();
    return;
  }

  std::set<GURL>* exceptions = new std::set<GURL>;
  for (std::map<GURL, int>::const_iterator p = origins_in_use_.begin();
       p != origins_in_use_.end();
       ++p) {
    if (p->second > 0)
      exceptions->insert(p->first);
  }
  for (std::map<GURL, int>::const_iterator p = origins_in_error_.begin();
       p != origins_in_error_.end();
       ++p) {
    if (p->second > QuotaManager::kThresholdOfErrorsToBeBlacklisted)
      exceptions->insert(p->first);
  }

  GURL* url = new GURL;
  PostTaskAndReplyWithResultForDBThread(
      FROM_HERE,
      base::Bind(&GetLRUOriginOnDBThread,
                 type,
                 base::Owned(exceptions),
                 special_storage_policy_,
                 base::Unretained(url)),
      base::Bind(&QuotaManager::DidGetLRUOrigin,
                 weak_factory_.GetWeakPtr(),
                 base::Owned(url)));
}

void QuotaManager::EvictOriginData(
    const GURL& origin,
    StorageType type,
    const EvictOriginDataCallback& callback) {
  DCHECK(io_thread_->BelongsToCurrentThread());
  DCHECK_EQ(type, kStorageTypeTemporary);

  eviction_context_.evicted_origin = origin;
  eviction_context_.evicted_type = type;
  eviction_context_.evict_origin_data_callback = callback;

  DeleteOriginData(origin, type, QuotaClient::kAllClientsMask,
      base::Bind(&QuotaManager::DidOriginDataEvicted,
                 weak_factory_.GetWeakPtr()));
}

void QuotaManager::GetUsageAndQuotaForEviction(
    const UsageAndQuotaCallback& callback) {
  DCHECK(io_thread_->BelongsToCurrentThread());
  LazyInitialize();

  UsageAndQuotaCallbackDispatcher* dispatcher =
      new UsageAndQuotaCallbackDispatcher(this);
  GetUsageTracker(kStorageTypeTemporary)->
      GetGlobalLimitedUsage(dispatcher->GetGlobalLimitedUsageCallback());
  GetTemporaryGlobalQuota(dispatcher->GetQuotaCallback());
  GetAvailableSpace(dispatcher->GetAvailableSpaceCallback());
  dispatcher->WaitForResults(callback);
}

void QuotaManager::DidSetTemporaryGlobalOverrideQuota(
    const QuotaCallback& callback,
    const int64* new_quota,
    bool success) {
  QuotaStatusCode status = kQuotaErrorInvalidAccess;
  DidDatabaseWork(success);
  if (success) {
    temporary_quota_override_ = *new_quota;
    status = kQuotaStatusOk;
  }

  if (callback.is_null())
    return;

  callback.Run(status, *new_quota);
}

void QuotaManager::DidGetPersistentHostQuota(const std::string& host,
                                             const int64* quota,
                                             bool success) {
  DidDatabaseWork(success);
  persistent_host_quota_callbacks_.Run(
      host, MakeTuple(kQuotaStatusOk, *quota));
}

void QuotaManager::DidSetPersistentHostQuota(const std::string& host,
                                             const QuotaCallback& callback,
                                             const int64* new_quota,
                                             bool success) {
  DidDatabaseWork(success);
  callback.Run(success ? kQuotaStatusOk : kQuotaErrorInvalidAccess, *new_quota);
}

void QuotaManager::DidInitialize(int64* temporary_quota_override,
                                 int64* desired_available_space,
                                 bool success) {
  temporary_quota_override_ = *temporary_quota_override;
  desired_available_space_ = *desired_available_space;
  temporary_quota_initialized_ = true;
  DidDatabaseWork(success);

  histogram_timer_.Start(FROM_HERE,
                         base::TimeDelta::FromMilliseconds(
                             kReportHistogramInterval),
                         this, &QuotaManager::ReportHistogram);

  db_initialization_callbacks_.Run(MakeTuple());
  GetTemporaryGlobalQuota(
      base::Bind(&QuotaManager::DidGetInitialTemporaryGlobalQuota,
                 weak_factory_.GetWeakPtr()));
}

void QuotaManager::DidGetLRUOrigin(const GURL* origin,
                                   bool success) {
  DidDatabaseWork(success);
  // Make sure the returned origin is (still) not in the origin_in_use_ set
  // and has not been accessed since we posted the task.
  if (origins_in_use_.find(*origin) != origins_in_use_.end() ||
      access_notified_origins_.find(*origin) != access_notified_origins_.end())
    lru_origin_callback_.Run(GURL());
  else
    lru_origin_callback_.Run(*origin);
  access_notified_origins_.clear();
  lru_origin_callback_.Reset();
}

void QuotaManager::DidGetInitialTemporaryGlobalQuota(
    QuotaStatusCode status, int64 quota_unused) {
  if (eviction_disabled_)
    return;

  std::set<GURL>* origins = new std::set<GURL>;
  temporary_usage_tracker_->GetCachedOrigins(origins);
  // This will call the StartEviction() when initial origin registration
  // is completed.
  PostTaskAndReplyWithResultForDBThread(
      FROM_HERE,
      base::Bind(&InitializeTemporaryOriginsInfoOnDBThread,
                 base::Owned(origins)),
      base::Bind(&QuotaManager::DidInitializeTemporaryOriginsInfo,
                 weak_factory_.GetWeakPtr()));
}

void QuotaManager::DidInitializeTemporaryOriginsInfo(bool success) {
  DidDatabaseWork(success);
  if (success)
    StartEviction();
}

void QuotaManager::DidGetAvailableSpace(int64 space) {
  available_space_callbacks_.Run(MakeTuple(kQuotaStatusOk, space));
}

void QuotaManager::DidDatabaseWork(bool success) {
  db_disabled_ = !success;
}

void QuotaManager::DeleteOnCorrectThread() const {
  if (!io_thread_->BelongsToCurrentThread() &&
      io_thread_->DeleteSoon(FROM_HERE, this)) {
    return;
  }
  delete this;
}

void QuotaManager::PostTaskAndReplyWithResultForDBThread(
    const tracked_objects::Location& from_here,
    const base::Callback<bool(QuotaDatabase*)>& task,
    const base::Callback<void(bool)>& reply) {
  // Deleting manager will post another task to DB thread to delete
  // |database_|, therefore we can be sure that database_ is alive when this
  // task runs.
  base::PostTaskAndReplyWithResult(
      db_thread_.get(),
      from_here,
      base::Bind(task, base::Unretained(database_.get())),
      reply);
}

}  // namespace quota

/* [<][>][^][v][top][bottom][index][help] */