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