Bug 1190672 - Fix use of AutoReleasePromiseWorkerProxy in PushManager. r=catalinb

This commit is contained in:
Nikhil Marathe 2015-08-03 21:47:16 -07:00
parent eff4fc7c12
commit 163db26016

View File

@ -215,38 +215,29 @@ WorkerPushSubscription::Constructor(GlobalObject& aGlobal, const nsAString& aEnd
return sub.forget(); return sub.forget();
} }
class MOZ_STACK_CLASS AutoReleasePromiseWorkerProxy final namespace {
// The caller MUST take ownership of the proxy's lock before it calls this.
void
ReleasePromiseWorkerProxy(already_AddRefed<PromiseWorkerProxy> aProxy)
{ {
public: AssertIsOnMainThread();
explicit AutoReleasePromiseWorkerProxy(PromiseWorkerProxy* aProxy) nsRefPtr<PromiseWorkerProxy> proxy = aProxy;
: mProxy(aProxy) MOZ_ASSERT(proxy);
{ proxy->GetCleanUpLock().AssertCurrentThreadOwns();
AssertIsOnMainThread(); if (proxy->IsClean()) {
MOZ_ASSERT(aProxy); return;
aProxy->GetCleanUpLock().AssertCurrentThreadOwns();
if (aProxy->IsClean()) {
mProxy = nullptr;
}
} }
~AutoReleasePromiseWorkerProxy() AutoJSAPI jsapi;
{ jsapi.Init();
if (mProxy) {
AutoJSAPI jsapi;
jsapi.Init();
nsRefPtr<PromiseWorkerProxyControlRunnable> cr = nsRefPtr<PromiseWorkerProxyControlRunnable> cr =
new PromiseWorkerProxyControlRunnable(mProxy->GetWorkerPrivate(), new PromiseWorkerProxyControlRunnable(proxy->GetWorkerPrivate(),
mProxy); proxy);
DebugOnly<bool> ok = cr->Dispatch(jsapi.cx()); MOZ_ALWAYS_TRUE(cr->Dispatch(jsapi.cx()));
MOZ_ASSERT(ok); }
mProxy = nullptr; } // anonymous namespace
}
}
private:
nsRefPtr<PromiseWorkerProxy> mProxy;
};
class UnsubscribeResultRunnable final : public WorkerRunnable class UnsubscribeResultRunnable final : public WorkerRunnable
{ {
@ -281,12 +272,7 @@ public:
} }
private: private:
~UnsubscribeResultRunnable() ~UnsubscribeResultRunnable()
{ {}
if (mProxy) {
AutoReleasePromiseWorkerProxy autoRelease(mProxy);
mProxy = nullptr;
}
}
nsRefPtr<PromiseWorkerProxy> mProxy; nsRefPtr<PromiseWorkerProxy> mProxy;
nsresult mStatus; nsresult mStatus;
@ -300,6 +286,7 @@ public:
explicit WorkerUnsubscribeResultCallback(PromiseWorkerProxy* aProxy) explicit WorkerUnsubscribeResultCallback(PromiseWorkerProxy* aProxy)
: mProxy(aProxy) : mProxy(aProxy)
, mCallbackCalled(false)
{ {
AssertIsOnMainThread(); AssertIsOnMainThread();
} }
@ -308,6 +295,7 @@ public:
OnUnsubscribe(nsresult aStatus, bool aSuccess) override OnUnsubscribe(nsresult aStatus, bool aSuccess) override
{ {
AssertIsOnMainThread(); AssertIsOnMainThread();
mCallbackCalled = true;
if (!mProxy) { if (!mProxy) {
return NS_OK; return NS_OK;
} }
@ -322,22 +310,23 @@ public:
nsRefPtr<UnsubscribeResultRunnable> r = nsRefPtr<UnsubscribeResultRunnable> r =
new UnsubscribeResultRunnable(mProxy, aStatus, aSuccess); new UnsubscribeResultRunnable(mProxy, aStatus, aSuccess);
mProxy = nullptr; if (!r->Dispatch(jsapi.cx())) {
ReleasePromiseWorkerProxy(mProxy.forget());
}
r->Dispatch(jsapi.cx());
return NS_OK; return NS_OK;
} }
private: private:
~WorkerUnsubscribeResultCallback() ~WorkerUnsubscribeResultCallback()
{ {
if (mProxy) { // Enforces that UnsubscribeRunnable uses the callback for error
AutoReleasePromiseWorkerProxy autoRelease(mProxy); // reporting once it creates the callback.
mProxy = nullptr; MOZ_ASSERT(mCallbackCalled);
}
} }
nsRefPtr<PromiseWorkerProxy> mProxy; nsRefPtr<PromiseWorkerProxy> mProxy;
DebugOnly<bool> mCallbackCalled;
}; };
NS_IMPL_ISUPPORTS(WorkerUnsubscribeResultCallback, nsIUnsubscribeResultCallback) NS_IMPL_ISUPPORTS(WorkerUnsubscribeResultCallback, nsIUnsubscribeResultCallback)
@ -363,36 +352,27 @@ public:
return NS_OK; return NS_OK;
} }
nsRefPtr<WorkerUnsubscribeResultCallback> callback =
new WorkerUnsubscribeResultCallback(mProxy);
nsCOMPtr<nsIPushClient> client = nsCOMPtr<nsIPushClient> client =
do_CreateInstance("@mozilla.org/push/PushClient;1"); do_CreateInstance("@mozilla.org/push/PushClient;1");
if (!client) { if (!client) {
AutoJSAPI jsapi; callback->OnUnsubscribe(NS_ERROR_FAILURE, false);
jsapi.Init();
nsRefPtr<UnsubscribeResultRunnable> r =
new UnsubscribeResultRunnable(mProxy, NS_ERROR_FAILURE, false);
mProxy = nullptr;
r->Dispatch(jsapi.cx());
return NS_OK;
} }
nsCOMPtr<nsIPrincipal> principal = mProxy->GetWorkerPrivate()->GetPrincipal(); nsCOMPtr<nsIPrincipal> principal = mProxy->GetWorkerPrivate()->GetPrincipal();
nsRefPtr<WorkerUnsubscribeResultCallback> callback = if (NS_WARN_IF(NS_FAILED(client->Unsubscribe(mScope, principal, callback)))) {
new WorkerUnsubscribeResultCallback(mProxy); callback->OnUnsubscribe(NS_ERROR_FAILURE, false);
mProxy = nullptr; return NS_ERROR_FAILURE;
client->Unsubscribe(mScope, principal, callback); }
return NS_OK; return NS_OK;
} }
private: private:
~UnsubscribeRunnable() ~UnsubscribeRunnable()
{ {}
if (mProxy) {
AutoReleasePromiseWorkerProxy autoRelease(mProxy);
mProxy = nullptr;
}
}
nsRefPtr<PromiseWorkerProxy> mProxy; nsRefPtr<PromiseWorkerProxy> mProxy;
nsString mScope; nsString mScope;
}; };
@ -418,7 +398,7 @@ WorkerPushSubscription::Unsubscribe(ErrorResult &aRv)
nsRefPtr<UnsubscribeRunnable> r = nsRefPtr<UnsubscribeRunnable> r =
new UnsubscribeRunnable(proxy, mScope); new UnsubscribeRunnable(proxy, mScope);
NS_DispatchToMainThread(r); MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(r)));
return p.forget(); return p.forget();
} }
@ -476,12 +456,7 @@ public:
} }
private: private:
~GetSubscriptionResultRunnable() ~GetSubscriptionResultRunnable()
{ {}
if (mProxy) {
AutoReleasePromiseWorkerProxy autoRelease(mProxy);
mProxy = nullptr;
}
}
nsRefPtr<PromiseWorkerProxy> mProxy; nsRefPtr<PromiseWorkerProxy> mProxy;
nsresult mStatus; nsresult mStatus;
@ -498,12 +473,15 @@ public:
const nsAString& aScope) const nsAString& aScope)
: mProxy(aProxy) : mProxy(aProxy)
, mScope(aScope) , mScope(aScope)
, mCallbackCalled(false)
{} {}
NS_IMETHOD NS_IMETHOD
OnPushEndpoint(nsresult aStatus, const nsAString& aEndpoint) override OnPushEndpoint(nsresult aStatus, const nsAString& aEndpoint) override
{ {
AssertIsOnMainThread(); AssertIsOnMainThread();
mCallbackCalled = true;
if (!mProxy) { if (!mProxy) {
return NS_OK; return NS_OK;
} }
@ -518,24 +496,24 @@ public:
nsRefPtr<GetSubscriptionResultRunnable> r = nsRefPtr<GetSubscriptionResultRunnable> r =
new GetSubscriptionResultRunnable(mProxy, aStatus, aEndpoint, mScope); new GetSubscriptionResultRunnable(mProxy, aStatus, aEndpoint, mScope);
mProxy = nullptr; if (!r->Dispatch(jsapi.cx())) {
ReleasePromiseWorkerProxy(mProxy.forget());
r->Dispatch(jsapi.cx()); }
return NS_OK; return NS_OK;
} }
protected: protected:
~GetSubscriptionCallback() ~GetSubscriptionCallback()
{ {
if (mProxy) { // Enforces that GetSubscriptionRunnable uses the callback for error
AutoReleasePromiseWorkerProxy autoRelease(mProxy); // reporting once it creates the callback.
mProxy = nullptr; MOZ_ASSERT(mCallbackCalled);
}
} }
private: private:
nsRefPtr<PromiseWorkerProxy> mProxy; nsRefPtr<PromiseWorkerProxy> mProxy;
nsString mScope; nsString mScope;
DebugOnly<bool> mCallbackCalled;
}; };
NS_IMPL_ISUPPORTS(GetSubscriptionCallback, nsIPushEndpointCallback) NS_IMPL_ISUPPORTS(GetSubscriptionCallback, nsIPushEndpointCallback)
@ -559,14 +537,12 @@ public:
return NS_OK; return NS_OK;
} }
nsRefPtr<GetSubscriptionCallback> callback = new GetSubscriptionCallback(mProxy, mScope);
nsCOMPtr<nsIPermissionManager> permManager = nsCOMPtr<nsIPermissionManager> permManager =
mozilla::services::GetPermissionManager(); mozilla::services::GetPermissionManager();
AutoJSAPI jsapi;
jsapi.Init();
if (!permManager) { if (!permManager) {
Fail(jsapi.cx()); callback->OnPushEndpoint(NS_ERROR_FAILURE, EmptyString());
return NS_OK; return NS_OK;
} }
@ -577,47 +553,38 @@ public:
&permission); &permission);
if (NS_WARN_IF(NS_FAILED(rv)) || permission != nsIPermissionManager::ALLOW_ACTION) { if (NS_WARN_IF(NS_FAILED(rv)) || permission != nsIPermissionManager::ALLOW_ACTION) {
Fail(jsapi.cx()); callback->OnPushEndpoint(NS_ERROR_FAILURE, EmptyString());
return NS_OK; return NS_OK;
} }
nsCOMPtr<nsIPushClient> client = nsCOMPtr<nsIPushClient> client =
do_CreateInstance("@mozilla.org/push/PushClient;1"); do_CreateInstance("@mozilla.org/push/PushClient;1");
if (!client) { if (!client) {
Fail(jsapi.cx()); callback->OnPushEndpoint(NS_ERROR_FAILURE, EmptyString());
return NS_OK; return NS_OK;
} }
nsCOMPtr<nsIPrincipal> principal = mProxy->GetWorkerPrivate()->GetPrincipal(); nsCOMPtr<nsIPrincipal> principal = mProxy->GetWorkerPrivate()->GetPrincipal();
nsRefPtr<GetSubscriptionCallback> callback = new GetSubscriptionCallback(mProxy, mScope);
mProxy = nullptr; mProxy = nullptr;
if (mAction == WorkerPushManager::SubscribeAction) { if (mAction == WorkerPushManager::SubscribeAction) {
return client->Subscribe(mScope, principal, callback); rv = client->Subscribe(mScope, principal, callback);
} else { } else {
MOZ_ASSERT(mAction == WorkerPushManager::GetSubscriptionAction); MOZ_ASSERT(mAction == WorkerPushManager::GetSubscriptionAction);
return client->GetSubscription(mScope, principal, callback); rv = client->GetSubscription(mScope, principal, callback);
} }
if (NS_WARN_IF(NS_FAILED(rv))) {
callback->OnPushEndpoint(NS_ERROR_FAILURE, EmptyString());
return rv;
}
return NS_OK;
} }
private: private:
void
Fail(JSContext* aCx)
{
nsRefPtr<GetSubscriptionResultRunnable> r =
new GetSubscriptionResultRunnable(mProxy, NS_ERROR_FAILURE, EmptyString(), mScope);
mProxy = nullptr;
r->Dispatch(aCx);
}
~GetSubscriptionRunnable() ~GetSubscriptionRunnable()
{ {}
if (mProxy) {
AutoReleasePromiseWorkerProxy autoRelease(mProxy);
mProxy = nullptr;
}
}
nsRefPtr<PromiseWorkerProxy> mProxy; nsRefPtr<PromiseWorkerProxy> mProxy;
nsString mScope; nsString mScope;
@ -645,7 +612,7 @@ WorkerPushManager::PerformSubscriptionAction(SubscriptionAction aAction, ErrorRe
nsRefPtr<GetSubscriptionRunnable> r = nsRefPtr<GetSubscriptionRunnable> r =
new GetSubscriptionRunnable(proxy, mScope, aAction); new GetSubscriptionRunnable(proxy, mScope, aAction);
NS_DispatchToMainThread(r); MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(r)));
return p.forget(); return p.forget();
} }
@ -698,12 +665,7 @@ public:
private: private:
~PermissionResultRunnable() ~PermissionResultRunnable()
{ {}
if (mProxy) {
AutoReleasePromiseWorkerProxy autoRelease(mProxy);
mProxy = nullptr;
}
}
nsRefPtr<PromiseWorkerProxy> mProxy; nsRefPtr<PromiseWorkerProxy> mProxy;
nsresult mStatus; nsresult mStatus;
@ -729,55 +691,46 @@ public:
nsCOMPtr<nsIPermissionManager> permManager = nsCOMPtr<nsIPermissionManager> permManager =
mozilla::services::GetPermissionManager(); mozilla::services::GetPermissionManager();
nsresult rv = NS_ERROR_FAILURE;
PushPermissionState state = PushPermissionState::Denied; PushPermissionState state = PushPermissionState::Denied;
if (permManager) {
uint32_t permission = nsIPermissionManager::DENY_ACTION;
rv = permManager->TestExactPermissionFromPrincipal(
mProxy->GetWorkerPrivate()->GetPrincipal(),
"push",
&permission);
if (NS_SUCCEEDED(rv)) {
switch (permission) {
case nsIPermissionManager::ALLOW_ACTION:
state = PushPermissionState::Granted;
break;
case nsIPermissionManager::DENY_ACTION:
state = PushPermissionState::Denied;
break;
case nsIPermissionManager::PROMPT_ACTION:
state = PushPermissionState::Prompt;
break;
default:
MOZ_CRASH("Unexpected case!");
}
}
}
AutoJSAPI jsapi; AutoJSAPI jsapi;
jsapi.Init(); jsapi.Init();
if (!permManager) {
nsRefPtr<PermissionResultRunnable> r =
new PermissionResultRunnable(mProxy, NS_ERROR_FAILURE, state);
mProxy = nullptr;
r->Dispatch(jsapi.cx());
return NS_OK;
}
uint32_t permission = nsIPermissionManager::DENY_ACTION;
nsresult rv = permManager->TestExactPermissionFromPrincipal(
mProxy->GetWorkerPrivate()->GetPrincipal(),
"push",
&permission);
switch (permission) {
case nsIPermissionManager::ALLOW_ACTION:
state = PushPermissionState::Granted;
break;
case nsIPermissionManager::DENY_ACTION:
state = PushPermissionState::Denied;
break;
case nsIPermissionManager::PROMPT_ACTION:
state = PushPermissionState::Prompt;
break;
default:
MOZ_CRASH("Unexpected case!");
}
nsRefPtr<PermissionResultRunnable> r = nsRefPtr<PermissionResultRunnable> r =
new PermissionResultRunnable(mProxy, rv, state); new PermissionResultRunnable(mProxy, rv, state);
mProxy = nullptr; if (!r->Dispatch(jsapi.cx())) {
r->Dispatch(jsapi.cx()); ReleasePromiseWorkerProxy(mProxy.forget());
}
return NS_OK; return NS_OK;
} }
private: private:
~PermissionStateRunnable() ~PermissionStateRunnable()
{ {}
if (mProxy) {
AutoReleasePromiseWorkerProxy autoRelease(mProxy);
mProxy = nullptr;
}
}
nsRefPtr<PromiseWorkerProxy> mProxy; nsRefPtr<PromiseWorkerProxy> mProxy;
}; };