/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "QuotaManagerService.h"

#include "ActorsChild.h"
#include "mozilla/BasePrincipal.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/Hal.h"
#include "mozilla/Preferences.h"
#include "mozilla/Unused.h"
#include "mozilla/ipc/BackgroundChild.h"
#include "mozilla/ipc/BackgroundParent.h"
#include "mozilla/ipc/BackgroundUtils.h"
#include "mozilla/ipc/PBackgroundChild.h"
#include "nsIIdleService.h"
#include "nsIObserverService.h"
#include "nsIScriptSecurityManager.h"
#include "nsXULAppAPI.h"
#include "QuotaManager.h"
#include "QuotaRequests.h"

#define PROFILE_BEFORE_CHANGE_QM_OBSERVER_ID "profile-before-change-qm"

namespace mozilla {
namespace dom {
namespace quota {

using namespace mozilla::ipc;

namespace {

// Preference that is used to enable testing features.
const char kTestingPref[] = "dom.quotaManager.testing";

const char kIdleServiceContractId[] = "@mozilla.org/widget/idleservice;1";

// The number of seconds we will wait after receiving the idle-daily
// notification before beginning maintenance.
const uint32_t kIdleObserverTimeSec = 1;

mozilla::StaticRefPtr<QuotaManagerService> gQuotaManagerService;

mozilla::Atomic<bool> gInitialized(false);
mozilla::Atomic<bool> gClosed(false);
mozilla::Atomic<bool> gTestingMode(false);

void
TestingPrefChangedCallback(const char* aPrefName,
                           void* aClosure)
{
  MOZ_ASSERT(NS_IsMainThread());
  MOZ_ASSERT(!strcmp(aPrefName, kTestingPref));
  MOZ_ASSERT(!aClosure);

  gTestingMode = Preferences::GetBool(aPrefName);
}

nsresult
CheckedPrincipalToPrincipalInfo(nsIPrincipal* aPrincipal,
                                PrincipalInfo& aPrincipalInfo)
{
  MOZ_ASSERT(aPrincipal);

  nsresult rv = PrincipalToPrincipalInfo(aPrincipal, &aPrincipalInfo);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }

  if (aPrincipalInfo.type() != PrincipalInfo::TContentPrincipalInfo &&
      aPrincipalInfo.type() != PrincipalInfo::TSystemPrincipalInfo) {
    return NS_ERROR_UNEXPECTED;
  }

  return NS_OK;
}

class AbortOperationsRunnable final
  : public Runnable
{
  ContentParentId mContentParentId;

public:
  explicit AbortOperationsRunnable(ContentParentId aContentParentId)
    : Runnable("dom::quota::AbortOperationsRunnable")
    , mContentParentId(aContentParentId)
  { }

private:
  NS_DECL_NSIRUNNABLE
};

} // namespace

class QuotaManagerService::PendingRequestInfo
{
protected:
  RefPtr<RequestBase> mRequest;

public:
  explicit PendingRequestInfo(RequestBase* aRequest)
    : mRequest(aRequest)
  { }

  virtual ~PendingRequestInfo()
  { }

  RequestBase*
  GetRequest() const
  {
    return mRequest;
  }

  virtual nsresult
  InitiateRequest(QuotaChild* aActor) = 0;
};

class QuotaManagerService::UsageRequestInfo
  : public PendingRequestInfo
{
  UsageRequestParams mParams;

public:
  UsageRequestInfo(UsageRequest* aRequest,
                   const UsageRequestParams& aParams)
    : PendingRequestInfo(aRequest)
    , mParams(aParams)
  {
    MOZ_ASSERT(aRequest);
    MOZ_ASSERT(aParams.type() != UsageRequestParams::T__None);
  }

  virtual nsresult
  InitiateRequest(QuotaChild* aActor) override;
};

class QuotaManagerService::RequestInfo
  : public PendingRequestInfo
{
  RequestParams mParams;

public:
  RequestInfo(Request* aRequest,
              const RequestParams& aParams)
    : PendingRequestInfo(aRequest)
    , mParams(aParams)
  {
    MOZ_ASSERT(aRequest);
    MOZ_ASSERT(aParams.type() != RequestParams::T__None);
  }

  virtual nsresult
  InitiateRequest(QuotaChild* aActor) override;
};

class QuotaManagerService::IdleMaintenanceInfo
  : public PendingRequestInfo
{
  const bool mStart;

public:
  explicit IdleMaintenanceInfo(bool aStart)
    : PendingRequestInfo(nullptr)
    , mStart(aStart)
  { }

  virtual nsresult
  InitiateRequest(QuotaChild* aActor) override;
};

QuotaManagerService::QuotaManagerService()
  : mBackgroundActor(nullptr)
  , mBackgroundActorFailed(false)
  , mIdleObserverRegistered(false)
{
  MOZ_ASSERT(NS_IsMainThread());
}

QuotaManagerService::~QuotaManagerService()
{
  MOZ_ASSERT(NS_IsMainThread());
  MOZ_ASSERT(!mIdleObserverRegistered);
}

// static
QuotaManagerService*
QuotaManagerService::GetOrCreate()
{
  MOZ_ASSERT(NS_IsMainThread());

  if (gClosed) {
    MOZ_ASSERT(false, "Calling GetOrCreate() after shutdown!");
    return nullptr;
  }

  if (!gQuotaManagerService) {
    RefPtr<QuotaManagerService> instance(new QuotaManagerService());

    nsresult rv = instance->Init();
    if (NS_WARN_IF(NS_FAILED(rv))) {
      return nullptr;
    }

    if (gInitialized.exchange(true)) {
      MOZ_ASSERT(false, "Initialized more than once?!");
    }

    gQuotaManagerService = instance;

    ClearOnShutdown(&gQuotaManagerService);
  }

  return gQuotaManagerService;
}

// static
QuotaManagerService*
QuotaManagerService::Get()
{
  // Does not return an owning reference.
  return gQuotaManagerService;
}

// static
already_AddRefed<QuotaManagerService>
QuotaManagerService::FactoryCreate()
{
  RefPtr<QuotaManagerService> quotaManagerService = GetOrCreate();
  return quotaManagerService.forget();
}

void
QuotaManagerService::ClearBackgroundActor()
{
  MOZ_ASSERT(NS_IsMainThread());

  mBackgroundActor = nullptr;
}

void
QuotaManagerService::NoteLiveManager(QuotaManager* aManager)
{
  MOZ_ASSERT(XRE_IsParentProcess());
  MOZ_ASSERT(NS_IsMainThread());
  MOZ_ASSERT(aManager);

  mBackgroundThread = aManager->OwningThread();
}

void
QuotaManagerService::NoteShuttingDownManager()
{
  MOZ_ASSERT(XRE_IsParentProcess());
  MOZ_ASSERT(NS_IsMainThread());

  mBackgroundThread = nullptr;
}

void
QuotaManagerService::AbortOperationsForProcess(ContentParentId aContentParentId)
{
  MOZ_ASSERT(XRE_IsParentProcess());
  MOZ_ASSERT(NS_IsMainThread());

  if (!mBackgroundThread) {
    return;
  }

  RefPtr<AbortOperationsRunnable> runnable =
    new AbortOperationsRunnable(aContentParentId);

  MOZ_ALWAYS_SUCCEEDS(
    mBackgroundThread->Dispatch(runnable, NS_DISPATCH_NORMAL));
}

nsresult
QuotaManagerService::Init()
{
  MOZ_ASSERT(NS_IsMainThread());

  if (XRE_IsParentProcess()) {
    nsCOMPtr<nsIObserverService> observerService =
      mozilla::services::GetObserverService();
    if (NS_WARN_IF(!observerService)) {
      return NS_ERROR_FAILURE;
    }

    nsresult rv =
      observerService->AddObserver(this,
                                   PROFILE_BEFORE_CHANGE_QM_OBSERVER_ID,
                                   false);
    if (NS_WARN_IF(NS_FAILED(rv))) {
      return rv;
    }
  }

  Preferences::RegisterCallbackAndCall(TestingPrefChangedCallback,
                                       kTestingPref);

  return NS_OK;
}

void
QuotaManagerService::Destroy()
{
  // Setting the closed flag prevents the service from being recreated.
  // Don't set it though if there's no real instance created.
  if (gInitialized && gClosed.exchange(true)) {
    MOZ_ASSERT(false, "Shutdown more than once?!");
  }

  Preferences::UnregisterCallback(TestingPrefChangedCallback, kTestingPref);

  delete this;
}

nsresult
QuotaManagerService::InitiateRequest(nsAutoPtr<PendingRequestInfo>& aInfo)
{
  // Nothing can be done here if we have previously failed to create a
  // background actor.
  if (mBackgroundActorFailed) {
    return NS_ERROR_FAILURE;
  }

  if (!mBackgroundActor) {
    PBackgroundChild* backgroundActor =
      BackgroundChild::GetOrCreateForCurrentThread();
    if (NS_WARN_IF(!backgroundActor)) {
      mBackgroundActorFailed = true;
      return NS_ERROR_FAILURE;
    }

    {
      QuotaChild* actor = new QuotaChild(this);

      mBackgroundActor =
        static_cast<QuotaChild*>(backgroundActor->SendPQuotaConstructor(actor));
    }
  }

  if (!mBackgroundActor) {
    mBackgroundActorFailed = true;
    return NS_ERROR_FAILURE;
  }

  // If we already have a background actor then we can start this request now.
  nsresult rv = aInfo->InitiateRequest(mBackgroundActor);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }

  return NS_OK;
}

void
QuotaManagerService::PerformIdleMaintenance()
{
  using namespace mozilla::hal;

  MOZ_ASSERT(XRE_IsParentProcess());
  MOZ_ASSERT(NS_IsMainThread());

  // If we're running on battery power then skip all idle maintenance since we
  // would otherwise be doing lots of disk I/O.
  BatteryInformation batteryInfo;

#ifdef MOZ_WIDGET_ANDROID
  // Android XPCShell doesn't load the AndroidBridge that is needed to make
  // GetCurrentBatteryInformation work...
  if (!QuotaManager::IsRunningXPCShellTests())
#endif
  {
    // In order to give the correct battery level, hal must have registered
    // battery observers.
    RegisterBatteryObserver(this);
    GetCurrentBatteryInformation(&batteryInfo);
    UnregisterBatteryObserver(this);
  }

  // If we're running XPCShell because we always want to be able to test this
  // code so pretend that we're always charging.
  if (QuotaManager::IsRunningXPCShellTests()) {
    batteryInfo.level() = 100;
    batteryInfo.charging() = true;
  }

  if (NS_WARN_IF(!batteryInfo.charging())) {
    return;
  }

  if (QuotaManager::IsRunningXPCShellTests()) {
    // We don't want user activity to impact this code if we're running tests.
    Unused << Observe(nullptr, OBSERVER_TOPIC_IDLE, nullptr);
  } else if (!mIdleObserverRegistered) {
    nsCOMPtr<nsIIdleService> idleService =
      do_GetService(kIdleServiceContractId);
    MOZ_ASSERT(idleService);

    MOZ_ALWAYS_SUCCEEDS(
      idleService->AddIdleObserver(this, kIdleObserverTimeSec));

    mIdleObserverRegistered = true;
  }
}

void
QuotaManagerService::RemoveIdleObserver()
{
  MOZ_ASSERT(XRE_IsParentProcess());
  MOZ_ASSERT(NS_IsMainThread());

  if (mIdleObserverRegistered) {
    nsCOMPtr<nsIIdleService> idleService =
      do_GetService(kIdleServiceContractId);
    MOZ_ASSERT(idleService);

    // Ignore the return value of RemoveIdleObserver, it may fail if the
    // observer has already been unregistered during shutdown.
    Unused <<
      idleService->RemoveIdleObserver(this, kIdleObserverTimeSec);

    mIdleObserverRegistered = false;
  }
}

NS_IMPL_ADDREF(QuotaManagerService)
NS_IMPL_RELEASE_WITH_DESTROY(QuotaManagerService, Destroy())
NS_IMPL_QUERY_INTERFACE(QuotaManagerService,
                        nsIQuotaManagerService,
                        nsIObserver)

NS_IMETHODIMP
QuotaManagerService::Init(nsIQuotaRequest** _retval)
{
  MOZ_ASSERT(NS_IsMainThread());
  MOZ_ASSERT(nsContentUtils::IsCallerChrome());

  if (NS_WARN_IF(!gTestingMode)) {
    return NS_ERROR_UNEXPECTED;
  }

  RefPtr<Request> request = new Request();

  InitParams params;

  nsAutoPtr<PendingRequestInfo> info(new RequestInfo(request, params));

  nsresult rv = InitiateRequest(info);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }

  request.forget(_retval);
  return NS_OK;
}

NS_IMETHODIMP
QuotaManagerService::InitStoragesForPrincipal(
                                             nsIPrincipal* aPrincipal,
                                             const nsACString& aPersistenceType,
                                             nsIQuotaRequest** _retval)
{
  MOZ_ASSERT(NS_IsMainThread());
  MOZ_ASSERT(nsContentUtils::IsCallerChrome());

  if (NS_WARN_IF(!gTestingMode)) {
    return NS_ERROR_UNEXPECTED;
  }

  RefPtr<Request> request = new Request();

  InitOriginParams params;

  nsresult rv = CheckedPrincipalToPrincipalInfo(aPrincipal,
                                                params.principalInfo());
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }

  Nullable<PersistenceType> persistenceType;
  rv = NullablePersistenceTypeFromText(aPersistenceType, &persistenceType);
  if (NS_WARN_IF(NS_FAILED(rv)) || persistenceType.IsNull()) {
    return NS_ERROR_INVALID_ARG;
  }

  params.persistenceType() = persistenceType.Value();

  nsAutoPtr<PendingRequestInfo> info(new RequestInfo(request, params));

  rv = InitiateRequest(info);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }

  request.forget(_retval);
  return NS_OK;
}

NS_IMETHODIMP
QuotaManagerService::GetUsage(nsIQuotaUsageCallback* aCallback,
                              bool aGetAll,
                              nsIQuotaUsageRequest** _retval)
{
  MOZ_ASSERT(NS_IsMainThread());
  MOZ_ASSERT(aCallback);

  RefPtr<UsageRequest> request = new UsageRequest(aCallback);

  AllUsageParams params;

  params.getAll() = aGetAll;

  nsAutoPtr<PendingRequestInfo> info(new UsageRequestInfo(request, params));

  nsresult rv = InitiateRequest(info);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }

  request.forget(_retval);
  return NS_OK;
}

NS_IMETHODIMP
QuotaManagerService::GetUsageForPrincipal(nsIPrincipal* aPrincipal,
                                          nsIQuotaUsageCallback* aCallback,
                                          bool aGetGroupUsage,
                                          nsIQuotaUsageRequest** _retval)
{
  MOZ_ASSERT(NS_IsMainThread());
  MOZ_ASSERT(aPrincipal);
  MOZ_ASSERT(aCallback);

  RefPtr<UsageRequest> request = new UsageRequest(aPrincipal, aCallback);

  OriginUsageParams params;

  nsresult rv = CheckedPrincipalToPrincipalInfo(aPrincipal,
                                                params.principalInfo());
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }

  params.getGroupUsage() = aGetGroupUsage;

  nsAutoPtr<PendingRequestInfo> info(new UsageRequestInfo(request, params));

  rv = InitiateRequest(info);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }

  request.forget(_retval);
  return NS_OK;
}

NS_IMETHODIMP
QuotaManagerService::Clear(nsIQuotaRequest** _retval)
{
  MOZ_ASSERT(NS_IsMainThread());

  if (NS_WARN_IF(!gTestingMode)) {
    return NS_ERROR_UNEXPECTED;
  }

  RefPtr<Request> request = new Request();

  ClearAllParams params;

  nsAutoPtr<PendingRequestInfo> info(new RequestInfo(request, params));

  nsresult rv = InitiateRequest(info);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }

  request.forget(_retval);
  return NS_OK;
}

NS_IMETHODIMP
QuotaManagerService::ClearStoragesForPrincipal(nsIPrincipal* aPrincipal,
                                               const nsACString& aPersistenceType,
                                               bool aClearAll,
                                               nsIQuotaRequest** _retval)
{
  MOZ_ASSERT(NS_IsMainThread());
  MOZ_ASSERT(aPrincipal);

  nsCString suffix;
  aPrincipal->OriginAttributesRef().CreateSuffix(suffix);

  if (NS_WARN_IF(aClearAll && !suffix.IsEmpty())) {
    // The originAttributes should be default originAttributes when the
    // aClearAll flag is set.
    return NS_ERROR_INVALID_ARG;
  }

  RefPtr<Request> request = new Request(aPrincipal);

  ClearOriginParams params;

  nsresult rv = CheckedPrincipalToPrincipalInfo(aPrincipal,
                                                params.principalInfo());
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }

  Nullable<PersistenceType> persistenceType;
  rv = NullablePersistenceTypeFromText(aPersistenceType, &persistenceType);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return NS_ERROR_INVALID_ARG;
  }

  if (persistenceType.IsNull()) {
    params.persistenceTypeIsExplicit() = false;
  } else {
    params.persistenceType() = persistenceType.Value();
    params.persistenceTypeIsExplicit() = true;
  }

  params.clearAll() = aClearAll;

  nsAutoPtr<PendingRequestInfo> info(new RequestInfo(request, params));

  rv = InitiateRequest(info);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }

  request.forget(_retval);
  return NS_OK;
}

NS_IMETHODIMP
QuotaManagerService::Reset(nsIQuotaRequest** _retval)
{
  MOZ_ASSERT(NS_IsMainThread());

  if (NS_WARN_IF(!gTestingMode)) {
    return NS_ERROR_UNEXPECTED;
  }

  RefPtr<Request> request = new Request();

  ResetAllParams params;

  nsAutoPtr<PendingRequestInfo> info(new RequestInfo(request, params));

  nsresult rv = InitiateRequest(info);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }

  request.forget(_retval);
  return NS_OK;
}

NS_IMETHODIMP
QuotaManagerService::Persisted(nsIPrincipal* aPrincipal,
                               nsIQuotaRequest** _retval)
{
  MOZ_ASSERT(NS_IsMainThread());
  MOZ_ASSERT(aPrincipal);
  MOZ_ASSERT(_retval);

  RefPtr<Request> request = new Request(aPrincipal);

  PersistedParams params;

  nsresult rv = CheckedPrincipalToPrincipalInfo(aPrincipal,
                                                params.principalInfo());
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }

  nsAutoPtr<PendingRequestInfo> info(new RequestInfo(request, params));

  rv = InitiateRequest(info);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }

  request.forget(_retval);
  return NS_OK;
}

NS_IMETHODIMP
QuotaManagerService::Persist(nsIPrincipal* aPrincipal,
                             nsIQuotaRequest** _retval)
{
  MOZ_ASSERT(NS_IsMainThread());
  MOZ_ASSERT(aPrincipal);
  MOZ_ASSERT(_retval);

  RefPtr<Request> request = new Request(aPrincipal);

  PersistParams params;

  nsresult rv = CheckedPrincipalToPrincipalInfo(aPrincipal,
                                                params.principalInfo());
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }

  nsAutoPtr<PendingRequestInfo> info(new RequestInfo(request, params));

  rv = InitiateRequest(info);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }

  request.forget(_retval);
  return NS_OK;
}

NS_IMETHODIMP
QuotaManagerService::Observe(nsISupports* aSubject,
                             const char* aTopic,
                             const char16_t* aData)
{
  MOZ_ASSERT(XRE_IsParentProcess());
  MOZ_ASSERT(NS_IsMainThread());

  if (!strcmp(aTopic, PROFILE_BEFORE_CHANGE_QM_OBSERVER_ID)) {
    RemoveIdleObserver();
    return NS_OK;
  }

  if (!strcmp(aTopic, "clear-origin-attributes-data")) {
    RefPtr<Request> request = new Request();

    ClearDataParams params;
    params.pattern() = nsDependentString(aData);

    nsAutoPtr<PendingRequestInfo> info(new RequestInfo(request, params));

    nsresult rv = InitiateRequest(info);
    if (NS_WARN_IF(NS_FAILED(rv))) {
      return rv;
    }

    return NS_OK;
  }

  if (!strcmp(aTopic, OBSERVER_TOPIC_IDLE_DAILY)) {
    PerformIdleMaintenance();
    return NS_OK;
  }

  if (!strcmp(aTopic, OBSERVER_TOPIC_IDLE)) {
    nsAutoPtr<PendingRequestInfo> info(
      new IdleMaintenanceInfo(/* aStart */ true));

    nsresult rv = InitiateRequest(info);
    if (NS_WARN_IF(NS_FAILED(rv))) {
      return rv;
    }

    return NS_OK;
  }

  if (!strcmp(aTopic, OBSERVER_TOPIC_ACTIVE)) {
    RemoveIdleObserver();

    nsAutoPtr<PendingRequestInfo> info(
      new IdleMaintenanceInfo(/* aStart */ false));

    nsresult rv = InitiateRequest(info);
    if (NS_WARN_IF(NS_FAILED(rv))) {
      return rv;
    }

    return NS_OK;
  }

  MOZ_ASSERT_UNREACHABLE("Should never get here!");
  return NS_OK;
}

void
QuotaManagerService::Notify(const hal::BatteryInformation& aBatteryInfo)
{
  // This notification is received when battery data changes. We don't need to
  // deal with this notification.
}

NS_IMETHODIMP
AbortOperationsRunnable::Run()
{
  AssertIsOnBackgroundThread();

  if (QuotaManager::IsShuttingDown()) {
    return NS_OK;
  }

  QuotaManager* quotaManager = QuotaManager::Get();
  if (!quotaManager) {
    return NS_OK;
  }

  quotaManager->AbortOperationsForProcess(mContentParentId);

  return NS_OK;
}

nsresult
QuotaManagerService::
UsageRequestInfo::InitiateRequest(QuotaChild* aActor)
{
  MOZ_ASSERT(aActor);

  auto request = static_cast<UsageRequest*>(mRequest.get());

  auto actor = new QuotaUsageRequestChild(request);

  if (!aActor->SendPQuotaUsageRequestConstructor(actor, mParams)) {
    request->SetError(NS_ERROR_FAILURE);
    return NS_ERROR_FAILURE;
  }

  request->SetBackgroundActor(actor);

  return NS_OK;
}

nsresult
QuotaManagerService::
RequestInfo::InitiateRequest(QuotaChild* aActor)
{
  MOZ_ASSERT(aActor);

  auto request = static_cast<Request*>(mRequest.get());

  auto actor = new QuotaRequestChild(request);

  if (!aActor->SendPQuotaRequestConstructor(actor, mParams)) {
    request->SetError(NS_ERROR_FAILURE);
    return NS_ERROR_FAILURE;
  }

  return NS_OK;
}

nsresult
QuotaManagerService::
IdleMaintenanceInfo::InitiateRequest(QuotaChild* aActor)
{
  MOZ_ASSERT(aActor);

  bool result;

  if (mStart) {
    result = aActor->SendStartIdleMaintenance();
  } else {
    result = aActor->SendStopIdleMaintenance();
  }

  if (!result) {
    return NS_ERROR_FAILURE;
  }

  return NS_OK;
}

} // namespace quota
} // namespace dom
} // namespace mozilla
