diff options
author | bors-servo <lbergstrom+bors@mozilla.com> | 2017-08-17 12:27:19 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-08-17 12:27:19 -0500 |
commit | 6988c7424ddfdd9a98e6a458ff2ad307a74237aa (patch) | |
tree | 6725b4cf17157fcec74f0c4a327394f563a01a91 /components/script/dom/performance.rs | |
parent | cc86ca2bcdec5e89ee5279085ea38db63ef41af9 (diff) | |
parent | 52348f1fccea3e4abc2ad80d001a43f5f51cbdc8 (diff) | |
download | servo-6988c7424ddfdd9a98e6a458ff2ad307a74237aa.tar.gz servo-6988c7424ddfdd9a98e6a458ff2ad307a74237aa.zip |
Auto merge of #18028 - ferjm:performance.timeline, r=jdm
Performance Timeline API
[Performance Timeline API](https://www.w3.org/TR/performance-timeline-2/) implementation.
This API is required to allow DOM access to the [Paint Timing API](https://wicg.github.io/paint-timing/#example) metrics implemented in #17256. Unfortunately, I couldn't test it properly, as its usage depends on other APIs like the Paint Timing, User Timing, Resource Timing or Server Timing APIs. I'll work in the integration with the Paint Timing API next.
- [X] `./mach build -d` does not report any errors
- [X] `./mach test-tidy` does not report any errors
- [ ] There are [WPTs](https://github.com/servo/servo/tree/master/tests/wpt/web-platform-tests/performance-timeline) for this API, however they depend on the implementation of the User Timing and the Resource Timing APIs, which I'll hopefully be implementing soon.
<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/18028)
<!-- Reviewable:end -->
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) + } } |