no bug - Bumping Firefox l10n changesets r=release a=l10n-bump DONTBUILD CLOSED TREE
[gecko.git] / dom / serviceworkers / ServiceWorkerRegistrationProxy.cpp
blob60756b828d79e49db7b156f3575a7a13223b98aa
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,
22 public nsINamed {
23 RefPtr<ServiceWorkerRegistrationProxy> mProxy;
24 RefPtr<ServiceWorkerRegistrationPromise::Private> mPromise;
25 nsCOMPtr<nsITimer> mTimer;
26 nsCString mNewestWorkerScriptUrl;
28 ~DelayedUpdate() = default;
30 public:
31 NS_DECL_THREADSAFE_ISUPPORTS
32 NS_DECL_NSITIMERCALLBACK
33 NS_DECL_NSINAMED
35 DelayedUpdate(RefPtr<ServiceWorkerRegistrationProxy>&& aProxy,
36 RefPtr<ServiceWorkerRegistrationPromise::Private>&& aPromise,
37 nsCString&& aNewestWorkerScriptUrl, uint32_t delay);
39 void ChainTo(RefPtr<ServiceWorkerRegistrationPromise::Private> aPromise);
41 void Reject();
43 void SetNewestWorkerScriptUrl(nsCString&& aNewestWorkerScriptUrl);
46 ServiceWorkerRegistrationProxy::~ServiceWorkerRegistrationProxy() {
47 // Any thread
48 MOZ_DIAGNOSTIC_ASSERT(!mActor);
49 MOZ_DIAGNOSTIC_ASSERT(!mReg);
52 void ServiceWorkerRegistrationProxy::MaybeShutdownOnBGThread() {
53 AssertIsOnBackgroundThread();
54 if (!mActor) {
55 return;
57 mActor->MaybeSendDelete();
60 void ServiceWorkerRegistrationProxy::UpdateStateOnBGThread(
61 const ServiceWorkerRegistrationDescriptor& aDescriptor) {
62 AssertIsOnBackgroundThread();
63 if (!mActor) {
64 return;
66 Unused << mActor->SendUpdateState(aDescriptor.ToIPC());
69 void ServiceWorkerRegistrationProxy::FireUpdateFoundOnBGThread() {
70 AssertIsOnBackgroundThread();
71 if (!mActor) {
72 return;
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.
91 return;
94 scopeExit.release();
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();
118 if (!mReg) {
119 return;
122 mReg->RemoveInstance(this);
123 mReg = nullptr;
126 void ServiceWorkerRegistrationProxy::UpdateState(
127 const ServiceWorkerRegistrationDescriptor& aDescriptor) {
128 AssertIsOnMainThread();
130 if (mDescriptor == aDescriptor) {
131 return;
133 mDescriptor = aDescriptor;
135 nsCOMPtr<nsIRunnable> r =
136 NewRunnableMethod<ServiceWorkerRegistrationDescriptor>(
137 __func__, this,
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(
147 __func__, this,
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);
180 mActor = aActor;
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
185 // returns.
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);
197 mActor = nullptr;
199 nsCOMPtr<nsIRunnable> r = NewRunnableMethod(
200 __func__, this,
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);
228 scopeExit.release();
231 MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch(r.forget()));
233 return promise;
236 namespace {
238 class UpdateCallback final : public ServiceWorkerUpdateFinishCallback {
239 RefPtr<ServiceWorkerRegistrationPromise::Private> mPromise;
241 ~UpdateCallback() = default;
243 public:
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);
292 if (mTimer) {
293 mTimer->Cancel();
294 mTimer = nullptr;
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);
305 NS_IMETHODIMP
306 ServiceWorkerRegistrationProxy::DelayedUpdate::Notify(nsITimer* aTimer) {
307 // Already shutting down.
308 if (mProxy->mDelayedUpdate != this) {
309 return NS_OK;
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);
324 mTimer = nullptr;
325 mProxy->mDelayedUpdate = nullptr;
327 scopeExit.release();
328 return NS_OK;
331 NS_IMETHODIMP
332 ServiceWorkerRegistrationProxy::DelayedUpdate::GetName(nsACString& aName) {
333 aName.AssignLiteral("ServiceWorkerRegistrationProxy::DelayedUpdate");
334 return NS_OK;
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(
346 __func__,
347 [self, promise,
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.
358 if (delay) {
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));
368 } else {
369 RefPtr<ServiceWorkerRegistrationProxy::DelayedUpdate> du =
370 new ServiceWorkerRegistrationProxy::DelayedUpdate(
371 std::move(self), std::move(promise),
372 std::move(newestWorkerScriptUrl), delay);
374 } else {
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);
383 scopeExit.release();
386 MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch(r.forget()));
388 return promise;
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);
415 scopeExit.release();
417 promise->Resolve(true, __func__);
420 MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch(r.forget()));
422 return promise;
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);
449 scopeExit.release();
451 promise->Resolve(true, __func__);
454 MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch(r.forget()));
456 return promise;
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);
473 scopeExit.release();
475 promise->Resolve(self->mReg->GetNavigationPreloadState(), __func__);
478 MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch(r.forget()));
480 return promise;
483 } // namespace mozilla::dom