Source code

Revision control

Other Tools

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
#ifndef mozilla_dom_workers_serviceworkermanager_h
8
#define mozilla_dom_workers_serviceworkermanager_h
9
10
#include "nsIServiceWorkerManager.h"
11
#include "nsCOMPtr.h"
12
13
#include "ipc/IPCMessageUtils.h"
14
#include "mozilla/Attributes.h"
15
#include "mozilla/AutoRestore.h"
16
#include "mozilla/ConsoleReportCollector.h"
17
#include "mozilla/HashTable.h"
18
#include "mozilla/LinkedList.h"
19
#include "mozilla/MozPromise.h"
20
#include "mozilla/Preferences.h"
21
#include "mozilla/TypedEnumBits.h"
22
#include "mozilla/UniquePtr.h"
23
#include "mozilla/WeakPtr.h"
24
#include "mozilla/dom/BindingUtils.h"
25
#include "mozilla/dom/ClientHandle.h"
26
#include "mozilla/dom/Promise.h"
27
#include "mozilla/dom/ServiceWorkerRegistrar.h"
28
#include "mozilla/dom/ServiceWorkerRegistrarTypes.h"
29
#include "mozilla/dom/ServiceWorkerRegistrationInfo.h"
30
#include "mozilla/dom/ServiceWorkerUtils.h"
31
#include "mozilla/ipc/BackgroundUtils.h"
32
#include "nsClassHashtable.h"
33
#include "nsDataHashtable.h"
34
#include "nsRefPtrHashtable.h"
35
#include "nsTArrayForwardDeclare.h"
36
37
class nsIConsoleReportCollector;
38
39
namespace mozilla {
40
41
class OriginAttributes;
42
43
namespace ipc {
44
class PrincipalInfo;
45
} // namespace ipc
46
47
namespace dom {
48
49
class ContentParent;
50
class ServiceWorkerInfo;
51
class ServiceWorkerJobQueue;
52
class ServiceWorkerManagerChild;
53
class ServiceWorkerPrivate;
54
class ServiceWorkerRegistrar;
55
class ServiceWorkerShutdownBlocker;
56
57
class ServiceWorkerUpdateFinishCallback {
58
protected:
59
virtual ~ServiceWorkerUpdateFinishCallback() {}
60
61
public:
62
NS_INLINE_DECL_REFCOUNTING(ServiceWorkerUpdateFinishCallback)
63
64
virtual void UpdateSucceeded(ServiceWorkerRegistrationInfo* aInfo) = 0;
65
66
virtual void UpdateFailed(ErrorResult& aStatus) = 0;
67
};
68
69
#define NS_SERVICEWORKERMANAGER_IMPL_IID \
70
{ /* f4f8755a-69ca-46e8-a65d-775745535990 */ \
71
0xf4f8755a, 0x69ca, 0x46e8, { \
72
0xa6, 0x5d, 0x77, 0x57, 0x45, 0x53, 0x59, 0x90 \
73
} \
74
}
75
76
/*
77
* The ServiceWorkerManager is a per-process global that deals with the
78
* installation, querying and event dispatch of ServiceWorkers for all the
79
* origins in the process.
80
*
81
* NOTE: the following documentation is a WIP and only applies with
82
* dom.serviceWorkers.parent_intercept=true:
83
*
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.
90
*
91
* Example usage:
92
*
93
* MOZ_ASSERT(NS_IsMainThread(), "SWM is main-thread only");
94
*
95
* RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
96
*
97
* // Nullness must be checked by code that possibly executes during browser
98
* // shutdown, which is when the SWM is destroyed.
99
* if (swm) {
100
* // Do something with the SWM.
101
* }
102
*/
103
class ServiceWorkerManager final : public nsIServiceWorkerManager,
104
public nsIObserver {
105
friend class GetRegistrationsRunnable;
106
friend class GetRegistrationRunnable;
107
friend class ServiceWorkerJob;
108
friend class ServiceWorkerRegistrationInfo;
109
friend class ServiceWorkerUnregisterJob;
110
friend class ServiceWorkerUpdateJob;
111
friend class UpdateTimerCallback;
112
113
public:
114
NS_DECL_ISUPPORTS
115
NS_DECL_NSISERVICEWORKERMANAGER
116
NS_DECL_NSIOBSERVER
117
118
bool IsAvailable(nsIPrincipal* aPrincipal, nsIURI* aURI);
119
120
// Return true if the given content process could potentially be executing
121
// service worker code with the given principal. At the current time, this
122
// just means that we have any registration for the origin, regardless of
123
// scope. This is a very weak guarantee but is the best we can do when push
124
// notifications can currently spin up a service worker in content processes
125
// without our involvement in the parent process.
126
//
127
// In the future when there is only a single ServiceWorkerManager in the
128
// parent process that is entirely in control of spawning and running service
129
// worker code, we will be able to authoritatively indicate whether there is
130
// an activate service worker in the given content process. At that time we
131
// will rename this method HasActiveServiceWorkerInstance and provide
132
// semantics that ensure this method returns true until the worker is known to
133
// have shut down in order to allow the caller to induce a crash for security
134
// reasons without having to worry about shutdown races with the worker.
135
bool MayHaveActiveServiceWorkerInstance(ContentParent* aContent,
136
nsIPrincipal* aPrincipal);
137
138
void DispatchFetchEvent(nsIInterceptedChannel* aChannel, ErrorResult& aRv);
139
140
void Update(nsIPrincipal* aPrincipal, const nsACString& aScope,
141
ServiceWorkerUpdateFinishCallback* aCallback);
142
143
void UpdateInternal(nsIPrincipal* aPrincipal, const nsACString& aScope,
144
ServiceWorkerUpdateFinishCallback* aCallback);
145
146
void SoftUpdate(const OriginAttributes& aOriginAttributes,
147
const nsACString& aScope);
148
149
void SoftUpdateInternal(const OriginAttributes& aOriginAttributes,
150
const nsACString& aScope,
151
ServiceWorkerUpdateFinishCallback* aCallback);
152
153
void PropagateSoftUpdate(const OriginAttributes& aOriginAttributes,
154
const nsAString& aScope);
155
156
void PropagateRemove(const nsACString& aHost);
157
158
void Remove(const nsACString& aHost);
159
160
void PropagateRemoveAll();
161
162
void RemoveAll();
163
164
RefPtr<ServiceWorkerRegistrationPromise> Register(
165
const ClientInfo& aClientInfo, const nsACString& aScopeURL,
166
const nsACString& aScriptURL,
167
ServiceWorkerUpdateViaCache aUpdateViaCache);
168
169
RefPtr<ServiceWorkerRegistrationPromise> GetRegistration(
170
const ClientInfo& aClientInfo, const nsACString& aURL) const;
171
172
RefPtr<ServiceWorkerRegistrationListPromise> GetRegistrations(
173
const ClientInfo& aClientInfo) const;
174
175
already_AddRefed<ServiceWorkerRegistrationInfo> GetRegistration(
176
nsIPrincipal* aPrincipal, const nsACString& aScope) const;
177
178
already_AddRefed<ServiceWorkerRegistrationInfo> GetRegistration(
179
const mozilla::ipc::PrincipalInfo& aPrincipal,
180
const nsACString& aScope) const;
181
182
already_AddRefed<ServiceWorkerRegistrationInfo> CreateNewRegistration(
183
const nsCString& aScope, nsIPrincipal* aPrincipal,
184
ServiceWorkerUpdateViaCache aUpdateViaCache);
185
186
void RemoveRegistration(ServiceWorkerRegistrationInfo* aRegistration);
187
188
void StoreRegistration(nsIPrincipal* aPrincipal,
189
ServiceWorkerRegistrationInfo* aRegistration);
190
191
void FinishFetch(ServiceWorkerRegistrationInfo* aRegistration);
192
193
/**
194
* Report an error for the given scope to any window we think might be
195
* interested, failing over to the Browser Console if we couldn't find any.
196
*
197
* Error messages should be localized, so you probably want to call
198
* LocalizeAndReportToAllClients instead, which in turn calls us after
199
* localizing the error.
200
*/
201
void ReportToAllClients(const nsCString& aScope, const nsString& aMessage,
202
const nsString& aFilename, const nsString& aLine,
203
uint32_t aLineNumber, uint32_t aColumnNumber,
204
uint32_t aFlags);
205
206
/**
207
* Report a localized error for the given scope to any window we think might
208
* be interested.
209
*
210
* Note that this method takes an nsTArray<nsString> for the parameters, not
211
* bare chart16_t*[]. You can use a std::initializer_list constructor inline
212
* so that argument might look like: nsTArray<nsString> { some_nsString,
213
* PromiseFlatString(some_nsSubString_aka_nsAString),
214
* NS_ConvertUTF8toUTF16(some_nsCString_or_nsCSubString),
215
* NS_LITERAL_STRING("some literal") }. If you have anything else, like a
216
* number, you can use an nsAutoString with AppendInt/friends.
217
*
218
* @param [aFlags]
219
* The nsIScriptError flag, one of errorFlag (0x0), warningFlag (0x1),
220
* infoFlag (0x8). We default to error if omitted because usually we're
221
* logging exceptional and/or obvious breakage.
222
*/
223
static void LocalizeAndReportToAllClients(
224
const nsCString& aScope, const char* aStringKey,
225
const nsTArray<nsString>& aParamArray, uint32_t aFlags = 0x0,
226
const nsString& aFilename = EmptyString(),
227
const nsString& aLine = EmptyString(), uint32_t aLineNumber = 0,
228
uint32_t aColumnNumber = 0);
229
230
// Always consumes the error by reporting to consoles of all controlled
231
// documents.
232
void HandleError(JSContext* aCx, nsIPrincipal* aPrincipal,
233
const nsCString& aScope, const nsString& aWorkerURL,
234
const nsString& aMessage, const nsString& aFilename,
235
const nsString& aLine, uint32_t aLineNumber,
236
uint32_t aColumnNumber, uint32_t aFlags, JSExnType aExnType);
237
238
MOZ_MUST_USE RefPtr<GenericPromise> MaybeClaimClient(
239
const ClientInfo& aClientInfo,
240
ServiceWorkerRegistrationInfo* aWorkerRegistration);
241
242
MOZ_MUST_USE RefPtr<GenericPromise> MaybeClaimClient(
243
const ClientInfo& aClientInfo,
244
const ServiceWorkerDescriptor& aServiceWorker);
245
246
void SetSkipWaitingFlag(nsIPrincipal* aPrincipal, const nsCString& aScope,
247
uint64_t aServiceWorkerID);
248
249
static already_AddRefed<ServiceWorkerManager> GetInstance();
250
251
void LoadRegistration(const ServiceWorkerRegistrationData& aRegistration);
252
253
void LoadRegistrations(
254
const nsTArray<ServiceWorkerRegistrationData>& aRegistrations);
255
256
void MaybeCheckNavigationUpdate(const ClientInfo& aClientInfo);
257
258
nsresult SendPushEvent(const nsACString& aOriginAttributes,
259
const nsACString& aScope, const nsAString& aMessageId,
260
const Maybe<nsTArray<uint8_t>>& aData);
261
262
nsresult NotifyUnregister(nsIPrincipal* aPrincipal, const nsAString& aScope);
263
264
void WorkerIsIdle(ServiceWorkerInfo* aWorker);
265
266
RefPtr<ServiceWorkerRegistrationPromise> WhenReady(
267
const ClientInfo& aClientInfo);
268
269
void CheckPendingReadyPromises();
270
271
void RemovePendingReadyPromise(const ClientInfo& aClientInfo);
272
273
void NoteInheritedController(const ClientInfo& aClientInfo,
274
const ServiceWorkerDescriptor& aController);
275
276
void BlockShutdownOn(GenericNonExclusivePromise* aPromise);
277
278
nsresult GetClientRegistration(
279
const ClientInfo& aClientInfo,
280
ServiceWorkerRegistrationInfo** aRegistrationInfo);
281
282
void UpdateControlledClient(const ClientInfo& aOldClientInfo,
283
const ClientInfo& aNewClientInfo,
284
const ServiceWorkerDescriptor& aServiceWorker);
285
286
private:
287
struct RegistrationDataPerPrincipal;
288
289
static bool FindScopeForPath(const nsACString& aScopeKey,
290
const nsACString& aPath,
291
RegistrationDataPerPrincipal** aData,
292
nsACString& aMatch);
293
294
ServiceWorkerManager();
295
~ServiceWorkerManager();
296
297
void Init(ServiceWorkerRegistrar* aRegistrar);
298
299
RefPtr<GenericPromise> StartControllingClient(
300
const ClientInfo& aClientInfo,
301
ServiceWorkerRegistrationInfo* aRegistrationInfo,
302
bool aControlClientHandle = true);
303
304
void StopControllingClient(const ClientInfo& aClientInfo);
305
306
void MaybeStartShutdown();
307
308
already_AddRefed<ServiceWorkerJobQueue> GetOrCreateJobQueue(
309
const nsACString& aOriginSuffix, const nsACString& aScope);
310
311
void MaybeRemoveRegistrationInfo(const nsACString& aScopeKey);
312
313
already_AddRefed<ServiceWorkerRegistrationInfo> GetRegistration(
314
const nsACString& aScopeKey, const nsACString& aScope) const;
315
316
void AbortCurrentUpdate(ServiceWorkerRegistrationInfo* aRegistration);
317
318
nsresult Update(ServiceWorkerRegistrationInfo* aRegistration);
319
320
ServiceWorkerInfo* GetActiveWorkerInfoForScope(
321
const OriginAttributes& aOriginAttributes, const nsACString& aScope);
322
323
void StopControllingRegistration(
324
ServiceWorkerRegistrationInfo* aRegistration);
325
326
already_AddRefed<ServiceWorkerRegistrationInfo>
327
GetServiceWorkerRegistrationInfo(const ClientInfo& aClientInfo) const;
328
329
already_AddRefed<ServiceWorkerRegistrationInfo>
330
GetServiceWorkerRegistrationInfo(nsIPrincipal* aPrincipal,
331
nsIURI* aURI) const;
332
333
already_AddRefed<ServiceWorkerRegistrationInfo>
334
GetServiceWorkerRegistrationInfo(const nsACString& aScopeKey,
335
nsIURI* aURI) const;
336
337
// This method generates a key using isInElementBrowser from the principal. We
338
// don't use the origin because it can change during the loading.
339
static nsresult PrincipalToScopeKey(nsIPrincipal* aPrincipal,
340
nsACString& aKey);
341
342
static nsresult PrincipalInfoToScopeKey(
343
const mozilla::ipc::PrincipalInfo& aPrincipalInfo, nsACString& aKey);
344
345
static void AddScopeAndRegistration(
346
const nsACString& aScope, ServiceWorkerRegistrationInfo* aRegistation);
347
348
static bool HasScope(nsIPrincipal* aPrincipal, const nsACString& aScope);
349
350
static void RemoveScopeAndRegistration(
351
ServiceWorkerRegistrationInfo* aRegistration);
352
353
void QueueFireEventOnServiceWorkerRegistrations(
354
ServiceWorkerRegistrationInfo* aRegistration, const nsAString& aName);
355
356
void UpdateClientControllers(ServiceWorkerRegistrationInfo* aRegistration);
357
358
void MaybeRemoveRegistration(ServiceWorkerRegistrationInfo* aRegistration);
359
360
// Removes all service worker registrations that matches the given pattern.
361
void RemoveAllRegistrations(OriginAttributesPattern* aPattern);
362
363
RefPtr<ServiceWorkerManagerChild> mActor;
364
365
bool mShuttingDown;
366
367
nsTArray<nsCOMPtr<nsIServiceWorkerManagerListener>> mListeners;
368
369
void NotifyListenersOnRegister(
370
nsIServiceWorkerRegistrationInfo* aRegistration);
371
372
void NotifyListenersOnUnregister(
373
nsIServiceWorkerRegistrationInfo* aRegistration);
374
375
void ScheduleUpdateTimer(nsIPrincipal* aPrincipal, const nsACString& aScope);
376
377
void UpdateTimerFired(nsIPrincipal* aPrincipal, const nsACString& aScope);
378
379
void MaybeSendUnregister(nsIPrincipal* aPrincipal, const nsACString& aScope);
380
381
nsresult SendNotificationEvent(const nsAString& aEventName,
382
const nsACString& aOriginSuffix,
383
const nsACString& aScope, const nsAString& aID,
384
const nsAString& aTitle, const nsAString& aDir,
385
const nsAString& aLang, const nsAString& aBody,
386
const nsAString& aTag, const nsAString& aIcon,
387
const nsAString& aData,
388
const nsAString& aBehavior);
389
390
// Used by remove() and removeAll() when clearing history.
391
// MUST ONLY BE CALLED FROM UnregisterIfMatchesHost!
392
void ForceUnregister(RegistrationDataPerPrincipal* aRegistrationData,
393
ServiceWorkerRegistrationInfo* aRegistration);
394
395
// An "orphaned" registration is one that is unregistered and not controlling
396
// clients. The ServiceWorkerManager must know about all orphaned
397
// registrations to forcefully shutdown all Service Workers during browser
398
// shutdown.
399
void AddOrphanedRegistration(ServiceWorkerRegistrationInfo* aRegistration);
400
401
void RemoveOrphanedRegistration(ServiceWorkerRegistrationInfo* aRegistration);
402
403
HashSet<RefPtr<ServiceWorkerRegistrationInfo>,
404
PointerHasher<ServiceWorkerRegistrationInfo*>>
405
mOrphanedRegistrations;
406
407
RefPtr<ServiceWorkerShutdownBlocker> mShutdownBlocker;
408
409
nsClassHashtable<nsCStringHashKey, RegistrationDataPerPrincipal>
410
mRegistrationInfos;
411
412
struct ControlledClientData {
413
RefPtr<ClientHandle> mClientHandle;
414
RefPtr<ServiceWorkerRegistrationInfo> mRegistrationInfo;
415
416
ControlledClientData(ClientHandle* aClientHandle,
417
ServiceWorkerRegistrationInfo* aRegistrationInfo)
418
: mClientHandle(aClientHandle), mRegistrationInfo(aRegistrationInfo) {}
419
};
420
421
nsClassHashtable<nsIDHashKey, ControlledClientData> mControlledClients;
422
423
struct PendingReadyData {
424
RefPtr<ClientHandle> mClientHandle;
425
RefPtr<ServiceWorkerRegistrationPromise::Private> mPromise;
426
427
explicit PendingReadyData(ClientHandle* aClientHandle)
428
: mClientHandle(aClientHandle),
429
mPromise(new ServiceWorkerRegistrationPromise::Private(__func__)) {}
430
};
431
432
nsTArray<UniquePtr<PendingReadyData>> mPendingReadyList;
433
};
434
435
} // namespace dom
436
} // namespace mozilla
437
438
#endif // mozilla_dom_workers_serviceworkermanager_h