script/dom/
performanceobserver.rs1use std::cell::Cell;
6use std::rc::Rc;
7
8use dom_struct::dom_struct;
9use js::rust::{HandleObject, MutableHandleValue};
10
11use crate::dom::bindings::callback::ExceptionHandling;
12use crate::dom::bindings::cell::DomRefCell;
13use crate::dom::bindings::codegen::Bindings::PerformanceBinding::PerformanceEntryList as DOMPerformanceEntryList;
14use crate::dom::bindings::codegen::Bindings::PerformanceObserverBinding::{
15 PerformanceObserverCallback, PerformanceObserverInit, PerformanceObserverMethods,
16};
17use crate::dom::bindings::error::{Error, Fallible};
18use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object_with_proto};
19use crate::dom::bindings::root::DomRoot;
20use crate::dom::bindings::str::DOMString;
21use crate::dom::console::Console;
22use crate::dom::globalscope::GlobalScope;
23use crate::dom::performance::PerformanceEntryList;
24use crate::dom::performanceentry::PerformanceEntry;
25use crate::dom::performanceobserverentrylist::PerformanceObserverEntryList;
26use crate::script_runtime::{CanGc, JSContext};
27
28pub(crate) const VALID_ENTRY_TYPES: &[&str] = &[
30 "mark", "measure", "navigation", "paint", "resource", ];
38
39#[derive(Clone, Copy, JSTraceable, MallocSizeOf, PartialEq)]
40enum ObserverType {
41 Undefined,
42 Single,
43 Multiple,
44}
45
46#[dom_struct]
47pub(crate) struct PerformanceObserver {
48 reflector_: Reflector,
49 #[ignore_malloc_size_of = "can't measure Rc values"]
50 callback: Rc<PerformanceObserverCallback>,
51 entries: DomRefCell<DOMPerformanceEntryList>,
52 observer_type: Cell<ObserverType>,
53}
54
55impl PerformanceObserver {
56 fn new_inherited(
57 callback: Rc<PerformanceObserverCallback>,
58 entries: DomRefCell<DOMPerformanceEntryList>,
59 ) -> PerformanceObserver {
60 PerformanceObserver {
61 reflector_: Reflector::new(),
62 callback,
63 entries,
64 observer_type: Cell::new(ObserverType::Undefined),
65 }
66 }
67
68 pub(crate) fn new(
69 global: &GlobalScope,
70 callback: Rc<PerformanceObserverCallback>,
71 entries: DOMPerformanceEntryList,
72 can_gc: CanGc,
73 ) -> DomRoot<PerformanceObserver> {
74 Self::new_with_proto(global, None, callback, entries, can_gc)
75 }
76
77 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
78 fn new_with_proto(
79 global: &GlobalScope,
80 proto: Option<HandleObject>,
81 callback: Rc<PerformanceObserverCallback>,
82 entries: DOMPerformanceEntryList,
83 can_gc: CanGc,
84 ) -> DomRoot<PerformanceObserver> {
85 let observer = PerformanceObserver::new_inherited(callback, DomRefCell::new(entries));
86 reflect_dom_object_with_proto(Box::new(observer), global, proto, can_gc)
87 }
88
89 pub(crate) fn queue_entry(&self, entry: &PerformanceEntry) {
91 self.entries.borrow_mut().push(DomRoot::from_ref(entry));
92 }
93
94 pub(crate) fn notify(&self, can_gc: CanGc) {
97 if self.entries.borrow().is_empty() {
98 return;
99 }
100 let entry_list = PerformanceEntryList::new(self.entries.borrow_mut().drain(..).collect());
101 let observer_entry_list =
102 PerformanceObserverEntryList::new(&self.global(), entry_list, can_gc);
103 let _ = self.callback.Call_(
105 self,
106 &observer_entry_list,
107 self,
108 ExceptionHandling::Report,
109 can_gc,
110 );
111 }
112
113 pub(crate) fn callback(&self) -> Rc<PerformanceObserverCallback> {
114 self.callback.clone()
115 }
116
117 pub(crate) fn entries(&self) -> DOMPerformanceEntryList {
118 self.entries.borrow().clone()
119 }
120
121 pub(crate) fn set_entries(&self, entries: DOMPerformanceEntryList) {
122 *self.entries.borrow_mut() = entries;
123 }
124}
125
126impl PerformanceObserverMethods<crate::DomTypeHolder> for PerformanceObserver {
127 fn Constructor(
129 global: &GlobalScope,
130 proto: Option<HandleObject>,
131 can_gc: CanGc,
132 callback: Rc<PerformanceObserverCallback>,
133 ) -> Fallible<DomRoot<PerformanceObserver>> {
134 Ok(PerformanceObserver::new_with_proto(
135 global,
136 proto,
137 callback,
138 Vec::new(),
139 can_gc,
140 ))
141 }
142
143 fn SupportedEntryTypes(
145 cx: JSContext,
146 global: &GlobalScope,
147 can_gc: CanGc,
148 retval: MutableHandleValue,
149 ) {
150 global.supported_performance_entry_types(cx, retval, can_gc)
153 }
154
155 fn Observe(&self, options: &PerformanceObserverInit) -> Fallible<()> {
157 if options.entryTypes.is_none() && options.type_.is_none() {
163 return Err(Error::Syntax(None));
164 }
165
166 if options.entryTypes.is_some() && (options.buffered.is_some() || options.type_.is_some()) {
168 return Err(Error::Syntax(None));
169 }
170
171 match self.observer_type.get() {
176 ObserverType::Undefined => {
177 if options.entryTypes.is_some() {
178 self.observer_type.set(ObserverType::Multiple);
179 } else {
180 self.observer_type.set(ObserverType::Single);
181 }
182 },
183 ObserverType::Single => {
184 if options.entryTypes.is_some() {
185 return Err(Error::InvalidModification);
186 }
187 },
188 ObserverType::Multiple => {
189 if options.type_.is_some() {
190 return Err(Error::InvalidModification);
191 }
192 },
193 }
194
195 if let Some(entry_types) = &options.entryTypes {
197 let entry_types = entry_types
199 .iter()
200 .filter(|e| VALID_ENTRY_TYPES.contains(&e.as_ref()))
201 .cloned()
202 .collect::<Vec<DOMString>>();
203
204 if entry_types.is_empty() {
206 Console::internal_warn(
207 &self.global(),
208 DOMString::from("No valid entry type provided to observe()."),
209 );
210 return Ok(());
211 }
212
213 self.global()
217 .performance()
218 .add_multiple_type_observer(self, entry_types);
219 Ok(())
220 } else if let Some(entry_type) = &options.type_ {
221 if !VALID_ENTRY_TYPES.contains(&entry_type.as_ref()) {
223 Console::internal_warn(
224 &self.global(),
225 DOMString::from("No valid entry type provided to observe()."),
226 );
227 return Ok(());
228 }
229
230 self.global().performance().add_single_type_observer(
234 self,
235 entry_type,
236 options.buffered.unwrap_or(false),
237 );
238 Ok(())
239 } else {
240 unreachable!()
242 }
243 }
244
245 fn Disconnect(&self) {
247 self.global().performance().remove_observer(self);
248 self.entries.borrow_mut().clear();
249 }
250
251 fn TakeRecords(&self) -> Vec<DomRoot<PerformanceEntry>> {
253 let mut entries = self.entries.borrow_mut();
254 let taken = entries
255 .iter()
256 .map(|entry| DomRoot::from_ref(&**entry))
257 .collect();
258 entries.clear();
259 taken
260 }
261}