script/dom/performance/
performanceobserver.rs1use std::cell::{Cell, RefMut};
6use std::rc::Rc;
7
8use dom_struct::dom_struct;
9use js::context::JSContext;
10use js::rust::{HandleObject, MutableHandleValue};
11use script_bindings::callback::OwnerWindow;
12use script_bindings::cell::DomRefCell;
13use script_bindings::reflector::{Reflector, reflect_dom_object_with_proto};
14
15use super::performanceentry::{EntryType, PerformanceEntry};
16use super::performanceobserverentrylist::PerformanceObserverEntryList;
17use crate::dom::bindings::callback::ExceptionHandling;
18use crate::dom::bindings::codegen::Bindings::PerformanceObserverBinding::{
19 PerformanceObserverCallback, PerformanceObserverInit, PerformanceObserverMethods,
20};
21use crate::dom::bindings::error::{Error, Fallible};
22use crate::dom::bindings::reflector::DomGlobal;
23use crate::dom::bindings::root::{Dom, DomRoot};
24use crate::dom::console::Console;
25use crate::dom::globalscope::GlobalScope;
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<Vec<Dom<PerformanceEntry>>>,
40 observer_type: Cell<ObserverType>,
41}
42
43impl PerformanceObserver {
44 fn new_inherited(callback: Rc<PerformanceObserverCallback>) -> PerformanceObserver {
45 PerformanceObserver {
46 reflector_: Reflector::new(),
47 callback,
48 entries: Default::default(),
49 observer_type: Cell::new(ObserverType::Undefined),
50 }
51 }
52
53 fn new_with_proto(
54 cx: &mut JSContext,
55 global: &GlobalScope,
56 proto: Option<HandleObject>,
57 callback: Rc<PerformanceObserverCallback>,
58 ) -> DomRoot<PerformanceObserver> {
59 reflect_dom_object_with_proto(
60 cx,
61 Box::new(PerformanceObserver::new_inherited(callback)),
62 global,
63 proto,
64 )
65 }
66
67 pub(crate) fn queue_entry(&self, entry: &PerformanceEntry) {
69 self.entries.borrow_mut().push(Dom::from_ref(entry));
70 }
71
72 pub(crate) fn notify(&self, cx: &mut JSContext) {
75 if self.entries.borrow().is_empty() {
76 return;
77 }
78 let entries = self
79 .entries
80 .borrow_mut()
81 .drain(..)
82 .map(|entry| entry.as_rooted())
83 .collect();
84 let observer_entry_list = PerformanceObserverEntryList::new(cx, &self.global(), entries);
85 let _ = self.callback.Call_(
87 cx,
88 self,
89 &observer_entry_list,
90 self,
91 ExceptionHandling::Report,
92 );
93 }
94
95 pub(crate) fn entries_mut(&self) -> RefMut<'_, Vec<Dom<PerformanceEntry>>> {
96 self.entries.borrow_mut()
97 }
98}
99
100impl PerformanceObserverMethods<crate::DomTypeHolder> for PerformanceObserver {
101 fn Constructor(
103 cx: &mut js::context::JSContext,
104 global: &GlobalScope,
105 proto: Option<HandleObject>,
106 callback: Rc<PerformanceObserverCallback>,
107 ) -> Fallible<DomRoot<PerformanceObserver>> {
108 Ok(PerformanceObserver::new_with_proto(
109 cx, global, proto, callback,
110 ))
111 }
112
113 fn SupportedEntryTypes(cx: &mut JSContext, global: &GlobalScope, retval: MutableHandleValue) {
115 global.supported_performance_entry_types(cx, retval)
118 }
119
120 fn Observe(&self, cx: &mut JSContext, options: &PerformanceObserverInit) -> Fallible<()> {
122 if options.entryTypes.is_none() && options.type_.is_none() {
128 return Err(Error::Syntax(None));
129 }
130
131 if options.entryTypes.is_some() && (options.buffered.is_some() || options.type_.is_some()) {
133 return Err(Error::Syntax(None));
134 }
135
136 match self.observer_type.get() {
141 ObserverType::Undefined => {
142 if options.entryTypes.is_some() {
143 self.observer_type.set(ObserverType::Multiple);
144 } else {
145 self.observer_type.set(ObserverType::Single);
146 }
147 },
148 ObserverType::Single => {
149 if options.entryTypes.is_some() {
150 return Err(Error::InvalidModification(None));
151 }
152 },
153 ObserverType::Multiple => {
154 if options.type_.is_some() {
155 return Err(Error::InvalidModification(None));
156 }
157 },
158 }
159
160 const NO_VALID_ENTRY_TYPE: &str = "No valid entry type provided to observe().";
162 if let Some(entry_types) = &options.entryTypes {
163 let entry_types = entry_types
165 .iter()
166 .filter_map(|e| EntryType::try_from(&*e.str()).ok())
167 .collect::<Vec<EntryType>>();
168
169 if entry_types.is_empty() {
171 Console::internal_warn(cx, &self.global(), NO_VALID_ENTRY_TYPE.to_string());
172 return Ok(());
173 }
174
175 self.global()
179 .performance(cx)
180 .add_multiple_type_observer(self, entry_types);
181 Ok(())
182 } else if let Some(entry_type) = &options.type_ {
183 let Ok(entry_type) = EntryType::try_from(&*entry_type.str()) else {
185 Console::internal_warn(cx, &self.global(), NO_VALID_ENTRY_TYPE.to_string());
186 return Ok(());
187 };
188
189 self.global().performance(cx).add_single_type_observer(
193 self,
194 entry_type,
195 options.buffered.unwrap_or(false),
196 );
197 Ok(())
198 } else {
199 unreachable!()
201 }
202 }
203
204 fn Disconnect(&self, cx: &mut JSContext) {
206 self.global().performance(cx).remove_observer(self);
207 self.entries.borrow_mut().clear();
208 }
209
210 fn TakeRecords(&self) -> Vec<DomRoot<PerformanceEntry>> {
212 let mut entries = self.entries.borrow_mut();
213 let taken = entries.iter().map(|entry| entry.as_rooted()).collect();
214 entries.clear();
215 taken
216 }
217}
218
219impl OwnerWindow<crate::DomTypeHolder> for PerformanceObserver {}