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 "ServiceWorkerShutdownState.h"
8 
9 #include <array>
10 #include <type_traits>
11 
12 #include "MainThreadUtils.h"
13 #include "ServiceWorkerUtils.h"
14 #include "mozilla/Assertions.h"
15 #include "mozilla/SchedulerGroup.h"
16 #include "mozilla/Unused.h"
17 #include "mozilla/dom/ContentChild.h"
18 #include "mozilla/dom/RemoteWorkerService.h"
19 #include "mozilla/dom/ServiceWorkerManager.h"
20 #include "mozilla/dom/WorkerCommon.h"
21 #include "mozilla/ipc/BackgroundParent.h"
22 #include "nsDebug.h"
23 #include "nsThreadUtils.h"
24 #include "nsXULAppAPI.h"
25 
26 namespace mozilla {
27 namespace dom {
28 
29 using Progress = ServiceWorkerShutdownState::Progress;
30 
31 namespace {
32 
UnderlyingProgressValue(Progress aProgress)33 constexpr inline auto UnderlyingProgressValue(Progress aProgress) {
34   return std::underlying_type_t<Progress>(aProgress);
35 }
36 
37 constexpr std::array<const char*, UnderlyingProgressValue(Progress::EndGuard_)>
38     gProgressStrings = {{
39         // clang-format off
40       "parent process main thread",
41       "parent process IPDL background thread",
42       "content process worker launcher thread",
43       "content process main thread",
44       "shutdown completed"
45         // clang-format on
46     }};
47 
48 }  // anonymous namespace
49 
ServiceWorkerShutdownState()50 ServiceWorkerShutdownState::ServiceWorkerShutdownState()
51     : mProgress(Progress::ParentProcessMainThread) {
52   MOZ_ASSERT(XRE_IsParentProcess());
53   MOZ_ASSERT(NS_IsMainThread());
54 }
55 
~ServiceWorkerShutdownState()56 ServiceWorkerShutdownState::~ServiceWorkerShutdownState() {
57   Unused << NS_WARN_IF(mProgress != Progress::ShutdownCompleted);
58 }
59 
GetProgressString() const60 const char* ServiceWorkerShutdownState::GetProgressString() const {
61   return gProgressStrings[UnderlyingProgressValue(mProgress)];
62 }
63 
SetProgress(Progress aProgress)64 void ServiceWorkerShutdownState::SetProgress(Progress aProgress) {
65   MOZ_ASSERT(aProgress != Progress::EndGuard_);
66   MOZ_RELEASE_ASSERT(UnderlyingProgressValue(mProgress) + 1 ==
67                      UnderlyingProgressValue(aProgress));
68 
69   mProgress = aProgress;
70 }
71 
72 namespace {
73 
ReportProgressToServiceWorkerManager(uint32_t aShutdownStateId,Progress aProgress)74 void ReportProgressToServiceWorkerManager(uint32_t aShutdownStateId,
75                                           Progress aProgress) {
76   MOZ_ASSERT(XRE_IsParentProcess());
77   MOZ_ASSERT(NS_IsMainThread());
78 
79   RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
80   MOZ_RELEASE_ASSERT(swm, "ServiceWorkers should shutdown before SWM.");
81 
82   swm->ReportServiceWorkerShutdownProgress(aShutdownStateId, aProgress);
83 }
84 
ReportProgressToParentProcess(uint32_t aShutdownStateId,Progress aProgress)85 void ReportProgressToParentProcess(uint32_t aShutdownStateId,
86                                    Progress aProgress) {
87   MOZ_ASSERT(XRE_IsContentProcess());
88   MOZ_ASSERT(NS_IsMainThread());
89 
90   ContentChild* contentChild = ContentChild::GetSingleton();
91   MOZ_ASSERT(contentChild);
92 
93   contentChild->SendReportServiceWorkerShutdownProgress(aShutdownStateId,
94                                                         aProgress);
95 }
96 
ReportServiceWorkerShutdownProgress(uint32_t aShutdownStateId,Progress aProgress)97 void ReportServiceWorkerShutdownProgress(uint32_t aShutdownStateId,
98                                          Progress aProgress) {
99   MOZ_ASSERT(UnderlyingProgressValue(Progress::ParentProcessMainThread) <
100              UnderlyingProgressValue(aProgress));
101   MOZ_ASSERT(UnderlyingProgressValue(aProgress) <
102              UnderlyingProgressValue(Progress::EndGuard_));
103 
104   nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
105       __func__, [shutdownStateId = aShutdownStateId, progress = aProgress] {
106         if (XRE_IsParentProcess()) {
107           ReportProgressToServiceWorkerManager(shutdownStateId, progress);
108         } else {
109           ReportProgressToParentProcess(shutdownStateId, progress);
110         }
111       });
112 
113   if (NS_IsMainThread()) {
114     MOZ_ALWAYS_SUCCEEDS(r->Run());
115   } else {
116     MOZ_ALWAYS_SUCCEEDS(
117         SchedulerGroup::Dispatch(TaskCategory::Other, r.forget()));
118   }
119 }
120 
ReportServiceWorkerShutdownProgress(uint32_t aShutdownStateId)121 void ReportServiceWorkerShutdownProgress(uint32_t aShutdownStateId) {
122   Progress progress = Progress::EndGuard_;
123 
124   if (XRE_IsParentProcess()) {
125     mozilla::ipc::AssertIsOnBackgroundThread();
126 
127     progress = Progress::ParentProcessIpdlBackgroundThread;
128   } else {
129     if (NS_IsMainThread()) {
130       progress = Progress::ContentProcessMainThread;
131     } else {
132       MOZ_ASSERT(RemoteWorkerService::Thread()->IsOnCurrentThread());
133       progress = Progress::ContentProcessWorkerLauncherThread;
134     }
135   }
136 
137   ReportServiceWorkerShutdownProgress(aShutdownStateId, progress);
138 }
139 
140 }  // anonymous namespace
141 
MaybeReportServiceWorkerShutdownProgress(const ServiceWorkerOpArgs & aArgs,bool aShutdownCompleted)142 void MaybeReportServiceWorkerShutdownProgress(const ServiceWorkerOpArgs& aArgs,
143                                               bool aShutdownCompleted) {
144   if (XRE_IsParentProcess() && !XRE_IsE10sParentProcess()) {
145     return;
146   }
147 
148   if (aShutdownCompleted) {
149     MOZ_ASSERT(aArgs.type() ==
150                ServiceWorkerOpArgs::TServiceWorkerTerminateWorkerOpArgs);
151 
152     ReportServiceWorkerShutdownProgress(
153         aArgs.get_ServiceWorkerTerminateWorkerOpArgs().shutdownStateId(),
154         Progress::ShutdownCompleted);
155 
156     return;
157   }
158 
159   if (aArgs.type() ==
160       ServiceWorkerOpArgs::TServiceWorkerTerminateWorkerOpArgs) {
161     ReportServiceWorkerShutdownProgress(
162         aArgs.get_ServiceWorkerTerminateWorkerOpArgs().shutdownStateId());
163   }
164 }
165 
166 }  // namespace dom
167 }  // namespace mozilla
168