mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-25 22:01:30 +00:00
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:
parent
5679c542e5
commit
8e6024c408
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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));
|
||||
|
Loading…
Reference in New Issue
Block a user