1use std::cell::Cell;
6use std::rc::Rc;
7use std::time::Duration;
8
9use base::id::WebViewId;
10use ipc_channel::ipc;
11use js::jsapi::{ExceptionStackBehavior, JS_IsExceptionPending};
12use js::jsval::UndefinedValue;
13use js::realm::CurrentRealm;
14use js::rust::HandleValue;
15use js::rust::wrappers::JS_SetPendingException;
16use net_traits::request::{
17 CorsSettings, CredentialsMode, Destination, Referrer, Request as NetTraitsRequest,
18 RequestBuilder, RequestId, RequestMode, ServiceWorkersMode,
19};
20use net_traits::{
21 CoreResourceMsg, CoreResourceThread, FetchChannels, FetchMetadata, FetchResponseMsg,
22 FilteredMetadata, Metadata, NetworkError, ResourceFetchTiming, cancel_async_fetch,
23};
24use rustc_hash::FxHashMap;
25use script_bindings::cformat;
26use serde::{Deserialize, Serialize};
27use servo_url::ServoUrl;
28use timers::TimerEventRequest;
29use uuid::Uuid;
30
31use crate::body::BodyMixin;
32use crate::dom::abortsignal::AbortAlgorithm;
33use crate::dom::bindings::codegen::Bindings::AbortSignalBinding::AbortSignalMethods;
34use crate::dom::bindings::codegen::Bindings::RequestBinding::{
35 RequestInfo, RequestInit, RequestMethods,
36};
37use crate::dom::bindings::codegen::Bindings::ResponseBinding::Response_Binding::ResponseMethods;
38use crate::dom::bindings::codegen::Bindings::ResponseBinding::ResponseType as DOMResponseType;
39use crate::dom::bindings::codegen::Bindings::WindowBinding::{DeferredRequestInit, WindowMethods};
40use crate::dom::bindings::error::{Error, Fallible};
41use crate::dom::bindings::inheritance::Castable;
42use crate::dom::bindings::num::Finite;
43use crate::dom::bindings::refcounted::{Trusted, TrustedPromise};
44use crate::dom::bindings::reflector::DomGlobal;
45use crate::dom::bindings::root::DomRoot;
46use crate::dom::bindings::trace::RootedTraceableBox;
47use crate::dom::csp::{GlobalCspReporting, Violation};
48use crate::dom::fetchlaterresult::FetchLaterResult;
49use crate::dom::globalscope::GlobalScope;
50use crate::dom::headers::Guard;
51use crate::dom::performance::performanceresourcetiming::InitiatorType;
52use crate::dom::promise::Promise;
53use crate::dom::request::Request;
54use crate::dom::response::Response;
55use crate::dom::serviceworkerglobalscope::ServiceWorkerGlobalScope;
56use crate::dom::window::Window;
57use crate::network_listener::{
58 self, FetchResponseListener, NetworkListener, ResourceTimingListener, submit_timing_data,
59};
60use crate::realms::{enter_auto_realm, enter_realm};
61use crate::script_runtime::CanGc;
62
63#[derive(Default, JSTraceable, MallocSizeOf)]
68pub(crate) struct FetchCanceller {
69 #[no_trace]
70 request_id: Option<RequestId>,
71 #[no_trace]
72 core_resource_thread: Option<CoreResourceThread>,
73 keep_alive: bool,
74}
75
76impl FetchCanceller {
77 pub(crate) fn new(
80 request_id: RequestId,
81 keep_alive: bool,
82 core_resource_thread: CoreResourceThread,
83 ) -> Self {
84 Self {
85 request_id: Some(request_id),
86 core_resource_thread: Some(core_resource_thread),
87 keep_alive,
88 }
89 }
90
91 pub(crate) fn keep_alive(&self) -> bool {
92 self.keep_alive
93 }
94
95 fn cancel(&mut self) {
96 if let Some(request_id) = self.request_id.take() {
97 if let Some(ref core_resource_thread) = self.core_resource_thread {
101 cancel_async_fetch(vec![request_id], core_resource_thread);
104 }
105 }
106 }
107
108 pub(crate) fn ignore(&mut self) {
111 let _ = self.request_id.take();
112 }
113
114 pub(crate) fn abort(&mut self) {
116 self.cancel();
117 }
118
119 pub(crate) fn terminate(&mut self) {
121 self.cancel();
122 }
123}
124
125#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
126pub(crate) struct DeferredFetchRecordId(Uuid);
128
129impl Default for DeferredFetchRecordId {
130 fn default() -> Self {
131 Self(Uuid::new_v4())
132 }
133}
134
135pub(crate) type QueuedDeferredFetchRecord = Rc<DeferredFetchRecord>;
136
137#[derive(Default, MallocSizeOf)]
139pub(crate) struct FetchGroup {
140 #[conditional_malloc_size_of]
142 pub(crate) deferred_fetch_records: FxHashMap<DeferredFetchRecordId, QueuedDeferredFetchRecord>,
143}
144
145fn request_init_from_request(request: NetTraitsRequest, global: &GlobalScope) -> RequestBuilder {
146 let mut builder =
147 RequestBuilder::new(request.target_webview_id, request.url(), request.referrer)
148 .method(request.method)
149 .headers(request.headers)
150 .unsafe_request(request.unsafe_request)
151 .body(request.body)
152 .destination(request.destination)
153 .synchronous(request.synchronous)
154 .mode(request.mode)
155 .cache_mode(request.cache_mode)
156 .use_cors_preflight(request.use_cors_preflight)
157 .credentials_mode(request.credentials_mode)
158 .use_url_credentials(request.use_url_credentials)
159 .referrer_policy(request.referrer_policy)
160 .pipeline_id(request.pipeline_id)
161 .redirect_mode(request.redirect_mode)
162 .integrity_metadata(request.integrity_metadata)
163 .cryptographic_nonce_metadata(request.cryptographic_nonce_metadata)
164 .parser_metadata(request.parser_metadata)
165 .initiator(request.initiator)
166 .client(global.request_client())
167 .insecure_requests_policy(request.insecure_requests_policy)
168 .has_trustworthy_ancestor_origin(request.has_trustworthy_ancestor_origin)
169 .https_state(request.https_state)
170 .response_tainting(request.response_tainting);
171 builder.id = request.id;
172 builder
173}
174
175fn abort_fetch_call(
177 promise: Rc<Promise>,
178 request: &Request,
179 response_object: Option<&Response>,
180 abort_reason: HandleValue,
181 global: &GlobalScope,
182 cx: &mut js::context::JSContext,
183) {
184 promise.reject(cx.into(), abort_reason, CanGc::from_cx(cx));
186 if let Some(body) = request.body() {
188 if body.is_readable() {
189 body.cancel(cx, global, abort_reason);
190 }
191 }
192 let Some(response) = response_object else {
195 return;
196 };
197 if let Some(body) = response.body() {
199 if body.is_readable() {
200 body.error(abort_reason, CanGc::from_cx(cx));
201 }
202 }
203}
204
205#[expect(non_snake_case)]
207pub(crate) fn Fetch(
208 global: &GlobalScope,
209 input: RequestInfo,
210 init: RootedTraceableBox<RequestInit>,
211 cx: &mut CurrentRealm,
212) -> Rc<Promise> {
213 let promise = Promise::new_in_realm(cx);
215
216 let response = Response::new(global, CanGc::from_cx(cx));
219 response
220 .Headers(CanGc::from_cx(cx))
221 .set_guard(Guard::Immutable);
222
223 let request_object = match Request::Constructor(global, None, CanGc::from_cx(cx), input, init) {
226 Err(e) => {
227 response.error_stream(e.clone(), CanGc::from_cx(cx));
228 promise.reject_error(e, CanGc::from_cx(cx));
229 return promise;
230 },
231 Ok(r) => r,
232 };
233 let request = request_object.get_request();
235 let request_id = request.id;
236
237 let signal = request_object.Signal();
239 if signal.aborted() {
240 rooted!(&in(cx) let mut abort_reason = UndefinedValue());
242 signal.Reason(cx.into(), abort_reason.handle_mut());
243 abort_fetch_call(
244 promise.clone(),
245 &request_object,
246 None,
247 abort_reason.handle(),
248 global,
249 cx,
250 );
251 return promise;
253 }
254
255 let keep_alive = request.keep_alive;
256 let mut request_init = request_init_from_request(request, global);
259
260 if global.is::<ServiceWorkerGlobalScope>() {
263 request_init.service_workers_mode = ServiceWorkersMode::None;
264 }
265
266 let fetch_context = FetchContext {
273 fetch_promise: Some(TrustedPromise::new(promise.clone())),
274 response_object: Trusted::new(&*response),
275 request: Trusted::new(&*request_object),
276 global: Trusted::new(global),
277 locally_aborted: false,
278 canceller: FetchCanceller::new(request_id, keep_alive, global.core_resource_thread()),
279 url: request_init.url.clone(),
280 };
281 let network_listener = NetworkListener::new(
282 fetch_context,
283 global.task_manager().networking_task_source().to_sendable(),
284 );
285 let fetch_context = network_listener.context.clone();
286
287 signal.add(&AbortAlgorithm::Fetch(fetch_context));
289
290 global.fetch_with_network_listener(request_init, network_listener);
293
294 promise
296}
297
298fn queue_deferred_fetch(
300 request: NetTraitsRequest,
301 activate_after: Finite<f64>,
302 global: &GlobalScope,
303) -> DeferredFetchRecordId {
304 let trusted_global = Trusted::new(global);
305 let mut request = request;
306 request.client = Some(global.request_client());
308 request.populate_request_from_client();
309 request.service_workers_mode = ServiceWorkersMode::None;
311 request.keep_alive = true;
313 let deferred_record = Rc::new(DeferredFetchRecord {
315 request,
316 invoke_state: Cell::new(DeferredFetchRecordInvokeState::Pending),
317 activated: Cell::new(false),
318 });
319 let deferred_fetch_record_id = global.append_deferred_fetch(deferred_record);
321 global.schedule_timer(TimerEventRequest {
323 callback: Box::new(move || {
324 let global = trusted_global.root();
326 global.deferred_fetch_record_for_id(&deferred_fetch_record_id).process(&global);
327
328 let trusted_global = trusted_global.clone();
333 global.task_manager().deferred_fetch_task_source().queue(
334 task!(notify_deferred_record: move || {
335 trusted_global.root().deferred_fetch_record_for_id(&deferred_fetch_record_id).activate();
336 }),
337 );
338 }),
339 duration: Duration::from_millis(*activate_after as u64),
341 });
342 deferred_fetch_record_id
344}
345
346#[expect(non_snake_case, unsafe_code)]
348pub(crate) fn FetchLater(
349 window: &Window,
350 input: RequestInfo,
351 init: RootedTraceableBox<DeferredRequestInit>,
352 can_gc: CanGc,
353) -> Fallible<DomRoot<FetchLaterResult>> {
354 let global_scope = window.upcast();
355 let document = window.Document();
356 let request_object = Request::constructor(global_scope, None, can_gc, input, &init.parent)?;
359 let signal = request_object.Signal();
361 if signal.aborted() {
362 let cx = GlobalScope::get_cx();
363 rooted!(in(*cx) let mut abort_reason = UndefinedValue());
364 signal.Reason(cx, abort_reason.handle_mut());
365 unsafe {
366 assert!(!JS_IsExceptionPending(*cx));
367 JS_SetPendingException(*cx, abort_reason.handle(), ExceptionStackBehavior::Capture);
368 }
369 return Err(Error::JSFailed);
370 }
371 let request = request_object.get_request();
373 let mut activate_after = Finite::wrap(0_f64);
375 if let Some(init_activate_after) = init.activateAfter.as_ref() {
378 activate_after = *init_activate_after;
379 }
380 if *activate_after < 0.0 {
382 return Err(Error::Range(c"activateAfter must be at least 0".to_owned()));
383 }
384 if !document.is_fully_active() {
386 return Err(Error::Type(c"Document is not fully active".to_owned()));
387 }
388 let url = request.url();
389 if !matches!(url.scheme(), "http" | "https") {
391 return Err(Error::Type(c"URL is not http(s)".to_owned()));
392 }
393 if !url.is_potentially_trustworthy() {
395 return Err(Error::Type(c"URL is not trustworthy".to_owned()));
396 }
397 if let Some(body) = request.body.as_ref() {
399 if body.len().is_none_or(|len| len == 0) {
400 return Err(Error::Type(c"Body is empty".to_owned()));
401 }
402 }
403 let quota = document.available_deferred_fetch_quota(request.url().origin());
406 let requested = request.total_request_length() as isize;
407 if quota < requested {
408 return Err(Error::QuotaExceeded {
409 quota: Some(Finite::wrap(quota as f64)),
410 requested: Some(Finite::wrap(requested as f64)),
411 });
412 }
413 let deferred_record_id = queue_deferred_fetch(request, activate_after, global_scope);
417 signal.add(&AbortAlgorithm::FetchLater(deferred_record_id));
419 Ok(FetchLaterResult::new(window, deferred_record_id, can_gc))
421}
422
423#[derive(Clone, Copy, MallocSizeOf, PartialEq)]
425pub(crate) enum DeferredFetchRecordInvokeState {
426 Pending,
427 Sent,
428 Aborted,
429}
430
431#[derive(MallocSizeOf)]
433pub(crate) struct DeferredFetchRecord {
434 pub(crate) request: NetTraitsRequest,
436 pub(crate) invoke_state: Cell<DeferredFetchRecordInvokeState>,
438 activated: Cell<bool>,
439}
440
441impl DeferredFetchRecord {
442 fn activate(&self) {
444 self.activated.set(true);
446 }
447 pub(crate) fn abort(&self) {
449 self.invoke_state
451 .set(DeferredFetchRecordInvokeState::Aborted);
452 }
453 pub(crate) fn activated_getter_steps(&self) -> bool {
455 self.activated.get()
457 }
458 pub(crate) fn process(&self, global: &GlobalScope) {
460 if self.invoke_state.get() != DeferredFetchRecordInvokeState::Pending {
462 return;
463 }
464 self.invoke_state.set(DeferredFetchRecordInvokeState::Sent);
466 let fetch_later_listener = FetchLaterListener {
468 url: self.request.url(),
469 global: Trusted::new(global),
470 };
471 let request_init = request_init_from_request(self.request.clone(), global);
472 global.fetch(
473 request_init,
474 fetch_later_listener,
475 global.task_manager().networking_task_source().to_sendable(),
476 );
477 }
479}
480
481#[derive(JSTraceable, MallocSizeOf)]
482pub(crate) struct FetchContext {
483 #[ignore_malloc_size_of = "unclear ownership semantics"]
484 fetch_promise: Option<TrustedPromise>,
485 response_object: Trusted<Response>,
486 request: Trusted<Request>,
487 global: Trusted<GlobalScope>,
488 locally_aborted: bool,
489 canceller: FetchCanceller,
490 #[no_trace]
491 url: ServoUrl,
492}
493
494impl FetchContext {
495 pub(crate) fn abort_fetch(
497 &mut self,
498 abort_reason: HandleValue,
499 cx: &mut js::context::JSContext,
500 ) {
501 self.locally_aborted = true;
503 self.canceller.abort();
509
510 let promise = self
513 .fetch_promise
514 .take()
515 .expect("fetch promise is missing")
516 .root();
517 abort_fetch_call(
518 promise,
519 &self.request.root(),
520 Some(&self.response_object.root()),
521 abort_reason,
522 &self.global.root(),
523 cx,
524 );
525 }
526}
527
528impl FetchResponseListener for FetchContext {
530 fn process_request_body(&mut self, _: RequestId) {
531 }
533
534 fn process_request_eof(&mut self, _: RequestId) {
535 }
537
538 fn process_response(
539 &mut self,
540 cx: &mut js::context::JSContext,
541 _: RequestId,
542 fetch_metadata: Result<FetchMetadata, NetworkError>,
543 ) {
544 if self.locally_aborted {
546 return;
547 }
548 let promise = self
549 .fetch_promise
550 .take()
551 .expect("fetch promise is missing")
552 .root();
553
554 let mut realm = enter_auto_realm(cx, &*promise);
555 let cx = &mut realm.current_realm();
556 match fetch_metadata {
557 Err(error) => {
560 promise.reject_error(
561 Error::Type(cformat!("Network error: {:?}", error)),
562 CanGc::from_cx(cx),
563 );
564 self.fetch_promise = Some(TrustedPromise::new(promise));
565 let response = self.response_object.root();
566 response.set_type(DOMResponseType::Error, CanGc::from_cx(cx));
567 response.error_stream(
568 Error::Type(c"Network error occurred".to_owned()),
569 CanGc::from_cx(cx),
570 );
571 return;
572 },
573 Ok(metadata) => match metadata {
576 FetchMetadata::Unfiltered(m) => {
577 fill_headers_with_metadata(self.response_object.root(), m, CanGc::from_cx(cx));
578 self.response_object
579 .root()
580 .set_type(DOMResponseType::Default, CanGc::from_cx(cx));
581 },
582 FetchMetadata::Filtered { filtered, .. } => match filtered {
583 FilteredMetadata::Basic(m) => {
584 fill_headers_with_metadata(
585 self.response_object.root(),
586 m,
587 CanGc::from_cx(cx),
588 );
589 self.response_object
590 .root()
591 .set_type(DOMResponseType::Basic, CanGc::from_cx(cx));
592 },
593 FilteredMetadata::Cors(m) => {
594 fill_headers_with_metadata(
595 self.response_object.root(),
596 m,
597 CanGc::from_cx(cx),
598 );
599 self.response_object
600 .root()
601 .set_type(DOMResponseType::Cors, CanGc::from_cx(cx));
602 },
603 FilteredMetadata::Opaque => {
604 self.response_object
605 .root()
606 .set_type(DOMResponseType::Opaque, CanGc::from_cx(cx));
607 },
608 FilteredMetadata::OpaqueRedirect(url) => {
609 let r = self.response_object.root();
610 r.set_type(DOMResponseType::Opaqueredirect, CanGc::from_cx(cx));
611 r.set_final_url(url);
612 },
613 },
614 },
615 }
616
617 promise.resolve_native(&self.response_object.root(), CanGc::from_cx(cx));
619 self.fetch_promise = Some(TrustedPromise::new(promise));
620 }
621
622 fn process_response_chunk(&mut self, _: RequestId, chunk: Vec<u8>) {
623 let response = self.response_object.root();
624 response.stream_chunk(chunk, CanGc::note());
625 }
626
627 fn process_response_eof(
628 self,
629 cx: &mut js::context::JSContext,
630 _: RequestId,
631 response: Result<(), NetworkError>,
632 timing: ResourceFetchTiming,
633 ) {
634 let response_object = self.response_object.root();
635 let _ac = enter_realm(&*response_object);
636 if let Err(ref error) = response {
637 if *error == NetworkError::DecompressionError {
638 response_object.error_stream(
639 Error::Type(c"Network error occurred".to_owned()),
640 CanGc::from_cx(cx),
641 );
642 }
643 }
644 response_object.finish(CanGc::from_cx(cx));
645 network_listener::submit_timing(&self, &response, &timing, CanGc::from_cx(cx));
650 }
651
652 fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec<Violation>) {
653 let global = &self.resource_timing_global();
654 global.report_csp_violations(violations, None, None);
655 }
656}
657
658impl ResourceTimingListener for FetchContext {
659 fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
660 (InitiatorType::Fetch, self.url.clone())
661 }
662
663 fn resource_timing_global(&self) -> DomRoot<GlobalScope> {
664 self.response_object.root().global()
665 }
666}
667
668struct FetchLaterListener {
669 url: ServoUrl,
671 global: Trusted<GlobalScope>,
673}
674
675impl FetchResponseListener for FetchLaterListener {
676 fn process_request_body(&mut self, _: RequestId) {}
677
678 fn process_request_eof(&mut self, _: RequestId) {}
679
680 fn process_response(
681 &mut self,
682 _: &mut js::context::JSContext,
683 _: RequestId,
684 fetch_metadata: Result<FetchMetadata, NetworkError>,
685 ) {
686 _ = fetch_metadata;
687 }
688
689 fn process_response_chunk(&mut self, _: RequestId, chunk: Vec<u8>) {
690 _ = chunk;
691 }
692
693 fn process_response_eof(
694 self,
695 cx: &mut js::context::JSContext,
696 _: RequestId,
697 response: Result<(), NetworkError>,
698 timing: ResourceFetchTiming,
699 ) {
700 network_listener::submit_timing(&self, &response, &timing, CanGc::from_cx(cx));
701 }
702
703 fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec<Violation>) {
704 let global = self.resource_timing_global();
705 global.report_csp_violations(violations, None, None);
706 }
707}
708
709impl ResourceTimingListener for FetchLaterListener {
710 fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
711 (InitiatorType::Fetch, self.url.clone())
712 }
713
714 fn resource_timing_global(&self) -> DomRoot<GlobalScope> {
715 self.global.root()
716 }
717}
718
719fn fill_headers_with_metadata(r: DomRoot<Response>, m: Metadata, can_gc: CanGc) {
720 r.set_headers(m.headers, can_gc);
721 r.set_status(&m.status);
722 r.set_final_url(m.final_url);
723 r.set_redirected(m.redirected);
724}
725
726pub(crate) trait CspViolationsProcessor {
727 fn process_csp_violations(&self, violations: Vec<Violation>);
728}
729
730pub(crate) fn load_whole_resource(
732 request: RequestBuilder,
733 core_resource_thread: &CoreResourceThread,
734 global: &GlobalScope,
735 csp_violations_processor: &dyn CspViolationsProcessor,
736 cx: &mut js::context::JSContext,
737) -> Result<(Metadata, Vec<u8>, bool), NetworkError> {
738 let request = request.https_state(global.get_https_state());
739 let (action_sender, action_receiver) = ipc::channel().unwrap();
740 let url = request.url.clone();
741 core_resource_thread
742 .send(CoreResourceMsg::Fetch(
743 request,
744 FetchChannels::ResponseMsg(action_sender),
745 ))
746 .unwrap();
747
748 let mut buf = vec![];
749 let mut metadata = None;
750 let mut muted_errors = false;
751 loop {
752 match action_receiver.recv().unwrap() {
753 FetchResponseMsg::ProcessRequestBody(..) | FetchResponseMsg::ProcessRequestEOF(..) => {
754 },
755 FetchResponseMsg::ProcessResponse(_, Ok(m)) => {
756 muted_errors = m.is_cors_cross_origin();
757 metadata = Some(match m {
758 FetchMetadata::Unfiltered(m) => m,
759 FetchMetadata::Filtered { unsafe_, .. } => unsafe_,
760 })
761 },
762 FetchResponseMsg::ProcessResponseChunk(_, data) => buf.extend_from_slice(&data),
763 FetchResponseMsg::ProcessResponseEOF(_, Ok(_), _) => {
764 let metadata = metadata.unwrap();
765 if let Some(timing) = &metadata.timing {
766 submit_timing_data(
767 global,
768 url,
769 InitiatorType::Other,
770 timing,
771 CanGc::from_cx(cx),
772 );
773 }
774 return Ok((metadata, buf, muted_errors));
775 },
776 FetchResponseMsg::ProcessResponse(_, Err(e)) |
777 FetchResponseMsg::ProcessResponseEOF(_, Err(e), _) => return Err(e),
778 FetchResponseMsg::ProcessCspViolations(_, violations) => {
779 csp_violations_processor.process_csp_violations(violations);
780 },
781 }
782 }
783}
784
785pub(crate) trait RequestWithGlobalScope {
786 fn with_global_scope(self, global: &GlobalScope) -> Self;
787}
788
789impl RequestWithGlobalScope for RequestBuilder {
790 fn with_global_scope(self, global: &GlobalScope) -> Self {
791 self.insecure_requests_policy(global.insecure_requests_policy())
792 .has_trustworthy_ancestor_origin(global.has_trustworthy_ancestor_or_current_origin())
793 .policy_container(global.policy_container())
794 .client(global.request_client())
795 .pipeline_id(Some(global.pipeline_id()))
796 .origin(global.origin().immutable().clone())
797 .https_state(global.get_https_state())
798 }
799}
800
801#[allow(clippy::too_many_arguments)]
803pub(crate) fn create_a_potential_cors_request(
804 webview_id: Option<WebViewId>,
805 url: ServoUrl,
806 destination: Destination,
807 cors_setting: Option<CorsSettings>,
808 same_origin_fallback: Option<bool>,
809 referrer: Referrer,
810) -> RequestBuilder {
811 RequestBuilder::new(webview_id, url, referrer)
812 .mode(match cors_setting {
814 Some(_) => RequestMode::CorsMode,
815 None if same_origin_fallback == Some(true) => RequestMode::SameOrigin,
817 None => RequestMode::NoCors,
818 })
819 .credentials_mode(match cors_setting {
820 Some(CorsSettings::Anonymous) => CredentialsMode::CredentialsSameOrigin,
822 _ => CredentialsMode::Include,
824 })
825 .destination(destination)
828 .use_url_credentials(true)
829}