script/dom/performance/
performanceresourcetiming.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 base::cross_process_instant::CrossProcessInstant;
6use dom_struct::dom_struct;
7use net_traits::ResourceFetchTiming;
8use servo_url::ServoUrl;
9use time::Duration;
10
11use super::performanceentry::{EntryType, PerformanceEntry};
12use crate::dom::bindings::codegen::Bindings::PerformanceBinding::DOMHighResTimeStamp;
13use crate::dom::bindings::codegen::Bindings::PerformanceResourceTimingBinding::PerformanceResourceTimingMethods;
14use crate::dom::bindings::reflector::{DomGlobal, reflect_dom_object};
15use crate::dom::bindings::root::DomRoot;
16use crate::dom::bindings::str::DOMString;
17use crate::dom::globalscope::GlobalScope;
18use crate::script_runtime::CanGc;
19// TODO UA may choose to limit how many resources are included as PerformanceResourceTiming objects
20// recommended minimum is 150, can be changed by setResourceTimingBufferSize in performance
21// https://w3c.github.io/resource-timing/#sec-extensions-performance-interface
22
23// TODO Cross origin resources MUST BE INCLUDED as PerformanceResourceTiming objects
24// https://w3c.github.io/resource-timing/#sec-cross-origin-resources
25
26// TODO CSS
27#[derive(Debug, JSTraceable, MallocSizeOf, PartialEq)]
28pub(crate) enum InitiatorType {
29    Beacon,
30    Css,
31    LocalName(String),
32    Navigation,
33    XMLHttpRequest,
34    Fetch,
35    Other,
36}
37
38#[dom_struct]
39pub(crate) struct PerformanceResourceTiming {
40    entry: PerformanceEntry,
41    initiator_type: InitiatorType,
42    next_hop: Option<DOMString>,
43    #[no_trace]
44    worker_start: Option<CrossProcessInstant>,
45    #[no_trace]
46    redirect_start: Option<CrossProcessInstant>,
47    #[no_trace]
48    redirect_end: Option<CrossProcessInstant>,
49    #[no_trace]
50    fetch_start: Option<CrossProcessInstant>,
51    #[no_trace]
52    domain_lookup_start: Option<CrossProcessInstant>,
53    #[no_trace]
54    domain_lookup_end: Option<CrossProcessInstant>,
55    #[no_trace]
56    connect_start: Option<CrossProcessInstant>,
57    #[no_trace]
58    connect_end: Option<CrossProcessInstant>,
59    #[no_trace]
60    secure_connection_start: Option<CrossProcessInstant>,
61    #[no_trace]
62    request_start: Option<CrossProcessInstant>,
63    #[no_trace]
64    response_start: Option<CrossProcessInstant>,
65    #[no_trace]
66    response_end: Option<CrossProcessInstant>,
67    transfer_size: u64,     // size in octets
68    encoded_body_size: u64, // size in octets
69    decoded_body_size: u64, // size in octets
70}
71
72// TODO(#21269): next_hop
73// TODO(#21264): worker_start
74// TODO(#21258): fetch_start
75// TODO(#21259): domain_lookup_start
76// TODO(#21260): domain_lookup_end
77// TODO(#21261): connect_start
78// TODO(#21262): connect_end
79impl PerformanceResourceTiming {
80    pub(crate) fn new_inherited(
81        url: ServoUrl,
82        initiator_type: InitiatorType,
83        next_hop: Option<DOMString>,
84        fetch_start: Option<CrossProcessInstant>,
85    ) -> PerformanceResourceTiming {
86        let entry_type = if initiator_type == InitiatorType::Navigation {
87            EntryType::Navigation
88        } else {
89            EntryType::Resource
90        };
91        PerformanceResourceTiming {
92            entry: PerformanceEntry::new_inherited(
93                DOMString::from(url.into_string()),
94                entry_type,
95                None,
96                Duration::ZERO,
97            ),
98            initiator_type,
99            next_hop,
100            worker_start: None,
101            redirect_start: None,
102            redirect_end: None,
103            fetch_start,
104            domain_lookup_end: None,
105            domain_lookup_start: None,
106            connect_start: None,
107            connect_end: None,
108            secure_connection_start: None,
109            request_start: None,
110            response_start: None,
111            response_end: None,
112            transfer_size: 0,
113            encoded_body_size: 0,
114            decoded_body_size: 0,
115        }
116    }
117
118    // TODO fetch start should be in RFT
119    #[cfg_attr(crown, expect(crown::unrooted_must_root))]
120    fn from_resource_timing(
121        url: ServoUrl,
122        initiator_type: InitiatorType,
123        next_hop: Option<DOMString>,
124        resource_timing: &ResourceFetchTiming,
125    ) -> PerformanceResourceTiming {
126        let duration = match (resource_timing.start_time, resource_timing.response_end) {
127            (Some(start_time), Some(end_time)) => end_time - start_time,
128            _ => Duration::ZERO,
129        };
130        PerformanceResourceTiming {
131            entry: PerformanceEntry::new_inherited(
132                DOMString::from(url.into_string()),
133                EntryType::Resource,
134                resource_timing.start_time,
135                duration,
136            ),
137            initiator_type,
138            next_hop,
139            worker_start: None,
140            redirect_start: resource_timing.redirect_start,
141            redirect_end: resource_timing.redirect_end,
142            fetch_start: resource_timing.fetch_start,
143            domain_lookup_start: resource_timing.domain_lookup_start,
144            // TODO (#21260)
145            domain_lookup_end: None,
146            connect_start: resource_timing.connect_start,
147            connect_end: resource_timing.connect_end,
148            secure_connection_start: resource_timing.secure_connection_start,
149            request_start: resource_timing.request_start,
150            response_start: resource_timing.response_start,
151            response_end: resource_timing.response_end,
152            transfer_size: 0,
153            encoded_body_size: 0,
154            decoded_body_size: 0,
155        }
156    }
157
158    pub(crate) fn new(
159        global: &GlobalScope,
160        url: ServoUrl,
161        initiator_type: InitiatorType,
162        next_hop: Option<DOMString>,
163        resource_timing: &ResourceFetchTiming,
164        can_gc: CanGc,
165    ) -> DomRoot<PerformanceResourceTiming> {
166        reflect_dom_object(
167            Box::new(PerformanceResourceTiming::from_resource_timing(
168                url,
169                initiator_type,
170                next_hop,
171                resource_timing,
172            )),
173            global,
174            can_gc,
175        )
176    }
177
178    /// Convert an optional [`CrossProcessInstant`] to a [`DOMHighResTimeStamp`]. If none
179    /// return a timestamp for [`Self::fetch_start`] instead, so that timestamps are
180    /// always after that time.
181    pub(crate) fn to_dom_high_res_time_stamp(
182        &self,
183        instant: Option<CrossProcessInstant>,
184    ) -> DOMHighResTimeStamp {
185        self.global()
186            .performance()
187            .maybe_to_dom_high_res_time_stamp(instant)
188    }
189}
190
191// https://w3c.github.io/resource-timing/
192impl PerformanceResourceTimingMethods<crate::DomTypeHolder> for PerformanceResourceTiming {
193    /// <https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-initiatortype>
194    fn InitiatorType(&self) -> DOMString {
195        match self.initiator_type {
196            InitiatorType::Beacon => DOMString::from("beacon"),
197            InitiatorType::Css => DOMString::from("css"),
198            InitiatorType::LocalName(ref n) => DOMString::from(n.clone()),
199            InitiatorType::Navigation => DOMString::from("navigation"),
200            InitiatorType::XMLHttpRequest => DOMString::from("xmlhttprequest"),
201            InitiatorType::Fetch => DOMString::from("fetch"),
202            InitiatorType::Other => DOMString::from("other"),
203        }
204    }
205
206    // https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-nexthopprotocol
207    // returns the ALPN protocol ID of the network protocol used to fetch the resource
208    // when a proxy is configured
209    fn NextHopProtocol(&self) -> DOMString {
210        match self.next_hop {
211            Some(ref protocol) => protocol.clone(),
212            None => DOMString::from(""),
213        }
214    }
215
216    /// <https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-domainlookupstart>
217    fn DomainLookupStart(&self) -> DOMHighResTimeStamp {
218        self.to_dom_high_res_time_stamp(self.domain_lookup_start)
219    }
220
221    /// <https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-domainlookupend>
222    fn DomainLookupEnd(&self) -> DOMHighResTimeStamp {
223        self.to_dom_high_res_time_stamp(self.domain_lookup_end)
224    }
225
226    /// <https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-secureconnectionstart>
227    fn SecureConnectionStart(&self) -> DOMHighResTimeStamp {
228        self.to_dom_high_res_time_stamp(self.secure_connection_start)
229    }
230
231    /// <https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-transfersize>
232    fn TransferSize(&self) -> u64 {
233        self.transfer_size
234    }
235
236    /// <https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-encodedbodysize>
237    fn EncodedBodySize(&self) -> u64 {
238        self.encoded_body_size
239    }
240
241    /// <https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-decodedbodysize>
242    fn DecodedBodySize(&self) -> u64 {
243        self.decoded_body_size
244    }
245
246    /// <https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-requeststart>
247    fn RequestStart(&self) -> DOMHighResTimeStamp {
248        self.to_dom_high_res_time_stamp(self.request_start)
249    }
250
251    /// <https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-redirectstart>
252    fn RedirectStart(&self) -> DOMHighResTimeStamp {
253        self.to_dom_high_res_time_stamp(self.redirect_start)
254    }
255
256    /// <https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-redirectend>
257    fn RedirectEnd(&self) -> DOMHighResTimeStamp {
258        self.to_dom_high_res_time_stamp(self.redirect_end)
259    }
260
261    /// <https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-responsestart>
262    fn ResponseStart(&self) -> DOMHighResTimeStamp {
263        self.to_dom_high_res_time_stamp(self.response_start)
264    }
265
266    /// <https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-fetchstart>
267    fn FetchStart(&self) -> DOMHighResTimeStamp {
268        self.to_dom_high_res_time_stamp(self.fetch_start)
269    }
270
271    /// <https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-connectstart>
272    fn ConnectStart(&self) -> DOMHighResTimeStamp {
273        self.to_dom_high_res_time_stamp(self.connect_start)
274    }
275
276    /// <https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-connectend>
277    fn ConnectEnd(&self) -> DOMHighResTimeStamp {
278        self.to_dom_high_res_time_stamp(self.connect_end)
279    }
280
281    /// <https://w3c.github.io/resource-timing/#dom-performanceresourcetiming-responseend>
282    fn ResponseEnd(&self) -> DOMHighResTimeStamp {
283        self.to_dom_high_res_time_stamp(self.response_end)
284    }
285}