diff options
Diffstat (limited to 'dom/workers/ServiceWorkerPrivate.h')
-rw-r--r-- | dom/workers/ServiceWorkerPrivate.h | 236 |
1 files changed, 236 insertions, 0 deletions
diff --git a/dom/workers/ServiceWorkerPrivate.h b/dom/workers/ServiceWorkerPrivate.h new file mode 100644 index 000000000..8d59ea1d0 --- /dev/null +++ b/dom/workers/ServiceWorkerPrivate.h @@ -0,0 +1,236 @@ +/* -*- 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, + 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 |