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