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