script/dom/performance/
performanceobserver.rs1use std::cell::{Cell, RefMut};
6
7use dom_struct::dom_struct;
8use js::context::JSContext;
9use js::rust::{HandleObject, MutableHandleValue};
10use script_bindings::callback::{OwnerWindow, RootedCallback, TracedCallback};
11use script_bindings::cell::DomRefCell;
12use script_bindings::reflector::{Reflector, reflect_dom_object_with_proto};
13
14use super::performanceentry::{EntryType, PerformanceEntry};
15use super::performanceobserverentrylist::PerformanceObserverEntryList;
16use crate::dom::bindings::callback::ExceptionHandling;
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;
22use crate::dom::bindings::root::{Dom, DomRoot};
23use crate::dom::console::Console;
24use crate::dom::globalscope::GlobalScope;
25
26#[derive(Clone, Copy, JSTraceable, MallocSizeOf, PartialEq)]
27enum ObserverType {
28 Undefined,
29 Single,
30 Multiple,
31}
32
33#[dom_struct]
34pub(crate) struct PerformanceObserver {
35 reflector_: Reflector,
36 callback: TracedCallback<PerformanceObserverCallback>,
37 entries: DomRefCell<Vec<Dom<PerformanceEntry>>>,
38 observer_type: Cell<ObserverType>,
39}
40
41impl PerformanceObserver {
42 fn new_inherited(callback: RootedCallback<PerformanceObserverCallback>) -> PerformanceObserver {
43 PerformanceObserver {
44 reflector_: Reflector::new(),
45 callback: callback.to_traced(),
46 entries: Default::default(),
47 observer_type: Cell::new(ObserverType::Undefined),
48 }
49 }
50
51 fn new_with_proto(
52 cx: &mut JSContext,
53 global: &GlobalScope,
54 proto: Option<HandleObject>,
55 callback: RootedCallback<PerformanceObserverCallback>,
56 ) -> DomRoot<PerformanceObserver> {
57 reflect_dom_object_with_proto(
58 cx,
59 Box::new(PerformanceObserver::new_inherited(callback)),
60 global,
61 proto,
62 )
63 }
64
65 pub(crate) fn queue_entry(&self, entry: &PerformanceEntry) {
67 self.entries.borrow_mut().push(Dom::from_ref(entry));
68 }
69
70 pub(crate) fn notify(&self, cx: &mut JSContext) {
73 if self.entries.borrow().is_empty() {
74 return;
75 }
76 let entries = self
77 .entries
78 .borrow_mut()
79 .drain(..)
80 .map(|entry| entry.as_rooted())
81 .collect();
82 let observer_entry_list = PerformanceObserverEntryList::new(cx, &self.global(), entries);
83 let _ = self.callback.Call_(
85 cx,
86 self,
87 &observer_entry_list,
88 self,
89 ExceptionHandling::Report,
90 );
91 }
92
93 pub(crate) fn entries_mut(&self) -> RefMut<'_, Vec<Dom<PerformanceEntry>>> {
94 self.entries.borrow_mut()
95 }
96}
97
98impl PerformanceObserverMethods<crate::DomTypeHolder> for PerformanceObserver {
99 fn Constructor(
101 cx: &mut js::context::JSContext,
102 global: &GlobalScope,
103 proto: Option<HandleObject>,
104 callback: RootedCallback<PerformanceObserverCallback>,
105 ) -> Fallible<DomRoot<PerformanceObserver>> {
106 Ok(PerformanceObserver::new_with_proto(
107 cx, global, proto, callback,
108 ))
109 }
110
111 fn SupportedEntryTypes(cx: &mut JSContext, global: &GlobalScope, retval: MutableHandleValue) {
113 global.supported_performance_entry_types(cx, retval)
116 }
117
118 fn Observe(&self, cx: &mut JSContext, options: &PerformanceObserverInit) -> Fallible<()> {
120 if options.entryTypes.is_none() && options.type_.is_none() {
126 return Err(Error::Syntax(None));
127 }
128
129 if options.entryTypes.is_some() && (options.buffered.is_some() || options.type_.is_some()) {
131 return Err(Error::Syntax(None));
132 }
133
134 match self.observer_type.get() {
139 ObserverType::Undefined => {
140 if options.entryTypes.is_some() {
141 self.observer_type.set(ObserverType::Multiple);
142 } else {
143 self.observer_type.set(ObserverType::Single);
144 }
145 },
146 ObserverType::Single => {
147 if options.entryTypes.is_some() {
148 return Err(Error::InvalidModification(None));
149 }
150 },
151 ObserverType::Multiple => {
152 if options.type_.is_some() {
153 return Err(Error::InvalidModification(None));
154 }
155 },
156 }
157
158 const NO_VALID_ENTRY_TYPE: &str = "No valid entry type provided to observe().";
160 if let Some(entry_types) = &options.entryTypes {
161 let entry_types = entry_types
163 .iter()
164 .filter_map(|e| EntryType::try_from(&*e.str()).ok())
165 .collect::<Vec<EntryType>>();
166
167 if entry_types.is_empty() {
169 Console::internal_warn(cx, &self.global(), NO_VALID_ENTRY_TYPE.to_string());
170 return Ok(());
171 }
172
173 self.global()
177 .performance(cx)
178 .add_multiple_type_observer(self, entry_types);
179 Ok(())
180 } else if let Some(entry_type) = &options.type_ {
181 let Ok(entry_type) = EntryType::try_from(&*entry_type.str()) else {
183 Console::internal_warn(cx, &self.global(), NO_VALID_ENTRY_TYPE.to_string());
184 return Ok(());
185 };
186
187 self.global().performance(cx).add_single_type_observer(
191 self,
192 entry_type,
193 options.buffered.unwrap_or(false),
194 );
195 Ok(())
196 } else {
197 unreachable!()
199 }
200 }
201
202 fn Disconnect(&self, cx: &mut JSContext) {
204 self.global().performance(cx).remove_observer(self);
205 self.entries.borrow_mut().clear();
206 }
207
208 fn TakeRecords(&self) -> Vec<DomRoot<PerformanceEntry>> {
210 let mut entries = self.entries.borrow_mut();
211 let taken = entries.iter().map(|entry| entry.as_rooted()).collect();
212 entries.clear();
213 taken
214 }
215}
216
217impl OwnerWindow<crate::DomTypeHolder> for PerformanceObserver {}