mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-23 12:51:06 +00:00
a8506400af
Differential Revision: https://phabricator.services.mozilla.com/D223992
442 lines
9.7 KiB
C++
442 lines
9.7 KiB
C++
/* -*- 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 "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/TypedArray.h"
|
|
#include "mozilla/dom/quota/PrincipalUtils.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 {
|
|
|
|
template <typename BufferT>
|
|
nsresult AppendDataToString(const BufferT& aBuffer, nsCString& aData) {
|
|
return aBuffer.ProcessData(
|
|
[&aData](const mozilla::Span<const uint8_t>& aSrcData,
|
|
JS::AutoCheckCannotGC&&) {
|
|
if (aSrcData.LengthBytes() > INT32_MAX) {
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
|
}
|
|
if (NS_WARN_IF(!aData.Append(aSrcData, fallible))) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
return NS_OK;
|
|
});
|
|
}
|
|
|
|
nsresult GetWriteData(JSContext* aCx, JS::Handle<JS::Value> aValue,
|
|
nsCString& aData) {
|
|
MOZ_ASSERT(aData.IsEmpty());
|
|
|
|
if (!aValue.isObject()) {
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
mozilla::dom::ArrayBufferView view;
|
|
if (view.Init(&aValue.toObject())) {
|
|
return AppendDataToString(view, aData);
|
|
}
|
|
|
|
mozilla::dom::ArrayBuffer arrayBuffer;
|
|
if (arrayBuffer.Init(&aValue.toObject())) {
|
|
return AppendDataToString(arrayBuffer, aData);
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool SDBConnection::IsAllowedToClose() const {
|
|
AssertIsOnOwningThread();
|
|
|
|
return mAllowedToClose;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
RefPtr<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::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
|