tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

ServiceWorkerEvents.cpp (45591B)


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