Skip to main content

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