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(#21258): fetch_start
76// TODO(#21259): domain_lookup_start
77// TODO(#21260): domain_lookup_end
78// TODO(#21261): connect_start
79// TODO(#21262): connect_end
80impl PerformanceResourceTiming {
81    pub(crate) fn new_inherited(
82        url: ServoUrl,
83        initiator_type: InitiatorType,
84        next_hop: Option<DOMString>,
85        fetch_start: Option<CrossProcessInstant>,
86    ) -> PerformanceResourceTiming {
87        let entry_type = if initiator_type == InitiatorType::Navigation {
88            EntryType::Navigation
89        } else {
90            EntryType::Resource
91        };
92        PerformanceResourceTiming {
93            entry: PerformanceEntry::new_inherited(
94                DOMString::from(url.into_string()),
95                entry_type,
96                None,
97                Duration::ZERO,
98            ),
99            initiator_type,
100            next_hop,
101            worker_start: None,
102            redirect_start: None,
103            redirect_end: None,
104            fetch_start,
105            domain_lookup_end: None,
106            domain_lookup_start: None,
107            connect_start: None,
108            connect_end: None,
109            secure_connection_start: None,
110            request_start: None,
111            response_start: None,
112            response_end: None,
113            transfer_size: 0,
114            encoded_body_size: 0,
115            decoded_body_size: 0,
116        }
117    }
118
119    // TODO fetch start should be in RFT
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        cx: &mut JSContext,
160        global: &GlobalScope,
161        url: ServoUrl,
162        initiator_type: InitiatorType,
163        next_hop: Option<DOMString>,
164        resource_timing: &ResourceFetchTiming,
165    ) -> DomRoot<PerformanceResourceTiming> {
166        reflect_dom_object_with_cx(
167            Box::new(PerformanceResourceTiming::from_resource_timing(
168                url,
169                initiator_type,
170                next_hop,
171                resource_timing,
172            )),
173            global,
174            cx,
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}