mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1197421 - Fix promise worker proxy cleanup and update callers. r=catalinb
Get rid of having users dispatch control runnables. It was error prone and required too much reasoning. It was also possible to end up in a state where callers would dispatch a WorkerRunnable, which would succeed, so they would not dispatch a WorkerControlRunnable. Then the worker would stop Running, canceling and releasing the runnable leading to releasing the proxy in an unclean state. Instead, we AddRef() and add the feature and remove the feature and Release() on Notify(). If callers successfully run a WorkerRunnable they clean the proxy. If not, the proxy stays alive until the worker switches to Canceling state.
This commit is contained in:
parent
f90caaab5c
commit
17f9c6955f
@ -1718,7 +1718,7 @@ public:
|
||||
void
|
||||
WorkerRunInternal(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
|
||||
{
|
||||
nsRefPtr<Promise> workerPromise = mPromiseProxy->GetWorkerPromise();
|
||||
nsRefPtr<Promise> workerPromise = mPromiseProxy->WorkerPromise();
|
||||
|
||||
ErrorResult result;
|
||||
nsAutoTArray<nsRefPtr<Notification>, 5> notifications;
|
||||
@ -1767,27 +1767,19 @@ public:
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(mPromiseProxy, "Was Done() called twice?");
|
||||
MutexAutoLock lock(mPromiseProxy->GetCleanUpLock());
|
||||
if (mPromiseProxy->IsClean()) {
|
||||
|
||||
nsRefPtr<PromiseWorkerProxy> proxy = mPromiseProxy.forget();
|
||||
MutexAutoLock lock(proxy->Lock());
|
||||
if (proxy->CleanedUp()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mPromiseProxy->GetWorkerPrivate());
|
||||
nsRefPtr<WorkerGetResultRunnable> r =
|
||||
new WorkerGetResultRunnable(mPromiseProxy->GetWorkerPrivate(),
|
||||
mPromiseProxy,
|
||||
new WorkerGetResultRunnable(proxy->GetWorkerPrivate(),
|
||||
proxy,
|
||||
Move(mStrings));
|
||||
|
||||
if (!r->Dispatch(aCx)) {
|
||||
nsRefPtr<PromiseWorkerProxyControlRunnable> cr =
|
||||
new PromiseWorkerProxyControlRunnable(mPromiseProxy->GetWorkerPrivate(),
|
||||
mPromiseProxy);
|
||||
|
||||
DebugOnly<bool> ok = cr->Dispatch(aCx);
|
||||
MOZ_ASSERT(ok);
|
||||
}
|
||||
|
||||
mPromiseProxy = nullptr;
|
||||
r->Dispatch(aCx);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -1804,31 +1796,18 @@ class WorkerGetRunnable final : public nsRunnable
|
||||
const nsString mTag;
|
||||
const nsString mScope;
|
||||
public:
|
||||
WorkerGetRunnable(WorkerPrivate* aWorkerPrivate,
|
||||
Promise* aWorkerPromise,
|
||||
WorkerGetRunnable(PromiseWorkerProxy* aProxy,
|
||||
const nsAString& aTag,
|
||||
const nsAString& aScope)
|
||||
: mTag(aTag), mScope(aScope)
|
||||
: mPromiseProxy(aProxy), mTag(aTag), mScope(aScope)
|
||||
{
|
||||
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||
mPromiseProxy =
|
||||
PromiseWorkerProxy::Create(aWorkerPrivate,
|
||||
aWorkerPromise);
|
||||
|
||||
if (!mPromiseProxy || !mPromiseProxy->GetWorkerPromise()) {
|
||||
aWorkerPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
|
||||
mPromiseProxy = nullptr;
|
||||
}
|
||||
MOZ_ASSERT(mPromiseProxy);
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
Run() override
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
if (!mPromiseProxy) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsINotificationStorageCallback> callback =
|
||||
new WorkerGetCallback(mPromiseProxy, mScope);
|
||||
|
||||
@ -1843,8 +1822,8 @@ public:
|
||||
return rv;
|
||||
}
|
||||
|
||||
MutexAutoLock lock(mPromiseProxy->GetCleanUpLock());
|
||||
if (mPromiseProxy->IsClean()) {
|
||||
MutexAutoLock lock(mPromiseProxy->Lock());
|
||||
if (mPromiseProxy->CleanedUp()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -1883,13 +1862,18 @@ Notification::WorkerGet(WorkerPrivate* aWorkerPrivate,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<WorkerGetRunnable> r =
|
||||
new WorkerGetRunnable(aWorkerPrivate, p, aFilter.mTag, aScope);
|
||||
if (NS_WARN_IF(NS_FAILED(NS_DispatchToMainThread(r)))) {
|
||||
nsRefPtr<PromiseWorkerProxy> proxy =
|
||||
PromiseWorkerProxy::Create(aWorkerPrivate, p);
|
||||
if (!proxy) {
|
||||
aRv.Throw(NS_ERROR_DOM_ABORT_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<WorkerGetRunnable> r =
|
||||
new WorkerGetRunnable(proxy, aFilter.mTag, aScope);
|
||||
// Since this is called from script via
|
||||
// ServiceWorkerRegistration::GetNotifications, we can assert dispatch.
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(r)));
|
||||
return p.forget();
|
||||
}
|
||||
|
||||
|
@ -1509,8 +1509,7 @@ public:
|
||||
MOZ_ASSERT(aWorkerPrivate == mWorkerPrivate);
|
||||
|
||||
MOZ_ASSERT(mPromiseWorkerProxy);
|
||||
nsRefPtr<Promise> workerPromise = mPromiseWorkerProxy->GetWorkerPromise();
|
||||
MOZ_ASSERT(workerPromise);
|
||||
nsRefPtr<Promise> workerPromise = mPromiseWorkerProxy->WorkerPromise();
|
||||
|
||||
// Here we convert the buffer to a JS::Value.
|
||||
JS::Rooted<JS::Value> value(aCx);
|
||||
@ -1553,11 +1552,10 @@ PromiseWorkerProxy::Create(workers::WorkerPrivate* aWorkerPrivate,
|
||||
|
||||
// We do this to make sure the worker thread won't shut down before the
|
||||
// promise is resolved/rejected on the worker thread.
|
||||
if (!aWorkerPrivate->AddFeature(aWorkerPrivate->GetJSContext(), proxy)) {
|
||||
if (!proxy->AddRefObject()) {
|
||||
// Probably the worker is terminating. We cannot complete the operation
|
||||
// and we have to release all the resources.
|
||||
proxy->mCleanedUp = true;
|
||||
proxy->mWorkerPromise = nullptr;
|
||||
proxy->CleanProperties();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -1574,40 +1572,76 @@ PromiseWorkerProxy::PromiseWorkerProxy(workers::WorkerPrivate* aWorkerPrivate,
|
||||
, mCleanedUp(false)
|
||||
, mCallbacks(aCallbacks)
|
||||
, mCleanUpLock("cleanUpLock")
|
||||
, mFeatureAdded(false)
|
||||
{
|
||||
}
|
||||
|
||||
PromiseWorkerProxy::~PromiseWorkerProxy()
|
||||
{
|
||||
MOZ_ASSERT(mCleanedUp);
|
||||
MOZ_ASSERT(!mFeatureAdded);
|
||||
MOZ_ASSERT(!mWorkerPromise);
|
||||
MOZ_ASSERT(!mWorkerPrivate);
|
||||
}
|
||||
|
||||
workers::WorkerPrivate*
|
||||
PromiseWorkerProxy::GetWorkerPrivate() const
|
||||
void
|
||||
PromiseWorkerProxy::CleanProperties()
|
||||
{
|
||||
// It's ok to race on |mCleanedUp|, because it will never cause us to fire
|
||||
// the assertion when we should not.
|
||||
MOZ_ASSERT(!mCleanedUp);
|
||||
|
||||
#ifdef DEBUG
|
||||
if (NS_IsMainThread()) {
|
||||
mCleanUpLock.AssertCurrentThreadOwns();
|
||||
}
|
||||
#endif
|
||||
|
||||
return mWorkerPrivate;
|
||||
}
|
||||
|
||||
Promise*
|
||||
PromiseWorkerProxy::GetWorkerPromise() const
|
||||
{
|
||||
|
||||
#ifdef DEBUG
|
||||
workers::WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
|
||||
MOZ_ASSERT(worker);
|
||||
worker->AssertIsOnWorkerThread();
|
||||
#endif
|
||||
// Ok to do this unprotected from Create().
|
||||
// CleanUp() holds the lock before calling this.
|
||||
mCleanedUp = true;
|
||||
mWorkerPromise = nullptr;
|
||||
mWorkerPrivate = nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
PromiseWorkerProxy::AddRefObject()
|
||||
{
|
||||
MOZ_ASSERT(mWorkerPrivate);
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
MOZ_ASSERT(!mFeatureAdded);
|
||||
if (!mWorkerPrivate->AddFeature(mWorkerPrivate->GetJSContext(),
|
||||
this)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mFeatureAdded = true;
|
||||
// Maintain a reference so that we have a valid object to clean up when
|
||||
// removing the feature.
|
||||
AddRef();
|
||||
return true;
|
||||
}
|
||||
|
||||
workers::WorkerPrivate*
|
||||
PromiseWorkerProxy::GetWorkerPrivate() const
|
||||
{
|
||||
#ifdef DEBUG
|
||||
if (NS_IsMainThread()) {
|
||||
mCleanUpLock.AssertCurrentThreadOwns();
|
||||
}
|
||||
#endif
|
||||
// Safe to check this without a lock since we assert lock ownership on the
|
||||
// main thread above.
|
||||
MOZ_ASSERT(!mCleanedUp);
|
||||
MOZ_ASSERT(mFeatureAdded);
|
||||
|
||||
return mWorkerPrivate;
|
||||
}
|
||||
|
||||
Promise*
|
||||
PromiseWorkerProxy::WorkerPromise() const
|
||||
{
|
||||
#ifdef DEBUG
|
||||
workers::WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
|
||||
MOZ_ASSERT(worker);
|
||||
worker->AssertIsOnWorkerThread();
|
||||
#endif
|
||||
MOZ_ASSERT(mWorkerPromise);
|
||||
return mWorkerPromise;
|
||||
}
|
||||
|
||||
@ -1621,14 +1655,6 @@ PromiseWorkerProxy::StoreISupports(nsISupports* aSupports)
|
||||
mSupportsArray.AppendElement(supports);
|
||||
}
|
||||
|
||||
bool
|
||||
PromiseWorkerProxyControlRunnable::WorkerRun(JSContext* aCx,
|
||||
workers::WorkerPrivate* aWorkerPrivate)
|
||||
{
|
||||
mProxy->CleanUp(aCx);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
PromiseWorkerProxy::RunCallback(JSContext* aCx,
|
||||
JS::Handle<JS::Value> aValue,
|
||||
@ -1636,9 +1662,9 @@ PromiseWorkerProxy::RunCallback(JSContext* aCx,
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
MutexAutoLock lock(GetCleanUpLock());
|
||||
MutexAutoLock lock(Lock());
|
||||
// If the worker thread's been cancelled we don't need to resolve the Promise.
|
||||
if (IsClean()) {
|
||||
if (CleanedUp()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1657,11 +1683,7 @@ PromiseWorkerProxy::RunCallback(JSContext* aCx,
|
||||
Move(buffer),
|
||||
aFunc);
|
||||
|
||||
if (!runnable->Dispatch(aCx)) {
|
||||
nsRefPtr<WorkerControlRunnable> runnable =
|
||||
new PromiseWorkerProxyControlRunnable(mWorkerPrivate, this);
|
||||
mWorkerPrivate->DispatchControlRunnable(runnable.forget());
|
||||
}
|
||||
runnable->Dispatch(aCx);
|
||||
}
|
||||
|
||||
void
|
||||
@ -1681,10 +1703,6 @@ PromiseWorkerProxy::RejectedCallback(JSContext* aCx,
|
||||
bool
|
||||
PromiseWorkerProxy::Notify(JSContext* aCx, Status aStatus)
|
||||
{
|
||||
MOZ_ASSERT(mWorkerPrivate);
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
MOZ_ASSERT(mWorkerPrivate->GetJSContext() == aCx);
|
||||
|
||||
if (aStatus >= Canceling) {
|
||||
CleanUp(aCx);
|
||||
}
|
||||
@ -1695,24 +1713,29 @@ PromiseWorkerProxy::Notify(JSContext* aCx, Status aStatus)
|
||||
void
|
||||
PromiseWorkerProxy::CleanUp(JSContext* aCx)
|
||||
{
|
||||
MutexAutoLock lock(mCleanUpLock);
|
||||
// Can't release Mutex while it is still locked, so scope the lock.
|
||||
{
|
||||
MutexAutoLock lock(Lock());
|
||||
|
||||
// |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;
|
||||
// |mWorkerPrivate| is not safe to use anymore if we have already
|
||||
// cleaned up and RemoveFeature(), so we need to check |mCleanedUp| first.
|
||||
if (CleanedUp()) {
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mWorkerPrivate);
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
MOZ_ASSERT(mWorkerPrivate->GetJSContext() == aCx);
|
||||
|
||||
// Release the Promise and remove the PromiseWorkerProxy from the features of
|
||||
// the worker thread since the Promise has been resolved/rejected or the
|
||||
// worker thread has been cancelled.
|
||||
MOZ_ASSERT(mFeatureAdded);
|
||||
mWorkerPrivate->RemoveFeature(mWorkerPrivate->GetJSContext(), this);
|
||||
mFeatureAdded = false;
|
||||
CleanProperties();
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mWorkerPrivate);
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
MOZ_ASSERT(mWorkerPrivate->GetJSContext() == aCx);
|
||||
|
||||
// Release the Promise and remove the PromiseWorkerProxy from the features of
|
||||
// the worker thread since the Promise has been resolved/rejected or the
|
||||
// worker thread has been cancelled.
|
||||
mWorkerPromise = nullptr;
|
||||
mWorkerPrivate->RemoveFeature(aCx, this);
|
||||
mCleanedUp = true;
|
||||
Release();
|
||||
}
|
||||
|
||||
// Specializations of MaybeRejectBrokenly we actually support.
|
||||
|
@ -24,8 +24,8 @@ namespace workers {
|
||||
class WorkerPrivate;
|
||||
} // namespace workers
|
||||
|
||||
// A proxy to catch the resolved/rejected Promise's result from the main thread
|
||||
// and resolve/reject that on the worker thread eventually.
|
||||
// A proxy to (eventually) mirror a resolved/rejected Promise's result from the
|
||||
// main thread to a Promise on the worker thread.
|
||||
//
|
||||
// How to use:
|
||||
//
|
||||
@ -36,13 +36,27 @@ class WorkerPrivate;
|
||||
// if (aRv.Failed()) {
|
||||
// return nullptr;
|
||||
// }
|
||||
// // Pass |promise| around to the WorkerMainThreadRunnable
|
||||
//
|
||||
// 2. Create a PromiseWorkerProxy wrapping the Promise. If this fails, the
|
||||
// worker is shutting down and you should fail the original call. This is
|
||||
// only likely to happen in (Gecko-specific) worker onclose handlers.
|
||||
//
|
||||
// nsRefPtr<PromiseWorkerProxy> proxy =
|
||||
// PromiseWorkerProxy::Create(workerPrivate, promise);
|
||||
// if (!proxy) {
|
||||
// // You may also reject the Promise with an AbortError or similar.
|
||||
// return nullptr;
|
||||
// }
|
||||
//
|
||||
// 3. Dispatch a runnable to the main thread, with a reference to the proxy to
|
||||
// perform the main thread operation. PromiseWorkerProxy is thread-safe
|
||||
// refcounted.
|
||||
//
|
||||
// 4. Return the worker thread promise to the JS caller:
|
||||
//
|
||||
// return promise.forget();
|
||||
//
|
||||
// 2. In your WorkerMainThreadRunnable's ctor, create a PromiseWorkerProxy
|
||||
// which holds a nsRefPtr<Promise> to the Promise created at #1.
|
||||
//
|
||||
// 3. In your WorkerMainThreadRunnable::MainThreadRun(), obtain a Promise on
|
||||
// 5. In your main thread runnable Run(), obtain a Promise on
|
||||
// the main thread and call its AppendNativeHandler(PromiseNativeHandler*)
|
||||
// to bind the PromiseWorkerProxy created at #2.
|
||||
//
|
||||
@ -52,17 +66,49 @@ class WorkerPrivate;
|
||||
//
|
||||
// PromiseWorkerProxy can also be used in situations where there is no main
|
||||
// thread Promise, or where special handling is required on the worker thread
|
||||
// for promise resolution. Create a PromiseWorkerProxy as in steps 1 and
|
||||
// 2 above. When the main thread is ready to resolve the worker thread promise,
|
||||
// dispatch a runnable to the worker. Use GetWorkerPrivate() to acquire the
|
||||
// worker. This might be null! In the WorkerRunnable's WorkerRun() use
|
||||
// GetWorkerPromise() to access the Promise and resolve/reject it. Then call
|
||||
// CleanUp() on the worker thread.
|
||||
// for promise resolution. Create a PromiseWorkerProxy as in steps 1 to 3
|
||||
// above. When the main thread is ready to resolve the worker thread promise:
|
||||
//
|
||||
// IMPORTANT: Dispatching the runnable to the worker thread may fail causing
|
||||
// the promise to leak. To successfully release the promise on the
|
||||
// worker thread in this case, use |PromiseWorkerProxyControlRunnable| to
|
||||
// dispatch a control runnable that will deref the object on the correct thread.
|
||||
// 1. Acquire the mutex before attempting to access the worker private.
|
||||
//
|
||||
// AssertIsOnMainThread();
|
||||
// MutexAutoLock lock(proxy->Lock());
|
||||
// if (proxy->CleanedUp()) {
|
||||
// // Worker has already shut down, can't access worker private.
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// 2. Dispatch a runnable to the worker. Use GetWorkerPrivate() to acquire the
|
||||
// worker.
|
||||
//
|
||||
// nsRefPtr<FinishTaskWorkerRunnable> runnable =
|
||||
// new FinishTaskWorkerRunnable(proxy->GetWorkerPrivate(), proxy, result);
|
||||
// AutoJSAPI jsapi;
|
||||
// jsapi.Init();
|
||||
// if (!r->Dispatch(jsapi.cx())) {
|
||||
// // Worker is alive but not Running any more, so the Promise can't
|
||||
// // be resolved, give up. The proxy will get Release()d at some
|
||||
// // point.
|
||||
//
|
||||
// // Usually do nothing, but you may want to log the fact.
|
||||
// }
|
||||
//
|
||||
// 3. In the WorkerRunnable's WorkerRun() use WorkerPromise() to access the
|
||||
// Promise and resolve/reject it. Then call CleanUp().
|
||||
//
|
||||
// bool
|
||||
// WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
|
||||
// {
|
||||
// aWorkerPrivate->AssertIsOnWorkerThread();
|
||||
// nsRefPtr<Promise> promise = mProxy->WorkerPromise();
|
||||
// promise->MaybeResolve(mResult);
|
||||
// mProxy->CleanUp(aCx);
|
||||
// }
|
||||
//
|
||||
// Note: If a PromiseWorkerProxy is not cleaned up by a WorkerRunnable - this
|
||||
// can happen if the main thread Promise is never fulfilled - it will
|
||||
// stay alive till the worker reaches a Canceling state, even if all external
|
||||
// references to it are dropped.
|
||||
|
||||
class PromiseWorkerProxy : public PromiseNativeHandler,
|
||||
public workers::WorkerFeature
|
||||
@ -77,20 +123,29 @@ public:
|
||||
Promise* aWorkerPromise,
|
||||
const JSStructuredCloneCallbacks* aCallbacks = nullptr);
|
||||
|
||||
// Main thread callers must hold Lock() and check CleanUp() before calling this.
|
||||
// Worker thread callers, this will assert that the proxy has not been cleaned
|
||||
// up.
|
||||
workers::WorkerPrivate* GetWorkerPrivate() const;
|
||||
|
||||
Promise* GetWorkerPromise() const;
|
||||
// This should only be used within WorkerRunnable::WorkerRun() running on the
|
||||
// worker thread! Do not call this after calling CleanUp().
|
||||
Promise* WorkerPromise() const;
|
||||
|
||||
void StoreISupports(nsISupports* aSupports);
|
||||
|
||||
// Worker thread only. Calling this invalidates several assumptions, so be
|
||||
// sure this is the last thing you do.
|
||||
// 1. WorkerPrivate() will no longer return a valid worker.
|
||||
// 2. WorkerPromise() will crash!
|
||||
void CleanUp(JSContext* aCx);
|
||||
|
||||
Mutex& GetCleanUpLock()
|
||||
Mutex& Lock()
|
||||
{
|
||||
return mCleanUpLock;
|
||||
}
|
||||
|
||||
bool IsClean() const
|
||||
bool CleanedUp() const
|
||||
{
|
||||
mCleanUpLock.AssertCurrentThreadOwns();
|
||||
return mCleanedUp;
|
||||
@ -112,6 +167,11 @@ private:
|
||||
|
||||
virtual ~PromiseWorkerProxy();
|
||||
|
||||
bool AddRefObject();
|
||||
|
||||
// If not called from Create(), be sure to hold Lock().
|
||||
void CleanProperties();
|
||||
|
||||
// Function pointer for calling Promise::{ResolveInternal,RejectInternal}.
|
||||
typedef void (Promise::*RunCallbackFunc)(JSContext*,
|
||||
JS::Handle<JS::Value>);
|
||||
@ -120,11 +180,15 @@ private:
|
||||
JS::Handle<JS::Value> aValue,
|
||||
RunCallbackFunc aFunc);
|
||||
|
||||
// Any thread with appropriate checks.
|
||||
workers::WorkerPrivate* mWorkerPrivate;
|
||||
|
||||
// This lives on the worker thread.
|
||||
// Worker thread only.
|
||||
nsRefPtr<Promise> mWorkerPromise;
|
||||
|
||||
// Modified on the worker thread.
|
||||
// It is ok to *read* this without a lock on the worker.
|
||||
// Main thread must always acquire a lock.
|
||||
bool mCleanedUp; // To specify if the cleanUp() has been done.
|
||||
|
||||
const JSStructuredCloneCallbacks* mCallbacks;
|
||||
@ -135,32 +199,10 @@ private:
|
||||
|
||||
// Ensure the worker and the main thread won't race to access |mCleanedUp|.
|
||||
Mutex mCleanUpLock;
|
||||
|
||||
// Maybe get rid of this entirely and rely on mCleanedUp
|
||||
DebugOnly<bool> mFeatureAdded;
|
||||
};
|
||||
|
||||
// Helper runnable used for releasing the proxied promise when the worker
|
||||
// is not accepting runnables and the promise object would leak.
|
||||
// See the instructions above.
|
||||
class PromiseWorkerProxyControlRunnable final : public workers::WorkerControlRunnable
|
||||
{
|
||||
nsRefPtr<PromiseWorkerProxy> mProxy;
|
||||
|
||||
public:
|
||||
PromiseWorkerProxyControlRunnable(workers::WorkerPrivate* aWorkerPrivate,
|
||||
PromiseWorkerProxy* aProxy)
|
||||
: WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
|
||||
, mProxy(aProxy)
|
||||
{
|
||||
MOZ_ASSERT(aProxy);
|
||||
}
|
||||
|
||||
virtual bool
|
||||
WorkerRun(JSContext* aCx, workers::WorkerPrivate* aWorkerPrivate) override;
|
||||
|
||||
private:
|
||||
~PromiseWorkerProxyControlRunnable()
|
||||
{}
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
|
@ -216,30 +216,6 @@ WorkerPushSubscription::Constructor(GlobalObject& aGlobal, const nsAString& aEnd
|
||||
return sub.forget();
|
||||
}
|
||||
|
||||
namespace {
|
||||
// The caller MUST take ownership of the proxy's lock before it calls this.
|
||||
void
|
||||
ReleasePromiseWorkerProxy(already_AddRefed<PromiseWorkerProxy> aProxy)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
nsRefPtr<PromiseWorkerProxy> proxy = aProxy;
|
||||
MOZ_ASSERT(proxy);
|
||||
proxy->GetCleanUpLock().AssertCurrentThreadOwns();
|
||||
if (proxy->IsClean()) {
|
||||
return;
|
||||
}
|
||||
|
||||
AutoJSAPI jsapi;
|
||||
jsapi.Init();
|
||||
|
||||
nsRefPtr<PromiseWorkerProxyControlRunnable> cr =
|
||||
new PromiseWorkerProxyControlRunnable(proxy->GetWorkerPrivate(),
|
||||
proxy);
|
||||
|
||||
MOZ_ALWAYS_TRUE(cr->Dispatch(jsapi.cx()));
|
||||
}
|
||||
} // anonymous namespace
|
||||
|
||||
class UnsubscribeResultRunnable final : public WorkerRunnable
|
||||
{
|
||||
public:
|
||||
@ -260,15 +236,14 @@ public:
|
||||
MOZ_ASSERT(aWorkerPrivate);
|
||||
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
nsRefPtr<PromiseWorkerProxy> proxy = mProxy.forget();
|
||||
nsRefPtr<Promise> promise = proxy->GetWorkerPromise();
|
||||
nsRefPtr<Promise> promise = mProxy->WorkerPromise();
|
||||
if (NS_SUCCEEDED(mStatus)) {
|
||||
promise->MaybeResolve(mSuccess);
|
||||
} else {
|
||||
promise->MaybeReject(NS_ERROR_DOM_NETWORK_ERR);
|
||||
}
|
||||
|
||||
proxy->CleanUp(aCx);
|
||||
mProxy->CleanUp(aCx);
|
||||
return true;
|
||||
}
|
||||
private:
|
||||
@ -295,12 +270,12 @@ public:
|
||||
OnUnsubscribe(nsresult aStatus, bool aSuccess) override
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
if (!mProxy) {
|
||||
return NS_OK;
|
||||
}
|
||||
MOZ_ASSERT(mProxy, "OnUnsubscribe() called twice?");
|
||||
|
||||
MutexAutoLock lock(mProxy->GetCleanUpLock());
|
||||
if (mProxy->IsClean()) {
|
||||
nsRefPtr<PromiseWorkerProxy> proxy = mProxy.forget();
|
||||
|
||||
MutexAutoLock lock(proxy->Lock());
|
||||
if (proxy->CleanedUp()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -308,29 +283,14 @@ public:
|
||||
jsapi.Init();
|
||||
|
||||
nsRefPtr<UnsubscribeResultRunnable> r =
|
||||
new UnsubscribeResultRunnable(mProxy, aStatus, aSuccess);
|
||||
if (!r->Dispatch(jsapi.cx())) {
|
||||
ReleasePromiseWorkerProxy(mProxy.forget());
|
||||
}
|
||||
|
||||
mProxy = nullptr;
|
||||
new UnsubscribeResultRunnable(proxy, aStatus, aSuccess);
|
||||
r->Dispatch(jsapi.cx());
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
~WorkerUnsubscribeResultCallback()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
if (mProxy) {
|
||||
MutexAutoLock lock(mProxy->GetCleanUpLock());
|
||||
if (!mProxy->IsClean()) {
|
||||
AutoJSAPI jsapi;
|
||||
jsapi.Init();
|
||||
nsRefPtr<PromiseWorkerProxyControlRunnable> cr =
|
||||
new PromiseWorkerProxyControlRunnable(mProxy->GetWorkerPrivate(), mProxy);
|
||||
cr->Dispatch(jsapi.cx());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsRefPtr<PromiseWorkerProxy> mProxy;
|
||||
@ -354,8 +314,8 @@ public:
|
||||
Run() override
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MutexAutoLock lock(mProxy->GetCleanUpLock());
|
||||
if (mProxy->IsClean()) {
|
||||
MutexAutoLock lock(mProxy->Lock());
|
||||
if (mProxy->CleanedUp()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -448,8 +408,7 @@ public:
|
||||
bool
|
||||
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
|
||||
{
|
||||
nsRefPtr<PromiseWorkerProxy> proxy = mProxy.forget();
|
||||
nsRefPtr<Promise> promise = proxy->GetWorkerPromise();
|
||||
nsRefPtr<Promise> promise = mProxy->WorkerPromise();
|
||||
if (NS_SUCCEEDED(mStatus)) {
|
||||
if (mEndpoint.IsEmpty()) {
|
||||
promise->MaybeResolve(JS::NullHandleValue);
|
||||
@ -462,7 +421,7 @@ public:
|
||||
promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
|
||||
}
|
||||
|
||||
proxy->CleanUp(aCx);
|
||||
mProxy->CleanUp(aCx);
|
||||
return true;
|
||||
}
|
||||
private:
|
||||
@ -490,13 +449,12 @@ public:
|
||||
OnPushEndpoint(nsresult aStatus, const nsAString& aEndpoint) override
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(mProxy, "OnPushEndpoint() called twice?");
|
||||
|
||||
if (!mProxy) {
|
||||
return NS_OK;
|
||||
}
|
||||
nsRefPtr<PromiseWorkerProxy> proxy = mProxy.forget();
|
||||
|
||||
MutexAutoLock lock(mProxy->GetCleanUpLock());
|
||||
if (mProxy->IsClean()) {
|
||||
MutexAutoLock lock(proxy->Lock());
|
||||
if (proxy->CleanedUp()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -504,30 +462,14 @@ public:
|
||||
jsapi.Init();
|
||||
|
||||
nsRefPtr<GetSubscriptionResultRunnable> r =
|
||||
new GetSubscriptionResultRunnable(mProxy, aStatus, aEndpoint, mScope);
|
||||
if (!r->Dispatch(jsapi.cx())) {
|
||||
ReleasePromiseWorkerProxy(mProxy.forget());
|
||||
}
|
||||
|
||||
mProxy = nullptr;
|
||||
new GetSubscriptionResultRunnable(proxy, aStatus, aEndpoint, mScope);
|
||||
r->Dispatch(jsapi.cx());
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
protected:
|
||||
~GetSubscriptionCallback()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
if (mProxy) {
|
||||
MutexAutoLock lock(mProxy->GetCleanUpLock());
|
||||
if (!mProxy->IsClean()) {
|
||||
AutoJSAPI jsapi;
|
||||
jsapi.Init();
|
||||
nsRefPtr<PromiseWorkerProxyControlRunnable> cr =
|
||||
new PromiseWorkerProxyControlRunnable(mProxy->GetWorkerPrivate(), mProxy);
|
||||
cr->Dispatch(jsapi.cx());
|
||||
}
|
||||
}
|
||||
}
|
||||
{}
|
||||
|
||||
private:
|
||||
nsRefPtr<PromiseWorkerProxy> mProxy;
|
||||
@ -550,8 +492,8 @@ public:
|
||||
Run() override
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MutexAutoLock lock(mProxy->GetCleanUpLock());
|
||||
if (mProxy->IsClean()) {
|
||||
MutexAutoLock lock(mProxy->Lock());
|
||||
if (mProxy->CleanedUp()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -564,9 +506,11 @@ public:
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIPrincipal> principal = mProxy->GetWorkerPrivate()->GetPrincipal();
|
||||
|
||||
uint32_t permission = nsIPermissionManager::DENY_ACTION;
|
||||
nsresult rv = permManager->TestExactPermissionFromPrincipal(
|
||||
mProxy->GetWorkerPrivate()->GetPrincipal(),
|
||||
principal,
|
||||
"push",
|
||||
&permission);
|
||||
|
||||
@ -582,9 +526,6 @@ public:
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIPrincipal> principal = mProxy->GetWorkerPrivate()->GetPrincipal();
|
||||
mProxy = nullptr;
|
||||
|
||||
if (mAction == WorkerPushManager::SubscribeAction) {
|
||||
rv = client->Subscribe(mScope, principal, callback);
|
||||
} else {
|
||||
@ -667,17 +608,17 @@ public:
|
||||
MOZ_ASSERT(aWorkerPrivate);
|
||||
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
nsRefPtr<PromiseWorkerProxy> proxy = mProxy.forget();
|
||||
nsRefPtr<Promise> promise = proxy->GetWorkerPromise();
|
||||
nsRefPtr<Promise> promise = mProxy->WorkerPromise();
|
||||
if (NS_SUCCEEDED(mStatus)) {
|
||||
MOZ_ASSERT(uint32_t(mState) < ArrayLength(PushPermissionStateValues::strings));
|
||||
nsAutoCString stringState(PushPermissionStateValues::strings[uint32_t(mState)].value, PushPermissionStateValues::strings[uint32_t(mState)].length);
|
||||
nsAutoCString stringState(PushPermissionStateValues::strings[uint32_t(mState)].value,
|
||||
PushPermissionStateValues::strings[uint32_t(mState)].length);
|
||||
promise->MaybeResolve(NS_ConvertUTF8toUTF16(stringState));
|
||||
} else {
|
||||
promise->MaybeReject(aCx, JS::UndefinedHandleValue);
|
||||
}
|
||||
|
||||
proxy->CleanUp(aCx);
|
||||
mProxy->CleanUp(aCx);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -701,8 +642,8 @@ public:
|
||||
Run() override
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MutexAutoLock lock(mProxy->GetCleanUpLock());
|
||||
if (mProxy->IsClean()) {
|
||||
MutexAutoLock lock(mProxy->Lock());
|
||||
if (mProxy->CleanedUp()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -740,9 +681,7 @@ public:
|
||||
jsapi.Init();
|
||||
nsRefPtr<PermissionResultRunnable> r =
|
||||
new PermissionResultRunnable(mProxy, rv, state);
|
||||
if (!r->Dispatch(jsapi.cx())) {
|
||||
ReleasePromiseWorkerProxy(mProxy.forget());
|
||||
}
|
||||
r->Dispatch(jsapi.cx());
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -65,7 +65,7 @@ public:
|
||||
MOZ_ASSERT(aWorkerPrivate);
|
||||
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
Promise* promise = mPromiseProxy->GetWorkerPromise();
|
||||
Promise* promise = mPromiseProxy->WorkerPromise();
|
||||
MOZ_ASSERT(promise);
|
||||
|
||||
nsTArray<nsRefPtr<ServiceWorkerClient>> ret;
|
||||
@ -74,30 +74,24 @@ public:
|
||||
new ServiceWorkerWindowClient(promise->GetParentObject(),
|
||||
mValue.ElementAt(i))));
|
||||
}
|
||||
|
||||
promise->MaybeResolve(ret);
|
||||
|
||||
// release the reference on the worker thread.
|
||||
mPromiseProxy->CleanUp(aCx);
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class MatchAllRunnable final : public nsRunnable
|
||||
{
|
||||
WorkerPrivate* mWorkerPrivate;
|
||||
nsRefPtr<PromiseWorkerProxy> mPromiseProxy;
|
||||
nsCString mScope;
|
||||
public:
|
||||
MatchAllRunnable(WorkerPrivate* aWorkerPrivate,
|
||||
PromiseWorkerProxy* aPromiseProxy,
|
||||
MatchAllRunnable(PromiseWorkerProxy* aPromiseProxy,
|
||||
const nsCString& aScope)
|
||||
: mWorkerPrivate(aWorkerPrivate),
|
||||
mPromiseProxy(aPromiseProxy),
|
||||
: mPromiseProxy(aPromiseProxy),
|
||||
mScope(aScope)
|
||||
{
|
||||
MOZ_ASSERT(aWorkerPrivate);
|
||||
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||
MOZ_ASSERT(mPromiseProxy);
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
@ -105,33 +99,22 @@ public:
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
MutexAutoLock lock(mPromiseProxy->GetCleanUpLock());
|
||||
if (mPromiseProxy->IsClean()) {
|
||||
// Don't resolve the promise if it was already released.
|
||||
MutexAutoLock lock(mPromiseProxy->Lock());
|
||||
if (mPromiseProxy->CleanedUp()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
nsTArray<ServiceWorkerClientInfo> result;
|
||||
|
||||
swm->GetAllClients(mWorkerPrivate->GetPrincipal(), mScope, result);
|
||||
swm->GetAllClients(mPromiseProxy->GetWorkerPrivate()->GetPrincipal(), mScope, result);
|
||||
nsRefPtr<ResolvePromiseWorkerRunnable> r =
|
||||
new ResolvePromiseWorkerRunnable(mWorkerPrivate, mPromiseProxy, result);
|
||||
|
||||
AutoSafeJSContext cx;
|
||||
if (r->Dispatch(cx)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Dispatch to worker thread failed because the worker is shutting down.
|
||||
// Use a control runnable to release the runnable on the worker thread.
|
||||
nsRefPtr<PromiseWorkerProxyControlRunnable> releaseRunnable =
|
||||
new PromiseWorkerProxyControlRunnable(mWorkerPrivate, mPromiseProxy);
|
||||
|
||||
if (!releaseRunnable->Dispatch(cx)) {
|
||||
NS_RUNTIMEABORT("Failed to dispatch MatchAll promise control runnable.");
|
||||
}
|
||||
new ResolvePromiseWorkerRunnable(mPromiseProxy->GetWorkerPrivate(),
|
||||
mPromiseProxy, result);
|
||||
|
||||
AutoJSAPI jsapi;
|
||||
jsapi.Init();
|
||||
r->Dispatch(jsapi.cx());
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
@ -158,7 +141,7 @@ public:
|
||||
MOZ_ASSERT(aWorkerPrivate);
|
||||
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
Promise* promise = mPromiseProxy->GetWorkerPromise();
|
||||
nsRefPtr<Promise> promise = mPromiseProxy->WorkerPromise();
|
||||
MOZ_ASSERT(promise);
|
||||
|
||||
if (NS_SUCCEEDED(mResult)) {
|
||||
@ -167,9 +150,7 @@ public:
|
||||
promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
}
|
||||
|
||||
// Release the reference on the worker thread.
|
||||
mPromiseProxy->CleanUp(aCx);
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
@ -178,14 +159,14 @@ class ClaimRunnable final : public nsRunnable
|
||||
{
|
||||
nsRefPtr<PromiseWorkerProxy> mPromiseProxy;
|
||||
nsCString mScope;
|
||||
// We grab the ID so we don't have to hold a lock the entire time the claim
|
||||
// operation is happening on the main thread.
|
||||
uint64_t mServiceWorkerID;
|
||||
|
||||
public:
|
||||
ClaimRunnable(PromiseWorkerProxy* aPromiseProxy, const nsCString& aScope)
|
||||
: mPromiseProxy(aPromiseProxy)
|
||||
, mScope(aScope)
|
||||
// Safe to call GetWorkerPrivate() since we are being called on the worker
|
||||
// thread via script (so no clean up has occured yet).
|
||||
, mServiceWorkerID(aPromiseProxy->GetWorkerPrivate()->ServiceWorkerID())
|
||||
{
|
||||
MOZ_ASSERT(aPromiseProxy);
|
||||
@ -194,9 +175,8 @@ public:
|
||||
NS_IMETHOD
|
||||
Run() override
|
||||
{
|
||||
MutexAutoLock lock(mPromiseProxy->GetCleanUpLock());
|
||||
if (mPromiseProxy->IsClean()) {
|
||||
// Don't resolve the promise if it was already released.
|
||||
MutexAutoLock lock(mPromiseProxy->Lock());
|
||||
if (mPromiseProxy->CleanedUp()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -214,20 +194,7 @@ public:
|
||||
|
||||
AutoJSAPI jsapi;
|
||||
jsapi.Init();
|
||||
JSContext* cx = jsapi.cx();
|
||||
if (r->Dispatch(cx)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Dispatch to worker thread failed because the worker is shutting down.
|
||||
// Use a control runnable to release the runnable on the worker thread.
|
||||
nsRefPtr<PromiseWorkerProxyControlRunnable> releaseRunnable =
|
||||
new PromiseWorkerProxyControlRunnable(workerPrivate, mPromiseProxy);
|
||||
|
||||
if (!releaseRunnable->Dispatch(cx)) {
|
||||
NS_RUNTIMEABORT("Failed to dispatch Claim control runnable.");
|
||||
}
|
||||
|
||||
r->Dispatch(jsapi.cx());
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
@ -257,21 +224,15 @@ ServiceWorkerClients::MatchAll(const ClientQueryOptions& aOptions,
|
||||
|
||||
nsRefPtr<PromiseWorkerProxy> promiseProxy =
|
||||
PromiseWorkerProxy::Create(workerPrivate, promise);
|
||||
if (!promiseProxy->GetWorkerPromise()) {
|
||||
// Don't dispatch if adding the worker feature failed.
|
||||
if (!promiseProxy) {
|
||||
promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
nsRefPtr<MatchAllRunnable> r =
|
||||
new MatchAllRunnable(workerPrivate,
|
||||
promiseProxy,
|
||||
new MatchAllRunnable(promiseProxy,
|
||||
NS_ConvertUTF16toUTF8(scope));
|
||||
nsresult rv = NS_DispatchToMainThread(r);
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
promise->MaybeReject(NS_ERROR_NOT_AVAILABLE);
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(r)));
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
@ -301,8 +262,8 @@ ServiceWorkerClients::Claim(ErrorResult& aRv)
|
||||
|
||||
nsRefPtr<PromiseWorkerProxy> promiseProxy =
|
||||
PromiseWorkerProxy::Create(workerPrivate, promise);
|
||||
if (!promiseProxy->GetWorkerPromise()) {
|
||||
// Don't dispatch if adding the worker feature failed.
|
||||
if (!promiseProxy) {
|
||||
promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
@ -312,10 +273,6 @@ ServiceWorkerClients::Claim(ErrorResult& aRv)
|
||||
nsRefPtr<ClaimRunnable> runnable =
|
||||
new ClaimRunnable(promiseProxy, NS_ConvertUTF16toUTF8(scope));
|
||||
|
||||
aRv = NS_DispatchToMainThread(runnable);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(runnable)));
|
||||
return promise.forget();
|
||||
}
|
||||
|
@ -301,7 +301,7 @@ public:
|
||||
bool
|
||||
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
|
||||
{
|
||||
Promise* promise = mPromiseProxy->GetWorkerPromise();
|
||||
Promise* promise = mPromiseProxy->WorkerPromise();
|
||||
if (NS_SUCCEEDED(mStatus)) {
|
||||
promise->MaybeResolve(JS::UndefinedHandleValue);
|
||||
} else {
|
||||
@ -318,7 +318,6 @@ class WorkerThreadUpdateCallback final : public ServiceWorkerUpdateFinishCallbac
|
||||
|
||||
~WorkerThreadUpdateCallback()
|
||||
{
|
||||
Finish(NS_ERROR_FAILURE);
|
||||
}
|
||||
|
||||
public:
|
||||
@ -351,8 +350,8 @@ public:
|
||||
|
||||
nsRefPtr<PromiseWorkerProxy> proxy = mPromiseProxy.forget();
|
||||
|
||||
MutexAutoLock lock(proxy->GetCleanUpLock());
|
||||
if (proxy->IsClean()) {
|
||||
MutexAutoLock lock(proxy->Lock());
|
||||
if (proxy->CleanedUp()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -361,11 +360,7 @@ public:
|
||||
|
||||
nsRefPtr<UpdateResultRunnable> r =
|
||||
new UpdateResultRunnable(proxy, aStatus);
|
||||
if (!r->Dispatch(jsapi.cx())) {
|
||||
nsRefPtr<PromiseWorkerProxyControlRunnable> r =
|
||||
new PromiseWorkerProxyControlRunnable(proxy->GetWorkerPrivate(), proxy);
|
||||
r->Dispatch(jsapi.cx());
|
||||
}
|
||||
r->Dispatch(jsapi.cx());
|
||||
}
|
||||
};
|
||||
|
||||
@ -384,8 +379,8 @@ public:
|
||||
AssertIsOnMainThread();
|
||||
ErrorResult result;
|
||||
|
||||
MutexAutoLock lock(mPromiseProxy->GetCleanUpLock());
|
||||
if (mPromiseProxy->IsClean()) {
|
||||
MutexAutoLock lock(mPromiseProxy->Lock());
|
||||
if (mPromiseProxy->CleanedUp()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -445,10 +440,9 @@ class FulfillUnregisterPromiseRunnable final : public WorkerRunnable
|
||||
nsRefPtr<PromiseWorkerProxy> mPromiseWorkerProxy;
|
||||
Maybe<bool> mState;
|
||||
public:
|
||||
FulfillUnregisterPromiseRunnable(WorkerPrivate* aWorkerPrivate,
|
||||
PromiseWorkerProxy* aProxy,
|
||||
FulfillUnregisterPromiseRunnable(PromiseWorkerProxy* aProxy,
|
||||
Maybe<bool> aState)
|
||||
: WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount)
|
||||
: WorkerRunnable(aProxy->GetWorkerPrivate(), WorkerThreadModifyBusyCount)
|
||||
, mPromiseWorkerProxy(aProxy)
|
||||
, mState(aState)
|
||||
{
|
||||
@ -459,8 +453,7 @@ public:
|
||||
bool
|
||||
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
|
||||
{
|
||||
Promise* promise = mPromiseWorkerProxy->GetWorkerPromise();
|
||||
MOZ_ASSERT(promise);
|
||||
nsRefPtr<Promise> promise = mPromiseWorkerProxy->WorkerPromise();
|
||||
if (mState.isSome()) {
|
||||
promise->MaybeResolve(mState.value());
|
||||
} else {
|
||||
@ -481,6 +474,7 @@ public:
|
||||
explicit WorkerUnregisterCallback(PromiseWorkerProxy* aProxy)
|
||||
: mPromiseWorkerProxy(aProxy)
|
||||
{
|
||||
MOZ_ASSERT(aProxy);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
@ -501,7 +495,7 @@ public:
|
||||
|
||||
private:
|
||||
~WorkerUnregisterCallback()
|
||||
{ }
|
||||
{}
|
||||
|
||||
void
|
||||
Finish(Maybe<bool> aState)
|
||||
@ -511,24 +505,18 @@ private:
|
||||
return;
|
||||
}
|
||||
|
||||
MutexAutoLock lock(mPromiseWorkerProxy->GetCleanUpLock());
|
||||
if (mPromiseWorkerProxy->IsClean()) {
|
||||
nsRefPtr<PromiseWorkerProxy> proxy = mPromiseWorkerProxy.forget();
|
||||
MutexAutoLock lock(proxy->Lock());
|
||||
if (proxy->CleanedUp()) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsRefPtr<WorkerRunnable> r =
|
||||
new FulfillUnregisterPromiseRunnable(mPromiseWorkerProxy->GetWorkerPrivate(),
|
||||
mPromiseWorkerProxy, aState);
|
||||
new FulfillUnregisterPromiseRunnable(proxy, aState);
|
||||
|
||||
AutoJSAPI jsapi;
|
||||
jsapi.Init();
|
||||
if (!r->Dispatch(jsapi.cx())) {
|
||||
nsRefPtr<WorkerControlRunnable> cr =
|
||||
new PromiseWorkerProxyControlRunnable(
|
||||
mPromiseWorkerProxy->GetWorkerPrivate(),
|
||||
mPromiseWorkerProxy);
|
||||
cr->Dispatch(jsapi.cx());
|
||||
}
|
||||
r->Dispatch(jsapi.cx());
|
||||
}
|
||||
};
|
||||
|
||||
@ -544,12 +532,12 @@ class StartUnregisterRunnable final : public nsRunnable
|
||||
const nsString mScope;
|
||||
|
||||
public:
|
||||
StartUnregisterRunnable(WorkerPrivate* aWorker, Promise* aPromise,
|
||||
StartUnregisterRunnable(PromiseWorkerProxy* aProxy,
|
||||
const nsAString& aScope)
|
||||
: mPromiseWorkerProxy(PromiseWorkerProxy::Create(aWorker, aPromise))
|
||||
: mPromiseWorkerProxy(aProxy)
|
||||
, mScope(aScope)
|
||||
{
|
||||
// mPromiseWorkerProxy may be null if AddFeature failed.
|
||||
MOZ_ASSERT(aProxy);
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
@ -557,8 +545,6 @@ public:
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
nsRefPtr<WorkerUnregisterCallback> cb = new WorkerUnregisterCallback(mPromiseWorkerProxy);
|
||||
|
||||
// XXXnsm: There is a rare chance of this failing if the worker gets
|
||||
// destroyed. In that case, unregister() called from a SW is no longer
|
||||
// guaranteed to run. We should fix this by having a main thread proxy
|
||||
@ -566,8 +552,8 @@ public:
|
||||
// principal. Can that be trusted?
|
||||
nsCOMPtr<nsIPrincipal> principal;
|
||||
{
|
||||
MutexAutoLock lock(mPromiseWorkerProxy->GetCleanUpLock());
|
||||
if (mPromiseWorkerProxy->IsClean()) {
|
||||
MutexAutoLock lock(mPromiseWorkerProxy->Lock());
|
||||
if (mPromiseWorkerProxy->CleanedUp()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -577,6 +563,8 @@ public:
|
||||
}
|
||||
MOZ_ASSERT(principal);
|
||||
|
||||
nsRefPtr<WorkerUnregisterCallback> cb =
|
||||
new WorkerUnregisterCallback(mPromiseWorkerProxy);
|
||||
nsCOMPtr<nsIServiceWorkerManager> swm =
|
||||
mozilla::services::GetServiceWorkerManager();
|
||||
nsresult rv = swm->Unregister(principal, cb, mScope);
|
||||
@ -962,8 +950,8 @@ ServiceWorkerRegistrationWorkerThread::Update(ErrorResult& aRv)
|
||||
|
||||
nsRefPtr<PromiseWorkerProxy> proxy = PromiseWorkerProxy::Create(worker, promise);
|
||||
if (!proxy) {
|
||||
promise->MaybeResolve(NS_ERROR_DOM_ABORT_ERR);
|
||||
return promise.forget();
|
||||
aRv.Throw(NS_ERROR_DOM_ABORT_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<UpdateRunnable> r = new UpdateRunnable(proxy, mScope);
|
||||
@ -992,7 +980,13 @@ ServiceWorkerRegistrationWorkerThread::Unregister(ErrorResult& aRv)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<StartUnregisterRunnable> r = new StartUnregisterRunnable(worker, promise, mScope);
|
||||
nsRefPtr<PromiseWorkerProxy> proxy = PromiseWorkerProxy::Create(worker, promise);
|
||||
if (!proxy) {
|
||||
aRv.Throw(NS_ERROR_DOM_ABORT_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<StartUnregisterRunnable> r = new StartUnregisterRunnable(proxy, mScope);
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(r)));
|
||||
|
||||
return promise.forget();
|
||||
|
@ -51,7 +51,7 @@ public:
|
||||
MOZ_ASSERT(aWorkerPrivate);
|
||||
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
Promise* promise = mPromiseProxy->GetWorkerPromise();
|
||||
nsRefPtr<Promise> promise = mPromiseProxy->WorkerPromise();
|
||||
MOZ_ASSERT(promise);
|
||||
|
||||
if (mClientInfo) {
|
||||
@ -80,7 +80,6 @@ public:
|
||||
, mPromiseProxy(aPromiseProxy)
|
||||
{
|
||||
MOZ_ASSERT(mPromiseProxy);
|
||||
MOZ_ASSERT(mPromiseProxy->GetWorkerPromise());
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
@ -110,28 +109,18 @@ private:
|
||||
DispatchResult(UniquePtr<ServiceWorkerClientInfo>&& aClientInfo)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MutexAutoLock lock(mPromiseProxy->GetCleanUpLock());
|
||||
if (mPromiseProxy->IsClean()) {
|
||||
MutexAutoLock lock(mPromiseProxy->Lock());
|
||||
if (mPromiseProxy->CleanedUp()) {
|
||||
return;
|
||||
}
|
||||
|
||||
WorkerPrivate* workerPrivate = mPromiseProxy->GetWorkerPrivate();
|
||||
MOZ_ASSERT(workerPrivate);
|
||||
|
||||
nsRefPtr<ResolveOrRejectPromiseRunnable> resolveRunnable =
|
||||
new ResolveOrRejectPromiseRunnable(workerPrivate, mPromiseProxy,
|
||||
Move(aClientInfo));
|
||||
new ResolveOrRejectPromiseRunnable(mPromiseProxy->GetWorkerPrivate(),
|
||||
mPromiseProxy, Move(aClientInfo));
|
||||
|
||||
AutoJSAPI jsapi;
|
||||
jsapi.Init();
|
||||
JSContext* cx = jsapi.cx();
|
||||
if (!resolveRunnable->Dispatch(cx)) {
|
||||
nsRefPtr<PromiseWorkerProxyControlRunnable> controlRunnable =
|
||||
new PromiseWorkerProxyControlRunnable(workerPrivate, mPromiseProxy);
|
||||
if (!controlRunnable->Dispatch(cx)) {
|
||||
NS_RUNTIMEABORT("Failed to dispatch Focus promise control runnable.");
|
||||
}
|
||||
}
|
||||
resolveRunnable->Dispatch(jsapi.cx());
|
||||
}
|
||||
};
|
||||
|
||||
@ -155,17 +144,14 @@ ServiceWorkerWindowClient::Focus(ErrorResult& aRv) const
|
||||
if (workerPrivate->GlobalScope()->WindowInteractionAllowed()) {
|
||||
nsRefPtr<PromiseWorkerProxy> promiseProxy =
|
||||
PromiseWorkerProxy::Create(workerPrivate, promise);
|
||||
if (!promiseProxy->GetWorkerPromise()) {
|
||||
// Don't dispatch if adding the worker feature failed.
|
||||
return promise.forget();
|
||||
if (promiseProxy) {
|
||||
nsRefPtr<ClientFocusRunnable> r = new ClientFocusRunnable(mWindowId,
|
||||
promiseProxy);
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(r)));
|
||||
} else {
|
||||
promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
|
||||
}
|
||||
|
||||
nsRefPtr<ClientFocusRunnable> r = new ClientFocusRunnable(mWindowId,
|
||||
promiseProxy);
|
||||
aRv = NS_DispatchToMainThread(r);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
promise->MaybeReject(aRv);
|
||||
}
|
||||
} else {
|
||||
promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
}
|
||||
|
@ -546,9 +546,7 @@ public:
|
||||
MOZ_ASSERT(aWorkerPrivate);
|
||||
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
Promise* promise = mPromiseProxy->GetWorkerPromise();
|
||||
MOZ_ASSERT(promise);
|
||||
|
||||
nsRefPtr<Promise> promise = mPromiseProxy->WorkerPromise();
|
||||
promise->MaybeResolve(JS::UndefinedHandleValue);
|
||||
|
||||
// Release the reference on the worker thread.
|
||||
@ -579,13 +577,12 @@ public:
|
||||
nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
MOZ_ASSERT(swm);
|
||||
|
||||
MutexAutoLock lock(mPromiseProxy->GetCleanUpLock());
|
||||
if (mPromiseProxy->IsClean()) {
|
||||
MutexAutoLock lock(mPromiseProxy->Lock());
|
||||
if (mPromiseProxy->CleanedUp()) {
|
||||
return NS_OK;
|
||||
}
|
||||
WorkerPrivate* workerPrivate = mPromiseProxy->GetWorkerPrivate();
|
||||
MOZ_ASSERT(workerPrivate);
|
||||
|
||||
WorkerPrivate* workerPrivate = mPromiseProxy->GetWorkerPrivate();
|
||||
swm->SetSkipWaitingFlag(workerPrivate->GetPrincipal(), mScope,
|
||||
workerPrivate->ServiceWorkerID());
|
||||
|
||||
@ -594,20 +591,7 @@ public:
|
||||
|
||||
AutoJSAPI jsapi;
|
||||
jsapi.Init();
|
||||
JSContext* cx = jsapi.cx();
|
||||
if (runnable->Dispatch(cx)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Dispatch to worker thread failed because the worker is shutting down.
|
||||
// Use a control runnable to release the runnable on the worker thread.
|
||||
nsRefPtr<PromiseWorkerProxyControlRunnable> releaseRunnable =
|
||||
new PromiseWorkerProxyControlRunnable(workerPrivate, mPromiseProxy);
|
||||
|
||||
if (!releaseRunnable->Dispatch(cx)) {
|
||||
NS_RUNTIMEABORT("Failed to dispatch Claim control runnable.");
|
||||
}
|
||||
|
||||
runnable->Dispatch(jsapi.cx());
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
@ -627,8 +611,7 @@ ServiceWorkerGlobalScope::SkipWaiting(ErrorResult& aRv)
|
||||
|
||||
nsRefPtr<PromiseWorkerProxy> promiseProxy =
|
||||
PromiseWorkerProxy::Create(mWorkerPrivate, promise);
|
||||
if (!promiseProxy->GetWorkerPromise()) {
|
||||
// Don't dispatch if adding the worker feature failed.
|
||||
if (!promiseProxy) {
|
||||
promise->MaybeResolve(JS::UndefinedHandleValue);
|
||||
return promise.forget();
|
||||
}
|
||||
@ -637,11 +620,7 @@ ServiceWorkerGlobalScope::SkipWaiting(ErrorResult& aRv)
|
||||
new WorkerScopeSkipWaitingRunnable(promiseProxy,
|
||||
NS_ConvertUTF16toUTF8(mScope));
|
||||
|
||||
aRv = NS_DispatchToMainThread(runnable);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(runnable)));
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user