net_traits/
resource_fetch_timing.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use 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    /// Number of redirects until final resource (currently limited to 20)
17    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// TODO: refactor existing code to use this enum for setting time attributes
44// suggest using this with all time attributes in the future
45#[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    // TODO currently this is being set with precise time ns when it should be time since
99    // time origin (as described in Performance::now)
100    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)]
163/// A simple container of [`ResourceFetchTiming`] to allow easy sharing between threads and allow to set multiple attributes in sequence.
164pub 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    /// Set multiple attributes in sequence.
174    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}