script/dom/performance/
performanceentry.rs1use 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#[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#[dom_struct]
104pub(crate) struct PerformanceEntry {
105 reflector_: Reflector,
106
107 name: RefCell<NameOrUrl>,
111
112 entry_type: EntryType,
114
115 #[no_trace]
117 start_time: Option<CrossProcessInstant>,
118
119 #[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 pub(crate) fn entry_type(&self) -> EntryType {
152 self.entry_type
153 }
154
155 pub(crate) fn start_time(&self) -> Option<CrossProcessInstant> {
157 self.start_time
158 }
159}
160
161impl PerformanceEntryMethods<crate::DomTypeHolder> for PerformanceEntry {
162 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 fn EntryType(&self) -> DOMString {
179 DOMString::from(self.entry_type.as_str())
180 }
181
182 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 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}