diff options
Diffstat (limited to 'components/script')
-rw-r--r-- | components/script/dom/performance.rs | 65 | ||||
-rw-r--r-- | components/script/dom/performanceobserver.rs | 140 | ||||
-rw-r--r-- | components/script/dom/webidls/PerformanceObserver.webidl | 9 |
3 files changed, 166 insertions, 48 deletions
diff --git a/components/script/dom/performance.rs b/components/script/dom/performance.rs index 33e39303cb4..9de98852df7 100644 --- a/components/script/dom/performance.rs +++ b/components/script/dom/performance.rs @@ -185,22 +185,12 @@ impl Performance { /// Add a PerformanceObserver to the list of observers with a set of /// observed entry types. - pub fn add_observer( + + pub fn add_multiple_type_observer( &self, observer: &DOMPerformanceObserver, entry_types: Vec<DOMString>, - buffered: bool, ) { - if buffered { - let buffer = self.buffer.borrow(); - let mut new_entries = entry_types - .iter() - .flat_map(|e| buffer.get_entries_by_name_and_type(None, Some(e.clone()))) - .collect::<DOMPerformanceEntryList>(); - let mut obs_entries = observer.entries(); - obs_entries.append(&mut new_entries); - observer.set_entries(obs_entries); - } let mut observers = self.observers.borrow_mut(); match observers.iter().position(|o| *o.observer == *observer) { // If the observer is already in the list, we only update the observed @@ -214,6 +204,46 @@ impl Performance { }; } + pub fn add_single_type_observer( + &self, + observer: &DOMPerformanceObserver, + entry_type: &DOMString, + buffered: bool, + ) { + if buffered { + let buffer = self.buffer.borrow(); + let mut new_entries = + buffer.get_entries_by_name_and_type(None, Some(entry_type.clone())); + if new_entries.len() > 0 { + let mut obs_entries = observer.entries(); + obs_entries.append(&mut new_entries); + observer.set_entries(obs_entries); + } + + if !self.pending_notification_observers_task.get() { + self.pending_notification_observers_task.set(true); + let task_source = self.global().performance_timeline_task_source(); + task_source.queue_notification(&self.global()); + } + } + let mut observers = self.observers.borrow_mut(); + match observers.iter().position(|o| *o.observer == *observer) { + // If the observer is already in the list, we only update + // the observed entry types. + Some(p) => { + // Append the type if not already present, otherwise do nothing + if !observers[p].entry_types.contains(entry_type) { + observers[p].entry_types.push(entry_type.clone()) + } + }, + // Otherwise, we create and insert the new PerformanceObserver. + None => observers.push(PerformanceObserver { + observer: DomRoot::from_ref(observer), + entry_types: vec![entry_type.clone()], + }), + }; + } + /// Remove a PerformanceObserver from the list of observers. pub fn remove_observer(&self, observer: &DOMPerformanceObserver) { let mut observers = self.observers.borrow_mut(); @@ -287,18 +317,13 @@ impl Performance { // Step 7.2. // We have to operate over a copy of the performance observers to avoid // the risk of an observer's callback modifying the list of registered - // observers. + // observers. This is a shallow copy, so observers can + // disconnect themselves by using the argument of their own callback. let observers: Vec<DomRoot<DOMPerformanceObserver>> = self .observers .borrow() .iter() - .map(|o| { - DOMPerformanceObserver::new( - &self.global(), - o.observer.callback(), - o.observer.entries(), - ) - }) + .map(|o| DomRoot::from_ref(&*o.observer)) .collect(); // Step 7.3. diff --git a/components/script/dom/performanceobserver.rs b/components/script/dom/performanceobserver.rs index 0683a3cb46e..e107c4a08d9 100644 --- a/components/script/dom/performanceobserver.rs +++ b/components/script/dom/performanceobserver.rs @@ -13,11 +13,13 @@ use crate::dom::bindings::error::{Error, Fallible}; use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector}; use crate::dom::bindings::root::DomRoot; use crate::dom::bindings::str::DOMString; +use crate::dom::console::Console; use crate::dom::globalscope::GlobalScope; use crate::dom::performance::PerformanceEntryList; use crate::dom::performanceentry::PerformanceEntry; use crate::dom::performanceobserverentrylist::PerformanceObserverEntryList; use dom_struct::dom_struct; +use std::cell::Cell; use std::rc::Rc; /// List of allowed performance entry types. @@ -31,12 +33,20 @@ const VALID_ENTRY_TYPES: &'static [&'static str] = &[ "paint", // Paint Timing API ]; +#[derive(Clone, Copy, JSTraceable, MallocSizeOf, PartialEq)] +enum ObserverType { + Undefined, + Single, + Multiple, +} + #[dom_struct] pub struct PerformanceObserver { reflector_: Reflector, #[ignore_malloc_size_of = "can't measure Rc values"] callback: Rc<PerformanceObserverCallback>, entries: DomRefCell<DOMPerformanceEntryList>, + observer_type: Cell<ObserverType>, } impl PerformanceObserver { @@ -48,6 +58,7 @@ impl PerformanceObserver { reflector_: Reflector::new(), callback, entries, + observer_type: Cell::new(ObserverType::Undefined), } } @@ -77,17 +88,15 @@ impl PerformanceObserver { /// Trigger performance observer callback with the list of performance entries /// buffered since the last callback call. pub fn notify(&self) { - let entries = self.entries.borrow(); - if entries.is_empty() { + if self.entries.borrow().is_empty() { return; } - let mut entries = entries.clone(); - let global = self.global(); - let entry_list = PerformanceEntryList::new(entries.drain(..).collect()); - let observer_entry_list = PerformanceObserverEntryList::new(&global, entry_list); + let entry_list = PerformanceEntryList::new(self.entries.borrow_mut().drain(..).collect()); + let observer_entry_list = PerformanceObserverEntryList::new(&self.global(), entry_list); + // using self both as thisArg and as the second formal argument let _ = self .callback - .Call__(&observer_entry_list, self, ExceptionHandling::Report); + .Call_(self, &observer_entry_list, self, ExceptionHandling::Report); } pub fn callback(&self) -> Rc<PerformanceObserverCallback> { @@ -106,31 +115,112 @@ impl PerformanceObserver { impl PerformanceObserverMethods for PerformanceObserver { // https://w3c.github.io/performance-timeline/#dom-performanceobserver-observe() fn Observe(&self, options: &PerformanceObserverInit) -> Fallible<()> { - // step 1 - // Make sure the client is asking to observe events from allowed entry types. - let entry_types = options - .entryTypes - .iter() - .filter(|e| VALID_ENTRY_TYPES.contains(&e.as_ref())) - .map(|e| e.clone()) - .collect::<Vec<DOMString>>(); - // step 2 - // There must be at least one valid entry type. - if entry_types.is_empty() { - return Err(Error::Type("entryTypes cannot be empty".to_string())); + // Step 1 is self + + // Step 2 is self.global() + + // Step 3 + if options.entryTypes.is_none() && options.type_.is_none() { + return Err(Error::Syntax); + } + + // Step 4 + if options.entryTypes.is_some() && (options.buffered.is_some() || options.type_.is_some()) { + return Err(Error::Syntax); + } + + // If this point is reached, then one of options.entryTypes or options.type_ + // is_some, but not both. + + // Step 5 + match self.observer_type.get() { + ObserverType::Undefined => { + if options.entryTypes.is_some() { + self.observer_type.set(ObserverType::Multiple); + } else { + self.observer_type.set(ObserverType::Single); + } + }, + ObserverType::Single => { + if options.entryTypes.is_some() { + return Err(Error::InvalidModification); + } + }, + ObserverType::Multiple => { + if options.type_.is_some() { + return Err(Error::InvalidModification); + } + }, } - // step 3-4-5 - self.global() - .performance() - .add_observer(self, entry_types, options.buffered); + // The entryTypes and type paths diverge here + if let Some(entry_types) = &options.entryTypes { + // Steps 6.1 - 6.2 + let entry_types = entry_types + .iter() + .filter(|e| VALID_ENTRY_TYPES.contains(&e.as_ref())) + .map(|e| e.clone()) + .collect::<Vec<DOMString>>(); + + // Step 6.3 + if entry_types.is_empty() { + Console::Warn( + &*self.global(), + vec![DOMString::from( + "No valid entry type provided to observe().", + )], + ); + return Ok(()); + } + + // Steps 6.4-6.5 + // This never pre-fills buffered entries, and + // any existing types are replaced. + self.global() + .performance() + .add_multiple_type_observer(self, entry_types); + Ok(()) + } else if let Some(entry_type) = &options.type_ { + // Step 7.2 + if !VALID_ENTRY_TYPES.contains(&entry_type.as_ref()) { + Console::Warn( + &*self.global(), + vec![DOMString::from( + "No valid entry type provided to observe().", + )], + ); + return Ok(()); + } - Ok(()) + // Steps 7.3-7.5 + // This may pre-fill buffered entries, and + // existing types are appended to. + self.global().performance().add_single_type_observer( + self, + entry_type, + options.buffered.unwrap_or(false), + ); + Ok(()) + } else { + // Step 7.1 + unreachable!() + } } - // https://w3c.github.io/performance-timeline/#dom-performanceobserver-disconnect() + // https://w3c.github.io/performance-timeline/#dom-performanceobserver-disconnect fn Disconnect(&self) { self.global().performance().remove_observer(self); self.entries.borrow_mut().clear(); } + + // https://w3c.github.io/performance-timeline/#takerecords-method + fn TakeRecords(&self) -> Vec<DomRoot<PerformanceEntry>> { + let mut entries = self.entries.borrow_mut(); + let taken = entries + .iter() + .map(|entry| DomRoot::from_ref(&**entry)) + .collect(); + entries.clear(); + return taken; + } } diff --git a/components/script/dom/webidls/PerformanceObserver.webidl b/components/script/dom/webidls/PerformanceObserver.webidl index 257ff96c46f..dd3a66b299d 100644 --- a/components/script/dom/webidls/PerformanceObserver.webidl +++ b/components/script/dom/webidls/PerformanceObserver.webidl @@ -7,8 +7,9 @@ */ dictionary PerformanceObserverInit { - required sequence<DOMString> entryTypes; - boolean buffered = false; + sequence<DOMString> entryTypes; + DOMString type; + boolean buffered; }; callback PerformanceObserverCallback = void (PerformanceObserverEntryList entries, PerformanceObserver observer); @@ -17,6 +18,8 @@ callback PerformanceObserverCallback = void (PerformanceObserverEntryList entrie interface PerformanceObserver { [Throws] constructor(PerformanceObserverCallback callback); [Throws] - void observe(PerformanceObserverInit options); + void observe(optional PerformanceObserverInit options = {}); void disconnect(); + PerformanceEntryList takeRecords(); + // [SameObject] static readonly attribute FrozenArray<DOMString> supportedEntryTypes; }; |