ServiceWorkerRegistrationProxy.cpp (16640B)
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 "ServiceWorkerRegistrationProxy.h" 8 9 #include "ServiceWorkerManager.h" 10 #include "ServiceWorkerRegistrationParent.h" 11 #include "ServiceWorkerUnregisterCallback.h" 12 #include "mozilla/SchedulerGroup.h" 13 #include "mozilla/ScopeExit.h" 14 #include "mozilla/dom/notification/NotificationUtils.h" 15 #include "mozilla/ipc/BackgroundParent.h" 16 #include "nsINotificationStorage.h" 17 18 namespace mozilla::dom { 19 20 using mozilla::ipc::AssertIsOnBackgroundThread; 21 using namespace mozilla::dom::notification; 22 23 class ServiceWorkerRegistrationProxy::DelayedUpdate final 24 : public nsITimerCallback, 25 public nsINamed { 26 RefPtr<ServiceWorkerRegistrationProxy> mProxy; 27 RefPtr<ServiceWorkerRegistrationPromise::Private> mPromise; 28 nsCOMPtr<nsITimer> mTimer; 29 nsCString mNewestWorkerScriptUrl; 30 31 ~DelayedUpdate() = default; 32 33 public: 34 NS_DECL_THREADSAFE_ISUPPORTS 35 NS_DECL_NSITIMERCALLBACK 36 NS_DECL_NSINAMED 37 38 DelayedUpdate(RefPtr<ServiceWorkerRegistrationProxy>&& aProxy, 39 RefPtr<ServiceWorkerRegistrationPromise::Private>&& aPromise, 40 nsCString&& aNewestWorkerScriptUrl, uint32_t delay); 41 42 void ChainTo(RefPtr<ServiceWorkerRegistrationPromise::Private> aPromise); 43 44 void Reject(); 45 46 void SetNewestWorkerScriptUrl(nsCString&& aNewestWorkerScriptUrl); 47 }; 48 49 ServiceWorkerRegistrationProxy::~ServiceWorkerRegistrationProxy() { 50 // Any thread 51 MOZ_DIAGNOSTIC_ASSERT(!mActor); 52 MOZ_DIAGNOSTIC_ASSERT(!mReg); 53 } 54 55 void ServiceWorkerRegistrationProxy::MaybeShutdownOnBGThread() { 56 AssertIsOnBackgroundThread(); 57 if (!mActor) { 58 return; 59 } 60 mActor->MaybeSendDelete(); 61 } 62 63 void ServiceWorkerRegistrationProxy::UpdateStateOnBGThread( 64 const ServiceWorkerRegistrationDescriptor& aDescriptor) { 65 AssertIsOnBackgroundThread(); 66 if (!mActor) { 67 return; 68 } 69 (void)mActor->SendUpdateState(aDescriptor.ToIPC()); 70 } 71 72 void ServiceWorkerRegistrationProxy::FireUpdateFoundOnBGThread() { 73 AssertIsOnBackgroundThread(); 74 if (!mActor) { 75 return; 76 } 77 (void)mActor->SendFireUpdateFound(); 78 } 79 80 void ServiceWorkerRegistrationProxy::InitOnMainThread() { 81 AssertIsOnMainThread(); 82 83 auto scopeExit = MakeScopeExit([&] { MaybeShutdownOnMainThread(); }); 84 85 RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); 86 NS_ENSURE_TRUE_VOID(swm); 87 88 RefPtr<ServiceWorkerRegistrationInfo> reg = 89 swm->GetRegistration(mDescriptor.PrincipalInfo(), mDescriptor.Scope()); 90 NS_ENSURE_TRUE_VOID(reg); 91 92 if (reg->Id() != mDescriptor.Id()) { 93 // This registration has already been replaced by another one. 94 return; 95 } 96 97 scopeExit.release(); 98 99 mReg = new nsMainThreadPtrHolder<ServiceWorkerRegistrationInfo>( 100 "ServiceWorkerRegistrationProxy::mInfo", reg); 101 102 mReg->AddInstance(this, mDescriptor); 103 } 104 105 void ServiceWorkerRegistrationProxy::MaybeShutdownOnMainThread() { 106 AssertIsOnMainThread(); 107 108 if (mDelayedUpdate) { 109 mDelayedUpdate->Reject(); 110 mDelayedUpdate = nullptr; 111 } 112 nsCOMPtr<nsIRunnable> r = NewRunnableMethod( 113 __func__, this, &ServiceWorkerRegistrationProxy::MaybeShutdownOnBGThread); 114 115 MOZ_ALWAYS_SUCCEEDS(mEventTarget->Dispatch(r.forget(), NS_DISPATCH_NORMAL)); 116 } 117 118 void ServiceWorkerRegistrationProxy::StopListeningOnMainThread() { 119 AssertIsOnMainThread(); 120 121 if (!mReg) { 122 return; 123 } 124 125 mReg->RemoveInstance(this); 126 mReg = nullptr; 127 } 128 129 void ServiceWorkerRegistrationProxy::UpdateState( 130 const ServiceWorkerRegistrationDescriptor& aDescriptor) { 131 AssertIsOnMainThread(); 132 133 if (mDescriptor == aDescriptor) { 134 return; 135 } 136 mDescriptor = aDescriptor; 137 138 nsCOMPtr<nsIRunnable> r = 139 NewRunnableMethod<ServiceWorkerRegistrationDescriptor>( 140 __func__, this, 141 &ServiceWorkerRegistrationProxy::UpdateStateOnBGThread, aDescriptor); 142 143 MOZ_ALWAYS_SUCCEEDS(mEventTarget->Dispatch(r.forget(), NS_DISPATCH_NORMAL)); 144 } 145 146 void ServiceWorkerRegistrationProxy::FireUpdateFound() { 147 AssertIsOnMainThread(); 148 149 nsCOMPtr<nsIRunnable> r = NewRunnableMethod( 150 __func__, this, 151 &ServiceWorkerRegistrationProxy::FireUpdateFoundOnBGThread); 152 153 MOZ_ALWAYS_SUCCEEDS(mEventTarget->Dispatch(r.forget(), NS_DISPATCH_NORMAL)); 154 } 155 156 void ServiceWorkerRegistrationProxy::RegistrationCleared() { 157 MaybeShutdownOnMainThread(); 158 } 159 160 void ServiceWorkerRegistrationProxy::GetScope(nsAString& aScope) const { 161 CopyUTF8toUTF16(mDescriptor.Scope(), aScope); 162 } 163 164 bool ServiceWorkerRegistrationProxy::MatchesDescriptor( 165 const ServiceWorkerRegistrationDescriptor& aDescriptor) { 166 AssertIsOnMainThread(); 167 return aDescriptor.Id() == mDescriptor.Id() && 168 aDescriptor.PrincipalInfo() == mDescriptor.PrincipalInfo() && 169 aDescriptor.Scope() == mDescriptor.Scope(); 170 } 171 172 ServiceWorkerRegistrationProxy::ServiceWorkerRegistrationProxy( 173 const ServiceWorkerRegistrationDescriptor& aDescriptor, 174 const ClientInfo& aForClient) 175 : mEventTarget(GetCurrentSerialEventTarget()), 176 mDescriptor(aDescriptor), 177 mListeningClientInfo(aForClient) {} 178 179 void ServiceWorkerRegistrationProxy::Init( 180 ServiceWorkerRegistrationParent* aActor) { 181 AssertIsOnBackgroundThread(); 182 MOZ_DIAGNOSTIC_ASSERT(aActor); 183 MOZ_DIAGNOSTIC_ASSERT(!mActor); 184 MOZ_DIAGNOSTIC_ASSERT(mEventTarget); 185 186 mActor = aActor; 187 188 // Note, this must be done from a separate Init() method and not in 189 // the constructor. If done from the constructor the runnable can 190 // execute, complete, and release its reference before the constructor 191 // returns. 192 nsCOMPtr<nsIRunnable> r = 193 NewRunnableMethod("ServiceWorkerRegistrationProxy::Init", this, 194 &ServiceWorkerRegistrationProxy::InitOnMainThread); 195 MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch(r.forget())); 196 } 197 198 void ServiceWorkerRegistrationProxy::RevokeActor( 199 ServiceWorkerRegistrationParent* aActor) { 200 AssertIsOnBackgroundThread(); 201 MOZ_DIAGNOSTIC_ASSERT(mActor); 202 MOZ_DIAGNOSTIC_ASSERT(mActor == aActor); 203 mActor = nullptr; 204 205 nsCOMPtr<nsIRunnable> r = NewRunnableMethod( 206 __func__, this, 207 &ServiceWorkerRegistrationProxy::StopListeningOnMainThread); 208 MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch(r.forget())); 209 } 210 211 RefPtr<GenericPromise> ServiceWorkerRegistrationProxy::Unregister() { 212 AssertIsOnBackgroundThread(); 213 214 RefPtr<ServiceWorkerRegistrationProxy> self = this; 215 RefPtr<GenericPromise::Private> promise = 216 new GenericPromise::Private(__func__); 217 218 nsCOMPtr<nsIRunnable> r = 219 NS_NewRunnableFunction(__func__, [self, promise]() mutable { 220 nsresult rv = NS_ERROR_DOM_INVALID_STATE_ERR; 221 auto scopeExit = MakeScopeExit([&] { promise->Reject(rv, __func__); }); 222 223 NS_ENSURE_TRUE_VOID(self->mReg); 224 225 RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); 226 NS_ENSURE_TRUE_VOID(swm); 227 228 RefPtr<UnregisterCallback> cb = new UnregisterCallback(promise); 229 230 rv = swm->Unregister(self->mReg->Principal(), cb, 231 NS_ConvertUTF8toUTF16(self->mReg->Scope())); 232 NS_ENSURE_SUCCESS_VOID(rv); 233 234 scopeExit.release(); 235 }); 236 237 MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch(r.forget())); 238 239 return promise; 240 } 241 242 namespace { 243 244 class UpdateCallback final : public ServiceWorkerUpdateFinishCallback { 245 RefPtr<ServiceWorkerRegistrationPromise::Private> mPromise; 246 247 ~UpdateCallback() = default; 248 249 public: 250 explicit UpdateCallback( 251 RefPtr<ServiceWorkerRegistrationPromise::Private>&& aPromise) 252 : mPromise(std::move(aPromise)) { 253 MOZ_DIAGNOSTIC_ASSERT(mPromise); 254 } 255 256 void UpdateSucceeded(ServiceWorkerRegistrationInfo* aInfo) override { 257 mPromise->Resolve(aInfo->Descriptor(), __func__); 258 } 259 260 void UpdateFailed(ErrorResult& aResult) override { 261 mPromise->Reject(CopyableErrorResult(aResult), __func__); 262 } 263 }; 264 265 } // anonymous namespace 266 267 NS_IMPL_ISUPPORTS(ServiceWorkerRegistrationProxy::DelayedUpdate, 268 nsITimerCallback, nsINamed) 269 270 ServiceWorkerRegistrationProxy::DelayedUpdate::DelayedUpdate( 271 RefPtr<ServiceWorkerRegistrationProxy>&& aProxy, 272 RefPtr<ServiceWorkerRegistrationPromise::Private>&& aPromise, 273 nsCString&& aNewestWorkerScriptUrl, uint32_t delay) 274 : mProxy(std::move(aProxy)), 275 mPromise(std::move(aPromise)), 276 mNewestWorkerScriptUrl(std::move(aNewestWorkerScriptUrl)) { 277 MOZ_DIAGNOSTIC_ASSERT(mProxy); 278 MOZ_DIAGNOSTIC_ASSERT(mPromise); 279 MOZ_ASSERT(!mNewestWorkerScriptUrl.IsEmpty()); 280 mProxy->mDelayedUpdate = this; 281 Result<nsCOMPtr<nsITimer>, nsresult> result = 282 NS_NewTimerWithCallback(this, delay, nsITimer::TYPE_ONE_SHOT); 283 mTimer = result.unwrapOr(nullptr); 284 MOZ_DIAGNOSTIC_ASSERT(mTimer); 285 } 286 287 void ServiceWorkerRegistrationProxy::DelayedUpdate::ChainTo( 288 RefPtr<ServiceWorkerRegistrationPromise::Private> aPromise) { 289 AssertIsOnMainThread(); 290 MOZ_ASSERT(mProxy->mDelayedUpdate == this); 291 MOZ_ASSERT(mPromise); 292 293 mPromise->ChainTo(aPromise.forget(), __func__); 294 } 295 296 void ServiceWorkerRegistrationProxy::DelayedUpdate::Reject() { 297 MOZ_DIAGNOSTIC_ASSERT(mPromise); 298 if (mTimer) { 299 mTimer->Cancel(); 300 mTimer = nullptr; 301 } 302 mPromise->Reject(NS_ERROR_DOM_INVALID_STATE_ERR, __func__); 303 } 304 305 void ServiceWorkerRegistrationProxy::DelayedUpdate::SetNewestWorkerScriptUrl( 306 nsCString&& aNewestWorkerScriptUrl) { 307 MOZ_ASSERT(NS_IsMainThread()); 308 mNewestWorkerScriptUrl = std::move(aNewestWorkerScriptUrl); 309 } 310 311 NS_IMETHODIMP 312 ServiceWorkerRegistrationProxy::DelayedUpdate::Notify(nsITimer* aTimer) { 313 // Already shutting down. 314 if (mProxy->mDelayedUpdate != this) { 315 return NS_OK; 316 } 317 318 auto scopeExit = MakeScopeExit( 319 [&] { mPromise->Reject(NS_ERROR_DOM_INVALID_STATE_ERR, __func__); }); 320 321 NS_ENSURE_TRUE(mProxy->mReg, NS_ERROR_FAILURE); 322 323 RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); 324 NS_ENSURE_TRUE(swm, NS_ERROR_FAILURE); 325 326 RefPtr<UpdateCallback> cb = new UpdateCallback(std::move(mPromise)); 327 swm->Update(mProxy->mListeningClientInfo, mProxy->mReg->Principal(), 328 mProxy->mReg->Scope(), std::move(mNewestWorkerScriptUrl), cb); 329 330 mTimer = nullptr; 331 mProxy->mDelayedUpdate = nullptr; 332 333 scopeExit.release(); 334 return NS_OK; 335 } 336 337 NS_IMETHODIMP 338 ServiceWorkerRegistrationProxy::DelayedUpdate::GetName(nsACString& aName) { 339 aName.AssignLiteral("ServiceWorkerRegistrationProxy::DelayedUpdate"); 340 return NS_OK; 341 } 342 343 RefPtr<ServiceWorkerRegistrationPromise> ServiceWorkerRegistrationProxy::Update( 344 const nsACString& aNewestWorkerScriptUrl) { 345 AssertIsOnBackgroundThread(); 346 347 RefPtr<ServiceWorkerRegistrationProxy> self = this; 348 RefPtr<ServiceWorkerRegistrationPromise::Private> promise = 349 new ServiceWorkerRegistrationPromise::Private(__func__); 350 351 nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction( 352 __func__, 353 [self, promise, 354 newestWorkerScriptUrl = nsCString(aNewestWorkerScriptUrl)]() mutable { 355 auto scopeExit = MakeScopeExit( 356 [&] { promise->Reject(NS_ERROR_DOM_INVALID_STATE_ERR, __func__); }); 357 358 // Get the delay value for the update 359 NS_ENSURE_TRUE_VOID(self->mReg); 360 uint32_t delay = self->mReg->GetUpdateDelay(false); 361 362 // If the delay value does not equal to 0, create a timer and a timer 363 // callback to perform the delayed update. Otherwise, update directly. 364 if (delay) { 365 if (self->mDelayedUpdate) { 366 // NOTE: if we `ChainTo(),` there will ultimately be a single 367 // update, and this update will resolve all promises that were 368 // issued while the update's timer was ticking down. 369 self->mDelayedUpdate->ChainTo(std::move(promise)); 370 371 // Use the "newest newest worker"'s script URL. 372 self->mDelayedUpdate->SetNewestWorkerScriptUrl( 373 std::move(newestWorkerScriptUrl)); 374 } else { 375 RefPtr<ServiceWorkerRegistrationProxy::DelayedUpdate> du = 376 new ServiceWorkerRegistrationProxy::DelayedUpdate( 377 std::move(self), std::move(promise), 378 std::move(newestWorkerScriptUrl), delay); 379 } 380 } else { 381 RefPtr<ServiceWorkerManager> swm = 382 ServiceWorkerManager::GetInstance(); 383 NS_ENSURE_TRUE_VOID(swm); 384 385 RefPtr<UpdateCallback> cb = new UpdateCallback(std::move(promise)); 386 swm->Update(self->mListeningClientInfo, self->mReg->Principal(), 387 self->mReg->Scope(), std::move(newestWorkerScriptUrl), 388 cb); 389 } 390 scopeExit.release(); 391 }); 392 393 MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch(r.forget())); 394 395 return promise; 396 } 397 398 RefPtr<GenericPromise> 399 ServiceWorkerRegistrationProxy::SetNavigationPreloadEnabled( 400 const bool& aEnabled) { 401 AssertIsOnBackgroundThread(); 402 403 RefPtr<ServiceWorkerRegistrationProxy> self = this; 404 RefPtr<GenericPromise::Private> promise = 405 new GenericPromise::Private(__func__); 406 407 nsCOMPtr<nsIRunnable> r = 408 NS_NewRunnableFunction(__func__, [aEnabled, self, promise]() mutable { 409 nsresult rv = NS_ERROR_DOM_INVALID_STATE_ERR; 410 auto scopeExit = MakeScopeExit([&] { promise->Reject(rv, __func__); }); 411 412 NS_ENSURE_TRUE_VOID(self->mReg); 413 NS_ENSURE_TRUE_VOID(self->mReg->GetActive()); 414 415 auto reg = self->mReg; 416 reg->SetNavigationPreloadEnabled(aEnabled); 417 418 RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); 419 NS_ENSURE_TRUE_VOID(swm); 420 swm->StoreRegistration(reg->Principal(), reg); 421 422 scopeExit.release(); 423 424 promise->Resolve(true, __func__); 425 }); 426 427 MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch(r.forget())); 428 429 return promise; 430 } 431 432 RefPtr<GenericPromise> 433 ServiceWorkerRegistrationProxy::SetNavigationPreloadHeader( 434 const nsACString& aHeader) { 435 AssertIsOnBackgroundThread(); 436 437 RefPtr<ServiceWorkerRegistrationProxy> self = this; 438 RefPtr<GenericPromise::Private> promise = 439 new GenericPromise::Private(__func__); 440 441 nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction( 442 __func__, [aHeader = nsCString(aHeader), self, promise]() mutable { 443 nsresult rv = NS_ERROR_DOM_INVALID_STATE_ERR; 444 auto scopeExit = MakeScopeExit([&] { promise->Reject(rv, __func__); }); 445 446 NS_ENSURE_TRUE_VOID(self->mReg); 447 NS_ENSURE_TRUE_VOID(self->mReg->GetActive()); 448 449 auto reg = self->mReg; 450 reg->SetNavigationPreloadHeader(aHeader); 451 452 RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); 453 NS_ENSURE_TRUE_VOID(swm); 454 swm->StoreRegistration(reg->Principal(), reg); 455 456 scopeExit.release(); 457 458 promise->Resolve(true, __func__); 459 }); 460 461 MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch(r.forget())); 462 463 return promise; 464 } 465 466 RefPtr<NavigationPreloadStatePromise> 467 ServiceWorkerRegistrationProxy::GetNavigationPreloadState() { 468 AssertIsOnBackgroundThread(); 469 470 RefPtr<ServiceWorkerRegistrationProxy> self = this; 471 RefPtr<NavigationPreloadStatePromise::Private> promise = 472 new NavigationPreloadStatePromise::Private(__func__); 473 474 nsCOMPtr<nsIRunnable> r = 475 NS_NewRunnableFunction(__func__, [self, promise]() mutable { 476 nsresult rv = NS_ERROR_DOM_INVALID_STATE_ERR; 477 auto scopeExit = MakeScopeExit([&] { promise->Reject(rv, __func__); }); 478 479 NS_ENSURE_TRUE_VOID(self->mReg); 480 scopeExit.release(); 481 482 promise->Resolve(self->mReg->GetNavigationPreloadState(), __func__); 483 }); 484 485 MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch(r.forget())); 486 487 return promise; 488 } 489 490 RefPtr<NotificationsPromise> ServiceWorkerRegistrationProxy::GetNotifications( 491 const nsAString& aTag) { 492 AssertIsOnBackgroundThread(); 493 494 RefPtr<ServiceWorkerRegistrationProxy> self = this; 495 return InvokeAsync( 496 GetMainThreadSerialEventTarget(), __func__, 497 [self, tag = nsString(aTag)]() { 498 Result<nsCOMPtr<nsIPrincipal>, nsresult> principalResult = 499 self->mListeningClientInfo.GetPrincipal(); 500 if (principalResult.isErr()) { 501 return NotificationsPromise::CreateAndReject( 502 NS_ERROR_DOM_INVALID_STATE_ERR, __func__); 503 } 504 505 nsCOMPtr<nsIPrincipal> principal = principalResult.unwrap(); 506 nsString origin; 507 GetOrigin(principal, origin); 508 509 RefPtr<NotificationsPromise> promise = GetStoredNotificationsForScope( 510 principal, self->mDescriptor.Scope(), tag); 511 return promise; 512 }); 513 } 514 515 } // namespace mozilla::dom