summaryrefslogtreecommitdiffstats
path: root/dom/serviceworkers/ServiceWorkerManager.h
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--dom/serviceworkers/ServiceWorkerManager.h439
1 files changed, 439 insertions, 0 deletions
diff --git a/dom/serviceworkers/ServiceWorkerManager.h b/dom/serviceworkers/ServiceWorkerManager.h
new file mode 100644
index 0000000000..9a3b9ee39f
--- /dev/null
+++ b/dom/serviceworkers/ServiceWorkerManager.h
@@ -0,0 +1,439 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_workers_serviceworkermanager_h
+#define mozilla_dom_workers_serviceworkermanager_h
+
+#include <cstdint>
+#include "ErrorList.h"
+#include "ServiceWorkerShutdownState.h"
+#include "js/ErrorReport.h"
+#include "mozilla/AlreadyAddRefed.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/HashTable.h"
+#include "mozilla/MozPromise.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/dom/ClientHandle.h"
+#include "mozilla/dom/ClientOpPromise.h"
+#include "mozilla/dom/ServiceWorkerRegistrationBinding.h"
+#include "mozilla/dom/ServiceWorkerRegistrationInfo.h"
+#include "mozilla/dom/ServiceWorkerUtils.h"
+#include "mozilla/mozalloc.h"
+#include "nsClassHashtable.h"
+#include "nsContentUtils.h"
+#include "nsHashKeys.h"
+#include "nsIObserver.h"
+#include "nsIServiceWorkerManager.h"
+#include "nsISupports.h"
+#include "nsStringFwd.h"
+#include "nsTArray.h"
+
+class nsIConsoleReportCollector;
+
+namespace mozilla {
+
+class OriginAttributes;
+
+namespace ipc {
+class PrincipalInfo;
+} // namespace ipc
+
+namespace dom {
+
+extern uint32_t gServiceWorkersRegistered;
+extern uint32_t gServiceWorkersRegisteredFetch;
+
+class ContentParent;
+class ServiceWorkerInfo;
+class ServiceWorkerJobQueue;
+class ServiceWorkerManagerChild;
+class ServiceWorkerPrivate;
+class ServiceWorkerRegistrar;
+class ServiceWorkerShutdownBlocker;
+
+class ServiceWorkerUpdateFinishCallback {
+ protected:
+ virtual ~ServiceWorkerUpdateFinishCallback() = default;
+
+ public:
+ NS_INLINE_DECL_REFCOUNTING(ServiceWorkerUpdateFinishCallback)
+
+ virtual void UpdateSucceeded(ServiceWorkerRegistrationInfo* aInfo) = 0;
+
+ virtual void UpdateFailed(ErrorResult& aStatus) = 0;
+};
+
+#define NS_SERVICEWORKERMANAGER_IMPL_IID \
+ { /* f4f8755a-69ca-46e8-a65d-775745535990 */ \
+ 0xf4f8755a, 0x69ca, 0x46e8, { \
+ 0xa6, 0x5d, 0x77, 0x57, 0x45, 0x53, 0x59, 0x90 \
+ } \
+ }
+
+/*
+ * The ServiceWorkerManager is a per-process global that deals with the
+ * installation, querying and event dispatch of ServiceWorkers for all the
+ * origins in the process.
+ *
+ * NOTE: the following documentation is a WIP:
+ *
+ * The ServiceWorkerManager (SWM) is a main-thread, parent-process singleton
+ * that encapsulates the browser-global state of service workers. This state
+ * includes, but is not limited to, all service worker registrations and all
+ * controlled service worker clients. The SWM also provides methods to read and
+ * mutate this state and to dispatch operations (e.g. DOM events such as a
+ * FetchEvent) to service workers.
+ *
+ * Example usage:
+ *
+ * MOZ_ASSERT(NS_IsMainThread(), "SWM is main-thread only");
+ *
+ * RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+ *
+ * // Nullness must be checked by code that possibly executes during browser
+ * // shutdown, which is when the SWM is destroyed.
+ * if (swm) {
+ * // Do something with the SWM.
+ * }
+ */
+class ServiceWorkerManager final : public nsIServiceWorkerManager,
+ public nsIObserver {
+ friend class GetRegistrationsRunnable;
+ friend class GetRegistrationRunnable;
+ friend class ServiceWorkerJob;
+ friend class ServiceWorkerRegistrationInfo;
+ friend class ServiceWorkerShutdownBlocker;
+ friend class ServiceWorkerUnregisterJob;
+ friend class ServiceWorkerUpdateJob;
+ friend class UpdateTimerCallback;
+
+ public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSISERVICEWORKERMANAGER
+ NS_DECL_NSIOBSERVER
+
+ // Return true if the given principal and URI matches a registered service
+ // worker which handles fetch event.
+ // If there is a matched service worker but doesn't handle fetch events, this
+ // method will try to set the matched service worker as the controller of the
+ // passed in channel. Then also schedule a soft-update job for the service
+ // worker.
+ bool IsAvailable(nsIPrincipal* aPrincipal, nsIURI* aURI,
+ nsIChannel* aChannel);
+
+ void DispatchFetchEvent(nsIInterceptedChannel* aChannel, ErrorResult& aRv);
+
+ void Update(nsIPrincipal* aPrincipal, const nsACString& aScope,
+ nsCString aNewestWorkerScriptUrl,
+ ServiceWorkerUpdateFinishCallback* aCallback);
+
+ void UpdateInternal(nsIPrincipal* aPrincipal, const nsACString& aScope,
+ nsCString&& aNewestWorkerScriptUrl,
+ ServiceWorkerUpdateFinishCallback* aCallback);
+
+ void SoftUpdate(const OriginAttributes& aOriginAttributes,
+ const nsACString& aScope);
+
+ void SoftUpdateInternal(const OriginAttributes& aOriginAttributes,
+ const nsACString& aScope,
+ ServiceWorkerUpdateFinishCallback* aCallback);
+
+ RefPtr<ServiceWorkerRegistrationPromise> Register(
+ const ClientInfo& aClientInfo, const nsACString& aScopeURL,
+ const nsACString& aScriptURL,
+ ServiceWorkerUpdateViaCache aUpdateViaCache);
+
+ RefPtr<ServiceWorkerRegistrationPromise> GetRegistration(
+ const ClientInfo& aClientInfo, const nsACString& aURL) const;
+
+ RefPtr<ServiceWorkerRegistrationListPromise> GetRegistrations(
+ const ClientInfo& aClientInfo) const;
+
+ already_AddRefed<ServiceWorkerRegistrationInfo> GetRegistration(
+ nsIPrincipal* aPrincipal, const nsACString& aScope) const;
+
+ already_AddRefed<ServiceWorkerRegistrationInfo> GetRegistration(
+ const mozilla::ipc::PrincipalInfo& aPrincipal,
+ const nsACString& aScope) const;
+
+ already_AddRefed<ServiceWorkerRegistrationInfo> CreateNewRegistration(
+ const nsCString& aScope, nsIPrincipal* aPrincipal,
+ ServiceWorkerUpdateViaCache aUpdateViaCache,
+ IPCNavigationPreloadState aNavigationPreloadState =
+ IPCNavigationPreloadState(false, "true"_ns));
+
+ void RemoveRegistration(ServiceWorkerRegistrationInfo* aRegistration);
+
+ void StoreRegistration(nsIPrincipal* aPrincipal,
+ ServiceWorkerRegistrationInfo* aRegistration);
+
+ /**
+ * Report an error for the given scope to any window we think might be
+ * interested, failing over to the Browser Console if we couldn't find any.
+ *
+ * Error messages should be localized, so you probably want to call
+ * LocalizeAndReportToAllClients instead, which in turn calls us after
+ * localizing the error.
+ */
+ void ReportToAllClients(const nsCString& aScope, const nsString& aMessage,
+ const nsString& aFilename, const nsString& aLine,
+ uint32_t aLineNumber, uint32_t aColumnNumber,
+ uint32_t aFlags);
+
+ /**
+ * Report a localized error for the given scope to any window we think might
+ * be interested.
+ *
+ * Note that this method takes an nsTArray<nsString> for the parameters, not
+ * bare chart16_t*[]. You can use a std::initializer_list constructor inline
+ * so that argument might look like: nsTArray<nsString> { some_nsString,
+ * PromiseFlatString(some_nsSubString_aka_nsAString),
+ * NS_ConvertUTF8toUTF16(some_nsCString_or_nsCSubString),
+ * u"some literal"_ns }. If you have anything else, like a
+ * number, you can use an nsAutoString with AppendInt/friends.
+ *
+ * @param [aFlags]
+ * The nsIScriptError flag, one of errorFlag (0x0), warningFlag (0x1),
+ * infoFlag (0x8). We default to error if omitted because usually we're
+ * logging exceptional and/or obvious breakage.
+ */
+ static void LocalizeAndReportToAllClients(
+ const nsCString& aScope, const char* aStringKey,
+ const nsTArray<nsString>& aParamArray, uint32_t aFlags = 0x0,
+ const nsString& aFilename = u""_ns, const nsString& aLine = u""_ns,
+ uint32_t aLineNumber = 0, uint32_t aColumnNumber = 0);
+
+ // Always consumes the error by reporting to consoles of all controlled
+ // documents.
+ void HandleError(JSContext* aCx, nsIPrincipal* aPrincipal,
+ const nsCString& aScope, const nsString& aWorkerURL,
+ const nsString& aMessage, const nsString& aFilename,
+ const nsString& aLine, uint32_t aLineNumber,
+ uint32_t aColumnNumber, uint32_t aFlags, JSExnType aExnType);
+
+ [[nodiscard]] RefPtr<GenericErrorResultPromise> MaybeClaimClient(
+ const ClientInfo& aClientInfo,
+ ServiceWorkerRegistrationInfo* aWorkerRegistration);
+
+ [[nodiscard]] RefPtr<GenericErrorResultPromise> MaybeClaimClient(
+ const ClientInfo& aClientInfo,
+ const ServiceWorkerDescriptor& aServiceWorker);
+
+ static already_AddRefed<ServiceWorkerManager> GetInstance();
+
+ void LoadRegistration(const ServiceWorkerRegistrationData& aRegistration);
+
+ void LoadRegistrations(
+ const nsTArray<ServiceWorkerRegistrationData>& aRegistrations);
+
+ void MaybeCheckNavigationUpdate(const ClientInfo& aClientInfo);
+
+ nsresult SendPushEvent(const nsACString& aOriginAttributes,
+ const nsACString& aScope, const nsAString& aMessageId,
+ const Maybe<nsTArray<uint8_t>>& aData);
+
+ void WorkerIsIdle(ServiceWorkerInfo* aWorker);
+
+ RefPtr<ServiceWorkerRegistrationPromise> WhenReady(
+ const ClientInfo& aClientInfo);
+
+ void CheckPendingReadyPromises();
+
+ void RemovePendingReadyPromise(const ClientInfo& aClientInfo);
+
+ void NoteInheritedController(const ClientInfo& aClientInfo,
+ const ServiceWorkerDescriptor& aController);
+
+ void BlockShutdownOn(GenericNonExclusivePromise* aPromise,
+ uint32_t aShutdownStateId);
+
+ nsresult GetClientRegistration(
+ const ClientInfo& aClientInfo,
+ ServiceWorkerRegistrationInfo** aRegistrationInfo);
+
+ int32_t GetPrincipalQuotaUsageCheckCount(nsIPrincipal* aPrincipal);
+
+ void CheckPrincipalQuotaUsage(nsIPrincipal* aPrincipal,
+ const nsACString& aScope);
+
+ // Returns the shutdown state ID (may be an invalid ID if an
+ // nsIAsyncShutdownBlocker is not used).
+ uint32_t MaybeInitServiceWorkerShutdownProgress() const;
+
+ void ReportServiceWorkerShutdownProgress(
+ uint32_t aShutdownStateId,
+ ServiceWorkerShutdownState::Progress aProgress) const;
+
+ // Record periodic telemetry on number of running ServiceWorkers. When
+ // the number of running ServiceWorkers changes (or on shutdown),
+ // ServiceWorkerPrivateImpl will call RecordTelemetry with the number of
+ // running serviceworkers and those supporting Fetch. We use
+ // mTelemetryLastChange to determine how many datapoints to inject into
+ // Telemetry, and dispatch a background runnable to call
+ // RecordTelemetryGap() and Accumulate them.
+ void RecordTelemetry(uint32_t aNumber, uint32_t aFetch);
+
+ void EvictFromBFCache(ServiceWorkerRegistrationInfo* aRegistration);
+
+ private:
+ struct RegistrationDataPerPrincipal;
+
+ static bool FindScopeForPath(const nsACString& aScopeKey,
+ const nsACString& aPath,
+ RegistrationDataPerPrincipal** aData,
+ nsACString& aMatch);
+
+ ServiceWorkerManager();
+ ~ServiceWorkerManager();
+
+ void Init(ServiceWorkerRegistrar* aRegistrar);
+
+ RefPtr<GenericErrorResultPromise> StartControllingClient(
+ const ClientInfo& aClientInfo,
+ ServiceWorkerRegistrationInfo* aRegistrationInfo,
+ bool aControlClientHandle = true);
+
+ void StopControllingClient(const ClientInfo& aClientInfo);
+
+ void MaybeStartShutdown();
+
+ void MaybeFinishShutdown();
+
+ already_AddRefed<ServiceWorkerJobQueue> GetOrCreateJobQueue(
+ const nsACString& aOriginSuffix, const nsACString& aScope);
+
+ void MaybeRemoveRegistrationInfo(const nsACString& aScopeKey);
+
+ already_AddRefed<ServiceWorkerRegistrationInfo> GetRegistration(
+ const nsACString& aScopeKey, const nsACString& aScope) const;
+
+ void AbortCurrentUpdate(ServiceWorkerRegistrationInfo* aRegistration);
+
+ nsresult Update(ServiceWorkerRegistrationInfo* aRegistration);
+
+ ServiceWorkerInfo* GetActiveWorkerInfoForScope(
+ const OriginAttributes& aOriginAttributes, const nsACString& aScope);
+
+ void StopControllingRegistration(
+ ServiceWorkerRegistrationInfo* aRegistration);
+
+ already_AddRefed<ServiceWorkerRegistrationInfo>
+ GetServiceWorkerRegistrationInfo(const ClientInfo& aClientInfo) const;
+
+ already_AddRefed<ServiceWorkerRegistrationInfo>
+ GetServiceWorkerRegistrationInfo(nsIPrincipal* aPrincipal,
+ nsIURI* aURI) const;
+
+ already_AddRefed<ServiceWorkerRegistrationInfo>
+ GetServiceWorkerRegistrationInfo(const nsACString& aScopeKey,
+ nsIURI* aURI) const;
+
+ // This method generates a key using isInElementBrowser from the principal. We
+ // don't use the origin because it can change during the loading.
+ static nsresult PrincipalToScopeKey(nsIPrincipal* aPrincipal,
+ nsACString& aKey);
+
+ static nsresult PrincipalInfoToScopeKey(
+ const mozilla::ipc::PrincipalInfo& aPrincipalInfo, nsACString& aKey);
+
+ static void AddScopeAndRegistration(
+ const nsACString& aScope, ServiceWorkerRegistrationInfo* aRegistation);
+
+ static bool HasScope(nsIPrincipal* aPrincipal, const nsACString& aScope);
+
+ static void RemoveScopeAndRegistration(
+ ServiceWorkerRegistrationInfo* aRegistration);
+
+ void QueueFireEventOnServiceWorkerRegistrations(
+ ServiceWorkerRegistrationInfo* aRegistration, const nsAString& aName);
+
+ void UpdateClientControllers(ServiceWorkerRegistrationInfo* aRegistration);
+
+ void MaybeRemoveRegistration(ServiceWorkerRegistrationInfo* aRegistration);
+
+ RefPtr<ServiceWorkerManagerChild> mActor;
+
+ bool mShuttingDown;
+
+ nsTArray<nsCOMPtr<nsIServiceWorkerManagerListener>> mListeners;
+
+ void NotifyListenersOnRegister(
+ nsIServiceWorkerRegistrationInfo* aRegistration);
+
+ void NotifyListenersOnUnregister(
+ nsIServiceWorkerRegistrationInfo* aRegistration);
+
+ void NotifyListenersOnQuotaUsageCheckFinish(
+ nsIServiceWorkerRegistrationInfo* aRegistration);
+
+ void ScheduleUpdateTimer(nsIPrincipal* aPrincipal, const nsACString& aScope);
+
+ void UpdateTimerFired(nsIPrincipal* aPrincipal, const nsACString& aScope);
+
+ void MaybeSendUnregister(nsIPrincipal* aPrincipal, const nsACString& aScope);
+
+ nsresult SendNotificationEvent(const nsAString& aEventName,
+ const nsACString& aOriginSuffix,
+ const nsACString& aScope, const nsAString& aID,
+ const nsAString& aTitle, const nsAString& aDir,
+ const nsAString& aLang, const nsAString& aBody,
+ const nsAString& aTag, const nsAString& aIcon,
+ const nsAString& aData,
+ const nsAString& aBehavior);
+
+ // Used by remove() and removeAll() when clearing history.
+ // MUST ONLY BE CALLED FROM UnregisterIfMatchesHost!
+ void ForceUnregister(RegistrationDataPerPrincipal* aRegistrationData,
+ ServiceWorkerRegistrationInfo* aRegistration);
+
+ // An "orphaned" registration is one that is unregistered and not controlling
+ // clients. The ServiceWorkerManager must know about all orphaned
+ // registrations to forcefully shutdown all Service Workers during browser
+ // shutdown.
+ void AddOrphanedRegistration(ServiceWorkerRegistrationInfo* aRegistration);
+
+ void RemoveOrphanedRegistration(ServiceWorkerRegistrationInfo* aRegistration);
+
+ HashSet<RefPtr<ServiceWorkerRegistrationInfo>,
+ PointerHasher<ServiceWorkerRegistrationInfo*>>
+ mOrphanedRegistrations;
+
+ RefPtr<ServiceWorkerShutdownBlocker> mShutdownBlocker;
+
+ nsClassHashtable<nsCStringHashKey, RegistrationDataPerPrincipal>
+ mRegistrationInfos;
+
+ struct ControlledClientData {
+ RefPtr<ClientHandle> mClientHandle;
+ RefPtr<ServiceWorkerRegistrationInfo> mRegistrationInfo;
+
+ ControlledClientData(ClientHandle* aClientHandle,
+ ServiceWorkerRegistrationInfo* aRegistrationInfo)
+ : mClientHandle(aClientHandle), mRegistrationInfo(aRegistrationInfo) {}
+ };
+
+ nsClassHashtable<nsIDHashKey, ControlledClientData> mControlledClients;
+
+ struct PendingReadyData {
+ RefPtr<ClientHandle> mClientHandle;
+ RefPtr<ServiceWorkerRegistrationPromise::Private> mPromise;
+
+ explicit PendingReadyData(ClientHandle* aClientHandle)
+ : mClientHandle(aClientHandle),
+ mPromise(new ServiceWorkerRegistrationPromise::Private(__func__)) {}
+ };
+
+ nsTArray<UniquePtr<PendingReadyData>> mPendingReadyList;
+
+ const uint32_t mTelemetryPeriodMs = 5 * 1000;
+ TimeStamp mTelemetryLastChange;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_workers_serviceworkermanager_h