summaryrefslogtreecommitdiffstats
path: root/dom/quota/StorageManager.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/quota/StorageManager.cpp')
-rw-r--r--dom/quota/StorageManager.cpp378
1 files changed, 378 insertions, 0 deletions
diff --git a/dom/quota/StorageManager.cpp b/dom/quota/StorageManager.cpp
new file mode 100644
index 000000000..c8455f0fe
--- /dev/null
+++ b/dom/quota/StorageManager.cpp
@@ -0,0 +1,378 @@
+/* -*- 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 "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 "nsPIDOMWindow.h"
+
+using namespace mozilla::dom::workers;
+
+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(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