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 "ServiceWorkerUnregisterJob.h"
8 
9 #include "nsIPushService.h"
10 
11 namespace mozilla {
12 namespace dom {
13 
14 class ServiceWorkerUnregisterJob::PushUnsubscribeCallback final
15     : public nsIUnsubscribeResultCallback {
16  public:
17   NS_DECL_ISUPPORTS
18 
PushUnsubscribeCallback(ServiceWorkerUnregisterJob * aJob)19   explicit PushUnsubscribeCallback(ServiceWorkerUnregisterJob* aJob)
20       : mJob(aJob) {
21     MOZ_ASSERT(NS_IsMainThread());
22   }
23 
24   NS_IMETHOD
OnUnsubscribe(nsresult aStatus,bool)25   OnUnsubscribe(nsresult aStatus, bool) override {
26     // Warn if unsubscribing fails, but don't prevent the worker from
27     // unregistering.
28     Unused << NS_WARN_IF(NS_FAILED(aStatus));
29     mJob->Unregister();
30     return NS_OK;
31   }
32 
33  private:
~PushUnsubscribeCallback()34   ~PushUnsubscribeCallback() {}
35 
36   RefPtr<ServiceWorkerUnregisterJob> mJob;
37 };
38 
NS_IMPL_ISUPPORTS(ServiceWorkerUnregisterJob::PushUnsubscribeCallback,nsIUnsubscribeResultCallback)39 NS_IMPL_ISUPPORTS(ServiceWorkerUnregisterJob::PushUnsubscribeCallback,
40                   nsIUnsubscribeResultCallback)
41 
42 ServiceWorkerUnregisterJob::ServiceWorkerUnregisterJob(nsIPrincipal* aPrincipal,
43                                                        const nsACString& aScope,
44                                                        bool aSendToParent)
45     : ServiceWorkerJob(Type::Unregister, aPrincipal, aScope, EmptyCString()),
46       mResult(false),
47       mSendToParent(aSendToParent) {}
48 
GetResult() const49 bool ServiceWorkerUnregisterJob::GetResult() const {
50   MOZ_ASSERT(NS_IsMainThread());
51   return mResult;
52 }
53 
~ServiceWorkerUnregisterJob()54 ServiceWorkerUnregisterJob::~ServiceWorkerUnregisterJob() {}
55 
AsyncExecute()56 void ServiceWorkerUnregisterJob::AsyncExecute() {
57   MOZ_ASSERT(NS_IsMainThread());
58 
59   if (Canceled()) {
60     Finish(NS_ERROR_DOM_ABORT_ERR);
61     return;
62   }
63 
64   // Push API, section 5: "When a service worker registration is unregistered,
65   // any associated push subscription must be deactivated." To ensure the
66   // service worker registration isn't cleared as we're unregistering, we
67   // unsubscribe first.
68   nsCOMPtr<nsIPushService> pushService =
69       do_GetService("@mozilla.org/push/Service;1");
70   if (NS_WARN_IF(!pushService)) {
71     Unregister();
72     return;
73   }
74   nsCOMPtr<nsIUnsubscribeResultCallback> unsubscribeCallback =
75       new PushUnsubscribeCallback(this);
76   nsresult rv = pushService->Unsubscribe(NS_ConvertUTF8toUTF16(mScope),
77                                          mPrincipal, unsubscribeCallback);
78   if (NS_WARN_IF(NS_FAILED(rv))) {
79     Unregister();
80   }
81 }
82 
Unregister()83 void ServiceWorkerUnregisterJob::Unregister() {
84   MOZ_ASSERT(NS_IsMainThread());
85 
86   RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
87   if (Canceled() || !swm) {
88     Finish(NS_ERROR_DOM_ABORT_ERR);
89     return;
90   }
91 
92   // Step 1 of the Unregister algorithm requires checking that the
93   // client origin matches the scope's origin.  We perform this in
94   // registration->update() method directly since we don't have that
95   // client information available here.
96 
97   // "Let registration be the result of running [[Get Registration]]
98   // algorithm passing scope as the argument."
99   RefPtr<ServiceWorkerRegistrationInfo> registration =
100       swm->GetRegistration(mPrincipal, mScope);
101   if (!registration) {
102     // "If registration is null, then, resolve promise with false."
103     Finish(NS_OK);
104     return;
105   }
106 
107   // Note, we send the message to remove the registration from disk now even
108   // though we may only set the pending uninstall flag below.  This is
109   // necessary to ensure the registration is removed if the controlled
110   // clients are closed by shutting down the browser.  If the registration
111   // is resurrected by clearing pending uninstall then it should be saved
112   // to disk again.
113   if (mSendToParent && !registration->IsPendingUninstall()) {
114     swm->MaybeSendUnregister(mPrincipal, mScope);
115   }
116 
117   // "Set registration's uninstalling flag."
118   registration->SetPendingUninstall();
119 
120   // "Resolve promise with true"
121   mResult = true;
122   InvokeResultCallbacks(NS_OK);
123 
124   // "If no service worker client is using registration..."
125   if (!registration->IsControllingClients() && registration->IsIdle()) {
126     // "Invoke [[Clear Registration]]..."
127     swm->RemoveRegistration(registration);
128   }
129 
130   Finish(NS_OK);
131 }
132 
133 }  // namespace dom
134 }  // namespace mozilla
135