/* -*- 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_serviceworkerprivate_h #define mozilla_dom_workers_serviceworkerprivate_h #include "nsCOMPtr.h" #include "WorkerPrivate.h" #define NOTIFICATION_CLICK_EVENT_NAME "notificationclick" #define NOTIFICATION_CLOSE_EVENT_NAME "notificationclose" class nsIInterceptedChannel; namespace mozilla { namespace dom { namespace workers { class ServiceWorkerInfo; class ServiceWorkerRegistrationInfo; class KeepAliveToken; class LifeCycleEventCallback : public Runnable { public: // Called on the worker thread. virtual void SetResult(bool aResult) = 0; }; // ServiceWorkerPrivate is a wrapper for managing the on-demand aspect of // service workers. It handles all event dispatching to the worker and ensures // the worker thread is running when needed. // // Lifetime management: To spin up the worker thread we own a |WorkerPrivate| // object which can be cancelled if no events are received for a certain // amount of time. The worker is kept alive by holding a |KeepAliveToken| // reference. // // Extendable events hold tokens for the duration of their handler execution // and until their waitUntil promise is resolved, while ServiceWorkerPrivate // will hold a token for |dom.serviceWorkers.idle_timeout| seconds after each // new event. // // Note: All timer events must be handled on the main thread because the // worker may block indefinitely the worker thread (e. g. infinite loop in the // script). // // There are 3 cases where we may ignore keep alive tokens: // 1. When ServiceWorkerPrivate's token expired, if there are still waitUntil // handlers holding tokens, we wait another |dom.serviceWorkers.idle_extended_timeout| // seconds before forcibly terminating the worker. // 2. If the worker stopped controlling documents and it is not handling push // events. // 3. The content process is shutting down. // // Adding an API function for a new event requires calling |SpawnWorkerIfNeeded| // with an appropriate reason before any runnable is dispatched to the worker. // If the event is extendable then the runnable should inherit // ExtendableEventWorkerRunnable. class ServiceWorkerPrivate final : public nsIObserver { friend class KeepAliveToken; public: NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_CYCLE_COLLECTION_CLASS(ServiceWorkerPrivate) NS_DECL_NSIOBSERVER explicit ServiceWorkerPrivate(ServiceWorkerInfo* aInfo); nsresult SendMessageEvent(JSContext* aCx, JS::Handle<JS::Value> aMessage, const Optional<Sequence<JS::Value>>& aTransferable, UniquePtr<ServiceWorkerClientInfo>&& aClientInfo); // This is used to validate the worker script and continue the installation // process. nsresult CheckScriptEvaluation(LifeCycleEventCallback* aCallback); nsresult SendLifeCycleEvent(const nsAString& aEventType, LifeCycleEventCallback* aCallback, nsIRunnable* aLoadFailure); nsresult SendPushEvent(const nsAString& aMessageId, const Maybe<nsTArray<uint8_t>>& aData, ServiceWorkerRegistrationInfo* aRegistration); nsresult SendPushSubscriptionChangeEvent(); nsresult SendNotificationEvent(const nsAString& aEventName, 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, const nsAString& aScope); nsresult SendFetchEvent(nsIInterceptedChannel* aChannel, nsILoadGroup* aLoadGroup, const nsAString& aDocumentId, bool aIsReload); void StoreISupports(nsISupports* aSupports); void RemoveISupports(nsISupports* aSupports); // This will terminate the current running worker thread and drop the // workerPrivate reference. // Called by ServiceWorkerInfo when [[Clear Registration]] is invoked // or whenever the spec mandates that we terminate the worker. // This is a no-op if the worker has already been stopped. void TerminateWorker(); void NoteDeadServiceWorkerInfo(); void NoteStoppedControllingDocuments(); void Activated(); nsresult GetDebugger(nsIWorkerDebugger** aResult); nsresult AttachDebugger(); nsresult DetachDebugger(); bool IsIdle() const; void AddPendingWindow(Runnable* aPendingWindow); private: enum WakeUpReason { FetchEvent = 0, PushEvent, PushSubscriptionChangeEvent, MessageEvent, NotificationClickEvent, NotificationCloseEvent, LifeCycleEvent, AttachEvent }; // Timer callbacks void NoteIdleWorkerCallback(nsITimer* aTimer); void TerminateWorkerCallback(nsITimer* aTimer); void RenewKeepAliveToken(WakeUpReason aWhy); void ResetIdleTimeout(); void AddToken(); void ReleaseToken(); // |aLoadFailedRunnable| is a runnable dispatched to the main thread // if the script loader failed for some reason, but can be null. nsresult SpawnWorkerIfNeeded(WakeUpReason aWhy, nsIRunnable* aLoadFailedRunnable, bool* aNewWorkerCreated = nullptr, nsILoadGroup* aLoadGroup = nullptr); ~ServiceWorkerPrivate(); already_AddRefed<KeepAliveToken> CreateEventKeepAliveToken(); // The info object owns us. It is possible to outlive it for a brief period // of time if there are pending waitUntil promises, in which case it // will be null and |SpawnWorkerIfNeeded| will always fail. ServiceWorkerInfo* MOZ_NON_OWNING_REF mInfo; // The WorkerPrivate object can only be closed by this class or by the // RuntimeService class if gecko is shutting down. Closing the worker // multiple times is OK, since the second attempt will be a no-op. RefPtr<WorkerPrivate> mWorkerPrivate; nsCOMPtr<nsITimer> mIdleWorkerTimer; // We keep a token for |dom.serviceWorkers.idle_timeout| seconds to give the // worker a grace period after each event. RefPtr<KeepAliveToken> mIdleKeepAliveToken; uint64_t mDebuggerCount; uint64_t mTokenCount; // Meant for keeping objects alive while handling requests from the worker // on the main thread. Access to this array is provided through // |StoreISupports| and |RemoveISupports|. Note that the array is also // cleared whenever the worker is terminated. nsTArray<nsCOMPtr<nsISupports>> mSupportsArray; // Array of function event worker runnables that are pending due to // the worker activating. Main thread only. nsTArray<RefPtr<WorkerRunnable>> mPendingFunctionalEvents; nsTArray<Runnable*> pendingWindows; }; } // namespace workers } // namespace dom } // namespace mozilla #endif // mozilla_dom_workers_serviceworkerprivate_h