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