summaryrefslogtreecommitdiffstats
path: root/dom/simpledb/SDBConnection.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/simpledb/SDBConnection.cpp')
-rw-r--r--dom/simpledb/SDBConnection.cpp433
1 files changed, 433 insertions, 0 deletions
diff --git a/dom/simpledb/SDBConnection.cpp b/dom/simpledb/SDBConnection.cpp
new file mode 100644
index 0000000000..25d3273c55
--- /dev/null
+++ b/dom/simpledb/SDBConnection.cpp
@@ -0,0 +1,433 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=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 "SDBConnection.h"
+
+// Local includes
+#include "ActorsChild.h"
+#include "SDBRequest.h"
+#include "SimpleDBCommon.h"
+
+// Global includes
+#include <stdint.h>
+#include <utility>
+#include "MainThreadUtils.h"
+#include "js/ArrayBuffer.h"
+#include "js/RootingAPI.h"
+#include "js/TypeDecls.h"
+#include "js/experimental/TypedData.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/MacroForEach.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/Variant.h"
+#include "mozilla/dom/PBackgroundSDBConnection.h"
+#include "mozilla/dom/quota/QuotaManager.h"
+#include "mozilla/fallible.h"
+#include "mozilla/ipc/BackgroundChild.h"
+#include "mozilla/ipc/BackgroundUtils.h"
+#include "mozilla/ipc/PBackgroundChild.h"
+#include "mozilla/ipc/PBackgroundSharedTypes.h"
+#include "nsDebug.h"
+#include "nsError.h"
+#include "nsISDBCallbacks.h"
+#include "nsISupportsUtils.h"
+#include "nsStringFwd.h"
+#include "nscore.h"
+
+namespace mozilla::dom {
+
+using namespace mozilla::ipc;
+
+namespace {
+
+nsresult GetWriteData(JSContext* aCx, JS::Handle<JS::Value> aValue,
+ nsCString& aData) {
+ if (aValue.isObject()) {
+ JS::Rooted<JSObject*> obj(aCx, &aValue.toObject());
+
+ bool isView = false;
+ if (JS::IsArrayBufferObject(obj) ||
+ (isView = JS_IsArrayBufferViewObject(obj))) {
+ uint8_t* data;
+ size_t length;
+ bool unused;
+ if (isView) {
+ JS_GetObjectAsArrayBufferView(obj, &length, &unused, &data);
+ } else {
+ JS::GetObjectAsArrayBuffer(obj, &length, &data);
+ }
+
+ // Throw for large buffers to prevent truncation.
+ if (length > INT32_MAX) {
+ return NS_ERROR_ILLEGAL_VALUE;
+ }
+
+ if (NS_WARN_IF(!aData.Assign(reinterpret_cast<char*>(data), length,
+ fallible_t()))) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ return NS_OK;
+ }
+ }
+
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+} // namespace
+
+SDBConnection::SDBConnection()
+ : mBackgroundActor(nullptr),
+ mPersistenceType(quota::PERSISTENCE_TYPE_INVALID),
+ mRunningRequest(false),
+ mOpen(false),
+ mAllowedToClose(false) {
+ AssertIsOnOwningThread();
+}
+
+SDBConnection::~SDBConnection() {
+ AssertIsOnOwningThread();
+
+ if (mBackgroundActor) {
+ mBackgroundActor->SendDeleteMeInternal();
+ MOZ_ASSERT(!mBackgroundActor, "SendDeleteMeInternal should have cleared!");
+ }
+}
+
+// static
+nsresult SDBConnection::Create(REFNSIID aIID, void** aResult) {
+ MOZ_ASSERT(aResult);
+
+ if (NS_WARN_IF(!Preferences::GetBool(kPrefSimpleDBEnabled, false))) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ RefPtr<SDBConnection> connection = new SDBConnection();
+
+ nsresult rv = connection->QueryInterface(aIID, aResult);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+void SDBConnection::ClearBackgroundActor() {
+ AssertIsOnOwningThread();
+
+ mBackgroundActor = nullptr;
+}
+
+void SDBConnection::OnNewRequest() {
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(!mRunningRequest);
+
+ mRunningRequest = true;
+}
+
+void SDBConnection::OnRequestFinished() {
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(mRunningRequest);
+
+ mRunningRequest = false;
+}
+
+void SDBConnection::OnOpen() {
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(!mOpen);
+
+ mOpen = true;
+}
+
+void SDBConnection::OnClose(bool aAbnormal) {
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(mOpen);
+
+ mOpen = false;
+
+ if (aAbnormal) {
+ MOZ_ASSERT(mAllowedToClose);
+
+ if (mCloseCallback) {
+ mCloseCallback->OnClose(this);
+ }
+ }
+}
+
+void SDBConnection::AllowToClose() {
+ AssertIsOnOwningThread();
+
+ mAllowedToClose = true;
+}
+
+nsresult SDBConnection::CheckState() {
+ AssertIsOnOwningThread();
+
+ if (mAllowedToClose) {
+ return NS_ERROR_ABORT;
+ }
+
+ if (mRunningRequest) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ return NS_OK;
+}
+
+nsresult SDBConnection::EnsureBackgroundActor() {
+ AssertIsOnOwningThread();
+
+ if (mBackgroundActor) {
+ return NS_OK;
+ }
+
+ PBackgroundChild* backgroundActor =
+ BackgroundChild::GetOrCreateForCurrentThread();
+ if (NS_WARN_IF(!backgroundActor)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ SDBConnectionChild* actor = new SDBConnectionChild(this);
+
+ mBackgroundActor = static_cast<SDBConnectionChild*>(
+ backgroundActor->SendPBackgroundSDBConnectionConstructor(
+ actor, mPersistenceType, *mPrincipalInfo));
+ if (NS_WARN_IF(!mBackgroundActor)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+nsresult SDBConnection::InitiateRequest(SDBRequest* aRequest,
+ const SDBRequestParams& aParams) {
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(aRequest);
+ MOZ_ASSERT(mBackgroundActor);
+
+ auto actor = new SDBRequestChild(aRequest);
+
+ if (!mBackgroundActor->SendPBackgroundSDBRequestConstructor(actor, aParams)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Balanced in SDBRequestChild::Recv__delete__().
+ OnNewRequest();
+
+ return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS(SDBConnection, nsISDBConnection)
+
+NS_IMETHODIMP
+SDBConnection::Init(nsIPrincipal* aPrincipal,
+ const nsACString& aPersistenceType) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aPrincipal);
+
+ UniquePtr<PrincipalInfo> principalInfo(new PrincipalInfo());
+ nsresult rv = PrincipalToPrincipalInfo(aPrincipal, principalInfo.get());
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (principalInfo->type() != PrincipalInfo::TContentPrincipalInfo &&
+ principalInfo->type() != PrincipalInfo::TSystemPrincipalInfo) {
+ NS_WARNING("Simpledb not allowed for this principal!");
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ if (NS_WARN_IF(!quota::QuotaManager::IsPrincipalInfoValid(*principalInfo))) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ PersistenceType persistenceType;
+ if (aPersistenceType.IsVoid()) {
+ persistenceType = quota::PERSISTENCE_TYPE_DEFAULT;
+ } else {
+ const auto maybePersistenceType =
+ quota::PersistenceTypeFromString(aPersistenceType, fallible);
+ if (NS_WARN_IF(maybePersistenceType.isNothing())) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ persistenceType = maybePersistenceType.value();
+ }
+
+ mPrincipalInfo = std::move(principalInfo);
+ mPersistenceType = persistenceType;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+SDBConnection::Open(const nsAString& aName, nsISDBRequest** _retval) {
+ AssertIsOnOwningThread();
+
+ nsresult rv = CheckState();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (mOpen) {
+ return NS_ERROR_ALREADY_INITIALIZED;
+ }
+
+ SDBRequestOpenParams params;
+ params.name() = aName;
+
+ RefPtr<SDBRequest> request = new SDBRequest(this);
+
+ rv = EnsureBackgroundActor();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = InitiateRequest(request, params);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ request.forget(_retval);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+SDBConnection::Seek(uint64_t aOffset, nsISDBRequest** _retval) {
+ AssertIsOnOwningThread();
+
+ nsresult rv = CheckState();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (!mOpen) {
+ return NS_BASE_STREAM_CLOSED;
+ }
+
+ SDBRequestSeekParams params;
+ params.offset() = aOffset;
+
+ RefPtr<SDBRequest> request = new SDBRequest(this);
+
+ rv = InitiateRequest(request, params);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ request.forget(_retval);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+SDBConnection::Read(uint64_t aSize, nsISDBRequest** _retval) {
+ AssertIsOnOwningThread();
+
+ nsresult rv = CheckState();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (!mOpen) {
+ return NS_BASE_STREAM_CLOSED;
+ }
+
+ SDBRequestReadParams params;
+ params.size() = aSize;
+
+ RefPtr<SDBRequest> request = new SDBRequest(this);
+
+ rv = InitiateRequest(request, params);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ request.forget(_retval);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+SDBConnection::Write(JS::Handle<JS::Value> aValue, JSContext* aCx,
+ nsISDBRequest** _retval) {
+ AssertIsOnOwningThread();
+
+ nsresult rv = CheckState();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (!mOpen) {
+ return NS_BASE_STREAM_CLOSED;
+ }
+
+ JS::Rooted<JS::Value> value(aCx, aValue);
+
+ nsCString data;
+ rv = GetWriteData(aCx, value, data);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ SDBRequestWriteParams params;
+ params.data() = data;
+
+ RefPtr<SDBRequest> request = new SDBRequest(this);
+
+ rv = InitiateRequest(request, params);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ request.forget(_retval);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+SDBConnection::Close(nsISDBRequest** _retval) {
+ AssertIsOnOwningThread();
+
+ nsresult rv = CheckState();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (!mOpen) {
+ return NS_BASE_STREAM_CLOSED;
+ }
+
+ SDBRequestCloseParams params;
+
+ RefPtr<SDBRequest> request = new SDBRequest(this);
+
+ rv = InitiateRequest(request, params);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ request.forget(_retval);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+SDBConnection::GetCloseCallback(nsISDBCloseCallback** aCloseCallback) {
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(aCloseCallback);
+
+ NS_IF_ADDREF(*aCloseCallback = mCloseCallback);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+SDBConnection::SetCloseCallback(nsISDBCloseCallback* aCloseCallback) {
+ AssertIsOnOwningThread();
+
+ mCloseCallback = aCloseCallback;
+ return NS_OK;
+}
+
+} // namespace mozilla::dom