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