diff --git a/components/script/dom/macros.rs b/components/script/dom/macros.rs index e49857684e1..075caf04c5f 100644 --- a/components/script/dom/macros.rs +++ b/components/script/dom/macros.rs @@ -595,3 +595,42 @@ macro_rules! rooted_vec { let mut $name = $crate::dom::bindings::trace::RootedVec::from_iter(&mut root, $iter); } } + +/// DOM struct implementation for simple interfaces inheriting from PerformanceEntry. +macro_rules! impl_performance_entry_struct( + ($binding:ident, $struct:ident, $type:expr) => ( + use dom::bindings::codegen::Bindings::$binding; + use dom::bindings::js::Root; + use dom::bindings::reflector::reflect_dom_object; + use dom::bindings::str::DOMString; + use dom::globalscope::GlobalScope; + use dom::performanceentry::PerformanceEntry; + use dom_struct::dom_struct; + + #[dom_struct] + pub struct $struct { + entry: PerformanceEntry, + } + + impl $struct { + fn new_inherited(name: DOMString, start_time: f64, duration: f64) + -> $struct { + $struct { + entry: PerformanceEntry::new_inherited(name, + DOMString::from($type), + start_time, + duration) + } + } + + #[allow(unrooted_must_root)] + pub fn new(global: &GlobalScope, + name: DOMString, + start_time: f64, + duration: f64) -> Root<$struct> { + let entry = $struct::new_inherited(name, start_time, duration); + reflect_dom_object(box entry, global, $binding::Wrap) + } + } + ); +); diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs index db065419b2b..a088b1ae96a 100644 --- a/components/script/dom/mod.rs +++ b/components/script/dom/mod.rs @@ -399,6 +399,8 @@ pub mod paintsize; pub mod paintworkletglobalscope; pub mod performance; pub mod performanceentry; +pub mod performancemark; +pub mod performancemeasure; pub mod performanceobserver; pub mod performanceobserverentrylist; pub mod performancepainttiming; diff --git a/components/script/dom/performance.rs b/components/script/dom/performance.rs index 311e1ecaa3b..8e85543f8e3 100644 --- a/components/script/dom/performance.rs +++ b/components/script/dom/performance.rs @@ -6,6 +6,7 @@ use dom::bindings::cell::DOMRefCell; use dom::bindings::codegen::Bindings::PerformanceBinding; use dom::bindings::codegen::Bindings::PerformanceBinding::{DOMHighResTimeStamp, PerformanceMethods}; use dom::bindings::codegen::Bindings::PerformanceBinding::PerformanceEntryList as DOMPerformanceEntryList; +use dom::bindings::error::{Error, Fallible}; use dom::bindings::inheritance::Castable; use dom::bindings::js::{JS, Root}; use dom::bindings::num::Finite; @@ -13,6 +14,8 @@ use dom::bindings::reflector::{DomObject, Reflector, reflect_dom_object}; use dom::bindings::str::DOMString; use dom::globalscope::GlobalScope; use dom::performanceentry::PerformanceEntry; +use dom::performancemark::PerformanceMark; +use dom::performancemeasure::PerformanceMeasure; use dom::performanceobserver::PerformanceObserver as DOMPerformanceObserver; use dom::performancetiming::PerformanceTiming; use dom::window::Window; @@ -21,6 +24,30 @@ use std::cell::Cell; use std::cmp::Ordering; use time; +const INVALID_ENTRY_NAMES: &'static [&'static str] = &[ + "navigationStart", + "unloadEventStart", + "unloadEventEnd", + "redirectStart", + "redirectEnd", + "fetchStart", + "domainLookupStart", + "domainLookupEnd", + "connectStart", + "connectEnd", + "secureConnectionStart", + "requestStart", + "responseStart", + "responseEnd", + "domLoading", + "domInteractive", + "domContentLoadedEventStart", + "domContentLoadedEventEnd", + "domComplete", + "loadEventStart", + "loadEventEnd", +]; + /// Implementation of a list of PerformanceEntry items shared by the /// Performance and PerformanceObserverEntryList interfaces implementations. #[derive(HeapSizeOf, JSTraceable)] @@ -44,6 +71,25 @@ impl PerformanceEntryList { res.sort_by(|a, b| a.start_time().partial_cmp(&b.start_time()).unwrap_or(Ordering::Equal)); res } + + pub fn clear_entries_by_name_and_type(&mut self, name: Option, + entry_type: Option) { + self.entries.retain(|e| + name.as_ref().map_or(true, |name_| *e.name() == *name_) && + entry_type.as_ref().map_or(true, |type_| *e.entry_type() == *type_) + ); + } + + fn get_last_entry_start_time_with_name_and_type(&self, name: DOMString, + entry_type: DOMString) -> f64 { + match self.entries.iter() + .rev() + .find(|e| *e.entry_type() == *entry_type && + *e.name() == *name) { + Some(entry) => entry.start_time(), + None => 0., + } + } } impl IntoIterator for PerformanceEntryList { @@ -145,9 +191,6 @@ impl Performance { /// /// 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. @@ -202,6 +245,14 @@ impl Performance { o.notify(); } } + + fn now(&self) -> f64 { + let nav_start = match self.timing { + Some(ref timing) => timing.navigation_start_precise(), + None => self.navigation_start_precise, + }; + (time::precise_time_ns() as f64 - nav_start) / 1000000 as f64 + } } impl PerformanceMethods for Performance { @@ -215,12 +266,7 @@ impl PerformanceMethods for Performance { // https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/HighResolutionTime/Overview.html#dom-performance-now fn Now(&self) -> DOMHighResTimeStamp { - let nav_start = match self.timing { - Some(ref timing) => timing.navigation_start_precise(), - None => self.navigation_start_precise, - }; - let now = (time::precise_time_ns() as f64 - nav_start) / 1000000 as f64; - Finite::wrap(now) + Finite::wrap(self.now()) } // https://www.w3.org/TR/performance-timeline-2/#dom-performance-getentries @@ -238,4 +284,72 @@ impl PerformanceMethods for Performance { -> Vec> { self.entries.borrow().get_entries_by_name_and_type(Some(name), entry_type) } + + // https://w3c.github.io/user-timing/#dom-performance-mark + fn Mark(&self, mark_name: DOMString) -> Fallible<()> { + let global = self.global(); + // Step 1. + if global.is::() && INVALID_ENTRY_NAMES.contains(&mark_name.as_ref()) { + return Err(Error::Syntax); + } + + // Steps 2 to 6. + let entry = PerformanceMark::new(&global, + mark_name, + self.now(), + 0.); + // Steps 7 and 8. + self.queue_entry(&entry.upcast::(), + true /* buffer performance entry */); + + // Step 9. + Ok(()) + } + + // https://w3c.github.io/user-timing/#dom-performance-clearmarks + fn ClearMarks(&self, mark_name: Option) { + self.entries.borrow_mut().clear_entries_by_name_and_type(mark_name, + Some(DOMString::from("mark"))); + } + + // https://w3c.github.io/user-timing/#dom-performance-measure + fn Measure(&self, + measure_name: DOMString, + start_mark: Option, + end_mark: Option) -> Fallible<()> { + // Steps 1 and 2. + let end_time = match end_mark { + Some(name) => + self.entries.borrow().get_last_entry_start_time_with_name_and_type( + DOMString::from("mark"), name), + None => self.now(), + }; + + // Step 3. + let start_time = match start_mark { + Some(name) => + self.entries.borrow().get_last_entry_start_time_with_name_and_type( + DOMString::from("mark"), name), + None => 0., + }; + + // Steps 4 to 8. + let entry = PerformanceMeasure::new(&self.global(), + measure_name, + start_time, + end_time - start_time); + + // Step 9 and 10. + self.queue_entry(&entry.upcast::(), + true /* buffer performance entry */); + + // Step 11. + Ok(()) + } + + // https://w3c.github.io/user-timing/#dom-performance-clearmeasures + fn ClearMeasures(&self, measure_name: Option) { + self.entries.borrow_mut().clear_entries_by_name_and_type(measure_name, + Some(DOMString::from("measure"))); + } } diff --git a/components/script/dom/performancemark.rs b/components/script/dom/performancemark.rs new file mode 100644 index 00000000000..36208a98645 --- /dev/null +++ b/components/script/dom/performancemark.rs @@ -0,0 +1,7 @@ +/* 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/. */ + +impl_performance_entry_struct!(PerformanceMarkBinding, + PerformanceMark, + "mark"); diff --git a/components/script/dom/performancemeasure.rs b/components/script/dom/performancemeasure.rs new file mode 100644 index 00000000000..79b185e2e25 --- /dev/null +++ b/components/script/dom/performancemeasure.rs @@ -0,0 +1,7 @@ +/* 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/. */ + +impl_performance_entry_struct!(PerformanceMeasureBinding, + PerformanceMeasure, + "measure"); diff --git a/components/script/dom/performanceobserver.rs b/components/script/dom/performanceobserver.rs index 6a93270971e..24bf84ffd5c 100644 --- a/components/script/dom/performanceobserver.rs +++ b/components/script/dom/performanceobserver.rs @@ -22,8 +22,8 @@ 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 + "mark", // User Timing API + "measure", // User Timing API // "resource", XXX Resource Timing API // "server", XXX Server Timing API "paint", // Paint Timing API diff --git a/components/script/dom/webidls/Performance.webidl b/components/script/dom/webidls/Performance.webidl index aa012b05b25..b2ecb7c4e83 100644 --- a/components/script/dom/webidls/Performance.webidl +++ b/components/script/dom/webidls/Performance.webidl @@ -28,3 +28,14 @@ partial interface Performance { PerformanceEntryList getEntriesByName(DOMString name, optional DOMString type); }; + +// https://w3c.github.io/user-timing/#extensions-performance-interface +[Exposed=(Window,Worker)] +partial interface Performance { + [Throws] + void mark(DOMString markName); + void clearMarks(optional DOMString markName); + [Throws] + void measure(DOMString measureName, optional DOMString startMark, optional DOMString endMark); + void clearMeasures(optional DOMString measureName); +}; diff --git a/components/script/dom/webidls/PerformanceMark.webidl b/components/script/dom/webidls/PerformanceMark.webidl new file mode 100644 index 00000000000..62a57b8971d --- /dev/null +++ b/components/script/dom/webidls/PerformanceMark.webidl @@ -0,0 +1,11 @@ +/* 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/user-timing/#performancemark + */ + +[Exposed=(Window,Worker)] +interface PerformanceMark : PerformanceEntry { +}; diff --git a/components/script/dom/webidls/PerformanceMeasure.webidl b/components/script/dom/webidls/PerformanceMeasure.webidl new file mode 100644 index 00000000000..27c238ec15b --- /dev/null +++ b/components/script/dom/webidls/PerformanceMeasure.webidl @@ -0,0 +1,11 @@ +/* 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/user-timing/#performancemeasure + */ + +[Exposed=(Window,Worker)] +interface PerformanceMeasure : PerformanceEntry { +}; diff --git a/tests/wpt/include.ini b/tests/wpt/include.ini index 82c87177c84..8d2487b7c19 100644 --- a/tests/wpt/include.ini +++ b/tests/wpt/include.ini @@ -45,6 +45,8 @@ skip: true skip: true [script_scheduling] skip: false +[performance-timeline] + skip: false [quirks-mode] skip: false [referrer-policy] diff --git a/tests/wpt/metadata/performance-timeline/case-sensitivity.any.js.ini b/tests/wpt/metadata/performance-timeline/case-sensitivity.any.js.ini new file mode 100644 index 00000000000..b86734a2075 --- /dev/null +++ b/tests/wpt/metadata/performance-timeline/case-sensitivity.any.js.ini @@ -0,0 +1,17 @@ +[case-sensitivity.any.worker.html] + type: testharness + [getEntriesByType values are case sensitive] + expected: FAIL + + [getEntriesByName values are case sensitive] + expected: FAIL + + +[case-sensitivity.any.html] + type: testharness + [getEntriesByType values are case sensitive] + expected: FAIL + + [getEntriesByName values are case sensitive] + expected: FAIL + diff --git a/tests/wpt/metadata/performance-timeline/po-disconnect.any.js.ini b/tests/wpt/metadata/performance-timeline/po-disconnect.any.js.ini new file mode 100644 index 00000000000..53a4c5c6b3e --- /dev/null +++ b/tests/wpt/metadata/performance-timeline/po-disconnect.any.js.ini @@ -0,0 +1,13 @@ +[po-disconnect.any.html] + type: testharness + expected: TIMEOUT + [An observer disconnected after a mark must receive the mark] + expected: TIMEOUT + + +[po-disconnect.any.worker.html] + type: testharness + expected: TIMEOUT + [An observer disconnected after a mark must receive the mark] + expected: TIMEOUT + diff --git a/tests/wpt/metadata/performance-timeline/po-navigation.html.ini b/tests/wpt/metadata/performance-timeline/po-navigation.html.ini new file mode 100644 index 00000000000..70b78ee4042 --- /dev/null +++ b/tests/wpt/metadata/performance-timeline/po-navigation.html.ini @@ -0,0 +1,5 @@ +[po-navigation.html] + type: testharness + [navigation entry is observable] + expected: FAIL + diff --git a/tests/wpt/metadata/performance-timeline/po-observe.any.js.ini b/tests/wpt/metadata/performance-timeline/po-observe.any.js.ini new file mode 100644 index 00000000000..245c7e6cc7d --- /dev/null +++ b/tests/wpt/metadata/performance-timeline/po-observe.any.js.ini @@ -0,0 +1,11 @@ +[po-observe.any.worker.html] + type: testharness + [replace observer if already present] + expected: FAIL + + +[po-observe.any.html] + type: testharness + [replace observer if already present] + expected: FAIL + diff --git a/tests/wpt/metadata/performance-timeline/po-resource.html.ini b/tests/wpt/metadata/performance-timeline/po-resource.html.ini new file mode 100644 index 00000000000..a2c15bd301b --- /dev/null +++ b/tests/wpt/metadata/performance-timeline/po-resource.html.ini @@ -0,0 +1,5 @@ +[po-resource.html] + type: testharness + [resource entries are observable] + expected: FAIL + diff --git a/tests/wpt/mozilla/meta/MANIFEST.json b/tests/wpt/mozilla/meta/MANIFEST.json index 91dc5cf0e16..75ef514e307 100644 --- a/tests/wpt/mozilla/meta/MANIFEST.json +++ b/tests/wpt/mozilla/meta/MANIFEST.json @@ -27988,7 +27988,7 @@ "testharness" ], "mozilla/interfaces.html": [ - "a7ed38b6069caffd87d9573978e6ef9938d87753", + "66e66d3bdfa3bebd5bf3d711c0737fb394efe973", "testharness" ], "mozilla/interfaces.js": [ @@ -27996,7 +27996,7 @@ "support" ], "mozilla/interfaces.worker.js": [ - "fb5537ec0753e3a1c56dff957bcc1c0660dcb7b9", + "c9dcc4f24540914b3be1ef18f13b721773eb76be", "testharness" ], "mozilla/iterable.html": [ diff --git a/tests/wpt/mozilla/tests/mozilla/interfaces.html b/tests/wpt/mozilla/tests/mozilla/interfaces.html index 5183b0a11ea..6fa817e559d 100644 --- a/tests/wpt/mozilla/tests/mozilla/interfaces.html +++ b/tests/wpt/mozilla/tests/mozilla/interfaces.html @@ -161,6 +161,8 @@ test_interfaces([ "PageTransitionEvent", "Performance", "PerformanceEntry", + "PerformanceMark", + "PerformanceMeasure", "PerformanceObserver", "PerformanceObserverEntryList", "PerformancePaintTiming", diff --git a/tests/wpt/mozilla/tests/mozilla/interfaces.worker.js b/tests/wpt/mozilla/tests/mozilla/interfaces.worker.js index fe1732b4a24..926ae2e1792 100644 --- a/tests/wpt/mozilla/tests/mozilla/interfaces.worker.js +++ b/tests/wpt/mozilla/tests/mozilla/interfaces.worker.js @@ -34,6 +34,8 @@ test_interfaces([ "MessageEvent", "Performance", "PerformanceEntry", + "PerformanceMark", + "PerformanceMeasure", "PerformanceObserver", "PerformanceObserverEntryList", "PerformancePaintTiming",