Files
tubestation/dom/workers/DataStore.cpp

713 lines
21 KiB
C++

/* 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 "DataStore.h"
#include "DataStoreCursor.h"
#include "mozilla/dom/DataStore.h"
#include "mozilla/dom/DataStoreCursor.h"
#include "mozilla/dom/DataStoreBinding.h"
#include "mozilla/dom/DataStoreImplBinding.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/PromiseWorkerProxy.h"
#include "mozilla/ErrorResult.h"
#include "WorkerPrivate.h"
#include "WorkerRunnable.h"
#include "WorkerScope.h"
BEGIN_WORKERS_NAMESPACE
NS_IMPL_ADDREF_INHERITED(WorkerDataStore, DOMEventTargetHelper)
NS_IMPL_RELEASE_INHERITED(WorkerDataStore, DOMEventTargetHelper)
NS_INTERFACE_MAP_BEGIN(WorkerDataStore)
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
WorkerDataStore::WorkerDataStore(WorkerGlobalScope* aScope)
: DOMEventTargetHelper(aScope)
{}
already_AddRefed<WorkerDataStore>
WorkerDataStore::Constructor(GlobalObject& aGlobal, ErrorResult& aRv)
{
JSContext* cx = aGlobal.GetContext();
WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(cx);
MOZ_ASSERT(workerPrivate);
workerPrivate->AssertIsOnWorkerThread();
nsRefPtr<WorkerDataStore> store =
new WorkerDataStore(workerPrivate->GlobalScope());
return store.forget();
}
JSObject*
WorkerDataStore::WrapObject(JSContext* aCx)
{
return DataStoreBinding_workers::Wrap(aCx, this);
}
// A WorkerMainThreadRunnable which holds a reference to WorkerDataStore.
class DataStoreRunnable : public WorkerMainThreadRunnable
{
protected:
nsMainThreadPtrHandle<DataStore> mBackingStore;
public:
DataStoreRunnable(WorkerPrivate* aWorkerPrivate,
const nsMainThreadPtrHandle<DataStore>& aBackingStore)
: WorkerMainThreadRunnable(aWorkerPrivate)
, mBackingStore(aBackingStore)
{
MOZ_ASSERT(aWorkerPrivate);
aWorkerPrivate->AssertIsOnWorkerThread();
}
};
// A DataStoreRunnable to run:
// - DataStore::GetName(...)
// - DataStore::GetOwner(...)
// - DataStore::GetRevisionId(...)
// on the main thread.
class DataStoreGetStringRunnable MOZ_FINAL : public DataStoreRunnable
{
typedef void
(DataStore::*FuncType)(nsAString&, ErrorResult&);
FuncType mFunc;
nsAString& mString;
ErrorResult& mRv;
public:
DataStoreGetStringRunnable(WorkerPrivate* aWorkerPrivate,
const nsMainThreadPtrHandle<DataStore>& aBackingStore,
FuncType aFunc,
nsAString& aString,
ErrorResult& aRv)
: DataStoreRunnable(aWorkerPrivate, aBackingStore)
, mFunc(aFunc)
, mString(aString)
, mRv(aRv)
{
MOZ_ASSERT(aWorkerPrivate);
aWorkerPrivate->AssertIsOnWorkerThread();
}
protected:
virtual bool
MainThreadRun() MOZ_OVERRIDE
{
AssertIsOnMainThread();
nsString string;
(mBackingStore.get()->*mFunc)(string, mRv);
mString.Assign(string);
return true;
}
};
// A DataStoreRunnable to run DataStore::GetReadOnly(...) on the main
// thread.
class DataStoreGetReadOnlyRunnable MOZ_FINAL : public DataStoreRunnable
{
ErrorResult& mRv;
public:
bool mReadOnly;
public:
DataStoreGetReadOnlyRunnable(WorkerPrivate* aWorkerPrivate,
const nsMainThreadPtrHandle<DataStore>& aBackingStore,
ErrorResult& aRv)
: DataStoreRunnable(aWorkerPrivate, aBackingStore)
, mRv(aRv)
{
MOZ_ASSERT(aWorkerPrivate);
aWorkerPrivate->AssertIsOnWorkerThread();
}
protected:
virtual bool
MainThreadRun() MOZ_OVERRIDE
{
AssertIsOnMainThread();
mReadOnly = mBackingStore->GetReadOnly(mRv);
return true;
}
};
// A DataStoreRunnable to run DataStore::Get(...) on the main thread.
class DataStoreGetRunnable MOZ_FINAL : public DataStoreRunnable
{
nsRefPtr<PromiseWorkerProxy> mPromiseWorkerProxy;
Sequence<OwningStringOrUnsignedLong> mId;
ErrorResult& mRv;
public:
DataStoreGetRunnable(WorkerPrivate* aWorkerPrivate,
const nsMainThreadPtrHandle<DataStore>& aBackingStore,
Promise* aWorkerPromise,
const Sequence<OwningStringOrUnsignedLong>& aId,
ErrorResult& aRv)
: DataStoreRunnable(aWorkerPrivate, aBackingStore)
, mRv(aRv)
{
MOZ_ASSERT(aWorkerPrivate);
aWorkerPrivate->AssertIsOnWorkerThread();
if (!mId.AppendElements(aId)) {
mRv.Throw(NS_ERROR_OUT_OF_MEMORY);
}
mPromiseWorkerProxy =
new PromiseWorkerProxy(aWorkerPrivate, aWorkerPromise);
}
protected:
virtual bool
MainThreadRun() MOZ_OVERRIDE
{
AssertIsOnMainThread();
nsRefPtr<Promise> promise = mBackingStore->Get(mId, mRv);
promise->AppendNativeHandler(mPromiseWorkerProxy);
return true;
}
};
// A DataStoreRunnable to run DataStore::Put(...) on the main thread.
class DataStorePutRunnable MOZ_FINAL : public DataStoreRunnable
{
nsRefPtr<PromiseWorkerProxy> mPromiseWorkerProxy;
JSAutoStructuredCloneBuffer mObjBuffer;
const StringOrUnsignedLong& mId;
const nsString mRevisionId;
ErrorResult& mRv;
public:
DataStorePutRunnable(WorkerPrivate* aWorkerPrivate,
const nsMainThreadPtrHandle<DataStore>& aBackingStore,
Promise* aWorkerPromise,
JSContext* aCx,
JS::Handle<JS::Value> aObj,
const StringOrUnsignedLong& aId,
const nsAString& aRevisionId,
ErrorResult& aRv)
: DataStoreRunnable(aWorkerPrivate, aBackingStore)
, mId(aId)
, mRevisionId(aRevisionId)
, mRv(aRv)
{
MOZ_ASSERT(aWorkerPrivate);
aWorkerPrivate->AssertIsOnWorkerThread();
// This needs to be structured cloned while it's still on the worker thread.
if (!mObjBuffer.write(aCx, aObj)) {
JS_ClearPendingException(aCx);
mRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
}
mPromiseWorkerProxy =
new PromiseWorkerProxy(aWorkerPrivate, aWorkerPromise);
}
protected:
virtual bool
MainThreadRun() MOZ_OVERRIDE
{
AssertIsOnMainThread();
// Get the JSContext for the target window
nsCOMPtr<nsIScriptGlobalObject> sgo =
do_QueryInterface(static_cast<DOMEventTargetHelper*>
(mBackingStore.get())->GetOwner());
MOZ_ASSERT(sgo);
nsCOMPtr<nsIScriptContext> scriptContext = sgo->GetContext();
AutoPushJSContext cx(scriptContext ? scriptContext->GetNativeContext()
: nsContentUtils::GetSafeJSContext());
MOZ_ASSERT(cx);
JS::Rooted<JS::Value> value(cx);
if (!mObjBuffer.read(cx, &value)) {
JS_ClearPendingException(cx);
mRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
return true;
}
nsRefPtr<Promise> promise = mBackingStore->Put(cx,
value,
mId,
mRevisionId,
mRv);
promise->AppendNativeHandler(mPromiseWorkerProxy);
return true;
}
};
// A DataStoreRunnable to run DataStore::Add(...) on the main thread.
class DataStoreAddRunnable MOZ_FINAL : public DataStoreRunnable
{
nsRefPtr<PromiseWorkerProxy> mPromiseWorkerProxy;
JSAutoStructuredCloneBuffer mObjBuffer;
const Optional<StringOrUnsignedLong>& mId;
const nsString mRevisionId;
ErrorResult& mRv;
public:
DataStoreAddRunnable(WorkerPrivate* aWorkerPrivate,
const nsMainThreadPtrHandle<DataStore>& aBackingStore,
Promise* aWorkerPromise,
JSContext* aCx,
JS::Handle<JS::Value> aObj,
const Optional<StringOrUnsignedLong>& aId,
const nsAString& aRevisionId,
ErrorResult& aRv)
: DataStoreRunnable(aWorkerPrivate, aBackingStore)
, mId(aId)
, mRevisionId(aRevisionId)
, mRv(aRv)
{
MOZ_ASSERT(aWorkerPrivate);
aWorkerPrivate->AssertIsOnWorkerThread();
// This needs to be structured cloned while it's still on the worker thread.
if (!mObjBuffer.write(aCx, aObj)) {
JS_ClearPendingException(aCx);
mRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
}
mPromiseWorkerProxy =
new PromiseWorkerProxy(aWorkerPrivate, aWorkerPromise);
}
protected:
virtual bool
MainThreadRun() MOZ_OVERRIDE
{
AssertIsOnMainThread();
// Get the JSContext for the target window
nsCOMPtr<nsIScriptGlobalObject> sgo =
do_QueryInterface(static_cast<DOMEventTargetHelper*>
(mBackingStore.get())->GetOwner());
MOZ_ASSERT(sgo);
nsCOMPtr<nsIScriptContext> scriptContext = sgo->GetContext();
AutoPushJSContext cx(scriptContext ? scriptContext->GetNativeContext()
: nsContentUtils::GetSafeJSContext());
MOZ_ASSERT(cx);
JS::Rooted<JS::Value> value(cx);
if (!mObjBuffer.read(cx, &value)) {
JS_ClearPendingException(cx);
mRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
return true;
}
nsRefPtr<Promise> promise = mBackingStore->Add(cx,
value,
mId,
mRevisionId,
mRv);
promise->AppendNativeHandler(mPromiseWorkerProxy);
return true;
}
};
// A DataStoreRunnable to run DataStore::Remove(...) on the main
// thread.
class DataStoreRemoveRunnable MOZ_FINAL : public DataStoreRunnable
{
nsRefPtr<PromiseWorkerProxy> mPromiseWorkerProxy;
const StringOrUnsignedLong& mId;
const nsString mRevisionId;
ErrorResult& mRv;
public:
DataStoreRemoveRunnable(WorkerPrivate* aWorkerPrivate,
const nsMainThreadPtrHandle<DataStore>& aBackingStore,
Promise* aWorkerPromise,
const StringOrUnsignedLong& aId,
const nsAString& aRevisionId,
ErrorResult& aRv)
: DataStoreRunnable(aWorkerPrivate, aBackingStore)
, mId(aId)
, mRevisionId(aRevisionId)
, mRv(aRv)
{
MOZ_ASSERT(aWorkerPrivate);
aWorkerPrivate->AssertIsOnWorkerThread();
mPromiseWorkerProxy =
new PromiseWorkerProxy(aWorkerPrivate, aWorkerPromise);
}
protected:
virtual bool
MainThreadRun() MOZ_OVERRIDE
{
AssertIsOnMainThread();
nsRefPtr<Promise> promise = mBackingStore->Remove(mId, mRevisionId, mRv);
promise->AppendNativeHandler(mPromiseWorkerProxy);
return true;
}
};
// A DataStoreRunnable to run DataStore::Sync(...) on the main thread.
class DataStoreSyncStoreRunnable MOZ_FINAL : public DataStoreRunnable
{
WorkerDataStoreCursor* mWorkerCursor;
const nsString mRevisionId;
ErrorResult& mRv;
public:
DataStoreSyncStoreRunnable(WorkerPrivate* aWorkerPrivate,
const nsMainThreadPtrHandle<DataStore>& aBackingStore,
WorkerDataStoreCursor* aWorkerCursor,
const nsAString& aRevisionId,
ErrorResult& aRv)
: DataStoreRunnable(aWorkerPrivate, aBackingStore)
, mWorkerCursor(aWorkerCursor)
, mRevisionId(aRevisionId)
, mRv(aRv)
{
MOZ_ASSERT(aWorkerPrivate);
aWorkerPrivate->AssertIsOnWorkerThread();
}
protected:
virtual bool
MainThreadRun() MOZ_OVERRIDE
{
AssertIsOnMainThread();
// Point WorkerDataStoreCursor to DataStoreCursor.
nsRefPtr<DataStoreCursor> cursor = mBackingStore->Sync(mRevisionId, mRv);
nsMainThreadPtrHandle<DataStoreCursor> backingCursor =
new nsMainThreadPtrHolder<DataStoreCursor>(cursor);
mWorkerCursor->SetBackingDataStoreCursor(backingCursor);
return true;
}
};
void
WorkerDataStore::GetName(JSContext* aCx, nsAString& aName, ErrorResult& aRv)
{
WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx);
MOZ_ASSERT(workerPrivate);
workerPrivate->AssertIsOnWorkerThread();
nsRefPtr<DataStoreGetStringRunnable> runnable =
new DataStoreGetStringRunnable(workerPrivate,
mBackingStore,
&DataStore::GetName,
aName,
aRv);
runnable->Dispatch(aCx);
}
void
WorkerDataStore::GetOwner(JSContext* aCx, nsAString& aOwner, ErrorResult& aRv)
{
WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx);
MOZ_ASSERT(workerPrivate);
workerPrivate->AssertIsOnWorkerThread();
nsRefPtr<DataStoreGetStringRunnable> runnable =
new DataStoreGetStringRunnable(workerPrivate,
mBackingStore,
&DataStore::GetOwner,
aOwner,
aRv);
runnable->Dispatch(aCx);
}
bool
WorkerDataStore::GetReadOnly(JSContext* aCx, ErrorResult& aRv)
{
WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx);
MOZ_ASSERT(workerPrivate);
workerPrivate->AssertIsOnWorkerThread();
nsRefPtr<DataStoreGetReadOnlyRunnable> runnable =
new DataStoreGetReadOnlyRunnable(workerPrivate, mBackingStore, aRv);
runnable->Dispatch(aCx);
return runnable->mReadOnly;
}
already_AddRefed<Promise>
WorkerDataStore::Get(JSContext* aCx,
const Sequence<OwningStringOrUnsignedLong>& aId,
ErrorResult& aRv)
{
WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx);
MOZ_ASSERT(workerPrivate);
workerPrivate->AssertIsOnWorkerThread();
nsRefPtr<Promise> promise = new Promise(workerPrivate->GlobalScope());
nsRefPtr<DataStoreGetRunnable> runnable =
new DataStoreGetRunnable(workerPrivate,
mBackingStore,
promise,
aId,
aRv);
runnable->Dispatch(aCx);
return promise.forget();
}
already_AddRefed<Promise>
WorkerDataStore::Put(JSContext* aCx,
JS::Handle<JS::Value> aObj,
const StringOrUnsignedLong& aId,
const nsAString& aRevisionId,
ErrorResult& aRv)
{
WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx);
MOZ_ASSERT(workerPrivate);
workerPrivate->AssertIsOnWorkerThread();
nsRefPtr<Promise> promise = new Promise(workerPrivate->GlobalScope());
nsRefPtr<DataStorePutRunnable> runnable =
new DataStorePutRunnable(workerPrivate,
mBackingStore,
promise,
aCx,
aObj,
aId,
aRevisionId,
aRv);
runnable->Dispatch(aCx);
return promise.forget();
}
already_AddRefed<Promise>
WorkerDataStore::Add(JSContext* aCx,
JS::Handle<JS::Value> aObj,
const Optional<StringOrUnsignedLong>& aId,
const nsAString& aRevisionId,
ErrorResult& aRv)
{
WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx);
MOZ_ASSERT(workerPrivate);
workerPrivate->AssertIsOnWorkerThread();
nsRefPtr<Promise> promise = new Promise(workerPrivate->GlobalScope());
nsRefPtr<DataStoreAddRunnable> runnable =
new DataStoreAddRunnable(workerPrivate,
mBackingStore,
promise,
aCx,
aObj,
aId,
aRevisionId,
aRv);
runnable->Dispatch(aCx);
return promise.forget();
}
already_AddRefed<Promise>
WorkerDataStore::Remove(JSContext* aCx,
const StringOrUnsignedLong& aId,
const nsAString& aRevisionId,
ErrorResult& aRv)
{
WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx);
MOZ_ASSERT(workerPrivate);
workerPrivate->AssertIsOnWorkerThread();
nsRefPtr<Promise> promise = new Promise(workerPrivate->GlobalScope());
nsRefPtr<DataStoreRemoveRunnable> runnable =
new DataStoreRemoveRunnable(workerPrivate,
mBackingStore,
promise,
aId,
aRevisionId,
aRv);
runnable->Dispatch(aCx);
return promise.forget();
}
// A DataStoreRunnable to run DataStore::Clear(...) on the main thread.
class DataStoreClearRunnable MOZ_FINAL : public DataStoreRunnable
{
nsRefPtr<PromiseWorkerProxy> mPromiseWorkerProxy;
const nsString mRevisionId;
ErrorResult& mRv;
public:
DataStoreClearRunnable(WorkerPrivate* aWorkerPrivate,
const nsMainThreadPtrHandle<DataStore>& aBackingStore,
Promise* aWorkerPromise,
const nsAString& aRevisionId,
ErrorResult& aRv)
: DataStoreRunnable(aWorkerPrivate, aBackingStore)
, mRevisionId(aRevisionId)
, mRv(aRv)
{
MOZ_ASSERT(aWorkerPrivate);
aWorkerPrivate->AssertIsOnWorkerThread();
mPromiseWorkerProxy =
new PromiseWorkerProxy(aWorkerPrivate, aWorkerPromise);
}
protected:
virtual bool
MainThreadRun() MOZ_OVERRIDE
{
AssertIsOnMainThread();
nsRefPtr<Promise> promise = mBackingStore->Clear(mRevisionId, mRv);
promise->AppendNativeHandler(mPromiseWorkerProxy);
return true;
}
};
already_AddRefed<Promise>
WorkerDataStore::Clear(JSContext* aCx,
const nsAString& aRevisionId,
ErrorResult& aRv)
{
WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx);
MOZ_ASSERT(workerPrivate);
workerPrivate->AssertIsOnWorkerThread();
nsRefPtr<Promise> promise = new Promise(workerPrivate->GlobalScope());
nsRefPtr<DataStoreClearRunnable> runnable =
new DataStoreClearRunnable(workerPrivate,
mBackingStore,
promise,
aRevisionId,
aRv);
runnable->Dispatch(aCx);
return promise.forget();
}
void
WorkerDataStore::GetRevisionId(JSContext* aCx,
nsAString& aRevisionId,
ErrorResult& aRv)
{
WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx);
MOZ_ASSERT(workerPrivate);
workerPrivate->AssertIsOnWorkerThread();
nsRefPtr<DataStoreGetStringRunnable> runnable =
new DataStoreGetStringRunnable(workerPrivate,
mBackingStore,
&DataStore::GetRevisionId,
aRevisionId,
aRv);
runnable->Dispatch(aCx);
}
// A DataStoreRunnable to run DataStore::GetLength(...) on the main thread.
class DataStoreGetLengthRunnable MOZ_FINAL : public DataStoreRunnable
{
nsRefPtr<PromiseWorkerProxy> mPromiseWorkerProxy;
ErrorResult& mRv;
public:
DataStoreGetLengthRunnable(WorkerPrivate* aWorkerPrivate,
const nsMainThreadPtrHandle<DataStore>& aBackingStore,
Promise* aWorkerPromise,
ErrorResult& aRv)
: DataStoreRunnable(aWorkerPrivate, aBackingStore)
, mRv(aRv)
{
MOZ_ASSERT(aWorkerPrivate);
aWorkerPrivate->AssertIsOnWorkerThread();
mPromiseWorkerProxy =
new PromiseWorkerProxy(aWorkerPrivate, aWorkerPromise);
}
protected:
virtual bool
MainThreadRun() MOZ_OVERRIDE
{
AssertIsOnMainThread();
nsRefPtr<Promise> promise = mBackingStore->GetLength(mRv);
promise->AppendNativeHandler(mPromiseWorkerProxy);
return true;
}
};
already_AddRefed<Promise>
WorkerDataStore::GetLength(JSContext* aCx, ErrorResult& aRv)
{
WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx);
MOZ_ASSERT(workerPrivate);
workerPrivate->AssertIsOnWorkerThread();
nsRefPtr<Promise> promise = new Promise(workerPrivate->GlobalScope());
nsRefPtr<DataStoreGetLengthRunnable> runnable =
new DataStoreGetLengthRunnable(workerPrivate,
mBackingStore,
promise,
aRv);
runnable->Dispatch(aCx);
return promise.forget();
}
already_AddRefed<WorkerDataStoreCursor>
WorkerDataStore::Sync(JSContext* aCx,
const nsAString& aRevisionId,
ErrorResult& aRv)
{
// Create a WorkerDataStoreCursor on the worker. DataStoreSyncStoreRunnable
// will point that to the DataStoreCursor created on the main thread.
nsRefPtr<WorkerDataStoreCursor> workerCursor = new WorkerDataStoreCursor();
WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx);
MOZ_ASSERT(workerPrivate);
workerPrivate->AssertIsOnWorkerThread();
nsRefPtr<DataStoreSyncStoreRunnable> runnable =
new DataStoreSyncStoreRunnable(workerPrivate,
mBackingStore,
workerCursor,
aRevisionId,
aRv);
runnable->Dispatch(aCx);
return workerCursor.forget();
}
void
WorkerDataStore::SetDataStoreImpl(DataStoreImpl& aStore, ErrorResult& aRv)
{
NS_NOTREACHED("We don't use this for the WorkerDataStore!");
}
void
WorkerDataStore::SetBackingDataStore(
const nsMainThreadPtrHandle<DataStore>& aBackingStore)
{
mBackingStore = aBackingStore;
}
// TODO How to handle the event? Will fix this in my later patch.
END_WORKERS_NAMESPACE