2016-03-31 20:49:07 +03:00
|
|
|
/* 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 "mozilla/dom/PushSubscription.h"
|
|
|
|
|
|
|
|
#include "nsIPushService.h"
|
|
|
|
#include "nsIScriptObjectPrincipal.h"
|
|
|
|
|
|
|
|
#include "mozilla/Base64.h"
|
|
|
|
#include "mozilla/unused.h"
|
|
|
|
|
|
|
|
#include "mozilla/dom/Promise.h"
|
|
|
|
#include "mozilla/dom/PromiseWorkerProxy.h"
|
|
|
|
#include "mozilla/dom/WorkerPrivate.h"
|
|
|
|
#include "mozilla/dom/WorkerScope.h"
|
|
|
|
#include "mozilla/dom/workers/Workers.h"
|
|
|
|
|
|
|
|
namespace mozilla {
|
|
|
|
namespace dom {
|
|
|
|
|
|
|
|
using namespace workers;
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
class UnsubscribeResultCallback final : public nsIUnsubscribeResultCallback
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
NS_DECL_ISUPPORTS
|
|
|
|
|
|
|
|
explicit UnsubscribeResultCallback(Promise* aPromise)
|
|
|
|
: mPromise(aPromise)
|
|
|
|
{
|
|
|
|
AssertIsOnMainThread();
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHOD
|
|
|
|
OnUnsubscribe(nsresult aStatus, bool aSuccess) override
|
|
|
|
{
|
|
|
|
if (NS_SUCCEEDED(aStatus)) {
|
|
|
|
mPromise->MaybeResolve(aSuccess);
|
|
|
|
} else {
|
|
|
|
mPromise->MaybeReject(NS_ERROR_DOM_PUSH_SERVICE_UNREACHABLE);
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
~UnsubscribeResultCallback()
|
|
|
|
{}
|
|
|
|
|
|
|
|
RefPtr<Promise> mPromise;
|
|
|
|
};
|
|
|
|
|
|
|
|
NS_IMPL_ISUPPORTS(UnsubscribeResultCallback, nsIUnsubscribeResultCallback)
|
|
|
|
|
|
|
|
class UnsubscribeResultRunnable final : public WorkerRunnable
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
UnsubscribeResultRunnable(WorkerPrivate* aWorkerPrivate,
|
|
|
|
already_AddRefed<PromiseWorkerProxy>&& aProxy,
|
|
|
|
nsresult aStatus,
|
|
|
|
bool aSuccess)
|
|
|
|
: WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount)
|
|
|
|
, mProxy(Move(aProxy))
|
|
|
|
, mStatus(aStatus)
|
|
|
|
, mSuccess(aSuccess)
|
|
|
|
{
|
|
|
|
AssertIsOnMainThread();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(aWorkerPrivate);
|
|
|
|
aWorkerPrivate->AssertIsOnWorkerThread();
|
|
|
|
|
|
|
|
RefPtr<Promise> promise = mProxy->WorkerPromise();
|
|
|
|
if (NS_SUCCEEDED(mStatus)) {
|
|
|
|
promise->MaybeResolve(mSuccess);
|
|
|
|
} else {
|
|
|
|
promise->MaybeReject(NS_ERROR_DOM_PUSH_SERVICE_UNREACHABLE);
|
|
|
|
}
|
|
|
|
|
|
|
|
mProxy->CleanUp();
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
private:
|
|
|
|
~UnsubscribeResultRunnable()
|
|
|
|
{}
|
|
|
|
|
|
|
|
RefPtr<PromiseWorkerProxy> mProxy;
|
|
|
|
nsresult mStatus;
|
|
|
|
bool mSuccess;
|
|
|
|
};
|
|
|
|
|
|
|
|
class WorkerUnsubscribeResultCallback final : public nsIUnsubscribeResultCallback
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
NS_DECL_ISUPPORTS
|
|
|
|
|
|
|
|
explicit WorkerUnsubscribeResultCallback(PromiseWorkerProxy* aProxy)
|
|
|
|
: mProxy(aProxy)
|
|
|
|
{
|
|
|
|
AssertIsOnMainThread();
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHOD
|
|
|
|
OnUnsubscribe(nsresult aStatus, bool aSuccess) override
|
|
|
|
{
|
|
|
|
AssertIsOnMainThread();
|
|
|
|
MOZ_ASSERT(mProxy, "OnUnsubscribe() called twice?");
|
|
|
|
|
|
|
|
MutexAutoLock lock(mProxy->Lock());
|
|
|
|
if (mProxy->CleanedUp()) {
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
WorkerPrivate* worker = mProxy->GetWorkerPrivate();
|
|
|
|
RefPtr<UnsubscribeResultRunnable> r =
|
|
|
|
new UnsubscribeResultRunnable(worker, mProxy.forget(), aStatus, aSuccess);
|
|
|
|
MOZ_ALWAYS_TRUE(r->Dispatch());
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
~WorkerUnsubscribeResultCallback()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
RefPtr<PromiseWorkerProxy> mProxy;
|
|
|
|
};
|
|
|
|
|
|
|
|
NS_IMPL_ISUPPORTS(WorkerUnsubscribeResultCallback, nsIUnsubscribeResultCallback)
|
|
|
|
|
|
|
|
class UnsubscribeRunnable final : public nsRunnable
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
UnsubscribeRunnable(PromiseWorkerProxy* aProxy,
|
|
|
|
const nsAString& aScope)
|
|
|
|
: mProxy(aProxy)
|
|
|
|
, mScope(aScope)
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(aProxy);
|
|
|
|
MOZ_ASSERT(!aScope.IsEmpty());
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHOD
|
|
|
|
Run() override
|
|
|
|
{
|
|
|
|
AssertIsOnMainThread();
|
|
|
|
|
|
|
|
nsCOMPtr<nsIPrincipal> principal;
|
|
|
|
|
|
|
|
{
|
|
|
|
MutexAutoLock lock(mProxy->Lock());
|
|
|
|
if (mProxy->CleanedUp()) {
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
principal = mProxy->GetWorkerPrivate()->GetPrincipal();
|
|
|
|
}
|
|
|
|
|
|
|
|
MOZ_ASSERT(principal);
|
|
|
|
|
|
|
|
RefPtr<WorkerUnsubscribeResultCallback> callback =
|
|
|
|
new WorkerUnsubscribeResultCallback(mProxy);
|
|
|
|
|
|
|
|
nsCOMPtr<nsIPushService> service =
|
|
|
|
do_GetService("@mozilla.org/push/Service;1");
|
|
|
|
if (NS_WARN_IF(!service)) {
|
|
|
|
callback->OnUnsubscribe(NS_ERROR_FAILURE, false);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (NS_WARN_IF(NS_FAILED(service->Unsubscribe(mScope, principal, callback)))) {
|
|
|
|
callback->OnUnsubscribe(NS_ERROR_FAILURE, false);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
~UnsubscribeRunnable()
|
|
|
|
{}
|
|
|
|
|
|
|
|
RefPtr<PromiseWorkerProxy> mProxy;
|
|
|
|
nsString mScope;
|
|
|
|
};
|
|
|
|
|
2016-04-02 01:25:49 +03:00
|
|
|
bool
|
|
|
|
CopyArrayBufferToArray(const ArrayBuffer& aBuffer,
|
|
|
|
nsTArray<uint8_t>& aArray)
|
|
|
|
{
|
|
|
|
aBuffer.ComputeLengthAndData();
|
|
|
|
if (!aArray.SetLength(aBuffer.Length(), fallible) ||
|
|
|
|
!aArray.ReplaceElementsAt(0, aBuffer.Length(), aBuffer.Data(),
|
|
|
|
aBuffer.Length(), fallible)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-03-31 20:49:07 +03:00
|
|
|
} // anonymous namespace
|
|
|
|
|
|
|
|
PushSubscription::PushSubscription(nsIGlobalObject* aGlobal,
|
|
|
|
const nsAString& aEndpoint,
|
|
|
|
const nsAString& aScope,
|
2016-04-02 01:25:49 +03:00
|
|
|
nsTArray<uint8_t>&& aRawP256dhKey,
|
|
|
|
nsTArray<uint8_t>&& aAuthSecret)
|
2016-03-31 20:49:07 +03:00
|
|
|
: mEndpoint(aEndpoint)
|
|
|
|
, mScope(aScope)
|
2016-04-02 01:25:49 +03:00
|
|
|
, mRawP256dhKey(Move(aRawP256dhKey))
|
|
|
|
, mAuthSecret(Move(aAuthSecret))
|
2016-03-31 20:49:07 +03:00
|
|
|
{
|
|
|
|
if (NS_IsMainThread()) {
|
|
|
|
mGlobal = aGlobal;
|
|
|
|
} else {
|
|
|
|
#ifdef DEBUG
|
|
|
|
// There's only one global on a worker, so we don't need to pass a global
|
|
|
|
// object to the constructor.
|
|
|
|
WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
|
|
|
|
MOZ_ASSERT(worker);
|
|
|
|
worker->AssertIsOnWorkerThread();
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
PushSubscription::~PushSubscription()
|
|
|
|
{}
|
|
|
|
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(PushSubscription, mGlobal)
|
|
|
|
|
|
|
|
NS_IMPL_CYCLE_COLLECTING_ADDREF(PushSubscription)
|
|
|
|
NS_IMPL_CYCLE_COLLECTING_RELEASE(PushSubscription)
|
|
|
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PushSubscription)
|
|
|
|
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
|
|
|
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
|
|
|
NS_INTERFACE_MAP_END
|
|
|
|
|
|
|
|
JSObject*
|
|
|
|
PushSubscription::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
|
|
|
{
|
|
|
|
return PushSubscriptionBinding::Wrap(aCx, this, aGivenProto);
|
|
|
|
}
|
|
|
|
|
|
|
|
// static
|
|
|
|
already_AddRefed<PushSubscription>
|
|
|
|
PushSubscription::Constructor(GlobalObject& aGlobal,
|
|
|
|
const nsAString& aEndpoint,
|
|
|
|
const nsAString& aScope,
|
|
|
|
const Nullable<ArrayBuffer>& aP256dhKey,
|
|
|
|
const Nullable<ArrayBuffer>& aAuthSecret,
|
|
|
|
ErrorResult& aRv)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
|
|
|
|
|
2016-04-02 01:25:49 +03:00
|
|
|
nsTArray<uint8_t> rawKey, authSecret;
|
|
|
|
if ((!aP256dhKey.IsNull() && !CopyArrayBufferToArray(aP256dhKey.Value(),
|
|
|
|
rawKey)) ||
|
|
|
|
(!aAuthSecret.IsNull() && !CopyArrayBufferToArray(aAuthSecret.Value(),
|
|
|
|
authSecret))) {
|
|
|
|
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
|
|
|
return nullptr;
|
2016-03-31 20:49:07 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
RefPtr<PushSubscription> sub = new PushSubscription(global,
|
|
|
|
aEndpoint,
|
|
|
|
aScope,
|
2016-04-02 01:25:49 +03:00
|
|
|
Move(rawKey),
|
|
|
|
Move(authSecret));
|
2016-03-31 20:49:07 +03:00
|
|
|
|
|
|
|
return sub.forget();
|
|
|
|
}
|
|
|
|
|
|
|
|
already_AddRefed<Promise>
|
|
|
|
PushSubscription::Unsubscribe(ErrorResult& aRv)
|
|
|
|
{
|
|
|
|
if (!NS_IsMainThread()) {
|
|
|
|
RefPtr<Promise> p = UnsubscribeFromWorker(aRv);
|
|
|
|
return p.forget();
|
|
|
|
}
|
|
|
|
|
|
|
|
MOZ_ASSERT(mGlobal);
|
|
|
|
|
|
|
|
nsCOMPtr<nsIPushService> service =
|
|
|
|
do_GetService("@mozilla.org/push/Service;1");
|
|
|
|
if (NS_WARN_IF(!service)) {
|
|
|
|
aRv.Throw(NS_ERROR_FAILURE);
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(mGlobal);
|
|
|
|
if (!sop) {
|
|
|
|
aRv.Throw(NS_ERROR_FAILURE);
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
RefPtr<Promise> p = Promise::Create(mGlobal, aRv);
|
|
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
RefPtr<UnsubscribeResultCallback> callback =
|
|
|
|
new UnsubscribeResultCallback(p);
|
|
|
|
Unused << NS_WARN_IF(NS_FAILED(
|
|
|
|
service->Unsubscribe(mScope, sop->GetPrincipal(), callback)));
|
|
|
|
|
|
|
|
return p.forget();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
PushSubscription::GetKey(JSContext* aCx,
|
|
|
|
PushEncryptionKeyName aType,
|
|
|
|
JS::MutableHandle<JSObject*> aKey)
|
|
|
|
{
|
|
|
|
if (aType == PushEncryptionKeyName::P256dh && !mRawP256dhKey.IsEmpty()) {
|
|
|
|
aKey.set(ArrayBuffer::Create(aCx,
|
|
|
|
mRawP256dhKey.Length(),
|
|
|
|
mRawP256dhKey.Elements()));
|
|
|
|
} else if (aType == PushEncryptionKeyName::Auth && !mAuthSecret.IsEmpty()) {
|
|
|
|
aKey.set(ArrayBuffer::Create(aCx,
|
|
|
|
mAuthSecret.Length(),
|
|
|
|
mAuthSecret.Elements()));
|
|
|
|
} else {
|
|
|
|
aKey.set(nullptr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2016-03-22 22:09:04 +03:00
|
|
|
PushSubscription::ToJSON(PushSubscriptionJSON& aJSON, ErrorResult& aRv)
|
2016-03-31 20:49:07 +03:00
|
|
|
{
|
|
|
|
aJSON.mEndpoint.Construct();
|
|
|
|
aJSON.mEndpoint.Value() = mEndpoint;
|
|
|
|
|
2016-03-22 22:09:04 +03:00
|
|
|
Base64URLEncodeOptions encodeOptions;
|
|
|
|
encodeOptions.mPad = false;
|
|
|
|
|
2016-03-31 20:49:07 +03:00
|
|
|
aJSON.mKeys.mP256dh.Construct();
|
|
|
|
nsresult rv = Base64URLEncode(mRawP256dhKey.Length(),
|
|
|
|
mRawP256dhKey.Elements(),
|
2016-03-22 22:09:04 +03:00
|
|
|
encodeOptions,
|
2016-03-31 20:49:07 +03:00
|
|
|
aJSON.mKeys.mP256dh.Value());
|
2016-03-22 22:09:04 +03:00
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
aRv.Throw(rv);
|
|
|
|
return;
|
|
|
|
}
|
2016-03-31 20:49:07 +03:00
|
|
|
|
|
|
|
aJSON.mKeys.mAuth.Construct();
|
|
|
|
rv = Base64URLEncode(mAuthSecret.Length(), mAuthSecret.Elements(),
|
2016-03-22 22:09:04 +03:00
|
|
|
encodeOptions, aJSON.mKeys.mAuth.Value());
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
aRv.Throw(rv);
|
|
|
|
return;
|
|
|
|
}
|
2016-03-31 20:49:07 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
already_AddRefed<Promise>
|
|
|
|
PushSubscription::UnsubscribeFromWorker(ErrorResult& aRv)
|
|
|
|
{
|
|
|
|
WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
|
|
|
|
MOZ_ASSERT(worker);
|
|
|
|
worker->AssertIsOnWorkerThread();
|
|
|
|
|
|
|
|
nsCOMPtr<nsIGlobalObject> global = worker->GlobalScope();
|
|
|
|
RefPtr<Promise> p = Promise::Create(global, aRv);
|
|
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
RefPtr<PromiseWorkerProxy> proxy = PromiseWorkerProxy::Create(worker, p);
|
|
|
|
if (!proxy) {
|
|
|
|
p->MaybeReject(NS_ERROR_DOM_PUSH_SERVICE_UNREACHABLE);
|
|
|
|
return p.forget();
|
|
|
|
}
|
|
|
|
|
|
|
|
RefPtr<UnsubscribeRunnable> r =
|
|
|
|
new UnsubscribeRunnable(proxy, mScope);
|
|
|
|
MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(r));
|
|
|
|
|
|
|
|
return p.forget();
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace dom
|
|
|
|
} // namespace mozilla
|