1use malloc_size_of_derive::MallocSizeOf;
6use parking_lot::{Mutex, MutexGuard};
7use serde::{Deserialize, Serialize};
8use servo_arc::Arc;
9use servo_base::cross_process_instant::CrossProcessInstant;
10
11#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
12pub struct ResourceFetchTiming {
13 pub domain_lookup_start: Option<CrossProcessInstant>,
14 pub timing_check_passed: bool,
15 pub timing_type: ResourceTimingType,
16 pub redirect_count: u16,
18 pub request_start: Option<CrossProcessInstant>,
20 pub secure_connection_start: Option<CrossProcessInstant>,
21 pub response_start: Option<CrossProcessInstant>,
22 pub fetch_start: Option<CrossProcessInstant>,
23 pub response_end: Option<CrossProcessInstant>,
25 pub redirect_start: Option<CrossProcessInstant>,
27 pub redirect_end: Option<CrossProcessInstant>,
29 pub connect_start: Option<CrossProcessInstant>,
30 pub connect_end: Option<CrossProcessInstant>,
31 pub start_time: Option<CrossProcessInstant>,
32 pub preloaded: bool,
33}
34
35#[derive(Clone)]
36pub enum RedirectStartValue {
37 Zero,
38 FetchStart,
39}
40
41#[derive(Clone)]
42pub enum RedirectEndValue {
43 Zero,
44 ResponseEnd,
45}
46
47#[derive(Clone)]
50pub enum ResourceTimeValue {
51 Zero,
52 Now,
53 FetchStart,
54 RedirectStart,
55}
56
57#[derive(Clone)]
58pub enum ResourceAttribute {
59 RedirectCount(u16),
60 DomainLookupStart,
61 RequestStart,
62 ResponseStart,
63 RedirectStart(RedirectStartValue),
64 RedirectEnd(RedirectEndValue),
65 FetchStart,
66 ConnectStart(CrossProcessInstant),
67 ConnectEnd(CrossProcessInstant),
68 SecureConnectionStart,
69 ResponseEnd,
70 StartTime(ResourceTimeValue),
71}
72
73#[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)]
74pub enum ResourceTimingType {
75 Resource,
76 Navigation,
77 Error,
78 None,
79}
80
81impl ResourceFetchTiming {
82 pub fn new(timing_type: ResourceTimingType) -> ResourceFetchTiming {
83 ResourceFetchTiming {
84 timing_type,
85 timing_check_passed: true,
86 domain_lookup_start: None,
87 redirect_count: 0,
88 secure_connection_start: None,
89 request_start: None,
90 response_start: None,
91 fetch_start: None,
92 redirect_start: None,
93 redirect_end: None,
94 connect_start: None,
95 connect_end: None,
96 response_end: None,
97 start_time: None,
98 preloaded: false,
99 }
100 }
101
102 pub fn set_attribute(&mut self, attribute: ResourceAttribute) {
105 let should_attribute_always_be_updated = matches!(
106 attribute,
107 ResourceAttribute::FetchStart |
108 ResourceAttribute::ResponseEnd |
109 ResourceAttribute::StartTime(_)
110 );
111 if !self.timing_check_passed && !should_attribute_always_be_updated {
112 return;
113 }
114 let now = Some(CrossProcessInstant::now());
115 match attribute {
116 ResourceAttribute::DomainLookupStart => self.domain_lookup_start = now,
117 ResourceAttribute::RedirectCount(count) => self.redirect_count = count,
118 ResourceAttribute::RequestStart => self.request_start = now,
119 ResourceAttribute::ResponseStart => self.response_start = now,
120 ResourceAttribute::RedirectStart(val) => match val {
121 RedirectStartValue::Zero => self.redirect_start = None,
122 RedirectStartValue::FetchStart => {
123 if self.redirect_start.is_none() {
124 self.redirect_start = self.fetch_start
125 }
126 },
127 },
128 ResourceAttribute::RedirectEnd(val) => match val {
129 RedirectEndValue::Zero => self.redirect_end = None,
130 RedirectEndValue::ResponseEnd => self.redirect_end = self.response_end,
131 },
132 ResourceAttribute::FetchStart => self.fetch_start = now,
133 ResourceAttribute::ConnectStart(instant) => self.connect_start = Some(instant),
134 ResourceAttribute::ConnectEnd(instant) => self.connect_end = Some(instant),
135 ResourceAttribute::SecureConnectionStart => self.secure_connection_start = now,
136 ResourceAttribute::ResponseEnd => self.response_end = now,
137 ResourceAttribute::StartTime(val) => match val {
138 ResourceTimeValue::RedirectStart
139 if self.redirect_start.is_none() || !self.timing_check_passed => {},
140 _ => self.start_time = self.get_time_value(val),
141 },
142 }
143 }
144
145 fn get_time_value(&self, time: ResourceTimeValue) -> Option<CrossProcessInstant> {
146 match time {
147 ResourceTimeValue::Zero => None,
148 ResourceTimeValue::Now => Some(CrossProcessInstant::now()),
149 ResourceTimeValue::FetchStart => self.fetch_start,
150 ResourceTimeValue::RedirectStart => self.redirect_start,
151 }
152 }
153
154 pub fn mark_timing_check_failed(&mut self) {
155 self.timing_check_passed = false;
156 self.domain_lookup_start = None;
157 self.redirect_count = 0;
158 self.request_start = None;
159 self.response_start = None;
160 self.redirect_start = None;
161 self.connect_start = None;
162 self.connect_end = None;
163 }
164}
165
166#[derive(Clone, Debug, Serialize, Deserialize, MallocSizeOf)]
167pub struct ResourceFetchTimingContainer(
169 #[conditional_malloc_size_of] Arc<Mutex<ResourceFetchTiming>>,
170);
171
172impl ResourceFetchTimingContainer {
173 pub fn set_attribute(&self, attribute: ResourceAttribute) {
174 self.0.lock().set_attribute(attribute);
175 }
176
177 pub fn set_attributes(&self, attributes: &[ResourceAttribute]) {
179 let mut inner = self.0.lock();
180 for attribute in attributes {
181 inner.set_attribute(attribute.clone());
182 }
183 }
184
185 pub fn inner(&self) -> MutexGuard<'_, ResourceFetchTiming> {
186 self.0.lock()
187 }
188}
189
190impl From<ResourceFetchTiming> for ResourceFetchTimingContainer {
191 fn from(value: ResourceFetchTiming) -> Self {
192 ResourceFetchTimingContainer(Arc::new(Mutex::new(value)))
193 }
194}