Backed out changeset 06f41c22f3a6 (bug 1888460) for causing linux xpcshell failures...
[gecko.git] / dom / fetch / Request.cpp
blobbfeae3aa79a9c95bf197f805abb638018bf61ba6
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/StaticPrefs_network.h"
16 #include "mozilla/dom/Headers.h"
17 #include "mozilla/dom/Fetch.h"
18 #include "mozilla/dom/FetchUtil.h"
19 #include "mozilla/dom/Promise.h"
20 #include "mozilla/dom/URL.h"
21 #include "mozilla/dom/WorkerPrivate.h"
22 #include "mozilla/dom/WorkerRunnable.h"
23 #include "mozilla/dom/WindowContext.h"
24 #include "mozilla/ipc/PBackgroundSharedTypes.h"
25 #include "mozilla/Unused.h"
27 #include "mozilla/dom/ReadableStreamDefaultReader.h"
29 namespace mozilla::dom {
31 NS_IMPL_ADDREF_INHERITED(Request, FetchBody<Request>)
32 NS_IMPL_RELEASE_INHERITED(Request, FetchBody<Request>)
34 // Can't use _INHERITED macro here because FetchBody<Request> is abstract.
35 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(Request)
37 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(Request, FetchBody<Request>)
38 NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner)
39 NS_IMPL_CYCLE_COLLECTION_UNLINK(mHeaders)
40 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSignal)
41 NS_IMPL_CYCLE_COLLECTION_UNLINK(mFetchStreamReader)
42 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
43 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
45 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(Request, FetchBody<Request>)
46 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner)
47 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHeaders)
48 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSignal)
49 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFetchStreamReader)
50 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
52 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Request)
53 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
54 NS_INTERFACE_MAP_END_INHERITING(FetchBody<Request>)
56 Request::Request(nsIGlobalObject* aOwner, SafeRefPtr<InternalRequest> aRequest,
57 AbortSignal* aSignal)
58 : FetchBody<Request>(aOwner), mRequest(std::move(aRequest)) {
59 MOZ_ASSERT(mRequest->Headers()->Guard() == HeadersGuardEnum::Immutable ||
60 mRequest->Headers()->Guard() == HeadersGuardEnum::Request ||
61 mRequest->Headers()->Guard() == HeadersGuardEnum::Request_no_cors);
62 if (aSignal) {
63 // If we don't have a signal as argument, we will create it when required by
64 // content, otherwise the Request's signal must follow what has been passed.
65 JS::Rooted<JS::Value> reason(RootingCx(), aSignal->RawReason());
66 mSignal = new AbortSignal(aOwner, aSignal->Aborted(), reason);
67 if (!mSignal->Aborted()) {
68 mSignal->Follow(aSignal);
73 Request::~Request() = default;
75 SafeRefPtr<InternalRequest> Request::GetInternalRequest() {
76 return mRequest.clonePtr();
79 namespace {
80 already_AddRefed<nsIURI> ParseURLFromDocument(Document* aDocument,
81 const nsAString& aInput,
82 ErrorResult& aRv) {
83 MOZ_ASSERT(aDocument);
84 MOZ_ASSERT(NS_IsMainThread());
86 // Don't use NS_ConvertUTF16toUTF8 because that doesn't let us handle OOM.
87 nsAutoCString input;
88 if (!AppendUTF16toUTF8(aInput, input, fallible)) {
89 aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
90 return nullptr;
93 nsCOMPtr<nsIURI> resolvedURI;
94 nsresult rv = NS_NewURI(getter_AddRefs(resolvedURI), input, nullptr,
95 aDocument->GetBaseURI());
96 if (NS_WARN_IF(NS_FAILED(rv))) {
97 aRv.ThrowTypeError<MSG_INVALID_URL>(input);
99 return resolvedURI.forget();
101 void GetRequestURLFromDocument(Document* aDocument, const nsAString& aInput,
102 nsAString& aRequestURL, nsACString& aURLfragment,
103 ErrorResult& aRv) {
104 nsCOMPtr<nsIURI> resolvedURI = ParseURLFromDocument(aDocument, aInput, aRv);
105 if (aRv.Failed()) {
106 return;
108 // This fails with URIs with weird protocols, even when they are valid,
109 // so we ignore the failure
110 nsAutoCString credentials;
111 Unused << resolvedURI->GetUserPass(credentials);
112 if (!credentials.IsEmpty()) {
113 aRv.ThrowTypeError<MSG_URL_HAS_CREDENTIALS>(NS_ConvertUTF16toUTF8(aInput));
114 return;
117 nsCOMPtr<nsIURI> resolvedURIClone;
118 aRv = NS_GetURIWithoutRef(resolvedURI, getter_AddRefs(resolvedURIClone));
119 if (NS_WARN_IF(aRv.Failed())) {
120 return;
122 nsAutoCString spec;
123 aRv = resolvedURIClone->GetSpec(spec);
124 if (NS_WARN_IF(aRv.Failed())) {
125 return;
127 CopyUTF8toUTF16(spec, aRequestURL);
129 // Get the fragment from nsIURI.
130 aRv = resolvedURI->GetRef(aURLfragment);
131 if (NS_WARN_IF(aRv.Failed())) {
132 return;
135 already_AddRefed<nsIURI> ParseURLFromChrome(const nsAString& aInput,
136 ErrorResult& aRv) {
137 MOZ_ASSERT(NS_IsMainThread());
138 // Don't use NS_ConvertUTF16toUTF8 because that doesn't let us handle OOM.
139 nsAutoCString input;
140 if (!AppendUTF16toUTF8(aInput, input, fallible)) {
141 aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
142 return nullptr;
145 nsCOMPtr<nsIURI> uri;
146 nsresult rv = NS_NewURI(getter_AddRefs(uri), input);
147 if (NS_FAILED(rv)) {
148 aRv.ThrowTypeError<MSG_INVALID_URL>(input);
150 return uri.forget();
152 void GetRequestURLFromChrome(const nsAString& aInput, nsAString& aRequestURL,
153 nsACString& aURLfragment, ErrorResult& aRv) {
154 nsCOMPtr<nsIURI> uri = ParseURLFromChrome(aInput, aRv);
155 if (aRv.Failed()) {
156 return;
158 // This fails with URIs with weird protocols, even when they are valid,
159 // so we ignore the failure
160 nsAutoCString credentials;
161 Unused << uri->GetUserPass(credentials);
162 if (!credentials.IsEmpty()) {
163 aRv.ThrowTypeError<MSG_URL_HAS_CREDENTIALS>(NS_ConvertUTF16toUTF8(aInput));
164 return;
167 nsCOMPtr<nsIURI> uriClone;
168 aRv = NS_GetURIWithoutRef(uri, getter_AddRefs(uriClone));
169 if (NS_WARN_IF(aRv.Failed())) {
170 return;
172 nsAutoCString spec;
173 aRv = uriClone->GetSpec(spec);
174 if (NS_WARN_IF(aRv.Failed())) {
175 return;
177 CopyUTF8toUTF16(spec, aRequestURL);
179 // Get the fragment from nsIURI.
180 aRv = uri->GetRef(aURLfragment);
181 if (NS_WARN_IF(aRv.Failed())) {
182 return;
185 already_AddRefed<URL> ParseURLFromWorker(nsIGlobalObject* aGlobal,
186 const nsAString& aInput,
187 ErrorResult& aRv) {
188 WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
189 MOZ_ASSERT(worker);
190 worker->AssertIsOnWorkerThread();
192 NS_ConvertUTF8toUTF16 baseURL(worker->GetLocationInfo().mHref);
193 RefPtr<URL> url = URL::Constructor(aGlobal, aInput, baseURL, aRv);
194 if (NS_WARN_IF(aRv.Failed())) {
195 aRv.ThrowTypeError<MSG_INVALID_URL>(NS_ConvertUTF16toUTF8(aInput));
197 return url.forget();
199 void GetRequestURLFromWorker(nsIGlobalObject* aGlobal, const nsAString& aInput,
200 nsAString& aRequestURL, nsACString& aURLfragment,
201 ErrorResult& aRv) {
202 RefPtr<URL> url = ParseURLFromWorker(aGlobal, aInput, aRv);
203 if (aRv.Failed()) {
204 return;
206 nsString username;
207 url->GetUsername(username);
209 nsString password;
210 url->GetPassword(password);
212 if (!username.IsEmpty() || !password.IsEmpty()) {
213 aRv.ThrowTypeError<MSG_URL_HAS_CREDENTIALS>(NS_ConvertUTF16toUTF8(aInput));
214 return;
217 // Get the fragment from URL.
218 nsAutoString fragment;
219 url->GetHash(fragment);
221 // Note: URL::GetHash() includes the "#" and we want the fragment with out
222 // the hash symbol.
223 if (!fragment.IsEmpty()) {
224 CopyUTF16toUTF8(Substring(fragment, 1), aURLfragment);
227 url->SetHash(u""_ns);
228 url->GetHref(aRequestURL);
231 class ReferrerSameOriginChecker final : public WorkerMainThreadRunnable {
232 public:
233 ReferrerSameOriginChecker(WorkerPrivate* aWorkerPrivate,
234 const nsAString& aReferrerURL, nsresult& aResult)
235 : WorkerMainThreadRunnable(aWorkerPrivate,
236 "Fetch :: Referrer same origin check"_ns),
237 mReferrerURL(aReferrerURL),
238 mResult(aResult) {
239 mWorkerPrivate->AssertIsOnWorkerThread();
242 bool MainThreadRun() override {
243 nsCOMPtr<nsIURI> uri;
244 if (NS_SUCCEEDED(NS_NewURI(getter_AddRefs(uri), mReferrerURL))) {
245 nsCOMPtr<nsIPrincipal> principal = mWorkerPrivate->GetPrincipal();
246 if (principal) {
247 mResult = principal->CheckMayLoad(uri,
248 /* allowIfInheritsPrincipal */ false);
251 return true;
254 private:
255 const nsString mReferrerURL;
256 nsresult& mResult;
259 } // namespace
261 /*static*/
262 SafeRefPtr<Request> Request::Constructor(const GlobalObject& aGlobal,
263 const RequestOrUSVString& aInput,
264 const RequestInit& aInit,
265 ErrorResult& aRv) {
266 nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
267 return Constructor(global, aGlobal.Context(), aInput, aInit,
268 aGlobal.CallerType(), aRv);
271 /*static*/
272 SafeRefPtr<Request> Request::Constructor(
273 nsIGlobalObject* aGlobal, JSContext* aCx, const RequestOrUSVString& aInput,
274 const RequestInit& aInit, CallerType aCallerType, 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 if (aCallerType == CallerType::System &&
355 StaticPrefs::network_fetch_systemDefaultsToOmittingCredentials()) {
356 credentials.emplace(RequestCredentials::Omit);
357 } else {
358 credentials.emplace(RequestCredentials::Same_origin);
361 if (cache.isNothing()) {
362 cache.emplace(RequestCache::Default);
366 if (aInit.IsAnyMemberPresent() && request->Mode() == RequestMode::Navigate) {
367 mode = Some(RequestMode::Same_origin);
370 if (aInit.IsAnyMemberPresent()) {
371 request->SetReferrer(
372 NS_LITERAL_STRING_FROM_CSTRING(kFETCH_CLIENT_REFERRER_STR));
373 request->SetReferrerPolicy(ReferrerPolicy::_empty);
375 if (aInit.mReferrer.WasPassed()) {
376 const nsString& referrer = aInit.mReferrer.Value();
377 if (referrer.IsEmpty()) {
378 request->SetReferrer(u""_ns);
379 } else {
380 nsAutoString referrerURL;
381 if (NS_IsMainThread()) {
382 nsCOMPtr<nsPIDOMWindowInner> inner(do_QueryInterface(aGlobal));
383 Document* doc = inner ? inner->GetExtantDoc() : nullptr;
384 nsCOMPtr<nsIURI> uri;
385 if (doc) {
386 uri = ParseURLFromDocument(doc, referrer, aRv);
387 } else {
388 // If we don't have a document, we must assume that this is a full
389 // URL.
390 uri = ParseURLFromChrome(referrer, aRv);
392 if (NS_WARN_IF(aRv.Failed())) {
393 aRv.ThrowTypeError<MSG_INVALID_REFERRER_URL>(
394 NS_ConvertUTF16toUTF8(referrer));
395 return nullptr;
397 nsAutoCString spec;
398 uri->GetSpec(spec);
399 CopyUTF8toUTF16(spec, referrerURL);
400 if (!referrerURL.EqualsLiteral(kFETCH_CLIENT_REFERRER_STR)) {
401 nsCOMPtr<nsIPrincipal> principal = aGlobal->PrincipalOrNull();
402 if (principal) {
403 nsresult rv =
404 principal->CheckMayLoad(uri,
405 /* allowIfInheritsPrincipal */ false);
406 if (NS_FAILED(rv)) {
407 referrerURL.AssignLiteral(kFETCH_CLIENT_REFERRER_STR);
411 } else {
412 RefPtr<URL> url = ParseURLFromWorker(aGlobal, referrer, aRv);
413 if (NS_WARN_IF(aRv.Failed())) {
414 aRv.ThrowTypeError<MSG_INVALID_REFERRER_URL>(
415 NS_ConvertUTF16toUTF8(referrer));
416 return nullptr;
418 url->GetHref(referrerURL);
419 if (!referrerURL.EqualsLiteral(kFETCH_CLIENT_REFERRER_STR)) {
420 WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
421 nsresult rv = NS_OK;
422 // ReferrerSameOriginChecker uses a sync loop to get the main thread
423 // to perform the same-origin check. Overall, on Workers this method
424 // can create 3 sync loops (two for constructing URLs and one here) so
425 // in the future we may want to optimize it all by off-loading all of
426 // this work in a single sync loop.
427 RefPtr<ReferrerSameOriginChecker> checker =
428 new ReferrerSameOriginChecker(worker, referrerURL, rv);
429 IgnoredErrorResult error;
430 checker->Dispatch(Canceling, error);
431 if (error.Failed() || NS_FAILED(rv)) {
432 referrerURL.AssignLiteral(kFETCH_CLIENT_REFERRER_STR);
436 request->SetReferrer(referrerURL);
440 if (aInit.mReferrerPolicy.WasPassed()) {
441 request->SetReferrerPolicy(aInit.mReferrerPolicy.Value());
444 if (aInit.mSignal.WasPassed()) {
445 signal = aInit.mSignal.Value();
448 // https://fetch.spec.whatwg.org/#dom-global-fetch
449 // https://fetch.spec.whatwg.org/#dom-request
450 // The priority of init overrides input's priority.
451 if (aInit.mPriority.WasPassed()) {
452 request->SetPriorityMode(aInit.mPriority.Value());
455 UniquePtr<mozilla::ipc::PrincipalInfo> principalInfo;
456 nsILoadInfo::CrossOriginEmbedderPolicy coep =
457 nsILoadInfo::EMBEDDER_POLICY_NULL;
459 if (NS_IsMainThread()) {
460 nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal);
461 if (window) {
462 nsCOMPtr<Document> doc;
463 doc = window->GetExtantDoc();
464 if (doc) {
465 request->SetEnvironmentReferrerPolicy(doc->GetReferrerPolicy());
467 principalInfo.reset(new mozilla::ipc::PrincipalInfo());
468 nsresult rv =
469 PrincipalToPrincipalInfo(doc->NodePrincipal(), principalInfo.get());
470 if (NS_WARN_IF(NS_FAILED(rv))) {
471 aRv.ThrowTypeError<MSG_FETCH_BODY_CONSUMED_ERROR>();
472 return nullptr;
475 if (window->GetWindowContext()) {
476 coep = window->GetWindowContext()->GetEmbedderPolicy();
479 } else {
480 WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
481 if (worker) {
482 worker->AssertIsOnWorkerThread();
483 request->SetEnvironmentReferrerPolicy(worker->GetReferrerPolicy());
484 principalInfo =
485 MakeUnique<mozilla::ipc::PrincipalInfo>(worker->GetPrincipalInfo());
486 coep = worker->GetEmbedderPolicy();
487 // For dedicated worker, the response must respect the owner's COEP.
488 if (coep == nsILoadInfo::EMBEDDER_POLICY_NULL &&
489 worker->IsDedicatedWorker()) {
490 coep = worker->GetOwnerEmbedderPolicy();
495 request->SetPrincipalInfo(std::move(principalInfo));
496 request->SetEmbedderPolicy(coep);
498 if (mode.isSome()) {
499 request->SetMode(mode.value());
502 if (credentials.isSome()) {
503 request->SetCredentialsMode(credentials.value());
506 if (cache.isSome()) {
507 if (cache.value() == RequestCache::Only_if_cached &&
508 request->Mode() != RequestMode::Same_origin) {
509 aRv.ThrowTypeError<MSG_ONLY_IF_CACHED_WITHOUT_SAME_ORIGIN>(
510 GetEnumString(request->Mode()));
511 return nullptr;
513 request->SetCacheMode(cache.value());
516 if (aInit.mRedirect.WasPassed()) {
517 request->SetRedirectMode(aInit.mRedirect.Value());
520 if (aInit.mIntegrity.WasPassed()) {
521 request->SetIntegrity(aInit.mIntegrity.Value());
524 if (aInit.mKeepalive.WasPassed()) {
525 request->SetKeepalive(aInit.mKeepalive.Value());
528 if (aInit.mMozErrors.WasPassed() && aInit.mMozErrors.Value()) {
529 request->SetMozErrors();
532 // Request constructor step 14.
533 if (aInit.mMethod.WasPassed()) {
534 nsAutoCString method(aInit.mMethod.Value());
536 // Step 14.1. Disallow forbidden methods, and anything that is not a HTTP
537 // token, since HTTP states that Method may be any of the defined values or
538 // a token (extension method).
539 nsAutoCString outMethod;
540 nsresult rv = FetchUtil::GetValidRequestMethod(method, outMethod);
541 if (NS_FAILED(rv)) {
542 aRv.ThrowTypeError<MSG_INVALID_REQUEST_METHOD>(method);
543 return nullptr;
546 // Step 14.2
547 request->SetMethod(outMethod);
550 RefPtr<InternalHeaders> requestHeaders = request->Headers();
552 RefPtr<InternalHeaders> headers;
553 if (aInit.mHeaders.WasPassed()) {
554 RefPtr<Headers> h = Headers::Create(aGlobal, aInit.mHeaders.Value(), aRv);
555 if (aRv.Failed()) {
556 return nullptr;
558 headers = h->GetInternalHeaders();
559 } else {
560 headers = new InternalHeaders(*requestHeaders);
563 requestHeaders->Clear();
564 // From "Let r be a new Request object associated with request and a new
565 // Headers object whose guard is "request"."
566 requestHeaders->SetGuard(HeadersGuardEnum::Request, aRv);
567 MOZ_ASSERT(!aRv.Failed());
569 if (request->Mode() == RequestMode::No_cors) {
570 if (!request->HasSimpleMethod()) {
571 nsAutoCString method;
572 request->GetMethod(method);
573 aRv.ThrowTypeError<MSG_INVALID_REQUEST_METHOD>(method);
574 return nullptr;
577 requestHeaders->SetGuard(HeadersGuardEnum::Request_no_cors, aRv);
578 if (aRv.Failed()) {
579 return nullptr;
583 requestHeaders->Fill(*headers, aRv);
584 if (aRv.Failed()) {
585 return nullptr;
588 if ((aInit.mBody.WasPassed() && !aInit.mBody.Value().IsNull()) ||
589 hasCopiedBody) {
590 // HEAD and GET are not allowed to have a body.
591 nsAutoCString method;
592 request->GetMethod(method);
593 // method is guaranteed to be uppercase due to step 14.2 above.
594 if (method.EqualsLiteral("HEAD") || method.EqualsLiteral("GET")) {
595 aRv.ThrowTypeError("HEAD or GET Request cannot have a body.");
596 return nullptr;
600 if (aInit.mBody.WasPassed()) {
601 const Nullable<fetch::OwningBodyInit>& bodyInitNullable =
602 aInit.mBody.Value();
603 if (!bodyInitNullable.IsNull()) {
604 const fetch::OwningBodyInit& bodyInit = bodyInitNullable.Value();
605 nsCOMPtr<nsIInputStream> stream;
606 nsAutoCString contentTypeWithCharset;
607 uint64_t contentLength = 0;
608 aRv = ExtractByteStreamFromBody(bodyInit, getter_AddRefs(stream),
609 contentTypeWithCharset, contentLength);
610 if (NS_WARN_IF(aRv.Failed())) {
611 return nullptr;
614 nsCOMPtr<nsIInputStream> temporaryBody = stream;
616 if (!contentTypeWithCharset.IsVoid() &&
617 !requestHeaders->Has("Content-Type"_ns, aRv)) {
618 requestHeaders->Append("Content-Type"_ns, contentTypeWithCharset, aRv);
621 if (NS_WARN_IF(aRv.Failed())) {
622 return nullptr;
625 if (hasCopiedBody) {
626 request->SetBody(nullptr, 0);
629 request->SetBody(temporaryBody, contentLength);
633 auto domRequest =
634 MakeSafeRefPtr<Request>(aGlobal, std::move(request), signal);
636 if (aInput.IsRequest() && !bodyFromInit) {
637 RefPtr<Request> inputReq = &aInput.GetAsRequest();
638 nsCOMPtr<nsIInputStream> body;
639 inputReq->GetBody(getter_AddRefs(body));
640 if (body) {
641 inputReq->SetBody(nullptr, 0);
642 inputReq->SetBodyUsed(aCx, aRv);
643 if (NS_WARN_IF(aRv.Failed())) {
644 return nullptr;
648 return domRequest;
651 SafeRefPtr<Request> Request::Clone(ErrorResult& aRv) {
652 if (BodyUsed()) {
653 aRv.ThrowTypeError<MSG_FETCH_BODY_CONSUMED_ERROR>();
654 return nullptr;
657 SafeRefPtr<InternalRequest> ir = mRequest->Clone();
658 if (!ir) {
659 aRv.Throw(NS_ERROR_FAILURE);
660 return nullptr;
663 return MakeSafeRefPtr<Request>(mOwner, std::move(ir), GetOrCreateSignal());
666 Headers* Request::Headers_() {
667 if (!mHeaders) {
668 mHeaders = new Headers(mOwner, mRequest->Headers());
671 return mHeaders;
674 AbortSignal* Request::GetOrCreateSignal() {
675 if (!mSignal) {
676 mSignal = new AbortSignal(mOwner, false, JS::UndefinedHandleValue);
679 return mSignal;
682 AbortSignalImpl* Request::GetSignalImpl() const { return mSignal; }
684 AbortSignalImpl* Request::GetSignalImplToConsumeBody() const {
685 // This is a hack, see Response::GetSignalImplToConsumeBody.
686 return nullptr;
689 } // namespace mozilla::dom