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 #ifndef mozilla_dom_workers_serviceworkermanager_h
8 #define mozilla_dom_workers_serviceworkermanager_h
11 #include "ErrorList.h"
12 #include "ServiceWorkerShutdownState.h"
13 #include "js/ErrorReport.h"
14 #include "mozilla/AlreadyAddRefed.h"
15 #include "mozilla/Assertions.h"
16 #include "mozilla/HashTable.h"
17 #include "mozilla/MozPromise.h"
18 #include "mozilla/RefPtr.h"
19 #include "mozilla/UniquePtr.h"
20 #include "mozilla/dom/ClientHandle.h"
21 #include "mozilla/dom/ClientOpPromise.h"
22 #include "mozilla/dom/ServiceWorkerRegistrationBinding.h"
23 #include "mozilla/dom/ServiceWorkerRegistrationInfo.h"
24 #include "mozilla/dom/ServiceWorkerUtils.h"
25 #include "mozilla/mozalloc.h"
26 #include "nsClassHashtable.h"
27 #include "nsContentUtils.h"
28 #include "nsHashKeys.h"
29 #include "nsIObserver.h"
30 #include "nsIServiceWorkerManager.h"
31 #include "nsISupports.h"
32 #include "nsStringFwd.h"
35 class nsIConsoleReportCollector
;
39 class OriginAttributes
;
47 extern uint32_t gServiceWorkersRegistered
;
48 extern uint32_t gServiceWorkersRegisteredFetch
;
51 class ServiceWorkerInfo
;
52 class ServiceWorkerJobQueue
;
53 class ServiceWorkerManagerChild
;
54 class ServiceWorkerPrivate
;
55 class ServiceWorkerRegistrar
;
56 class ServiceWorkerShutdownBlocker
;
58 class ServiceWorkerUpdateFinishCallback
{
60 virtual ~ServiceWorkerUpdateFinishCallback() = default;
63 NS_INLINE_DECL_REFCOUNTING(ServiceWorkerUpdateFinishCallback
)
65 virtual void UpdateSucceeded(ServiceWorkerRegistrationInfo
* aInfo
) = 0;
67 virtual void UpdateFailed(ErrorResult
& aStatus
) = 0;
70 #define NS_SERVICEWORKERMANAGER_IMPL_IID \
71 { /* f4f8755a-69ca-46e8-a65d-775745535990 */ \
72 0xf4f8755a, 0x69ca, 0x46e8, { \
73 0xa6, 0x5d, 0x77, 0x57, 0x45, 0x53, 0x59, 0x90 \
78 * The ServiceWorkerManager is a per-process global that deals with the
79 * installation, querying and event dispatch of ServiceWorkers for all the
80 * origins in the process.
82 * NOTE: the following documentation is a WIP:
84 * The ServiceWorkerManager (SWM) is a main-thread, parent-process singleton
85 * that encapsulates the browser-global state of service workers. This state
86 * includes, but is not limited to, all service worker registrations and all
87 * controlled service worker clients. The SWM also provides methods to read and
88 * mutate this state and to dispatch operations (e.g. DOM events such as a
89 * FetchEvent) to service workers.
93 * MOZ_ASSERT(NS_IsMainThread(), "SWM is main-thread only");
95 * RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
97 * // Nullness must be checked by code that possibly executes during browser
98 * // shutdown, which is when the SWM is destroyed.
100 * // Do something with the SWM.
103 class ServiceWorkerManager final
: public nsIServiceWorkerManager
,
105 friend class GetRegistrationsRunnable
;
106 friend class GetRegistrationRunnable
;
107 friend class ServiceWorkerJob
;
108 friend class ServiceWorkerRegistrationInfo
;
109 friend class ServiceWorkerShutdownBlocker
;
110 friend class ServiceWorkerUnregisterJob
;
111 friend class ServiceWorkerUpdateJob
;
112 friend class UpdateTimerCallback
;
116 NS_DECL_NSISERVICEWORKERMANAGER
119 // Return true if the given principal and URI matches a registered service
120 // worker which handles fetch event.
121 // If there is a matched service worker but doesn't handle fetch events, this
122 // method will try to set the matched service worker as the controller of the
123 // passed in channel. Then also schedule a soft-update job for the service
125 bool IsAvailable(nsIPrincipal
* aPrincipal
, nsIURI
* aURI
,
126 nsIChannel
* aChannel
);
128 void DispatchFetchEvent(nsIInterceptedChannel
* aChannel
, ErrorResult
& aRv
);
130 void Update(nsIPrincipal
* aPrincipal
, const nsACString
& aScope
,
131 nsCString aNewestWorkerScriptUrl
,
132 ServiceWorkerUpdateFinishCallback
* aCallback
);
134 void UpdateInternal(nsIPrincipal
* aPrincipal
, const nsACString
& aScope
,
135 nsCString
&& aNewestWorkerScriptUrl
,
136 ServiceWorkerUpdateFinishCallback
* aCallback
);
138 void SoftUpdate(const OriginAttributes
& aOriginAttributes
,
139 const nsACString
& aScope
);
141 void SoftUpdateInternal(const OriginAttributes
& aOriginAttributes
,
142 const nsACString
& aScope
,
143 ServiceWorkerUpdateFinishCallback
* aCallback
);
145 RefPtr
<ServiceWorkerRegistrationPromise
> Register(
146 const ClientInfo
& aClientInfo
, const nsACString
& aScopeURL
,
147 const nsACString
& aScriptURL
,
148 ServiceWorkerUpdateViaCache aUpdateViaCache
);
150 RefPtr
<ServiceWorkerRegistrationPromise
> GetRegistration(
151 const ClientInfo
& aClientInfo
, const nsACString
& aURL
) const;
153 RefPtr
<ServiceWorkerRegistrationListPromise
> GetRegistrations(
154 const ClientInfo
& aClientInfo
) const;
156 already_AddRefed
<ServiceWorkerRegistrationInfo
> GetRegistration(
157 nsIPrincipal
* aPrincipal
, const nsACString
& aScope
) const;
159 already_AddRefed
<ServiceWorkerRegistrationInfo
> GetRegistration(
160 const mozilla::ipc::PrincipalInfo
& aPrincipal
,
161 const nsACString
& aScope
) const;
163 already_AddRefed
<ServiceWorkerRegistrationInfo
> CreateNewRegistration(
164 const nsCString
& aScope
, nsIPrincipal
* aPrincipal
,
165 ServiceWorkerUpdateViaCache aUpdateViaCache
,
166 IPCNavigationPreloadState aNavigationPreloadState
=
167 IPCNavigationPreloadState(false, "true"_ns
));
169 void RemoveRegistration(ServiceWorkerRegistrationInfo
* aRegistration
);
171 void StoreRegistration(nsIPrincipal
* aPrincipal
,
172 ServiceWorkerRegistrationInfo
* aRegistration
);
175 * Report an error for the given scope to any window we think might be
176 * interested, failing over to the Browser Console if we couldn't find any.
178 * Error messages should be localized, so you probably want to call
179 * LocalizeAndReportToAllClients instead, which in turn calls us after
180 * localizing the error.
182 void ReportToAllClients(const nsCString
& aScope
, const nsString
& aMessage
,
183 const nsString
& aFilename
, const nsString
& aLine
,
184 uint32_t aLineNumber
, uint32_t aColumnNumber
,
188 * Report a localized error for the given scope to any window we think might
191 * Note that this method takes an nsTArray<nsString> for the parameters, not
192 * bare chart16_t*[]. You can use a std::initializer_list constructor inline
193 * so that argument might look like: nsTArray<nsString> { some_nsString,
194 * PromiseFlatString(some_nsSubString_aka_nsAString),
195 * NS_ConvertUTF8toUTF16(some_nsCString_or_nsCSubString),
196 * u"some literal"_ns }. If you have anything else, like a
197 * number, you can use an nsAutoString with AppendInt/friends.
200 * The nsIScriptError flag, one of errorFlag (0x0), warningFlag (0x1),
201 * infoFlag (0x8). We default to error if omitted because usually we're
202 * logging exceptional and/or obvious breakage.
204 static void LocalizeAndReportToAllClients(
205 const nsCString
& aScope
, const char* aStringKey
,
206 const nsTArray
<nsString
>& aParamArray
, uint32_t aFlags
= 0x0,
207 const nsString
& aFilename
= u
""_ns
, const nsString
& aLine
= u
""_ns
,
208 uint32_t aLineNumber
= 0, uint32_t aColumnNumber
= 0);
210 // Always consumes the error by reporting to consoles of all controlled
212 void HandleError(JSContext
* aCx
, nsIPrincipal
* aPrincipal
,
213 const nsCString
& aScope
, const nsString
& aWorkerURL
,
214 const nsString
& aMessage
, const nsString
& aFilename
,
215 const nsString
& aLine
, uint32_t aLineNumber
,
216 uint32_t aColumnNumber
, uint32_t aFlags
, JSExnType aExnType
);
218 [[nodiscard
]] RefPtr
<GenericErrorResultPromise
> MaybeClaimClient(
219 const ClientInfo
& aClientInfo
,
220 ServiceWorkerRegistrationInfo
* aWorkerRegistration
);
222 [[nodiscard
]] RefPtr
<GenericErrorResultPromise
> MaybeClaimClient(
223 const ClientInfo
& aClientInfo
,
224 const ServiceWorkerDescriptor
& aServiceWorker
);
226 static already_AddRefed
<ServiceWorkerManager
> GetInstance();
228 void LoadRegistration(const ServiceWorkerRegistrationData
& aRegistration
);
230 void LoadRegistrations(
231 const nsTArray
<ServiceWorkerRegistrationData
>& aRegistrations
);
233 void MaybeCheckNavigationUpdate(const ClientInfo
& aClientInfo
);
235 nsresult
SendPushEvent(const nsACString
& aOriginAttributes
,
236 const nsACString
& aScope
, const nsAString
& aMessageId
,
237 const Maybe
<nsTArray
<uint8_t>>& aData
);
239 void WorkerIsIdle(ServiceWorkerInfo
* aWorker
);
241 RefPtr
<ServiceWorkerRegistrationPromise
> WhenReady(
242 const ClientInfo
& aClientInfo
);
244 void CheckPendingReadyPromises();
246 void RemovePendingReadyPromise(const ClientInfo
& aClientInfo
);
248 void NoteInheritedController(const ClientInfo
& aClientInfo
,
249 const ServiceWorkerDescriptor
& aController
);
251 void BlockShutdownOn(GenericNonExclusivePromise
* aPromise
,
252 uint32_t aShutdownStateId
);
254 nsresult
GetClientRegistration(
255 const ClientInfo
& aClientInfo
,
256 ServiceWorkerRegistrationInfo
** aRegistrationInfo
);
258 int32_t GetPrincipalQuotaUsageCheckCount(nsIPrincipal
* aPrincipal
);
260 void CheckPrincipalQuotaUsage(nsIPrincipal
* aPrincipal
,
261 const nsACString
& aScope
);
263 // Returns the shutdown state ID (may be an invalid ID if an
264 // nsIAsyncShutdownBlocker is not used).
265 uint32_t MaybeInitServiceWorkerShutdownProgress() const;
267 void ReportServiceWorkerShutdownProgress(
268 uint32_t aShutdownStateId
,
269 ServiceWorkerShutdownState::Progress aProgress
) const;
271 // Record periodic telemetry on number of running ServiceWorkers. When
272 // the number of running ServiceWorkers changes (or on shutdown),
273 // ServiceWorkerPrivateImpl will call RecordTelemetry with the number of
274 // running serviceworkers and those supporting Fetch. We use
275 // mTelemetryLastChange to determine how many datapoints to inject into
276 // Telemetry, and dispatch a background runnable to call
277 // RecordTelemetryGap() and Accumulate them.
278 void RecordTelemetry(uint32_t aNumber
, uint32_t aFetch
);
280 void EvictFromBFCache(ServiceWorkerRegistrationInfo
* aRegistration
);
283 struct RegistrationDataPerPrincipal
;
285 static bool FindScopeForPath(const nsACString
& aScopeKey
,
286 const nsACString
& aPath
,
287 RegistrationDataPerPrincipal
** aData
,
290 ServiceWorkerManager();
291 ~ServiceWorkerManager();
293 void Init(ServiceWorkerRegistrar
* aRegistrar
);
295 RefPtr
<GenericErrorResultPromise
> StartControllingClient(
296 const ClientInfo
& aClientInfo
,
297 ServiceWorkerRegistrationInfo
* aRegistrationInfo
,
298 bool aControlClientHandle
= true);
300 void StopControllingClient(const ClientInfo
& aClientInfo
);
302 void MaybeStartShutdown();
304 void MaybeFinishShutdown();
306 already_AddRefed
<ServiceWorkerJobQueue
> GetOrCreateJobQueue(
307 const nsACString
& aOriginSuffix
, const nsACString
& aScope
);
309 void MaybeRemoveRegistrationInfo(const nsACString
& aScopeKey
);
311 already_AddRefed
<ServiceWorkerRegistrationInfo
> GetRegistration(
312 const nsACString
& aScopeKey
, const nsACString
& aScope
) const;
314 void AbortCurrentUpdate(ServiceWorkerRegistrationInfo
* aRegistration
);
316 nsresult
Update(ServiceWorkerRegistrationInfo
* aRegistration
);
318 ServiceWorkerInfo
* GetActiveWorkerInfoForScope(
319 const OriginAttributes
& aOriginAttributes
, const nsACString
& aScope
);
321 void StopControllingRegistration(
322 ServiceWorkerRegistrationInfo
* aRegistration
);
324 already_AddRefed
<ServiceWorkerRegistrationInfo
>
325 GetServiceWorkerRegistrationInfo(const ClientInfo
& aClientInfo
) const;
327 already_AddRefed
<ServiceWorkerRegistrationInfo
>
328 GetServiceWorkerRegistrationInfo(nsIPrincipal
* aPrincipal
,
331 already_AddRefed
<ServiceWorkerRegistrationInfo
>
332 GetServiceWorkerRegistrationInfo(const nsACString
& aScopeKey
,
335 // This method generates a key using isInElementBrowser from the principal. We
336 // don't use the origin because it can change during the loading.
337 static nsresult
PrincipalToScopeKey(nsIPrincipal
* aPrincipal
,
340 static nsresult
PrincipalInfoToScopeKey(
341 const mozilla::ipc::PrincipalInfo
& aPrincipalInfo
, nsACString
& aKey
);
343 static void AddScopeAndRegistration(
344 const nsACString
& aScope
, ServiceWorkerRegistrationInfo
* aRegistation
);
346 static bool HasScope(nsIPrincipal
* aPrincipal
, const nsACString
& aScope
);
348 static void RemoveScopeAndRegistration(
349 ServiceWorkerRegistrationInfo
* aRegistration
);
351 void QueueFireEventOnServiceWorkerRegistrations(
352 ServiceWorkerRegistrationInfo
* aRegistration
, const nsAString
& aName
);
354 void UpdateClientControllers(ServiceWorkerRegistrationInfo
* aRegistration
);
356 void MaybeRemoveRegistration(ServiceWorkerRegistrationInfo
* aRegistration
);
358 void PurgeServiceWorker(const ServiceWorkerRegistrationData
& aRegistration
,
359 nsIPrincipal
* aPrincipal
);
361 RefPtr
<ServiceWorkerManagerChild
> mActor
;
365 nsTArray
<nsCOMPtr
<nsIServiceWorkerManagerListener
>> mListeners
;
367 void NotifyListenersOnRegister(
368 nsIServiceWorkerRegistrationInfo
* aRegistration
);
370 void NotifyListenersOnUnregister(
371 nsIServiceWorkerRegistrationInfo
* aRegistration
);
373 void NotifyListenersOnQuotaUsageCheckFinish(
374 nsIServiceWorkerRegistrationInfo
* aRegistration
);
376 void ScheduleUpdateTimer(nsIPrincipal
* aPrincipal
, const nsACString
& aScope
);
378 void UpdateTimerFired(nsIPrincipal
* aPrincipal
, const nsACString
& aScope
);
380 void MaybeSendUnregister(nsIPrincipal
* aPrincipal
, const nsACString
& aScope
);
382 nsresult
SendNotificationEvent(const nsAString
& aEventName
,
383 const nsACString
& aOriginSuffix
,
384 const nsACString
& aScope
, const nsAString
& aID
,
385 const nsAString
& aTitle
, const nsAString
& aDir
,
386 const nsAString
& aLang
, const nsAString
& aBody
,
387 const nsAString
& aTag
, const nsAString
& aIcon
,
388 const nsAString
& aData
,
389 const nsAString
& aBehavior
);
391 // Used by remove() and removeAll() when clearing history.
392 // MUST ONLY BE CALLED FROM UnregisterIfMatchesHost!
393 void ForceUnregister(RegistrationDataPerPrincipal
* aRegistrationData
,
394 ServiceWorkerRegistrationInfo
* aRegistration
);
396 // An "orphaned" registration is one that is unregistered and not controlling
397 // clients. The ServiceWorkerManager must know about all orphaned
398 // registrations to forcefully shutdown all Service Workers during browser
400 void AddOrphanedRegistration(ServiceWorkerRegistrationInfo
* aRegistration
);
402 void RemoveOrphanedRegistration(ServiceWorkerRegistrationInfo
* aRegistration
);
404 HashSet
<RefPtr
<ServiceWorkerRegistrationInfo
>,
405 PointerHasher
<ServiceWorkerRegistrationInfo
*>>
406 mOrphanedRegistrations
;
408 RefPtr
<ServiceWorkerShutdownBlocker
> mShutdownBlocker
;
410 nsClassHashtable
<nsCStringHashKey
, RegistrationDataPerPrincipal
>
413 struct ControlledClientData
{
414 RefPtr
<ClientHandle
> mClientHandle
;
415 RefPtr
<ServiceWorkerRegistrationInfo
> mRegistrationInfo
;
417 ControlledClientData(ClientHandle
* aClientHandle
,
418 ServiceWorkerRegistrationInfo
* aRegistrationInfo
)
419 : mClientHandle(aClientHandle
), mRegistrationInfo(aRegistrationInfo
) {}
422 nsClassHashtable
<nsIDHashKey
, ControlledClientData
> mControlledClients
;
424 struct PendingReadyData
{
425 RefPtr
<ClientHandle
> mClientHandle
;
426 RefPtr
<ServiceWorkerRegistrationPromise::Private
> mPromise
;
428 explicit PendingReadyData(ClientHandle
* aClientHandle
)
429 : mClientHandle(aClientHandle
),
430 mPromise(new ServiceWorkerRegistrationPromise::Private(__func__
)) {}
433 nsTArray
<UniquePtr
<PendingReadyData
>> mPendingReadyList
;
435 const uint32_t mTelemetryPeriodMs
= 5 * 1000;
436 TimeStamp mTelemetryLastChange
;
440 } // namespace mozilla
442 #endif // mozilla_dom_workers_serviceworkermanager_h