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::gc::Rootable;
14use js::jsapi::ExceptionStackBehavior;
15use js::jsval::UndefinedValue;
16use js::realm::CurrentRealm;
17use js::rust::HandleValue;
18use net_traits::blob_url_store::UrlWithBlobClaim;
19use net_traits::fetch::fetch_controller::FetchController;
20use net_traits::request::{
21 CorsSettings, CredentialsMode, Destination, Referrer, Request as NetTraitsRequest,
22 RequestBuilder, RequestId, RequestMode, ServiceWorkersMode,
23};
24use net_traits::{
25 CoreResourceMsg, CoreResourceThread, FetchChannels, FetchMetadata, FetchResponseMsg,
26 FilteredMetadata, Metadata, NetworkError, ResourceFetchTiming, fetch_async,
27};
28use rustc_hash::FxHashMap;
29use script_bindings::cformat;
30use script_bindings::js::{JS_IsExceptionPending, JS_SetPendingException};
31use script_bindings::root::Dom;
32use serde::{Deserialize, Serialize};
33use servo_base::generic_channel::GenericCallback;
34use servo_base::id::WebViewId;
35use servo_url::ServoUrl;
36use timers::TimerEventRequest;
37use uuid::Uuid;
38
39use crate::dom::TracedPromise;
40use crate::dom::abortsignal::AbortAlgorithm;
41use crate::dom::bindings::codegen::Bindings::AbortSignalBinding::AbortSignalMethods;
42use crate::dom::bindings::codegen::Bindings::RequestBinding::{
43 RequestInfo, RequestInit, RequestMethods,
44};
45use crate::dom::bindings::codegen::Bindings::ResponseBinding::Response_Binding::ResponseMethods;
46use crate::dom::bindings::codegen::Bindings::ResponseBinding::ResponseType as DOMResponseType;
47use crate::dom::bindings::codegen::Bindings::WindowBinding::{DeferredRequestInit, WindowMethods};
48use crate::dom::bindings::error::{Error, Fallible};
49use crate::dom::bindings::inheritance::Castable;
50use crate::dom::bindings::num::Finite;
51use crate::dom::bindings::refcounted::Trusted;
52use crate::dom::bindings::root::DomRoot;
53use crate::dom::bindings::trace::NoTrace;
54use crate::dom::csp::{GlobalCspReporting, Violation};
55use crate::dom::fetchlaterresult::FetchLaterResult;
56use crate::dom::globalscope::GlobalScope;
57use crate::dom::headers::Guard;
58use crate::dom::performance::performanceresourcetiming::InitiatorType;
59use crate::dom::promise::{Promise, RootedPromise};
60use crate::dom::request::Request;
61use crate::dom::response::Response;
62use crate::dom::serviceworkerglobalscope::ServiceWorkerGlobalScope;
63use crate::dom::window::Window;
64use crate::fetch::body::BodyMixin;
65use crate::fetch::network_listener::{
66 self, FetchResponseListener, NetworkListener, ResourceTimingListener, submit_timing_data,
67};
68use crate::realms::enter_auto_realm;
69
70#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
72pub(crate) struct DeferredFetchRecordId(Uuid);
73
74impl Default for DeferredFetchRecordId {
75 fn default() -> Self {
76 Self(Uuid::new_v4())
77 }
78}
79
80pub(crate) type QueuedDeferredFetchRecord = Rc<DeferredFetchRecord>;
81
82#[derive(JSTraceable, MallocSizeOf)]
84#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
85pub(crate) struct FetchRecord {
86 #[no_trace]
88 controller: Option<FetchController>,
89 request: Option<Dom<Request>>,
92}
93
94#[derive(JSTraceable, MallocSizeOf)]
96#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
97pub(crate) struct FetchGroup {
98 #[no_trace]
100 core_resource_thread: CoreResourceThread,
101 #[conditional_malloc_size_of]
103 #[no_trace]
104 pub(crate) deferred_fetch_records: FxHashMap<DeferredFetchRecordId, QueuedDeferredFetchRecord>,
105 pub(crate) fetch_records: FxHashMap<NoTrace<RequestId>, FetchRecord>,
107}
108
109impl FetchGroup {
110 pub(crate) fn new(core_resource_thread: CoreResourceThread) -> Self {
111 Self {
112 core_resource_thread,
113 deferred_fetch_records: Default::default(),
114 fetch_records: Default::default(),
115 }
116 }
117
118 pub(crate) fn fetch<Listener: FetchResponseListener>(
119 &mut self,
120 request: RequestBuilder,
121 listener: NetworkListener<Listener>,
122 request_object: Option<&Request>,
123 ) {
124 self.fetch_records.insert(
125 NoTrace(request.id),
126 FetchRecord {
127 controller: Some(FetchController::new(
128 request.id,
129 request.keep_alive,
130 self.core_resource_thread.clone(),
131 )),
132 request: request_object.map(Dom::from_ref),
133 },
134 );
135 fetch_async(
136 &self.core_resource_thread,
137 request,
138 None,
139 listener.into_callback(),
140 );
141 }
142
143 pub(crate) fn deferred_fetches(&self) -> Vec<QueuedDeferredFetchRecord> {
144 self.deferred_fetch_records.values().cloned().collect()
145 }
146
147 fn append_deferred_fetch(
148 &mut self,
149 deferred_record: DeferredFetchRecord,
150 ) -> DeferredFetchRecordId {
151 let deferred_fetch_record_id = DeferredFetchRecordId::default();
152 self.deferred_fetch_records
153 .insert(deferred_fetch_record_id, Rc::new(deferred_record));
154 deferred_fetch_record_id
155 }
156
157 pub(crate) fn deferred_fetch_record_for_id(
158 &self,
159 deferred_fetch_record_id: &DeferredFetchRecordId,
160 ) -> QueuedDeferredFetchRecord {
161 self.deferred_fetch_records
162 .get(deferred_fetch_record_id)
163 .expect("Should always use a generated fetch_record_id instead of passing your own")
164 .clone()
165 }
166
167 pub(crate) fn fetch_controller(
168 &mut self,
169 request_id: &RequestId,
170 ) -> Option<&mut FetchController> {
171 self.fetch_records
172 .get_mut(&NoTrace(*request_id))?
173 .controller
174 .as_mut()
175 }
176
177 pub(crate) fn fetch_call_request(&self, request_id: &RequestId) -> Option<DomRoot<Request>> {
178 let fetch_record = self.fetch_records.get(&NoTrace(*request_id))?;
179 let request = fetch_record.request.as_ref()?;
180 Some(request.as_rooted())
181 }
182
183 pub(crate) fn terminate(&mut self, global: &GlobalScope) -> bool {
187 let mut cancelled_any = false;
195 self.fetch_records.retain(|_, fetch_record| {
196 let Some(controller) = fetch_record.controller.as_mut() else {
197 return false;
198 };
199 if !controller.keep_alive() {
200 controller.terminate();
201 cancelled_any = true;
202 return false;
203 }
204 true
205 });
206
207 self.process_deferred_fetches(global);
209
210 cancelled_any
211 }
212
213 pub(crate) fn process_deferred_fetches(&mut self, global: &GlobalScope) {
215 for deferred_fetch in self.deferred_fetches() {
218 self.process_a_deferred_fetch(global, &deferred_fetch);
219 }
220 }
221
222 pub(crate) fn process_a_deferred_fetch(
224 &mut self,
225 global: &GlobalScope,
226 deferred_fetch: &DeferredFetchRecord,
227 ) {
228 if deferred_fetch.invoke_state.get() != DeferredFetchRecordInvokeState::Pending {
230 return;
231 }
232 deferred_fetch
234 .invoke_state
235 .set(DeferredFetchRecordInvokeState::Sent);
236 let fetch_later_listener = FetchLaterListener {
238 url: deferred_fetch.request.url(),
239 };
240 let task_source = global.task_manager().networking_task_source().to_sendable();
241 self.fetch(
242 request_init_from_request(deferred_fetch.request.clone(), global),
243 NetworkListener::new(fetch_later_listener, task_source),
244 None, );
246 }
248
249 pub(crate) fn mark_fetch_request_as_done(&mut self, request_id: &RequestId) {
250 self.fetch_records.remove(&NoTrace(*request_id));
251 }
252}
253
254fn request_init_from_request(request: NetTraitsRequest, global: &GlobalScope) -> RequestBuilder {
255 let mut builder = RequestBuilder::new(
256 request.target_webview_id,
257 request.url_with_blob_claim(),
258 request.referrer,
259 )
260 .method(request.method)
261 .headers(request.headers)
262 .unsafe_request(request.unsafe_request)
263 .body(request.body)
264 .destination(request.destination)
265 .synchronous(request.synchronous)
266 .mode(request.mode)
267 .cache_mode(request.cache_mode)
268 .use_cors_preflight(request.use_cors_preflight)
269 .credentials_mode(request.credentials_mode)
270 .use_url_credentials(request.use_url_credentials)
271 .referrer_policy(request.referrer_policy)
272 .pipeline_id(request.pipeline_id)
273 .redirect_mode(request.redirect_mode)
274 .integrity_metadata(request.integrity_metadata)
275 .cryptographic_nonce_metadata(request.cryptographic_nonce_metadata)
276 .parser_metadata(request.parser_metadata)
277 .initiator(request.initiator)
278 .client(global.request_client(None))
279 .response_tainting(request.response_tainting);
280 builder.id = request.id;
281 builder.reload_navigation = request.reload_navigation;
282 builder.history_navigation = request.history_navigation;
283 builder
284}
285
286fn abort_fetch_call(
288 promise: &RootedPromise,
289 request: &Request,
290 response_object: Option<&Response>,
291 abort_reason: HandleValue,
292 global: &GlobalScope,
293 cx: &mut JSContext,
294) {
295 promise.reject(cx, abort_reason);
297 if let Some(body) = request.body() &&
299 body.is_readable()
300 {
301 body.cancel(cx, global, abort_reason);
302 }
303 let Some(response) = response_object else {
306 return;
307 };
308 if let Some(body) = response.body() &&
310 body.is_readable()
311 {
312 body.error(cx, abort_reason);
313 }
314}
315
316#[expect(non_snake_case)]
318pub(crate) fn Fetch(
319 global: &GlobalScope,
320 input: RequestInfo,
321 init: &RequestInit,
322 cx: &mut CurrentRealm,
323) -> RootedPromise {
324 let promise = Promise::new_in_realm(cx);
326
327 let response = Response::new_fetch_response(cx, global);
330 response.Headers(cx).set_guard(Guard::Immutable);
331
332 rooted!(&in(cx) let mut fetch_call_state = FetchCallState {
335 promise: promise.to_traced(),
336 response: response.as_traced(),
337 locally_aborted: Cell::new(false),
338 });
339 let request_object =
340 match Request::constructor(cx, global, None, input, init, Some(&fetch_call_state)) {
341 Err(error) => {
342 response.error_stream(cx, error.clone());
343 promise.reject_error(cx, error);
344 return promise;
345 },
346 Ok(request) => request,
347 };
348 let request = request_object.request().clone();
350 let request_id = request.id;
351
352 let signal = request_object.Signal();
354 if signal.aborted() {
355 rooted!(&in(cx) let mut abort_reason = UndefinedValue());
357 signal.Reason(abort_reason.handle_mut());
358 abort_fetch_call(
359 &promise,
360 &request_object,
361 None,
362 abort_reason.handle(),
363 global,
364 cx,
365 );
366 return promise;
368 }
369
370 let mut request_builder = request_init_from_request(request, global);
373
374 if global.is::<ServiceWorkerGlobalScope>() {
377 request_builder.service_workers_mode = ServiceWorkersMode::None;
378 }
379
380 let fetch_context = FetchContext {
387 request_id,
388 url: request_builder.url.url(),
389 };
390 let network_listener = NetworkListener::new(
391 fetch_context,
392 global.task_manager().networking_task_source().to_sendable(),
393 );
394
395 signal.add(&AbortAlgorithm::Fetch(request_object.as_traced()));
397
398 global
401 .fetch_group_mut()
402 .fetch(request_builder, network_listener, Some(&request_object));
403
404 promise
406}
407
408fn queue_deferred_fetch(
410 request: NetTraitsRequest,
411 activate_after: Finite<f64>,
412 global: &GlobalScope,
413) -> DeferredFetchRecordId {
414 let trusted_global = Trusted::new(global);
415 let mut request = request;
416 request.client = Some(global.request_client(None));
418 request.populate_request_from_client();
419 request.service_workers_mode = ServiceWorkersMode::None;
421 request.keep_alive = true;
423 let deferred_record = DeferredFetchRecord {
425 request,
426 invoke_state: Cell::new(DeferredFetchRecordInvokeState::Pending),
427 activated: Cell::new(false),
428 };
429
430 let deferred_fetch_record_id = global
432 .fetch_group_mut()
433 .append_deferred_fetch(deferred_record);
434
435 global.schedule_timer(TimerEventRequest {
437 callback: Box::new(move || {
438 let global = trusted_global.root();
440 let mut fetch_group = global.fetch_group_mut();
441 let deferred_fetch_record =
442 fetch_group.deferred_fetch_record_for_id(&deferred_fetch_record_id);
443 fetch_group.process_a_deferred_fetch(&global, &deferred_fetch_record);
444
445 let trusted_global = trusted_global.clone();
450 global.task_manager().deferred_fetch_task_source().queue(
451 task!(notify_deferred_record: move || {
452 trusted_global.root().fetch_group().deferred_fetch_record_for_id(&deferred_fetch_record_id).activate();
453 }),
454 );
455 }),
456 duration: Duration::from_millis(*activate_after as u64),
458 });
459 deferred_fetch_record_id
461}
462
463#[expect(non_snake_case)]
465pub(crate) fn FetchLater(
466 cx: &mut JSContext,
467 window: &Window,
468 input: RequestInfo,
469 init: &DeferredRequestInit,
470) -> Fallible<DomRoot<FetchLaterResult>> {
471 let global_scope = window.upcast();
472 let document = window.Document();
473 let request_object = Request::constructor(
476 cx,
477 global_scope,
478 None,
479 input,
480 &init.parent,
481 None, )?;
483 let signal = request_object.Signal();
485 if signal.aborted() {
486 rooted!(&in(cx) let mut abort_reason = UndefinedValue());
487 signal.Reason(abort_reason.handle_mut());
488 assert!(!JS_IsExceptionPending(cx));
489 JS_SetPendingException(cx, abort_reason.handle(), ExceptionStackBehavior::Capture);
490 return Err(Error::JSFailed);
491 }
492 let request = request_object.request();
494 let mut activate_after = Finite::wrap(0_f64);
496 if let Some(init_activate_after) = init.activateAfter.as_ref() {
499 activate_after = *init_activate_after;
500 }
501 if *activate_after < 0.0 {
503 return Err(Error::Range(c"activateAfter must be at least 0".to_owned()));
504 }
505 if !document.is_fully_active() {
507 return Err(Error::Type(c"Document is not fully active".to_owned()));
508 }
509 let url = request.url();
510 if !matches!(url.scheme(), "http" | "https") {
512 return Err(Error::Type(c"URL is not http(s)".to_owned()));
513 }
514 if !url.is_potentially_trustworthy() {
516 return Err(Error::Type(c"URL is not trustworthy".to_owned()));
517 }
518 if request
520 .body
521 .as_ref()
522 .is_some_and(|body| body.len().is_none())
523 {
524 return Err(Error::Type(c"Body is empty".to_owned()));
525 }
526 let quota = document.available_deferred_fetch_quota(request.url().origin());
529 let requested = request.total_request_length() as isize;
530 if quota < requested {
531 return Err(Error::QuotaExceeded {
532 quota: Some(Finite::wrap(quota as f64)),
533 requested: Some(Finite::wrap(requested as f64)),
534 });
535 }
536 let deferred_record_id = queue_deferred_fetch(request.clone(), activate_after, global_scope);
540 signal.add(&AbortAlgorithm::FetchLater(deferred_record_id));
542 Ok(FetchLaterResult::new(cx, window, deferred_record_id))
544}
545
546#[derive(Clone, Copy, MallocSizeOf, PartialEq)]
548pub(crate) enum DeferredFetchRecordInvokeState {
549 Pending,
550 Sent,
551 Aborted,
552}
553
554#[derive(MallocSizeOf)]
556pub(crate) struct DeferredFetchRecord {
557 pub(crate) request: NetTraitsRequest,
559 pub(crate) invoke_state: Cell<DeferredFetchRecordInvokeState>,
561 activated: Cell<bool>,
562}
563
564impl DeferredFetchRecord {
565 fn activate(&self) {
567 self.activated.set(true);
569 }
570 pub(crate) fn abort(&self) {
572 self.invoke_state
574 .set(DeferredFetchRecordInvokeState::Aborted);
575 }
576 pub(crate) fn activated_getter_steps(&self) -> bool {
578 self.activated.get()
580 }
581}
582
583#[derive(Clone, JSTraceable, MallocSizeOf)]
585#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
586pub(crate) struct FetchCallState {
587 pub promise: TracedPromise,
588 pub response: Dom<Response>,
589 pub locally_aborted: Cell<bool>,
590}
591
592impl Rootable for FetchCallState {}
593
594impl FetchCallState {
595 pub(crate) fn abort_fetch(
597 &self,
598 cx: &mut JSContext,
599 request: &Request,
600 abort_reason: HandleValue,
601 global: &GlobalScope,
602 ) {
603 self.locally_aborted.set(true);
605
606 if let Some(controller) = global
614 .fetch_group_mut()
615 .fetch_controller(&request.request().id)
616 {
617 controller.abort();
618 }
619
620 let promise = self.promise.root(cx);
623 abort_fetch_call(
624 &promise,
625 request,
626 Some(&self.response),
627 abort_reason,
628 global,
629 cx,
630 );
631 }
632
633 pub(crate) fn process_response(
634 &self,
635 cx: &mut JSContext,
636 fetch_metadata: Result<FetchMetadata, NetworkError>,
637 ) {
638 if self.locally_aborted.get() {
640 return;
641 }
642 let mut realm = enter_auto_realm(cx, &*self.promise);
643 let cx = &mut realm.current_realm();
644 match fetch_metadata {
645 Err(error) => {
648 self.promise
649 .reject_error(cx, Error::Type(cformat!("Network error: {:?}", error)));
650 self.response.set_type(cx, DOMResponseType::Error);
651 self.response
652 .error_stream(cx, Error::Type(c"Network error occurred".to_owned()));
653 return;
654 },
655 Ok(metadata) => match metadata {
658 FetchMetadata::Unfiltered(m) => {
659 fill_headers_with_metadata(cx, &self.response, m);
660 self.response.set_type(cx, DOMResponseType::Default);
661 },
662 FetchMetadata::Filtered { filtered, .. } => match filtered {
663 FilteredMetadata::Basic(m) => {
664 fill_headers_with_metadata(cx, &self.response, m);
665 self.response.set_type(cx, DOMResponseType::Basic);
666 },
667 FilteredMetadata::Cors(m) => {
668 fill_headers_with_metadata(cx, &self.response, m);
669 self.response.set_type(cx, DOMResponseType::Cors);
670 },
671 FilteredMetadata::Opaque => {
672 self.response.set_type(cx, DOMResponseType::Opaque);
673 },
674 FilteredMetadata::OpaqueRedirect(url) => {
675 self.response.set_type(cx, DOMResponseType::Opaqueredirect);
676 self.response.set_final_url(url);
677 },
678 },
679 },
680 }
681
682 self.promise.resolve_native(cx, &*self.response);
684 }
685
686 pub(crate) fn process_response_chunk(&self, cx: &mut JSContext, chunk: Bytes) {
687 self.response.stream_chunk(cx, chunk);
688 }
689
690 pub(crate) fn process_response_eof(
691 &self,
692 cx: &mut JSContext,
693 response: &Result<(), NetworkError>,
694 ) {
695 let mut realm = enter_auto_realm(cx, &*self.response);
696 let cx = &mut realm.current_realm();
697 if let Err(NetworkError::DecompressionError) = response {
698 self.response
699 .error_stream(cx, Error::Type(c"Network error occurred".to_owned()));
700 }
701 self.response.finish(cx);
702 }
705}
706
707#[derive(JSTraceable, MallocSizeOf)]
708pub(crate) struct FetchContext {
709 #[no_trace]
710 request_id: RequestId,
711 #[no_trace]
712 url: ServoUrl,
713}
714
715impl FetchResponseListener for FetchContext {
717 fn process_request_body(&mut self, _: &GlobalScope, _: RequestId) {
718 }
720
721 fn process_response(
722 &mut self,
723 cx: &mut JSContext,
724 global: &GlobalScope,
725 request_id: RequestId,
726 fetch_metadata: Result<FetchMetadata, NetworkError>,
727 ) {
728 let Some(request) = global.fetch_group().fetch_call_request(&request_id) else {
729 return;
730 };
731 request.process_response(cx, fetch_metadata);
732 }
733
734 fn process_response_chunk(
735 &mut self,
736 cx: &mut JSContext,
737 global: &GlobalScope,
738 request_id: RequestId,
739 chunk: Bytes,
740 ) {
741 let Some(call_state) = global.fetch_group().fetch_call_request(&request_id) else {
742 return;
743 };
744 call_state.process_response_chunk(cx, chunk);
745 }
746
747 fn process_response_eof(
748 self,
749 cx: &mut JSContext,
750 global: &GlobalScope,
751 request_id: RequestId,
752 response: Result<(), NetworkError>,
753 timing: ResourceFetchTiming,
754 ) {
755 let call_state = global.fetch_group().fetch_call_request(&request_id);
756 if let Some(call_state) = call_state {
757 call_state.process_response_eof(cx, &response);
758 };
759
760 network_listener::submit_timing(cx, global, &self, &response, &timing);
762 }
763
764 fn process_csp_violations(
765 &mut self,
766 cx: &mut JSContext,
767 global: &GlobalScope,
768 _request_id: RequestId,
769 violations: Vec<Violation>,
770 ) {
771 global.report_csp_violations(cx, violations, None, None);
772 }
773}
774
775impl ResourceTimingListener for FetchContext {
776 fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
777 (InitiatorType::Fetch, self.url.clone())
778 }
779}
780
781struct FetchLaterListener {
782 url: ServoUrl,
784}
785
786impl FetchResponseListener for FetchLaterListener {
787 fn process_request_body(&mut self, _: &GlobalScope, _: RequestId) {}
788
789 fn process_response(
790 &mut self,
791 _: &mut JSContext,
792 _: &GlobalScope,
793 _: RequestId,
794 fetch_metadata: Result<FetchMetadata, NetworkError>,
795 ) {
796 _ = fetch_metadata;
797 }
798
799 fn process_response_chunk(
800 &mut self,
801 _: &mut JSContext,
802 _: &GlobalScope,
803 _: RequestId,
804 chunk: Bytes,
805 ) {
806 _ = chunk;
807 }
808
809 fn process_response_eof(
810 self,
811 cx: &mut JSContext,
812 global: &GlobalScope,
813 _: RequestId,
814 response: Result<(), NetworkError>,
815 timing: ResourceFetchTiming,
816 ) {
817 network_listener::submit_timing(cx, global, &self, &response, &timing);
818 }
819
820 fn process_csp_violations(
821 &mut self,
822 cx: &mut JSContext,
823 global: &GlobalScope,
824 _request_id: RequestId,
825 violations: Vec<Violation>,
826 ) {
827 global.report_csp_violations(cx, violations, None, None);
828 }
829}
830
831impl ResourceTimingListener for FetchLaterListener {
832 fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
833 (InitiatorType::Fetch, self.url.clone())
834 }
835}
836
837fn fill_headers_with_metadata(cx: &mut JSContext, r: &Response, m: Metadata) {
838 r.set_headers(cx, m.headers);
839 r.set_status(&m.status);
840 r.set_final_url(m.final_url);
841 r.set_redirected(m.redirected);
842}
843
844pub(crate) trait CspViolationsProcessor {
845 fn process_csp_violations(&self, cx: &mut JSContext, violations: Vec<Violation>);
846}
847
848pub(crate) fn load_whole_resource(
850 request: RequestBuilder,
851 core_resource_thread: &CoreResourceThread,
852 global: &GlobalScope,
853 csp_violations_processor: &dyn CspViolationsProcessor,
854 cx: &mut JSContext,
855) -> Result<(Metadata, Vec<u8>, bool), NetworkError> {
856 let (action_sender, action_receiver) = GenericCallback::new_blocking().unwrap();
857 let url = request.url.url();
858 core_resource_thread
859 .send(CoreResourceMsg::Fetch(
860 request,
861 FetchChannels::ResponseMsg(action_sender),
862 ))
863 .unwrap();
864
865 let mut buf = vec![];
866 let mut metadata = None;
867 let mut muted_errors = false;
868 loop {
869 match action_receiver.recv().unwrap() {
870 FetchResponseMsg::ProcessRequestBody(..) => {},
871 FetchResponseMsg::ProcessResponse(_, Ok(m)) => {
872 muted_errors = m.is_cors_cross_origin();
873 metadata = Some(match m {
874 FetchMetadata::Unfiltered(m) => m,
875 FetchMetadata::Filtered { unsafe_, .. } => unsafe_,
876 })
877 },
878 FetchResponseMsg::ProcessResponseChunk(_, data) => buf.extend_from_slice(&data),
879 FetchResponseMsg::ProcessResponseEOF(_, Ok(_), _) => {
880 let metadata = metadata.unwrap();
881 if let Some(timing) = &metadata.timing {
882 submit_timing_data(cx, global, url, InitiatorType::Other, timing);
883 }
884 return Ok((metadata, buf, muted_errors));
885 },
886 FetchResponseMsg::ProcessResponse(_, Err(e)) |
887 FetchResponseMsg::ProcessResponseEOF(_, Err(e), _) => return Err(e),
888 FetchResponseMsg::ProcessCspViolations(_, violations) => {
889 csp_violations_processor.process_csp_violations(cx, violations);
890 },
891 FetchResponseMsg::ProcessContentLength(_request_id, size) => {
892 buf.reserve(size.saturating_sub(buf.len()))
893 },
894 }
895 }
896}
897
898pub(crate) trait RequestWithGlobalScope {
899 fn with_global_scope(self, global: &GlobalScope) -> Self;
900}
901
902impl RequestWithGlobalScope for RequestBuilder {
903 fn with_global_scope(self, global: &GlobalScope) -> Self {
904 self.client(global.request_client(None))
905 .pipeline_id(Some(global.pipeline_id()))
906 }
907}
908
909#[allow(clippy::too_many_arguments)]
913pub(crate) fn create_a_potential_cors_request(
914 webview_id: Option<WebViewId>,
915 url: ServoUrl,
916 destination: Destination,
917 cors_setting: Option<CorsSettings>,
918 same_origin_fallback: Option<bool>,
919 referrer: Referrer,
920) -> RequestBuilder {
921 create_a_potential_cors_request_with_claim(
922 webview_id,
923 UrlWithBlobClaim::from_url_without_having_claimed_blob(url),
924 destination,
925 cors_setting,
926 same_origin_fallback,
927 referrer,
928 )
929}
930
931#[allow(clippy::too_many_arguments)]
933pub(crate) fn create_a_potential_cors_request_with_claim(
934 webview_id: Option<WebViewId>,
935 url: UrlWithBlobClaim,
936 destination: Destination,
937 cors_setting: Option<CorsSettings>,
938 same_origin_fallback: Option<bool>,
939 referrer: Referrer,
940) -> RequestBuilder {
941 RequestBuilder::new(webview_id, url, referrer)
942 .mode(match cors_setting {
944 Some(_) => RequestMode::CorsMode,
945 None if same_origin_fallback == Some(true) => RequestMode::SameOrigin,
947 None => RequestMode::NoCors,
948 })
949 .credentials_mode(match cors_setting {
950 Some(CorsSettings::Anonymous) => CredentialsMode::CredentialsSameOrigin,
952 _ => CredentialsMode::Include,
954 })
955 .destination(destination)
958 .use_url_credentials(true)
959}