servo: Merge #18324 - Make Performance Timeline API work in Workers (from ferjm:performance.workers); r=jdm
authorFernando Jiménez Moreno <ferjmoreno@gmail.com>
Tue, 05 Sep 2017 17:00:26 -0500
changeset 428563 1e6a299da7376df96d15aaa72c8789967eb1fc12
parent 428562 73af09f7cb84c174f6608a545956d424866cfdc4
child 428564 fdb66df9fbc217386725e7adf66440f0c1f54ad5
push id7761
push userjlund@mozilla.com
push dateFri, 15 Sep 2017 00:19:52 +0000
treeherdermozilla-beta@c38455951db4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjdm
bugs18324, 18286, 18308
milestone57.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
servo: Merge #18324 - Make Performance Timeline API work in Workers (from ferjm:performance.workers); r=jdm - [X] `./mach build -d` does not report any errors - [X] `./mach test-tidy` does not report any errors - [X] These changes fix #18286 and #18308 This patch makes the Performance Timeline API work in workers, removes the exposure of `Performance.timing` in workers and sets the appropriate value of `Performance.now()` in workers. Source-Repo: https://github.com/servo/servo Source-Revision: 42de8e3d3fe8927b4730a7a42880eb46f743384a
servo/components/profile/time.rs
servo/components/profile_traits/time.rs
servo/components/script/dom/document.rs
servo/components/script/dom/globalscope.rs
servo/components/script/dom/performance.rs
servo/components/script/dom/performanceobserver.rs
servo/components/script/dom/webidls/Performance.webidl
servo/components/script/dom/webidls/PerformanceTiming.webidl
servo/components/script/dom/webidls/Window.webidl
servo/components/script/dom/webidls/WindowOrWorkerGlobalScope.webidl
servo/components/script/dom/window.rs
servo/components/script/dom/workerglobalscope.rs
servo/components/script/script_runtime.rs
servo/components/script/script_thread.rs
servo/components/script/task_source/performance_timeline.rs
--- a/servo/components/profile/time.rs
+++ b/servo/components/profile/time.rs
@@ -149,16 +149,17 @@ impl Formattable for ProfilerCategory {
             ProfilerCategory::ScriptStylesheetLoad => "Script Stylesheet Load",
             ProfilerCategory::ScriptWebSocketEvent => "Script Web Socket Event",
             ProfilerCategory::ScriptWorkerEvent => "Script Worker Event",
             ProfilerCategory::ScriptServiceWorkerEvent => "Script Service Worker Event",
             ProfilerCategory::ScriptEnterFullscreen => "Script Enter Fullscreen",
             ProfilerCategory::ScriptExitFullscreen => "Script Exit Fullscreen",
             ProfilerCategory::ScriptWebVREvent => "Script WebVR Event",
             ProfilerCategory::ScriptWorkletEvent => "Script Worklet Event",
+            ProfilerCategory::ScriptPerformanceEvent => "Script Performance Event",
             ProfilerCategory::TimeToFirstPaint => "Time To First Paint",
             ProfilerCategory::TimeToFirstContentfulPaint => "Time To First Contentful Paint",
             ProfilerCategory::ApplicationHeartbeat => "Application Heartbeat",
         };
         format!("{}{}", padding, name)
     }
 }
 
--- a/servo/components/profile_traits/time.rs
+++ b/servo/components/profile_traits/time.rs
@@ -85,16 +85,17 @@ pub enum ProfilerCategory {
     ScriptWebSocketEvent = 0x73,
     ScriptWorkerEvent = 0x74,
     ScriptServiceWorkerEvent = 0x75,
     ScriptParseXML = 0x76,
     ScriptEnterFullscreen = 0x77,
     ScriptExitFullscreen = 0x78,
     ScriptWebVREvent = 0x79,
     ScriptWorkletEvent = 0x7a,
+    ScriptPerformanceEvent = 0x7b,
     TimeToFirstPaint = 0x80,
     TimeToFirstContentfulPaint = 0x81,
     ApplicationHeartbeat = 0x90,
 }
 
 #[derive(Clone, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
 pub enum TimerMetadataFrameType {
     RootWindow,
--- a/servo/components/script/dom/document.rs
+++ b/servo/components/script/dom/document.rs
@@ -1581,17 +1581,17 @@ impl Document {
     pub fn run_the_animation_frame_callbacks(&self) {
         rooted_vec!(let mut animation_frame_list);
         mem::swap(
             &mut *animation_frame_list,
             &mut *self.animation_frame_list.borrow_mut());
 
         self.running_animation_callbacks.set(true);
         let was_faking_animation_frames = self.is_faking_animation_frames();
-        let timing = self.window.Performance().Now();
+        let timing = self.global().performance().Now();
 
         for (_, callback) in animation_frame_list.drain(..) {
             if let Some(callback) = callback {
                 callback.call(self, *timing);
             }
         }
 
         self.running_animation_callbacks.set(false);
--- a/servo/components/script/dom/globalscope.rs
+++ b/servo/components/script/dom/globalscope.rs
@@ -1,27 +1,29 @@
 /* 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 devtools_traits::{ScriptToDevtoolsControlMsg, WorkerId};
 use dom::bindings::cell::DOMRefCell;
 use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
+use dom::bindings::codegen::Bindings::WorkerGlobalScopeBinding::WorkerGlobalScopeMethods;
 use dom::bindings::conversions::root_from_object;
 use dom::bindings::error::{ErrorInfo, report_pending_exception};
 use dom::bindings::inheritance::Castable;
 use dom::bindings::js::{MutNullableJS, Root};
 use dom::bindings::reflector::DomObject;
 use dom::bindings::settings_stack::{AutoEntryScript, entry_global, incumbent_global};
 use dom::bindings::str::DOMString;
 use dom::crypto::Crypto;
 use dom::dedicatedworkerglobalscope::DedicatedWorkerGlobalScope;
 use dom::errorevent::ErrorEvent;
 use dom::event::{Event, EventBubbles, EventCancelable, EventStatus};
 use dom::eventtarget::EventTarget;
+use dom::performance::Performance;
 use dom::window::Window;
 use dom::workerglobalscope::WorkerGlobalScope;
 use dom::workletglobalscope::WorkletGlobalScope;
 use dom_struct::dom_struct;
 use ipc_channel::ipc::IpcSender;
 use js::{JSCLASS_IS_DOMJSCLASS, JSCLASS_IS_GLOBAL};
 use js::glue::{IsWrapper, UnwrapObject};
 use js::jsapi::{CurrentGlobalOrNull, GetGlobalForObjectCrossCompartment};
@@ -41,16 +43,17 @@ use script_traits::{MsDuration, ScriptTo
 use script_traits::{TimerEventId, TimerSchedulerMsg, TimerSource};
 use servo_url::{MutableOrigin, ServoUrl};
 use std::cell::Cell;
 use std::collections::HashMap;
 use std::collections::hash_map::Entry;
 use std::ffi::CString;
 use task_source::file_reading::FileReadingTaskSource;
 use task_source::networking::NetworkingTaskSource;
+use task_source::performance_timeline::PerformanceTimelineTaskSource;
 use time::{Timespec, get_time};
 use timers::{IsInterval, OneshotTimerCallback, OneshotTimerHandle};
 use timers::{OneshotTimers, TimerCallback};
 
 #[dom_struct]
 pub struct GlobalScope {
     eventtarget: EventTarget,
     crypto: MutNullableJS<Crypto>,
@@ -564,16 +567,39 @@ impl GlobalScope {
     }
 
     /// Returns the ["incumbent"] global object.
     ///
     /// ["incumbent"]: https://html.spec.whatwg.org/multipage/#incumbent
     pub fn incumbent() -> Option<Root<Self>> {
         incumbent_global()
     }
+
+    pub fn performance(&self) -> Root<Performance> {
+        if let Some(window) = self.downcast::<Window>() {
+            return window.Performance();
+        }
+        if let Some(worker) = self.downcast::<WorkerGlobalScope>() {
+            return worker.Performance();
+        }
+        unreachable!();
+    }
+
+    /// Channel to send messages to the performance timeline task source
+    /// of this global scope.
+    pub fn performance_timeline_task_source(&self) -> PerformanceTimelineTaskSource {
+        if let Some(window) = self.downcast::<Window>() {
+            return window.performance_timeline_task_source();
+        }
+        if let Some(worker) = self.downcast::<WorkerGlobalScope>() {
+            return worker.performance_timeline_task_source();
+        }
+        unreachable!();
+    }
+
 }
 
 fn timestamp_in_ms(time: Timespec) -> u64 {
     (time.sec * 1000 + (time.nsec / 1000000) as i64) as u64
 }
 
 /// Returns the Rust global scope from a JS global object.
 #[allow(unsafe_code)]
--- a/servo/components/script/dom/performance.rs
+++ b/servo/components/script/dom/performance.rs
@@ -1,27 +1,27 @@
 /* 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::inheritance::Castable;
 use dom::bindings::js::{JS, Root};
 use dom::bindings::num::Finite;
-use dom::bindings::refcounted::Trusted;
 use dom::bindings::reflector::{DomObject, Reflector, reflect_dom_object};
 use dom::bindings::str::DOMString;
+use dom::globalscope::GlobalScope;
 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 std::cmp::Ordering;
 use time;
 
 /// Implementation of a list of PerformanceEntry items shared by the
 /// Performance and PerformanceObserverEntryList interfaces implementations.
 #[derive(HeapSizeOf, JSTraceable)]
 pub struct PerformanceEntryList {
@@ -59,44 +59,50 @@ impl IntoIterator for PerformanceEntryLi
 struct PerformanceObserver {
     observer: Root<DOMPerformanceObserver>,
     entry_types: Vec<DOMString>,
 }
 
 #[dom_struct]
 pub struct Performance {
     reflector_: Reflector,
-    timing: JS<PerformanceTiming>,
+    timing: Option<JS<PerformanceTiming>>,
     entries: DOMRefCell<PerformanceEntryList>,
     observers: DOMRefCell<Vec<PerformanceObserver>>,
     pending_notification_observers_task: Cell<bool>,
+    navigation_start_precise: f64,
 }
 
 impl Performance {
-    fn new_inherited(window: &Window,
+    fn new_inherited(global: &GlobalScope,
                      navigation_start: u64,
                      navigation_start_precise: f64) -> Performance {
         Performance {
             reflector_: Reflector::new(),
-            timing: JS::from_ref(&*PerformanceTiming::new(window,
-                                                            navigation_start,
-                                                            navigation_start_precise)),
+            timing: if global.is::<Window>() {
+                Some(JS::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(window: &Window,
+    pub fn new(global: &GlobalScope,
                navigation_start: u64,
                navigation_start_precise: f64) -> Root<Performance> {
-        reflect_dom_object(box Performance::new_inherited(window,
+        reflect_dom_object(box Performance::new_inherited(global,
                                                           navigation_start,
                                                           navigation_start_precise),
-                           window,
+                           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>,
@@ -163,27 +169,25 @@ impl Performance {
         // 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);
+        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
-    fn notify_observers(&self) {
+    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<Root<DOMPerformanceObserver>> =
@@ -195,44 +199,31 @@ impl Performance {
 
         // 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 main_thread_handler(self: Box<NotifyPerformanceObserverRunnable>,
-                           _: &ScriptThread) {
-        self.owner.root().notify_observers();
-    }
-}
-
 impl PerformanceMethods for Performance {
     // https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/NavigationTiming/Overview.html#performance-timing-attribute
     fn Timing(&self) -> Root<PerformanceTiming> {
-        Root::from_ref(&*self.timing)
+        match self.timing {
+            Some(ref timing) => Root::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 {
-        let nav_start = self.timing.navigation_start_precise();
+        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)
     }
 
     // https://www.w3.org/TR/performance-timeline-2/#dom-performance-getentries
     fn GetEntries(&self) -> Vec<Root<PerformanceEntry>> {
         self.entries.borrow().get_entries_by_name_and_type(None, None)
     }
--- a/servo/components/script/dom/performanceobserver.rs
+++ b/servo/components/script/dom/performanceobserver.rs
@@ -4,17 +4,16 @@
 
 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;
@@ -46,18 +45,18 @@ impl PerformanceObserver {
             reflector_: Reflector::new(),
             callback,
             entries,
         }
     }
 
     #[allow(unrooted_must_root)]
     pub fn new(global: &GlobalScope,
-           callback: Rc<PerformanceObserverCallback>,
-           entries: DOMPerformanceEntryList)
+               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()))
@@ -105,20 +104,20 @@ impl PerformanceObserverMethods for Perf
                                             .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())));
         }
 
-        let performance = self.global().as_window().Performance();
         // step 3-4-5
-        performance.add_observer(self, entry_types, options.buffered);
+        self.global().performance().add_observer(self, entry_types, options.buffered);
+
         Ok(())
     }
 
     // https://w3c.github.io/performance-timeline/#dom-performanceobserver-disconnect()
     fn Disconnect(&self) {
-        self.global().as_window().Performance().remove_observer(self);
+        self.global().performance().remove_observer(self);
         self.entries.borrow_mut().clear();
     }
 }
--- a/servo/components/script/dom/webidls/Performance.webidl
+++ b/servo/components/script/dom/webidls/Performance.webidl
@@ -4,25 +4,27 @@
 /*
  * The origin of this IDL file is
  * https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/NavigationTiming/Overview.html#sec-window.performance-attribute
  */
 
 typedef double DOMHighResTimeStamp;
 typedef sequence<PerformanceEntry> PerformanceEntryList;
 
-[Exposed=(Window,Worker)]
+[Exposed=(Window, Worker)]
 interface Performance {
+  DOMHighResTimeStamp now();
+};
+
+[Exposed=(Window)]
+partial interface Performance {
   readonly attribute PerformanceTiming timing;
   /*  readonly attribute PerformanceNavigation navigation; */
 };
 
-partial interface Performance {
-  DOMHighResTimeStamp now();
-};
-
 // https://w3c.github.io/performance-timeline/#extensions-to-the-performance-interface
+[Exposed=(Window, Worker)]
 partial interface Performance {
   PerformanceEntryList getEntries();
   PerformanceEntryList getEntriesByType(DOMString type);
   PerformanceEntryList getEntriesByName(DOMString name,
                                         optional DOMString type);
 };
--- a/servo/components/script/dom/webidls/PerformanceTiming.webidl
+++ b/servo/components/script/dom/webidls/PerformanceTiming.webidl
@@ -1,17 +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://dvcs.w3.org/hg/webperf/raw-file/tip/specs/NavigationTiming/Overview.html#sec-navigation-timing-interface
  */
 
-[Exposed=(Window,Worker)]
+[Exposed=(Window)]
 interface PerformanceTiming {
   readonly attribute unsigned long long navigationStart;
   /*  readonly attribute unsigned long long unloadEventStart;
   readonly attribute unsigned long long unloadEventEnd;
   readonly attribute unsigned long long redirectStart;
   readonly attribute unsigned long long redirectEnd;
   readonly attribute unsigned long long fetchStart;
   readonly attribute unsigned long long domainLookupStart;
--- a/servo/components/script/dom/webidls/Window.webidl
+++ b/servo/components/script/dom/webidls/Window.webidl
@@ -93,21 +93,16 @@ Window implements WindowTimers;
 interface WindowBase64 {
   [Throws]
   DOMString btoa(DOMString btoa);
   [Throws]
   DOMString atob(DOMString atob);
 };
 Window implements WindowBase64;
 
-// https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/NavigationTiming/Overview.html#sec-window.performance-attribute
-partial interface Window {
-  [Replaceable] readonly attribute Performance performance;
-};
-
 // https://html.spec.whatwg.org/multipage/#Window-partial
 partial interface Window {
   void captureEvents();
   void releaseEvents();
 };
 
 // https://drafts.csswg.org/cssom/#extensions-to-the-window-interface
 partial interface Window {
--- a/servo/components/script/dom/webidls/WindowOrWorkerGlobalScope.webidl
+++ b/servo/components/script/dom/webidls/WindowOrWorkerGlobalScope.webidl
@@ -21,10 +21,16 @@ interface WindowOrWorkerGlobalScope {
   // void clearInterval(optional long handle = 0);
 
   // ImageBitmap
   // Promise<ImageBitmap> createImageBitmap(ImageBitmapSource image, optional ImageBitmapOptions options);
   // Promise<ImageBitmap> createImageBitmap(
   //   ImageBitmapSource image, long sx, long sy, long sw, long sh, optional ImageBitmapOptions options);
 };
 
+// https://w3c.github.io/hr-time/#the-performance-attribute
+partial interface WindowOrWorkerGlobalScope {
+    [Replaceable]
+    readonly attribute Performance performance;
+};
+
 Window implements WindowOrWorkerGlobalScope;
 WorkerGlobalScope implements WindowOrWorkerGlobalScope;
--- a/servo/components/script/dom/window.rs
+++ b/servo/components/script/dom/window.rs
@@ -705,17 +705,18 @@ impl WindowMethods for Window {
         // Steps 4-5.
         Some(Root::from_ref(window_proxy.top()))
     }
 
     // https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/
     // NavigationTiming/Overview.html#sec-window.performance-attribute
     fn Performance(&self) -> Root<Performance> {
         self.performance.or_init(|| {
-            Performance::new(self, self.navigation_start.get(),
+            let global_scope = self.upcast::<GlobalScope>();
+            Performance::new(global_scope, self.navigation_start.get(),
                              self.navigation_start_precise.get())
         })
     }
 
     // https://html.spec.whatwg.org/multipage/#globaleventhandlers
     global_event_handlers!();
 
     // https://html.spec.whatwg.org/multipage/#windoweventhandlers
--- a/servo/components/script/dom/workerglobalscope.rs
+++ b/servo/components/script/dom/workerglobalscope.rs
@@ -12,16 +12,17 @@ use dom::bindings::inheritance::Castable
 use dom::bindings::js::{MutNullableJS, Root};
 use dom::bindings::reflector::DomObject;
 use dom::bindings::settings_stack::AutoEntryScript;
 use dom::bindings::str::DOMString;
 use dom::bindings::trace::RootedTraceableBox;
 use dom::crypto::Crypto;
 use dom::dedicatedworkerglobalscope::DedicatedWorkerGlobalScope;
 use dom::globalscope::GlobalScope;
+use dom::performance::Performance;
 use dom::promise::Promise;
 use dom::serviceworkerglobalscope::ServiceWorkerGlobalScope;
 use dom::window::{base64_atob, base64_btoa};
 use dom::workerlocation::WorkerLocation;
 use dom::workernavigator::WorkerNavigator;
 use dom_struct::dom_struct;
 use fetch;
 use ipc_channel::ipc::IpcSender;
@@ -39,16 +40,18 @@ use script_traits::WorkerGlobalScopeInit
 use servo_url::{MutableOrigin, ServoUrl};
 use std::default::Default;
 use std::rc::Rc;
 use std::sync::Arc;
 use std::sync::atomic::{AtomicBool, Ordering};
 use std::sync::mpsc::Receiver;
 use task_source::file_reading::FileReadingTaskSource;
 use task_source::networking::NetworkingTaskSource;
+use task_source::performance_timeline::PerformanceTimelineTaskSource;
+use time::precise_time_ns;
 use timers::{IsInterval, TimerCallback};
 
 pub fn prepare_workerscope_init(global: &GlobalScope,
                                 devtools_sender: Option<IpcSender<DevtoolScriptControlMsg>>) -> WorkerGlobalScopeInit {
     let init = WorkerGlobalScopeInit {
             resource_threads: global.resource_threads().clone(),
             mem_profiler_chan: global.mem_profiler_chan().clone(),
             to_devtools_sender: global.devtools_chan().cloned(),
@@ -84,16 +87,19 @@ pub struct WorkerGlobalScope {
     from_devtools_sender: Option<IpcSender<DevtoolScriptControlMsg>>,
 
     #[ignore_heap_size_of = "Defined in std"]
     /// This `Receiver` will be ignored later if the corresponding
     /// `IpcSender` doesn't exist
     from_devtools_receiver: Receiver<DevtoolScriptControlMsg>,
 
     microtask_queue: MicrotaskQueue,
+
+    navigation_start_precise: f64,
+    performance: MutNullableJS<Performance>,
 }
 
 impl WorkerGlobalScope {
     pub fn new_inherited(init: WorkerGlobalScopeInit,
                          worker_url: ServoUrl,
                          runtime: Runtime,
                          from_devtools_receiver: Receiver<DevtoolScriptControlMsg>,
                          timer_event_chan: IpcSender<TimerEvent>,
@@ -115,16 +121,18 @@ impl WorkerGlobalScope {
             worker_url: worker_url,
             closing: closing,
             runtime: runtime,
             location: Default::default(),
             navigator: Default::default(),
             from_devtools_sender: init.from_devtools_sender,
             from_devtools_receiver: from_devtools_receiver,
             microtask_queue: MicrotaskQueue::default(),
+            navigation_start_precise: precise_time_ns() as f64,
+            performance: Default::default(),
         }
     }
 
     pub fn from_devtools_sender(&self) -> Option<IpcSender<DevtoolScriptControlMsg>> {
         self.from_devtools_sender.clone()
     }
 
     pub fn from_devtools_receiver(&self) -> &Receiver<DevtoolScriptControlMsg> {
@@ -315,16 +323,26 @@ impl WorkerGlobalScopeMethods for Worker
         self.ClearTimeout(handle);
     }
 
     #[allow(unrooted_must_root)]
     // https://fetch.spec.whatwg.org/#fetch-method
     fn Fetch(&self, input: RequestOrUSVString, init: RootedTraceableBox<RequestInit>) -> Rc<Promise> {
         fetch::Fetch(self.upcast(), input, init)
     }
+
+    // https://w3c.github.io/hr-time/#the-performance-attribute
+    fn Performance(&self) -> Root<Performance> {
+        self.performance.or_init(|| {
+            let global_scope = self.upcast::<GlobalScope>();
+            Performance::new(global_scope,
+                             0 /* navigation start is not used in workers */,
+                             self.navigation_start_precise)
+        })
+    }
 }
 
 
 impl WorkerGlobalScope {
     #[allow(unsafe_code)]
     pub fn execute_script(&self, source: DOMString) {
         let _aes = AutoEntryScript::new(self.upcast());
         rooted!(in(self.runtime.cx()) let mut rval = UndefinedValue());
@@ -363,16 +381,20 @@ impl WorkerGlobalScope {
     pub fn file_reading_task_source(&self) -> FileReadingTaskSource {
         FileReadingTaskSource(self.script_chan())
     }
 
     pub fn networking_task_source(&self) -> NetworkingTaskSource {
         NetworkingTaskSource(self.script_chan())
     }
 
+    pub fn performance_timeline_task_source(&self) -> PerformanceTimelineTaskSource {
+        PerformanceTimelineTaskSource(self.script_chan())
+    }
+
     pub fn new_script_pair(&self) -> (Box<ScriptChan + Send>, Box<ScriptPort + Send>) {
         let dedicated = self.downcast::<DedicatedWorkerGlobalScope>();
         if let Some(dedicated) = dedicated {
             return dedicated.new_script_pair();
         } else {
             panic!("need to implement a sender for SharedWorker/ServiceWorker")
         }
     }
--- a/servo/components/script/script_runtime.rs
+++ b/servo/components/script/script_runtime.rs
@@ -83,17 +83,18 @@ pub enum ScriptThreadEventCategory {
     TimerEvent,
     UpdateReplacedElement,
     WebSocketEvent,
     WorkerEvent,
     WorkletEvent,
     ServiceWorkerEvent,
     EnterFullscreen,
     ExitFullscreen,
-    WebVREvent
+    WebVREvent,
+    PerformanceTimelineTask,
 }
 
 /// An interface for receiving ScriptMsg values in an event loop. Used for synchronous DOM
 /// APIs that need to abstract over multiple kinds of event loops (worker/main thread) with
 /// different Receiver interfaces.
 pub trait ScriptPort {
     fn recv(&self) -> Result<CommonScriptMsg, ()>;
 }
--- a/servo/components/script/script_thread.rs
+++ b/servo/components/script/script_thread.rs
@@ -113,17 +113,17 @@ use std::sync::atomic::{AtomicBool, Orde
 use std::sync::mpsc::{Receiver, Select, Sender, channel};
 use std::thread;
 use style::context::ReflowGoal;
 use style::thread_state;
 use task_source::dom_manipulation::{DOMManipulationTask, DOMManipulationTaskSource};
 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::performance_timeline::PerformanceTimelineTaskSource;
 use task_source::user_interaction::{UserInteractionTask, UserInteractionTaskSource};
 use time::{get_time, precise_time_ns, Tm};
 use url::Position;
 use webdriver_handlers;
 use webvr_traits::{WebVREvent, WebVRMsg};
 
 pub type ImageCacheMsg = (PipelineId, PendingImageResponse);
 
@@ -267,18 +267,16 @@ pub enum MainThreadScriptMsg {
     Navigate(PipelineId, LoadData, bool),
     /// Tasks that originate from the DOM manipulation task source
     DOMManipulation(DOMManipulationTask),
     /// Tasks that originate from the user interaction task source
     UserInteraction(UserInteractionTask),
     /// 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> {
     fn send(&self, msg: CommonScriptMsg) {
         ScriptChan::send(&**self, msg).unwrap();
     }
 }
 
@@ -855,19 +853,19 @@ impl ScriptThread {
             bluetooth_thread: state.bluetooth_thread,
 
             port: port,
 
             chan: MainThreadScriptChan(chan.clone()),
             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.clone()),
-            file_reading_task_source: FileReadingTaskSource(boxed_script_sender),
-            performance_timeline_task_source: PerformanceTimelineTaskSource(chan),
+            history_traversal_task_source: HistoryTraversalTaskSource(chan),
+            file_reading_task_source: FileReadingTaskSource(boxed_script_sender.clone()),
+            performance_timeline_task_source: PerformanceTimelineTaskSource(boxed_script_sender),
 
             control_chan: state.control_chan,
             control_port: control_port,
             script_sender: state.script_to_constellation_chan.sender.clone(),
             time_profiler_chan: state.time_profiler_chan,
             mem_profiler_chan: state.mem_profiler_chan,
 
             devtools_chan: state.devtools_chan,
@@ -1187,16 +1185,17 @@ impl ScriptThread {
                 ScriptThreadEventCategory::TimerEvent => ProfilerCategory::ScriptTimerEvent,
                 ScriptThreadEventCategory::WebSocketEvent => ProfilerCategory::ScriptWebSocketEvent,
                 ScriptThreadEventCategory::WebVREvent => ProfilerCategory::ScriptWebVREvent,
                 ScriptThreadEventCategory::WorkerEvent => ProfilerCategory::ScriptWorkerEvent,
                 ScriptThreadEventCategory::WorkletEvent => ProfilerCategory::ScriptWorkletEvent,
                 ScriptThreadEventCategory::ServiceWorkerEvent => ProfilerCategory::ScriptServiceWorkerEvent,
                 ScriptThreadEventCategory::EnterFullscreen => ProfilerCategory::ScriptEnterFullscreen,
                 ScriptThreadEventCategory::ExitFullscreen => ProfilerCategory::ScriptExitFullscreen,
+                ScriptThreadEventCategory::PerformanceTimelineTask => ProfilerCategory::ScriptPerformanceEvent,
             };
             profile(profiler_cat, None, self.time_profiler_chan.clone(), f)
         } else {
             f()
         }
     }
 
     fn handle_msg_from_constellation(&self, msg: ConstellationControlMsg) {
@@ -1290,18 +1289,16 @@ impl ScriptThread {
                 }
             }
             MainThreadScriptMsg::Common(CommonScriptMsg::CollectReports(reports_chan)) =>
                 self.collect_reports(reports_chan),
             MainThreadScriptMsg::WorkletLoaded(pipeline_id) =>
                 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),
         }
     }
 
     fn handle_timer_event(&self, timer_event: TimerEvent) {
         let TimerEvent(source, id) = timer_event;
 
@@ -2004,17 +2001,16 @@ impl ScriptThread {
                 .unwrap();
         }
         debug!("ScriptThread: loading {} on pipeline {:?}", incomplete.url, incomplete.pipeline_id);
 
         let MainThreadScriptChan(ref sender) = self.chan;
         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,
                                                  self.timer_event_chan.clone());
 
         let origin = if final_url.as_str() == "about:blank" {
             incomplete.origin.clone()
         } else {
@@ -2029,17 +2025,17 @@ impl ScriptThread {
         // Create the window and document objects.
         let window = Window::new(self.js_runtime.clone(),
                                  MainThreadScriptChan(sender.clone()),
                                  DOMManipulationTaskSource(dom_sender.clone()),
                                  UserInteractionTaskSource(user_sender.clone()),
                                  self.networking_task_source.clone(),
                                  HistoryTraversalTaskSource(history_sender.clone()),
                                  self.file_reading_task_source.clone(),
-                                 PerformanceTimelineTaskSource(performance_sender.clone()),
+                                 self.performance_timeline_task_source.clone(),
                                  self.image_cache_channel.clone(),
                                  self.image_cache.clone(),
                                  self.resource_threads.clone(),
                                  self.bluetooth_thread.clone(),
                                  self.mem_profiler_chan.clone(),
                                  self.time_profiler_chan.clone(),
                                  self.devtools_chan.clone(),
                                  script_to_constellation_chan,
--- a/servo/components/script/task_source/performance_timeline.rs
+++ b/servo/components/script/task_source/performance_timeline.rs
@@ -1,58 +1,70 @@
 /* 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 dom::globalscope::GlobalScope;
+use dom::performance::Performance;
+use script_runtime::{CommonScriptMsg, ScriptChan, ScriptThreadEventCategory};
+use script_thread::{Runnable, RunnableWrapper};
 use std::fmt;
 use std::result::Result;
-use std::sync::mpsc::Sender;
 use task_source::TaskSource;
 
-#[derive(Clone, JSTraceable)]
-pub struct PerformanceTimelineTaskSource(pub Sender<MainThreadScriptMsg>);
+pub struct NotifyPerformanceObserverRunnable {
+    owner: Trusted<Performance>,
+}
+
+impl NotifyPerformanceObserverRunnable {
+    pub fn new(owner: Trusted<Performance>) -> Self {
+        NotifyPerformanceObserverRunnable {
+            owner,
+        }
+    }
+}
+
+impl Runnable for NotifyPerformanceObserverRunnable {
+    fn handler(self: Box<NotifyPerformanceObserverRunnable>) {
+        self.owner.root().notify_observers();
+    }
+}
+
+#[derive(JSTraceable)]
+pub struct PerformanceTimelineTaskSource(pub Box<ScriptChan + Send + 'static>);
+
+impl Clone for PerformanceTimelineTaskSource {
+    fn clone(&self) -> PerformanceTimelineTaskSource {
+        PerformanceTimelineTaskSource(self.0.clone())
+    }
+}
 
 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(|_| ())
+        let msg = CommonScriptMsg::RunnableMsg(
+            ScriptThreadEventCategory::PerformanceTimelineTask,
+            wrapper.wrap_runnable(msg)
+        );
+        self.0.send(msg).map_err(|_| ())
     }
 }
 
 impl PerformanceTimelineTaskSource {
-    pub fn queue_notification(&self, owner: &Performance, window: &Window) {
-        let owner = Trusted::new(owner);
+    pub fn queue_notification(&self, global: &GlobalScope) {
+        let owner = Trusted::new(&*global.performance());
         let runnable = box NotifyPerformanceObserverRunnable::new(owner);
-        let _ = self.queue(runnable, window.upcast());
+        let _ = self.queue(runnable, global);
     }
 }
-
-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);
-    }
-}