/src/mozilla-central/dom/serviceworkers/ServiceWorkerManager.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
3 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | #include "ServiceWorkerManager.h" |
8 | | |
9 | | #include "nsAutoPtr.h" |
10 | | #include "nsIConsoleService.h" |
11 | | #include "nsIEffectiveTLDService.h" |
12 | | #include "nsIScriptSecurityManager.h" |
13 | | #include "nsIStreamLoader.h" |
14 | | #include "nsIHttpChannel.h" |
15 | | #include "nsIHttpChannelInternal.h" |
16 | | #include "nsIHttpHeaderVisitor.h" |
17 | | #include "nsINamed.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 "nsServiceManagerUtils.h" |
25 | | #include "nsDebug.h" |
26 | | #include "nsISupportsPrimitives.h" |
27 | | #include "nsIPermissionManager.h" |
28 | | |
29 | | #include "jsapi.h" |
30 | | |
31 | | #include "mozilla/BasePrincipal.h" |
32 | | #include "mozilla/ClearOnShutdown.h" |
33 | | #include "mozilla/ErrorNames.h" |
34 | | #include "mozilla/LoadContext.h" |
35 | | #include "mozilla/SystemGroup.h" |
36 | | #include "mozilla/Telemetry.h" |
37 | | #include "mozilla/dom/BindingUtils.h" |
38 | | #include "mozilla/dom/ClientHandle.h" |
39 | | #include "mozilla/dom/ClientManager.h" |
40 | | #include "mozilla/dom/ClientSource.h" |
41 | | #include "mozilla/dom/ConsoleUtils.h" |
42 | | #include "mozilla/dom/ContentParent.h" |
43 | | #include "mozilla/dom/DOMPrefs.h" |
44 | | #include "mozilla/dom/ErrorEvent.h" |
45 | | #include "mozilla/dom/Headers.h" |
46 | | #include "mozilla/dom/InternalHeaders.h" |
47 | | #include "mozilla/dom/Navigator.h" |
48 | | #include "mozilla/dom/NotificationEvent.h" |
49 | | #include "mozilla/dom/PromiseNativeHandler.h" |
50 | | #include "mozilla/dom/Request.h" |
51 | | #include "mozilla/dom/RootedDictionary.h" |
52 | | #include "mozilla/dom/TypedArray.h" |
53 | | #include "mozilla/dom/SharedWorker.h" |
54 | | #include "mozilla/dom/WorkerPrivate.h" |
55 | | #include "mozilla/dom/WorkerRunnable.h" |
56 | | #include "mozilla/dom/WorkerScope.h" |
57 | | #include "mozilla/ipc/BackgroundChild.h" |
58 | | #include "mozilla/ipc/PBackgroundChild.h" |
59 | | #include "mozilla/ipc/PBackgroundSharedTypes.h" |
60 | | #include "mozilla/dom/ScriptLoader.h" |
61 | | #include "mozilla/Unused.h" |
62 | | #include "mozilla/EnumSet.h" |
63 | | |
64 | | #include "nsContentUtils.h" |
65 | | #include "nsNetUtil.h" |
66 | | #include "nsProxyRelease.h" |
67 | | #include "nsQueryObject.h" |
68 | | #include "nsTArray.h" |
69 | | |
70 | | #include "ServiceWorker.h" |
71 | | #include "ServiceWorkerContainer.h" |
72 | | #include "ServiceWorkerInfo.h" |
73 | | #include "ServiceWorkerJobQueue.h" |
74 | | #include "ServiceWorkerManagerChild.h" |
75 | | #include "ServiceWorkerPrivate.h" |
76 | | #include "ServiceWorkerRegisterJob.h" |
77 | | #include "ServiceWorkerRegistrar.h" |
78 | | #include "ServiceWorkerRegistration.h" |
79 | | #include "ServiceWorkerScriptCache.h" |
80 | | #include "ServiceWorkerEvents.h" |
81 | | #include "ServiceWorkerUnregisterJob.h" |
82 | | #include "ServiceWorkerUpdateJob.h" |
83 | | #include "ServiceWorkerUpdaterChild.h" |
84 | | #include "ServiceWorkerUtils.h" |
85 | | |
86 | | #ifdef PostMessage |
87 | | #undef PostMessage |
88 | | #endif |
89 | | |
90 | | using namespace mozilla; |
91 | | using namespace mozilla::dom; |
92 | | using namespace mozilla::ipc; |
93 | | |
94 | | namespace mozilla { |
95 | | namespace dom { |
96 | | |
97 | 0 | #define CLEAR_ORIGIN_DATA "clear-origin-attributes-data" |
98 | | |
99 | | static_assert(nsIHttpChannelInternal::CORS_MODE_SAME_ORIGIN == static_cast<uint32_t>(RequestMode::Same_origin), |
100 | | "RequestMode enumeration value should match Necko CORS mode value."); |
101 | | static_assert(nsIHttpChannelInternal::CORS_MODE_NO_CORS == static_cast<uint32_t>(RequestMode::No_cors), |
102 | | "RequestMode enumeration value should match Necko CORS mode value."); |
103 | | static_assert(nsIHttpChannelInternal::CORS_MODE_CORS == static_cast<uint32_t>(RequestMode::Cors), |
104 | | "RequestMode enumeration value should match Necko CORS mode value."); |
105 | | static_assert(nsIHttpChannelInternal::CORS_MODE_NAVIGATE == static_cast<uint32_t>(RequestMode::Navigate), |
106 | | "RequestMode enumeration value should match Necko CORS mode value."); |
107 | | |
108 | | static_assert(nsIHttpChannelInternal::REDIRECT_MODE_FOLLOW == static_cast<uint32_t>(RequestRedirect::Follow), |
109 | | "RequestRedirect enumeration value should make Necko Redirect mode value."); |
110 | | static_assert(nsIHttpChannelInternal::REDIRECT_MODE_ERROR == static_cast<uint32_t>(RequestRedirect::Error), |
111 | | "RequestRedirect enumeration value should make Necko Redirect mode value."); |
112 | | static_assert(nsIHttpChannelInternal::REDIRECT_MODE_MANUAL == static_cast<uint32_t>(RequestRedirect::Manual), |
113 | | "RequestRedirect enumeration value should make Necko Redirect mode value."); |
114 | | static_assert(3 == static_cast<uint32_t>(RequestRedirect::EndGuard_), |
115 | | "RequestRedirect enumeration value should make Necko Redirect mode value."); |
116 | | |
117 | | static_assert(nsIHttpChannelInternal::FETCH_CACHE_MODE_DEFAULT == static_cast<uint32_t>(RequestCache::Default), |
118 | | "RequestCache enumeration value should match Necko Cache mode value."); |
119 | | static_assert(nsIHttpChannelInternal::FETCH_CACHE_MODE_NO_STORE == static_cast<uint32_t>(RequestCache::No_store), |
120 | | "RequestCache enumeration value should match Necko Cache mode value."); |
121 | | static_assert(nsIHttpChannelInternal::FETCH_CACHE_MODE_RELOAD == static_cast<uint32_t>(RequestCache::Reload), |
122 | | "RequestCache enumeration value should match Necko Cache mode value."); |
123 | | static_assert(nsIHttpChannelInternal::FETCH_CACHE_MODE_NO_CACHE == static_cast<uint32_t>(RequestCache::No_cache), |
124 | | "RequestCache enumeration value should match Necko Cache mode value."); |
125 | | static_assert(nsIHttpChannelInternal::FETCH_CACHE_MODE_FORCE_CACHE == static_cast<uint32_t>(RequestCache::Force_cache), |
126 | | "RequestCache enumeration value should match Necko Cache mode value."); |
127 | | static_assert(nsIHttpChannelInternal::FETCH_CACHE_MODE_ONLY_IF_CACHED == static_cast<uint32_t>(RequestCache::Only_if_cached), |
128 | | "RequestCache enumeration value should match Necko Cache mode value."); |
129 | | static_assert(6 == static_cast<uint32_t>(RequestCache::EndGuard_), |
130 | | "RequestCache enumeration value should match Necko Cache mode value."); |
131 | | |
132 | | static_assert(static_cast<uint16_t>(ServiceWorkerUpdateViaCache::Imports) == |
133 | | nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_IMPORTS, |
134 | | "nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_*" |
135 | | " should match ServiceWorkerUpdateViaCache enumeration."); |
136 | | static_assert(static_cast<uint16_t>(ServiceWorkerUpdateViaCache::All) == |
137 | | nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_ALL, |
138 | | "nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_*" |
139 | | " should match ServiceWorkerUpdateViaCache enumeration."); |
140 | | static_assert(static_cast<uint16_t>(ServiceWorkerUpdateViaCache::None) == |
141 | | nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_NONE, |
142 | | "nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_*" |
143 | | " should match ServiceWorkerUpdateViaCache enumeration."); |
144 | | |
145 | | static StaticRefPtr<ServiceWorkerManager> gInstance; |
146 | | |
147 | | struct ServiceWorkerManager::RegistrationDataPerPrincipal final |
148 | | { |
149 | | // Ordered list of scopes for glob matching. |
150 | | // Each entry is an absolute URL representing the scope. |
151 | | // Each value of the hash table is an array of an absolute URLs representing |
152 | | // the scopes. |
153 | | // |
154 | | // An array is used for now since the number of controlled scopes per |
155 | | // domain is expected to be relatively low. If that assumption was proved |
156 | | // wrong this should be replaced with a better structure to avoid the |
157 | | // memmoves associated with inserting stuff in the middle of the array. |
158 | | nsTArray<nsCString> mOrderedScopes; |
159 | | |
160 | | // Scope to registration. |
161 | | // The scope should be a fully qualified valid URL. |
162 | | nsRefPtrHashtable<nsCStringHashKey, ServiceWorkerRegistrationInfo> mInfos; |
163 | | |
164 | | // Maps scopes to job queues. |
165 | | nsRefPtrHashtable<nsCStringHashKey, ServiceWorkerJobQueue> mJobQueues; |
166 | | |
167 | | // Map scopes to scheduled update timers. |
168 | | nsInterfaceHashtable<nsCStringHashKey, nsITimer> mUpdateTimers; |
169 | | }; |
170 | | |
171 | | namespace { |
172 | | |
173 | | nsresult |
174 | | PopulateRegistrationData(nsIPrincipal* aPrincipal, |
175 | | const ServiceWorkerRegistrationInfo* aRegistration, |
176 | | ServiceWorkerRegistrationData& aData) |
177 | 0 | { |
178 | 0 | MOZ_ASSERT(aPrincipal); |
179 | 0 | MOZ_ASSERT(aRegistration); |
180 | 0 |
|
181 | 0 | if (NS_WARN_IF(!BasePrincipal::Cast(aPrincipal)->IsCodebasePrincipal())) { |
182 | 0 | return NS_ERROR_FAILURE; |
183 | 0 | } |
184 | 0 | |
185 | 0 | nsresult rv = PrincipalToPrincipalInfo(aPrincipal, &aData.principal()); |
186 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
187 | 0 | return rv; |
188 | 0 | } |
189 | 0 | |
190 | 0 | aData.scope() = aRegistration->Scope(); |
191 | 0 |
|
192 | 0 | // TODO: When bug 1426401 is implemented we will need to handle more |
193 | 0 | // than just the active worker here. |
194 | 0 | RefPtr<ServiceWorkerInfo> active = aRegistration->GetActive(); |
195 | 0 | MOZ_ASSERT(active); |
196 | 0 | if (NS_WARN_IF(!active)) { |
197 | 0 | return NS_ERROR_FAILURE; |
198 | 0 | } |
199 | 0 | |
200 | 0 | aData.currentWorkerURL() = active->ScriptSpec(); |
201 | 0 | aData.cacheName() = active->CacheName(); |
202 | 0 | aData.currentWorkerHandlesFetch() = active->HandlesFetch(); |
203 | 0 |
|
204 | 0 | aData.currentWorkerInstalledTime() = active->GetInstalledTime(); |
205 | 0 | aData.currentWorkerActivatedTime() = active->GetActivatedTime(); |
206 | 0 |
|
207 | 0 | aData.updateViaCache() = |
208 | 0 | static_cast<uint32_t>(aRegistration->GetUpdateViaCache()); |
209 | 0 |
|
210 | 0 | aData.lastUpdateTime() = aRegistration->GetLastUpdateTime(); |
211 | 0 |
|
212 | 0 | MOZ_ASSERT(ServiceWorkerRegistrationDataIsValid(aData)); |
213 | 0 |
|
214 | 0 | return NS_OK; |
215 | 0 | } |
216 | | |
217 | | class TeardownRunnable final : public Runnable |
218 | | { |
219 | | public: |
220 | | explicit TeardownRunnable(ServiceWorkerManagerChild* aActor) |
221 | | : Runnable("dom::ServiceWorkerManager::TeardownRunnable") |
222 | | , mActor(aActor) |
223 | 0 | { |
224 | 0 | MOZ_ASSERT(mActor); |
225 | 0 | } |
226 | | |
227 | | NS_IMETHOD Run() override |
228 | 0 | { |
229 | 0 | MOZ_ASSERT(mActor); |
230 | 0 | mActor->SendShutdown(); |
231 | 0 | return NS_OK; |
232 | 0 | } |
233 | | |
234 | | private: |
235 | 0 | ~TeardownRunnable() {} |
236 | | |
237 | | RefPtr<ServiceWorkerManagerChild> mActor; |
238 | | }; |
239 | | |
240 | | } // namespace |
241 | | |
242 | | ////////////////////////// |
243 | | // ServiceWorkerManager // |
244 | | ////////////////////////// |
245 | | |
246 | | NS_IMPL_ADDREF(ServiceWorkerManager) |
247 | | NS_IMPL_RELEASE(ServiceWorkerManager) |
248 | | |
249 | 0 | NS_INTERFACE_MAP_BEGIN(ServiceWorkerManager) |
250 | 0 | NS_INTERFACE_MAP_ENTRY(nsIServiceWorkerManager) |
251 | 0 | NS_INTERFACE_MAP_ENTRY(nsIObserver) |
252 | 0 | NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIServiceWorkerManager) |
253 | 0 | NS_INTERFACE_MAP_END |
254 | | |
255 | | ServiceWorkerManager::ServiceWorkerManager() |
256 | | : mActor(nullptr) |
257 | | , mShuttingDown(false) |
258 | 0 | { |
259 | 0 | } |
260 | | |
261 | | ServiceWorkerManager::~ServiceWorkerManager() |
262 | 0 | { |
263 | 0 | // The map will assert if it is not empty when destroyed. |
264 | 0 | mRegistrationInfos.Clear(); |
265 | 0 | MOZ_ASSERT(!mActor); |
266 | 0 | } |
267 | | |
268 | | void |
269 | | ServiceWorkerManager::Init(ServiceWorkerRegistrar* aRegistrar) |
270 | 0 | { |
271 | 0 | nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); |
272 | 0 | if (obs) { |
273 | 0 | DebugOnly<nsresult> rv; |
274 | 0 | rv = obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false /* ownsWeak */); |
275 | 0 | MOZ_ASSERT(NS_SUCCEEDED(rv)); |
276 | 0 | } |
277 | 0 |
|
278 | 0 | if (XRE_IsParentProcess()) { |
279 | 0 | MOZ_DIAGNOSTIC_ASSERT(aRegistrar); |
280 | 0 |
|
281 | 0 | nsTArray<ServiceWorkerRegistrationData> data; |
282 | 0 | aRegistrar->GetRegistrations(data); |
283 | 0 | LoadRegistrations(data); |
284 | 0 |
|
285 | 0 | if (obs) { |
286 | 0 | DebugOnly<nsresult> rv; |
287 | 0 | rv = obs->AddObserver(this, CLEAR_ORIGIN_DATA, false /* ownsWeak */); |
288 | 0 | MOZ_ASSERT(NS_SUCCEEDED(rv)); |
289 | 0 | } |
290 | 0 | } |
291 | 0 |
|
292 | 0 | PBackgroundChild* actorChild = BackgroundChild::GetOrCreateForCurrentThread(); |
293 | 0 | if (NS_WARN_IF(!actorChild)) { |
294 | 0 | MaybeStartShutdown(); |
295 | 0 | return; |
296 | 0 | } |
297 | 0 | |
298 | 0 | PServiceWorkerManagerChild* actor = |
299 | 0 | actorChild->SendPServiceWorkerManagerConstructor(); |
300 | 0 | if (!actor) { |
301 | 0 | MaybeStartShutdown(); |
302 | 0 | return; |
303 | 0 | } |
304 | 0 | |
305 | 0 | mActor = static_cast<ServiceWorkerManagerChild*>(actor); |
306 | 0 | } |
307 | | |
308 | | RefPtr<GenericPromise> |
309 | | ServiceWorkerManager::StartControllingClient(const ClientInfo& aClientInfo, |
310 | | ServiceWorkerRegistrationInfo* aRegistrationInfo, |
311 | | bool aControlClientHandle) |
312 | 0 | { |
313 | 0 | MOZ_DIAGNOSTIC_ASSERT(aRegistrationInfo->GetActive()); |
314 | 0 |
|
315 | 0 | RefPtr<GenericPromise> ref; |
316 | 0 | RefPtr<ServiceWorkerManager> self(this); |
317 | 0 |
|
318 | 0 | const ServiceWorkerDescriptor& active = |
319 | 0 | aRegistrationInfo->GetActive()->Descriptor(); |
320 | 0 |
|
321 | 0 | auto entry = mControlledClients.LookupForAdd(aClientInfo.Id()); |
322 | 0 | if (entry) { |
323 | 0 | RefPtr<ServiceWorkerRegistrationInfo> old = |
324 | 0 | entry.Data()->mRegistrationInfo.forget(); |
325 | 0 |
|
326 | 0 | if (aControlClientHandle) { |
327 | 0 | ref = entry.Data()->mClientHandle->Control(active); |
328 | 0 | } else { |
329 | 0 | ref = GenericPromise::CreateAndResolve(false, __func__); |
330 | 0 | } |
331 | 0 |
|
332 | 0 | entry.Data()->mRegistrationInfo = aRegistrationInfo; |
333 | 0 |
|
334 | 0 | if (old != aRegistrationInfo) { |
335 | 0 | StopControllingRegistration(old); |
336 | 0 | aRegistrationInfo->StartControllingClient(); |
337 | 0 | } |
338 | 0 |
|
339 | 0 | Telemetry::Accumulate(Telemetry::SERVICE_WORKER_CONTROLLED_DOCUMENTS, 1); |
340 | 0 |
|
341 | 0 | // Always check to see if we failed to actually control the client. In |
342 | 0 | // that case removed the client from our list of controlled clients. |
343 | 0 | ref->Then( |
344 | 0 | SystemGroup::EventTargetFor(TaskCategory::Other), __func__, |
345 | 0 | [] (bool) { |
346 | 0 | // do nothing on success |
347 | 0 | }, [self, aClientInfo] (nsresult aRv) { |
348 | 0 | // failed to control, forget about this client |
349 | 0 | self->StopControllingClient(aClientInfo); |
350 | 0 | }); |
351 | 0 |
|
352 | 0 | return ref; |
353 | 0 | } |
354 | 0 |
|
355 | 0 | RefPtr<ClientHandle> clientHandle = |
356 | 0 | ClientManager::CreateHandle(aClientInfo, |
357 | 0 | SystemGroup::EventTargetFor(TaskCategory::Other)); |
358 | 0 |
|
359 | 0 | if (aControlClientHandle) { |
360 | 0 | ref = clientHandle->Control(active); |
361 | 0 | } else { |
362 | 0 | ref = GenericPromise::CreateAndResolve(false, __func__); |
363 | 0 | } |
364 | 0 |
|
365 | 0 | aRegistrationInfo->StartControllingClient(); |
366 | 0 |
|
367 | 0 | entry.OrInsert([&] { |
368 | 0 | return new ControlledClientData(clientHandle, aRegistrationInfo); |
369 | 0 | }); |
370 | 0 |
|
371 | 0 | clientHandle->OnDetach()->Then( |
372 | 0 | SystemGroup::EventTargetFor(TaskCategory::Other), __func__, |
373 | 0 | [self, aClientInfo] { |
374 | 0 | self->StopControllingClient(aClientInfo); |
375 | 0 | }); |
376 | 0 |
|
377 | 0 | Telemetry::Accumulate(Telemetry::SERVICE_WORKER_CONTROLLED_DOCUMENTS, 1); |
378 | 0 |
|
379 | 0 | // Always check to see if we failed to actually control the client. In |
380 | 0 | // that case removed the client from our list of controlled clients. |
381 | 0 | ref->Then( |
382 | 0 | SystemGroup::EventTargetFor(TaskCategory::Other), __func__, |
383 | 0 | [] (bool) { |
384 | 0 | // do nothing on success |
385 | 0 | }, [self, aClientInfo] (nsresult aRv) { |
386 | 0 | // failed to control, forget about this client |
387 | 0 | self->StopControllingClient(aClientInfo); |
388 | 0 | }); |
389 | 0 |
|
390 | 0 | return ref; |
391 | 0 | } |
392 | | |
393 | | void |
394 | | ServiceWorkerManager::StopControllingClient(const ClientInfo& aClientInfo) |
395 | 0 | { |
396 | 0 | auto entry = mControlledClients.Lookup(aClientInfo.Id()); |
397 | 0 | if (!entry) { |
398 | 0 | return; |
399 | 0 | } |
400 | 0 | |
401 | 0 | RefPtr<ServiceWorkerRegistrationInfo> reg = |
402 | 0 | entry.Data()->mRegistrationInfo.forget(); |
403 | 0 |
|
404 | 0 | entry.Remove(); |
405 | 0 |
|
406 | 0 | StopControllingRegistration(reg); |
407 | 0 | } |
408 | | |
409 | | void |
410 | | ServiceWorkerManager::MaybeStartShutdown() |
411 | 0 | { |
412 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
413 | 0 |
|
414 | 0 | if (mShuttingDown) { |
415 | 0 | return; |
416 | 0 | } |
417 | 0 | |
418 | 0 | mShuttingDown = true; |
419 | 0 |
|
420 | 0 | for (auto it1 = mRegistrationInfos.Iter(); !it1.Done(); it1.Next()) { |
421 | 0 | for (auto it2 = it1.UserData()->mUpdateTimers.Iter(); !it2.Done(); it2.Next()) { |
422 | 0 | nsCOMPtr<nsITimer> timer = it2.UserData(); |
423 | 0 | timer->Cancel(); |
424 | 0 | } |
425 | 0 | it1.UserData()->mUpdateTimers.Clear(); |
426 | 0 |
|
427 | 0 | for (auto it2 = it1.UserData()->mJobQueues.Iter(); !it2.Done(); it2.Next()) { |
428 | 0 | RefPtr<ServiceWorkerJobQueue> queue = it2.UserData(); |
429 | 0 | queue->CancelAll(); |
430 | 0 | } |
431 | 0 | it1.UserData()->mJobQueues.Clear(); |
432 | 0 | } |
433 | 0 |
|
434 | 0 | nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); |
435 | 0 | if (obs) { |
436 | 0 | obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID); |
437 | 0 |
|
438 | 0 | if (XRE_IsParentProcess()) { |
439 | 0 | obs->RemoveObserver(this, CLEAR_ORIGIN_DATA); |
440 | 0 | } |
441 | 0 | } |
442 | 0 |
|
443 | 0 | if (!mActor) { |
444 | 0 | return; |
445 | 0 | } |
446 | 0 | |
447 | 0 | mActor->ManagerShuttingDown(); |
448 | 0 |
|
449 | 0 | RefPtr<TeardownRunnable> runnable = new TeardownRunnable(mActor); |
450 | 0 | nsresult rv = NS_DispatchToMainThread(runnable); |
451 | 0 | Unused << NS_WARN_IF(NS_FAILED(rv)); |
452 | 0 | mActor = nullptr; |
453 | 0 | } |
454 | | |
455 | | class ServiceWorkerResolveWindowPromiseOnRegisterCallback final : public ServiceWorkerJob::Callback |
456 | | { |
457 | | RefPtr<ServiceWorkerRegistrationPromise::Private> mPromise; |
458 | | |
459 | | ~ServiceWorkerResolveWindowPromiseOnRegisterCallback() |
460 | 0 | {} |
461 | | |
462 | | virtual void |
463 | | JobFinished(ServiceWorkerJob* aJob, ErrorResult& aStatus) override |
464 | 0 | { |
465 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
466 | 0 | MOZ_ASSERT(aJob); |
467 | 0 |
|
468 | 0 | if (aStatus.Failed()) { |
469 | 0 | mPromise->Reject(std::move(aStatus), __func__); |
470 | 0 | return; |
471 | 0 | } |
472 | 0 | |
473 | 0 | MOZ_ASSERT(aJob->GetType() == ServiceWorkerJob::Type::Register); |
474 | 0 | RefPtr<ServiceWorkerRegisterJob> registerJob = |
475 | 0 | static_cast<ServiceWorkerRegisterJob*>(aJob); |
476 | 0 | RefPtr<ServiceWorkerRegistrationInfo> reg = registerJob->GetRegistration(); |
477 | 0 |
|
478 | 0 | mPromise->Resolve(reg->Descriptor(), __func__); |
479 | 0 | } |
480 | | |
481 | | public: |
482 | | ServiceWorkerResolveWindowPromiseOnRegisterCallback() |
483 | | : mPromise(new ServiceWorkerRegistrationPromise::Private(__func__)) |
484 | 0 | {} |
485 | | |
486 | | RefPtr<ServiceWorkerRegistrationPromise> |
487 | | Promise() const |
488 | 0 | { |
489 | 0 | return mPromise; |
490 | 0 | } |
491 | | |
492 | | NS_INLINE_DECL_REFCOUNTING(ServiceWorkerResolveWindowPromiseOnRegisterCallback, override) |
493 | | }; |
494 | | |
495 | | namespace { |
496 | | |
497 | | class PropagateSoftUpdateRunnable final : public Runnable |
498 | | { |
499 | | public: |
500 | | PropagateSoftUpdateRunnable(const OriginAttributes& aOriginAttributes, |
501 | | const nsAString& aScope) |
502 | | : Runnable("dom::ServiceWorkerManager::PropagateSoftUpdateRunnable") |
503 | | , mOriginAttributes(aOriginAttributes) |
504 | | , mScope(aScope) |
505 | 0 | {} |
506 | | |
507 | | NS_IMETHOD Run() override |
508 | 0 | { |
509 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
510 | 0 |
|
511 | 0 | RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); |
512 | 0 | if (swm) { |
513 | 0 | swm->PropagateSoftUpdate(mOriginAttributes, mScope); |
514 | 0 | } |
515 | 0 |
|
516 | 0 | return NS_OK; |
517 | 0 | } |
518 | | |
519 | | private: |
520 | | ~PropagateSoftUpdateRunnable() |
521 | 0 | {} |
522 | | |
523 | | const OriginAttributes mOriginAttributes; |
524 | | const nsString mScope; |
525 | | }; |
526 | | |
527 | | class PromiseResolverCallback final : public ServiceWorkerUpdateFinishCallback |
528 | | { |
529 | | public: |
530 | | PromiseResolverCallback(ServiceWorkerUpdateFinishCallback* aCallback, |
531 | | GenericPromise::Private* aPromise) |
532 | | : mCallback(aCallback) |
533 | | , mPromise(aPromise) |
534 | 0 | { |
535 | 0 | MOZ_DIAGNOSTIC_ASSERT(mPromise); |
536 | 0 | } |
537 | | |
538 | | void UpdateSucceeded(ServiceWorkerRegistrationInfo* aInfo) override |
539 | 0 | { |
540 | 0 | MOZ_DIAGNOSTIC_ASSERT(mPromise); |
541 | 0 |
|
542 | 0 | if (mCallback) { |
543 | 0 | mCallback->UpdateSucceeded(aInfo); |
544 | 0 | } |
545 | 0 |
|
546 | 0 | MaybeResolve(); |
547 | 0 | } |
548 | | |
549 | | void UpdateFailed(ErrorResult& aStatus) override |
550 | 0 | { |
551 | 0 | MOZ_DIAGNOSTIC_ASSERT(mPromise); |
552 | 0 |
|
553 | 0 | if (mCallback) { |
554 | 0 | mCallback->UpdateFailed(aStatus); |
555 | 0 | } |
556 | 0 |
|
557 | 0 | MaybeResolve(); |
558 | 0 | } |
559 | | |
560 | | private: |
561 | | ~PromiseResolverCallback() |
562 | 0 | { |
563 | 0 | MaybeResolve(); |
564 | 0 | } |
565 | | |
566 | | void |
567 | | MaybeResolve() |
568 | 0 | { |
569 | 0 | if (mPromise) { |
570 | 0 | mPromise->Resolve(true, __func__); |
571 | 0 | mPromise = nullptr; |
572 | 0 | } |
573 | 0 | } |
574 | | |
575 | | RefPtr<ServiceWorkerUpdateFinishCallback> mCallback; |
576 | | RefPtr<GenericPromise::Private> mPromise; |
577 | | }; |
578 | | |
579 | | // This runnable is used for 2 different tasks: |
580 | | // - to postpone the SoftUpdate() until the IPC SWM actor is created |
581 | | // (aInternalMethod == false) |
582 | | // - to call the 'real' SoftUpdate when the ServiceWorkerUpdaterChild is |
583 | | // notified by the parent (aInternalMethod == true) |
584 | | class SoftUpdateRunnable final : public CancelableRunnable |
585 | | { |
586 | | public: |
587 | | SoftUpdateRunnable(const OriginAttributes& aOriginAttributes, |
588 | | const nsACString& aScope, |
589 | | bool aInternalMethod, |
590 | | GenericPromise::Private* aPromise) |
591 | | : CancelableRunnable("dom::ServiceWorkerManager::SoftUpdateRunnable") |
592 | | , mAttrs(aOriginAttributes) |
593 | | , mScope(aScope) |
594 | | , mInternalMethod(aInternalMethod) |
595 | | , mPromise(aPromise) |
596 | 0 | {} |
597 | | |
598 | | NS_IMETHOD Run() override |
599 | 0 | { |
600 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
601 | 0 |
|
602 | 0 | RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); |
603 | 0 | if (!swm) { |
604 | 0 | return NS_ERROR_FAILURE; |
605 | 0 | } |
606 | 0 | |
607 | 0 | if (mInternalMethod) { |
608 | 0 | RefPtr<PromiseResolverCallback> callback = |
609 | 0 | new PromiseResolverCallback(nullptr, mPromise); |
610 | 0 | mPromise = nullptr; |
611 | 0 |
|
612 | 0 | swm->SoftUpdateInternal(mAttrs, mScope, callback); |
613 | 0 | } else { |
614 | 0 | swm->SoftUpdate(mAttrs, mScope); |
615 | 0 | } |
616 | 0 |
|
617 | 0 | return NS_OK; |
618 | 0 | } |
619 | | |
620 | | nsresult |
621 | | Cancel() override |
622 | 0 | { |
623 | 0 | mPromise = nullptr; |
624 | 0 | return NS_OK; |
625 | 0 | } |
626 | | |
627 | | private: |
628 | | ~SoftUpdateRunnable() |
629 | 0 | { |
630 | 0 | if (mPromise) { |
631 | 0 | mPromise->Resolve(true, __func__); |
632 | 0 | } |
633 | 0 | } |
634 | | |
635 | | const OriginAttributes mAttrs; |
636 | | const nsCString mScope; |
637 | | bool mInternalMethod; |
638 | | |
639 | | RefPtr<GenericPromise::Private> mPromise; |
640 | | }; |
641 | | |
642 | | // This runnable is used for 3 different tasks: |
643 | | // - to postpone the Update() until the IPC SWM actor is created |
644 | | // (aType == ePostpone) |
645 | | // - to call the 'real' Update when the ServiceWorkerUpdaterChild is |
646 | | // notified by the parent (aType == eSuccess) |
647 | | // - an error must be propagated (aType == eFailure) |
648 | | class UpdateRunnable final : public CancelableRunnable |
649 | | { |
650 | | public: |
651 | | enum Type { |
652 | | ePostpone, |
653 | | eSuccess, |
654 | | eFailure, |
655 | | }; |
656 | | |
657 | | UpdateRunnable(nsIPrincipal* aPrincipal, |
658 | | const nsACString& aScope, |
659 | | ServiceWorkerUpdateFinishCallback* aCallback, |
660 | | Type aType, |
661 | | GenericPromise::Private* aPromise) |
662 | | : CancelableRunnable("dom::ServiceWorkerManager::UpdateRunnable") |
663 | | , mPrincipal(aPrincipal) |
664 | | , mScope(aScope) |
665 | | , mCallback(aCallback) |
666 | | , mType(aType) |
667 | | , mPromise(aPromise) |
668 | 0 | {} |
669 | | |
670 | | NS_IMETHOD Run() override |
671 | 0 | { |
672 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
673 | 0 |
|
674 | 0 | RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); |
675 | 0 | if (!swm) { |
676 | 0 | return NS_ERROR_FAILURE; |
677 | 0 | } |
678 | 0 | |
679 | 0 | if (mType == ePostpone) { |
680 | 0 | swm->Update(mPrincipal, mScope, mCallback); |
681 | 0 | return NS_OK; |
682 | 0 | } |
683 | 0 | |
684 | 0 | MOZ_ASSERT(mPromise); |
685 | 0 |
|
686 | 0 | RefPtr<PromiseResolverCallback> callback = |
687 | 0 | new PromiseResolverCallback(mCallback, mPromise); |
688 | 0 | mPromise = nullptr; |
689 | 0 |
|
690 | 0 | if (mType == eSuccess) { |
691 | 0 | swm->UpdateInternal(mPrincipal, mScope, callback); |
692 | 0 | return NS_OK; |
693 | 0 | } |
694 | 0 | |
695 | 0 | ErrorResult error(NS_ERROR_DOM_ABORT_ERR); |
696 | 0 | callback->UpdateFailed(error); |
697 | 0 | return NS_OK; |
698 | 0 | } |
699 | | |
700 | | nsresult |
701 | | Cancel() override |
702 | 0 | { |
703 | 0 | mPromise = nullptr; |
704 | 0 | return NS_OK; |
705 | 0 | } |
706 | | |
707 | | private: |
708 | | ~UpdateRunnable() |
709 | 0 | { |
710 | 0 | if (mPromise) { |
711 | 0 | mPromise->Resolve(true, __func__); |
712 | 0 | } |
713 | 0 | } |
714 | | |
715 | | nsCOMPtr<nsIPrincipal> mPrincipal; |
716 | | const nsCString mScope; |
717 | | RefPtr<ServiceWorkerUpdateFinishCallback> mCallback; |
718 | | Type mType; |
719 | | |
720 | | RefPtr<GenericPromise::Private> mPromise; |
721 | | }; |
722 | | |
723 | | class ResolvePromiseRunnable final : public CancelableRunnable |
724 | | { |
725 | | public: |
726 | | explicit ResolvePromiseRunnable(GenericPromise::Private* aPromise) |
727 | | : CancelableRunnable("dom::ServiceWorkerManager::ResolvePromiseRunnable") |
728 | | , mPromise(aPromise) |
729 | 0 | {} |
730 | | |
731 | | NS_IMETHOD |
732 | | Run() override |
733 | 0 | { |
734 | 0 | MaybeResolve(); |
735 | 0 | return NS_OK; |
736 | 0 | } |
737 | | |
738 | | nsresult |
739 | | Cancel() override |
740 | 0 | { |
741 | 0 | mPromise = nullptr; |
742 | 0 | return NS_OK; |
743 | 0 | } |
744 | | |
745 | | private: |
746 | | ~ResolvePromiseRunnable() |
747 | 0 | { |
748 | 0 | MaybeResolve(); |
749 | 0 | } |
750 | | |
751 | | void |
752 | | MaybeResolve() |
753 | 0 | { |
754 | 0 | if (mPromise) { |
755 | 0 | mPromise->Resolve(true, __func__); |
756 | 0 | mPromise = nullptr; |
757 | 0 | } |
758 | 0 | } |
759 | | |
760 | | RefPtr<GenericPromise::Private> mPromise; |
761 | | }; |
762 | | |
763 | | } // namespace |
764 | | |
765 | | RefPtr<ServiceWorkerRegistrationPromise> |
766 | | ServiceWorkerManager::Register(const ClientInfo& aClientInfo, |
767 | | const nsACString& aScopeURL, |
768 | | const nsACString& aScriptURL, |
769 | | ServiceWorkerUpdateViaCache aUpdateViaCache) |
770 | 0 | { |
771 | 0 | nsCOMPtr<nsIURI> scopeURI; |
772 | 0 | nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), aScopeURL, nullptr, nullptr); |
773 | 0 | if (NS_FAILED(rv)) { |
774 | 0 | return ServiceWorkerRegistrationPromise::CreateAndReject(rv, __func__); |
775 | 0 | } |
776 | 0 | |
777 | 0 | nsCOMPtr<nsIURI> scriptURI; |
778 | 0 | rv = NS_NewURI(getter_AddRefs(scriptURI), aScriptURL, nullptr, nullptr); |
779 | 0 | if (NS_FAILED(rv)) { |
780 | 0 | return ServiceWorkerRegistrationPromise::CreateAndReject(rv, __func__); |
781 | 0 | } |
782 | 0 | |
783 | 0 | rv = ServiceWorkerScopeAndScriptAreValid(aClientInfo, scopeURI, scriptURI); |
784 | 0 | if (NS_FAILED(rv)) { |
785 | 0 | return ServiceWorkerRegistrationPromise::CreateAndReject(rv, __func__); |
786 | 0 | } |
787 | 0 | |
788 | 0 | // If the previous validation step passed then we must have a principal. |
789 | 0 | nsCOMPtr<nsIPrincipal> principal = aClientInfo.GetPrincipal(); |
790 | 0 |
|
791 | 0 | nsAutoCString scopeKey; |
792 | 0 | rv = PrincipalToScopeKey(principal, scopeKey); |
793 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
794 | 0 | return ServiceWorkerRegistrationPromise::CreateAndReject(rv, __func__); |
795 | 0 | } |
796 | 0 | |
797 | 0 | RefPtr<ServiceWorkerJobQueue> queue = GetOrCreateJobQueue(scopeKey, |
798 | 0 | aScopeURL); |
799 | 0 |
|
800 | 0 | RefPtr<ServiceWorkerResolveWindowPromiseOnRegisterCallback> cb = |
801 | 0 | new ServiceWorkerResolveWindowPromiseOnRegisterCallback(); |
802 | 0 |
|
803 | 0 | nsCOMPtr<nsILoadGroup> loadGroup = do_CreateInstance(NS_LOADGROUP_CONTRACTID); |
804 | 0 | RefPtr<ServiceWorkerRegisterJob> job = new ServiceWorkerRegisterJob( |
805 | 0 | principal, aScopeURL, aScriptURL, loadGroup, |
806 | 0 | static_cast<ServiceWorkerUpdateViaCache>(aUpdateViaCache) |
807 | 0 | ); |
808 | 0 |
|
809 | 0 | job->AppendResultCallback(cb); |
810 | 0 | queue->ScheduleJob(job); |
811 | 0 |
|
812 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
813 | 0 | Telemetry::Accumulate(Telemetry::SERVICE_WORKER_REGISTRATIONS, 1); |
814 | 0 |
|
815 | 0 | return cb->Promise(); |
816 | 0 | } |
817 | | |
818 | | /* |
819 | | * Implements the async aspects of the getRegistrations algorithm. |
820 | | */ |
821 | | class GetRegistrationsRunnable final : public Runnable |
822 | | { |
823 | | const ClientInfo mClientInfo; |
824 | | RefPtr<ServiceWorkerRegistrationListPromise::Private> mPromise; |
825 | | public: |
826 | | explicit GetRegistrationsRunnable(const ClientInfo& aClientInfo) |
827 | | : Runnable("dom::ServiceWorkerManager::GetRegistrationsRunnable") |
828 | | , mClientInfo(aClientInfo) |
829 | | , mPromise(new ServiceWorkerRegistrationListPromise::Private(__func__)) |
830 | 0 | {} |
831 | | |
832 | | RefPtr<ServiceWorkerRegistrationListPromise> |
833 | | Promise() const |
834 | 0 | { |
835 | 0 | return mPromise; |
836 | 0 | } |
837 | | |
838 | | NS_IMETHOD |
839 | | Run() override |
840 | 0 | { |
841 | 0 | auto scopeExit = MakeScopeExit([&] { |
842 | 0 | mPromise->Reject(NS_ERROR_DOM_INVALID_STATE_ERR, __func__); |
843 | 0 | }); |
844 | 0 |
|
845 | 0 | RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); |
846 | 0 | if (!swm) { |
847 | 0 | return NS_OK; |
848 | 0 | } |
849 | 0 | |
850 | 0 | nsCOMPtr<nsIPrincipal> principal = mClientInfo.GetPrincipal(); |
851 | 0 | if (!principal) { |
852 | 0 | return NS_OK; |
853 | 0 | } |
854 | 0 | |
855 | 0 | nsTArray<ServiceWorkerRegistrationDescriptor> array; |
856 | 0 |
|
857 | 0 | if (NS_WARN_IF(!BasePrincipal::Cast(principal)->IsCodebasePrincipal())) { |
858 | 0 | return NS_OK; |
859 | 0 | } |
860 | 0 | |
861 | 0 | nsAutoCString scopeKey; |
862 | 0 | nsresult rv = swm->PrincipalToScopeKey(principal, scopeKey); |
863 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
864 | 0 | return rv; |
865 | 0 | } |
866 | 0 | |
867 | 0 | ServiceWorkerManager::RegistrationDataPerPrincipal* data; |
868 | 0 | if (!swm->mRegistrationInfos.Get(scopeKey, &data)) { |
869 | 0 | scopeExit.release(); |
870 | 0 | mPromise->Resolve(array, __func__); |
871 | 0 | return NS_OK; |
872 | 0 | } |
873 | 0 | |
874 | 0 | for (uint32_t i = 0; i < data->mOrderedScopes.Length(); ++i) { |
875 | 0 | RefPtr<ServiceWorkerRegistrationInfo> info = |
876 | 0 | data->mInfos.GetWeak(data->mOrderedScopes[i]); |
877 | 0 | if (info->IsPendingUninstall()) { |
878 | 0 | continue; |
879 | 0 | } |
880 | 0 | |
881 | 0 | NS_ConvertUTF8toUTF16 scope(data->mOrderedScopes[i]); |
882 | 0 |
|
883 | 0 | nsCOMPtr<nsIURI> scopeURI; |
884 | 0 | nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), scope, nullptr, nullptr); |
885 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
886 | 0 | break; |
887 | 0 | } |
888 | 0 | |
889 | 0 | rv = principal->CheckMayLoad(scopeURI, true /* report */, |
890 | 0 | false /* allowIfInheritsPrincipal */); |
891 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
892 | 0 | continue; |
893 | 0 | } |
894 | 0 | |
895 | 0 | array.AppendElement(info->Descriptor()); |
896 | 0 | } |
897 | 0 |
|
898 | 0 | scopeExit.release(); |
899 | 0 | mPromise->Resolve(array, __func__); |
900 | 0 |
|
901 | 0 | return NS_OK; |
902 | 0 | } |
903 | | }; |
904 | | |
905 | | RefPtr<ServiceWorkerRegistrationListPromise> |
906 | | ServiceWorkerManager::GetRegistrations(const ClientInfo& aClientInfo) const |
907 | 0 | { |
908 | 0 | RefPtr<GetRegistrationsRunnable> runnable = |
909 | 0 | new GetRegistrationsRunnable(aClientInfo); |
910 | 0 | MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(runnable)); |
911 | 0 | return runnable->Promise();; |
912 | 0 | } |
913 | | |
914 | | /* |
915 | | * Implements the async aspects of the getRegistration algorithm. |
916 | | */ |
917 | | class GetRegistrationRunnable final : public Runnable |
918 | | { |
919 | | const ClientInfo mClientInfo; |
920 | | RefPtr<ServiceWorkerRegistrationPromise::Private> mPromise; |
921 | | nsCString mURL; |
922 | | |
923 | | public: |
924 | | GetRegistrationRunnable(const ClientInfo& aClientInfo, |
925 | | const nsACString& aURL) |
926 | | : Runnable("dom::ServiceWorkerManager::GetRegistrationRunnable") |
927 | | , mClientInfo(aClientInfo) |
928 | | , mPromise(new ServiceWorkerRegistrationPromise::Private(__func__)) |
929 | | , mURL(aURL) |
930 | 0 | {} |
931 | | |
932 | | RefPtr<ServiceWorkerRegistrationPromise> |
933 | | Promise() const |
934 | 0 | { |
935 | 0 | return mPromise; |
936 | 0 | } |
937 | | |
938 | | NS_IMETHOD |
939 | | Run() override |
940 | 0 | { |
941 | 0 | RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); |
942 | 0 | if (!swm) { |
943 | 0 | mPromise->Reject(NS_ERROR_DOM_INVALID_STATE_ERR, __func__); |
944 | 0 | return NS_OK; |
945 | 0 | } |
946 | 0 | |
947 | 0 | nsCOMPtr<nsIPrincipal> principal = mClientInfo.GetPrincipal(); |
948 | 0 | if (!principal) { |
949 | 0 | mPromise->Reject(NS_ERROR_DOM_INVALID_STATE_ERR, __func__); |
950 | 0 | return NS_OK; |
951 | 0 | } |
952 | 0 | |
953 | 0 | nsCOMPtr<nsIURI> uri; |
954 | 0 | nsresult rv = NS_NewURI(getter_AddRefs(uri), mURL, nullptr, nullptr); |
955 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
956 | 0 | mPromise->Reject(rv, __func__); |
957 | 0 | return NS_OK; |
958 | 0 | } |
959 | 0 | |
960 | 0 | rv = principal->CheckMayLoad(uri, true /* report */, |
961 | 0 | false /* allowIfInheritsPrinciple */); |
962 | 0 | if (NS_FAILED(rv)) { |
963 | 0 | mPromise->Reject(NS_ERROR_DOM_SECURITY_ERR, __func__); |
964 | 0 | return NS_OK; |
965 | 0 | } |
966 | 0 | |
967 | 0 | RefPtr<ServiceWorkerRegistrationInfo> registration = |
968 | 0 | swm->GetServiceWorkerRegistrationInfo(principal, uri); |
969 | 0 |
|
970 | 0 | if (!registration) { |
971 | 0 | // Reject with NS_OK means "not found". |
972 | 0 | mPromise->Reject(NS_OK, __func__); |
973 | 0 | return NS_OK; |
974 | 0 | } |
975 | 0 | |
976 | 0 | mPromise->Resolve(registration->Descriptor(), __func__); |
977 | 0 |
|
978 | 0 | return NS_OK; |
979 | 0 | } |
980 | | }; |
981 | | |
982 | | RefPtr<ServiceWorkerRegistrationPromise> |
983 | | ServiceWorkerManager::GetRegistration(const ClientInfo& aClientInfo, |
984 | | const nsACString& aURL) const |
985 | 0 | { |
986 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
987 | 0 |
|
988 | 0 | RefPtr<GetRegistrationRunnable> runnable = |
989 | 0 | new GetRegistrationRunnable(aClientInfo, aURL); |
990 | 0 | MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(runnable)); |
991 | 0 |
|
992 | 0 | return runnable->Promise(); |
993 | 0 | } |
994 | | |
995 | | NS_IMETHODIMP |
996 | | ServiceWorkerManager::SendPushEvent(const nsACString& aOriginAttributes, |
997 | | const nsACString& aScope, |
998 | | uint32_t aDataLength, |
999 | | uint8_t* aDataBytes, |
1000 | | uint8_t optional_argc) |
1001 | 0 | { |
1002 | 0 | if (optional_argc == 2) { |
1003 | 0 | nsTArray<uint8_t> data; |
1004 | 0 | if (!data.InsertElementsAt(0, aDataBytes, aDataLength, fallible)) { |
1005 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
1006 | 0 | } |
1007 | 0 | return SendPushEvent(aOriginAttributes, aScope, EmptyString(), Some(data)); |
1008 | 0 | } |
1009 | 0 | MOZ_ASSERT(optional_argc == 0); |
1010 | 0 | return SendPushEvent(aOriginAttributes, aScope, EmptyString(), Nothing()); |
1011 | 0 | } |
1012 | | |
1013 | | nsresult |
1014 | | ServiceWorkerManager::SendPushEvent(const nsACString& aOriginAttributes, |
1015 | | const nsACString& aScope, |
1016 | | const nsAString& aMessageId, |
1017 | | const Maybe<nsTArray<uint8_t>>& aData) |
1018 | 0 | { |
1019 | 0 | OriginAttributes attrs; |
1020 | 0 | if (!attrs.PopulateFromSuffix(aOriginAttributes)) { |
1021 | 0 | return NS_ERROR_INVALID_ARG; |
1022 | 0 | } |
1023 | 0 | |
1024 | 0 | ServiceWorkerInfo* serviceWorker = GetActiveWorkerInfoForScope(attrs, aScope); |
1025 | 0 | if (NS_WARN_IF(!serviceWorker)) { |
1026 | 0 | return NS_ERROR_FAILURE; |
1027 | 0 | } |
1028 | 0 | |
1029 | 0 | RefPtr<ServiceWorkerRegistrationInfo> registration = |
1030 | 0 | GetRegistration(serviceWorker->Principal(), aScope); |
1031 | 0 | MOZ_DIAGNOSTIC_ASSERT(registration); |
1032 | 0 |
|
1033 | 0 | return serviceWorker->WorkerPrivate()->SendPushEvent(aMessageId, aData, |
1034 | 0 | registration); |
1035 | 0 | } |
1036 | | |
1037 | | NS_IMETHODIMP |
1038 | | ServiceWorkerManager::SendPushSubscriptionChangeEvent(const nsACString& aOriginAttributes, |
1039 | | const nsACString& aScope) |
1040 | 0 | { |
1041 | 0 | OriginAttributes attrs; |
1042 | 0 | if (!attrs.PopulateFromSuffix(aOriginAttributes)) { |
1043 | 0 | return NS_ERROR_INVALID_ARG; |
1044 | 0 | } |
1045 | 0 | |
1046 | 0 | ServiceWorkerInfo* info = GetActiveWorkerInfoForScope(attrs, aScope); |
1047 | 0 | if (!info) { |
1048 | 0 | return NS_ERROR_FAILURE; |
1049 | 0 | } |
1050 | 0 | return info->WorkerPrivate()->SendPushSubscriptionChangeEvent(); |
1051 | 0 | } |
1052 | | |
1053 | | nsresult |
1054 | | ServiceWorkerManager::SendNotificationEvent(const nsAString& aEventName, |
1055 | | const nsACString& aOriginSuffix, |
1056 | | const nsACString& aScope, |
1057 | | const nsAString& aID, |
1058 | | const nsAString& aTitle, |
1059 | | const nsAString& aDir, |
1060 | | const nsAString& aLang, |
1061 | | const nsAString& aBody, |
1062 | | const nsAString& aTag, |
1063 | | const nsAString& aIcon, |
1064 | | const nsAString& aData, |
1065 | | const nsAString& aBehavior) |
1066 | 0 | { |
1067 | 0 | OriginAttributes attrs; |
1068 | 0 | if (!attrs.PopulateFromSuffix(aOriginSuffix)) { |
1069 | 0 | return NS_ERROR_INVALID_ARG; |
1070 | 0 | } |
1071 | 0 | |
1072 | 0 | ServiceWorkerInfo* info = GetActiveWorkerInfoForScope(attrs, aScope); |
1073 | 0 | if (!info) { |
1074 | 0 | return NS_ERROR_FAILURE; |
1075 | 0 | } |
1076 | 0 | |
1077 | 0 | ServiceWorkerPrivate* workerPrivate = info->WorkerPrivate(); |
1078 | 0 | return workerPrivate->SendNotificationEvent(aEventName, aID, aTitle, aDir, |
1079 | 0 | aLang, aBody, aTag, |
1080 | 0 | aIcon, aData, aBehavior, |
1081 | 0 | NS_ConvertUTF8toUTF16(aScope)); |
1082 | 0 | } |
1083 | | |
1084 | | NS_IMETHODIMP |
1085 | | ServiceWorkerManager::SendNotificationClickEvent(const nsACString& aOriginSuffix, |
1086 | | const nsACString& aScope, |
1087 | | const nsAString& aID, |
1088 | | const nsAString& aTitle, |
1089 | | const nsAString& aDir, |
1090 | | const nsAString& aLang, |
1091 | | const nsAString& aBody, |
1092 | | const nsAString& aTag, |
1093 | | const nsAString& aIcon, |
1094 | | const nsAString& aData, |
1095 | | const nsAString& aBehavior) |
1096 | 0 | { |
1097 | 0 | return SendNotificationEvent(NS_LITERAL_STRING(NOTIFICATION_CLICK_EVENT_NAME), |
1098 | 0 | aOriginSuffix, aScope, aID, aTitle, aDir, aLang, |
1099 | 0 | aBody, aTag, aIcon, aData, aBehavior); |
1100 | 0 | } |
1101 | | |
1102 | | NS_IMETHODIMP |
1103 | | ServiceWorkerManager::SendNotificationCloseEvent(const nsACString& aOriginSuffix, |
1104 | | const nsACString& aScope, |
1105 | | const nsAString& aID, |
1106 | | const nsAString& aTitle, |
1107 | | const nsAString& aDir, |
1108 | | const nsAString& aLang, |
1109 | | const nsAString& aBody, |
1110 | | const nsAString& aTag, |
1111 | | const nsAString& aIcon, |
1112 | | const nsAString& aData, |
1113 | | const nsAString& aBehavior) |
1114 | 0 | { |
1115 | 0 | return SendNotificationEvent(NS_LITERAL_STRING(NOTIFICATION_CLOSE_EVENT_NAME), |
1116 | 0 | aOriginSuffix, aScope, aID, aTitle, aDir, aLang, |
1117 | 0 | aBody, aTag, aIcon, aData, aBehavior); |
1118 | 0 | } |
1119 | | |
1120 | | RefPtr<ServiceWorkerRegistrationPromise> |
1121 | | ServiceWorkerManager::WhenReady(const ClientInfo& aClientInfo) |
1122 | 0 | { |
1123 | 0 | AssertIsOnMainThread(); |
1124 | 0 |
|
1125 | 0 | for (auto &prd : mPendingReadyList) { |
1126 | 0 | if (prd->mClientHandle->Info().Id() == aClientInfo.Id() && |
1127 | 0 | prd->mClientHandle->Info().PrincipalInfo() == aClientInfo.PrincipalInfo()) { |
1128 | 0 | return prd->mPromise; |
1129 | 0 | } |
1130 | 0 | } |
1131 | 0 |
|
1132 | 0 | RefPtr<ServiceWorkerRegistrationInfo> reg = |
1133 | 0 | GetServiceWorkerRegistrationInfo(aClientInfo); |
1134 | 0 | if (reg && reg->GetActive()) { |
1135 | 0 | return ServiceWorkerRegistrationPromise::CreateAndResolve(reg->Descriptor(), |
1136 | 0 | __func__); |
1137 | 0 | } |
1138 | 0 | |
1139 | 0 | nsCOMPtr<nsISerialEventTarget> target = |
1140 | 0 | SystemGroup::EventTargetFor(TaskCategory::Other); |
1141 | 0 |
|
1142 | 0 | RefPtr<ClientHandle> handle = ClientManager::CreateHandle(aClientInfo, target); |
1143 | 0 | mPendingReadyList.AppendElement(MakeUnique<PendingReadyData>(handle)); |
1144 | 0 |
|
1145 | 0 | RefPtr<ServiceWorkerManager> self(this); |
1146 | 0 | handle->OnDetach()->Then( target, __func__, |
1147 | 0 | [self = std::move(self), aClientInfo] { |
1148 | 0 | self->RemovePendingReadyPromise(aClientInfo); |
1149 | 0 | }); |
1150 | 0 |
|
1151 | 0 | return mPendingReadyList.LastElement()->mPromise; |
1152 | 0 | } |
1153 | | |
1154 | | void |
1155 | | ServiceWorkerManager::CheckPendingReadyPromises() |
1156 | 0 | { |
1157 | 0 | nsTArray<UniquePtr<PendingReadyData>> pendingReadyList; |
1158 | 0 | mPendingReadyList.SwapElements(pendingReadyList); |
1159 | 0 | for (uint32_t i = 0; i < pendingReadyList.Length(); ++i) { |
1160 | 0 | UniquePtr<PendingReadyData> prd(std::move(pendingReadyList[i])); |
1161 | 0 |
|
1162 | 0 | RefPtr<ServiceWorkerRegistrationInfo> reg = |
1163 | 0 | GetServiceWorkerRegistrationInfo(prd->mClientHandle->Info()); |
1164 | 0 |
|
1165 | 0 | if (reg && reg->GetActive()) { |
1166 | 0 | prd->mPromise->Resolve(reg->Descriptor(), __func__); |
1167 | 0 | } else { |
1168 | 0 | mPendingReadyList.AppendElement(std::move(prd)); |
1169 | 0 | } |
1170 | 0 | } |
1171 | 0 | } |
1172 | | |
1173 | | void |
1174 | | ServiceWorkerManager::RemovePendingReadyPromise(const ClientInfo& aClientInfo) |
1175 | 0 | { |
1176 | 0 | nsTArray<UniquePtr<PendingReadyData>> pendingReadyList; |
1177 | 0 | mPendingReadyList.SwapElements(pendingReadyList); |
1178 | 0 | for (uint32_t i = 0; i < pendingReadyList.Length(); ++i) { |
1179 | 0 | UniquePtr<PendingReadyData> prd(std::move(pendingReadyList[i])); |
1180 | 0 |
|
1181 | 0 | if (prd->mClientHandle->Info().Id() == aClientInfo.Id() && |
1182 | 0 | prd->mClientHandle->Info().PrincipalInfo() == aClientInfo.PrincipalInfo()) { |
1183 | 0 | prd->mPromise->Reject(NS_ERROR_DOM_ABORT_ERR, __func__); |
1184 | 0 | } else { |
1185 | 0 | mPendingReadyList.AppendElement(std::move(prd)); |
1186 | 0 | } |
1187 | 0 | } |
1188 | 0 | } |
1189 | | |
1190 | | void |
1191 | | ServiceWorkerManager::NoteInheritedController(const ClientInfo& aClientInfo, |
1192 | | const ServiceWorkerDescriptor& aController) |
1193 | 0 | { |
1194 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
1195 | 0 |
|
1196 | 0 | nsCOMPtr<nsIPrincipal> principal = |
1197 | 0 | PrincipalInfoToPrincipal(aController.PrincipalInfo()); |
1198 | 0 | NS_ENSURE_TRUE_VOID(principal); |
1199 | 0 |
|
1200 | 0 | nsCOMPtr<nsIURI> scope; |
1201 | 0 | nsresult rv = |
1202 | 0 | NS_NewURI(getter_AddRefs(scope), aController.Scope(), nullptr, nullptr); |
1203 | 0 | NS_ENSURE_SUCCESS_VOID(rv); |
1204 | 0 |
|
1205 | 0 | RefPtr<ServiceWorkerRegistrationInfo> registration = |
1206 | 0 | GetServiceWorkerRegistrationInfo(principal, scope); |
1207 | 0 | NS_ENSURE_TRUE_VOID(registration); |
1208 | 0 | NS_ENSURE_TRUE_VOID(registration->GetActive()); |
1209 | 0 |
|
1210 | 0 | StartControllingClient(aClientInfo, registration, false /* aControlClientHandle */); |
1211 | 0 | } |
1212 | | |
1213 | | ServiceWorkerInfo* |
1214 | | ServiceWorkerManager::GetActiveWorkerInfoForScope(const OriginAttributes& aOriginAttributes, |
1215 | | const nsACString& aScope) |
1216 | 0 | { |
1217 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
1218 | 0 |
|
1219 | 0 | nsCOMPtr<nsIURI> scopeURI; |
1220 | 0 | nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), aScope, nullptr, nullptr); |
1221 | 0 | if (NS_FAILED(rv)) { |
1222 | 0 | return nullptr; |
1223 | 0 | } |
1224 | 0 | nsCOMPtr<nsIPrincipal> principal = |
1225 | 0 | BasePrincipal::CreateCodebasePrincipal(scopeURI, aOriginAttributes); |
1226 | 0 | RefPtr<ServiceWorkerRegistrationInfo> registration = |
1227 | 0 | GetServiceWorkerRegistrationInfo(principal, scopeURI); |
1228 | 0 | if (!registration) { |
1229 | 0 | return nullptr; |
1230 | 0 | } |
1231 | 0 | |
1232 | 0 | return registration->GetActive(); |
1233 | 0 | } |
1234 | | |
1235 | | namespace { |
1236 | | |
1237 | | class UnregisterJobCallback final : public ServiceWorkerJob::Callback |
1238 | | { |
1239 | | nsCOMPtr<nsIServiceWorkerUnregisterCallback> mCallback; |
1240 | | |
1241 | | ~UnregisterJobCallback() |
1242 | 0 | { |
1243 | 0 | } |
1244 | | |
1245 | | public: |
1246 | | explicit UnregisterJobCallback(nsIServiceWorkerUnregisterCallback* aCallback) |
1247 | | : mCallback(aCallback) |
1248 | 0 | { |
1249 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
1250 | 0 | MOZ_ASSERT(mCallback); |
1251 | 0 | } |
1252 | | |
1253 | | void |
1254 | | JobFinished(ServiceWorkerJob* aJob, ErrorResult& aStatus) override |
1255 | 0 | { |
1256 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
1257 | 0 | MOZ_ASSERT(aJob); |
1258 | 0 |
|
1259 | 0 | if (aStatus.Failed()) { |
1260 | 0 | mCallback->UnregisterFailed(); |
1261 | 0 | return; |
1262 | 0 | } |
1263 | 0 | |
1264 | 0 | MOZ_ASSERT(aJob->GetType() == ServiceWorkerJob::Type::Unregister); |
1265 | 0 | RefPtr<ServiceWorkerUnregisterJob> unregisterJob = |
1266 | 0 | static_cast<ServiceWorkerUnregisterJob*>(aJob); |
1267 | 0 | mCallback->UnregisterSucceeded(unregisterJob->GetResult()); |
1268 | 0 | } |
1269 | | |
1270 | | NS_INLINE_DECL_REFCOUNTING(UnregisterJobCallback, override) |
1271 | | }; |
1272 | | |
1273 | | } // anonymous namespace |
1274 | | |
1275 | | NS_IMETHODIMP |
1276 | | ServiceWorkerManager::Unregister(nsIPrincipal* aPrincipal, |
1277 | | nsIServiceWorkerUnregisterCallback* aCallback, |
1278 | | const nsAString& aScope) |
1279 | 0 | { |
1280 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
1281 | 0 |
|
1282 | 0 | if (!aPrincipal) { |
1283 | 0 | return NS_ERROR_FAILURE; |
1284 | 0 | } |
1285 | 0 | |
1286 | 0 | nsresult rv; |
1287 | 0 |
|
1288 | 0 | // This is not accessible by content, and callers should always ensure scope is |
1289 | 0 | // a correct URI, so this is wrapped in DEBUG |
1290 | | #ifdef DEBUG |
1291 | | nsCOMPtr<nsIURI> scopeURI; |
1292 | | rv = NS_NewURI(getter_AddRefs(scopeURI), aScope, nullptr, nullptr); |
1293 | | if (NS_WARN_IF(NS_FAILED(rv))) { |
1294 | | return NS_ERROR_DOM_SECURITY_ERR; |
1295 | | } |
1296 | | #endif |
1297 | |
|
1298 | 0 | nsAutoCString scopeKey; |
1299 | 0 | rv = PrincipalToScopeKey(aPrincipal, scopeKey); |
1300 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
1301 | 0 | return rv; |
1302 | 0 | } |
1303 | 0 | |
1304 | 0 | NS_ConvertUTF16toUTF8 scope(aScope); |
1305 | 0 | RefPtr<ServiceWorkerJobQueue> queue = GetOrCreateJobQueue(scopeKey, scope); |
1306 | 0 |
|
1307 | 0 | RefPtr<ServiceWorkerUnregisterJob> job = |
1308 | 0 | new ServiceWorkerUnregisterJob(aPrincipal, scope, true /* send to parent */); |
1309 | 0 |
|
1310 | 0 | if (aCallback) { |
1311 | 0 | RefPtr<UnregisterJobCallback> cb = new UnregisterJobCallback(aCallback); |
1312 | 0 | job->AppendResultCallback(cb); |
1313 | 0 | } |
1314 | 0 |
|
1315 | 0 | queue->ScheduleJob(job); |
1316 | 0 | return NS_OK; |
1317 | 0 | } |
1318 | | |
1319 | | nsresult |
1320 | | ServiceWorkerManager::NotifyUnregister(nsIPrincipal* aPrincipal, |
1321 | | const nsAString& aScope) |
1322 | 0 | { |
1323 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
1324 | 0 | MOZ_ASSERT(aPrincipal); |
1325 | 0 |
|
1326 | 0 | nsresult rv; |
1327 | 0 |
|
1328 | 0 | // This is not accessible by content, and callers should always ensure scope is |
1329 | 0 | // a correct URI, so this is wrapped in DEBUG |
1330 | | #ifdef DEBUG |
1331 | | nsCOMPtr<nsIURI> scopeURI; |
1332 | | rv = NS_NewURI(getter_AddRefs(scopeURI), aScope, nullptr, nullptr); |
1333 | | if (NS_WARN_IF(NS_FAILED(rv))) { |
1334 | | return rv; |
1335 | | } |
1336 | | #endif |
1337 | |
|
1338 | 0 | nsAutoCString scopeKey; |
1339 | 0 | rv = PrincipalToScopeKey(aPrincipal, scopeKey); |
1340 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
1341 | 0 | return rv; |
1342 | 0 | } |
1343 | 0 | |
1344 | 0 | NS_ConvertUTF16toUTF8 scope(aScope); |
1345 | 0 | RefPtr<ServiceWorkerJobQueue> queue = GetOrCreateJobQueue(scopeKey, scope); |
1346 | 0 |
|
1347 | 0 | RefPtr<ServiceWorkerUnregisterJob> job = |
1348 | 0 | new ServiceWorkerUnregisterJob(aPrincipal, scope, |
1349 | 0 | false /* send to parent */); |
1350 | 0 |
|
1351 | 0 | queue->ScheduleJob(job); |
1352 | 0 | return NS_OK; |
1353 | 0 | } |
1354 | | |
1355 | | void |
1356 | | ServiceWorkerManager::WorkerIsIdle(ServiceWorkerInfo* aWorker) |
1357 | 0 | { |
1358 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
1359 | 0 | MOZ_DIAGNOSTIC_ASSERT(aWorker); |
1360 | 0 |
|
1361 | 0 | RefPtr<ServiceWorkerRegistrationInfo> reg = |
1362 | 0 | GetRegistration(aWorker->Principal(), aWorker->Scope()); |
1363 | 0 | if (!reg) { |
1364 | 0 | return; |
1365 | 0 | } |
1366 | 0 | |
1367 | 0 | if (reg->GetActive() != aWorker) { |
1368 | 0 | return; |
1369 | 0 | } |
1370 | 0 | |
1371 | 0 | if (!reg->IsControllingClients() && reg->IsPendingUninstall()) { |
1372 | 0 | RemoveRegistration(reg); |
1373 | 0 | return; |
1374 | 0 | } |
1375 | 0 | |
1376 | 0 | reg->TryToActivateAsync(); |
1377 | 0 | } |
1378 | | |
1379 | | already_AddRefed<ServiceWorkerJobQueue> |
1380 | | ServiceWorkerManager::GetOrCreateJobQueue(const nsACString& aKey, |
1381 | | const nsACString& aScope) |
1382 | 0 | { |
1383 | 0 | MOZ_ASSERT(!aKey.IsEmpty()); |
1384 | 0 | ServiceWorkerManager::RegistrationDataPerPrincipal* data; |
1385 | 0 | // XXX we could use LookupForAdd here to avoid a hashtable lookup, except that |
1386 | 0 | // leads to a false positive assertion, see bug 1370674 comment 7. |
1387 | 0 | if (!mRegistrationInfos.Get(aKey, &data)) { |
1388 | 0 | data = new RegistrationDataPerPrincipal(); |
1389 | 0 | mRegistrationInfos.Put(aKey, data); |
1390 | 0 | } |
1391 | 0 |
|
1392 | 0 | RefPtr<ServiceWorkerJobQueue> queue = |
1393 | 0 | data->mJobQueues.LookupForAdd(aScope).OrInsert( |
1394 | 0 | []() { return new ServiceWorkerJobQueue(); }); |
1395 | 0 |
|
1396 | 0 | return queue.forget(); |
1397 | 0 | } |
1398 | | |
1399 | | /* static */ |
1400 | | already_AddRefed<ServiceWorkerManager> |
1401 | | ServiceWorkerManager::GetInstance() |
1402 | 0 | { |
1403 | 0 | // Note: We don't simply check gInstance for null-ness here, since otherwise |
1404 | 0 | // this can resurrect the ServiceWorkerManager pretty late during shutdown. |
1405 | 0 | static bool firstTime = true; |
1406 | 0 | if (firstTime) { |
1407 | 0 | RefPtr<ServiceWorkerRegistrar> swr; |
1408 | 0 |
|
1409 | 0 | // Don't create the ServiceWorkerManager until the ServiceWorkerRegistrar is |
1410 | 0 | // initialized. |
1411 | 0 | if (XRE_IsParentProcess()) { |
1412 | 0 | swr = ServiceWorkerRegistrar::Get(); |
1413 | 0 | if (!swr) { |
1414 | 0 | return nullptr; |
1415 | 0 | } |
1416 | 0 | } |
1417 | 0 | |
1418 | 0 | firstTime = false; |
1419 | 0 |
|
1420 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
1421 | 0 |
|
1422 | 0 | gInstance = new ServiceWorkerManager(); |
1423 | 0 | gInstance->Init(swr); |
1424 | 0 | ClearOnShutdown(&gInstance); |
1425 | 0 | } |
1426 | 0 | RefPtr<ServiceWorkerManager> copy = gInstance.get(); |
1427 | 0 | return copy.forget(); |
1428 | 0 | } |
1429 | | |
1430 | | void |
1431 | | ServiceWorkerManager::FinishFetch(ServiceWorkerRegistrationInfo* aRegistration) |
1432 | 0 | { |
1433 | 0 | } |
1434 | | |
1435 | | void |
1436 | | ServiceWorkerManager::ReportToAllClients(const nsCString& aScope, |
1437 | | const nsString& aMessage, |
1438 | | const nsString& aFilename, |
1439 | | const nsString& aLine, |
1440 | | uint32_t aLineNumber, |
1441 | | uint32_t aColumnNumber, |
1442 | | uint32_t aFlags) |
1443 | 0 | { |
1444 | 0 | ConsoleUtils::ReportForServiceWorkerScope(NS_ConvertUTF8toUTF16(aScope), |
1445 | 0 | aMessage, |
1446 | 0 | aFilename, |
1447 | 0 | aLineNumber, |
1448 | 0 | aColumnNumber, |
1449 | 0 | ConsoleUtils::eError); |
1450 | 0 | } |
1451 | | |
1452 | | /* static */ |
1453 | | void |
1454 | | ServiceWorkerManager::LocalizeAndReportToAllClients( |
1455 | | const nsCString& aScope, |
1456 | | const char* aStringKey, |
1457 | | const nsTArray<nsString>& aParamArray, |
1458 | | uint32_t aFlags, |
1459 | | const nsString& aFilename, |
1460 | | const nsString& aLine, |
1461 | | uint32_t aLineNumber, |
1462 | | uint32_t aColumnNumber) |
1463 | 0 | { |
1464 | 0 | RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); |
1465 | 0 | if (!swm) { |
1466 | 0 | return; |
1467 | 0 | } |
1468 | 0 | |
1469 | 0 | nsresult rv; |
1470 | 0 | nsAutoString message; |
1471 | 0 | rv = nsContentUtils::FormatLocalizedString(nsContentUtils::eDOM_PROPERTIES, |
1472 | 0 | aStringKey, aParamArray, message); |
1473 | 0 | if (NS_SUCCEEDED(rv)) { |
1474 | 0 | swm->ReportToAllClients(aScope, message, |
1475 | 0 | aFilename, aLine, aLineNumber, aColumnNumber, |
1476 | 0 | aFlags); |
1477 | 0 | } else { |
1478 | 0 | NS_WARNING("Failed to format and therefore report localized error."); |
1479 | 0 | } |
1480 | 0 | } |
1481 | | |
1482 | | void |
1483 | | ServiceWorkerManager::HandleError(JSContext* aCx, |
1484 | | nsIPrincipal* aPrincipal, |
1485 | | const nsCString& aScope, |
1486 | | const nsString& aWorkerURL, |
1487 | | const nsString& aMessage, |
1488 | | const nsString& aFilename, |
1489 | | const nsString& aLine, |
1490 | | uint32_t aLineNumber, |
1491 | | uint32_t aColumnNumber, |
1492 | | uint32_t aFlags, |
1493 | | JSExnType aExnType) |
1494 | 0 | { |
1495 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
1496 | 0 | MOZ_ASSERT(aPrincipal); |
1497 | 0 |
|
1498 | 0 | nsAutoCString scopeKey; |
1499 | 0 | nsresult rv = PrincipalToScopeKey(aPrincipal, scopeKey); |
1500 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
1501 | 0 | return; |
1502 | 0 | } |
1503 | 0 | |
1504 | 0 | ServiceWorkerManager::RegistrationDataPerPrincipal* data; |
1505 | 0 | if (NS_WARN_IF(!mRegistrationInfos.Get(scopeKey, &data))) { |
1506 | 0 | return; |
1507 | 0 | } |
1508 | 0 | |
1509 | 0 | // Always report any uncaught exceptions or errors to the console of |
1510 | 0 | // each client. |
1511 | 0 | ReportToAllClients(aScope, aMessage, aFilename, aLine, aLineNumber, |
1512 | 0 | aColumnNumber, aFlags); |
1513 | 0 | } |
1514 | | |
1515 | | void |
1516 | | ServiceWorkerManager::LoadRegistration( |
1517 | | const ServiceWorkerRegistrationData& aRegistration) |
1518 | 0 | { |
1519 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
1520 | 0 |
|
1521 | 0 | nsCOMPtr<nsIPrincipal> principal = |
1522 | 0 | PrincipalInfoToPrincipal(aRegistration.principal()); |
1523 | 0 | if (!principal) { |
1524 | 0 | return; |
1525 | 0 | } |
1526 | 0 | |
1527 | 0 | RefPtr<ServiceWorkerRegistrationInfo> registration = |
1528 | 0 | GetRegistration(principal, aRegistration.scope()); |
1529 | 0 | if (!registration) { |
1530 | 0 | registration = |
1531 | 0 | CreateNewRegistration( |
1532 | 0 | aRegistration.scope(), |
1533 | 0 | principal, |
1534 | 0 | static_cast<ServiceWorkerUpdateViaCache>(aRegistration.updateViaCache()) |
1535 | 0 | ); |
1536 | 0 | } else { |
1537 | 0 | // If active worker script matches our expectations for a "current worker", |
1538 | 0 | // then we are done. Since scripts with the same URL might have different |
1539 | 0 | // contents such as updated scripts or scripts with different LoadFlags, we |
1540 | 0 | // use the CacheName to judje whether the two scripts are identical, where |
1541 | 0 | // the CacheName is an UUID generated when a new script is found. |
1542 | 0 | if (registration->GetActive() && |
1543 | 0 | registration->GetActive()->CacheName() == aRegistration.cacheName()) { |
1544 | 0 | // No needs for updates. |
1545 | 0 | return; |
1546 | 0 | } |
1547 | 0 | } |
1548 | 0 | |
1549 | 0 | registration->SetLastUpdateTime(aRegistration.lastUpdateTime()); |
1550 | 0 |
|
1551 | 0 | nsLoadFlags importsLoadFlags = nsIChannel::LOAD_BYPASS_SERVICE_WORKER; |
1552 | 0 | importsLoadFlags |= |
1553 | 0 | aRegistration.updateViaCache() == static_cast<uint16_t>(ServiceWorkerUpdateViaCache::None) |
1554 | 0 | ? nsIRequest::LOAD_NORMAL |
1555 | 0 | : nsIRequest::VALIDATE_ALWAYS; |
1556 | 0 |
|
1557 | 0 | const nsCString& currentWorkerURL = aRegistration.currentWorkerURL(); |
1558 | 0 | if (!currentWorkerURL.IsEmpty()) { |
1559 | 0 | registration->SetActive( |
1560 | 0 | new ServiceWorkerInfo(registration->Principal(), |
1561 | 0 | registration->Scope(), |
1562 | 0 | registration->Id(), |
1563 | 0 | registration->Version(), |
1564 | 0 | currentWorkerURL, |
1565 | 0 | aRegistration.cacheName(), |
1566 | 0 | importsLoadFlags)); |
1567 | 0 | registration->GetActive()->SetHandlesFetch(aRegistration.currentWorkerHandlesFetch()); |
1568 | 0 | registration->GetActive()->SetInstalledTime(aRegistration.currentWorkerInstalledTime()); |
1569 | 0 | registration->GetActive()->SetActivatedTime(aRegistration.currentWorkerActivatedTime()); |
1570 | 0 | } |
1571 | 0 | } |
1572 | | |
1573 | | void |
1574 | | ServiceWorkerManager::LoadRegistrations( |
1575 | | const nsTArray<ServiceWorkerRegistrationData>& aRegistrations) |
1576 | 0 | { |
1577 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
1578 | 0 |
|
1579 | 0 | for (uint32_t i = 0, len = aRegistrations.Length(); i < len; ++i) { |
1580 | 0 | LoadRegistration(aRegistrations[i]); |
1581 | 0 | } |
1582 | 0 | } |
1583 | | |
1584 | | void |
1585 | | ServiceWorkerManager::StoreRegistration( |
1586 | | nsIPrincipal* aPrincipal, |
1587 | | ServiceWorkerRegistrationInfo* aRegistration) |
1588 | 0 | { |
1589 | 0 | MOZ_ASSERT(aPrincipal); |
1590 | 0 | MOZ_ASSERT(aRegistration); |
1591 | 0 |
|
1592 | 0 | if (mShuttingDown) { |
1593 | 0 | return; |
1594 | 0 | } |
1595 | 0 | |
1596 | 0 | ServiceWorkerRegistrationData data; |
1597 | 0 | nsresult rv = PopulateRegistrationData(aPrincipal, aRegistration, data); |
1598 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
1599 | 0 | return; |
1600 | 0 | } |
1601 | 0 | |
1602 | 0 | PrincipalInfo principalInfo; |
1603 | 0 | if (NS_WARN_IF(NS_FAILED(PrincipalToPrincipalInfo(aPrincipal, |
1604 | 0 | &principalInfo)))) { |
1605 | 0 | return; |
1606 | 0 | } |
1607 | 0 | |
1608 | 0 | mActor->SendRegister(data); |
1609 | 0 | } |
1610 | | |
1611 | | already_AddRefed<ServiceWorkerRegistrationInfo> |
1612 | | ServiceWorkerManager::GetServiceWorkerRegistrationInfo(const ClientInfo& aClientInfo) const |
1613 | 0 | { |
1614 | 0 | nsCOMPtr<nsIPrincipal> principal = aClientInfo.GetPrincipal(); |
1615 | 0 | NS_ENSURE_TRUE(principal, nullptr); |
1616 | 0 |
|
1617 | 0 | nsCOMPtr<nsIURI> uri; |
1618 | 0 | nsresult rv = NS_NewURI(getter_AddRefs(uri), aClientInfo.URL(), |
1619 | 0 | nullptr, nullptr); |
1620 | 0 | NS_ENSURE_SUCCESS(rv, nullptr); |
1621 | 0 |
|
1622 | 0 | return GetServiceWorkerRegistrationInfo(principal, uri); |
1623 | 0 | } |
1624 | | |
1625 | | already_AddRefed<ServiceWorkerRegistrationInfo> |
1626 | | ServiceWorkerManager::GetServiceWorkerRegistrationInfo(nsIPrincipal* aPrincipal, |
1627 | | nsIURI* aURI) const |
1628 | 0 | { |
1629 | 0 | MOZ_ASSERT(aPrincipal); |
1630 | 0 | MOZ_ASSERT(aURI); |
1631 | 0 |
|
1632 | 0 | //XXXnsm Temporary fix until Bug 1171432 is fixed. |
1633 | 0 | if (NS_WARN_IF(BasePrincipal::Cast(aPrincipal)->AppId() == nsIScriptSecurityManager::UNKNOWN_APP_ID)) { |
1634 | 0 | return nullptr; |
1635 | 0 | } |
1636 | 0 | |
1637 | 0 | nsAutoCString scopeKey; |
1638 | 0 | nsresult rv = PrincipalToScopeKey(aPrincipal, scopeKey); |
1639 | 0 | if (NS_FAILED(rv)) { |
1640 | 0 | return nullptr; |
1641 | 0 | } |
1642 | 0 | |
1643 | 0 | return GetServiceWorkerRegistrationInfo(scopeKey, aURI); |
1644 | 0 | } |
1645 | | |
1646 | | already_AddRefed<ServiceWorkerRegistrationInfo> |
1647 | | ServiceWorkerManager::GetServiceWorkerRegistrationInfo(const nsACString& aScopeKey, |
1648 | | nsIURI* aURI) const |
1649 | 0 | { |
1650 | 0 | MOZ_ASSERT(aURI); |
1651 | 0 |
|
1652 | 0 | nsAutoCString spec; |
1653 | 0 | nsresult rv = aURI->GetSpec(spec); |
1654 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
1655 | 0 | return nullptr; |
1656 | 0 | } |
1657 | 0 | |
1658 | 0 | nsAutoCString scope; |
1659 | 0 | RegistrationDataPerPrincipal* data; |
1660 | 0 | if (!FindScopeForPath(aScopeKey, spec, &data, scope)) { |
1661 | 0 | return nullptr; |
1662 | 0 | } |
1663 | 0 | |
1664 | 0 | MOZ_ASSERT(data); |
1665 | 0 |
|
1666 | 0 | RefPtr<ServiceWorkerRegistrationInfo> registration; |
1667 | 0 | data->mInfos.Get(scope, getter_AddRefs(registration)); |
1668 | 0 | // ordered scopes and registrations better be in sync. |
1669 | 0 | MOZ_ASSERT(registration); |
1670 | 0 |
|
1671 | | #ifdef DEBUG |
1672 | | nsAutoCString origin; |
1673 | | rv = registration->Principal()->GetOrigin(origin); |
1674 | | MOZ_ASSERT(NS_SUCCEEDED(rv)); |
1675 | | MOZ_ASSERT(origin.Equals(aScopeKey)); |
1676 | | #endif |
1677 | |
|
1678 | 0 | if (registration->IsPendingUninstall()) { |
1679 | 0 | return nullptr; |
1680 | 0 | } |
1681 | 0 | return registration.forget(); |
1682 | 0 | } |
1683 | | |
1684 | | /* static */ nsresult |
1685 | | ServiceWorkerManager::PrincipalToScopeKey(nsIPrincipal* aPrincipal, |
1686 | | nsACString& aKey) |
1687 | 0 | { |
1688 | 0 | MOZ_ASSERT(aPrincipal); |
1689 | 0 |
|
1690 | 0 | if (!BasePrincipal::Cast(aPrincipal)->IsCodebasePrincipal()) { |
1691 | 0 | return NS_ERROR_FAILURE; |
1692 | 0 | } |
1693 | 0 | |
1694 | 0 | nsresult rv = aPrincipal->GetOrigin(aKey); |
1695 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
1696 | 0 | return rv; |
1697 | 0 | } |
1698 | 0 | |
1699 | 0 | return NS_OK; |
1700 | 0 | } |
1701 | | |
1702 | | /* static */ nsresult |
1703 | | ServiceWorkerManager::PrincipalInfoToScopeKey(const PrincipalInfo& aPrincipalInfo, |
1704 | | nsACString& aKey) |
1705 | 0 | { |
1706 | 0 | if (aPrincipalInfo.type() != PrincipalInfo::TContentPrincipalInfo) { |
1707 | 0 | return NS_ERROR_FAILURE; |
1708 | 0 | } |
1709 | 0 | |
1710 | 0 | auto content = aPrincipalInfo.get_ContentPrincipalInfo(); |
1711 | 0 |
|
1712 | 0 | nsAutoCString suffix; |
1713 | 0 | content.attrs().CreateSuffix(suffix); |
1714 | 0 |
|
1715 | 0 | aKey = content.originNoSuffix(); |
1716 | 0 | aKey.Append(suffix); |
1717 | 0 |
|
1718 | 0 | return NS_OK; |
1719 | 0 | } |
1720 | | |
1721 | | /* static */ void |
1722 | | ServiceWorkerManager::AddScopeAndRegistration(const nsACString& aScope, |
1723 | | ServiceWorkerRegistrationInfo* aInfo) |
1724 | 0 | { |
1725 | 0 | MOZ_ASSERT(aInfo); |
1726 | 0 | MOZ_ASSERT(aInfo->Principal()); |
1727 | 0 |
|
1728 | 0 | RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); |
1729 | 0 | if (!swm) { |
1730 | 0 | // browser shutdown |
1731 | 0 | return; |
1732 | 0 | } |
1733 | 0 | |
1734 | 0 | nsAutoCString scopeKey; |
1735 | 0 | nsresult rv = swm->PrincipalToScopeKey(aInfo->Principal(), scopeKey); |
1736 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
1737 | 0 | return; |
1738 | 0 | } |
1739 | 0 | |
1740 | 0 | MOZ_ASSERT(!scopeKey.IsEmpty()); |
1741 | 0 |
|
1742 | 0 | RegistrationDataPerPrincipal* data = |
1743 | 0 | swm->mRegistrationInfos.LookupForAdd(scopeKey).OrInsert( |
1744 | 0 | []() { return new RegistrationDataPerPrincipal(); }); |
1745 | 0 |
|
1746 | 0 | for (uint32_t i = 0; i < data->mOrderedScopes.Length(); ++i) { |
1747 | 0 | const nsCString& current = data->mOrderedScopes[i]; |
1748 | 0 |
|
1749 | 0 | // Perfect match! |
1750 | 0 | if (aScope.Equals(current)) { |
1751 | 0 | data->mInfos.Put(aScope, aInfo); |
1752 | 0 | swm->NotifyListenersOnRegister(aInfo); |
1753 | 0 | return; |
1754 | 0 | } |
1755 | 0 | |
1756 | 0 | // Sort by length, with longest match first. |
1757 | 0 | // /foo/bar should be before /foo/ |
1758 | 0 | // Similarly /foo/b is between the two. |
1759 | 0 | if (StringBeginsWith(aScope, current)) { |
1760 | 0 | data->mOrderedScopes.InsertElementAt(i, aScope); |
1761 | 0 | data->mInfos.Put(aScope, aInfo); |
1762 | 0 | swm->NotifyListenersOnRegister(aInfo); |
1763 | 0 | return; |
1764 | 0 | } |
1765 | 0 | } |
1766 | 0 |
|
1767 | 0 | data->mOrderedScopes.AppendElement(aScope); |
1768 | 0 | data->mInfos.Put(aScope, aInfo); |
1769 | 0 | swm->NotifyListenersOnRegister(aInfo); |
1770 | 0 | } |
1771 | | |
1772 | | /* static */ bool |
1773 | | ServiceWorkerManager::FindScopeForPath(const nsACString& aScopeKey, |
1774 | | const nsACString& aPath, |
1775 | | RegistrationDataPerPrincipal** aData, |
1776 | | nsACString& aMatch) |
1777 | 0 | { |
1778 | 0 | MOZ_ASSERT(aData); |
1779 | 0 |
|
1780 | 0 | RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); |
1781 | 0 |
|
1782 | 0 | if (!swm || !swm->mRegistrationInfos.Get(aScopeKey, aData)) { |
1783 | 0 | return false; |
1784 | 0 | } |
1785 | 0 | |
1786 | 0 | for (uint32_t i = 0; i < (*aData)->mOrderedScopes.Length(); ++i) { |
1787 | 0 | const nsCString& current = (*aData)->mOrderedScopes[i]; |
1788 | 0 | if (StringBeginsWith(aPath, current)) { |
1789 | 0 | aMatch = current; |
1790 | 0 | return true; |
1791 | 0 | } |
1792 | 0 | } |
1793 | 0 |
|
1794 | 0 | return false; |
1795 | 0 | } |
1796 | | |
1797 | | /* static */ bool |
1798 | | ServiceWorkerManager::HasScope(nsIPrincipal* aPrincipal, |
1799 | | const nsACString& aScope) |
1800 | 0 | { |
1801 | 0 | RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); |
1802 | 0 | if (!swm) { |
1803 | 0 | return false; |
1804 | 0 | } |
1805 | 0 | |
1806 | 0 | nsAutoCString scopeKey; |
1807 | 0 | nsresult rv = PrincipalToScopeKey(aPrincipal, scopeKey); |
1808 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
1809 | 0 | return false; |
1810 | 0 | } |
1811 | 0 | |
1812 | 0 | RegistrationDataPerPrincipal* data; |
1813 | 0 | if (!swm->mRegistrationInfos.Get(scopeKey, &data)) { |
1814 | 0 | return false; |
1815 | 0 | } |
1816 | 0 | |
1817 | 0 | return data->mOrderedScopes.Contains(aScope); |
1818 | 0 | } |
1819 | | |
1820 | | /* static */ void |
1821 | | ServiceWorkerManager::RemoveScopeAndRegistration(ServiceWorkerRegistrationInfo* aRegistration) |
1822 | 0 | { |
1823 | 0 | RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); |
1824 | 0 | if (!swm) { |
1825 | 0 | return; |
1826 | 0 | } |
1827 | 0 | |
1828 | 0 | nsAutoCString scopeKey; |
1829 | 0 | nsresult rv = swm->PrincipalToScopeKey(aRegistration->Principal(), scopeKey); |
1830 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
1831 | 0 | return; |
1832 | 0 | } |
1833 | 0 | |
1834 | 0 | RegistrationDataPerPrincipal* data; |
1835 | 0 | if (!swm->mRegistrationInfos.Get(scopeKey, &data)) { |
1836 | 0 | return; |
1837 | 0 | } |
1838 | 0 | |
1839 | 0 | if (auto entry = data->mUpdateTimers.Lookup(aRegistration->Scope())) { |
1840 | 0 | entry.Data()->Cancel(); |
1841 | 0 | entry.Remove(); |
1842 | 0 | } |
1843 | 0 |
|
1844 | 0 | // Verify there are no controlled clients for the purged registration. |
1845 | 0 | for (auto iter = swm->mControlledClients.Iter(); !iter.Done(); iter.Next()) { |
1846 | 0 | auto& reg = iter.UserData()->mRegistrationInfo; |
1847 | 0 | if (reg->Scope().Equals(aRegistration->Scope()) && |
1848 | 0 | reg->Principal()->Equals(aRegistration->Principal())) { |
1849 | 0 | MOZ_DIAGNOSTIC_ASSERT(aRegistration->IsCorrupt(), |
1850 | 0 | "controlled client when removing non-corrupt registration"); |
1851 | 0 | iter.Remove(); |
1852 | 0 | break; |
1853 | 0 | } |
1854 | 0 | } |
1855 | 0 |
|
1856 | 0 | RefPtr<ServiceWorkerRegistrationInfo> info; |
1857 | 0 | data->mInfos.Remove(aRegistration->Scope(), getter_AddRefs(info)); |
1858 | 0 | data->mOrderedScopes.RemoveElement(aRegistration->Scope()); |
1859 | 0 | swm->NotifyListenersOnUnregister(info); |
1860 | 0 |
|
1861 | 0 | swm->MaybeRemoveRegistrationInfo(scopeKey); |
1862 | 0 | aRegistration->NotifyRemoved(); |
1863 | 0 | } |
1864 | | |
1865 | | void |
1866 | | ServiceWorkerManager::MaybeRemoveRegistrationInfo(const nsACString& aScopeKey) |
1867 | 0 | { |
1868 | 0 | if (auto entry = mRegistrationInfos.Lookup(aScopeKey)) { |
1869 | 0 | if (entry.Data()->mOrderedScopes.IsEmpty() && |
1870 | 0 | entry.Data()->mJobQueues.Count() == 0) { |
1871 | 0 | entry.Remove(); |
1872 | 0 | } |
1873 | 0 | } |
1874 | 0 | } |
1875 | | |
1876 | | bool |
1877 | | ServiceWorkerManager::StartControlling(const ClientInfo& aClientInfo, |
1878 | | const ServiceWorkerDescriptor& aServiceWorker) |
1879 | 0 | { |
1880 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
1881 | 0 |
|
1882 | 0 | nsCOMPtr<nsIPrincipal> principal = |
1883 | 0 | PrincipalInfoToPrincipal(aServiceWorker.PrincipalInfo()); |
1884 | 0 | NS_ENSURE_TRUE(principal, false); |
1885 | 0 |
|
1886 | 0 | nsCOMPtr<nsIURI> scope; |
1887 | 0 | nsresult rv = |
1888 | 0 | NS_NewURI(getter_AddRefs(scope), aServiceWorker.Scope(), nullptr, nullptr); |
1889 | 0 | NS_ENSURE_SUCCESS(rv, false); |
1890 | 0 |
|
1891 | 0 | RefPtr<ServiceWorkerRegistrationInfo> registration = |
1892 | 0 | GetServiceWorkerRegistrationInfo(principal, scope); |
1893 | 0 | NS_ENSURE_TRUE(registration, false); |
1894 | 0 | NS_ENSURE_TRUE(registration->GetActive(), false); |
1895 | 0 |
|
1896 | 0 | StartControllingClient(aClientInfo, registration); |
1897 | 0 |
|
1898 | 0 | return true; |
1899 | 0 | } |
1900 | | |
1901 | | void |
1902 | | ServiceWorkerManager::MaybeCheckNavigationUpdate(const ClientInfo& aClientInfo) |
1903 | 0 | { |
1904 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
1905 | 0 | // We perform these success path navigation update steps when the |
1906 | 0 | // document tells us its more or less done loading. This avoids |
1907 | 0 | // slowing down page load and also lets pages consistently get |
1908 | 0 | // updatefound events when they fire. |
1909 | 0 | // |
1910 | 0 | // 9.8.20 If respondWithEntered is false, then: |
1911 | 0 | // 9.8.22 Else: (respondWith was entered and succeeded) |
1912 | 0 | // If request is a non-subresource request, then: Invoke Soft Update |
1913 | 0 | // algorithm. |
1914 | 0 | ControlledClientData* data = mControlledClients.Get(aClientInfo.Id()); |
1915 | 0 | if (data && data->mRegistrationInfo) { |
1916 | 0 | data->mRegistrationInfo->MaybeScheduleUpdate(); |
1917 | 0 | } |
1918 | 0 | } |
1919 | | |
1920 | | void |
1921 | | ServiceWorkerManager::StopControllingRegistration(ServiceWorkerRegistrationInfo* aRegistration) |
1922 | 0 | { |
1923 | 0 | aRegistration->StopControllingClient(); |
1924 | 0 | if (aRegistration->IsControllingClients() || !aRegistration->IsIdle()) { |
1925 | 0 | return; |
1926 | 0 | } |
1927 | 0 | |
1928 | 0 | if (aRegistration->IsPendingUninstall()) { |
1929 | 0 | RemoveRegistration(aRegistration); |
1930 | 0 | return; |
1931 | 0 | } |
1932 | 0 | |
1933 | 0 | // We use to aggressively terminate the worker at this point, but it |
1934 | 0 | // caused problems. There are more uses for a service worker than actively |
1935 | 0 | // controlled documents. We need to let the worker naturally terminate |
1936 | 0 | // in case its handling push events, message events, etc. |
1937 | 0 | aRegistration->TryToActivateAsync(); |
1938 | 0 | } |
1939 | | |
1940 | | NS_IMETHODIMP |
1941 | | ServiceWorkerManager::GetScopeForUrl(nsIPrincipal* aPrincipal, |
1942 | | const nsAString& aUrl, nsAString& aScope) |
1943 | 0 | { |
1944 | 0 | MOZ_ASSERT(aPrincipal); |
1945 | 0 |
|
1946 | 0 | nsCOMPtr<nsIURI> uri; |
1947 | 0 | nsresult rv = NS_NewURI(getter_AddRefs(uri), aUrl, nullptr, nullptr); |
1948 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
1949 | 0 | return NS_ERROR_FAILURE; |
1950 | 0 | } |
1951 | 0 | |
1952 | 0 | RefPtr<ServiceWorkerRegistrationInfo> r = |
1953 | 0 | GetServiceWorkerRegistrationInfo(aPrincipal, uri); |
1954 | 0 | if (!r) { |
1955 | 0 | return NS_ERROR_FAILURE; |
1956 | 0 | } |
1957 | 0 | |
1958 | 0 | aScope = NS_ConvertUTF8toUTF16(r->Scope()); |
1959 | 0 | return NS_OK; |
1960 | 0 | } |
1961 | | |
1962 | | namespace { |
1963 | | |
1964 | | class ContinueDispatchFetchEventRunnable : public Runnable |
1965 | | { |
1966 | | RefPtr<ServiceWorkerPrivate> mServiceWorkerPrivate; |
1967 | | nsCOMPtr<nsIInterceptedChannel> mChannel; |
1968 | | nsCOMPtr<nsILoadGroup> mLoadGroup; |
1969 | | bool mIsReload; |
1970 | | public: |
1971 | | ContinueDispatchFetchEventRunnable( |
1972 | | ServiceWorkerPrivate* aServiceWorkerPrivate, |
1973 | | nsIInterceptedChannel* aChannel, |
1974 | | nsILoadGroup* aLoadGroup, |
1975 | | bool aIsReload) |
1976 | | : Runnable("dom::ServiceWorkerManager::ContinueDispatchFetchEventRunnable") |
1977 | | , mServiceWorkerPrivate(aServiceWorkerPrivate) |
1978 | | , mChannel(aChannel) |
1979 | | , mLoadGroup(aLoadGroup) |
1980 | | , mIsReload(aIsReload) |
1981 | 0 | { |
1982 | 0 | MOZ_ASSERT(aServiceWorkerPrivate); |
1983 | 0 | MOZ_ASSERT(aChannel); |
1984 | 0 | } |
1985 | | |
1986 | | void |
1987 | | HandleError() |
1988 | 0 | { |
1989 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
1990 | 0 | NS_WARNING("Unexpected error while dispatching fetch event!"); |
1991 | 0 | nsresult rv = mChannel->ResetInterception(); |
1992 | 0 | if (NS_FAILED(rv)) { |
1993 | 0 | NS_WARNING("Failed to resume intercepted network request"); |
1994 | 0 | mChannel->CancelInterception(rv); |
1995 | 0 | } |
1996 | 0 | } |
1997 | | |
1998 | | NS_IMETHOD |
1999 | | Run() override |
2000 | 0 | { |
2001 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
2002 | 0 |
|
2003 | 0 | nsCOMPtr<nsIChannel> channel; |
2004 | 0 | nsresult rv = mChannel->GetChannel(getter_AddRefs(channel)); |
2005 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
2006 | 0 | HandleError(); |
2007 | 0 | return NS_OK; |
2008 | 0 | } |
2009 | 0 | |
2010 | 0 | // The channel might have encountered an unexpected error while ensuring |
2011 | 0 | // the upload stream is cloneable. Check here and reset the interception |
2012 | 0 | // if that happens. |
2013 | 0 | nsresult status; |
2014 | 0 | rv = channel->GetStatus(&status); |
2015 | 0 | if (NS_WARN_IF(NS_FAILED(rv) || NS_FAILED(status))) { |
2016 | 0 | HandleError(); |
2017 | 0 | return NS_OK; |
2018 | 0 | } |
2019 | 0 | |
2020 | 0 | nsString clientId; |
2021 | 0 | nsCOMPtr<nsILoadInfo> loadInfo = channel->GetLoadInfo(); |
2022 | 0 | if (loadInfo) { |
2023 | 0 | Maybe<ClientInfo> clientInfo = loadInfo->GetClientInfo(); |
2024 | 0 | if (clientInfo.isSome()) { |
2025 | 0 | char buf[NSID_LENGTH]; |
2026 | 0 | clientInfo.ref().Id().ToProvidedString(buf); |
2027 | 0 | NS_ConvertASCIItoUTF16 uuid(buf); |
2028 | 0 |
|
2029 | 0 | // Remove {} and the null terminator |
2030 | 0 | clientId.Assign(Substring(uuid, 1, NSID_LENGTH - 3)); |
2031 | 0 | } |
2032 | 0 | } |
2033 | 0 |
|
2034 | 0 | rv = mServiceWorkerPrivate->SendFetchEvent(mChannel, mLoadGroup, clientId, |
2035 | 0 | mIsReload); |
2036 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
2037 | 0 | HandleError(); |
2038 | 0 | } |
2039 | 0 |
|
2040 | 0 | return NS_OK; |
2041 | 0 | } |
2042 | | }; |
2043 | | |
2044 | | } // anonymous namespace |
2045 | | |
2046 | | void |
2047 | | ServiceWorkerManager::DispatchFetchEvent(nsIInterceptedChannel* aChannel, |
2048 | | ErrorResult& aRv) |
2049 | 0 | { |
2050 | 0 | MOZ_ASSERT(aChannel); |
2051 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
2052 | 0 |
|
2053 | 0 | nsCOMPtr<nsIChannel> internalChannel; |
2054 | 0 | aRv = aChannel->GetChannel(getter_AddRefs(internalChannel)); |
2055 | 0 | if (NS_WARN_IF(aRv.Failed())) { |
2056 | 0 | return; |
2057 | 0 | } |
2058 | 0 | |
2059 | 0 | nsCOMPtr<nsILoadGroup> loadGroup; |
2060 | 0 | aRv = internalChannel->GetLoadGroup(getter_AddRefs(loadGroup)); |
2061 | 0 | if (NS_WARN_IF(aRv.Failed())) { |
2062 | 0 | return; |
2063 | 0 | } |
2064 | 0 | |
2065 | 0 | nsCOMPtr<nsILoadInfo> loadInfo = internalChannel->GetLoadInfo(); |
2066 | 0 | if (NS_WARN_IF(!loadInfo)) { |
2067 | 0 | aRv.Throw(NS_ERROR_UNEXPECTED); |
2068 | 0 | return; |
2069 | 0 | } |
2070 | 0 | |
2071 | 0 | RefPtr<ServiceWorkerInfo> serviceWorker; |
2072 | 0 |
|
2073 | 0 | if (!nsContentUtils::IsNonSubresourceRequest(internalChannel)) { |
2074 | 0 | const Maybe<ServiceWorkerDescriptor>& controller = loadInfo->GetController(); |
2075 | 0 | if (NS_WARN_IF(controller.isNothing())) { |
2076 | 0 | aRv.Throw(NS_ERROR_FAILURE); |
2077 | 0 | return; |
2078 | 0 | } |
2079 | 0 | |
2080 | 0 | RefPtr<ServiceWorkerRegistrationInfo> registration = |
2081 | 0 | GetRegistration(controller.ref().PrincipalInfo(), controller.ref().Scope()); |
2082 | 0 | if (NS_WARN_IF(!registration)) { |
2083 | 0 | aRv.Throw(NS_ERROR_FAILURE); |
2084 | 0 | return; |
2085 | 0 | } |
2086 | 0 | |
2087 | 0 | serviceWorker = registration->GetActive(); |
2088 | 0 | if (NS_WARN_IF(!serviceWorker) || |
2089 | 0 | NS_WARN_IF(serviceWorker->Descriptor().Id() != controller.ref().Id())) { |
2090 | 0 | aRv.Throw(NS_ERROR_FAILURE); |
2091 | 0 | return; |
2092 | 0 | } |
2093 | 0 | } else { |
2094 | 0 | nsCOMPtr<nsIURI> uri; |
2095 | 0 | aRv = aChannel->GetSecureUpgradedChannelURI(getter_AddRefs(uri)); |
2096 | 0 | if (NS_WARN_IF(aRv.Failed())) { |
2097 | 0 | return; |
2098 | 0 | } |
2099 | 0 | |
2100 | 0 | // non-subresource request means the URI contains the principal |
2101 | 0 | nsCOMPtr<nsIPrincipal> principal = |
2102 | 0 | BasePrincipal::CreateCodebasePrincipal(uri, |
2103 | 0 | loadInfo->GetOriginAttributes()); |
2104 | 0 |
|
2105 | 0 | RefPtr<ServiceWorkerRegistrationInfo> registration = |
2106 | 0 | GetServiceWorkerRegistrationInfo(principal, uri); |
2107 | 0 | if (NS_WARN_IF(!registration)) { |
2108 | 0 | aRv.Throw(NS_ERROR_FAILURE); |
2109 | 0 | return; |
2110 | 0 | } |
2111 | 0 | |
2112 | 0 | // While we only enter this method if IsAvailable() previously saw |
2113 | 0 | // an active worker, it is possible for that worker to be removed |
2114 | 0 | // before we get to this point. Therefore we must handle a nullptr |
2115 | 0 | // active worker here. |
2116 | 0 | serviceWorker = registration->GetActive(); |
2117 | 0 | if (NS_WARN_IF(!serviceWorker)) { |
2118 | 0 | aRv.Throw(NS_ERROR_FAILURE); |
2119 | 0 | return; |
2120 | 0 | } |
2121 | 0 | |
2122 | 0 | // If there is a reserved client it should be marked as controlled before |
2123 | 0 | // the FetchEvent is dispatched. |
2124 | 0 | Maybe<ClientInfo> clientInfo = loadInfo->GetReservedClientInfo(); |
2125 | 0 |
|
2126 | 0 | // Also override the initial about:blank controller since the real |
2127 | 0 | // network load may be intercepted by a different service worker. If |
2128 | 0 | // the intial about:blank has a controller here its simply been |
2129 | 0 | // inherited from its parent. |
2130 | 0 | if (clientInfo.isNothing()) { |
2131 | 0 | clientInfo = loadInfo->GetInitialClientInfo(); |
2132 | 0 |
|
2133 | 0 | // TODO: We need to handle the case where the initial about:blank is |
2134 | 0 | // controlled, but the final document load is not. Right now |
2135 | 0 | // the spec does not really say what to do. There currently |
2136 | 0 | // is no way for the controller to be cleared from a client in |
2137 | 0 | // the spec or our implementation. We may want to force a |
2138 | 0 | // new inner window to be created instead of reusing the |
2139 | 0 | // initial about:blank global. See bug 1419620 and the spec |
2140 | 0 | // issue here: https://github.com/w3c/ServiceWorker/issues/1232 |
2141 | 0 | } |
2142 | 0 |
|
2143 | 0 | if (clientInfo.isSome()) { |
2144 | 0 | // ClientChannelHelper is not called for STS upgrades that get |
2145 | 0 | // intercepted by a service worker when interception occurs in |
2146 | 0 | // the content process. Therefore the reserved client is not |
2147 | 0 | // properly cleared in that case leading to a situation where |
2148 | 0 | // a ClientSource with an http:// principal is controlled by |
2149 | 0 | // a ServiceWorker with an https:// principal. |
2150 | 0 | // |
2151 | 0 | // This does not occur when interception is handled by the |
2152 | 0 | // simpler InterceptedHttpChannel approach in the parent. |
2153 | 0 | // |
2154 | 0 | // As a temporary work around check for this principal mismatch |
2155 | 0 | // here and perform the ClientChannelHelper's replacement of |
2156 | 0 | // reserved client automatically. |
2157 | 0 | if (!XRE_IsParentProcess()) { |
2158 | 0 | nsCOMPtr<nsIPrincipal> clientPrincipal = clientInfo.ref().GetPrincipal(); |
2159 | 0 | if (!clientPrincipal || !clientPrincipal->Equals(principal)) { |
2160 | 0 | UniquePtr<ClientSource> reservedClient = |
2161 | 0 | loadInfo->TakeReservedClientSource(); |
2162 | 0 |
|
2163 | 0 | nsCOMPtr<nsISerialEventTarget> target = |
2164 | 0 | reservedClient ? reservedClient->EventTarget() |
2165 | 0 | : SystemGroup::EventTargetFor(TaskCategory::Other); |
2166 | 0 |
|
2167 | 0 | reservedClient.reset(); |
2168 | 0 | reservedClient = ClientManager::CreateSource(ClientType::Window, |
2169 | 0 | target, |
2170 | 0 | principal); |
2171 | 0 |
|
2172 | 0 | loadInfo->GiveReservedClientSource(std::move(reservedClient)); |
2173 | 0 |
|
2174 | 0 | clientInfo = loadInfo->GetReservedClientInfo(); |
2175 | 0 | } |
2176 | 0 | } |
2177 | 0 |
|
2178 | 0 | // First, attempt to mark the reserved client controlled directly. This |
2179 | 0 | // will update the controlled status in the ClientManagerService in the |
2180 | 0 | // parent. It will also eventually propagate back to the ClientSource. |
2181 | 0 | StartControllingClient(clientInfo.ref(), registration); |
2182 | 0 | } |
2183 | 0 |
|
2184 | 0 | uint32_t redirectMode = nsIHttpChannelInternal::REDIRECT_MODE_MANUAL; |
2185 | 0 | nsCOMPtr<nsIHttpChannelInternal> http = do_QueryInterface(internalChannel); |
2186 | 0 | MOZ_ALWAYS_SUCCEEDS(http->GetRedirectMode(&redirectMode)); |
2187 | 0 |
|
2188 | 0 | // Synthetic redirects for non-subresource requests with a "follow" |
2189 | 0 | // redirect mode may switch controllers. This is basically worker |
2190 | 0 | // scripts right now. In this case we need to explicitly clear the |
2191 | 0 | // controller to avoid assertions on the SetController() below. |
2192 | 0 | if (redirectMode == nsIHttpChannelInternal::REDIRECT_MODE_FOLLOW) { |
2193 | 0 | loadInfo->ClearController(); |
2194 | 0 | } |
2195 | 0 |
|
2196 | 0 | // But we also note the reserved state on the LoadInfo. This allows the |
2197 | 0 | // ClientSource to be updated immediately after the nsIChannel starts. |
2198 | 0 | // This is necessary to have the correct controller in place for immediate |
2199 | 0 | // follow-on requests. |
2200 | 0 | loadInfo->SetController(serviceWorker->Descriptor()); |
2201 | 0 | } |
2202 | 0 |
|
2203 | 0 | MOZ_DIAGNOSTIC_ASSERT(serviceWorker); |
2204 | 0 |
|
2205 | 0 | nsCOMPtr<nsIRunnable> continueRunnable = |
2206 | 0 | new ContinueDispatchFetchEventRunnable(serviceWorker->WorkerPrivate(), |
2207 | 0 | aChannel, loadGroup, |
2208 | 0 | loadInfo->GetIsDocshellReload()); |
2209 | 0 |
|
2210 | 0 | // When this service worker was registered, we also sent down the permissions |
2211 | 0 | // for the runnable. They should have arrived by now, but we still need to |
2212 | 0 | // wait for them if they have not. |
2213 | 0 | nsCOMPtr<nsIRunnable> permissionsRunnable = NS_NewRunnableFunction( |
2214 | 0 | "dom::ServiceWorkerManager::DispatchFetchEvent", [=]() { |
2215 | 0 | nsCOMPtr<nsIPermissionManager> permMgr = services::GetPermissionManager(); |
2216 | 0 | MOZ_ALWAYS_SUCCEEDS(permMgr->WhenPermissionsAvailable(serviceWorker->Principal(), |
2217 | 0 | continueRunnable)); |
2218 | 0 | }); |
2219 | 0 |
|
2220 | 0 | nsCOMPtr<nsIUploadChannel2> uploadChannel = do_QueryInterface(internalChannel); |
2221 | 0 |
|
2222 | 0 | // If there is no upload stream, then continue immediately |
2223 | 0 | if (!uploadChannel) { |
2224 | 0 | MOZ_ALWAYS_SUCCEEDS(permissionsRunnable->Run()); |
2225 | 0 | return; |
2226 | 0 | } |
2227 | 0 | // Otherwise, ensure the upload stream can be cloned directly. This may |
2228 | 0 | // require some async copying, so provide a callback. |
2229 | 0 | aRv = uploadChannel->EnsureUploadStreamIsCloneable(permissionsRunnable); |
2230 | 0 | } |
2231 | | |
2232 | | bool |
2233 | | ServiceWorkerManager::IsAvailable(nsIPrincipal* aPrincipal, |
2234 | | nsIURI* aURI) |
2235 | 0 | { |
2236 | 0 | MOZ_ASSERT(aPrincipal); |
2237 | 0 | MOZ_ASSERT(aURI); |
2238 | 0 |
|
2239 | 0 | RefPtr<ServiceWorkerRegistrationInfo> registration = |
2240 | 0 | GetServiceWorkerRegistrationInfo(aPrincipal, aURI); |
2241 | 0 | return registration && registration->GetActive(); |
2242 | 0 | } |
2243 | | |
2244 | | nsresult |
2245 | | ServiceWorkerManager::GetClientRegistration(const ClientInfo& aClientInfo, |
2246 | | ServiceWorkerRegistrationInfo** aRegistrationInfo) |
2247 | 0 | { |
2248 | 0 | ControlledClientData* data = mControlledClients.Get(aClientInfo.Id()); |
2249 | 0 | if (!data || !data->mRegistrationInfo) { |
2250 | 0 | return NS_ERROR_NOT_AVAILABLE; |
2251 | 0 | } |
2252 | 0 | |
2253 | 0 | // If the document is controlled, the current worker MUST be non-null. |
2254 | 0 | if (!data->mRegistrationInfo->GetActive()) { |
2255 | 0 | return NS_ERROR_NOT_AVAILABLE; |
2256 | 0 | } |
2257 | 0 | |
2258 | 0 | RefPtr<ServiceWorkerRegistrationInfo> ref = data->mRegistrationInfo; |
2259 | 0 | ref.forget(aRegistrationInfo); |
2260 | 0 | return NS_OK; |
2261 | 0 | } |
2262 | | |
2263 | | void |
2264 | | ServiceWorkerManager::SoftUpdate(const OriginAttributes& aOriginAttributes, |
2265 | | const nsACString& aScope) |
2266 | 0 | { |
2267 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
2268 | 0 |
|
2269 | 0 | if (mShuttingDown) { |
2270 | 0 | return; |
2271 | 0 | } |
2272 | 0 | |
2273 | 0 | if (ServiceWorkerParentInterceptEnabled()) { |
2274 | 0 | SoftUpdateInternal(aOriginAttributes, aScope, nullptr); |
2275 | 0 | return; |
2276 | 0 | } |
2277 | 0 | |
2278 | 0 | RefPtr<GenericPromise::Private> promise = |
2279 | 0 | new GenericPromise::Private(__func__); |
2280 | 0 |
|
2281 | 0 | RefPtr<CancelableRunnable> successRunnable = |
2282 | 0 | new SoftUpdateRunnable(aOriginAttributes, aScope, true, promise); |
2283 | 0 |
|
2284 | 0 | RefPtr<CancelableRunnable> failureRunnable = |
2285 | 0 | new ResolvePromiseRunnable(promise); |
2286 | 0 |
|
2287 | 0 | ServiceWorkerUpdaterChild* actor = |
2288 | 0 | new ServiceWorkerUpdaterChild(promise, successRunnable, failureRunnable); |
2289 | 0 |
|
2290 | 0 | mActor->SendPServiceWorkerUpdaterConstructor(actor, aOriginAttributes, |
2291 | 0 | nsCString(aScope)); |
2292 | 0 | } |
2293 | | |
2294 | | namespace { |
2295 | | |
2296 | | class UpdateJobCallback final : public ServiceWorkerJob::Callback |
2297 | | { |
2298 | | RefPtr<ServiceWorkerUpdateFinishCallback> mCallback; |
2299 | | |
2300 | 0 | ~UpdateJobCallback() = default; |
2301 | | |
2302 | | public: |
2303 | | explicit UpdateJobCallback(ServiceWorkerUpdateFinishCallback* aCallback) |
2304 | | : mCallback(aCallback) |
2305 | 0 | { |
2306 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
2307 | 0 | MOZ_ASSERT(mCallback); |
2308 | 0 | } |
2309 | | |
2310 | | void |
2311 | | JobFinished(ServiceWorkerJob* aJob, ErrorResult& aStatus) override |
2312 | 0 | { |
2313 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
2314 | 0 | MOZ_ASSERT(aJob); |
2315 | 0 |
|
2316 | 0 | if (aStatus.Failed()) { |
2317 | 0 | mCallback->UpdateFailed(aStatus); |
2318 | 0 | return; |
2319 | 0 | } |
2320 | 0 | |
2321 | 0 | MOZ_DIAGNOSTIC_ASSERT(aJob->GetType() == ServiceWorkerJob::Type::Update); |
2322 | 0 | RefPtr<ServiceWorkerUpdateJob> updateJob = |
2323 | 0 | static_cast<ServiceWorkerUpdateJob*>(aJob); |
2324 | 0 | RefPtr<ServiceWorkerRegistrationInfo> reg = updateJob->GetRegistration(); |
2325 | 0 | mCallback->UpdateSucceeded(reg); |
2326 | 0 | } |
2327 | | |
2328 | | NS_INLINE_DECL_REFCOUNTING(UpdateJobCallback, override) |
2329 | | }; |
2330 | | |
2331 | | } // anonymous namespace |
2332 | | |
2333 | | void |
2334 | | ServiceWorkerManager::SoftUpdateInternal(const OriginAttributes& aOriginAttributes, |
2335 | | const nsACString& aScope, |
2336 | | ServiceWorkerUpdateFinishCallback* aCallback) |
2337 | 0 | { |
2338 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
2339 | 0 |
|
2340 | 0 | if (mShuttingDown) { |
2341 | 0 | return; |
2342 | 0 | } |
2343 | 0 | |
2344 | 0 | nsCOMPtr<nsIURI> scopeURI; |
2345 | 0 | nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), aScope); |
2346 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
2347 | 0 | return; |
2348 | 0 | } |
2349 | 0 | |
2350 | 0 | nsCOMPtr<nsIPrincipal> principal = |
2351 | 0 | BasePrincipal::CreateCodebasePrincipal(scopeURI, aOriginAttributes); |
2352 | 0 | if (NS_WARN_IF(!principal)) { |
2353 | 0 | return; |
2354 | 0 | } |
2355 | 0 | |
2356 | 0 | nsAutoCString scopeKey; |
2357 | 0 | rv = PrincipalToScopeKey(principal, scopeKey); |
2358 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
2359 | 0 | return; |
2360 | 0 | } |
2361 | 0 | |
2362 | 0 | RefPtr<ServiceWorkerRegistrationInfo> registration = |
2363 | 0 | GetRegistration(scopeKey, aScope); |
2364 | 0 | if (NS_WARN_IF(!registration)) { |
2365 | 0 | return; |
2366 | 0 | } |
2367 | 0 | |
2368 | 0 | // "If registration's uninstalling flag is set, abort these steps." |
2369 | 0 | if (registration->IsPendingUninstall()) { |
2370 | 0 | return; |
2371 | 0 | } |
2372 | 0 | |
2373 | 0 | // "If registration's installing worker is not null, abort these steps." |
2374 | 0 | if (registration->GetInstalling()) { |
2375 | 0 | return; |
2376 | 0 | } |
2377 | 0 | |
2378 | 0 | // "Let newestWorker be the result of running Get Newest Worker algorithm |
2379 | 0 | // passing registration as its argument. |
2380 | 0 | // If newestWorker is null, abort these steps." |
2381 | 0 | RefPtr<ServiceWorkerInfo> newest = registration->Newest(); |
2382 | 0 | if (!newest) { |
2383 | 0 | return; |
2384 | 0 | } |
2385 | 0 | |
2386 | 0 | // "If the registration queue for registration is empty, invoke Update algorithm, |
2387 | 0 | // or its equivalent, with client, registration as its argument." |
2388 | 0 | // TODO(catalinb): We don't implement the force bypass cache flag. |
2389 | 0 | // See: https://github.com/slightlyoff/ServiceWorker/issues/759 |
2390 | 0 | RefPtr<ServiceWorkerJobQueue> queue = GetOrCreateJobQueue(scopeKey, |
2391 | 0 | aScope); |
2392 | 0 |
|
2393 | 0 | RefPtr<ServiceWorkerUpdateJob> job = |
2394 | 0 | new ServiceWorkerUpdateJob(principal, registration->Scope(), |
2395 | 0 | newest->ScriptSpec(), nullptr, |
2396 | 0 | registration->GetUpdateViaCache()); |
2397 | 0 |
|
2398 | 0 | if (aCallback) { |
2399 | 0 | RefPtr<UpdateJobCallback> cb = new UpdateJobCallback(aCallback); |
2400 | 0 | job->AppendResultCallback(cb); |
2401 | 0 | } |
2402 | 0 |
|
2403 | 0 | queue->ScheduleJob(job); |
2404 | 0 | } |
2405 | | |
2406 | | void |
2407 | | ServiceWorkerManager::Update(nsIPrincipal* aPrincipal, |
2408 | | const nsACString& aScope, |
2409 | | ServiceWorkerUpdateFinishCallback* aCallback) |
2410 | 0 | { |
2411 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
2412 | 0 |
|
2413 | 0 | if (ServiceWorkerParentInterceptEnabled()) { |
2414 | 0 | UpdateInternal(aPrincipal, aScope, aCallback); |
2415 | 0 | return; |
2416 | 0 | } |
2417 | 0 | |
2418 | 0 | RefPtr<GenericPromise::Private> promise = |
2419 | 0 | new GenericPromise::Private(__func__); |
2420 | 0 |
|
2421 | 0 | RefPtr<CancelableRunnable> successRunnable = |
2422 | 0 | new UpdateRunnable(aPrincipal, aScope, aCallback, |
2423 | 0 | UpdateRunnable::eSuccess, promise); |
2424 | 0 |
|
2425 | 0 | RefPtr<CancelableRunnable> failureRunnable = |
2426 | 0 | new UpdateRunnable(aPrincipal, aScope, aCallback, |
2427 | 0 | UpdateRunnable::eFailure, promise); |
2428 | 0 |
|
2429 | 0 | ServiceWorkerUpdaterChild* actor = |
2430 | 0 | new ServiceWorkerUpdaterChild(promise, successRunnable, failureRunnable); |
2431 | 0 |
|
2432 | 0 | mActor->SendPServiceWorkerUpdaterConstructor(actor, |
2433 | 0 | aPrincipal->OriginAttributesRef(), |
2434 | 0 | nsCString(aScope)); |
2435 | 0 | } |
2436 | | |
2437 | | void |
2438 | | ServiceWorkerManager::UpdateInternal(nsIPrincipal* aPrincipal, |
2439 | | const nsACString& aScope, |
2440 | | ServiceWorkerUpdateFinishCallback* aCallback) |
2441 | 0 | { |
2442 | 0 | MOZ_ASSERT(aPrincipal); |
2443 | 0 | MOZ_ASSERT(aCallback); |
2444 | 0 |
|
2445 | 0 | nsAutoCString scopeKey; |
2446 | 0 | nsresult rv = PrincipalToScopeKey(aPrincipal, scopeKey); |
2447 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
2448 | 0 | return; |
2449 | 0 | } |
2450 | 0 | |
2451 | 0 | RefPtr<ServiceWorkerRegistrationInfo> registration = |
2452 | 0 | GetRegistration(scopeKey, aScope); |
2453 | 0 | if (NS_WARN_IF(!registration)) { |
2454 | 0 | return; |
2455 | 0 | } |
2456 | 0 | |
2457 | 0 | // "Let newestWorker be the result of running Get Newest Worker algorithm |
2458 | 0 | // passing registration as its argument. |
2459 | 0 | // If newestWorker is null, return a promise rejected with "InvalidStateError" |
2460 | 0 | RefPtr<ServiceWorkerInfo> newest = registration->Newest(); |
2461 | 0 | if (!newest) { |
2462 | 0 | ErrorResult error(NS_ERROR_DOM_INVALID_STATE_ERR); |
2463 | 0 | aCallback->UpdateFailed(error); |
2464 | 0 |
|
2465 | 0 | // In case the callback does not consume the exception |
2466 | 0 | error.SuppressException(); |
2467 | 0 |
|
2468 | 0 | return; |
2469 | 0 | } |
2470 | 0 | |
2471 | 0 | RefPtr<ServiceWorkerJobQueue> queue = GetOrCreateJobQueue(scopeKey, aScope); |
2472 | 0 |
|
2473 | 0 | // "Invoke Update algorithm, or its equivalent, with client, registration as |
2474 | 0 | // its argument." |
2475 | 0 | RefPtr<ServiceWorkerUpdateJob> job = |
2476 | 0 | new ServiceWorkerUpdateJob(aPrincipal, registration->Scope(), |
2477 | 0 | newest->ScriptSpec(), nullptr, |
2478 | 0 | registration->GetUpdateViaCache()); |
2479 | 0 |
|
2480 | 0 | RefPtr<UpdateJobCallback> cb = new UpdateJobCallback(aCallback); |
2481 | 0 | job->AppendResultCallback(cb); |
2482 | 0 |
|
2483 | 0 | queue->ScheduleJob(job); |
2484 | 0 | } |
2485 | | |
2486 | | already_AddRefed<GenericPromise> |
2487 | | ServiceWorkerManager::MaybeClaimClient(const ClientInfo& aClientInfo, |
2488 | | ServiceWorkerRegistrationInfo* aWorkerRegistration) |
2489 | 0 | { |
2490 | 0 | MOZ_DIAGNOSTIC_ASSERT(aWorkerRegistration); |
2491 | 0 |
|
2492 | 0 | RefPtr<GenericPromise> ref; |
2493 | 0 |
|
2494 | 0 | if (!aWorkerRegistration->GetActive()) { |
2495 | 0 | ref = GenericPromise::CreateAndReject(NS_ERROR_DOM_INVALID_STATE_ERR, |
2496 | 0 | __func__); |
2497 | 0 | return ref.forget(); |
2498 | 0 | } |
2499 | 0 | |
2500 | 0 | // Same origin check |
2501 | 0 | nsCOMPtr<nsIPrincipal> principal(aClientInfo.GetPrincipal()); |
2502 | 0 | if (!aWorkerRegistration->Principal()->Equals(principal)) { |
2503 | 0 | ref = GenericPromise::CreateAndReject(NS_ERROR_DOM_SECURITY_ERR, __func__); |
2504 | 0 | return ref.forget(); |
2505 | 0 | } |
2506 | 0 | |
2507 | 0 | // The registration that should be controlling the client |
2508 | 0 | RefPtr<ServiceWorkerRegistrationInfo> matchingRegistration = |
2509 | 0 | GetServiceWorkerRegistrationInfo(aClientInfo); |
2510 | 0 |
|
2511 | 0 | // The registration currently controlling the client |
2512 | 0 | RefPtr<ServiceWorkerRegistrationInfo> controllingRegistration; |
2513 | 0 | GetClientRegistration(aClientInfo, getter_AddRefs(controllingRegistration)); |
2514 | 0 |
|
2515 | 0 | if (aWorkerRegistration != matchingRegistration || |
2516 | 0 | aWorkerRegistration == controllingRegistration) { |
2517 | 0 | ref = GenericPromise::CreateAndResolve(true, __func__); |
2518 | 0 | return ref.forget(); |
2519 | 0 | } |
2520 | 0 | |
2521 | 0 | ref = StartControllingClient(aClientInfo, aWorkerRegistration); |
2522 | 0 | return ref.forget(); |
2523 | 0 | } |
2524 | | |
2525 | | already_AddRefed<GenericPromise> |
2526 | | ServiceWorkerManager::MaybeClaimClient(const ClientInfo& aClientInfo, |
2527 | | const ServiceWorkerDescriptor& aServiceWorker) |
2528 | 0 | { |
2529 | 0 | RefPtr<GenericPromise> ref; |
2530 | 0 |
|
2531 | 0 | nsCOMPtr<nsIPrincipal> principal = aServiceWorker.GetPrincipal(); |
2532 | 0 | if (!principal) { |
2533 | 0 | ref = GenericPromise::CreateAndResolve(false, __func__); |
2534 | 0 | return ref.forget(); |
2535 | 0 | } |
2536 | 0 | |
2537 | 0 | RefPtr<ServiceWorkerRegistrationInfo> registration = |
2538 | 0 | GetRegistration(principal, aServiceWorker.Scope()); |
2539 | 0 |
|
2540 | 0 | // While ServiceWorkerManager is distributed across child processes its |
2541 | 0 | // possible for us to sometimes get a claim for a new worker that has |
2542 | 0 | // not propagated to this process yet. For now, simply note that we |
2543 | 0 | // are done. The fix for this is to move the SWM to the parent process |
2544 | 0 | // so there are no consistency errors. |
2545 | 0 | if (NS_WARN_IF(!registration) || NS_WARN_IF(!registration->GetActive())) { |
2546 | 0 | ref = GenericPromise::CreateAndResolve(false, __func__); |
2547 | 0 | return ref.forget(); |
2548 | 0 | } |
2549 | 0 | |
2550 | 0 | ref = MaybeClaimClient(aClientInfo, registration); |
2551 | 0 | return ref.forget(); |
2552 | 0 | } |
2553 | | |
2554 | | void |
2555 | | ServiceWorkerManager::SetSkipWaitingFlag(nsIPrincipal* aPrincipal, |
2556 | | const nsCString& aScope, |
2557 | | uint64_t aServiceWorkerID) |
2558 | 0 | { |
2559 | 0 | RefPtr<ServiceWorkerRegistrationInfo> registration = |
2560 | 0 | GetRegistration(aPrincipal, aScope); |
2561 | 0 | if (NS_WARN_IF(!registration)) { |
2562 | 0 | return; |
2563 | 0 | } |
2564 | 0 | |
2565 | 0 | RefPtr<ServiceWorkerInfo> worker = |
2566 | 0 | registration->GetServiceWorkerInfoById(aServiceWorkerID); |
2567 | 0 |
|
2568 | 0 | if (NS_WARN_IF(!worker)) { |
2569 | 0 | return; |
2570 | 0 | } |
2571 | 0 | |
2572 | 0 | worker->SetSkipWaitingFlag(); |
2573 | 0 |
|
2574 | 0 | if (worker->State() == ServiceWorkerState::Installed) { |
2575 | 0 | registration->TryToActivateAsync(); |
2576 | 0 | } |
2577 | 0 | } |
2578 | | |
2579 | | void |
2580 | | ServiceWorkerManager::UpdateClientControllers(ServiceWorkerRegistrationInfo* aRegistration) |
2581 | 0 | { |
2582 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
2583 | 0 |
|
2584 | 0 | RefPtr<ServiceWorkerInfo> activeWorker = aRegistration->GetActive(); |
2585 | 0 | MOZ_DIAGNOSTIC_ASSERT(activeWorker); |
2586 | 0 |
|
2587 | 0 | AutoTArray<RefPtr<ClientHandle>, 16> handleList; |
2588 | 0 | for (auto iter = mControlledClients.Iter(); !iter.Done(); iter.Next()) { |
2589 | 0 | if (iter.UserData()->mRegistrationInfo != aRegistration) { |
2590 | 0 | continue; |
2591 | 0 | } |
2592 | 0 | |
2593 | 0 | handleList.AppendElement(iter.UserData()->mClientHandle); |
2594 | 0 | } |
2595 | 0 |
|
2596 | 0 | // Fire event after iterating mControlledClients is done to prevent |
2597 | 0 | // modification by reentering from the event handlers during iteration. |
2598 | 0 | for (auto& handle : handleList) { |
2599 | 0 | RefPtr<GenericPromise> p = handle->Control(activeWorker->Descriptor()); |
2600 | 0 |
|
2601 | 0 | RefPtr<ServiceWorkerManager> self = this; |
2602 | 0 |
|
2603 | 0 | // If we fail to control the client, then automatically remove it |
2604 | 0 | // from our list of controlled clients. |
2605 | 0 | p->Then( |
2606 | 0 | SystemGroup::EventTargetFor(TaskCategory::Other), __func__, |
2607 | 0 | [] (bool) { |
2608 | 0 | // do nothing on success |
2609 | 0 | }, [self, clientInfo = handle->Info()] (nsresult aRv) { |
2610 | 0 | // failed to control, forget about this client |
2611 | 0 | self->StopControllingClient(clientInfo); |
2612 | 0 | }); |
2613 | 0 | } |
2614 | 0 | } |
2615 | | |
2616 | | already_AddRefed<ServiceWorkerRegistrationInfo> |
2617 | | ServiceWorkerManager::GetRegistration(nsIPrincipal* aPrincipal, |
2618 | | const nsACString& aScope) const |
2619 | 0 | { |
2620 | 0 | MOZ_ASSERT(aPrincipal); |
2621 | 0 |
|
2622 | 0 | nsAutoCString scopeKey; |
2623 | 0 | nsresult rv = PrincipalToScopeKey(aPrincipal, scopeKey); |
2624 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
2625 | 0 | return nullptr; |
2626 | 0 | } |
2627 | 0 | |
2628 | 0 | return GetRegistration(scopeKey, aScope); |
2629 | 0 | } |
2630 | | |
2631 | | already_AddRefed<ServiceWorkerRegistrationInfo> |
2632 | | ServiceWorkerManager::GetRegistration(const PrincipalInfo& aPrincipalInfo, |
2633 | | const nsACString& aScope) const |
2634 | 0 | { |
2635 | 0 | nsAutoCString scopeKey; |
2636 | 0 | nsresult rv = PrincipalInfoToScopeKey(aPrincipalInfo, scopeKey); |
2637 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
2638 | 0 | return nullptr; |
2639 | 0 | } |
2640 | 0 | |
2641 | 0 | return GetRegistration(scopeKey, aScope); |
2642 | 0 | } |
2643 | | |
2644 | | NS_IMETHODIMP |
2645 | | ServiceWorkerManager::GetRegistrationByPrincipal(nsIPrincipal* aPrincipal, |
2646 | | const nsAString& aScope, |
2647 | | nsIServiceWorkerRegistrationInfo** aInfo) |
2648 | 0 | { |
2649 | 0 | MOZ_ASSERT(aPrincipal); |
2650 | 0 | MOZ_ASSERT(aInfo); |
2651 | 0 |
|
2652 | 0 | nsCOMPtr<nsIURI> scopeURI; |
2653 | 0 | nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), aScope, nullptr, nullptr); |
2654 | 0 | if (NS_FAILED(rv)) { |
2655 | 0 | return NS_ERROR_FAILURE; |
2656 | 0 | } |
2657 | 0 | |
2658 | 0 | RefPtr<ServiceWorkerRegistrationInfo> info = |
2659 | 0 | GetServiceWorkerRegistrationInfo(aPrincipal, scopeURI); |
2660 | 0 | if (!info) { |
2661 | 0 | return NS_ERROR_FAILURE; |
2662 | 0 | } |
2663 | 0 | info.forget(aInfo); |
2664 | 0 |
|
2665 | 0 | return NS_OK; |
2666 | 0 | } |
2667 | | |
2668 | | already_AddRefed<ServiceWorkerRegistrationInfo> |
2669 | | ServiceWorkerManager::GetRegistration(const nsACString& aScopeKey, |
2670 | | const nsACString& aScope) const |
2671 | 0 | { |
2672 | 0 | RefPtr<ServiceWorkerRegistrationInfo> reg; |
2673 | 0 |
|
2674 | 0 | RegistrationDataPerPrincipal* data; |
2675 | 0 | if (!mRegistrationInfos.Get(aScopeKey, &data)) { |
2676 | 0 | return reg.forget(); |
2677 | 0 | } |
2678 | 0 | |
2679 | 0 | data->mInfos.Get(aScope, getter_AddRefs(reg)); |
2680 | 0 | return reg.forget(); |
2681 | 0 | } |
2682 | | |
2683 | | already_AddRefed<ServiceWorkerRegistrationInfo> |
2684 | | ServiceWorkerManager::CreateNewRegistration( |
2685 | | const nsCString& aScope, |
2686 | | nsIPrincipal* aPrincipal, |
2687 | | ServiceWorkerUpdateViaCache aUpdateViaCache) |
2688 | 0 | { |
2689 | | #ifdef DEBUG |
2690 | | MOZ_ASSERT(NS_IsMainThread()); |
2691 | | nsCOMPtr<nsIURI> scopeURI; |
2692 | | nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), aScope, nullptr, nullptr); |
2693 | | MOZ_ASSERT(NS_SUCCEEDED(rv)); |
2694 | | |
2695 | | RefPtr<ServiceWorkerRegistrationInfo> tmp = |
2696 | | GetRegistration(aPrincipal, aScope); |
2697 | | MOZ_ASSERT(!tmp); |
2698 | | #endif |
2699 | |
|
2700 | 0 | RefPtr<ServiceWorkerRegistrationInfo> registration = |
2701 | 0 | new ServiceWorkerRegistrationInfo(aScope, aPrincipal, aUpdateViaCache); |
2702 | 0 |
|
2703 | 0 | // From now on ownership of registration is with |
2704 | 0 | // mServiceWorkerRegistrationInfos. |
2705 | 0 | AddScopeAndRegistration(aScope, registration); |
2706 | 0 | return registration.forget(); |
2707 | 0 | } |
2708 | | |
2709 | | void |
2710 | | ServiceWorkerManager::MaybeRemoveRegistration(ServiceWorkerRegistrationInfo* aRegistration) |
2711 | 0 | { |
2712 | 0 | MOZ_ASSERT(aRegistration); |
2713 | 0 | RefPtr<ServiceWorkerInfo> newest = aRegistration->Newest(); |
2714 | 0 | if (!newest && HasScope(aRegistration->Principal(), aRegistration->Scope())) { |
2715 | 0 | RemoveRegistration(aRegistration); |
2716 | 0 | } |
2717 | 0 | } |
2718 | | |
2719 | | void |
2720 | | ServiceWorkerManager::RemoveRegistration(ServiceWorkerRegistrationInfo* aRegistration) |
2721 | 0 | { |
2722 | 0 | // Note, we do not need to call mActor->SendUnregister() here. There are a few |
2723 | 0 | // ways we can get here: |
2724 | 0 | // 1) Through a normal unregister which calls SendUnregister() in the unregister |
2725 | 0 | // job Start() method. |
2726 | 0 | // 2) Through origin storage being purged. These result in ForceUnregister() |
2727 | 0 | // starting unregister jobs which in turn call SendUnregister(). |
2728 | 0 | // 3) Through the failure to install a new service worker. Since we don't store |
2729 | 0 | // the registration until install succeeds, we do not need to call |
2730 | 0 | // SendUnregister here. |
2731 | 0 | // Assert these conditions by testing for pending uninstall (cases 1 and 2) or |
2732 | 0 | // null workers (case 3). |
2733 | | #ifdef DEBUG |
2734 | | RefPtr<ServiceWorkerInfo> newest = aRegistration->Newest(); |
2735 | | MOZ_ASSERT(aRegistration->IsPendingUninstall() || !newest); |
2736 | | #endif |
2737 | |
|
2738 | 0 | MOZ_ASSERT(HasScope(aRegistration->Principal(), aRegistration->Scope())); |
2739 | 0 |
|
2740 | 0 | // When a registration is removed, we must clear its contents since the DOM |
2741 | 0 | // object may be held by content script. |
2742 | 0 | aRegistration->Clear(); |
2743 | 0 |
|
2744 | 0 | RemoveScopeAndRegistration(aRegistration); |
2745 | 0 | } |
2746 | | |
2747 | | NS_IMETHODIMP |
2748 | | ServiceWorkerManager::GetAllRegistrations(nsIArray** aResult) |
2749 | 0 | { |
2750 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
2751 | 0 |
|
2752 | 0 | nsCOMPtr<nsIMutableArray> array(do_CreateInstance(NS_ARRAY_CONTRACTID)); |
2753 | 0 | if (!array) { |
2754 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
2755 | 0 | } |
2756 | 0 | |
2757 | 0 | for (auto it1 = mRegistrationInfos.Iter(); !it1.Done(); it1.Next()) { |
2758 | 0 | for (auto it2 = it1.UserData()->mInfos.Iter(); !it2.Done(); it2.Next()) { |
2759 | 0 | ServiceWorkerRegistrationInfo* reg = it2.UserData(); |
2760 | 0 | MOZ_ASSERT(reg); |
2761 | 0 |
|
2762 | 0 | if (reg->IsPendingUninstall()) { |
2763 | 0 | continue; |
2764 | 0 | } |
2765 | 0 | |
2766 | 0 | array->AppendElement(reg); |
2767 | 0 | } |
2768 | 0 | } |
2769 | 0 |
|
2770 | 0 | array.forget(aResult); |
2771 | 0 | return NS_OK; |
2772 | 0 | } |
2773 | | |
2774 | | // MUST ONLY BE CALLED FROM Remove(), RemoveAll() and RemoveAllRegistrations()! |
2775 | | void |
2776 | | ServiceWorkerManager::ForceUnregister(RegistrationDataPerPrincipal* aRegistrationData, |
2777 | | ServiceWorkerRegistrationInfo* aRegistration) |
2778 | 0 | { |
2779 | 0 | MOZ_ASSERT(aRegistrationData); |
2780 | 0 | MOZ_ASSERT(aRegistration); |
2781 | 0 |
|
2782 | 0 | RefPtr<ServiceWorkerJobQueue> queue; |
2783 | 0 | aRegistrationData->mJobQueues.Get(aRegistration->Scope(), getter_AddRefs(queue)); |
2784 | 0 | if (queue) { |
2785 | 0 | queue->CancelAll(); |
2786 | 0 | } |
2787 | 0 |
|
2788 | 0 | if (auto entry = aRegistrationData->mUpdateTimers.Lookup(aRegistration->Scope())) { |
2789 | 0 | entry.Data()->Cancel(); |
2790 | 0 | entry.Remove(); |
2791 | 0 | } |
2792 | 0 |
|
2793 | 0 | // Since Unregister is async, it is ok to call it in an enumeration. |
2794 | 0 | Unregister(aRegistration->Principal(), nullptr, NS_ConvertUTF8toUTF16(aRegistration->Scope())); |
2795 | 0 | } |
2796 | | |
2797 | | void |
2798 | | ServiceWorkerManager::Remove(const nsACString& aHost) |
2799 | 0 | { |
2800 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
2801 | 0 |
|
2802 | 0 | nsCOMPtr<nsIEffectiveTLDService> tldService = |
2803 | 0 | do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID); |
2804 | 0 | if (NS_WARN_IF(!tldService)) { |
2805 | 0 | return; |
2806 | 0 | } |
2807 | 0 | |
2808 | 0 | for (auto it1 = mRegistrationInfos.Iter(); !it1.Done(); it1.Next()) { |
2809 | 0 | ServiceWorkerManager::RegistrationDataPerPrincipal* data = it1.UserData(); |
2810 | 0 | for (auto it2 = data->mInfos.Iter(); !it2.Done(); it2.Next()) { |
2811 | 0 | ServiceWorkerRegistrationInfo* reg = it2.UserData(); |
2812 | 0 | nsCOMPtr<nsIURI> scopeURI; |
2813 | 0 | nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), it2.Key(), |
2814 | 0 | nullptr, nullptr); |
2815 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
2816 | 0 | continue; |
2817 | 0 | } |
2818 | 0 | |
2819 | 0 | nsAutoCString host; |
2820 | 0 | rv = scopeURI->GetHost(host); |
2821 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
2822 | 0 | continue; |
2823 | 0 | } |
2824 | 0 | |
2825 | 0 | // This way subdomains are also cleared. |
2826 | 0 | bool hasRootDomain = false; |
2827 | 0 | rv = tldService->HasRootDomain(host, aHost, &hasRootDomain); |
2828 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
2829 | 0 | continue; |
2830 | 0 | } |
2831 | 0 | |
2832 | 0 | if (hasRootDomain) { |
2833 | 0 | ForceUnregister(data, reg); |
2834 | 0 | } |
2835 | 0 | } |
2836 | 0 | } |
2837 | 0 | } |
2838 | | |
2839 | | void |
2840 | | ServiceWorkerManager::PropagateRemove(const nsACString& aHost) |
2841 | 0 | { |
2842 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
2843 | 0 | mActor->SendPropagateRemove(nsCString(aHost)); |
2844 | 0 | } |
2845 | | |
2846 | | void |
2847 | | ServiceWorkerManager::RemoveAll() |
2848 | 0 | { |
2849 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
2850 | 0 |
|
2851 | 0 | for (auto it1 = mRegistrationInfos.Iter(); !it1.Done(); it1.Next()) { |
2852 | 0 | ServiceWorkerManager::RegistrationDataPerPrincipal* data = it1.UserData(); |
2853 | 0 | for (auto it2 = data->mInfos.Iter(); !it2.Done(); it2.Next()) { |
2854 | 0 | ServiceWorkerRegistrationInfo* reg = it2.UserData(); |
2855 | 0 | ForceUnregister(data, reg); |
2856 | 0 | } |
2857 | 0 | } |
2858 | 0 | } |
2859 | | |
2860 | | void |
2861 | | ServiceWorkerManager::PropagateRemoveAll() |
2862 | 0 | { |
2863 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
2864 | 0 | MOZ_ASSERT(XRE_IsParentProcess()); |
2865 | 0 | mActor->SendPropagateRemoveAll(); |
2866 | 0 | } |
2867 | | |
2868 | | void |
2869 | | ServiceWorkerManager::RemoveAllRegistrations(OriginAttributesPattern* aPattern) |
2870 | 0 | { |
2871 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
2872 | 0 |
|
2873 | 0 | MOZ_ASSERT(aPattern); |
2874 | 0 |
|
2875 | 0 | for (auto it1 = mRegistrationInfos.Iter(); !it1.Done(); it1.Next()) { |
2876 | 0 | ServiceWorkerManager::RegistrationDataPerPrincipal* data = it1.UserData(); |
2877 | 0 |
|
2878 | 0 | // We can use iteration because ForceUnregister (and Unregister) are |
2879 | 0 | // async. Otherwise doing some R/W operations on an hashtable during |
2880 | 0 | // iteration will crash. |
2881 | 0 | for (auto it2 = data->mInfos.Iter(); !it2.Done(); it2.Next()) { |
2882 | 0 | ServiceWorkerRegistrationInfo* reg = it2.UserData(); |
2883 | 0 |
|
2884 | 0 | MOZ_ASSERT(reg); |
2885 | 0 | MOZ_ASSERT(reg->Principal()); |
2886 | 0 |
|
2887 | 0 | bool matches = |
2888 | 0 | aPattern->Matches(reg->Principal()->OriginAttributesRef()); |
2889 | 0 | if (!matches) { |
2890 | 0 | continue; |
2891 | 0 | } |
2892 | 0 | |
2893 | 0 | ForceUnregister(data, reg); |
2894 | 0 | } |
2895 | 0 | } |
2896 | 0 | } |
2897 | | |
2898 | | NS_IMETHODIMP |
2899 | | ServiceWorkerManager::AddListener(nsIServiceWorkerManagerListener* aListener) |
2900 | 0 | { |
2901 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
2902 | 0 |
|
2903 | 0 | if (!aListener || mListeners.Contains(aListener)) { |
2904 | 0 | return NS_ERROR_INVALID_ARG; |
2905 | 0 | } |
2906 | 0 | |
2907 | 0 | mListeners.AppendElement(aListener); |
2908 | 0 |
|
2909 | 0 | return NS_OK; |
2910 | 0 | } |
2911 | | |
2912 | | NS_IMETHODIMP |
2913 | | ServiceWorkerManager::RemoveListener(nsIServiceWorkerManagerListener* aListener) |
2914 | 0 | { |
2915 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
2916 | 0 |
|
2917 | 0 | if (!aListener || !mListeners.Contains(aListener)) { |
2918 | 0 | return NS_ERROR_INVALID_ARG; |
2919 | 0 | } |
2920 | 0 | |
2921 | 0 | mListeners.RemoveElement(aListener); |
2922 | 0 |
|
2923 | 0 | return NS_OK; |
2924 | 0 | } |
2925 | | |
2926 | | NS_IMETHODIMP |
2927 | | ServiceWorkerManager::Observe(nsISupports* aSubject, |
2928 | | const char* aTopic, |
2929 | | const char16_t* aData) |
2930 | 0 | { |
2931 | 0 | if (strcmp(aTopic, CLEAR_ORIGIN_DATA) == 0) { |
2932 | 0 | MOZ_ASSERT(XRE_IsParentProcess()); |
2933 | 0 | OriginAttributesPattern pattern; |
2934 | 0 | MOZ_ALWAYS_TRUE(pattern.Init(nsAutoString(aData))); |
2935 | 0 |
|
2936 | 0 | RemoveAllRegistrations(&pattern); |
2937 | 0 | return NS_OK; |
2938 | 0 | } |
2939 | 0 |
|
2940 | 0 | if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) { |
2941 | 0 | MaybeStartShutdown(); |
2942 | 0 | return NS_OK; |
2943 | 0 | } |
2944 | 0 | |
2945 | 0 | MOZ_CRASH("Received message we aren't supposed to be registered for!"); |
2946 | 0 | return NS_OK; |
2947 | 0 | } |
2948 | | |
2949 | | NS_IMETHODIMP |
2950 | | ServiceWorkerManager::PropagateSoftUpdate(JS::Handle<JS::Value> aOriginAttributes, |
2951 | | const nsAString& aScope, |
2952 | | JSContext* aCx) |
2953 | 0 | { |
2954 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
2955 | 0 |
|
2956 | 0 | OriginAttributes attrs; |
2957 | 0 | if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) { |
2958 | 0 | return NS_ERROR_INVALID_ARG; |
2959 | 0 | } |
2960 | 0 | |
2961 | 0 | PropagateSoftUpdate(attrs, aScope); |
2962 | 0 | return NS_OK; |
2963 | 0 | } |
2964 | | |
2965 | | void |
2966 | | ServiceWorkerManager::PropagateSoftUpdate(const OriginAttributes& aOriginAttributes, |
2967 | | const nsAString& aScope) |
2968 | 0 | { |
2969 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
2970 | 0 | mActor->SendPropagateSoftUpdate(aOriginAttributes, nsString(aScope)); |
2971 | 0 | } |
2972 | | |
2973 | | NS_IMETHODIMP |
2974 | | ServiceWorkerManager::PropagateUnregister(nsIPrincipal* aPrincipal, |
2975 | | nsIServiceWorkerUnregisterCallback* aCallback, |
2976 | | const nsAString& aScope) |
2977 | 0 | { |
2978 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
2979 | 0 | MOZ_ASSERT(aPrincipal); |
2980 | 0 |
|
2981 | 0 | PrincipalInfo principalInfo; |
2982 | 0 | if (NS_WARN_IF(NS_FAILED(PrincipalToPrincipalInfo(aPrincipal, |
2983 | 0 | &principalInfo)))) { |
2984 | 0 | return NS_ERROR_FAILURE; |
2985 | 0 | } |
2986 | 0 | |
2987 | 0 | mActor->SendPropagateUnregister(principalInfo, nsString(aScope)); |
2988 | 0 |
|
2989 | 0 | nsresult rv = Unregister(aPrincipal, aCallback, aScope); |
2990 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
2991 | 0 | return rv; |
2992 | 0 | } |
2993 | 0 | |
2994 | 0 | return NS_OK; |
2995 | 0 | } |
2996 | | |
2997 | | void |
2998 | | ServiceWorkerManager::NotifyListenersOnRegister( |
2999 | | nsIServiceWorkerRegistrationInfo* aInfo) |
3000 | 0 | { |
3001 | 0 | nsTArray<nsCOMPtr<nsIServiceWorkerManagerListener>> listeners(mListeners); |
3002 | 0 | for (size_t index = 0; index < listeners.Length(); ++index) { |
3003 | 0 | listeners[index]->OnRegister(aInfo); |
3004 | 0 | } |
3005 | 0 | } |
3006 | | |
3007 | | void |
3008 | | ServiceWorkerManager::NotifyListenersOnUnregister( |
3009 | | nsIServiceWorkerRegistrationInfo* aInfo) |
3010 | 0 | { |
3011 | 0 | nsTArray<nsCOMPtr<nsIServiceWorkerManagerListener>> listeners(mListeners); |
3012 | 0 | for (size_t index = 0; index < listeners.Length(); ++index) { |
3013 | 0 | listeners[index]->OnUnregister(aInfo); |
3014 | 0 | } |
3015 | 0 | } |
3016 | | |
3017 | | class UpdateTimerCallback final : public nsITimerCallback |
3018 | | , public nsINamed |
3019 | | { |
3020 | | nsCOMPtr<nsIPrincipal> mPrincipal; |
3021 | | const nsCString mScope; |
3022 | | |
3023 | | ~UpdateTimerCallback() |
3024 | 0 | { |
3025 | 0 | } |
3026 | | |
3027 | | public: |
3028 | | UpdateTimerCallback(nsIPrincipal* aPrincipal, const nsACString& aScope) |
3029 | | : mPrincipal(aPrincipal) |
3030 | | , mScope(aScope) |
3031 | 0 | { |
3032 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
3033 | 0 | MOZ_ASSERT(mPrincipal); |
3034 | 0 | MOZ_ASSERT(!mScope.IsEmpty()); |
3035 | 0 | } |
3036 | | |
3037 | | NS_IMETHOD |
3038 | | Notify(nsITimer* aTimer) override |
3039 | 0 | { |
3040 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
3041 | 0 |
|
3042 | 0 | RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); |
3043 | 0 | if (!swm) { |
3044 | 0 | // shutting down, do nothing |
3045 | 0 | return NS_OK; |
3046 | 0 | } |
3047 | 0 | |
3048 | 0 | swm->UpdateTimerFired(mPrincipal, mScope); |
3049 | 0 | return NS_OK; |
3050 | 0 | } |
3051 | | |
3052 | | NS_IMETHOD |
3053 | | GetName(nsACString& aName) override |
3054 | 0 | { |
3055 | 0 | aName.AssignLiteral("UpdateTimerCallback"); |
3056 | 0 | return NS_OK; |
3057 | 0 | } |
3058 | | |
3059 | | NS_DECL_ISUPPORTS |
3060 | | }; |
3061 | | |
3062 | | NS_IMPL_ISUPPORTS(UpdateTimerCallback, nsITimerCallback, nsINamed) |
3063 | | |
3064 | | bool |
3065 | | ServiceWorkerManager::MayHaveActiveServiceWorkerInstance(ContentParent* aContent, |
3066 | | nsIPrincipal* aPrincipal) |
3067 | 0 | { |
3068 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
3069 | 0 | MOZ_ASSERT(aPrincipal); |
3070 | 0 |
|
3071 | 0 | if (mShuttingDown) { |
3072 | 0 | return false; |
3073 | 0 | } |
3074 | 0 | |
3075 | 0 | nsAutoCString scopeKey; |
3076 | 0 | nsresult rv = PrincipalToScopeKey(aPrincipal, scopeKey); |
3077 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
3078 | 0 | return false; |
3079 | 0 | } |
3080 | 0 | |
3081 | 0 | RegistrationDataPerPrincipal* data; |
3082 | 0 | if (!mRegistrationInfos.Get(scopeKey, &data)) { |
3083 | 0 | return false; |
3084 | 0 | } |
3085 | 0 | |
3086 | 0 | return true; |
3087 | 0 | } |
3088 | | |
3089 | | void |
3090 | | ServiceWorkerManager::ScheduleUpdateTimer(nsIPrincipal* aPrincipal, |
3091 | | const nsACString& aScope) |
3092 | 0 | { |
3093 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
3094 | 0 | MOZ_ASSERT(aPrincipal); |
3095 | 0 | MOZ_ASSERT(!aScope.IsEmpty()); |
3096 | 0 |
|
3097 | 0 | if (mShuttingDown) { |
3098 | 0 | return; |
3099 | 0 | } |
3100 | 0 | |
3101 | 0 | nsAutoCString scopeKey; |
3102 | 0 | nsresult rv = PrincipalToScopeKey(aPrincipal, scopeKey); |
3103 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
3104 | 0 | return; |
3105 | 0 | } |
3106 | 0 | |
3107 | 0 | RegistrationDataPerPrincipal* data; |
3108 | 0 | if (!mRegistrationInfos.Get(scopeKey, &data)) { |
3109 | 0 | return; |
3110 | 0 | } |
3111 | 0 | |
3112 | 0 | nsCOMPtr<nsITimer>& timer = data->mUpdateTimers.GetOrInsert(aScope); |
3113 | 0 | if (timer) { |
3114 | 0 | // There is already a timer scheduled. In this case just use the original |
3115 | 0 | // schedule time. We don't want to push it out to a later time since that |
3116 | 0 | // could allow updates to be starved forever if events are continuously |
3117 | 0 | // fired. |
3118 | 0 | return; |
3119 | 0 | } |
3120 | 0 | |
3121 | 0 | nsCOMPtr<nsITimerCallback> callback = new UpdateTimerCallback(aPrincipal, |
3122 | 0 | aScope); |
3123 | 0 |
|
3124 | 0 | const uint32_t UPDATE_DELAY_MS = 1000; |
3125 | 0 |
|
3126 | 0 | // Label with SystemGroup because UpdateTimerCallback only sends an IPC message |
3127 | 0 | // (PServiceWorkerUpdaterConstructor) without touching any web contents. |
3128 | 0 | rv = NS_NewTimerWithCallback(getter_AddRefs(timer), |
3129 | 0 | callback, UPDATE_DELAY_MS, |
3130 | 0 | nsITimer::TYPE_ONE_SHOT, |
3131 | 0 | SystemGroup::EventTargetFor(TaskCategory::Other)); |
3132 | 0 |
|
3133 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
3134 | 0 | data->mUpdateTimers.Remove(aScope); // another lookup, but very rare |
3135 | 0 | return; |
3136 | 0 | } |
3137 | 0 | } |
3138 | | |
3139 | | void |
3140 | | ServiceWorkerManager::UpdateTimerFired(nsIPrincipal* aPrincipal, |
3141 | | const nsACString& aScope) |
3142 | 0 | { |
3143 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
3144 | 0 | MOZ_ASSERT(aPrincipal); |
3145 | 0 | MOZ_ASSERT(!aScope.IsEmpty()); |
3146 | 0 |
|
3147 | 0 | if (mShuttingDown) { |
3148 | 0 | return; |
3149 | 0 | } |
3150 | 0 | |
3151 | 0 | // First cleanup the timer. |
3152 | 0 | nsAutoCString scopeKey; |
3153 | 0 | nsresult rv = PrincipalToScopeKey(aPrincipal, scopeKey); |
3154 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
3155 | 0 | return; |
3156 | 0 | } |
3157 | 0 | |
3158 | 0 | RegistrationDataPerPrincipal* data; |
3159 | 0 | if (!mRegistrationInfos.Get(scopeKey, &data)) { |
3160 | 0 | return; |
3161 | 0 | } |
3162 | 0 | |
3163 | 0 | if (auto entry = data->mUpdateTimers.Lookup(aScope)) { |
3164 | 0 | entry.Data()->Cancel(); |
3165 | 0 | entry.Remove(); |
3166 | 0 | } |
3167 | 0 |
|
3168 | 0 | RefPtr<ServiceWorkerRegistrationInfo> registration; |
3169 | 0 | data->mInfos.Get(aScope, getter_AddRefs(registration)); |
3170 | 0 | if (!registration) { |
3171 | 0 | return; |
3172 | 0 | } |
3173 | 0 | |
3174 | 0 | if (!registration->CheckAndClearIfUpdateNeeded()) { |
3175 | 0 | return; |
3176 | 0 | } |
3177 | 0 | |
3178 | 0 | OriginAttributes attrs = aPrincipal->OriginAttributesRef(); |
3179 | 0 |
|
3180 | 0 | SoftUpdate(attrs, aScope); |
3181 | 0 | } |
3182 | | |
3183 | | void |
3184 | | ServiceWorkerManager::MaybeSendUnregister(nsIPrincipal* aPrincipal, |
3185 | | const nsACString& aScope) |
3186 | 0 | { |
3187 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
3188 | 0 | MOZ_ASSERT(aPrincipal); |
3189 | 0 | MOZ_ASSERT(!aScope.IsEmpty()); |
3190 | 0 |
|
3191 | 0 | if (!mActor) { |
3192 | 0 | return; |
3193 | 0 | } |
3194 | 0 | |
3195 | 0 | PrincipalInfo principalInfo; |
3196 | 0 | nsresult rv = PrincipalToPrincipalInfo(aPrincipal, &principalInfo); |
3197 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
3198 | 0 | return; |
3199 | 0 | } |
3200 | 0 | |
3201 | 0 | Unused << mActor->SendUnregister(principalInfo, NS_ConvertUTF8toUTF16(aScope)); |
3202 | 0 | } |
3203 | | |
3204 | | } // namespace dom |
3205 | | } // namespace mozilla |