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
#include "ServiceWorkerRegistrationProxy.h"
8
9
#include "mozilla/ipc/BackgroundParent.h"
10
#include "ServiceWorkerManager.h"
11
#include "ServiceWorkerRegistrationParent.h"
12
#include "ServiceWorkerUnregisterCallback.h"
13
14
namespace mozilla {
15
namespace dom {
16
17
using mozilla::ipc::AssertIsOnBackgroundThread;
18
19
class ServiceWorkerRegistrationProxy::DelayedUpdate final
20
: public nsITimerCallback {
21
RefPtr<ServiceWorkerRegistrationProxy> mProxy;
22
RefPtr<ServiceWorkerRegistrationPromise::Private> mPromise;
23
nsCOMPtr<nsITimer> mTimer;
24
25
~DelayedUpdate() = default;
26
27
public:
28
NS_DECL_THREADSAFE_ISUPPORTS
29
NS_DECL_NSITIMERCALLBACK
30
31
DelayedUpdate(RefPtr<ServiceWorkerRegistrationProxy>&& aProxy,
32
RefPtr<ServiceWorkerRegistrationPromise::Private>&& aPromise,
33
uint32_t delay);
34
35
void ChainTo(RefPtr<ServiceWorkerRegistrationPromise::Private> aPromise);
36
37
void Reject();
38
};
39
40
ServiceWorkerRegistrationProxy::~ServiceWorkerRegistrationProxy() {
41
// Any thread
42
MOZ_DIAGNOSTIC_ASSERT(!mActor);
43
MOZ_DIAGNOSTIC_ASSERT(!mReg);
44
}
45
46
void ServiceWorkerRegistrationProxy::MaybeShutdownOnBGThread() {
47
AssertIsOnBackgroundThread();
48
if (!mActor) {
49
return;
50
}
51
mActor->MaybeSendDelete();
52
}
53
54
void ServiceWorkerRegistrationProxy::UpdateStateOnBGThread(
55
const ServiceWorkerRegistrationDescriptor& aDescriptor) {
56
AssertIsOnBackgroundThread();
57
if (!mActor) {
58
return;
59
}
60
Unused << mActor->SendUpdateState(aDescriptor.ToIPC());
61
}
62
63
void ServiceWorkerRegistrationProxy::FireUpdateFoundOnBGThread() {
64
AssertIsOnBackgroundThread();
65
if (!mActor) {
66
return;
67
}
68
Unused << mActor->SendFireUpdateFound();
69
}
70
71
void ServiceWorkerRegistrationProxy::InitOnMainThread() {
72
AssertIsOnMainThread();
73
74
auto scopeExit = MakeScopeExit([&] { MaybeShutdownOnMainThread(); });
75
76
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
77
NS_ENSURE_TRUE_VOID(swm);
78
79
RefPtr<ServiceWorkerRegistrationInfo> reg =
80
swm->GetRegistration(mDescriptor.PrincipalInfo(), mDescriptor.Scope());
81
NS_ENSURE_TRUE_VOID(reg);
82
83
scopeExit.release();
84
85
mReg = new nsMainThreadPtrHolder<ServiceWorkerRegistrationInfo>(
86
"ServiceWorkerRegistrationProxy::mInfo", reg);
87
88
mReg->AddInstance(this, mDescriptor);
89
}
90
91
void ServiceWorkerRegistrationProxy::MaybeShutdownOnMainThread() {
92
AssertIsOnMainThread();
93
94
if (mDelayedUpdate) {
95
mDelayedUpdate->Reject();
96
mDelayedUpdate = nullptr;
97
}
98
nsCOMPtr<nsIRunnable> r = NewRunnableMethod(
99
__func__, this, &ServiceWorkerRegistrationProxy::MaybeShutdownOnBGThread);
100
101
MOZ_ALWAYS_SUCCEEDS(mEventTarget->Dispatch(r.forget(), NS_DISPATCH_NORMAL));
102
}
103
104
void ServiceWorkerRegistrationProxy::StopListeningOnMainThread() {
105
AssertIsOnMainThread();
106
107
if (!mReg) {
108
return;
109
}
110
111
mReg->RemoveInstance(this);
112
mReg = nullptr;
113
}
114
115
void ServiceWorkerRegistrationProxy::UpdateState(
116
const ServiceWorkerRegistrationDescriptor& aDescriptor) {
117
AssertIsOnMainThread();
118
119
if (mDescriptor == aDescriptor) {
120
return;
121
}
122
mDescriptor = aDescriptor;
123
124
nsCOMPtr<nsIRunnable> r =
125
NewRunnableMethod<ServiceWorkerRegistrationDescriptor>(
126
__func__, this,
127
&ServiceWorkerRegistrationProxy::UpdateStateOnBGThread, aDescriptor);
128
129
MOZ_ALWAYS_SUCCEEDS(mEventTarget->Dispatch(r.forget(), NS_DISPATCH_NORMAL));
130
}
131
132
void ServiceWorkerRegistrationProxy::FireUpdateFound() {
133
AssertIsOnMainThread();
134
135
nsCOMPtr<nsIRunnable> r = NewRunnableMethod(
136
__func__, this,
137
&ServiceWorkerRegistrationProxy::FireUpdateFoundOnBGThread);
138
139
MOZ_ALWAYS_SUCCEEDS(mEventTarget->Dispatch(r.forget(), NS_DISPATCH_NORMAL));
140
}
141
142
void ServiceWorkerRegistrationProxy::RegistrationCleared() {
143
MaybeShutdownOnMainThread();
144
}
145
146
void ServiceWorkerRegistrationProxy::GetScope(nsAString& aScope) const {
147
CopyUTF8toUTF16(mDescriptor.Scope(), aScope);
148
}
149
150
bool ServiceWorkerRegistrationProxy::MatchesDescriptor(
151
const ServiceWorkerRegistrationDescriptor& aDescriptor) {
152
AssertIsOnMainThread();
153
return aDescriptor.Id() == mDescriptor.Id() &&
154
aDescriptor.PrincipalInfo() == mDescriptor.PrincipalInfo() &&
155
aDescriptor.Scope() == mDescriptor.Scope();
156
}
157
158
ServiceWorkerRegistrationProxy::ServiceWorkerRegistrationProxy(
159
const ServiceWorkerRegistrationDescriptor& aDescriptor)
160
: mActor(nullptr),
161
mEventTarget(GetCurrentThreadSerialEventTarget()),
162
mDescriptor(aDescriptor) {}
163
164
void ServiceWorkerRegistrationProxy::Init(
165
ServiceWorkerRegistrationParent* aActor) {
166
AssertIsOnBackgroundThread();
167
MOZ_DIAGNOSTIC_ASSERT(aActor);
168
MOZ_DIAGNOSTIC_ASSERT(!mActor);
169
MOZ_DIAGNOSTIC_ASSERT(mEventTarget);
170
171
mActor = aActor;
172
173
// Note, this must be done from a separate Init() method and not in
174
// the constructor. If done from the constructor the runnable can
175
// execute, complete, and release its reference before the constructor
176
// returns.
177
nsCOMPtr<nsIRunnable> r =
178
NewRunnableMethod("ServiceWorkerRegistrationProxy::Init", this,
179
&ServiceWorkerRegistrationProxy::InitOnMainThread);
180
MOZ_ALWAYS_SUCCEEDS(SystemGroup::Dispatch(TaskCategory::Other, r.forget()));
181
}
182
183
void ServiceWorkerRegistrationProxy::RevokeActor(
184
ServiceWorkerRegistrationParent* aActor) {
185
AssertIsOnBackgroundThread();
186
MOZ_DIAGNOSTIC_ASSERT(mActor);
187
MOZ_DIAGNOSTIC_ASSERT(mActor == aActor);
188
mActor = nullptr;
189
190
nsCOMPtr<nsIRunnable> r = NewRunnableMethod(
191
__func__, this,
192
&ServiceWorkerRegistrationProxy::StopListeningOnMainThread);
193
MOZ_ALWAYS_SUCCEEDS(SystemGroup::Dispatch(TaskCategory::Other, r.forget()));
194
}
195
196
RefPtr<GenericPromise> ServiceWorkerRegistrationProxy::Unregister() {
197
AssertIsOnBackgroundThread();
198
199
RefPtr<ServiceWorkerRegistrationProxy> self = this;
200
RefPtr<GenericPromise::Private> promise =
201
new GenericPromise::Private(__func__);
202
203
nsCOMPtr<nsIRunnable> r =
204
NS_NewRunnableFunction(__func__, [self, promise]() mutable {
205
nsresult rv = NS_ERROR_DOM_INVALID_STATE_ERR;
206
auto scopeExit = MakeScopeExit([&] { promise->Reject(rv, __func__); });
207
208
NS_ENSURE_TRUE_VOID(self->mReg);
209
210
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
211
NS_ENSURE_TRUE_VOID(swm);
212
213
RefPtr<UnregisterCallback> cb = new UnregisterCallback(promise);
214
215
rv = swm->Unregister(self->mReg->Principal(), cb,
216
NS_ConvertUTF8toUTF16(self->mReg->Scope()));
217
NS_ENSURE_SUCCESS_VOID(rv);
218
219
scopeExit.release();
220
});
221
222
MOZ_ALWAYS_SUCCEEDS(SystemGroup::Dispatch(TaskCategory::Other, r.forget()));
223
224
return promise.forget();
225
}
226
227
namespace {
228
229
class UpdateCallback final : public ServiceWorkerUpdateFinishCallback {
230
RefPtr<ServiceWorkerRegistrationPromise::Private> mPromise;
231
232
~UpdateCallback() = default;
233
234
public:
235
explicit UpdateCallback(
236
RefPtr<ServiceWorkerRegistrationPromise::Private>&& aPromise)
237
: mPromise(std::move(aPromise)) {
238
MOZ_DIAGNOSTIC_ASSERT(mPromise);
239
}
240
241
void UpdateSucceeded(ServiceWorkerRegistrationInfo* aInfo) override {
242
mPromise->Resolve(aInfo->Descriptor(), __func__);
243
}
244
245
void UpdateFailed(ErrorResult& aResult) override {
246
mPromise->Reject(CopyableErrorResult(aResult), __func__);
247
}
248
};
249
250
} // anonymous namespace
251
252
NS_IMPL_ISUPPORTS(ServiceWorkerRegistrationProxy::DelayedUpdate,
253
nsITimerCallback)
254
255
ServiceWorkerRegistrationProxy::DelayedUpdate::DelayedUpdate(
256
RefPtr<ServiceWorkerRegistrationProxy>&& aProxy,
257
RefPtr<ServiceWorkerRegistrationPromise::Private>&& aPromise,
258
uint32_t delay)
259
: mProxy(std::move(aProxy)), mPromise(std::move(aPromise)) {
260
MOZ_DIAGNOSTIC_ASSERT(mProxy);
261
MOZ_DIAGNOSTIC_ASSERT(mPromise);
262
mProxy->mDelayedUpdate = this;
263
Result<nsCOMPtr<nsITimer>, nsresult> result =
264
NS_NewTimerWithCallback(this, delay, nsITimer::TYPE_ONE_SHOT,
265
SystemGroup::EventTargetFor(TaskCategory::Other));
266
mTimer = result.unwrapOr(nullptr);
267
MOZ_DIAGNOSTIC_ASSERT(mTimer);
268
}
269
270
void ServiceWorkerRegistrationProxy::DelayedUpdate::ChainTo(
271
RefPtr<ServiceWorkerRegistrationPromise::Private> aPromise) {
272
AssertIsOnMainThread();
273
MOZ_ASSERT(mProxy->mDelayedUpdate == this);
274
MOZ_ASSERT(mPromise);
275
276
mPromise->ChainTo(aPromise.forget(), __func__);
277
}
278
279
void ServiceWorkerRegistrationProxy::DelayedUpdate::Reject() {
280
MOZ_DIAGNOSTIC_ASSERT(mPromise);
281
if (mTimer) {
282
mTimer->Cancel();
283
mTimer = nullptr;
284
}
285
mPromise->Reject(NS_ERROR_DOM_INVALID_STATE_ERR, __func__);
286
}
287
288
NS_IMETHODIMP
289
ServiceWorkerRegistrationProxy::DelayedUpdate::Notify(nsITimer* aTimer) {
290
// Already shutting down.
291
if (mProxy->mDelayedUpdate != this) {
292
return NS_OK;
293
}
294
295
auto scopeExit = MakeScopeExit(
296
[&] { mPromise->Reject(NS_ERROR_DOM_INVALID_STATE_ERR, __func__); });
297
298
NS_ENSURE_TRUE(mProxy->mReg, NS_ERROR_FAILURE);
299
300
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
301
NS_ENSURE_TRUE(swm, NS_ERROR_FAILURE);
302
303
RefPtr<UpdateCallback> cb = new UpdateCallback(std::move(mPromise));
304
swm->Update(mProxy->mReg->Principal(), mProxy->mReg->Scope(), cb);
305
306
mTimer = nullptr;
307
mProxy->mDelayedUpdate = nullptr;
308
309
scopeExit.release();
310
return NS_OK;
311
}
312
313
RefPtr<ServiceWorkerRegistrationPromise>
314
ServiceWorkerRegistrationProxy::Update() {
315
AssertIsOnBackgroundThread();
316
317
RefPtr<ServiceWorkerRegistrationProxy> self = this;
318
RefPtr<ServiceWorkerRegistrationPromise::Private> promise =
319
new ServiceWorkerRegistrationPromise::Private(__func__);
320
321
nsCOMPtr<nsIRunnable> r =
322
NS_NewRunnableFunction(__func__, [self, promise]() mutable {
323
auto scopeExit = MakeScopeExit(
324
[&] { promise->Reject(NS_ERROR_DOM_INVALID_STATE_ERR, __func__); });
325
326
// Get the delay value for the update
327
NS_ENSURE_TRUE_VOID(self->mReg);
328
uint32_t delay = self->mReg->GetUpdateDelay(false);
329
330
// If the delay value does not equal to 0, create a timer and a timer
331
// callback to perform the delayed update. Otherwise, update directly.
332
if (delay) {
333
if (self->mDelayedUpdate) {
334
// NOTE: if we `ChainTo(),` there will ultimately be a single
335
// update, and this update will resolve all promises that were
336
// issued while the update's timer was ticking down.
337
self->mDelayedUpdate->ChainTo(std::move(promise));
338
} else {
339
RefPtr<ServiceWorkerRegistrationProxy::DelayedUpdate> du =
340
new ServiceWorkerRegistrationProxy::DelayedUpdate(
341
std::move(self), std::move(promise), delay);
342
}
343
} else {
344
RefPtr<ServiceWorkerManager> swm =
345
ServiceWorkerManager::GetInstance();
346
NS_ENSURE_TRUE_VOID(swm);
347
348
RefPtr<UpdateCallback> cb = new UpdateCallback(std::move(promise));
349
swm->Update(self->mReg->Principal(), self->mReg->Scope(), cb);
350
}
351
scopeExit.release();
352
});
353
354
MOZ_ALWAYS_SUCCEEDS(SystemGroup::Dispatch(TaskCategory::Other, r.forget()));
355
356
return promise;
357
}
358
359
} // namespace dom
360
} // namespace mozilla