1use 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::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
28pub(crate) const VALID_ENTRY_TYPES: &[&str] = &[
30 "largest-contentful-paint", "mark", "measure", "navigation", "paint", "resource", ];
39
40#[derive(Clone, Copy, JSTraceable, MallocSizeOf, PartialEq)]
41enum ObserverType {
42 Undefined,
43 Single,
44 Multiple,
45}
46
47#[dom_struct]
48pub(crate) struct PerformanceObserver {
49 reflector_: Reflector,
50 #[ignore_malloc_size_of = "can't measure Rc values"]
51 callback: Rc<PerformanceObserverCallback>,
52 entries: DomRefCell<DOMPerformanceEntryList>,
53 observer_type: Cell<ObserverType>,
54}
55
56impl PerformanceObserver {
57 fn new_inherited(
58 callback: Rc<PerformanceObserverCallback>,
59 entries: DomRefCell<DOMPerformanceEntryList>,
60 ) -> PerformanceObserver {
61 PerformanceObserver {
62 reflector_: Reflector::new(),
63 callback,
64 entries,
65 observer_type: Cell::new(ObserverType::Undefined),
66 }
67 }
68
69 pub(crate) fn new(
70 global: &GlobalScope,
71 callback: Rc<PerformanceObserverCallback>,
72 entries: DOMPerformanceEntryList,
73 can_gc: CanGc,
74 ) -> DomRoot<PerformanceObserver> {
75 Self::new_with_proto(global, None, callback, entries, can_gc)
76 }
77
78 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
79 fn new_with_proto(
80 global: &GlobalScope,
81 proto: Option<HandleObject>,
82 callback: Rc<PerformanceObserverCallback>,
83 entries: DOMPerformanceEntryList,
84 can_gc: CanGc,
85 ) -> DomRoot<PerformanceObserver> {
86 let observer = PerformanceObserver::new_inherited(callback, DomRefCell::new(entries));
87 reflect_dom_object_with_proto(Box::new(observer), global, proto, can_gc)
88 }
89
90 pub(crate) fn queue_entry(&self, entry: &PerformanceEntry) {
92 self.entries.borrow_mut().push(DomRoot::from_ref(entry));
93 }
94
95 pub(crate) fn notify(&self, can_gc: CanGc) {
98 if self.entries.borrow().is_empty() {
99 return;
100 }
101 let entry_list = PerformanceEntryList::new(self.entries.borrow_mut().drain(..).collect());
102 let observer_entry_list =
103 PerformanceObserverEntryList::new(&self.global(), entry_list, can_gc);
104 let _ = self.callback.Call_(
106 self,
107 &observer_entry_list,
108 self,
109 ExceptionHandling::Report,
110 can_gc,
111 );
112 }
113
114 pub(crate) fn callback(&self) -> Rc<PerformanceObserverCallback> {
115 self.callback.clone()
116 }
117
118 pub(crate) fn entries(&self) -> DOMPerformanceEntryList {
119 self.entries.borrow().clone()
120 }
121
122 pub(crate) fn set_entries(&self, entries: DOMPerformanceEntryList) {
123 *self.entries.borrow_mut() = entries;
124 }
125}
126
127impl PerformanceObserverMethods<crate::DomTypeHolder> for PerformanceObserver {
128 fn Constructor(
130 global: &GlobalScope,
131 proto: Option<HandleObject>,
132 can_gc: CanGc,
133 callback: Rc<PerformanceObserverCallback>,
134 ) -> Fallible<DomRoot<PerformanceObserver>> {
135 Ok(PerformanceObserver::new_with_proto(
136 global,
137 proto,
138 callback,
139 Vec::new(),
140 can_gc,
141 ))
142 }
143
144 fn SupportedEntryTypes(
146 cx: JSContext,
147 global: &GlobalScope,
148 can_gc: CanGc,
149 retval: MutableHandleValue,
150 ) {
151 global.supported_performance_entry_types(cx, retval, can_gc)
154 }
155
156 fn Observe(&self, options: &PerformanceObserverInit) -> Fallible<()> {
158 if options.entryTypes.is_none() && options.type_.is_none() {
164 return Err(Error::Syntax(None));
165 }
166
167 if options.entryTypes.is_some() && (options.buffered.is_some() || options.type_.is_some()) {
169 return Err(Error::Syntax(None));
170 }
171
172 match self.observer_type.get() {
177 ObserverType::Undefined => {
178 if options.entryTypes.is_some() {
179 self.observer_type.set(ObserverType::Multiple);
180 } else {
181 self.observer_type.set(ObserverType::Single);
182 }
183 },
184 ObserverType::Single => {
185 if options.entryTypes.is_some() {
186 return Err(Error::InvalidModification);
187 }
188 },
189 ObserverType::Multiple => {
190 if options.type_.is_some() {
191 return Err(Error::InvalidModification);
192 }
193 },
194 }
195
196 if let Some(entry_types) = &options.entryTypes {
198 let entry_types = entry_types
200 .iter()
201 .filter(|e| VALID_ENTRY_TYPES.contains(&&*e.str()))
202 .cloned()
203 .collect::<Vec<DOMString>>();
204
205 if entry_types.is_empty() {
207 Console::internal_warn(
208 &self.global(),
209 DOMString::from("No valid entry type provided to observe()."),
210 );
211 return Ok(());
212 }
213
214 self.global()
218 .performance()
219 .add_multiple_type_observer(self, entry_types);
220 Ok(())
221 } else if let Some(entry_type) = &options.type_ {
222 if !VALID_ENTRY_TYPES.contains(&&*entry_type.str()) {
224 Console::internal_warn(
225 &self.global(),
226 DOMString::from("No valid entry type provided to observe()."),
227 );
228 return Ok(());
229 }
230
231 self.global().performance().add_single_type_observer(
235 self,
236 entry_type,
237 options.buffered.unwrap_or(false),
238 );
239 Ok(())
240 } else {
241 unreachable!()
243 }
244 }
245
246 fn Disconnect(&self) {
248 self.global().performance().remove_observer(self);
249 self.entries.borrow_mut().clear();
250 }
251
252 fn TakeRecords(&self) -> Vec<DomRoot<PerformanceEntry>> {
254 let mut entries = self.entries.borrow_mut();
255 let taken = entries
256 .iter()
257 .map(|entry| DomRoot::from_ref(&**entry))
258 .collect();
259 entries.clear();
260 taken
261 }
262}