diff options
18 files changed, 599 insertions, 7 deletions
diff --git a/components/script/dom/bindings/str.rs b/components/script/dom/bindings/str.rs index 761acab3068..36b104eb262 100644 --- a/components/script/dom/bindings/str.rs +++ b/components/script/dom/bindings/str.rs @@ -122,7 +122,7 @@ pub fn is_token(s: &[u8]) -> bool { /// /// [idl]: https://heycam.github.io/webidl/#idl-DOMString /// -/// Cenceptually, a DOMString has the same value space as a JavaScript String, +/// Conceptually, a DOMString has the same value space as a JavaScript String, /// i.e., an array of 16-bit *code units* representing UTF-16, potentially with /// unpaired surrogates present (also sometimes called WTF-16). /// diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs index fe17bb712d7..a3b280668e3 100644 --- a/components/script/dom/mod.rs +++ b/components/script/dom/mod.rs @@ -397,6 +397,9 @@ pub mod paintrenderingcontext2d; pub mod paintsize; pub mod paintworkletglobalscope; pub mod performance; +pub mod performanceentry; +pub mod performanceobserver; +pub mod performanceobserverentrylist; pub mod performancetiming; pub mod permissions; pub mod permissionstatus; 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) + } } diff --git a/components/script/dom/performanceentry.rs b/components/script/dom/performanceentry.rs new file mode 100644 index 00000000000..7de1116c320 --- /dev/null +++ b/components/script/dom/performanceentry.rs @@ -0,0 +1,76 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * 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::codegen::Bindings::PerformanceEntryBinding; +use dom::bindings::codegen::Bindings::PerformanceEntryBinding::PerformanceEntryMethods; +use dom::bindings::js::Root; +use dom::bindings::num::Finite; +use dom::bindings::reflector::{Reflector, reflect_dom_object}; +use dom::bindings::str::DOMString; +use dom::globalscope::GlobalScope; +use dom_struct::dom_struct; + +#[dom_struct] +pub struct PerformanceEntry { + reflector_: Reflector, + name: DOMString, + entry_type: DOMString, + start_time: f64, + duration: f64, +} + +impl PerformanceEntry { + fn new_inherited(name: DOMString, + entry_type: DOMString, + start_time: f64, + duration: f64) -> PerformanceEntry { + PerformanceEntry { + reflector_: Reflector::new(), + name, + entry_type, + start_time, + duration, + } + } + + #[allow(unrooted_must_root)] + pub fn new(global: &GlobalScope, + name: DOMString, + entry_type: DOMString, + start_time: f64, + duration: f64) -> Root<PerformanceEntry> { + let entry = PerformanceEntry::new_inherited(name, entry_type, start_time, duration); + reflect_dom_object(box entry, global, PerformanceEntryBinding::Wrap) + } + + pub fn entry_type(&self) -> &DOMString { + &self.entry_type + } + + pub fn name(&self) -> &DOMString { + &self.name + } +} + +impl PerformanceEntryMethods for PerformanceEntry { + // https://w3c.github.io/performance-timeline/#dom-performanceentry-name + fn Name(&self) -> DOMString { + DOMString::from(self.name.clone()) + } + + // https://w3c.github.io/performance-timeline/#dom-performanceentry-entrytype + fn EntryType(&self) -> DOMString { + DOMString::from(self.entry_type.clone()) + } + + // https://w3c.github.io/performance-timeline/#dom-performanceentry-starttime + fn StartTime(&self) -> Finite<f64> { + Finite::wrap(self.start_time) + } + + // https://w3c.github.io/performance-timeline/#dom-performanceentry-duration + fn Duration(&self) -> Finite<f64> { + Finite::wrap(self.duration) + } +} diff --git a/components/script/dom/performanceobserver.rs b/components/script/dom/performanceobserver.rs new file mode 100644 index 00000000000..e0d2e49a57b --- /dev/null +++ b/components/script/dom/performanceobserver.rs @@ -0,0 +1,117 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * 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::callback::ExceptionHandling; +use dom::bindings::cell::DOMRefCell; +use dom::bindings::codegen::Bindings::PerformanceBinding::PerformanceEntryList as DOMPerformanceEntryList; +use dom::bindings::codegen::Bindings::PerformanceObserverBinding; +use dom::bindings::codegen::Bindings::PerformanceObserverBinding::PerformanceObserverCallback; +use dom::bindings::codegen::Bindings::PerformanceObserverBinding::PerformanceObserverInit; +use dom::bindings::codegen::Bindings::PerformanceObserverBinding::PerformanceObserverMethods; +use dom::bindings::codegen::Bindings::WindowBinding::WindowBinding::WindowMethods; +use dom::bindings::error::{Error, Fallible}; +use dom::bindings::js::Root; +use dom::bindings::reflector::{DomObject, Reflector, reflect_dom_object}; +use dom::bindings::str::DOMString; +use dom::globalscope::GlobalScope; +use dom::performance::PerformanceEntryList; +use dom::performanceentry::PerformanceEntry; +use dom::performanceobserverentrylist::PerformanceObserverEntryList; +use dom_struct::dom_struct; +use std::rc::Rc; + +/// List of allowed performance entry types. +const VALID_ENTRY_TYPES: &'static [&'static str] = &[ + // "mark", XXX User Timing API + // "measure", XXX User Timing API + // "resource", XXX Resource Timing API + // "server", XXX Server Timing API + // "paint", XXX Paint Timing API +]; + +#[dom_struct] +pub struct PerformanceObserver { + reflector_: Reflector, + #[ignore_heap_size_of = "can't measure Rc values"] + callback: Rc<PerformanceObserverCallback>, + entries: DOMRefCell<DOMPerformanceEntryList>, +} + +impl PerformanceObserver { + fn new_inherited(callback: Rc<PerformanceObserverCallback>, + entries: DOMRefCell<DOMPerformanceEntryList>) + -> PerformanceObserver { + PerformanceObserver { + reflector_: Reflector::new(), + callback, + entries, + } + } + + #[allow(unrooted_must_root)] + pub fn new(global: &GlobalScope, + callback: Rc<PerformanceObserverCallback>, + entries: DOMPerformanceEntryList) + -> Root<PerformanceObserver> { + let observer = PerformanceObserver::new_inherited(callback, DOMRefCell::new(entries)); + reflect_dom_object(box observer, global, PerformanceObserverBinding::Wrap) + } + + pub fn Constructor(global: &GlobalScope, callback: Rc<PerformanceObserverCallback>) + -> Fallible<Root<PerformanceObserver>> { + Ok(PerformanceObserver::new(global, callback, Vec::new())) + } + + /// Buffer a new performance entry. + pub fn queue_entry(&self, entry: &PerformanceEntry) { + self.entries.borrow_mut().push(Root::from_ref(entry)); + } + + /// 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() { + 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 _ = self.callback.Call__(&observer_entry_list, self, ExceptionHandling::Report); + } + + pub fn callback(&self) -> Rc<PerformanceObserverCallback> { + self.callback.clone() + } + + pub fn entries(&self) -> DOMPerformanceEntryList { + self.entries.borrow().clone() + } +} + +impl PerformanceObserverMethods for PerformanceObserver { + // https://w3c.github.io/performance-timeline/#dom-performanceobserver-observe() + fn Observe(&self, options: &PerformanceObserverInit) -> Fallible<()> { + // 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>>(); + // There must be at least one valid entry type. + if entry_types.is_empty() { + return Err((Error::Type("entryTypes cannot be empty".to_string()))); + } + + let performance = self.global().as_window().Performance(); + performance.add_observer(self, entry_types); + Ok(()) + } + + // https://w3c.github.io/performance-timeline/#dom-performanceobserver-disconnect() + fn Disconnect(&self) { + self.global().as_window().Performance().remove_observer(self); + self.entries.borrow_mut().clear(); + } +} diff --git a/components/script/dom/performanceobserverentrylist.rs b/components/script/dom/performanceobserverentrylist.rs new file mode 100644 index 00000000000..9c6a039cd4c --- /dev/null +++ b/components/script/dom/performanceobserverentrylist.rs @@ -0,0 +1,54 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * 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::PerformanceObserverEntryListBinding; +use dom::bindings::codegen::Bindings::PerformanceObserverEntryListBinding::PerformanceObserverEntryListMethods; +use dom::bindings::js::Root; +use dom::bindings::reflector::{Reflector, reflect_dom_object}; +use dom::bindings::str::DOMString; +use dom::globalscope::GlobalScope; +use dom::performance::PerformanceEntryList; +use dom::performanceentry::PerformanceEntry; +use dom_struct::dom_struct; + +#[dom_struct] +pub struct PerformanceObserverEntryList { + reflector_: Reflector, + entries: DOMRefCell<PerformanceEntryList>, +} + +impl PerformanceObserverEntryList { + fn new_inherited(entries: PerformanceEntryList) -> PerformanceObserverEntryList { + PerformanceObserverEntryList { + reflector_: Reflector::new(), + entries: DOMRefCell::new(entries), + } + } + + #[allow(unrooted_must_root)] + pub fn new(global: &GlobalScope, entries: PerformanceEntryList) + -> Root<PerformanceObserverEntryList> { + let observer_entry_list = PerformanceObserverEntryList::new_inherited(entries); + reflect_dom_object(box observer_entry_list, global, PerformanceObserverEntryListBinding::Wrap) + } +} + +impl PerformanceObserverEntryListMethods for PerformanceObserverEntryList { + // https://w3c.github.io/performance-timeline/#dom-performanceobserver + fn GetEntries(&self) -> Vec<Root<PerformanceEntry>> { + self.entries.borrow().get_entries() + } + + // https://w3c.github.io/performance-timeline/#dom-performanceobserver + fn GetEntriesByType(&self, entry_type: DOMString) -> Vec<Root<PerformanceEntry>> { + self.entries.borrow().get_entries_by_type(entry_type) + } + + // https://w3c.github.io/performance-timeline/#dom-performanceobserver + fn GetEntriesByName(&self, name: DOMString, entry_type: Option<DOMString>) + -> Vec<Root<PerformanceEntry>> { + self.entries.borrow().get_entries_by_name(name, entry_type) + } +} diff --git a/components/script/dom/webidls/Performance.webidl b/components/script/dom/webidls/Performance.webidl index 6aeaa6d11aa..30f2c41e2bc 100644 --- a/components/script/dom/webidls/Performance.webidl +++ b/components/script/dom/webidls/Performance.webidl @@ -7,6 +7,7 @@ */ typedef double DOMHighResTimeStamp; +typedef sequence<PerformanceEntry> PerformanceEntryList; [Exposed=(Window,Worker)] interface Performance { @@ -17,3 +18,11 @@ interface Performance { partial interface Performance { DOMHighResTimeStamp now(); }; + +// https://w3c.github.io/performance-timeline/#extensions-to-the-performance-interface +partial interface Performance { + PerformanceEntryList getEntries(); + PerformanceEntryList getEntriesByType(DOMString type); + PerformanceEntryList getEntriesByName(DOMString name, + optional DOMString type); +}; diff --git a/components/script/dom/webidls/PerformanceEntry.webidl b/components/script/dom/webidls/PerformanceEntry.webidl new file mode 100644 index 00000000000..8980f769888 --- /dev/null +++ b/components/script/dom/webidls/PerformanceEntry.webidl @@ -0,0 +1,17 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * 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/. + * + * The origin of this IDL file is + * https://w3c.github.io/performance-timeline/#the-performanceentry-interface + */ + +[Exposed=(Window,Worker)] +interface PerformanceEntry { + readonly attribute DOMString name; + readonly attribute DOMString entryType; + readonly attribute DOMHighResTimeStamp startTime; + readonly attribute DOMHighResTimeStamp duration; + + // [Default] object toJSON(); +}; diff --git a/components/script/dom/webidls/PerformanceObserver.webidl b/components/script/dom/webidls/PerformanceObserver.webidl new file mode 100644 index 00000000000..a67c52e2730 --- /dev/null +++ b/components/script/dom/webidls/PerformanceObserver.webidl @@ -0,0 +1,21 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * 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/. + * + * The origin of this IDL file is + * https://w3c.github.io/performance-timeline/#the-performanceobserver-interface + */ + +dictionary PerformanceObserverInit { + required sequence<DOMString> entryTypes; +}; + +callback PerformanceObserverCallback = void (PerformanceObserverEntryList entries, PerformanceObserver observer); + +[Constructor(PerformanceObserverCallback callback), + Exposed=(Window,Worker)] +interface PerformanceObserver { + [Throws] + void observe(PerformanceObserverInit options); + void disconnect(); +}; diff --git a/components/script/dom/webidls/PerformanceObserverEntryList.webidl b/components/script/dom/webidls/PerformanceObserverEntryList.webidl new file mode 100644 index 00000000000..a65316cdacc --- /dev/null +++ b/components/script/dom/webidls/PerformanceObserverEntryList.webidl @@ -0,0 +1,15 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * 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/. + * + * The origin of this IDL file is + * https://w3c.github.io/performance-timeline/#performanceobserverentrylist-interface + */ + +[Exposed=(Window,Worker)] +interface PerformanceObserverEntryList { + PerformanceEntryList getEntries(); + PerformanceEntryList getEntriesByType(DOMString entryType); + PerformanceEntryList getEntriesByName(DOMString name, + optional DOMString entryType); +}; diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index 2da650ee1ee..48677a64d38 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -118,6 +118,7 @@ use task_source::dom_manipulation::DOMManipulationTaskSource; use task_source::file_reading::FileReadingTaskSource; use task_source::history_traversal::HistoryTraversalTaskSource; use task_source::networking::NetworkingTaskSource; +use task_source::performance_timeline::PerformanceTimelineTaskSource; use task_source::user_interaction::UserInteractionTaskSource; use time; use timers::{IsInterval, TimerCallback}; @@ -175,6 +176,8 @@ pub struct Window { history_traversal_task_source: HistoryTraversalTaskSource, #[ignore_heap_size_of = "task sources are hard"] file_reading_task_source: FileReadingTaskSource, + #[ignore_heap_size_of = "task sources are hard"] + performance_timeline_task_source: PerformanceTimelineTaskSource, navigator: MutNullableJS<Navigator>, #[ignore_heap_size_of = "Arc"] image_cache: Arc<ImageCache>, @@ -329,6 +332,10 @@ impl Window { self.file_reading_task_source.clone() } + pub fn performance_timeline_task_source(&self) -> PerformanceTimelineTaskSource { + self.performance_timeline_task_source.clone() + } + pub fn main_thread_script_chan(&self) -> &Sender<MainThreadScriptMsg> { &self.script_chan.0 } @@ -1791,6 +1798,7 @@ impl Window { network_task_source: NetworkingTaskSource, history_task_source: HistoryTraversalTaskSource, file_task_source: FileReadingTaskSource, + performance_timeline_task_source: PerformanceTimelineTaskSource, image_cache_chan: Sender<ImageCacheMsg>, image_cache: Arc<ImageCache>, resource_threads: ResourceThreads, @@ -1839,6 +1847,7 @@ impl Window { networking_task_source: network_task_source, history_traversal_task_source: history_task_source, file_reading_task_source: file_task_source, + performance_timeline_task_source, image_cache_chan: image_cache_chan, image_cache: image_cache.clone(), navigator: Default::default(), diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index 4a05002637a..85874ece2ca 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -115,6 +115,7 @@ use task_source::dom_manipulation::{DOMManipulationTask, DOMManipulationTaskSour use task_source::file_reading::FileReadingTaskSource; use task_source::history_traversal::HistoryTraversalTaskSource; use task_source::networking::NetworkingTaskSource; +use task_source::performance_timeline::{PerformanceTimelineTask, PerformanceTimelineTaskSource}; use task_source::user_interaction::{UserInteractionTask, UserInteractionTaskSource}; use time::{get_time, precise_time_ns, Tm}; use url::Position; @@ -270,6 +271,8 @@ pub enum MainThreadScriptMsg { /// Notifies the script thread that a new worklet has been loaded, and thus the page should be /// reflowed. WorkletLoaded(PipelineId), + /// Tasks that originate from the performance timeline task source. + PerformanceTimeline(PerformanceTimelineTask), } impl OpaqueSender<CommonScriptMsg> for Box<ScriptChan + Send> { @@ -455,6 +458,8 @@ pub struct ScriptThread { file_reading_task_source: FileReadingTaskSource, + performance_timeline_task_source: PerformanceTimelineTaskSource, + /// A channel to hand out to threads that need to respond to a message from the script thread. control_chan: IpcSender<ConstellationControlMsg>, @@ -854,8 +859,9 @@ impl ScriptThread { dom_manipulation_task_source: DOMManipulationTaskSource(chan.clone()), user_interaction_task_source: UserInteractionTaskSource(chan.clone()), networking_task_source: NetworkingTaskSource(boxed_script_sender.clone()), - history_traversal_task_source: HistoryTraversalTaskSource(chan), + history_traversal_task_source: HistoryTraversalTaskSource(chan.clone()), file_reading_task_source: FileReadingTaskSource(boxed_script_sender), + performance_timeline_task_source: PerformanceTimelineTaskSource(chan), control_chan: state.control_chan, control_port: control_port, @@ -1286,6 +1292,8 @@ impl ScriptThread { self.handle_worklet_loaded(pipeline_id), MainThreadScriptMsg::DOMManipulation(task) => task.handle_task(self), + MainThreadScriptMsg::PerformanceTimeline(task) => + task.handle_task(self), MainThreadScriptMsg::UserInteraction(task) => task.handle_task(self), } @@ -1717,6 +1725,10 @@ impl ScriptThread { &self.dom_manipulation_task_source } + pub fn performance_timeline_task_source(&self) -> &PerformanceTimelineTaskSource { + &self.performance_timeline_task_source + } + /// Handles a request for the window title. fn handle_get_title_msg(&self, pipeline_id: PipelineId) { let document = match { self.documents.borrow().find_document(pipeline_id) } { @@ -1991,6 +2003,7 @@ impl ScriptThread { let DOMManipulationTaskSource(ref dom_sender) = self.dom_manipulation_task_source; let UserInteractionTaskSource(ref user_sender) = self.user_interaction_task_source; let HistoryTraversalTaskSource(ref history_sender) = self.history_traversal_task_source; + let PerformanceTimelineTaskSource(ref performance_sender) = self.performance_timeline_task_source; let (ipc_timer_event_chan, ipc_timer_event_port) = ipc::channel().unwrap(); ROUTER.route_ipc_receiver_to_mpsc_sender(ipc_timer_event_port, @@ -2015,6 +2028,7 @@ impl ScriptThread { self.networking_task_source.clone(), HistoryTraversalTaskSource(history_sender.clone()), self.file_reading_task_source.clone(), + PerformanceTimelineTaskSource(performance_sender.clone()), self.image_cache_channel.clone(), self.image_cache.clone(), self.resource_threads.clone(), diff --git a/components/script/task_source/mod.rs b/components/script/task_source/mod.rs index 6e2a9985fa0..ff41a63f118 100644 --- a/components/script/task_source/mod.rs +++ b/components/script/task_source/mod.rs @@ -6,6 +6,7 @@ pub mod dom_manipulation; pub mod file_reading; pub mod history_traversal; pub mod networking; +pub mod performance_timeline; pub mod user_interaction; use dom::globalscope::GlobalScope; diff --git a/components/script/task_source/performance_timeline.rs b/components/script/task_source/performance_timeline.rs new file mode 100644 index 00000000000..272d971737d --- /dev/null +++ b/components/script/task_source/performance_timeline.rs @@ -0,0 +1,58 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * 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/. */ + +// XXX The spec says that the performance timeline task source should be +// a low priority task and it should be processed during idle periods. +// We are currently treating this task queue as a normal priority queue. + +use dom::bindings::inheritance::Castable; +use dom::bindings::refcounted::Trusted; +use dom::performance::{NotifyPerformanceObserverRunnable, Performance}; +use dom::window::Window; +use script_thread::{MainThreadScriptMsg, Runnable, RunnableWrapper, ScriptThread}; +use std::fmt; +use std::result::Result; +use std::sync::mpsc::Sender; +use task_source::TaskSource; + +#[derive(JSTraceable, Clone)] +pub struct PerformanceTimelineTaskSource(pub Sender<MainThreadScriptMsg>); + +impl fmt::Debug for PerformanceTimelineTaskSource { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "PerformanceTimelineTaskSource(...)") + } +} + +impl TaskSource for PerformanceTimelineTaskSource { + fn queue_with_wrapper<T>(&self, + msg: Box<T>, + wrapper: &RunnableWrapper) -> Result<(), ()> + where T: Runnable + Send + 'static { + let msg = PerformanceTimelineTask(wrapper.wrap_runnable(msg)); + self.0.send(MainThreadScriptMsg::PerformanceTimeline(msg)).map_err(|_| ()) + } +} + +impl PerformanceTimelineTaskSource { + pub fn queue_notification(&self, owner: &Performance, window: &Window) { + let owner = Trusted::new(owner); + let runnable = box NotifyPerformanceObserverRunnable::new(owner); + let _ = self.queue(runnable, window.upcast()); + } +} + +pub struct PerformanceTimelineTask(pub Box<Runnable + Send>); + +impl fmt::Debug for PerformanceTimelineTask { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "PerformanceTimelineTask(...)") + } +} + +impl PerformanceTimelineTask { + pub fn handle_task(self, script_thread: &ScriptThread) { + self.0.main_thread_handler(script_thread); + } +} diff --git a/tests/wpt/metadata/navigation-timing/nav2_test_document_replaced.html.ini b/tests/wpt/metadata/navigation-timing/nav2_test_document_replaced.html.ini index 123c842b619..ed236342863 100644 --- a/tests/wpt/metadata/navigation-timing/nav2_test_document_replaced.html.ini +++ b/tests/wpt/metadata/navigation-timing/nav2_test_document_replaced.html.ini @@ -1,5 +1,6 @@ [nav2_test_document_replaced.html] type: testharness + expected: TIMEOUT [Navigation Timing 2 WPT] expected: FAIL diff --git a/tests/wpt/mozilla/meta/MANIFEST.json b/tests/wpt/mozilla/meta/MANIFEST.json index a17f094b697..81e261a2eff 100644 --- a/tests/wpt/mozilla/meta/MANIFEST.json +++ b/tests/wpt/mozilla/meta/MANIFEST.json @@ -27276,7 +27276,7 @@ "testharness" ], "mozilla/interfaces.html": [ - "c884fee8603f93099ffd0acc30f0ab0cbee5b5f8", + "7ac46204fb780c96344f166d34d0fb888c9e25c4", "testharness" ], "mozilla/interfaces.js": [ @@ -27284,7 +27284,7 @@ "support" ], "mozilla/interfaces.worker.js": [ - "9b3a3c96ec539bb323a0def92c747996deaa7331", + "1474c6500ce1c4aef99d200dae5407324ddbdd4a", "testharness" ], "mozilla/iterable.html": [ diff --git a/tests/wpt/mozilla/tests/mozilla/interfaces.html b/tests/wpt/mozilla/tests/mozilla/interfaces.html index 33aa76d0481..90c4531467b 100644 --- a/tests/wpt/mozilla/tests/mozilla/interfaces.html +++ b/tests/wpt/mozilla/tests/mozilla/interfaces.html @@ -159,6 +159,9 @@ test_interfaces([ "NodeList", "PageTransitionEvent", "Performance", + "PerformanceEntry", + "PerformanceObserver", + "PerformanceObserverEntryList", "PerformanceTiming", "Plugin", "PluginArray", diff --git a/tests/wpt/mozilla/tests/mozilla/interfaces.worker.js b/tests/wpt/mozilla/tests/mozilla/interfaces.worker.js index 780200349de..e410bb863b0 100644 --- a/tests/wpt/mozilla/tests/mozilla/interfaces.worker.js +++ b/tests/wpt/mozilla/tests/mozilla/interfaces.worker.js @@ -34,6 +34,9 @@ test_interfaces([ "MessageEvent", "Performance", "PerformanceTiming", + "PerformanceEntry", + "PerformanceObserver", + "PerformanceObserverEntryList", "ProgressEvent", "Request", "Response", |