script/dom/performance/
performanceobserver.rs1use std::cell::Cell;
6use std::rc::Rc;
7
8use dom_struct::dom_struct;
9use js::rust::{HandleObject, MutableHandleValue};
10
11use super::performance::PerformanceEntryList;
12use super::performanceentry::{EntryType, PerformanceEntry};
13use super::performanceobserverentrylist::PerformanceObserverEntryList;
14use crate::dom::bindings::callback::ExceptionHandling;
15use crate::dom::bindings::cell::DomRefCell;
16use crate::dom::bindings::codegen::Bindings::PerformanceBinding::PerformanceEntryList as DOMPerformanceEntryList;
17use crate::dom::bindings::codegen::Bindings::PerformanceObserverBinding::{
18 PerformanceObserverCallback, PerformanceObserverInit, PerformanceObserverMethods,
19};
20use crate::dom::bindings::error::{Error, Fallible};
21use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object_with_proto};
22use crate::dom::bindings::root::DomRoot;
23use crate::dom::console::Console;
24use crate::dom::globalscope::GlobalScope;
25use crate::script_runtime::{CanGc, JSContext};
26
27#[derive(Clone, Copy, JSTraceable, MallocSizeOf, PartialEq)]
28enum ObserverType {
29 Undefined,
30 Single,
31 Multiple,
32}
33
34#[dom_struct]
35pub(crate) struct PerformanceObserver {
36 reflector_: Reflector,
37 #[conditional_malloc_size_of]
38 callback: Rc<PerformanceObserverCallback>,
39 entries: DomRefCell<DOMPerformanceEntryList>,
40 observer_type: Cell<ObserverType>,
41}
42
43impl PerformanceObserver {
44 fn new_inherited(
45 callback: Rc<PerformanceObserverCallback>,
46 entries: DomRefCell<DOMPerformanceEntryList>,
47 ) -> PerformanceObserver {
48 PerformanceObserver {
49 reflector_: Reflector::new(),
50 callback,
51 entries,
52 observer_type: Cell::new(ObserverType::Undefined),
53 }
54 }
55
56 pub(crate) fn new(
57 global: &GlobalScope,
58 callback: Rc<PerformanceObserverCallback>,
59 entries: DOMPerformanceEntryList,
60 can_gc: CanGc,
61 ) -> DomRoot<PerformanceObserver> {
62 Self::new_with_proto(global, None, callback, entries, can_gc)
63 }
64
65 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
66 fn new_with_proto(
67 global: &GlobalScope,
68 proto: Option<HandleObject>,
69 callback: Rc<PerformanceObserverCallback>,
70 entries: DOMPerformanceEntryList,
71 can_gc: CanGc,
72 ) -> DomRoot<PerformanceObserver> {
73 let observer = PerformanceObserver::new_inherited(callback, DomRefCell::new(entries));
74 reflect_dom_object_with_proto(Box::new(observer), global, proto, can_gc)
75 }
76
77 pub(crate) fn queue_entry(&self, entry: &PerformanceEntry) {
79 self.entries.borrow_mut().push(DomRoot::from_ref(entry));
80 }
81
82 pub(crate) fn notify(&self, can_gc: CanGc) {
85 if self.entries.borrow().is_empty() {
86 return;
87 }
88 let entry_list = PerformanceEntryList::new(self.entries.borrow_mut().drain(..).collect());
89 let observer_entry_list =
90 PerformanceObserverEntryList::new(&self.global(), entry_list, can_gc);
91 let _ = self.callback.Call_(
93 self,
94 &observer_entry_list,
95 self,
96 ExceptionHandling::Report,
97 can_gc,
98 );
99 }
100
101 pub(crate) fn callback(&self) -> Rc<PerformanceObserverCallback> {
102 self.callback.clone()
103 }
104
105 pub(crate) fn entries(&self) -> DOMPerformanceEntryList {
106 self.entries.borrow().clone()
107 }
108
109 pub(crate) fn set_entries(&self, entries: DOMPerformanceEntryList) {
110 *self.entries.borrow_mut() = entries;
111 }
112}
113
114impl PerformanceObserverMethods<crate::DomTypeHolder> for PerformanceObserver {
115 fn Constructor(
117 global: &GlobalScope,
118 proto: Option<HandleObject>,
119 can_gc: CanGc,
120 callback: Rc<PerformanceObserverCallback>,
121 ) -> Fallible<DomRoot<PerformanceObserver>> {
122 Ok(PerformanceObserver::new_with_proto(
123 global,
124 proto,
125 callback,
126 Vec::new(),
127 can_gc,
128 ))
129 }
130
131 fn SupportedEntryTypes(
133 cx: JSContext,
134 global: &GlobalScope,
135 can_gc: CanGc,
136 retval: MutableHandleValue,
137 ) {
138 global.supported_performance_entry_types(cx, retval, can_gc)
141 }
142
143 fn Observe(&self, options: &PerformanceObserverInit) -> Fallible<()> {
145 if options.entryTypes.is_none() && options.type_.is_none() {
151 return Err(Error::Syntax(None));
152 }
153
154 if options.entryTypes.is_some() && (options.buffered.is_some() || options.type_.is_some()) {
156 return Err(Error::Syntax(None));
157 }
158
159 match self.observer_type.get() {
164 ObserverType::Undefined => {
165 if options.entryTypes.is_some() {
166 self.observer_type.set(ObserverType::Multiple);
167 } else {
168 self.observer_type.set(ObserverType::Single);
169 }
170 },
171 ObserverType::Single => {
172 if options.entryTypes.is_some() {
173 return Err(Error::InvalidModification(None));
174 }
175 },
176 ObserverType::Multiple => {
177 if options.type_.is_some() {
178 return Err(Error::InvalidModification(None));
179 }
180 },
181 }
182
183 const NO_VALID_ENTRY_TYPE: &str = "No valid entry type provided to observe().";
185 if let Some(entry_types) = &options.entryTypes {
186 let entry_types = entry_types
188 .iter()
189 .filter_map(|e| EntryType::try_from(&*e.str()).ok())
190 .collect::<Vec<EntryType>>();
191
192 if entry_types.is_empty() {
194 Console::internal_warn(&self.global(), NO_VALID_ENTRY_TYPE.to_string());
195 return Ok(());
196 }
197
198 self.global()
202 .performance()
203 .add_multiple_type_observer(self, entry_types);
204 Ok(())
205 } else if let Some(entry_type) = &options.type_ {
206 let Ok(entry_type) = EntryType::try_from(&*entry_type.str()) else {
208 Console::internal_warn(&self.global(), NO_VALID_ENTRY_TYPE.to_string());
209 return Ok(());
210 };
211
212 self.global().performance().add_single_type_observer(
216 self,
217 entry_type,
218 options.buffered.unwrap_or(false),
219 );
220 Ok(())
221 } else {
222 unreachable!()
224 }
225 }
226
227 fn Disconnect(&self) {
229 self.global().performance().remove_observer(self);
230 self.entries.borrow_mut().clear();
231 }
232
233 fn TakeRecords(&self) -> Vec<DomRoot<PerformanceEntry>> {
235 let mut entries = self.entries.borrow_mut();
236 let taken = entries
237 .iter()
238 .map(|entry| DomRoot::from_ref(&**entry))
239 .collect();
240 entries.clear();
241 taken
242 }
243}