ServiceWorkerPrivate.h (15152B)
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 #ifndef mozilla_dom_serviceworkerprivate_h 8 #define mozilla_dom_serviceworkerprivate_h 9 10 #include <functional> 11 12 #include "mozilla/Attributes.h" 13 #include "mozilla/Maybe.h" 14 #include "mozilla/MozPromise.h" 15 #include "mozilla/RefPtr.h" 16 #include "mozilla/TimeStamp.h" 17 #include "mozilla/UniquePtr.h" 18 #include "mozilla/dom/FetchService.h" 19 #include "mozilla/dom/Promise.h" 20 #include "mozilla/dom/RemoteWorkerController.h" 21 #include "mozilla/dom/RemoteWorkerTypes.h" 22 #include "mozilla/dom/ServiceWorkerLifetimeExtension.h" 23 #include "mozilla/dom/ServiceWorkerOpArgs.h" 24 #include "nsCOMPtr.h" 25 #include "nsISupportsImpl.h" 26 #include "nsTArray.h" 27 28 #define NOTIFICATION_CLICK_EVENT_NAME u"notificationclick" 29 #define NOTIFICATION_CLOSE_EVENT_NAME u"notificationclose" 30 31 class nsIInterceptedChannel; 32 class nsIPushSubscription; 33 class nsIWorkerDebugger; 34 35 namespace mozilla { 36 37 template <typename T> 38 class Maybe; 39 40 class JSObjectHolder; 41 42 namespace net { 43 class CookieStruct; 44 } 45 46 namespace dom { 47 48 class PostMessageSource; 49 class RemoteWorkerControllerChild; 50 class ServiceWorkerCloneData; 51 class ServiceWorkerInfo; 52 class ServiceWorkerPrivate; 53 class ServiceWorkerRegistrationInfo; 54 struct CookieListItem; 55 56 namespace ipc { 57 class StructuredCloneData; 58 } // namespace ipc 59 60 class LifeCycleEventCallback : public Runnable { 61 public: 62 LifeCycleEventCallback() : Runnable("dom::LifeCycleEventCallback") {} 63 64 // Called on the worker thread. 65 virtual void SetResult(bool aResult) = 0; 66 }; 67 68 // Used to keep track of pending waitUntil as well as in-flight extendable 69 // events. When the last token is released, we attempt to terminate the worker. 70 class KeepAliveToken final : public nsISupports { 71 public: 72 NS_DECL_ISUPPORTS 73 74 explicit KeepAliveToken(ServiceWorkerPrivate* aPrivate); 75 76 private: 77 ~KeepAliveToken(); 78 79 RefPtr<ServiceWorkerPrivate> mPrivate; 80 }; 81 82 class ServiceWorkerPrivate final : public RemoteWorkerObserver { 83 friend class KeepAliveToken; 84 85 public: 86 NS_INLINE_DECL_REFCOUNTING(ServiceWorkerPrivate, override); 87 88 using PromiseExtensionWorkerHasListener = MozPromise<bool, nsresult, false>; 89 90 public: 91 explicit ServiceWorkerPrivate(ServiceWorkerInfo* aInfo); 92 93 Maybe<ClientInfo> GetClientInfo() { return mClientInfo; } 94 95 nsresult SendMessageEvent( 96 RefPtr<ServiceWorkerCloneData>&& aData, 97 const ServiceWorkerLifetimeExtension& aLifetimeExtension, 98 const PostMessageSource& aSource); 99 100 // This is used to validate the worker script and continue the installation 101 // process. 102 nsresult CheckScriptEvaluation( 103 const ServiceWorkerLifetimeExtension& aLifetimeExtension, 104 RefPtr<LifeCycleEventCallback> aCallback); 105 106 nsresult SendLifeCycleEvent( 107 const nsAString& aEventType, 108 const ServiceWorkerLifetimeExtension& aLifetimeExtension, 109 const RefPtr<LifeCycleEventCallback>& aCallback); 110 111 nsresult SendCookieChangeEvent( 112 const net::CookieStruct& aCookie, bool aCookieDeleted, 113 RefPtr<ServiceWorkerRegistrationInfo> aRegistration); 114 115 nsresult SendPushEvent(const nsAString& aMessageId, 116 const Maybe<nsTArray<uint8_t>>& aData, 117 RefPtr<ServiceWorkerRegistrationInfo> aRegistration); 118 119 nsresult SendPushSubscriptionChangeEvent( 120 const RefPtr<nsIPushSubscription>& aOldSubscription); 121 122 nsresult SendNotificationClickEvent(const IPCNotification& aNotification, 123 const nsAString& aAction); 124 125 nsresult SendNotificationCloseEvent(const IPCNotification& aNotification); 126 127 nsresult SendFetchEvent(nsCOMPtr<nsIInterceptedChannel> aChannel, 128 nsILoadGroup* aLoadGroup, const nsAString& aClientId, 129 const nsAString& aResultingClientId); 130 131 Result<RefPtr<PromiseExtensionWorkerHasListener>, nsresult> 132 WakeForExtensionAPIEvent(const nsAString& aExtensionAPINamespace, 133 const nsAString& aEXtensionAPIEventName); 134 135 // This will terminate the current running worker thread and drop the 136 // workerPrivate reference. 137 // Called by ServiceWorkerInfo when [[Clear Registration]] is invoked 138 // or whenever the spec mandates that we terminate the worker. 139 // This is a no-op if the worker has already been stopped. 140 // 141 // Now takes an optional promise that will be resolved when the worker is 142 // dead, including if the worker was not running at all. 143 void TerminateWorker(Maybe<RefPtr<Promise>> aMaybePromise = Nothing()); 144 145 void NoteDeadServiceWorkerInfo(); 146 147 void NoteStoppedControllingDocuments(); 148 149 void UpdateState(ServiceWorkerState aState); 150 151 void UpdateIsOnContentBlockingAllowList(bool aOnContentBlockingAllowList); 152 153 nsresult GetDebugger(nsIWorkerDebugger** aResult); 154 155 nsresult AttachDebugger(); 156 157 nsresult DetachDebugger(); 158 159 // Return the current lifetime deadline for this ServiceWorker; this value may 160 // be null or in the past. 161 // 162 // This value always only reflects the explicit lifetime extensions 163 // resulting from functional events and will never reflect the extra "grace 164 // period". 165 TimeStamp GetLifetimeDeadline() { return mIdleDeadline; } 166 167 uint32_t GetLaunchCount() { return mLaunchCount; } 168 169 bool IsIdle() const; 170 171 // This promise is used schedule clearing of the owning registrations and its 172 // associated Service Workers if that registration becomes "unreachable" by 173 // the ServiceWorkerManager. This occurs under two conditions, which are the 174 // preconditions to calling this method: 175 // - The owning registration must be unregistered. 176 // - The associated Service Worker must *not* be controlling clients. 177 // 178 // Additionally, perhaps stating the obvious, the associated Service Worker 179 // must *not* be idle (whatever must be done "when idle" can just be done 180 // immediately). 181 RefPtr<GenericPromise> GetIdlePromise(); 182 183 void SetHandlesFetch(bool aValue); 184 185 RefPtr<GenericPromise> SetSkipWaitingFlag(); 186 187 static void RunningShutdown() { 188 // Force a final update of the number of running ServiceWorkers 189 UpdateRunning(0, 0); 190 MOZ_ASSERT(sRunningServiceWorkers == 0); 191 MOZ_ASSERT(sRunningServiceWorkersFetch == 0); 192 } 193 194 /** 195 * Update Telemetry for # of running ServiceWorkers 196 */ 197 static void UpdateRunning(int32_t aDelta, int32_t aFetchDelta); 198 199 private: 200 // Timer callbacks 201 void NoteIdleWorkerCallback(nsITimer* aTimer); 202 203 void TerminateWorkerCallback(nsITimer* aTimer); 204 205 void RenewKeepAliveToken( 206 const ServiceWorkerLifetimeExtension& aLifetimeExtension); 207 208 void ResetIdleTimeout( 209 const ServiceWorkerLifetimeExtension& aLifetimeExtension); 210 211 void AddToken(); 212 213 void ReleaseToken(); 214 215 already_AddRefed<KeepAliveToken> CreateEventKeepAliveToken(); 216 217 nsresult SpawnWorkerIfNeeded( 218 const ServiceWorkerLifetimeExtension& aLifetimeExtension); 219 220 ~ServiceWorkerPrivate(); 221 222 nsresult Initialize(); 223 224 void RegenerateClientInfo(); 225 226 /** 227 * RemoteWorkerObserver 228 */ 229 void CreationFailed() override; 230 231 void CreationSucceeded() override; 232 233 void ErrorReceived(const ErrorValue& aError) override; 234 235 void LockNotified(bool aCreated) final { 236 // no-op for service workers 237 } 238 239 void WebTransportNotified(bool aCreated) final { 240 // no-op for service workers 241 } 242 243 void Terminated() override; 244 245 // Refreshes only the parts of mRemoteWorkerData that may change over time. 246 void RefreshRemoteWorkerData( 247 const RefPtr<ServiceWorkerRegistrationInfo>& aRegistration); 248 249 nsresult SendCookieChangeEventInternal( 250 RefPtr<ServiceWorkerRegistrationInfo>&& aRegistration, 251 ServiceWorkerCookieChangeEventOpArgs&& aArgs); 252 253 nsresult SendPushEventInternal( 254 RefPtr<ServiceWorkerRegistrationInfo>&& aRegistration, 255 ServiceWorkerPushEventOpArgs&& aArgs); 256 257 // Setup the navigation preload by the intercepted channel and the 258 // RegistrationInfo. 259 RefPtr<FetchServicePromises> SetupNavigationPreload( 260 nsCOMPtr<nsIInterceptedChannel>& aChannel, 261 const RefPtr<ServiceWorkerRegistrationInfo>& aRegistration); 262 263 nsresult SendFetchEventInternal( 264 RefPtr<ServiceWorkerRegistrationInfo>&& aRegistration, 265 ParentToParentServiceWorkerFetchEventOpArgs&& aArgs, 266 nsCOMPtr<nsIInterceptedChannel>&& aChannel, 267 RefPtr<FetchServicePromises>&& aPreloadResponseReadyPromises); 268 269 void Shutdown(Maybe<RefPtr<Promise>>&& aMaybePromise = Nothing()); 270 271 RefPtr<GenericNonExclusivePromise> ShutdownInternal( 272 uint32_t aShutdownStateId); 273 274 nsresult ExecServiceWorkerOp( 275 ServiceWorkerOpArgs&& aArgs, 276 const ServiceWorkerLifetimeExtension& aLifetimeExtension, 277 std::function<void(ServiceWorkerOpResult&&)>&& aSuccessCallback, 278 std::function<void()>&& aFailureCallback = [] {}); 279 280 class PendingFunctionalEvent { 281 public: 282 PendingFunctionalEvent( 283 ServiceWorkerPrivate* aOwner, 284 RefPtr<ServiceWorkerRegistrationInfo>&& aRegistration); 285 286 virtual ~PendingFunctionalEvent(); 287 288 virtual nsresult Send() = 0; 289 290 protected: 291 ServiceWorkerPrivate* const MOZ_NON_OWNING_REF mOwner; 292 RefPtr<ServiceWorkerRegistrationInfo> mRegistration; 293 }; 294 295 class PendingCookieChangeEvent final : public PendingFunctionalEvent { 296 public: 297 PendingCookieChangeEvent( 298 ServiceWorkerPrivate* aOwner, 299 RefPtr<ServiceWorkerRegistrationInfo>&& aRegistration, 300 ServiceWorkerCookieChangeEventOpArgs&& aArgs); 301 302 nsresult Send() override; 303 304 private: 305 ServiceWorkerCookieChangeEventOpArgs mArgs; 306 }; 307 308 class PendingPushEvent final : public PendingFunctionalEvent { 309 public: 310 PendingPushEvent(ServiceWorkerPrivate* aOwner, 311 RefPtr<ServiceWorkerRegistrationInfo>&& aRegistration, 312 ServiceWorkerPushEventOpArgs&& aArgs); 313 314 nsresult Send() override; 315 316 private: 317 ServiceWorkerPushEventOpArgs mArgs; 318 }; 319 320 class PendingFetchEvent final : public PendingFunctionalEvent { 321 public: 322 PendingFetchEvent( 323 ServiceWorkerPrivate* aOwner, 324 RefPtr<ServiceWorkerRegistrationInfo>&& aRegistration, 325 ParentToParentServiceWorkerFetchEventOpArgs&& aArgs, 326 nsCOMPtr<nsIInterceptedChannel>&& aChannel, 327 RefPtr<FetchServicePromises>&& aPreloadResponseReadyPromises); 328 329 nsresult Send() override; 330 331 ~PendingFetchEvent(); 332 333 private: 334 ParentToParentServiceWorkerFetchEventOpArgs mArgs; 335 nsCOMPtr<nsIInterceptedChannel> mChannel; 336 // The promises from FetchService. It indicates if the preload response is 337 // ready or not. The promise's resolve/reject value should be handled in 338 // FetchEventOpChild, such that the preload result can be propagated to the 339 // ServiceWorker through IPC. However, FetchEventOpChild creation could be 340 // pending here, so this member is needed. And it will be forwarded to 341 // FetchEventOpChild when crearting the FetchEventOpChild. 342 RefPtr<FetchServicePromises> mPreloadResponseReadyPromises; 343 }; 344 345 nsTArray<UniquePtr<PendingFunctionalEvent>> mPendingFunctionalEvents; 346 347 /** 348 * It's possible that there are still in-progress operations when a 349 * a termination operation is issued. In this case, it's important to keep 350 * the RemoteWorkerControllerChild actor alive until all pending operations 351 * have completed before destroying it with Send__delete__(). 352 * 353 * RAIIActorPtrHolder holds a singular, owning reference to a 354 * RemoteWorkerControllerChild actor and is responsible for destroying the 355 * actor in its (i.e. the holder's) destructor. This implies that all 356 * in-progress operations must maintain a strong reference to their 357 * corresponding holders and release the reference once completed/canceled. 358 * 359 * Additionally a RAIIActorPtrHolder must be initialized with a non-null actor 360 * and cannot be moved or copied. Therefore, the identities of two held 361 * actors can be compared by simply comparing their holders' addresses. 362 */ 363 class RAIIActorPtrHolder final { 364 public: 365 NS_INLINE_DECL_REFCOUNTING(RAIIActorPtrHolder) 366 367 explicit RAIIActorPtrHolder( 368 already_AddRefed<RemoteWorkerControllerChild> aActor); 369 370 RAIIActorPtrHolder(const RAIIActorPtrHolder& aOther) = delete; 371 RAIIActorPtrHolder& operator=(const RAIIActorPtrHolder& aOther) = delete; 372 373 RAIIActorPtrHolder(RAIIActorPtrHolder&& aOther) = delete; 374 RAIIActorPtrHolder& operator=(RAIIActorPtrHolder&& aOther) = delete; 375 376 RemoteWorkerControllerChild* operator->() const 377 MOZ_NO_ADDREF_RELEASE_ON_RETURN; 378 379 RemoteWorkerControllerChild* get() const; 380 381 RefPtr<GenericPromise> OnDestructor(); 382 383 private: 384 ~RAIIActorPtrHolder(); 385 386 MozPromiseHolder<GenericPromise> mDestructorPromiseHolder; 387 388 const RefPtr<RemoteWorkerControllerChild> mActor; 389 }; 390 391 RefPtr<RAIIActorPtrHolder> mControllerChild; 392 393 RemoteWorkerData mRemoteWorkerData; 394 Maybe<ClientInfo> mClientInfo; 395 396 TimeStamp mServiceWorkerLaunchTimeStart; 397 398 // Counters for Telemetry - totals running simultaneously, and those that 399 // handle Fetch, plus Max values for each 400 static uint32_t sRunningServiceWorkers; 401 static uint32_t sRunningServiceWorkersFetch; 402 static uint32_t sRunningServiceWorkersMax; 403 static uint32_t sRunningServiceWorkersFetchMax; 404 405 // We know the state after we've evaluated the worker, and we then store 406 // it in the registration. The only valid state transition should be 407 // from Unknown to Enabled or Disabled. 408 enum { Unknown, Enabled, Disabled } mHandlesFetch{Unknown}; 409 410 // The info object owns us. It is possible to outlive it for a brief period 411 // of time if there are pending waitUntil promises, in which case it 412 // will be null and |SpawnWorkerIfNeeded| will always fail. 413 ServiceWorkerInfo* MOZ_NON_OWNING_REF mInfo; 414 415 nsCOMPtr<nsITimer> mIdleWorkerTimer; 416 417 ServiceWorkerLifetimeExtension mPendingSpawnLifetime; 418 419 // This is the current time in the future that the idle timer is set to expire 420 // for keepalive purposes. This will not be updated for the 421 // "dom.serviceWorkers.idle_extended_timeout" grace period after the time 422 // first expires. 423 TimeStamp mIdleDeadline; 424 425 // We keep a token for |dom.serviceWorkers.idle_timeout| seconds to give the 426 // worker a grace period after each event. 427 RefPtr<KeepAliveToken> mIdleKeepAliveToken; 428 429 uint64_t mDebuggerCount; 430 431 uint64_t mTokenCount; 432 433 uint32_t mLaunchCount; 434 435 // Used by the owning `ServiceWorkerRegistrationInfo` when it wants to call 436 // `Clear` after being unregistered and isn't controlling any clients but this 437 // worker (i.e. the registration's active worker) isn't idle yet. Note that 438 // such an event should happen at most once in a 439 // `ServiceWorkerRegistrationInfo`s lifetime, so this promise should also only 440 // be obtained at most once. 441 MozPromiseHolder<GenericPromise> mIdlePromiseHolder; 442 443 #ifdef DEBUG 444 bool mIdlePromiseObtained = false; 445 #endif 446 }; 447 448 } // namespace dom 449 } // namespace mozilla 450 451 #endif // mozilla_dom_serviceworkerprivate_h