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