mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
http://www.robohornet.org gives a score of 101.36 on master, and 102.68 with this PR. The latter is slightly better, but probably within noise level. So it looks like this PR does not affect DOM performance. This is expected since `Box::new` is defined as: ```rust impl<T> Box<T> { #[inline(always)] pub fn new(x: T) -> Box<T> { box x } } ``` With inlining, it should compile to the same as box syntax.
364 lines
14 KiB
Rust
364 lines
14 KiB
Rust
/* 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::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::num::Finite;
|
|
use dom::bindings::reflector::{DomObject, Reflector, reflect_dom_object};
|
|
use dom::bindings::root::{Dom, DomRoot};
|
|
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;
|
|
use dom_struct::dom_struct;
|
|
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)]
|
|
pub struct PerformanceEntryList {
|
|
entries: DOMPerformanceEntryList,
|
|
}
|
|
|
|
impl PerformanceEntryList {
|
|
pub fn new(entries: DOMPerformanceEntryList) -> Self {
|
|
PerformanceEntryList {
|
|
entries,
|
|
}
|
|
}
|
|
|
|
pub fn get_entries_by_name_and_type(&self, name: Option<DOMString>, entry_type: Option<DOMString>)
|
|
-> Vec<DomRoot<PerformanceEntry>> {
|
|
let mut res = self.entries.iter().filter(|e|
|
|
name.as_ref().map_or(true, |name_| *e.name() == *name_) &&
|
|
entry_type.as_ref().map_or(true, |type_| *e.entry_type() == *type_)
|
|
).map(|e| e.clone()).collect::<Vec<DomRoot<PerformanceEntry>>>();
|
|
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<DOMString>,
|
|
entry_type: Option<DOMString>) {
|
|
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 {
|
|
type Item = DomRoot<PerformanceEntry>;
|
|
type IntoIter = ::std::vec::IntoIter<DomRoot<PerformanceEntry>>;
|
|
|
|
fn into_iter(self) -> Self::IntoIter {
|
|
self.entries.into_iter()
|
|
}
|
|
}
|
|
|
|
#[derive(HeapSizeOf, JSTraceable)]
|
|
struct PerformanceObserver {
|
|
observer: DomRoot<DOMPerformanceObserver>,
|
|
entry_types: Vec<DOMString>,
|
|
}
|
|
|
|
#[dom_struct]
|
|
pub struct Performance {
|
|
reflector_: Reflector,
|
|
timing: Option<Dom<PerformanceTiming>>,
|
|
entries: DomRefCell<PerformanceEntryList>,
|
|
observers: DomRefCell<Vec<PerformanceObserver>>,
|
|
pending_notification_observers_task: Cell<bool>,
|
|
navigation_start_precise: f64,
|
|
}
|
|
|
|
impl Performance {
|
|
fn new_inherited(global: &GlobalScope,
|
|
navigation_start: u64,
|
|
navigation_start_precise: f64) -> Performance {
|
|
Performance {
|
|
reflector_: Reflector::new(),
|
|
timing: if global.is::<Window>() {
|
|
Some(Dom::from_ref(&*PerformanceTiming::new(global.as_window(),
|
|
navigation_start,
|
|
navigation_start_precise)))
|
|
} else {
|
|
None
|
|
},
|
|
entries: DomRefCell::new(PerformanceEntryList::new(Vec::new())),
|
|
observers: DomRefCell::new(Vec::new()),
|
|
pending_notification_observers_task: Cell::new(false),
|
|
navigation_start_precise,
|
|
}
|
|
}
|
|
|
|
pub fn new(global: &GlobalScope,
|
|
navigation_start: u64,
|
|
navigation_start_precise: f64) -> DomRoot<Performance> {
|
|
reflect_dom_object(
|
|
Box::new(Performance::new_inherited(global, navigation_start, navigation_start_precise)),
|
|
global,
|
|
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>,
|
|
buffered: bool) {
|
|
if buffered {
|
|
let entries = self.entries.borrow();
|
|
let mut new_entries = entry_types.iter()
|
|
.flat_map(|e| entries.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
|
|
// entry types.
|
|
Some(p) => observers[p].entry_types = entry_types,
|
|
// Otherwise, we create and insert the new PerformanceObserver.
|
|
None => observers.push(PerformanceObserver {
|
|
observer: DomRoot::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,
|
|
};
|
|
|
|
if self.pending_notification_observers_task.get() {
|
|
if let Some(o) = observers.iter().nth(index) {
|
|
DOMPerformanceObserver::new(&self.global(),
|
|
o.observer.callback(),
|
|
o.observer.entries()).notify();
|
|
}
|
|
}
|
|
|
|
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
|
|
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(DomRoot::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 task_source = self.global().performance_timeline_task_source();
|
|
task_source.queue_notification(&self.global());
|
|
}
|
|
|
|
/// Observers notifications task.
|
|
///
|
|
/// Algorithm spec (step 7):
|
|
/// https://w3c.github.io/performance-timeline/#queue-a-performanceentry
|
|
pub 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<DomRoot<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();
|
|
}
|
|
}
|
|
|
|
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 {
|
|
// https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/NavigationTiming/Overview.html#performance-timing-attribute
|
|
fn Timing(&self) -> DomRoot<PerformanceTiming> {
|
|
match self.timing {
|
|
Some(ref timing) => DomRoot::from_ref(&*timing),
|
|
None => unreachable!("Are we trying to expose Performance.timing in workers?"),
|
|
}
|
|
}
|
|
|
|
// https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/HighResolutionTime/Overview.html#dom-performance-now
|
|
fn Now(&self) -> DOMHighResTimeStamp {
|
|
Finite::wrap(self.now())
|
|
}
|
|
|
|
// https://www.w3.org/TR/performance-timeline-2/#dom-performance-getentries
|
|
fn GetEntries(&self) -> Vec<DomRoot<PerformanceEntry>> {
|
|
self.entries.borrow().get_entries_by_name_and_type(None, None)
|
|
}
|
|
|
|
// https://www.w3.org/TR/performance-timeline-2/#dom-performance-getentriesbytype
|
|
fn GetEntriesByType(&self, entry_type: DOMString) -> Vec<DomRoot<PerformanceEntry>> {
|
|
self.entries.borrow().get_entries_by_name_and_type(None, Some(entry_type))
|
|
}
|
|
|
|
// https://www.w3.org/TR/performance-timeline-2/#dom-performance-getentriesbyname
|
|
fn GetEntriesByName(&self, name: DOMString, entry_type: Option<DOMString>)
|
|
-> Vec<DomRoot<PerformanceEntry>> {
|
|
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::<Window>() && 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::<PerformanceEntry>(),
|
|
true /* buffer performance entry */);
|
|
|
|
// Step 9.
|
|
Ok(())
|
|
}
|
|
|
|
// https://w3c.github.io/user-timing/#dom-performance-clearmarks
|
|
fn ClearMarks(&self, mark_name: Option<DOMString>) {
|
|
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<DOMString>,
|
|
end_mark: Option<DOMString>) -> 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::<PerformanceEntry>(),
|
|
true /* buffer performance entry */);
|
|
|
|
// Step 11.
|
|
Ok(())
|
|
}
|
|
|
|
// https://w3c.github.io/user-timing/#dom-performance-clearmeasures
|
|
fn ClearMeasures(&self, measure_name: Option<DOMString>) {
|
|
self.entries.borrow_mut().clear_entries_by_name_and_type(measure_name,
|
|
Some(DOMString::from("measure")));
|
|
}
|
|
}
|