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