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::bindings::trace::RootedTraceableBox;
51use crate::dom::csp::{GlobalCspReporting, Violation};
52use crate::dom::fetchlaterresult::FetchLaterResult;
53use crate::dom::globalscope::GlobalScope;
54use crate::dom::headers::Guard;
55use crate::dom::performance::performanceresourcetiming::InitiatorType;
56use crate::dom::promise::{Promise, RootedPromise};
57use crate::dom::request::Request;
58use crate::dom::response::Response;
59use crate::dom::serviceworkerglobalscope::ServiceWorkerGlobalScope;
60use crate::dom::window::Window;
61use crate::fetch::body::BodyMixin;
62use crate::fetch::network_listener::{
63 self, FetchResponseListener, NetworkListener, ResourceTimingListener, submit_timing_data,
64};
65use crate::realms::enter_auto_realm;
66
67#[derive(Default, JSTraceable, MallocSizeOf)]
72pub(crate) struct FetchCanceller {
73 #[no_trace]
74 request_id: Option<RequestId>,
75 #[no_trace]
76 core_resource_thread: Option<CoreResourceThread>,
77 keep_alive: bool,
78}
79
80impl FetchCanceller {
81 pub(crate) fn new(
84 request_id: RequestId,
85 keep_alive: bool,
86 core_resource_thread: CoreResourceThread,
87 ) -> Self {
88 Self {
89 request_id: Some(request_id),
90 core_resource_thread: Some(core_resource_thread),
91 keep_alive,
92 }
93 }
94
95 pub(crate) fn keep_alive(&self) -> bool {
96 self.keep_alive
97 }
98
99 fn cancel(&mut self) {
100 if let Some(request_id) = self.request_id.take() {
101 if let Some(ref core_resource_thread) = self.core_resource_thread {
105 cancel_async_fetch(vec![request_id], core_resource_thread);
108 }
109 }
110 }
111
112 pub(crate) fn ignore(&mut self) {
115 let _ = self.request_id.take();
116 }
117
118 pub(crate) fn abort(&mut self) {
120 self.cancel();
121 }
122
123 pub(crate) fn terminate(&mut self) {
125 self.cancel();
126 }
127}
128
129#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
131pub(crate) struct DeferredFetchRecordId(Uuid);
132
133impl Default for DeferredFetchRecordId {
134 fn default() -> Self {
135 Self(Uuid::new_v4())
136 }
137}
138
139pub(crate) type QueuedDeferredFetchRecord = Rc<DeferredFetchRecord>;
140
141#[derive(MallocSizeOf)]
143pub(crate) struct FetchRecord {
144 controller: Option<FetchCanceller>,
148 done: bool,
153}
154
155#[derive(MallocSizeOf)]
157pub(crate) struct FetchGroup {
158 core_resource_thread: CoreResourceThread,
160 #[conditional_malloc_size_of]
162 pub(crate) deferred_fetch_records: FxHashMap<DeferredFetchRecordId, QueuedDeferredFetchRecord>,
163 pub(crate) fetch_records: FxHashMap<RequestId, FetchRecord>,
165}
166
167impl FetchGroup {
168 pub(crate) fn new(core_resource_thread: CoreResourceThread) -> Self {
169 Self {
170 core_resource_thread,
171 deferred_fetch_records: Default::default(),
172 fetch_records: Default::default(),
173 }
174 }
175
176 pub(crate) fn fetch<Listener: FetchResponseListener>(
177 &mut self,
178 request: RequestBuilder,
179 listener: NetworkListener<Listener>,
180 ) {
181 self.fetch_records.insert(
182 request.id,
183 FetchRecord {
184 controller: Some(FetchCanceller::new(
185 request.id,
186 request.keep_alive,
187 self.core_resource_thread.clone(),
188 )),
189 done: false,
190 },
191 );
192 fetch_async(
193 &self.core_resource_thread,
194 request,
195 None,
196 listener.into_callback(),
197 );
198 }
199
200 pub(crate) fn deferred_fetches(&self) -> Vec<QueuedDeferredFetchRecord> {
201 self.deferred_fetch_records.values().cloned().collect()
202 }
203
204 fn append_deferred_fetch(
205 &mut self,
206 deferred_record: DeferredFetchRecord,
207 ) -> DeferredFetchRecordId {
208 let deferred_fetch_record_id = DeferredFetchRecordId::default();
209 self.deferred_fetch_records
210 .insert(deferred_fetch_record_id, Rc::new(deferred_record));
211 deferred_fetch_record_id
212 }
213
214 pub(crate) fn deferred_fetch_record_for_id(
215 &self,
216 deferred_fetch_record_id: &DeferredFetchRecordId,
217 ) -> QueuedDeferredFetchRecord {
218 self.deferred_fetch_records
219 .get(deferred_fetch_record_id)
220 .expect("Should always use a generated fetch_record_id instead of passing your own")
221 .clone()
222 }
223
224 pub(crate) fn fetch_controller(
225 &mut self,
226 request_id: &RequestId,
227 ) -> Option<&mut FetchCanceller> {
228 self.fetch_records.get_mut(request_id)?.controller.as_mut()
229 }
230
231 pub(crate) fn terminate(&mut self, global: &GlobalScope) -> bool {
235 let mut cancelled_any = false;
239 self.fetch_records.retain(|_, fetch_record| {
240 let Some(controller) = fetch_record.controller.as_mut() else {
241 return false;
242 };
243 if fetch_record.done {
244 return false;
245 }
246 if !controller.keep_alive() {
247 controller.terminate();
248 cancelled_any = true;
249 return false;
250 }
251 true
252 });
253
254 self.process_deferred_fetches(global);
256
257 cancelled_any
258 }
259
260 pub(crate) fn process_deferred_fetches(&mut self, global: &GlobalScope) {
262 for deferred_fetch in self.deferred_fetches() {
265 self.process_a_deferred_fetch(global, &deferred_fetch);
266 }
267 }
268
269 pub(crate) fn process_a_deferred_fetch(
271 &mut self,
272 global: &GlobalScope,
273 deferred_fetch: &DeferredFetchRecord,
274 ) {
275 if deferred_fetch.invoke_state.get() != DeferredFetchRecordInvokeState::Pending {
277 return;
278 }
279 deferred_fetch
281 .invoke_state
282 .set(DeferredFetchRecordInvokeState::Sent);
283 let fetch_later_listener = FetchLaterListener {
285 url: deferred_fetch.request.url(),
286 global: Trusted::new(global),
287 };
288 let task_source = global.task_manager().networking_task_source().to_sendable();
289 self.fetch(
290 request_init_from_request(deferred_fetch.request.clone(), global),
291 NetworkListener::new(fetch_later_listener, task_source, global),
292 );
293 }
295
296 pub(crate) fn mark_fetch_request_as_done(&mut self, request_id: &RequestId) {
297 if let Some(fetch_record) = self.fetch_records.get_mut(request_id) {
298 fetch_record.done = true;
299 }
300 }
301}
302
303fn request_init_from_request(request: NetTraitsRequest, global: &GlobalScope) -> RequestBuilder {
304 let mut builder = RequestBuilder::new(
305 request.target_webview_id,
306 request.url_with_blob_claim(),
307 request.referrer,
308 )
309 .method(request.method)
310 .headers(request.headers)
311 .unsafe_request(request.unsafe_request)
312 .body(request.body)
313 .destination(request.destination)
314 .synchronous(request.synchronous)
315 .mode(request.mode)
316 .cache_mode(request.cache_mode)
317 .use_cors_preflight(request.use_cors_preflight)
318 .credentials_mode(request.credentials_mode)
319 .use_url_credentials(request.use_url_credentials)
320 .referrer_policy(request.referrer_policy)
321 .pipeline_id(request.pipeline_id)
322 .redirect_mode(request.redirect_mode)
323 .integrity_metadata(request.integrity_metadata)
324 .cryptographic_nonce_metadata(request.cryptographic_nonce_metadata)
325 .parser_metadata(request.parser_metadata)
326 .initiator(request.initiator)
327 .client(global.request_client(None))
328 .response_tainting(request.response_tainting);
329 builder.id = request.id;
330 builder.reload_navigation = request.reload_navigation;
331 builder.history_navigation = request.history_navigation;
332 builder
333}
334
335fn abort_fetch_call(
337 promise: &RootedPromise,
338 request: &Request,
339 response_object: Option<&Response>,
340 abort_reason: HandleValue,
341 global: &GlobalScope,
342 cx: &mut JSContext,
343) {
344 promise.reject(cx, abort_reason);
346 if let Some(body) = request.body() &&
348 body.is_readable()
349 {
350 body.cancel(cx, global, abort_reason);
351 }
352 let Some(response) = response_object else {
355 return;
356 };
357 if let Some(body) = response.body() &&
359 body.is_readable()
360 {
361 body.error(cx, abort_reason);
362 }
363}
364
365#[expect(non_snake_case)]
367pub(crate) fn Fetch(
368 global: &GlobalScope,
369 input: RequestInfo,
370 init: RootedTraceableBox<RequestInit>,
371 cx: &mut CurrentRealm,
372) -> RootedPromise {
373 let promise = Promise::new_in_realm_rooted(cx);
375
376 let response = Response::new(cx, global);
379 response.Headers(cx).set_guard(Guard::Immutable);
380
381 let request_object = match Request::Constructor(cx, global, None, input, init) {
384 Err(e) => {
385 response.error_stream(cx, e.clone());
386 promise.reject_error(cx, e);
387 return promise;
388 },
389 Ok(r) => r,
390 };
391 let request = request_object.request().clone();
393
394 let signal = request_object.Signal();
396 if signal.aborted() {
397 rooted!(&in(cx) let mut abort_reason = UndefinedValue());
399 signal.Reason(abort_reason.handle_mut());
400 abort_fetch_call(
401 &promise,
402 &request_object,
403 None,
404 abort_reason.handle(),
405 global,
406 cx,
407 );
408 return promise;
410 }
411
412 let mut request_builder = request_init_from_request(request, global);
415
416 if global.is::<ServiceWorkerGlobalScope>() {
419 request_builder.service_workers_mode = ServiceWorkersMode::None;
420 }
421
422 let fetch_context = FetchContext {
429 fetch_promise: Some(TrustedPromise::from(&promise)),
430 response_object: Trusted::new(&*response),
431 request: Trusted::new(&*request_object),
432 global: Trusted::new(global),
433 locally_aborted: false,
434 url: request_builder.url.url(),
435 };
436 let network_listener = NetworkListener::new(
437 fetch_context,
438 global.task_manager().networking_task_source().to_sendable(),
439 global,
440 );
441 let fetch_context = network_listener.context.clone();
442
443 signal.add(&AbortAlgorithm::Fetch(fetch_context));
445
446 global
449 .fetch_group_mut()
450 .fetch(request_builder, network_listener);
451
452 promise
454}
455
456fn queue_deferred_fetch(
458 request: NetTraitsRequest,
459 activate_after: Finite<f64>,
460 global: &GlobalScope,
461) -> DeferredFetchRecordId {
462 let trusted_global = Trusted::new(global);
463 let mut request = request;
464 request.client = Some(global.request_client(None));
466 request.populate_request_from_client();
467 request.service_workers_mode = ServiceWorkersMode::None;
469 request.keep_alive = true;
471 let deferred_record = DeferredFetchRecord {
473 request,
474 invoke_state: Cell::new(DeferredFetchRecordInvokeState::Pending),
475 activated: Cell::new(false),
476 };
477
478 let deferred_fetch_record_id = global
480 .fetch_group_mut()
481 .append_deferred_fetch(deferred_record);
482
483 global.schedule_timer(TimerEventRequest {
485 callback: Box::new(move || {
486 let global = trusted_global.root();
488 let mut fetch_group = global.fetch_group_mut();
489 let deferred_fetch_record =
490 fetch_group.deferred_fetch_record_for_id(&deferred_fetch_record_id);
491 fetch_group.process_a_deferred_fetch(&global, &deferred_fetch_record);
492
493 let trusted_global = trusted_global.clone();
498 global.task_manager().deferred_fetch_task_source().queue(
499 task!(notify_deferred_record: move || {
500 trusted_global.root().fetch_group().deferred_fetch_record_for_id(&deferred_fetch_record_id).activate();
501 }),
502 );
503 }),
504 duration: Duration::from_millis(*activate_after as u64),
506 });
507 deferred_fetch_record_id
509}
510
511#[expect(non_snake_case, unsafe_code)]
513pub(crate) fn FetchLater(
514 cx: &mut JSContext,
515 window: &Window,
516 input: RequestInfo,
517 init: RootedTraceableBox<DeferredRequestInit>,
518) -> Fallible<DomRoot<FetchLaterResult>> {
519 let global_scope = window.upcast();
520 let document = window.Document();
521 let request_object = Request::constructor(cx, global_scope, None, input, &init.parent)?;
524 let signal = request_object.Signal();
526 if signal.aborted() {
527 rooted!(&in(cx) let mut abort_reason = UndefinedValue());
528 signal.Reason(abort_reason.handle_mut());
529 unsafe {
530 assert!(!JS_IsExceptionPending(cx));
531 JS_SetPendingException(cx, abort_reason.handle(), ExceptionStackBehavior::Capture)
532 };
533 return Err(Error::JSFailed);
534 }
535 let request = request_object.request();
537 let mut activate_after = Finite::wrap(0_f64);
539 if let Some(init_activate_after) = init.activateAfter.as_ref() {
542 activate_after = *init_activate_after;
543 }
544 if *activate_after < 0.0 {
546 return Err(Error::Range(c"activateAfter must be at least 0".to_owned()));
547 }
548 if !document.is_fully_active() {
550 return Err(Error::Type(c"Document is not fully active".to_owned()));
551 }
552 let url = request.url();
553 if !matches!(url.scheme(), "http" | "https") {
555 return Err(Error::Type(c"URL is not http(s)".to_owned()));
556 }
557 if !url.is_potentially_trustworthy() {
559 return Err(Error::Type(c"URL is not trustworthy".to_owned()));
560 }
561 if request
563 .body
564 .as_ref()
565 .is_some_and(|body| body.len().is_none())
566 {
567 return Err(Error::Type(c"Body is empty".to_owned()));
568 }
569 let quota = document.available_deferred_fetch_quota(request.url().origin());
572 let requested = request.total_request_length() as isize;
573 if quota < requested {
574 return Err(Error::QuotaExceeded {
575 quota: Some(Finite::wrap(quota as f64)),
576 requested: Some(Finite::wrap(requested as f64)),
577 });
578 }
579 let deferred_record_id = queue_deferred_fetch(request.clone(), activate_after, global_scope);
583 signal.add(&AbortAlgorithm::FetchLater(deferred_record_id));
585 Ok(FetchLaterResult::new(cx, window, deferred_record_id))
587}
588
589#[derive(Clone, Copy, MallocSizeOf, PartialEq)]
591pub(crate) enum DeferredFetchRecordInvokeState {
592 Pending,
593 Sent,
594 Aborted,
595}
596
597#[derive(MallocSizeOf)]
599pub(crate) struct DeferredFetchRecord {
600 pub(crate) request: NetTraitsRequest,
602 pub(crate) invoke_state: Cell<DeferredFetchRecordInvokeState>,
604 activated: Cell<bool>,
605}
606
607impl DeferredFetchRecord {
608 fn activate(&self) {
610 self.activated.set(true);
612 }
613 pub(crate) fn abort(&self) {
615 self.invoke_state
617 .set(DeferredFetchRecordInvokeState::Aborted);
618 }
619 pub(crate) fn activated_getter_steps(&self) -> bool {
621 self.activated.get()
623 }
624}
625
626#[derive(JSTraceable, MallocSizeOf)]
627pub(crate) struct FetchContext {
628 #[ignore_malloc_size_of = "unclear ownership semantics"]
629 fetch_promise: Option<TrustedPromise>,
630 response_object: Trusted<Response>,
631 request: Trusted<Request>,
632 global: Trusted<GlobalScope>,
633 locally_aborted: bool,
634 #[no_trace]
635 url: ServoUrl,
636}
637
638impl FetchContext {
639 pub(crate) fn abort_fetch(&mut self, abort_reason: HandleValue, cx: &mut JSContext) {
641 self.locally_aborted = true;
643
644 let global = self.global.root();
652 let request = self.request.root();
653 if let Some(controller) = global
654 .fetch_group_mut()
655 .fetch_controller(&request.request().id)
656 {
657 controller.abort();
658 }
659
660 let promise = self
663 .fetch_promise
664 .take()
665 .expect("fetch promise is missing")
666 .root(cx);
667 abort_fetch_call(
668 &promise,
669 &request,
670 Some(&self.response_object.root()),
671 abort_reason,
672 &global,
673 cx,
674 );
675 }
676}
677
678impl FetchResponseListener for FetchContext {
680 fn process_request_body(&mut self, _: RequestId) {
681 }
683
684 fn process_response(
685 &mut self,
686 cx: &mut JSContext,
687 _: RequestId,
688 fetch_metadata: Result<FetchMetadata, NetworkError>,
689 ) {
690 if self.locally_aborted {
692 return;
693 }
694 let promise = self
695 .fetch_promise
696 .take()
697 .expect("fetch promise is missing")
698 .root(cx);
699
700 let mut realm = enter_auto_realm(cx, &*promise);
701 let cx = &mut realm.current_realm();
702 match fetch_metadata {
703 Err(error) => {
706 promise.reject_error(cx, Error::Type(cformat!("Network error: {:?}", error)));
707 self.fetch_promise = Some(TrustedPromise::from(&promise));
708 let response = self.response_object.root();
709 response.set_type(cx, DOMResponseType::Error);
710 response.error_stream(cx, Error::Type(c"Network error occurred".to_owned()));
711 return;
712 },
713 Ok(metadata) => match metadata {
716 FetchMetadata::Unfiltered(m) => {
717 let r = self.response_object.root();
718 fill_headers_with_metadata(cx, &r, m);
719 r.set_type(cx, DOMResponseType::Default);
720 },
721 FetchMetadata::Filtered { filtered, .. } => match filtered {
722 FilteredMetadata::Basic(m) => {
723 let r = self.response_object.root();
724 fill_headers_with_metadata(cx, &r, m);
725 r.set_type(cx, DOMResponseType::Basic);
726 },
727 FilteredMetadata::Cors(m) => {
728 let r = self.response_object.root();
729 fill_headers_with_metadata(cx, &r, m);
730 r.set_type(cx, DOMResponseType::Cors);
731 },
732 FilteredMetadata::Opaque => {
733 self.response_object
734 .root()
735 .set_type(cx, DOMResponseType::Opaque);
736 },
737 FilteredMetadata::OpaqueRedirect(url) => {
738 let r = self.response_object.root();
739 r.set_type(cx, DOMResponseType::Opaqueredirect);
740 r.set_final_url(url);
741 },
742 },
743 },
744 }
745
746 promise.resolve_native(cx, &self.response_object.root());
748 self.fetch_promise = Some(TrustedPromise::from(&promise));
749 }
750
751 fn process_response_chunk(&mut self, cx: &mut JSContext, _: RequestId, chunk: Bytes) {
752 let response = self.response_object.root();
753 response.stream_chunk(cx, chunk);
754 }
755
756 fn process_response_eof(
757 self,
758 cx: &mut JSContext,
759 _: RequestId,
760 response: Result<(), NetworkError>,
761 timing: ResourceFetchTiming,
762 ) {
763 let response_object = self.response_object.root();
764 let mut realm = enter_auto_realm(cx, &*response_object);
765 let cx = &mut realm.current_realm();
766 if let Err(ref error) = response &&
767 *error == NetworkError::DecompressionError
768 {
769 response_object.error_stream(cx, Error::Type(c"Network error occurred".to_owned()));
770 }
771 response_object.finish(cx);
772 network_listener::submit_timing(cx, &self, &response, &timing);
777 }
778
779 fn process_csp_violations(
780 &mut self,
781 cx: &mut JSContext,
782 _request_id: RequestId,
783 violations: Vec<Violation>,
784 ) {
785 let global = &self.resource_timing_global();
786 global.report_csp_violations(cx, violations, None, None);
787 }
788}
789
790impl ResourceTimingListener for FetchContext {
791 fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
792 (InitiatorType::Fetch, self.url.clone())
793 }
794
795 fn resource_timing_global(&self) -> DomRoot<GlobalScope> {
796 self.response_object.root().global()
797 }
798}
799
800struct FetchLaterListener {
801 url: ServoUrl,
803 global: Trusted<GlobalScope>,
805}
806
807impl FetchResponseListener for FetchLaterListener {
808 fn process_request_body(&mut self, _: RequestId) {}
809
810 fn process_response(
811 &mut self,
812 _: &mut JSContext,
813 _: RequestId,
814 fetch_metadata: Result<FetchMetadata, NetworkError>,
815 ) {
816 _ = fetch_metadata;
817 }
818
819 fn process_response_chunk(&mut self, _: &mut JSContext, _: RequestId, chunk: Bytes) {
820 _ = chunk;
821 }
822
823 fn process_response_eof(
824 self,
825 cx: &mut JSContext,
826 _: RequestId,
827 response: Result<(), NetworkError>,
828 timing: ResourceFetchTiming,
829 ) {
830 network_listener::submit_timing(cx, &self, &response, &timing);
831 }
832
833 fn process_csp_violations(
834 &mut self,
835 cx: &mut JSContext,
836 _request_id: RequestId,
837 violations: Vec<Violation>,
838 ) {
839 let global = self.resource_timing_global();
840 global.report_csp_violations(cx, violations, None, None);
841 }
842}
843
844impl ResourceTimingListener for FetchLaterListener {
845 fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
846 (InitiatorType::Fetch, self.url.clone())
847 }
848
849 fn resource_timing_global(&self) -> DomRoot<GlobalScope> {
850 self.global.root()
851 }
852}
853
854fn fill_headers_with_metadata(cx: &mut JSContext, r: &Response, m: Metadata) {
855 r.set_headers(cx, m.headers);
856 r.set_status(&m.status);
857 r.set_final_url(m.final_url);
858 r.set_redirected(m.redirected);
859}
860
861pub(crate) trait CspViolationsProcessor {
862 fn process_csp_violations(&self, cx: &mut JSContext, violations: Vec<Violation>);
863}
864
865pub(crate) fn load_whole_resource(
867 request: RequestBuilder,
868 core_resource_thread: &CoreResourceThread,
869 global: &GlobalScope,
870 csp_violations_processor: &dyn CspViolationsProcessor,
871 cx: &mut JSContext,
872) -> Result<(Metadata, Vec<u8>, bool), NetworkError> {
873 let (action_sender, action_receiver) = GenericCallback::new_blocking().unwrap();
874 let url = request.url.url();
875 core_resource_thread
876 .send(CoreResourceMsg::Fetch(
877 request,
878 FetchChannels::ResponseMsg(action_sender),
879 ))
880 .unwrap();
881
882 let mut buf = vec![];
883 let mut metadata = None;
884 let mut muted_errors = false;
885 loop {
886 match action_receiver.recv().unwrap() {
887 FetchResponseMsg::ProcessRequestBody(..) => {},
888 FetchResponseMsg::ProcessResponse(_, Ok(m)) => {
889 muted_errors = m.is_cors_cross_origin();
890 metadata = Some(match m {
891 FetchMetadata::Unfiltered(m) => m,
892 FetchMetadata::Filtered { unsafe_, .. } => unsafe_,
893 })
894 },
895 FetchResponseMsg::ProcessResponseChunk(_, data) => buf.extend_from_slice(&data),
896 FetchResponseMsg::ProcessResponseEOF(_, Ok(_), _) => {
897 let metadata = metadata.unwrap();
898 if let Some(timing) = &metadata.timing {
899 submit_timing_data(cx, global, url, InitiatorType::Other, timing);
900 }
901 return Ok((metadata, buf, muted_errors));
902 },
903 FetchResponseMsg::ProcessResponse(_, Err(e)) |
904 FetchResponseMsg::ProcessResponseEOF(_, Err(e), _) => return Err(e),
905 FetchResponseMsg::ProcessCspViolations(_, violations) => {
906 csp_violations_processor.process_csp_violations(cx, violations);
907 },
908 FetchResponseMsg::ProcessContentLength(_request_id, size) => {
909 buf.reserve(size.saturating_sub(buf.len()))
910 },
911 }
912 }
913}
914
915pub(crate) trait RequestWithGlobalScope {
916 fn with_global_scope(self, global: &GlobalScope) -> Self;
917}
918
919impl RequestWithGlobalScope for RequestBuilder {
920 fn with_global_scope(self, global: &GlobalScope) -> Self {
921 self.client(global.request_client(None))
922 .pipeline_id(Some(global.pipeline_id()))
923 }
924}
925
926#[allow(clippy::too_many_arguments)]
930pub(crate) fn create_a_potential_cors_request(
931 webview_id: Option<WebViewId>,
932 url: ServoUrl,
933 destination: Destination,
934 cors_setting: Option<CorsSettings>,
935 same_origin_fallback: Option<bool>,
936 referrer: Referrer,
937) -> RequestBuilder {
938 create_a_potential_cors_request_with_claim(
939 webview_id,
940 UrlWithBlobClaim::from_url_without_having_claimed_blob(url),
941 destination,
942 cors_setting,
943 same_origin_fallback,
944 referrer,
945 )
946}
947
948#[allow(clippy::too_many_arguments)]
950pub(crate) fn create_a_potential_cors_request_with_claim(
951 webview_id: Option<WebViewId>,
952 url: UrlWithBlobClaim,
953 destination: Destination,
954 cors_setting: Option<CorsSettings>,
955 same_origin_fallback: Option<bool>,
956 referrer: Referrer,
957) -> RequestBuilder {
958 RequestBuilder::new(webview_id, url, referrer)
959 .mode(match cors_setting {
961 Some(_) => RequestMode::CorsMode,
962 None if same_origin_fallback == Some(true) => RequestMode::SameOrigin,
964 None => RequestMode::NoCors,
965 })
966 .credentials_mode(match cors_setting {
967 Some(CorsSettings::Anonymous) => CredentialsMode::CredentialsSameOrigin,
969 _ => CredentialsMode::Include,
971 })
972 .destination(destination)
975 .use_url_credentials(true)
976}