Mypal/dom/quota/StorageManager.cpp

381 lines
9.0 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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 "StorageManager.h"
#include "mozilla/dom/PromiseWorkerProxy.h"
#include "mozilla/dom/quota/QuotaManagerService.h"
#include "mozilla/dom/StorageManagerBinding.h"
#include "mozilla/dom/WorkerPrivate.h"
#include "mozilla/ErrorResult.h"
#include "nsIQuotaCallbacks.h"
#include "nsIQuotaRequests.h"
#include "nsIQuotaResults.h"
#include "nsPIDOMWindow.h"
#include "QuotaManagerService.h"
using namespace mozilla::dom::workers;
using namespace mozilla::dom::quota;
namespace mozilla {
namespace dom {
namespace {
// This class is used to get quota usage callback.
class EstimateResolver final
: public nsIQuotaUsageCallback
{
class FinishWorkerRunnable;
// If this resolver was created for a window then mPromise must be non-null.
// Otherwise mProxy must be non-null.
RefPtr<Promise> mPromise;
RefPtr<PromiseWorkerProxy> mProxy;
nsresult mResultCode;
StorageEstimate mStorageEstimate;
public:
explicit EstimateResolver(Promise* aPromise)
: mPromise(aPromise)
, mResultCode(NS_OK)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aPromise);
}
explicit EstimateResolver(PromiseWorkerProxy* aProxy)
: mProxy(aProxy)
, mResultCode(NS_OK)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aProxy);
}
void
ResolveOrReject(Promise* aPromise);
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIQUOTAUSAGECALLBACK
private:
~EstimateResolver()
{ }
};
// This class is used to return promise on worker thread.
class EstimateResolver::FinishWorkerRunnable final
: public WorkerRunnable
{
RefPtr<EstimateResolver> mResolver;
public:
explicit FinishWorkerRunnable(EstimateResolver* aResolver)
: WorkerRunnable(aResolver->mProxy->GetWorkerPrivate())
, mResolver(aResolver)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aResolver);
}
virtual bool
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override;
};
class EstimateWorkerMainThreadRunnable
: public WorkerMainThreadRunnable
{
RefPtr<PromiseWorkerProxy> mProxy;
public:
EstimateWorkerMainThreadRunnable(WorkerPrivate* aWorkerPrivate,
PromiseWorkerProxy* aProxy)
: WorkerMainThreadRunnable(aWorkerPrivate,
NS_LITERAL_CSTRING("StorageManager :: Estimate"))
, mProxy(aProxy)
{
MOZ_ASSERT(aWorkerPrivate);
aWorkerPrivate->AssertIsOnWorkerThread();
MOZ_ASSERT(aProxy);
}
virtual bool
MainThreadRun() override;
};
nsresult
GetUsageForPrincipal(nsIPrincipal* aPrincipal,
nsIQuotaUsageCallback* aCallback,
nsIQuotaUsageRequest** aRequest)
{
MOZ_ASSERT(aPrincipal);
MOZ_ASSERT(aCallback);
MOZ_ASSERT(aRequest);
nsCOMPtr<nsIQuotaManagerService> qms = QuotaManagerService::GetOrCreate();
if (NS_WARN_IF(!qms)) {
return NS_ERROR_FAILURE;
}
nsresult rv = qms->GetUsageForPrincipal(aPrincipal, aCallback, true, aRequest);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
};
nsresult
GetStorageEstimate(nsIQuotaUsageRequest* aRequest,
StorageEstimate& aStorageEstimate)
{
MOZ_ASSERT(aRequest);
nsCOMPtr<nsIVariant> result;
nsresult rv = aRequest->GetResult(getter_AddRefs(result));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
nsID* iid;
nsCOMPtr<nsISupports> supports;
rv = result->GetAsInterface(&iid, getter_AddRefs(supports));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
free(iid);
nsCOMPtr<nsIQuotaOriginUsageResult> originUsageResult =
do_QueryInterface(supports);
MOZ_ASSERT(originUsageResult);
MOZ_ALWAYS_SUCCEEDS(
originUsageResult->GetUsage(&aStorageEstimate.mUsage.Construct()));
MOZ_ALWAYS_SUCCEEDS(
originUsageResult->GetLimit(&aStorageEstimate.mQuota.Construct()));
return NS_OK;
}
} // namespace
/*******************************************************************************
* Local class implementations
******************************************************************************/
void
EstimateResolver::ResolveOrReject(Promise* aPromise)
{
MOZ_ASSERT(aPromise);
if (NS_SUCCEEDED(mResultCode)) {
aPromise->MaybeResolve(mStorageEstimate);
} else {
aPromise->MaybeReject(mResultCode);
}
}
NS_IMPL_ISUPPORTS(EstimateResolver, nsIQuotaUsageCallback)
NS_IMETHODIMP
EstimateResolver::OnUsageResult(nsIQuotaUsageRequest *aRequest)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aRequest);
nsresult rv = aRequest->GetResultCode(&mResultCode);
if (NS_WARN_IF(NS_FAILED(rv))) {
mResultCode = rv;
} else if (NS_SUCCEEDED(mResultCode)) {
rv = GetStorageEstimate(aRequest, mStorageEstimate);
if (NS_WARN_IF(NS_FAILED(rv))) {
mResultCode = rv;
}
}
// In a main thread request.
if (!mProxy) {
MOZ_ASSERT(mPromise);
ResolveOrReject(mPromise);
return NS_OK;
}
// In a worker thread request.
MutexAutoLock lock(mProxy->Lock());
if (NS_WARN_IF(mProxy->CleanedUp())) {
return NS_ERROR_FAILURE;
}
RefPtr<FinishWorkerRunnable> runnable = new FinishWorkerRunnable(this);
if (NS_WARN_IF(!runnable->Dispatch())) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
bool
EstimateResolver::
FinishWorkerRunnable::WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
{
MOZ_ASSERT(aWorkerPrivate);
aWorkerPrivate->AssertIsOnWorkerThread();
RefPtr<PromiseWorkerProxy> proxy = mResolver->mProxy;
MOZ_ASSERT(proxy);
RefPtr<Promise> promise = proxy->WorkerPromise();
MOZ_ASSERT(promise);
mResolver->ResolveOrReject(promise);
proxy->CleanUp();
return true;
}
bool
EstimateWorkerMainThreadRunnable::MainThreadRun()
{
MOZ_ASSERT(NS_IsMainThread());
nsCOMPtr<nsIPrincipal> principal;
{
MutexAutoLock lock(mProxy->Lock());
if (mProxy->CleanedUp()) {
return true;
}
principal = mProxy->GetWorkerPrivate()->GetPrincipal();
}
MOZ_ASSERT(principal);
RefPtr<EstimateResolver> resolver = new EstimateResolver(mProxy);
RefPtr<nsIQuotaUsageRequest> request;
nsresult rv =
GetUsageForPrincipal(principal, resolver, getter_AddRefs(request));
if (NS_WARN_IF(NS_FAILED(rv))) {
return false;
}
return true;
}
/*******************************************************************************
* StorageManager
******************************************************************************/
StorageManager::StorageManager(nsIGlobalObject* aGlobal)
: mOwner(aGlobal)
{
MOZ_ASSERT(aGlobal);
}
StorageManager::~StorageManager()
{
}
already_AddRefed<Promise>
StorageManager::Estimate(ErrorResult& aRv)
{
MOZ_ASSERT(mOwner);
RefPtr<Promise> promise = Promise::Create(mOwner, aRv);
if (NS_WARN_IF(!promise)) {
return nullptr;
}
if (NS_IsMainThread()) {
nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(mOwner);
if (NS_WARN_IF(!window)) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
if (NS_WARN_IF(!doc)) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
nsCOMPtr<nsIPrincipal> principal = doc->NodePrincipal();
MOZ_ASSERT(principal);
RefPtr<EstimateResolver> resolver = new EstimateResolver(promise);
RefPtr<nsIQuotaUsageRequest> request;
nsresult rv =
GetUsageForPrincipal(principal, resolver, getter_AddRefs(request));
if (NS_WARN_IF(NS_FAILED(rv))) {
aRv.Throw(rv);
return nullptr;
}
return promise.forget();
}
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
MOZ_ASSERT(workerPrivate);
RefPtr<PromiseWorkerProxy> promiseProxy =
PromiseWorkerProxy::Create(workerPrivate, promise);
if (NS_WARN_IF(!promiseProxy)) {
return nullptr;
}
RefPtr<EstimateWorkerMainThreadRunnable> runnnable =
new EstimateWorkerMainThreadRunnable(promiseProxy->GetWorkerPrivate(),
promiseProxy);
runnnable->Dispatch(Terminating, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
return promise.forget();
}
// static
bool
StorageManager::PrefEnabled(JSContext* aCx, JSObject* aObj)
{
if (NS_IsMainThread()) {
return Preferences::GetBool("dom.storageManager.enabled");
}
WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx);
MOZ_ASSERT(workerPrivate);
return workerPrivate->StorageManagerEnabled();
}
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(StorageManager, mOwner)
NS_IMPL_CYCLE_COLLECTING_ADDREF(StorageManager)
NS_IMPL_CYCLE_COLLECTING_RELEASE(StorageManager)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(StorageManager)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
JSObject*
StorageManager::WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto)
{
return StorageManagerBinding::Wrap(aCx, this, aGivenProto);
}
} // namespace dom
} // namespace mozilla