Line data Source code
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/Preferences.h"
19 : #include "mozilla/TypedEnumBits.h"
20 : #include "mozilla/UniquePtr.h"
21 : #include "mozilla/WeakPtr.h"
22 : #include "mozilla/dom/BindingUtils.h"
23 : #include "mozilla/dom/Promise.h"
24 : #include "mozilla/dom/ServiceWorkerCommon.h"
25 : #include "mozilla/dom/ServiceWorkerRegistrar.h"
26 : #include "mozilla/dom/ServiceWorkerRegistrarTypes.h"
27 : #include "mozilla/dom/workers/ServiceWorkerRegistrationInfo.h"
28 : #include "mozilla/ipc/BackgroundUtils.h"
29 : #include "nsClassHashtable.h"
30 : #include "nsDataHashtable.h"
31 : #include "nsIIPCBackgroundChildCreateCallback.h"
32 : #include "nsRefPtrHashtable.h"
33 : #include "nsTArrayForwardDeclare.h"
34 : #include "nsTObserverArray.h"
35 :
36 : class nsIConsoleReportCollector;
37 :
38 : namespace mozilla {
39 :
40 : class OriginAttributes;
41 :
42 : namespace dom {
43 :
44 : class ServiceWorkerRegistrar;
45 : class ServiceWorkerRegistrationListener;
46 :
47 : namespace workers {
48 :
49 : class ServiceWorkerClientInfo;
50 : class ServiceWorkerInfo;
51 : class ServiceWorkerJobQueue;
52 : class ServiceWorkerManagerChild;
53 : class ServiceWorkerPrivate;
54 :
55 0 : class ServiceWorkerUpdateFinishCallback
56 : {
57 : protected:
58 0 : virtual ~ServiceWorkerUpdateFinishCallback()
59 0 : {}
60 :
61 : public:
62 0 : NS_INLINE_DECL_REFCOUNTING(ServiceWorkerUpdateFinishCallback)
63 :
64 : virtual
65 : void UpdateSucceeded(ServiceWorkerRegistrationInfo* aInfo) = 0;
66 :
67 : virtual
68 : void UpdateFailed(ErrorResult& aStatus) = 0;
69 : };
70 :
71 : #define NS_SERVICEWORKERMANAGER_IMPL_IID \
72 : { /* f4f8755a-69ca-46e8-a65d-775745535990 */ \
73 : 0xf4f8755a, \
74 : 0x69ca, \
75 : 0x46e8, \
76 : { 0xa6, 0x5d, 0x77, 0x57, 0x45, 0x53, 0x59, 0x90 } \
77 : }
78 :
79 : /*
80 : * The ServiceWorkerManager is a per-process global that deals with the
81 : * installation, querying and event dispatch of ServiceWorkers for all the
82 : * origins in the process.
83 : */
84 : class ServiceWorkerManager final
85 : : public nsIServiceWorkerManager
86 : , public nsIIPCBackgroundChildCreateCallback
87 : , public nsIObserver
88 : {
89 : friend class GetReadyPromiseRunnable;
90 : friend class GetRegistrationsRunnable;
91 : friend class GetRegistrationRunnable;
92 : friend class ServiceWorkerJob;
93 : friend class ServiceWorkerRegistrationInfo;
94 : friend class ServiceWorkerUnregisterJob;
95 : friend class ServiceWorkerUpdateJob;
96 : friend class UpdateTimerCallback;
97 :
98 : public:
99 : NS_DECL_ISUPPORTS
100 : NS_DECL_NSISERVICEWORKERMANAGER
101 : NS_DECL_NSIIPCBACKGROUNDCHILDCREATECALLBACK
102 : NS_DECL_NSIOBSERVER
103 :
104 : struct RegistrationDataPerPrincipal;
105 : nsClassHashtable<nsCStringHashKey, RegistrationDataPerPrincipal> mRegistrationInfos;
106 :
107 : nsTObserverArray<ServiceWorkerRegistrationListener*> mServiceWorkerRegistrationListeners;
108 :
109 : nsRefPtrHashtable<nsISupportsHashKey, ServiceWorkerRegistrationInfo> mControlledDocuments;
110 :
111 : // Track all documents that have attempted to register a service worker for a
112 : // given scope.
113 : typedef nsTArray<nsCOMPtr<nsIWeakReference>> WeakDocumentList;
114 : nsClassHashtable<nsCStringHashKey, WeakDocumentList> mRegisteringDocuments;
115 :
116 : // Track all intercepted navigation channels for a given scope. Channels are
117 : // placed in the appropriate list before dispatch the FetchEvent to the worker
118 : // thread and removed once FetchEvent processing dispatches back to the main
119 : // thread.
120 : //
121 : // Note: Its safe to use weak references here because a RAII-style callback
122 : // is registered with the channel before its added to this list. We
123 : // are guaranteed the callback will fire before and remove the ref
124 : // from this list before the channel is destroyed.
125 : typedef nsTArray<nsIInterceptedChannel*> InterceptionList;
126 : nsClassHashtable<nsCStringHashKey, InterceptionList> mNavigationInterceptions;
127 :
128 : bool
129 : IsAvailable(nsIPrincipal* aPrincipal, nsIURI* aURI);
130 :
131 : bool
132 : IsControlled(nsIDocument* aDocument, ErrorResult& aRv);
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(const OriginAttributes& aOriginAttributes,
155 : nsIDocument* aDoc,
156 : const nsAString& aDocumentIdForTopLevelNavigation,
157 : nsIInterceptedChannel* aChannel,
158 : bool aIsReload,
159 : bool aIsSubresourceLoad,
160 : ErrorResult& aRv);
161 :
162 : void
163 : Update(nsIPrincipal* aPrincipal,
164 : const nsACString& aScope,
165 : ServiceWorkerUpdateFinishCallback* aCallback);
166 :
167 : void
168 : UpdateInternal(nsIPrincipal* aPrincipal,
169 : const nsACString& aScope,
170 : ServiceWorkerUpdateFinishCallback* aCallback);
171 :
172 : void
173 : SoftUpdate(const OriginAttributes& aOriginAttributes,
174 : const nsACString& aScope);
175 :
176 : void
177 : SoftUpdateInternal(const OriginAttributes& aOriginAttributes,
178 : const nsACString& aScope,
179 : ServiceWorkerUpdateFinishCallback* aCallback);
180 :
181 :
182 : void
183 : PropagateSoftUpdate(const OriginAttributes& aOriginAttributes,
184 : const nsAString& aScope);
185 :
186 : void
187 : PropagateRemove(const nsACString& aHost);
188 :
189 : void
190 : Remove(const nsACString& aHost);
191 :
192 : void
193 : PropagateRemoveAll();
194 :
195 : void
196 : RemoveAll();
197 :
198 : already_AddRefed<ServiceWorkerRegistrationInfo>
199 : GetRegistration(nsIPrincipal* aPrincipal, const nsACString& aScope) const;
200 :
201 : already_AddRefed<ServiceWorkerRegistrationInfo>
202 : CreateNewRegistration(const nsCString& aScope,
203 : nsIPrincipal* aPrincipal,
204 : nsLoadFlags aLoadFlags);
205 :
206 : void
207 : RemoveRegistration(ServiceWorkerRegistrationInfo* aRegistration);
208 :
209 : void StoreRegistration(nsIPrincipal* aPrincipal,
210 : ServiceWorkerRegistrationInfo* aRegistration);
211 :
212 : void
213 : FinishFetch(ServiceWorkerRegistrationInfo* aRegistration);
214 :
215 : /**
216 : * Report an error for the given scope to any window we think might be
217 : * interested, failing over to the Browser Console if we couldn't find any.
218 : *
219 : * Error messages should be localized, so you probably want to call
220 : * LocalizeAndReportToAllClients instead, which in turn calls us after
221 : * localizing the error.
222 : */
223 : void
224 : ReportToAllClients(const nsCString& aScope,
225 : const nsString& aMessage,
226 : const nsString& aFilename,
227 : const nsString& aLine,
228 : uint32_t aLineNumber,
229 : uint32_t aColumnNumber,
230 : uint32_t aFlags);
231 :
232 : /**
233 : * Report a localized error for the given scope to any window we think might
234 : * be interested.
235 : *
236 : * Note that this method takes an nsTArray<nsString> for the parameters, not
237 : * bare chart16_t*[]. You can use a std::initializer_list constructor inline
238 : * so that argument might look like: nsTArray<nsString> { some_nsString,
239 : * PromiseFlatString(some_nsSubString_aka_nsAString),
240 : * NS_ConvertUTF8toUTF16(some_nsCString_or_nsCSubString),
241 : * NS_LITERAL_STRING("some literal") }. If you have anything else, like a
242 : * number, you can use an nsAutoString with AppendInt/friends.
243 : *
244 : * @param [aFlags]
245 : * The nsIScriptError flag, one of errorFlag (0x0), warningFlag (0x1),
246 : * infoFlag (0x8). We default to error if omitted because usually we're
247 : * logging exceptional and/or obvious breakage.
248 : */
249 : static void
250 : LocalizeAndReportToAllClients(const nsCString& aScope,
251 : const char* aStringKey,
252 : const nsTArray<nsString>& aParamArray,
253 : uint32_t aFlags = 0x0,
254 : const nsString& aFilename = EmptyString(),
255 : const nsString& aLine = EmptyString(),
256 : uint32_t aLineNumber = 0,
257 : uint32_t aColumnNumber = 0);
258 :
259 : void
260 : FlushReportsToAllClients(const nsACString& aScope,
261 : nsIConsoleReportCollector* aReporter);
262 :
263 : // Always consumes the error by reporting to consoles of all controlled
264 : // documents.
265 : void
266 : HandleError(JSContext* aCx,
267 : nsIPrincipal* aPrincipal,
268 : const nsCString& aScope,
269 : const nsString& aWorkerURL,
270 : const nsString& aMessage,
271 : const nsString& aFilename,
272 : const nsString& aLine,
273 : uint32_t aLineNumber,
274 : uint32_t aColumnNumber,
275 : uint32_t aFlags,
276 : JSExnType aExnType);
277 :
278 : UniquePtr<ServiceWorkerClientInfo>
279 : GetClient(nsIPrincipal* aPrincipal,
280 : const nsAString& aClientId,
281 : ErrorResult& aRv);
282 :
283 : void
284 : GetAllClients(nsIPrincipal* aPrincipal,
285 : const nsCString& aScope,
286 : uint64_t aServiceWorkerID,
287 : bool aIncludeUncontrolled,
288 : nsTArray<ServiceWorkerClientInfo>& aDocuments);
289 :
290 : void
291 : MaybeClaimClient(nsIDocument* aDocument,
292 : ServiceWorkerRegistrationInfo* aWorkerRegistration);
293 :
294 : nsresult
295 : ClaimClients(nsIPrincipal* aPrincipal, const nsCString& aScope, uint64_t aId);
296 :
297 : void
298 : SetSkipWaitingFlag(nsIPrincipal* aPrincipal, const nsCString& aScope,
299 : uint64_t aServiceWorkerID);
300 :
301 : static already_AddRefed<ServiceWorkerManager>
302 : GetInstance();
303 :
304 : void
305 : LoadRegistration(const ServiceWorkerRegistrationData& aRegistration);
306 :
307 : void
308 : LoadRegistrations(const nsTArray<ServiceWorkerRegistrationData>& aRegistrations);
309 :
310 : // Used by remove() and removeAll() when clearing history.
311 : // MUST ONLY BE CALLED FROM UnregisterIfMatchesHost!
312 : void
313 : ForceUnregister(RegistrationDataPerPrincipal* aRegistrationData,
314 : ServiceWorkerRegistrationInfo* aRegistration);
315 :
316 : NS_IMETHOD
317 : AddRegistrationEventListener(const nsAString& aScope,
318 : ServiceWorkerRegistrationListener* aListener);
319 :
320 : NS_IMETHOD
321 : RemoveRegistrationEventListener(const nsAString& aScope,
322 : ServiceWorkerRegistrationListener* aListener);
323 :
324 : void
325 : MaybeCheckNavigationUpdate(nsIDocument* aDoc);
326 :
327 : nsresult
328 : SendPushEvent(const nsACString& aOriginAttributes,
329 : const nsACString& aScope,
330 : const nsAString& aMessageId,
331 : const Maybe<nsTArray<uint8_t>>& aData);
332 :
333 : nsresult
334 : NotifyUnregister(nsIPrincipal* aPrincipal, const nsAString& aScope);
335 :
336 : void
337 : WorkerIsIdle(ServiceWorkerInfo* aWorker);
338 :
339 : private:
340 : ServiceWorkerManager();
341 : ~ServiceWorkerManager();
342 :
343 : void
344 : Init(ServiceWorkerRegistrar* aRegistrar);
345 :
346 : void
347 : MaybeStartShutdown();
348 :
349 : already_AddRefed<ServiceWorkerJobQueue>
350 : GetOrCreateJobQueue(const nsACString& aOriginSuffix,
351 : const nsACString& aScope);
352 :
353 : void
354 : MaybeRemoveRegistrationInfo(const nsACString& aScopeKey);
355 :
356 : already_AddRefed<ServiceWorkerRegistrationInfo>
357 : GetRegistration(const nsACString& aScopeKey,
358 : const nsACString& aScope) const;
359 :
360 : void
361 : AbortCurrentUpdate(ServiceWorkerRegistrationInfo* aRegistration);
362 :
363 : nsresult
364 : Update(ServiceWorkerRegistrationInfo* aRegistration);
365 :
366 : nsresult
367 : GetDocumentRegistration(nsIDocument* aDoc,
368 : ServiceWorkerRegistrationInfo** aRegistrationInfo);
369 :
370 : nsresult
371 : GetServiceWorkerForScope(nsPIDOMWindowInner* aWindow,
372 : const nsAString& aScope,
373 : WhichServiceWorker aWhichWorker,
374 : nsISupports** aServiceWorker);
375 :
376 : ServiceWorkerInfo*
377 : GetActiveWorkerInfoForScope(const OriginAttributes& aOriginAttributes,
378 : const nsACString& aScope);
379 :
380 : ServiceWorkerInfo*
381 : GetActiveWorkerInfoForDocument(nsIDocument* aDocument);
382 :
383 : void
384 : TransitionServiceWorkerRegistrationWorker(ServiceWorkerRegistrationInfo* aRegistration,
385 : WhichServiceWorker aWhichOne);
386 : void
387 : InvalidateServiceWorkerRegistrationWorker(ServiceWorkerRegistrationInfo* aRegistration,
388 : WhichServiceWorker aWhichOnes);
389 :
390 : void
391 : NotifyServiceWorkerRegistrationRemoved(ServiceWorkerRegistrationInfo* aRegistration);
392 :
393 : void
394 : StartControllingADocument(ServiceWorkerRegistrationInfo* aRegistration,
395 : nsIDocument* aDoc,
396 : const nsAString& aDocumentId);
397 :
398 : void
399 : StopControllingADocument(ServiceWorkerRegistrationInfo* aRegistration);
400 :
401 : already_AddRefed<ServiceWorkerRegistrationInfo>
402 : GetServiceWorkerRegistrationInfo(nsPIDOMWindowInner* aWindow);
403 :
404 : already_AddRefed<ServiceWorkerRegistrationInfo>
405 : GetServiceWorkerRegistrationInfo(nsIDocument* aDoc);
406 :
407 : already_AddRefed<ServiceWorkerRegistrationInfo>
408 : GetServiceWorkerRegistrationInfo(nsIPrincipal* aPrincipal, nsIURI* aURI);
409 :
410 : already_AddRefed<ServiceWorkerRegistrationInfo>
411 : GetServiceWorkerRegistrationInfo(const nsACString& aScopeKey,
412 : nsIURI* aURI);
413 :
414 : // This method generates a key using appId and isInElementBrowser from the
415 : // principal. We don't use the origin because it can change during the
416 : // loading.
417 : static nsresult
418 : PrincipalToScopeKey(nsIPrincipal* aPrincipal, nsACString& aKey);
419 :
420 : static void
421 : AddScopeAndRegistration(const nsACString& aScope,
422 : ServiceWorkerRegistrationInfo* aRegistation);
423 :
424 : static bool
425 : FindScopeForPath(const nsACString& aScopeKey,
426 : const nsACString& aPath,
427 : RegistrationDataPerPrincipal** aData, nsACString& aMatch);
428 :
429 : static bool
430 : HasScope(nsIPrincipal* aPrincipal, const nsACString& aScope);
431 :
432 : static void
433 : RemoveScopeAndRegistration(ServiceWorkerRegistrationInfo* aRegistration);
434 :
435 : void
436 : QueueFireEventOnServiceWorkerRegistrations(ServiceWorkerRegistrationInfo* aRegistration,
437 : const nsAString& aName);
438 :
439 : void
440 : FireUpdateFoundOnServiceWorkerRegistrations(ServiceWorkerRegistrationInfo* aRegistration);
441 :
442 : void
443 : FireControllerChange(ServiceWorkerRegistrationInfo* aRegistration);
444 :
445 : void
446 : StorePendingReadyPromise(nsPIDOMWindowInner* aWindow, nsIURI* aURI,
447 : Promise* aPromise);
448 :
449 : void
450 : CheckPendingReadyPromises();
451 :
452 : bool
453 : CheckReadyPromise(nsPIDOMWindowInner* aWindow, nsIURI* aURI,
454 : Promise* aPromise);
455 :
456 0 : struct PendingReadyPromise final
457 : {
458 0 : PendingReadyPromise(nsIURI* aURI, Promise* aPromise)
459 0 : : mURI(aURI), mPromise(aPromise)
460 0 : {}
461 :
462 : nsCOMPtr<nsIURI> mURI;
463 : RefPtr<Promise> mPromise;
464 : };
465 :
466 : void AppendPendingOperation(nsIRunnable* aRunnable);
467 :
468 0 : bool HasBackgroundActor() const
469 : {
470 0 : return !!mActor;
471 : }
472 :
473 : nsClassHashtable<nsISupportsHashKey, PendingReadyPromise> mPendingReadyPromises;
474 :
475 : void
476 : MaybeRemoveRegistration(ServiceWorkerRegistrationInfo* aRegistration);
477 :
478 : // Removes all service worker registrations that matches the given pattern.
479 : void
480 : RemoveAllRegistrations(OriginAttributesPattern* aPattern);
481 :
482 : RefPtr<ServiceWorkerManagerChild> mActor;
483 :
484 : nsTArray<nsCOMPtr<nsIRunnable>> mPendingOperations;
485 :
486 : bool mShuttingDown;
487 :
488 : nsTArray<nsCOMPtr<nsIServiceWorkerManagerListener>> mListeners;
489 :
490 : void
491 : NotifyListenersOnRegister(nsIServiceWorkerRegistrationInfo* aRegistration);
492 :
493 : void
494 : NotifyListenersOnUnregister(nsIServiceWorkerRegistrationInfo* aRegistration);
495 :
496 : void
497 : AddRegisteringDocument(const nsACString& aScope, nsIDocument* aDoc);
498 :
499 : class InterceptionReleaseHandle;
500 :
501 : void
502 : AddNavigationInterception(const nsACString& aScope,
503 : nsIInterceptedChannel* aChannel);
504 :
505 : void
506 : RemoveNavigationInterception(const nsACString& aScope,
507 : nsIInterceptedChannel* aChannel);
508 :
509 : void
510 : ScheduleUpdateTimer(nsIPrincipal* aPrincipal, const nsACString& aScope);
511 :
512 : void
513 : UpdateTimerFired(nsIPrincipal* aPrincipal, const nsACString& aScope);
514 :
515 : void
516 : MaybeSendUnregister(nsIPrincipal* aPrincipal, const nsACString& aScope);
517 :
518 : nsresult
519 : SendNotificationEvent(const nsAString& aEventName,
520 : const nsACString& aOriginSuffix,
521 : const nsACString& aScope,
522 : const nsAString& aID,
523 : const nsAString& aTitle,
524 : const nsAString& aDir,
525 : const nsAString& aLang,
526 : const nsAString& aBody,
527 : const nsAString& aTag,
528 : const nsAString& aIcon,
529 : const nsAString& aData,
530 : const nsAString& aBehavior);
531 : };
532 :
533 : } // namespace workers
534 : } // namespace dom
535 : } // namespace mozilla
536 :
537 : #endif // mozilla_dom_workers_serviceworkermanager_h
|