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/. */
7 #include "ServiceWorkerRegistrationProxy.h"
9 #include "mozilla/SchedulerGroup.h"
10 #include "mozilla/ScopeExit.h"
11 #include "mozilla/ipc/BackgroundParent.h"
12 #include "ServiceWorkerManager.h"
13 #include "ServiceWorkerRegistrationParent.h"
14 #include "ServiceWorkerUnregisterCallback.h"
16 namespace mozilla::dom
{
18 using mozilla::ipc::AssertIsOnBackgroundThread
;
20 class ServiceWorkerRegistrationProxy::DelayedUpdate final
21 : public nsITimerCallback
,
23 RefPtr
<ServiceWorkerRegistrationProxy
> mProxy
;
24 RefPtr
<ServiceWorkerRegistrationPromise::Private
> mPromise
;
25 nsCOMPtr
<nsITimer
> mTimer
;
26 nsCString mNewestWorkerScriptUrl
;
28 ~DelayedUpdate() = default;
31 NS_DECL_THREADSAFE_ISUPPORTS
32 NS_DECL_NSITIMERCALLBACK
35 DelayedUpdate(RefPtr
<ServiceWorkerRegistrationProxy
>&& aProxy
,
36 RefPtr
<ServiceWorkerRegistrationPromise::Private
>&& aPromise
,
37 nsCString
&& aNewestWorkerScriptUrl
, uint32_t delay
);
39 void ChainTo(RefPtr
<ServiceWorkerRegistrationPromise::Private
> aPromise
);
43 void SetNewestWorkerScriptUrl(nsCString
&& aNewestWorkerScriptUrl
);
46 ServiceWorkerRegistrationProxy::~ServiceWorkerRegistrationProxy() {
48 MOZ_DIAGNOSTIC_ASSERT(!mActor
);
49 MOZ_DIAGNOSTIC_ASSERT(!mReg
);
52 void ServiceWorkerRegistrationProxy::MaybeShutdownOnBGThread() {
53 AssertIsOnBackgroundThread();
57 mActor
->MaybeSendDelete();
60 void ServiceWorkerRegistrationProxy::UpdateStateOnBGThread(
61 const ServiceWorkerRegistrationDescriptor
& aDescriptor
) {
62 AssertIsOnBackgroundThread();
66 Unused
<< mActor
->SendUpdateState(aDescriptor
.ToIPC());
69 void ServiceWorkerRegistrationProxy::FireUpdateFoundOnBGThread() {
70 AssertIsOnBackgroundThread();
74 Unused
<< mActor
->SendFireUpdateFound();
77 void ServiceWorkerRegistrationProxy::InitOnMainThread() {
78 AssertIsOnMainThread();
80 auto scopeExit
= MakeScopeExit([&] { MaybeShutdownOnMainThread(); });
82 RefPtr
<ServiceWorkerManager
> swm
= ServiceWorkerManager::GetInstance();
83 NS_ENSURE_TRUE_VOID(swm
);
85 RefPtr
<ServiceWorkerRegistrationInfo
> reg
=
86 swm
->GetRegistration(mDescriptor
.PrincipalInfo(), mDescriptor
.Scope());
87 NS_ENSURE_TRUE_VOID(reg
);
89 if (reg
->Id() != mDescriptor
.Id()) {
90 // This registration has already been replaced by another one.
96 mReg
= new nsMainThreadPtrHolder
<ServiceWorkerRegistrationInfo
>(
97 "ServiceWorkerRegistrationProxy::mInfo", reg
);
99 mReg
->AddInstance(this, mDescriptor
);
102 void ServiceWorkerRegistrationProxy::MaybeShutdownOnMainThread() {
103 AssertIsOnMainThread();
105 if (mDelayedUpdate
) {
106 mDelayedUpdate
->Reject();
107 mDelayedUpdate
= nullptr;
109 nsCOMPtr
<nsIRunnable
> r
= NewRunnableMethod(
110 __func__
, this, &ServiceWorkerRegistrationProxy::MaybeShutdownOnBGThread
);
112 MOZ_ALWAYS_SUCCEEDS(mEventTarget
->Dispatch(r
.forget(), NS_DISPATCH_NORMAL
));
115 void ServiceWorkerRegistrationProxy::StopListeningOnMainThread() {
116 AssertIsOnMainThread();
122 mReg
->RemoveInstance(this);
126 void ServiceWorkerRegistrationProxy::UpdateState(
127 const ServiceWorkerRegistrationDescriptor
& aDescriptor
) {
128 AssertIsOnMainThread();
130 if (mDescriptor
== aDescriptor
) {
133 mDescriptor
= aDescriptor
;
135 nsCOMPtr
<nsIRunnable
> r
=
136 NewRunnableMethod
<ServiceWorkerRegistrationDescriptor
>(
138 &ServiceWorkerRegistrationProxy::UpdateStateOnBGThread
, aDescriptor
);
140 MOZ_ALWAYS_SUCCEEDS(mEventTarget
->Dispatch(r
.forget(), NS_DISPATCH_NORMAL
));
143 void ServiceWorkerRegistrationProxy::FireUpdateFound() {
144 AssertIsOnMainThread();
146 nsCOMPtr
<nsIRunnable
> r
= NewRunnableMethod(
148 &ServiceWorkerRegistrationProxy::FireUpdateFoundOnBGThread
);
150 MOZ_ALWAYS_SUCCEEDS(mEventTarget
->Dispatch(r
.forget(), NS_DISPATCH_NORMAL
));
153 void ServiceWorkerRegistrationProxy::RegistrationCleared() {
154 MaybeShutdownOnMainThread();
157 void ServiceWorkerRegistrationProxy::GetScope(nsAString
& aScope
) const {
158 CopyUTF8toUTF16(mDescriptor
.Scope(), aScope
);
161 bool ServiceWorkerRegistrationProxy::MatchesDescriptor(
162 const ServiceWorkerRegistrationDescriptor
& aDescriptor
) {
163 AssertIsOnMainThread();
164 return aDescriptor
.Id() == mDescriptor
.Id() &&
165 aDescriptor
.PrincipalInfo() == mDescriptor
.PrincipalInfo() &&
166 aDescriptor
.Scope() == mDescriptor
.Scope();
169 ServiceWorkerRegistrationProxy::ServiceWorkerRegistrationProxy(
170 const ServiceWorkerRegistrationDescriptor
& aDescriptor
)
171 : mEventTarget(GetCurrentSerialEventTarget()), mDescriptor(aDescriptor
) {}
173 void ServiceWorkerRegistrationProxy::Init(
174 ServiceWorkerRegistrationParent
* aActor
) {
175 AssertIsOnBackgroundThread();
176 MOZ_DIAGNOSTIC_ASSERT(aActor
);
177 MOZ_DIAGNOSTIC_ASSERT(!mActor
);
178 MOZ_DIAGNOSTIC_ASSERT(mEventTarget
);
182 // Note, this must be done from a separate Init() method and not in
183 // the constructor. If done from the constructor the runnable can
184 // execute, complete, and release its reference before the constructor
186 nsCOMPtr
<nsIRunnable
> r
=
187 NewRunnableMethod("ServiceWorkerRegistrationProxy::Init", this,
188 &ServiceWorkerRegistrationProxy::InitOnMainThread
);
189 MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch(r
.forget()));
192 void ServiceWorkerRegistrationProxy::RevokeActor(
193 ServiceWorkerRegistrationParent
* aActor
) {
194 AssertIsOnBackgroundThread();
195 MOZ_DIAGNOSTIC_ASSERT(mActor
);
196 MOZ_DIAGNOSTIC_ASSERT(mActor
== aActor
);
199 nsCOMPtr
<nsIRunnable
> r
= NewRunnableMethod(
201 &ServiceWorkerRegistrationProxy::StopListeningOnMainThread
);
202 MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch(r
.forget()));
205 RefPtr
<GenericPromise
> ServiceWorkerRegistrationProxy::Unregister() {
206 AssertIsOnBackgroundThread();
208 RefPtr
<ServiceWorkerRegistrationProxy
> self
= this;
209 RefPtr
<GenericPromise::Private
> promise
=
210 new GenericPromise::Private(__func__
);
212 nsCOMPtr
<nsIRunnable
> r
=
213 NS_NewRunnableFunction(__func__
, [self
, promise
]() mutable {
214 nsresult rv
= NS_ERROR_DOM_INVALID_STATE_ERR
;
215 auto scopeExit
= MakeScopeExit([&] { promise
->Reject(rv
, __func__
); });
217 NS_ENSURE_TRUE_VOID(self
->mReg
);
219 RefPtr
<ServiceWorkerManager
> swm
= ServiceWorkerManager::GetInstance();
220 NS_ENSURE_TRUE_VOID(swm
);
222 RefPtr
<UnregisterCallback
> cb
= new UnregisterCallback(promise
);
224 rv
= swm
->Unregister(self
->mReg
->Principal(), cb
,
225 NS_ConvertUTF8toUTF16(self
->mReg
->Scope()));
226 NS_ENSURE_SUCCESS_VOID(rv
);
231 MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch(r
.forget()));
238 class UpdateCallback final
: public ServiceWorkerUpdateFinishCallback
{
239 RefPtr
<ServiceWorkerRegistrationPromise::Private
> mPromise
;
241 ~UpdateCallback() = default;
244 explicit UpdateCallback(
245 RefPtr
<ServiceWorkerRegistrationPromise::Private
>&& aPromise
)
246 : mPromise(std::move(aPromise
)) {
247 MOZ_DIAGNOSTIC_ASSERT(mPromise
);
250 void UpdateSucceeded(ServiceWorkerRegistrationInfo
* aInfo
) override
{
251 mPromise
->Resolve(aInfo
->Descriptor(), __func__
);
254 void UpdateFailed(ErrorResult
& aResult
) override
{
255 mPromise
->Reject(CopyableErrorResult(aResult
), __func__
);
259 } // anonymous namespace
261 NS_IMPL_ISUPPORTS(ServiceWorkerRegistrationProxy::DelayedUpdate
,
262 nsITimerCallback
, nsINamed
)
264 ServiceWorkerRegistrationProxy::DelayedUpdate::DelayedUpdate(
265 RefPtr
<ServiceWorkerRegistrationProxy
>&& aProxy
,
266 RefPtr
<ServiceWorkerRegistrationPromise::Private
>&& aPromise
,
267 nsCString
&& aNewestWorkerScriptUrl
, uint32_t delay
)
268 : mProxy(std::move(aProxy
)),
269 mPromise(std::move(aPromise
)),
270 mNewestWorkerScriptUrl(std::move(aNewestWorkerScriptUrl
)) {
271 MOZ_DIAGNOSTIC_ASSERT(mProxy
);
272 MOZ_DIAGNOSTIC_ASSERT(mPromise
);
273 MOZ_ASSERT(!mNewestWorkerScriptUrl
.IsEmpty());
274 mProxy
->mDelayedUpdate
= this;
275 Result
<nsCOMPtr
<nsITimer
>, nsresult
> result
=
276 NS_NewTimerWithCallback(this, delay
, nsITimer::TYPE_ONE_SHOT
);
277 mTimer
= result
.unwrapOr(nullptr);
278 MOZ_DIAGNOSTIC_ASSERT(mTimer
);
281 void ServiceWorkerRegistrationProxy::DelayedUpdate::ChainTo(
282 RefPtr
<ServiceWorkerRegistrationPromise::Private
> aPromise
) {
283 AssertIsOnMainThread();
284 MOZ_ASSERT(mProxy
->mDelayedUpdate
== this);
285 MOZ_ASSERT(mPromise
);
287 mPromise
->ChainTo(aPromise
.forget(), __func__
);
290 void ServiceWorkerRegistrationProxy::DelayedUpdate::Reject() {
291 MOZ_DIAGNOSTIC_ASSERT(mPromise
);
296 mPromise
->Reject(NS_ERROR_DOM_INVALID_STATE_ERR
, __func__
);
299 void ServiceWorkerRegistrationProxy::DelayedUpdate::SetNewestWorkerScriptUrl(
300 nsCString
&& aNewestWorkerScriptUrl
) {
301 MOZ_ASSERT(NS_IsMainThread());
302 mNewestWorkerScriptUrl
= std::move(aNewestWorkerScriptUrl
);
306 ServiceWorkerRegistrationProxy::DelayedUpdate::Notify(nsITimer
* aTimer
) {
307 // Already shutting down.
308 if (mProxy
->mDelayedUpdate
!= this) {
312 auto scopeExit
= MakeScopeExit(
313 [&] { mPromise
->Reject(NS_ERROR_DOM_INVALID_STATE_ERR
, __func__
); });
315 NS_ENSURE_TRUE(mProxy
->mReg
, NS_ERROR_FAILURE
);
317 RefPtr
<ServiceWorkerManager
> swm
= ServiceWorkerManager::GetInstance();
318 NS_ENSURE_TRUE(swm
, NS_ERROR_FAILURE
);
320 RefPtr
<UpdateCallback
> cb
= new UpdateCallback(std::move(mPromise
));
321 swm
->Update(mProxy
->mReg
->Principal(), mProxy
->mReg
->Scope(),
322 std::move(mNewestWorkerScriptUrl
), cb
);
325 mProxy
->mDelayedUpdate
= nullptr;
332 ServiceWorkerRegistrationProxy::DelayedUpdate::GetName(nsACString
& aName
) {
333 aName
.AssignLiteral("ServiceWorkerRegistrationProxy::DelayedUpdate");
337 RefPtr
<ServiceWorkerRegistrationPromise
> ServiceWorkerRegistrationProxy::Update(
338 const nsACString
& aNewestWorkerScriptUrl
) {
339 AssertIsOnBackgroundThread();
341 RefPtr
<ServiceWorkerRegistrationProxy
> self
= this;
342 RefPtr
<ServiceWorkerRegistrationPromise::Private
> promise
=
343 new ServiceWorkerRegistrationPromise::Private(__func__
);
345 nsCOMPtr
<nsIRunnable
> r
= NS_NewRunnableFunction(
348 newestWorkerScriptUrl
= nsCString(aNewestWorkerScriptUrl
)]() mutable {
349 auto scopeExit
= MakeScopeExit(
350 [&] { promise
->Reject(NS_ERROR_DOM_INVALID_STATE_ERR
, __func__
); });
352 // Get the delay value for the update
353 NS_ENSURE_TRUE_VOID(self
->mReg
);
354 uint32_t delay
= self
->mReg
->GetUpdateDelay(false);
356 // If the delay value does not equal to 0, create a timer and a timer
357 // callback to perform the delayed update. Otherwise, update directly.
359 if (self
->mDelayedUpdate
) {
360 // NOTE: if we `ChainTo(),` there will ultimately be a single
361 // update, and this update will resolve all promises that were
362 // issued while the update's timer was ticking down.
363 self
->mDelayedUpdate
->ChainTo(std::move(promise
));
365 // Use the "newest newest worker"'s script URL.
366 self
->mDelayedUpdate
->SetNewestWorkerScriptUrl(
367 std::move(newestWorkerScriptUrl
));
369 RefPtr
<ServiceWorkerRegistrationProxy::DelayedUpdate
> du
=
370 new ServiceWorkerRegistrationProxy::DelayedUpdate(
371 std::move(self
), std::move(promise
),
372 std::move(newestWorkerScriptUrl
), delay
);
375 RefPtr
<ServiceWorkerManager
> swm
=
376 ServiceWorkerManager::GetInstance();
377 NS_ENSURE_TRUE_VOID(swm
);
379 RefPtr
<UpdateCallback
> cb
= new UpdateCallback(std::move(promise
));
380 swm
->Update(self
->mReg
->Principal(), self
->mReg
->Scope(),
381 std::move(newestWorkerScriptUrl
), cb
);
386 MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch(r
.forget()));
391 RefPtr
<GenericPromise
>
392 ServiceWorkerRegistrationProxy::SetNavigationPreloadEnabled(
393 const bool& aEnabled
) {
394 AssertIsOnBackgroundThread();
396 RefPtr
<ServiceWorkerRegistrationProxy
> self
= this;
397 RefPtr
<GenericPromise::Private
> promise
=
398 new GenericPromise::Private(__func__
);
400 nsCOMPtr
<nsIRunnable
> r
=
401 NS_NewRunnableFunction(__func__
, [aEnabled
, self
, promise
]() mutable {
402 nsresult rv
= NS_ERROR_DOM_INVALID_STATE_ERR
;
403 auto scopeExit
= MakeScopeExit([&] { promise
->Reject(rv
, __func__
); });
405 NS_ENSURE_TRUE_VOID(self
->mReg
);
406 NS_ENSURE_TRUE_VOID(self
->mReg
->GetActive());
408 auto reg
= self
->mReg
;
409 reg
->SetNavigationPreloadEnabled(aEnabled
);
411 RefPtr
<ServiceWorkerManager
> swm
= ServiceWorkerManager::GetInstance();
412 NS_ENSURE_TRUE_VOID(swm
);
413 swm
->StoreRegistration(reg
->Principal(), reg
);
417 promise
->Resolve(true, __func__
);
420 MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch(r
.forget()));
425 RefPtr
<GenericPromise
>
426 ServiceWorkerRegistrationProxy::SetNavigationPreloadHeader(
427 const nsACString
& aHeader
) {
428 AssertIsOnBackgroundThread();
430 RefPtr
<ServiceWorkerRegistrationProxy
> self
= this;
431 RefPtr
<GenericPromise::Private
> promise
=
432 new GenericPromise::Private(__func__
);
434 nsCOMPtr
<nsIRunnable
> r
= NS_NewRunnableFunction(
435 __func__
, [aHeader
= nsCString(aHeader
), self
, promise
]() mutable {
436 nsresult rv
= NS_ERROR_DOM_INVALID_STATE_ERR
;
437 auto scopeExit
= MakeScopeExit([&] { promise
->Reject(rv
, __func__
); });
439 NS_ENSURE_TRUE_VOID(self
->mReg
);
440 NS_ENSURE_TRUE_VOID(self
->mReg
->GetActive());
442 auto reg
= self
->mReg
;
443 reg
->SetNavigationPreloadHeader(aHeader
);
445 RefPtr
<ServiceWorkerManager
> swm
= ServiceWorkerManager::GetInstance();
446 NS_ENSURE_TRUE_VOID(swm
);
447 swm
->StoreRegistration(reg
->Principal(), reg
);
451 promise
->Resolve(true, __func__
);
454 MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch(r
.forget()));
459 RefPtr
<NavigationPreloadStatePromise
>
460 ServiceWorkerRegistrationProxy::GetNavigationPreloadState() {
461 AssertIsOnBackgroundThread();
463 RefPtr
<ServiceWorkerRegistrationProxy
> self
= this;
464 RefPtr
<NavigationPreloadStatePromise::Private
> promise
=
465 new NavigationPreloadStatePromise::Private(__func__
);
467 nsCOMPtr
<nsIRunnable
> r
=
468 NS_NewRunnableFunction(__func__
, [self
, promise
]() mutable {
469 nsresult rv
= NS_ERROR_DOM_INVALID_STATE_ERR
;
470 auto scopeExit
= MakeScopeExit([&] { promise
->Reject(rv
, __func__
); });
472 NS_ENSURE_TRUE_VOID(self
->mReg
);
475 promise
->Resolve(self
->mReg
->GetNavigationPreloadState(), __func__
);
478 MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch(r
.forget()));
483 } // namespace mozilla::dom