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::bindings::str::DOMString;
24use crate::dom::console::Console;
25use crate::dom::globalscope::GlobalScope;
26use crate::script_runtime::{CanGc, JSContext};
27
28#[derive(Clone, Copy, JSTraceable, MallocSizeOf, PartialEq)]
29enum ObserverType {
30 Undefined,
31 Single,
32 Multiple,
33}
34
35#[dom_struct]
36pub(crate) struct PerformanceObserver {
37 reflector_: Reflector,
38 #[ignore_malloc_size_of = "can't measure Rc values"]
39 callback: Rc<PerformanceObserverCallback>,
40 entries: DomRefCell<DOMPerformanceEntryList>,
41 observer_type: Cell<ObserverType>,
42}
43
44impl PerformanceObserver {
45 fn new_inherited(
46 callback: Rc<PerformanceObserverCallback>,
47 entries: DomRefCell<DOMPerformanceEntryList>,
48 ) -> PerformanceObserver {
49 PerformanceObserver {
50 reflector_: Reflector::new(),
51 callback,
52 entries,
53 observer_type: Cell::new(ObserverType::Undefined),
54 }
55 }
56
57 pub(crate) fn new(
58 global: &GlobalScope,
59 callback: Rc<PerformanceObserverCallback>,
60 entries: DOMPerformanceEntryList,
61 can_gc: CanGc,
62 ) -> DomRoot<PerformanceObserver> {
63 Self::new_with_proto(global, None, callback, entries, can_gc)
64 }
65
66 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
67 fn new_with_proto(
68 global: &GlobalScope,
69 proto: Option<HandleObject>,
70 callback: Rc<PerformanceObserverCallback>,
71 entries: DOMPerformanceEntryList,
72 can_gc: CanGc,
73 ) -> DomRoot<PerformanceObserver> {
74 let observer = PerformanceObserver::new_inherited(callback, DomRefCell::new(entries));
75 reflect_dom_object_with_proto(Box::new(observer), global, proto, can_gc)
76 }
77
78 pub(crate) fn queue_entry(&self, entry: &PerformanceEntry) {
80 self.entries.borrow_mut().push(DomRoot::from_ref(entry));
81 }
82
83 pub(crate) fn notify(&self, can_gc: CanGc) {
86 if self.entries.borrow().is_empty() {
87 return;
88 }
89 let entry_list = PerformanceEntryList::new(self.entries.borrow_mut().drain(..).collect());
90 let observer_entry_list =
91 PerformanceObserverEntryList::new(&self.global(), entry_list, can_gc);
92 let _ = self.callback.Call_(
94 self,
95 &observer_entry_list,
96 self,
97 ExceptionHandling::Report,
98 can_gc,
99 );
100 }
101
102 pub(crate) fn callback(&self) -> Rc<PerformanceObserverCallback> {
103 self.callback.clone()
104 }
105
106 pub(crate) fn entries(&self) -> DOMPerformanceEntryList {
107 self.entries.borrow().clone()
108 }
109
110 pub(crate) fn set_entries(&self, entries: DOMPerformanceEntryList) {
111 *self.entries.borrow_mut() = entries;
112 }
113}
114
115impl PerformanceObserverMethods<crate::DomTypeHolder> for PerformanceObserver {
116 fn Constructor(
118 global: &GlobalScope,
119 proto: Option<HandleObject>,
120 can_gc: CanGc,
121 callback: Rc<PerformanceObserverCallback>,
122 ) -> Fallible<DomRoot<PerformanceObserver>> {
123 Ok(PerformanceObserver::new_with_proto(
124 global,
125 proto,
126 callback,
127 Vec::new(),
128 can_gc,
129 ))
130 }
131
132 fn SupportedEntryTypes(
134 cx: JSContext,
135 global: &GlobalScope,
136 can_gc: CanGc,
137 retval: MutableHandleValue,
138 ) {
139 global.supported_performance_entry_types(cx, retval, can_gc)
142 }
143
144 fn Observe(&self, options: &PerformanceObserverInit) -> Fallible<()> {
146 if options.entryTypes.is_none() && options.type_.is_none() {
152 return Err(Error::Syntax(None));
153 }
154
155 if options.entryTypes.is_some() && (options.buffered.is_some() || options.type_.is_some()) {
157 return Err(Error::Syntax(None));
158 }
159
160 match self.observer_type.get() {
165 ObserverType::Undefined => {
166 if options.entryTypes.is_some() {
167 self.observer_type.set(ObserverType::Multiple);
168 } else {
169 self.observer_type.set(ObserverType::Single);
170 }
171 },
172 ObserverType::Single => {
173 if options.entryTypes.is_some() {
174 return Err(Error::InvalidModification(None));
175 }
176 },
177 ObserverType::Multiple => {
178 if options.type_.is_some() {
179 return Err(Error::InvalidModification(None));
180 }
181 },
182 }
183
184 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(
195 &self.global(),
196 DOMString::from("No valid entry type provided to observe()."),
197 );
198 return Ok(());
199 }
200
201 self.global()
205 .performance()
206 .add_multiple_type_observer(self, entry_types);
207 Ok(())
208 } else if let Some(entry_type) = &options.type_ {
209 let Ok(entry_type) = EntryType::try_from(&*entry_type.str()) else {
211 Console::internal_warn(
212 &self.global(),
213 DOMString::from("No valid entry type provided to observe()."),
214 );
215 return Ok(());
216 };
217
218 self.global().performance().add_single_type_observer(
222 self,
223 entry_type,
224 options.buffered.unwrap_or(false),
225 );
226 Ok(())
227 } else {
228 unreachable!()
230 }
231 }
232
233 fn Disconnect(&self) {
235 self.global().performance().remove_observer(self);
236 self.entries.borrow_mut().clear();
237 }
238
239 fn TakeRecords(&self) -> Vec<DomRoot<PerformanceEntry>> {
241 let mut entries = self.entries.borrow_mut();
242 let taken = entries
243 .iter()
244 .map(|entry| DomRoot::from_ref(&**entry))
245 .collect();
246 entries.clear();
247 taken
248 }
249}