script/
network_listener.rs1use std::sync::{Arc, Mutex};
6
7use content_security_policy::Violation;
8use net_traits::request::RequestId;
9use net_traits::{
10 BoxedFetchCallback, FetchMetadata, FetchResponseMsg, NetworkError, ResourceFetchTiming,
11 ResourceTimingType,
12};
13use servo_url::ServoUrl;
14
15use crate::dom::bindings::inheritance::Castable;
16use crate::dom::bindings::root::DomRoot;
17use crate::dom::globalscope::GlobalScope;
18use crate::dom::performance::performanceentry::PerformanceEntry;
19use crate::dom::performance::performanceresourcetiming::{
20 InitiatorType, PerformanceResourceTiming,
21};
22use crate::script_runtime::CanGc;
23use crate::task_source::SendableTaskSource;
24
25pub(crate) trait ResourceTimingListener {
26 fn resource_timing_information(&self) -> (InitiatorType, ServoUrl);
27 fn resource_timing_global(&self) -> DomRoot<GlobalScope>;
28}
29
30pub(crate) fn submit_timing<T: ResourceTimingListener + FetchResponseListener>(
31 listener: &T,
32 resource_timing: &ResourceFetchTiming,
33 can_gc: CanGc,
34) {
35 if resource_timing.timing_type != ResourceTimingType::Resource {
42 warn!(
43 "Submitting non-resource ({:?}) timing as resource",
44 resource_timing.timing_type
45 );
46 return;
47 }
48
49 let (initiator_type, url) = listener.resource_timing_information();
50 if initiator_type == InitiatorType::Other {
51 warn!("Ignoring InitiatorType::Other resource {:?}", url);
52 return;
53 }
54
55 submit_timing_data(
56 &listener.resource_timing_global(),
57 url,
58 initiator_type,
59 resource_timing,
60 can_gc,
61 );
62}
63
64pub(crate) fn submit_timing_data(
65 global: &GlobalScope,
66 url: ServoUrl,
67 initiator_type: InitiatorType,
68 resource_timing: &ResourceFetchTiming,
69 can_gc: CanGc,
70) {
71 let performance_entry =
72 PerformanceResourceTiming::new(global, url, initiator_type, None, resource_timing, can_gc);
73 global
74 .performance()
75 .queue_entry(performance_entry.upcast::<PerformanceEntry>(), can_gc);
76}
77
78pub(crate) trait FetchResponseListener: Send + 'static {
79 fn should_invoke(&self) -> bool {
83 true
84 }
85
86 fn process_request_body(&mut self, request_id: RequestId);
87 fn process_request_eof(&mut self, request_id: RequestId);
88 fn process_response(
89 &mut self,
90 request_id: RequestId,
91 metadata: Result<FetchMetadata, NetworkError>,
92 );
93 fn process_response_chunk(&mut self, request_id: RequestId, chunk: Vec<u8>);
94 fn process_response_eof(
95 self,
96 request_id: RequestId,
97 response: Result<ResourceFetchTiming, NetworkError>,
98 );
99 fn process_csp_violations(&mut self, request_id: RequestId, violations: Vec<Violation>);
100}
101
102pub(crate) struct NetworkListener<Listener: FetchResponseListener> {
105 pub(crate) context: Arc<Mutex<Option<Listener>>>,
106 pub(crate) task_source: SendableTaskSource,
107}
108
109impl<Listener: FetchResponseListener> NetworkListener<Listener> {
110 pub(crate) fn new(context: Listener, task_source: SendableTaskSource) -> Self {
111 Self {
112 context: Arc::new(Mutex::new(Some(context))),
113 task_source,
114 }
115 }
116
117 pub(crate) fn notify(&mut self, message: FetchResponseMsg) {
118 let context = self.context.clone();
119 self.task_source
120 .queue(task!(network_listener_response: move || {
121 let mut context = context.lock().unwrap();
122 let Some(fetch_listener) = &mut *context else {
123 return;
124 };
125
126 if !fetch_listener.should_invoke() {
127 return;
128 }
129
130 match message {
131 FetchResponseMsg::ProcessRequestBody(request_id) => {
132 fetch_listener.process_request_body(request_id)
133 },
134 FetchResponseMsg::ProcessRequestEOF(request_id) => {
135 fetch_listener.process_request_eof(request_id)
136 },
137 FetchResponseMsg::ProcessResponse(request_id, meta) => {
138 fetch_listener.process_response(request_id, meta)
139 },
140 FetchResponseMsg::ProcessResponseChunk(request_id, data) => {
141 fetch_listener.process_response_chunk(request_id, data.0)
142 },
143 FetchResponseMsg::ProcessResponseEOF(request_id, resource_timing_result) => {
144 if let Some(fetch_listener) = context.take() {
145 fetch_listener.process_response_eof(request_id, resource_timing_result);
146 };
147 },
148 FetchResponseMsg::ProcessCspViolations(request_id, violations) => {
149 fetch_listener.process_csp_violations(request_id, violations)
150 },
151 }
152 }));
153 }
154
155 pub(crate) fn into_callback(mut self) -> BoxedFetchCallback {
156 Box::new(move |response_msg| self.notify(response_msg))
157 }
158}