diff options
author | Fernando Jiménez Moreno <ferjmoreno@gmail.com> | 2017-08-01 14:57:55 +0200 |
---|---|---|
committer | Fernando Jiménez Moreno <ferjmoreno@gmail.com> | 2017-08-17 11:48:34 +0200 |
commit | 52348f1fccea3e4abc2ad80d001a43f5f51cbdc8 (patch) | |
tree | 5e6b6edd3b2bf5368d9ccdd681b49f88bbac2889 /components/script/dom/performance.rs | |
parent | 24270f93571e0a1254d402f1c897bcc1ebbbd459 (diff) | |
download | servo-52348f1fccea3e4abc2ad80d001a43f5f51cbdc8.tar.gz servo-52348f1fccea3e4abc2ad80d001a43f5f51cbdc8.zip |
Performance Timeline API
Diffstat (limited to 'components/script/dom/performance.rs')
-rw-r--r-- | components/script/dom/performance.rs | 197 |
1 files changed, 194 insertions, 3 deletions
diff --git a/components/script/dom/performance.rs b/components/script/dom/performance.rs index 7ce0cd7d1eb..bb2c5885bcb 100644 --- a/components/script/dom/performance.rs +++ b/components/script/dom/performance.rs @@ -2,22 +2,79 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +use dom::bindings::cell::DOMRefCell; use dom::bindings::codegen::Bindings::PerformanceBinding; -use dom::bindings::codegen::Bindings::PerformanceBinding::PerformanceMethods; +use dom::bindings::codegen::Bindings::PerformanceBinding::{DOMHighResTimeStamp, PerformanceMethods}; +use dom::bindings::codegen::Bindings::PerformanceBinding::PerformanceEntryList as DOMPerformanceEntryList; use dom::bindings::js::{JS, Root}; use dom::bindings::num::Finite; -use dom::bindings::reflector::{Reflector, reflect_dom_object}; +use dom::bindings::refcounted::Trusted; +use dom::bindings::reflector::{DomObject, Reflector, reflect_dom_object}; +use dom::bindings::str::DOMString; +use dom::performanceentry::PerformanceEntry; +use dom::performanceobserver::PerformanceObserver as DOMPerformanceObserver; use dom::performancetiming::PerformanceTiming; use dom::window::Window; use dom_struct::dom_struct; +use script_thread::{Runnable, ScriptThread}; +use std::cell::Cell; use time; -pub type DOMHighResTimeStamp = Finite<f64>; +/// Implementation of a list of PerformanceEntry items shared by the +/// Performance and PerformanceObserverEntryList interfaces implementations. +#[derive(HeapSizeOf, JSTraceable)] +pub struct PerformanceEntryList { + entries: DOMPerformanceEntryList, +} + +impl PerformanceEntryList { + pub fn new(entries: DOMPerformanceEntryList) -> Self { + PerformanceEntryList { + entries, + } + } + + pub fn get_entries(&self) -> Vec<Root<PerformanceEntry>> { + self.entries.clone() + } + + pub fn get_entries_by_type(&self, entry_type: DOMString) -> Vec<Root<PerformanceEntry>> { + self.entries.iter().filter(|e| *e.entry_type() == entry_type) + .map(|e| e.clone()) + .collect() + } + + pub fn get_entries_by_name(&self, name: DOMString, entry_type: Option<DOMString>) + -> Vec<Root<PerformanceEntry>> { + self.entries.iter().filter(|e| + *e.name() == name && + entry_type.as_ref().map_or(true, |type_| *e.entry_type() == *type_) + ).map(|e| e.clone()).collect() + } +} + +impl IntoIterator for PerformanceEntryList { + type Item = Root<PerformanceEntry>; + type IntoIter = ::std::vec::IntoIter<Root<PerformanceEntry>>; + + fn into_iter(self) -> Self::IntoIter { + self.entries.into_iter() + } +} + +#[derive(HeapSizeOf, JSTraceable)] +struct PerformanceObserver { + observer: Root<DOMPerformanceObserver>, + entry_types: Vec<DOMString>, +} #[dom_struct] pub struct Performance { reflector_: Reflector, timing: JS<PerformanceTiming>, + entries: DOMRefCell<PerformanceEntryList>, + observers: DOMRefCell<Vec<PerformanceObserver>>, + pending_notification_observers_task: Cell<bool>, } impl Performance { @@ -29,6 +86,9 @@ impl Performance { timing: JS::from_ref(&*PerformanceTiming::new(window, navigation_start, navigation_start_precise)), + entries: DOMRefCell::new(PerformanceEntryList::new(Vec::new())), + observers: DOMRefCell::new(Vec::new()), + pending_notification_observers_task: Cell::new(false), } } @@ -41,6 +101,121 @@ impl Performance { window, PerformanceBinding::Wrap) } + + /// Add a PerformanceObserver to the list of observers with a set of + /// observed entry types. + pub fn add_observer(&self, + observer: &DOMPerformanceObserver, + entry_types: Vec<DOMString>) { + 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) => observers[p].entry_types = entry_types, + // Otherwise, we create and insert the new PerformanceObserver. + None => observers.push(PerformanceObserver { + observer: Root::from_ref(observer), + entry_types + }) + }; + } + + /// Remove a PerformanceObserver from the list of observers. + pub fn remove_observer(&self, observer: &DOMPerformanceObserver) { + let mut observers = self.observers.borrow_mut(); + let index = match observers.iter().position(|o| &(*o.observer) == observer) { + Some(p) => p, + None => return, + }; + observers.remove(index); + } + + /// Queue a notification for each performance observer interested in + /// this type of performance entry and queue a low priority task to + /// notify the observers if no other notification task is already queued. + /// + /// Algorithm spec: + /// https://w3c.github.io/performance-timeline/#queue-a-performanceentry + /// + /// XXX This should be called at some point by the User Timing, Resource + /// Timing, Server Timing and Paint Timing APIs. + pub fn queue_entry(&self, entry: &PerformanceEntry, + add_to_performance_entries_buffer: bool) { + // Steps 1-3. + // Add the performance entry to the list of performance entries that have not + // been notified to each performance observer owner, filtering the ones it's + // interested in. + for o in self.observers.borrow().iter().filter(|o| o.entry_types.contains(entry.entry_type())) { + o.observer.queue_entry(entry); + } + + // Step 4. + // If the "add to performance entry buffer flag" is set, add the + // new entry to the buffer. + if add_to_performance_entries_buffer { + self.entries.borrow_mut().entries.push(Root::from_ref(entry)); + } + + // Step 5. + // If there is already a queued notification task, we just bail out. + if self.pending_notification_observers_task.get() { + return; + } + + // Step 6. + // Queue a new notification task. + self.pending_notification_observers_task.set(true); + let global = self.global(); + let window = global.as_window(); + let task_source = window.performance_timeline_task_source(); + task_source.queue_notification(self, window); + } + + /// Observers notifications task. + /// + /// Algorithm spec (step 7): + /// https://w3c.github.io/performance-timeline/#queue-a-performanceentry + fn notify_observers(&self) { + // Step 7.1. + self.pending_notification_observers_task.set(false); + + // 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. + let observers: Vec<Root<DOMPerformanceObserver>> = + self.observers.borrow().iter() + .map(|o| DOMPerformanceObserver::new(&self.global(), + o.observer.callback(), + o.observer.entries())) + .collect(); + + // Step 7.3. + for o in observers.iter() { + o.notify(); + } + } +} + +pub struct NotifyPerformanceObserverRunnable { + owner: Trusted<Performance>, +} + +impl NotifyPerformanceObserverRunnable { + pub fn new(owner: Trusted<Performance>) -> Self { + NotifyPerformanceObserverRunnable { + owner, + } + } +} + +impl Runnable for NotifyPerformanceObserverRunnable { + fn name(&self) -> &'static str { "NotifyPerformanceObserverRunnable" } + + fn main_thread_handler(self: Box<NotifyPerformanceObserverRunnable>, + _: &ScriptThread) { + self.owner.root().notify_observers(); + } } impl PerformanceMethods for Performance { @@ -55,4 +230,20 @@ impl PerformanceMethods for Performance { let now = (time::precise_time_ns() as f64 - nav_start) / 1000000 as f64; Finite::wrap(now) } + + // https://www.w3.org/TR/performance-timeline-2/#dom-performance-getentries + fn GetEntries(&self) -> Vec<Root<PerformanceEntry>> { + self.entries.borrow().get_entries() + } + + // https://www.w3.org/TR/performance-timeline-2/#dom-performance-getentriesbytype + fn GetEntriesByType(&self, entry_type: DOMString) -> Vec<Root<PerformanceEntry>> { + self.entries.borrow().get_entries_by_type(entry_type) + } + + // https://www.w3.org/TR/performance-timeline-2/#dom-performance-getentriesbyname + fn GetEntriesByName(&self, name: DOMString, entry_type: Option<DOMString>) + -> Vec<Root<PerformanceEntry>> { + self.entries.borrow().get_entries_by_name(name, entry_type) + } } |