/* -*- 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 "nsIServiceWorkerManager.h" #include "nsCOMPtr.h" #include "ipc/IPCMessageUtils.h" #include "mozilla/Attributes.h" #include "mozilla/AutoRestore.h" #include "mozilla/ConsoleReportCollector.h" #include "mozilla/LinkedList.h" #include "mozilla/MozPromise.h" #include "mozilla/Preferences.h" #include "mozilla/TypedEnumBits.h" #include "mozilla/UniquePtr.h" #include "mozilla/WeakPtr.h" #include "mozilla/dom/BindingUtils.h" #include "mozilla/dom/ClientHandle.h" #include "mozilla/dom/Promise.h" #include "mozilla/dom/ServiceWorkerRegistrar.h" #include "mozilla/dom/ServiceWorkerRegistrarTypes.h" #include "mozilla/dom/ServiceWorkerRegistrationInfo.h" #include "mozilla/dom/ServiceWorkerUtils.h" #include "mozilla/ipc/BackgroundUtils.h" #include "nsClassHashtable.h" #include "nsDataHashtable.h" #include "nsRefPtrHashtable.h" #include "nsTArrayForwardDeclare.h" class nsIConsoleReportCollector; namespace mozilla { class OriginAttributes; namespace ipc { class PrincipalInfo; } // namespace ipc namespace dom { class ContentParent; class ServiceWorkerInfo; class ServiceWorkerJobQueue; class ServiceWorkerManagerChild; class ServiceWorkerPrivate; class ServiceWorkerRegistrar; class ServiceWorkerUpdateFinishCallback { protected: virtual ~ServiceWorkerUpdateFinishCallback() {} 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. */ class ServiceWorkerManager final : public nsIServiceWorkerManager, public nsIObserver { friend class GetRegistrationsRunnable; friend class GetRegistrationRunnable; friend class ServiceWorkerJob; friend class ServiceWorkerRegistrationInfo; friend class ServiceWorkerUnregisterJob; friend class ServiceWorkerUpdateJob; friend class UpdateTimerCallback; public: NS_DECL_ISUPPORTS NS_DECL_NSISERVICEWORKERMANAGER NS_DECL_NSIOBSERVER struct RegistrationDataPerPrincipal; nsClassHashtable mRegistrationInfos; struct ControlledClientData { RefPtr mClientHandle; RefPtr mRegistrationInfo; ControlledClientData(ClientHandle* aClientHandle, ServiceWorkerRegistrationInfo* aRegistrationInfo) : mClientHandle(aClientHandle), mRegistrationInfo(aRegistrationInfo) {} }; nsClassHashtable mControlledClients; struct PendingReadyData { RefPtr mClientHandle; RefPtr mPromise; explicit PendingReadyData(ClientHandle* aClientHandle) : mClientHandle(aClientHandle), mPromise(new ServiceWorkerRegistrationPromise::Private(__func__)) {} }; nsTArray> mPendingReadyList; bool IsAvailable(nsIPrincipal* aPrincipal, nsIURI* aURI); // Return true if the given content process could potentially be executing // service worker code with the given principal. At the current time, this // just means that we have any registration for the origin, regardless of // scope. This is a very weak guarantee but is the best we can do when push // notifications can currently spin up a service worker in content processes // without our involvement in the parent process. // // In the future when there is only a single ServiceWorkerManager in the // parent process that is entirely in control of spawning and running service // worker code, we will be able to authoritatively indicate whether there is // an activate service worker in the given content process. At that time we // will rename this method HasActiveServiceWorkerInstance and provide // semantics that ensure this method returns true until the worker is known to // have shut down in order to allow the caller to induce a crash for security // reasons without having to worry about shutdown races with the worker. bool MayHaveActiveServiceWorkerInstance(ContentParent* aContent, nsIPrincipal* aPrincipal); void DispatchFetchEvent(nsIInterceptedChannel* aChannel, ErrorResult& aRv); void Update(nsIPrincipal* aPrincipal, const nsACString& aScope, ServiceWorkerUpdateFinishCallback* aCallback); void UpdateInternal(nsIPrincipal* aPrincipal, const nsACString& aScope, ServiceWorkerUpdateFinishCallback* aCallback); void SoftUpdate(const OriginAttributes& aOriginAttributes, const nsACString& aScope); void SoftUpdateInternal(const OriginAttributes& aOriginAttributes, const nsACString& aScope, ServiceWorkerUpdateFinishCallback* aCallback); void PropagateSoftUpdate(const OriginAttributes& aOriginAttributes, const nsAString& aScope); void PropagateRemove(const nsACString& aHost); void Remove(const nsACString& aHost); void PropagateRemoveAll(); void RemoveAll(); RefPtr Register( const ClientInfo& aClientInfo, const nsACString& aScopeURL, const nsACString& aScriptURL, ServiceWorkerUpdateViaCache aUpdateViaCache); RefPtr GetRegistration( const ClientInfo& aClientInfo, const nsACString& aURL) const; RefPtr GetRegistrations( const ClientInfo& aClientInfo) const; already_AddRefed GetRegistration( nsIPrincipal* aPrincipal, const nsACString& aScope) const; already_AddRefed GetRegistration( const mozilla::ipc::PrincipalInfo& aPrincipal, const nsACString& aScope) const; already_AddRefed CreateNewRegistration( const nsCString& aScope, nsIPrincipal* aPrincipal, ServiceWorkerUpdateViaCache aUpdateViaCache); void RemoveRegistration(ServiceWorkerRegistrationInfo* aRegistration); void StoreRegistration(nsIPrincipal* aPrincipal, ServiceWorkerRegistrationInfo* aRegistration); void FinishFetch(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 for the parameters, not * bare chart16_t*[]. You can use a std::initializer_list constructor inline * so that argument might look like: nsTArray { some_nsString, * PromiseFlatString(some_nsSubString_aka_nsAString), * NS_ConvertUTF8toUTF16(some_nsCString_or_nsCSubString), * NS_LITERAL_STRING("some literal") }. 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& aParamArray, uint32_t aFlags = 0x0, const nsString& aFilename = EmptyString(), const nsString& aLine = EmptyString(), 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); MOZ_MUST_USE RefPtr MaybeClaimClient( const ClientInfo& aClientInfo, ServiceWorkerRegistrationInfo* aWorkerRegistration); MOZ_MUST_USE RefPtr MaybeClaimClient( const ClientInfo& aClientInfo, const ServiceWorkerDescriptor& aServiceWorker); void SetSkipWaitingFlag(nsIPrincipal* aPrincipal, const nsCString& aScope, uint64_t aServiceWorkerID); static already_AddRefed GetInstance(); void LoadRegistration(const ServiceWorkerRegistrationData& aRegistration); void LoadRegistrations( const nsTArray& aRegistrations); // Used by remove() and removeAll() when clearing history. // MUST ONLY BE CALLED FROM UnregisterIfMatchesHost! void ForceUnregister(RegistrationDataPerPrincipal* aRegistrationData, ServiceWorkerRegistrationInfo* aRegistration); void MaybeCheckNavigationUpdate(const ClientInfo& aClientInfo); nsresult SendPushEvent(const nsACString& aOriginAttributes, const nsACString& aScope, const nsAString& aMessageId, const Maybe>& aData); nsresult NotifyUnregister(nsIPrincipal* aPrincipal, const nsAString& aScope); void WorkerIsIdle(ServiceWorkerInfo* aWorker); RefPtr WhenReady( const ClientInfo& aClientInfo); void CheckPendingReadyPromises(); void RemovePendingReadyPromise(const ClientInfo& aClientInfo); void NoteInheritedController(const ClientInfo& aClientInfo, const ServiceWorkerDescriptor& aController); private: ServiceWorkerManager(); ~ServiceWorkerManager(); void Init(ServiceWorkerRegistrar* aRegistrar); RefPtr StartControllingClient( const ClientInfo& aClientInfo, ServiceWorkerRegistrationInfo* aRegistrationInfo, bool aControlClientHandle = true); void StopControllingClient(const ClientInfo& aClientInfo); void MaybeStartShutdown(); already_AddRefed GetOrCreateJobQueue( const nsACString& aOriginSuffix, const nsACString& aScope); void MaybeRemoveRegistrationInfo(const nsACString& aScopeKey); already_AddRefed GetRegistration( const nsACString& aScopeKey, const nsACString& aScope) const; void AbortCurrentUpdate(ServiceWorkerRegistrationInfo* aRegistration); nsresult Update(ServiceWorkerRegistrationInfo* aRegistration); nsresult GetClientRegistration( const ClientInfo& aClientInfo, ServiceWorkerRegistrationInfo** aRegistrationInfo); ServiceWorkerInfo* GetActiveWorkerInfoForScope( const OriginAttributes& aOriginAttributes, const nsACString& aScope); void StopControllingRegistration( ServiceWorkerRegistrationInfo* aRegistration); already_AddRefed GetServiceWorkerRegistrationInfo(const ClientInfo& aClientInfo) const; already_AddRefed GetServiceWorkerRegistrationInfo(nsIPrincipal* aPrincipal, nsIURI* aURI) const; already_AddRefed GetServiceWorkerRegistrationInfo(const nsACString& aScopeKey, nsIURI* aURI) const; // This method generates a key using appId and 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 FindScopeForPath(const nsACString& aScopeKey, const nsACString& aPath, RegistrationDataPerPrincipal** aData, nsACString& aMatch); 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); // Removes all service worker registrations that matches the given pattern. void RemoveAllRegistrations(OriginAttributesPattern* aPattern); RefPtr mActor; bool mShuttingDown; nsTArray> mListeners; void NotifyListenersOnRegister( nsIServiceWorkerRegistrationInfo* aRegistration); void NotifyListenersOnUnregister( 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); }; } // namespace dom } // namespace mozilla #endif // mozilla_dom_workers_serviceworkermanager_h