1use std::cell::Cell;
6use std::rc::Rc;
7use std::sync::{Arc, Mutex};
8use std::time::Duration;
9
10use base::id::WebViewId;
11use ipc_channel::ipc;
12use js::jsapi::{ExceptionStackBehavior, JS_IsExceptionPending};
13use js::jsval::UndefinedValue;
14use js::rust::HandleValue;
15use js::rust::wrappers::JS_SetPendingException;
16use net_traits::policy_container::PolicyContainer;
17use net_traits::request::{
18 CorsSettings, CredentialsMode, Destination, InsecureRequestsPolicy, Referrer,
19 Request as NetTraitsRequest, RequestBuilder, RequestId, RequestMode, ServiceWorkersMode,
20};
21use net_traits::{
22 CoreResourceMsg, CoreResourceThread, FetchChannels, FetchMetadata, FetchResponseMsg,
23 FilteredMetadata, Metadata, NetworkError, ResourceFetchTiming, ResourceTimingType,
24 cancel_async_fetch,
25};
26use servo_url::ServoUrl;
27use timers::TimerEventRequest;
28
29use crate::body::BodyMixin;
30use crate::dom::abortsignal::AbortAlgorithm;
31use crate::dom::bindings::codegen::Bindings::AbortSignalBinding::AbortSignalMethods;
32use crate::dom::bindings::codegen::Bindings::RequestBinding::{
33 RequestInfo, RequestInit, RequestMethods,
34};
35use crate::dom::bindings::codegen::Bindings::ResponseBinding::Response_Binding::ResponseMethods;
36use crate::dom::bindings::codegen::Bindings::ResponseBinding::ResponseType as DOMResponseType;
37use crate::dom::bindings::codegen::Bindings::WindowBinding::{DeferredRequestInit, WindowMethods};
38use crate::dom::bindings::error::{Error, Fallible};
39use crate::dom::bindings::import::module::SafeJSContext;
40use crate::dom::bindings::inheritance::Castable;
41use crate::dom::bindings::num::Finite;
42use crate::dom::bindings::refcounted::{Trusted, TrustedPromise};
43use crate::dom::bindings::reflector::DomGlobal;
44use crate::dom::bindings::root::DomRoot;
45use crate::dom::bindings::trace::RootedTraceableBox;
46use crate::dom::csp::{GlobalCspReporting, Violation};
47use crate::dom::fetchlaterresult::FetchLaterResult;
48use crate::dom::globalscope::GlobalScope;
49use crate::dom::headers::Guard;
50use crate::dom::performance::performanceresourcetiming::InitiatorType;
51use crate::dom::promise::Promise;
52use crate::dom::request::Request;
53use crate::dom::response::Response;
54use crate::dom::serviceworkerglobalscope::ServiceWorkerGlobalScope;
55use crate::dom::window::Window;
56use crate::network_listener::{
57 self, FetchResponseListener, NetworkListener, ResourceTimingListener, submit_timing_data,
58};
59use crate::realms::{InRealm, enter_realm};
60use crate::script_runtime::CanGc;
61
62#[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}
75
76impl FetchCanceller {
77 pub(crate) fn new(request_id: RequestId, core_resource_thread: CoreResourceThread) -> Self {
80 Self {
81 request_id: Some(request_id),
82 core_resource_thread: Some(core_resource_thread),
83 }
84 }
85
86 pub(crate) fn cancel(&mut self) {
88 if let Some(request_id) = self.request_id.take() {
89 if let Some(ref core_resource_thread) = self.core_resource_thread {
93 cancel_async_fetch(vec![request_id], core_resource_thread);
96 }
97 }
98 }
99
100 pub(crate) fn ignore(&mut self) {
103 let _ = self.request_id.take();
104 }
105}
106
107impl Drop for FetchCanceller {
108 fn drop(&mut self) {
109 self.cancel()
110 }
111}
112
113fn request_init_from_request(request: NetTraitsRequest, global: &GlobalScope) -> RequestBuilder {
114 let mut builder =
115 RequestBuilder::new(request.target_webview_id, request.url(), request.referrer)
116 .method(request.method)
117 .headers(request.headers)
118 .unsafe_request(request.unsafe_request)
119 .body(request.body)
120 .destination(request.destination)
121 .synchronous(request.synchronous)
122 .mode(request.mode)
123 .cache_mode(request.cache_mode)
124 .use_cors_preflight(request.use_cors_preflight)
125 .credentials_mode(request.credentials_mode)
126 .use_url_credentials(request.use_url_credentials)
127 .referrer_policy(request.referrer_policy)
128 .pipeline_id(request.pipeline_id)
129 .redirect_mode(request.redirect_mode)
130 .integrity_metadata(request.integrity_metadata)
131 .cryptographic_nonce_metadata(request.cryptographic_nonce_metadata)
132 .parser_metadata(request.parser_metadata)
133 .initiator(request.initiator)
134 .client(global.request_client())
135 .insecure_requests_policy(request.insecure_requests_policy)
136 .has_trustworthy_ancestor_origin(request.has_trustworthy_ancestor_origin)
137 .https_state(request.https_state)
138 .response_tainting(request.response_tainting);
139 builder.id = request.id;
140 builder
141}
142
143fn abort_fetch_call(
145 promise: Rc<Promise>,
146 request: &Request,
147 response_object: Option<&Response>,
148 abort_reason: HandleValue,
149 global: &GlobalScope,
150 cx: SafeJSContext,
151 can_gc: CanGc,
152) {
153 promise.reject(cx, abort_reason, can_gc);
155 if let Some(body) = request.body() {
157 if body.is_readable() {
158 body.cancel(cx, global, abort_reason, can_gc);
159 }
160 }
161 let Some(response) = response_object else {
164 return;
165 };
166 if let Some(body) = response.body() {
168 if body.is_readable() {
169 body.error(abort_reason, can_gc);
170 }
171 }
172}
173
174#[allow(non_snake_case)]
176#[cfg_attr(crown, allow(crown::unrooted_must_root))]
177pub(crate) fn Fetch(
178 global: &GlobalScope,
179 input: RequestInfo,
180 init: RootedTraceableBox<RequestInit>,
181 comp: InRealm,
182 can_gc: CanGc,
183) -> Rc<Promise> {
184 let promise = Promise::new_in_current_realm(comp, can_gc);
186 let cx = GlobalScope::get_cx();
187
188 let response = Response::new(global, can_gc);
191 response.Headers(can_gc).set_guard(Guard::Immutable);
192
193 let request_object = match Request::Constructor(global, None, can_gc, input, init) {
196 Err(e) => {
197 response.error_stream(e.clone(), can_gc);
198 promise.reject_error(e, can_gc);
199 return promise;
200 },
201 Ok(r) => r,
202 };
203 let request = request_object.get_request();
205 let request_id = request.id;
206
207 let signal = request_object.Signal();
209 if signal.aborted() {
210 rooted!(in(*cx) let mut abort_reason = UndefinedValue());
212 signal.Reason(cx, abort_reason.handle_mut());
213 abort_fetch_call(
214 promise.clone(),
215 &request_object,
216 None,
217 abort_reason.handle(),
218 global,
219 cx,
220 can_gc,
221 );
222 return promise;
224 }
225
226 let mut request_init = request_init_from_request(request, global);
229
230 if global.is::<ServiceWorkerGlobalScope>() {
233 request_init.service_workers_mode = ServiceWorkersMode::None;
234 }
235
236 let fetch_context = FetchContext {
243 fetch_promise: Some(TrustedPromise::new(promise.clone())),
244 response_object: Trusted::new(&*response),
245 request: Trusted::new(&*request_object),
246 global: Trusted::new(global),
247 locally_aborted: false,
248 canceller: FetchCanceller::new(request_id, global.core_resource_thread()),
249 };
250 let network_listener = NetworkListener::new(
251 fetch_context,
252 global.task_manager().networking_task_source().to_sendable(),
253 );
254 let fetch_context = network_listener.context.clone();
255
256 signal.add(&AbortAlgorithm::Fetch(fetch_context));
258
259 global.fetch_with_network_listener(request_init, network_listener);
262
263 promise
265}
266
267fn queue_deferred_fetch(
269 request: NetTraitsRequest,
270 activate_after: Finite<f64>,
271 global: &GlobalScope,
272) -> Arc<Mutex<DeferredFetchRecord>> {
273 let trusted_global = Trusted::new(global);
274 let deferred_record = Arc::new(Mutex::new(DeferredFetchRecord {
282 request,
283 global: trusted_global.clone(),
284 invoke_state: Cell::new(DeferredFetchRecordInvokeState::Pending),
285 activated: Cell::new(false),
286 }));
287 let deferred_record_clone = deferred_record.clone();
291 global.schedule_timer(TimerEventRequest {
292 callback: Box::new(move || {
293 deferred_record_clone.lock().unwrap().process();
295
296 let deferred_record_clone = deferred_record_clone.clone();
301 trusted_global
302 .root()
303 .task_manager()
304 .deferred_fetch_task_source()
305 .queue(task!(notify_deferred_record: move || {
306 deferred_record_clone.lock().unwrap().activate();
307 }));
308 }),
309 duration: Duration::from_millis(*activate_after as u64),
311 });
312 deferred_record
314}
315
316#[allow(non_snake_case, unsafe_code)]
318pub(crate) fn FetchLater(
319 window: &Window,
320 input: RequestInfo,
321 init: RootedTraceableBox<DeferredRequestInit>,
322 can_gc: CanGc,
323) -> Fallible<DomRoot<FetchLaterResult>> {
324 let global_scope = window.upcast();
325 let request_object = Request::constructor(global_scope, None, can_gc, input, &init.parent)?;
328 let signal = request_object.Signal();
330 if signal.aborted() {
331 let cx = GlobalScope::get_cx();
332 rooted!(in(*cx) let mut abort_reason = UndefinedValue());
333 signal.Reason(cx, abort_reason.handle_mut());
334 unsafe {
335 assert!(!JS_IsExceptionPending(*cx));
336 JS_SetPendingException(*cx, abort_reason.handle(), ExceptionStackBehavior::Capture);
337 }
338 return Err(Error::JSFailed);
339 }
340 let request = request_object.get_request();
342 let mut activate_after = Finite::wrap(0_f64);
344 if let Some(init_activate_after) = init.activateAfter.as_ref() {
347 activate_after = *init_activate_after;
348 }
349 if *activate_after < 0.0 {
351 return Err(Error::Range("activateAfter must be at least 0".to_owned()));
352 }
353 if !window.Document().is_fully_active() {
355 return Err(Error::Type("Document is not fully active".to_owned()));
356 }
357 let url = request.url();
358 if !matches!(url.scheme(), "http" | "https") {
360 return Err(Error::Type("URL is not http(s)".to_owned()));
361 }
362 if !url.is_potentially_trustworthy() {
364 return Err(Error::Type("URL is not trustworthy".to_owned()));
365 }
366 if let Some(body) = request.body.as_ref() {
368 if body.len().is_none() {
369 return Err(Error::Type("Body is null".to_owned()));
370 }
371 }
372 let deferred_record = queue_deferred_fetch(request, activate_after, global_scope);
379 signal.add(&AbortAlgorithm::FetchLater(deferred_record.clone()));
381 Ok(FetchLaterResult::new(window, deferred_record, can_gc))
383}
384
385#[derive(Clone, Copy, MallocSizeOf, PartialEq)]
387enum DeferredFetchRecordInvokeState {
388 Pending,
389 Sent,
390 Aborted,
391}
392
393#[derive(MallocSizeOf)]
395pub(crate) struct DeferredFetchRecord {
396 request: NetTraitsRequest,
398 invoke_state: Cell<DeferredFetchRecordInvokeState>,
400 global: Trusted<GlobalScope>,
401 activated: Cell<bool>,
402}
403
404impl DeferredFetchRecord {
405 fn activate(&self) {
407 self.activated.set(true);
409 }
410 pub(crate) fn abort(&self) {
412 self.invoke_state
414 .set(DeferredFetchRecordInvokeState::Aborted);
415 }
416 pub(crate) fn activated_getter_steps(&self) -> bool {
418 self.activated.get()
420 }
421 fn process(&self) {
423 if self.invoke_state.get() != DeferredFetchRecordInvokeState::Pending {
425 return;
426 }
427 self.invoke_state.set(DeferredFetchRecordInvokeState::Sent);
429 let url = self.request.url().clone();
431 let fetch_later_listener = FetchLaterListener {
432 url,
433 global: self.global.clone(),
434 };
435 let global = self.global.root();
436 let request_init = request_init_from_request(self.request.clone(), &global);
437 global.fetch(
438 request_init,
439 fetch_later_listener,
440 global.task_manager().networking_task_source().to_sendable(),
441 );
442 }
444}
445
446#[derive(JSTraceable, MallocSizeOf)]
447pub(crate) struct FetchContext {
448 #[ignore_malloc_size_of = "unclear ownership semantics"]
449 fetch_promise: Option<TrustedPromise>,
450 response_object: Trusted<Response>,
451 request: Trusted<Request>,
452 global: Trusted<GlobalScope>,
453 locally_aborted: bool,
454 canceller: FetchCanceller,
455}
456
457impl FetchContext {
458 pub(crate) fn abort_fetch(
460 &mut self,
461 abort_reason: HandleValue,
462 cx: SafeJSContext,
463 can_gc: CanGc,
464 ) {
465 self.locally_aborted = true;
467 self.canceller.cancel();
473
474 let promise = self
477 .fetch_promise
478 .take()
479 .expect("fetch promise is missing")
480 .root();
481 abort_fetch_call(
482 promise,
483 &self.request.root(),
484 Some(&self.response_object.root()),
485 abort_reason,
486 &self.global.root(),
487 cx,
488 can_gc,
489 );
490 }
491}
492
493impl FetchResponseListener for FetchContext {
495 fn process_request_body(&mut self, _: RequestId) {
496 }
498
499 fn process_request_eof(&mut self, _: RequestId) {
500 }
502
503 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
504 fn process_response(
505 &mut self,
506 _: RequestId,
507 fetch_metadata: Result<FetchMetadata, NetworkError>,
508 ) {
509 if self.locally_aborted {
511 return;
512 }
513 let promise = self
514 .fetch_promise
515 .take()
516 .expect("fetch promise is missing")
517 .root();
518
519 let _ac = enter_realm(&*promise);
520 match fetch_metadata {
521 Err(_) => {
524 promise.reject_error(
525 Error::Type("Network error occurred".to_string()),
526 CanGc::note(),
527 );
528 self.fetch_promise = Some(TrustedPromise::new(promise));
529 let response = self.response_object.root();
530 response.set_type(DOMResponseType::Error, CanGc::note());
531 response.error_stream(
532 Error::Type("Network error occurred".to_string()),
533 CanGc::note(),
534 );
535 return;
536 },
537 Ok(metadata) => match metadata {
540 FetchMetadata::Unfiltered(m) => {
541 fill_headers_with_metadata(self.response_object.root(), m, CanGc::note());
542 self.response_object
543 .root()
544 .set_type(DOMResponseType::Default, CanGc::note());
545 },
546 FetchMetadata::Filtered { filtered, .. } => match filtered {
547 FilteredMetadata::Basic(m) => {
548 fill_headers_with_metadata(self.response_object.root(), m, CanGc::note());
549 self.response_object
550 .root()
551 .set_type(DOMResponseType::Basic, CanGc::note());
552 },
553 FilteredMetadata::Cors(m) => {
554 fill_headers_with_metadata(self.response_object.root(), m, CanGc::note());
555 self.response_object
556 .root()
557 .set_type(DOMResponseType::Cors, CanGc::note());
558 },
559 FilteredMetadata::Opaque => {
560 self.response_object
561 .root()
562 .set_type(DOMResponseType::Opaque, CanGc::note());
563 },
564 FilteredMetadata::OpaqueRedirect(url) => {
565 let r = self.response_object.root();
566 r.set_type(DOMResponseType::Opaqueredirect, CanGc::note());
567 r.set_final_url(url);
568 },
569 },
570 },
571 }
572
573 promise.resolve_native(&self.response_object.root(), CanGc::note());
575 self.fetch_promise = Some(TrustedPromise::new(promise));
576 }
577
578 fn process_response_chunk(&mut self, _: RequestId, chunk: Vec<u8>) {
579 let response = self.response_object.root();
580 response.stream_chunk(chunk, CanGc::note());
581 }
582
583 fn process_response_eof(
584 self,
585 _: RequestId,
586 response: Result<ResourceFetchTiming, NetworkError>,
587 ) {
588 let response_object = self.response_object.root();
589 let _ac = enter_realm(&*response_object);
590 response_object.finish(CanGc::note());
591 if let Ok(response) = response {
596 if response.timing_type == ResourceTimingType::Resource {
597 network_listener::submit_timing(&self, &response, CanGc::note());
598 }
599 }
600 }
601
602 fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec<Violation>) {
603 let global = &self.resource_timing_global();
604 global.report_csp_violations(violations, None, None);
605 }
606}
607
608impl ResourceTimingListener for FetchContext {
609 fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
610 (
611 InitiatorType::Fetch,
612 self.resource_timing_global().get_url().clone(),
613 )
614 }
615
616 fn resource_timing_global(&self) -> DomRoot<GlobalScope> {
617 self.response_object.root().global()
618 }
619}
620
621struct FetchLaterListener {
622 url: ServoUrl,
624 global: Trusted<GlobalScope>,
626}
627
628impl FetchResponseListener for FetchLaterListener {
629 fn process_request_body(&mut self, _: RequestId) {}
630
631 fn process_request_eof(&mut self, _: RequestId) {}
632
633 fn process_response(
634 &mut self,
635 _: RequestId,
636 fetch_metadata: Result<FetchMetadata, NetworkError>,
637 ) {
638 _ = fetch_metadata;
639 }
640
641 fn process_response_chunk(&mut self, _: RequestId, chunk: Vec<u8>) {
642 _ = chunk;
643 }
644
645 fn process_response_eof(
646 self,
647 _: RequestId,
648 response: Result<ResourceFetchTiming, NetworkError>,
649 ) {
650 if let Ok(response) = response {
651 network_listener::submit_timing(&self, &response, CanGc::note());
652 }
653 }
654
655 fn process_csp_violations(&mut self, _request_id: RequestId, violations: Vec<Violation>) {
656 let global = self.resource_timing_global();
657 global.report_csp_violations(violations, None, None);
658 }
659}
660
661impl ResourceTimingListener for FetchLaterListener {
662 fn resource_timing_information(&self) -> (InitiatorType, ServoUrl) {
663 (InitiatorType::Fetch, self.url.clone())
664 }
665
666 fn resource_timing_global(&self) -> DomRoot<GlobalScope> {
667 self.global.root()
668 }
669}
670
671fn fill_headers_with_metadata(r: DomRoot<Response>, m: Metadata, can_gc: CanGc) {
672 r.set_headers(m.headers, can_gc);
673 r.set_status(&m.status);
674 r.set_final_url(m.final_url);
675 r.set_redirected(m.redirected);
676}
677
678pub(crate) trait CspViolationsProcessor {
679 fn process_csp_violations(&self, violations: Vec<Violation>);
680}
681
682pub(crate) fn load_whole_resource(
684 request: RequestBuilder,
685 core_resource_thread: &CoreResourceThread,
686 global: &GlobalScope,
687 csp_violations_processor: &dyn CspViolationsProcessor,
688 can_gc: CanGc,
689) -> Result<(Metadata, Vec<u8>), NetworkError> {
690 let request = request.https_state(global.get_https_state());
691 let (action_sender, action_receiver) = ipc::channel().unwrap();
692 let url = request.url.clone();
693 core_resource_thread
694 .send(CoreResourceMsg::Fetch(
695 request,
696 FetchChannels::ResponseMsg(action_sender),
697 ))
698 .unwrap();
699
700 let mut buf = vec![];
701 let mut metadata = None;
702 loop {
703 match action_receiver.recv().unwrap() {
704 FetchResponseMsg::ProcessRequestBody(..) | FetchResponseMsg::ProcessRequestEOF(..) => {
705 },
706 FetchResponseMsg::ProcessResponse(_, Ok(m)) => {
707 metadata = Some(match m {
708 FetchMetadata::Unfiltered(m) => m,
709 FetchMetadata::Filtered { unsafe_, .. } => unsafe_,
710 })
711 },
712 FetchResponseMsg::ProcessResponseChunk(_, data) => buf.extend_from_slice(&data),
713 FetchResponseMsg::ProcessResponseEOF(_, Ok(_)) => {
714 let metadata = metadata.unwrap();
715 if let Some(timing) = &metadata.timing {
716 submit_timing_data(global, url, InitiatorType::Other, timing, can_gc);
717 }
718 return Ok((metadata, buf));
719 },
720 FetchResponseMsg::ProcessResponse(_, Err(e)) |
721 FetchResponseMsg::ProcessResponseEOF(_, Err(e)) => return Err(e),
722 FetchResponseMsg::ProcessCspViolations(_, violations) => {
723 csp_violations_processor.process_csp_violations(violations);
724 },
725 }
726 }
727}
728
729#[allow(clippy::too_many_arguments)]
731pub(crate) fn create_a_potential_cors_request(
732 webview_id: Option<WebViewId>,
733 url: ServoUrl,
734 destination: Destination,
735 cors_setting: Option<CorsSettings>,
736 same_origin_fallback: Option<bool>,
737 referrer: Referrer,
738 insecure_requests_policy: InsecureRequestsPolicy,
739 has_trustworthy_ancestor_origin: bool,
740 policy_container: PolicyContainer,
741) -> RequestBuilder {
742 RequestBuilder::new(webview_id, url, referrer)
743 .mode(match cors_setting {
746 Some(_) => RequestMode::CorsMode,
747 None if same_origin_fallback == Some(true) => RequestMode::SameOrigin,
748 None => RequestMode::NoCors,
749 })
750 .credentials_mode(match cors_setting {
753 Some(CorsSettings::Anonymous) => CredentialsMode::CredentialsSameOrigin,
754 _ => CredentialsMode::Include,
755 })
756 .destination(destination)
758 .use_url_credentials(true)
759 .insecure_requests_policy(insecure_requests_policy)
760 .has_trustworthy_ancestor_origin(has_trustworthy_ancestor_origin)
761 .policy_container(policy_container)
762}