diff options
Diffstat (limited to 'dom/simpledb/SDBConnection.cpp')
-rw-r--r-- | dom/simpledb/SDBConnection.cpp | 433 |
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 |