/* 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/DataStoreChangeEvent.h" #include "mozilla/dom/DataStoreBinding.h" #include "mozilla/dom/DataStoreImplBinding.h" #include "mozilla/dom/Promise.h" #include "mozilla/dom/PromiseWorkerProxy.h" #include "mozilla/dom/ScriptSettings.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::Constructor(GlobalObject& aGlobal, ErrorResult& aRv) { // We don't allow Gecko to create WorkerDataStore through JS codes like // window.DataStore() on the worker, so disable this for now. NS_NOTREACHED("Cannot use the chrome constructor on the worker!"); return nullptr; } 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 mBackingStore; public: DataStoreRunnable(WorkerPrivate* aWorkerPrivate, const nsMainThreadPtrHandle& 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& 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& 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 mPromiseWorkerProxy; Sequence mId; ErrorResult& mRv; public: DataStoreGetRunnable(WorkerPrivate* aWorkerPrivate, const nsMainThreadPtrHandle& aBackingStore, Promise* aWorkerPromise, const Sequence& 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 = 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 mPromiseWorkerProxy; JSAutoStructuredCloneBuffer mObjBuffer; const StringOrUnsignedLong& mId; const nsString mRevisionId; ErrorResult& mRv; public: DataStorePutRunnable(WorkerPrivate* aWorkerPrivate, const nsMainThreadPtrHandle& aBackingStore, Promise* aWorkerPromise, JSContext* aCx, JS::Handle 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(); // Initialise an AutoJSAPI with the target window. AutoJSAPI jsapi; if (NS_WARN_IF(!jsapi.Init(mBackingStore->GetParentObject()))) { mRv.Throw(NS_ERROR_UNEXPECTED); return true; } JSContext* cx = jsapi.cx(); JS::Rooted value(cx); if (!mObjBuffer.read(cx, &value)) { JS_ClearPendingException(cx); mRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR); return true; } nsRefPtr 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 mPromiseWorkerProxy; JSAutoStructuredCloneBuffer mObjBuffer; const Optional& mId; const nsString mRevisionId; ErrorResult& mRv; public: DataStoreAddRunnable(WorkerPrivate* aWorkerPrivate, const nsMainThreadPtrHandle& aBackingStore, Promise* aWorkerPromise, JSContext* aCx, JS::Handle aObj, const Optional& 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(); // Initialise an AutoJSAPI with the target window. AutoJSAPI jsapi; if (NS_WARN_IF(!jsapi.Init(mBackingStore->GetParentObject()))) { mRv.Throw(NS_ERROR_UNEXPECTED); return true; } JSContext* cx = jsapi.cx(); JS::Rooted value(cx); if (!mObjBuffer.read(cx, &value)) { JS_ClearPendingException(cx); mRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR); return true; } nsRefPtr 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 mPromiseWorkerProxy; const StringOrUnsignedLong& mId; const nsString mRevisionId; ErrorResult& mRv; public: DataStoreRemoveRunnable(WorkerPrivate* aWorkerPrivate, const nsMainThreadPtrHandle& 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 = mBackingStore->Remove(mId, mRevisionId, mRv); promise->AppendNativeHandler(mPromiseWorkerProxy); return true; } }; // A DataStoreRunnable to run DataStore::Clear(...) on the main thread. class DataStoreClearRunnable MOZ_FINAL : public DataStoreRunnable { nsRefPtr mPromiseWorkerProxy; const nsString mRevisionId; ErrorResult& mRv; public: DataStoreClearRunnable(WorkerPrivate* aWorkerPrivate, const nsMainThreadPtrHandle& 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 = mBackingStore->Clear(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& 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 cursor = mBackingStore->Sync(mRevisionId, mRv); nsMainThreadPtrHandle backingCursor( new nsMainThreadPtrHolder(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 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 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 runnable = new DataStoreGetReadOnlyRunnable(workerPrivate, mBackingStore, aRv); runnable->Dispatch(aCx); return runnable->mReadOnly; } already_AddRefed WorkerDataStore::Get(JSContext* aCx, const Sequence& aId, ErrorResult& aRv) { WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx); MOZ_ASSERT(workerPrivate); workerPrivate->AssertIsOnWorkerThread(); nsRefPtr promise = Promise::Create(workerPrivate->GlobalScope(), aRv); if (aRv.Failed()) { return nullptr; } nsRefPtr runnable = new DataStoreGetRunnable(workerPrivate, mBackingStore, promise, aId, aRv); runnable->Dispatch(aCx); return promise.forget(); } already_AddRefed WorkerDataStore::Put(JSContext* aCx, JS::Handle aObj, const StringOrUnsignedLong& aId, const nsAString& aRevisionId, ErrorResult& aRv) { WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx); MOZ_ASSERT(workerPrivate); workerPrivate->AssertIsOnWorkerThread(); nsRefPtr promise = Promise::Create(workerPrivate->GlobalScope(), aRv); if (aRv.Failed()) { return nullptr; } nsRefPtr runnable = new DataStorePutRunnable(workerPrivate, mBackingStore, promise, aCx, aObj, aId, aRevisionId, aRv); runnable->Dispatch(aCx); return promise.forget(); } already_AddRefed WorkerDataStore::Add(JSContext* aCx, JS::Handle aObj, const Optional& aId, const nsAString& aRevisionId, ErrorResult& aRv) { WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx); MOZ_ASSERT(workerPrivate); workerPrivate->AssertIsOnWorkerThread(); nsRefPtr promise = Promise::Create(workerPrivate->GlobalScope(), aRv); if (aRv.Failed()) { return nullptr; } nsRefPtr runnable = new DataStoreAddRunnable(workerPrivate, mBackingStore, promise, aCx, aObj, aId, aRevisionId, aRv); runnable->Dispatch(aCx); return promise.forget(); } already_AddRefed WorkerDataStore::Remove(JSContext* aCx, const StringOrUnsignedLong& aId, const nsAString& aRevisionId, ErrorResult& aRv) { WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx); MOZ_ASSERT(workerPrivate); workerPrivate->AssertIsOnWorkerThread(); nsRefPtr promise = Promise::Create(workerPrivate->GlobalScope(), aRv); if (aRv.Failed()) { return nullptr; } nsRefPtr runnable = new DataStoreRemoveRunnable(workerPrivate, mBackingStore, promise, aId, aRevisionId, aRv); runnable->Dispatch(aCx); return promise.forget(); } already_AddRefed WorkerDataStore::Clear(JSContext* aCx, const nsAString& aRevisionId, ErrorResult& aRv) { WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx); MOZ_ASSERT(workerPrivate); workerPrivate->AssertIsOnWorkerThread(); nsRefPtr promise = Promise::Create(workerPrivate->GlobalScope(), aRv); if (aRv.Failed()) { return nullptr; } nsRefPtr 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 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 mPromiseWorkerProxy; ErrorResult& mRv; public: DataStoreGetLengthRunnable(WorkerPrivate* aWorkerPrivate, const nsMainThreadPtrHandle& 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 = mBackingStore->GetLength(mRv); promise->AppendNativeHandler(mPromiseWorkerProxy); return true; } }; already_AddRefed WorkerDataStore::GetLength(JSContext* aCx, ErrorResult& aRv) { WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx); MOZ_ASSERT(workerPrivate); workerPrivate->AssertIsOnWorkerThread(); nsRefPtr promise = Promise::Create(workerPrivate->GlobalScope(), aRv); if (aRv.Failed()) { return nullptr; } nsRefPtr runnable = new DataStoreGetLengthRunnable(workerPrivate, mBackingStore, promise, aRv); runnable->Dispatch(aCx); return promise.forget(); } already_AddRefed WorkerDataStore::Sync(JSContext* aCx, const nsAString& aRevisionId, ErrorResult& aRv) { WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx); MOZ_ASSERT(workerPrivate); workerPrivate->AssertIsOnWorkerThread(); // Create a WorkerDataStoreCursor on the worker. Note that we need to pass // this WorkerDataStore into the WorkerDataStoreCursor, so that it can keep // track of which WorkerDataStore owns the WorkerDataStoreCursor. nsRefPtr workerCursor = new WorkerDataStoreCursor(this); // DataStoreSyncStoreRunnable will point the WorkerDataStoreCursor to the // DataStoreCursor created on the main thread. nsRefPtr 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& aBackingStore) { mBackingStore = aBackingStore; } 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 workerStore = mDataStoreChangeEventProxy->GetWorkerStore(); DataStoreChangeEventInit eventInit; eventInit.mBubbles = false; eventInit.mCancelable = false; eventInit.mRevisionId = mRevisionId; eventInit.mId = mId; eventInit.mOperation = mOperation; eventInit.mOwner = mOwner; nsRefPtr event = DataStoreChangeEvent::Constructor(workerStore, NS_LITERAL_STRING("change"), eventInit); workerStore->DispatchDOMEvent(nullptr, event, nullptr, nullptr); return true; } protected: ~DispatchDataStoreChangeEventRunnable() {} private: nsRefPtr mDataStoreChangeEventProxy; nsString mRevisionId; Nullable 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 event = static_cast(aEvent); nsRefPtr 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