Bug 949325 - C++ wrapper to support DataStore API on the worker (part 2-5, a proxy to dispatch the change event on workers). r=khuey

This commit is contained in:
Gene Lian 2014-02-24 21:57:34 +08:00
parent 5679c542e5
commit 8e6024c408
4 changed files with 286 additions and 3 deletions

View File

@ -7,6 +7,7 @@
#include "mozilla/dom/DataStore.h"
#include "mozilla/dom/DataStoreCursor.h"
#include "mozilla/dom/DataStoreChangeEvent.h"
#include "mozilla/dom/DataStoreBinding.h"
#include "mozilla/dom/DataStoreImplBinding.h"
@ -707,6 +708,182 @@ WorkerDataStore::SetBackingDataStore(
mBackingStore = aBackingStore;
}
// TODO How to handle the event? Will fix this in my later patch.
void
WorkerDataStore::SetDataStoreChangeEventProxy(
DataStoreChangeEventProxy* aEventProxy)
{
mEventProxy = aEventProxy;
}
// A WorkerRunnable to dispatch the DataStoreChangeEvent on the worker thread.
class DispatchDataStoreChangeEventRunnable : public WorkerRunnable
{
public:
DispatchDataStoreChangeEventRunnable(
DataStoreChangeEventProxy* aDataStoreChangeEventProxy,
DataStoreChangeEvent* aEvent)
: WorkerRunnable(aDataStoreChangeEventProxy->GetWorkerPrivate(),
WorkerThreadUnchangedBusyCount)
, mDataStoreChangeEventProxy(aDataStoreChangeEventProxy)
{
AssertIsOnMainThread();
MOZ_ASSERT(mDataStoreChangeEventProxy);
aEvent->GetRevisionId(mRevisionId);
aEvent->GetId(mId);
aEvent->GetOperation(mOperation);
aEvent->GetOwner(mOwner);
}
virtual bool
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
{
MOZ_ASSERT(aWorkerPrivate);
aWorkerPrivate->AssertIsOnWorkerThread();
MOZ_ASSERT(aWorkerPrivate == mWorkerPrivate);
MOZ_ASSERT(mDataStoreChangeEventProxy);
nsRefPtr<WorkerDataStore> workerStore =
mDataStoreChangeEventProxy->GetWorkerStore();
DataStoreChangeEventInit eventInit;
eventInit.mBubbles = false;
eventInit.mCancelable = false;
eventInit.mRevisionId = mRevisionId;
// TODO Bug 981984: OwningStringOrUnsignedLong union value cannot be set if
// the type is not matched.
//
// This is a work-around to clean up the OwningStringOrUnsignedLong value
// initialized by DataStoreChangeEventInit, which will always set |mId| to
// a UnsignedLong type by default (see DataStoreChangeEvent.webidl). This
// will fail the later assignment when the type of value we want to assign
// is actually String.
// eventInit.mId.~OwningStringOrUnsignedLong();
eventInit.mId = mId;
eventInit.mOperation = mOperation;
eventInit.mOwner = mOwner;
nsRefPtr<DataStoreChangeEvent> event =
DataStoreChangeEvent::Constructor(workerStore,
NS_LITERAL_STRING("change"),
eventInit);
workerStore->DispatchDOMEvent(nullptr, event, nullptr, nullptr);
return true;
}
protected:
~DispatchDataStoreChangeEventRunnable()
{}
private:
nsRefPtr<DataStoreChangeEventProxy> mDataStoreChangeEventProxy;
nsString mRevisionId;
Nullable<OwningStringOrUnsignedLong> mId;
nsString mOperation;
nsString mOwner;
};
DataStoreChangeEventProxy::DataStoreChangeEventProxy(
WorkerPrivate* aWorkerPrivate,
WorkerDataStore* aWorkerStore)
: mWorkerPrivate(aWorkerPrivate)
, mWorkerStore(aWorkerStore)
, mCleanedUp(false)
, mCleanUpLock("cleanUpLock")
{
MOZ_ASSERT(mWorkerPrivate);
mWorkerPrivate->AssertIsOnWorkerThread();
MOZ_ASSERT(mWorkerStore);
// Let the WorkerDataStore keep the DataStoreChangeEventProxy alive to catch
// the coming events until the WorkerDataStore is released.
mWorkerStore->SetDataStoreChangeEventProxy(this);
// We do this to make sure the worker thread won't shut down before the event
// is dispatched to the WorkerStore on the worker thread.
if (!mWorkerPrivate->AddFeature(mWorkerPrivate->GetJSContext(), this)) {
MOZ_ASSERT(false, "cannot add the worker feature!");
return;
}
}
WorkerPrivate*
DataStoreChangeEventProxy::GetWorkerPrivate() const
{
// It's ok to race on |mCleanedUp|, because it will never cause us to fire
// the assertion when we should not.
MOZ_ASSERT(!mCleanedUp);
return mWorkerPrivate;
}
WorkerDataStore*
DataStoreChangeEventProxy::GetWorkerStore() const
{
return mWorkerStore;
}
// nsIDOMEventListener implementation.
NS_IMPL_ISUPPORTS(DataStoreChangeEventProxy, nsIDOMEventListener)
NS_IMETHODIMP
DataStoreChangeEventProxy::HandleEvent(nsIDOMEvent* aEvent)
{
AssertIsOnMainThread();
MutexAutoLock lock(mCleanUpLock);
// If the worker thread's been cancelled we don't need to dispatch the event.
if (mCleanedUp) {
return NS_OK;
}
nsRefPtr<DataStoreChangeEvent> event =
static_cast<DataStoreChangeEvent*>(aEvent);
nsRefPtr<DispatchDataStoreChangeEventRunnable> runnable =
new DispatchDataStoreChangeEventRunnable(this, event);
{
AutoSafeJSContext cx;
JSAutoRequest ar(cx);
runnable->Dispatch(cx);
}
return NS_OK;
}
// WorkerFeature implementation.
bool
DataStoreChangeEventProxy::Notify(JSContext* aCx, Status aStatus)
{
MutexAutoLock lock(mCleanUpLock);
// |mWorkerPrivate| might not be safe to use anymore if we have already
// cleaned up and RemoveFeature(), so we need to check |mCleanedUp| first.
if (mCleanedUp) {
return true;
}
MOZ_ASSERT(mWorkerPrivate);
mWorkerPrivate->AssertIsOnWorkerThread();
MOZ_ASSERT(mWorkerPrivate->GetJSContext() == aCx);
// Release the WorkerStore and remove the DataStoreChangeEventProxy from the
// features of the worker thread since the worker thread has been cancelled.
if (aStatus >= Canceling) {
mWorkerStore = nullptr;
mWorkerPrivate->RemoveFeature(aCx, this);
mCleanedUp = true;
}
return true;
}
END_WORKERS_NAMESPACE

View File

@ -8,6 +8,8 @@
#include "mozilla/DOMEventTargetHelper.h"
#include "nsProxyRelease.h"
#include "WorkerFeature.h"
namespace mozilla {
class ErrorResult;
@ -22,6 +24,7 @@ class OwningStringOrUnsignedLong;
namespace workers {
class DataStoreChangeEventProxy;
class WorkerDataStoreCursor;
class WorkerGlobalScope;
@ -88,11 +91,46 @@ public:
void SetBackingDataStore(
const nsMainThreadPtrHandle<DataStore>& aBackingStore);
void SetDataStoreChangeEventProxy(DataStoreChangeEventProxy* aEventProxy);
protected:
virtual ~WorkerDataStore() {}
private:
nsMainThreadPtrHandle<DataStore> mBackingStore;
nsRefPtr<DataStoreChangeEventProxy> mEventProxy;
};
class DataStoreChangeEventProxy MOZ_FINAL : public nsIDOMEventListener
, public WorkerFeature
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIDOMEVENTLISTENER
DataStoreChangeEventProxy(WorkerPrivate* aWorkerPrivate,
WorkerDataStore* aWorkerStore);
WorkerPrivate* GetWorkerPrivate() const;
WorkerDataStore* GetWorkerStore() const;
protected:
// WorkerFeature implementation.
bool Notify(JSContext* aCx, Status aStatus) MOZ_OVERRIDE;
private:
~DataStoreChangeEventProxy() {};
WorkerPrivate* mWorkerPrivate;
nsRefPtr<WorkerDataStore> mWorkerStore;
bool mCleanedUp; // To specify if the worker has been cancelled.
// Ensure the worker and the main thread won't race to access |mCleanedUp|.
Mutex mCleanUpLock;
};
} //namespace workers

View File

@ -57,6 +57,7 @@ class DataStoreCursorGetStoreRunnable MOZ_FINAL : public DataStoreCursorRunnable
{
WorkerDataStore* mWorkerStore;
ErrorResult& mRv;
nsRefPtr<DataStoreChangeEventProxy> mEventProxy;
public:
DataStoreCursorGetStoreRunnable(WorkerPrivate* aWorkerPrivate,
@ -69,6 +70,9 @@ public:
{
MOZ_ASSERT(aWorkerPrivate);
aWorkerPrivate->AssertIsOnWorkerThread();
// When we're on the worker thread, prepare an DataStoreChangeEventProxy.
mEventProxy = new DataStoreChangeEventProxy(aWorkerPrivate, mWorkerStore);
}
protected:
@ -77,8 +81,19 @@ protected:
{
AssertIsOnMainThread();
// Point WorkerDataStore to DataStore.
nsRefPtr<DataStore> store = mBackingCursor->GetStore(mRv);
// Add |mEventProxy| as an event listner to DataStore;
if (NS_FAILED(store->AddEventListener(NS_LITERAL_STRING("change"),
mEventProxy,
false,
false,
2))) {
NS_WARNING("Failed to add event listener!");
return false;
}
// Point WorkerDataStore to DataStore.
nsMainThreadPtrHandle<DataStore> backingStore =
new nsMainThreadPtrHolder<DataStore>(store);
mWorkerStore->SetBackingDataStore(backingStore);

View File

@ -49,6 +49,47 @@ WorkerNavigator::WrapObject(JSContext* aCx)
return WorkerNavigatorBinding_workers::Wrap(aCx, this);
}
// A WorkerMainThreadRunnable to synchronously add DataStoreChangeEventProxy on
// the main thread. We need this because we have to access |mBackingStore| on
// the main thread.
class DataStoreAddEventListenerRunnable : public WorkerMainThreadRunnable
{
nsMainThreadPtrHandle<DataStore> mBackingStore;
DataStoreChangeEventProxy* mEventProxy;
protected:
virtual bool
MainThreadRun() MOZ_OVERRIDE
{
AssertIsOnMainThread();
// Add |mEventProxy| as an event listner to DataStore.
if (NS_FAILED(mBackingStore->AddEventListener(NS_LITERAL_STRING("change"),
mEventProxy,
false,
false,
2))) {
MOZ_ASSERT(false, "failed to add event listener!");
return false;
}
return true;
}
public:
DataStoreAddEventListenerRunnable(
WorkerPrivate* aWorkerPrivate,
const nsMainThreadPtrHandle<DataStore>& aBackingStore,
DataStoreChangeEventProxy* aEventProxy)
: WorkerMainThreadRunnable(aWorkerPrivate)
, mBackingStore(aBackingStore)
, mEventProxy(aEventProxy)
{
MOZ_ASSERT(aWorkerPrivate);
aWorkerPrivate->AssertIsOnWorkerThread();
}
};
#define WORKER_DATA_STORES_TAG JS_SCTAG_USER_MIN
static JSObject*
@ -76,10 +117,22 @@ GetDataStoresStructuredCloneCallbacksRead(JSContext* aCx,
return nullptr;
}
// Point WorkerDataStore to DataStore.
nsRefPtr<WorkerDataStore> workerStore =
new WorkerDataStore(workerPrivate->GlobalScope());
nsMainThreadPtrHandle<DataStore> backingStore = dataStoreholder;
// When we're on the worker thread, prepare a DataStoreChangeEventProxy.
nsRefPtr<DataStoreChangeEventProxy> eventProxy =
new DataStoreChangeEventProxy(workerPrivate, workerStore);
// Add the DataStoreChangeEventProxy as an event listener on the main thread.
nsRefPtr<DataStoreAddEventListenerRunnable> runnable =
new DataStoreAddEventListenerRunnable(workerPrivate,
backingStore,
eventProxy);
runnable->Dispatch(aCx);
// Point WorkerDataStore to DataStore.
workerStore->SetBackingDataStore(backingStore);
JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));