Bug 1708193 - Remove mozapps/extensions/internal/Content.js r=rpl
[gecko.git] / dom / push / PushSubscription.cpp
blobfd1ddb8a671699b2b7f635773271dd5808b5ade7
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/dom/PushSubscription.h"
9 #include "nsIPushService.h"
10 #include "nsIScriptObjectPrincipal.h"
11 #include "nsServiceManagerUtils.h"
13 #include "mozilla/Base64.h"
14 #include "mozilla/Unused.h"
16 #include "mozilla/dom/Promise.h"
17 #include "mozilla/dom/PromiseWorkerProxy.h"
18 #include "mozilla/dom/PushSubscriptionOptions.h"
19 #include "mozilla/dom/PushUtil.h"
20 #include "mozilla/dom/WorkerCommon.h"
21 #include "mozilla/dom/WorkerPrivate.h"
22 #include "mozilla/dom/WorkerRunnable.h"
23 #include "mozilla/dom/WorkerScope.h"
25 namespace mozilla {
26 namespace dom {
28 namespace {
30 class UnsubscribeResultCallback final : public nsIUnsubscribeResultCallback {
31 public:
32 NS_DECL_ISUPPORTS
34 explicit UnsubscribeResultCallback(Promise* aPromise) : mPromise(aPromise) {
35 AssertIsOnMainThread();
38 NS_IMETHOD
39 OnUnsubscribe(nsresult aStatus, bool aSuccess) override {
40 if (NS_SUCCEEDED(aStatus)) {
41 mPromise->MaybeResolve(aSuccess);
42 } else {
43 mPromise->MaybeReject(NS_ERROR_DOM_PUSH_SERVICE_UNREACHABLE);
46 return NS_OK;
49 private:
50 ~UnsubscribeResultCallback() = default;
52 RefPtr<Promise> mPromise;
55 NS_IMPL_ISUPPORTS(UnsubscribeResultCallback, nsIUnsubscribeResultCallback)
57 class UnsubscribeResultRunnable final : public WorkerRunnable {
58 public:
59 UnsubscribeResultRunnable(WorkerPrivate* aWorkerPrivate,
60 RefPtr<PromiseWorkerProxy>&& aProxy,
61 nsresult aStatus, bool aSuccess)
62 : WorkerRunnable(aWorkerPrivate),
63 mProxy(std::move(aProxy)),
64 mStatus(aStatus),
65 mSuccess(aSuccess) {
66 AssertIsOnMainThread();
69 bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
70 MOZ_ASSERT(aWorkerPrivate);
71 aWorkerPrivate->AssertIsOnWorkerThread();
73 RefPtr<Promise> promise = mProxy->WorkerPromise();
74 if (NS_SUCCEEDED(mStatus)) {
75 promise->MaybeResolve(mSuccess);
76 } else {
77 promise->MaybeReject(NS_ERROR_DOM_PUSH_SERVICE_UNREACHABLE);
80 mProxy->CleanUp();
82 return true;
85 private:
86 ~UnsubscribeResultRunnable() = default;
88 RefPtr<PromiseWorkerProxy> mProxy;
89 nsresult mStatus;
90 bool mSuccess;
93 class WorkerUnsubscribeResultCallback final
94 : public nsIUnsubscribeResultCallback {
95 public:
96 NS_DECL_ISUPPORTS
98 explicit WorkerUnsubscribeResultCallback(PromiseWorkerProxy* aProxy)
99 : mProxy(aProxy) {
100 AssertIsOnMainThread();
103 NS_IMETHOD
104 OnUnsubscribe(nsresult aStatus, bool aSuccess) override {
105 AssertIsOnMainThread();
106 MOZ_ASSERT(mProxy, "OnUnsubscribe() called twice?");
108 MutexAutoLock lock(mProxy->Lock());
109 if (mProxy->CleanedUp()) {
110 return NS_OK;
113 WorkerPrivate* worker = mProxy->GetWorkerPrivate();
114 RefPtr<UnsubscribeResultRunnable> r = new UnsubscribeResultRunnable(
115 worker, std::move(mProxy), aStatus, aSuccess);
116 MOZ_ALWAYS_TRUE(r->Dispatch());
118 return NS_OK;
121 private:
122 ~WorkerUnsubscribeResultCallback() = default;
124 RefPtr<PromiseWorkerProxy> mProxy;
127 NS_IMPL_ISUPPORTS(WorkerUnsubscribeResultCallback, nsIUnsubscribeResultCallback)
129 class UnsubscribeRunnable final : public Runnable {
130 public:
131 UnsubscribeRunnable(PromiseWorkerProxy* aProxy, const nsAString& aScope)
132 : Runnable("dom::UnsubscribeRunnable"), mProxy(aProxy), mScope(aScope) {
133 MOZ_ASSERT(aProxy);
134 MOZ_ASSERT(!aScope.IsEmpty());
137 NS_IMETHOD
138 Run() override {
139 AssertIsOnMainThread();
141 nsCOMPtr<nsIPrincipal> principal;
144 MutexAutoLock lock(mProxy->Lock());
145 if (mProxy->CleanedUp()) {
146 return NS_OK;
148 principal = mProxy->GetWorkerPrivate()->GetPrincipal();
151 MOZ_ASSERT(principal);
153 RefPtr<WorkerUnsubscribeResultCallback> callback =
154 new WorkerUnsubscribeResultCallback(mProxy);
156 nsCOMPtr<nsIPushService> service =
157 do_GetService("@mozilla.org/push/Service;1");
158 if (NS_WARN_IF(!service)) {
159 callback->OnUnsubscribe(NS_ERROR_FAILURE, false);
160 return NS_OK;
163 if (NS_WARN_IF(
164 NS_FAILED(service->Unsubscribe(mScope, principal, callback)))) {
165 callback->OnUnsubscribe(NS_ERROR_FAILURE, false);
166 return NS_OK;
169 return NS_OK;
172 private:
173 ~UnsubscribeRunnable() = default;
175 RefPtr<PromiseWorkerProxy> mProxy;
176 nsString mScope;
179 } // anonymous namespace
181 PushSubscription::PushSubscription(nsIGlobalObject* aGlobal,
182 const nsAString& aEndpoint,
183 const nsAString& aScope,
184 Nullable<EpochTimeStamp>&& aExpirationTime,
185 nsTArray<uint8_t>&& aRawP256dhKey,
186 nsTArray<uint8_t>&& aAuthSecret,
187 nsTArray<uint8_t>&& aAppServerKey)
188 : mEndpoint(aEndpoint),
189 mScope(aScope),
190 mExpirationTime(std::move(aExpirationTime)),
191 mRawP256dhKey(std::move(aRawP256dhKey)),
192 mAuthSecret(std::move(aAuthSecret)) {
193 if (NS_IsMainThread()) {
194 mGlobal = aGlobal;
195 } else {
196 #ifdef DEBUG
197 // There's only one global on a worker, so we don't need to pass a global
198 // object to the constructor.
199 WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
200 MOZ_ASSERT(worker);
201 worker->AssertIsOnWorkerThread();
202 #endif
204 mOptions = new PushSubscriptionOptions(mGlobal, std::move(aAppServerKey));
207 PushSubscription::~PushSubscription() = default;
209 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(PushSubscription, mGlobal, mOptions)
210 NS_IMPL_CYCLE_COLLECTING_ADDREF(PushSubscription)
211 NS_IMPL_CYCLE_COLLECTING_RELEASE(PushSubscription)
212 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PushSubscription)
213 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
214 NS_INTERFACE_MAP_ENTRY(nsISupports)
215 NS_INTERFACE_MAP_END
217 JSObject* PushSubscription::WrapObject(JSContext* aCx,
218 JS::Handle<JSObject*> aGivenProto) {
219 return PushSubscription_Binding::Wrap(aCx, this, aGivenProto);
222 // static
223 already_AddRefed<PushSubscription> PushSubscription::Constructor(
224 GlobalObject& aGlobal, const PushSubscriptionInit& aInitDict,
225 ErrorResult& aRv) {
226 nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
228 nsTArray<uint8_t> rawKey;
229 if (aInitDict.mP256dhKey.WasPassed() &&
230 !aInitDict.mP256dhKey.Value().IsNull() &&
231 !PushUtil::CopyArrayBufferToArray(aInitDict.mP256dhKey.Value().Value(),
232 rawKey)) {
233 aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
234 return nullptr;
237 nsTArray<uint8_t> authSecret;
238 if (aInitDict.mAuthSecret.WasPassed() &&
239 !aInitDict.mAuthSecret.Value().IsNull() &&
240 !PushUtil::CopyArrayBufferToArray(aInitDict.mAuthSecret.Value().Value(),
241 authSecret)) {
242 aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
243 return nullptr;
246 nsTArray<uint8_t> appServerKey;
247 if (aInitDict.mAppServerKey.WasPassed() &&
248 !aInitDict.mAppServerKey.Value().IsNull()) {
249 const OwningArrayBufferViewOrArrayBuffer& bufferSource =
250 aInitDict.mAppServerKey.Value().Value();
251 if (!PushUtil::CopyBufferSourceToArray(bufferSource, appServerKey)) {
252 aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
253 return nullptr;
257 Nullable<EpochTimeStamp> expirationTime;
258 if (aInitDict.mExpirationTime.IsNull()) {
259 expirationTime.SetNull();
260 } else {
261 expirationTime.SetValue(aInitDict.mExpirationTime.Value());
264 RefPtr<PushSubscription> sub = new PushSubscription(
265 global, aInitDict.mEndpoint, aInitDict.mScope, std::move(expirationTime),
266 std::move(rawKey), std::move(authSecret), std::move(appServerKey));
268 return sub.forget();
271 already_AddRefed<Promise> PushSubscription::Unsubscribe(ErrorResult& aRv) {
272 if (!NS_IsMainThread()) {
273 RefPtr<Promise> p = UnsubscribeFromWorker(aRv);
274 return p.forget();
277 MOZ_ASSERT(mGlobal);
279 nsCOMPtr<nsIPushService> service =
280 do_GetService("@mozilla.org/push/Service;1");
281 if (NS_WARN_IF(!service)) {
282 aRv.Throw(NS_ERROR_FAILURE);
283 return nullptr;
286 nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(mGlobal);
287 if (!sop) {
288 aRv.Throw(NS_ERROR_FAILURE);
289 return nullptr;
292 RefPtr<Promise> p = Promise::Create(mGlobal, aRv);
293 if (NS_WARN_IF(aRv.Failed())) {
294 return nullptr;
297 RefPtr<UnsubscribeResultCallback> callback = new UnsubscribeResultCallback(p);
298 Unused << NS_WARN_IF(
299 NS_FAILED(service->Unsubscribe(mScope, sop->GetPrincipal(), callback)));
301 return p.forget();
304 void PushSubscription::GetKey(JSContext* aCx, PushEncryptionKeyName aType,
305 JS::MutableHandle<JSObject*> aKey,
306 ErrorResult& aRv) {
307 if (aType == PushEncryptionKeyName::P256dh) {
308 PushUtil::CopyArrayToArrayBuffer(aCx, mRawP256dhKey, aKey, aRv);
309 } else if (aType == PushEncryptionKeyName::Auth) {
310 PushUtil::CopyArrayToArrayBuffer(aCx, mAuthSecret, aKey, aRv);
311 } else {
312 aKey.set(nullptr);
316 void PushSubscription::ToJSON(PushSubscriptionJSON& aJSON, ErrorResult& aRv) {
317 aJSON.mEndpoint.Construct();
318 aJSON.mEndpoint.Value() = mEndpoint;
320 aJSON.mKeys.mP256dh.Construct();
321 nsresult rv = Base64URLEncode(
322 mRawP256dhKey.Length(), mRawP256dhKey.Elements(),
323 Base64URLEncodePaddingPolicy::Omit, aJSON.mKeys.mP256dh.Value());
324 if (NS_WARN_IF(NS_FAILED(rv))) {
325 aRv.Throw(rv);
326 return;
329 aJSON.mKeys.mAuth.Construct();
330 rv = Base64URLEncode(mAuthSecret.Length(), mAuthSecret.Elements(),
331 Base64URLEncodePaddingPolicy::Omit,
332 aJSON.mKeys.mAuth.Value());
333 if (NS_WARN_IF(NS_FAILED(rv))) {
334 aRv.Throw(rv);
335 return;
337 aJSON.mExpirationTime.Construct(mExpirationTime);
340 already_AddRefed<PushSubscriptionOptions> PushSubscription::Options() {
341 RefPtr<PushSubscriptionOptions> options = mOptions;
342 return options.forget();
345 already_AddRefed<Promise> PushSubscription::UnsubscribeFromWorker(
346 ErrorResult& aRv) {
347 WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
348 MOZ_ASSERT(worker);
349 worker->AssertIsOnWorkerThread();
351 nsCOMPtr<nsIGlobalObject> global = worker->GlobalScope();
352 RefPtr<Promise> p = Promise::Create(global, aRv);
353 if (NS_WARN_IF(aRv.Failed())) {
354 return nullptr;
357 RefPtr<PromiseWorkerProxy> proxy = PromiseWorkerProxy::Create(worker, p);
358 if (!proxy) {
359 p->MaybeReject(NS_ERROR_DOM_PUSH_SERVICE_UNREACHABLE);
360 return p.forget();
363 RefPtr<UnsubscribeRunnable> r = new UnsubscribeRunnable(proxy, mScope);
364 MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(r));
366 return p.forget();
369 } // namespace dom
370 } // namespace mozilla