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