зеркало из https://github.com/mozilla/gecko-dev.git
2011 строки
43 KiB
C++
2011 строки
43 KiB
C++
/* -*- 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 "ActorsParent.h"
|
|
|
|
#include "mozilla/Unused.h"
|
|
#include "mozilla/dom/PBackgroundSDBConnectionParent.h"
|
|
#include "mozilla/dom/PBackgroundSDBRequestParent.h"
|
|
#include "mozilla/dom/quota/Client.h"
|
|
#include "mozilla/dom/quota/FileStreams.h"
|
|
#include "mozilla/dom/quota/MemoryOutputStream.h"
|
|
#include "mozilla/dom/quota/QuotaManager.h"
|
|
#include "mozilla/dom/quota/UsageInfo.h"
|
|
#include "mozilla/ipc/BackgroundParent.h"
|
|
#include "mozilla/ipc/PBackgroundParent.h"
|
|
#include "mozilla/ipc/PBackgroundSharedTypes.h"
|
|
#include "nsIFileStreams.h"
|
|
#include "nsIDirectoryEnumerator.h"
|
|
#include "nsStringStream.h"
|
|
#include "prio.h"
|
|
#include "SimpleDBCommon.h"
|
|
|
|
#define DISABLE_ASSERTS_FOR_FUZZING 0
|
|
|
|
#if DISABLE_ASSERTS_FOR_FUZZING
|
|
#define ASSERT_UNLESS_FUZZING(...) do { } while (0)
|
|
#else
|
|
#define ASSERT_UNLESS_FUZZING(...) MOZ_ASSERT(false, __VA_ARGS__)
|
|
#endif
|
|
|
|
namespace mozilla {
|
|
namespace dom {
|
|
|
|
using namespace mozilla::dom::quota;
|
|
using namespace mozilla::ipc;
|
|
|
|
namespace {
|
|
|
|
/*******************************************************************************
|
|
* Constants
|
|
******************************************************************************/
|
|
|
|
const uint32_t kCopyBufferSize = 32768;
|
|
|
|
/*******************************************************************************
|
|
* Actor class declarations
|
|
******************************************************************************/
|
|
|
|
class StreamHelper final
|
|
: public Runnable
|
|
{
|
|
nsCOMPtr<nsIEventTarget> mOwningEventTarget;
|
|
nsCOMPtr<nsIFileStream> mFileStream;
|
|
nsCOMPtr<nsIRunnable> mCallback;
|
|
|
|
public:
|
|
StreamHelper(nsIFileStream* aFileStream,
|
|
nsIRunnable* aCallback);
|
|
|
|
void
|
|
AsyncClose();
|
|
|
|
private:
|
|
~StreamHelper() override;
|
|
|
|
void
|
|
RunOnBackgroundThread();
|
|
|
|
void
|
|
RunOnIOThread();
|
|
|
|
NS_DECL_NSIRUNNABLE
|
|
};
|
|
|
|
class Connection final
|
|
: public PBackgroundSDBConnectionParent
|
|
{
|
|
RefPtr<DirectoryLock> mDirectoryLock;
|
|
nsCOMPtr<nsIFileStream> mFileStream;
|
|
const PrincipalInfo mPrincipalInfo;
|
|
nsCString mOrigin;
|
|
nsString mName;
|
|
|
|
bool mRunningRequest;
|
|
bool mOpen;
|
|
bool mAllowedToClose;
|
|
bool mActorDestroyed;
|
|
|
|
public:
|
|
explicit Connection(const PrincipalInfo& aPrincipalInfo);
|
|
|
|
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::dom::Connection)
|
|
|
|
nsIFileStream*
|
|
GetFileStream() const
|
|
{
|
|
AssertIsOnIOThread();
|
|
|
|
return mFileStream;
|
|
}
|
|
|
|
const PrincipalInfo&
|
|
GetPrincipalInfo() const
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
return mPrincipalInfo;
|
|
}
|
|
|
|
const nsCString&
|
|
Origin() const
|
|
{
|
|
AssertIsOnBackgroundThread();
|
|
MOZ_ASSERT(!mOrigin.IsEmpty());
|
|
|
|
return mOrigin;
|
|
}
|
|
|
|
const nsString&
|
|
Name() const
|
|
{
|
|
AssertIsOnBackgroundThread();
|
|
MOZ_ASSERT(!mName.IsEmpty());
|
|
|
|
return mName;
|
|
}
|
|
|
|
void
|
|
OnNewRequest();
|
|
|
|
void
|
|
OnRequestFinished();
|
|
|
|
void
|
|
OnOpen(const nsACString& aOrigin,
|
|
const nsAString& aName,
|
|
already_AddRefed<DirectoryLock> aDirectoryLock,
|
|
already_AddRefed<nsIFileStream> aFileStream);
|
|
|
|
void
|
|
OnClose();
|
|
|
|
void
|
|
AllowToClose();
|
|
|
|
private:
|
|
~Connection();
|
|
|
|
void
|
|
MaybeCloseStream();
|
|
|
|
bool
|
|
VerifyRequestParams(const SDBRequestParams& aParams) const;
|
|
|
|
// IPDL methods.
|
|
virtual void
|
|
ActorDestroy(ActorDestroyReason aWhy) override;
|
|
|
|
mozilla::ipc::IPCResult
|
|
RecvDeleteMe() override;
|
|
|
|
virtual PBackgroundSDBRequestParent*
|
|
AllocPBackgroundSDBRequestParent(const SDBRequestParams& aParams) override;
|
|
|
|
virtual mozilla::ipc::IPCResult
|
|
RecvPBackgroundSDBRequestConstructor(PBackgroundSDBRequestParent* aActor,
|
|
const SDBRequestParams& aParams)
|
|
override;
|
|
|
|
virtual bool
|
|
DeallocPBackgroundSDBRequestParent(PBackgroundSDBRequestParent* aActor)
|
|
override;
|
|
};
|
|
|
|
class ConnectionOperationBase
|
|
: public Runnable
|
|
, public PBackgroundSDBRequestParent
|
|
{
|
|
nsCOMPtr<nsIEventTarget> mOwningEventTarget;
|
|
RefPtr<Connection> mConnection;
|
|
nsresult mResultCode;
|
|
Atomic<bool> mOperationMayProceed;
|
|
bool mActorDestroyed;
|
|
|
|
public:
|
|
nsIEventTarget*
|
|
OwningEventTarget() const
|
|
{
|
|
MOZ_ASSERT(mOwningEventTarget);
|
|
|
|
return mOwningEventTarget;
|
|
}
|
|
|
|
bool
|
|
IsOnOwningThread() const
|
|
{
|
|
MOZ_ASSERT(mOwningEventTarget);
|
|
|
|
bool current;
|
|
return
|
|
NS_SUCCEEDED(mOwningEventTarget->IsOnCurrentThread(¤t)) && current;
|
|
}
|
|
|
|
void
|
|
AssertIsOnOwningThread() const
|
|
{
|
|
MOZ_ASSERT(IsOnBackgroundThread());
|
|
MOZ_ASSERT(IsOnOwningThread());
|
|
}
|
|
|
|
Connection*
|
|
GetConnection() const
|
|
{
|
|
MOZ_ASSERT(mConnection);
|
|
|
|
return mConnection;
|
|
}
|
|
|
|
nsresult
|
|
ResultCode() const
|
|
{
|
|
return mResultCode;
|
|
}
|
|
|
|
void
|
|
MaybeSetFailureCode(nsresult aErrorCode)
|
|
{
|
|
MOZ_ASSERT(NS_FAILED(aErrorCode));
|
|
|
|
if (NS_SUCCEEDED(mResultCode)) {
|
|
mResultCode = aErrorCode;
|
|
}
|
|
}
|
|
|
|
// May be called on any thread, but you should call IsActorDestroyed() if
|
|
// you know you're on the background thread because it is slightly faster.
|
|
bool
|
|
OperationMayProceed() const
|
|
{
|
|
return mOperationMayProceed;
|
|
}
|
|
|
|
bool
|
|
IsActorDestroyed() const
|
|
{
|
|
AssertIsOnOwningThread();
|
|
|
|
return mActorDestroyed;
|
|
}
|
|
|
|
// May be overridden by subclasses if they need to perform work on the
|
|
// background thread before being dispatched but must always call the base
|
|
// class implementation. Returning false will kill the child actors and
|
|
// prevent dispatch.
|
|
virtual bool
|
|
Init();
|
|
|
|
virtual nsresult
|
|
Dispatch();
|
|
|
|
// This callback will be called on the background thread before releasing the
|
|
// final reference to this request object. Subclasses may perform any
|
|
// additional cleanup here but must always call the base class implementation.
|
|
virtual void
|
|
Cleanup();
|
|
|
|
protected:
|
|
ConnectionOperationBase(Connection* aConnection)
|
|
: Runnable("dom::ConnectionOperationBase")
|
|
, mOwningEventTarget(GetCurrentThreadEventTarget())
|
|
, mConnection(aConnection)
|
|
, mResultCode(NS_OK)
|
|
, mOperationMayProceed(true)
|
|
, mActorDestroyed(false)
|
|
{
|
|
AssertIsOnOwningThread();
|
|
}
|
|
|
|
~ConnectionOperationBase() override;
|
|
|
|
void
|
|
SendResults();
|
|
|
|
void
|
|
DatabaseWork();
|
|
|
|
// Methods that subclasses must implement.
|
|
virtual nsresult
|
|
DoDatabaseWork(nsIFileStream* aFileStream) = 0;
|
|
|
|
// Subclasses use this override to set the IPDL response value.
|
|
virtual void
|
|
GetResponse(SDBRequestResponse& aResponse) = 0;
|
|
|
|
// A method that subclasses may implement.
|
|
virtual void
|
|
OnSuccess();
|
|
|
|
private:
|
|
NS_IMETHOD
|
|
Run() override;
|
|
|
|
// IPDL methods.
|
|
void
|
|
ActorDestroy(ActorDestroyReason aWhy) override;
|
|
};
|
|
|
|
class OpenOp final
|
|
: public ConnectionOperationBase
|
|
, public OpenDirectoryListener
|
|
{
|
|
enum class State
|
|
{
|
|
// Just created on the PBackground thread, dispatched to the main thread.
|
|
// Next step is FinishOpen.
|
|
Initial,
|
|
|
|
// Opening directory or initializing quota manager on the PBackground
|
|
// thread. Next step is either DirectoryOpenPending if quota manager is
|
|
// already initialized or QuotaManagerPending if quota manager needs to be
|
|
// initialized.
|
|
FinishOpen,
|
|
|
|
// Waiting for quota manager initialization to complete on the PBackground
|
|
// thread. Next step is either SendingResults if initialization failed or
|
|
// DirectoryOpenPending if initialization succeeded.
|
|
QuotaManagerPending,
|
|
|
|
// Waiting for directory open allowed on the PBackground thread. The next
|
|
// step is either SendingResults if directory lock failed to acquire, or
|
|
// DatabaseWorkOpen if directory lock is acquired.
|
|
DirectoryOpenPending,
|
|
|
|
// Waiting to do/doing work on the QuotaManager IO thread. Its next step is
|
|
// SendingResults.
|
|
DatabaseWorkOpen,
|
|
|
|
// Waiting to send/sending results on the PBackground thread. Next step is
|
|
// Completed.
|
|
SendingResults,
|
|
|
|
// All done.
|
|
Completed
|
|
};
|
|
|
|
const SDBRequestOpenParams mParams;
|
|
RefPtr<DirectoryLock> mDirectoryLock;
|
|
nsCOMPtr<nsIFileStream> mFileStream;
|
|
nsCString mSuffix;
|
|
nsCString mGroup;
|
|
nsCString mOrigin;
|
|
State mState;
|
|
bool mFileStreamOpen;
|
|
|
|
public:
|
|
OpenOp(Connection* aConnection, const SDBRequestParams& aParams);
|
|
|
|
nsresult
|
|
Dispatch() override;
|
|
|
|
private:
|
|
~OpenOp() override;
|
|
|
|
nsresult
|
|
Open();
|
|
|
|
nsresult
|
|
FinishOpen();
|
|
|
|
nsresult
|
|
QuotaManagerOpen();
|
|
|
|
nsresult
|
|
OpenDirectory();
|
|
|
|
nsresult
|
|
SendToIOThread();
|
|
|
|
nsresult
|
|
DatabaseWork();
|
|
|
|
void
|
|
StreamClosedCallback();
|
|
|
|
// ConnectionOperationBase overrides
|
|
nsresult
|
|
DoDatabaseWork(nsIFileStream* aFileStream) override;
|
|
|
|
void
|
|
GetResponse(SDBRequestResponse& aResponse) override;
|
|
|
|
void
|
|
OnSuccess() override;
|
|
|
|
void
|
|
Cleanup() override;
|
|
|
|
NS_DECL_ISUPPORTS_INHERITED
|
|
|
|
NS_IMETHOD
|
|
Run() override;
|
|
|
|
// OpenDirectoryListener overrides.
|
|
void
|
|
DirectoryLockAcquired(DirectoryLock* aLock) override;
|
|
|
|
void
|
|
DirectoryLockFailed() override;
|
|
};
|
|
|
|
class SeekOp final
|
|
: public ConnectionOperationBase
|
|
{
|
|
const SDBRequestSeekParams mParams;
|
|
|
|
public:
|
|
SeekOp(Connection* aConnection,
|
|
const SDBRequestParams& aParams);
|
|
|
|
private:
|
|
~SeekOp() override = default;
|
|
|
|
nsresult
|
|
DoDatabaseWork(nsIFileStream* aFileStream) override;
|
|
|
|
void
|
|
GetResponse(SDBRequestResponse& aResponse) override;
|
|
};
|
|
|
|
class ReadOp final
|
|
: public ConnectionOperationBase
|
|
{
|
|
const SDBRequestReadParams mParams;
|
|
|
|
RefPtr<MemoryOutputStream> mOutputStream;
|
|
|
|
public:
|
|
ReadOp(Connection* aConnection,
|
|
const SDBRequestParams& aParams);
|
|
|
|
bool
|
|
Init() override;
|
|
|
|
private:
|
|
~ReadOp() override = default;
|
|
|
|
nsresult
|
|
DoDatabaseWork(nsIFileStream* aFileStream) override;
|
|
|
|
void
|
|
GetResponse(SDBRequestResponse& aResponse) override;
|
|
};
|
|
|
|
class WriteOp final
|
|
: public ConnectionOperationBase
|
|
{
|
|
const SDBRequestWriteParams mParams;
|
|
|
|
nsCOMPtr<nsIInputStream> mInputStream;
|
|
|
|
uint64_t mSize;
|
|
|
|
public:
|
|
WriteOp(Connection* aConnection,
|
|
const SDBRequestParams& aParams);
|
|
|
|
bool
|
|
Init() override;
|
|
|
|
private:
|
|
~WriteOp() override = default;
|
|
|
|
nsresult
|
|
DoDatabaseWork(nsIFileStream* aFileStream) override;
|
|
|
|
void
|
|
GetResponse(SDBRequestResponse& aResponse) override;
|
|
};
|
|
|
|
class CloseOp final
|
|
: public ConnectionOperationBase
|
|
{
|
|
public:
|
|
explicit CloseOp(Connection* aConnection);
|
|
|
|
private:
|
|
~CloseOp() override = default;
|
|
|
|
nsresult
|
|
DoDatabaseWork(nsIFileStream* aFileStream) override;
|
|
|
|
void
|
|
GetResponse(SDBRequestResponse& aResponse) override;
|
|
|
|
void
|
|
OnSuccess() override;
|
|
};
|
|
|
|
/*******************************************************************************
|
|
* Other class declarations
|
|
******************************************************************************/
|
|
|
|
class QuotaClient final
|
|
: public mozilla::dom::quota::Client
|
|
{
|
|
static QuotaClient* sInstance;
|
|
|
|
bool mShutdownRequested;
|
|
|
|
public:
|
|
QuotaClient();
|
|
|
|
static bool
|
|
IsShuttingDownOnBackgroundThread()
|
|
{
|
|
AssertIsOnBackgroundThread();
|
|
|
|
if (sInstance) {
|
|
return sInstance->IsShuttingDown();
|
|
}
|
|
|
|
return QuotaManager::IsShuttingDown();
|
|
}
|
|
|
|
static bool
|
|
IsShuttingDownOnNonBackgroundThread()
|
|
{
|
|
MOZ_ASSERT(!IsOnBackgroundThread());
|
|
|
|
return QuotaManager::IsShuttingDown();
|
|
}
|
|
|
|
bool
|
|
IsShuttingDown() const
|
|
{
|
|
AssertIsOnBackgroundThread();
|
|
|
|
return mShutdownRequested;
|
|
}
|
|
|
|
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(QuotaClient, override)
|
|
|
|
Type
|
|
GetType() override;
|
|
|
|
nsresult
|
|
InitOrigin(PersistenceType aPersistenceType,
|
|
const nsACString& aGroup,
|
|
const nsACString& aOrigin,
|
|
const AtomicBool& aCanceled,
|
|
UsageInfo* aUsageInfo) override;
|
|
|
|
nsresult
|
|
GetUsageForOrigin(PersistenceType aPersistenceType,
|
|
const nsACString& aGroup,
|
|
const nsACString& aOrigin,
|
|
const AtomicBool& aCanceled,
|
|
UsageInfo* aUsageInfo) override;
|
|
|
|
void
|
|
OnOriginClearCompleted(PersistenceType aPersistenceType,
|
|
const nsACString& aOrigin)
|
|
override;
|
|
|
|
void
|
|
ReleaseIOThreadObjects() override;
|
|
|
|
void
|
|
AbortOperations(const nsACString& aOrigin) override;
|
|
|
|
void
|
|
AbortOperationsForProcess(ContentParentId aContentParentId) override;
|
|
|
|
void
|
|
StartIdleMaintenance() override;
|
|
|
|
void
|
|
StopIdleMaintenance() override;
|
|
|
|
void
|
|
ShutdownWorkThreads() override;
|
|
|
|
private:
|
|
~QuotaClient() override;
|
|
};
|
|
|
|
/*******************************************************************************
|
|
* Globals
|
|
******************************************************************************/
|
|
|
|
typedef nsTArray<RefPtr<Connection>> ConnectionArray;
|
|
|
|
StaticAutoPtr<ConnectionArray> gOpenConnections;
|
|
|
|
} // namespace
|
|
|
|
/*******************************************************************************
|
|
* Exported functions
|
|
******************************************************************************/
|
|
|
|
PBackgroundSDBConnectionParent*
|
|
AllocPBackgroundSDBConnectionParent(const PrincipalInfo& aPrincipalInfo)
|
|
{
|
|
AssertIsOnBackgroundThread();
|
|
|
|
if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread())) {
|
|
return nullptr;
|
|
}
|
|
|
|
if (NS_WARN_IF(aPrincipalInfo.type() == PrincipalInfo::TNullPrincipalInfo)) {
|
|
ASSERT_UNLESS_FUZZING();
|
|
return nullptr;
|
|
}
|
|
|
|
RefPtr<Connection> actor = new Connection(aPrincipalInfo);
|
|
|
|
return actor.forget().take();
|
|
}
|
|
|
|
bool
|
|
RecvPBackgroundSDBConnectionConstructor(PBackgroundSDBConnectionParent* aActor,
|
|
const PrincipalInfo& aPrincipalInfo)
|
|
{
|
|
AssertIsOnBackgroundThread();
|
|
MOZ_ASSERT(aActor);
|
|
MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
DeallocPBackgroundSDBConnectionParent(PBackgroundSDBConnectionParent* aActor)
|
|
{
|
|
AssertIsOnBackgroundThread();
|
|
MOZ_ASSERT(aActor);
|
|
|
|
RefPtr<Connection> actor = dont_AddRef(static_cast<Connection*>(aActor));
|
|
return true;
|
|
}
|
|
|
|
namespace simpledb {
|
|
|
|
already_AddRefed<mozilla::dom::quota::Client>
|
|
CreateQuotaClient()
|
|
{
|
|
AssertIsOnBackgroundThread();
|
|
|
|
RefPtr<QuotaClient> client = new QuotaClient();
|
|
return client.forget();
|
|
}
|
|
|
|
} // namespace simpledb
|
|
|
|
/*******************************************************************************
|
|
* StreamHelper
|
|
******************************************************************************/
|
|
|
|
StreamHelper::StreamHelper(nsIFileStream* aFileStream,
|
|
nsIRunnable* aCallback)
|
|
: Runnable("dom::StreamHelper")
|
|
, mOwningEventTarget(GetCurrentThreadEventTarget())
|
|
, mFileStream(aFileStream)
|
|
, mCallback(aCallback)
|
|
{
|
|
AssertIsOnBackgroundThread();
|
|
MOZ_ASSERT(aFileStream);
|
|
MOZ_ASSERT(aCallback);
|
|
}
|
|
|
|
StreamHelper::~StreamHelper()
|
|
{
|
|
MOZ_ASSERT(!mFileStream);
|
|
MOZ_ASSERT(!mCallback);
|
|
}
|
|
|
|
void
|
|
StreamHelper::AsyncClose()
|
|
{
|
|
AssertIsOnBackgroundThread();
|
|
|
|
QuotaManager* quotaManager = QuotaManager::Get();
|
|
MOZ_ASSERT(quotaManager);
|
|
|
|
MOZ_ALWAYS_SUCCEEDS(
|
|
quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL));
|
|
}
|
|
|
|
void
|
|
StreamHelper::RunOnBackgroundThread()
|
|
{
|
|
AssertIsOnBackgroundThread();
|
|
|
|
nsCOMPtr<nsIFileStream> fileStream;
|
|
mFileStream.swap(fileStream);
|
|
|
|
nsCOMPtr<nsIRunnable> callback;
|
|
mCallback.swap(callback);
|
|
|
|
callback->Run();
|
|
}
|
|
|
|
void
|
|
StreamHelper::RunOnIOThread()
|
|
{
|
|
AssertIsOnIOThread();
|
|
MOZ_ASSERT(mFileStream);
|
|
|
|
nsCOMPtr<nsIInputStream> inputStream = do_QueryInterface(mFileStream);
|
|
MOZ_ASSERT(inputStream);
|
|
|
|
nsresult rv = inputStream->Close();
|
|
Unused << NS_WARN_IF(NS_FAILED(rv));
|
|
|
|
MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL));
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
StreamHelper::Run()
|
|
{
|
|
MOZ_ASSERT(mCallback);
|
|
|
|
if (IsOnBackgroundThread()) {
|
|
RunOnBackgroundThread();
|
|
} else {
|
|
RunOnIOThread();
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* Connection
|
|
******************************************************************************/
|
|
|
|
Connection::Connection(const PrincipalInfo& aPrincipalInfo)
|
|
: mPrincipalInfo(aPrincipalInfo)
|
|
, mRunningRequest(false)
|
|
, mOpen(false)
|
|
, mAllowedToClose(false)
|
|
, mActorDestroyed(false)
|
|
{
|
|
AssertIsOnBackgroundThread();
|
|
MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
|
|
}
|
|
|
|
Connection::~Connection()
|
|
{
|
|
MOZ_ASSERT(!mRunningRequest);
|
|
MOZ_ASSERT(!mOpen);
|
|
MOZ_ASSERT(mActorDestroyed);
|
|
}
|
|
|
|
void
|
|
Connection::OnNewRequest()
|
|
{
|
|
AssertIsOnBackgroundThread();
|
|
MOZ_ASSERT(!mRunningRequest);
|
|
|
|
mRunningRequest = true;
|
|
}
|
|
|
|
void
|
|
Connection::OnRequestFinished()
|
|
{
|
|
AssertIsOnBackgroundThread();
|
|
MOZ_ASSERT(mRunningRequest);
|
|
|
|
mRunningRequest = false;
|
|
|
|
MaybeCloseStream();
|
|
}
|
|
|
|
void
|
|
Connection::OnOpen(const nsACString& aOrigin,
|
|
const nsAString& aName,
|
|
already_AddRefed<DirectoryLock> aDirectoryLock,
|
|
already_AddRefed<nsIFileStream> aFileStream)
|
|
{
|
|
AssertIsOnBackgroundThread();
|
|
MOZ_ASSERT(!aOrigin.IsEmpty());
|
|
MOZ_ASSERT(!aName.IsEmpty());
|
|
MOZ_ASSERT(mOrigin.IsEmpty());
|
|
MOZ_ASSERT(mName.IsEmpty());
|
|
MOZ_ASSERT(!mDirectoryLock);
|
|
MOZ_ASSERT(!mFileStream);
|
|
MOZ_ASSERT(!mOpen);
|
|
|
|
mOrigin = aOrigin;
|
|
mName = aName;
|
|
mDirectoryLock = aDirectoryLock;
|
|
mFileStream = aFileStream;
|
|
mOpen = true;
|
|
|
|
if (!gOpenConnections) {
|
|
gOpenConnections = new ConnectionArray();
|
|
}
|
|
|
|
gOpenConnections->AppendElement(this);
|
|
}
|
|
|
|
void
|
|
Connection::OnClose()
|
|
{
|
|
AssertIsOnBackgroundThread();
|
|
MOZ_ASSERT(!mOrigin.IsEmpty());
|
|
MOZ_ASSERT(mDirectoryLock);
|
|
MOZ_ASSERT(mFileStream);
|
|
MOZ_ASSERT(mOpen);
|
|
|
|
mOrigin.Truncate();
|
|
mName.Truncate();
|
|
mDirectoryLock = nullptr;
|
|
mFileStream = nullptr;
|
|
mOpen = false;
|
|
|
|
MOZ_ASSERT(gOpenConnections);
|
|
gOpenConnections->RemoveElement(this);
|
|
|
|
if (gOpenConnections->IsEmpty()) {
|
|
gOpenConnections = nullptr;
|
|
}
|
|
|
|
if (mAllowedToClose && !mActorDestroyed) {
|
|
Unused << SendClosed();
|
|
}
|
|
}
|
|
|
|
void
|
|
Connection::AllowToClose()
|
|
{
|
|
AssertIsOnBackgroundThread();
|
|
|
|
if (mAllowedToClose) {
|
|
return;
|
|
}
|
|
|
|
mAllowedToClose = true;
|
|
|
|
if (!mActorDestroyed) {
|
|
Unused << SendAllowToClose();
|
|
}
|
|
|
|
MaybeCloseStream();
|
|
}
|
|
|
|
void
|
|
Connection::MaybeCloseStream()
|
|
{
|
|
AssertIsOnBackgroundThread();
|
|
|
|
if (!mRunningRequest &&
|
|
mOpen &&
|
|
mAllowedToClose) {
|
|
nsCOMPtr<nsIRunnable> callback =
|
|
NewRunnableMethod("dom::Connection::OnClose",
|
|
this,
|
|
&Connection::OnClose);
|
|
|
|
RefPtr<StreamHelper> helper = new StreamHelper(mFileStream, callback);
|
|
helper->AsyncClose();
|
|
}
|
|
}
|
|
|
|
bool
|
|
Connection::VerifyRequestParams(const SDBRequestParams& aParams) const
|
|
{
|
|
AssertIsOnBackgroundThread();
|
|
MOZ_ASSERT(aParams.type() != SDBRequestParams::T__None);
|
|
|
|
switch (aParams.type()) {
|
|
case SDBRequestParams::TSDBRequestOpenParams: {
|
|
if (NS_WARN_IF(mOpen)) {
|
|
ASSERT_UNLESS_FUZZING();
|
|
return false;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case SDBRequestParams::TSDBRequestSeekParams:
|
|
case SDBRequestParams::TSDBRequestReadParams:
|
|
case SDBRequestParams::TSDBRequestWriteParams:
|
|
case SDBRequestParams::TSDBRequestCloseParams: {
|
|
if (NS_WARN_IF(!mOpen)) {
|
|
ASSERT_UNLESS_FUZZING();
|
|
return false;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
MOZ_CRASH("Should never get here!");
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void
|
|
Connection::ActorDestroy(ActorDestroyReason aWhy)
|
|
{
|
|
AssertIsOnBackgroundThread();
|
|
MOZ_ASSERT(!mActorDestroyed);
|
|
|
|
mActorDestroyed = true;
|
|
|
|
AllowToClose();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult
|
|
Connection::RecvDeleteMe()
|
|
{
|
|
AssertIsOnBackgroundThread();
|
|
MOZ_ASSERT(!mActorDestroyed);
|
|
|
|
IProtocol* mgr = Manager();
|
|
if (!PBackgroundSDBConnectionParent::Send__delete__(this)) {
|
|
return IPC_FAIL_NO_REASON(mgr);
|
|
}
|
|
|
|
return IPC_OK();
|
|
}
|
|
|
|
PBackgroundSDBRequestParent*
|
|
Connection::AllocPBackgroundSDBRequestParent(const SDBRequestParams& aParams)
|
|
{
|
|
AssertIsOnBackgroundThread();
|
|
MOZ_ASSERT(aParams.type() != SDBRequestParams::T__None);
|
|
|
|
if (aParams.type() == SDBRequestParams::TSDBRequestOpenParams &&
|
|
NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread())) {
|
|
return nullptr;
|
|
}
|
|
|
|
if (mAllowedToClose) {
|
|
return nullptr;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
// Always verify parameters in DEBUG builds!
|
|
bool trustParams = false;
|
|
#else
|
|
PBackgroundParent* backgroundActor = Manager();
|
|
MOZ_ASSERT(backgroundActor);
|
|
|
|
bool trustParams = !BackgroundParent::IsOtherProcessActor(backgroundActor);
|
|
#endif
|
|
|
|
if (NS_WARN_IF(!trustParams && !VerifyRequestParams(aParams))) {
|
|
ASSERT_UNLESS_FUZZING();
|
|
return nullptr;
|
|
}
|
|
|
|
if (NS_WARN_IF(mRunningRequest)) {
|
|
ASSERT_UNLESS_FUZZING();
|
|
return nullptr;
|
|
}
|
|
|
|
RefPtr<ConnectionOperationBase> actor;
|
|
|
|
switch (aParams.type()) {
|
|
case SDBRequestParams::TSDBRequestOpenParams:
|
|
actor = new OpenOp(this, aParams);
|
|
break;
|
|
|
|
case SDBRequestParams::TSDBRequestSeekParams:
|
|
actor = new SeekOp(this, aParams);
|
|
break;
|
|
|
|
case SDBRequestParams::TSDBRequestReadParams:
|
|
actor = new ReadOp(this, aParams);
|
|
break;
|
|
|
|
case SDBRequestParams::TSDBRequestWriteParams:
|
|
actor = new WriteOp(this, aParams);
|
|
break;
|
|
|
|
case SDBRequestParams::TSDBRequestCloseParams:
|
|
actor = new CloseOp(this);
|
|
break;
|
|
|
|
default:
|
|
MOZ_CRASH("Should never get here!");
|
|
}
|
|
|
|
// Transfer ownership to IPDL.
|
|
return actor.forget().take();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult
|
|
Connection::RecvPBackgroundSDBRequestConstructor(
|
|
PBackgroundSDBRequestParent* aActor,
|
|
const SDBRequestParams& aParams)
|
|
{
|
|
AssertIsOnBackgroundThread();
|
|
MOZ_ASSERT(aActor);
|
|
MOZ_ASSERT(aParams.type() != SDBRequestParams::T__None);
|
|
MOZ_ASSERT_IF(aParams.type() == SDBRequestParams::TSDBRequestOpenParams,
|
|
!QuotaClient::IsShuttingDownOnBackgroundThread());
|
|
MOZ_ASSERT(!mAllowedToClose);
|
|
MOZ_ASSERT(!mRunningRequest);
|
|
|
|
auto* op = static_cast<ConnectionOperationBase*>(aActor);
|
|
|
|
if (NS_WARN_IF(!op->Init())) {
|
|
op->Cleanup();
|
|
return IPC_FAIL_NO_REASON(this);
|
|
}
|
|
|
|
if (NS_WARN_IF(NS_FAILED(op->Dispatch()))) {
|
|
op->Cleanup();
|
|
return IPC_FAIL_NO_REASON(this);
|
|
}
|
|
|
|
return IPC_OK();
|
|
}
|
|
|
|
bool
|
|
Connection::DeallocPBackgroundSDBRequestParent(
|
|
PBackgroundSDBRequestParent* aActor)
|
|
{
|
|
AssertIsOnBackgroundThread();
|
|
MOZ_ASSERT(aActor);
|
|
|
|
// Transfer ownership back from IPDL.
|
|
RefPtr<ConnectionOperationBase> actor =
|
|
dont_AddRef(static_cast<ConnectionOperationBase*>(aActor));
|
|
return true;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* ConnectionOperationBase
|
|
******************************************************************************/
|
|
|
|
ConnectionOperationBase::~ConnectionOperationBase()
|
|
{
|
|
MOZ_ASSERT(!mConnection,
|
|
"ConnectionOperationBase::Cleanup() was not called by a subclass!");
|
|
MOZ_ASSERT(mActorDestroyed);
|
|
}
|
|
|
|
bool
|
|
ConnectionOperationBase::Init()
|
|
{
|
|
AssertIsOnBackgroundThread();
|
|
MOZ_ASSERT(mConnection);
|
|
|
|
mConnection->OnNewRequest();
|
|
|
|
return true;
|
|
}
|
|
|
|
nsresult
|
|
ConnectionOperationBase::Dispatch()
|
|
{
|
|
if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
|
|
IsActorDestroyed()) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
QuotaManager* quotaManager = QuotaManager::Get();
|
|
MOZ_ASSERT(quotaManager);
|
|
|
|
nsresult rv = quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
ConnectionOperationBase::Cleanup()
|
|
{
|
|
AssertIsOnOwningThread();
|
|
MOZ_ASSERT(mConnection);
|
|
|
|
mConnection->OnRequestFinished();
|
|
|
|
mConnection = nullptr;
|
|
}
|
|
|
|
void
|
|
ConnectionOperationBase::SendResults()
|
|
{
|
|
AssertIsOnOwningThread();
|
|
|
|
if (IsActorDestroyed()) {
|
|
MaybeSetFailureCode(NS_ERROR_FAILURE);
|
|
} else {
|
|
SDBRequestResponse response;
|
|
|
|
if (NS_SUCCEEDED(mResultCode)) {
|
|
GetResponse(response);
|
|
|
|
MOZ_ASSERT(response.type() != SDBRequestResponse::T__None);
|
|
MOZ_ASSERT(response.type() != SDBRequestResponse::Tnsresult);
|
|
} else {
|
|
response = mResultCode;
|
|
}
|
|
|
|
Unused <<
|
|
PBackgroundSDBRequestParent::Send__delete__(this, response);
|
|
|
|
if (NS_SUCCEEDED(mResultCode)) {
|
|
OnSuccess();
|
|
}
|
|
}
|
|
|
|
Cleanup();
|
|
}
|
|
|
|
void
|
|
ConnectionOperationBase::DatabaseWork()
|
|
{
|
|
AssertIsOnIOThread();
|
|
MOZ_ASSERT(NS_SUCCEEDED(mResultCode));
|
|
|
|
if (!OperationMayProceed()) {
|
|
// The operation was canceled in some way, likely because the child process
|
|
// has crashed.
|
|
mResultCode = NS_ERROR_FAILURE;
|
|
} else {
|
|
nsIFileStream* fileStream = mConnection->GetFileStream();
|
|
MOZ_ASSERT(fileStream);
|
|
|
|
nsresult rv = DoDatabaseWork(fileStream);
|
|
if (NS_FAILED(rv)) {
|
|
mResultCode = rv;
|
|
}
|
|
}
|
|
|
|
MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL));
|
|
}
|
|
|
|
void
|
|
ConnectionOperationBase::OnSuccess()
|
|
{
|
|
AssertIsOnOwningThread();
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
ConnectionOperationBase::Run()
|
|
{
|
|
if (IsOnBackgroundThread()) {
|
|
SendResults();
|
|
} else {
|
|
DatabaseWork();
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
ConnectionOperationBase::ActorDestroy(ActorDestroyReason aWhy)
|
|
{
|
|
AssertIsOnBackgroundThread();
|
|
|
|
mOperationMayProceed = false;
|
|
mActorDestroyed = true;
|
|
}
|
|
|
|
OpenOp::OpenOp(Connection* aConnection, const SDBRequestParams& aParams)
|
|
: ConnectionOperationBase(aConnection)
|
|
, mParams(aParams.get_SDBRequestOpenParams())
|
|
, mState(State::Initial)
|
|
, mFileStreamOpen(false)
|
|
{
|
|
MOZ_ASSERT(aParams.type() == SDBRequestParams::TSDBRequestOpenParams);
|
|
}
|
|
|
|
OpenOp::~OpenOp()
|
|
{
|
|
MOZ_ASSERT(!mDirectoryLock);
|
|
MOZ_ASSERT(!mFileStream);
|
|
MOZ_ASSERT(!mFileStreamOpen);
|
|
MOZ_ASSERT_IF(OperationMayProceed(),
|
|
mState == State::Initial || mState == State::Completed);
|
|
}
|
|
|
|
nsresult
|
|
OpenOp::Dispatch()
|
|
{
|
|
MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(this));
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
OpenOp::Open()
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
MOZ_ASSERT(mState == State::Initial);
|
|
|
|
if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
|
|
!OperationMayProceed()) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
if (NS_WARN_IF(!Preferences::GetBool(kPrefSimpleDBEnabled, false))) {
|
|
return NS_ERROR_UNEXPECTED;
|
|
}
|
|
|
|
const PrincipalInfo& principalInfo = GetConnection()->GetPrincipalInfo();
|
|
|
|
if (principalInfo.type() == PrincipalInfo::TSystemPrincipalInfo) {
|
|
QuotaManager::GetInfoForChrome(&mSuffix, &mGroup, &mOrigin);
|
|
} else {
|
|
MOZ_ASSERT(principalInfo.type() == PrincipalInfo::TContentPrincipalInfo);
|
|
|
|
nsresult rv;
|
|
nsCOMPtr<nsIPrincipal> principal =
|
|
PrincipalInfoToPrincipal(principalInfo, &rv);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
|
|
rv = QuotaManager::GetInfoFromPrincipal(principal,
|
|
&mSuffix,
|
|
&mGroup,
|
|
&mOrigin);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
}
|
|
|
|
mState = State::FinishOpen;
|
|
MOZ_ALWAYS_SUCCEEDS(OwningEventTarget()->Dispatch(this, NS_DISPATCH_NORMAL));
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
OpenOp::FinishOpen()
|
|
{
|
|
AssertIsOnOwningThread();
|
|
MOZ_ASSERT(mState == State::FinishOpen);
|
|
|
|
if (gOpenConnections) {
|
|
for (Connection* connection : *gOpenConnections) {
|
|
if (connection->Origin() == mOrigin &&
|
|
connection->Name() == mParams.name()) {
|
|
return NS_ERROR_STORAGE_BUSY;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (QuotaManager::Get()) {
|
|
nsresult rv = OpenDirectory();
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
mState = State::QuotaManagerPending;
|
|
QuotaManager::GetOrCreate(this);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
OpenOp::QuotaManagerOpen()
|
|
{
|
|
AssertIsOnOwningThread();
|
|
MOZ_ASSERT(mState == State::QuotaManagerPending);
|
|
|
|
if (NS_WARN_IF(!QuotaManager::Get())) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
nsresult rv = OpenDirectory();
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
OpenOp::OpenDirectory()
|
|
{
|
|
AssertIsOnOwningThread();
|
|
MOZ_ASSERT(mState == State::FinishOpen ||
|
|
mState == State::QuotaManagerPending);
|
|
MOZ_ASSERT(!mOrigin.IsEmpty());
|
|
MOZ_ASSERT(!mDirectoryLock);
|
|
MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
|
|
MOZ_ASSERT(QuotaManager::Get());
|
|
|
|
mState = State::DirectoryOpenPending;
|
|
QuotaManager::Get()->OpenDirectory(PERSISTENCE_TYPE_DEFAULT,
|
|
mGroup,
|
|
mOrigin,
|
|
mozilla::dom::quota::Client::SDB,
|
|
/* aExclusive */ false,
|
|
this);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
OpenOp::SendToIOThread()
|
|
{
|
|
AssertIsOnOwningThread();
|
|
MOZ_ASSERT(mState == State::DirectoryOpenPending);
|
|
|
|
if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
|
|
IsActorDestroyed()) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
mFileStream = new FileStream(PERSISTENCE_TYPE_DEFAULT, mGroup, mOrigin);
|
|
|
|
QuotaManager* quotaManager = QuotaManager::Get();
|
|
MOZ_ASSERT(quotaManager);
|
|
|
|
// Must set this before dispatching otherwise we will race with the IO thread.
|
|
mState = State::DatabaseWorkOpen;
|
|
|
|
nsresult rv = quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
OpenOp::DatabaseWork()
|
|
{
|
|
AssertIsOnIOThread();
|
|
MOZ_ASSERT(mState == State::DatabaseWorkOpen);
|
|
MOZ_ASSERT(mFileStream);
|
|
MOZ_ASSERT(!mFileStreamOpen);
|
|
|
|
if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
|
|
!OperationMayProceed()) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
QuotaManager* quotaManager = QuotaManager::Get();
|
|
MOZ_ASSERT(quotaManager);
|
|
|
|
nsCOMPtr<nsIFile> dbDirectory;
|
|
nsresult rv =
|
|
quotaManager->EnsureOriginIsInitialized(PERSISTENCE_TYPE_DEFAULT,
|
|
mSuffix,
|
|
mGroup,
|
|
mOrigin,
|
|
getter_AddRefs(dbDirectory));
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
|
|
rv = dbDirectory->Append(NS_LITERAL_STRING(SDB_DIRECTORY_NAME));
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
|
|
bool exists;
|
|
rv = dbDirectory->Exists(&exists);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
|
|
if (!exists) {
|
|
rv = dbDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
}
|
|
#ifdef DEBUG
|
|
else {
|
|
bool isDirectory;
|
|
MOZ_ASSERT(NS_SUCCEEDED(dbDirectory->IsDirectory(&isDirectory)));
|
|
MOZ_ASSERT(isDirectory);
|
|
}
|
|
#endif
|
|
|
|
nsCOMPtr<nsIFile> dbFile;
|
|
rv = dbDirectory->Clone(getter_AddRefs(dbFile));
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
|
|
rv = dbFile->Append(mParams.name());
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
|
|
nsString databaseFilePath;
|
|
rv = dbFile->GetPath(databaseFilePath);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
|
|
rv = mFileStream->Init(dbFile, PR_RDWR | PR_CREATE_FILE, 0644, 0);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
|
|
mFileStreamOpen = true;
|
|
|
|
rv = DoDatabaseWork(mFileStream);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
|
|
// Must set mState before dispatching otherwise we will race with the owning
|
|
// thread.
|
|
mState = State::SendingResults;
|
|
|
|
rv = OwningEventTarget()->Dispatch(this, NS_DISPATCH_NORMAL);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
OpenOp::StreamClosedCallback()
|
|
{
|
|
AssertIsOnOwningThread();
|
|
MOZ_ASSERT(NS_FAILED(ResultCode()));
|
|
MOZ_ASSERT(mDirectoryLock);
|
|
MOZ_ASSERT(mFileStream);
|
|
MOZ_ASSERT(mFileStreamOpen);
|
|
|
|
mDirectoryLock = nullptr;
|
|
mFileStream = nullptr;
|
|
mFileStreamOpen = false;
|
|
}
|
|
|
|
nsresult
|
|
OpenOp::DoDatabaseWork(nsIFileStream* aFileStream)
|
|
{
|
|
AssertIsOnIOThread();
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
OpenOp::GetResponse(SDBRequestResponse& aResponse)
|
|
{
|
|
AssertIsOnOwningThread();
|
|
|
|
aResponse = SDBRequestOpenResponse();
|
|
}
|
|
|
|
void
|
|
OpenOp::OnSuccess()
|
|
{
|
|
AssertIsOnOwningThread();
|
|
MOZ_ASSERT(NS_SUCCEEDED(ResultCode()));
|
|
MOZ_ASSERT(!mOrigin.IsEmpty());
|
|
MOZ_ASSERT(mDirectoryLock);
|
|
MOZ_ASSERT(mFileStream);
|
|
MOZ_ASSERT(mFileStreamOpen);
|
|
|
|
RefPtr<DirectoryLock> directoryLock;
|
|
nsCOMPtr<nsIFileStream> fileStream;
|
|
|
|
mDirectoryLock.swap(directoryLock);
|
|
mFileStream.swap(fileStream);
|
|
mFileStreamOpen = false;
|
|
|
|
GetConnection()->OnOpen(mOrigin,
|
|
mParams.name(),
|
|
directoryLock.forget(),
|
|
fileStream.forget());
|
|
}
|
|
|
|
void
|
|
OpenOp::Cleanup()
|
|
{
|
|
AssertIsOnOwningThread();
|
|
MOZ_ASSERT_IF(mFileStreamOpen, mFileStream);
|
|
|
|
if (mFileStream && mFileStreamOpen) {
|
|
// If we have an initialized file stream then the operation must have failed
|
|
// and there must be a directory lock too.
|
|
MOZ_ASSERT(NS_FAILED(ResultCode()));
|
|
MOZ_ASSERT(mDirectoryLock);
|
|
|
|
// We must close the stream on the I/O thread before releasing it on this
|
|
// thread. The directory lock can't be released either.
|
|
nsCOMPtr<nsIRunnable> callback =
|
|
NewRunnableMethod("dom::OpenOp::StreamClosedCallback",
|
|
this,
|
|
&OpenOp::StreamClosedCallback);
|
|
|
|
RefPtr<StreamHelper> helper = new StreamHelper(mFileStream, callback);
|
|
helper->AsyncClose();
|
|
} else {
|
|
MOZ_ASSERT(!mFileStreamOpen);
|
|
|
|
mDirectoryLock = nullptr;
|
|
mFileStream = nullptr;
|
|
}
|
|
|
|
ConnectionOperationBase::Cleanup();
|
|
}
|
|
|
|
NS_IMPL_ISUPPORTS_INHERITED0(OpenOp, ConnectionOperationBase)
|
|
|
|
NS_IMETHODIMP
|
|
OpenOp::Run()
|
|
{
|
|
nsresult rv;
|
|
|
|
switch (mState) {
|
|
case State::Initial:
|
|
rv = Open();
|
|
break;
|
|
|
|
case State::FinishOpen:
|
|
rv = FinishOpen();
|
|
break;
|
|
|
|
case State::QuotaManagerPending:
|
|
rv = QuotaManagerOpen();
|
|
break;
|
|
|
|
case State::DatabaseWorkOpen:
|
|
rv = DatabaseWork();
|
|
break;
|
|
|
|
case State::SendingResults:
|
|
SendResults();
|
|
return NS_OK;
|
|
|
|
default:
|
|
MOZ_CRASH("Bad state!");
|
|
}
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv)) && mState != State::SendingResults) {
|
|
MaybeSetFailureCode(rv);
|
|
|
|
// Must set mState before dispatching otherwise we will race with the owning
|
|
// thread.
|
|
mState = State::SendingResults;
|
|
|
|
if (IsOnOwningThread()) {
|
|
SendResults();
|
|
} else {
|
|
MOZ_ALWAYS_SUCCEEDS(
|
|
OwningEventTarget()->Dispatch(this, NS_DISPATCH_NORMAL));
|
|
}
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
OpenOp::DirectoryLockAcquired(DirectoryLock* aLock)
|
|
{
|
|
AssertIsOnOwningThread();
|
|
MOZ_ASSERT(mState == State::DirectoryOpenPending);
|
|
MOZ_ASSERT(!mDirectoryLock);
|
|
|
|
mDirectoryLock = aLock;
|
|
|
|
nsresult rv = SendToIOThread();
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
MaybeSetFailureCode(rv);
|
|
|
|
// The caller holds a strong reference to us, no need for a self reference
|
|
// before calling Run().
|
|
|
|
mState = State::SendingResults;
|
|
MOZ_ALWAYS_SUCCEEDS(Run());
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
void
|
|
OpenOp::DirectoryLockFailed()
|
|
{
|
|
AssertIsOnOwningThread();
|
|
MOZ_ASSERT(mState == State::DirectoryOpenPending);
|
|
MOZ_ASSERT(!mDirectoryLock);
|
|
|
|
MaybeSetFailureCode(NS_ERROR_FAILURE);
|
|
|
|
// The caller holds a strong reference to us, no need for a self reference
|
|
// before calling Run().
|
|
|
|
mState = State::SendingResults;
|
|
MOZ_ALWAYS_SUCCEEDS(Run());
|
|
}
|
|
|
|
SeekOp::SeekOp(Connection* aConnection,
|
|
const SDBRequestParams& aParams)
|
|
: ConnectionOperationBase(aConnection)
|
|
, mParams(aParams.get_SDBRequestSeekParams())
|
|
{
|
|
MOZ_ASSERT(aParams.type() == SDBRequestParams::TSDBRequestSeekParams);
|
|
}
|
|
|
|
nsresult
|
|
SeekOp::DoDatabaseWork(nsIFileStream* aFileStream)
|
|
{
|
|
AssertIsOnIOThread();
|
|
MOZ_ASSERT(aFileStream);
|
|
|
|
nsCOMPtr<nsISeekableStream> seekableStream = do_QueryInterface(aFileStream);
|
|
MOZ_ASSERT(seekableStream);
|
|
|
|
nsresult rv = seekableStream->Seek(nsISeekableStream::NS_SEEK_SET,
|
|
mParams.offset());
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
SeekOp::GetResponse(SDBRequestResponse& aResponse)
|
|
{
|
|
aResponse = SDBRequestSeekResponse();
|
|
}
|
|
|
|
ReadOp::ReadOp(Connection* aConnection,
|
|
const SDBRequestParams& aParams)
|
|
: ConnectionOperationBase(aConnection)
|
|
, mParams(aParams.get_SDBRequestReadParams())
|
|
{
|
|
MOZ_ASSERT(aParams.type() == SDBRequestParams::TSDBRequestReadParams);
|
|
}
|
|
|
|
bool
|
|
ReadOp::Init()
|
|
{
|
|
AssertIsOnOwningThread();
|
|
|
|
if (NS_WARN_IF(!ConnectionOperationBase::Init())) {
|
|
return false;
|
|
}
|
|
|
|
mOutputStream = MemoryOutputStream::Create(mParams.size());
|
|
if (NS_WARN_IF(!mOutputStream)) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
nsresult
|
|
ReadOp::DoDatabaseWork(nsIFileStream* aFileStream)
|
|
{
|
|
AssertIsOnIOThread();
|
|
MOZ_ASSERT(aFileStream);
|
|
|
|
nsCOMPtr<nsIInputStream> inputStream = do_QueryInterface(aFileStream);
|
|
MOZ_ASSERT(inputStream);
|
|
|
|
nsresult rv;
|
|
|
|
uint64_t offset = 0;
|
|
|
|
do {
|
|
char copyBuffer[kCopyBufferSize];
|
|
|
|
uint64_t max = mParams.size() - offset;
|
|
if (max == 0) {
|
|
break;
|
|
}
|
|
|
|
uint32_t count = sizeof(copyBuffer);
|
|
if (count > max) {
|
|
count = max;
|
|
}
|
|
|
|
uint32_t numRead;
|
|
rv = inputStream->Read(copyBuffer, count, &numRead);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
|
|
if (!numRead) {
|
|
break;
|
|
}
|
|
|
|
uint32_t numWrite;
|
|
rv = mOutputStream->Write(copyBuffer, numRead, &numWrite);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
|
|
if (NS_WARN_IF(numWrite != numRead)) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
offset += numWrite;
|
|
} while (true);
|
|
|
|
MOZ_ASSERT(offset == mParams.size());
|
|
|
|
MOZ_ALWAYS_SUCCEEDS(mOutputStream->Close());
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
ReadOp::GetResponse(SDBRequestResponse& aResponse)
|
|
{
|
|
aResponse = SDBRequestReadResponse(mOutputStream->Data());
|
|
}
|
|
|
|
WriteOp::WriteOp(Connection* aConnection,
|
|
const SDBRequestParams& aParams)
|
|
: ConnectionOperationBase(aConnection)
|
|
, mParams(aParams.get_SDBRequestWriteParams())
|
|
, mSize(0)
|
|
{
|
|
MOZ_ASSERT(aParams.type() == SDBRequestParams::TSDBRequestWriteParams);
|
|
}
|
|
|
|
bool
|
|
WriteOp::Init()
|
|
{
|
|
AssertIsOnOwningThread();
|
|
|
|
if (NS_WARN_IF(!ConnectionOperationBase::Init())) {
|
|
return false;
|
|
}
|
|
|
|
const nsCString& string = mParams.data();
|
|
|
|
nsCOMPtr<nsIInputStream> inputStream;
|
|
nsresult rv =
|
|
NS_NewCStringInputStream(getter_AddRefs(inputStream), string);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return false;
|
|
}
|
|
|
|
mInputStream = std::move(inputStream);
|
|
mSize = string.Length();
|
|
|
|
return true;
|
|
}
|
|
|
|
nsresult
|
|
WriteOp::DoDatabaseWork(nsIFileStream* aFileStream)
|
|
{
|
|
AssertIsOnIOThread();
|
|
MOZ_ASSERT(aFileStream);
|
|
|
|
nsCOMPtr<nsIOutputStream> outputStream = do_QueryInterface(aFileStream);
|
|
MOZ_ASSERT(outputStream);
|
|
|
|
nsresult rv;
|
|
|
|
do {
|
|
char copyBuffer[kCopyBufferSize];
|
|
|
|
uint32_t numRead;
|
|
rv = mInputStream->Read(copyBuffer, sizeof(copyBuffer), &numRead);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
break;
|
|
}
|
|
|
|
if (!numRead) {
|
|
break;
|
|
}
|
|
|
|
uint32_t numWrite;
|
|
rv = outputStream->Write(copyBuffer, numRead, &numWrite);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
|
|
if (NS_WARN_IF(numWrite != numRead)) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
} while (true);
|
|
|
|
MOZ_ALWAYS_SUCCEEDS(mInputStream->Close());
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
WriteOp::GetResponse(SDBRequestResponse& aResponse)
|
|
{
|
|
aResponse = SDBRequestWriteResponse();
|
|
}
|
|
|
|
CloseOp::CloseOp(Connection* aConnection)
|
|
: ConnectionOperationBase(aConnection)
|
|
{ }
|
|
|
|
nsresult
|
|
CloseOp::DoDatabaseWork(nsIFileStream* aFileStream)
|
|
{
|
|
AssertIsOnIOThread();
|
|
MOZ_ASSERT(aFileStream);
|
|
|
|
nsCOMPtr<nsIInputStream> inputStream = do_QueryInterface(aFileStream);
|
|
MOZ_ASSERT(inputStream);
|
|
|
|
nsresult rv = inputStream->Close();
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
CloseOp::GetResponse(SDBRequestResponse& aResponse)
|
|
{
|
|
aResponse = SDBRequestCloseResponse();
|
|
}
|
|
|
|
void
|
|
CloseOp::OnSuccess()
|
|
{
|
|
AssertIsOnOwningThread();
|
|
|
|
GetConnection()->OnClose();
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* QuotaClient
|
|
******************************************************************************/
|
|
|
|
QuotaClient* QuotaClient::sInstance = nullptr;
|
|
|
|
QuotaClient::QuotaClient()
|
|
: mShutdownRequested(false)
|
|
{
|
|
AssertIsOnBackgroundThread();
|
|
MOZ_ASSERT(!sInstance, "We expect this to be a singleton!");
|
|
|
|
sInstance = this;
|
|
}
|
|
|
|
QuotaClient::~QuotaClient()
|
|
{
|
|
AssertIsOnBackgroundThread();
|
|
MOZ_ASSERT(sInstance == this, "We expect this to be a singleton!");
|
|
|
|
sInstance = nullptr;
|
|
}
|
|
|
|
mozilla::dom::quota::Client::Type
|
|
QuotaClient::GetType()
|
|
{
|
|
return QuotaClient::SDB;
|
|
}
|
|
|
|
nsresult
|
|
QuotaClient::InitOrigin(PersistenceType aPersistenceType,
|
|
const nsACString& aGroup,
|
|
const nsACString& aOrigin,
|
|
const AtomicBool& aCanceled,
|
|
UsageInfo* aUsageInfo)
|
|
{
|
|
AssertIsOnIOThread();
|
|
|
|
if (!aUsageInfo) {
|
|
return NS_OK;
|
|
}
|
|
|
|
return GetUsageForOrigin(aPersistenceType,
|
|
aGroup,
|
|
aOrigin,
|
|
aCanceled,
|
|
aUsageInfo);
|
|
}
|
|
|
|
nsresult
|
|
QuotaClient::GetUsageForOrigin(PersistenceType aPersistenceType,
|
|
const nsACString& aGroup,
|
|
const nsACString& aOrigin,
|
|
const AtomicBool& aCanceled,
|
|
UsageInfo* aUsageInfo)
|
|
{
|
|
AssertIsOnIOThread();
|
|
MOZ_ASSERT(aUsageInfo);
|
|
|
|
QuotaManager* quotaManager = QuotaManager::Get();
|
|
MOZ_ASSERT(quotaManager);
|
|
|
|
nsCOMPtr<nsIFile> directory;
|
|
nsresult rv = quotaManager->GetDirectoryForOrigin(aPersistenceType, aOrigin,
|
|
getter_AddRefs(directory));
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
|
|
MOZ_ASSERT(directory);
|
|
|
|
rv = directory->Append(NS_LITERAL_STRING(SDB_DIRECTORY_NAME));
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
|
|
DebugOnly<bool> exists;
|
|
MOZ_ASSERT(NS_SUCCEEDED(directory->Exists(&exists)) && exists);
|
|
|
|
nsCOMPtr<nsIDirectoryEnumerator> entries;
|
|
rv = directory->GetDirectoryEntries(getter_AddRefs(entries));
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
|
|
bool hasMore;
|
|
while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) &&
|
|
hasMore && !aCanceled) {
|
|
nsCOMPtr<nsISupports> entry;
|
|
rv = entries->GetNext(getter_AddRefs(entry));
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
|
|
nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
|
|
MOZ_ASSERT(file);
|
|
|
|
int64_t fileSize;
|
|
rv = file->GetFileSize(&fileSize);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
|
|
MOZ_ASSERT(fileSize >= 0);
|
|
|
|
aUsageInfo->AppendToDatabaseUsage(uint64_t(fileSize));
|
|
}
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return rv;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
QuotaClient::OnOriginClearCompleted(PersistenceType aPersistenceType,
|
|
const nsACString& aOrigin)
|
|
{
|
|
AssertIsOnIOThread();
|
|
}
|
|
|
|
void
|
|
QuotaClient::ReleaseIOThreadObjects()
|
|
{
|
|
AssertIsOnIOThread();
|
|
}
|
|
|
|
void
|
|
QuotaClient::AbortOperations(const nsACString& aOrigin)
|
|
{
|
|
AssertIsOnBackgroundThread();
|
|
|
|
if (gOpenConnections) {
|
|
for (Connection* connection : *gOpenConnections) {
|
|
if (aOrigin.IsVoid() || connection->Origin() == aOrigin) {
|
|
connection->AllowToClose();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
QuotaClient::AbortOperationsForProcess(ContentParentId aContentParentId)
|
|
{
|
|
AssertIsOnBackgroundThread();
|
|
}
|
|
|
|
void
|
|
QuotaClient::StartIdleMaintenance()
|
|
{
|
|
AssertIsOnBackgroundThread();
|
|
}
|
|
|
|
void
|
|
QuotaClient::StopIdleMaintenance()
|
|
{
|
|
AssertIsOnBackgroundThread();
|
|
}
|
|
|
|
void
|
|
QuotaClient::ShutdownWorkThreads()
|
|
{
|
|
AssertIsOnBackgroundThread();
|
|
MOZ_ASSERT(!mShutdownRequested);
|
|
|
|
mShutdownRequested = true;
|
|
|
|
if (gOpenConnections) {
|
|
for (Connection* connection : *gOpenConnections) {
|
|
connection->AllowToClose();
|
|
}
|
|
|
|
MOZ_ALWAYS_TRUE(SpinEventLoopUntil([&]() { return !gOpenConnections; }));
|
|
}
|
|
}
|
|
|
|
} // namespace dom
|
|
} // namespace mozilla
|