Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/fetch/FetchUtil.cpp
Line
Count
Source (jump to first uncovered line)
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 "FetchUtil.h"
8
9
#include "nsError.h"
10
#include "nsString.h"
11
#include "nsIDocument.h"
12
13
#include "mozilla/dom/InternalRequest.h"
14
#include "mozilla/dom/WorkerRef.h"
15
16
namespace mozilla {
17
namespace dom {
18
19
// static
20
nsresult
21
FetchUtil::GetValidRequestMethod(const nsACString& aMethod, nsCString& outMethod)
22
0
{
23
0
  nsAutoCString upperCaseMethod(aMethod);
24
0
  ToUpperCase(upperCaseMethod);
25
0
  if (!NS_IsValidHTTPToken(aMethod)) {
26
0
    outMethod.SetIsVoid(true);
27
0
    return NS_ERROR_DOM_SYNTAX_ERR;
28
0
  }
29
0
30
0
  if (upperCaseMethod.EqualsLiteral("CONNECT") ||
31
0
      upperCaseMethod.EqualsLiteral("TRACE") ||
32
0
      upperCaseMethod.EqualsLiteral("TRACK")) {
33
0
    outMethod.SetIsVoid(true);
34
0
    return NS_ERROR_DOM_SECURITY_ERR;
35
0
  }
36
0
37
0
  if (upperCaseMethod.EqualsLiteral("DELETE") ||
38
0
      upperCaseMethod.EqualsLiteral("GET") ||
39
0
      upperCaseMethod.EqualsLiteral("HEAD") ||
40
0
      upperCaseMethod.EqualsLiteral("OPTIONS") ||
41
0
      upperCaseMethod.EqualsLiteral("POST") ||
42
0
      upperCaseMethod.EqualsLiteral("PUT")) {
43
0
    outMethod = upperCaseMethod;
44
0
  }
45
0
  else {
46
0
    outMethod = aMethod; // Case unchanged for non-standard methods
47
0
  }
48
0
  return NS_OK;
49
0
}
50
51
static bool
52
FindCRLF(nsACString::const_iterator& aStart,
53
         nsACString::const_iterator& aEnd)
54
0
{
55
0
  nsACString::const_iterator end(aEnd);
56
0
  return FindInReadable(NS_LITERAL_CSTRING("\r\n"), aStart, end);
57
0
}
58
59
// Reads over a CRLF and positions start after it.
60
static bool
61
PushOverLine(nsACString::const_iterator& aStart,
62
       const nsACString::const_iterator& aEnd)
63
0
{
64
0
  if (*aStart == nsCRT::CR && (aEnd - aStart > 1) && *(++aStart) == nsCRT::LF) {
65
0
    ++aStart; // advance to after CRLF
66
0
    return true;
67
0
  }
68
0
69
0
  return false;
70
0
}
71
72
// static
73
bool
74
FetchUtil::ExtractHeader(nsACString::const_iterator& aStart,
75
                         nsACString::const_iterator& aEnd,
76
                         nsCString& aHeaderName,
77
                         nsCString& aHeaderValue,
78
                         bool* aWasEmptyHeader)
79
0
{
80
0
  MOZ_ASSERT(aWasEmptyHeader);
81
0
  // Set it to a valid value here so we don't forget later.
82
0
  *aWasEmptyHeader = false;
83
0
84
0
  const char* beginning = aStart.get();
85
0
  nsACString::const_iterator end(aEnd);
86
0
  if (!FindCRLF(aStart, end)) {
87
0
    return false;
88
0
  }
89
0
90
0
  if (aStart.get() == beginning) {
91
0
    *aWasEmptyHeader = true;
92
0
    return true;
93
0
  }
94
0
95
0
  nsAutoCString header(beginning, aStart.get() - beginning);
96
0
97
0
  nsACString::const_iterator headerStart, iter, headerEnd;
98
0
  header.BeginReading(headerStart);
99
0
  header.EndReading(headerEnd);
100
0
  iter = headerStart;
101
0
  if (!FindCharInReadable(':', iter, headerEnd)) {
102
0
    return false;
103
0
  }
104
0
105
0
  aHeaderName.Assign(StringHead(header, iter - headerStart));
106
0
  aHeaderName.CompressWhitespace();
107
0
  if (!NS_IsValidHTTPToken(aHeaderName)) {
108
0
    return false;
109
0
  }
110
0
111
0
  aHeaderValue.Assign(Substring(++iter, headerEnd));
112
0
  if (!NS_IsReasonableHTTPHeaderValue(aHeaderValue)) {
113
0
    return false;
114
0
  }
115
0
  aHeaderValue.CompressWhitespace();
116
0
117
0
  return PushOverLine(aStart, aEnd);
118
0
}
119
120
// static
121
nsresult
122
FetchUtil::SetRequestReferrer(nsIPrincipal* aPrincipal,
123
                              nsIDocument* aDoc,
124
                              nsIHttpChannel* aChannel,
125
0
                              InternalRequest* aRequest) {
126
0
  MOZ_ASSERT(NS_IsMainThread());
127
0
128
0
  nsAutoString referrer;
129
0
  aRequest->GetReferrer(referrer);
130
0
  net::ReferrerPolicy policy = aRequest->GetReferrerPolicy();
131
0
132
0
  nsresult rv = NS_OK;
133
0
  if (referrer.IsEmpty()) {
134
0
    // This is the case request’s referrer is "no-referrer"
135
0
    rv = aChannel->SetReferrerWithPolicy(nullptr, net::RP_No_Referrer);
136
0
    NS_ENSURE_SUCCESS(rv, rv);
137
0
  } else if (referrer.EqualsLiteral(kFETCH_CLIENT_REFERRER_STR)) {
138
0
    rv = nsContentUtils::SetFetchReferrerURIWithPolicy(aPrincipal,
139
0
                                                       aDoc,
140
0
                                                       aChannel,
141
0
                                                       policy);
142
0
    NS_ENSURE_SUCCESS(rv, rv);
143
0
  } else {
144
0
    // From "Determine request's Referrer" step 3
145
0
    // "If request's referrer is a URL, let referrerSource be request's
146
0
    // referrer."
147
0
    nsCOMPtr<nsIURI> referrerURI;
148
0
    rv = NS_NewURI(getter_AddRefs(referrerURI), referrer, nullptr, nullptr);
149
0
    NS_ENSURE_SUCCESS(rv, rv);
150
0
151
0
    rv = aChannel->SetReferrerWithPolicy(referrerURI, policy);
152
0
    NS_ENSURE_SUCCESS(rv, rv);
153
0
  }
154
0
155
0
  nsCOMPtr<nsIURI> referrerURI;
156
0
  Unused << aChannel->GetReferrer(getter_AddRefs(referrerURI));
157
0
158
0
  // Step 8 https://fetch.spec.whatwg.org/#main-fetch
159
0
  // If request’s referrer is not "no-referrer", set request’s referrer to
160
0
  // the result of invoking determine request’s referrer.
161
0
  if (referrerURI) {
162
0
    nsAutoCString spec;
163
0
    rv = referrerURI->GetSpec(spec);
164
0
    NS_ENSURE_SUCCESS(rv, rv);
165
0
166
0
    aRequest->SetReferrer(NS_ConvertUTF8toUTF16(spec));
167
0
  } else {
168
0
    aRequest->SetReferrer(EmptyString());
169
0
  }
170
0
171
0
  return NS_OK;
172
0
}
173
174
class WindowStreamOwner final : public nsIObserver
175
                              , public nsSupportsWeakReference
176
{
177
  // Read from any thread but only set/cleared on the main thread. The lifecycle
178
  // of WindowStreamOwner prevents concurrent read/clear.
179
  nsCOMPtr<nsIAsyncInputStream> mStream;
180
181
  nsCOMPtr<nsIGlobalObject> mGlobal;
182
183
  ~WindowStreamOwner()
184
0
  {
185
0
    MOZ_ASSERT(NS_IsMainThread());
186
0
187
0
    nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
188
0
    if (obs) {
189
0
      obs->RemoveObserver(this, DOM_WINDOW_DESTROYED_TOPIC);
190
0
    }
191
0
  }
192
193
public:
194
  NS_DECL_ISUPPORTS
195
196
  WindowStreamOwner(nsIAsyncInputStream* aStream, nsIGlobalObject* aGlobal)
197
    : mStream(aStream)
198
    , mGlobal(aGlobal)
199
0
  {
200
0
    MOZ_DIAGNOSTIC_ASSERT(mGlobal);
201
0
    MOZ_ASSERT(NS_IsMainThread());
202
0
  }
203
204
  static already_AddRefed<WindowStreamOwner>
205
  Create(nsIAsyncInputStream* aStream, nsIGlobalObject* aGlobal)
206
0
  {
207
0
    nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
208
0
    if (NS_WARN_IF(!os)) {
209
0
      return nullptr;
210
0
    }
211
0
212
0
    RefPtr<WindowStreamOwner> self = new WindowStreamOwner(aStream, aGlobal);
213
0
214
0
    // Holds nsIWeakReference to self.
215
0
    nsresult rv = os->AddObserver(self, DOM_WINDOW_DESTROYED_TOPIC, true);
216
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
217
0
      return nullptr;
218
0
    }
219
0
220
0
    return self.forget();
221
0
  }
222
223
  struct Destroyer final : Runnable
224
  {
225
    RefPtr<WindowStreamOwner> mDoomed;
226
227
    explicit Destroyer(already_AddRefed<WindowStreamOwner> aDoomed)
228
      : Runnable("WindowStreamOwner::Destroyer")
229
      , mDoomed(aDoomed)
230
0
    {}
231
232
    NS_IMETHOD
233
    Run() override
234
0
    {
235
0
      mDoomed = nullptr;
236
0
      return NS_OK;
237
0
    }
238
  };
239
240
  // nsIObserver:
241
242
  NS_IMETHOD
243
  Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData) override
244
0
  {
245
0
    MOZ_ASSERT(NS_IsMainThread());
246
0
    MOZ_DIAGNOSTIC_ASSERT(strcmp(aTopic, DOM_WINDOW_DESTROYED_TOPIC) == 0);
247
0
248
0
    if (!mStream) {
249
0
      return NS_OK;
250
0
    }
251
0
252
0
    nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(mGlobal);
253
0
    if (!SameCOMIdentity(aSubject, window)) {
254
0
      return NS_OK;
255
0
    }
256
0
257
0
    // mStream->Close() will call JSStreamConsumer::OnInputStreamReady which may
258
0
    // then destory itself, dropping the last reference to 'this'.
259
0
    RefPtr<WindowStreamOwner> keepAlive(this);
260
0
261
0
    mStream->Close();
262
0
    mStream = nullptr;
263
0
    mGlobal = nullptr;
264
0
    return NS_OK;
265
0
  }
266
};
267
268
NS_IMPL_ISUPPORTS(WindowStreamOwner, nsIObserver, nsISupportsWeakReference)
269
270
class WorkerStreamOwner final
271
{
272
public:
273
  NS_INLINE_DECL_REFCOUNTING(WorkerStreamOwner)
274
275
  explicit WorkerStreamOwner(nsIAsyncInputStream* aStream)
276
    : mStream(aStream)
277
0
  {}
278
279
  static already_AddRefed<WorkerStreamOwner>
280
  Create(nsIAsyncInputStream* aStream, WorkerPrivate* aWorker)
281
0
  {
282
0
    RefPtr<WorkerStreamOwner> self = new WorkerStreamOwner(aStream);
283
0
284
0
    self->mWorkerRef = WeakWorkerRef::Create(aWorker, [self]() {
285
0
      if (self->mStream) {
286
0
        // If this Close() calls JSStreamConsumer::OnInputStreamReady and drops
287
0
        // the last reference to the JSStreamConsumer, 'this' will not be
288
0
        // destroyed since ~JSStreamConsumer() only enqueues a Destroyer.
289
0
        self->mStream->Close();
290
0
        self->mStream = nullptr;
291
0
        self->mWorkerRef = nullptr;
292
0
      }
293
0
    });
294
0
295
0
    if (!self->mWorkerRef) {
296
0
      return nullptr;
297
0
    }
298
0
299
0
    return self.forget();
300
0
  }
301
302
  struct Destroyer final : CancelableRunnable
303
  {
304
    RefPtr<WorkerStreamOwner> mDoomed;
305
306
    explicit Destroyer(already_AddRefed<WorkerStreamOwner>&& aDoomed)
307
      : CancelableRunnable("WorkerStreamOwner::Destroyer")
308
      , mDoomed(std::move(aDoomed))
309
0
    {}
310
311
    NS_IMETHOD
312
    Run() override
313
0
    {
314
0
      mDoomed = nullptr;
315
0
      return NS_OK;
316
0
    }
317
318
    nsresult
319
    Cancel() override
320
0
    {
321
0
      return Run();
322
0
    }
323
  };
324
325
private:
326
0
  ~WorkerStreamOwner() = default;
327
328
  // Read from any thread but only set/cleared on the worker thread. The
329
  // lifecycle of WorkerStreamOwner prevents concurrent read/clear.
330
  nsCOMPtr<nsIAsyncInputStream> mStream;
331
  RefPtr<WeakWorkerRef> mWorkerRef;
332
333
};
334
335
class JSStreamConsumer final : public nsIInputStreamCallback
336
{
337
  nsCOMPtr<nsIEventTarget> mOwningEventTarget;
338
  RefPtr<WindowStreamOwner> mWindowStreamOwner;
339
  RefPtr<WorkerStreamOwner> mWorkerStreamOwner;
340
  JS::StreamConsumer* mConsumer;
341
  bool mConsumerAborted;
342
343
  JSStreamConsumer(already_AddRefed<WindowStreamOwner> aWindowStreamOwner,
344
                   nsIGlobalObject* aGlobal,
345
                   JS::StreamConsumer* aConsumer)
346
   : mOwningEventTarget(aGlobal->EventTargetFor(TaskCategory::Other))
347
   , mWindowStreamOwner(aWindowStreamOwner)
348
   , mConsumer(aConsumer)
349
   , mConsumerAborted(false)
350
0
  {
351
0
    MOZ_DIAGNOSTIC_ASSERT(mWindowStreamOwner);
352
0
    MOZ_DIAGNOSTIC_ASSERT(mConsumer);
353
0
  }
354
355
  JSStreamConsumer(RefPtr<WorkerStreamOwner> aWorkerStreamOwner,
356
                   nsIGlobalObject* aGlobal,
357
                   JS::StreamConsumer* aConsumer)
358
   : mOwningEventTarget(aGlobal->EventTargetFor(TaskCategory::Other))
359
   , mWorkerStreamOwner(std::move(aWorkerStreamOwner))
360
   , mConsumer(aConsumer)
361
   , mConsumerAborted(false)
362
0
  {
363
0
    MOZ_DIAGNOSTIC_ASSERT(mWorkerStreamOwner);
364
0
    MOZ_DIAGNOSTIC_ASSERT(mConsumer);
365
0
  }
366
367
  ~JSStreamConsumer()
368
0
  {
369
0
    // Both WindowStreamOwner and WorkerStreamOwner need to be destroyed on
370
0
    // their global's event target thread.
371
0
372
0
    RefPtr<Runnable> destroyer;
373
0
    if (mWindowStreamOwner) {
374
0
      MOZ_DIAGNOSTIC_ASSERT(!mWorkerStreamOwner);
375
0
      destroyer = new WindowStreamOwner::Destroyer(mWindowStreamOwner.forget());
376
0
    } else {
377
0
      MOZ_DIAGNOSTIC_ASSERT(mWorkerStreamOwner);
378
0
      destroyer = new WorkerStreamOwner::Destroyer(mWorkerStreamOwner.forget());
379
0
    }
380
0
381
0
    MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget->Dispatch(destroyer.forget()));
382
0
  }
383
384
  static nsresult WriteSegment(nsIInputStream* aStream,
385
                               void* aClosure,
386
                               const char* aFromSegment,
387
                               uint32_t aToOffset,
388
                               uint32_t aCount,
389
                               uint32_t* aWriteCount)
390
0
  {
391
0
    JSStreamConsumer* self = reinterpret_cast<JSStreamConsumer*>(aClosure);
392
0
    MOZ_DIAGNOSTIC_ASSERT(!self->mConsumerAborted);
393
0
394
0
    // This callback can be called on any thread which is explicitly allowed by
395
0
    // this particular JS API call.
396
0
    if (!self->mConsumer->consumeChunk((const uint8_t*)aFromSegment, aCount)) {
397
0
      self->mConsumerAborted = true;
398
0
      return NS_ERROR_UNEXPECTED;
399
0
    }
400
0
401
0
    *aWriteCount = aCount;
402
0
    return NS_OK;
403
0
  }
404
405
public:
406
  NS_DECL_THREADSAFE_ISUPPORTS
407
408
  static bool Start(nsIInputStream* aStream,
409
                    JS::StreamConsumer* aConsumer,
410
                    nsIGlobalObject* aGlobal,
411
                    WorkerPrivate* aMaybeWorker)
412
0
  {
413
0
    nsresult rv;
414
0
415
0
    bool nonBlocking = false;
416
0
    rv = aStream->IsNonBlocking(&nonBlocking);
417
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
418
0
      return false;
419
0
    }
420
0
421
0
    // Use a pipe to create an nsIAsyncInputStream if we don't already have one.
422
0
    nsCOMPtr<nsIAsyncInputStream> asyncStream = do_QueryInterface(aStream);
423
0
    if (!asyncStream || !nonBlocking) {
424
0
      nsCOMPtr<nsIAsyncOutputStream> pipe;
425
0
      rv = NS_NewPipe2(getter_AddRefs(asyncStream), getter_AddRefs(pipe),
426
0
                       true, true);
427
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
428
0
        return false;
429
0
      }
430
0
431
0
      nsCOMPtr<nsIEventTarget> thread =
432
0
        do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
433
0
434
0
      rv = NS_AsyncCopy(aStream, pipe, thread,
435
0
                        NS_ASYNCCOPY_VIA_WRITESEGMENTS);
436
0
      if (NS_WARN_IF(NS_FAILED(rv))) {
437
0
        return false;
438
0
      }
439
0
    }
440
0
441
0
    RefPtr<JSStreamConsumer> consumer;
442
0
    if (aMaybeWorker) {
443
0
      RefPtr<WorkerStreamOwner> owner =
444
0
        WorkerStreamOwner::Create(asyncStream, aMaybeWorker);
445
0
      if (!owner) {
446
0
        return false;
447
0
      }
448
0
449
0
      consumer = new JSStreamConsumer(std::move(owner), aGlobal, aConsumer);
450
0
    } else {
451
0
      RefPtr<WindowStreamOwner> owner =
452
0
        WindowStreamOwner::Create(asyncStream, aGlobal);
453
0
      if (!owner) {
454
0
        return false;
455
0
      }
456
0
457
0
      consumer = new JSStreamConsumer(owner.forget(), aGlobal, aConsumer);
458
0
    }
459
0
460
0
    // This AsyncWait() creates a ref-cycle between asyncStream and consumer:
461
0
    //
462
0
    //   asyncStream -> consumer -> (Window|Worker)StreamOwner -> asyncStream
463
0
    //
464
0
    // The cycle is broken when the stream completes or errors out and
465
0
    // asyncStream drops its reference to consumer.
466
0
    return NS_SUCCEEDED(asyncStream->AsyncWait(consumer, 0, 0, nullptr));
467
0
  }
468
469
  // nsIInputStreamCallback:
470
471
  NS_IMETHOD
472
  OnInputStreamReady(nsIAsyncInputStream* aStream) override
473
0
  {
474
0
    // Can be called on any stream. The JS API calls made below explicitly
475
0
    // support being called from any thread.
476
0
    MOZ_DIAGNOSTIC_ASSERT(!mConsumerAborted);
477
0
478
0
    nsresult rv;
479
0
480
0
    uint64_t available = 0;
481
0
    rv = aStream->Available(&available);
482
0
    if (NS_SUCCEEDED(rv) && available == 0) {
483
0
      rv = NS_BASE_STREAM_CLOSED;
484
0
    }
485
0
486
0
    if (rv == NS_BASE_STREAM_CLOSED) {
487
0
      mConsumer->streamClosed(JS::StreamConsumer::EndOfFile);
488
0
      return NS_OK;
489
0
    }
490
0
491
0
    if (NS_FAILED(rv)) {
492
0
      mConsumer->streamClosed(JS::StreamConsumer::Error);
493
0
      return NS_OK;
494
0
    }
495
0
496
0
    // Check mConsumerAborted before NS_FAILED to avoid calling streamClosed()
497
0
    // if consumeChunk() returned false per JS API contract.
498
0
    uint32_t written = 0;
499
0
    rv = aStream->ReadSegments(WriteSegment, this, available, &written);
500
0
    if (mConsumerAborted) {
501
0
      return NS_OK;
502
0
    }
503
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
504
0
      mConsumer->streamClosed(JS::StreamConsumer::Error);
505
0
      return NS_OK;
506
0
    }
507
0
508
0
    rv = aStream->AsyncWait(this, 0, 0, nullptr);
509
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
510
0
      mConsumer->streamClosed(JS::StreamConsumer::Error);
511
0
      return NS_OK;
512
0
    }
513
0
514
0
    return NS_OK;
515
0
  }
516
};
517
518
NS_IMPL_ISUPPORTS(JSStreamConsumer,
519
                  nsIInputStreamCallback)
520
521
static bool
522
ThrowException(JSContext* aCx, unsigned errorNumber)
523
0
{
524
0
  JS_ReportErrorNumberASCII(aCx, js::GetErrorMessage, nullptr, errorNumber);
525
0
  return false;
526
0
}
527
528
// static
529
bool
530
FetchUtil::StreamResponseToJS(JSContext* aCx,
531
                              JS::HandleObject aObj,
532
                              JS::MimeType aMimeType,
533
                              JS::StreamConsumer* aConsumer,
534
                              WorkerPrivate* aMaybeWorker)
535
0
{
536
0
  MOZ_ASSERT(!aMaybeWorker == NS_IsMainThread());
537
0
538
0
  RefPtr<Response> response;
539
0
  nsresult rv = UNWRAP_OBJECT(Response, aObj, response);
540
0
  if (NS_FAILED(rv)) {
541
0
    return ThrowException(aCx, JSMSG_BAD_RESPONSE_VALUE);
542
0
  }
543
0
544
0
  const char* requiredMimeType = nullptr;
545
0
  switch (aMimeType) {
546
0
    case JS::MimeType::Wasm:
547
0
      requiredMimeType = "application/wasm";
548
0
      break;
549
0
  }
550
0
551
0
  if (strcmp(requiredMimeType, response->MimeType().Data())) {
552
0
    return ThrowException(aCx, JSMSG_BAD_RESPONSE_MIME_TYPE);
553
0
  }
554
0
555
0
  if (response->Type() != ResponseType::Basic &&
556
0
      response->Type() != ResponseType::Cors &&
557
0
      response->Type() != ResponseType::Default) {
558
0
    return ThrowException(aCx, JSMSG_BAD_RESPONSE_CORS_SAME_ORIGIN);
559
0
  }
560
0
561
0
  if (!response->Ok()) {
562
0
    return ThrowException(aCx, JSMSG_BAD_RESPONSE_STATUS);
563
0
  }
564
0
565
0
  if (response->BodyUsed()) {
566
0
    return ThrowException(aCx, JSMSG_RESPONSE_ALREADY_CONSUMED);
567
0
  }
568
0
569
0
  switch (aMimeType) {
570
0
    case JS::MimeType::Wasm:
571
0
      nsAutoString url;
572
0
      response->GetUrl(url);
573
0
574
0
      IgnoredErrorResult result;
575
0
      nsCString sourceMapUrl;
576
0
      response->GetInternalHeaders()->Get(NS_LITERAL_CSTRING("SourceMap"), sourceMapUrl, result);
577
0
      if (NS_WARN_IF(result.Failed())) {
578
0
        return ThrowException(aCx, JSMSG_ERROR_CONSUMING_RESPONSE);
579
0
      }
580
0
      NS_ConvertUTF16toUTF8 urlUTF8(url);
581
0
      aConsumer->noteResponseURLs(urlUTF8.get(),
582
0
                                  sourceMapUrl.IsVoid() ? nullptr : sourceMapUrl.get());
583
0
      break;
584
0
  }
585
0
586
0
  RefPtr<InternalResponse> ir = response->GetInternalResponse();
587
0
  if (NS_WARN_IF(!ir)) {
588
0
    return ThrowException(aCx, JSMSG_OUT_OF_MEMORY);
589
0
  }
590
0
591
0
  nsCOMPtr<nsIInputStream> body;
592
0
  ir->GetUnfilteredBody(getter_AddRefs(body));
593
0
  if (!body) {
594
0
    aConsumer->streamClosed(JS::StreamConsumer::EndOfFile);
595
0
    return true;
596
0
  }
597
0
598
0
  IgnoredErrorResult error;
599
0
  response->SetBodyUsed(aCx, error);
600
0
  if (NS_WARN_IF(error.Failed())) {
601
0
    return ThrowException(aCx, JSMSG_ERROR_CONSUMING_RESPONSE);
602
0
  }
603
0
604
0
  nsIGlobalObject* global = xpc::NativeGlobal(js::UncheckedUnwrap(aObj));
605
0
606
0
  if (!JSStreamConsumer::Start(body, aConsumer, global, aMaybeWorker)) {
607
0
    return ThrowException(aCx, JSMSG_OUT_OF_MEMORY);
608
0
  }
609
0
610
0
  return true;
611
0
}
612
613
} // namespace dom
614
} // namespace mozilla