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 "ServiceWorkerOp.h"
8
9
#include <utility>
10
11
#include "jsapi.h"
12
13
#include "nsCOMPtr.h"
14
#include "nsContentUtils.h"
15
#include "nsDebug.h"
16
#include "nsError.h"
17
#include "nsINamed.h"
18
#include "nsIPushErrorReporter.h"
19
#include "nsISupportsImpl.h"
20
#include "nsITimer.h"
21
#include "nsProxyRelease.h"
22
#include "nsServiceManagerUtils.h"
23
#include "nsTArray.h"
24
#include "nsThreadUtils.h"
25
26
#include "ServiceWorkerCloneData.h"
27
#include "mozilla/Assertions.h"
28
#include "mozilla/CycleCollectedJSContext.h"
29
#include "mozilla/DebugOnly.h"
30
#include "mozilla/ErrorResult.h"
31
#include "mozilla/OwningNonNull.h"
32
#include "mozilla/ScopeExit.h"
33
#include "mozilla/SystemGroup.h"
34
#include "mozilla/Telemetry.h"
35
#include "mozilla/Unused.h"
36
#include "mozilla/dom/BindingDeclarations.h"
37
#include "mozilla/dom/Client.h"
38
#include "mozilla/dom/ExtendableMessageEventBinding.h"
39
#include "mozilla/dom/FetchEventBinding.h"
40
#include "mozilla/dom/FetchEventOpProxyChild.h"
41
#include "mozilla/dom/InternalHeaders.h"
42
#include "mozilla/dom/InternalRequest.h"
43
#include "mozilla/dom/InternalResponse.h"
44
#include "mozilla/dom/Notification.h"
45
#include "mozilla/dom/PushEventBinding.h"
46
#include "mozilla/dom/RemoteWorkerChild.h"
47
#include "mozilla/dom/RemoteWorkerService.h"
48
#include "mozilla/dom/Request.h"
49
#include "mozilla/dom/Response.h"
50
#include "mozilla/dom/RootedDictionary.h"
51
#include "mozilla/dom/ServiceWorkerBinding.h"
52
#include "mozilla/dom/WorkerCommon.h"
53
#include "mozilla/dom/WorkerRef.h"
54
#include "mozilla/ipc/IPCStreamUtils.h"
55
56
namespace mozilla {
57
namespace dom {
58
59
namespace {
60
61
class ExtendableEventKeepAliveHandler final
62
: public ExtendableEvent::ExtensionsHandler,
63
public PromiseNativeHandler {
64
public:
65
NS_DECL_ISUPPORTS
66
67
static RefPtr<ExtendableEventKeepAliveHandler> Create(
68
RefPtr<ExtendableEventCallback> aCallback) {
69
MOZ_ASSERT(IsCurrentThreadRunningWorker());
70
71
RefPtr<ExtendableEventKeepAliveHandler> self =
72
new ExtendableEventKeepAliveHandler(std::move(aCallback));
73
74
self->mWorkerRef = StrongWorkerRef::Create(
75
GetCurrentThreadWorkerPrivate(), "ExtendableEventKeepAliveHandler",
76
[self]() { self->Cleanup(); });
77
78
if (NS_WARN_IF(!self->mWorkerRef)) {
79
return nullptr;
80
}
81
82
return self;
83
}
84
85
/**
86
* ExtendableEvent::ExtensionsHandler interface
87
*/
88
bool WaitOnPromise(Promise& aPromise) override {
89
if (!mAcceptingPromises) {
90
MOZ_ASSERT(!mSelfRef, "We shouldn't be holding a self reference!");
91
return false;
92
}
93
94
if (!mSelfRef) {
95
MOZ_ASSERT(!mPendingPromisesCount);
96
mSelfRef = this;
97
}
98
99
++mPendingPromisesCount;
100
aPromise.AppendNativeHandler(this);
101
102
return true;
103
}
104
105
/**
106
* PromiseNativeHandler interface
107
*/
108
void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override {
109
RemovePromise(Resolved);
110
}
111
112
void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override {
113
RemovePromise(Rejected);
114
}
115
116
void MaybeDone() {
117
MOZ_ASSERT(IsCurrentThreadRunningWorker());
118
119
if (mPendingPromisesCount) {
120
return;
121
}
122
123
if (mCallback) {
124
mCallback->FinishedWithResult(mRejected ? Rejected : Resolved);
125
}
126
127
Cleanup();
128
}
129
130
private:
131
/**
132
* This class is useful for the case where pending microtasks will continue
133
* extending the event, which means that the event is not "done." For example:
134
*
135
* // `e` is an ExtendableEvent, `p` is a Promise
136
* e.waitUntil(p);
137
* p.then(() => e.waitUntil(otherPromise));
138
*/
139
class MaybeDoneRunner : public MicroTaskRunnable {
140
public:
141
explicit MaybeDoneRunner(RefPtr<ExtendableEventKeepAliveHandler> aHandler)
142
: mHandler(std::move(aHandler)) {}
143
144
void Run(AutoSlowOperation& /* unused */) override {
145
mHandler->MaybeDone();
146
}
147
148
private:
149
RefPtr<ExtendableEventKeepAliveHandler> mHandler;
150
};
151
152
explicit ExtendableEventKeepAliveHandler(
153
RefPtr<ExtendableEventCallback> aCallback)
154
: mCallback(std::move(aCallback)) {}
155
156
~ExtendableEventKeepAliveHandler() { Cleanup(); }
157
158
void Cleanup() {
159
MOZ_ASSERT(IsCurrentThreadRunningWorker());
160
161
mSelfRef = nullptr;
162
mWorkerRef = nullptr;
163
mCallback = nullptr;
164
mAcceptingPromises = false;
165
}
166
167
void RemovePromise(ExtendableEventResult aResult) {
168
MOZ_ASSERT(IsCurrentThreadRunningWorker());
169
MOZ_DIAGNOSTIC_ASSERT(mPendingPromisesCount > 0);
170
171
// NOTE: mSelfRef can be nullptr here if MaybeCleanup() was just called
172
// before a promise settled. This can happen, for example, if the worker
173
// thread is being terminated for running too long, browser shutdown, etc.
174
175
mRejected |= (aResult == Rejected);
176
177
--mPendingPromisesCount;
178
if (mPendingPromisesCount) {
179
return;
180
}
181
182
CycleCollectedJSContext* cx = CycleCollectedJSContext::Get();
183
MOZ_ASSERT(cx);
184
185
RefPtr<MaybeDoneRunner> r = new MaybeDoneRunner(this);
186
cx->DispatchToMicroTask(r.forget());
187
}
188
189
/**
190
* We start holding a self reference when the first extension promise is
191
* added, and this reference is released when the last promise settles or
192
* when the worker is shutting down.
193
*
194
* This is needed in the case that we're waiting indefinitely on a to-be-GC'ed
195
* promise that's no longer reachable and will never be settled.
196
*/
197
RefPtr<ExtendableEventKeepAliveHandler> mSelfRef;
198
199
RefPtr<StrongWorkerRef> mWorkerRef;
200
201
RefPtr<ExtendableEventCallback> mCallback;
202
203
uint32_t mPendingPromisesCount = 0;
204
205
bool mRejected = false;
206
bool mAcceptingPromises = true;
207
};
208
209
NS_IMPL_ISUPPORTS0(ExtendableEventKeepAliveHandler)
210
211
nsresult DispatchExtendableEventOnWorkerScope(
212
JSContext* aCx, WorkerGlobalScope* aWorkerScope, ExtendableEvent* aEvent,
213
RefPtr<ExtendableEventCallback> aCallback) {
214
MOZ_ASSERT(aCx);
215
MOZ_ASSERT(aWorkerScope);
216
MOZ_ASSERT(aEvent);
217
218
nsCOMPtr<nsIGlobalObject> globalObject = aWorkerScope;
219
WidgetEvent* internalEvent = aEvent->WidgetEventPtr();
220
221
RefPtr<ExtendableEventKeepAliveHandler> keepAliveHandler =
222
ExtendableEventKeepAliveHandler::Create(std::move(aCallback));
223
if (NS_WARN_IF(!keepAliveHandler)) {
224
return NS_ERROR_FAILURE;
225
}
226
227
// This must be always set *before* dispatching the event, otherwise
228
// waitUntil() calls will fail.
229
aEvent->SetKeepAliveHandler(keepAliveHandler);
230
231
ErrorResult result;
232
aWorkerScope->DispatchEvent(*aEvent, result);
233
if (NS_WARN_IF(result.Failed())) {
234
result.SuppressException();
235
return NS_ERROR_FAILURE;
236
}
237
238
keepAliveHandler->MaybeDone();
239
240
// We don't block the event when getting an exception but still report the
241
// error message. NOTE: this will not stop the event.
242
if (internalEvent->mFlags.mExceptionWasRaised) {
243
return NS_ERROR_XPC_JS_THREW_EXCEPTION;
244
}
245
246
return NS_OK;
247
}
248
249
bool DispatchFailed(nsresult aStatus) {
250
return NS_FAILED(aStatus) && aStatus != NS_ERROR_XPC_JS_THREW_EXCEPTION;
251
}
252
253
} // anonymous namespace
254
255
class ServiceWorkerOp::ServiceWorkerOpRunnable : public WorkerDebuggeeRunnable {
256
public:
257
NS_DECL_ISUPPORTS_INHERITED
258
259
ServiceWorkerOpRunnable(RefPtr<ServiceWorkerOp> aOwner,
260
WorkerPrivate* aWorkerPrivate)
261
: WorkerDebuggeeRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount),
262
mOwner(std::move(aOwner)) {
263
AssertIsOnMainThread();
264
MOZ_ASSERT(mOwner);
265
MOZ_ASSERT(aWorkerPrivate);
266
}
267
268
private:
269
~ServiceWorkerOpRunnable() = default;
270
271
bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
272
MOZ_ASSERT(aWorkerPrivate);
273
aWorkerPrivate->AssertIsOnWorkerThread();
274
MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
275
MOZ_ASSERT(mOwner);
276
277
bool rv = mOwner->Exec(aCx, aWorkerPrivate);
278
Unused << NS_WARN_IF(!rv);
279
mOwner = nullptr;
280
281
return rv;
282
}
283
284
nsresult Cancel() override {
285
MOZ_ASSERT(mOwner);
286
287
mOwner->RejectAll(NS_ERROR_DOM_ABORT_ERR);
288
mOwner = nullptr;
289
290
return WorkerRunnable::Cancel();
291
}
292
293
RefPtr<ServiceWorkerOp> mOwner;
294
};
295
296
NS_IMPL_ISUPPORTS_INHERITED0(ServiceWorkerOp::ServiceWorkerOpRunnable,
297
WorkerRunnable)
298
299
bool ServiceWorkerOp::MaybeStart(RemoteWorkerChild* aOwner,
300
RemoteWorkerChild::State& aState) {
301
MOZ_ASSERT(!mStarted);
302
MOZ_ASSERT(aOwner);
303
MOZ_ASSERT(aOwner->GetOwningEventTarget()->IsOnCurrentThread());
304
305
MOZ_ACCESS_THREAD_BOUND(aOwner->mLauncherData, launcherData);
306
307
if (NS_WARN_IF(!launcherData->mIPCActive)) {
308
RejectAll(NS_ERROR_DOM_ABORT_ERR);
309
mStarted = true;
310
return true;
311
}
312
313
// Allow termination to happen while the Service Worker is initializing.
314
if (aState.is<Pending>() && !IsTerminationOp()) {
315
return false;
316
}
317
318
if (NS_WARN_IF(aState.is<RemoteWorkerChild::PendingTerminated>()) ||
319
NS_WARN_IF(aState.is<RemoteWorkerChild::Terminated>())) {
320
RejectAll(NS_ERROR_DOM_INVALID_STATE_ERR);
321
mStarted = true;
322
return true;
323
}
324
325
MOZ_ASSERT(aState.is<RemoteWorkerChild::Running>() || IsTerminationOp());
326
327
RefPtr<ServiceWorkerOp> self = this;
328
329
if (IsTerminationOp()) {
330
aOwner->GetTerminationPromise()->Then(
331
GetCurrentThreadSerialEventTarget(), __func__,
332
[self](
333
const GenericNonExclusivePromise::ResolveOrRejectValue& aResult) {
334
MOZ_ASSERT(!self->mPromiseHolder.IsEmpty());
335
336
if (NS_WARN_IF(aResult.IsReject())) {
337
self->mPromiseHolder.Reject(aResult.RejectValue(), __func__);
338
return;
339
}
340
341
self->mPromiseHolder.Resolve(NS_OK, __func__);
342
});
343
}
344
345
RefPtr<RemoteWorkerChild> owner = aOwner;
346
347
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
348
__func__, [self = std::move(self), owner = std::move(owner)]() mutable {
349
auto lock = owner->mState.Lock();
350
auto& state = lock.ref();
351
352
if (NS_WARN_IF(!state.is<Running>() && !self->IsTerminationOp())) {
353
self->RejectAll(NS_ERROR_DOM_INVALID_STATE_ERR);
354
return;
355
}
356
357
if (self->IsTerminationOp()) {
358
owner->CloseWorkerOnMainThread(state);
359
} else {
360
MOZ_ASSERT(state.is<Running>());
361
362
RefPtr<WorkerRunnable> workerRunnable =
363
self->GetRunnable(state.as<Running>().mWorkerPrivate);
364
365
if (NS_WARN_IF(!workerRunnable->Dispatch())) {
366
self->RejectAll(NS_ERROR_FAILURE);
367
}
368
}
369
370
nsCOMPtr<nsIEventTarget> target = owner->GetOwningEventTarget();
371
NS_ProxyRelease(__func__, target, owner.forget());
372
});
373
374
mStarted = true;
375
376
MOZ_ALWAYS_SUCCEEDS(SystemGroup::Dispatch(TaskCategory::Other, r.forget()));
377
378
return true;
379
}
380
381
void ServiceWorkerOp::Cancel() { RejectAll(NS_ERROR_DOM_ABORT_ERR); }
382
383
ServiceWorkerOp::ServiceWorkerOp(
384
const ServiceWorkerOpArgs& aArgs,
385
std::function<void(const ServiceWorkerOpResult&)>&& aCallback)
386
: mArgs(aArgs) {
387
MOZ_ASSERT(RemoteWorkerService::Thread()->IsOnCurrentThread());
388
389
RefPtr<ServiceWorkerOpPromise> promise = mPromiseHolder.Ensure(__func__);
390
391
promise->Then(
392
GetCurrentThreadSerialEventTarget(), __func__,
393
[callback = std::move(aCallback)](
394
ServiceWorkerOpPromise::ResolveOrRejectValue&& aResult) mutable {
395
if (NS_WARN_IF(aResult.IsReject())) {
396
MOZ_ASSERT(NS_FAILED(aResult.RejectValue()));
397
callback(aResult.RejectValue());
398
return;
399
}
400
401
callback(aResult.ResolveValue());
402
});
403
}
404
405
ServiceWorkerOp::~ServiceWorkerOp() {
406
Unused << NS_WARN_IF(!mPromiseHolder.IsEmpty());
407
mPromiseHolder.RejectIfExists(NS_ERROR_DOM_ABORT_ERR, __func__);
408
}
409
410
bool ServiceWorkerOp::Started() const {
411
MOZ_ASSERT(RemoteWorkerService::Thread()->IsOnCurrentThread());
412
413
return mStarted;
414
}
415
416
bool ServiceWorkerOp::IsTerminationOp() const {
417
return mArgs.type() ==
418
ServiceWorkerOpArgs::TServiceWorkerTerminateWorkerOpArgs;
419
}
420
421
RefPtr<WorkerRunnable> ServiceWorkerOp::GetRunnable(
422
WorkerPrivate* aWorkerPrivate) {
423
AssertIsOnMainThread();
424
MOZ_ASSERT(aWorkerPrivate);
425
426
return new ServiceWorkerOpRunnable(this, aWorkerPrivate);
427
}
428
429
void ServiceWorkerOp::RejectAll(nsresult aStatus) {
430
MOZ_ASSERT(!mPromiseHolder.IsEmpty());
431
mPromiseHolder.Reject(aStatus, __func__);
432
}
433
434
class CheckScriptEvaluationOp final : public ServiceWorkerOp {
435
using ServiceWorkerOp::ServiceWorkerOp;
436
437
public:
438
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CheckScriptEvaluationOp, override)
439
440
private:
441
~CheckScriptEvaluationOp() = default;
442
443
bool Exec(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
444
MOZ_ASSERT(aWorkerPrivate);
445
aWorkerPrivate->AssertIsOnWorkerThread();
446
MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
447
MOZ_ASSERT(!mPromiseHolder.IsEmpty());
448
449
ServiceWorkerCheckScriptEvaluationOpResult result;
450
result.workerScriptExecutedSuccessfully() =
451
aWorkerPrivate->WorkerScriptExecutedSuccessfully();
452
result.fetchHandlerWasAdded() = aWorkerPrivate->FetchHandlerWasAdded();
453
454
mPromiseHolder.Resolve(result, __func__);
455
456
return true;
457
}
458
};
459
460
class TerminateServiceWorkerOp final : public ServiceWorkerOp {
461
using ServiceWorkerOp::ServiceWorkerOp;
462
463
public:
464
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TerminateServiceWorkerOp, override)
465
466
private:
467
~TerminateServiceWorkerOp() = default;
468
469
bool Exec(JSContext*, WorkerPrivate*) override {
470
MOZ_ASSERT_UNREACHABLE(
471
"Worker termination should be handled in "
472
"`ServiceWorkerOp::MaybeStart()`");
473
474
return false;
475
}
476
};
477
478
class UpdateServiceWorkerStateOp final : public ServiceWorkerOp {
479
using ServiceWorkerOp::ServiceWorkerOp;
480
481
public:
482
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(UpdateServiceWorkerStateOp, override);
483
484
private:
485
class UpdateStateOpRunnable final : public MainThreadWorkerControlRunnable {
486
public:
487
NS_DECL_ISUPPORTS_INHERITED
488
489
UpdateStateOpRunnable(RefPtr<UpdateServiceWorkerStateOp> aOwner,
490
WorkerPrivate* aWorkerPrivate)
491
: MainThreadWorkerControlRunnable(aWorkerPrivate),
492
mOwner(std::move(aOwner)) {
493
AssertIsOnMainThread();
494
MOZ_ASSERT(mOwner);
495
MOZ_ASSERT(aWorkerPrivate);
496
}
497
498
private:
499
~UpdateStateOpRunnable() = default;
500
501
bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
502
MOZ_ASSERT(aWorkerPrivate);
503
aWorkerPrivate->AssertIsOnWorkerThread();
504
MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
505
MOZ_ASSERT(mOwner);
506
507
Unused << mOwner->Exec(aCx, aWorkerPrivate);
508
mOwner = nullptr;
509
510
return true;
511
}
512
513
nsresult Cancel() override {
514
MOZ_ASSERT(mOwner);
515
516
mOwner->RejectAll(NS_ERROR_DOM_ABORT_ERR);
517
mOwner = nullptr;
518
519
return MainThreadWorkerControlRunnable::Cancel();
520
}
521
522
RefPtr<UpdateServiceWorkerStateOp> mOwner;
523
};
524
525
~UpdateServiceWorkerStateOp() = default;
526
527
RefPtr<WorkerRunnable> GetRunnable(WorkerPrivate* aWorkerPrivate) override {
528
AssertIsOnMainThread();
529
MOZ_ASSERT(aWorkerPrivate);
530
MOZ_ASSERT(mArgs.type() ==
531
ServiceWorkerOpArgs::TServiceWorkerUpdateStateOpArgs);
532
533
return new UpdateStateOpRunnable(this, aWorkerPrivate);
534
}
535
536
bool Exec(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
537
MOZ_ASSERT(aWorkerPrivate);
538
aWorkerPrivate->AssertIsOnWorkerThread();
539
MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
540
MOZ_ASSERT(!mPromiseHolder.IsEmpty());
541
542
ServiceWorkerState state =
543
mArgs.get_ServiceWorkerUpdateStateOpArgs().state();
544
aWorkerPrivate->UpdateServiceWorkerState(state);
545
546
mPromiseHolder.Resolve(NS_OK, __func__);
547
548
return true;
549
}
550
};
551
552
NS_IMPL_ISUPPORTS_INHERITED0(UpdateServiceWorkerStateOp::UpdateStateOpRunnable,
553
MainThreadWorkerControlRunnable)
554
555
void ExtendableEventOp::FinishedWithResult(ExtendableEventResult aResult) {
556
MOZ_ASSERT(IsCurrentThreadRunningWorker());
557
MOZ_ASSERT(!mPromiseHolder.IsEmpty());
558
559
mPromiseHolder.Resolve(aResult == Resolved ? NS_OK : NS_ERROR_FAILURE,
560
__func__);
561
}
562
563
class LifeCycleEventOp final : public ExtendableEventOp {
564
using ExtendableEventOp::ExtendableEventOp;
565
566
public:
567
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(LifeCycleEventOp, override)
568
569
private:
570
~LifeCycleEventOp() = default;
571
572
bool Exec(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
573
MOZ_ASSERT(aWorkerPrivate);
574
aWorkerPrivate->AssertIsOnWorkerThread();
575
MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
576
MOZ_ASSERT(!mPromiseHolder.IsEmpty());
577
578
RefPtr<ExtendableEvent> event;
579
RefPtr<EventTarget> target = aWorkerPrivate->GlobalScope();
580
581
const nsString& eventName =
582
mArgs.get_ServiceWorkerLifeCycleEventOpArgs().eventName();
583
584
if (eventName.EqualsASCII("install") || eventName.EqualsASCII("activate")) {
585
ExtendableEventInit init;
586
init.mBubbles = false;
587
init.mCancelable = false;
588
event = ExtendableEvent::Constructor(target, eventName, init);
589
} else {
590
MOZ_CRASH("Unexpected lifecycle event");
591
}
592
593
event->SetTrusted(true);
594
595
nsresult rv = DispatchExtendableEventOnWorkerScope(
596
aCx, aWorkerPrivate->GlobalScope(), event, this);
597
598
if (NS_WARN_IF(DispatchFailed(rv))) {
599
RejectAll(rv);
600
}
601
602
return DispatchFailed(rv);
603
}
604
};
605
606
/**
607
* PushEventOp
608
*/
609
class PushEventOp final : public ExtendableEventOp {
610
using ExtendableEventOp::ExtendableEventOp;
611
612
public:
613
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PushEventOp, override)
614
615
private:
616
~PushEventOp() = default;
617
618
bool Exec(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
619
MOZ_ASSERT(aWorkerPrivate);
620
aWorkerPrivate->AssertIsOnWorkerThread();
621
MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
622
MOZ_ASSERT(!mPromiseHolder.IsEmpty());
623
624
ErrorResult result;
625
626
auto scopeExit = MakeScopeExit([&] {
627
MOZ_ASSERT(result.Failed());
628
629
RejectAll(result.StealNSResult());
630
ReportError(aWorkerPrivate);
631
});
632
633
const ServiceWorkerPushEventOpArgs& args =
634
mArgs.get_ServiceWorkerPushEventOpArgs();
635
636
PushEventInit pushEventInit;
637
638
if (args.data().type() != OptionalPushData::Tvoid_t) {
639
auto& bytes = args.data().get_ArrayOfuint8_t();
640
JSObject* data =
641
Uint8Array::Create(aCx, bytes.Length(), bytes.Elements());
642
643
if (!data) {
644
result = ErrorResult(NS_ERROR_FAILURE);
645
return false;
646
}
647
648
DebugOnly<bool> inited =
649
pushEventInit.mData.Construct().SetAsArrayBufferView().Init(data);
650
MOZ_ASSERT(inited);
651
}
652
653
pushEventInit.mBubbles = false;
654
pushEventInit.mCancelable = false;
655
656
GlobalObject globalObj(aCx, aWorkerPrivate->GlobalScope()->GetWrapper());
657
RefPtr<PushEvent> pushEvent = PushEvent::Constructor(
658
globalObj, NS_LITERAL_STRING("push"), pushEventInit, result);
659
660
if (NS_WARN_IF(result.Failed())) {
661
return false;
662
}
663
664
pushEvent->SetTrusted(true);
665
666
scopeExit.release();
667
668
nsresult rv = DispatchExtendableEventOnWorkerScope(
669
aCx, aWorkerPrivate->GlobalScope(), pushEvent, this);
670
671
if (NS_FAILED(rv)) {
672
if (NS_WARN_IF(DispatchFailed(rv))) {
673
RejectAll(rv);
674
}
675
676
// We don't cancel WorkerPrivate when catching an exception.
677
ReportError(aWorkerPrivate,
678
nsIPushErrorReporter::DELIVERY_UNCAUGHT_EXCEPTION);
679
}
680
681
return DispatchFailed(rv);
682
}
683
684
void FinishedWithResult(ExtendableEventResult aResult) override {
685
MOZ_ASSERT(IsCurrentThreadRunningWorker());
686
687
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
688
689
if (aResult == Rejected) {
690
ReportError(workerPrivate,
691
nsIPushErrorReporter::DELIVERY_UNHANDLED_REJECTION);
692
}
693
694
ExtendableEventOp::FinishedWithResult(aResult);
695
}
696
697
void ReportError(
698
WorkerPrivate* aWorkerPrivate,
699
uint16_t aError = nsIPushErrorReporter::DELIVERY_INTERNAL_ERROR) {
700
MOZ_ASSERT(aWorkerPrivate);
701
aWorkerPrivate->AssertIsOnWorkerThread();
702
MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
703
704
if (NS_WARN_IF(aError > nsIPushErrorReporter::DELIVERY_INTERNAL_ERROR) ||
705
mArgs.get_ServiceWorkerPushEventOpArgs().messageId().IsEmpty()) {
706
return;
707
}
708
709
nsString messageId = mArgs.get_ServiceWorkerPushEventOpArgs().messageId();
710
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
711
__func__, [messageId = std::move(messageId), error = aError] {
712
nsCOMPtr<nsIPushErrorReporter> reporter =
713
do_GetService("@mozilla.org/push/Service;1");
714
715
if (reporter) {
716
nsresult rv = reporter->ReportDeliveryError(messageId, error);
717
Unused << NS_WARN_IF(NS_FAILED(rv));
718
}
719
});
720
721
MOZ_ALWAYS_SUCCEEDS(aWorkerPrivate->DispatchToMainThread(r.forget()));
722
}
723
};
724
725
class PushSubscriptionChangeEventOp final : public ExtendableEventOp {
726
using ExtendableEventOp::ExtendableEventOp;
727
728
public:
729
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PushSubscriptionChangeEventOp, override)
730
731
private:
732
~PushSubscriptionChangeEventOp() = default;
733
734
bool Exec(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
735
MOZ_ASSERT(aWorkerPrivate);
736
aWorkerPrivate->AssertIsOnWorkerThread();
737
MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
738
MOZ_ASSERT(!mPromiseHolder.IsEmpty());
739
740
RefPtr<EventTarget> target = aWorkerPrivate->GlobalScope();
741
742
ExtendableEventInit init;
743
init.mBubbles = false;
744
init.mCancelable = false;
745
746
RefPtr<ExtendableEvent> event = ExtendableEvent::Constructor(
747
target, NS_LITERAL_STRING("pushsubscriptionchange"), init);
748
event->SetTrusted(true);
749
750
nsresult rv = DispatchExtendableEventOnWorkerScope(
751
aCx, aWorkerPrivate->GlobalScope(), event, this);
752
753
if (NS_WARN_IF(DispatchFailed(rv))) {
754
RejectAll(rv);
755
}
756
757
return DispatchFailed(rv);
758
}
759
};
760
761
class NotificationEventOp : public ExtendableEventOp,
762
public nsITimerCallback,
763
public nsINamed {
764
using ExtendableEventOp::ExtendableEventOp;
765
766
public:
767
NS_DECL_THREADSAFE_ISUPPORTS
768
769
private:
770
~NotificationEventOp() {
771
MOZ_DIAGNOSTIC_ASSERT(!mTimer);
772
MOZ_DIAGNOSTIC_ASSERT(!mWorkerRef);
773
}
774
775
void ClearWindowAllowed(WorkerPrivate* aWorkerPrivate) {
776
MOZ_ASSERT(aWorkerPrivate);
777
aWorkerPrivate->AssertIsOnWorkerThread();
778
MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
779
780
if (!mTimer) {
781
return;
782
}
783
784
// This might be executed after the global was unrooted, in which case
785
// GlobalScope() will return null. Making the check here just to be safe.
786
WorkerGlobalScope* globalScope = aWorkerPrivate->GlobalScope();
787
if (!globalScope) {
788
return;
789
}
790
791
globalScope->ConsumeWindowInteraction();
792
mTimer->Cancel();
793
mTimer = nullptr;
794
795
mWorkerRef = nullptr;
796
}
797
798
void StartClearWindowTimer(WorkerPrivate* aWorkerPrivate) {
799
MOZ_ASSERT(aWorkerPrivate);
800
aWorkerPrivate->AssertIsOnWorkerThread();
801
MOZ_ASSERT(!mTimer);
802
803
nsresult rv;
804
nsCOMPtr<nsITimer> timer =
805
NS_NewTimer(aWorkerPrivate->ControlEventTarget());
806
if (NS_WARN_IF(!timer)) {
807
return;
808
}
809
810
MOZ_ASSERT(!mWorkerRef);
811
RefPtr<NotificationEventOp> self = this;
812
mWorkerRef = StrongWorkerRef::Create(
813
aWorkerPrivate, "NotificationEventOp", [self = std::move(self)] {
814
// We could try to hold the worker alive until the timer fires, but
815
// other APIs are not likely to work in this partially shutdown state.
816
// We might as well let the worker thread exit.
817
self->ClearWindowAllowed(self->mWorkerRef->Private());
818
});
819
820
if (!mWorkerRef) {
821
return;
822
}
823
824
aWorkerPrivate->GlobalScope()->AllowWindowInteraction();
825
timer.swap(mTimer);
826
827
// We swap first and then initialize the timer so that even if initializing
828
// fails, we still clean the busy count and interaction count correctly.
829
// The timer can't be initialized before modyfing the busy count since the
830
// timer thread could run and call the timeout but the worker may
831
// already be terminating and modifying the busy count could fail.
832
uint32_t delay = mArgs.get_ServiceWorkerNotificationEventOpArgs()
833
.disableOpenClickDelay();
834
rv = mTimer->InitWithCallback(this, delay, nsITimer::TYPE_ONE_SHOT);
835
836
if (NS_WARN_IF(NS_FAILED(rv))) {
837
ClearWindowAllowed(aWorkerPrivate);
838
return;
839
}
840
}
841
842
// ExtendableEventOp interface
843
bool Exec(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
844
MOZ_ASSERT(aWorkerPrivate);
845
aWorkerPrivate->AssertIsOnWorkerThread();
846
MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
847
MOZ_ASSERT(!mPromiseHolder.IsEmpty());
848
849
RefPtr<EventTarget> target = aWorkerPrivate->GlobalScope();
850
851
ServiceWorkerNotificationEventOpArgs& args =
852
mArgs.get_ServiceWorkerNotificationEventOpArgs();
853
854
ErrorResult result;
855
RefPtr<Notification> notification = Notification::ConstructFromFields(
856
aWorkerPrivate->GlobalScope(), args.id(), args.title(), args.dir(),
857
args.lang(), args.body(), args.tag(), args.icon(), args.data(),
858
args.scope(), result);
859
860
if (NS_WARN_IF(result.Failed())) {
861
return false;
862
}
863
864
NotificationEventInit init;
865
init.mNotification = notification;
866
init.mBubbles = false;
867
init.mCancelable = false;
868
869
RefPtr<NotificationEvent> notificationEvent =
870
NotificationEvent::Constructor(target, args.eventName(), init);
871
872
notificationEvent->SetTrusted(true);
873
874
if (args.eventName().EqualsLiteral("notificationclick")) {
875
StartClearWindowTimer(aWorkerPrivate);
876
}
877
878
nsresult rv = DispatchExtendableEventOnWorkerScope(
879
aCx, aWorkerPrivate->GlobalScope(), notificationEvent, this);
880
881
if (NS_WARN_IF(DispatchFailed(rv))) {
882
// This will reject mPromiseHolder.
883
FinishedWithResult(Rejected);
884
}
885
886
return DispatchFailed(rv);
887
}
888
889
void FinishedWithResult(ExtendableEventResult aResult) override {
890
MOZ_ASSERT(IsCurrentThreadRunningWorker());
891
892
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
893
MOZ_ASSERT(workerPrivate);
894
895
ClearWindowAllowed(workerPrivate);
896
897
ExtendableEventOp::FinishedWithResult(aResult);
898
}
899
900
// nsITimerCallback interface
901
NS_IMETHOD Notify(nsITimer* aTimer) override {
902
MOZ_DIAGNOSTIC_ASSERT(mTimer == aTimer);
903
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
904
ClearWindowAllowed(workerPrivate);
905
return NS_OK;
906
}
907
908
// nsINamed interface
909
NS_IMETHOD GetName(nsACString& aName) override {
910
aName.AssignLiteral("NotificationEventOp");
911
return NS_OK;
912
}
913
914
nsCOMPtr<nsITimer> mTimer;
915
RefPtr<StrongWorkerRef> mWorkerRef;
916
};
917
918
NS_IMPL_ISUPPORTS(NotificationEventOp, nsITimerCallback, nsINamed)
919
920
class MessageEventOp final : public ExtendableEventOp {
921
using ExtendableEventOp::ExtendableEventOp;
922
923
public:
924
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MessageEventOp, override)
925
926
MessageEventOp(const ServiceWorkerOpArgs& aArgs,
927
std::function<void(const ServiceWorkerOpResult&)>&& aCallback)
928
: ExtendableEventOp(aArgs, std::move(aCallback)),
929
mData(new ServiceWorkerCloneData()) {
930
mData->CopyFromClonedMessageDataForBackgroundChild(
931
mArgs.get_ServiceWorkerMessageEventOpArgs().clonedData());
932
}
933
934
private:
935
~MessageEventOp() = default;
936
937
bool Exec(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
938
MOZ_ASSERT(aWorkerPrivate);
939
aWorkerPrivate->AssertIsOnWorkerThread();
940
MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
941
MOZ_ASSERT(!mPromiseHolder.IsEmpty());
942
943
JS::Rooted<JS::Value> messageData(aCx);
944
nsCOMPtr<nsIGlobalObject> sgo = aWorkerPrivate->GlobalScope();
945
ErrorResult rv;
946
mData->Read(aCx, &messageData, rv);
947
948
// If deserialization fails, we will fire a messageerror event
949
bool deserializationFailed = rv.ErrorCodeIs(NS_ERROR_DOM_DATA_CLONE_ERR);
950
951
if (!deserializationFailed && NS_WARN_IF(rv.Failed())) {
952
RejectAll(rv.StealNSResult());
953
return true;
954
}
955
956
Sequence<OwningNonNull<MessagePort>> ports;
957
if (!mData->TakeTransferredPortsAsSequence(ports)) {
958
RejectAll(NS_ERROR_FAILURE);
959
rv.SuppressException();
960
return true;
961
}
962
963
RootedDictionary<ExtendableMessageEventInit> init(aCx);
964
965
init.mBubbles = false;
966
init.mCancelable = false;
967
968
// On a messageerror event, we disregard ports:
970
if (!deserializationFailed) {
971
init.mData = messageData;
972
init.mPorts = ports;
973
}
974
975
init.mSource.SetValue().SetAsClient() = new Client(
976
sgo, mArgs.get_ServiceWorkerMessageEventOpArgs().clientInfoAndState());
977
978
rv.SuppressException();
979
RefPtr<EventTarget> target = aWorkerPrivate->GlobalScope();
980
RefPtr<ExtendableMessageEvent> extendableEvent =
981
ExtendableMessageEvent::Constructor(
982
target,
983
deserializationFailed ? NS_LITERAL_STRING("messageerror")
984
: NS_LITERAL_STRING("message"),
985
init);
986
987
extendableEvent->SetTrusted(true);
988
989
nsresult rv2 = DispatchExtendableEventOnWorkerScope(
990
aCx, aWorkerPrivate->GlobalScope(), extendableEvent, this);
991
992
if (NS_WARN_IF(DispatchFailed(rv2))) {
993
RejectAll(rv2);
994
}
995
996
return DispatchFailed(rv2);
997
}
998
999
RefPtr<ServiceWorkerCloneData> mData;
1000
};
1001
1002
/**
1003
* Used for ScopeExit-style network request cancelation in
1004
* `ResolvedCallback()` (e.g. if `FetchEvent::RespondWith()` is resolved with
1005
* a non-JS object).
1006
*/
1007
class MOZ_STACK_CLASS FetchEventOp::AutoCancel {
1008
public:
1009
explicit AutoCancel(FetchEventOp* aOwner)
1010
: mOwner(aOwner),
1011
mLine(0),
1012
mColumn(0),
1013
mMessageName(NS_LITERAL_CSTRING("InterceptionFailedWithURL")) {
1014
MOZ_ASSERT(IsCurrentThreadRunningWorker());
1015
MOZ_ASSERT(mOwner);
1016
1017
nsAutoString requestURL;
1018
mOwner->GetRequestURL(requestURL);
1019
mParams.AppendElement(requestURL);
1020
}
1021
1022
~AutoCancel() {
1023
if (mOwner) {
1024
if (mSourceSpec.IsEmpty()) {
1025
mOwner->AsyncLog(mMessageName, std::move(mParams));
1026
} else {
1027
mOwner->AsyncLog(mSourceSpec, mLine, mColumn, mMessageName,
1028
std::move(mParams));
1029
}
1030
1031
MOZ_ASSERT(!mOwner->mRespondWithPromiseHolder.IsEmpty());
1032
mOwner->mRespondWithPromiseHolder.Reject(NS_ERROR_INTERCEPTION_FAILED,
1033
__func__);
1034
}
1035
}
1036
1037
// This function steals the error message from a ErrorResult.
1038
void SetCancelErrorResult(JSContext* aCx, ErrorResult& aRv) {
1039
MOZ_DIAGNOSTIC_ASSERT(aRv.Failed());
1040
MOZ_DIAGNOSTIC_ASSERT(!JS_IsExceptionPending(aCx));
1041
1042
// Storing the error as exception in the JSContext.
1043
if (!aRv.MaybeSetPendingException(aCx)) {
1044
return;
1045
}
1046
1047
MOZ_ASSERT(!aRv.Failed());
1048
1049
// Let's take the pending exception.
1050
JS::Rooted<JS::Value> exn(aCx);
1051
if (!JS_GetPendingException(aCx, &exn)) {
1052
return;
1053
}
1054
1055
JS_ClearPendingException(aCx);
1056
1057
// Converting the exception in a js::ErrorReport.
1058
js::ErrorReport report(aCx);
1059
if (!report.init(aCx, exn, js::ErrorReport::WithSideEffects)) {
1060
JS_ClearPendingException(aCx);
1061
return;
1062
}
1063
1064
MOZ_ASSERT(mOwner);
1065
MOZ_ASSERT(mMessageName.EqualsLiteral("InterceptionFailedWithURL"));
1066
MOZ_ASSERT(mParams.Length() == 1);
1067
1068
// Let's store the error message here.
1069
mMessageName.Assign(report.toStringResult().c_str());
1070
mParams.Clear();
1071
}
1072
1073
template <typename... Params>
1074
void SetCancelMessage(const nsACString& aMessageName, Params&&... aParams) {
1075
MOZ_ASSERT(mOwner);
1076
MOZ_ASSERT(mMessageName.EqualsLiteral("InterceptionFailedWithURL"));
1077
MOZ_ASSERT(mParams.Length() == 1);
1078
mMessageName = aMessageName;
1079
mParams.Clear();
1080
StringArrayAppender::Append(mParams, sizeof...(Params),
1081
std::forward<Params>(aParams)...);
1082
}
1083
1084
template <typename... Params>
1085
void SetCancelMessageAndLocation(const nsACString& aSourceSpec,
1086
uint32_t aLine, uint32_t aColumn,
1087
const nsACString& aMessageName,
1088
Params&&... aParams) {
1089
MOZ_ASSERT(mOwner);
1090
MOZ_ASSERT(mMessageName.EqualsLiteral("InterceptionFailedWithURL"));
1091
MOZ_ASSERT(mParams.Length() == 1);
1092
1093
mSourceSpec = aSourceSpec;
1094
mLine = aLine;
1095
mColumn = aColumn;
1096
1097
mMessageName = aMessageName;
1098
mParams.Clear();
1099
StringArrayAppender::Append(mParams, sizeof...(Params),
1100
std::forward<Params>(aParams)...);
1101
}
1102
1103
void Reset() { mOwner = nullptr; }
1104
1105
private:
1106
FetchEventOp* MOZ_NON_OWNING_REF mOwner;
1107
nsCString mSourceSpec;
1108
uint32_t mLine;
1109
uint32_t mColumn;
1110
nsCString mMessageName;
1111
nsTArray<nsString> mParams;
1112
};
1113
1114
NS_IMPL_ISUPPORTS0(FetchEventOp)
1115
1116
void FetchEventOp::SetActor(RefPtr<FetchEventOpProxyChild> aActor) {
1117
MOZ_ASSERT(RemoteWorkerService::Thread()->IsOnCurrentThread());
1118
MOZ_ASSERT(!Started());
1119
MOZ_ASSERT(!mActor);
1120
1121
mActor = std::move(aActor);
1122
}
1123
1124
void FetchEventOp::RevokeActor(FetchEventOpProxyChild* aActor) {
1125
MOZ_ASSERT(aActor);
1126
MOZ_ASSERT_IF(mActor, mActor == aActor);
1127
1128
mActor = nullptr;
1129
}
1130
1131
RefPtr<FetchEventRespondWithPromise> FetchEventOp::GetRespondWithPromise() {
1132
MOZ_ASSERT(RemoteWorkerService::Thread()->IsOnCurrentThread());
1133
MOZ_ASSERT(!Started());
1134
MOZ_ASSERT(mRespondWithPromiseHolder.IsEmpty());
1135
1136
return mRespondWithPromiseHolder.Ensure(__func__);
1137
}
1138
1139
void FetchEventOp::RespondWithCalledAt(const nsCString& aRespondWithScriptSpec,
1140
uint32_t aRespondWithLineNumber,
1141
uint32_t aRespondWithColumnNumber) {
1142
MOZ_ASSERT(IsCurrentThreadRunningWorker());
1143
MOZ_ASSERT(!mRespondWithClosure);
1144
1145
mRespondWithClosure.emplace(aRespondWithScriptSpec, aRespondWithLineNumber,
1146
aRespondWithColumnNumber);
1147
}
1148
1149
void FetchEventOp::ReportCanceled(const nsCString& aPreventDefaultScriptSpec,
1150
uint32_t aPreventDefaultLineNumber,
1151
uint32_t aPreventDefaultColumnNumber) {
1152
MOZ_ASSERT(IsCurrentThreadRunningWorker());
1153
MOZ_ASSERT(mActor);
1154
MOZ_ASSERT(!mPromiseHolder.IsEmpty());
1155
1156
nsString requestURL;
1157
GetRequestURL(requestURL);
1158
1159
AsyncLog(aPreventDefaultScriptSpec, aPreventDefaultLineNumber,
1160
aPreventDefaultColumnNumber,
1161
NS_LITERAL_CSTRING("InterceptionCanceledWithURL"),
1162
{std::move(requestURL)});
1163
}
1164
1165
FetchEventOp::~FetchEventOp() {
1166
mRespondWithPromiseHolder.RejectIfExists(NS_ERROR_DOM_ABORT_ERR, __func__);
1167
1168
if (mActor) {
1169
NS_ProxyRelease("FetchEventOp::mActor", RemoteWorkerService::Thread(),
1170
mActor.forget());
1171
}
1172
}
1173
1174
void FetchEventOp::RejectAll(nsresult aStatus) {
1175
MOZ_ASSERT(!mRespondWithPromiseHolder.IsEmpty());
1176
MOZ_ASSERT(!mPromiseHolder.IsEmpty());
1177
1178
mRespondWithPromiseHolder.Reject(aStatus, __func__);
1179
mPromiseHolder.Reject(aStatus, __func__);
1180
}
1181
1182
void FetchEventOp::FinishedWithResult(ExtendableEventResult aResult) {
1183
MOZ_ASSERT(IsCurrentThreadRunningWorker());
1184
MOZ_ASSERT(!mPromiseHolder.IsEmpty());
1185
MOZ_ASSERT(!mResult);
1186
1187
mResult.emplace(aResult);
1188
1189
/**
1190
* This should only return early if neither waitUntil() nor respondWith()
1191
* are called. The early return is so that mRespondWithPromiseHolder has a
1192
* chance to settle before mPromiseHolder does.
1193
*/
1194
if (!mPostDispatchChecksDone) {
1195
return;
1196
}
1197
1198
MaybeFinished();
1199
}
1200
1201
void FetchEventOp::MaybeFinished() {
1202
MOZ_ASSERT(IsCurrentThreadRunningWorker());
1203
MOZ_ASSERT(!mPromiseHolder.IsEmpty());
1204
1205
if (mResult) {
1206
// mRespondWithPromiseHolder should have been settled in
1207
// {Resolve,Reject}Callback by now.
1208
MOZ_DIAGNOSTIC_ASSERT(mRespondWithPromiseHolder.IsEmpty());
1209
1210
ServiceWorkerFetchEventOpResult result(
1211
mResult.value() == Resolved ? NS_OK : NS_ERROR_FAILURE);
1212
1213
mPromiseHolder.Resolve(result, __func__);
1214
}
1215
}
1216
1217
bool FetchEventOp::Exec(JSContext* aCx, WorkerPrivate* aWorkerPrivate) {
1218
aWorkerPrivate->AssertIsOnWorkerThread();
1219
MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
1220
MOZ_ASSERT(!mRespondWithPromiseHolder.IsEmpty());
1221
MOZ_ASSERT(!mPromiseHolder.IsEmpty());
1222
1223
nsresult rv = DispatchFetchEvent(aCx, aWorkerPrivate);
1224
1225
if (NS_WARN_IF(NS_FAILED(rv))) {
1226
RejectAll(rv);
1227
}
1228
1229
return NS_SUCCEEDED(rv);
1230
}
1231
1232
void FetchEventOp::AsyncLog(const nsCString& aMessageName,
1233
nsTArray<nsString> aParams) {
1234
MOZ_ASSERT(mActor);
1235
MOZ_ASSERT(!mPromiseHolder.IsEmpty());
1236
MOZ_ASSERT(mRespondWithClosure);
1237
1238
const FetchEventRespondWithClosure& closure = mRespondWithClosure.ref();
1239
1240
AsyncLog(closure.respondWithScriptSpec(), closure.respondWithLineNumber(),
1241
closure.respondWithColumnNumber(), aMessageName, std::move(aParams));
1242
}
1243
1244
void FetchEventOp::AsyncLog(const nsCString& aScriptSpec, uint32_t aLineNumber,
1245
uint32_t aColumnNumber,
1246
const nsCString& aMessageName,
1247
nsTArray<nsString> aParams) {
1248
MOZ_ASSERT(mActor);
1249
MOZ_ASSERT(!mPromiseHolder.IsEmpty());
1250
1251
// Capture `this` because FetchEventOpProxyChild (mActor) is not thread
1252
// safe, so an AddRef from RefPtr<FetchEventOpProxyChild>'s constructor will
1253
// assert.
1254
RefPtr<FetchEventOp> self = this;
1255
1256
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
1257
__func__, [self = std::move(self), spec = aScriptSpec, line = aLineNumber,
1258
column = aColumnNumber, messageName = aMessageName,
1259
params = std::move(aParams)] {
1260
if (NS_WARN_IF(!self->mActor)) {
1261
return;
1262
}
1263
1264
Unused << self->mActor->SendAsyncLog(spec, line, column, messageName,
1265
params);
1266
});
1267
1268
MOZ_ALWAYS_SUCCEEDS(
1269
RemoteWorkerService::Thread()->Dispatch(r.forget(), NS_DISPATCH_NORMAL));
1270
}
1271
1272
void FetchEventOp::GetRequestURL(nsAString& aOutRequestURL) {
1273
nsTArray<nsCString>& urls =
1274
mArgs.get_ServiceWorkerFetchEventOpArgs().internalRequest().urlList();
1275
MOZ_ASSERT(!urls.IsEmpty());
1276
1277
aOutRequestURL = NS_ConvertUTF8toUTF16(urls.LastElement());
1278
}
1279
1280
void FetchEventOp::ResolvedCallback(JSContext* aCx,
1281
JS::Handle<JS::Value> aValue) {
1282
MOZ_ASSERT(IsCurrentThreadRunningWorker());
1283
MOZ_ASSERT(mRespondWithClosure);
1284
MOZ_ASSERT(!mRespondWithPromiseHolder.IsEmpty());
1285
MOZ_ASSERT(!mPromiseHolder.IsEmpty());
1286
1287
nsAutoString requestURL;
1288
GetRequestURL(requestURL);
1289
1290
AutoCancel autoCancel(this);
1291
1292
if (!aValue.isObject()) {
1293
NS_WARNING(
1294
"FetchEvent::RespondWith was passed a promise resolved to a "
1295
"non-Object "
1296
"value");
1297
1298
nsCString sourceSpec;
1299
uint32_t line = 0;
1300
uint32_t column = 0;
1301
nsString valueString;
1302
nsContentUtils::ExtractErrorValues(aCx, aValue, sourceSpec, &line, &column,
1303
valueString);
1304
1305
autoCancel.SetCancelMessageAndLocation(
1306
sourceSpec, line, column,
1307
NS_LITERAL_CSTRING("InterceptedNonResponseWithURL"), requestURL,
1308
valueString);
1309
return;
1310
}
1311
1312
RefPtr<Response> response;
1313
nsresult rv = UNWRAP_OBJECT(Response, &aValue.toObject(), response);
1314
if (NS_FAILED(rv)) {
1315
nsCString sourceSpec;
1316
uint32_t line = 0;
1317
uint32_t column = 0;
1318
nsString valueString;
1319
nsContentUtils::ExtractErrorValues(aCx, aValue, sourceSpec, &line, &column,
1320
valueString);
1321
1322
autoCancel.SetCancelMessageAndLocation(
1323
sourceSpec, line, column,
1324
NS_LITERAL_CSTRING("InterceptedNonResponseWithURL"), requestURL,
1325
valueString);
1326
return;
1327
}
1328
1329
WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
1330
MOZ_ASSERT(worker);
1331
worker->AssertIsOnWorkerThread();
1332
1333
// Section "HTTP Fetch", step 3.3:
1334
// If one of the following conditions is true, return a network error:
1335
// * response's type is "error".
1336
// * request's mode is not "no-cors" and response's type is "opaque".
1337
// * request's redirect mode is not "manual" and response's type is
1338
// "opaqueredirect".
1339
// * request's redirect mode is not "follow" and response's url list
1340
// has more than one item.
1341
1342
if (response->Type() == ResponseType::Error) {
1343
autoCancel.SetCancelMessage(
1344
NS_LITERAL_CSTRING("InterceptedErrorResponseWithURL"), requestURL);
1345
return;
1346
}
1347
1348
const ServiceWorkerFetchEventOpArgs& args =
1349
mArgs.get_ServiceWorkerFetchEventOpArgs();
1350
const RequestMode requestMode = args.internalRequest().requestMode();
1351
1352
if (response->Type() == ResponseType::Opaque &&
1353
requestMode != RequestMode::No_cors) {
1354
NS_ConvertASCIItoUTF16 modeString(
1355
RequestModeValues::GetString(requestMode));
1356
1357
nsAutoString requestURL;
1358
GetRequestURL(requestURL);
1359
1360
autoCancel.SetCancelMessage(
1361
NS_LITERAL_CSTRING("BadOpaqueInterceptionRequestModeWithURL"),
1362
requestURL, modeString);
1363
return;
1364
}
1365
1366
const RequestRedirect requestRedirectMode =
1367
args.internalRequest().requestRedirect();
1368
1369
if (requestRedirectMode != RequestRedirect::Manual &&
1370
response->Type() == ResponseType::Opaqueredirect) {
1371
autoCancel.SetCancelMessage(
1372
NS_LITERAL_CSTRING("BadOpaqueRedirectInterceptionWithURL"), requestURL);
1373
return;
1374
}
1375
1376
if (requestRedirectMode != RequestRedirect::Follow &&
1377
response->Redirected()) {
1378
autoCancel.SetCancelMessage(
1379
NS_LITERAL_CSTRING("BadRedirectModeInterceptionWithURL"), requestURL);
1380
return;
1381
}
1382
1383
{
1384
ErrorResult error;
1385
bool bodyUsed = response->GetBodyUsed(error);
1386
error.WouldReportJSException();
1387
if (NS_WARN_IF(error.Failed())) {
1388
autoCancel.SetCancelErrorResult(aCx, error);
1389
return;
1390
}
1391
if (NS_WARN_IF(bodyUsed)) {
1392
autoCancel.SetCancelMessage(
1393
NS_LITERAL_CSTRING("InterceptedUsedResponseWithURL"), requestURL);
1394
return;
1395
}
1396
}
1397
1398
RefPtr<InternalResponse> ir = response->GetInternalResponse();
1399
if (NS_WARN_IF(!ir)) {
1400
return;
1401
}
1402
1403
// An extra safety check to make sure our invariant that opaque and cors
1404
// responses always have a URL does not break.
1405
if (NS_WARN_IF((response->Type() == ResponseType::Opaque ||
1406
response->Type() == ResponseType::Cors) &&
1407
ir->GetUnfilteredURL().IsEmpty())) {
1408
MOZ_DIAGNOSTIC_ASSERT(false, "Cors or opaque Response without a URL");
1409
return;
1410
}
1411
1412
Telemetry::ScalarAdd(Telemetry::ScalarID::SW_SYNTHESIZED_RES_COUNT, 1);
1413
1414
if (requestMode == RequestMode::Same_origin &&
1415
response->Type() == ResponseType::Cors) {
1416
Telemetry::ScalarAdd(Telemetry::ScalarID::SW_CORS_RES_FOR_SO_REQ_COUNT, 1);
1417
1418
// XXXtt: Will have a pref to enable the quirk response in bug 1419684.
1419
// The variadic template provided by StringArrayAppender requires exactly
1420
// an nsString.
1421
NS_ConvertUTF8toUTF16 responseURL(ir->GetUnfilteredURL());
1422
autoCancel.SetCancelMessage(
1423
NS_LITERAL_CSTRING("CorsResponseForSameOriginRequest"), requestURL,
1424
responseURL);
1425
return;
1426
}
1427
1428
nsCOMPtr<nsIInputStream> body;
1429
ir->GetUnfilteredBody(getter_AddRefs(body));
1430
// Errors and redirects may not have a body.
1431
if (body) {
1432
ErrorResult error;
1433
response->SetBodyUsed(aCx, error);
1434
error.WouldReportJSException();
1435
if (NS_WARN_IF(error.Failed())) {
1436
autoCancel.SetCancelErrorResult(aCx, error);
1437
return;
1438
}
1439
}
1440
1441
if (!ir->GetChannelInfo().IsInitialized()) {
1442
// This is a synthetic response (I think and hope so).
1443
ir->InitChannelInfo(worker->GetChannelInfo());
1444
}
1445
1446
autoCancel.Reset();
1447
1448
mRespondWithPromiseHolder.Resolve(
1449
FetchEventRespondWithResult(
1450
SynthesizeResponseArgs(ir, mRespondWithClosure.ref())),
1451
__func__);
1452
}
1453
1454
void FetchEventOp::RejectedCallback(JSContext* aCx,
1455
JS::Handle<JS::Value> aValue) {
1456
MOZ_ASSERT(IsCurrentThreadRunningWorker());
1457
MOZ_ASSERT(mRespondWithClosure);
1458
MOZ_ASSERT(!mRespondWithPromiseHolder.IsEmpty());
1459
MOZ_ASSERT(!mPromiseHolder.IsEmpty());
1460
1461
FetchEventRespondWithClosure& closure = mRespondWithClosure.ref();
1462
1463
nsCString sourceSpec = closure.respondWithScriptSpec();
1464
uint32_t line = closure.respondWithLineNumber();
1465
uint32_t column = closure.respondWithColumnNumber();
1466
nsString valueString;
1467
1468
nsContentUtils::ExtractErrorValues(aCx, aValue, sourceSpec, &line, &column,
1469
valueString);
1470
1471
nsString requestURL;
1472
GetRequestURL(requestURL);
1473
1474
AsyncLog(sourceSpec, line, column,
1475
NS_LITERAL_CSTRING("InterceptionRejectedResponseWithURL"),
1476
{std::move(requestURL), valueString});
1477
1478
mRespondWithPromiseHolder.Resolve(
1479
FetchEventRespondWithResult(
1480
CancelInterceptionArgs(NS_ERROR_INTERCEPTION_FAILED)),
1481
__func__);
1482
}
1483
1484
nsresult FetchEventOp::DispatchFetchEvent(JSContext* aCx,
1485
WorkerPrivate* aWorkerPrivate) {
1486
MOZ_ASSERT(aCx);
1487
MOZ_ASSERT(aWorkerPrivate);
1488
aWorkerPrivate->AssertIsOnWorkerThread();
1489
MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
1490
1491
ServiceWorkerFetchEventOpArgs& args =
1492
mArgs.get_ServiceWorkerFetchEventOpArgs();
1493
1494
/**
1495
* Step 1: get the InternalRequest. The InternalRequest can't be constructed
1496
* here from mArgs because the IPCStream has to be deserialized on the
1497
* thread receiving the ServiceWorkerFetchEventOpArgs.
1498
* FetchEventOpProxyChild will have already deserialized the stream on the
1499
* correct thread before creating this op, so we can take its saved
1500
* InternalRequest.
1501
*/
1502
RefPtr<InternalRequest> internalRequest = mActor->ExtractInternalRequest();
1503
1504
/**
1505
* Step 2: get the worker's global object
1506
*/
1507
GlobalObject globalObject(aCx, aWorkerPrivate->GlobalScope()->GetWrapper());
1508
nsCOMPtr<nsIGlobalObject> globalObjectAsSupports =
1509
do_QueryInterface(globalObject.GetAsSupports());
1510
if (NS_WARN_IF(!globalObjectAsSupports)) {
1511
return NS_ERROR_DOM_INVALID_STATE_ERR;
1512
}
1513
1514
/**
1515
* Step 3: create the public DOM Request object
1516
* TODO: this Request object should be created with an AbortSignal object
1517
* which should be aborted if the loading is aborted. See but 1394102.
1518
*/
1519
RefPtr<Request> request =
1520
new Request(globalObjectAsSupports, internalRequest, nullptr);
1521
MOZ_ASSERT_IF(internalRequest->IsNavigationRequest(),
1522
request->Redirect() == RequestRedirect::Manual);
1523
1524
/**
1525
* Step 4a: create the FetchEventInit
1526
*/
1527
RootedDictionary<FetchEventInit> fetchEventInit(aCx);
1528
fetchEventInit.mRequest = request;
1529
fetchEventInit.mBubbles = false;
1530
fetchEventInit.mCancelable = true;
1531
fetchEventInit.mIsReload = args.isReload();
1532
1533
/**
1534
* TODO: only expose the FetchEvent.clientId on subresource requests for
1535
* now. Once we implement .targetClientId we can then start exposing
1536
* .clientId on non-subresource requests as well. See bug 1487534.
1537
*/
1538
if (!args.clientId().IsEmpty() && !internalRequest->IsNavigationRequest()) {
1539
fetchEventInit.mClientId = args.clientId();
1540
}
1541
1542
/*
1544
*
1545
* "If request is a non-subresource request and request’s
1546
* destination is not "report", initialize e’s resultingClientId attribute
1547
* to reservedClient’s [resultingClient's] id, and to the empty string
1548
* otherwise." (Step 18.8)
1549
*/
1550
if (!args.resultingClientId().IsEmpty() && args.isNonSubresourceRequest() &&
1551
internalRequest->Destination() != RequestDestination::Report) {
1552
fetchEventInit.mResultingClientId = args.resultingClientId();
1553
}
1554
1555
/**
1556
* Step 4b: create the FetchEvent
1557
*/
1558
RefPtr<FetchEvent> fetchEvent = FetchEvent::Constructor(
1559
globalObject, NS_LITERAL_STRING("fetch"), fetchEventInit);
1560
fetchEvent->SetTrusted(true);
1561
fetchEvent->PostInit(args.workerScriptSpec(), this);
1562
1563
/**
1564
* Step 5: Dispatch the FetchEvent to the worker's global object
1565
*/
1566
nsresult rv = DispatchExtendableEventOnWorkerScope(
1567
aCx, aWorkerPrivate->GlobalScope(), fetchEvent, this);
1568
bool dispatchFailed = NS_FAILED(rv) && rv != NS_ERROR_XPC_JS_THREW_EXCEPTION;
1569
1570
if (NS_WARN_IF(dispatchFailed)) {
1571
return rv;
1572
}
1573
1574
/**
1575
* At this point, there are 4 (legal) scenarios:
1576
*
1577
* 1) If neither waitUntil() nor respondWith() are called,
1578
* DispatchExtendableEventOnWorkerScope() will have already called
1579
* FinishedWithResult(), but this call will have recorded the result
1580
* (mResult) and returned early so that mRespondWithPromiseHolder can be
1581
* settled first. mRespondWithPromiseHolder will be settled below, followed
1582
* by a call to MaybeFinished() which settles mPromiseHolder.
1583
*
1584
* 2) If waitUntil() is called at least once, and respondWith() is not
1585
* called, DispatchExtendableEventOnWorkerScope() will NOT have called
1586
* FinishedWithResult(). We'll settle mRespondWithPromiseHolder first, and
1587
* at some point in the future when the last waitUntil() promise settles,
1588
* FinishedWithResult() will be called, settling mPromiseHolder.
1589
*
1590
* 3) If waitUntil() is not called, and respondWith() is called,
1591
* DispatchExtendableEventOnWorkerScope() will NOT have called
1592
* FinishedWithResult(). We can also guarantee that
1593
* mRespondWithPromiseHolder will be settled before mPromiseHolder, due to
1594
* the Promise::AppendNativeHandler() call ordering in
1595
* FetchEvent::RespondWith().
1596
*
1597
* 4) If waitUntil() is called at least once, and respondWith() is also
1598
* called, the effect is similar to scenario 3), with the most imporant
1599
* property being mRespondWithPromiseHolder settling before mPromiseHolder.
1600
*
1601
* Note that if mPromiseHolder is settled before mRespondWithPromiseHolder,
1602
* FetchEventOpChild will cancel the interception.
1603
*/
1604
if (!fetchEvent->WaitToRespond()) {
1605
MOZ_ASSERT(!mRespondWithPromiseHolder.IsEmpty());
1606
MOZ_ASSERT(!aWorkerPrivate->UsesSystemPrincipal(),
1607
"We don't support system-principal serviceworkers");
1608
1609
if (fetchEvent->DefaultPrevented(CallerType::NonSystem)) {
1610
mRespondWithPromiseHolder.Resolve(
1611
FetchEventRespondWithResult(
1612
CancelInterceptionArgs(NS_ERROR_INTERCEPTION_FAILED)),
1613
__func__);
1614
} else {
1615
mRespondWithPromiseHolder.Resolve(
1616
FetchEventRespondWithResult(ResetInterceptionArgs()), __func__);
1617
}
1618
} else {
1619
MOZ_ASSERT(mRespondWithClosure);
1620
}
1621
1622
mPostDispatchChecksDone = true;
1623
MaybeFinished();
1624
1625
return NS_OK;
1626
}
1627
1628
/* static */ already_AddRefed<ServiceWorkerOp> ServiceWorkerOp::Create(
1629
const ServiceWorkerOpArgs& aArgs,
1630
std::function<void(const ServiceWorkerOpResult&)>&& aCallback) {
1631
MOZ_ASSERT(RemoteWorkerService::Thread()->IsOnCurrentThread());
1632
1633
RefPtr<ServiceWorkerOp> op;
1634
1635
switch (aArgs.type()) {
1636
case ServiceWorkerOpArgs::TServiceWorkerCheckScriptEvaluationOpArgs:
1637
op = MakeRefPtr<CheckScriptEvaluationOp>(aArgs, std::move(aCallback));
1638
break;
1639
case ServiceWorkerOpArgs::TServiceWorkerUpdateStateOpArgs:
1640
op = MakeRefPtr<UpdateServiceWorkerStateOp>(aArgs, std::move(aCallback));
1641
break;
1642
case ServiceWorkerOpArgs::TServiceWorkerTerminateWorkerOpArgs:
1643
op = MakeRefPtr<TerminateServiceWorkerOp>(aArgs, std::move(aCallback));
1644
break;
1645
case ServiceWorkerOpArgs::TServiceWorkerLifeCycleEventOpArgs:
1646
op = MakeRefPtr<LifeCycleEventOp>(aArgs, std::move(aCallback));
1647
break;
1648
case ServiceWorkerOpArgs::TServiceWorkerPushEventOpArgs:
1649
op = MakeRefPtr<PushEventOp>(aArgs, std::move(aCallback));
1650
break;
1651
case ServiceWorkerOpArgs::TServiceWorkerPushSubscriptionChangeEventOpArgs:
1652
op = MakeRefPtr<PushSubscriptionChangeEventOp>(aArgs,
1653
std::move(aCallback));
1654
break;
1655
case ServiceWorkerOpArgs::TServiceWorkerNotificationEventOpArgs:
1656
op = MakeRefPtr<NotificationEventOp>(aArgs, std::move(aCallback));
1657
break;
1658
case ServiceWorkerOpArgs::TServiceWorkerMessageEventOpArgs:
1659
op = MakeRefPtr<MessageEventOp>(aArgs, std::move(aCallback));
1660
break;
1661
case ServiceWorkerOpArgs::TServiceWorkerFetchEventOpArgs:
1662
op = MakeRefPtr<FetchEventOp>(aArgs, std::move(aCallback));
1663
break;
1664
default:
1665
MOZ_CRASH("Unknown Service Worker operation!");
1666
return nullptr;
1667
}
1668
1669
return op.forget();
1670
}
1671
1672
} // namespace dom
1673
} // namespace mozilla