Line data Source code
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/. */
6 :
7 : #include "ServiceWorkerRegistration.h"
8 :
9 : #include "ipc/ErrorIPCUtils.h"
10 : #include "mozilla/dom/Notification.h"
11 : #include "mozilla/dom/Promise.h"
12 : #include "mozilla/dom/PromiseWorkerProxy.h"
13 : #include "mozilla/dom/PushManagerBinding.h"
14 : #include "mozilla/dom/PushManager.h"
15 : #include "mozilla/dom/ServiceWorkerRegistrationBinding.h"
16 : #include "mozilla/Preferences.h"
17 : #include "mozilla/Services.h"
18 : #include "mozilla/Unused.h"
19 : #include "nsCycleCollectionParticipant.h"
20 : #include "nsNetUtil.h"
21 : #include "nsServiceManagerUtils.h"
22 : #include "ServiceWorker.h"
23 : #include "ServiceWorkerManager.h"
24 :
25 : #include "nsIDocument.h"
26 : #include "nsIServiceWorkerManager.h"
27 : #include "nsISupportsPrimitives.h"
28 : #include "nsPIDOMWindow.h"
29 : #include "nsContentUtils.h"
30 :
31 : #include "WorkerPrivate.h"
32 : #include "Workers.h"
33 : #include "WorkerScope.h"
34 :
35 : using namespace mozilla::dom::workers;
36 :
37 : namespace mozilla {
38 : namespace dom {
39 :
40 : /* static */ bool
41 1 : ServiceWorkerRegistration::Visible(JSContext* aCx, JSObject* aObj)
42 : {
43 1 : if (NS_IsMainThread()) {
44 0 : return Preferences::GetBool("dom.serviceWorkers.enabled", false);
45 : }
46 :
47 : // Otherwise check the pref via the work private helper
48 1 : WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx);
49 1 : if (!workerPrivate) {
50 0 : return false;
51 : }
52 :
53 1 : return workerPrivate->ServiceWorkersEnabled();
54 : }
55 :
56 : /* static */ bool
57 1 : ServiceWorkerRegistration::NotificationAPIVisible(JSContext* aCx, JSObject* aObj)
58 : {
59 1 : if (NS_IsMainThread()) {
60 0 : return Preferences::GetBool("dom.webnotifications.serviceworker.enabled", false);
61 : }
62 :
63 : // Otherwise check the pref via the work private helper
64 1 : WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx);
65 1 : if (!workerPrivate) {
66 0 : return false;
67 : }
68 :
69 1 : return workerPrivate->DOMServiceWorkerNotificationEnabled();
70 : }
71 :
72 : ////////////////////////////////////////////////////
73 : // Main Thread implementation
74 :
75 : class ServiceWorkerRegistrationMainThread final : public ServiceWorkerRegistration,
76 : public ServiceWorkerRegistrationListener
77 : {
78 : friend nsPIDOMWindowInner;
79 : public:
80 : NS_DECL_ISUPPORTS_INHERITED
81 0 : NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ServiceWorkerRegistrationMainThread,
82 : ServiceWorkerRegistration)
83 :
84 : ServiceWorkerRegistrationMainThread(nsPIDOMWindowInner* aWindow,
85 : const nsAString& aScope);
86 :
87 : already_AddRefed<Promise>
88 : Update(ErrorResult& aRv) override;
89 :
90 : already_AddRefed<Promise>
91 : Unregister(ErrorResult& aRv) override;
92 :
93 : // Partial interface from Notification API.
94 : already_AddRefed<Promise>
95 : ShowNotification(JSContext* aCx,
96 : const nsAString& aTitle,
97 : const NotificationOptions& aOptions,
98 : ErrorResult& aRv) override;
99 :
100 : already_AddRefed<Promise>
101 : GetNotifications(const GetNotificationOptions& aOptions,
102 : ErrorResult& aRv) override;
103 :
104 : already_AddRefed<ServiceWorker>
105 : GetInstalling() override;
106 :
107 : already_AddRefed<ServiceWorker>
108 : GetWaiting() override;
109 :
110 : already_AddRefed<ServiceWorker>
111 : GetActive() override;
112 :
113 : already_AddRefed<PushManager>
114 : GetPushManager(JSContext* aCx, ErrorResult& aRv) override;
115 :
116 : // DOMEventTargethelper
117 0 : void DisconnectFromOwner() override
118 : {
119 0 : StopListeningForEvents();
120 0 : ServiceWorkerRegistration::DisconnectFromOwner();
121 0 : }
122 :
123 : // ServiceWorkerRegistrationListener
124 : void
125 : UpdateFound() override;
126 :
127 : void
128 : InvalidateWorkers(WhichServiceWorker aWhichOnes) override;
129 :
130 : void
131 : TransitionWorker(WhichServiceWorker aWhichOne) override;
132 :
133 : void
134 : RegistrationRemoved() override;
135 :
136 : void
137 0 : GetScope(nsAString& aScope) const override
138 : {
139 0 : aScope = mScope;
140 0 : }
141 :
142 : private:
143 : ~ServiceWorkerRegistrationMainThread();
144 :
145 : already_AddRefed<ServiceWorker>
146 : GetWorkerReference(WhichServiceWorker aWhichOne);
147 :
148 : void
149 : StartListeningForEvents();
150 :
151 : void
152 : StopListeningForEvents();
153 :
154 : bool mListeningForEvents;
155 :
156 : // The following properties are cached here to ensure JS equality is satisfied
157 : // instead of acquiring a new worker instance from the ServiceWorkerManager
158 : // for every access. A null value is considered a cache miss.
159 : // These three may change to a new worker at any time.
160 : RefPtr<ServiceWorker> mInstallingWorker;
161 : RefPtr<ServiceWorker> mWaitingWorker;
162 : RefPtr<ServiceWorker> mActiveWorker;
163 :
164 : RefPtr<PushManager> mPushManager;
165 : };
166 :
167 0 : NS_IMPL_ADDREF_INHERITED(ServiceWorkerRegistrationMainThread, ServiceWorkerRegistration)
168 0 : NS_IMPL_RELEASE_INHERITED(ServiceWorkerRegistrationMainThread, ServiceWorkerRegistration)
169 :
170 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(ServiceWorkerRegistrationMainThread)
171 0 : NS_INTERFACE_MAP_END_INHERITING(ServiceWorkerRegistration)
172 :
173 0 : NS_IMPL_CYCLE_COLLECTION_INHERITED(ServiceWorkerRegistrationMainThread,
174 : ServiceWorkerRegistration,
175 : mPushManager,
176 : mInstallingWorker, mWaitingWorker, mActiveWorker);
177 :
178 0 : ServiceWorkerRegistrationMainThread::ServiceWorkerRegistrationMainThread(nsPIDOMWindowInner* aWindow,
179 0 : const nsAString& aScope)
180 : : ServiceWorkerRegistration(aWindow, aScope)
181 0 : , mListeningForEvents(false)
182 : {
183 0 : AssertIsOnMainThread();
184 0 : MOZ_ASSERT(aWindow);
185 0 : MOZ_ASSERT(aWindow->IsInnerWindow());
186 0 : StartListeningForEvents();
187 0 : }
188 :
189 0 : ServiceWorkerRegistrationMainThread::~ServiceWorkerRegistrationMainThread()
190 : {
191 0 : StopListeningForEvents();
192 0 : MOZ_ASSERT(!mListeningForEvents);
193 0 : }
194 :
195 :
196 : already_AddRefed<ServiceWorker>
197 0 : ServiceWorkerRegistrationMainThread::GetWorkerReference(WhichServiceWorker aWhichOne)
198 : {
199 0 : nsCOMPtr<nsPIDOMWindowInner> window = GetOwner();
200 0 : if (!window) {
201 0 : return nullptr;
202 : }
203 :
204 : nsresult rv;
205 : nsCOMPtr<nsIServiceWorkerManager> swm =
206 0 : do_GetService(SERVICEWORKERMANAGER_CONTRACTID, &rv);
207 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
208 0 : return nullptr;
209 : }
210 :
211 0 : nsCOMPtr<nsISupports> serviceWorker;
212 0 : switch(aWhichOne) {
213 : case WhichServiceWorker::INSTALLING_WORKER:
214 0 : rv = swm->GetInstalling(window, mScope, getter_AddRefs(serviceWorker));
215 0 : break;
216 : case WhichServiceWorker::WAITING_WORKER:
217 0 : rv = swm->GetWaiting(window, mScope, getter_AddRefs(serviceWorker));
218 0 : break;
219 : case WhichServiceWorker::ACTIVE_WORKER:
220 0 : rv = swm->GetActive(window, mScope, getter_AddRefs(serviceWorker));
221 0 : break;
222 : default:
223 0 : MOZ_CRASH("Invalid enum value");
224 : }
225 :
226 0 : NS_WARNING_ASSERTION(
227 : NS_SUCCEEDED(rv) || rv == NS_ERROR_DOM_NOT_FOUND_ERR,
228 : "Unexpected error getting service worker instance from "
229 : "ServiceWorkerManager");
230 0 : if (NS_FAILED(rv)) {
231 0 : return nullptr;
232 : }
233 :
234 : RefPtr<ServiceWorker> ref =
235 0 : static_cast<ServiceWorker*>(serviceWorker.get());
236 0 : return ref.forget();
237 : }
238 :
239 : // XXXnsm, maybe this can be optimized to only add when a event handler is
240 : // registered.
241 : void
242 0 : ServiceWorkerRegistrationMainThread::StartListeningForEvents()
243 : {
244 0 : AssertIsOnMainThread();
245 0 : MOZ_ASSERT(!mListeningForEvents);
246 0 : RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
247 0 : if (swm) {
248 0 : swm->AddRegistrationEventListener(mScope, this);
249 0 : mListeningForEvents = true;
250 : }
251 0 : }
252 :
253 : void
254 0 : ServiceWorkerRegistrationMainThread::StopListeningForEvents()
255 : {
256 0 : AssertIsOnMainThread();
257 0 : if (!mListeningForEvents) {
258 0 : return;
259 : }
260 :
261 0 : RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
262 0 : if (swm) {
263 0 : swm->RemoveRegistrationEventListener(mScope, this);
264 : }
265 0 : mListeningForEvents = false;
266 : }
267 :
268 : already_AddRefed<ServiceWorker>
269 0 : ServiceWorkerRegistrationMainThread::GetInstalling()
270 : {
271 0 : AssertIsOnMainThread();
272 0 : if (!mInstallingWorker) {
273 0 : mInstallingWorker = GetWorkerReference(WhichServiceWorker::INSTALLING_WORKER);
274 : }
275 :
276 0 : RefPtr<ServiceWorker> ret = mInstallingWorker;
277 0 : return ret.forget();
278 : }
279 :
280 : already_AddRefed<ServiceWorker>
281 0 : ServiceWorkerRegistrationMainThread::GetWaiting()
282 : {
283 0 : AssertIsOnMainThread();
284 0 : if (!mWaitingWorker) {
285 0 : mWaitingWorker = GetWorkerReference(WhichServiceWorker::WAITING_WORKER);
286 : }
287 :
288 0 : RefPtr<ServiceWorker> ret = mWaitingWorker;
289 0 : return ret.forget();
290 : }
291 :
292 : already_AddRefed<ServiceWorker>
293 0 : ServiceWorkerRegistrationMainThread::GetActive()
294 : {
295 0 : AssertIsOnMainThread();
296 0 : if (!mActiveWorker) {
297 0 : mActiveWorker = GetWorkerReference(WhichServiceWorker::ACTIVE_WORKER);
298 : }
299 :
300 0 : RefPtr<ServiceWorker> ret = mActiveWorker;
301 0 : return ret.forget();
302 : }
303 :
304 : void
305 0 : ServiceWorkerRegistrationMainThread::UpdateFound()
306 : {
307 0 : DispatchTrustedEvent(NS_LITERAL_STRING("updatefound"));
308 0 : }
309 :
310 : void
311 0 : ServiceWorkerRegistrationMainThread::TransitionWorker(WhichServiceWorker aWhichOne)
312 : {
313 0 : AssertIsOnMainThread();
314 :
315 : // We assert the worker's previous state because the 'statechange'
316 : // event is dispatched in a queued runnable.
317 0 : if (aWhichOne == WhichServiceWorker::INSTALLING_WORKER) {
318 0 : MOZ_ASSERT_IF(mInstallingWorker, mInstallingWorker->State() == ServiceWorkerState::Installing);
319 0 : mWaitingWorker = mInstallingWorker.forget();
320 0 : } else if (aWhichOne == WhichServiceWorker::WAITING_WORKER) {
321 0 : MOZ_ASSERT_IF(mWaitingWorker, mWaitingWorker->State() == ServiceWorkerState::Installed);
322 0 : mActiveWorker = mWaitingWorker.forget();
323 : } else {
324 0 : MOZ_ASSERT_UNREACHABLE("Invalid transition!");
325 : }
326 0 : }
327 :
328 : void
329 0 : ServiceWorkerRegistrationMainThread::InvalidateWorkers(WhichServiceWorker aWhichOnes)
330 : {
331 0 : AssertIsOnMainThread();
332 0 : if (aWhichOnes & WhichServiceWorker::INSTALLING_WORKER) {
333 0 : mInstallingWorker = nullptr;
334 : }
335 :
336 0 : if (aWhichOnes & WhichServiceWorker::WAITING_WORKER) {
337 0 : mWaitingWorker = nullptr;
338 : }
339 :
340 0 : if (aWhichOnes & WhichServiceWorker::ACTIVE_WORKER) {
341 0 : mActiveWorker = nullptr;
342 : }
343 :
344 0 : }
345 :
346 : void
347 0 : ServiceWorkerRegistrationMainThread::RegistrationRemoved()
348 : {
349 : // If the registration is being removed completely, remove it from the
350 : // window registration hash table so that a new registration would get a new
351 : // wrapper JS object.
352 0 : if (nsCOMPtr<nsPIDOMWindowInner> window = GetOwner()) {
353 0 : window->InvalidateServiceWorkerRegistration(mScope);
354 : }
355 0 : }
356 :
357 : namespace {
358 :
359 : void
360 0 : UpdateInternal(nsIPrincipal* aPrincipal,
361 : const nsAString& aScope,
362 : ServiceWorkerUpdateFinishCallback* aCallback)
363 : {
364 0 : AssertIsOnMainThread();
365 0 : MOZ_ASSERT(aPrincipal);
366 0 : MOZ_ASSERT(aCallback);
367 :
368 0 : RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
369 0 : if (!swm) {
370 : // browser shutdown
371 0 : return;
372 : }
373 :
374 0 : swm->Update(aPrincipal, NS_ConvertUTF16toUTF8(aScope), aCallback);
375 : }
376 :
377 : class MainThreadUpdateCallback final : public ServiceWorkerUpdateFinishCallback
378 : {
379 : RefPtr<Promise> mPromise;
380 :
381 0 : ~MainThreadUpdateCallback()
382 0 : { }
383 :
384 : public:
385 0 : explicit MainThreadUpdateCallback(Promise* aPromise)
386 0 : : mPromise(aPromise)
387 : {
388 0 : AssertIsOnMainThread();
389 0 : }
390 :
391 : void
392 0 : UpdateSucceeded(ServiceWorkerRegistrationInfo* aRegistration) override
393 : {
394 0 : mPromise->MaybeResolveWithUndefined();
395 0 : }
396 :
397 : void
398 0 : UpdateFailed(ErrorResult& aStatus) override
399 : {
400 0 : mPromise->MaybeReject(aStatus);
401 0 : }
402 : };
403 :
404 : class UpdateResultRunnable final : public WorkerRunnable
405 : {
406 : RefPtr<PromiseWorkerProxy> mPromiseProxy;
407 : IPC::Message mSerializedErrorResult;
408 :
409 0 : ~UpdateResultRunnable()
410 0 : {}
411 :
412 : public:
413 0 : UpdateResultRunnable(PromiseWorkerProxy* aPromiseProxy, ErrorResult& aStatus)
414 0 : : WorkerRunnable(aPromiseProxy->GetWorkerPrivate())
415 0 : , mPromiseProxy(aPromiseProxy)
416 : {
417 : // ErrorResult is not thread safe. Serialize it for transfer across
418 : // threads.
419 0 : IPC::WriteParam(&mSerializedErrorResult, aStatus);
420 0 : aStatus.SuppressException();
421 0 : }
422 :
423 : bool
424 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
425 : {
426 : // Deserialize the ErrorResult now that we are back in the worker
427 : // thread.
428 0 : ErrorResult status;
429 0 : PickleIterator iter = PickleIterator(mSerializedErrorResult);
430 0 : Unused << IPC::ReadParam(&mSerializedErrorResult, &iter, &status);
431 :
432 0 : Promise* promise = mPromiseProxy->WorkerPromise();
433 0 : if (status.Failed()) {
434 0 : promise->MaybeReject(status);
435 : } else {
436 0 : promise->MaybeResolveWithUndefined();
437 : }
438 0 : status.SuppressException();
439 0 : mPromiseProxy->CleanUp();
440 0 : return true;
441 : }
442 : };
443 :
444 : class WorkerThreadUpdateCallback final : public ServiceWorkerUpdateFinishCallback
445 : {
446 : RefPtr<PromiseWorkerProxy> mPromiseProxy;
447 :
448 0 : ~WorkerThreadUpdateCallback()
449 0 : {
450 0 : }
451 :
452 : public:
453 0 : explicit WorkerThreadUpdateCallback(PromiseWorkerProxy* aPromiseProxy)
454 0 : : mPromiseProxy(aPromiseProxy)
455 : {
456 0 : AssertIsOnMainThread();
457 0 : }
458 :
459 : void
460 0 : UpdateSucceeded(ServiceWorkerRegistrationInfo* aRegistration) override
461 : {
462 0 : ErrorResult rv(NS_OK);
463 0 : Finish(rv);
464 0 : }
465 :
466 : void
467 0 : UpdateFailed(ErrorResult& aStatus) override
468 : {
469 0 : Finish(aStatus);
470 0 : }
471 :
472 : void
473 0 : Finish(ErrorResult& aStatus)
474 : {
475 0 : if (!mPromiseProxy) {
476 0 : return;
477 : }
478 :
479 0 : RefPtr<PromiseWorkerProxy> proxy = mPromiseProxy.forget();
480 :
481 0 : MutexAutoLock lock(proxy->Lock());
482 0 : if (proxy->CleanedUp()) {
483 0 : return;
484 : }
485 :
486 : RefPtr<UpdateResultRunnable> r =
487 0 : new UpdateResultRunnable(proxy, aStatus);
488 0 : r->Dispatch();
489 : }
490 : };
491 :
492 : class UpdateRunnable final : public Runnable
493 : {
494 : public:
495 0 : UpdateRunnable(PromiseWorkerProxy* aPromiseProxy, const nsAString& aScope)
496 0 : : Runnable("dom::UpdateRunnable")
497 : , mPromiseProxy(aPromiseProxy)
498 0 : , mScope(aScope)
499 0 : {}
500 :
501 : NS_IMETHOD
502 0 : Run() override
503 : {
504 0 : AssertIsOnMainThread();
505 0 : ErrorResult result;
506 :
507 0 : nsCOMPtr<nsIPrincipal> principal;
508 : // UpdateInternal may try to reject the promise synchronously leading
509 : // to a deadlock.
510 : {
511 0 : MutexAutoLock lock(mPromiseProxy->Lock());
512 0 : if (mPromiseProxy->CleanedUp()) {
513 0 : return NS_OK;
514 : }
515 :
516 0 : principal = mPromiseProxy->GetWorkerPrivate()->GetPrincipal();
517 : }
518 0 : MOZ_ASSERT(principal);
519 :
520 : RefPtr<WorkerThreadUpdateCallback> cb =
521 0 : new WorkerThreadUpdateCallback(mPromiseProxy);
522 0 : UpdateInternal(principal, mScope, cb);
523 0 : return NS_OK;
524 : }
525 :
526 : private:
527 0 : ~UpdateRunnable()
528 0 : {}
529 :
530 : RefPtr<PromiseWorkerProxy> mPromiseProxy;
531 : const nsString mScope;
532 : };
533 :
534 : class UnregisterCallback final : public nsIServiceWorkerUnregisterCallback
535 : {
536 : RefPtr<Promise> mPromise;
537 :
538 : public:
539 : NS_DECL_ISUPPORTS
540 :
541 0 : explicit UnregisterCallback(Promise* aPromise)
542 0 : : mPromise(aPromise)
543 : {
544 0 : MOZ_ASSERT(mPromise);
545 0 : }
546 :
547 : NS_IMETHOD
548 0 : UnregisterSucceeded(bool aState) override
549 : {
550 0 : AssertIsOnMainThread();
551 0 : mPromise->MaybeResolve(aState);
552 0 : return NS_OK;
553 : }
554 :
555 : NS_IMETHOD
556 0 : UnregisterFailed() override
557 : {
558 0 : AssertIsOnMainThread();
559 :
560 0 : mPromise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
561 0 : return NS_OK;
562 : }
563 :
564 : private:
565 0 : ~UnregisterCallback()
566 0 : { }
567 : };
568 :
569 0 : NS_IMPL_ISUPPORTS(UnregisterCallback, nsIServiceWorkerUnregisterCallback)
570 :
571 0 : class FulfillUnregisterPromiseRunnable final : public WorkerRunnable
572 : {
573 : RefPtr<PromiseWorkerProxy> mPromiseWorkerProxy;
574 : Maybe<bool> mState;
575 : public:
576 0 : FulfillUnregisterPromiseRunnable(PromiseWorkerProxy* aProxy,
577 : const Maybe<bool>& aState)
578 0 : : WorkerRunnable(aProxy->GetWorkerPrivate())
579 : , mPromiseWorkerProxy(aProxy)
580 0 : , mState(aState)
581 : {
582 0 : AssertIsOnMainThread();
583 0 : MOZ_ASSERT(mPromiseWorkerProxy);
584 0 : }
585 :
586 : bool
587 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
588 : {
589 0 : RefPtr<Promise> promise = mPromiseWorkerProxy->WorkerPromise();
590 0 : if (mState.isSome()) {
591 0 : promise->MaybeResolve(mState.value());
592 : } else {
593 0 : promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
594 : }
595 :
596 0 : mPromiseWorkerProxy->CleanUp();
597 0 : return true;
598 : }
599 : };
600 :
601 : class WorkerUnregisterCallback final : public nsIServiceWorkerUnregisterCallback
602 : {
603 : RefPtr<PromiseWorkerProxy> mPromiseWorkerProxy;
604 : public:
605 : NS_DECL_ISUPPORTS
606 :
607 0 : explicit WorkerUnregisterCallback(PromiseWorkerProxy* aProxy)
608 0 : : mPromiseWorkerProxy(aProxy)
609 : {
610 0 : MOZ_ASSERT(aProxy);
611 0 : }
612 :
613 : NS_IMETHOD
614 0 : UnregisterSucceeded(bool aState) override
615 : {
616 0 : AssertIsOnMainThread();
617 0 : Finish(Some(aState));
618 0 : return NS_OK;
619 : }
620 :
621 : NS_IMETHOD
622 0 : UnregisterFailed() override
623 : {
624 0 : AssertIsOnMainThread();
625 0 : Finish(Nothing());
626 0 : return NS_OK;
627 : }
628 :
629 : private:
630 0 : ~WorkerUnregisterCallback()
631 0 : {}
632 :
633 : void
634 0 : Finish(const Maybe<bool>& aState)
635 : {
636 0 : AssertIsOnMainThread();
637 0 : if (!mPromiseWorkerProxy) {
638 0 : return;
639 : }
640 :
641 0 : RefPtr<PromiseWorkerProxy> proxy = mPromiseWorkerProxy.forget();
642 0 : MutexAutoLock lock(proxy->Lock());
643 0 : if (proxy->CleanedUp()) {
644 0 : return;
645 : }
646 :
647 : RefPtr<WorkerRunnable> r =
648 0 : new FulfillUnregisterPromiseRunnable(proxy, aState);
649 :
650 0 : r->Dispatch();
651 : }
652 : };
653 :
654 0 : NS_IMPL_ISUPPORTS(WorkerUnregisterCallback, nsIServiceWorkerUnregisterCallback);
655 :
656 : /*
657 : * If the worker goes away, we still continue to unregister, but we don't try to
658 : * resolve the worker Promise (which doesn't exist by that point).
659 : */
660 0 : class StartUnregisterRunnable final : public Runnable
661 : {
662 : RefPtr<PromiseWorkerProxy> mPromiseWorkerProxy;
663 : const nsString mScope;
664 :
665 : public:
666 0 : StartUnregisterRunnable(PromiseWorkerProxy* aProxy, const nsAString& aScope)
667 0 : : Runnable("dom::StartUnregisterRunnable")
668 : , mPromiseWorkerProxy(aProxy)
669 0 : , mScope(aScope)
670 : {
671 0 : MOZ_ASSERT(aProxy);
672 0 : }
673 :
674 : NS_IMETHOD
675 0 : Run() override
676 : {
677 0 : AssertIsOnMainThread();
678 :
679 : // XXXnsm: There is a rare chance of this failing if the worker gets
680 : // destroyed. In that case, unregister() called from a SW is no longer
681 : // guaranteed to run. We should fix this by having a main thread proxy
682 : // maintain a strongref to ServiceWorkerRegistrationInfo and use its
683 : // principal. Can that be trusted?
684 0 : nsCOMPtr<nsIPrincipal> principal;
685 : {
686 0 : MutexAutoLock lock(mPromiseWorkerProxy->Lock());
687 0 : if (mPromiseWorkerProxy->CleanedUp()) {
688 0 : return NS_OK;
689 : }
690 :
691 0 : WorkerPrivate* worker = mPromiseWorkerProxy->GetWorkerPrivate();
692 0 : MOZ_ASSERT(worker);
693 0 : principal = worker->GetPrincipal();
694 : }
695 0 : MOZ_ASSERT(principal);
696 :
697 : RefPtr<WorkerUnregisterCallback> cb =
698 0 : new WorkerUnregisterCallback(mPromiseWorkerProxy);
699 : nsCOMPtr<nsIServiceWorkerManager> swm =
700 0 : mozilla::services::GetServiceWorkerManager();
701 0 : nsresult rv = swm->Unregister(principal, cb, mScope);
702 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
703 0 : cb->UnregisterFailed();
704 : }
705 :
706 0 : return NS_OK;
707 : }
708 : };
709 : } // namespace
710 :
711 : already_AddRefed<Promise>
712 0 : ServiceWorkerRegistrationMainThread::Update(ErrorResult& aRv)
713 : {
714 0 : AssertIsOnMainThread();
715 0 : nsCOMPtr<nsIGlobalObject> go = do_QueryInterface(GetOwner());
716 0 : if (!go) {
717 0 : aRv.Throw(NS_ERROR_FAILURE);
718 0 : return nullptr;
719 : }
720 :
721 0 : RefPtr<Promise> promise = Promise::Create(go, aRv);
722 0 : if (NS_WARN_IF(aRv.Failed())) {
723 0 : return nullptr;
724 : }
725 :
726 0 : nsCOMPtr<nsIDocument> doc = GetOwner()->GetExtantDoc();
727 0 : MOZ_ASSERT(doc);
728 :
729 : RefPtr<MainThreadUpdateCallback> cb =
730 0 : new MainThreadUpdateCallback(promise);
731 0 : UpdateInternal(doc->NodePrincipal(), mScope, cb);
732 :
733 0 : return promise.forget();
734 : }
735 :
736 : already_AddRefed<Promise>
737 0 : ServiceWorkerRegistrationMainThread::Unregister(ErrorResult& aRv)
738 : {
739 0 : AssertIsOnMainThread();
740 0 : nsCOMPtr<nsIGlobalObject> go = do_QueryInterface(GetOwner());
741 0 : if (!go) {
742 0 : aRv.Throw(NS_ERROR_FAILURE);
743 0 : return nullptr;
744 : }
745 :
746 : // Although the spec says that the same-origin checks should also be done
747 : // asynchronously, we do them in sync because the Promise created by the
748 : // WebIDL infrastructure due to a returned error will be resolved
749 : // asynchronously. We aren't making any internal state changes in these
750 : // checks, so ordering of multiple calls is not affected.
751 0 : nsCOMPtr<nsIDocument> document = GetOwner()->GetExtantDoc();
752 0 : if (!document) {
753 0 : aRv.Throw(NS_ERROR_FAILURE);
754 0 : return nullptr;
755 : }
756 :
757 0 : nsCOMPtr<nsIURI> scopeURI;
758 0 : nsCOMPtr<nsIURI> baseURI = document->GetBaseURI();
759 : // "If the origin of scope is not client's origin..."
760 0 : nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), mScope, nullptr, baseURI);
761 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
762 0 : aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
763 0 : return nullptr;
764 : }
765 :
766 0 : nsCOMPtr<nsIPrincipal> documentPrincipal = document->NodePrincipal();
767 0 : rv = documentPrincipal->CheckMayLoad(scopeURI, true /* report */,
768 0 : false /* allowIfInheritsPrinciple */);
769 0 : if (NS_FAILED(rv)) {
770 0 : aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
771 0 : return nullptr;
772 : }
773 :
774 0 : nsAutoCString uriSpec;
775 0 : aRv = scopeURI->GetSpecIgnoringRef(uriSpec);
776 0 : if (NS_WARN_IF(aRv.Failed())) {
777 0 : return nullptr;
778 : }
779 :
780 : nsCOMPtr<nsIServiceWorkerManager> swm =
781 0 : mozilla::services::GetServiceWorkerManager();
782 :
783 0 : RefPtr<Promise> promise = Promise::Create(go, aRv);
784 0 : if (NS_WARN_IF(aRv.Failed())) {
785 0 : return nullptr;
786 : }
787 :
788 0 : RefPtr<UnregisterCallback> cb = new UnregisterCallback(promise);
789 :
790 0 : NS_ConvertUTF8toUTF16 scope(uriSpec);
791 0 : aRv = swm->Unregister(documentPrincipal, cb, scope);
792 0 : if (aRv.Failed()) {
793 0 : return nullptr;
794 : }
795 :
796 0 : return promise.forget();
797 : }
798 :
799 : // Notification API extension.
800 : already_AddRefed<Promise>
801 0 : ServiceWorkerRegistrationMainThread::ShowNotification(JSContext* aCx,
802 : const nsAString& aTitle,
803 : const NotificationOptions& aOptions,
804 : ErrorResult& aRv)
805 : {
806 0 : AssertIsOnMainThread();
807 0 : nsCOMPtr<nsPIDOMWindowInner> window = GetOwner();
808 0 : if (NS_WARN_IF(!window)) {
809 0 : aRv.Throw(NS_ERROR_FAILURE);
810 0 : return nullptr;
811 : }
812 :
813 0 : nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
814 0 : if (NS_WARN_IF(!doc)) {
815 0 : aRv.Throw(NS_ERROR_FAILURE);
816 0 : return nullptr;
817 : }
818 :
819 0 : RefPtr<ServiceWorker> worker = GetActive();
820 0 : if (!worker) {
821 0 : aRv.ThrowTypeError<MSG_NO_ACTIVE_WORKER>(mScope);
822 0 : return nullptr;
823 : }
824 :
825 0 : nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(window);
826 : RefPtr<Promise> p =
827 0 : Notification::ShowPersistentNotification(aCx, global, mScope, aTitle,
828 0 : aOptions, aRv);
829 0 : if (NS_WARN_IF(aRv.Failed())) {
830 0 : return nullptr;
831 : }
832 :
833 0 : return p.forget();
834 : }
835 :
836 : already_AddRefed<Promise>
837 0 : ServiceWorkerRegistrationMainThread::GetNotifications(const GetNotificationOptions& aOptions, ErrorResult& aRv)
838 : {
839 0 : AssertIsOnMainThread();
840 0 : nsCOMPtr<nsPIDOMWindowInner> window = GetOwner();
841 0 : if (NS_WARN_IF(!window)) {
842 0 : aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
843 0 : return nullptr;
844 : }
845 0 : return Notification::Get(window, aOptions, mScope, aRv);
846 : }
847 :
848 : already_AddRefed<PushManager>
849 0 : ServiceWorkerRegistrationMainThread::GetPushManager(JSContext* aCx,
850 : ErrorResult& aRv)
851 : {
852 0 : AssertIsOnMainThread();
853 :
854 0 : if (!mPushManager) {
855 0 : nsCOMPtr<nsIGlobalObject> globalObject = do_QueryInterface(GetOwner());
856 :
857 0 : if (!globalObject) {
858 0 : aRv.Throw(NS_ERROR_FAILURE);
859 0 : return nullptr;
860 : }
861 :
862 0 : GlobalObject global(aCx, globalObject->GetGlobalJSObject());
863 0 : mPushManager = PushManager::Constructor(global, mScope, aRv);
864 0 : if (aRv.Failed()) {
865 0 : return nullptr;
866 : }
867 : }
868 :
869 0 : RefPtr<PushManager> ret = mPushManager;
870 0 : return ret.forget();
871 : }
872 :
873 : ////////////////////////////////////////////////////
874 : // Worker Thread implementation
875 :
876 : class ServiceWorkerRegistrationWorkerThread final : public ServiceWorkerRegistration
877 : , public WorkerHolder
878 : {
879 : public:
880 : NS_DECL_ISUPPORTS_INHERITED
881 0 : NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ServiceWorkerRegistrationWorkerThread,
882 : ServiceWorkerRegistration)
883 :
884 : ServiceWorkerRegistrationWorkerThread(WorkerPrivate* aWorkerPrivate,
885 : const nsAString& aScope);
886 :
887 : already_AddRefed<Promise>
888 : Update(ErrorResult& aRv) override;
889 :
890 : already_AddRefed<Promise>
891 : Unregister(ErrorResult& aRv) override;
892 :
893 : // Partial interface from Notification API.
894 : already_AddRefed<Promise>
895 : ShowNotification(JSContext* aCx,
896 : const nsAString& aTitle,
897 : const NotificationOptions& aOptions,
898 : ErrorResult& aRv) override;
899 :
900 : already_AddRefed<Promise>
901 : GetNotifications(const GetNotificationOptions& aOptions,
902 : ErrorResult& aRv) override;
903 :
904 : already_AddRefed<ServiceWorker>
905 : GetInstalling() override;
906 :
907 : already_AddRefed<ServiceWorker>
908 : GetWaiting() override;
909 :
910 : already_AddRefed<ServiceWorker>
911 : GetActive() override;
912 :
913 : void
914 0 : GetScope(nsAString& aScope) const override
915 : {
916 0 : aScope = mScope;
917 0 : }
918 :
919 : bool
920 : Notify(Status aStatus) override;
921 :
922 : already_AddRefed<PushManager>
923 : GetPushManager(JSContext* aCx, ErrorResult& aRv) override;
924 :
925 : private:
926 : ~ServiceWorkerRegistrationWorkerThread();
927 :
928 : void
929 : InitListener();
930 :
931 : void
932 : ReleaseListener();
933 :
934 : WorkerPrivate* mWorkerPrivate;
935 : RefPtr<WorkerListener> mListener;
936 :
937 : RefPtr<PushManager> mPushManager;
938 : };
939 :
940 : class WorkerListener final : public ServiceWorkerRegistrationListener
941 : {
942 : // Accessed on the main thread.
943 : WorkerPrivate* mWorkerPrivate;
944 : nsString mScope;
945 : bool mListeningForEvents;
946 :
947 : // Accessed on the worker thread.
948 : ServiceWorkerRegistrationWorkerThread* mRegistration;
949 :
950 : public:
951 0 : NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WorkerListener, override)
952 :
953 0 : WorkerListener(WorkerPrivate* aWorkerPrivate,
954 : ServiceWorkerRegistrationWorkerThread* aReg)
955 0 : : mWorkerPrivate(aWorkerPrivate)
956 : , mListeningForEvents(false)
957 0 : , mRegistration(aReg)
958 : {
959 0 : MOZ_ASSERT(mWorkerPrivate);
960 0 : mWorkerPrivate->AssertIsOnWorkerThread();
961 0 : MOZ_ASSERT(mRegistration);
962 : // Copy scope so we can return it on the main thread.
963 0 : mRegistration->GetScope(mScope);
964 0 : }
965 :
966 : void
967 0 : StartListeningForEvents()
968 : {
969 0 : AssertIsOnMainThread();
970 0 : MOZ_ASSERT(!mListeningForEvents);
971 0 : MOZ_ASSERT(mWorkerPrivate);
972 0 : RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
973 0 : if (swm) {
974 : // FIXME(nsm): Maybe the function shouldn't take an explicit scope.
975 0 : swm->AddRegistrationEventListener(mScope, this);
976 0 : mListeningForEvents = true;
977 : }
978 0 : }
979 :
980 : void
981 0 : StopListeningForEvents()
982 : {
983 0 : AssertIsOnMainThread();
984 :
985 0 : MOZ_ASSERT(mListeningForEvents);
986 :
987 0 : RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
988 :
989 : // We aren't going to need this anymore and we shouldn't hold on since the
990 : // worker will go away soon.
991 0 : mWorkerPrivate = nullptr;
992 :
993 0 : if (swm) {
994 : // FIXME(nsm): Maybe the function shouldn't take an explicit scope.
995 0 : swm->RemoveRegistrationEventListener(mScope, this);
996 0 : mListeningForEvents = false;
997 : }
998 0 : }
999 :
1000 : // ServiceWorkerRegistrationListener
1001 : void
1002 : UpdateFound() override;
1003 :
1004 : void
1005 0 : TransitionWorker(WhichServiceWorker aWhichOne) override
1006 : {
1007 0 : AssertIsOnMainThread();
1008 0 : NS_WARNING("FIXME: Not implemented!");
1009 0 : }
1010 :
1011 : void
1012 0 : InvalidateWorkers(WhichServiceWorker aWhichOnes) override
1013 : {
1014 0 : AssertIsOnMainThread();
1015 : // FIXME(nsm);
1016 0 : }
1017 :
1018 : void
1019 0 : RegistrationRemoved() override
1020 : {
1021 0 : AssertIsOnMainThread();
1022 0 : }
1023 :
1024 : void
1025 0 : GetScope(nsAString& aScope) const override
1026 : {
1027 0 : aScope = mScope;
1028 0 : }
1029 :
1030 : ServiceWorkerRegistrationWorkerThread*
1031 0 : GetRegistration() const
1032 : {
1033 0 : if (mWorkerPrivate) {
1034 0 : mWorkerPrivate->AssertIsOnWorkerThread();
1035 : }
1036 0 : return mRegistration;
1037 : }
1038 :
1039 : void
1040 0 : ClearRegistration()
1041 : {
1042 0 : if (mWorkerPrivate) {
1043 0 : mWorkerPrivate->AssertIsOnWorkerThread();
1044 : }
1045 0 : mRegistration = nullptr;
1046 0 : }
1047 :
1048 : private:
1049 0 : ~WorkerListener()
1050 0 : {
1051 0 : MOZ_ASSERT(!mListeningForEvents);
1052 0 : }
1053 : };
1054 :
1055 0 : NS_IMPL_ADDREF_INHERITED(ServiceWorkerRegistrationWorkerThread, ServiceWorkerRegistration)
1056 0 : NS_IMPL_RELEASE_INHERITED(ServiceWorkerRegistrationWorkerThread, ServiceWorkerRegistration)
1057 :
1058 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(ServiceWorkerRegistrationWorkerThread)
1059 0 : NS_INTERFACE_MAP_END_INHERITING(ServiceWorkerRegistration)
1060 :
1061 : // Expanded macros since we need special behaviour to release the proxy.
1062 : NS_IMPL_CYCLE_COLLECTION_CLASS(ServiceWorkerRegistrationWorkerThread)
1063 :
1064 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ServiceWorkerRegistrationWorkerThread,
1065 : ServiceWorkerRegistration)
1066 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPushManager)
1067 :
1068 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
1069 :
1070 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(ServiceWorkerRegistrationWorkerThread,
1071 : ServiceWorkerRegistration)
1072 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mPushManager)
1073 0 : tmp->ReleaseListener();
1074 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
1075 :
1076 0 : ServiceWorkerRegistrationWorkerThread::ServiceWorkerRegistrationWorkerThread(WorkerPrivate* aWorkerPrivate,
1077 0 : const nsAString& aScope)
1078 : : ServiceWorkerRegistration(nullptr, aScope)
1079 0 : , mWorkerPrivate(aWorkerPrivate)
1080 : {
1081 0 : InitListener();
1082 0 : }
1083 :
1084 0 : ServiceWorkerRegistrationWorkerThread::~ServiceWorkerRegistrationWorkerThread()
1085 : {
1086 0 : ReleaseListener();
1087 0 : MOZ_ASSERT(!mListener);
1088 0 : }
1089 :
1090 : already_AddRefed<workers::ServiceWorker>
1091 0 : ServiceWorkerRegistrationWorkerThread::GetInstalling()
1092 : {
1093 : // FIXME(nsm): Will be implemented after Bug 1113522.
1094 0 : return nullptr;
1095 : }
1096 :
1097 : already_AddRefed<ServiceWorker>
1098 0 : ServiceWorkerRegistrationWorkerThread::GetWaiting()
1099 : {
1100 : // FIXME(nsm): Will be implemented after Bug 1113522.
1101 0 : return nullptr;
1102 : }
1103 :
1104 : already_AddRefed<ServiceWorker>
1105 0 : ServiceWorkerRegistrationWorkerThread::GetActive()
1106 : {
1107 : // FIXME(nsm): Will be implemented after Bug 1113522.
1108 0 : return nullptr;
1109 : }
1110 :
1111 : already_AddRefed<Promise>
1112 0 : ServiceWorkerRegistrationWorkerThread::Update(ErrorResult& aRv)
1113 : {
1114 0 : WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
1115 0 : MOZ_ASSERT(worker);
1116 0 : worker->AssertIsOnWorkerThread();
1117 :
1118 0 : RefPtr<Promise> promise = Promise::Create(worker->GlobalScope(), aRv);
1119 0 : if (aRv.Failed()) {
1120 0 : return nullptr;
1121 : }
1122 :
1123 : // Avoid infinite update loops by ignoring update() calls during top
1124 : // level script evaluation. See:
1125 : // https://github.com/slightlyoff/ServiceWorker/issues/800
1126 0 : if (worker->LoadScriptAsPartOfLoadingServiceWorkerScript()) {
1127 0 : promise->MaybeResolveWithUndefined();
1128 0 : return promise.forget();
1129 : }
1130 :
1131 0 : RefPtr<PromiseWorkerProxy> proxy = PromiseWorkerProxy::Create(worker, promise);
1132 0 : if (!proxy) {
1133 0 : aRv.Throw(NS_ERROR_DOM_ABORT_ERR);
1134 0 : return nullptr;
1135 : }
1136 :
1137 0 : RefPtr<UpdateRunnable> r = new UpdateRunnable(proxy, mScope);
1138 0 : MOZ_ALWAYS_SUCCEEDS(worker->DispatchToMainThread(r.forget()));
1139 :
1140 0 : return promise.forget();
1141 : }
1142 :
1143 : already_AddRefed<Promise>
1144 0 : ServiceWorkerRegistrationWorkerThread::Unregister(ErrorResult& aRv)
1145 : {
1146 0 : WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
1147 0 : MOZ_ASSERT(worker);
1148 0 : worker->AssertIsOnWorkerThread();
1149 :
1150 0 : if (!worker->IsServiceWorker()) {
1151 : // For other workers, the registration probably originated from
1152 : // getRegistration(), so we may have to validate origin etc. Let's do this
1153 : // this later.
1154 0 : aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
1155 0 : return nullptr;
1156 : }
1157 :
1158 0 : RefPtr<Promise> promise = Promise::Create(worker->GlobalScope(), aRv);
1159 0 : if (aRv.Failed()) {
1160 0 : return nullptr;
1161 : }
1162 :
1163 0 : RefPtr<PromiseWorkerProxy> proxy = PromiseWorkerProxy::Create(worker, promise);
1164 0 : if (!proxy) {
1165 0 : aRv.Throw(NS_ERROR_DOM_ABORT_ERR);
1166 0 : return nullptr;
1167 : }
1168 :
1169 0 : RefPtr<StartUnregisterRunnable> r = new StartUnregisterRunnable(proxy, mScope);
1170 0 : MOZ_ALWAYS_SUCCEEDS(worker->DispatchToMainThread(r.forget()));
1171 :
1172 0 : return promise.forget();
1173 : }
1174 :
1175 : void
1176 0 : ServiceWorkerRegistrationWorkerThread::InitListener()
1177 : {
1178 0 : MOZ_ASSERT(!mListener);
1179 0 : WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
1180 0 : MOZ_ASSERT(worker);
1181 0 : worker->AssertIsOnWorkerThread();
1182 :
1183 0 : mListener = new WorkerListener(worker, this);
1184 0 : if (!HoldWorker(worker, Closing)) {
1185 0 : mListener = nullptr;
1186 0 : NS_WARNING("Could not add feature");
1187 0 : return;
1188 : }
1189 :
1190 : nsCOMPtr<nsIRunnable> r =
1191 0 : NewRunnableMethod("dom::WorkerListener::StartListeningForEvents",
1192 : mListener,
1193 0 : &WorkerListener::StartListeningForEvents);
1194 0 : MOZ_ALWAYS_SUCCEEDS(worker->DispatchToMainThread(r.forget()));
1195 : }
1196 :
1197 : void
1198 0 : ServiceWorkerRegistrationWorkerThread::ReleaseListener()
1199 : {
1200 0 : if (!mListener) {
1201 0 : return;
1202 : }
1203 :
1204 : // We can assert worker here, because:
1205 : // 1) We always HoldWorker, so if the worker has shutdown already, we'll
1206 : // have received Notify and removed it. If HoldWorker had failed,
1207 : // mListener will be null and we won't reach here.
1208 : // 2) Otherwise, worker is still around even if we are going away.
1209 0 : mWorkerPrivate->AssertIsOnWorkerThread();
1210 0 : ReleaseWorker();
1211 :
1212 0 : mListener->ClearRegistration();
1213 :
1214 : nsCOMPtr<nsIRunnable> r =
1215 0 : NewRunnableMethod("dom::WorkerListener::StopListeningForEvents",
1216 : mListener,
1217 0 : &WorkerListener::StopListeningForEvents);
1218 0 : MOZ_ALWAYS_SUCCEEDS(mWorkerPrivate->DispatchToMainThread(r.forget()));
1219 :
1220 0 : mListener = nullptr;
1221 0 : mWorkerPrivate = nullptr;
1222 : }
1223 :
1224 : bool
1225 0 : ServiceWorkerRegistrationWorkerThread::Notify(Status aStatus)
1226 : {
1227 0 : ReleaseListener();
1228 0 : return true;
1229 : }
1230 :
1231 0 : class FireUpdateFoundRunnable final : public WorkerRunnable
1232 : {
1233 : RefPtr<WorkerListener> mListener;
1234 : public:
1235 0 : FireUpdateFoundRunnable(WorkerPrivate* aWorkerPrivate,
1236 : WorkerListener* aListener)
1237 0 : : WorkerRunnable(aWorkerPrivate)
1238 0 : , mListener(aListener)
1239 : {
1240 : // Need this assertion for now since runnables which modify busy count can
1241 : // only be dispatched from parent thread to worker thread and we don't deal
1242 : // with nested workers. SW threads can't be nested.
1243 0 : MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
1244 0 : }
1245 :
1246 : bool
1247 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
1248 : {
1249 0 : MOZ_ASSERT(aWorkerPrivate);
1250 0 : aWorkerPrivate->AssertIsOnWorkerThread();
1251 :
1252 0 : ServiceWorkerRegistrationWorkerThread* reg = mListener->GetRegistration();
1253 0 : if (reg) {
1254 0 : reg->DispatchTrustedEvent(NS_LITERAL_STRING("updatefound"));
1255 : }
1256 0 : return true;
1257 : }
1258 : };
1259 :
1260 : void
1261 0 : WorkerListener::UpdateFound()
1262 : {
1263 0 : AssertIsOnMainThread();
1264 0 : if (mWorkerPrivate) {
1265 : RefPtr<FireUpdateFoundRunnable> r =
1266 0 : new FireUpdateFoundRunnable(mWorkerPrivate, this);
1267 0 : Unused << NS_WARN_IF(!r->Dispatch());
1268 : }
1269 0 : }
1270 :
1271 : // Notification API extension.
1272 : already_AddRefed<Promise>
1273 0 : ServiceWorkerRegistrationWorkerThread::ShowNotification(JSContext* aCx,
1274 : const nsAString& aTitle,
1275 : const NotificationOptions& aOptions,
1276 : ErrorResult& aRv)
1277 : {
1278 : // Until Bug 1131324 exposes ServiceWorkerContainer on workers,
1279 : // ShowPersistentNotification() checks for valid active worker while it is
1280 : // also verifying scope so that we block the worker on the main thread only
1281 : // once.
1282 : RefPtr<Promise> p =
1283 0 : Notification::ShowPersistentNotification(aCx, mWorkerPrivate->GlobalScope(),
1284 0 : mScope, aTitle, aOptions, aRv);
1285 0 : if (NS_WARN_IF(aRv.Failed())) {
1286 0 : return nullptr;
1287 : }
1288 :
1289 0 : return p.forget();
1290 : }
1291 :
1292 : already_AddRefed<Promise>
1293 0 : ServiceWorkerRegistrationWorkerThread::GetNotifications(const GetNotificationOptions& aOptions,
1294 : ErrorResult& aRv)
1295 : {
1296 0 : return Notification::WorkerGet(mWorkerPrivate, aOptions, mScope, aRv);
1297 : }
1298 :
1299 : already_AddRefed<PushManager>
1300 0 : ServiceWorkerRegistrationWorkerThread::GetPushManager(JSContext* aCx, ErrorResult& aRv)
1301 : {
1302 0 : if (!mPushManager) {
1303 0 : mPushManager = new PushManager(mScope);
1304 : }
1305 :
1306 0 : RefPtr<PushManager> ret = mPushManager;
1307 0 : return ret.forget();
1308 : }
1309 :
1310 : ////////////////////////////////////////////////////
1311 : // Base class implementation
1312 :
1313 0 : NS_IMPL_ADDREF_INHERITED(ServiceWorkerRegistration, DOMEventTargetHelper)
1314 0 : NS_IMPL_RELEASE_INHERITED(ServiceWorkerRegistration, DOMEventTargetHelper)
1315 :
1316 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(ServiceWorkerRegistration)
1317 0 : NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
1318 :
1319 0 : ServiceWorkerRegistration::ServiceWorkerRegistration(nsPIDOMWindowInner* aWindow,
1320 0 : const nsAString& aScope)
1321 : : DOMEventTargetHelper(aWindow)
1322 0 : , mScope(aScope)
1323 0 : {}
1324 :
1325 : JSObject*
1326 0 : ServiceWorkerRegistration::WrapObject(JSContext* aCx,
1327 : JS::Handle<JSObject*> aGivenProto)
1328 : {
1329 0 : return ServiceWorkerRegistrationBinding::Wrap(aCx, this, aGivenProto);
1330 : }
1331 :
1332 : /* static */ already_AddRefed<ServiceWorkerRegistration>
1333 0 : ServiceWorkerRegistration::CreateForMainThread(nsPIDOMWindowInner* aWindow,
1334 : const nsAString& aScope)
1335 : {
1336 0 : MOZ_ASSERT(aWindow);
1337 0 : MOZ_ASSERT(NS_IsMainThread());
1338 :
1339 : RefPtr<ServiceWorkerRegistration> registration =
1340 0 : new ServiceWorkerRegistrationMainThread(aWindow, aScope);
1341 :
1342 0 : return registration.forget();
1343 : }
1344 :
1345 : /* static */ already_AddRefed<ServiceWorkerRegistration>
1346 0 : ServiceWorkerRegistration::CreateForWorker(workers::WorkerPrivate* aWorkerPrivate,
1347 : const nsAString& aScope)
1348 : {
1349 0 : MOZ_ASSERT(aWorkerPrivate);
1350 0 : aWorkerPrivate->AssertIsOnWorkerThread();
1351 :
1352 : RefPtr<ServiceWorkerRegistration> registration =
1353 0 : new ServiceWorkerRegistrationWorkerThread(aWorkerPrivate, aScope);
1354 :
1355 0 : return registration.forget();
1356 : }
1357 :
1358 : } // dom namespace
1359 : } // mozilla namespace
|