tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

ServiceWorkerManager.h (19172B)


      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_workers_serviceworkermanager_h
      8 #define mozilla_dom_workers_serviceworkermanager_h
      9 
     10 #include <cstdint>
     11 
     12 #include "ErrorList.h"
     13 #include "ServiceWorkerDescriptor.h"
     14 #include "ServiceWorkerShutdownState.h"
     15 #include "js/ErrorReport.h"
     16 #include "mozilla/AlreadyAddRefed.h"
     17 #include "mozilla/Assertions.h"
     18 #include "mozilla/HashTable.h"
     19 #include "mozilla/MozPromise.h"
     20 #include "mozilla/RefPtr.h"
     21 #include "mozilla/UniquePtr.h"
     22 #include "mozilla/dom/ClientHandle.h"
     23 #include "mozilla/dom/ClientOpPromise.h"
     24 #include "mozilla/dom/ServiceWorkerLifetimeExtension.h"
     25 #include "mozilla/dom/ServiceWorkerRegistrationBinding.h"
     26 #include "mozilla/dom/ServiceWorkerRegistrationInfo.h"
     27 #include "mozilla/dom/ServiceWorkerUtils.h"
     28 #include "mozilla/mozalloc.h"
     29 #include "nsClassHashtable.h"
     30 #include "nsContentUtils.h"
     31 #include "nsHashKeys.h"
     32 #include "nsIObserver.h"
     33 #include "nsIServiceWorkerManager.h"
     34 #include "nsISupports.h"
     35 #include "nsStringFwd.h"
     36 #include "nsTArray.h"
     37 
     38 class nsIConsoleReportCollector;
     39 
     40 namespace mozilla {
     41 
     42 class OriginAttributes;
     43 
     44 namespace ipc {
     45 class PrincipalInfo;
     46 }  // namespace ipc
     47 
     48 namespace net {
     49 class CookieStruct;
     50 }
     51 
     52 namespace dom {
     53 
     54 class ContentParent;
     55 class ServiceWorkerInfo;
     56 class ServiceWorkerJobQueue;
     57 class ServiceWorkerManagerChild;
     58 class ServiceWorkerPrivate;
     59 class ServiceWorkerRegistrar;
     60 class ServiceWorkerShutdownBlocker;
     61 struct CookieListItem;
     62 
     63 class ServiceWorkerUpdateFinishCallback {
     64 protected:
     65  virtual ~ServiceWorkerUpdateFinishCallback() = default;
     66 
     67 public:
     68  NS_INLINE_DECL_REFCOUNTING(ServiceWorkerUpdateFinishCallback)
     69 
     70  virtual void UpdateSucceeded(ServiceWorkerRegistrationInfo* aInfo) = 0;
     71 
     72  virtual void UpdateFailed(ErrorResult& aStatus) = 0;
     73 };
     74 
     75 #define NS_SERVICEWORKERMANAGER_IMPL_IID      \
     76  {/* f4f8755a-69ca-46e8-a65d-775745535990 */ \
     77   0xf4f8755a,                                \
     78   0x69ca,                                    \
     79   0x46e8,                                    \
     80   {0xa6, 0x5d, 0x77, 0x57, 0x45, 0x53, 0x59, 0x90}}
     81 
     82 class ETPPermissionObserver final : public nsIObserver {
     83 public:
     84  NS_DECL_ISUPPORTS
     85  NS_DECL_NSIOBSERVER
     86 
     87  ETPPermissionObserver();
     88 
     89 private:
     90  ~ETPPermissionObserver();
     91 
     92  void RegisterObserverEvents();
     93  void UnregisterObserverEvents();
     94 };
     95 
     96 /*
     97 * The ServiceWorkerManager is a per-process global that deals with the
     98 * installation, querying and event dispatch of ServiceWorkers for all the
     99 * origins in the process.
    100 *
    101 * NOTE: the following documentation is a WIP:
    102 *
    103 * The ServiceWorkerManager (SWM) is a main-thread, parent-process singleton
    104 * that encapsulates the browser-global state of service workers. This state
    105 * includes, but is not limited to, all service worker registrations and all
    106 * controlled service worker clients. The SWM also provides methods to read and
    107 * mutate this state and to dispatch operations (e.g. DOM events such as a
    108 * FetchEvent) to service workers.
    109 *
    110 * Example usage:
    111 *
    112 * MOZ_ASSERT(NS_IsMainThread(), "SWM is main-thread only");
    113 *
    114 * RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
    115 *
    116 * // Nullness must be checked by code that possibly executes during browser
    117 * // shutdown, which is when the SWM is destroyed.
    118 * if (swm) {
    119 *   // Do something with the SWM.
    120 * }
    121 */
    122 class ServiceWorkerManager final : public nsIServiceWorkerManager,
    123                                   public nsIObserver {
    124  friend class GetRegistrationsRunnable;
    125  friend class GetRegistrationRunnable;
    126  friend class ServiceWorkerInfo;
    127  friend class ServiceWorkerJob;
    128  friend class ServiceWorkerRegistrationInfo;
    129  friend class ServiceWorkerShutdownBlocker;
    130  friend class ServiceWorkerUnregisterJob;
    131  friend class ServiceWorkerUpdateJob;
    132  friend class UpdateTimerCallback;
    133 
    134 public:
    135  NS_DECL_ISUPPORTS
    136  NS_DECL_NSISERVICEWORKERMANAGER
    137  NS_DECL_NSIOBSERVER
    138 
    139  // Determine the correct lifetime extension to use for a given client.  This
    140  // will work for ServiceWorker clients, but ideally you should have a
    141  // ServiceWorkerDescriptor in that case.
    142  ServiceWorkerLifetimeExtension DetermineLifetimeForClient(
    143      const ClientInfo& aClientInfo);
    144 
    145  // Determine the correct lifetime extension to use for a given client.  This
    146  // will work for ServiceWorker clients, but ideally you should have a
    147  // ServiceWorkerDescriptor in that case.
    148  ServiceWorkerLifetimeExtension DetermineLifetimeForServiceWorker(
    149      const ServiceWorkerDescriptor& aServiceWorker);
    150 
    151  // Return true if the given principal and URI matches a registered service
    152  // worker which handles fetch event.
    153  // If there is a matched service worker but doesn't handle fetch events, this
    154  // method will try to set the matched service worker as the controller of the
    155  // passed in channel. Then also schedule a soft-update job for the service
    156  // worker.
    157  bool IsAvailable(nsIPrincipal* aPrincipal, nsIURI* aURI,
    158                   nsIChannel* aChannel);
    159 
    160  void DispatchFetchEvent(nsIInterceptedChannel* aChannel, ErrorResult& aRv);
    161 
    162  void Update(const ClientInfo& aClientInfo, nsIPrincipal* aPrincipal,
    163              const nsACString& aScope, nsCString aNewestWorkerScriptUrl,
    164              ServiceWorkerUpdateFinishCallback* aCallback);
    165 
    166  void UpdateInternal(const ClientInfo& aClientInfo, nsIPrincipal* aPrincipal,
    167                      const nsACString& aScope,
    168                      nsCString&& aNewestWorkerScriptUrl,
    169                      ServiceWorkerUpdateFinishCallback* aCallback);
    170 
    171  void SoftUpdate(const OriginAttributes& aOriginAttributes,
    172                  const nsACString& aScope);
    173 
    174  void SoftUpdateInternal(const OriginAttributes& aOriginAttributes,
    175                          const nsACString& aScope,
    176                          ServiceWorkerUpdateFinishCallback* aCallback);
    177 
    178  RefPtr<ServiceWorkerRegistrationPromise> Register(
    179      const ClientInfo& aClientInfo, const nsACString& aScopeURL,
    180      const WorkerType& aType, const nsACString& aScriptURL,
    181      ServiceWorkerUpdateViaCache aUpdateViaCache);
    182 
    183  RefPtr<ServiceWorkerRegistrationPromise> GetRegistration(
    184      const ClientInfo& aClientInfo, const nsACString& aURL) const;
    185 
    186  RefPtr<ServiceWorkerRegistrationListPromise> GetRegistrations(
    187      const ClientInfo& aClientInfo) const;
    188 
    189  already_AddRefed<ServiceWorkerRegistrationInfo> GetRegistration(
    190      nsIPrincipal* aPrincipal, const nsACString& aScope) const;
    191 
    192  already_AddRefed<ServiceWorkerRegistrationInfo> GetRegistration(
    193      const mozilla::ipc::PrincipalInfo& aPrincipal,
    194      const nsACString& aScope) const;
    195 
    196  already_AddRefed<ServiceWorkerRegistrationInfo> CreateNewRegistration(
    197      const nsCString& aScope, const WorkerType& aType,
    198      nsIPrincipal* aPrincipal, ServiceWorkerUpdateViaCache aUpdateViaCache,
    199      IPCNavigationPreloadState aNavigationPreloadState =
    200          IPCNavigationPreloadState(false, "true"_ns));
    201 
    202  void RemoveRegistration(ServiceWorkerRegistrationInfo* aRegistration);
    203 
    204  void StoreRegistration(nsIPrincipal* aPrincipal,
    205                         ServiceWorkerRegistrationInfo* aRegistration);
    206 
    207  /**
    208   * Report an error for the given scope to any window we think might be
    209   * interested, failing over to the Browser Console if we couldn't find any.
    210   *
    211   * Error messages should be localized, so you probably want to call
    212   * LocalizeAndReportToAllClients instead, which in turn calls us after
    213   * localizing the error.
    214   */
    215  void ReportToAllClients(const nsCString& aScope, const nsString& aMessage,
    216                          const nsCString& aFilename, const nsString& aLine,
    217                          uint32_t aLineNumber, uint32_t aColumnNumber,
    218                          uint32_t aFlags);
    219 
    220  /**
    221   * Report a localized error for the given scope to any window we think might
    222   * be interested.
    223   *
    224   * Note that this method takes an nsTArray<nsString> for the parameters, not
    225   * bare chart16_t*[].  You can use a std::initializer_list constructor inline
    226   * so that argument might look like: nsTArray<nsString> { some_nsString,
    227   * PromiseFlatString(some_nsSubString_aka_nsAString),
    228   * NS_ConvertUTF8toUTF16(some_nsCString_or_nsCSubString),
    229   * u"some literal"_ns }.  If you have anything else, like a
    230   * number, you can use an nsAutoString with AppendInt/friends.
    231   *
    232   * @param [aFlags]
    233   *   The nsIScriptError flag, one of errorFlag (0x0), warningFlag (0x1),
    234   *   infoFlag (0x8).  We default to error if omitted because usually we're
    235   *   logging exceptional and/or obvious breakage.
    236   */
    237  static void LocalizeAndReportToAllClients(
    238      const nsCString& aScope, const char* aStringKey,
    239      const nsTArray<nsString>& aParamArray, uint32_t aFlags = 0x0,
    240      const nsCString& aFilename = ""_ns, const nsString& aLine = u""_ns,
    241      uint32_t aLineNumber = 0, uint32_t aColumnNumber = 0);
    242 
    243  // Always consumes the error by reporting to consoles of all controlled
    244  // documents.
    245  void HandleError(JSContext* aCx, nsIPrincipal* aPrincipal,
    246                   const nsCString& aScope, const nsCString& aWorkerURL,
    247                   const nsString& aMessage, const nsCString& aFilename,
    248                   const nsString& aLine, uint32_t aLineNumber,
    249                   uint32_t aColumnNumber, uint32_t aFlags, JSExnType aExnType);
    250 
    251  [[nodiscard]] RefPtr<GenericErrorResultPromise> MaybeClaimClient(
    252      const ClientInfo& aClientInfo,
    253      ServiceWorkerRegistrationInfo* aWorkerRegistration);
    254 
    255  [[nodiscard]] RefPtr<GenericErrorResultPromise> MaybeClaimClient(
    256      const ClientInfo& aClientInfo,
    257      const ServiceWorkerDescriptor& aServiceWorker);
    258 
    259  static already_AddRefed<ServiceWorkerManager> GetInstance();
    260 
    261  void LoadRegistration(const ServiceWorkerRegistrationData& aRegistration);
    262 
    263  void LoadRegistrations(
    264      const nsTArray<ServiceWorkerRegistrationData>& aRegistrations);
    265 
    266  void MaybeCheckNavigationUpdate(const ClientInfo& aClientInfo);
    267 
    268  nsresult SendCookieChangeEvent(const OriginAttributes& aOriginAttributes,
    269                                 const nsACString& aScope,
    270                                 const net::CookieStruct& aCookie,
    271                                 bool aCookieDeleted);
    272 
    273  nsresult SendPushEvent(const nsACString& aOriginAttributes,
    274                         const nsACString& aScope, const nsAString& aMessageId,
    275                         const Maybe<nsTArray<uint8_t>>& aData);
    276 
    277  void WorkerIsIdle(ServiceWorkerInfo* aWorker);
    278 
    279  RefPtr<ServiceWorkerRegistrationPromise> WhenReady(
    280      const ClientInfo& aClientInfo);
    281 
    282  void CheckPendingReadyPromises();
    283 
    284  void RemovePendingReadyPromise(const ClientInfo& aClientInfo);
    285 
    286  void NoteInheritedController(const ClientInfo& aClientInfo,
    287                               const ServiceWorkerDescriptor& aController);
    288 
    289  void BlockShutdownOn(GenericNonExclusivePromise* aPromise,
    290                       uint32_t aShutdownStateId);
    291 
    292  nsresult GetClientRegistration(
    293      const ClientInfo& aClientInfo,
    294      ServiceWorkerRegistrationInfo** aRegistrationInfo);
    295 
    296  int32_t GetPrincipalQuotaUsageCheckCount(nsIPrincipal* aPrincipal);
    297 
    298  void CheckPrincipalQuotaUsage(nsIPrincipal* aPrincipal,
    299                                const nsACString& aScope);
    300 
    301  // Returns the shutdown state ID (may be an invalid ID if an
    302  // nsIAsyncShutdownBlocker is not used).
    303  uint32_t MaybeInitServiceWorkerShutdownProgress() const;
    304 
    305  void ReportServiceWorkerShutdownProgress(
    306      uint32_t aShutdownStateId,
    307      ServiceWorkerShutdownState::Progress aProgress) const;
    308 
    309  // Record periodic telemetry on number of running ServiceWorkers.  When
    310  // the number of running ServiceWorkers changes (or on shutdown),
    311  // ServiceWorkerPrivateImpl will call RecordTelemetry with the number of
    312  // running serviceworkers and those supporting Fetch.  We use
    313  // mTelemetryLastChange to determine how many datapoints to inject into
    314  // Telemetry, and dispatch a background runnable to call
    315  // RecordTelemetryGap() and Accumulate them.
    316  void RecordTelemetry(uint32_t aNumber, uint32_t aFetch);
    317 
    318  void EvictFromBFCache(ServiceWorkerRegistrationInfo* aRegistration);
    319 
    320  nsRefPtrHashtable<nsCStringHashKey, ServiceWorkerRegistrationInfo>
    321  GetRegistrations(nsIPrincipal* aPrincipal);
    322 
    323 private:
    324  struct RegistrationDataPerPrincipal;
    325 
    326  static bool FindScopeForPath(const nsACString& aScopeKey,
    327                               const nsACString& aPath,
    328                               RegistrationDataPerPrincipal** aData,
    329                               nsACString& aMatch);
    330 
    331  ServiceWorkerManager();
    332  ~ServiceWorkerManager();
    333 
    334  void Init(ServiceWorkerRegistrar* aRegistrar);
    335 
    336  RefPtr<GenericErrorResultPromise> StartControllingClient(
    337      const ClientInfo& aClientInfo,
    338      ServiceWorkerRegistrationInfo* aRegistrationInfo,
    339      bool aControlClientHandle = true);
    340 
    341  void StopControllingClient(const ClientInfo& aClientInfo);
    342 
    343  void MaybeStartShutdown();
    344 
    345  void MaybeFinishShutdown();
    346 
    347  already_AddRefed<ServiceWorkerJobQueue> GetOrCreateJobQueue(
    348      const nsACString& aOriginSuffix, const nsACString& aScope);
    349 
    350  void MaybeRemoveRegistrationInfo(const nsACString& aScopeKey);
    351 
    352  already_AddRefed<ServiceWorkerRegistrationInfo> GetRegistration(
    353      const nsACString& aScopeKey, const nsACString& aScope) const;
    354 
    355  void AbortCurrentUpdate(ServiceWorkerRegistrationInfo* aRegistration);
    356 
    357  nsresult Update(ServiceWorkerRegistrationInfo* aRegistration);
    358 
    359  ServiceWorkerInfo* GetActiveWorkerInfoForScope(
    360      const OriginAttributes& aOriginAttributes, const nsACString& aScope);
    361 
    362  // Given the ClientInfo for a ServiceWorker global, return the corresponding
    363  // ServiceWorkerInfo, nullptr otherwise.  Do not use this for clients for
    364  // globals that are not ServiceWorkers (and ideally you should be using
    365  // GetServiceWorkerByDescriptor instead).
    366  ServiceWorkerInfo* GetServiceWorkerByClientInfo(
    367      const ClientInfo& aClientInfo) const;
    368 
    369  // Given the ServiceWorkerDescriptor for a ServiceWorker, return the
    370  // corresponding nullptr otherwise.
    371  ServiceWorkerInfo* GetServiceWorkerByDescriptor(
    372      const ServiceWorkerDescriptor& aServiceWorker) const;
    373 
    374  void StopControllingRegistration(
    375      ServiceWorkerRegistrationInfo* aRegistration);
    376 
    377  // Find the ServiceWorkerRegistration whose scope best matches the URL of the
    378  // given window or worker client (for the origin of the client based on its
    379  // principal).  This cannot be used with ServiceWorker ClientInfos.
    380  already_AddRefed<ServiceWorkerRegistrationInfo>
    381  GetServiceWorkerRegistrationInfo(const ClientInfo& aClientInfo) const;
    382 
    383  // Find the ServiceWorkerRegistration whose scope best matches the provided
    384  // URL (for the origin of the given principal).
    385  //
    386  // Note that `GetRegistration` should be used in cases where you already have
    387  // an exact scope.
    388  already_AddRefed<ServiceWorkerRegistrationInfo>
    389  GetServiceWorkerRegistrationInfo(nsIPrincipal* aPrincipal,
    390                                   nsIURI* aURI) const;
    391 
    392  // Find the ServiceWorkerRegistration whose scope best matches the provided
    393  // URL for the origin encoded as a scope key that has been obtained from
    394  // PrincipToScopeKey.
    395  already_AddRefed<ServiceWorkerRegistrationInfo>
    396  GetServiceWorkerRegistrationInfo(const nsACString& aScopeKey,
    397                                   nsIURI* aURI) const;
    398 
    399  static nsresult PrincipalToScopeKey(nsIPrincipal* aPrincipal,
    400                                      nsACString& aKey);
    401 
    402  static nsresult PrincipalInfoToScopeKey(
    403      const mozilla::ipc::PrincipalInfo& aPrincipalInfo, nsACString& aKey);
    404 
    405  static void AddScopeAndRegistration(
    406      const nsACString& aScope, ServiceWorkerRegistrationInfo* aRegistation);
    407 
    408  static bool HasScope(nsIPrincipal* aPrincipal, const nsACString& aScope);
    409 
    410  static void RemoveScopeAndRegistration(
    411      ServiceWorkerRegistrationInfo* aRegistration);
    412 
    413  void QueueFireEventOnServiceWorkerRegistrations(
    414      ServiceWorkerRegistrationInfo* aRegistration, const nsAString& aName);
    415 
    416  void UpdateClientControllers(ServiceWorkerRegistrationInfo* aRegistration);
    417 
    418  void MaybeRemoveRegistration(ServiceWorkerRegistrationInfo* aRegistration);
    419 
    420  void PurgeServiceWorker(const ServiceWorkerRegistrationData& aRegistration,
    421                          nsIPrincipal* aPrincipal);
    422 
    423  RefPtr<ServiceWorkerManagerChild> mActor;
    424 
    425  bool mShuttingDown;
    426 
    427  nsTArray<nsCOMPtr<nsIServiceWorkerManagerListener>> mListeners;
    428 
    429  void NotifyListenersOnRegister(
    430      nsIServiceWorkerRegistrationInfo* aRegistration);
    431 
    432  void NotifyListenersOnUnregister(
    433      nsIServiceWorkerRegistrationInfo* aRegistration);
    434 
    435  void NotifyListenersOnQuotaUsageCheckFinish(
    436      nsIServiceWorkerRegistrationInfo* aRegistration);
    437 
    438  void ScheduleUpdateTimer(nsIPrincipal* aPrincipal, const nsACString& aScope);
    439 
    440  void UpdateTimerFired(nsIPrincipal* aPrincipal, const nsACString& aScope);
    441 
    442  void MaybeSendUnregister(nsIPrincipal* aPrincipal, const nsACString& aScope);
    443 
    444  // Used by remove() and removeAll() when clearing history.
    445  // MUST ONLY BE CALLED FROM UnregisterIfMatchesHost!
    446  void ForceUnregister(RegistrationDataPerPrincipal* aRegistrationData,
    447                       ServiceWorkerRegistrationInfo* aRegistration);
    448 
    449  // An "orphaned" registration is one that is unregistered and not controlling
    450  // clients. The ServiceWorkerManager must know about all orphaned
    451  // registrations to forcefully shutdown all Service Workers during browser
    452  // shutdown.
    453  void AddOrphanedRegistration(ServiceWorkerRegistrationInfo* aRegistration);
    454 
    455  void RemoveOrphanedRegistration(ServiceWorkerRegistrationInfo* aRegistration);
    456 
    457  HashSet<RefPtr<ServiceWorkerRegistrationInfo>,
    458          PointerHasher<ServiceWorkerRegistrationInfo*>>
    459      mOrphanedRegistrations;
    460 
    461  RefPtr<ServiceWorkerShutdownBlocker> mShutdownBlocker;
    462 
    463  nsClassHashtable<nsCStringHashKey, RegistrationDataPerPrincipal>
    464      mRegistrationInfos;
    465 
    466  struct ControlledClientData {
    467    RefPtr<ClientHandle> mClientHandle;
    468    RefPtr<ServiceWorkerRegistrationInfo> mRegistrationInfo;
    469 
    470    ControlledClientData(ClientHandle* aClientHandle,
    471                         ServiceWorkerRegistrationInfo* aRegistrationInfo)
    472        : mClientHandle(aClientHandle), mRegistrationInfo(aRegistrationInfo) {}
    473  };
    474 
    475  nsClassHashtable<nsIDHashKey, ControlledClientData> mControlledClients;
    476 
    477  struct PendingReadyData {
    478    RefPtr<ClientHandle> mClientHandle;
    479    RefPtr<ServiceWorkerRegistrationPromise::Private> mPromise;
    480 
    481    explicit PendingReadyData(ClientHandle* aClientHandle)
    482        : mClientHandle(aClientHandle),
    483          mPromise(new ServiceWorkerRegistrationPromise::Private(__func__)) {}
    484  };
    485 
    486  nsTArray<UniquePtr<PendingReadyData>> mPendingReadyList;
    487 
    488  const uint32_t mTelemetryPeriodMs = 5 * 1000;
    489  TimeStamp mTelemetryLastChange;
    490  RefPtr<ETPPermissionObserver> mETPPermissionObserver;
    491 };
    492 
    493 }  // namespace dom
    494 }  // namespace mozilla
    495 
    496 #endif  // mozilla_dom_workers_serviceworkermanager_h