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_serviceworkerprivate_h 8 #define mozilla_dom_workers_serviceworkerprivate_h 9 10 #include "nsCOMPtr.h" 11 12 #include "WorkerPrivate.h" 13 14 #define NOTIFICATION_CLICK_EVENT_NAME "notificationclick" 15 #define NOTIFICATION_CLOSE_EVENT_NAME "notificationclose" 16 17 class nsIInterceptedChannel; 18 19 namespace mozilla { 20 namespace dom { 21 namespace workers { 22 23 class ServiceWorkerInfo; 24 class ServiceWorkerRegistrationInfo; 25 class KeepAliveToken; 26 27 class LifeCycleEventCallback : public Runnable 28 { 29 public: 30 // Called on the worker thread. 31 virtual void 32 SetResult(bool aResult) = 0; 33 }; 34 35 // ServiceWorkerPrivate is a wrapper for managing the on-demand aspect of 36 // service workers. It handles all event dispatching to the worker and ensures 37 // the worker thread is running when needed. 38 // 39 // Lifetime management: To spin up the worker thread we own a |WorkerPrivate| 40 // object which can be cancelled if no events are received for a certain 41 // amount of time. The worker is kept alive by holding a |KeepAliveToken| 42 // reference. 43 // 44 // Extendable events hold tokens for the duration of their handler execution 45 // and until their waitUntil promise is resolved, while ServiceWorkerPrivate 46 // will hold a token for |dom.serviceWorkers.idle_timeout| seconds after each 47 // new event. 48 // 49 // Note: All timer events must be handled on the main thread because the 50 // worker may block indefinitely the worker thread (e. g. infinite loop in the 51 // script). 52 // 53 // There are 3 cases where we may ignore keep alive tokens: 54 // 1. When ServiceWorkerPrivate's token expired, if there are still waitUntil 55 // handlers holding tokens, we wait another |dom.serviceWorkers.idle_extended_timeout| 56 // seconds before forcibly terminating the worker. 57 // 2. If the worker stopped controlling documents and it is not handling push 58 // events. 59 // 3. The content process is shutting down. 60 // 61 // Adding an API function for a new event requires calling |SpawnWorkerIfNeeded| 62 // with an appropriate reason before any runnable is dispatched to the worker. 63 // If the event is extendable then the runnable should inherit 64 // ExtendableEventWorkerRunnable. 65 class ServiceWorkerPrivate final : public nsIObserver 66 { 67 friend class KeepAliveToken; 68 69 public: 70 NS_DECL_CYCLE_COLLECTING_ISUPPORTS 71 NS_DECL_CYCLE_COLLECTION_CLASS(ServiceWorkerPrivate) 72 NS_DECL_NSIOBSERVER 73 74 explicit ServiceWorkerPrivate(ServiceWorkerInfo* aInfo); 75 76 nsresult 77 SendMessageEvent(JSContext* aCx, JS::Handle<JS::Value> aMessage, 78 const Optional<Sequence<JS::Value>>& aTransferable, 79 UniquePtr<ServiceWorkerClientInfo>&& aClientInfo); 80 81 // This is used to validate the worker script and continue the installation 82 // process. 83 nsresult 84 CheckScriptEvaluation(LifeCycleEventCallback* aCallback); 85 86 nsresult 87 SendLifeCycleEvent(const nsAString& aEventType, 88 LifeCycleEventCallback* aCallback, 89 nsIRunnable* aLoadFailure); 90 91 nsresult 92 SendPushEvent(const nsAString& aMessageId, 93 const Maybe<nsTArray<uint8_t>>& aData, 94 ServiceWorkerRegistrationInfo* aRegistration); 95 96 nsresult 97 SendPushSubscriptionChangeEvent(); 98 99 nsresult 100 SendNotificationEvent(const nsAString& aEventName, 101 const nsAString& aID, 102 const nsAString& aTitle, 103 const nsAString& aDir, 104 const nsAString& aLang, 105 const nsAString& aBody, 106 const nsAString& aTag, 107 const nsAString& aIcon, 108 const nsAString& aData, 109 const nsAString& aBehavior, 110 const nsAString& aScope); 111 112 nsresult 113 SendFetchEvent(nsIInterceptedChannel* aChannel, 114 nsILoadGroup* aLoadGroup, 115 const nsAString& aDocumentId, 116 bool aIsReload); 117 118 void 119 StoreISupports(nsISupports* aSupports); 120 121 void 122 RemoveISupports(nsISupports* aSupports); 123 124 // This will terminate the current running worker thread and drop the 125 // workerPrivate reference. 126 // Called by ServiceWorkerInfo when [[Clear Registration]] is invoked 127 // or whenever the spec mandates that we terminate the worker. 128 // This is a no-op if the worker has already been stopped. 129 void 130 TerminateWorker(); 131 132 void 133 NoteDeadServiceWorkerInfo(); 134 135 void 136 NoteStoppedControllingDocuments(); 137 138 void 139 Activated(); 140 141 nsresult 142 GetDebugger(nsIWorkerDebugger** aResult); 143 144 nsresult 145 AttachDebugger(); 146 147 nsresult 148 DetachDebugger(); 149 150 bool 151 IsIdle() const; 152 153 void 154 AddPendingWindow(Runnable* aPendingWindow); 155 156 private: 157 enum WakeUpReason { 158 FetchEvent = 0, 159 PushEvent, 160 PushSubscriptionChangeEvent, 161 MessageEvent, 162 NotificationClickEvent, 163 NotificationCloseEvent, 164 LifeCycleEvent, 165 AttachEvent 166 }; 167 168 // Timer callbacks 169 void 170 NoteIdleWorkerCallback(nsITimer* aTimer); 171 172 void 173 TerminateWorkerCallback(nsITimer* aTimer); 174 175 void 176 RenewKeepAliveToken(WakeUpReason aWhy); 177 178 void 179 ResetIdleTimeout(); 180 181 void 182 AddToken(); 183 184 void 185 ReleaseToken(); 186 187 // |aLoadFailedRunnable| is a runnable dispatched to the main thread 188 // if the script loader failed for some reason, but can be null. 189 nsresult 190 SpawnWorkerIfNeeded(WakeUpReason aWhy, 191 nsIRunnable* aLoadFailedRunnable, 192 nsILoadGroup* aLoadGroup = nullptr); 193 194 ~ServiceWorkerPrivate(); 195 196 already_AddRefed<KeepAliveToken> 197 CreateEventKeepAliveToken(); 198 199 // The info object owns us. It is possible to outlive it for a brief period 200 // of time if there are pending waitUntil promises, in which case it 201 // will be null and |SpawnWorkerIfNeeded| will always fail. 202 ServiceWorkerInfo* MOZ_NON_OWNING_REF mInfo; 203 204 // The WorkerPrivate object can only be closed by this class or by the 205 // RuntimeService class if gecko is shutting down. Closing the worker 206 // multiple times is OK, since the second attempt will be a no-op. 207 RefPtr<WorkerPrivate> mWorkerPrivate; 208 209 nsCOMPtr<nsITimer> mIdleWorkerTimer; 210 211 // We keep a token for |dom.serviceWorkers.idle_timeout| seconds to give the 212 // worker a grace period after each event. 213 RefPtr<KeepAliveToken> mIdleKeepAliveToken; 214 215 uint64_t mDebuggerCount; 216 217 uint64_t mTokenCount; 218 219 // Meant for keeping objects alive while handling requests from the worker 220 // on the main thread. Access to this array is provided through 221 // |StoreISupports| and |RemoveISupports|. Note that the array is also 222 // cleared whenever the worker is terminated. 223 nsTArray<nsCOMPtr<nsISupports>> mSupportsArray; 224 225 // Array of function event worker runnables that are pending due to 226 // the worker activating. Main thread only. 227 nsTArray<RefPtr<WorkerRunnable>> mPendingFunctionalEvents; 228 229 nsTArray<Runnable*> pendingWindows; 230 }; 231 232 } // namespace workers 233 } // namespace dom 234 } // namespace mozilla 235 236 #endif // mozilla_dom_workers_serviceworkerprivate_h 237