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