1#![cfg_attr(crown, allow(crown::jscontext_first_arg))]
6
7use std::cell::Cell;
8use std::rc::Rc;
9use std::time::Duration;
10
11use bytes::Bytes;
12use js::context::JSContext;
13use js::jsapi::ExceptionStackBehavior;
14use js::jsval::UndefinedValue;
15use js::realm::CurrentRealm;
16use js::rust::HandleValue;
17use js::rust::wrappers2::{JS_IsExceptionPending, JS_SetPendingException};
18use net_traits::blob_url_store::UrlWithBlobClaim;
19use net_traits::request::{
20 CorsSettings, CredentialsMode, Destination, Referrer, Request as NetTraitsRequest,
21 RequestBuilder, RequestId, RequestMode, ServiceWorkersMode,
22};
23use net_traits::{
24 CoreResourceMsg, CoreResourceThread, FetchChannels, FetchMetadata, FetchResponseMsg,
25 FilteredMetadata, Metadata, NetworkError, ResourceFetchTiming, cancel_async_fetch, fetch_async,
26};
27use rustc_hash::FxHashMap;
28use script_bindings::cformat;
29use serde::{Deserialize, Serialize};
30use servo_base::generic_channel::GenericCallback;
31use servo_base::id::WebViewId;
32use servo_url::ServoUrl;
33use timers::TimerEventRequest;
34use uuid::Uuid;
35
36use crate::dom::abortsignal::AbortAlgorithm;
37use crate::dom::bindings::codegen::Bindings::AbortSignalBinding::AbortSignalMethods;
38use crate::dom::bindings::codegen::Bindings::RequestBinding::{
39 RequestInfo, RequestInit, RequestMethods,
40};
41use crate::dom::bindings::codegen::Bindings::ResponseBinding::Response_Binding::ResponseMethods;
42use crate::dom::bindings::codegen::Bindings::ResponseBinding::ResponseType as DOMResponseType;
43use crate::dom::bindings::codegen::Bindings::WindowBinding::{DeferredRequestInit, WindowMethods};
44use crate::dom::bindings::error::{Error, Fallible};
45use crate::dom::bindings::inheritance::Castable;
46use crate::dom::bindings::num::Finite;
47use crate::dom::bindings::refcounted::{Trusted, TrustedPromise};
48use crate::dom::bindings::reflector::DomGlobal;
49use crate::dom::bindings::root::DomRoot;
50use crate::dom::csp::{GlobalCspReporting, Violation};
51use crate::dom::fetchlaterresult::FetchLaterResult;
52use crate::dom::globalscope::GlobalScope;
53use crate::dom::headers::Guard;
54use crate::dom::performance::performanceresourcetiming::InitiatorType;
55use crate::dom::promise::{Promise, RootedPromise};
56use crate::dom::request::Request;
57use crate::dom::response::Response;
58use crate::dom::serviceworkerglobalscope::ServiceWorkerGlobalScope;
59use crate::dom::window::Window;
60use crate::fetch::body::BodyMixin;
61use crate::fetch::network_listener::{
62 self, FetchResponseListener, NetworkListener, ResourceTimingListener, submit_timing_data,
63};
64use crate::realms::enter_auto_realm;
65
66#[derive(Default, JSTraceable, MallocSizeOf)]
71pub(crate) struct FetchCanceller {
72 #[no_trace]
73 request_id: Option<RequestId>,
74 #[no_trace]
75 core_resource_thread: Option<CoreResourceThread>,
76 keep_alive: bool,
77}
78
79impl FetchCanceller {
80 pub(crate) fn new(
83 request_id: RequestId,
84 keep_alive: bool,
85 core_resource_thread: CoreResourceThread,
86 ) -> Self {
87 Self {
88 request_id: Some(request_id),
89 core_resource_thread: Some(core_resource_thread),
90 keep_alive,
91 }
92 }
93
94 pub(crate) fn keep_alive(&self) -> bool {
95 self.keep_alive
96 }
97
98 fn cancel(&mut self) {
99 if let Some(request_id) = self.request_id.take() {
100 if let Some(ref core_resource_thread) = self.core_resource_thread {
104 cancel_async_fetch(vec![request_id], core_resource_thread);
107 }
108 }
109 }
110
111 pub(crate) fn ignore(&mut self) {
114 let _ = self.request_id.take();
115 }
116
117 pub(crate) fn abort(&mut self) {
119 self.cancel();
120 }
121
122 pub(crate) fn terminate(&mut self) {
124 self.cancel();
125 }
126}
127
128#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
130pub(crate) struct DeferredFetchRecordId(Uuid);
131
132impl Default for DeferredFetchRecordId {
133 fn default() -> Self {
134 Self(Uuid::new_v4())
135 }
136}
137
138pub(crate) type QueuedDeferredFetchRecord = Rc<DeferredFetchRecord>;
139
140#[derive(MallocSizeOf)]
142pub(crate) struct FetchRecord {
143 controller: Option<FetchCanceller>,
147 done: bool,
152}
153
154#[derive(MallocSizeOf)]
156pub(crate) struct FetchGroup {
157 core_resource_thread: CoreResourceThread,
159 #[conditional_malloc_size_of]
161 pub(crate) deferred_fetch_records: FxHashMap<DeferredFetchRecordId, QueuedDeferredFetchRecord>,
162 pub(crate) fetch_records: FxHashMap<RequestId, FetchRecord>,
164}
165
166impl FetchGroup {
167 pub(crate) fn new(core_resource_thread: CoreResourceThread) -> Self {
168 Self {
169 core_resource_thread,
170 deferred_fetch_records: Default::default(),
171 fetch_records: Default::default(),
172 }
173 }
174
175 pub(crate) fn fetch<Listener: FetchResponseListener>(
176 &mut self,
177 request: RequestBuilder,
178 listener: NetworkListener<Listener>,
179 ) {
180 self.fetch_records.insert(
181 request.id,
182 FetchRecord {
183 controller: Some(FetchCanceller::new(
184 request.id,
185 request.keep_alive,
186 self.core_resource_thread.clone(),
187 )),
188 done: false,
189 },
190 );
191 fetch_async(
192 &self.core_resource_thread,
193 request,
194 None,
195 listener.into_callback(),
196 );
197 }
198
199 pub(crate) fn deferred_fetches(&self) -> Vec<QueuedDeferredFetchRecord> {
200 self.deferred_fetch_records.values().cloned().collect()
201 }
202
203 fn append_deferred_fetch(
204 &mut self,
205 deferred_record: DeferredFetchRecord,
206 ) -> DeferredFetchRecordId {
207 let deferred_fetch_record_id = DeferredFetchRecordId::default();
208 self.deferred_fetch_records
209 .insert(deferred_fetch_record_id, Rc::new(deferred_record));
210 deferred_fetch_record_id
211 }
212
213 pub(crate) fn deferred_fetch_record_for_id(
214 &self,
215 deferred_fetch_record_id: &DeferredFetchRecordId,
216 ) -> QueuedDeferredFetchRecord {
217 self.deferred_fetch_records
218 .get(deferred_fetch_record_id)
219 .expect("Should always use a generated fetch_record_id instead of passing your own")
220 .clone()
221 }
222
223 pub(crate) fn fetch_controller(
224 &mut self,
225 request_id: &RequestId,
226 ) -> Option<&mut FetchCanceller> {
227 self.fetch_records.get_mut(request_id)?.controller.as_mut()
228 }
229
230 pub(crate) fn terminate(&mut self, global: &GlobalScope) -> bool {
234 let mut cancelled_any = false;
238 self.fetch_records.retain(|_, fetch_record| {
239 let Some(controller) = fetch_record.controller.as_mut() else {
240 return false;
241 };
242 if fetch_record.done {
243 return false;
244 }
245 if !controller.keep_alive() {
246 controller.terminate();
247 cancelled_any = true;
248 return false;
249 }
250 true
251 });
252
253 self.process_deferred_fetches(global);
255
256 cancelled_any
257 }
258
259 pub(crate) fn process_deferred_fetches(&mut self, global: &GlobalScope) {
261 for deferred_fetch in self.deferred_fetches() {
264 self.process_a_deferred_fetch(global, &deferred_fetch);
265 }
266 }
267
268 pub(crate) fn process_a_deferred_fetch(
270 &mut self,
271 global: &GlobalScope,
272 deferred_fetch: &DeferredFetchRecord,
273 ) {
274 if deferred_fetch.invoke_state.get() != DeferredFetchRecordInvokeState::Pending {
276 return;
277 }
278 deferred_fetch
280 .invoke_state
281 .set(DeferredFetchRecordInvokeState::Sent);
282 let fetch_later_listener = FetchLaterListener {
284 url: deferred_fetch.request.url(),
285 global: Trusted::new(global),
286 };
287 let task_source = global.task_manager().networking_task_source().to_sendable();
288 self.fetch(
289 request_init_from_request(deferred_fetch.request.clone(), global),
290 NetworkListener::new(fetch_later_listener, task_source, global),
291 );
292 }
294
295 pub(crate) fn mark_fetch_request_as_done(&mut self, request_id: &RequestId) {
296 if let Some(fetch_record) = self.fetch_records.get_mut(request_id) {
297 fetch_record.done = true;
298 }
299 }
300}
301
302fn request_init_from_request(request: NetTraitsRequest, global: &GlobalScope) -> RequestBuilder {
303 let mut builder = RequestBuilder::new(
304 request.target_webview_id,
305 request.url_with_blob_claim(),
306 request.referrer,
307 )
308 .method(request.method)
309 .headers(request.headers)
310 .unsafe_request(request.unsafe_request)
311 .body(request.body)
312 .destination(request.destination)
313 .synchronous(request.synchronous)
314 .mode(request.mode)
315 .cache_mode(request.cache_mode)
316 .use_cors_preflight(request.use_cors_preflight)
317 .credentials_mode(request.credentials_mode)
318 .use_url_credentials(request.use_url_credentials)
319 .referrer_policy(request.referrer_policy)
320 .pipeline_id(request.pipeline_id)
321 .redirect_mode(request.redirect_mode)
322 .integrity_metadata(request.integrity_metadata)
323 .cryptographic_nonce_metadata(request.cryptographic_nonce_metadata)
324 .parser_metadata(request.parser_metadata)
325 .initiator(request.initiator)
326 .client(global.request_client(None))
327 .response_tainting(request.response_tainting);
328 builder.id = request.id;
329 builder.reload_navigation = request.reload_navigation;
330 builder.history_navigation = request.history_navigation;
331 builder
332}
333
334fn abort_fetch_call(
336 promise: &RootedPromise,
337 request: &Request,
338 response_object: Option<&Response>,
339 abort_reason: HandleValue,
340 global: &GlobalScope,
341 cx: &mut JSContext,
342) {
343 promise.reject(cx, abort_reason);
345 if let Some(body) = request.body() &&
347 body.is_readable()
348 {
349 body.cancel(cx, global, abort_reason);
350 }
351 let Some(response) = response_object else {
354 return;
355 };
356 if let Some(body) = response.body() &&
358 body.is_readable()
359 {
360 body.error(cx, abort_reason);
361 }
362}
363
364#[expect(non_snake_case)]
366pub(crate) fn Fetch(
367 global: &GlobalScope,
368 input: RequestInfo,
369 init: &RequestInit,
370 cx: &mut CurrentRealm,
371) -> RootedPromise {
372 let promise = Promise::new_in_realm(cx);
374
375 let response = Response::new_fetch_response(cx, global);
378 response.Headers(cx).set_guard(Guard::Immutable);
379
380 let request_object = match Request::Constructor(cx, global, None, input, init) {
383 Err(e) => {
384 response.error_stream(cx, e.clone());
385 promise.reject_error(cx, e);
386 return promise;
387 },
388 Ok(r) => r,
389 };
390 let request = request_object.request().clone();
392
393 let signal = request_object.Signal();
395 if signal.aborted() {
396 rooted!(&in(cx) let mut abort_reason = UndefinedValue());
398 signal.Reason(abort_reason.handle_mut());
399 abort_fetch_call(
400 &promise,
401 &request_object,
402 None,
403 abort_reason.handle(),
404 global,
405 cx,
406 );
407 return promise;
409 }
410
411 let mut request_builder = request_init_from_request(request, global);
414
415 if global.is::<ServiceWorkerGlobalScope>() {
418 request_builder.service_workers_mode = ServiceWorkersMode::None;
419 }
420
421 let fetch_context = FetchContext {
428 fetch_promise: Some(TrustedPromise::from(&promise)),
429 response_object: Trusted::new(&*response),
430 request: Trusted::new(&*request_object),
431 global: Trusted::new(global),
432 locally_aborted: false,
433 url: request_builder.url.url(),
434 };
435 let network_listener = NetworkListener::new(
436 fetch_context,
437 global.task_manager().networking_task_source().to_sendable(),
438 global,
439 );
440 let fetch_context = network_listener.context.clone();
441
442 signal.add(&AbortAlgorithm::Fetch(fetch_context));
444
445 global
448 .fetch_group_mut()
449 .fetch(request_builder, network_listener);
450
451 promise
453}
454
455fn queue_deferred_fetch(
457 request: NetTraitsRequest,
458 activate_after: Finite<f64>,
459 global: &GlobalScope,
460) -> DeferredFetchRecordId {
461 let trusted_global = Trusted::new(global);
462 let mut request = request;
463 request.client = Some(global.request_client(None));
465 request.populate_request_from_client();
466 request.service_workers_mode = ServiceWorkersMode::None;
468 request.keep_alive = true;
470 let deferred_record = DeferredFetchRecord {
472 request,
473 invoke_state: Cell::new(DeferredFetchRecordInvokeState::Pending),
474 activated: Cell::new(false),
475 };
476
477 let deferred_fetch_record_id = global
479 .fetch_group_mut()
480 .append_deferred_fetch(deferred_record);
481
482 global.schedule_timer(TimerEventRequest {
484 callback: Box::new(move || {
485 let global = trusted_global.root();
487 let mut fetch_group = global.fetch_group_mut();
488 let deferred_fetch_record =
489 fetch_group.deferred_fetch_record_for_id(&deferred_fetch_record_id);
490 fetch_group.process_a_deferred_fetch(&global, &deferred_fetch_record);
491
492 let trusted_global = trusted_global.clone();
497 global.task_manager().deferred_fetch_task_source().queue(
498 task!(notify_deferred_record: move || {
499 trusted_global.root().fetch_group().deferred_fetch_record_for_id(&deferred_fetch_record_id).activate();
500 }),
501 );
502 }),
503 duration: Duration::from_millis(*activate_after as u64),
505 });
506 deferred_fetch_record_id
508}
509
510#[expect(non_snake_case, unsafe_code)]
512pub(crate) fn FetchLater(
513 cx: &mut JSContext,
514 window: &Window,
515 input: RequestInfo,
516 init: &DeferredRequestInit,
517) -> Fallible<DomRoot<FetchLaterResult>> {
518 let global_scope = window.upcast();
519 let document = window.Document();
520 let request_object = Request::constructor(cx, global_scope, None, input, &init.parent)?;
523 let signal = request_object.Signal();
525 if signal.aborted() {
526 rooted!(&in(cx) let mut abort_reason = UndefinedValue());
527 signal.Reason(abort_reason.handle_mut());
528 unsafe {
529 assert!(!JS_IsExceptionPending(cx));
530 JS_SetPendingException(cx, abort_reason.handle(), ExceptionStackBehavior::Capture)
531 };
532 return Err(Error::JSFailed);
533 }
534 let request = request_object.request();
536 let mut activate_after = Finite::wrap(0_f64);
538 if let Some(init_activate_after) = init.activateAfter.as_ref() {
541 activate_after = *init_activate_after;
542 }
543 if *activate_after < 0.0 {
545 return Err(Error::Range(c"activateAfter must be at least 0".to_owned()));
546 }
547 if !document.is_fully_active() {
549 return Err(Error::Type(c"Document is not fully active".to_owned()));
550 }
551 let url = request.url();
552 if !matches!(url.scheme(), "http" | "https") {
554 return Err(Error::Type(c"URL is not http(s)".to_owned()));
555 }
556 if !url.is_potentially_trustworthy() {
558 return Err(Error::Type(c"URL is not trustworthy".to_owned()));
559 }
560 if request
562 .body
563 .as_ref()
564 .is_some_and(|body| body.len().is_none())
565 {
566 return Err(Error::Type(c"Body is empty".to_owned()));
567 }
568 let quota = document.available_deferred_fetch_quota(request.url().origin());
571 let requested = request.total_request_length() as isize;
572 if quota < requested {
573 return Err(Error::QuotaExceeded {
574 quota: Some(Finite::wrap(quota as f64)),
575 requested: Some(Finite::wrap(requested as f64)),
576 });
577 }
578 let deferred_record_id = queue_deferred_fetch(request.clone(), activate_after, global_scope);
582 signal.add(&AbortAlgorithm::FetchLater(deferred_record_id));
584 Ok(FetchLaterResult::new(cx, window, deferred_record_id))
586}
587
588#[derive(Clone, Copy, MallocSizeOf, PartialEq)]
590pub(crate) enum DeferredFetchRecordInvokeState {
591 Pending,
592 Sent,
593 Aborted,
594}
595
596#[derive(MallocSizeOf)]
598pub(crate) struct DeferredFetchRecord {
599 pub(crate) request: NetTraitsRequest,
601 pub(crate) invoke_state: Cell<DeferredFetchRecordInvokeState>,
603 activated: Cell<bool>,
604}
605
606impl DeferredFetchRecord {
607 fn activate(&self) {
609 self.activated.set(true);
611 }
612 pub(crate) fn abort(&self) {
614 self.invoke_state
616 .set(DeferredFetchRecordInvokeState::Aborted);
617 }
618 pub(crate) fn activated_getter_steps(&self) -> bool {
620 self.activated.get()
622 }
623}
624
625#[derive(JSTraceable, MallocSizeOf)]
626pub(crate) struct FetchContext {
627 #[ignore_malloc_size_of = "unclear ownership semantics"]
628 fetch_promise: Option<TrustedPromise>,
629 response_object: Trusted<Response>,
630 request: Trusted<Request>,
631 global: Trusted<GlobalScope>,
632 locally_aborted: bool,
633 #[no_trace]
634 url: ServoUrl,
635}
636
637impl FetchContext {
638 pub(crate) fn abort_fetch(&mut self, abort_reason: HandleValue, cx: &mut JSContext) {
640 self.locally_aborted = true;
642
643 let global = self.global.root();
651 let request = self.request.root();
652 if let Some(controller) = global
653 .fetch_group_mut()
654 .fetch_controller(&request.request().id)
655 {
656 controller.abort();
657 }
658
659 let promise = self
662 .fetch_promise
663 .take()
664 .expect("fetch promise is missing")
665 .root(cx);
666 abort_fetch_call(
667 &promise,
668 &request,
669 Some(&self.response_object.root()),
670 abort_reason,
671 &global,
672 cx,
673 );
674 }
675}
676
677impl FetchResponseListener for FetchContext {
679 fn process_request_body(&mut self, _: RequestId) {
680 }
682
683 fn process_response(
684 &mut self,
685 cx: &mut JSContext,
686 _: RequestId,
687 fetch_metadata: Result<FetchMetadata, NetworkError>,
688 ) {
689 if self.locally_aborted {
691 return;
692 }
693 let promise = self
694 .fetch_promise
695 .take()
696 .expect("fetch promise is missing")
697 .root(cx);
698
699 let mut realm = enter_auto_realm(cx, &*promise);
700 let cx = &mut realm.current_realm();
701 match fetch_metadata {
702 Err(error) => {
705 promise.reject_error(cx, Error::Type(cformat!("Network error: {:?}", error)));
706 self.fetch_promise = Some(TrustedPromise::from(&promise));
707 let response = self.response_object.root();
708 response.set_type(cx, DOMResponseType::Error);
709 response.error_stream(cx, Error::Type(c"Network error occurred".to_owned()));
710 return;
711 },
712 Ok(metadata) => match metadata {
715 FetchMetadata::Unfiltered(m) => {
716 let r = self.response_object.root();
717 fill_headers_with_metadata(cx, &r, m);
718 r.set_type(cx, DOMResponseType::Default);
719 },
720 FetchMetadata::Filtered { filtered, .. } => match filtered {
721 FilteredMetadata::Basic(m) => {
722 let r = self.response_object.root();
723 fill_headers_with_metadata(cx, &r, m);
724 r.set_type(cx, DOMResponseType::Basic);
725 },
726 FilteredMetadata::Cors(m) => {
727 let r = self.response_object.root();
728 fill_headers_with_metadata(cx, &r, m);
729 r.set_type(cx, DOMResponseType::Cors);
730 },
731 FilteredMetadata::Opaque => {
732 self.response_object
733 .root()
734 .set_type(cx, DOMResponseType::Opaque);
735 },
736 FilteredMetadata::OpaqueRedirect(url) => {
737 let r = self.response_object.root();
738 r.set_type(cx, DOMResponseType::Opaqueredirect);
739 r.set_final_url(url);
740 },
741 },
742 },
743 }
744
745 promise.resolve_native(cx, &self.response_object.root());
747 self.fetch_promise = Some(TrustedPromise::from(&promise));
748 }
749
750 fn process_response_chunk(&mut self, cx: &mut JSContext, _: RequestId, chunk: Bytes) {
751 let response = self.response_object.root();
752 response.stream_chunk(cx, chunk);
753 }
754
755 fn process_response_eof(
756 self,
757 cx: &mut JSContext,
758 _: RequestId,
759 response: Result<(), NetworkError>,
760 timing: ResourceFetchTiming,
761 ) {
762 let response_object = self.response_object.root();
763 let mut realm = enter_auto_realm(cx, &*response_object);
764 let cx = &mut realm.current_realm();
765 if let Err(ref error) = response &&
766 *error == NetworkError::DecompressionError
767 {
768 response_object.error_stream(cx, Error::Type(c"Network error occurred".to_owned()));
769 }
770 response_object.finish(cx);
771 network_listener::submit_timing(cx, &self, &response, &timing);
776 }
777
778 fn process_csp_violations(
779 &mut self,
780 cx: &mut JSContext,
781 _request_id: RequestId,
782 violations: Vec<Violation>,
783 ) {
784 let global = &self.resource_timing_global();
785 global.report_csp_violations(cx, violations, None, None);
786 }
787}
788
789impl ResourceTimingListener for FetchContext {
790 fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
791 (InitiatorType::Fetch, self.url.clone())
792 }
793
794 fn resource_timing_global(&self) -> DomRoot<GlobalScope> {
795 self.response_object.root().global()
796 }
797}
798
799struct FetchLaterListener {
800 url: ServoUrl,
802 global: Trusted<GlobalScope>,
804}
805
806impl FetchResponseListener for FetchLaterListener {
807 fn process_request_body(&mut self, _: RequestId) {}
808
809 fn process_response(
810 &mut self,
811 _: &mut JSContext,
812 _: RequestId,
813 fetch_metadata: Result<FetchMetadata, NetworkError>,
814 ) {
815 _ = fetch_metadata;
816 }
817
818 fn process_response_chunk(&mut self, _: &mut JSContext, _: RequestId, chunk: Bytes) {
819 _ = chunk;
820 }
821
822 fn process_response_eof(
823 self,
824 cx: &mut JSContext,
825 _: RequestId,
826 response: Result<(), NetworkError>,
827 timing: ResourceFetchTiming,
828 ) {
829 network_listener::submit_timing(cx, &self, &response, &timing);
830 }
831
832 fn process_csp_violations(
833 &mut self,
834 cx: &mut JSContext,
835 _request_id: RequestId,
836 violations: Vec<Violation>,
837 ) {
838 let global = self.resource_timing_global();
839 global.report_csp_violations(cx, violations, None, None);
840 }
841}
842
843impl ResourceTimingListener for FetchLaterListener {
844 fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
845 (InitiatorType::Fetch, self.url.clone())
846 }
847
848 fn resource_timing_global(&self) -> DomRoot<GlobalScope> {
849 self.global.root()
850 }
851}
852
853fn fill_headers_with_metadata(cx: &mut JSContext, r: &Response, m: Metadata) {
854 r.set_headers(cx, m.headers);
855 r.set_status(&m.status);
856 r.set_final_url(m.final_url);
857 r.set_redirected(m.redirected);
858}
859
860pub(crate) trait CspViolationsProcessor {
861 fn process_csp_violations(&self, cx: &mut JSContext, violations: Vec<Violation>);
862}
863
864pub(crate) fn load_whole_resource(
866 request: RequestBuilder,
867 core_resource_thread: &CoreResourceThread,
868 global: &GlobalScope,
869 csp_violations_processor: &dyn CspViolationsProcessor,
870 cx: &mut JSContext,
871) -> Result<(Metadata, Vec<u8>, bool), NetworkError> {
872 let (action_sender, action_receiver) = GenericCallback::new_blocking().unwrap();
873 let url = request.url.url();
874 core_resource_thread
875 .send(CoreResourceMsg::Fetch(
876 request,
877 FetchChannels::ResponseMsg(action_sender),
878 ))
879 .unwrap();
880
881 let mut buf = vec![];
882 let mut metadata = None;
883 let mut muted_errors = false;
884 loop {
885 match action_receiver.recv().unwrap() {
886 FetchResponseMsg::ProcessRequestBody(..) => {},
887 FetchResponseMsg::ProcessResponse(_, Ok(m)) => {
888 muted_errors = m.is_cors_cross_origin();
889 metadata = Some(match m {
890 FetchMetadata::Unfiltered(m) => m,
891 FetchMetadata::Filtered { unsafe_, .. } => unsafe_,
892 })
893 },
894 FetchResponseMsg::ProcessResponseChunk(_, data) => buf.extend_from_slice(&data),
895 FetchResponseMsg::ProcessResponseEOF(_, Ok(_), _) => {
896 let metadata = metadata.unwrap();
897 if let Some(timing) = &metadata.timing {
898 submit_timing_data(cx, global, url, InitiatorType::Other, timing);
899 }
900 return Ok((metadata, buf, muted_errors));
901 },
902 FetchResponseMsg::ProcessResponse(_, Err(e)) |
903 FetchResponseMsg::ProcessResponseEOF(_, Err(e), _) => return Err(e),
904 FetchResponseMsg::ProcessCspViolations(_, violations) => {
905 csp_violations_processor.process_csp_violations(cx, violations);
906 },
907 FetchResponseMsg::ProcessContentLength(_request_id, size) => {
908 buf.reserve(size.saturating_sub(buf.len()))
909 },
910 }
911 }
912}
913
914pub(crate) trait RequestWithGlobalScope {
915 fn with_global_scope(self, global: &GlobalScope) -> Self;
916}
917
918impl RequestWithGlobalScope for RequestBuilder {
919 fn with_global_scope(self, global: &GlobalScope) -> Self {
920 self.client(global.request_client(None))
921 .pipeline_id(Some(global.pipeline_id()))
922 }
923}
924
925#[allow(clippy::too_many_arguments)]
929pub(crate) fn create_a_potential_cors_request(
930 webview_id: Option<WebViewId>,
931 url: ServoUrl,
932 destination: Destination,
933 cors_setting: Option<CorsSettings>,
934 same_origin_fallback: Option<bool>,
935 referrer: Referrer,
936) -> RequestBuilder {
937 create_a_potential_cors_request_with_claim(
938 webview_id,
939 UrlWithBlobClaim::from_url_without_having_claimed_blob(url),
940 destination,
941 cors_setting,
942 same_origin_fallback,
943 referrer,
944 )
945}
946
947#[allow(clippy::too_many_arguments)]
949pub(crate) fn create_a_potential_cors_request_with_claim(
950 webview_id: Option<WebViewId>,
951 url: UrlWithBlobClaim,
952 destination: Destination,
953 cors_setting: Option<CorsSettings>,
954 same_origin_fallback: Option<bool>,
955 referrer: Referrer,
956) -> RequestBuilder {
957 RequestBuilder::new(webview_id, url, referrer)
958 .mode(match cors_setting {
960 Some(_) => RequestMode::CorsMode,
961 None if same_origin_fallback == Some(true) => RequestMode::SameOrigin,
963 None => RequestMode::NoCors,
964 })
965 .credentials_mode(match cors_setting {
966 Some(CorsSettings::Anonymous) => CredentialsMode::CredentialsSameOrigin,
968 _ => CredentialsMode::Include,
970 })
971 .destination(destination)
974 .use_url_credentials(true)
975}