Skip to main content

script/dom/performance/
performanceentry.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 std::cell::RefCell;
6
7use dom_struct::dom_struct;
8use js::context::JSContext;
9use js::rust::Trace;
10use script_bindings::domstring::TracedDOMString;
11use script_bindings::inheritance::Castable;
12use script_bindings::num::Finite;
13use script_bindings::reflector::Reflector;
14use servo_base::cross_process_instant::CrossProcessInstant;
15use servo_url::ServoUrl;
16use strum::VariantArray;
17use time::Duration;
18
19use super::performance::ToDOMHighResTimeStamp;
20use crate::dom::bindings::codegen::Bindings::PerformanceBinding::DOMHighResTimeStamp;
21use crate::dom::bindings::codegen::Bindings::PerformanceEntryBinding::PerformanceEntryMethods;
22use crate::dom::bindings::codegen::Bindings::PerformanceResourceTimingBinding::PerformanceResourceTimingMethods;
23use crate::dom::bindings::reflector::DomGlobal;
24use crate::dom::bindings::str::DOMString;
25use crate::dom::performance::performanceresourcetiming::PerformanceResourceTiming;
26
27/// All supported entry types, in alphabetical order.
28#[derive(Clone, Copy, JSTraceable, MallocSizeOf, PartialEq, VariantArray)]
29pub(crate) enum EntryType {
30    ContainerTiming,
31    LargestContentfulPaint,
32    Mark,
33    Measure,
34    Navigation,
35    Paint,
36    Resource,
37    VisibilityState,
38}
39
40impl EntryType {
41    pub(crate) fn as_str(&self) -> &'static str {
42        match self {
43            EntryType::ContainerTiming => "container",
44            EntryType::Measure => "measure",
45            EntryType::Mark => "mark",
46            EntryType::LargestContentfulPaint => "largest-contentful-paint",
47            EntryType::Paint => "paint",
48            EntryType::Navigation => "navigation",
49            EntryType::Resource => "resource",
50            EntryType::VisibilityState => "visibility-state",
51        }
52    }
53}
54
55impl<'a> TryFrom<&'a str> for EntryType {
56    type Error = ();
57
58    fn try_from(value: &'a str) -> Result<EntryType, ()> {
59        Ok(match value {
60            "container" => EntryType::ContainerTiming,
61            "measure" => EntryType::Measure,
62            "mark" => EntryType::Mark,
63            "largest-contentful-paint" => EntryType::LargestContentfulPaint,
64            "paint" => EntryType::Paint,
65            "navigation" => EntryType::Navigation,
66            "resource" => EntryType::Resource,
67            "visibility-state" => EntryType::VisibilityState,
68            _ => return Err(()),
69        })
70    }
71}
72
73#[derive(MallocSizeOf)]
74#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
75pub(crate) enum NameOrUrl {
76    Url(ServoUrl),
77    Name(TracedDOMString),
78}
79
80#[expect(unsafe_code)]
81unsafe impl Trace for NameOrUrl {
82    unsafe fn trace(&self, trc: *mut js::jsapi::JSTracer) {
83        match self {
84            NameOrUrl::Url(_) => {},
85            NameOrUrl::Name(traced_domstring) => unsafe { traced_domstring.trace(trc) },
86        }
87    }
88}
89
90impl From<ServoUrl> for NameOrUrl {
91    fn from(value: ServoUrl) -> Self {
92        NameOrUrl::Url(value)
93    }
94}
95
96impl From<DOMString> for NameOrUrl {
97    fn from(value: DOMString) -> Self {
98        NameOrUrl::Name(value.traced())
99    }
100}
101
102/// <https://www.w3.org/TR/performance-timeline/#dom-performanceentry>
103#[dom_struct]
104pub(crate) struct PerformanceEntry {
105    reflector_: Reflector,
106
107    /// <https://www.w3.org/TR/performance-timeline/#dom-performanceentry-name>
108    /// The name of this entry or the URL. This conversion is done lazily in order
109    /// to optimize the size of the [`PerformanceEntry`].
110    name: RefCell<NameOrUrl>,
111
112    /// <https://www.w3.org/TR/performance-timeline/#dom-performanceentry-entrytype>
113    entry_type: EntryType,
114
115    /// <https://www.w3.org/TR/performance-timeline/#dom-performanceentry-starttime>
116    #[no_trace]
117    start_time: Option<CrossProcessInstant>,
118
119    /// The duration of this [`PerformanceEntry`]. This is a [`time::Duration`],
120    /// because it can be negative and `std::time::Duration` cannot be.
121    ///
122    /// <https://www.w3.org/TR/performance-timeline/#dom-performanceentry-duration>
123    #[no_trace]
124    duration: Duration,
125}
126
127impl PerformanceEntry {
128    #[cfg_attr(
129        crown,
130        expect(
131            crown::unrooted_must_root,
132            reason = "Not rooting this value is fine, we do not have a `&mut JSContext`, hence, no GC can happen"
133        )
134    )]
135    pub(crate) fn new_inherited(
136        name_or_url: NameOrUrl,
137        entry_type: EntryType,
138        start_time: Option<CrossProcessInstant>,
139        duration: Duration,
140    ) -> PerformanceEntry {
141        PerformanceEntry {
142            reflector_: Reflector::new(),
143            name: name_or_url.into(),
144            entry_type,
145            start_time,
146            duration,
147        }
148    }
149
150    /// <https://www.w3.org/TR/performance-timeline/#dom-performanceentry-name>
151    pub(crate) fn entry_type(&self) -> EntryType {
152        self.entry_type
153    }
154
155    /// <https://www.w3.org/TR/performance-timeline/#dom-performanceentry-starttime>
156    pub(crate) fn start_time(&self) -> Option<CrossProcessInstant> {
157        self.start_time
158    }
159}
160
161impl PerformanceEntryMethods<crate::DomTypeHolder> for PerformanceEntry {
162    /// <https://w3c.github.io/performance-timeline/#dom-performanceentry-name>
163    fn Name(&self) -> DOMString {
164        {
165            if let NameOrUrl::Name(name) = &*self.name.borrow() {
166                return name.clone().root();
167            }
168            let mut name = self.name.borrow_mut();
169
170            if let NameOrUrl::Url(url) = &*name {
171                *name = NameOrUrl::Name(url.to_string().into());
172            }
173        }
174        self.Name()
175    }
176
177    /// <https://w3c.github.io/performance-timeline/#dom-performanceentry-entrytype>
178    fn EntryType(&self) -> DOMString {
179        DOMString::from(self.entry_type.as_str())
180    }
181
182    /// <https://w3c.github.io/performance-timeline/#dom-performanceentry-starttime>
183    fn StartTime(&self, cx: &mut JSContext) -> DOMHighResTimeStamp {
184        self.global()
185            .performance(cx)
186            .maybe_to_dom_high_res_time_stamp(self.start_time)
187    }
188
189    /// <https://w3c.github.io/performance-timeline/#dom-performanceentry-duration>
190    fn Duration(&self, cx: &mut JSContext) -> DOMHighResTimeStamp {
191        if let Some(resource_timing) = self.downcast::<PerformanceResourceTiming>() {
192            Finite::new(*resource_timing.ResponseEnd(cx) - *self.StartTime(cx))
193                .unwrap_or(Finite::default())
194        } else {
195            self.duration.to_dom_high_res_time_stamp()
196        }
197    }
198}