/* -*- 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" #include "ActorsChild.h" #include "jsfriendapi.h" // JS_GetObjectAsArrayBufferView #include "js/ArrayBuffer.h" // JS::{GetObjectAsArrayBuffer,IsArrayBufferObject} #include "js/RootingAPI.h" // JS::{Handle,Rooted} #include "js/Value.h" // JS::Value #include "mozilla/ipc/BackgroundChild.h" #include "mozilla/ipc/BackgroundParent.h" #include "mozilla/ipc/BackgroundUtils.h" #include "mozilla/ipc/PBackgroundChild.h" #include "nsISDBCallbacks.h" #include "SDBRequest.h" #include "SimpleDBCommon.h" namespace mozilla { namespace dom { using namespace mozilla::ipc; namespace { nsresult GetWriteData(JSContext* aCx, JS::Handle aValue, nsCString& aData) { if (aValue.isObject()) { JS::Rooted obj(aCx, &aValue.toObject()); bool isView = false; if (JS::IsArrayBufferObject(obj) || (isView = JS_IsArrayBufferViewObject(obj))) { uint8_t* data; uint32_t length; bool unused; if (isView) { JS_GetObjectAsArrayBufferView(obj, &length, &unused, &data); } else { JS::GetObjectAsArrayBuffer(obj, &length, &data); } if (NS_WARN_IF(!aData.Assign(reinterpret_cast(data), length, fallible_t()))) { return NS_ERROR_OUT_OF_MEMORY; } return NS_OK; } } return NS_ERROR_NOT_IMPLEMENTED; } } // namespace SDBConnection::SDBConnection() : mBackgroundActor(nullptr), 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(nsISupports* aOuter, REFNSIID aIID, void** aResult) { MOZ_ASSERT(aResult); if (NS_WARN_IF(!Preferences::GetBool(kPrefSimpleDBEnabled, false))) { return NS_ERROR_NOT_AVAILABLE; } if (aOuter) { return NS_ERROR_NO_AGGREGATION; } RefPtr 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( backgroundActor->SendPBackgroundSDBConnectionConstructor( actor, *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) { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(aPrincipal); nsAutoPtr principalInfo(new PrincipalInfo()); nsresult rv = PrincipalToPrincipalInfo(aPrincipal, principalInfo); 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(!QuotaManager::IsPrincipalInfoValid(*principalInfo))) { return NS_ERROR_INVALID_ARG; } mPrincipalInfo = std::move(principalInfo); 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 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 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 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::HandleValue 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 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 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 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 dom } // namespace mozilla