aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorbors-servo <lbergstrom+bors@mozilla.com>2020-02-12 11:09:52 -0500
committerGitHub <noreply@github.com>2020-02-12 11:09:52 -0500
commited9b5843443db7164bda6eb6f3cb7caff2ff5a3c (patch)
tree5bd36aa1072679638d1712a526636e92c2bcd397
parent0790c856d5c376a0839347a3f0d4e4f7b07779c6 (diff)
parent5c00acca9828f6d35ecec7b55b2bcb19e1a73fd9 (diff)
downloadservo-ed9b5843443db7164bda6eb6f3cb7caff2ff5a3c.tar.gz
servo-ed9b5843443db7164bda6eb6f3cb7caff2ff5a3c.zip
Auto merge of #25590 - pshaughn:perfobs, r=jdm
Make performance observers take "type" and "buffered" options without panicking. <!-- Please describe your changes on the following line: --> I updated the observe() method to align with spec, and fixed the borrow duration bug @jdm pointed out in #25589 so it wouldn't cause a hard crash. Some tests go from failing to passing, but others go from early failing to later timeout; performance observers still aren't doing the right thing in all cases. --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `___` with appropriate data: --> - [X] `./mach build -d` does not report any errors - [X] `./mach test-tidy` does not report any errors - [X] These changes fix #24781 and fix #25589 and fix #23225 <!-- Either: --> - [X] There are tests for these changes <!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.--> <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. -->
-rw-r--r--components/script/dom/performance.rs65
-rw-r--r--components/script/dom/performanceobserver.rs140
-rw-r--r--components/script/dom/webidls/PerformanceObserver.webidl9
-rw-r--r--tests/wpt/metadata/performance-timeline/buffered-flag-after-timeout.any.js.ini9
-rw-r--r--tests/wpt/metadata/performance-timeline/buffered-flag-observer.any.js.ini9
-rw-r--r--tests/wpt/metadata/performance-timeline/idlharness.any.js.ini18
-rw-r--r--tests/wpt/metadata/performance-timeline/multiple-buffered-flag-observers.any.js.ini11
-rw-r--r--tests/wpt/metadata/performance-timeline/observer-buffered-false.any.js.ini9
-rw-r--r--tests/wpt/metadata/performance-timeline/po-callback-mutate.any.js.ini11
-rw-r--r--tests/wpt/metadata/performance-timeline/po-observe-type.any.js.ini33
-rw-r--r--tests/wpt/metadata/performance-timeline/po-observe.any.js.ini41
-rw-r--r--tests/wpt/metadata/performance-timeline/po-observe.html.ini4
-rw-r--r--tests/wpt/metadata/performance-timeline/po-takeRecords.any.js.ini9
-rw-r--r--tests/wpt/metadata/resource-timing/buffered-flag.any.js.ini8
14 files changed, 171 insertions, 205 deletions
diff --git a/components/script/dom/performance.rs b/components/script/dom/performance.rs
index 33e39303cb4..9de98852df7 100644
--- a/components/script/dom/performance.rs
+++ b/components/script/dom/performance.rs
@@ -185,22 +185,12 @@ impl Performance {
/// Add a PerformanceObserver to the list of observers with a set of
/// observed entry types.
- pub fn add_observer(
+
+ pub fn add_multiple_type_observer(
&self,
observer: &DOMPerformanceObserver,
entry_types: Vec<DOMString>,
- buffered: bool,
) {
- if buffered {
- let buffer = self.buffer.borrow();
- let mut new_entries = entry_types
- .iter()
- .flat_map(|e| buffer.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
@@ -214,6 +204,46 @@ impl Performance {
};
}
+ pub fn add_single_type_observer(
+ &self,
+ observer: &DOMPerformanceObserver,
+ entry_type: &DOMString,
+ buffered: bool,
+ ) {
+ if buffered {
+ let buffer = self.buffer.borrow();
+ let mut new_entries =
+ buffer.get_entries_by_name_and_type(None, Some(entry_type.clone()));
+ if new_entries.len() > 0 {
+ let mut obs_entries = observer.entries();
+ obs_entries.append(&mut new_entries);
+ observer.set_entries(obs_entries);
+ }
+
+ if !self.pending_notification_observers_task.get() {
+ self.pending_notification_observers_task.set(true);
+ let task_source = self.global().performance_timeline_task_source();
+ task_source.queue_notification(&self.global());
+ }
+ }
+ 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) => {
+ // Append the type if not already present, otherwise do nothing
+ if !observers[p].entry_types.contains(entry_type) {
+ observers[p].entry_types.push(entry_type.clone())
+ }
+ },
+ // Otherwise, we create and insert the new PerformanceObserver.
+ None => observers.push(PerformanceObserver {
+ observer: DomRoot::from_ref(observer),
+ entry_types: vec![entry_type.clone()],
+ }),
+ };
+ }
+
/// Remove a PerformanceObserver from the list of observers.
pub fn remove_observer(&self, observer: &DOMPerformanceObserver) {
let mut observers = self.observers.borrow_mut();
@@ -287,18 +317,13 @@ impl Performance {
// 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.
+ // observers. This is a shallow copy, so observers can
+ // disconnect themselves by using the argument of their own callback.
let observers: Vec<DomRoot<DOMPerformanceObserver>> = self
.observers
.borrow()
.iter()
- .map(|o| {
- DOMPerformanceObserver::new(
- &self.global(),
- o.observer.callback(),
- o.observer.entries(),
- )
- })
+ .map(|o| DomRoot::from_ref(&*o.observer))
.collect();
// Step 7.3.
diff --git a/components/script/dom/performanceobserver.rs b/components/script/dom/performanceobserver.rs
index 0683a3cb46e..e107c4a08d9 100644
--- a/components/script/dom/performanceobserver.rs
+++ b/components/script/dom/performanceobserver.rs
@@ -13,11 +13,13 @@ use crate::dom::bindings::error::{Error, Fallible};
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector};
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::str::DOMString;
+use crate::dom::console::Console;
use crate::dom::globalscope::GlobalScope;
use crate::dom::performance::PerformanceEntryList;
use crate::dom::performanceentry::PerformanceEntry;
use crate::dom::performanceobserverentrylist::PerformanceObserverEntryList;
use dom_struct::dom_struct;
+use std::cell::Cell;
use std::rc::Rc;
/// List of allowed performance entry types.
@@ -31,12 +33,20 @@ const VALID_ENTRY_TYPES: &'static [&'static str] = &[
"paint", // Paint Timing API
];
+#[derive(Clone, Copy, JSTraceable, MallocSizeOf, PartialEq)]
+enum ObserverType {
+ Undefined,
+ Single,
+ Multiple,
+}
+
#[dom_struct]
pub struct PerformanceObserver {
reflector_: Reflector,
#[ignore_malloc_size_of = "can't measure Rc values"]
callback: Rc<PerformanceObserverCallback>,
entries: DomRefCell<DOMPerformanceEntryList>,
+ observer_type: Cell<ObserverType>,
}
impl PerformanceObserver {
@@ -48,6 +58,7 @@ impl PerformanceObserver {
reflector_: Reflector::new(),
callback,
entries,
+ observer_type: Cell::new(ObserverType::Undefined),
}
}
@@ -77,17 +88,15 @@ impl PerformanceObserver {
/// 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() {
+ if self.entries.borrow().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 entry_list = PerformanceEntryList::new(self.entries.borrow_mut().drain(..).collect());
+ let observer_entry_list = PerformanceObserverEntryList::new(&self.global(), entry_list);
+ // using self both as thisArg and as the second formal argument
let _ = self
.callback
- .Call__(&observer_entry_list, self, ExceptionHandling::Report);
+ .Call_(self, &observer_entry_list, self, ExceptionHandling::Report);
}
pub fn callback(&self) -> Rc<PerformanceObserverCallback> {
@@ -106,31 +115,112 @@ impl PerformanceObserver {
impl PerformanceObserverMethods for PerformanceObserver {
// https://w3c.github.io/performance-timeline/#dom-performanceobserver-observe()
fn Observe(&self, options: &PerformanceObserverInit) -> Fallible<()> {
- // step 1
- // 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>>();
- // step 2
- // There must be at least one valid entry type.
- if entry_types.is_empty() {
- return Err(Error::Type("entryTypes cannot be empty".to_string()));
+ // Step 1 is self
+
+ // Step 2 is self.global()
+
+ // Step 3
+ if options.entryTypes.is_none() && options.type_.is_none() {
+ return Err(Error::Syntax);
+ }
+
+ // Step 4
+ if options.entryTypes.is_some() && (options.buffered.is_some() || options.type_.is_some()) {
+ return Err(Error::Syntax);
+ }
+
+ // If this point is reached, then one of options.entryTypes or options.type_
+ // is_some, but not both.
+
+ // Step 5
+ match self.observer_type.get() {
+ ObserverType::Undefined => {
+ if options.entryTypes.is_some() {
+ self.observer_type.set(ObserverType::Multiple);
+ } else {
+ self.observer_type.set(ObserverType::Single);
+ }
+ },
+ ObserverType::Single => {
+ if options.entryTypes.is_some() {
+ return Err(Error::InvalidModification);
+ }
+ },
+ ObserverType::Multiple => {
+ if options.type_.is_some() {
+ return Err(Error::InvalidModification);
+ }
+ },
}
- // step 3-4-5
- self.global()
- .performance()
- .add_observer(self, entry_types, options.buffered);
+ // The entryTypes and type paths diverge here
+ if let Some(entry_types) = &options.entryTypes {
+ // Steps 6.1 - 6.2
+ let entry_types = entry_types
+ .iter()
+ .filter(|e| VALID_ENTRY_TYPES.contains(&e.as_ref()))
+ .map(|e| e.clone())
+ .collect::<Vec<DOMString>>();
+
+ // Step 6.3
+ if entry_types.is_empty() {
+ Console::Warn(
+ &*self.global(),
+ vec![DOMString::from(
+ "No valid entry type provided to observe().",
+ )],
+ );
+ return Ok(());
+ }
+
+ // Steps 6.4-6.5
+ // This never pre-fills buffered entries, and
+ // any existing types are replaced.
+ self.global()
+ .performance()
+ .add_multiple_type_observer(self, entry_types);
+ Ok(())
+ } else if let Some(entry_type) = &options.type_ {
+ // Step 7.2
+ if !VALID_ENTRY_TYPES.contains(&entry_type.as_ref()) {
+ Console::Warn(
+ &*self.global(),
+ vec![DOMString::from(
+ "No valid entry type provided to observe().",
+ )],
+ );
+ return Ok(());
+ }
- Ok(())
+ // Steps 7.3-7.5
+ // This may pre-fill buffered entries, and
+ // existing types are appended to.
+ self.global().performance().add_single_type_observer(
+ self,
+ entry_type,
+ options.buffered.unwrap_or(false),
+ );
+ Ok(())
+ } else {
+ // Step 7.1
+ unreachable!()
+ }
}
- // https://w3c.github.io/performance-timeline/#dom-performanceobserver-disconnect()
+ // https://w3c.github.io/performance-timeline/#dom-performanceobserver-disconnect
fn Disconnect(&self) {
self.global().performance().remove_observer(self);
self.entries.borrow_mut().clear();
}
+
+ // https://w3c.github.io/performance-timeline/#takerecords-method
+ fn TakeRecords(&self) -> Vec<DomRoot<PerformanceEntry>> {
+ let mut entries = self.entries.borrow_mut();
+ let taken = entries
+ .iter()
+ .map(|entry| DomRoot::from_ref(&**entry))
+ .collect();
+ entries.clear();
+ return taken;
+ }
}
diff --git a/components/script/dom/webidls/PerformanceObserver.webidl b/components/script/dom/webidls/PerformanceObserver.webidl
index 257ff96c46f..dd3a66b299d 100644
--- a/components/script/dom/webidls/PerformanceObserver.webidl
+++ b/components/script/dom/webidls/PerformanceObserver.webidl
@@ -7,8 +7,9 @@
*/
dictionary PerformanceObserverInit {
- required sequence<DOMString> entryTypes;
- boolean buffered = false;
+ sequence<DOMString> entryTypes;
+ DOMString type;
+ boolean buffered;
};
callback PerformanceObserverCallback = void (PerformanceObserverEntryList entries, PerformanceObserver observer);
@@ -17,6 +18,8 @@ callback PerformanceObserverCallback = void (PerformanceObserverEntryList entrie
interface PerformanceObserver {
[Throws] constructor(PerformanceObserverCallback callback);
[Throws]
- void observe(PerformanceObserverInit options);
+ void observe(optional PerformanceObserverInit options = {});
void disconnect();
+ PerformanceEntryList takeRecords();
+ // [SameObject] static readonly attribute FrozenArray<DOMString> supportedEntryTypes;
};
diff --git a/tests/wpt/metadata/performance-timeline/buffered-flag-after-timeout.any.js.ini b/tests/wpt/metadata/performance-timeline/buffered-flag-after-timeout.any.js.ini
deleted file mode 100644
index 1069b54261c..00000000000
--- a/tests/wpt/metadata/performance-timeline/buffered-flag-after-timeout.any.js.ini
+++ /dev/null
@@ -1,9 +0,0 @@
-[buffered-flag-after-timeout.any.html]
- [PerformanceObserver with buffered flag sees entry after timeout]
- expected: FAIL
-
-
-[buffered-flag-after-timeout.any.worker.html]
- [PerformanceObserver with buffered flag sees entry after timeout]
- expected: FAIL
-
diff --git a/tests/wpt/metadata/performance-timeline/buffered-flag-observer.any.js.ini b/tests/wpt/metadata/performance-timeline/buffered-flag-observer.any.js.ini
deleted file mode 100644
index b076184db3e..00000000000
--- a/tests/wpt/metadata/performance-timeline/buffered-flag-observer.any.js.ini
+++ /dev/null
@@ -1,9 +0,0 @@
-[buffered-flag-observer.any.html]
- [PerformanceObserver with buffered flag should see past and future entries.]
- expected: FAIL
-
-
-[buffered-flag-observer.any.worker.html]
- [PerformanceObserver with buffered flag should see past and future entries.]
- expected: FAIL
-
diff --git a/tests/wpt/metadata/performance-timeline/idlharness.any.js.ini b/tests/wpt/metadata/performance-timeline/idlharness.any.js.ini
index 00d9f1831b0..73206303830 100644
--- a/tests/wpt/metadata/performance-timeline/idlharness.any.js.ini
+++ b/tests/wpt/metadata/performance-timeline/idlharness.any.js.ini
@@ -8,12 +8,6 @@
[idlharness.any.worker.html]
- [PerformanceObserver interface: operation takeRecords()]
- expected: FAIL
-
- [PerformanceObserver interface: observer must inherit property "takeRecords()" with the proper type]
- expected: FAIL
-
[Test default toJSON operation of PerformanceMark]
expected: FAIL
@@ -29,20 +23,11 @@
[PerformanceMark interface object length]
expected: FAIL
- [PerformanceObserver interface: operation observe(PerformanceObserverInit)]
- expected: FAIL
-
[idlharness.any.html]
[Untitled]
expected: FAIL
- [PerformanceObserver interface: operation takeRecords()]
- expected: FAIL
-
- [PerformanceObserver interface: observer must inherit property "takeRecords()" with the proper type]
- expected: FAIL
-
[Test default toJSON operation of PerformanceMark]
expected: FAIL
@@ -58,9 +43,6 @@
[PerformanceMark interface object length]
expected: FAIL
- [PerformanceObserver interface: operation observe(PerformanceObserverInit)]
- expected: FAIL
-
[idlharness.https.any.serviceworker.html]
type: testharness
diff --git a/tests/wpt/metadata/performance-timeline/multiple-buffered-flag-observers.any.js.ini b/tests/wpt/metadata/performance-timeline/multiple-buffered-flag-observers.any.js.ini
deleted file mode 100644
index 390c2e4cec2..00000000000
--- a/tests/wpt/metadata/performance-timeline/multiple-buffered-flag-observers.any.js.ini
+++ /dev/null
@@ -1,11 +0,0 @@
-[multiple-buffered-flag-observers.any.html]
- expected: ERROR
- [Multiple PerformanceObservers with buffered flag see all entries]
- expected: TIMEOUT
-
-
-[multiple-buffered-flag-observers.any.worker.html]
- expected: ERROR
- [Multiple PerformanceObservers with buffered flag see all entries]
- expected: TIMEOUT
-
diff --git a/tests/wpt/metadata/performance-timeline/observer-buffered-false.any.js.ini b/tests/wpt/metadata/performance-timeline/observer-buffered-false.any.js.ini
deleted file mode 100644
index 5939a89c650..00000000000
--- a/tests/wpt/metadata/performance-timeline/observer-buffered-false.any.js.ini
+++ /dev/null
@@ -1,9 +0,0 @@
-[observer-buffered-false.any.html]
- [PerformanceObserver without buffered flag set to false cannot see past entries.]
- expected: FAIL
-
-
-[observer-buffered-false.any.worker.html]
- [PerformanceObserver without buffered flag set to false cannot see past entries.]
- expected: FAIL
-
diff --git a/tests/wpt/metadata/performance-timeline/po-callback-mutate.any.js.ini b/tests/wpt/metadata/performance-timeline/po-callback-mutate.any.js.ini
deleted file mode 100644
index 4d80c38a5cb..00000000000
--- a/tests/wpt/metadata/performance-timeline/po-callback-mutate.any.js.ini
+++ /dev/null
@@ -1,11 +0,0 @@
-[po-callback-mutate.any.worker.html]
- type: testharness
- [PerformanceObserver modifications inside callback should update filtering and not clear buffer]
- expected: FAIL
-
-
-[po-callback-mutate.any.html]
- type: testharness
- [PerformanceObserver modifications inside callback should update filtering and not clear buffer]
- expected: FAIL
-
diff --git a/tests/wpt/metadata/performance-timeline/po-observe-type.any.js.ini b/tests/wpt/metadata/performance-timeline/po-observe-type.any.js.ini
deleted file mode 100644
index 928849cd870..00000000000
--- a/tests/wpt/metadata/performance-timeline/po-observe-type.any.js.ini
+++ /dev/null
@@ -1,33 +0,0 @@
-[po-observe-type.any.html]
- [Calling observe() with entryTypes and then type should throw an InvalidModificationError]
- expected: FAIL
-
- [Calling observe() with type and then entryTypes should throw an InvalidModificationError]
- expected: FAIL
-
- [Calling observe() with type and entryTypes should throw a SyntaxError]
- expected: FAIL
-
- [Passing in unknown values to type does throw an exception.]
- expected: FAIL
-
- [observe() with different type values stacks.]
- expected: FAIL
-
-
-[po-observe-type.any.worker.html]
- [Calling observe() with entryTypes and then type should throw an InvalidModificationError]
- expected: FAIL
-
- [Calling observe() with type and then entryTypes should throw an InvalidModificationError]
- expected: FAIL
-
- [Calling observe() with type and entryTypes should throw a SyntaxError]
- expected: FAIL
-
- [Passing in unknown values to type does throw an exception.]
- expected: FAIL
-
- [observe() with different type values stacks.]
- 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
deleted file mode 100644
index 9f2213a6720..00000000000
--- a/tests/wpt/metadata/performance-timeline/po-observe.any.js.ini
+++ /dev/null
@@ -1,41 +0,0 @@
-[po-observe.any.worker.html]
- type: testharness
- [Empty sequence entryTypes is a no-op]
- expected: FAIL
-
- [Unknown entryTypes are no-op]
- expected: FAIL
-
- [Check observer callback parameter and this values]
- expected: FAIL
-
- [no 'type' or 'entryTypes' throws a SyntaxError]
- expected: FAIL
-
- [Empty sequence entryTypes does not throw an exception.]
- expected: FAIL
-
- [Unknown entryTypes do not throw an exception.]
- expected: FAIL
-
-
-[po-observe.any.html]
- type: testharness
- [Empty sequence entryTypes is a no-op]
- expected: FAIL
-
- [Unknown entryTypes are no-op]
- expected: FAIL
-
- [Check observer callback parameter and this values]
- expected: FAIL
-
- [no 'type' or 'entryTypes' throws a SyntaxError]
- expected: FAIL
-
- [Empty sequence entryTypes does not throw an exception.]
- expected: FAIL
-
- [Unknown entryTypes do not throw an exception.]
- expected: FAIL
-
diff --git a/tests/wpt/metadata/performance-timeline/po-observe.html.ini b/tests/wpt/metadata/performance-timeline/po-observe.html.ini
index d38a5fd3ac5..e8bdb8ddd77 100644
--- a/tests/wpt/metadata/performance-timeline/po-observe.html.ini
+++ b/tests/wpt/metadata/performance-timeline/po-observe.html.ini
@@ -1,3 +1,5 @@
[po-observe.html]
type: testharness
- expected: CRASH
+ expected: TIMEOUT
+ [PerformanceObserverInit.buffered should retrieve previously buffered entries]
+ expected: TIMEOUT
diff --git a/tests/wpt/metadata/performance-timeline/po-takeRecords.any.js.ini b/tests/wpt/metadata/performance-timeline/po-takeRecords.any.js.ini
deleted file mode 100644
index 851af386c48..00000000000
--- a/tests/wpt/metadata/performance-timeline/po-takeRecords.any.js.ini
+++ /dev/null
@@ -1,9 +0,0 @@
-[po-takeRecords.any.worker.html]
- [Test PerformanceObserver's takeRecords()]
- expected: FAIL
-
-
-[po-takeRecords.any.html]
- [Test PerformanceObserver's takeRecords()]
- expected: FAIL
-
diff --git a/tests/wpt/metadata/resource-timing/buffered-flag.any.js.ini b/tests/wpt/metadata/resource-timing/buffered-flag.any.js.ini
index dfdea1a3197..9c7751a519d 100644
--- a/tests/wpt/metadata/resource-timing/buffered-flag.any.js.ini
+++ b/tests/wpt/metadata/resource-timing/buffered-flag.any.js.ini
@@ -1,11 +1,7 @@
[buffered-flag.any.worker.html]
- expected: ERROR
[PerformanceObserver with buffered flag sees previous resource entries.]
- expected: TIMEOUT
-
+ expected: FAIL
[buffered-flag.any.html]
- expected: ERROR
[PerformanceObserver with buffered flag sees previous resource entries.]
- expected: TIMEOUT
-
+ expected: FAIL