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 : #include "ServiceWorkerManager.h"
8 :
9 : #include "nsAutoPtr.h"
10 : #include "nsIConsoleService.h"
11 : #include "nsIDOMEventTarget.h"
12 : #include "nsIDocument.h"
13 : #include "nsIScriptSecurityManager.h"
14 : #include "nsIStreamLoader.h"
15 : #include "nsIHttpChannel.h"
16 : #include "nsIHttpChannelInternal.h"
17 : #include "nsIHttpHeaderVisitor.h"
18 : #include "nsINetworkInterceptController.h"
19 : #include "nsIMutableArray.h"
20 : #include "nsIScriptError.h"
21 : #include "nsISimpleEnumerator.h"
22 : #include "nsITimer.h"
23 : #include "nsIUploadChannel2.h"
24 : #include "nsPIDOMWindow.h"
25 : #include "nsServiceManagerUtils.h"
26 : #include "nsDebug.h"
27 : #include "nsISupportsPrimitives.h"
28 : #include "nsIPermissionManager.h"
29 :
30 : #include "jsapi.h"
31 :
32 : #include "mozilla/BasePrincipal.h"
33 : #include "mozilla/ClearOnShutdown.h"
34 : #include "mozilla/ErrorNames.h"
35 : #include "mozilla/LoadContext.h"
36 : #include "mozilla/Telemetry.h"
37 : #include "mozilla/dom/BindingUtils.h"
38 : #include "mozilla/dom/ContentParent.h"
39 : #include "mozilla/dom/ContentChild.h"
40 : #include "mozilla/dom/DOMError.h"
41 : #include "mozilla/dom/ErrorEvent.h"
42 : #include "mozilla/dom/Headers.h"
43 : #include "mozilla/dom/InternalHeaders.h"
44 : #include "mozilla/dom/Navigator.h"
45 : #include "mozilla/dom/NotificationEvent.h"
46 : #include "mozilla/dom/PromiseNativeHandler.h"
47 : #include "mozilla/dom/Request.h"
48 : #include "mozilla/dom/RootedDictionary.h"
49 : #include "mozilla/dom/TypedArray.h"
50 : #include "mozilla/ipc/BackgroundChild.h"
51 : #include "mozilla/ipc/PBackgroundChild.h"
52 : #include "mozilla/ipc/PBackgroundSharedTypes.h"
53 : #include "mozilla/dom/ScriptLoader.h"
54 : #include "mozilla/Unused.h"
55 : #include "mozilla/EnumSet.h"
56 :
57 : #include "nsContentPolicyUtils.h"
58 : #include "nsContentSecurityManager.h"
59 : #include "nsContentUtils.h"
60 : #include "nsGlobalWindow.h"
61 : #include "nsNetUtil.h"
62 : #include "nsProxyRelease.h"
63 : #include "nsQueryObject.h"
64 : #include "nsTArray.h"
65 :
66 : #include "RuntimeService.h"
67 : #include "ServiceWorker.h"
68 : #include "ServiceWorkerClient.h"
69 : #include "ServiceWorkerContainer.h"
70 : #include "ServiceWorkerInfo.h"
71 : #include "ServiceWorkerJobQueue.h"
72 : #include "ServiceWorkerManagerChild.h"
73 : #include "ServiceWorkerPrivate.h"
74 : #include "ServiceWorkerRegisterJob.h"
75 : #include "ServiceWorkerRegistrar.h"
76 : #include "ServiceWorkerRegistration.h"
77 : #include "ServiceWorkerScriptCache.h"
78 : #include "ServiceWorkerEvents.h"
79 : #include "ServiceWorkerUnregisterJob.h"
80 : #include "ServiceWorkerUpdateJob.h"
81 : #include "ServiceWorkerUpdaterChild.h"
82 : #include "SharedWorker.h"
83 : #include "WorkerInlines.h"
84 : #include "WorkerPrivate.h"
85 : #include "WorkerRunnable.h"
86 : #include "WorkerScope.h"
87 :
88 : #ifdef PostMessage
89 : #undef PostMessage
90 : #endif
91 :
92 : using namespace mozilla;
93 : using namespace mozilla::dom;
94 : using namespace mozilla::ipc;
95 :
96 : BEGIN_WORKERS_NAMESPACE
97 :
98 : #define PURGE_DOMAIN_DATA "browser:purge-domain-data"
99 : #define PURGE_SESSION_HISTORY "browser:purge-session-history"
100 : #define CLEAR_ORIGIN_DATA "clear-origin-attributes-data"
101 :
102 : static_assert(nsIHttpChannelInternal::CORS_MODE_SAME_ORIGIN == static_cast<uint32_t>(RequestMode::Same_origin),
103 : "RequestMode enumeration value should match Necko CORS mode value.");
104 : static_assert(nsIHttpChannelInternal::CORS_MODE_NO_CORS == static_cast<uint32_t>(RequestMode::No_cors),
105 : "RequestMode enumeration value should match Necko CORS mode value.");
106 : static_assert(nsIHttpChannelInternal::CORS_MODE_CORS == static_cast<uint32_t>(RequestMode::Cors),
107 : "RequestMode enumeration value should match Necko CORS mode value.");
108 : static_assert(nsIHttpChannelInternal::CORS_MODE_NAVIGATE == static_cast<uint32_t>(RequestMode::Navigate),
109 : "RequestMode enumeration value should match Necko CORS mode value.");
110 :
111 : static_assert(nsIHttpChannelInternal::REDIRECT_MODE_FOLLOW == static_cast<uint32_t>(RequestRedirect::Follow),
112 : "RequestRedirect enumeration value should make Necko Redirect mode value.");
113 : static_assert(nsIHttpChannelInternal::REDIRECT_MODE_ERROR == static_cast<uint32_t>(RequestRedirect::Error),
114 : "RequestRedirect enumeration value should make Necko Redirect mode value.");
115 : static_assert(nsIHttpChannelInternal::REDIRECT_MODE_MANUAL == static_cast<uint32_t>(RequestRedirect::Manual),
116 : "RequestRedirect enumeration value should make Necko Redirect mode value.");
117 : static_assert(3 == static_cast<uint32_t>(RequestRedirect::EndGuard_),
118 : "RequestRedirect enumeration value should make Necko Redirect mode value.");
119 :
120 : static_assert(nsIHttpChannelInternal::FETCH_CACHE_MODE_DEFAULT == static_cast<uint32_t>(RequestCache::Default),
121 : "RequestCache enumeration value should match Necko Cache mode value.");
122 : static_assert(nsIHttpChannelInternal::FETCH_CACHE_MODE_NO_STORE == static_cast<uint32_t>(RequestCache::No_store),
123 : "RequestCache enumeration value should match Necko Cache mode value.");
124 : static_assert(nsIHttpChannelInternal::FETCH_CACHE_MODE_RELOAD == static_cast<uint32_t>(RequestCache::Reload),
125 : "RequestCache enumeration value should match Necko Cache mode value.");
126 : static_assert(nsIHttpChannelInternal::FETCH_CACHE_MODE_NO_CACHE == static_cast<uint32_t>(RequestCache::No_cache),
127 : "RequestCache enumeration value should match Necko Cache mode value.");
128 : static_assert(nsIHttpChannelInternal::FETCH_CACHE_MODE_FORCE_CACHE == static_cast<uint32_t>(RequestCache::Force_cache),
129 : "RequestCache enumeration value should match Necko Cache mode value.");
130 : static_assert(nsIHttpChannelInternal::FETCH_CACHE_MODE_ONLY_IF_CACHED == static_cast<uint32_t>(RequestCache::Only_if_cached),
131 : "RequestCache enumeration value should match Necko Cache mode value.");
132 : static_assert(6 == static_cast<uint32_t>(RequestCache::EndGuard_),
133 : "RequestCache enumeration value should match Necko Cache mode value.");
134 :
135 3 : static StaticRefPtr<ServiceWorkerManager> gInstance;
136 :
137 0 : struct ServiceWorkerManager::RegistrationDataPerPrincipal final
138 : {
139 : // Ordered list of scopes for glob matching.
140 : // Each entry is an absolute URL representing the scope.
141 : // Each value of the hash table is an array of an absolute URLs representing
142 : // the scopes.
143 : //
144 : // An array is used for now since the number of controlled scopes per
145 : // domain is expected to be relatively low. If that assumption was proved
146 : // wrong this should be replaced with a better structure to avoid the
147 : // memmoves associated with inserting stuff in the middle of the array.
148 : nsTArray<nsCString> mOrderedScopes;
149 :
150 : // Scope to registration.
151 : // The scope should be a fully qualified valid URL.
152 : nsRefPtrHashtable<nsCStringHashKey, ServiceWorkerRegistrationInfo> mInfos;
153 :
154 : // Maps scopes to job queues.
155 : nsRefPtrHashtable<nsCStringHashKey, ServiceWorkerJobQueue> mJobQueues;
156 :
157 : // Map scopes to scheduled update timers.
158 : nsInterfaceHashtable<nsCStringHashKey, nsITimer> mUpdateTimers;
159 : };
160 :
161 : namespace {
162 :
163 : nsresult
164 0 : PopulateRegistrationData(nsIPrincipal* aPrincipal,
165 : const ServiceWorkerRegistrationInfo* aRegistration,
166 : ServiceWorkerRegistrationData& aData)
167 : {
168 0 : MOZ_ASSERT(aPrincipal);
169 0 : MOZ_ASSERT(aRegistration);
170 :
171 0 : if (NS_WARN_IF(!BasePrincipal::Cast(aPrincipal)->IsCodebasePrincipal())) {
172 0 : return NS_ERROR_FAILURE;
173 : }
174 :
175 0 : nsresult rv = PrincipalToPrincipalInfo(aPrincipal, &aData.principal());
176 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
177 0 : return rv;
178 : }
179 :
180 0 : aData.scope() = aRegistration->mScope;
181 :
182 0 : RefPtr<ServiceWorkerInfo> newest = aRegistration->Newest();
183 0 : if (NS_WARN_IF(!newest)) {
184 0 : return NS_ERROR_FAILURE;
185 : }
186 :
187 0 : if (aRegistration->GetActive()) {
188 0 : aData.currentWorkerURL() = aRegistration->GetActive()->ScriptSpec();
189 0 : aData.cacheName() = aRegistration->GetActive()->CacheName();
190 0 : aData.currentWorkerHandlesFetch() = aRegistration->GetActive()->HandlesFetch();
191 :
192 0 : aData.currentWorkerInstalledTime() =
193 0 : aRegistration->GetActive()->GetInstalledTime();
194 0 : aData.currentWorkerActivatedTime() =
195 0 : aRegistration->GetActive()->GetActivatedTime();
196 : }
197 :
198 0 : aData.loadFlags() = aRegistration->GetLoadFlags();
199 :
200 0 : aData.lastUpdateTime() = aRegistration->GetLastUpdateTime();
201 :
202 0 : return NS_OK;
203 : }
204 :
205 : class TeardownRunnable final : public Runnable
206 : {
207 : public:
208 0 : explicit TeardownRunnable(ServiceWorkerManagerChild* aActor)
209 0 : : Runnable("dom::workers::TeardownRunnable")
210 0 : , mActor(aActor)
211 : {
212 0 : MOZ_ASSERT(mActor);
213 0 : }
214 :
215 0 : NS_IMETHOD Run() override
216 : {
217 0 : MOZ_ASSERT(mActor);
218 0 : mActor->SendShutdown();
219 0 : return NS_OK;
220 : }
221 :
222 : private:
223 0 : ~TeardownRunnable() {}
224 :
225 : RefPtr<ServiceWorkerManagerChild> mActor;
226 : };
227 :
228 : } // namespace
229 :
230 : //////////////////////////
231 : // ServiceWorkerManager //
232 : //////////////////////////
233 :
234 101 : NS_IMPL_ADDREF(ServiceWorkerManager)
235 87 : NS_IMPL_RELEASE(ServiceWorkerManager)
236 :
237 16 : NS_INTERFACE_MAP_BEGIN(ServiceWorkerManager)
238 16 : NS_INTERFACE_MAP_ENTRY(nsIServiceWorkerManager)
239 6 : NS_INTERFACE_MAP_ENTRY(nsIIPCBackgroundChildCreateCallback)
240 1 : NS_INTERFACE_MAP_ENTRY(nsIObserver)
241 1 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIServiceWorkerManager)
242 0 : NS_INTERFACE_MAP_END
243 :
244 3 : ServiceWorkerManager::ServiceWorkerManager()
245 : : mActor(nullptr)
246 3 : , mShuttingDown(false)
247 : {
248 3 : }
249 :
250 0 : ServiceWorkerManager::~ServiceWorkerManager()
251 : {
252 : // The map will assert if it is not empty when destroyed.
253 0 : mRegistrationInfos.Clear();
254 0 : MOZ_ASSERT(!mActor);
255 0 : }
256 :
257 : void
258 3 : ServiceWorkerManager::Init(ServiceWorkerRegistrar* aRegistrar)
259 : {
260 6 : nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
261 3 : if (obs) {
262 6 : DebugOnly<nsresult> rv;
263 3 : rv = obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false /* ownsWeak */);
264 3 : MOZ_ASSERT(NS_SUCCEEDED(rv));
265 : }
266 :
267 3 : if (XRE_IsParentProcess()) {
268 1 : MOZ_DIAGNOSTIC_ASSERT(aRegistrar);
269 :
270 2 : nsTArray<ServiceWorkerRegistrationData> data;
271 1 : aRegistrar->GetRegistrations(data);
272 1 : LoadRegistrations(data);
273 :
274 1 : if (obs) {
275 2 : DebugOnly<nsresult> rv;
276 1 : rv = obs->AddObserver(this, PURGE_SESSION_HISTORY, false /* ownsWeak */);
277 1 : MOZ_ASSERT(NS_SUCCEEDED(rv));
278 1 : rv = obs->AddObserver(this, PURGE_DOMAIN_DATA, false /* ownsWeak */);
279 1 : MOZ_ASSERT(NS_SUCCEEDED(rv));
280 1 : rv = obs->AddObserver(this, CLEAR_ORIGIN_DATA, false /* ownsWeak */);
281 1 : MOZ_ASSERT(NS_SUCCEEDED(rv));
282 : }
283 : }
284 :
285 3 : if (!BackgroundChild::GetOrCreateForCurrentThread(this)) {
286 : // Make sure to do this last as our failure cleanup expects Init() to have
287 : // executed.
288 0 : ActorFailed();
289 : }
290 3 : }
291 :
292 : void
293 0 : ServiceWorkerManager::MaybeStartShutdown()
294 : {
295 0 : MOZ_ASSERT(NS_IsMainThread());
296 :
297 0 : if (mShuttingDown) {
298 0 : return;
299 : }
300 :
301 0 : mShuttingDown = true;
302 :
303 0 : for (auto it1 = mRegistrationInfos.Iter(); !it1.Done(); it1.Next()) {
304 0 : for (auto it2 = it1.UserData()->mUpdateTimers.Iter(); !it2.Done(); it2.Next()) {
305 0 : nsCOMPtr<nsITimer> timer = it2.UserData();
306 0 : timer->Cancel();
307 : }
308 0 : it1.UserData()->mUpdateTimers.Clear();
309 :
310 0 : for (auto it2 = it1.UserData()->mJobQueues.Iter(); !it2.Done(); it2.Next()) {
311 0 : RefPtr<ServiceWorkerJobQueue> queue = it2.UserData();
312 0 : queue->CancelAll();
313 : }
314 0 : it1.UserData()->mJobQueues.Clear();
315 : }
316 :
317 0 : nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
318 0 : if (obs) {
319 0 : obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
320 :
321 0 : if (XRE_IsParentProcess()) {
322 0 : obs->RemoveObserver(this, PURGE_SESSION_HISTORY);
323 0 : obs->RemoveObserver(this, PURGE_DOMAIN_DATA);
324 0 : obs->RemoveObserver(this, CLEAR_ORIGIN_DATA);
325 : }
326 : }
327 :
328 0 : mPendingOperations.Clear();
329 :
330 0 : if (!mActor) {
331 0 : return;
332 : }
333 :
334 0 : mActor->ManagerShuttingDown();
335 :
336 0 : RefPtr<TeardownRunnable> runnable = new TeardownRunnable(mActor);
337 0 : nsresult rv = NS_DispatchToMainThread(runnable);
338 0 : Unused << NS_WARN_IF(NS_FAILED(rv));
339 0 : mActor = nullptr;
340 : }
341 :
342 : class ServiceWorkerResolveWindowPromiseOnRegisterCallback final : public ServiceWorkerJob::Callback
343 : {
344 : RefPtr<nsPIDOMWindowInner> mWindow;
345 : // The promise "returned" by the call to Update up to
346 : // navigator.serviceWorker.register().
347 : RefPtr<Promise> mPromise;
348 :
349 0 : ~ServiceWorkerResolveWindowPromiseOnRegisterCallback()
350 0 : {}
351 :
352 : virtual void
353 0 : JobFinished(ServiceWorkerJob* aJob, ErrorResult& aStatus) override
354 : {
355 0 : AssertIsOnMainThread();
356 0 : MOZ_ASSERT(aJob);
357 :
358 0 : if (aStatus.Failed()) {
359 0 : mPromise->MaybeReject(aStatus);
360 0 : return;
361 : }
362 :
363 0 : MOZ_ASSERT(aJob->GetType() == ServiceWorkerJob::Type::Register);
364 : RefPtr<ServiceWorkerRegisterJob> registerJob =
365 0 : static_cast<ServiceWorkerRegisterJob*>(aJob);
366 0 : RefPtr<ServiceWorkerRegistrationInfo> reg = registerJob->GetRegistration();
367 :
368 : RefPtr<ServiceWorkerRegistration> swr =
369 0 : mWindow->GetServiceWorkerRegistration(NS_ConvertUTF8toUTF16(reg->mScope));
370 0 : mPromise->MaybeResolve(swr);
371 : }
372 :
373 : public:
374 0 : ServiceWorkerResolveWindowPromiseOnRegisterCallback(nsPIDOMWindowInner* aWindow,
375 : Promise* aPromise)
376 0 : : mWindow(aWindow)
377 0 : , mPromise(aPromise)
378 0 : {}
379 :
380 0 : NS_INLINE_DECL_REFCOUNTING(ServiceWorkerResolveWindowPromiseOnRegisterCallback, override)
381 : };
382 :
383 : namespace {
384 :
385 : class PropagateSoftUpdateRunnable final : public Runnable
386 : {
387 : public:
388 0 : PropagateSoftUpdateRunnable(const OriginAttributes& aOriginAttributes,
389 : const nsAString& aScope)
390 0 : : Runnable("dom::workers::PropagateSoftUpdateRunnable")
391 : , mOriginAttributes(aOriginAttributes)
392 0 : , mScope(aScope)
393 0 : {}
394 :
395 0 : NS_IMETHOD Run() override
396 : {
397 0 : AssertIsOnMainThread();
398 :
399 0 : RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
400 0 : if (swm) {
401 0 : swm->PropagateSoftUpdate(mOriginAttributes, mScope);
402 : }
403 :
404 0 : return NS_OK;
405 : }
406 :
407 : private:
408 0 : ~PropagateSoftUpdateRunnable()
409 0 : {}
410 :
411 : const OriginAttributes mOriginAttributes;
412 : const nsString mScope;
413 : };
414 :
415 : class PropagateUnregisterRunnable final : public Runnable
416 : {
417 : public:
418 0 : PropagateUnregisterRunnable(nsIPrincipal* aPrincipal,
419 : nsIServiceWorkerUnregisterCallback* aCallback,
420 : const nsAString& aScope)
421 0 : : Runnable("dom::workers::PropagateUnregisterRunnable")
422 : , mPrincipal(aPrincipal)
423 : , mCallback(aCallback)
424 0 : , mScope(aScope)
425 : {
426 0 : MOZ_ASSERT(aPrincipal);
427 0 : }
428 :
429 0 : NS_IMETHOD Run() override
430 : {
431 0 : AssertIsOnMainThread();
432 :
433 0 : RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
434 0 : if (swm) {
435 0 : swm->PropagateUnregister(mPrincipal, mCallback, mScope);
436 : }
437 :
438 0 : return NS_OK;
439 : }
440 :
441 : private:
442 0 : ~PropagateUnregisterRunnable()
443 0 : {}
444 :
445 : nsCOMPtr<nsIPrincipal> mPrincipal;
446 : nsCOMPtr<nsIServiceWorkerUnregisterCallback> mCallback;
447 : const nsString mScope;
448 : };
449 :
450 : class RemoveRunnable final : public Runnable
451 : {
452 : public:
453 0 : explicit RemoveRunnable(const nsACString& aHost)
454 0 : : Runnable("dom::workers::RemoveRunnable")
455 : {
456 0 : }
457 :
458 0 : NS_IMETHOD Run() override
459 : {
460 0 : AssertIsOnMainThread();
461 :
462 0 : RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
463 0 : if (swm) {
464 0 : swm->Remove(mHost);
465 : }
466 :
467 0 : return NS_OK;
468 : }
469 :
470 : private:
471 0 : ~RemoveRunnable()
472 0 : {}
473 :
474 : const nsCString mHost;
475 : };
476 :
477 : class PropagateRemoveRunnable final : public Runnable
478 : {
479 : public:
480 0 : explicit PropagateRemoveRunnable(const nsACString& aHost)
481 0 : : Runnable("dom::workers::PropagateRemoveRunnable")
482 : {
483 0 : }
484 :
485 0 : NS_IMETHOD Run() override
486 : {
487 0 : AssertIsOnMainThread();
488 :
489 0 : RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
490 0 : if (swm) {
491 0 : swm->PropagateRemove(mHost);
492 : }
493 :
494 0 : return NS_OK;
495 : }
496 :
497 : private:
498 0 : ~PropagateRemoveRunnable()
499 0 : {}
500 :
501 : const nsCString mHost;
502 : };
503 :
504 : class PropagateRemoveAllRunnable final : public Runnable
505 : {
506 : public:
507 0 : PropagateRemoveAllRunnable()
508 0 : : Runnable("dom::workers::PropagateRemoveAllRunnable")
509 : {
510 0 : }
511 :
512 0 : NS_IMETHOD Run() override
513 : {
514 0 : AssertIsOnMainThread();
515 :
516 0 : RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
517 0 : if (swm) {
518 0 : swm->PropagateRemoveAll();
519 : }
520 :
521 0 : return NS_OK;
522 : }
523 :
524 : private:
525 0 : ~PropagateRemoveAllRunnable()
526 0 : {}
527 : };
528 :
529 : class PromiseResolverCallback final : public ServiceWorkerUpdateFinishCallback
530 : {
531 : public:
532 0 : PromiseResolverCallback(ServiceWorkerUpdateFinishCallback* aCallback,
533 : GenericPromise::Private* aPromise)
534 0 : : mCallback(aCallback)
535 0 : , mPromise(aPromise)
536 : {
537 0 : MOZ_DIAGNOSTIC_ASSERT(mPromise);
538 0 : }
539 :
540 0 : void UpdateSucceeded(ServiceWorkerRegistrationInfo* aInfo) override
541 : {
542 0 : MOZ_DIAGNOSTIC_ASSERT(mPromise);
543 :
544 0 : if (mCallback) {
545 0 : mCallback->UpdateSucceeded(aInfo);
546 : }
547 :
548 0 : MaybeResolve();
549 0 : }
550 :
551 0 : void UpdateFailed(ErrorResult& aStatus) override
552 : {
553 0 : MOZ_DIAGNOSTIC_ASSERT(mPromise);
554 :
555 0 : if (mCallback) {
556 0 : mCallback->UpdateFailed(aStatus);
557 : }
558 :
559 0 : MaybeResolve();
560 0 : }
561 :
562 : private:
563 0 : ~PromiseResolverCallback()
564 0 : {
565 0 : MaybeResolve();
566 0 : }
567 :
568 : void
569 0 : MaybeResolve()
570 : {
571 0 : if (mPromise) {
572 0 : mPromise->Resolve(true, __func__);
573 0 : mPromise = nullptr;
574 : }
575 0 : }
576 :
577 : RefPtr<ServiceWorkerUpdateFinishCallback> mCallback;
578 : RefPtr<GenericPromise::Private> mPromise;
579 : };
580 :
581 : // This runnable is used for 2 different tasks:
582 : // - to postpone the SoftUpdate() until the IPC SWM actor is created
583 : // (aInternalMethod == false)
584 : // - to call the 'real' SoftUpdate when the ServiceWorkerUpdaterChild is
585 : // notified by the parent (aInternalMethod == true)
586 : class SoftUpdateRunnable final : public CancelableRunnable
587 : {
588 : public:
589 0 : SoftUpdateRunnable(const OriginAttributes& aOriginAttributes,
590 : const nsACString& aScope,
591 : bool aInternalMethod,
592 : GenericPromise::Private* aPromise)
593 0 : : CancelableRunnable("dom::workers::SoftUpdateRunnable")
594 : , mAttrs(aOriginAttributes)
595 : , mScope(aScope)
596 : , mInternalMethod(aInternalMethod)
597 0 : , mPromise(aPromise)
598 0 : {}
599 :
600 0 : NS_IMETHOD Run() override
601 : {
602 0 : AssertIsOnMainThread();
603 :
604 0 : RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
605 0 : if (!swm) {
606 0 : return NS_ERROR_FAILURE;
607 : }
608 :
609 0 : if (mInternalMethod) {
610 : RefPtr<PromiseResolverCallback> callback =
611 0 : new PromiseResolverCallback(nullptr, mPromise);
612 0 : mPromise = nullptr;
613 :
614 0 : swm->SoftUpdateInternal(mAttrs, mScope, callback);
615 : } else {
616 0 : swm->SoftUpdate(mAttrs, mScope);
617 : }
618 :
619 0 : return NS_OK;
620 : }
621 :
622 : nsresult
623 0 : Cancel() override
624 : {
625 0 : mPromise = nullptr;
626 0 : return NS_OK;
627 : }
628 :
629 : private:
630 0 : ~SoftUpdateRunnable()
631 0 : {
632 0 : if (mPromise) {
633 0 : mPromise->Resolve(true, __func__);
634 : }
635 0 : }
636 :
637 : const OriginAttributes mAttrs;
638 : const nsCString mScope;
639 : bool mInternalMethod;
640 :
641 : RefPtr<GenericPromise::Private> mPromise;
642 : };
643 :
644 : // This runnable is used for 3 different tasks:
645 : // - to postpone the Update() until the IPC SWM actor is created
646 : // (aType == ePostpone)
647 : // - to call the 'real' Update when the ServiceWorkerUpdaterChild is
648 : // notified by the parent (aType == eSuccess)
649 : // - an error must be propagated (aType == eFailure)
650 : class UpdateRunnable final : public CancelableRunnable
651 : {
652 : public:
653 : enum Type {
654 : ePostpone,
655 : eSuccess,
656 : eFailure,
657 : };
658 :
659 0 : UpdateRunnable(nsIPrincipal* aPrincipal,
660 : const nsACString& aScope,
661 : ServiceWorkerUpdateFinishCallback* aCallback,
662 : Type aType,
663 : GenericPromise::Private* aPromise)
664 0 : : CancelableRunnable("dom::workers::UpdateRunnable")
665 : , mPrincipal(aPrincipal)
666 : , mScope(aScope)
667 : , mCallback(aCallback)
668 : , mType(aType)
669 0 : , mPromise(aPromise)
670 0 : {}
671 :
672 0 : NS_IMETHOD Run() override
673 : {
674 0 : AssertIsOnMainThread();
675 :
676 0 : RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
677 0 : if (!swm) {
678 0 : return NS_ERROR_FAILURE;
679 : }
680 :
681 0 : if (mType == ePostpone) {
682 0 : swm->Update(mPrincipal, mScope, mCallback);
683 0 : return NS_OK;
684 : }
685 :
686 0 : MOZ_ASSERT(mPromise);
687 :
688 : RefPtr<PromiseResolverCallback> callback =
689 0 : new PromiseResolverCallback(mCallback, mPromise);
690 0 : mPromise = nullptr;
691 :
692 0 : if (mType == eSuccess) {
693 0 : swm->UpdateInternal(mPrincipal, mScope, callback);
694 0 : return NS_OK;
695 : }
696 :
697 0 : ErrorResult error(NS_ERROR_DOM_ABORT_ERR);
698 0 : callback->UpdateFailed(error);
699 0 : return NS_OK;
700 : }
701 :
702 : nsresult
703 0 : Cancel() override
704 : {
705 0 : mPromise = nullptr;
706 0 : return NS_OK;
707 : }
708 :
709 : private:
710 0 : ~UpdateRunnable()
711 0 : {
712 0 : if (mPromise) {
713 0 : mPromise->Resolve(true, __func__);
714 : }
715 0 : }
716 :
717 : nsCOMPtr<nsIPrincipal> mPrincipal;
718 : const nsCString mScope;
719 : RefPtr<ServiceWorkerUpdateFinishCallback> mCallback;
720 : Type mType;
721 :
722 : RefPtr<GenericPromise::Private> mPromise;
723 : };
724 :
725 : class ResolvePromiseRunnable final : public CancelableRunnable
726 : {
727 : public:
728 0 : explicit ResolvePromiseRunnable(GenericPromise::Private* aPromise)
729 0 : : CancelableRunnable("dom::workers::ResolvePromiseRunnable")
730 0 : , mPromise(aPromise)
731 0 : {}
732 :
733 : NS_IMETHOD
734 0 : Run() override
735 : {
736 0 : MaybeResolve();
737 0 : return NS_OK;
738 : }
739 :
740 : nsresult
741 0 : Cancel() override
742 : {
743 0 : mPromise = nullptr;
744 0 : return NS_OK;
745 : }
746 :
747 : private:
748 0 : ~ResolvePromiseRunnable()
749 0 : {
750 0 : MaybeResolve();
751 0 : }
752 :
753 : void
754 0 : MaybeResolve()
755 : {
756 0 : if (mPromise) {
757 0 : mPromise->Resolve(true, __func__);
758 0 : mPromise = nullptr;
759 : }
760 0 : }
761 :
762 : RefPtr<GenericPromise::Private> mPromise;
763 : };
764 :
765 : } // namespace
766 :
767 : // This function implements parts of the step 3 of the following algorithm:
768 : // https://w3c.github.io/webappsec/specs/powerfulfeatures/#settings-secure
769 : static bool
770 0 : IsFromAuthenticatedOrigin(nsIDocument* aDoc)
771 : {
772 0 : MOZ_ASSERT(aDoc);
773 0 : nsCOMPtr<nsIDocument> doc(aDoc);
774 0 : nsCOMPtr<nsIContentSecurityManager> csm = do_GetService(NS_CONTENTSECURITYMANAGER_CONTRACTID);
775 0 : if (NS_WARN_IF(!csm)) {
776 0 : return false;
777 : }
778 :
779 0 : while (doc && !nsContentUtils::IsChromeDoc(doc)) {
780 0 : bool trustworthyOrigin = false;
781 :
782 : // The origin of the document may be different from the document URI
783 : // itself. Check the principal, not the document URI itself.
784 0 : nsCOMPtr<nsIPrincipal> documentPrincipal = doc->NodePrincipal();
785 :
786 : // The check for IsChromeDoc() above should mean we never see a system
787 : // principal inside the loop.
788 0 : MOZ_ASSERT(!nsContentUtils::IsSystemPrincipal(documentPrincipal));
789 :
790 0 : csm->IsOriginPotentiallyTrustworthy(documentPrincipal, &trustworthyOrigin);
791 0 : if (!trustworthyOrigin) {
792 0 : return false;
793 : }
794 :
795 0 : doc = doc->GetParentDocument();
796 : }
797 0 : return true;
798 : }
799 :
800 : // If we return an error code here, the ServiceWorkerContainer will
801 : // automatically reject the Promise.
802 : NS_IMETHODIMP
803 0 : ServiceWorkerManager::Register(mozIDOMWindow* aWindow,
804 : nsIURI* aScopeURI,
805 : nsIURI* aScriptURI,
806 : nsLoadFlags aLoadFlags,
807 : nsISupports** aPromise)
808 : {
809 0 : AssertIsOnMainThread();
810 :
811 0 : if (NS_WARN_IF(!aWindow)) {
812 0 : return NS_ERROR_DOM_INVALID_STATE_ERR;
813 : }
814 :
815 0 : auto* window = nsPIDOMWindowInner::From(aWindow);
816 0 : nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
817 0 : if (!doc) {
818 0 : return NS_ERROR_FAILURE;
819 : }
820 :
821 : // Don't allow service workers to register when the *document* is chrome.
822 0 : if (NS_WARN_IF(nsContentUtils::IsSystemPrincipal(doc->NodePrincipal()))) {
823 0 : return NS_ERROR_DOM_SECURITY_ERR;
824 : }
825 :
826 0 : nsCOMPtr<nsPIDOMWindowOuter> outerWindow = window->GetOuterWindow();
827 : bool serviceWorkersTestingEnabled =
828 0 : outerWindow->GetServiceWorkersTestingEnabled();
829 :
830 : bool authenticatedOrigin;
831 0 : if (Preferences::GetBool("dom.serviceWorkers.testing.enabled") ||
832 : serviceWorkersTestingEnabled) {
833 0 : authenticatedOrigin = true;
834 : } else {
835 0 : authenticatedOrigin = IsFromAuthenticatedOrigin(doc);
836 : }
837 :
838 0 : if (!authenticatedOrigin) {
839 0 : NS_WARNING("ServiceWorker registration from insecure websites is not allowed.");
840 0 : return NS_ERROR_DOM_SECURITY_ERR;
841 : }
842 :
843 : // Data URLs are not allowed.
844 0 : nsCOMPtr<nsIPrincipal> documentPrincipal = doc->NodePrincipal();
845 :
846 0 : nsresult rv = documentPrincipal->CheckMayLoad(aScriptURI, true /* report */,
847 0 : false /* allowIfInheritsPrincipal */);
848 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
849 0 : return NS_ERROR_DOM_SECURITY_ERR;
850 : }
851 :
852 : // Check content policy.
853 0 : int16_t decision = nsIContentPolicy::ACCEPT;
854 0 : rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_INTERNAL_SERVICE_WORKER,
855 : aScriptURI,
856 : documentPrincipal,
857 : doc,
858 0 : EmptyCString(),
859 : nullptr,
860 0 : &decision);
861 0 : NS_ENSURE_SUCCESS(rv, rv);
862 0 : if (NS_WARN_IF(decision != nsIContentPolicy::ACCEPT)) {
863 0 : return NS_ERROR_CONTENT_BLOCKED;
864 : }
865 :
866 :
867 0 : rv = documentPrincipal->CheckMayLoad(aScopeURI, true /* report */,
868 0 : false /* allowIfInheritsPrinciple */);
869 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
870 0 : return NS_ERROR_DOM_SECURITY_ERR;
871 : }
872 :
873 : // The IsOriginPotentiallyTrustworthy() check allows file:// and possibly other
874 : // URI schemes. We need to explicitly only allows http and https schemes.
875 : // Note, we just use the aScriptURI here for the check since its already
876 : // been verified as same origin with the document principal. This also
877 : // is a good block against accidentally allowing blob: script URIs which
878 : // might inherit the origin.
879 0 : bool isHttp = false;
880 0 : bool isHttps = false;
881 0 : aScriptURI->SchemeIs("http", &isHttp);
882 0 : aScriptURI->SchemeIs("https", &isHttps);
883 0 : if (NS_WARN_IF(!isHttp && !isHttps)) {
884 0 : return NS_ERROR_DOM_SECURITY_ERR;
885 : }
886 :
887 0 : nsCString cleanedScope;
888 0 : rv = aScopeURI->GetSpecIgnoringRef(cleanedScope);
889 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
890 0 : return NS_ERROR_FAILURE;
891 : }
892 :
893 0 : nsAutoCString spec;
894 0 : rv = aScriptURI->GetSpecIgnoringRef(spec);
895 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
896 0 : return rv;
897 : }
898 :
899 0 : nsCOMPtr<nsIGlobalObject> sgo = do_QueryInterface(window);
900 0 : ErrorResult result;
901 0 : RefPtr<Promise> promise = Promise::Create(sgo, result);
902 0 : if (result.Failed()) {
903 0 : return result.StealNSResult();
904 : }
905 :
906 0 : nsAutoCString scopeKey;
907 0 : rv = PrincipalToScopeKey(documentPrincipal, scopeKey);
908 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
909 0 : return rv;
910 : }
911 :
912 0 : AddRegisteringDocument(cleanedScope, doc);
913 :
914 0 : RefPtr<ServiceWorkerJobQueue> queue = GetOrCreateJobQueue(scopeKey,
915 0 : cleanedScope);
916 :
917 : RefPtr<ServiceWorkerResolveWindowPromiseOnRegisterCallback> cb =
918 0 : new ServiceWorkerResolveWindowPromiseOnRegisterCallback(window, promise);
919 :
920 0 : nsCOMPtr<nsILoadGroup> docLoadGroup = doc->GetDocumentLoadGroup();
921 : RefPtr<WorkerLoadInfo::InterfaceRequestor> ir =
922 0 : new WorkerLoadInfo::InterfaceRequestor(documentPrincipal, docLoadGroup);
923 0 : ir->MaybeAddTabChild(docLoadGroup);
924 :
925 : // Create a load group that is separate from, yet related to, the document's load group.
926 : // This allows checks for interfaces like nsILoadContext to yield the values used by the
927 : // the document, yet will not cancel the update job if the document's load group is cancelled.
928 0 : nsCOMPtr<nsILoadGroup> loadGroup = do_CreateInstance(NS_LOADGROUP_CONTRACTID);
929 0 : MOZ_ALWAYS_SUCCEEDS(loadGroup->SetNotificationCallbacks(ir));
930 :
931 : RefPtr<ServiceWorkerRegisterJob> job =
932 : new ServiceWorkerRegisterJob(documentPrincipal, cleanedScope, spec,
933 0 : loadGroup, aLoadFlags);
934 0 : job->AppendResultCallback(cb);
935 0 : queue->ScheduleJob(job);
936 :
937 0 : AssertIsOnMainThread();
938 0 : Telemetry::Accumulate(Telemetry::SERVICE_WORKER_REGISTRATIONS, 1);
939 :
940 0 : ContentChild* contentChild = ContentChild::GetSingleton();
941 0 : if (contentChild &&
942 0 : contentChild->GetRemoteType().EqualsLiteral(FILE_REMOTE_TYPE)) {
943 0 : nsString message(NS_LITERAL_STRING("ServiceWorker registered by document "
944 : "embedded in a file:/// URI. This may "
945 0 : "result in unexpected behavior."));
946 0 : ReportToAllClients(cleanedScope, message, EmptyString(),
947 0 : EmptyString(), 0, 0, nsIScriptError::warningFlag);
948 0 : Telemetry::Accumulate(Telemetry::FILE_EMBEDDED_SERVICEWORKERS, 1);
949 : }
950 :
951 0 : promise.forget(aPromise);
952 0 : return NS_OK;
953 : }
954 :
955 : void
956 0 : ServiceWorkerManager::AppendPendingOperation(nsIRunnable* aRunnable)
957 : {
958 0 : MOZ_ASSERT(!mActor);
959 0 : MOZ_ASSERT(aRunnable);
960 :
961 0 : if (!mShuttingDown) {
962 0 : mPendingOperations.AppendElement(aRunnable);
963 : }
964 0 : }
965 :
966 : /*
967 : * Implements the async aspects of the getRegistrations algorithm.
968 : */
969 0 : class GetRegistrationsRunnable final : public Runnable
970 : {
971 : nsCOMPtr<nsPIDOMWindowInner> mWindow;
972 : RefPtr<Promise> mPromise;
973 : public:
974 0 : GetRegistrationsRunnable(nsPIDOMWindowInner* aWindow, Promise* aPromise)
975 0 : : Runnable("dom::workers::GetRegistrationsRunnable")
976 : , mWindow(aWindow)
977 0 : , mPromise(aPromise)
978 0 : {}
979 :
980 : NS_IMETHOD
981 0 : Run() override
982 : {
983 0 : RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
984 0 : if (!swm) {
985 0 : mPromise->MaybeReject(NS_ERROR_UNEXPECTED);
986 0 : return NS_OK;
987 : }
988 :
989 0 : nsIDocument* doc = mWindow->GetExtantDoc();
990 0 : if (!doc) {
991 0 : mPromise->MaybeReject(NS_ERROR_UNEXPECTED);
992 0 : return NS_OK;
993 : }
994 :
995 0 : nsCOMPtr<nsIURI> docURI = doc->GetDocumentURI();
996 0 : if (!docURI) {
997 0 : mPromise->MaybeReject(NS_ERROR_UNEXPECTED);
998 0 : return NS_OK;
999 : }
1000 :
1001 0 : nsCOMPtr<nsIPrincipal> principal = doc->NodePrincipal();
1002 0 : if (!principal) {
1003 0 : mPromise->MaybeReject(NS_ERROR_UNEXPECTED);
1004 0 : return NS_OK;
1005 : }
1006 :
1007 0 : nsTArray<RefPtr<ServiceWorkerRegistration>> array;
1008 :
1009 0 : if (NS_WARN_IF(!BasePrincipal::Cast(principal)->IsCodebasePrincipal())) {
1010 0 : return NS_OK;
1011 : }
1012 :
1013 0 : nsAutoCString scopeKey;
1014 0 : nsresult rv = swm->PrincipalToScopeKey(principal, scopeKey);
1015 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1016 0 : return rv;
1017 : }
1018 :
1019 : ServiceWorkerManager::RegistrationDataPerPrincipal* data;
1020 0 : if (!swm->mRegistrationInfos.Get(scopeKey, &data)) {
1021 0 : mPromise->MaybeResolve(array);
1022 0 : return NS_OK;
1023 : }
1024 :
1025 0 : for (uint32_t i = 0; i < data->mOrderedScopes.Length(); ++i) {
1026 : RefPtr<ServiceWorkerRegistrationInfo> info =
1027 0 : data->mInfos.GetWeak(data->mOrderedScopes[i]);
1028 0 : if (info->mPendingUninstall) {
1029 0 : continue;
1030 : }
1031 :
1032 0 : NS_ConvertUTF8toUTF16 scope(data->mOrderedScopes[i]);
1033 :
1034 0 : nsCOMPtr<nsIURI> scopeURI;
1035 0 : nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), scope, nullptr, nullptr);
1036 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1037 0 : mPromise->MaybeReject(rv);
1038 0 : break;
1039 : }
1040 :
1041 0 : rv = principal->CheckMayLoad(scopeURI, true /* report */,
1042 0 : false /* allowIfInheritsPrincipal */);
1043 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1044 0 : continue;
1045 : }
1046 :
1047 : RefPtr<ServiceWorkerRegistration> swr =
1048 0 : mWindow->GetServiceWorkerRegistration(scope);
1049 :
1050 0 : array.AppendElement(swr);
1051 : }
1052 :
1053 0 : mPromise->MaybeResolve(array);
1054 0 : return NS_OK;
1055 : }
1056 : };
1057 :
1058 : // If we return an error code here, the ServiceWorkerContainer will
1059 : // automatically reject the Promise.
1060 : NS_IMETHODIMP
1061 0 : ServiceWorkerManager::GetRegistrations(mozIDOMWindow* aWindow,
1062 : nsISupports** aPromise)
1063 : {
1064 0 : AssertIsOnMainThread();
1065 :
1066 0 : if (NS_WARN_IF(!aWindow)) {
1067 0 : return NS_ERROR_DOM_INVALID_STATE_ERR;
1068 : }
1069 :
1070 0 : auto* window = nsPIDOMWindowInner::From(aWindow);
1071 0 : nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
1072 0 : if (NS_WARN_IF(!doc)) {
1073 0 : return NS_ERROR_DOM_INVALID_STATE_ERR;
1074 : }
1075 :
1076 : // Don't allow service workers to register when the *document* is chrome for
1077 : // now.
1078 0 : MOZ_ASSERT(!nsContentUtils::IsSystemPrincipal(doc->NodePrincipal()));
1079 :
1080 0 : nsCOMPtr<nsIGlobalObject> sgo = do_QueryInterface(window);
1081 0 : ErrorResult result;
1082 0 : RefPtr<Promise> promise = Promise::Create(sgo, result);
1083 0 : if (result.Failed()) {
1084 0 : return result.StealNSResult();
1085 : }
1086 :
1087 : nsCOMPtr<nsIRunnable> runnable =
1088 0 : new GetRegistrationsRunnable(window, promise);
1089 0 : promise.forget(aPromise);
1090 0 : return NS_DispatchToCurrentThread(runnable);
1091 : }
1092 :
1093 : /*
1094 : * Implements the async aspects of the getRegistration algorithm.
1095 : */
1096 0 : class GetRegistrationRunnable final : public Runnable
1097 : {
1098 : nsCOMPtr<nsPIDOMWindowInner> mWindow;
1099 : RefPtr<Promise> mPromise;
1100 : nsString mDocumentURL;
1101 :
1102 : public:
1103 0 : GetRegistrationRunnable(nsPIDOMWindowInner* aWindow,
1104 : Promise* aPromise,
1105 : const nsAString& aDocumentURL)
1106 0 : : Runnable("dom::workers::GetRegistrationRunnable")
1107 : , mWindow(aWindow)
1108 : , mPromise(aPromise)
1109 0 : , mDocumentURL(aDocumentURL)
1110 0 : {}
1111 :
1112 : NS_IMETHOD
1113 0 : Run() override
1114 : {
1115 0 : RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
1116 0 : if (!swm) {
1117 0 : mPromise->MaybeReject(NS_ERROR_UNEXPECTED);
1118 0 : return NS_OK;
1119 : }
1120 :
1121 0 : nsIDocument* doc = mWindow->GetExtantDoc();
1122 0 : if (!doc) {
1123 0 : mPromise->MaybeReject(NS_ERROR_UNEXPECTED);
1124 0 : return NS_OK;
1125 : }
1126 :
1127 0 : nsCOMPtr<nsIURI> docURI = doc->GetDocumentURI();
1128 0 : if (!docURI) {
1129 0 : mPromise->MaybeReject(NS_ERROR_UNEXPECTED);
1130 0 : return NS_OK;
1131 : }
1132 :
1133 0 : nsCOMPtr<nsIURI> uri;
1134 0 : nsresult rv = NS_NewURI(getter_AddRefs(uri), mDocumentURL, nullptr, docURI);
1135 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1136 0 : mPromise->MaybeReject(rv);
1137 0 : return NS_OK;
1138 : }
1139 :
1140 0 : nsCOMPtr<nsIPrincipal> principal = doc->NodePrincipal();
1141 0 : if (!principal) {
1142 0 : mPromise->MaybeReject(NS_ERROR_UNEXPECTED);
1143 0 : return NS_OK;
1144 : }
1145 :
1146 0 : rv = principal->CheckMayLoad(uri, true /* report */,
1147 0 : false /* allowIfInheritsPrinciple */);
1148 0 : if (NS_FAILED(rv)) {
1149 0 : mPromise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
1150 0 : return NS_OK;
1151 : }
1152 :
1153 : RefPtr<ServiceWorkerRegistrationInfo> registration =
1154 0 : swm->GetServiceWorkerRegistrationInfo(principal, uri);
1155 :
1156 0 : if (!registration) {
1157 0 : mPromise->MaybeResolveWithUndefined();
1158 0 : return NS_OK;
1159 : }
1160 :
1161 0 : NS_ConvertUTF8toUTF16 scope(registration->mScope);
1162 : RefPtr<ServiceWorkerRegistration> swr =
1163 0 : mWindow->GetServiceWorkerRegistration(scope);
1164 0 : mPromise->MaybeResolve(swr);
1165 :
1166 0 : return NS_OK;
1167 : }
1168 : };
1169 :
1170 : // If we return an error code here, the ServiceWorkerContainer will
1171 : // automatically reject the Promise.
1172 : NS_IMETHODIMP
1173 0 : ServiceWorkerManager::GetRegistration(mozIDOMWindow* aWindow,
1174 : const nsAString& aDocumentURL,
1175 : nsISupports** aPromise)
1176 : {
1177 0 : AssertIsOnMainThread();
1178 :
1179 0 : if (NS_WARN_IF(!aWindow)) {
1180 0 : return NS_ERROR_DOM_INVALID_STATE_ERR;
1181 : }
1182 :
1183 0 : auto* window = nsPIDOMWindowInner::From(aWindow);
1184 0 : nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
1185 0 : if (NS_WARN_IF(!doc)) {
1186 0 : return NS_ERROR_DOM_INVALID_STATE_ERR;
1187 : }
1188 :
1189 : // Don't allow service workers to register when the *document* is chrome for
1190 : // now.
1191 0 : MOZ_ASSERT(!nsContentUtils::IsSystemPrincipal(doc->NodePrincipal()));
1192 :
1193 0 : nsCOMPtr<nsIGlobalObject> sgo = do_QueryInterface(window);
1194 0 : ErrorResult result;
1195 0 : RefPtr<Promise> promise = Promise::Create(sgo, result);
1196 0 : if (result.Failed()) {
1197 0 : return result.StealNSResult();
1198 : }
1199 :
1200 : nsCOMPtr<nsIRunnable> runnable =
1201 0 : new GetRegistrationRunnable(window, promise, aDocumentURL);
1202 0 : promise.forget(aPromise);
1203 0 : return NS_DispatchToCurrentThread(runnable);
1204 : }
1205 :
1206 0 : class GetReadyPromiseRunnable final : public Runnable
1207 : {
1208 : nsCOMPtr<nsPIDOMWindowInner> mWindow;
1209 : RefPtr<Promise> mPromise;
1210 :
1211 : public:
1212 0 : GetReadyPromiseRunnable(nsPIDOMWindowInner* aWindow, Promise* aPromise)
1213 0 : : Runnable("dom::workers::GetReadyPromiseRunnable")
1214 : , mWindow(aWindow)
1215 0 : , mPromise(aPromise)
1216 0 : {}
1217 :
1218 : NS_IMETHOD
1219 0 : Run() override
1220 : {
1221 0 : RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
1222 0 : if (!swm) {
1223 0 : mPromise->MaybeReject(NS_ERROR_UNEXPECTED);
1224 0 : return NS_OK;
1225 : }
1226 :
1227 0 : nsIDocument* doc = mWindow->GetExtantDoc();
1228 0 : if (!doc) {
1229 0 : mPromise->MaybeReject(NS_ERROR_UNEXPECTED);
1230 0 : return NS_OK;
1231 : }
1232 :
1233 0 : nsCOMPtr<nsIURI> docURI = doc->GetDocumentURI();
1234 0 : if (!docURI) {
1235 0 : mPromise->MaybeReject(NS_ERROR_UNEXPECTED);
1236 0 : return NS_OK;
1237 : }
1238 :
1239 0 : if (!swm->CheckReadyPromise(mWindow, docURI, mPromise)) {
1240 0 : swm->StorePendingReadyPromise(mWindow, docURI, mPromise);
1241 : }
1242 :
1243 0 : return NS_OK;
1244 : }
1245 : };
1246 :
1247 : NS_IMETHODIMP
1248 0 : ServiceWorkerManager::SendPushEvent(const nsACString& aOriginAttributes,
1249 : const nsACString& aScope,
1250 : uint32_t aDataLength,
1251 : uint8_t* aDataBytes,
1252 : uint8_t optional_argc)
1253 : {
1254 0 : if (optional_argc == 2) {
1255 0 : nsTArray<uint8_t> data;
1256 0 : if (!data.InsertElementsAt(0, aDataBytes, aDataLength, fallible)) {
1257 0 : return NS_ERROR_OUT_OF_MEMORY;
1258 : }
1259 0 : return SendPushEvent(aOriginAttributes, aScope, EmptyString(), Some(data));
1260 : }
1261 0 : MOZ_ASSERT(optional_argc == 0);
1262 0 : return SendPushEvent(aOriginAttributes, aScope, EmptyString(), Nothing());
1263 : }
1264 :
1265 : nsresult
1266 0 : ServiceWorkerManager::SendPushEvent(const nsACString& aOriginAttributes,
1267 : const nsACString& aScope,
1268 : const nsAString& aMessageId,
1269 : const Maybe<nsTArray<uint8_t>>& aData)
1270 : {
1271 0 : OriginAttributes attrs;
1272 0 : if (!attrs.PopulateFromSuffix(aOriginAttributes)) {
1273 0 : return NS_ERROR_INVALID_ARG;
1274 : }
1275 :
1276 0 : ServiceWorkerInfo* serviceWorker = GetActiveWorkerInfoForScope(attrs, aScope);
1277 0 : if (NS_WARN_IF(!serviceWorker)) {
1278 0 : return NS_ERROR_FAILURE;
1279 : }
1280 :
1281 : RefPtr<ServiceWorkerRegistrationInfo> registration =
1282 0 : GetRegistration(serviceWorker->Principal(), aScope);
1283 0 : MOZ_DIAGNOSTIC_ASSERT(registration);
1284 :
1285 0 : return serviceWorker->WorkerPrivate()->SendPushEvent(aMessageId, aData,
1286 0 : registration);
1287 : }
1288 :
1289 : NS_IMETHODIMP
1290 0 : ServiceWorkerManager::SendPushSubscriptionChangeEvent(const nsACString& aOriginAttributes,
1291 : const nsACString& aScope)
1292 : {
1293 0 : OriginAttributes attrs;
1294 0 : if (!attrs.PopulateFromSuffix(aOriginAttributes)) {
1295 0 : return NS_ERROR_INVALID_ARG;
1296 : }
1297 :
1298 0 : ServiceWorkerInfo* info = GetActiveWorkerInfoForScope(attrs, aScope);
1299 0 : if (!info) {
1300 0 : return NS_ERROR_FAILURE;
1301 : }
1302 0 : return info->WorkerPrivate()->SendPushSubscriptionChangeEvent();
1303 : }
1304 :
1305 : nsresult
1306 0 : ServiceWorkerManager::SendNotificationEvent(const nsAString& aEventName,
1307 : const nsACString& aOriginSuffix,
1308 : const nsACString& aScope,
1309 : const nsAString& aID,
1310 : const nsAString& aTitle,
1311 : const nsAString& aDir,
1312 : const nsAString& aLang,
1313 : const nsAString& aBody,
1314 : const nsAString& aTag,
1315 : const nsAString& aIcon,
1316 : const nsAString& aData,
1317 : const nsAString& aBehavior)
1318 : {
1319 0 : OriginAttributes attrs;
1320 0 : if (!attrs.PopulateFromSuffix(aOriginSuffix)) {
1321 0 : return NS_ERROR_INVALID_ARG;
1322 : }
1323 :
1324 0 : ServiceWorkerInfo* info = GetActiveWorkerInfoForScope(attrs, aScope);
1325 0 : if (!info) {
1326 0 : return NS_ERROR_FAILURE;
1327 : }
1328 :
1329 0 : ServiceWorkerPrivate* workerPrivate = info->WorkerPrivate();
1330 : return workerPrivate->SendNotificationEvent(aEventName, aID, aTitle, aDir,
1331 : aLang, aBody, aTag,
1332 : aIcon, aData, aBehavior,
1333 0 : NS_ConvertUTF8toUTF16(aScope));
1334 : }
1335 :
1336 : NS_IMETHODIMP
1337 0 : ServiceWorkerManager::SendNotificationClickEvent(const nsACString& aOriginSuffix,
1338 : const nsACString& aScope,
1339 : const nsAString& aID,
1340 : const nsAString& aTitle,
1341 : const nsAString& aDir,
1342 : const nsAString& aLang,
1343 : const nsAString& aBody,
1344 : const nsAString& aTag,
1345 : const nsAString& aIcon,
1346 : const nsAString& aData,
1347 : const nsAString& aBehavior)
1348 : {
1349 0 : return SendNotificationEvent(NS_LITERAL_STRING(NOTIFICATION_CLICK_EVENT_NAME),
1350 : aOriginSuffix, aScope, aID, aTitle, aDir, aLang,
1351 0 : aBody, aTag, aIcon, aData, aBehavior);
1352 : }
1353 :
1354 : NS_IMETHODIMP
1355 0 : ServiceWorkerManager::SendNotificationCloseEvent(const nsACString& aOriginSuffix,
1356 : const nsACString& aScope,
1357 : const nsAString& aID,
1358 : const nsAString& aTitle,
1359 : const nsAString& aDir,
1360 : const nsAString& aLang,
1361 : const nsAString& aBody,
1362 : const nsAString& aTag,
1363 : const nsAString& aIcon,
1364 : const nsAString& aData,
1365 : const nsAString& aBehavior)
1366 : {
1367 0 : return SendNotificationEvent(NS_LITERAL_STRING(NOTIFICATION_CLOSE_EVENT_NAME),
1368 : aOriginSuffix, aScope, aID, aTitle, aDir, aLang,
1369 0 : aBody, aTag, aIcon, aData, aBehavior);
1370 : }
1371 :
1372 : NS_IMETHODIMP
1373 0 : ServiceWorkerManager::GetReadyPromise(mozIDOMWindow* aWindow,
1374 : nsISupports** aPromise)
1375 : {
1376 0 : AssertIsOnMainThread();
1377 :
1378 0 : if (NS_WARN_IF(!aWindow)) {
1379 0 : return NS_ERROR_DOM_INVALID_STATE_ERR;
1380 : }
1381 :
1382 0 : auto* window = nsPIDOMWindowInner::From(aWindow);
1383 :
1384 0 : nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
1385 0 : if (NS_WARN_IF(!doc)) {
1386 0 : return NS_ERROR_FAILURE;
1387 : }
1388 :
1389 : // Don't allow service workers to register when the *document* is chrome for
1390 : // now.
1391 0 : MOZ_ASSERT(!nsContentUtils::IsSystemPrincipal(doc->NodePrincipal()));
1392 :
1393 0 : MOZ_ASSERT(!mPendingReadyPromises.Contains(window));
1394 :
1395 0 : nsCOMPtr<nsIGlobalObject> sgo = do_QueryInterface(window);
1396 0 : ErrorResult result;
1397 0 : RefPtr<Promise> promise = Promise::Create(sgo, result);
1398 0 : if (result.Failed()) {
1399 0 : return result.StealNSResult();
1400 : }
1401 :
1402 : nsCOMPtr<nsIRunnable> runnable =
1403 0 : new GetReadyPromiseRunnable(window, promise);
1404 0 : promise.forget(aPromise);
1405 0 : return NS_DispatchToCurrentThread(runnable);
1406 : }
1407 :
1408 : NS_IMETHODIMP
1409 0 : ServiceWorkerManager::RemoveReadyPromise(mozIDOMWindow* aWindow)
1410 : {
1411 0 : AssertIsOnMainThread();
1412 0 : MOZ_ASSERT(aWindow);
1413 :
1414 0 : if (!aWindow) {
1415 0 : return NS_ERROR_FAILURE;
1416 : }
1417 :
1418 0 : mPendingReadyPromises.Remove(aWindow);
1419 0 : return NS_OK;
1420 : }
1421 :
1422 : void
1423 0 : ServiceWorkerManager::StorePendingReadyPromise(nsPIDOMWindowInner* aWindow,
1424 : nsIURI* aURI,
1425 : Promise* aPromise)
1426 : {
1427 : PendingReadyPromise* data;
1428 :
1429 : // We should not have 2 pending promises for the same window.
1430 0 : MOZ_ASSERT(!mPendingReadyPromises.Get(aWindow, &data));
1431 :
1432 0 : data = new PendingReadyPromise(aURI, aPromise);
1433 0 : mPendingReadyPromises.Put(aWindow, data);
1434 0 : }
1435 :
1436 : void
1437 0 : ServiceWorkerManager::CheckPendingReadyPromises()
1438 : {
1439 0 : for (auto iter = mPendingReadyPromises.Iter(); !iter.Done(); iter.Next()) {
1440 0 : nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(iter.Key());
1441 0 : MOZ_ASSERT(window);
1442 :
1443 0 : nsAutoPtr<PendingReadyPromise>& pendingReadyPromise = iter.Data();
1444 0 : if (CheckReadyPromise(window, pendingReadyPromise->mURI,
1445 0 : pendingReadyPromise->mPromise)) {
1446 0 : iter.Remove();
1447 : }
1448 : }
1449 0 : }
1450 :
1451 : bool
1452 0 : ServiceWorkerManager::CheckReadyPromise(nsPIDOMWindowInner* aWindow,
1453 : nsIURI* aURI, Promise* aPromise)
1454 : {
1455 0 : MOZ_ASSERT(aWindow);
1456 0 : MOZ_ASSERT(aURI);
1457 :
1458 0 : nsCOMPtr<nsIDocument> doc = aWindow->GetExtantDoc();
1459 0 : MOZ_ASSERT(doc);
1460 :
1461 0 : nsCOMPtr<nsIPrincipal> principal = doc->NodePrincipal();
1462 0 : MOZ_ASSERT(principal);
1463 :
1464 : RefPtr<ServiceWorkerRegistrationInfo> registration =
1465 0 : GetServiceWorkerRegistrationInfo(principal, aURI);
1466 :
1467 0 : if (registration && registration->GetActive()) {
1468 0 : NS_ConvertUTF8toUTF16 scope(registration->mScope);
1469 : RefPtr<ServiceWorkerRegistration> swr =
1470 0 : aWindow->GetServiceWorkerRegistration(scope);
1471 0 : aPromise->MaybeResolve(swr);
1472 0 : return true;
1473 : }
1474 :
1475 0 : return false;
1476 : }
1477 :
1478 : ServiceWorkerInfo*
1479 0 : ServiceWorkerManager::GetActiveWorkerInfoForScope(const OriginAttributes& aOriginAttributes,
1480 : const nsACString& aScope)
1481 : {
1482 0 : AssertIsOnMainThread();
1483 :
1484 0 : nsCOMPtr<nsIURI> scopeURI;
1485 0 : nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), aScope, nullptr, nullptr);
1486 0 : if (NS_FAILED(rv)) {
1487 0 : return nullptr;
1488 : }
1489 : nsCOMPtr<nsIPrincipal> principal =
1490 0 : BasePrincipal::CreateCodebasePrincipal(scopeURI, aOriginAttributes);
1491 : RefPtr<ServiceWorkerRegistrationInfo> registration =
1492 0 : GetServiceWorkerRegistrationInfo(principal, scopeURI);
1493 0 : if (!registration) {
1494 0 : return nullptr;
1495 : }
1496 :
1497 0 : return registration->GetActive();
1498 : }
1499 :
1500 : ServiceWorkerInfo*
1501 0 : ServiceWorkerManager::GetActiveWorkerInfoForDocument(nsIDocument* aDocument)
1502 : {
1503 0 : AssertIsOnMainThread();
1504 :
1505 0 : RefPtr<ServiceWorkerRegistrationInfo> registration;
1506 0 : GetDocumentRegistration(aDocument, getter_AddRefs(registration));
1507 :
1508 0 : if (!registration) {
1509 0 : return nullptr;
1510 : }
1511 :
1512 0 : return registration->GetActive();
1513 : }
1514 :
1515 : namespace {
1516 :
1517 : class UnregisterJobCallback final : public ServiceWorkerJob::Callback
1518 : {
1519 : nsCOMPtr<nsIServiceWorkerUnregisterCallback> mCallback;
1520 :
1521 0 : ~UnregisterJobCallback()
1522 0 : {
1523 0 : }
1524 :
1525 : public:
1526 0 : explicit UnregisterJobCallback(nsIServiceWorkerUnregisterCallback* aCallback)
1527 0 : : mCallback(aCallback)
1528 : {
1529 0 : AssertIsOnMainThread();
1530 0 : MOZ_ASSERT(mCallback);
1531 0 : }
1532 :
1533 : void
1534 0 : JobFinished(ServiceWorkerJob* aJob, ErrorResult& aStatus)
1535 : {
1536 0 : AssertIsOnMainThread();
1537 0 : MOZ_ASSERT(aJob);
1538 :
1539 0 : if (aStatus.Failed()) {
1540 0 : mCallback->UnregisterFailed();
1541 0 : return;
1542 : }
1543 :
1544 0 : MOZ_ASSERT(aJob->GetType() == ServiceWorkerJob::Type::Unregister);
1545 : RefPtr<ServiceWorkerUnregisterJob> unregisterJob =
1546 0 : static_cast<ServiceWorkerUnregisterJob*>(aJob);
1547 0 : mCallback->UnregisterSucceeded(unregisterJob->GetResult());
1548 : }
1549 :
1550 0 : NS_INLINE_DECL_REFCOUNTING(UnregisterJobCallback)
1551 : };
1552 :
1553 : } // anonymous namespace
1554 :
1555 : NS_IMETHODIMP
1556 0 : ServiceWorkerManager::Unregister(nsIPrincipal* aPrincipal,
1557 : nsIServiceWorkerUnregisterCallback* aCallback,
1558 : const nsAString& aScope)
1559 : {
1560 0 : AssertIsOnMainThread();
1561 :
1562 0 : if (!aPrincipal) {
1563 0 : return NS_ERROR_FAILURE;
1564 : }
1565 :
1566 : nsresult rv;
1567 :
1568 : // This is not accessible by content, and callers should always ensure scope is
1569 : // a correct URI, so this is wrapped in DEBUG
1570 : #ifdef DEBUG
1571 0 : nsCOMPtr<nsIURI> scopeURI;
1572 0 : rv = NS_NewURI(getter_AddRefs(scopeURI), aScope, nullptr, nullptr);
1573 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1574 0 : return NS_ERROR_DOM_SECURITY_ERR;
1575 : }
1576 : #endif
1577 :
1578 0 : nsAutoCString scopeKey;
1579 0 : rv = PrincipalToScopeKey(aPrincipal, scopeKey);
1580 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1581 0 : return rv;
1582 : }
1583 :
1584 0 : NS_ConvertUTF16toUTF8 scope(aScope);
1585 0 : RefPtr<ServiceWorkerJobQueue> queue = GetOrCreateJobQueue(scopeKey, scope);
1586 :
1587 : RefPtr<ServiceWorkerUnregisterJob> job =
1588 0 : new ServiceWorkerUnregisterJob(aPrincipal, scope, true /* send to parent */);
1589 :
1590 0 : if (aCallback) {
1591 0 : RefPtr<UnregisterJobCallback> cb = new UnregisterJobCallback(aCallback);
1592 0 : job->AppendResultCallback(cb);
1593 : }
1594 :
1595 0 : queue->ScheduleJob(job);
1596 0 : return NS_OK;
1597 : }
1598 :
1599 : nsresult
1600 0 : ServiceWorkerManager::NotifyUnregister(nsIPrincipal* aPrincipal,
1601 : const nsAString& aScope)
1602 : {
1603 0 : AssertIsOnMainThread();
1604 0 : MOZ_ASSERT(aPrincipal);
1605 :
1606 : nsresult rv;
1607 :
1608 : // This is not accessible by content, and callers should always ensure scope is
1609 : // a correct URI, so this is wrapped in DEBUG
1610 : #ifdef DEBUG
1611 0 : nsCOMPtr<nsIURI> scopeURI;
1612 0 : rv = NS_NewURI(getter_AddRefs(scopeURI), aScope, nullptr, nullptr);
1613 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1614 0 : return rv;
1615 : }
1616 : #endif
1617 :
1618 0 : nsAutoCString scopeKey;
1619 0 : rv = PrincipalToScopeKey(aPrincipal, scopeKey);
1620 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1621 0 : return rv;
1622 : }
1623 :
1624 0 : NS_ConvertUTF16toUTF8 scope(aScope);
1625 0 : RefPtr<ServiceWorkerJobQueue> queue = GetOrCreateJobQueue(scopeKey, scope);
1626 :
1627 : RefPtr<ServiceWorkerUnregisterJob> job =
1628 : new ServiceWorkerUnregisterJob(aPrincipal, scope,
1629 0 : false /* send to parent */);
1630 :
1631 0 : queue->ScheduleJob(job);
1632 0 : return NS_OK;
1633 : }
1634 :
1635 : void
1636 0 : ServiceWorkerManager::WorkerIsIdle(ServiceWorkerInfo* aWorker)
1637 : {
1638 0 : AssertIsOnMainThread();
1639 0 : MOZ_DIAGNOSTIC_ASSERT(aWorker);
1640 :
1641 : RefPtr<ServiceWorkerRegistrationInfo> reg =
1642 0 : GetRegistration(aWorker->Principal(), aWorker->Scope());
1643 0 : if (!reg) {
1644 0 : return;
1645 : }
1646 :
1647 0 : if (reg->GetActive() != aWorker) {
1648 0 : return;
1649 : }
1650 :
1651 0 : if (!reg->IsControllingDocuments() && reg->mPendingUninstall) {
1652 0 : RemoveRegistration(reg);
1653 0 : return;
1654 : }
1655 :
1656 0 : reg->TryToActivateAsync();
1657 : }
1658 :
1659 : already_AddRefed<ServiceWorkerJobQueue>
1660 0 : ServiceWorkerManager::GetOrCreateJobQueue(const nsACString& aKey,
1661 : const nsACString& aScope)
1662 : {
1663 0 : MOZ_ASSERT(!aKey.IsEmpty());
1664 : ServiceWorkerManager::RegistrationDataPerPrincipal* data;
1665 : // XXX we could use LookupForAdd here to avoid a hashtable lookup, except that
1666 : // leads to a false positive assertion, see bug 1370674 comment 7.
1667 0 : if (!mRegistrationInfos.Get(aKey, &data)) {
1668 0 : data = new RegistrationDataPerPrincipal();
1669 0 : mRegistrationInfos.Put(aKey, data);
1670 : }
1671 :
1672 : RefPtr<ServiceWorkerJobQueue> queue =
1673 0 : data->mJobQueues.LookupForAdd(aScope).OrInsert(
1674 0 : []() { return new ServiceWorkerJobQueue(); });
1675 :
1676 0 : return queue.forget();
1677 : }
1678 :
1679 : /* static */
1680 : already_AddRefed<ServiceWorkerManager>
1681 67 : ServiceWorkerManager::GetInstance()
1682 : {
1683 : // Note: We don't simply check gInstance for null-ness here, since otherwise
1684 : // this can resurrect the ServiceWorkerManager pretty late during shutdown.
1685 : static bool firstTime = true;
1686 67 : if (firstTime) {
1687 6 : RefPtr<ServiceWorkerRegistrar> swr;
1688 :
1689 : // Don't create the ServiceWorkerManager until the ServiceWorkerRegistrar is
1690 : // initialized.
1691 3 : if (XRE_IsParentProcess()) {
1692 1 : swr = ServiceWorkerRegistrar::Get();
1693 1 : if (!swr) {
1694 0 : return nullptr;
1695 : }
1696 : }
1697 :
1698 3 : firstTime = false;
1699 :
1700 3 : AssertIsOnMainThread();
1701 :
1702 3 : gInstance = new ServiceWorkerManager();
1703 3 : gInstance->Init(swr);
1704 3 : ClearOnShutdown(&gInstance);
1705 : }
1706 134 : RefPtr<ServiceWorkerManager> copy = gInstance.get();
1707 67 : return copy.forget();
1708 : }
1709 :
1710 : void
1711 0 : ServiceWorkerManager::FinishFetch(ServiceWorkerRegistrationInfo* aRegistration)
1712 : {
1713 0 : }
1714 :
1715 : void
1716 0 : ServiceWorkerManager::ReportToAllClients(const nsCString& aScope,
1717 : const nsString& aMessage,
1718 : const nsString& aFilename,
1719 : const nsString& aLine,
1720 : uint32_t aLineNumber,
1721 : uint32_t aColumnNumber,
1722 : uint32_t aFlags)
1723 : {
1724 0 : nsCOMPtr<nsIURI> uri;
1725 : nsresult rv;
1726 :
1727 0 : if (!aFilename.IsEmpty()) {
1728 0 : rv = NS_NewURI(getter_AddRefs(uri), aFilename);
1729 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1730 0 : return;
1731 : }
1732 : }
1733 :
1734 0 : AutoTArray<uint64_t, 16> windows;
1735 :
1736 : // Report errors to every controlled document.
1737 0 : for (auto iter = mControlledDocuments.Iter(); !iter.Done(); iter.Next()) {
1738 0 : ServiceWorkerRegistrationInfo* reg = iter.UserData();
1739 0 : MOZ_ASSERT(reg);
1740 0 : if (!reg->mScope.Equals(aScope)) {
1741 0 : continue;
1742 : }
1743 :
1744 0 : nsCOMPtr<nsIDocument> doc = do_QueryInterface(iter.Key());
1745 0 : if (!doc || !doc->IsCurrentActiveDocument() || !doc->GetWindow()) {
1746 0 : continue;
1747 : }
1748 :
1749 0 : windows.AppendElement(doc->InnerWindowID());
1750 :
1751 0 : nsContentUtils::ReportToConsoleNonLocalized(aMessage,
1752 : aFlags,
1753 0 : NS_LITERAL_CSTRING("Service Workers"),
1754 : doc,
1755 : uri,
1756 : aLine,
1757 : aLineNumber,
1758 : aColumnNumber,
1759 0 : nsContentUtils::eOMIT_LOCATION);
1760 : }
1761 :
1762 : // Report to any documents that have called .register() for this scope. They
1763 : // may not be controlled, but will still want to see error reports.
1764 0 : WeakDocumentList* regList = mRegisteringDocuments.Get(aScope);
1765 0 : if (regList) {
1766 0 : for (int32_t i = regList->Length() - 1; i >= 0; --i) {
1767 0 : nsCOMPtr<nsIDocument> doc = do_QueryReferent(regList->ElementAt(i));
1768 0 : if (!doc) {
1769 0 : regList->RemoveElementAt(i);
1770 0 : continue;
1771 : }
1772 :
1773 0 : if (!doc->IsCurrentActiveDocument()) {
1774 0 : continue;
1775 : }
1776 :
1777 0 : uint64_t innerWindowId = doc->InnerWindowID();
1778 0 : if (windows.Contains(innerWindowId)) {
1779 0 : continue;
1780 : }
1781 :
1782 0 : windows.AppendElement(innerWindowId);
1783 :
1784 0 : nsContentUtils::ReportToConsoleNonLocalized(aMessage,
1785 : aFlags,
1786 0 : NS_LITERAL_CSTRING("Service Workers"),
1787 : doc,
1788 : uri,
1789 : aLine,
1790 : aLineNumber,
1791 : aColumnNumber,
1792 0 : nsContentUtils::eOMIT_LOCATION);
1793 : }
1794 :
1795 0 : if (regList->IsEmpty()) {
1796 0 : regList = nullptr;
1797 0 : mRegisteringDocuments.Remove(aScope);
1798 : }
1799 : }
1800 :
1801 0 : InterceptionList* intList = mNavigationInterceptions.Get(aScope);
1802 0 : if (intList) {
1803 0 : nsIConsoleService* consoleService = nullptr;
1804 0 : for (uint32_t i = 0; i < intList->Length(); ++i) {
1805 0 : nsCOMPtr<nsIInterceptedChannel> channel = intList->ElementAt(i);
1806 :
1807 0 : nsCOMPtr<nsIChannel> inner;
1808 0 : rv = channel->GetChannel(getter_AddRefs(inner));
1809 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1810 0 : continue;
1811 : }
1812 :
1813 0 : uint64_t innerWindowId = nsContentUtils::GetInnerWindowID(inner);
1814 0 : if (innerWindowId == 0 || windows.Contains(innerWindowId)) {
1815 0 : continue;
1816 : }
1817 :
1818 0 : windows.AppendElement(innerWindowId);
1819 :
1820 : // Unfortunately the nsContentUtils helpers don't provide a convenient
1821 : // way to log to a window ID without a document. Use console service
1822 : // directly.
1823 : nsCOMPtr<nsIScriptError> errorObject =
1824 0 : do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv);
1825 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1826 0 : return;
1827 : }
1828 :
1829 0 : rv = errorObject->InitWithWindowID(aMessage,
1830 : aFilename,
1831 : aLine,
1832 : aLineNumber,
1833 : aColumnNumber,
1834 : aFlags,
1835 0 : NS_LITERAL_CSTRING("Service Workers"),
1836 0 : innerWindowId);
1837 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1838 0 : return;
1839 : }
1840 :
1841 0 : if (!consoleService) {
1842 0 : rv = CallGetService(NS_CONSOLESERVICE_CONTRACTID, &consoleService);
1843 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1844 0 : return;
1845 : }
1846 : }
1847 :
1848 0 : consoleService->LogMessage(errorObject);
1849 : }
1850 : }
1851 :
1852 : // If there are no documents to report to, at least report something to the
1853 : // browser console.
1854 0 : if (windows.IsEmpty()) {
1855 0 : nsContentUtils::ReportToConsoleNonLocalized(aMessage,
1856 : aFlags,
1857 0 : NS_LITERAL_CSTRING("Service Workers"),
1858 : nullptr, // document
1859 : uri,
1860 : aLine,
1861 : aLineNumber,
1862 : aColumnNumber,
1863 0 : nsContentUtils::eOMIT_LOCATION);
1864 0 : return;
1865 : }
1866 : }
1867 :
1868 : /* static */
1869 : void
1870 0 : ServiceWorkerManager::LocalizeAndReportToAllClients(
1871 : const nsCString& aScope,
1872 : const char* aStringKey,
1873 : const nsTArray<nsString>& aParamArray,
1874 : uint32_t aFlags,
1875 : const nsString& aFilename,
1876 : const nsString& aLine,
1877 : uint32_t aLineNumber,
1878 : uint32_t aColumnNumber)
1879 : {
1880 0 : RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
1881 0 : if (!swm) {
1882 0 : return;
1883 : }
1884 :
1885 : nsresult rv;
1886 0 : nsXPIDLString message;
1887 : rv = nsContentUtils::FormatLocalizedString(nsContentUtils::eDOM_PROPERTIES,
1888 0 : aStringKey, aParamArray, message);
1889 0 : if (NS_SUCCEEDED(rv)) {
1890 0 : swm->ReportToAllClients(aScope, message,
1891 : aFilename, aLine, aLineNumber, aColumnNumber,
1892 0 : aFlags);
1893 : } else {
1894 0 : NS_WARNING("Failed to format and therefore report localized error.");
1895 : }
1896 : }
1897 :
1898 : void
1899 0 : ServiceWorkerManager::FlushReportsToAllClients(const nsACString& aScope,
1900 : nsIConsoleReportCollector* aReporter)
1901 : {
1902 0 : AutoTArray<uint64_t, 16> windows;
1903 :
1904 : // Report errors to every controlled document.
1905 0 : for (auto iter = mControlledDocuments.Iter(); !iter.Done(); iter.Next()) {
1906 0 : ServiceWorkerRegistrationInfo* reg = iter.UserData();
1907 0 : MOZ_ASSERT(reg);
1908 0 : if (!reg->mScope.Equals(aScope)) {
1909 0 : continue;
1910 : }
1911 :
1912 0 : nsCOMPtr<nsIDocument> doc = do_QueryInterface(iter.Key());
1913 0 : if (!doc || !doc->IsCurrentActiveDocument() || !doc->GetWindow()) {
1914 0 : continue;
1915 : }
1916 :
1917 0 : uint64_t innerWindowId = doc->InnerWindowID();
1918 0 : windows.AppendElement(innerWindowId);
1919 :
1920 : aReporter->FlushReportsToConsole(
1921 0 : innerWindowId, nsIConsoleReportCollector::ReportAction::Save);
1922 : }
1923 :
1924 : // Report to any documents that have called .register() for this scope. They
1925 : // may not be controlled, but will still want to see error reports.
1926 0 : WeakDocumentList* regList = mRegisteringDocuments.Get(aScope);
1927 0 : if (regList) {
1928 0 : for (int32_t i = regList->Length() - 1; i >= 0; --i) {
1929 0 : nsCOMPtr<nsIDocument> doc = do_QueryReferent(regList->ElementAt(i));
1930 0 : if (!doc) {
1931 0 : regList->RemoveElementAt(i);
1932 0 : continue;
1933 : }
1934 :
1935 0 : if (!doc->IsCurrentActiveDocument()) {
1936 0 : continue;
1937 : }
1938 :
1939 0 : uint64_t innerWindowId = doc->InnerWindowID();
1940 0 : if (windows.Contains(innerWindowId)) {
1941 0 : continue;
1942 : }
1943 :
1944 0 : windows.AppendElement(innerWindowId);
1945 :
1946 : aReporter->FlushReportsToConsole(
1947 0 : innerWindowId, nsIConsoleReportCollector::ReportAction::Save);
1948 : }
1949 :
1950 0 : if (regList->IsEmpty()) {
1951 0 : regList = nullptr;
1952 0 : mRegisteringDocuments.Remove(aScope);
1953 : }
1954 : }
1955 :
1956 : nsresult rv;
1957 0 : InterceptionList* intList = mNavigationInterceptions.Get(aScope);
1958 0 : if (intList) {
1959 0 : for (uint32_t i = 0; i < intList->Length(); ++i) {
1960 0 : nsCOMPtr<nsIInterceptedChannel> channel = intList->ElementAt(i);
1961 :
1962 0 : nsCOMPtr<nsIChannel> inner;
1963 0 : rv = channel->GetChannel(getter_AddRefs(inner));
1964 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1965 0 : continue;
1966 : }
1967 :
1968 0 : uint64_t innerWindowId = nsContentUtils::GetInnerWindowID(inner);
1969 0 : if (innerWindowId == 0 || windows.Contains(innerWindowId)) {
1970 0 : continue;
1971 : }
1972 :
1973 0 : windows.AppendElement(innerWindowId);
1974 :
1975 : aReporter->FlushReportsToConsole(
1976 0 : innerWindowId, nsIConsoleReportCollector::ReportAction::Save);
1977 : }
1978 : }
1979 :
1980 : // If there are no documents to report to, at least report something to the
1981 : // browser console.
1982 0 : if (windows.IsEmpty()) {
1983 0 : aReporter->FlushReportsToConsole(0);
1984 0 : return;
1985 : }
1986 :
1987 0 : aReporter->ClearConsoleReports();
1988 : }
1989 :
1990 : void
1991 0 : ServiceWorkerManager::HandleError(JSContext* aCx,
1992 : nsIPrincipal* aPrincipal,
1993 : const nsCString& aScope,
1994 : const nsString& aWorkerURL,
1995 : const nsString& aMessage,
1996 : const nsString& aFilename,
1997 : const nsString& aLine,
1998 : uint32_t aLineNumber,
1999 : uint32_t aColumnNumber,
2000 : uint32_t aFlags,
2001 : JSExnType aExnType)
2002 : {
2003 0 : AssertIsOnMainThread();
2004 0 : MOZ_ASSERT(aPrincipal);
2005 :
2006 0 : nsAutoCString scopeKey;
2007 0 : nsresult rv = PrincipalToScopeKey(aPrincipal, scopeKey);
2008 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2009 0 : return;
2010 : }
2011 :
2012 : ServiceWorkerManager::RegistrationDataPerPrincipal* data;
2013 0 : if (NS_WARN_IF(!mRegistrationInfos.Get(scopeKey, &data))) {
2014 0 : return;
2015 : }
2016 :
2017 : // Always report any uncaught exceptions or errors to the console of
2018 : // each client.
2019 : ReportToAllClients(aScope, aMessage, aFilename, aLine, aLineNumber,
2020 0 : aColumnNumber, aFlags);
2021 : }
2022 :
2023 : void
2024 0 : ServiceWorkerManager::LoadRegistration(
2025 : const ServiceWorkerRegistrationData& aRegistration)
2026 : {
2027 0 : AssertIsOnMainThread();
2028 :
2029 : nsCOMPtr<nsIPrincipal> principal =
2030 0 : PrincipalInfoToPrincipal(aRegistration.principal());
2031 0 : if (!principal) {
2032 0 : return;
2033 : }
2034 :
2035 : RefPtr<ServiceWorkerRegistrationInfo> registration =
2036 0 : GetRegistration(principal, aRegistration.scope());
2037 0 : if (!registration) {
2038 0 : registration = CreateNewRegistration(aRegistration.scope(), principal,
2039 0 : aRegistration.loadFlags());
2040 : } else {
2041 : // If active worker script matches our expectations for a "current worker",
2042 : // then we are done. Since scripts with the same URL might have different
2043 : // contents such as updated scripts or scripts with different LoadFlags, we
2044 : // use the CacheName to judje whether the two scripts are identical, where
2045 : // the CacheName is an UUID generated when a new script is found.
2046 0 : if (registration->GetActive() &&
2047 0 : registration->GetActive()->CacheName() == aRegistration.cacheName()) {
2048 : // No needs for updates.
2049 0 : return;
2050 : }
2051 : }
2052 :
2053 0 : registration->SetLastUpdateTime(aRegistration.lastUpdateTime());
2054 :
2055 0 : const nsCString& currentWorkerURL = aRegistration.currentWorkerURL();
2056 0 : if (!currentWorkerURL.IsEmpty()) {
2057 0 : registration->SetActive(
2058 0 : new ServiceWorkerInfo(registration->mPrincipal,
2059 0 : registration->mScope,
2060 : currentWorkerURL,
2061 0 : aRegistration.cacheName(),
2062 0 : registration->GetLoadFlags()));
2063 0 : registration->GetActive()->SetHandlesFetch(aRegistration.currentWorkerHandlesFetch());
2064 0 : registration->GetActive()->SetInstalledTime(aRegistration.currentWorkerInstalledTime());
2065 0 : registration->GetActive()->SetActivatedTime(aRegistration.currentWorkerActivatedTime());
2066 : }
2067 : }
2068 :
2069 : void
2070 3 : ServiceWorkerManager::LoadRegistrations(
2071 : const nsTArray<ServiceWorkerRegistrationData>& aRegistrations)
2072 : {
2073 3 : AssertIsOnMainThread();
2074 :
2075 3 : for (uint32_t i = 0, len = aRegistrations.Length(); i < len; ++i) {
2076 0 : LoadRegistration(aRegistrations[i]);
2077 : }
2078 3 : }
2079 :
2080 : void
2081 0 : ServiceWorkerManager::ActorFailed()
2082 : {
2083 0 : MOZ_DIAGNOSTIC_ASSERT(!mActor);
2084 0 : MaybeStartShutdown();
2085 0 : }
2086 :
2087 : void
2088 2 : ServiceWorkerManager::ActorCreated(mozilla::ipc::PBackgroundChild* aActor)
2089 : {
2090 2 : MOZ_ASSERT(aActor);
2091 2 : MOZ_ASSERT(!mActor);
2092 :
2093 2 : if (mShuttingDown) {
2094 0 : MOZ_DIAGNOSTIC_ASSERT(mPendingOperations.IsEmpty());
2095 0 : return;
2096 : }
2097 :
2098 : PServiceWorkerManagerChild* actor =
2099 2 : aActor->SendPServiceWorkerManagerConstructor();
2100 2 : if (!actor) {
2101 0 : ActorFailed();
2102 0 : return;
2103 : }
2104 :
2105 2 : mActor = static_cast<ServiceWorkerManagerChild*>(actor);
2106 :
2107 : // Flush the pending requests.
2108 2 : for (uint32_t i = 0, len = mPendingOperations.Length(); i < len; ++i) {
2109 0 : MOZ_ASSERT(mPendingOperations[i]);
2110 0 : nsresult rv = NS_DispatchToCurrentThread(mPendingOperations[i].forget());
2111 0 : if (NS_FAILED(rv)) {
2112 0 : NS_WARNING("Failed to dispatch a runnable.");
2113 : }
2114 : }
2115 :
2116 2 : mPendingOperations.Clear();
2117 : }
2118 :
2119 : void
2120 0 : ServiceWorkerManager::StoreRegistration(
2121 : nsIPrincipal* aPrincipal,
2122 : ServiceWorkerRegistrationInfo* aRegistration)
2123 : {
2124 0 : MOZ_ASSERT(aPrincipal);
2125 0 : MOZ_ASSERT(aRegistration);
2126 :
2127 0 : if (mShuttingDown) {
2128 0 : return;
2129 : }
2130 :
2131 0 : MOZ_DIAGNOSTIC_ASSERT(mActor);
2132 0 : if (!mActor) {
2133 0 : return;
2134 : }
2135 :
2136 0 : ServiceWorkerRegistrationData data;
2137 0 : nsresult rv = PopulateRegistrationData(aPrincipal, aRegistration, data);
2138 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2139 0 : return;
2140 : }
2141 :
2142 0 : PrincipalInfo principalInfo;
2143 0 : if (NS_WARN_IF(NS_FAILED(PrincipalToPrincipalInfo(aPrincipal,
2144 : &principalInfo)))) {
2145 0 : return;
2146 : }
2147 :
2148 0 : mActor->SendRegister(data);
2149 : }
2150 :
2151 : already_AddRefed<ServiceWorkerRegistrationInfo>
2152 0 : ServiceWorkerManager::GetServiceWorkerRegistrationInfo(nsPIDOMWindowInner* aWindow)
2153 : {
2154 0 : MOZ_ASSERT(aWindow);
2155 0 : nsCOMPtr<nsIDocument> document = aWindow->GetExtantDoc();
2156 0 : return GetServiceWorkerRegistrationInfo(document);
2157 : }
2158 :
2159 : already_AddRefed<ServiceWorkerRegistrationInfo>
2160 4 : ServiceWorkerManager::GetServiceWorkerRegistrationInfo(nsIDocument* aDoc)
2161 : {
2162 4 : MOZ_ASSERT(aDoc);
2163 8 : nsCOMPtr<nsIURI> documentURI = aDoc->GetDocumentURI();
2164 8 : nsCOMPtr<nsIPrincipal> principal = aDoc->NodePrincipal();
2165 8 : return GetServiceWorkerRegistrationInfo(principal, documentURI);
2166 : }
2167 :
2168 : already_AddRefed<ServiceWorkerRegistrationInfo>
2169 5 : ServiceWorkerManager::GetServiceWorkerRegistrationInfo(nsIPrincipal* aPrincipal,
2170 : nsIURI* aURI)
2171 : {
2172 5 : MOZ_ASSERT(aPrincipal);
2173 5 : MOZ_ASSERT(aURI);
2174 :
2175 : //XXXnsm Temporary fix until Bug 1171432 is fixed.
2176 5 : if (NS_WARN_IF(BasePrincipal::Cast(aPrincipal)->AppId() == nsIScriptSecurityManager::UNKNOWN_APP_ID)) {
2177 0 : return nullptr;
2178 : }
2179 :
2180 10 : nsAutoCString scopeKey;
2181 5 : nsresult rv = PrincipalToScopeKey(aPrincipal, scopeKey);
2182 5 : if (NS_FAILED(rv)) {
2183 2 : return nullptr;
2184 : }
2185 :
2186 3 : return GetServiceWorkerRegistrationInfo(scopeKey, aURI);
2187 : }
2188 :
2189 : already_AddRefed<ServiceWorkerRegistrationInfo>
2190 3 : ServiceWorkerManager::GetServiceWorkerRegistrationInfo(const nsACString& aScopeKey,
2191 : nsIURI* aURI)
2192 : {
2193 3 : MOZ_ASSERT(aURI);
2194 :
2195 6 : nsAutoCString spec;
2196 3 : nsresult rv = aURI->GetSpec(spec);
2197 3 : if (NS_WARN_IF(NS_FAILED(rv))) {
2198 0 : return nullptr;
2199 : }
2200 :
2201 6 : nsAutoCString scope;
2202 : RegistrationDataPerPrincipal* data;
2203 3 : if (!FindScopeForPath(aScopeKey, spec, &data, scope)) {
2204 3 : return nullptr;
2205 : }
2206 :
2207 0 : MOZ_ASSERT(data);
2208 :
2209 0 : RefPtr<ServiceWorkerRegistrationInfo> registration;
2210 0 : data->mInfos.Get(scope, getter_AddRefs(registration));
2211 : // ordered scopes and registrations better be in sync.
2212 0 : MOZ_ASSERT(registration);
2213 :
2214 : #ifdef DEBUG
2215 0 : nsAutoCString origin;
2216 0 : rv = registration->mPrincipal->GetOrigin(origin);
2217 0 : MOZ_ASSERT(NS_SUCCEEDED(rv));
2218 0 : MOZ_ASSERT(origin.Equals(aScopeKey));
2219 : #endif
2220 :
2221 0 : if (registration->mPendingUninstall) {
2222 0 : return nullptr;
2223 : }
2224 0 : return registration.forget();
2225 : }
2226 :
2227 : /* static */ nsresult
2228 5 : ServiceWorkerManager::PrincipalToScopeKey(nsIPrincipal* aPrincipal,
2229 : nsACString& aKey)
2230 : {
2231 5 : MOZ_ASSERT(aPrincipal);
2232 :
2233 5 : if (!BasePrincipal::Cast(aPrincipal)->IsCodebasePrincipal()) {
2234 2 : return NS_ERROR_FAILURE;
2235 : }
2236 :
2237 3 : nsresult rv = aPrincipal->GetOrigin(aKey);
2238 3 : if (NS_WARN_IF(NS_FAILED(rv))) {
2239 0 : return rv;
2240 : }
2241 :
2242 3 : return NS_OK;
2243 : }
2244 :
2245 : /* static */ void
2246 0 : ServiceWorkerManager::AddScopeAndRegistration(const nsACString& aScope,
2247 : ServiceWorkerRegistrationInfo* aInfo)
2248 : {
2249 0 : MOZ_ASSERT(aInfo);
2250 0 : MOZ_ASSERT(aInfo->mPrincipal);
2251 :
2252 0 : RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
2253 0 : if (!swm) {
2254 : // browser shutdown
2255 0 : return;
2256 : }
2257 :
2258 0 : nsAutoCString scopeKey;
2259 0 : nsresult rv = swm->PrincipalToScopeKey(aInfo->mPrincipal, scopeKey);
2260 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2261 0 : return;
2262 : }
2263 :
2264 0 : MOZ_ASSERT(!scopeKey.IsEmpty());
2265 :
2266 : RegistrationDataPerPrincipal* data =
2267 0 : swm->mRegistrationInfos.LookupForAdd(scopeKey).OrInsert(
2268 0 : []() { return new RegistrationDataPerPrincipal(); });
2269 :
2270 0 : for (uint32_t i = 0; i < data->mOrderedScopes.Length(); ++i) {
2271 0 : const nsCString& current = data->mOrderedScopes[i];
2272 :
2273 : // Perfect match!
2274 0 : if (aScope.Equals(current)) {
2275 0 : data->mInfos.Put(aScope, aInfo);
2276 0 : swm->NotifyListenersOnRegister(aInfo);
2277 0 : return;
2278 : }
2279 :
2280 : // Sort by length, with longest match first.
2281 : // /foo/bar should be before /foo/
2282 : // Similarly /foo/b is between the two.
2283 0 : if (StringBeginsWith(aScope, current)) {
2284 0 : data->mOrderedScopes.InsertElementAt(i, aScope);
2285 0 : data->mInfos.Put(aScope, aInfo);
2286 0 : swm->NotifyListenersOnRegister(aInfo);
2287 0 : return;
2288 : }
2289 : }
2290 :
2291 0 : data->mOrderedScopes.AppendElement(aScope);
2292 0 : data->mInfos.Put(aScope, aInfo);
2293 0 : swm->NotifyListenersOnRegister(aInfo);
2294 : }
2295 :
2296 : /* static */ bool
2297 3 : ServiceWorkerManager::FindScopeForPath(const nsACString& aScopeKey,
2298 : const nsACString& aPath,
2299 : RegistrationDataPerPrincipal** aData,
2300 : nsACString& aMatch)
2301 : {
2302 3 : MOZ_ASSERT(aData);
2303 :
2304 6 : RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
2305 :
2306 3 : if (!swm || !swm->mRegistrationInfos.Get(aScopeKey, aData)) {
2307 3 : return false;
2308 : }
2309 :
2310 0 : for (uint32_t i = 0; i < (*aData)->mOrderedScopes.Length(); ++i) {
2311 0 : const nsCString& current = (*aData)->mOrderedScopes[i];
2312 0 : if (StringBeginsWith(aPath, current)) {
2313 0 : aMatch = current;
2314 0 : return true;
2315 : }
2316 : }
2317 :
2318 0 : return false;
2319 : }
2320 :
2321 : /* static */ bool
2322 0 : ServiceWorkerManager::HasScope(nsIPrincipal* aPrincipal,
2323 : const nsACString& aScope)
2324 : {
2325 0 : RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
2326 0 : if (!swm) {
2327 0 : return false;
2328 : }
2329 :
2330 0 : nsAutoCString scopeKey;
2331 0 : nsresult rv = PrincipalToScopeKey(aPrincipal, scopeKey);
2332 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2333 0 : return false;
2334 : }
2335 :
2336 : RegistrationDataPerPrincipal* data;
2337 0 : if (!swm->mRegistrationInfos.Get(scopeKey, &data)) {
2338 0 : return false;
2339 : }
2340 :
2341 0 : return data->mOrderedScopes.Contains(aScope);
2342 : }
2343 :
2344 : /* static */ void
2345 0 : ServiceWorkerManager::RemoveScopeAndRegistration(ServiceWorkerRegistrationInfo* aRegistration)
2346 : {
2347 0 : RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
2348 0 : if (!swm) {
2349 0 : return;
2350 : }
2351 :
2352 0 : nsAutoCString scopeKey;
2353 0 : nsresult rv = swm->PrincipalToScopeKey(aRegistration->mPrincipal, scopeKey);
2354 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2355 0 : return;
2356 : }
2357 :
2358 : RegistrationDataPerPrincipal* data;
2359 0 : if (!swm->mRegistrationInfos.Get(scopeKey, &data)) {
2360 0 : return;
2361 : }
2362 :
2363 0 : if (auto entry = data->mUpdateTimers.Lookup(aRegistration->mScope)) {
2364 0 : entry.Data()->Cancel();
2365 0 : entry.Remove();
2366 : }
2367 :
2368 : // The registration should generally only be removed if there are no controlled
2369 : // documents, but mControlledDocuments can contain references to potentially
2370 : // controlled docs. This happens when the service worker is not active yet.
2371 : // We must purge these references since we are evicting the registration.
2372 0 : for (auto iter = swm->mControlledDocuments.Iter(); !iter.Done(); iter.Next()) {
2373 0 : ServiceWorkerRegistrationInfo* reg = iter.UserData();
2374 0 : MOZ_ASSERT(reg);
2375 0 : if (reg->mScope.Equals(aRegistration->mScope)) {
2376 0 : iter.Remove();
2377 : }
2378 : }
2379 :
2380 0 : RefPtr<ServiceWorkerRegistrationInfo> info;
2381 0 : data->mInfos.Remove(aRegistration->mScope, getter_AddRefs(info));
2382 0 : data->mOrderedScopes.RemoveElement(aRegistration->mScope);
2383 0 : swm->NotifyListenersOnUnregister(info);
2384 :
2385 0 : swm->MaybeRemoveRegistrationInfo(scopeKey);
2386 0 : swm->NotifyServiceWorkerRegistrationRemoved(aRegistration);
2387 : }
2388 :
2389 : void
2390 0 : ServiceWorkerManager::MaybeRemoveRegistrationInfo(const nsACString& aScopeKey)
2391 : {
2392 0 : if (auto entry = mRegistrationInfos.Lookup(aScopeKey)) {
2393 0 : if (entry.Data()->mOrderedScopes.IsEmpty() &&
2394 0 : entry.Data()->mJobQueues.Count() == 0) {
2395 0 : entry.Remove();
2396 : }
2397 : }
2398 0 : }
2399 :
2400 : void
2401 4 : ServiceWorkerManager::MaybeStartControlling(nsIDocument* aDoc,
2402 : const nsAString& aDocumentId)
2403 : {
2404 4 : AssertIsOnMainThread();
2405 4 : MOZ_ASSERT(aDoc);
2406 : RefPtr<ServiceWorkerRegistrationInfo> registration =
2407 8 : GetServiceWorkerRegistrationInfo(aDoc);
2408 4 : if (registration) {
2409 0 : MOZ_ASSERT(!mControlledDocuments.Contains(aDoc));
2410 0 : StartControllingADocument(registration, aDoc, aDocumentId);
2411 : }
2412 4 : }
2413 :
2414 : void
2415 4 : ServiceWorkerManager::MaybeStopControlling(nsIDocument* aDoc)
2416 : {
2417 4 : AssertIsOnMainThread();
2418 4 : MOZ_ASSERT(aDoc);
2419 8 : RefPtr<ServiceWorkerRegistrationInfo> registration;
2420 4 : mControlledDocuments.Remove(aDoc, getter_AddRefs(registration));
2421 : // A document which was uncontrolled does not maintain that state itself, so
2422 : // it will always call MaybeStopControlling() even if there isn't an
2423 : // associated registration. So this check is required.
2424 4 : if (registration) {
2425 0 : StopControllingADocument(registration);
2426 : }
2427 4 : }
2428 :
2429 : void
2430 4 : ServiceWorkerManager::MaybeCheckNavigationUpdate(nsIDocument* aDoc)
2431 : {
2432 4 : AssertIsOnMainThread();
2433 4 : MOZ_ASSERT(aDoc);
2434 : // We perform these success path navigation update steps when the
2435 : // document tells us its more or less done loading. This avoids
2436 : // slowing down page load and also lets pages consistently get
2437 : // updatefound events when they fire.
2438 : //
2439 : // 9.8.20 If respondWithEntered is false, then:
2440 : // 9.8.22 Else: (respondWith was entered and succeeded)
2441 : // If request is a non-subresource request, then: Invoke Soft Update
2442 : // algorithm.
2443 8 : RefPtr<ServiceWorkerRegistrationInfo> registration;
2444 4 : mControlledDocuments.Get(aDoc, getter_AddRefs(registration));
2445 4 : if (registration) {
2446 0 : registration->MaybeScheduleUpdate();
2447 : }
2448 4 : }
2449 :
2450 : void
2451 0 : ServiceWorkerManager::StartControllingADocument(ServiceWorkerRegistrationInfo* aRegistration,
2452 : nsIDocument* aDoc,
2453 : const nsAString& aDocumentId)
2454 : {
2455 0 : MOZ_ASSERT(aRegistration);
2456 0 : MOZ_ASSERT(aDoc);
2457 :
2458 0 : aRegistration->StartControllingADocument();
2459 0 : mControlledDocuments.Put(aDoc, aRegistration);
2460 0 : if (!aDocumentId.IsEmpty()) {
2461 0 : aDoc->SetId(aDocumentId);
2462 : }
2463 0 : Telemetry::Accumulate(Telemetry::SERVICE_WORKER_CONTROLLED_DOCUMENTS, 1);
2464 0 : }
2465 :
2466 : void
2467 0 : ServiceWorkerManager::StopControllingADocument(ServiceWorkerRegistrationInfo* aRegistration)
2468 : {
2469 0 : aRegistration->StopControllingADocument();
2470 0 : if (aRegistration->IsControllingDocuments() || !aRegistration->IsIdle()) {
2471 0 : return;
2472 : }
2473 :
2474 0 : if (aRegistration->mPendingUninstall) {
2475 0 : RemoveRegistration(aRegistration);
2476 0 : return;
2477 : }
2478 :
2479 : // We use to aggressively terminate the worker at this point, but it
2480 : // caused problems. There are more uses for a service worker than actively
2481 : // controlled documents. We need to let the worker naturally terminate
2482 : // in case its handling push events, message events, etc.
2483 0 : aRegistration->TryToActivateAsync();
2484 : }
2485 :
2486 : NS_IMETHODIMP
2487 0 : ServiceWorkerManager::GetScopeForUrl(nsIPrincipal* aPrincipal,
2488 : const nsAString& aUrl, nsAString& aScope)
2489 : {
2490 0 : MOZ_ASSERT(aPrincipal);
2491 :
2492 0 : nsCOMPtr<nsIURI> uri;
2493 0 : nsresult rv = NS_NewURI(getter_AddRefs(uri), aUrl, nullptr, nullptr);
2494 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2495 0 : return NS_ERROR_FAILURE;
2496 : }
2497 :
2498 : RefPtr<ServiceWorkerRegistrationInfo> r =
2499 0 : GetServiceWorkerRegistrationInfo(aPrincipal, uri);
2500 0 : if (!r) {
2501 0 : return NS_ERROR_FAILURE;
2502 : }
2503 :
2504 0 : aScope = NS_ConvertUTF8toUTF16(r->mScope);
2505 0 : return NS_OK;
2506 : }
2507 :
2508 : NS_IMETHODIMP
2509 0 : ServiceWorkerManager::AddRegistrationEventListener(const nsAString& aScope,
2510 : ServiceWorkerRegistrationListener* aListener)
2511 : {
2512 0 : AssertIsOnMainThread();
2513 0 : MOZ_ASSERT(aListener);
2514 : #ifdef DEBUG
2515 : // Ensure a registration is only listening for it's own scope.
2516 0 : nsAutoString regScope;
2517 0 : aListener->GetScope(regScope);
2518 0 : MOZ_ASSERT(!regScope.IsEmpty());
2519 0 : MOZ_ASSERT(aScope.Equals(regScope));
2520 : #endif
2521 :
2522 0 : MOZ_ASSERT(!mServiceWorkerRegistrationListeners.Contains(aListener));
2523 0 : mServiceWorkerRegistrationListeners.AppendElement(aListener);
2524 0 : return NS_OK;
2525 : }
2526 :
2527 : NS_IMETHODIMP
2528 0 : ServiceWorkerManager::RemoveRegistrationEventListener(const nsAString& aScope,
2529 : ServiceWorkerRegistrationListener* aListener)
2530 : {
2531 0 : AssertIsOnMainThread();
2532 0 : MOZ_ASSERT(aListener);
2533 : #ifdef DEBUG
2534 : // Ensure a registration is unregistering for it's own scope.
2535 0 : nsAutoString regScope;
2536 0 : aListener->GetScope(regScope);
2537 0 : MOZ_ASSERT(!regScope.IsEmpty());
2538 0 : MOZ_ASSERT(aScope.Equals(regScope));
2539 : #endif
2540 :
2541 0 : MOZ_ASSERT(mServiceWorkerRegistrationListeners.Contains(aListener));
2542 0 : mServiceWorkerRegistrationListeners.RemoveElement(aListener);
2543 0 : return NS_OK;
2544 : }
2545 :
2546 : void
2547 0 : ServiceWorkerManager::FireUpdateFoundOnServiceWorkerRegistrations(
2548 : ServiceWorkerRegistrationInfo* aRegistration)
2549 : {
2550 0 : AssertIsOnMainThread();
2551 :
2552 0 : nsTObserverArray<ServiceWorkerRegistrationListener*>::ForwardIterator it(mServiceWorkerRegistrationListeners);
2553 0 : while (it.HasMore()) {
2554 0 : RefPtr<ServiceWorkerRegistrationListener> target = it.GetNext();
2555 0 : nsAutoString regScope;
2556 0 : target->GetScope(regScope);
2557 0 : MOZ_ASSERT(!regScope.IsEmpty());
2558 :
2559 0 : NS_ConvertUTF16toUTF8 utf8Scope(regScope);
2560 0 : if (utf8Scope.Equals(aRegistration->mScope)) {
2561 0 : target->UpdateFound();
2562 : }
2563 : }
2564 0 : }
2565 :
2566 : /*
2567 : * This is used for installing, waiting and active.
2568 : */
2569 : nsresult
2570 0 : ServiceWorkerManager::GetServiceWorkerForScope(nsPIDOMWindowInner* aWindow,
2571 : const nsAString& aScope,
2572 : WhichServiceWorker aWhichWorker,
2573 : nsISupports** aServiceWorker)
2574 : {
2575 0 : AssertIsOnMainThread();
2576 :
2577 0 : if (NS_WARN_IF(!aWindow)) {
2578 0 : return NS_ERROR_DOM_INVALID_STATE_ERR;
2579 : }
2580 :
2581 0 : nsCOMPtr<nsIDocument> doc = aWindow->GetExtantDoc();
2582 0 : MOZ_ASSERT(doc);
2583 :
2584 : ///////////////////////////////////////////
2585 : // Security check
2586 0 : nsAutoCString scope = NS_ConvertUTF16toUTF8(aScope);
2587 0 : nsCOMPtr<nsIURI> scopeURI;
2588 : // We pass nullptr as the base URI since scopes obtained from
2589 : // ServiceWorkerRegistrations MUST be fully qualified URIs.
2590 0 : nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), scope, nullptr, nullptr);
2591 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2592 0 : return NS_ERROR_DOM_SECURITY_ERR;
2593 : }
2594 :
2595 0 : nsCOMPtr<nsIPrincipal> documentPrincipal = doc->NodePrincipal();
2596 0 : rv = documentPrincipal->CheckMayLoad(scopeURI, true /* report */,
2597 0 : false /* allowIfInheritsPrinciple */);
2598 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2599 0 : return NS_ERROR_DOM_SECURITY_ERR;
2600 : }
2601 : ////////////////////////////////////////////
2602 :
2603 : RefPtr<ServiceWorkerRegistrationInfo> registration =
2604 0 : GetRegistration(documentPrincipal, scope);
2605 0 : if (NS_WARN_IF(!registration)) {
2606 0 : return NS_ERROR_FAILURE;
2607 : }
2608 :
2609 0 : RefPtr<ServiceWorkerInfo> info;
2610 0 : if (aWhichWorker == WhichServiceWorker::INSTALLING_WORKER) {
2611 0 : info = registration->GetInstalling();
2612 0 : } else if (aWhichWorker == WhichServiceWorker::WAITING_WORKER) {
2613 0 : info = registration->GetWaiting();
2614 0 : } else if (aWhichWorker == WhichServiceWorker::ACTIVE_WORKER) {
2615 0 : info = registration->GetActive();
2616 : } else {
2617 0 : MOZ_CRASH("Invalid worker type");
2618 : }
2619 :
2620 0 : if (NS_WARN_IF(!info)) {
2621 0 : return NS_ERROR_DOM_NOT_FOUND_ERR;
2622 : }
2623 :
2624 0 : RefPtr<ServiceWorker> serviceWorker = info->GetOrCreateInstance(aWindow);
2625 :
2626 0 : serviceWorker->SetState(info->State());
2627 0 : serviceWorker.forget(aServiceWorker);
2628 0 : return NS_OK;
2629 : }
2630 :
2631 : namespace {
2632 :
2633 0 : class ContinueDispatchFetchEventRunnable : public Runnable
2634 : {
2635 : RefPtr<ServiceWorkerPrivate> mServiceWorkerPrivate;
2636 : nsCOMPtr<nsIInterceptedChannel> mChannel;
2637 : nsCOMPtr<nsILoadGroup> mLoadGroup;
2638 : nsString mDocumentId;
2639 : bool mIsReload;
2640 : public:
2641 0 : ContinueDispatchFetchEventRunnable(
2642 : ServiceWorkerPrivate* aServiceWorkerPrivate,
2643 : nsIInterceptedChannel* aChannel,
2644 : nsILoadGroup* aLoadGroup,
2645 : const nsAString& aDocumentId,
2646 : bool aIsReload)
2647 0 : : Runnable("dom::workers::ContinueDispatchFetchEventRunnable")
2648 : , mServiceWorkerPrivate(aServiceWorkerPrivate)
2649 : , mChannel(aChannel)
2650 : , mLoadGroup(aLoadGroup)
2651 : , mDocumentId(aDocumentId)
2652 0 : , mIsReload(aIsReload)
2653 : {
2654 0 : MOZ_ASSERT(aServiceWorkerPrivate);
2655 0 : MOZ_ASSERT(aChannel);
2656 0 : }
2657 :
2658 : void
2659 0 : HandleError()
2660 : {
2661 0 : AssertIsOnMainThread();
2662 0 : NS_WARNING("Unexpected error while dispatching fetch event!");
2663 0 : DebugOnly<nsresult> rv = mChannel->ResetInterception();
2664 0 : NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
2665 : "Failed to resume intercepted network request");
2666 0 : }
2667 :
2668 : NS_IMETHOD
2669 0 : Run() override
2670 : {
2671 0 : AssertIsOnMainThread();
2672 :
2673 0 : nsCOMPtr<nsIChannel> channel;
2674 0 : nsresult rv = mChannel->GetChannel(getter_AddRefs(channel));
2675 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2676 0 : HandleError();
2677 0 : return NS_OK;
2678 : }
2679 :
2680 : // The channel might have encountered an unexpected error while ensuring
2681 : // the upload stream is cloneable. Check here and reset the interception
2682 : // if that happens.
2683 : nsresult status;
2684 0 : rv = channel->GetStatus(&status);
2685 0 : if (NS_WARN_IF(NS_FAILED(rv) || NS_FAILED(status))) {
2686 0 : HandleError();
2687 0 : return NS_OK;
2688 : }
2689 :
2690 0 : rv = mServiceWorkerPrivate->SendFetchEvent(mChannel, mLoadGroup,
2691 0 : mDocumentId, mIsReload);
2692 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2693 0 : HandleError();
2694 : }
2695 :
2696 0 : return NS_OK;
2697 : }
2698 : };
2699 :
2700 : } // anonymous namespace
2701 :
2702 : void
2703 0 : ServiceWorkerManager::DispatchFetchEvent(const OriginAttributes& aOriginAttributes,
2704 : nsIDocument* aDoc,
2705 : const nsAString& aDocumentIdForTopLevelNavigation,
2706 : nsIInterceptedChannel* aChannel,
2707 : bool aIsReload,
2708 : bool aIsSubresourceLoad,
2709 : ErrorResult& aRv)
2710 : {
2711 0 : MOZ_ASSERT(aChannel);
2712 0 : AssertIsOnMainThread();
2713 :
2714 0 : RefPtr<ServiceWorkerInfo> serviceWorker;
2715 0 : nsCOMPtr<nsILoadGroup> loadGroup;
2716 0 : nsAutoString documentId;
2717 :
2718 0 : if (aIsSubresourceLoad) {
2719 0 : MOZ_ASSERT(aDoc);
2720 :
2721 0 : serviceWorker = GetActiveWorkerInfoForDocument(aDoc);
2722 0 : if (!serviceWorker) {
2723 0 : aRv.Throw(NS_ERROR_FAILURE);
2724 0 : return;
2725 : }
2726 :
2727 0 : loadGroup = aDoc->GetDocumentLoadGroup();
2728 0 : nsresult rv = aDoc->GetOrCreateId(documentId);
2729 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2730 0 : return;
2731 : }
2732 : } else {
2733 0 : nsCOMPtr<nsIChannel> internalChannel;
2734 0 : aRv = aChannel->GetChannel(getter_AddRefs(internalChannel));
2735 0 : if (NS_WARN_IF(aRv.Failed())) {
2736 0 : return;
2737 : }
2738 :
2739 0 : internalChannel->GetLoadGroup(getter_AddRefs(loadGroup));
2740 :
2741 : // TODO: Use aDocumentIdForTopLevelNavigation for potentialClientId, pending
2742 : // the spec change.
2743 :
2744 0 : nsCOMPtr<nsIURI> uri;
2745 0 : aRv = aChannel->GetSecureUpgradedChannelURI(getter_AddRefs(uri));
2746 0 : if (NS_WARN_IF(aRv.Failed())) {
2747 0 : return;
2748 : }
2749 :
2750 : // non-subresource request means the URI contains the principal
2751 : nsCOMPtr<nsIPrincipal> principal =
2752 0 : BasePrincipal::CreateCodebasePrincipal(uri, aOriginAttributes);
2753 :
2754 : RefPtr<ServiceWorkerRegistrationInfo> registration =
2755 0 : GetServiceWorkerRegistrationInfo(principal, uri);
2756 0 : if (!registration) {
2757 0 : NS_WARNING("No registration found when dispatching the fetch event");
2758 0 : aRv.Throw(NS_ERROR_FAILURE);
2759 0 : return;
2760 : }
2761 :
2762 : // While we only enter this method if IsAvailable() previously saw
2763 : // an active worker, it is possible for that worker to be removed
2764 : // before we get to this point. Therefore we must handle a nullptr
2765 : // active worker here.
2766 0 : serviceWorker = registration->GetActive();
2767 0 : if (!serviceWorker) {
2768 0 : aRv.Throw(NS_ERROR_FAILURE);
2769 0 : return;
2770 : }
2771 :
2772 0 : AddNavigationInterception(serviceWorker->Scope(), aChannel);
2773 : }
2774 :
2775 0 : if (NS_WARN_IF(aRv.Failed())) {
2776 0 : return;
2777 : }
2778 :
2779 0 : MOZ_DIAGNOSTIC_ASSERT(serviceWorker);
2780 :
2781 : nsCOMPtr<nsIRunnable> continueRunnable =
2782 0 : new ContinueDispatchFetchEventRunnable(serviceWorker->WorkerPrivate(),
2783 : aChannel, loadGroup,
2784 0 : documentId, aIsReload);
2785 :
2786 : // When this service worker was registered, we also sent down the permissions
2787 : // for the runnable. They should have arrived by now, but we still need to
2788 : // wait for them if they have not.
2789 0 : nsCOMPtr<nsIRunnable> permissionsRunnable = NS_NewRunnableFunction(
2790 0 : "dom::workers::ServiceWorkerManager::DispatchFetchEvent", [=]() {
2791 0 : nsCOMPtr<nsIPermissionManager> permMgr = services::GetPermissionManager();
2792 0 : MOZ_ALWAYS_SUCCEEDS(permMgr->WhenPermissionsAvailable(serviceWorker->Principal(),
2793 : continueRunnable));
2794 0 : });
2795 :
2796 0 : nsCOMPtr<nsIChannel> innerChannel;
2797 0 : aRv = aChannel->GetChannel(getter_AddRefs(innerChannel));
2798 0 : if (NS_WARN_IF(aRv.Failed())) {
2799 0 : return;
2800 : }
2801 :
2802 0 : nsCOMPtr<nsIUploadChannel2> uploadChannel = do_QueryInterface(innerChannel);
2803 :
2804 : // If there is no upload stream, then continue immediately
2805 0 : if (!uploadChannel) {
2806 0 : MOZ_ALWAYS_SUCCEEDS(permissionsRunnable->Run());
2807 0 : return;
2808 : }
2809 : // Otherwise, ensure the upload stream can be cloned directly. This may
2810 : // require some async copying, so provide a callback.
2811 0 : aRv = uploadChannel->EnsureUploadStreamIsCloneable(permissionsRunnable);
2812 : }
2813 :
2814 : bool
2815 1 : ServiceWorkerManager::IsAvailable(nsIPrincipal* aPrincipal,
2816 : nsIURI* aURI)
2817 : {
2818 1 : MOZ_ASSERT(aPrincipal);
2819 1 : MOZ_ASSERT(aURI);
2820 :
2821 : RefPtr<ServiceWorkerRegistrationInfo> registration =
2822 2 : GetServiceWorkerRegistrationInfo(aPrincipal, aURI);
2823 2 : return registration && registration->GetActive();
2824 : }
2825 :
2826 : bool
2827 55 : ServiceWorkerManager::IsControlled(nsIDocument* aDoc, ErrorResult& aRv)
2828 : {
2829 55 : MOZ_ASSERT(aDoc);
2830 :
2831 55 : if (nsContentUtils::IsInPrivateBrowsing(aDoc)) {
2832 : // Handle the case where a service worker was previously registered in
2833 : // a non-private window (bug 1255621).
2834 0 : return false;
2835 : }
2836 :
2837 110 : RefPtr<ServiceWorkerRegistrationInfo> registration;
2838 55 : nsresult rv = GetDocumentRegistration(aDoc, getter_AddRefs(registration));
2839 55 : if (NS_WARN_IF(NS_FAILED(rv) && rv != NS_ERROR_NOT_AVAILABLE)) {
2840 : // It's OK to ignore the case where we don't have a registration.
2841 0 : aRv.Throw(rv);
2842 0 : return false;
2843 : }
2844 :
2845 55 : return !!registration;
2846 : }
2847 :
2848 : nsresult
2849 55 : ServiceWorkerManager::GetDocumentRegistration(nsIDocument* aDoc,
2850 : ServiceWorkerRegistrationInfo** aRegistrationInfo)
2851 : {
2852 110 : RefPtr<ServiceWorkerRegistrationInfo> registration;
2853 55 : if (!mControlledDocuments.Get(aDoc, getter_AddRefs(registration))) {
2854 55 : return NS_ERROR_NOT_AVAILABLE;
2855 : }
2856 :
2857 : // If the document is controlled, the current worker MUST be non-null.
2858 0 : if (!registration->GetActive()) {
2859 0 : return NS_ERROR_NOT_AVAILABLE;
2860 : }
2861 :
2862 0 : registration.forget(aRegistrationInfo);
2863 0 : return NS_OK;
2864 : }
2865 :
2866 : /*
2867 : * The .controller is for the registration associated with the document when
2868 : * the document was loaded.
2869 : */
2870 : NS_IMETHODIMP
2871 0 : ServiceWorkerManager::GetDocumentController(nsPIDOMWindowInner* aWindow,
2872 : nsISupports** aServiceWorker)
2873 : {
2874 0 : if (NS_WARN_IF(!aWindow)) {
2875 0 : return NS_ERROR_DOM_INVALID_STATE_ERR;
2876 : }
2877 :
2878 0 : nsCOMPtr<nsIDocument> doc = aWindow->GetExtantDoc();
2879 0 : if (!doc) {
2880 0 : return NS_ERROR_DOM_INVALID_STATE_ERR;
2881 : }
2882 :
2883 0 : RefPtr<ServiceWorkerRegistrationInfo> registration;
2884 0 : nsresult rv = GetDocumentRegistration(doc, getter_AddRefs(registration));
2885 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
2886 0 : return rv;
2887 : }
2888 :
2889 0 : MOZ_ASSERT(registration->GetActive());
2890 : RefPtr<ServiceWorker> serviceWorker =
2891 0 : registration->GetActive()->GetOrCreateInstance(aWindow);
2892 :
2893 0 : serviceWorker.forget(aServiceWorker);
2894 0 : return NS_OK;
2895 : }
2896 :
2897 : NS_IMETHODIMP
2898 0 : ServiceWorkerManager::GetInstalling(nsPIDOMWindowInner* aWindow,
2899 : const nsAString& aScope,
2900 : nsISupports** aServiceWorker)
2901 : {
2902 : return GetServiceWorkerForScope(aWindow, aScope,
2903 : WhichServiceWorker::INSTALLING_WORKER,
2904 0 : aServiceWorker);
2905 : }
2906 :
2907 : NS_IMETHODIMP
2908 0 : ServiceWorkerManager::GetWaiting(nsPIDOMWindowInner* aWindow,
2909 : const nsAString& aScope,
2910 : nsISupports** aServiceWorker)
2911 : {
2912 : return GetServiceWorkerForScope(aWindow, aScope,
2913 : WhichServiceWorker::WAITING_WORKER,
2914 0 : aServiceWorker);
2915 : }
2916 :
2917 : NS_IMETHODIMP
2918 0 : ServiceWorkerManager::GetActive(nsPIDOMWindowInner* aWindow,
2919 : const nsAString& aScope,
2920 : nsISupports** aServiceWorker)
2921 : {
2922 : return GetServiceWorkerForScope(aWindow, aScope,
2923 : WhichServiceWorker::ACTIVE_WORKER,
2924 0 : aServiceWorker);
2925 : }
2926 :
2927 : void
2928 0 : ServiceWorkerManager::TransitionServiceWorkerRegistrationWorker(ServiceWorkerRegistrationInfo* aRegistration,
2929 : WhichServiceWorker aWhichOne)
2930 : {
2931 0 : AssertIsOnMainThread();
2932 0 : nsTObserverArray<ServiceWorkerRegistrationListener*>::ForwardIterator it(mServiceWorkerRegistrationListeners);
2933 0 : while (it.HasMore()) {
2934 0 : RefPtr<ServiceWorkerRegistrationListener> target = it.GetNext();
2935 0 : nsAutoString regScope;
2936 0 : target->GetScope(regScope);
2937 0 : MOZ_ASSERT(!regScope.IsEmpty());
2938 :
2939 0 : NS_ConvertUTF16toUTF8 utf8Scope(regScope);
2940 :
2941 0 : if (utf8Scope.Equals(aRegistration->mScope)) {
2942 0 : target->TransitionWorker(aWhichOne);
2943 : }
2944 : }
2945 0 : }
2946 :
2947 : void
2948 0 : ServiceWorkerManager::InvalidateServiceWorkerRegistrationWorker(ServiceWorkerRegistrationInfo* aRegistration,
2949 : WhichServiceWorker aWhichOnes)
2950 : {
2951 0 : AssertIsOnMainThread();
2952 0 : nsTObserverArray<ServiceWorkerRegistrationListener*>::ForwardIterator it(mServiceWorkerRegistrationListeners);
2953 0 : while (it.HasMore()) {
2954 0 : RefPtr<ServiceWorkerRegistrationListener> target = it.GetNext();
2955 0 : nsAutoString regScope;
2956 0 : target->GetScope(regScope);
2957 0 : MOZ_ASSERT(!regScope.IsEmpty());
2958 :
2959 0 : NS_ConvertUTF16toUTF8 utf8Scope(regScope);
2960 :
2961 0 : if (utf8Scope.Equals(aRegistration->mScope)) {
2962 0 : target->InvalidateWorkers(aWhichOnes);
2963 : }
2964 : }
2965 0 : }
2966 :
2967 : void
2968 0 : ServiceWorkerManager::NotifyServiceWorkerRegistrationRemoved(ServiceWorkerRegistrationInfo* aRegistration)
2969 : {
2970 0 : AssertIsOnMainThread();
2971 0 : nsTObserverArray<ServiceWorkerRegistrationListener*>::ForwardIterator it(mServiceWorkerRegistrationListeners);
2972 0 : while (it.HasMore()) {
2973 0 : RefPtr<ServiceWorkerRegistrationListener> target = it.GetNext();
2974 0 : nsAutoString regScope;
2975 0 : target->GetScope(regScope);
2976 0 : MOZ_ASSERT(!regScope.IsEmpty());
2977 :
2978 0 : NS_ConvertUTF16toUTF8 utf8Scope(regScope);
2979 :
2980 0 : if (utf8Scope.Equals(aRegistration->mScope)) {
2981 0 : target->RegistrationRemoved();
2982 : }
2983 : }
2984 0 : }
2985 :
2986 : void
2987 0 : ServiceWorkerManager::SoftUpdate(const OriginAttributes& aOriginAttributes,
2988 : const nsACString& aScope)
2989 : {
2990 0 : AssertIsOnMainThread();
2991 :
2992 0 : if (mShuttingDown) {
2993 0 : return;
2994 : }
2995 :
2996 0 : if (!mActor) {
2997 : RefPtr<Runnable> runnable =
2998 0 : new SoftUpdateRunnable(aOriginAttributes, aScope, false, nullptr);
2999 0 : AppendPendingOperation(runnable);
3000 0 : return;
3001 : }
3002 :
3003 : RefPtr<GenericPromise::Private> promise =
3004 0 : new GenericPromise::Private(__func__);
3005 :
3006 : RefPtr<CancelableRunnable> successRunnable =
3007 0 : new SoftUpdateRunnable(aOriginAttributes, aScope, true, promise);
3008 :
3009 : RefPtr<CancelableRunnable> failureRunnable =
3010 0 : new ResolvePromiseRunnable(promise);
3011 :
3012 : ServiceWorkerUpdaterChild* actor =
3013 0 : new ServiceWorkerUpdaterChild(promise, successRunnable, failureRunnable);
3014 :
3015 0 : mActor->SendPServiceWorkerUpdaterConstructor(actor, aOriginAttributes,
3016 0 : nsCString(aScope));
3017 : }
3018 :
3019 : namespace {
3020 :
3021 : class UpdateJobCallback final : public ServiceWorkerJob::Callback
3022 : {
3023 : RefPtr<ServiceWorkerUpdateFinishCallback> mCallback;
3024 :
3025 0 : ~UpdateJobCallback() = default;
3026 :
3027 : public:
3028 0 : explicit UpdateJobCallback(ServiceWorkerUpdateFinishCallback* aCallback)
3029 0 : : mCallback(aCallback)
3030 : {
3031 0 : AssertIsOnMainThread();
3032 0 : MOZ_ASSERT(mCallback);
3033 0 : }
3034 :
3035 : void
3036 0 : JobFinished(ServiceWorkerJob* aJob, ErrorResult& aStatus)
3037 : {
3038 0 : AssertIsOnMainThread();
3039 0 : MOZ_ASSERT(aJob);
3040 :
3041 0 : if (aStatus.Failed()) {
3042 0 : mCallback->UpdateFailed(aStatus);
3043 0 : return;
3044 : }
3045 :
3046 0 : MOZ_DIAGNOSTIC_ASSERT(aJob->GetType() == ServiceWorkerJob::Type::Update);
3047 : RefPtr<ServiceWorkerUpdateJob> updateJob =
3048 0 : static_cast<ServiceWorkerUpdateJob*>(aJob);
3049 0 : RefPtr<ServiceWorkerRegistrationInfo> reg = updateJob->GetRegistration();
3050 0 : mCallback->UpdateSucceeded(reg);
3051 : }
3052 :
3053 0 : NS_INLINE_DECL_REFCOUNTING(UpdateJobCallback)
3054 : };
3055 :
3056 : } // anonymous namespace
3057 :
3058 : void
3059 0 : ServiceWorkerManager::SoftUpdateInternal(const OriginAttributes& aOriginAttributes,
3060 : const nsACString& aScope,
3061 : ServiceWorkerUpdateFinishCallback* aCallback)
3062 : {
3063 0 : AssertIsOnMainThread();
3064 0 : MOZ_ASSERT(aCallback);
3065 :
3066 0 : if (mShuttingDown) {
3067 0 : return;
3068 : }
3069 :
3070 0 : nsCOMPtr<nsIURI> scopeURI;
3071 0 : nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), aScope);
3072 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3073 0 : return;
3074 : }
3075 :
3076 : nsCOMPtr<nsIPrincipal> principal =
3077 0 : BasePrincipal::CreateCodebasePrincipal(scopeURI, aOriginAttributes);
3078 0 : if (NS_WARN_IF(!principal)) {
3079 0 : return;
3080 : }
3081 :
3082 0 : nsAutoCString scopeKey;
3083 0 : rv = PrincipalToScopeKey(principal, scopeKey);
3084 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3085 0 : return;
3086 : }
3087 :
3088 : RefPtr<ServiceWorkerRegistrationInfo> registration =
3089 0 : GetRegistration(scopeKey, aScope);
3090 0 : if (NS_WARN_IF(!registration)) {
3091 0 : return;
3092 : }
3093 :
3094 : // "If registration's uninstalling flag is set, abort these steps."
3095 0 : if (registration->mPendingUninstall) {
3096 0 : return;
3097 : }
3098 :
3099 : // "If registration's installing worker is not null, abort these steps."
3100 0 : if (registration->GetInstalling()) {
3101 0 : return;
3102 : }
3103 :
3104 : // "Let newestWorker be the result of running Get Newest Worker algorithm
3105 : // passing registration as its argument.
3106 : // If newestWorker is null, abort these steps."
3107 0 : RefPtr<ServiceWorkerInfo> newest = registration->Newest();
3108 0 : if (!newest) {
3109 0 : return;
3110 : }
3111 :
3112 : // "If the registration queue for registration is empty, invoke Update algorithm,
3113 : // or its equivalent, with client, registration as its argument."
3114 : // TODO(catalinb): We don't implement the force bypass cache flag.
3115 : // See: https://github.com/slightlyoff/ServiceWorker/issues/759
3116 0 : RefPtr<ServiceWorkerJobQueue> queue = GetOrCreateJobQueue(scopeKey,
3117 0 : aScope);
3118 :
3119 : RefPtr<ServiceWorkerUpdateJob> job =
3120 0 : new ServiceWorkerUpdateJob(principal, registration->mScope,
3121 0 : newest->ScriptSpec(), nullptr,
3122 0 : registration->GetLoadFlags());
3123 :
3124 0 : RefPtr<UpdateJobCallback> cb = new UpdateJobCallback(aCallback);
3125 0 : job->AppendResultCallback(cb);
3126 :
3127 0 : queue->ScheduleJob(job);
3128 : }
3129 :
3130 : void
3131 0 : ServiceWorkerManager::Update(nsIPrincipal* aPrincipal,
3132 : const nsACString& aScope,
3133 : ServiceWorkerUpdateFinishCallback* aCallback)
3134 : {
3135 0 : AssertIsOnMainThread();
3136 :
3137 0 : if (!mActor) {
3138 : RefPtr<Runnable> runnable =
3139 : new UpdateRunnable(aPrincipal, aScope, aCallback,
3140 0 : UpdateRunnable::ePostpone, nullptr);
3141 0 : AppendPendingOperation(runnable);
3142 0 : return;
3143 : }
3144 :
3145 : RefPtr<GenericPromise::Private> promise =
3146 0 : new GenericPromise::Private(__func__);
3147 :
3148 : RefPtr<CancelableRunnable> successRunnable =
3149 : new UpdateRunnable(aPrincipal, aScope, aCallback,
3150 0 : UpdateRunnable::eSuccess, promise);
3151 :
3152 : RefPtr<CancelableRunnable> failureRunnable =
3153 : new UpdateRunnable(aPrincipal, aScope, aCallback,
3154 0 : UpdateRunnable::eFailure, promise);
3155 :
3156 : ServiceWorkerUpdaterChild* actor =
3157 0 : new ServiceWorkerUpdaterChild(promise, successRunnable, failureRunnable);
3158 :
3159 0 : mActor->SendPServiceWorkerUpdaterConstructor(actor,
3160 0 : aPrincipal->OriginAttributesRef(),
3161 0 : nsCString(aScope));
3162 : }
3163 :
3164 : void
3165 0 : ServiceWorkerManager::UpdateInternal(nsIPrincipal* aPrincipal,
3166 : const nsACString& aScope,
3167 : ServiceWorkerUpdateFinishCallback* aCallback)
3168 : {
3169 0 : MOZ_ASSERT(aPrincipal);
3170 0 : MOZ_ASSERT(aCallback);
3171 :
3172 0 : nsAutoCString scopeKey;
3173 0 : nsresult rv = PrincipalToScopeKey(aPrincipal, scopeKey);
3174 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3175 0 : return;
3176 : }
3177 :
3178 : RefPtr<ServiceWorkerRegistrationInfo> registration =
3179 0 : GetRegistration(scopeKey, aScope);
3180 0 : if (NS_WARN_IF(!registration)) {
3181 0 : return;
3182 : }
3183 :
3184 : // "Let newestWorker be the result of running Get Newest Worker algorithm
3185 : // passing registration as its argument.
3186 : // If newestWorker is null, return a promise rejected with "InvalidStateError"
3187 0 : RefPtr<ServiceWorkerInfo> newest = registration->Newest();
3188 0 : if (!newest) {
3189 0 : ErrorResult error(NS_ERROR_DOM_INVALID_STATE_ERR);
3190 0 : aCallback->UpdateFailed(error);
3191 :
3192 : // In case the callback does not consume the exception
3193 0 : error.SuppressException();
3194 :
3195 0 : return;
3196 : }
3197 :
3198 0 : RefPtr<ServiceWorkerJobQueue> queue = GetOrCreateJobQueue(scopeKey, aScope);
3199 :
3200 : // "Invoke Update algorithm, or its equivalent, with client, registration as
3201 : // its argument."
3202 : RefPtr<ServiceWorkerUpdateJob> job =
3203 0 : new ServiceWorkerUpdateJob(aPrincipal, registration->mScope,
3204 0 : newest->ScriptSpec(), nullptr,
3205 0 : registration->GetLoadFlags());
3206 :
3207 0 : RefPtr<UpdateJobCallback> cb = new UpdateJobCallback(aCallback);
3208 0 : job->AppendResultCallback(cb);
3209 :
3210 0 : queue->ScheduleJob(job);
3211 : }
3212 :
3213 : namespace {
3214 :
3215 : static void
3216 0 : FireControllerChangeOnDocument(nsIDocument* aDocument)
3217 : {
3218 0 : AssertIsOnMainThread();
3219 0 : MOZ_ASSERT(aDocument);
3220 :
3221 0 : nsCOMPtr<nsPIDOMWindowInner> w = aDocument->GetInnerWindow();
3222 0 : if (!w) {
3223 0 : NS_WARNING("Failed to dispatch controllerchange event");
3224 0 : return;
3225 : }
3226 :
3227 0 : auto* window = nsGlobalWindow::Cast(w.get());
3228 0 : dom::Navigator* navigator = window->Navigator();
3229 0 : if (!navigator) {
3230 0 : return;
3231 : }
3232 :
3233 0 : RefPtr<ServiceWorkerContainer> container = navigator->ServiceWorker();
3234 0 : ErrorResult result;
3235 0 : container->ControllerChanged(result);
3236 0 : if (result.Failed()) {
3237 0 : NS_WARNING("Failed to dispatch controllerchange event");
3238 : }
3239 : }
3240 :
3241 : } // anonymous namespace
3242 :
3243 : UniquePtr<ServiceWorkerClientInfo>
3244 0 : ServiceWorkerManager::GetClient(nsIPrincipal* aPrincipal,
3245 : const nsAString& aClientId,
3246 : ErrorResult& aRv)
3247 : {
3248 0 : UniquePtr<ServiceWorkerClientInfo> clientInfo;
3249 : nsCOMPtr<nsISupportsInterfacePointer> ifptr =
3250 0 : do_CreateInstance(NS_SUPPORTS_INTERFACE_POINTER_CONTRACTID);
3251 0 : if (NS_WARN_IF(!ifptr)) {
3252 0 : return clientInfo;
3253 : }
3254 0 : nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
3255 0 : if (NS_WARN_IF(!obs)) {
3256 0 : return clientInfo;
3257 : }
3258 :
3259 0 : nsresult rv = obs->NotifyObservers(ifptr, "service-worker-get-client",
3260 0 : PromiseFlatString(aClientId).get());
3261 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3262 0 : return clientInfo;
3263 : }
3264 :
3265 0 : nsCOMPtr<nsISupports> ptr;
3266 0 : ifptr->GetData(getter_AddRefs(ptr));
3267 0 : nsCOMPtr<nsIDocument> doc = do_QueryInterface(ptr);
3268 0 : if (NS_WARN_IF(!doc)) {
3269 0 : return clientInfo;
3270 : }
3271 :
3272 0 : bool equals = false;
3273 0 : aPrincipal->Equals(doc->NodePrincipal(), &equals);
3274 0 : if (!equals) {
3275 0 : return clientInfo;
3276 : }
3277 :
3278 0 : if (!IsFromAuthenticatedOrigin(doc)) {
3279 0 : aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
3280 0 : return clientInfo;
3281 : }
3282 :
3283 0 : clientInfo.reset(new ServiceWorkerClientInfo(doc));
3284 0 : return clientInfo;
3285 : }
3286 :
3287 : void
3288 0 : ServiceWorkerManager::GetAllClients(nsIPrincipal* aPrincipal,
3289 : const nsCString& aScope,
3290 : uint64_t aServiceWorkerID,
3291 : bool aIncludeUncontrolled,
3292 : nsTArray<ServiceWorkerClientInfo>& aDocuments)
3293 : {
3294 0 : MOZ_ASSERT(aPrincipal);
3295 :
3296 : RefPtr<ServiceWorkerRegistrationInfo> registration =
3297 0 : GetRegistration(aPrincipal, aScope);
3298 :
3299 0 : if (!registration) {
3300 : // The registration was removed, leave the array empty.
3301 0 : return;
3302 : }
3303 :
3304 0 : nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
3305 0 : if (NS_WARN_IF(!obs)) {
3306 0 : return;
3307 : }
3308 :
3309 0 : nsCOMPtr<nsISimpleEnumerator> enumerator;
3310 0 : nsresult rv = obs->EnumerateObservers("service-worker-get-client",
3311 0 : getter_AddRefs(enumerator));
3312 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3313 0 : return;
3314 : }
3315 :
3316 : // Get a list of Client documents out of the observer service
3317 0 : AutoTArray<nsCOMPtr<nsIDocument>, 32> docList;
3318 0 : bool loop = true;
3319 0 : while (NS_SUCCEEDED(enumerator->HasMoreElements(&loop)) && loop) {
3320 0 : nsCOMPtr<nsISupports> ptr;
3321 0 : rv = enumerator->GetNext(getter_AddRefs(ptr));
3322 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3323 0 : continue;
3324 : }
3325 :
3326 0 : nsCOMPtr<nsIDocument> doc = do_QueryInterface(ptr);
3327 0 : if (!doc || !doc->GetWindow()) {
3328 0 : continue;
3329 : }
3330 :
3331 0 : bool equals = false;
3332 0 : Unused << aPrincipal->Equals(doc->NodePrincipal(), &equals);
3333 0 : if (!equals) {
3334 0 : continue;
3335 : }
3336 :
3337 : // Treat http windows with devtools opened as secure if the correct devtools
3338 : // setting is enabled.
3339 0 : if (!doc->GetWindow()->GetServiceWorkersTestingEnabled() &&
3340 0 : !Preferences::GetBool("dom.serviceWorkers.testing.enabled") &&
3341 0 : !IsFromAuthenticatedOrigin(doc)) {
3342 0 : continue;
3343 : }
3344 :
3345 : // If we are only returning controlled Clients then skip any documents
3346 : // that are for different registrations. We also skip service workers
3347 : // that don't match the ID of our calling service worker. We should
3348 : // only return Clients controlled by that precise service worker.
3349 0 : if (!aIncludeUncontrolled) {
3350 0 : ServiceWorkerRegistrationInfo* reg = mControlledDocuments.GetWeak(doc);
3351 0 : if (!reg || reg->mScope != aScope || !reg->GetActive() ||
3352 0 : reg->GetActive()->ID() != aServiceWorkerID) {
3353 0 : continue;
3354 : }
3355 : }
3356 :
3357 0 : if (!aIncludeUncontrolled && !mControlledDocuments.Contains(doc)) {
3358 0 : continue;
3359 : }
3360 :
3361 0 : docList.AppendElement(doc.forget());
3362 : }
3363 :
3364 : // The observer service gives us the list in reverse creation order.
3365 : // We need to maintain creation order, so reverse the list before
3366 : // processing.
3367 0 : docList.Reverse();
3368 :
3369 : // Finally convert to the list of ServiceWorkerClientInfo objects.
3370 0 : uint32_t ordinal = 0;
3371 0 : for (uint32_t i = 0; i < docList.Length(); ++i) {
3372 0 : aDocuments.AppendElement(ServiceWorkerClientInfo(docList[i], ordinal));
3373 0 : ordinal += 1;
3374 : }
3375 :
3376 0 : aDocuments.Sort();
3377 : }
3378 :
3379 : void
3380 0 : ServiceWorkerManager::MaybeClaimClient(nsIDocument* aDocument,
3381 : ServiceWorkerRegistrationInfo* aWorkerRegistration)
3382 : {
3383 0 : MOZ_ASSERT(aWorkerRegistration);
3384 0 : MOZ_ASSERT(aWorkerRegistration->GetActive());
3385 :
3386 : // Same origin check
3387 0 : if (!aWorkerRegistration->mPrincipal->Equals(aDocument->NodePrincipal())) {
3388 0 : return;
3389 : }
3390 :
3391 : // The registration that should be controlling the client
3392 : RefPtr<ServiceWorkerRegistrationInfo> matchingRegistration =
3393 0 : GetServiceWorkerRegistrationInfo(aDocument);
3394 :
3395 : // The registration currently controlling the client
3396 0 : RefPtr<ServiceWorkerRegistrationInfo> controllingRegistration;
3397 0 : GetDocumentRegistration(aDocument, getter_AddRefs(controllingRegistration));
3398 :
3399 0 : if (aWorkerRegistration != matchingRegistration ||
3400 0 : aWorkerRegistration == controllingRegistration) {
3401 0 : return;
3402 : }
3403 :
3404 0 : if (controllingRegistration) {
3405 0 : StopControllingADocument(controllingRegistration);
3406 : }
3407 :
3408 0 : StartControllingADocument(aWorkerRegistration, aDocument, NS_LITERAL_STRING(""));
3409 0 : FireControllerChangeOnDocument(aDocument);
3410 : }
3411 :
3412 : nsresult
3413 0 : ServiceWorkerManager::ClaimClients(nsIPrincipal* aPrincipal,
3414 : const nsCString& aScope, uint64_t aId)
3415 : {
3416 : RefPtr<ServiceWorkerRegistrationInfo> registration =
3417 0 : GetRegistration(aPrincipal, aScope);
3418 :
3419 0 : if (!registration || !registration->GetActive() ||
3420 0 : !(registration->GetActive()->ID() == aId)) {
3421 : // The worker is not active.
3422 0 : return NS_ERROR_DOM_INVALID_STATE_ERR;
3423 : }
3424 :
3425 0 : nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
3426 0 : if (NS_WARN_IF(!obs)) {
3427 0 : return NS_ERROR_FAILURE;
3428 : }
3429 :
3430 0 : nsCOMPtr<nsISimpleEnumerator> enumerator;
3431 0 : nsresult rv = obs->EnumerateObservers("service-worker-get-client",
3432 0 : getter_AddRefs(enumerator));
3433 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3434 0 : return rv;
3435 : }
3436 :
3437 0 : bool loop = true;
3438 0 : while (NS_SUCCEEDED(enumerator->HasMoreElements(&loop)) && loop) {
3439 0 : nsCOMPtr<nsISupports> ptr;
3440 0 : rv = enumerator->GetNext(getter_AddRefs(ptr));
3441 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3442 0 : continue;
3443 : }
3444 :
3445 0 : nsCOMPtr<nsIDocument> doc = do_QueryInterface(ptr);
3446 0 : MaybeClaimClient(doc, registration);
3447 : }
3448 :
3449 0 : return NS_OK;
3450 : }
3451 :
3452 : void
3453 0 : ServiceWorkerManager::SetSkipWaitingFlag(nsIPrincipal* aPrincipal,
3454 : const nsCString& aScope,
3455 : uint64_t aServiceWorkerID)
3456 : {
3457 : RefPtr<ServiceWorkerRegistrationInfo> registration =
3458 0 : GetRegistration(aPrincipal, aScope);
3459 0 : if (NS_WARN_IF(!registration)) {
3460 0 : return;
3461 : }
3462 :
3463 : RefPtr<ServiceWorkerInfo> worker =
3464 0 : registration->GetServiceWorkerInfoById(aServiceWorkerID);
3465 :
3466 0 : if (NS_WARN_IF(!worker)) {
3467 0 : return;
3468 : }
3469 :
3470 0 : worker->SetSkipWaitingFlag();
3471 :
3472 0 : if (worker->State() == ServiceWorkerState::Installed) {
3473 0 : registration->TryToActivateAsync();
3474 : }
3475 : }
3476 :
3477 : void
3478 0 : ServiceWorkerManager::FireControllerChange(ServiceWorkerRegistrationInfo* aRegistration)
3479 : {
3480 0 : AssertIsOnMainThread();
3481 0 : for (auto iter = mControlledDocuments.Iter(); !iter.Done(); iter.Next()) {
3482 0 : if (iter.UserData() != aRegistration) {
3483 0 : continue;
3484 : }
3485 :
3486 0 : nsCOMPtr<nsIDocument> doc = do_QueryInterface(iter.Key());
3487 0 : if (NS_WARN_IF(!doc)) {
3488 0 : continue;
3489 : }
3490 :
3491 0 : FireControllerChangeOnDocument(doc);
3492 : }
3493 0 : }
3494 :
3495 : already_AddRefed<ServiceWorkerRegistrationInfo>
3496 0 : ServiceWorkerManager::GetRegistration(nsIPrincipal* aPrincipal,
3497 : const nsACString& aScope) const
3498 : {
3499 0 : MOZ_ASSERT(aPrincipal);
3500 :
3501 0 : nsAutoCString scopeKey;
3502 0 : nsresult rv = PrincipalToScopeKey(aPrincipal, scopeKey);
3503 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3504 0 : return nullptr;
3505 : }
3506 :
3507 0 : return GetRegistration(scopeKey, aScope);
3508 : }
3509 :
3510 : NS_IMETHODIMP
3511 0 : ServiceWorkerManager::GetRegistrationByPrincipal(nsIPrincipal* aPrincipal,
3512 : const nsAString& aScope,
3513 : nsIServiceWorkerRegistrationInfo** aInfo)
3514 : {
3515 0 : MOZ_ASSERT(aPrincipal);
3516 0 : MOZ_ASSERT(aInfo);
3517 :
3518 0 : nsCOMPtr<nsIURI> scopeURI;
3519 0 : nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), aScope, nullptr, nullptr);
3520 0 : if (NS_FAILED(rv)) {
3521 0 : return NS_ERROR_FAILURE;
3522 : }
3523 :
3524 : RefPtr<ServiceWorkerRegistrationInfo> info =
3525 0 : GetServiceWorkerRegistrationInfo(aPrincipal, scopeURI);
3526 0 : if (!info) {
3527 0 : return NS_ERROR_FAILURE;
3528 : }
3529 0 : info.forget(aInfo);
3530 :
3531 0 : return NS_OK;
3532 : }
3533 :
3534 : already_AddRefed<ServiceWorkerRegistrationInfo>
3535 0 : ServiceWorkerManager::GetRegistration(const nsACString& aScopeKey,
3536 : const nsACString& aScope) const
3537 : {
3538 0 : RefPtr<ServiceWorkerRegistrationInfo> reg;
3539 :
3540 : RegistrationDataPerPrincipal* data;
3541 0 : if (!mRegistrationInfos.Get(aScopeKey, &data)) {
3542 0 : return reg.forget();
3543 : }
3544 :
3545 0 : data->mInfos.Get(aScope, getter_AddRefs(reg));
3546 0 : return reg.forget();
3547 : }
3548 :
3549 : already_AddRefed<ServiceWorkerRegistrationInfo>
3550 0 : ServiceWorkerManager::CreateNewRegistration(const nsCString& aScope,
3551 : nsIPrincipal* aPrincipal,
3552 : nsLoadFlags aLoadFlags)
3553 : {
3554 : #ifdef DEBUG
3555 0 : AssertIsOnMainThread();
3556 0 : nsCOMPtr<nsIURI> scopeURI;
3557 0 : nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), aScope, nullptr, nullptr);
3558 0 : MOZ_ASSERT(NS_SUCCEEDED(rv));
3559 :
3560 : RefPtr<ServiceWorkerRegistrationInfo> tmp =
3561 0 : GetRegistration(aPrincipal, aScope);
3562 0 : MOZ_ASSERT(!tmp);
3563 : #endif
3564 :
3565 : RefPtr<ServiceWorkerRegistrationInfo> registration =
3566 0 : new ServiceWorkerRegistrationInfo(aScope, aPrincipal, aLoadFlags);
3567 : // From now on ownership of registration is with
3568 : // mServiceWorkerRegistrationInfos.
3569 0 : AddScopeAndRegistration(aScope, registration);
3570 0 : return registration.forget();
3571 : }
3572 :
3573 : void
3574 0 : ServiceWorkerManager::MaybeRemoveRegistration(ServiceWorkerRegistrationInfo* aRegistration)
3575 : {
3576 0 : MOZ_ASSERT(aRegistration);
3577 0 : RefPtr<ServiceWorkerInfo> newest = aRegistration->Newest();
3578 0 : if (!newest && HasScope(aRegistration->mPrincipal, aRegistration->mScope)) {
3579 0 : RemoveRegistration(aRegistration);
3580 : }
3581 0 : }
3582 :
3583 : void
3584 0 : ServiceWorkerManager::RemoveRegistration(ServiceWorkerRegistrationInfo* aRegistration)
3585 : {
3586 : // Note, we do not need to call mActor->SendUnregister() here. There are a few
3587 : // ways we can get here:
3588 : // 1) Through a normal unregister which calls SendUnregister() in the unregister
3589 : // job Start() method.
3590 : // 2) Through origin storage being purged. These result in ForceUnregister()
3591 : // starting unregister jobs which in turn call SendUnregister().
3592 : // 3) Through the failure to install a new service worker. Since we don't store
3593 : // the registration until install succeeds, we do not need to call
3594 : // SendUnregister here.
3595 : // Assert these conditions by testing for pending uninstall (cases 1 and 2) or
3596 : // null workers (case 3).
3597 : #ifdef DEBUG
3598 0 : RefPtr<ServiceWorkerInfo> newest = aRegistration->Newest();
3599 0 : MOZ_ASSERT(aRegistration->mPendingUninstall || !newest);
3600 : #endif
3601 :
3602 0 : MOZ_ASSERT(HasScope(aRegistration->mPrincipal, aRegistration->mScope));
3603 :
3604 : // When a registration is removed, we must clear its contents since the DOM
3605 : // object may be held by content script.
3606 0 : aRegistration->Clear();
3607 :
3608 0 : RemoveScopeAndRegistration(aRegistration);
3609 0 : }
3610 :
3611 : namespace {
3612 : /**
3613 : * See toolkit/modules/sessionstore/Utils.jsm function hasRootDomain().
3614 : *
3615 : * Returns true if the |url| passed in is part of the given root |domain|.
3616 : * For example, if |url| is "www.mozilla.org", and we pass in |domain| as
3617 : * "mozilla.org", this will return true. It would return false the other way
3618 : * around.
3619 : */
3620 : bool
3621 0 : HasRootDomain(nsIURI* aURI, const nsACString& aDomain)
3622 : {
3623 0 : AssertIsOnMainThread();
3624 0 : MOZ_ASSERT(aURI);
3625 :
3626 0 : nsAutoCString host;
3627 0 : nsresult rv = aURI->GetHost(host);
3628 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3629 0 : return false;
3630 : }
3631 :
3632 0 : nsACString::const_iterator start, end;
3633 0 : host.BeginReading(start);
3634 0 : host.EndReading(end);
3635 0 : if (!FindInReadable(aDomain, start, end)) {
3636 0 : return false;
3637 : }
3638 :
3639 0 : if (host.Equals(aDomain)) {
3640 0 : return true;
3641 : }
3642 :
3643 : // Beginning of the string matches, can't look at the previous char.
3644 0 : if (start.get() == host.BeginReading()) {
3645 : // Equals failed so this is fine.
3646 0 : return false;
3647 : }
3648 :
3649 0 : char prevChar = *(--start);
3650 0 : return prevChar == '.';
3651 : }
3652 :
3653 : } // namespace
3654 :
3655 : NS_IMETHODIMP
3656 0 : ServiceWorkerManager::GetAllRegistrations(nsIArray** aResult)
3657 : {
3658 0 : AssertIsOnMainThread();
3659 :
3660 0 : nsCOMPtr<nsIMutableArray> array(do_CreateInstance(NS_ARRAY_CONTRACTID));
3661 0 : if (!array) {
3662 0 : return NS_ERROR_OUT_OF_MEMORY;
3663 : }
3664 :
3665 0 : for (auto it1 = mRegistrationInfos.Iter(); !it1.Done(); it1.Next()) {
3666 0 : for (auto it2 = it1.UserData()->mInfos.Iter(); !it2.Done(); it2.Next()) {
3667 0 : ServiceWorkerRegistrationInfo* reg = it2.UserData();
3668 0 : MOZ_ASSERT(reg);
3669 :
3670 0 : if (reg->mPendingUninstall) {
3671 0 : continue;
3672 : }
3673 :
3674 0 : array->AppendElement(reg, false);
3675 : }
3676 : }
3677 :
3678 0 : array.forget(aResult);
3679 0 : return NS_OK;
3680 : }
3681 :
3682 : // MUST ONLY BE CALLED FROM Remove(), RemoveAll() and RemoveAllRegistrations()!
3683 : void
3684 0 : ServiceWorkerManager::ForceUnregister(RegistrationDataPerPrincipal* aRegistrationData,
3685 : ServiceWorkerRegistrationInfo* aRegistration)
3686 : {
3687 0 : MOZ_ASSERT(aRegistrationData);
3688 0 : MOZ_ASSERT(aRegistration);
3689 :
3690 0 : RefPtr<ServiceWorkerJobQueue> queue;
3691 0 : aRegistrationData->mJobQueues.Get(aRegistration->mScope, getter_AddRefs(queue));
3692 0 : if (queue) {
3693 0 : queue->CancelAll();
3694 : }
3695 :
3696 0 : if (auto entry = aRegistrationData->mUpdateTimers.Lookup(aRegistration->mScope)) {
3697 0 : entry.Data()->Cancel();
3698 0 : entry.Remove();
3699 : }
3700 :
3701 : // Since Unregister is async, it is ok to call it in an enumeration.
3702 0 : Unregister(aRegistration->mPrincipal, nullptr, NS_ConvertUTF8toUTF16(aRegistration->mScope));
3703 0 : }
3704 :
3705 : NS_IMETHODIMP
3706 0 : ServiceWorkerManager::RemoveAndPropagate(const nsACString& aHost)
3707 : {
3708 0 : Remove(aHost);
3709 0 : PropagateRemove(aHost);
3710 0 : return NS_OK;
3711 : }
3712 :
3713 : void
3714 0 : ServiceWorkerManager::Remove(const nsACString& aHost)
3715 : {
3716 0 : AssertIsOnMainThread();
3717 :
3718 : // We need to postpone this operation in case we don't have an actor because
3719 : // this is needed by the ForceUnregister.
3720 0 : if (!mActor) {
3721 0 : RefPtr<nsIRunnable> runnable = new RemoveRunnable(aHost);
3722 0 : AppendPendingOperation(runnable);
3723 0 : return;
3724 : }
3725 :
3726 0 : for (auto it1 = mRegistrationInfos.Iter(); !it1.Done(); it1.Next()) {
3727 0 : ServiceWorkerManager::RegistrationDataPerPrincipal* data = it1.UserData();
3728 0 : for (auto it2 = data->mInfos.Iter(); !it2.Done(); it2.Next()) {
3729 0 : ServiceWorkerRegistrationInfo* reg = it2.UserData();
3730 0 : nsCOMPtr<nsIURI> scopeURI;
3731 0 : nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), it2.Key(),
3732 0 : nullptr, nullptr);
3733 : // This way subdomains are also cleared.
3734 0 : if (NS_SUCCEEDED(rv) && HasRootDomain(scopeURI, aHost)) {
3735 0 : ForceUnregister(data, reg);
3736 : }
3737 : }
3738 : }
3739 : }
3740 :
3741 : void
3742 0 : ServiceWorkerManager::PropagateRemove(const nsACString& aHost)
3743 : {
3744 0 : AssertIsOnMainThread();
3745 :
3746 0 : if (!mActor) {
3747 0 : RefPtr<nsIRunnable> runnable = new PropagateRemoveRunnable(aHost);
3748 0 : AppendPendingOperation(runnable);
3749 0 : return;
3750 : }
3751 :
3752 0 : mActor->SendPropagateRemove(nsCString(aHost));
3753 : }
3754 :
3755 : void
3756 0 : ServiceWorkerManager::RemoveAll()
3757 : {
3758 0 : AssertIsOnMainThread();
3759 :
3760 0 : for (auto it1 = mRegistrationInfos.Iter(); !it1.Done(); it1.Next()) {
3761 0 : ServiceWorkerManager::RegistrationDataPerPrincipal* data = it1.UserData();
3762 0 : for (auto it2 = data->mInfos.Iter(); !it2.Done(); it2.Next()) {
3763 0 : ServiceWorkerRegistrationInfo* reg = it2.UserData();
3764 0 : ForceUnregister(data, reg);
3765 : }
3766 : }
3767 0 : }
3768 :
3769 : void
3770 0 : ServiceWorkerManager::PropagateRemoveAll()
3771 : {
3772 0 : AssertIsOnMainThread();
3773 0 : MOZ_ASSERT(XRE_IsParentProcess());
3774 :
3775 0 : if (!mActor) {
3776 0 : RefPtr<nsIRunnable> runnable = new PropagateRemoveAllRunnable();
3777 0 : AppendPendingOperation(runnable);
3778 0 : return;
3779 : }
3780 :
3781 0 : mActor->SendPropagateRemoveAll();
3782 : }
3783 :
3784 : void
3785 0 : ServiceWorkerManager::RemoveAllRegistrations(OriginAttributesPattern* aPattern)
3786 : {
3787 0 : AssertIsOnMainThread();
3788 :
3789 0 : MOZ_ASSERT(aPattern);
3790 :
3791 0 : for (auto it1 = mRegistrationInfos.Iter(); !it1.Done(); it1.Next()) {
3792 0 : ServiceWorkerManager::RegistrationDataPerPrincipal* data = it1.UserData();
3793 :
3794 : // We can use iteration because ForceUnregister (and Unregister) are
3795 : // async. Otherwise doing some R/W operations on an hashtable during
3796 : // iteration will crash.
3797 0 : for (auto it2 = data->mInfos.Iter(); !it2.Done(); it2.Next()) {
3798 0 : ServiceWorkerRegistrationInfo* reg = it2.UserData();
3799 :
3800 0 : MOZ_ASSERT(reg);
3801 0 : MOZ_ASSERT(reg->mPrincipal);
3802 :
3803 : bool matches =
3804 0 : aPattern->Matches(reg->mPrincipal->OriginAttributesRef());
3805 0 : if (!matches) {
3806 0 : continue;
3807 : }
3808 :
3809 0 : ForceUnregister(data, reg);
3810 : }
3811 : }
3812 0 : }
3813 :
3814 : NS_IMETHODIMP
3815 0 : ServiceWorkerManager::AddListener(nsIServiceWorkerManagerListener* aListener)
3816 : {
3817 0 : AssertIsOnMainThread();
3818 :
3819 0 : if (!aListener || mListeners.Contains(aListener)) {
3820 0 : return NS_ERROR_INVALID_ARG;
3821 : }
3822 :
3823 0 : mListeners.AppendElement(aListener);
3824 :
3825 0 : return NS_OK;
3826 : }
3827 :
3828 : NS_IMETHODIMP
3829 0 : ServiceWorkerManager::RemoveListener(nsIServiceWorkerManagerListener* aListener)
3830 : {
3831 0 : AssertIsOnMainThread();
3832 :
3833 0 : if (!aListener || !mListeners.Contains(aListener)) {
3834 0 : return NS_ERROR_INVALID_ARG;
3835 : }
3836 :
3837 0 : mListeners.RemoveElement(aListener);
3838 :
3839 0 : return NS_OK;
3840 : }
3841 :
3842 : NS_IMETHODIMP
3843 0 : ServiceWorkerManager::ShouldReportToWindow(mozIDOMWindowProxy* aWindow,
3844 : const nsACString& aScope,
3845 : bool* aResult)
3846 : {
3847 0 : AssertIsOnMainThread();
3848 0 : MOZ_ASSERT(aResult);
3849 :
3850 0 : *aResult = false;
3851 :
3852 : // Get the inner window ID to compare to our document windows below.
3853 0 : nsCOMPtr<nsPIDOMWindowOuter> targetWin = nsPIDOMWindowOuter::From(aWindow);
3854 0 : if (NS_WARN_IF(!targetWin)) {
3855 0 : return NS_OK;
3856 : }
3857 :
3858 0 : targetWin = targetWin->GetScriptableTop();
3859 0 : uint64_t winId = targetWin->WindowID();
3860 :
3861 : // Check our weak registering document references first. This way we clear
3862 : // out as many dead weak references as possible when this method is called.
3863 0 : WeakDocumentList* list = mRegisteringDocuments.Get(aScope);
3864 0 : if (list) {
3865 0 : for (int32_t i = list->Length() - 1; i >= 0; --i) {
3866 0 : nsCOMPtr<nsIDocument> doc = do_QueryReferent(list->ElementAt(i));
3867 0 : if (!doc) {
3868 0 : list->RemoveElementAt(i);
3869 0 : continue;
3870 : }
3871 :
3872 0 : if (!doc->IsCurrentActiveDocument()) {
3873 0 : continue;
3874 : }
3875 :
3876 0 : nsCOMPtr<nsPIDOMWindowOuter> win = doc->GetWindow();
3877 0 : if (!win) {
3878 0 : continue;
3879 : }
3880 :
3881 0 : win = win->GetScriptableTop();
3882 :
3883 : // Match. We should report to this window.
3884 0 : if (win && winId == win->WindowID()) {
3885 0 : *aResult = true;
3886 0 : return NS_OK;
3887 : }
3888 : }
3889 :
3890 0 : if (list->IsEmpty()) {
3891 0 : list = nullptr;
3892 0 : mRegisteringDocuments.Remove(aScope);
3893 : }
3894 : }
3895 :
3896 : // Examine any windows performing a navigation that we are currently
3897 : // intercepting.
3898 0 : InterceptionList* intList = mNavigationInterceptions.Get(aScope);
3899 0 : if (intList) {
3900 0 : for (uint32_t i = 0; i < intList->Length(); ++i) {
3901 0 : nsCOMPtr<nsIInterceptedChannel> channel = intList->ElementAt(i);
3902 :
3903 0 : nsCOMPtr<nsIChannel> inner;
3904 0 : nsresult rv = channel->GetChannel(getter_AddRefs(inner));
3905 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
3906 0 : continue;
3907 : }
3908 :
3909 0 : uint64_t id = nsContentUtils::GetInnerWindowID(inner);
3910 0 : if (id == 0) {
3911 0 : continue;
3912 : }
3913 :
3914 0 : nsCOMPtr<nsPIDOMWindowInner> win = nsGlobalWindow::GetInnerWindowWithId(id)->AsInner();
3915 0 : if (!win) {
3916 0 : continue;
3917 : }
3918 :
3919 0 : nsCOMPtr<nsPIDOMWindowOuter> outer = win->GetScriptableTop();
3920 :
3921 : // Match. We should report to this window.
3922 0 : if (outer && winId == outer->WindowID()) {
3923 0 : *aResult = true;
3924 0 : return NS_OK;
3925 : }
3926 : }
3927 : }
3928 :
3929 : // Next examine controlled documents to see if the windows match.
3930 0 : for (auto iter = mControlledDocuments.Iter(); !iter.Done(); iter.Next()) {
3931 0 : ServiceWorkerRegistrationInfo* reg = iter.UserData();
3932 0 : MOZ_ASSERT(reg);
3933 0 : if (!reg->mScope.Equals(aScope)) {
3934 0 : continue;
3935 : }
3936 :
3937 0 : nsCOMPtr<nsIDocument> doc = do_QueryInterface(iter.Key());
3938 0 : if (!doc || !doc->IsCurrentActiveDocument()) {
3939 0 : continue;
3940 : }
3941 :
3942 0 : nsCOMPtr<nsPIDOMWindowOuter> win = doc->GetWindow();
3943 0 : if (!win) {
3944 0 : continue;
3945 : }
3946 :
3947 0 : win = win->GetScriptableTop();
3948 :
3949 : // Match. We should report to this window.
3950 0 : if (win && winId == win->WindowID()) {
3951 0 : *aResult = true;
3952 0 : return NS_OK;
3953 : }
3954 : }
3955 :
3956 : // No match. We should not report to this window.
3957 0 : return NS_OK;
3958 : }
3959 :
3960 : NS_IMETHODIMP
3961 0 : ServiceWorkerManager::Observe(nsISupports* aSubject,
3962 : const char* aTopic,
3963 : const char16_t* aData)
3964 : {
3965 0 : if (strcmp(aTopic, PURGE_SESSION_HISTORY) == 0) {
3966 0 : MOZ_ASSERT(XRE_IsParentProcess());
3967 0 : RemoveAll();
3968 0 : PropagateRemoveAll();
3969 0 : return NS_OK;
3970 : }
3971 :
3972 0 : if (strcmp(aTopic, PURGE_DOMAIN_DATA) == 0) {
3973 0 : MOZ_ASSERT(XRE_IsParentProcess());
3974 0 : nsAutoString domain(aData);
3975 0 : RemoveAndPropagate(NS_ConvertUTF16toUTF8(domain));
3976 0 : return NS_OK;
3977 : }
3978 :
3979 0 : if (strcmp(aTopic, CLEAR_ORIGIN_DATA) == 0) {
3980 0 : MOZ_ASSERT(XRE_IsParentProcess());
3981 0 : OriginAttributesPattern pattern;
3982 0 : MOZ_ALWAYS_TRUE(pattern.Init(nsAutoString(aData)));
3983 :
3984 0 : RemoveAllRegistrations(&pattern);
3985 0 : return NS_OK;
3986 : }
3987 :
3988 0 : if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
3989 0 : MaybeStartShutdown();
3990 0 : return NS_OK;
3991 : }
3992 :
3993 0 : MOZ_CRASH("Received message we aren't supposed to be registered for!");
3994 : return NS_OK;
3995 : }
3996 :
3997 : NS_IMETHODIMP
3998 0 : ServiceWorkerManager::PropagateSoftUpdate(JS::Handle<JS::Value> aOriginAttributes,
3999 : const nsAString& aScope,
4000 : JSContext* aCx)
4001 : {
4002 0 : AssertIsOnMainThread();
4003 :
4004 0 : OriginAttributes attrs;
4005 0 : if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) {
4006 0 : return NS_ERROR_INVALID_ARG;
4007 : }
4008 :
4009 0 : PropagateSoftUpdate(attrs, aScope);
4010 0 : return NS_OK;
4011 : }
4012 :
4013 : void
4014 0 : ServiceWorkerManager::PropagateSoftUpdate(const OriginAttributes& aOriginAttributes,
4015 : const nsAString& aScope)
4016 : {
4017 0 : AssertIsOnMainThread();
4018 :
4019 0 : if (!mActor) {
4020 : RefPtr<nsIRunnable> runnable =
4021 0 : new PropagateSoftUpdateRunnable(aOriginAttributes, aScope);
4022 0 : AppendPendingOperation(runnable);
4023 0 : return;
4024 : }
4025 :
4026 0 : mActor->SendPropagateSoftUpdate(aOriginAttributes, nsString(aScope));
4027 : }
4028 :
4029 : NS_IMETHODIMP
4030 0 : ServiceWorkerManager::PropagateUnregister(nsIPrincipal* aPrincipal,
4031 : nsIServiceWorkerUnregisterCallback* aCallback,
4032 : const nsAString& aScope)
4033 : {
4034 0 : AssertIsOnMainThread();
4035 0 : MOZ_ASSERT(aPrincipal);
4036 :
4037 0 : if (!mActor) {
4038 : RefPtr<nsIRunnable> runnable =
4039 0 : new PropagateUnregisterRunnable(aPrincipal, aCallback, aScope);
4040 0 : AppendPendingOperation(runnable);
4041 0 : return NS_OK;
4042 : }
4043 :
4044 0 : PrincipalInfo principalInfo;
4045 0 : if (NS_WARN_IF(NS_FAILED(PrincipalToPrincipalInfo(aPrincipal,
4046 : &principalInfo)))) {
4047 0 : return NS_ERROR_FAILURE;
4048 : }
4049 :
4050 0 : mActor->SendPropagateUnregister(principalInfo, nsString(aScope));
4051 :
4052 0 : nsresult rv = Unregister(aPrincipal, aCallback, aScope);
4053 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4054 0 : return rv;
4055 : }
4056 :
4057 0 : return NS_OK;
4058 : }
4059 :
4060 : void
4061 0 : ServiceWorkerManager::NotifyListenersOnRegister(
4062 : nsIServiceWorkerRegistrationInfo* aInfo)
4063 : {
4064 0 : nsTArray<nsCOMPtr<nsIServiceWorkerManagerListener>> listeners(mListeners);
4065 0 : for (size_t index = 0; index < listeners.Length(); ++index) {
4066 0 : listeners[index]->OnRegister(aInfo);
4067 : }
4068 0 : }
4069 :
4070 : void
4071 0 : ServiceWorkerManager::NotifyListenersOnUnregister(
4072 : nsIServiceWorkerRegistrationInfo* aInfo)
4073 : {
4074 0 : nsTArray<nsCOMPtr<nsIServiceWorkerManagerListener>> listeners(mListeners);
4075 0 : for (size_t index = 0; index < listeners.Length(); ++index) {
4076 0 : listeners[index]->OnUnregister(aInfo);
4077 : }
4078 0 : }
4079 :
4080 : void
4081 0 : ServiceWorkerManager::AddRegisteringDocument(const nsACString& aScope,
4082 : nsIDocument* aDoc)
4083 : {
4084 0 : AssertIsOnMainThread();
4085 0 : MOZ_ASSERT(!aScope.IsEmpty());
4086 0 : MOZ_ASSERT(aDoc);
4087 :
4088 0 : WeakDocumentList* list = mRegisteringDocuments.LookupOrAdd(aScope);
4089 0 : MOZ_ASSERT(list);
4090 :
4091 0 : for (int32_t i = list->Length() - 1; i >= 0; --i) {
4092 0 : nsCOMPtr<nsIDocument> existing = do_QueryReferent(list->ElementAt(i));
4093 0 : if (!existing) {
4094 0 : list->RemoveElementAt(i);
4095 0 : continue;
4096 : }
4097 0 : if (existing == aDoc) {
4098 0 : return;
4099 : }
4100 : }
4101 :
4102 0 : list->AppendElement(do_GetWeakReference(aDoc));
4103 : }
4104 :
4105 : class ServiceWorkerManager::InterceptionReleaseHandle final : public nsISupports
4106 : {
4107 : const nsCString mScope;
4108 :
4109 : // Weak reference to channel is safe, because the channel holds a
4110 : // reference to this object. Also, the pointer is only used for
4111 : // comparison purposes.
4112 : nsIInterceptedChannel* mChannel;
4113 :
4114 0 : ~InterceptionReleaseHandle()
4115 0 : {
4116 0 : RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
4117 0 : if (swm) {
4118 0 : swm->RemoveNavigationInterception(mScope, mChannel);
4119 : }
4120 0 : }
4121 :
4122 : public:
4123 0 : InterceptionReleaseHandle(const nsACString& aScope,
4124 : nsIInterceptedChannel* aChannel)
4125 0 : : mScope(aScope)
4126 0 : , mChannel(aChannel)
4127 : {
4128 0 : AssertIsOnMainThread();
4129 0 : MOZ_ASSERT(!aScope.IsEmpty());
4130 0 : MOZ_ASSERT(mChannel);
4131 0 : }
4132 :
4133 : NS_DECL_ISUPPORTS
4134 : };
4135 :
4136 0 : NS_IMPL_ISUPPORTS0(ServiceWorkerManager::InterceptionReleaseHandle);
4137 :
4138 : void
4139 0 : ServiceWorkerManager::AddNavigationInterception(const nsACString& aScope,
4140 : nsIInterceptedChannel* aChannel)
4141 : {
4142 0 : AssertIsOnMainThread();
4143 0 : MOZ_ASSERT(!aScope.IsEmpty());
4144 0 : MOZ_ASSERT(aChannel);
4145 :
4146 : InterceptionList* list =
4147 0 : mNavigationInterceptions.LookupOrAdd(aScope);
4148 0 : MOZ_ASSERT(list);
4149 0 : MOZ_ASSERT(!list->Contains(aChannel));
4150 :
4151 : nsCOMPtr<nsISupports> releaseHandle =
4152 0 : new InterceptionReleaseHandle(aScope, aChannel);
4153 0 : aChannel->SetReleaseHandle(releaseHandle);
4154 :
4155 0 : list->AppendElement(aChannel);
4156 0 : }
4157 :
4158 : void
4159 0 : ServiceWorkerManager::RemoveNavigationInterception(const nsACString& aScope,
4160 : nsIInterceptedChannel* aChannel)
4161 : {
4162 0 : AssertIsOnMainThread();
4163 0 : MOZ_ASSERT(aChannel);
4164 : InterceptionList* list =
4165 0 : mNavigationInterceptions.Get(aScope);
4166 0 : if (list) {
4167 0 : MOZ_ALWAYS_TRUE(list->RemoveElement(aChannel));
4168 0 : MOZ_ASSERT(!list->Contains(aChannel));
4169 0 : if (list->IsEmpty()) {
4170 0 : list = nullptr;
4171 0 : mNavigationInterceptions.Remove(aScope);
4172 : }
4173 : }
4174 0 : }
4175 :
4176 : class UpdateTimerCallback final : public nsITimerCallback
4177 : {
4178 : nsCOMPtr<nsIPrincipal> mPrincipal;
4179 : const nsCString mScope;
4180 :
4181 0 : ~UpdateTimerCallback()
4182 0 : {
4183 0 : }
4184 :
4185 : public:
4186 0 : UpdateTimerCallback(nsIPrincipal* aPrincipal, const nsACString& aScope)
4187 0 : : mPrincipal(aPrincipal)
4188 0 : , mScope(aScope)
4189 : {
4190 0 : AssertIsOnMainThread();
4191 0 : MOZ_ASSERT(mPrincipal);
4192 0 : MOZ_ASSERT(!mScope.IsEmpty());
4193 0 : }
4194 :
4195 : NS_IMETHOD
4196 0 : Notify(nsITimer* aTimer) override
4197 : {
4198 0 : AssertIsOnMainThread();
4199 :
4200 0 : RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
4201 0 : if (!swm) {
4202 : // shutting down, do nothing
4203 0 : return NS_OK;
4204 : }
4205 :
4206 0 : swm->UpdateTimerFired(mPrincipal, mScope);
4207 0 : return NS_OK;
4208 : }
4209 :
4210 : NS_DECL_ISUPPORTS
4211 : };
4212 :
4213 0 : NS_IMPL_ISUPPORTS(UpdateTimerCallback, nsITimerCallback)
4214 :
4215 : bool
4216 0 : ServiceWorkerManager::MayHaveActiveServiceWorkerInstance(ContentParent* aContent,
4217 : nsIPrincipal* aPrincipal)
4218 : {
4219 0 : AssertIsOnMainThread();
4220 0 : MOZ_ASSERT(aPrincipal);
4221 :
4222 0 : if (mShuttingDown) {
4223 0 : return false;
4224 : }
4225 :
4226 0 : nsAutoCString scopeKey;
4227 0 : nsresult rv = PrincipalToScopeKey(aPrincipal, scopeKey);
4228 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4229 0 : return false;
4230 : }
4231 :
4232 : RegistrationDataPerPrincipal* data;
4233 0 : if (!mRegistrationInfos.Get(scopeKey, &data)) {
4234 0 : return false;
4235 : }
4236 :
4237 0 : return true;
4238 : }
4239 :
4240 : void
4241 0 : ServiceWorkerManager::ScheduleUpdateTimer(nsIPrincipal* aPrincipal,
4242 : const nsACString& aScope)
4243 : {
4244 0 : AssertIsOnMainThread();
4245 0 : MOZ_ASSERT(aPrincipal);
4246 0 : MOZ_ASSERT(!aScope.IsEmpty());
4247 :
4248 0 : if (mShuttingDown) {
4249 0 : return;
4250 : }
4251 :
4252 0 : nsAutoCString scopeKey;
4253 0 : nsresult rv = PrincipalToScopeKey(aPrincipal, scopeKey);
4254 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4255 0 : return;
4256 : }
4257 :
4258 : RegistrationDataPerPrincipal* data;
4259 0 : if (!mRegistrationInfos.Get(scopeKey, &data)) {
4260 0 : return;
4261 : }
4262 :
4263 0 : nsCOMPtr<nsITimer>& timer = data->mUpdateTimers.GetOrInsert(aScope);
4264 0 : if (timer) {
4265 : // There is already a timer scheduled. In this case just use the original
4266 : // schedule time. We don't want to push it out to a later time since that
4267 : // could allow updates to be starved forever if events are continuously
4268 : // fired.
4269 0 : return;
4270 : }
4271 :
4272 0 : timer = do_CreateInstance("@mozilla.org/timer;1", &rv);
4273 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4274 0 : data->mUpdateTimers.Remove(aScope); // another lookup, but very rare
4275 0 : return;
4276 : }
4277 :
4278 : nsCOMPtr<nsITimerCallback> callback = new UpdateTimerCallback(aPrincipal,
4279 0 : aScope);
4280 :
4281 0 : const uint32_t UPDATE_DELAY_MS = 1000;
4282 :
4283 0 : rv = timer->InitWithCallback(callback, UPDATE_DELAY_MS,
4284 0 : nsITimer::TYPE_ONE_SHOT);
4285 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4286 0 : data->mUpdateTimers.Remove(aScope); // another lookup, but very rare
4287 0 : return;
4288 : }
4289 : }
4290 :
4291 : void
4292 0 : ServiceWorkerManager::UpdateTimerFired(nsIPrincipal* aPrincipal,
4293 : const nsACString& aScope)
4294 : {
4295 0 : AssertIsOnMainThread();
4296 0 : MOZ_ASSERT(aPrincipal);
4297 0 : MOZ_ASSERT(!aScope.IsEmpty());
4298 :
4299 0 : if (mShuttingDown) {
4300 0 : return;
4301 : }
4302 :
4303 : // First cleanup the timer.
4304 0 : nsAutoCString scopeKey;
4305 0 : nsresult rv = PrincipalToScopeKey(aPrincipal, scopeKey);
4306 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4307 0 : return;
4308 : }
4309 :
4310 : RegistrationDataPerPrincipal* data;
4311 0 : if (!mRegistrationInfos.Get(scopeKey, &data)) {
4312 0 : return;
4313 : }
4314 :
4315 0 : if (auto entry = data->mUpdateTimers.Lookup(aScope)) {
4316 0 : entry.Data()->Cancel();
4317 0 : entry.Remove();
4318 : }
4319 :
4320 0 : RefPtr<ServiceWorkerRegistrationInfo> registration;
4321 0 : data->mInfos.Get(aScope, getter_AddRefs(registration));
4322 0 : if (!registration) {
4323 0 : return;
4324 : }
4325 :
4326 0 : if (!registration->CheckAndClearIfUpdateNeeded()) {
4327 0 : return;
4328 : }
4329 :
4330 0 : OriginAttributes attrs = aPrincipal->OriginAttributesRef();
4331 :
4332 0 : SoftUpdate(attrs, aScope);
4333 : }
4334 :
4335 : void
4336 0 : ServiceWorkerManager::MaybeSendUnregister(nsIPrincipal* aPrincipal,
4337 : const nsACString& aScope)
4338 : {
4339 0 : AssertIsOnMainThread();
4340 0 : MOZ_ASSERT(aPrincipal);
4341 0 : MOZ_ASSERT(!aScope.IsEmpty());
4342 :
4343 0 : if (!mActor) {
4344 0 : return;
4345 : }
4346 :
4347 0 : PrincipalInfo principalInfo;
4348 0 : nsresult rv = PrincipalToPrincipalInfo(aPrincipal, &principalInfo);
4349 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
4350 0 : return;
4351 : }
4352 :
4353 0 : Unused << mActor->SendUnregister(principalInfo, NS_ConvertUTF8toUTF16(aScope));
4354 : }
4355 :
4356 : END_WORKERS_NAMESPACE
|