Bug 1857841 - pt 3. Add a new page kind named "fresh" r=glandium
[gecko.git] / dom / fetch / Request.cpp
blobe042dd62715a434fc84d204a24ab6bba30595137
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/. */
7 #include "Request.h"
9 #include "js/Value.h"
10 #include "nsIURI.h"
11 #include "nsNetUtil.h"
12 #include "nsPIDOMWindow.h"
14 #include "mozilla/ErrorResult.h"
15 #include "mozilla/dom/Headers.h"
16 #include "mozilla/dom/Fetch.h"
17 #include "mozilla/dom/FetchUtil.h"
18 #include "mozilla/dom/Promise.h"
19 #include "mozilla/dom/URL.h"
20 #include "mozilla/dom/WorkerPrivate.h"
21 #include "mozilla/dom/WorkerRunnable.h"
22 #include "mozilla/dom/WindowContext.h"
23 #include "mozilla/ipc/PBackgroundSharedTypes.h"
24 #include "mozilla/Unused.h"
26 #include "mozilla/dom/ReadableStreamDefaultReader.h"
28 namespace mozilla::dom {
30 NS_IMPL_ADDREF_INHERITED(Request, FetchBody<Request>)
31 NS_IMPL_RELEASE_INHERITED(Request, FetchBody<Request>)
33 // Can't use _INHERITED macro here because FetchBody<Request> is abstract.
34 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(Request)
36 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(Request, FetchBody<Request>)
37 NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner)
38 NS_IMPL_CYCLE_COLLECTION_UNLINK(mHeaders)
39 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSignal)
40 NS_IMPL_CYCLE_COLLECTION_UNLINK(mFetchStreamReader)
41 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
42 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
44 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(Request, FetchBody<Request>)
45 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner)
46 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHeaders)
47 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSignal)
48 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFetchStreamReader)
49 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
51 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Request)
52 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
53 NS_INTERFACE_MAP_END_INHERITING(FetchBody<Request>)
55 Request::Request(nsIGlobalObject* aOwner, SafeRefPtr<InternalRequest> aRequest,
56 AbortSignal* aSignal)
57 : FetchBody<Request>(aOwner), mRequest(std::move(aRequest)) {
58 MOZ_ASSERT(mRequest->Headers()->Guard() == HeadersGuardEnum::Immutable ||
59 mRequest->Headers()->Guard() == HeadersGuardEnum::Request ||
60 mRequest->Headers()->Guard() == HeadersGuardEnum::Request_no_cors);
61 if (aSignal) {
62 // If we don't have a signal as argument, we will create it when required by
63 // content, otherwise the Request's signal must follow what has been passed.
64 JS::Rooted<JS::Value> reason(RootingCx(), aSignal->RawReason());
65 mSignal = new AbortSignal(aOwner, aSignal->Aborted(), reason);
66 if (!mSignal->Aborted()) {
67 mSignal->Follow(aSignal);
72 Request::~Request() = default;
74 SafeRefPtr<InternalRequest> Request::GetInternalRequest() {
75 return mRequest.clonePtr();
78 namespace {
79 already_AddRefed<nsIURI> ParseURLFromDocument(Document* aDocument,
80 const nsAString& aInput,
81 ErrorResult& aRv) {
82 MOZ_ASSERT(aDocument);
83 MOZ_ASSERT(NS_IsMainThread());
85 // Don't use NS_ConvertUTF16toUTF8 because that doesn't let us handle OOM.
86 nsAutoCString input;
87 if (!AppendUTF16toUTF8(aInput, input, fallible)) {
88 aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
89 return nullptr;
92 nsCOMPtr<nsIURI> resolvedURI;
93 nsresult rv = NS_NewURI(getter_AddRefs(resolvedURI), input, nullptr,
94 aDocument->GetBaseURI());
95 if (NS_WARN_IF(NS_FAILED(rv))) {
96 aRv.ThrowTypeError<MSG_INVALID_URL>(input);
98 return resolvedURI.forget();
100 void GetRequestURLFromDocument(Document* aDocument, const nsAString& aInput,
101 nsAString& aRequestURL, nsACString& aURLfragment,
102 ErrorResult& aRv) {
103 nsCOMPtr<nsIURI> resolvedURI = ParseURLFromDocument(aDocument, aInput, aRv);
104 if (aRv.Failed()) {
105 return;
107 // This fails with URIs with weird protocols, even when they are valid,
108 // so we ignore the failure
109 nsAutoCString credentials;
110 Unused << resolvedURI->GetUserPass(credentials);
111 if (!credentials.IsEmpty()) {
112 aRv.ThrowTypeError<MSG_URL_HAS_CREDENTIALS>(NS_ConvertUTF16toUTF8(aInput));
113 return;
116 nsCOMPtr<nsIURI> resolvedURIClone;
117 aRv = NS_GetURIWithoutRef(resolvedURI, getter_AddRefs(resolvedURIClone));
118 if (NS_WARN_IF(aRv.Failed())) {
119 return;
121 nsAutoCString spec;
122 aRv = resolvedURIClone->GetSpec(spec);
123 if (NS_WARN_IF(aRv.Failed())) {
124 return;
126 CopyUTF8toUTF16(spec, aRequestURL);
128 // Get the fragment from nsIURI.
129 aRv = resolvedURI->GetRef(aURLfragment);
130 if (NS_WARN_IF(aRv.Failed())) {
131 return;
134 already_AddRefed<nsIURI> ParseURLFromChrome(const nsAString& aInput,
135 ErrorResult& aRv) {
136 MOZ_ASSERT(NS_IsMainThread());
137 // Don't use NS_ConvertUTF16toUTF8 because that doesn't let us handle OOM.
138 nsAutoCString input;
139 if (!AppendUTF16toUTF8(aInput, input, fallible)) {
140 aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
141 return nullptr;
144 nsCOMPtr<nsIURI> uri;
145 nsresult rv = NS_NewURI(getter_AddRefs(uri), input);
146 if (NS_FAILED(rv)) {
147 aRv.ThrowTypeError<MSG_INVALID_URL>(input);
149 return uri.forget();
151 void GetRequestURLFromChrome(const nsAString& aInput, nsAString& aRequestURL,
152 nsACString& aURLfragment, ErrorResult& aRv) {
153 nsCOMPtr<nsIURI> uri = ParseURLFromChrome(aInput, aRv);
154 if (aRv.Failed()) {
155 return;
157 // This fails with URIs with weird protocols, even when they are valid,
158 // so we ignore the failure
159 nsAutoCString credentials;
160 Unused << uri->GetUserPass(credentials);
161 if (!credentials.IsEmpty()) {
162 aRv.ThrowTypeError<MSG_URL_HAS_CREDENTIALS>(NS_ConvertUTF16toUTF8(aInput));
163 return;
166 nsCOMPtr<nsIURI> uriClone;
167 aRv = NS_GetURIWithoutRef(uri, getter_AddRefs(uriClone));
168 if (NS_WARN_IF(aRv.Failed())) {
169 return;
171 nsAutoCString spec;
172 aRv = uriClone->GetSpec(spec);
173 if (NS_WARN_IF(aRv.Failed())) {
174 return;
176 CopyUTF8toUTF16(spec, aRequestURL);
178 // Get the fragment from nsIURI.
179 aRv = uri->GetRef(aURLfragment);
180 if (NS_WARN_IF(aRv.Failed())) {
181 return;
184 already_AddRefed<URL> ParseURLFromWorker(nsIGlobalObject* aGlobal,
185 const nsAString& aInput,
186 ErrorResult& aRv) {
187 WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
188 MOZ_ASSERT(worker);
189 worker->AssertIsOnWorkerThread();
191 NS_ConvertUTF8toUTF16 baseURL(worker->GetLocationInfo().mHref);
192 RefPtr<URL> url = URL::Constructor(aGlobal, aInput, baseURL, aRv);
193 if (NS_WARN_IF(aRv.Failed())) {
194 aRv.ThrowTypeError<MSG_INVALID_URL>(NS_ConvertUTF16toUTF8(aInput));
196 return url.forget();
198 void GetRequestURLFromWorker(nsIGlobalObject* aGlobal, const nsAString& aInput,
199 nsAString& aRequestURL, nsACString& aURLfragment,
200 ErrorResult& aRv) {
201 RefPtr<URL> url = ParseURLFromWorker(aGlobal, aInput, aRv);
202 if (aRv.Failed()) {
203 return;
205 nsString username;
206 url->GetUsername(username);
208 nsString password;
209 url->GetPassword(password);
211 if (!username.IsEmpty() || !password.IsEmpty()) {
212 aRv.ThrowTypeError<MSG_URL_HAS_CREDENTIALS>(NS_ConvertUTF16toUTF8(aInput));
213 return;
216 // Get the fragment from URL.
217 nsAutoString fragment;
218 url->GetHash(fragment);
220 // Note: URL::GetHash() includes the "#" and we want the fragment with out
221 // the hash symbol.
222 if (!fragment.IsEmpty()) {
223 CopyUTF16toUTF8(Substring(fragment, 1), aURLfragment);
226 url->SetHash(u""_ns);
227 url->GetHref(aRequestURL);
230 class ReferrerSameOriginChecker final : public WorkerMainThreadRunnable {
231 public:
232 ReferrerSameOriginChecker(WorkerPrivate* aWorkerPrivate,
233 const nsAString& aReferrerURL, nsresult& aResult)
234 : WorkerMainThreadRunnable(aWorkerPrivate,
235 "Fetch :: Referrer same origin check"_ns),
236 mReferrerURL(aReferrerURL),
237 mResult(aResult) {
238 mWorkerPrivate->AssertIsOnWorkerThread();
241 bool MainThreadRun() override {
242 nsCOMPtr<nsIURI> uri;
243 if (NS_SUCCEEDED(NS_NewURI(getter_AddRefs(uri), mReferrerURL))) {
244 nsCOMPtr<nsIPrincipal> principal = mWorkerPrivate->GetPrincipal();
245 if (principal) {
246 mResult = principal->CheckMayLoad(uri,
247 /* allowIfInheritsPrincipal */ false);
250 return true;
253 private:
254 const nsString mReferrerURL;
255 nsresult& mResult;
258 } // namespace
260 /*static*/
261 SafeRefPtr<Request> Request::Constructor(const GlobalObject& aGlobal,
262 const RequestOrUSVString& aInput,
263 const RequestInit& aInit,
264 ErrorResult& aRv) {
265 nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
266 return Constructor(global, aGlobal.Context(), aInput, aInit, aRv);
269 /*static*/
270 SafeRefPtr<Request> Request::Constructor(nsIGlobalObject* aGlobal,
271 JSContext* aCx,
272 const RequestOrUSVString& aInput,
273 const RequestInit& aInit,
274 ErrorResult& aRv) {
275 bool hasCopiedBody = false;
276 SafeRefPtr<InternalRequest> request;
278 RefPtr<AbortSignal> signal;
279 bool bodyFromInit = false;
281 if (aInput.IsRequest()) {
282 RefPtr<Request> inputReq = &aInput.GetAsRequest();
283 nsCOMPtr<nsIInputStream> body;
285 if (aInit.mBody.WasPassed() && !aInit.mBody.Value().IsNull()) {
286 bodyFromInit = true;
287 hasCopiedBody = true;
288 } else {
289 inputReq->GetBody(getter_AddRefs(body));
290 if (inputReq->BodyUsed()) {
291 aRv.ThrowTypeError<MSG_FETCH_BODY_CONSUMED_ERROR>();
292 return nullptr;
295 // The body will be copied when GetRequestConstructorCopy() is executed.
296 if (body) {
297 hasCopiedBody = true;
301 request = inputReq->GetInternalRequest();
302 signal = inputReq->GetOrCreateSignal();
303 } else {
304 // aInput is USVString.
305 // We need to get url before we create a InternalRequest.
306 nsAutoString input;
307 input.Assign(aInput.GetAsUSVString());
308 nsAutoString requestURL;
309 nsCString fragment;
310 if (NS_IsMainThread()) {
311 nsCOMPtr<nsPIDOMWindowInner> inner(do_QueryInterface(aGlobal));
312 Document* doc = inner ? inner->GetExtantDoc() : nullptr;
313 if (doc) {
314 GetRequestURLFromDocument(doc, input, requestURL, fragment, aRv);
315 } else {
316 // If we don't have a document, we must assume that this is a full URL.
317 GetRequestURLFromChrome(input, requestURL, fragment, aRv);
319 } else {
320 GetRequestURLFromWorker(aGlobal, input, requestURL, fragment, aRv);
322 if (aRv.Failed()) {
323 return nullptr;
325 request = MakeSafeRefPtr<InternalRequest>(NS_ConvertUTF16toUTF8(requestURL),
326 fragment);
328 request = request->GetRequestConstructorCopy(aGlobal, aRv);
329 if (NS_WARN_IF(aRv.Failed())) {
330 return nullptr;
332 Maybe<RequestMode> mode;
333 if (aInit.mMode.WasPassed()) {
334 if (aInit.mMode.Value() == RequestMode::Navigate) {
335 aRv.ThrowTypeError<MSG_INVALID_REQUEST_MODE>("navigate");
336 return nullptr;
339 mode.emplace(aInit.mMode.Value());
341 Maybe<RequestCredentials> credentials;
342 if (aInit.mCredentials.WasPassed()) {
343 credentials.emplace(aInit.mCredentials.Value());
345 Maybe<RequestCache> cache;
346 if (aInit.mCache.WasPassed()) {
347 cache.emplace(aInit.mCache.Value());
349 if (aInput.IsUSVString()) {
350 if (mode.isNothing()) {
351 mode.emplace(RequestMode::Cors);
353 if (credentials.isNothing()) {
354 credentials.emplace(RequestCredentials::Same_origin);
356 if (cache.isNothing()) {
357 cache.emplace(RequestCache::Default);
361 if (aInit.IsAnyMemberPresent() && request->Mode() == RequestMode::Navigate) {
362 mode = Some(RequestMode::Same_origin);
365 if (aInit.IsAnyMemberPresent()) {
366 request->SetReferrer(
367 NS_LITERAL_STRING_FROM_CSTRING(kFETCH_CLIENT_REFERRER_STR));
368 request->SetReferrerPolicy(ReferrerPolicy::_empty);
370 if (aInit.mReferrer.WasPassed()) {
371 const nsString& referrer = aInit.mReferrer.Value();
372 if (referrer.IsEmpty()) {
373 request->SetReferrer(u""_ns);
374 } else {
375 nsAutoString referrerURL;
376 if (NS_IsMainThread()) {
377 nsCOMPtr<nsPIDOMWindowInner> inner(do_QueryInterface(aGlobal));
378 Document* doc = inner ? inner->GetExtantDoc() : nullptr;
379 nsCOMPtr<nsIURI> uri;
380 if (doc) {
381 uri = ParseURLFromDocument(doc, referrer, aRv);
382 } else {
383 // If we don't have a document, we must assume that this is a full
384 // URL.
385 uri = ParseURLFromChrome(referrer, aRv);
387 if (NS_WARN_IF(aRv.Failed())) {
388 aRv.ThrowTypeError<MSG_INVALID_REFERRER_URL>(
389 NS_ConvertUTF16toUTF8(referrer));
390 return nullptr;
392 nsAutoCString spec;
393 uri->GetSpec(spec);
394 CopyUTF8toUTF16(spec, referrerURL);
395 if (!referrerURL.EqualsLiteral(kFETCH_CLIENT_REFERRER_STR)) {
396 nsCOMPtr<nsIPrincipal> principal = aGlobal->PrincipalOrNull();
397 if (principal) {
398 nsresult rv =
399 principal->CheckMayLoad(uri,
400 /* allowIfInheritsPrincipal */ false);
401 if (NS_FAILED(rv)) {
402 referrerURL.AssignLiteral(kFETCH_CLIENT_REFERRER_STR);
406 } else {
407 RefPtr<URL> url = ParseURLFromWorker(aGlobal, referrer, aRv);
408 if (NS_WARN_IF(aRv.Failed())) {
409 aRv.ThrowTypeError<MSG_INVALID_REFERRER_URL>(
410 NS_ConvertUTF16toUTF8(referrer));
411 return nullptr;
413 url->GetHref(referrerURL);
414 if (!referrerURL.EqualsLiteral(kFETCH_CLIENT_REFERRER_STR)) {
415 WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
416 nsresult rv = NS_OK;
417 // ReferrerSameOriginChecker uses a sync loop to get the main thread
418 // to perform the same-origin check. Overall, on Workers this method
419 // can create 3 sync loops (two for constructing URLs and one here) so
420 // in the future we may want to optimize it all by off-loading all of
421 // this work in a single sync loop.
422 RefPtr<ReferrerSameOriginChecker> checker =
423 new ReferrerSameOriginChecker(worker, referrerURL, rv);
424 IgnoredErrorResult error;
425 checker->Dispatch(Canceling, error);
426 if (error.Failed() || NS_FAILED(rv)) {
427 referrerURL.AssignLiteral(kFETCH_CLIENT_REFERRER_STR);
431 request->SetReferrer(referrerURL);
435 if (aInit.mReferrerPolicy.WasPassed()) {
436 request->SetReferrerPolicy(aInit.mReferrerPolicy.Value());
439 if (aInit.mSignal.WasPassed()) {
440 signal = aInit.mSignal.Value();
443 // https://fetch.spec.whatwg.org/#dom-global-fetch
444 // https://fetch.spec.whatwg.org/#dom-request
445 // The priority of init overrides input's priority.
446 if (aInit.mPriority.WasPassed()) {
447 request->SetPriorityMode(aInit.mPriority.Value());
450 UniquePtr<mozilla::ipc::PrincipalInfo> principalInfo;
451 nsILoadInfo::CrossOriginEmbedderPolicy coep =
452 nsILoadInfo::EMBEDDER_POLICY_NULL;
454 if (NS_IsMainThread()) {
455 nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal);
456 if (window) {
457 nsCOMPtr<Document> doc;
458 doc = window->GetExtantDoc();
459 if (doc) {
460 request->SetEnvironmentReferrerPolicy(doc->GetReferrerPolicy());
462 principalInfo.reset(new mozilla::ipc::PrincipalInfo());
463 nsresult rv =
464 PrincipalToPrincipalInfo(doc->NodePrincipal(), principalInfo.get());
465 if (NS_WARN_IF(NS_FAILED(rv))) {
466 aRv.ThrowTypeError<MSG_FETCH_BODY_CONSUMED_ERROR>();
467 return nullptr;
470 if (window->GetWindowContext()) {
471 coep = window->GetWindowContext()->GetEmbedderPolicy();
474 } else {
475 WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
476 if (worker) {
477 worker->AssertIsOnWorkerThread();
478 request->SetEnvironmentReferrerPolicy(worker->GetReferrerPolicy());
479 principalInfo =
480 MakeUnique<mozilla::ipc::PrincipalInfo>(worker->GetPrincipalInfo());
481 coep = worker->GetEmbedderPolicy();
482 // For dedicated worker, the response must respect the owner's COEP.
483 if (coep == nsILoadInfo::EMBEDDER_POLICY_NULL &&
484 worker->IsDedicatedWorker()) {
485 coep = worker->GetOwnerEmbedderPolicy();
490 request->SetPrincipalInfo(std::move(principalInfo));
491 request->SetEmbedderPolicy(coep);
493 if (mode.isSome()) {
494 request->SetMode(mode.value());
497 if (credentials.isSome()) {
498 request->SetCredentialsMode(credentials.value());
501 if (cache.isSome()) {
502 if (cache.value() == RequestCache::Only_if_cached &&
503 request->Mode() != RequestMode::Same_origin) {
504 aRv.ThrowTypeError<MSG_ONLY_IF_CACHED_WITHOUT_SAME_ORIGIN>(
505 GetEnumString(request->Mode()));
506 return nullptr;
508 request->SetCacheMode(cache.value());
511 if (aInit.mRedirect.WasPassed()) {
512 request->SetRedirectMode(aInit.mRedirect.Value());
515 if (aInit.mIntegrity.WasPassed()) {
516 request->SetIntegrity(aInit.mIntegrity.Value());
519 if (aInit.mMozErrors.WasPassed() && aInit.mMozErrors.Value()) {
520 request->SetMozErrors();
523 // Request constructor step 14.
524 if (aInit.mMethod.WasPassed()) {
525 nsAutoCString method(aInit.mMethod.Value());
527 // Step 14.1. Disallow forbidden methods, and anything that is not a HTTP
528 // token, since HTTP states that Method may be any of the defined values or
529 // a token (extension method).
530 nsAutoCString outMethod;
531 nsresult rv = FetchUtil::GetValidRequestMethod(method, outMethod);
532 if (NS_FAILED(rv)) {
533 aRv.ThrowTypeError<MSG_INVALID_REQUEST_METHOD>(method);
534 return nullptr;
537 // Step 14.2
538 request->SetMethod(outMethod);
541 RefPtr<InternalHeaders> requestHeaders = request->Headers();
543 RefPtr<InternalHeaders> headers;
544 if (aInit.mHeaders.WasPassed()) {
545 RefPtr<Headers> h = Headers::Create(aGlobal, aInit.mHeaders.Value(), aRv);
546 if (aRv.Failed()) {
547 return nullptr;
549 headers = h->GetInternalHeaders();
550 } else {
551 headers = new InternalHeaders(*requestHeaders);
554 requestHeaders->Clear();
555 // From "Let r be a new Request object associated with request and a new
556 // Headers object whose guard is "request"."
557 requestHeaders->SetGuard(HeadersGuardEnum::Request, aRv);
558 MOZ_ASSERT(!aRv.Failed());
560 if (request->Mode() == RequestMode::No_cors) {
561 if (!request->HasSimpleMethod()) {
562 nsAutoCString method;
563 request->GetMethod(method);
564 aRv.ThrowTypeError<MSG_INVALID_REQUEST_METHOD>(method);
565 return nullptr;
568 requestHeaders->SetGuard(HeadersGuardEnum::Request_no_cors, aRv);
569 if (aRv.Failed()) {
570 return nullptr;
574 requestHeaders->Fill(*headers, aRv);
575 if (aRv.Failed()) {
576 return nullptr;
579 if ((aInit.mBody.WasPassed() && !aInit.mBody.Value().IsNull()) ||
580 hasCopiedBody) {
581 // HEAD and GET are not allowed to have a body.
582 nsAutoCString method;
583 request->GetMethod(method);
584 // method is guaranteed to be uppercase due to step 14.2 above.
585 if (method.EqualsLiteral("HEAD") || method.EqualsLiteral("GET")) {
586 aRv.ThrowTypeError("HEAD or GET Request cannot have a body.");
587 return nullptr;
591 if (aInit.mBody.WasPassed()) {
592 const Nullable<fetch::OwningBodyInit>& bodyInitNullable =
593 aInit.mBody.Value();
594 if (!bodyInitNullable.IsNull()) {
595 const fetch::OwningBodyInit& bodyInit = bodyInitNullable.Value();
596 nsCOMPtr<nsIInputStream> stream;
597 nsAutoCString contentTypeWithCharset;
598 uint64_t contentLength = 0;
599 aRv = ExtractByteStreamFromBody(bodyInit, getter_AddRefs(stream),
600 contentTypeWithCharset, contentLength);
601 if (NS_WARN_IF(aRv.Failed())) {
602 return nullptr;
605 nsCOMPtr<nsIInputStream> temporaryBody = stream;
607 if (!contentTypeWithCharset.IsVoid() &&
608 !requestHeaders->Has("Content-Type"_ns, aRv)) {
609 requestHeaders->Append("Content-Type"_ns, contentTypeWithCharset, aRv);
612 if (NS_WARN_IF(aRv.Failed())) {
613 return nullptr;
616 if (hasCopiedBody) {
617 request->SetBody(nullptr, 0);
620 request->SetBody(temporaryBody, contentLength);
624 auto domRequest =
625 MakeSafeRefPtr<Request>(aGlobal, std::move(request), signal);
627 if (aInput.IsRequest() && !bodyFromInit) {
628 RefPtr<Request> inputReq = &aInput.GetAsRequest();
629 nsCOMPtr<nsIInputStream> body;
630 inputReq->GetBody(getter_AddRefs(body));
631 if (body) {
632 inputReq->SetBody(nullptr, 0);
633 inputReq->SetBodyUsed(aCx, aRv);
634 if (NS_WARN_IF(aRv.Failed())) {
635 return nullptr;
639 return domRequest;
642 SafeRefPtr<Request> Request::Clone(ErrorResult& aRv) {
643 if (BodyUsed()) {
644 aRv.ThrowTypeError<MSG_FETCH_BODY_CONSUMED_ERROR>();
645 return nullptr;
648 SafeRefPtr<InternalRequest> ir = mRequest->Clone();
649 if (!ir) {
650 aRv.Throw(NS_ERROR_FAILURE);
651 return nullptr;
654 return MakeSafeRefPtr<Request>(mOwner, std::move(ir), GetOrCreateSignal());
657 Headers* Request::Headers_() {
658 if (!mHeaders) {
659 mHeaders = new Headers(mOwner, mRequest->Headers());
662 return mHeaders;
665 AbortSignal* Request::GetOrCreateSignal() {
666 if (!mSignal) {
667 mSignal = new AbortSignal(mOwner, false, JS::UndefinedHandleValue);
670 return mSignal;
673 AbortSignalImpl* Request::GetSignalImpl() const { return mSignal; }
675 AbortSignalImpl* Request::GetSignalImplToConsumeBody() const {
676 // This is a hack, see Response::GetSignalImplToConsumeBody.
677 return nullptr;
680 } // namespace mozilla::dom