Source code

Revision control

Other Tools

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_serviceworkerprivate_h
8
#define mozilla_dom_serviceworkerprivate_h
9
10
#include "nsCOMPtr.h"
11
#include "mozilla/dom/WorkerPrivate.h"
12
#include "mozilla/MozPromise.h"
13
14
#define NOTIFICATION_CLICK_EVENT_NAME "notificationclick"
15
#define NOTIFICATION_CLOSE_EVENT_NAME "notificationclose"
16
17
class nsIInterceptedChannel;
18
class nsIWorkerDebugger;
19
20
namespace mozilla {
21
22
class JSObjectHolder;
23
24
namespace dom {
25
26
class ClientInfoAndState;
27
class ServiceWorkerCloneData;
28
class ServiceWorkerInfo;
29
class ServiceWorkerPrivate;
30
class ServiceWorkerPrivateImpl;
31
class ServiceWorkerRegistrationInfo;
32
33
namespace ipc {
34
class StructuredCloneData;
35
} // namespace ipc
36
37
class LifeCycleEventCallback : public Runnable {
38
public:
39
LifeCycleEventCallback() : Runnable("dom::LifeCycleEventCallback") {}
40
41
// Called on the worker thread.
42
virtual void SetResult(bool aResult) = 0;
43
};
44
45
// Used to keep track of pending waitUntil as well as in-flight extendable
46
// events. When the last token is released, we attempt to terminate the worker.
47
class KeepAliveToken final : public nsISupports {
48
public:
49
NS_DECL_ISUPPORTS
50
51
explicit KeepAliveToken(ServiceWorkerPrivate* aPrivate);
52
53
private:
54
~KeepAliveToken();
55
56
RefPtr<ServiceWorkerPrivate> mPrivate;
57
};
58
59
// ServiceWorkerPrivate is a wrapper for managing the on-demand aspect of
60
// service workers. It handles all event dispatching to the worker and ensures
61
// the worker thread is running when needed.
62
//
63
// Lifetime management: To spin up the worker thread we own a |WorkerPrivate|
64
// object which can be cancelled if no events are received for a certain
65
// amount of time. The worker is kept alive by holding a |KeepAliveToken|
66
// reference.
67
//
68
// Extendable events hold tokens for the duration of their handler execution
69
// and until their waitUntil promise is resolved, while ServiceWorkerPrivate
70
// will hold a token for |dom.serviceWorkers.idle_timeout| seconds after each
71
// new event.
72
//
73
// Note: All timer events must be handled on the main thread because the
74
// worker may block indefinitely the worker thread (e. g. infinite loop in the
75
// script).
76
//
77
// There are 3 cases where we may ignore keep alive tokens:
78
// 1. When ServiceWorkerPrivate's token expired, if there are still waitUntil
79
// handlers holding tokens, we wait another
80
// |dom.serviceWorkers.idle_extended_timeout| seconds before forcibly
81
// terminating the worker.
82
// 2. If the worker stopped controlling documents and it is not handling push
83
// events.
84
// 3. The content process is shutting down.
85
//
86
// Adding an API function for a new event requires calling |SpawnWorkerIfNeeded|
87
// with an appropriate reason before any runnable is dispatched to the worker.
88
// If the event is extendable then the runnable should inherit
89
// ExtendableEventWorkerRunnable.
90
class ServiceWorkerPrivate final {
91
friend class KeepAliveToken;
92
friend class ServiceWorkerPrivateImpl;
93
94
public:
95
NS_IMETHOD_(MozExternalRefCountType) AddRef();
96
NS_IMETHOD_(MozExternalRefCountType) Release();
97
NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(ServiceWorkerPrivate)
98
99
typedef mozilla::FalseType HasThreadSafeRefCnt;
100
101
protected:
102
nsCycleCollectingAutoRefCnt mRefCnt;
103
NS_DECL_OWNINGTHREAD
104
105
public:
106
// TODO: remove this class. There's one (and only should be one) concrete
107
// class that derives this abstract base class.
108
class Inner {
109
public:
110
NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
111
112
virtual nsresult SendMessageEvent(
113
RefPtr<ServiceWorkerCloneData>&& aData,
114
const ClientInfoAndState& aClientInfoAndState) = 0;
115
116
virtual nsresult CheckScriptEvaluation(
117
RefPtr<LifeCycleEventCallback> aScriptEvaluationCallback) = 0;
118
119
virtual nsresult SendLifeCycleEvent(
120
const nsAString& aEventName,
121
RefPtr<LifeCycleEventCallback> aCallback) = 0;
122
123
virtual nsresult SendPushEvent(
124
RefPtr<ServiceWorkerRegistrationInfo> aRegistration,
125
const nsAString& aMessageId, const Maybe<nsTArray<uint8_t>>& aData) = 0;
126
127
virtual nsresult SendPushSubscriptionChangeEvent() = 0;
128
129
virtual nsresult SendNotificationEvent(
130
const nsAString& aEventName, const nsAString& aID,
131
const nsAString& aTitle, const nsAString& aDir, const nsAString& aLang,
132
const nsAString& aBody, const nsAString& aTag, const nsAString& aIcon,
133
const nsAString& aData, const nsAString& aBehavior,
134
const nsAString& aScope, uint32_t aDisableOpenClickDelay) = 0;
135
136
virtual nsresult SendFetchEvent(
137
RefPtr<ServiceWorkerRegistrationInfo> aRegistration,
138
nsCOMPtr<nsIInterceptedChannel> aChannel, const nsAString& aClientId,
139
const nsAString& aResultingClientId, bool aIsReload) = 0;
140
141
virtual nsresult SpawnWorkerIfNeeded() = 0;
142
143
virtual void TerminateWorker() = 0;
144
145
virtual void UpdateState(ServiceWorkerState aState) = 0;
146
147
virtual void NoteDeadOuter() = 0;
148
149
virtual bool WorkerIsDead() const = 0;
150
};
151
152
explicit ServiceWorkerPrivate(ServiceWorkerInfo* aInfo);
153
154
nsresult SendMessageEvent(RefPtr<ServiceWorkerCloneData>&& aData,
155
const ClientInfoAndState& aClientInfoAndState);
156
157
// This is used to validate the worker script and continue the installation
158
// process.
159
nsresult CheckScriptEvaluation(LifeCycleEventCallback* aCallback);
160
161
nsresult SendLifeCycleEvent(const nsAString& aEventType,
162
LifeCycleEventCallback* aCallback);
163
164
nsresult SendPushEvent(const nsAString& aMessageId,
165
const Maybe<nsTArray<uint8_t>>& aData,
166
ServiceWorkerRegistrationInfo* aRegistration);
167
168
nsresult SendPushSubscriptionChangeEvent();
169
170
nsresult SendNotificationEvent(const nsAString& aEventName,
171
const nsAString& aID, const nsAString& aTitle,
172
const nsAString& aDir, const nsAString& aLang,
173
const nsAString& aBody, const nsAString& aTag,
174
const nsAString& aIcon, const nsAString& aData,
175
const nsAString& aBehavior,
176
const nsAString& aScope);
177
178
nsresult SendFetchEvent(nsIInterceptedChannel* aChannel,
179
nsILoadGroup* aLoadGroup, const nsAString& aClientId,
180
const nsAString& aResultingClientId, bool aIsReload);
181
182
bool MaybeStoreISupports(nsISupports* aSupports);
183
184
void RemoveISupports(nsISupports* aSupports);
185
186
// This will terminate the current running worker thread and drop the
187
// workerPrivate reference.
188
// Called by ServiceWorkerInfo when [[Clear Registration]] is invoked
189
// or whenever the spec mandates that we terminate the worker.
190
// This is a no-op if the worker has already been stopped.
191
void TerminateWorker();
192
193
void NoteDeadServiceWorkerInfo();
194
195
void NoteStoppedControllingDocuments();
196
197
void UpdateState(ServiceWorkerState aState);
198
199
nsresult GetDebugger(nsIWorkerDebugger** aResult);
200
201
nsresult AttachDebugger();
202
203
nsresult DetachDebugger();
204
205
bool IsIdle() const;
206
207
// This promise is used schedule clearing of the owning registrations and its
208
// associated Service Workers if that registration becomes "unreachable" by
209
// the ServiceWorkerManager. This occurs under two conditions, which are the
210
// preconditions to calling this method:
211
// - The owning registration must be unregistered.
212
// - The associated Service Worker must *not* be controlling clients.
213
//
214
// Additionally, perhaps stating the obvious, the associated Service Worker
215
// must *not* be idle (whatever must be done "when idle" can just be done
216
// immediately).
217
RefPtr<GenericPromise> GetIdlePromise();
218
219
void SetHandlesFetch(bool aValue);
220
221
private:
222
enum WakeUpReason {
223
FetchEvent = 0,
224
PushEvent,
225
PushSubscriptionChangeEvent,
226
MessageEvent,
227
NotificationClickEvent,
228
NotificationCloseEvent,
229
LifeCycleEvent,
230
AttachEvent,
231
Unknown
232
};
233
234
// Timer callbacks
235
void NoteIdleWorkerCallback(nsITimer* aTimer);
236
237
void TerminateWorkerCallback(nsITimer* aTimer);
238
239
void RenewKeepAliveToken(WakeUpReason aWhy);
240
241
void ResetIdleTimeout();
242
243
void AddToken();
244
245
void ReleaseToken();
246
247
nsresult SpawnWorkerIfNeeded(WakeUpReason aWhy,
248
bool* aNewWorkerCreated = nullptr,
249
nsILoadGroup* aLoadGroup = nullptr);
250
251
~ServiceWorkerPrivate();
252
253
already_AddRefed<KeepAliveToken> CreateEventKeepAliveToken();
254
255
// The info object owns us. It is possible to outlive it for a brief period
256
// of time if there are pending waitUntil promises, in which case it
257
// will be null and |SpawnWorkerIfNeeded| will always fail.
258
ServiceWorkerInfo* MOZ_NON_OWNING_REF mInfo;
259
260
// The WorkerPrivate object can only be closed by this class or by the
261
// RuntimeService class if gecko is shutting down. Closing the worker
262
// multiple times is OK, since the second attempt will be a no-op.
263
RefPtr<WorkerPrivate> mWorkerPrivate;
264
265
nsCOMPtr<nsITimer> mIdleWorkerTimer;
266
267
// We keep a token for |dom.serviceWorkers.idle_timeout| seconds to give the
268
// worker a grace period after each event.
269
RefPtr<KeepAliveToken> mIdleKeepAliveToken;
270
271
uint64_t mDebuggerCount;
272
273
uint64_t mTokenCount;
274
275
// Meant for keeping objects alive while handling requests from the worker
276
// on the main thread. Access to this array is provided through
277
// |StoreISupports| and |RemoveISupports|. Note that the array is also
278
// cleared whenever the worker is terminated.
279
nsTArray<nsCOMPtr<nsISupports>> mSupportsArray;
280
281
// Array of function event worker runnables that are pending due to
282
// the worker activating. Main thread only.
283
nsTArray<RefPtr<WorkerRunnable>> mPendingFunctionalEvents;
284
285
RefPtr<Inner> mInner;
286
287
// Used by the owning `ServiceWorkerRegistrationInfo` when it wants to call
288
// `Clear` after being unregistered and isn't controlling any clients but this
289
// worker (i.e. the registration's active worker) isn't idle yet. Note that
290
// such an event should happen at most once in a
291
// `ServiceWorkerRegistrationInfo`s lifetime, so this promise should also only
292
// be obtained at most once.
293
MozPromiseHolder<GenericPromise> mIdlePromiseHolder;
294
295
#ifdef DEBUG
296
bool mIdlePromiseObtained = false;
297
#endif
298
};
299
300
} // namespace dom
301
} // namespace mozilla
302
303
#endif // mozilla_dom_serviceworkerprivate_h