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 "ServiceWorkerEvents.h"
8
9 #include "nsAutoPtr.h"
10 #include "nsContentUtils.h"
11 #include "nsIConsoleReportCollector.h"
12 #include "nsIHttpChannelInternal.h"
13 #include "nsINetworkInterceptController.h"
14 #include "nsIOutputStream.h"
15 #include "nsIScriptError.h"
16 #include "nsITimedChannel.h"
17 #include "mozilla/Encoding.h"
18 #include "nsContentPolicyUtils.h"
19 #include "nsContentUtils.h"
20 #include "nsComponentManagerUtils.h"
21 #include "nsServiceManagerUtils.h"
22 #include "nsStreamUtils.h"
23 #include "nsNetCID.h"
24 #include "nsNetUtil.h"
25 #include "nsSerializationHelper.h"
26 #include "nsQueryObject.h"
27 #include "ServiceWorker.h"
28 #include "ServiceWorkerManager.h"
29
30 #include "mozilla/ErrorResult.h"
31 #include "mozilla/LoadInfo.h"
32 #include "mozilla/Move.h"
33 #include "mozilla/Preferences.h"
34 #include "mozilla/dom/BodyUtil.h"
35 #include "mozilla/dom/Client.h"
36 #include "mozilla/dom/FetchEventBinding.h"
37 #include "mozilla/dom/MessagePort.h"
38 #include "mozilla/dom/PromiseNativeHandler.h"
39 #include "mozilla/dom/PushEventBinding.h"
40 #include "mozilla/dom/PushMessageDataBinding.h"
41 #include "mozilla/dom/PushUtil.h"
42 #include "mozilla/dom/Request.h"
43 #include "mozilla/dom/TypedArray.h"
44 #include "mozilla/dom/Response.h"
45 #include "mozilla/dom/WorkerScope.h"
46 #include "mozilla/dom/WorkerPrivate.h"
47
48 #include "js/Conversions.h"
49 #include "js/TypeDecls.h"
50 #include "xpcpublic.h"
51
52 using namespace mozilla;
53 using namespace mozilla::dom;
54
55 namespace {
56
AsyncLog(nsIInterceptedChannel * aInterceptedChannel,const nsACString & aRespondWithScriptSpec,uint32_t aRespondWithLineNumber,uint32_t aRespondWithColumnNumber,const nsACString & aMessageName,const nsTArray<nsString> & aParams)57 void AsyncLog(nsIInterceptedChannel* aInterceptedChannel,
58 const nsACString& aRespondWithScriptSpec,
59 uint32_t aRespondWithLineNumber,
60 uint32_t aRespondWithColumnNumber, const nsACString& aMessageName,
61 const nsTArray<nsString>& aParams) {
62 MOZ_ASSERT(aInterceptedChannel);
63 nsCOMPtr<nsIConsoleReportCollector> reporter =
64 aInterceptedChannel->GetConsoleReportCollector();
65 if (reporter) {
66 reporter->AddConsoleReport(
67 nsIScriptError::errorFlag,
68 NS_LITERAL_CSTRING("Service Worker Interception"),
69 nsContentUtils::eDOM_PROPERTIES, aRespondWithScriptSpec,
70 aRespondWithLineNumber, aRespondWithColumnNumber, aMessageName,
71 aParams);
72 }
73 }
74
75 template <typename... Params>
AsyncLog(nsIInterceptedChannel * aInterceptedChannel,const nsACString & aRespondWithScriptSpec,uint32_t aRespondWithLineNumber,uint32_t aRespondWithColumnNumber,const nsACString & aMessageName,const nsAString & aFirstParam,Params &&...aParams)76 void AsyncLog(nsIInterceptedChannel* aInterceptedChannel,
77 const nsACString& aRespondWithScriptSpec,
78 uint32_t aRespondWithLineNumber,
79 uint32_t aRespondWithColumnNumber,
80 // We have to list one explicit string so that calls with an
81 // nsTArray of params won't end up in here.
82 const nsACString& aMessageName, const nsAString& aFirstParam,
83 Params&&... aParams) {
84 nsTArray<nsString> paramsList(sizeof...(Params) + 1);
85 StringArrayAppender::Append(paramsList, sizeof...(Params) + 1, aFirstParam,
86 Forward<Params>(aParams)...);
87 AsyncLog(aInterceptedChannel, aRespondWithScriptSpec, aRespondWithLineNumber,
88 aRespondWithColumnNumber, aMessageName, paramsList);
89 }
90
91 } // anonymous namespace
92
93 namespace mozilla {
94 namespace dom {
95
CancelChannelRunnable(nsMainThreadPtrHandle<nsIInterceptedChannel> & aChannel,nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo> & aRegistration,nsresult aStatus)96 CancelChannelRunnable::CancelChannelRunnable(
97 nsMainThreadPtrHandle<nsIInterceptedChannel>& aChannel,
98 nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo>& aRegistration,
99 nsresult aStatus)
100 : Runnable("dom::CancelChannelRunnable"),
101 mChannel(aChannel),
102 mRegistration(aRegistration),
103 mStatus(aStatus) {}
104
105 NS_IMETHODIMP
Run()106 CancelChannelRunnable::Run() {
107 MOZ_ASSERT(NS_IsMainThread());
108
109 // TODO: When bug 1204254 is implemented, this time marker should be moved to
110 // the point where the body of the network request is complete.
111 mChannel->SetHandleFetchEventEnd(TimeStamp::Now());
112 mChannel->SaveTimeStamps();
113
114 mChannel->CancelInterception(mStatus);
115 mRegistration->MaybeScheduleUpdate();
116 return NS_OK;
117 }
118
FetchEvent(EventTarget * aOwner)119 FetchEvent::FetchEvent(EventTarget* aOwner)
120 : ExtendableEvent(aOwner),
121 mPreventDefaultLineNumber(0),
122 mPreventDefaultColumnNumber(0),
123 mIsReload(false),
124 mWaitToRespond(false) {}
125
~FetchEvent()126 FetchEvent::~FetchEvent() {}
127
PostInit(nsMainThreadPtrHandle<nsIInterceptedChannel> & aChannel,nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo> & aRegistration,const nsACString & aScriptSpec)128 void FetchEvent::PostInit(
129 nsMainThreadPtrHandle<nsIInterceptedChannel>& aChannel,
130 nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo>& aRegistration,
131 const nsACString& aScriptSpec) {
132 mChannel = aChannel;
133 mRegistration = aRegistration;
134 mScriptSpec.Assign(aScriptSpec);
135 }
136
Constructor(const GlobalObject & aGlobal,const nsAString & aType,const FetchEventInit & aOptions,ErrorResult & aRv)137 /*static*/ already_AddRefed<FetchEvent> FetchEvent::Constructor(
138 const GlobalObject& aGlobal, const nsAString& aType,
139 const FetchEventInit& aOptions, ErrorResult& aRv) {
140 RefPtr<EventTarget> owner = do_QueryObject(aGlobal.GetAsSupports());
141 MOZ_ASSERT(owner);
142 RefPtr<FetchEvent> e = new FetchEvent(owner);
143 bool trusted = e->Init(owner);
144 e->InitEvent(aType, aOptions.mBubbles, aOptions.mCancelable);
145 e->SetTrusted(trusted);
146 e->SetComposed(aOptions.mComposed);
147 e->mRequest = aOptions.mRequest;
148 e->mClientId = aOptions.mClientId;
149 e->mIsReload = aOptions.mIsReload;
150 return e.forget();
151 }
152
153 namespace {
154
155 struct RespondWithClosure {
156 nsMainThreadPtrHandle<nsIInterceptedChannel> mInterceptedChannel;
157 nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo> mRegistration;
158 const nsString mRequestURL;
159 const nsCString mRespondWithScriptSpec;
160 const uint32_t mRespondWithLineNumber;
161 const uint32_t mRespondWithColumnNumber;
162
RespondWithClosuremozilla::dom::__anon613384910211::RespondWithClosure163 RespondWithClosure(
164 nsMainThreadPtrHandle<nsIInterceptedChannel>& aChannel,
165 nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo>& aRegistration,
166 const nsAString& aRequestURL, const nsACString& aRespondWithScriptSpec,
167 uint32_t aRespondWithLineNumber, uint32_t aRespondWithColumnNumber)
168 : mInterceptedChannel(aChannel),
169 mRegistration(aRegistration),
170 mRequestURL(aRequestURL),
171 mRespondWithScriptSpec(aRespondWithScriptSpec),
172 mRespondWithLineNumber(aRespondWithLineNumber),
173 mRespondWithColumnNumber(aRespondWithColumnNumber) {}
174 };
175
176 class FinishResponse final : public Runnable {
177 nsMainThreadPtrHandle<nsIInterceptedChannel> mChannel;
178
179 public:
FinishResponse(nsMainThreadPtrHandle<nsIInterceptedChannel> & aChannel)180 explicit FinishResponse(
181 nsMainThreadPtrHandle<nsIInterceptedChannel>& aChannel)
182 : Runnable("dom::FinishResponse"), mChannel(aChannel) {}
183
184 NS_IMETHOD
Run()185 Run() override {
186 MOZ_ASSERT(NS_IsMainThread());
187
188 nsresult rv = mChannel->FinishSynthesizedResponse();
189 if (NS_WARN_IF(NS_FAILED(rv))) {
190 mChannel->CancelInterception(NS_ERROR_INTERCEPTION_FAILED);
191 return NS_OK;
192 }
193
194 TimeStamp timeStamp = TimeStamp::Now();
195 mChannel->SetHandleFetchEventEnd(timeStamp);
196 mChannel->SetFinishSynthesizedResponseEnd(timeStamp);
197 mChannel->SaveTimeStamps();
198
199 return rv;
200 }
201 };
202
203 class BodyCopyHandle final : public nsIInterceptedBodyCallback {
204 UniquePtr<RespondWithClosure> mClosure;
205
~BodyCopyHandle()206 ~BodyCopyHandle() {}
207
208 public:
209 NS_DECL_THREADSAFE_ISUPPORTS
210
BodyCopyHandle(UniquePtr<RespondWithClosure> && aClosure)211 explicit BodyCopyHandle(UniquePtr<RespondWithClosure>&& aClosure)
212 : mClosure(Move(aClosure)) {}
213
214 NS_IMETHOD
BodyComplete(nsresult aRv)215 BodyComplete(nsresult aRv) override {
216 MOZ_ASSERT(NS_IsMainThread());
217
218 nsCOMPtr<nsIRunnable> event;
219 if (NS_WARN_IF(NS_FAILED(aRv))) {
220 AsyncLog(mClosure->mInterceptedChannel, mClosure->mRespondWithScriptSpec,
221 mClosure->mRespondWithLineNumber,
222 mClosure->mRespondWithColumnNumber,
223 NS_LITERAL_CSTRING("InterceptionFailedWithURL"),
224 mClosure->mRequestURL);
225 event = new CancelChannelRunnable(mClosure->mInterceptedChannel,
226 mClosure->mRegistration,
227 NS_ERROR_INTERCEPTION_FAILED);
228 } else {
229 event = new FinishResponse(mClosure->mInterceptedChannel);
230 }
231
232 mClosure.reset();
233
234 event->Run();
235
236 return NS_OK;
237 }
238 };
239
240 NS_IMPL_ISUPPORTS(BodyCopyHandle, nsIInterceptedBodyCallback)
241
242 class StartResponse final : public Runnable {
243 nsMainThreadPtrHandle<nsIInterceptedChannel> mChannel;
244 RefPtr<InternalResponse> mInternalResponse;
245 ChannelInfo mWorkerChannelInfo;
246 const nsCString mScriptSpec;
247 const nsCString mResponseURLSpec;
248 UniquePtr<RespondWithClosure> mClosure;
249
250 public:
StartResponse(nsMainThreadPtrHandle<nsIInterceptedChannel> & aChannel,InternalResponse * aInternalResponse,const ChannelInfo & aWorkerChannelInfo,const nsACString & aScriptSpec,const nsACString & aResponseURLSpec,UniquePtr<RespondWithClosure> && aClosure)251 StartResponse(nsMainThreadPtrHandle<nsIInterceptedChannel>& aChannel,
252 InternalResponse* aInternalResponse,
253 const ChannelInfo& aWorkerChannelInfo,
254 const nsACString& aScriptSpec,
255 const nsACString& aResponseURLSpec,
256 UniquePtr<RespondWithClosure>&& aClosure)
257 : Runnable("dom::StartResponse"),
258 mChannel(aChannel),
259 mInternalResponse(aInternalResponse),
260 mWorkerChannelInfo(aWorkerChannelInfo),
261 mScriptSpec(aScriptSpec),
262 mResponseURLSpec(aResponseURLSpec),
263 mClosure(Move(aClosure)) {}
264
265 NS_IMETHOD
Run()266 Run() override {
267 MOZ_ASSERT(NS_IsMainThread());
268
269 nsCOMPtr<nsIChannel> underlyingChannel;
270 nsresult rv = mChannel->GetChannel(getter_AddRefs(underlyingChannel));
271 NS_ENSURE_SUCCESS(rv, rv);
272 NS_ENSURE_TRUE(underlyingChannel, NS_ERROR_UNEXPECTED);
273 nsCOMPtr<nsILoadInfo> loadInfo = underlyingChannel->GetLoadInfo();
274
275 if (!loadInfo || !CSPPermitsResponse(loadInfo)) {
276 mChannel->CancelInterception(NS_ERROR_CONTENT_BLOCKED);
277 return NS_OK;
278 }
279
280 ChannelInfo channelInfo;
281 if (mInternalResponse->GetChannelInfo().IsInitialized()) {
282 channelInfo = mInternalResponse->GetChannelInfo();
283 } else {
284 // We are dealing with a synthesized response here, so fall back to the
285 // channel info for the worker script.
286 channelInfo = mWorkerChannelInfo;
287 }
288 rv = mChannel->SetChannelInfo(&channelInfo);
289 if (NS_WARN_IF(NS_FAILED(rv))) {
290 mChannel->CancelInterception(NS_ERROR_INTERCEPTION_FAILED);
291 return NS_OK;
292 }
293
294 rv = mChannel->SynthesizeStatus(
295 mInternalResponse->GetUnfilteredStatus(),
296 mInternalResponse->GetUnfilteredStatusText());
297 if (NS_WARN_IF(NS_FAILED(rv))) {
298 mChannel->CancelInterception(NS_ERROR_INTERCEPTION_FAILED);
299 return NS_OK;
300 }
301
302 AutoTArray<InternalHeaders::Entry, 5> entries;
303 mInternalResponse->UnfilteredHeaders()->GetEntries(entries);
304 for (uint32_t i = 0; i < entries.Length(); ++i) {
305 mChannel->SynthesizeHeader(entries[i].mName, entries[i].mValue);
306 }
307
308 auto castLoadInfo = static_cast<LoadInfo*>(loadInfo.get());
309 castLoadInfo->SynthesizeServiceWorkerTainting(
310 mInternalResponse->GetTainting());
311
312 // Get the preferred alternative data type of outter channel
313 nsAutoCString preferredAltDataType(EmptyCString());
314 nsCOMPtr<nsICacheInfoChannel> outerChannel =
315 do_QueryInterface(underlyingChannel);
316 if (outerChannel) {
317 outerChannel->GetPreferredAlternativeDataType(preferredAltDataType);
318 }
319
320 // Get the alternative data type saved in the InternalResponse
321 nsAutoCString altDataType;
322 nsCOMPtr<nsICacheInfoChannel> cacheInfoChannel =
323 mInternalResponse->TakeCacheInfoChannel().get();
324 if (cacheInfoChannel) {
325 cacheInfoChannel->GetAlternativeDataType(altDataType);
326 }
327
328 nsCOMPtr<nsIInputStream> body;
329 if (preferredAltDataType.Equals(altDataType)) {
330 body = mInternalResponse->TakeAlternativeBody();
331 }
332 if (!body) {
333 mInternalResponse->GetUnfilteredBody(getter_AddRefs(body));
334 } else {
335 Telemetry::ScalarAdd(Telemetry::ScalarID::SW_ALTERNATIVE_BODY_USED_COUNT,
336 1);
337 }
338
339 RefPtr<BodyCopyHandle> copyHandle;
340 copyHandle = new BodyCopyHandle(Move(mClosure));
341
342 rv = mChannel->StartSynthesizedResponse(body, copyHandle, cacheInfoChannel,
343 mResponseURLSpec,
344 mInternalResponse->IsRedirected());
345 if (NS_WARN_IF(NS_FAILED(rv))) {
346 mChannel->CancelInterception(NS_ERROR_INTERCEPTION_FAILED);
347 return NS_OK;
348 }
349
350 nsCOMPtr<nsIObserverService> obsService = services::GetObserverService();
351 if (obsService) {
352 obsService->NotifyObservers(
353 underlyingChannel, "service-worker-synthesized-response", nullptr);
354 }
355
356 return rv;
357 }
358
CSPPermitsResponse(nsILoadInfo * aLoadInfo)359 bool CSPPermitsResponse(nsILoadInfo* aLoadInfo) {
360 MOZ_ASSERT(NS_IsMainThread());
361 MOZ_ASSERT(aLoadInfo);
362 nsresult rv;
363 nsCOMPtr<nsIURI> uri;
364 nsCString url = mInternalResponse->GetUnfilteredURL();
365 if (url.IsEmpty()) {
366 // Synthetic response. The buck stops at the worker script.
367 url = mScriptSpec;
368 }
369 rv = NS_NewURI(getter_AddRefs(uri), url, nullptr, nullptr);
370 NS_ENSURE_SUCCESS(rv, false);
371 int16_t decision = nsIContentPolicy::ACCEPT;
372 rv = NS_CheckContentLoadPolicy(
373 aLoadInfo->InternalContentPolicyType(), uri,
374 aLoadInfo->LoadingPrincipal(), aLoadInfo->TriggeringPrincipal(),
375 aLoadInfo->LoadingNode(), EmptyCString(), nullptr, &decision);
376 NS_ENSURE_SUCCESS(rv, false);
377 return decision == nsIContentPolicy::ACCEPT;
378 }
379 };
380
381 class RespondWithHandler final : public PromiseNativeHandler {
382 nsMainThreadPtrHandle<nsIInterceptedChannel> mInterceptedChannel;
383 nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo> mRegistration;
384 const RequestMode mRequestMode;
385 const RequestRedirect mRequestRedirectMode;
386 #ifdef DEBUG
387 const bool mIsClientRequest;
388 #endif
389 const nsCString mScriptSpec;
390 const nsString mRequestURL;
391 const nsCString mRequestFragment;
392 const nsCString mRespondWithScriptSpec;
393 const uint32_t mRespondWithLineNumber;
394 const uint32_t mRespondWithColumnNumber;
395 bool mRequestWasHandled;
396
397 public:
398 NS_DECL_ISUPPORTS
399
RespondWithHandler(nsMainThreadPtrHandle<nsIInterceptedChannel> & aChannel,nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo> & aRegistration,RequestMode aRequestMode,bool aIsClientRequest,RequestRedirect aRedirectMode,const nsACString & aScriptSpec,const nsAString & aRequestURL,const nsACString & aRequestFragment,const nsACString & aRespondWithScriptSpec,uint32_t aRespondWithLineNumber,uint32_t aRespondWithColumnNumber)400 RespondWithHandler(
401 nsMainThreadPtrHandle<nsIInterceptedChannel>& aChannel,
402 nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo>& aRegistration,
403 RequestMode aRequestMode, bool aIsClientRequest,
404 RequestRedirect aRedirectMode, const nsACString& aScriptSpec,
405 const nsAString& aRequestURL, const nsACString& aRequestFragment,
406 const nsACString& aRespondWithScriptSpec, uint32_t aRespondWithLineNumber,
407 uint32_t aRespondWithColumnNumber)
408 : mInterceptedChannel(aChannel),
409 mRegistration(aRegistration),
410 mRequestMode(aRequestMode),
411 mRequestRedirectMode(aRedirectMode)
412 #ifdef DEBUG
413 ,
414 mIsClientRequest(aIsClientRequest)
415 #endif
416 ,
417 mScriptSpec(aScriptSpec),
418 mRequestURL(aRequestURL),
419 mRequestFragment(aRequestFragment),
420 mRespondWithScriptSpec(aRespondWithScriptSpec),
421 mRespondWithLineNumber(aRespondWithLineNumber),
422 mRespondWithColumnNumber(aRespondWithColumnNumber),
423 mRequestWasHandled(false) {
424 }
425
426 void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override;
427
428 void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override;
429
430 void CancelRequest(nsresult aStatus);
431
AsyncLog(const nsACString & aMessageName,const nsTArray<nsString> & aParams)432 void AsyncLog(const nsACString& aMessageName,
433 const nsTArray<nsString>& aParams) {
434 ::AsyncLog(mInterceptedChannel, mRespondWithScriptSpec,
435 mRespondWithLineNumber, mRespondWithColumnNumber, aMessageName,
436 aParams);
437 }
438
AsyncLog(const nsACString & aSourceSpec,uint32_t aLine,uint32_t aColumn,const nsACString & aMessageName,const nsTArray<nsString> & aParams)439 void AsyncLog(const nsACString& aSourceSpec, uint32_t aLine, uint32_t aColumn,
440 const nsACString& aMessageName,
441 const nsTArray<nsString>& aParams) {
442 ::AsyncLog(mInterceptedChannel, aSourceSpec, aLine, aColumn, aMessageName,
443 aParams);
444 }
445
446 private:
~RespondWithHandler()447 ~RespondWithHandler() {
448 if (!mRequestWasHandled) {
449 ::AsyncLog(mInterceptedChannel, mRespondWithScriptSpec,
450 mRespondWithLineNumber, mRespondWithColumnNumber,
451 NS_LITERAL_CSTRING("InterceptionFailedWithURL"), mRequestURL);
452 CancelRequest(NS_ERROR_INTERCEPTION_FAILED);
453 }
454 }
455 };
456
457 class MOZ_STACK_CLASS AutoCancel {
458 RefPtr<RespondWithHandler> mOwner;
459 nsCString mSourceSpec;
460 uint32_t mLine;
461 uint32_t mColumn;
462 nsCString mMessageName;
463 nsTArray<nsString> mParams;
464
465 public:
AutoCancel(RespondWithHandler * aOwner,const nsString & aRequestURL)466 AutoCancel(RespondWithHandler* aOwner, const nsString& aRequestURL)
467 : mOwner(aOwner),
468 mLine(0),
469 mColumn(0),
470 mMessageName(NS_LITERAL_CSTRING("InterceptionFailedWithURL")) {
471 mParams.AppendElement(aRequestURL);
472 }
473
~AutoCancel()474 ~AutoCancel() {
475 if (mOwner) {
476 if (mSourceSpec.IsEmpty()) {
477 mOwner->AsyncLog(mMessageName, mParams);
478 } else {
479 mOwner->AsyncLog(mSourceSpec, mLine, mColumn, mMessageName, mParams);
480 }
481 mOwner->CancelRequest(NS_ERROR_INTERCEPTION_FAILED);
482 }
483 }
484
485 // This function steals the error message from a ErrorResult.
SetCancelErrorResult(JSContext * aCx,ErrorResult & aRv)486 void SetCancelErrorResult(JSContext* aCx, ErrorResult& aRv) {
487 MOZ_DIAGNOSTIC_ASSERT(aRv.Failed());
488 MOZ_DIAGNOSTIC_ASSERT(!JS_IsExceptionPending(aCx));
489
490 // Storing the error as exception in the JSContext.
491 if (!aRv.MaybeSetPendingException(aCx)) {
492 return;
493 }
494
495 MOZ_ASSERT(!aRv.Failed());
496
497 // Let's take the pending exception.
498 JS::Rooted<JS::Value> exn(aCx);
499 if (!JS_GetPendingException(aCx, &exn)) {
500 return;
501 }
502
503 JS_ClearPendingException(aCx);
504
505 // Converting the exception in a js::ErrorReport.
506 js::ErrorReport report(aCx);
507 if (!report.init(aCx, exn, js::ErrorReport::WithSideEffects)) {
508 JS_ClearPendingException(aCx);
509 return;
510 }
511
512 MOZ_ASSERT(mOwner);
513 MOZ_ASSERT(mMessageName.EqualsLiteral("InterceptionFailedWithURL"));
514 MOZ_ASSERT(mParams.Length() == 1);
515
516 // Let's store the error message here.
517 mMessageName.Assign(report.toStringResult().c_str());
518 mParams.Clear();
519 }
520
521 template <typename... Params>
SetCancelMessage(const nsACString & aMessageName,Params &&...aParams)522 void SetCancelMessage(const nsACString& aMessageName, Params&&... aParams) {
523 MOZ_ASSERT(mOwner);
524 MOZ_ASSERT(mMessageName.EqualsLiteral("InterceptionFailedWithURL"));
525 MOZ_ASSERT(mParams.Length() == 1);
526 mMessageName = aMessageName;
527 mParams.Clear();
528 StringArrayAppender::Append(mParams, sizeof...(Params),
529 Forward<Params>(aParams)...);
530 }
531
532 template <typename... Params>
SetCancelMessageAndLocation(const nsACString & aSourceSpec,uint32_t aLine,uint32_t aColumn,const nsACString & aMessageName,Params &&...aParams)533 void SetCancelMessageAndLocation(const nsACString& aSourceSpec,
534 uint32_t aLine, uint32_t aColumn,
535 const nsACString& aMessageName,
536 Params&&... aParams) {
537 MOZ_ASSERT(mOwner);
538 MOZ_ASSERT(mMessageName.EqualsLiteral("InterceptionFailedWithURL"));
539 MOZ_ASSERT(mParams.Length() == 1);
540
541 mSourceSpec = aSourceSpec;
542 mLine = aLine;
543 mColumn = aColumn;
544
545 mMessageName = aMessageName;
546 mParams.Clear();
547 StringArrayAppender::Append(mParams, sizeof...(Params),
548 Forward<Params>(aParams)...);
549 }
550
Reset()551 void Reset() { mOwner = nullptr; }
552 };
553
NS_IMPL_ISUPPORTS0(RespondWithHandler)554 NS_IMPL_ISUPPORTS0(RespondWithHandler)
555
556 void RespondWithHandler::ResolvedCallback(JSContext* aCx,
557 JS::Handle<JS::Value> aValue) {
558 AutoCancel autoCancel(this, mRequestURL);
559 mInterceptedChannel->SetFinishResponseStart(TimeStamp::Now());
560
561 if (!aValue.isObject()) {
562 NS_WARNING(
563 "FetchEvent::RespondWith was passed a promise resolved to a non-Object "
564 "value");
565
566 nsCString sourceSpec;
567 uint32_t line = 0;
568 uint32_t column = 0;
569 nsString valueString;
570 nsContentUtils::ExtractErrorValues(aCx, aValue, sourceSpec, &line, &column,
571 valueString);
572
573 autoCancel.SetCancelMessageAndLocation(
574 sourceSpec, line, column,
575 NS_LITERAL_CSTRING("InterceptedNonResponseWithURL"), mRequestURL,
576 valueString);
577 return;
578 }
579
580 RefPtr<Response> response;
581 nsresult rv = UNWRAP_OBJECT(Response, &aValue.toObject(), response);
582 if (NS_FAILED(rv)) {
583 nsCString sourceSpec;
584 uint32_t line = 0;
585 uint32_t column = 0;
586 nsString valueString;
587 nsContentUtils::ExtractErrorValues(aCx, aValue, sourceSpec, &line, &column,
588 valueString);
589
590 autoCancel.SetCancelMessageAndLocation(
591 sourceSpec, line, column,
592 NS_LITERAL_CSTRING("InterceptedNonResponseWithURL"), mRequestURL,
593 valueString);
594 return;
595 }
596
597 WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
598 MOZ_ASSERT(worker);
599 worker->AssertIsOnWorkerThread();
600
601 // Section "HTTP Fetch", step 3.3:
602 // If one of the following conditions is true, return a network error:
603 // * response's type is "error".
604 // * request's mode is not "no-cors" and response's type is "opaque".
605 // * request's redirect mode is not "manual" and response's type is
606 // "opaqueredirect".
607 // * request's redirect mode is not "follow" and response's url list
608 // has more than one item.
609
610 if (response->Type() == ResponseType::Error) {
611 autoCancel.SetCancelMessage(
612 NS_LITERAL_CSTRING("InterceptedErrorResponseWithURL"), mRequestURL);
613 return;
614 }
615
616 MOZ_ASSERT_IF(mIsClientRequest, mRequestMode == RequestMode::Same_origin ||
617 mRequestMode == RequestMode::Navigate);
618
619 if (response->Type() == ResponseType::Opaque &&
620 mRequestMode != RequestMode::No_cors) {
621 uint32_t mode = static_cast<uint32_t>(mRequestMode);
622 NS_ConvertASCIItoUTF16 modeString(RequestModeValues::strings[mode].value,
623 RequestModeValues::strings[mode].length);
624
625 autoCancel.SetCancelMessage(
626 NS_LITERAL_CSTRING("BadOpaqueInterceptionRequestModeWithURL"),
627 mRequestURL, modeString);
628 return;
629 }
630
631 if (mRequestRedirectMode != RequestRedirect::Manual &&
632 response->Type() == ResponseType::Opaqueredirect) {
633 autoCancel.SetCancelMessage(
634 NS_LITERAL_CSTRING("BadOpaqueRedirectInterceptionWithURL"),
635 mRequestURL);
636 return;
637 }
638
639 if (mRequestRedirectMode != RequestRedirect::Follow &&
640 response->Redirected()) {
641 autoCancel.SetCancelMessage(
642 NS_LITERAL_CSTRING("BadRedirectModeInterceptionWithURL"), mRequestURL);
643 return;
644 }
645
646 if (NS_WARN_IF(response->BodyUsed())) {
647 autoCancel.SetCancelMessage(
648 NS_LITERAL_CSTRING("InterceptedUsedResponseWithURL"), mRequestURL);
649 return;
650 }
651
652 RefPtr<InternalResponse> ir = response->GetInternalResponse();
653 if (NS_WARN_IF(!ir)) {
654 return;
655 }
656
657 // An extra safety check to make sure our invariant that opaque and cors
658 // responses always have a URL does not break.
659 if (NS_WARN_IF((response->Type() == ResponseType::Opaque ||
660 response->Type() == ResponseType::Cors) &&
661 ir->GetUnfilteredURL().IsEmpty())) {
662 MOZ_DIAGNOSTIC_ASSERT(false, "Cors or opaque Response without a URL");
663 return;
664 }
665
666 Telemetry::ScalarAdd(Telemetry::ScalarID::SW_SYNTHESIZED_RES_COUNT, 1);
667
668 if (mRequestMode == RequestMode::Same_origin &&
669 response->Type() == ResponseType::Cors) {
670 Telemetry::ScalarAdd(Telemetry::ScalarID::SW_CORS_RES_FOR_SO_REQ_COUNT, 1);
671
672 // XXXtt: Will have a pref to enable the quirk response in bug 1419684.
673 // The variadic template provided by StringArrayAppender requires exactly
674 // an nsString.
675 NS_ConvertUTF8toUTF16 responseURL(ir->GetUnfilteredURL());
676 autoCancel.SetCancelMessage(
677 NS_LITERAL_CSTRING("CorsResponseForSameOriginRequest"), mRequestURL,
678 responseURL);
679 return;
680 }
681
682 // Propagate the URL to the content if the request mode is not "navigate".
683 // Note that, we only reflect the final URL if the response.redirected is
684 // false. We propagate all the URLs if the response.redirected is true.
685 nsCString responseURL;
686 if (mRequestMode != RequestMode::Navigate) {
687 responseURL = ir->GetUnfilteredURL();
688
689 // Similar to how we apply the request fragment to redirects automatically
690 // we also want to apply it automatically when propagating the response
691 // URL from a service worker interception. Currently response.url strips
692 // the fragment, so this will never conflict with an existing fragment
693 // on the response. In the future we will have to check for a response
694 // fragment and avoid overriding in that case.
695 if (!mRequestFragment.IsEmpty() && !responseURL.IsEmpty()) {
696 MOZ_ASSERT(!responseURL.Contains('#'));
697 responseURL.Append(NS_LITERAL_CSTRING("#"));
698 responseURL.Append(mRequestFragment);
699 }
700 }
701
702 UniquePtr<RespondWithClosure> closure(new RespondWithClosure(
703 mInterceptedChannel, mRegistration, mRequestURL, mRespondWithScriptSpec,
704 mRespondWithLineNumber, mRespondWithColumnNumber));
705
706 nsCOMPtr<nsIRunnable> startRunnable =
707 new StartResponse(mInterceptedChannel, ir, worker->GetChannelInfo(),
708 mScriptSpec, responseURL, Move(closure));
709
710 nsCOMPtr<nsIInputStream> body;
711 ir->GetUnfilteredBody(getter_AddRefs(body));
712 // Errors and redirects may not have a body.
713 if (body) {
714 ErrorResult error;
715 response->SetBodyUsed(aCx, error);
716 if (NS_WARN_IF(error.Failed())) {
717 autoCancel.SetCancelErrorResult(aCx, error);
718 return;
719 }
720 }
721
722 MOZ_ALWAYS_SUCCEEDS(worker->DispatchToMainThread(startRunnable.forget()));
723
724 MOZ_ASSERT(!closure);
725 autoCancel.Reset();
726 mRequestWasHandled = true;
727 }
728
RejectedCallback(JSContext * aCx,JS::Handle<JS::Value> aValue)729 void RespondWithHandler::RejectedCallback(JSContext* aCx,
730 JS::Handle<JS::Value> aValue) {
731 nsCString sourceSpec = mRespondWithScriptSpec;
732 uint32_t line = mRespondWithLineNumber;
733 uint32_t column = mRespondWithColumnNumber;
734 nsString valueString;
735
736 mInterceptedChannel->SetFinishResponseStart(TimeStamp::Now());
737
738 nsContentUtils::ExtractErrorValues(aCx, aValue, sourceSpec, &line, &column,
739 valueString);
740
741 ::AsyncLog(mInterceptedChannel, sourceSpec, line, column,
742 NS_LITERAL_CSTRING("InterceptionRejectedResponseWithURL"),
743 mRequestURL, valueString);
744
745 CancelRequest(NS_ERROR_INTERCEPTION_FAILED);
746 }
747
CancelRequest(nsresult aStatus)748 void RespondWithHandler::CancelRequest(nsresult aStatus) {
749 nsCOMPtr<nsIRunnable> runnable =
750 new CancelChannelRunnable(mInterceptedChannel, mRegistration, aStatus);
751 // Note, this may run off the worker thread during worker termination.
752 WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
753 if (worker) {
754 MOZ_ALWAYS_SUCCEEDS(worker->DispatchToMainThread(runnable.forget()));
755 } else {
756 MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(runnable.forget()));
757 }
758 mRequestWasHandled = true;
759 }
760
761 } // namespace
762
RespondWith(JSContext * aCx,Promise & aArg,ErrorResult & aRv)763 void FetchEvent::RespondWith(JSContext* aCx, Promise& aArg, ErrorResult& aRv) {
764 if (EventPhase() == nsIDOMEvent::NONE || mWaitToRespond) {
765 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
766 return;
767 }
768
769 // Record where respondWith() was called in the script so we can include the
770 // information in any error reporting. We should be guaranteed not to get
771 // a file:// string here because service workers require http/https.
772 nsCString spec;
773 uint32_t line = 0;
774 uint32_t column = 0;
775 nsJSUtils::GetCallingLocation(aCx, spec, &line, &column);
776
777 RefPtr<InternalRequest> ir = mRequest->GetInternalRequest();
778
779 nsAutoCString requestURL;
780 ir->GetURL(requestURL);
781
782 StopImmediatePropagation();
783 mWaitToRespond = true;
784 RefPtr<RespondWithHandler> handler = new RespondWithHandler(
785 mChannel, mRegistration, mRequest->Mode(), ir->IsClientRequest(),
786 mRequest->Redirect(), mScriptSpec, NS_ConvertUTF8toUTF16(requestURL),
787 ir->GetFragment(), spec, line, column);
788 aArg.AppendNativeHandler(handler);
789
790 if (!WaitOnPromise(aArg)) {
791 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
792 }
793 }
794
PreventDefault(JSContext * aCx,CallerType aCallerType)795 void FetchEvent::PreventDefault(JSContext* aCx, CallerType aCallerType) {
796 MOZ_ASSERT(aCx);
797 MOZ_ASSERT(aCallerType != CallerType::System,
798 "Since when do we support system-principal service workers?");
799
800 if (mPreventDefaultScriptSpec.IsEmpty()) {
801 // Note when the FetchEvent might have been canceled by script, but don't
802 // actually log the location until we are sure it matters. This is
803 // determined in ServiceWorkerPrivate.cpp. We only remember the first
804 // call to preventDefault() as its the most likely to have actually canceled
805 // the event.
806 nsJSUtils::GetCallingLocation(aCx, mPreventDefaultScriptSpec,
807 &mPreventDefaultLineNumber,
808 &mPreventDefaultColumnNumber);
809 }
810
811 Event::PreventDefault(aCx, aCallerType);
812 }
813
ReportCanceled()814 void FetchEvent::ReportCanceled() {
815 MOZ_ASSERT(!mPreventDefaultScriptSpec.IsEmpty());
816
817 RefPtr<InternalRequest> ir = mRequest->GetInternalRequest();
818 nsAutoCString url;
819 ir->GetURL(url);
820
821 // The variadic template provided by StringArrayAppender requires exactly
822 // an nsString.
823 NS_ConvertUTF8toUTF16 requestURL(url);
824 // nsString requestURL;
825 // CopyUTF8toUTF16(url, requestURL);
826
827 ::AsyncLog(mChannel.get(), mPreventDefaultScriptSpec,
828 mPreventDefaultLineNumber, mPreventDefaultColumnNumber,
829 NS_LITERAL_CSTRING("InterceptionCanceledWithURL"), requestURL);
830 }
831
832 namespace {
833
834 class WaitUntilHandler final : public PromiseNativeHandler {
835 WorkerPrivate* mWorkerPrivate;
836 const nsCString mScope;
837 nsCString mSourceSpec;
838 uint32_t mLine;
839 uint32_t mColumn;
840 nsString mRejectValue;
841
~WaitUntilHandler()842 ~WaitUntilHandler() {}
843
844 public:
845 NS_DECL_THREADSAFE_ISUPPORTS
846
WaitUntilHandler(WorkerPrivate * aWorkerPrivate,JSContext * aCx)847 WaitUntilHandler(WorkerPrivate* aWorkerPrivate, JSContext* aCx)
848 : mWorkerPrivate(aWorkerPrivate),
849 mScope(mWorkerPrivate->ServiceWorkerScope()),
850 mLine(0),
851 mColumn(0) {
852 mWorkerPrivate->AssertIsOnWorkerThread();
853
854 // Save the location of the waitUntil() call itself as a fallback
855 // in case the rejection value does not contain any location info.
856 nsJSUtils::GetCallingLocation(aCx, mSourceSpec, &mLine, &mColumn);
857 }
858
ResolvedCallback(JSContext * aCx,JS::Handle<JS::Value> aValue)859 void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override {
860 // do nothing, we are only here to report errors
861 }
862
RejectedCallback(JSContext * aCx,JS::Handle<JS::Value> aValue)863 void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override {
864 mWorkerPrivate->AssertIsOnWorkerThread();
865
866 nsCString spec;
867 uint32_t line = 0;
868 uint32_t column = 0;
869 nsContentUtils::ExtractErrorValues(aCx, aValue, spec, &line, &column,
870 mRejectValue);
871
872 // only use the extracted location if we found one
873 if (!spec.IsEmpty()) {
874 mSourceSpec = spec;
875 mLine = line;
876 mColumn = column;
877 }
878
879 MOZ_ALWAYS_SUCCEEDS(mWorkerPrivate->DispatchToMainThread(
880 NewRunnableMethod("WaitUntilHandler::ReportOnMainThread", this,
881 &WaitUntilHandler::ReportOnMainThread)));
882 }
883
ReportOnMainThread()884 void ReportOnMainThread() {
885 MOZ_ASSERT(NS_IsMainThread());
886 RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
887 if (!swm) {
888 // browser shutdown
889 return;
890 }
891
892 // TODO: Make the error message a localized string. (bug 1222720)
893 nsString message;
894 message.AppendLiteral(
895 "Service worker event waitUntil() was passed a "
896 "promise that rejected with '");
897 message.Append(mRejectValue);
898 message.AppendLiteral("'.");
899
900 // Note, there is a corner case where this won't report to the window
901 // that triggered the error. Consider a navigation fetch event that
902 // rejects waitUntil() without holding respondWith() open. In this case
903 // there is no controlling document yet, the window did call .register()
904 // because there is no documeny yet, and the navigation is no longer
905 // being intercepted.
906
907 swm->ReportToAllClients(mScope, message, NS_ConvertUTF8toUTF16(mSourceSpec),
908 EmptyString(), mLine, mColumn,
909 nsIScriptError::errorFlag);
910 }
911 };
912
913 NS_IMPL_ISUPPORTS0(WaitUntilHandler)
914
915 } // anonymous namespace
916
NS_IMPL_ADDREF_INHERITED(FetchEvent,ExtendableEvent)917 NS_IMPL_ADDREF_INHERITED(FetchEvent, ExtendableEvent)
918 NS_IMPL_RELEASE_INHERITED(FetchEvent, ExtendableEvent)
919
920 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(FetchEvent)
921 NS_INTERFACE_MAP_END_INHERITING(ExtendableEvent)
922
923 NS_IMPL_CYCLE_COLLECTION_INHERITED(FetchEvent, ExtendableEvent, mRequest)
924
925 ExtendableEvent::ExtendableEvent(EventTarget* aOwner)
926 : Event(aOwner, nullptr, nullptr) {}
927
WaitOnPromise(Promise & aPromise)928 bool ExtendableEvent::WaitOnPromise(Promise& aPromise) {
929 if (!mExtensionsHandler) {
930 return false;
931 }
932 return mExtensionsHandler->WaitOnPromise(aPromise);
933 }
934
SetKeepAliveHandler(ExtensionsHandler * aExtensionsHandler)935 void ExtendableEvent::SetKeepAliveHandler(
936 ExtensionsHandler* aExtensionsHandler) {
937 MOZ_ASSERT(!mExtensionsHandler);
938 WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
939 MOZ_ASSERT(worker);
940 worker->AssertIsOnWorkerThread();
941 mExtensionsHandler = aExtensionsHandler;
942 }
943
WaitUntil(JSContext * aCx,Promise & aPromise,ErrorResult & aRv)944 void ExtendableEvent::WaitUntil(JSContext* aCx, Promise& aPromise,
945 ErrorResult& aRv) {
946 MOZ_ASSERT(!NS_IsMainThread());
947
948 if (!WaitOnPromise(aPromise)) {
949 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
950 return;
951 }
952
953 // Append our handler to each waitUntil promise separately so we
954 // can record the location in script where waitUntil was called.
955 RefPtr<WaitUntilHandler> handler =
956 new WaitUntilHandler(GetCurrentThreadWorkerPrivate(), aCx);
957 aPromise.AppendNativeHandler(handler);
958 }
959
960 NS_IMPL_ADDREF_INHERITED(ExtendableEvent, Event)
961 NS_IMPL_RELEASE_INHERITED(ExtendableEvent, Event)
962
963 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ExtendableEvent)
964 NS_INTERFACE_MAP_END_INHERITING(Event)
965
966 namespace {
ExtractBytesFromUSVString(const nsAString & aStr,nsTArray<uint8_t> & aBytes)967 nsresult ExtractBytesFromUSVString(const nsAString& aStr,
968 nsTArray<uint8_t>& aBytes) {
969 MOZ_ASSERT(aBytes.IsEmpty());
970 auto encoder = UTF_8_ENCODING->NewEncoder();
971 CheckedInt<size_t> needed =
972 encoder->MaxBufferLengthFromUTF16WithoutReplacement(aStr.Length());
973 if (NS_WARN_IF(!needed.isValid() ||
974 !aBytes.SetLength(needed.value(), fallible))) {
975 return NS_ERROR_OUT_OF_MEMORY;
976 }
977 uint32_t result;
978 size_t read;
979 size_t written;
980 Tie(result, read, written) =
981 encoder->EncodeFromUTF16WithoutReplacement(aStr, aBytes, true);
982 MOZ_ASSERT(result == kInputEmpty);
983 MOZ_ASSERT(read == aStr.Length());
984 aBytes.TruncateLength(written);
985 return NS_OK;
986 }
987
ExtractBytesFromData(const OwningArrayBufferViewOrArrayBufferOrUSVString & aDataInit,nsTArray<uint8_t> & aBytes)988 nsresult ExtractBytesFromData(
989 const OwningArrayBufferViewOrArrayBufferOrUSVString& aDataInit,
990 nsTArray<uint8_t>& aBytes) {
991 if (aDataInit.IsArrayBufferView()) {
992 const ArrayBufferView& view = aDataInit.GetAsArrayBufferView();
993 if (NS_WARN_IF(!PushUtil::CopyArrayBufferViewToArray(view, aBytes))) {
994 return NS_ERROR_OUT_OF_MEMORY;
995 }
996 return NS_OK;
997 }
998 if (aDataInit.IsArrayBuffer()) {
999 const ArrayBuffer& buffer = aDataInit.GetAsArrayBuffer();
1000 if (NS_WARN_IF(!PushUtil::CopyArrayBufferToArray(buffer, aBytes))) {
1001 return NS_ERROR_OUT_OF_MEMORY;
1002 }
1003 return NS_OK;
1004 }
1005 if (aDataInit.IsUSVString()) {
1006 return ExtractBytesFromUSVString(aDataInit.GetAsUSVString(), aBytes);
1007 }
1008 NS_NOTREACHED("Unexpected push message data");
1009 return NS_ERROR_FAILURE;
1010 }
1011 } // namespace
1012
PushMessageData(nsISupports * aOwner,nsTArray<uint8_t> && aBytes)1013 PushMessageData::PushMessageData(nsISupports* aOwner,
1014 nsTArray<uint8_t>&& aBytes)
1015 : mOwner(aOwner), mBytes(Move(aBytes)) {}
1016
~PushMessageData()1017 PushMessageData::~PushMessageData() {}
1018
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(PushMessageData,mOwner)1019 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(PushMessageData, mOwner)
1020
1021 NS_IMPL_CYCLE_COLLECTING_ADDREF(PushMessageData)
1022 NS_IMPL_CYCLE_COLLECTING_RELEASE(PushMessageData)
1023
1024 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PushMessageData)
1025 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
1026 NS_INTERFACE_MAP_ENTRY(nsISupports)
1027 NS_INTERFACE_MAP_END
1028
1029 JSObject* PushMessageData::WrapObject(JSContext* aCx,
1030 JS::Handle<JSObject*> aGivenProto) {
1031 return mozilla::dom::PushMessageDataBinding::Wrap(aCx, this, aGivenProto);
1032 }
1033
Json(JSContext * cx,JS::MutableHandle<JS::Value> aRetval,ErrorResult & aRv)1034 void PushMessageData::Json(JSContext* cx, JS::MutableHandle<JS::Value> aRetval,
1035 ErrorResult& aRv) {
1036 if (NS_FAILED(EnsureDecodedText())) {
1037 aRv.Throw(NS_ERROR_DOM_UNKNOWN_ERR);
1038 return;
1039 }
1040 BodyUtil::ConsumeJson(cx, aRetval, mDecodedText, aRv);
1041 }
1042
Text(nsAString & aData)1043 void PushMessageData::Text(nsAString& aData) {
1044 if (NS_SUCCEEDED(EnsureDecodedText())) {
1045 aData = mDecodedText;
1046 }
1047 }
1048
ArrayBuffer(JSContext * cx,JS::MutableHandle<JSObject * > aRetval,ErrorResult & aRv)1049 void PushMessageData::ArrayBuffer(JSContext* cx,
1050 JS::MutableHandle<JSObject*> aRetval,
1051 ErrorResult& aRv) {
1052 uint8_t* data = GetContentsCopy();
1053 if (data) {
1054 BodyUtil::ConsumeArrayBuffer(cx, aRetval, mBytes.Length(), data, aRv);
1055 }
1056 }
1057
Blob(ErrorResult & aRv)1058 already_AddRefed<mozilla::dom::Blob> PushMessageData::Blob(ErrorResult& aRv) {
1059 uint8_t* data = GetContentsCopy();
1060 if (data) {
1061 RefPtr<mozilla::dom::Blob> blob = BodyUtil::ConsumeBlob(
1062 mOwner, EmptyString(), mBytes.Length(), data, aRv);
1063 if (blob) {
1064 return blob.forget();
1065 }
1066 }
1067 return nullptr;
1068 }
1069
EnsureDecodedText()1070 nsresult PushMessageData::EnsureDecodedText() {
1071 if (mBytes.IsEmpty() || !mDecodedText.IsEmpty()) {
1072 return NS_OK;
1073 }
1074 nsresult rv = BodyUtil::ConsumeText(
1075 mBytes.Length(), reinterpret_cast<uint8_t*>(mBytes.Elements()),
1076 mDecodedText);
1077 if (NS_WARN_IF(NS_FAILED(rv))) {
1078 mDecodedText.Truncate();
1079 return rv;
1080 }
1081 return NS_OK;
1082 }
1083
GetContentsCopy()1084 uint8_t* PushMessageData::GetContentsCopy() {
1085 uint32_t length = mBytes.Length();
1086 void* data = malloc(length);
1087 if (!data) {
1088 return nullptr;
1089 }
1090 memcpy(data, mBytes.Elements(), length);
1091 return reinterpret_cast<uint8_t*>(data);
1092 }
1093
PushEvent(EventTarget * aOwner)1094 PushEvent::PushEvent(EventTarget* aOwner) : ExtendableEvent(aOwner) {}
1095
Constructor(mozilla::dom::EventTarget * aOwner,const nsAString & aType,const PushEventInit & aOptions,ErrorResult & aRv)1096 already_AddRefed<PushEvent> PushEvent::Constructor(
1097 mozilla::dom::EventTarget* aOwner, const nsAString& aType,
1098 const PushEventInit& aOptions, ErrorResult& aRv) {
1099 RefPtr<PushEvent> e = new PushEvent(aOwner);
1100 bool trusted = e->Init(aOwner);
1101 e->InitEvent(aType, aOptions.mBubbles, aOptions.mCancelable);
1102 e->SetTrusted(trusted);
1103 e->SetComposed(aOptions.mComposed);
1104 if (aOptions.mData.WasPassed()) {
1105 nsTArray<uint8_t> bytes;
1106 nsresult rv = ExtractBytesFromData(aOptions.mData.Value(), bytes);
1107 if (NS_FAILED(rv)) {
1108 aRv.Throw(rv);
1109 return nullptr;
1110 }
1111 e->mData = new PushMessageData(aOwner, Move(bytes));
1112 }
1113 return e.forget();
1114 }
1115
NS_IMPL_ADDREF_INHERITED(PushEvent,ExtendableEvent)1116 NS_IMPL_ADDREF_INHERITED(PushEvent, ExtendableEvent)
1117 NS_IMPL_RELEASE_INHERITED(PushEvent, ExtendableEvent)
1118
1119 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PushEvent)
1120 NS_INTERFACE_MAP_END_INHERITING(ExtendableEvent)
1121
1122 NS_IMPL_CYCLE_COLLECTION_INHERITED(PushEvent, ExtendableEvent, mData)
1123
1124 JSObject* PushEvent::WrapObjectInternal(JSContext* aCx,
1125 JS::Handle<JSObject*> aGivenProto) {
1126 return mozilla::dom::PushEventBinding::Wrap(aCx, this, aGivenProto);
1127 }
1128
ExtendableMessageEvent(EventTarget * aOwner)1129 ExtendableMessageEvent::ExtendableMessageEvent(EventTarget* aOwner)
1130 : ExtendableEvent(aOwner), mData(JS::UndefinedValue()) {
1131 mozilla::HoldJSObjects(this);
1132 }
1133
~ExtendableMessageEvent()1134 ExtendableMessageEvent::~ExtendableMessageEvent() {
1135 mData.setUndefined();
1136 DropJSObjects(this);
1137 }
1138
GetData(JSContext * aCx,JS::MutableHandle<JS::Value> aData,ErrorResult & aRv)1139 void ExtendableMessageEvent::GetData(JSContext* aCx,
1140 JS::MutableHandle<JS::Value> aData,
1141 ErrorResult& aRv) {
1142 aData.set(mData);
1143 if (!JS_WrapValue(aCx, aData)) {
1144 aRv.Throw(NS_ERROR_FAILURE);
1145 }
1146 }
1147
GetSource(Nullable<OwningClientOrServiceWorkerOrMessagePort> & aValue) const1148 void ExtendableMessageEvent::GetSource(
1149 Nullable<OwningClientOrServiceWorkerOrMessagePort>& aValue) const {
1150 if (mClient) {
1151 aValue.SetValue().SetAsClient() = mClient;
1152 } else if (mServiceWorker) {
1153 aValue.SetValue().SetAsServiceWorker() = mServiceWorker;
1154 } else if (mMessagePort) {
1155 aValue.SetValue().SetAsMessagePort() = mMessagePort;
1156 } else {
1157 // nullptr source is possible for manually constructed event
1158 aValue.SetNull();
1159 }
1160 }
1161
1162 /* static */ already_AddRefed<ExtendableMessageEvent>
Constructor(const GlobalObject & aGlobal,const nsAString & aType,const ExtendableMessageEventInit & aOptions,ErrorResult & aRv)1163 ExtendableMessageEvent::Constructor(const GlobalObject& aGlobal,
1164 const nsAString& aType,
1165 const ExtendableMessageEventInit& aOptions,
1166 ErrorResult& aRv) {
1167 nsCOMPtr<EventTarget> t = do_QueryInterface(aGlobal.GetAsSupports());
1168 return Constructor(t, aType, aOptions, aRv);
1169 }
1170
1171 /* static */ already_AddRefed<ExtendableMessageEvent>
Constructor(mozilla::dom::EventTarget * aEventTarget,const nsAString & aType,const ExtendableMessageEventInit & aOptions,ErrorResult & aRv)1172 ExtendableMessageEvent::Constructor(mozilla::dom::EventTarget* aEventTarget,
1173 const nsAString& aType,
1174 const ExtendableMessageEventInit& aOptions,
1175 ErrorResult& aRv) {
1176 RefPtr<ExtendableMessageEvent> event =
1177 new ExtendableMessageEvent(aEventTarget);
1178
1179 event->InitEvent(aType, aOptions.mBubbles, aOptions.mCancelable);
1180 bool trusted = event->Init(aEventTarget);
1181 event->SetTrusted(trusted);
1182
1183 event->mData = aOptions.mData;
1184 event->mOrigin = aOptions.mOrigin;
1185 event->mLastEventId = aOptions.mLastEventId;
1186
1187 if (!aOptions.mSource.IsNull()) {
1188 if (aOptions.mSource.Value().IsClient()) {
1189 event->mClient = aOptions.mSource.Value().GetAsClient();
1190 } else if (aOptions.mSource.Value().IsServiceWorker()) {
1191 event->mServiceWorker = aOptions.mSource.Value().GetAsServiceWorker();
1192 } else if (aOptions.mSource.Value().IsMessagePort()) {
1193 event->mMessagePort = aOptions.mSource.Value().GetAsMessagePort();
1194 }
1195 }
1196
1197 event->mPorts.AppendElements(aOptions.mPorts);
1198 return event.forget();
1199 }
1200
GetPorts(nsTArray<RefPtr<MessagePort>> & aPorts)1201 void ExtendableMessageEvent::GetPorts(nsTArray<RefPtr<MessagePort>>& aPorts) {
1202 aPorts = mPorts;
1203 }
1204
1205 NS_IMPL_CYCLE_COLLECTION_CLASS(ExtendableMessageEvent)
1206
1207 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(ExtendableMessageEvent, Event)
1208 tmp->mData.setUndefined();
1209 NS_IMPL_CYCLE_COLLECTION_UNLINK(mClient)
1210 NS_IMPL_CYCLE_COLLECTION_UNLINK(mServiceWorker)
1211 NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessagePort)
1212 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPorts)
1213 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
1214
1215 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ExtendableMessageEvent, Event)
1216 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mClient)
1217 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mServiceWorker)
1218 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMessagePort)
1219 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPorts)
1220 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
1221
1222 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(ExtendableMessageEvent, Event)
1223 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mData)
1224 NS_IMPL_CYCLE_COLLECTION_TRACE_END
1225
1226 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ExtendableMessageEvent)
1227 NS_INTERFACE_MAP_END_INHERITING(Event)
1228
1229 NS_IMPL_ADDREF_INHERITED(ExtendableMessageEvent, Event)
1230 NS_IMPL_RELEASE_INHERITED(ExtendableMessageEvent, Event)
1231
1232 } // namespace dom
1233 } // namespace mozilla
1234