aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--components/compositing/constellation.rs6
-rw-r--r--components/compositing/lib.rs2
-rw-r--r--components/compositing/pipeline.rs6
-rw-r--r--components/compositing/timer_scheduler.rs221
-rw-r--r--components/profile/time.rs1
-rw-r--r--components/profile_traits/time.rs1
-rw-r--r--components/script/dom/bindings/global.rs10
-rw-r--r--components/script/dom/bindings/trace.rs12
-rw-r--r--components/script/dom/dedicatedworkerglobalscope.rs77
-rw-r--r--components/script/dom/window.rs35
-rw-r--r--components/script/dom/worker.rs2
-rw-r--r--components/script/dom/workerglobalscope.rs37
-rw-r--r--components/script/horribly_inefficient_timers.rs31
-rw-r--r--components/script/lib.rs1
-rw-r--r--components/script/script_task.rs117
-rw-r--r--components/script/timers.rs478
-rw-r--r--components/script_traits/Cargo.toml1
-rw-r--r--components/script_traits/lib.rs59
-rw-r--r--components/servo/Cargo.lock1
-rw-r--r--ports/cef/Cargo.lock1
-rw-r--r--ports/gonk/Cargo.lock1
21 files changed, 776 insertions, 324 deletions
diff --git a/components/compositing/constellation.rs b/components/compositing/constellation.rs
index 5f6a1f6527f..d0c5003f908 100644
--- a/components/compositing/constellation.rs
+++ b/components/compositing/constellation.rs
@@ -41,6 +41,7 @@ use profile_traits::mem;
use profile_traits::time;
use script_traits::{CompositorEvent, ConstellationControlMsg, LayoutControlMsg};
use script_traits::{ScriptState, ScriptTaskFactory};
+use script_traits::{TimerEventRequest};
use std::borrow::ToOwned;
use std::collections::HashMap;
use std::io::{self, Write};
@@ -49,6 +50,7 @@ use std::mem::replace;
use std::process;
use std::sync::mpsc::{Receiver, Sender, channel};
use style_traits::viewport::ViewportConstraints;
+use timer_scheduler::TimerScheduler;
use url::Url;
use util::cursor::Cursor;
use util::geometry::PagePx;
@@ -135,6 +137,8 @@ pub struct Constellation<LTF, STF> {
/// A list of in-process senders to `WebGLPaintTask`s.
webgl_paint_tasks: Vec<Sender<CanvasMsg>>,
+
+ scheduler_chan: Sender<TimerEventRequest>,
}
/// State needed to construct a constellation.
@@ -280,6 +284,7 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
webdriver: WebDriverData::new(),
canvas_paint_tasks: Vec::new(),
webgl_paint_tasks: Vec::new(),
+ scheduler_chan: TimerScheduler::start(),
};
let namespace_id = constellation.next_pipeline_namespace_id();
PipelineNamespace::install(namespace_id);
@@ -317,6 +322,7 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
id: pipeline_id,
parent_info: parent_info,
constellation_chan: self.chan.clone(),
+ scheduler_chan: self.scheduler_chan.clone(),
compositor_proxy: self.compositor_proxy.clone_compositor_proxy(),
devtools_chan: self.devtools_chan.clone(),
image_cache_task: self.image_cache_task.clone(),
diff --git a/components/compositing/lib.rs b/components/compositing/lib.rs
index b8d6355bee2..a38f1d8490f 100644
--- a/components/compositing/lib.rs
+++ b/components/compositing/lib.rs
@@ -6,6 +6,7 @@
#![feature(iter_cmp)]
#![feature(slice_bytes)]
#![feature(vec_push_all)]
+#![feature(mpsc_select)]
#![feature(plugin)]
#![plugin(plugins)]
@@ -54,6 +55,7 @@ mod compositor_layer;
mod headless;
mod scrolling;
mod surface_map;
+mod timer_scheduler;
pub mod compositor_task;
pub mod constellation;
pub mod pipeline;
diff --git a/components/compositing/pipeline.rs b/components/compositing/pipeline.rs
index 34490730708..ad425224d26 100644
--- a/components/compositing/pipeline.rs
+++ b/components/compositing/pipeline.rs
@@ -24,6 +24,7 @@ use profile_traits::mem as profile_mem;
use profile_traits::time;
use script_traits::{ConstellationControlMsg, InitialScriptState};
use script_traits::{LayoutControlMsg, NewLayoutInfo, ScriptTaskFactory};
+use script_traits::{TimerEventRequest};
use std::any::Any;
use std::mem;
use std::sync::mpsc::{Receiver, Sender, channel};
@@ -75,6 +76,8 @@ pub struct InitialPipelineState {
pub parent_info: Option<(PipelineId, SubpageId)>,
/// A channel to the associated constellation.
pub constellation_chan: ConstellationChan,
+ /// A channel to schedule timer events.
+ pub scheduler_chan: Sender<TimerEventRequest>,
/// A channel to the compositor.
pub compositor_proxy: Box<CompositorProxy + 'static + Send>,
/// A channel to the developer tools, if applicable.
@@ -181,6 +184,7 @@ impl Pipeline {
id: state.id,
parent_info: state.parent_info,
constellation_chan: state.constellation_chan,
+ scheduler_chan: state.scheduler_chan,
compositor_proxy: state.compositor_proxy,
devtools_chan: script_to_devtools_chan,
image_cache_task: state.image_cache_task,
@@ -316,6 +320,7 @@ pub struct PipelineContent {
id: PipelineId,
parent_info: Option<(PipelineId, SubpageId)>,
constellation_chan: ConstellationChan,
+ scheduler_chan: Sender<TimerEventRequest>,
compositor_proxy: Box<CompositorProxy + Send + 'static>,
devtools_chan: Option<IpcSender<ScriptToDevtoolsControlMsg>>,
image_cache_task: ImageCacheTask,
@@ -361,6 +366,7 @@ impl PipelineContent {
control_chan: self.script_chan.clone(),
control_port: mem::replace(&mut self.script_port, None).unwrap(),
constellation_chan: self.constellation_chan.clone(),
+ scheduler_chan: self.scheduler_chan.clone(),
failure_info: self.failure.clone(),
resource_task: self.resource_task,
storage_task: self.storage_task.clone(),
diff --git a/components/compositing/timer_scheduler.rs b/components/compositing/timer_scheduler.rs
new file mode 100644
index 00000000000..d29137be788
--- /dev/null
+++ b/components/compositing/timer_scheduler.rs
@@ -0,0 +1,221 @@
+/* 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 euclid::length::Length;
+use num::traits::Saturating;
+use script_traits::{MsDuration, NsDuration, precise_time_ms, precise_time_ns};
+use script_traits::{TimerEvent, TimerEventRequest};
+use std::cell::RefCell;
+use std::cmp::{self, Ord};
+use std::collections::BinaryHeap;
+use std::sync::Arc;
+use std::sync::atomic::{self, AtomicBool};
+use std::sync::mpsc::{channel, Receiver, Select, Sender};
+use std::thread::{self, spawn, Thread};
+use util::task::spawn_named;
+
+/// A quick hack to work around the removal of [`std::old_io::timer::Timer`](
+/// http://doc.rust-lang.org/1.0.0-beta/std/old_io/timer/struct.Timer.html )
+struct CancelableOneshotTimer {
+ thread: Thread,
+ canceled: Arc<AtomicBool>,
+ port: Receiver<()>,
+}
+
+impl CancelableOneshotTimer {
+ fn new(duration: MsDuration) -> CancelableOneshotTimer {
+ let (tx, rx) = channel();
+ let canceled = Arc::new(AtomicBool::new(false));
+ let canceled_clone = canceled.clone();
+
+ let thread = spawn(move || {
+ let due_time = precise_time_ms() + duration;
+
+ let mut park_time = duration;
+
+ loop {
+ thread::park_timeout_ms(park_time.get() as u32);
+
+ if canceled_clone.load(atomic::Ordering::Relaxed) {
+ return;
+ }
+
+ // park_timeout_ms does not guarantee parking for the
+ // given amout. We might have woken up early.
+ let current_time = precise_time_ms();
+ if current_time >= due_time {
+ let _ = tx.send(());
+ return;
+ }
+ park_time = due_time - current_time;
+ }
+ }).thread().clone();
+
+ CancelableOneshotTimer {
+ thread: thread,
+ canceled: canceled,
+ port: rx,
+ }
+ }
+
+ fn port(&self) -> &Receiver<()> {
+ &self.port
+ }
+
+ fn cancel(&self) {
+ self.canceled.store(true, atomic::Ordering::Relaxed);
+ self.thread.unpark();
+ }
+}
+
+pub struct TimerScheduler {
+ port: Receiver<TimerEventRequest>,
+
+ scheduled_events: RefCell<BinaryHeap<ScheduledEvent>>,
+
+ timer: RefCell<Option<CancelableOneshotTimer>>,
+}
+
+struct ScheduledEvent {
+ request: TimerEventRequest,
+ for_time: NsDuration,
+}
+
+impl Ord for ScheduledEvent {
+ fn cmp(&self, other: &ScheduledEvent) -> cmp::Ordering {
+ self.for_time.cmp(&other.for_time).reverse()
+ }
+}
+
+impl PartialOrd for ScheduledEvent {
+ fn partial_cmp(&self, other: &ScheduledEvent) -> Option<cmp::Ordering> {
+ Some(self.cmp(other))
+ }
+}
+
+impl Eq for ScheduledEvent {}
+impl PartialEq for ScheduledEvent {
+ fn eq(&self, other: &ScheduledEvent) -> bool {
+ self as *const ScheduledEvent == other as *const ScheduledEvent
+ }
+}
+
+enum Task {
+ HandleRequest(TimerEventRequest),
+ DispatchDueEvents,
+}
+
+impl TimerScheduler {
+ pub fn start() -> Sender<TimerEventRequest> {
+ let (chan, port) = channel();
+
+ let timer_scheduler = TimerScheduler {
+ port: port,
+
+ scheduled_events: RefCell::new(BinaryHeap::new()),
+
+ timer: RefCell::new(None),
+ };
+
+ spawn_named("TimerScheduler".to_owned(), move || {
+ timer_scheduler.run_event_loop();
+ });
+
+ chan
+ }
+
+ fn run_event_loop(&self) {
+ loop {
+ match self.receive_next_task() {
+ Some(Task::HandleRequest(request)) => self.handle_request(request),
+ Some(Task::DispatchDueEvents) => self.dispatch_due_events(),
+ None => break,
+ }
+ }
+ }
+
+ #[allow(unsafe_code)]
+ fn receive_next_task(&self) -> Option<Task> {
+ let port = &self.port;
+ let timer = self.timer.borrow();
+ let timer_port = timer.as_ref().map(|timer| timer.port());
+
+ if let Some(ref timer_port) = timer_port {
+ let sel = Select::new();
+ let mut scheduler_handle = sel.handle(port);
+ let mut timer_handle = sel.handle(timer_port);
+
+ unsafe {
+ scheduler_handle.add();
+ timer_handle.add();
+ }
+
+ let ret = sel.wait();
+ if ret == scheduler_handle.id() {
+ port.recv().ok().map(|req| Task::HandleRequest(req))
+ } else if ret == timer_handle.id() {
+ timer_port.recv().ok().map(|_| Task::DispatchDueEvents)
+ } else {
+ panic!("unexpected select result!")
+ }
+ } else {
+ port.recv().ok().map(|req| Task::HandleRequest(req))
+ }
+ }
+
+ fn handle_request(&self, request: TimerEventRequest) {
+ let TimerEventRequest(_, _, _, duration_ms) = request;
+ let duration_ns = Length::new(duration_ms.get() * 1000 * 1000);
+ let schedule_for = precise_time_ns() + duration_ns;
+
+ let previously_earliest = self.scheduled_events.borrow().peek()
+ .map(|scheduled| scheduled.for_time)
+ .unwrap_or(Length::new(u64::max_value()));
+
+ self.scheduled_events.borrow_mut().push(ScheduledEvent {
+ request: request,
+ for_time: schedule_for,
+ });
+
+ if schedule_for < previously_earliest {
+ self.start_timer_for_next_event();
+ }
+ }
+
+ fn dispatch_due_events(&self) {
+ let now = precise_time_ns();
+
+ {
+ let mut events = self.scheduled_events.borrow_mut();
+
+ while !events.is_empty() && events.peek().as_ref().unwrap().for_time <= now {
+ let event = events.pop().unwrap();
+ let TimerEventRequest(chan, source, id, _) = event.request;
+
+ let _ = chan.send(TimerEvent(source, id));
+ }
+ }
+
+ self.start_timer_for_next_event();
+ }
+
+ fn start_timer_for_next_event(&self) {
+ let events = self.scheduled_events.borrow();
+ let next_event = events.peek();
+
+ let mut timer = self.timer.borrow_mut();
+
+ if let Some(ref mut timer) = *timer {
+ timer.cancel();
+ }
+
+ *timer = next_event.map(|next_event| {
+ let delay_ns = next_event.for_time.get().saturating_sub(precise_time_ns().get());
+ // Round up, we'd rather be late than early…
+ let delay_ms = Length::new(delay_ns.saturating_add(999999) / (1000 * 1000));
+
+ CancelableOneshotTimer::new(delay_ms)
+ });
+ }
+}
diff --git a/components/profile/time.rs b/components/profile/time.rs
index 43afb37ae17..21acdba8366 100644
--- a/components/profile/time.rs
+++ b/components/profile/time.rs
@@ -92,6 +92,7 @@ impl Formattable for ProfilerCategory {
ProfilerCategory::ScriptEvent => "Script Event",
ProfilerCategory::ScriptUpdateReplacedElement => "Script Update Replaced Element",
ProfilerCategory::ScriptSetViewport => "Script Set Viewport",
+ ProfilerCategory::ScriptTimerEvent => "Script Timer Event",
ProfilerCategory::ScriptWebSocketEvent => "Script Web Socket Event",
ProfilerCategory::ScriptWorkerEvent => "Script Worker Event",
ProfilerCategory::ScriptXhrEvent => "Script Xhr Event",
diff --git a/components/profile_traits/time.rs b/components/profile_traits/time.rs
index 4056ee295d9..8cf485348f7 100644
--- a/components/profile_traits/time.rs
+++ b/components/profile_traits/time.rs
@@ -70,6 +70,7 @@ pub enum ProfilerCategory {
ScriptEvent,
ScriptUpdateReplacedElement,
ScriptSetViewport,
+ ScriptTimerEvent,
ScriptWebSocketEvent,
ScriptWorkerEvent,
ScriptXhrEvent,
diff --git a/components/script/dom/bindings/global.rs b/components/script/dom/bindings/global.rs
index d45d3b154b1..cfb1acb99e9 100644
--- a/components/script/dom/bindings/global.rs
+++ b/components/script/dom/bindings/global.rs
@@ -22,6 +22,8 @@ use msg::constellation_msg::{ConstellationChan, PipelineId, WorkerId};
use net_traits::ResourceTask;
use profile_traits::mem;
use script_task::{CommonScriptMsg, ScriptChan, ScriptPort, ScriptTask};
+use script_traits::TimerEventRequest;
+use std::sync::mpsc::Sender;
use url::Url;
use util::mem::HeapSizeOf;
@@ -96,6 +98,14 @@ impl<'a> GlobalRef<'a> {
}
}
+ /// Get the scheduler channel to request timer events.
+ pub fn scheduler_chan(&self) -> Sender<TimerEventRequest> {
+ match *self {
+ GlobalRef::Window(window) => window.scheduler_chan(),
+ GlobalRef::Worker(worker) => worker.scheduler_chan(),
+ }
+ }
+
/// Get an `IpcSender<ScriptToDevtoolsControlMsg>` to send messages to Devtools
/// task when available.
pub fn devtools_chan(&self) -> Option<IpcSender<ScriptToDevtoolsControlMsg>> {
diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs
index 00a7f6c6c90..3a9c256155a 100644
--- a/components/script/dom/bindings/trace.rs
+++ b/components/script/dom/bindings/trace.rs
@@ -37,6 +37,7 @@ use dom::bindings::js::{JS, Root};
use dom::bindings::refcounted::Trusted;
use dom::bindings::utils::{Reflectable, Reflector, WindowProxyHandler};
use encoding::types::EncodingRef;
+use euclid::length::Length as EuclidLength;
use euclid::matrix2d::Matrix2D;
use euclid::rect::Rect;
use euclid::size::Size2D;
@@ -58,7 +59,7 @@ use net_traits::storage_task::StorageType;
use profile_traits::mem::ProfilerChan as MemProfilerChan;
use profile_traits::time::ProfilerChan as TimeProfilerChan;
use script_task::ScriptChan;
-use script_traits::UntrustedNodeAddress;
+use script_traits::{TimerEventChan, TimerEventId, TimerSource, UntrustedNodeAddress};
use selectors::parser::PseudoElement;
use serde::{Deserialize, Serialize};
use smallvec::SmallVec;
@@ -283,6 +284,7 @@ no_jsmanaged_fields!(HashSet<T>);
// These three are interdependent, if you plan to put jsmanaged data
// in one of these make sure it is propagated properly to containing structs
no_jsmanaged_fields!(SubpageId, WindowSizeData, PipelineId);
+no_jsmanaged_fields!(TimerEventId, TimerSource);
no_jsmanaged_fields!(WorkerId);
no_jsmanaged_fields!(QuirksMode);
no_jsmanaged_fields!(Runtime);
@@ -293,6 +295,7 @@ no_jsmanaged_fields!(WindowProxyHandler);
no_jsmanaged_fields!(UntrustedNodeAddress);
no_jsmanaged_fields!(LengthOrPercentageOrAuto);
no_jsmanaged_fields!(RGBA);
+no_jsmanaged_fields!(EuclidLength<Unit, T>);
no_jsmanaged_fields!(Matrix2D<T>);
no_jsmanaged_fields!(StorageType);
no_jsmanaged_fields!(CanvasGradientStop, LinearGradientStyle, RadialGradientStyle);
@@ -311,6 +314,13 @@ impl JSTraceable for Box<ScriptChan + Send> {
}
}
+impl JSTraceable for Box<TimerEventChan + Send> {
+ #[inline]
+ fn trace(&self, _trc: *mut JSTracer) {
+ // Do nothing
+ }
+}
+
impl JSTraceable for Box<FnBox(f64, )> {
#[inline]
fn trace(&self, _trc: *mut JSTracer) {
diff --git a/components/script/dom/dedicatedworkerglobalscope.rs b/components/script/dom/dedicatedworkerglobalscope.rs
index 413a47bb526..2c9a6f9f629 100644
--- a/components/script/dom/dedicatedworkerglobalscope.rs
+++ b/components/script/dom/dedicatedworkerglobalscope.rs
@@ -29,7 +29,8 @@ use msg::constellation_msg::PipelineId;
use net_traits::load_whole_resource;
use rand::random;
use script_task::ScriptTaskEventCategory::WorkerEvent;
-use script_task::{CommonScriptMsg, ScriptChan, ScriptPort, ScriptTask, StackRootTLS, TimerSource};
+use script_task::{ScriptTask, ScriptChan, ScriptPort, StackRootTLS, CommonScriptMsg};
+use script_traits::{TimerEvent, TimerEventChan, TimerSource};
use std::mem::replace;
use std::rc::Rc;
use std::sync::mpsc::{Receiver, RecvError, Select, Sender, channel};
@@ -101,6 +102,29 @@ impl ScriptPort for Receiver<(TrustedWorkerAddress, WorkerScriptMsg)> {
}
}
+/// A TimerEventChan that can be cloned freely and will silently send a TrustedWorkerAddress
+/// with timer events. While this SendableWorkerScriptChan is alive, the associated Worker
+/// object will remain alive.
+struct WorkerThreadTimerEventChan {
+ sender: Sender<(TrustedWorkerAddress, TimerEvent)>,
+ worker: TrustedWorkerAddress,
+}
+
+impl TimerEventChan for WorkerThreadTimerEventChan {
+ fn send(&self, event: TimerEvent) -> Result<(), ()> {
+ self.sender
+ .send((self.worker.clone(), event))
+ .map_err(|_| ())
+ }
+
+ fn clone(&self) -> Box<TimerEventChan + Send> {
+ box WorkerThreadTimerEventChan {
+ sender: self.sender.clone(),
+ worker: self.worker.clone(),
+ }
+ }
+}
+
/// Set the `worker` field of a related DedicatedWorkerGlobalScope object to a particular
/// value for the duration of this object's lifetime. This ensures that the related Worker
/// object only lives as long as necessary (ie. while events are being executed), while
@@ -127,6 +151,7 @@ impl<'a> Drop for AutoWorkerReset<'a> {
enum MixedMessage {
FromWorker((TrustedWorkerAddress, WorkerScriptMsg)),
+ FromScheduler((TrustedWorkerAddress, TimerEvent)),
FromDevtools(DevtoolScriptControlMsg),
}
@@ -139,6 +164,8 @@ pub struct DedicatedWorkerGlobalScope {
receiver: Receiver<(TrustedWorkerAddress, WorkerScriptMsg)>,
#[ignore_heap_size_of = "Defined in std"]
own_sender: Sender<(TrustedWorkerAddress, WorkerScriptMsg)>,
+ #[ignore_heap_size_of = "Defined in std"]
+ timer_event_port: Receiver<(TrustedWorkerAddress, TimerEvent)>,
#[ignore_heap_size_of = "Trusted<T> has unclear ownership like JS<T>"]
worker: DOMRefCell<Option<TrustedWorkerAddress>>,
#[ignore_heap_size_of = "Can't measure trait objects"]
@@ -154,14 +181,18 @@ impl DedicatedWorkerGlobalScope {
runtime: Rc<Runtime>,
parent_sender: Box<ScriptChan + Send>,
own_sender: Sender<(TrustedWorkerAddress, WorkerScriptMsg)>,
- receiver: Receiver<(TrustedWorkerAddress, WorkerScriptMsg)>)
+ receiver: Receiver<(TrustedWorkerAddress, WorkerScriptMsg)>,
+ timer_event_chan: Box<TimerEventChan + Send>,
+ timer_event_port: Receiver<(TrustedWorkerAddress, TimerEvent)>)
-> DedicatedWorkerGlobalScope {
+
DedicatedWorkerGlobalScope {
workerglobalscope: WorkerGlobalScope::new_inherited(
- init, worker_url, runtime, from_devtools_receiver),
+ init, worker_url, runtime, from_devtools_receiver, timer_event_chan),
id: id,
receiver: receiver,
own_sender: own_sender,
+ timer_event_port: timer_event_port,
parent_sender: parent_sender,
worker: DOMRefCell::new(None),
}
@@ -174,11 +205,13 @@ impl DedicatedWorkerGlobalScope {
runtime: Rc<Runtime>,
parent_sender: Box<ScriptChan + Send>,
own_sender: Sender<(TrustedWorkerAddress, WorkerScriptMsg)>,
- receiver: Receiver<(TrustedWorkerAddress, WorkerScriptMsg)>)
+ receiver: Receiver<(TrustedWorkerAddress, WorkerScriptMsg)>,
+ timer_event_chan: Box<TimerEventChan + Send>,
+ timer_event_port: Receiver<(TrustedWorkerAddress, TimerEvent)>)
-> Root<DedicatedWorkerGlobalScope> {
let scope = box DedicatedWorkerGlobalScope::new_inherited(
init, worker_url, id, from_devtools_receiver, runtime.clone(), parent_sender,
- own_sender, receiver);
+ own_sender, receiver, timer_event_chan, timer_event_port);
DedicatedWorkerGlobalScopeBinding::Wrap(runtime.cx(), scope)
}
@@ -214,9 +247,16 @@ impl DedicatedWorkerGlobalScope {
let (devtools_mpsc_chan, devtools_mpsc_port) = channel();
ROUTER.route_ipc_receiver_to_mpsc_sender(from_devtools_receiver, devtools_mpsc_chan);
+ let (timer_tx, timer_rx) = channel();
+ let timer_event_chan = box WorkerThreadTimerEventChan {
+ sender: timer_tx,
+ worker: worker.clone(),
+ };
+
let global = DedicatedWorkerGlobalScope::new(
init, url, id, devtools_mpsc_port, runtime.clone(),
- parent_sender.clone(), own_sender, receiver);
+ parent_sender.clone(), own_sender, receiver,
+ timer_event_chan, timer_rx);
// FIXME(njn): workers currently don't have a unique ID suitable for using in reporter
// registration (#6631), so we instead use a random number and cross our fingers.
let scope = global.upcast::<WorkerGlobalScope>();
@@ -263,13 +303,16 @@ impl DedicatedWorkerGlobalScope {
fn receive_event(&self) -> Result<MixedMessage, RecvError> {
let scope = self.upcast::<WorkerGlobalScope>();
let worker_port = &self.receiver;
+ let timer_event_port = &self.timer_event_port;
let devtools_port = scope.from_devtools_receiver();
let sel = Select::new();
let mut worker_handle = sel.handle(worker_port);
+ let mut timer_event_handle = sel.handle(timer_event_port);
let mut devtools_handle = sel.handle(devtools_port);
unsafe {
worker_handle.add();
+ timer_event_handle.add();
if scope.from_devtools_sender().is_some() {
devtools_handle.add();
}
@@ -277,6 +320,8 @@ impl DedicatedWorkerGlobalScope {
let ret = sel.wait();
if ret == worker_handle.id() {
Ok(MixedMessage::FromWorker(try!(worker_port.recv())))
+ } else if ret == timer_event_handle.id() {
+ Ok(MixedMessage::FromScheduler(try!(timer_event_port.recv())))
} else if ret == devtools_handle.id() {
Ok(MixedMessage::FromDevtools(try!(devtools_port.recv())))
} else {
@@ -301,11 +346,6 @@ impl DedicatedWorkerGlobalScope {
WorkerScriptMsg::Common(CommonScriptMsg::RefcountCleanup(addr)) => {
LiveDOMReferences::cleanup(addr);
},
- WorkerScriptMsg::Common(
- CommonScriptMsg::FireTimer(TimerSource::FromWorker, timer_id)) => {
- let scope = self.upcast::<WorkerGlobalScope>();
- scope.handle_fire_timer(timer_id);
- },
WorkerScriptMsg::Common(CommonScriptMsg::CollectReports(reports_chan)) => {
let scope = self.upcast::<WorkerGlobalScope>();
let cx = scope.get_cx();
@@ -313,9 +353,6 @@ impl DedicatedWorkerGlobalScope {
let reports = ScriptTask::get_reports(cx, path_seg);
reports_chan.send(reports);
},
- WorkerScriptMsg::Common(CommonScriptMsg::FireTimer(_, _)) => {
- panic!("obtained a fire timeout from window for the worker!")
- },
}
}
@@ -333,6 +370,18 @@ impl DedicatedWorkerGlobalScope {
_ => debug!("got an unusable devtools control message inside the worker!"),
}
},
+ MixedMessage::FromScheduler((linked_worker, timer_event)) => {
+ match timer_event {
+ TimerEvent(TimerSource::FromWorker, id) => {
+ let _ar = AutoWorkerReset::new(self, linked_worker);
+ let scope = self.upcast::<WorkerGlobalScope>();
+ scope.handle_fire_timer(id);
+ },
+ TimerEvent(_, _) => {
+ panic!("A worker received a TimerEvent from a window.")
+ }
+ }
+ }
MixedMessage::FromWorker((linked_worker, msg)) => {
let _ar = AutoWorkerReset::new(self, linked_worker);
self.handle_script_event(msg);
diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs
index 5a9233319c1..5906a28cb99 100644
--- a/components/script/dom/window.rs
+++ b/components/script/dom/window.rs
@@ -52,9 +52,9 @@ use num::traits::ToPrimitive;
use page::Page;
use profile_traits::mem;
use rustc_serialize::base64::{FromBase64, STANDARD, ToBase64};
-use script_task::{MainThreadScriptChan, SendableMainThreadScriptChan};
-use script_task::{MainThreadScriptMsg, ScriptChan, ScriptPort, TimerSource};
-use script_traits::ConstellationControlMsg;
+use script_task::{ScriptChan, ScriptPort, MainThreadScriptMsg};
+use script_task::{SendableMainThreadScriptChan, MainThreadScriptChan, MainThreadTimerEventChan};
+use script_traits::{ConstellationControlMsg, TimerEventChan, TimerEventId, TimerEventRequest, TimerSource};
use selectors::parser::PseudoElement;
use std::ascii::AsciiExt;
use std::borrow::ToOwned;
@@ -70,7 +70,7 @@ use std::sync::mpsc::TryRecvError::{Disconnected, Empty};
use std::sync::mpsc::{Receiver, Sender, channel};
use string_cache::Atom;
use time;
-use timers::{IsInterval, TimerCallback, TimerId, TimerManager};
+use timers::{ActiveTimers, IsInterval, TimerCallback};
use url::Url;
use util::geometry::{self, MAX_RECT};
use util::str::{DOMString, HTML_SPACE_CHARACTERS};
@@ -129,7 +129,9 @@ pub struct Window {
screen: MutNullableHeap<JS<Screen>>,
session_storage: MutNullableHeap<JS<Storage>>,
local_storage: MutNullableHeap<JS<Storage>>,
- timers: TimerManager,
+ #[ignore_heap_size_of = "channels are hard"]
+ scheduler_chan: Sender<TimerEventRequest>,
+ timers: ActiveTimers,
next_worker_id: Cell<WorkerId>,
@@ -425,8 +427,7 @@ impl WindowMethods for Window {
args,
timeout,
IsInterval::NonInterval,
- TimerSource::FromWindow(self.id.clone()),
- self.script_chan.clone())
+ TimerSource::FromWindow(self.id.clone()))
}
// https://html.spec.whatwg.org/multipage/#dom-windowtimers-settimeout
@@ -435,8 +436,7 @@ impl WindowMethods for Window {
args,
timeout,
IsInterval::NonInterval,
- TimerSource::FromWindow(self.id.clone()),
- self.script_chan.clone())
+ TimerSource::FromWindow(self.id.clone()))
}
// https://html.spec.whatwg.org/multipage/#dom-windowtimers-cleartimeout
@@ -450,8 +450,7 @@ impl WindowMethods for Window {
args,
timeout,
IsInterval::Interval,
- TimerSource::FromWindow(self.id.clone()),
- self.script_chan.clone())
+ TimerSource::FromWindow(self.id.clone()))
}
// https://html.spec.whatwg.org/multipage/#dom-windowtimers-setinterval
@@ -460,8 +459,7 @@ impl WindowMethods for Window {
args,
timeout,
IsInterval::Interval,
- TimerSource::FromWindow(self.id.clone()),
- self.script_chan.clone())
+ TimerSource::FromWindow(self.id.clone()))
}
// https://html.spec.whatwg.org/multipage/#dom-windowtimers-clearinterval
@@ -1076,7 +1074,7 @@ impl Window {
MainThreadScriptMsg::Navigate(self.id, LoadData::new(url))).unwrap();
}
- pub fn handle_fire_timer(&self, timer_id: TimerId) {
+ pub fn handle_fire_timer(&self, timer_id: TimerEventId) {
self.timers.fire_timer(timer_id, self);
self.reflow(ReflowGoal::ForDisplay, ReflowQueryType::NoQuery, ReflowReason::Timer);
}
@@ -1122,6 +1120,10 @@ impl Window {
self.constellation_chan.clone()
}
+ pub fn scheduler_chan(&self) -> Sender<TimerEventRequest> {
+ self.scheduler_chan.clone()
+ }
+
pub fn windowproxy_handler(&self) -> WindowProxyHandler {
WindowProxyHandler(self.dom_static.windowproxy_handler.0)
}
@@ -1267,6 +1269,8 @@ impl Window {
mem_profiler_chan: mem::ProfilerChan,
devtools_chan: Option<IpcSender<ScriptToDevtoolsControlMsg>>,
constellation_chan: ConstellationChan,
+ scheduler_chan: Sender<TimerEventRequest>,
+ timer_event_chan: MainThreadTimerEventChan,
layout_chan: LayoutChan,
id: PipelineId,
parent_info: Option<(PipelineId, SubpageId)>,
@@ -1299,7 +1303,8 @@ impl Window {
screen: Default::default(),
session_storage: Default::default(),
local_storage: Default::default(),
- timers: TimerManager::new(),
+ scheduler_chan: scheduler_chan.clone(),
+ timers: ActiveTimers::new(box timer_event_chan, scheduler_chan),
next_worker_id: Cell::new(WorkerId(0)),
id: id,
parent_info: parent_info,
diff --git a/components/script/dom/worker.rs b/components/script/dom/worker.rs
index e3fc2a202e5..b80f89720fa 100644
--- a/components/script/dom/worker.rs
+++ b/components/script/dom/worker.rs
@@ -72,6 +72,7 @@ impl Worker {
let resource_task = global.resource_task();
let constellation_chan = global.constellation_chan();
+ let scheduler_chan = global.scheduler_chan();
let (sender, receiver) = channel();
let worker = Worker::new(global, sender.clone());
@@ -101,6 +102,7 @@ impl Worker {
to_devtools_sender: global.devtools_chan(),
from_devtools_sender: optional_sender,
constellation_chan: constellation_chan,
+ scheduler_chan: scheduler_chan,
worker_id: worker_id,
};
DedicatedWorkerGlobalScope::run_worker_scope(
diff --git a/components/script/dom/workerglobalscope.rs b/components/script/dom/workerglobalscope.rs
index 40692b99e7e..abf7f935240 100644
--- a/components/script/dom/workerglobalscope.rs
+++ b/components/script/dom/workerglobalscope.rs
@@ -23,12 +23,13 @@ use js::rust::Runtime;
use msg::constellation_msg::{ConstellationChan, PipelineId, WorkerId};
use net_traits::{ResourceTask, load_whole_resource};
use profile_traits::mem;
-use script_task::{CommonScriptMsg, ScriptChan, ScriptPort, TimerSource};
+use script_task::{CommonScriptMsg, ScriptChan, ScriptPort};
+use script_traits::{TimerEventChan, TimerEventId, TimerEventRequest, TimerSource};
use std::cell::Cell;
use std::default::Default;
use std::rc::Rc;
-use std::sync::mpsc::Receiver;
-use timers::{IsInterval, TimerCallback, TimerId, TimerManager};
+use std::sync::mpsc::{Receiver, Sender};
+use timers::{ActiveTimers, IsInterval, TimerCallback};
use url::{Url, UrlParser};
use util::str::DOMString;
@@ -43,6 +44,7 @@ pub struct WorkerGlobalScopeInit {
pub to_devtools_sender: Option<IpcSender<ScriptToDevtoolsControlMsg>>,
pub from_devtools_sender: Option<IpcSender<DevtoolScriptControlMsg>>,
pub constellation_chan: ConstellationChan,
+ pub scheduler_chan: Sender<TimerEventRequest>,
pub worker_id: WorkerId,
}
@@ -61,7 +63,7 @@ pub struct WorkerGlobalScope {
navigator: MutNullableHeap<JS<WorkerNavigator>>,
console: MutNullableHeap<JS<Console>>,
crypto: MutNullableHeap<JS<Crypto>>,
- timers: TimerManager,
+ timers: ActiveTimers,
#[ignore_heap_size_of = "Defined in std"]
mem_profiler_chan: mem::ProfilerChan,
#[ignore_heap_size_of = "Defined in ipc-channel"]
@@ -83,13 +85,17 @@ pub struct WorkerGlobalScope {
#[ignore_heap_size_of = "Defined in std"]
constellation_chan: ConstellationChan,
+
+ #[ignore_heap_size_of = "Defined in std"]
+ scheduler_chan: Sender<TimerEventRequest>,
}
impl WorkerGlobalScope {
pub fn new_inherited(init: WorkerGlobalScopeInit,
worker_url: Url,
runtime: Rc<Runtime>,
- from_devtools_receiver: Receiver<DevtoolScriptControlMsg>)
+ from_devtools_receiver: Receiver<DevtoolScriptControlMsg>,
+ timer_event_chan: Box<TimerEventChan + Send>)
-> WorkerGlobalScope {
WorkerGlobalScope {
eventtarget: EventTarget::new_inherited(),
@@ -102,13 +108,14 @@ impl WorkerGlobalScope {
navigator: Default::default(),
console: Default::default(),
crypto: Default::default(),
- timers: TimerManager::new(),
+ timers: ActiveTimers::new(timer_event_chan, init.scheduler_chan.clone()),
mem_profiler_chan: init.mem_profiler_chan,
to_devtools_sender: init.to_devtools_sender,
from_devtools_sender: init.from_devtools_sender,
from_devtools_receiver: from_devtools_receiver,
devtools_wants_updates: Cell::new(false),
constellation_chan: init.constellation_chan,
+ scheduler_chan: init.scheduler_chan,
}
}
@@ -132,6 +139,10 @@ impl WorkerGlobalScope {
self.constellation_chan.clone()
}
+ pub fn scheduler_chan(&self) -> Sender<TimerEventRequest> {
+ self.scheduler_chan.clone()
+ }
+
pub fn get_cx(&self) -> *mut JSContext {
self.runtime.cx()
}
@@ -233,8 +244,7 @@ impl WorkerGlobalScopeMethods for WorkerGlobalScope {
args,
timeout,
IsInterval::NonInterval,
- TimerSource::FromWorker,
- self.script_chan())
+ TimerSource::FromWorker)
}
// https://html.spec.whatwg.org/multipage/#dom-windowtimers-setinterval
@@ -243,8 +253,7 @@ impl WorkerGlobalScopeMethods for WorkerGlobalScope {
args,
timeout,
IsInterval::NonInterval,
- TimerSource::FromWorker,
- self.script_chan())
+ TimerSource::FromWorker)
}
// https://html.spec.whatwg.org/multipage/#dom-windowtimers-clearinterval
@@ -258,8 +267,7 @@ impl WorkerGlobalScopeMethods for WorkerGlobalScope {
args,
timeout,
IsInterval::Interval,
- TimerSource::FromWorker,
- self.script_chan())
+ TimerSource::FromWorker)
}
// https://html.spec.whatwg.org/multipage/#dom-windowtimers-setinterval
@@ -268,8 +276,7 @@ impl WorkerGlobalScopeMethods for WorkerGlobalScope {
args,
timeout,
IsInterval::Interval,
- TimerSource::FromWorker,
- self.script_chan())
+ TimerSource::FromWorker)
}
// https://html.spec.whatwg.org/multipage/#dom-windowtimers-clearinterval
@@ -330,7 +337,7 @@ impl WorkerGlobalScope {
}
}
- pub fn handle_fire_timer(&self, timer_id: TimerId) {
+ pub fn handle_fire_timer(&self, timer_id: TimerEventId) {
self.timers.fire_timer(timer_id, self);
}
diff --git a/components/script/horribly_inefficient_timers.rs b/components/script/horribly_inefficient_timers.rs
deleted file mode 100644
index 2e1ca0f12c3..00000000000
--- a/components/script/horribly_inefficient_timers.rs
+++ /dev/null
@@ -1,31 +0,0 @@
-/* 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/. */
-
-/// A quick hack to work around the removal of [`std::old_io::timer::Timer`](
-/// http://doc.rust-lang.org/1.0.0-beta/std/old_io/timer/struct.Timer.html )
-
-use std::sync::mpsc::{Receiver, channel};
-use std::thread::{sleep_ms, spawn};
-
-pub fn oneshot(duration_ms: u32) -> Receiver<()> {
- let (tx, rx) = channel();
- spawn(move || {
- sleep_ms(duration_ms);
- let _ = tx.send(());
- });
- rx
-}
-
-pub fn periodic(duration_ms: u32) -> Receiver<()> {
- let (tx, rx) = channel();
- spawn(move || {
- loop {
- sleep_ms(duration_ms);
- if tx.send(()).is_err() {
- break
- }
- }
- });
- rx
-}
diff --git a/components/script/lib.rs b/components/script/lib.rs
index 5ebaa787dfd..5b1705c52f3 100644
--- a/components/script/lib.rs
+++ b/components/script/lib.rs
@@ -86,7 +86,6 @@ mod devtools;
pub mod document_loader;
#[macro_use]
pub mod dom;
-mod horribly_inefficient_timers;
pub mod layout_interface;
mod mem;
mod network_listener;
diff --git a/components/script/script_task.rs b/components/script/script_task.rs
index 79e749b82d6..a090d1c91ef 100644
--- a/components/script/script_task.rs
+++ b/components/script/script_task.rs
@@ -82,6 +82,7 @@ use script_traits::CompositorEvent::{MouseDownEvent, MouseUpEvent};
use script_traits::{CompositorEvent, ConstellationControlMsg};
use script_traits::{InitialScriptState, MouseButton, NewLayoutInfo};
use script_traits::{OpaqueScriptLayoutChannel, ScriptState, ScriptTaskFactory};
+use script_traits::{TimerEvent, TimerEventChan, TimerEventRequest, TimerSource};
use std::any::Any;
use std::borrow::ToOwned;
use std::cell::{Cell, RefCell};
@@ -96,7 +97,6 @@ use std::sync::mpsc::{Receiver, Select, Sender, channel};
use std::sync::{Arc, Mutex};
use string_cache::Atom;
use time::{Tm, now};
-use timers::TimerId;
use url::{Url, UrlParser};
use util::opts;
use util::str::DOMString;
@@ -156,12 +156,6 @@ impl InProgressLoad {
}
}
-#[derive(Copy, Clone)]
-pub enum TimerSource {
- FromWindow(PipelineId),
- FromWorker
-}
-
pub trait Runnable {
fn handler(self: Box<Self>);
}
@@ -175,6 +169,7 @@ enum MixedMessage {
FromScript(MainThreadScriptMsg),
FromDevtools(DevtoolScriptControlMsg),
FromImageCache(ImageCacheResult),
+ FromScheduler(TimerEvent),
}
/// Common messages used to control the event loops in both the script and the worker
@@ -182,10 +177,6 @@ pub enum CommonScriptMsg {
/// Requests that the script task measure its memory usage. The results are sent back via the
/// supplied channel.
CollectReports(ReportsChan),
- /// Fires a JavaScript timeout
- /// TimerSource must be FromWindow when dispatched to ScriptTask and
- /// must be FromWorker when dispatched to a DedicatedGlobalWorkerScope
- FireTimer(TimerSource, TimerId),
/// A DOM object's last pinned reference was removed (dispatched to all tasks).
RefcountCleanup(TrustedReference),
/// Generic message that encapsulates event handling.
@@ -205,6 +196,7 @@ pub enum ScriptTaskEventCategory {
NetworkEvent,
Resize,
ScriptEvent,
+ TimerEvent,
UpdateReplacedElement,
SetViewport,
WebSocketEvent,
@@ -327,6 +319,20 @@ impl MainThreadScriptChan {
}
}
+pub struct MainThreadTimerEventChan(Sender<TimerEvent>);
+
+impl TimerEventChan for MainThreadTimerEventChan {
+ fn send(&self, event: TimerEvent) -> Result<(), ()> {
+ let MainThreadTimerEventChan(ref chan) = *self;
+ chan.send(event).map_err(|_| ())
+ }
+
+ fn clone(&self) -> Box<TimerEventChan + Send> {
+ let MainThreadTimerEventChan(ref chan) = *self;
+ box MainThreadTimerEventChan((*chan).clone())
+ }
+}
+
pub struct StackRootTLS;
impl StackRootTLS {
@@ -408,6 +414,10 @@ pub struct ScriptTask {
/// List of pipelines that have been owned and closed by this script task.
closed_pipelines: RefCell<HashSet<PipelineId>>,
+
+ scheduler_chan: Sender<TimerEventRequest>,
+ timer_event_chan: Sender<TimerEvent>,
+ timer_event_port: Receiver<TimerEvent>,
}
/// In the event of task failure, all data on the stack runs its destructor. However, there
@@ -604,6 +614,8 @@ impl ScriptTask {
let image_cache_port =
ROUTER.route_ipc_receiver_to_new_mpsc_receiver(ipc_image_cache_port);
+ let (timer_event_chan, timer_event_port) = channel();
+
ScriptTask {
page: DOMRefCell::new(None),
incomplete_loads: DOMRefCell::new(vec!()),
@@ -631,6 +643,10 @@ impl ScriptTask {
js_runtime: Rc::new(runtime),
mouse_over_targets: DOMRefCell::new(vec!()),
closed_pipelines: RefCell::new(HashSet::new()),
+
+ scheduler_chan: state.scheduler_chan,
+ timer_event_chan: timer_event_chan,
+ timer_event_port: timer_event_port,
}
}
@@ -717,26 +733,30 @@ impl ScriptTask {
// Receive at least one message so we don't spinloop.
let mut event = {
let sel = Select::new();
- let mut port1 = sel.handle(&self.port);
- let mut port2 = sel.handle(&self.control_port);
- let mut port3 = sel.handle(&self.devtools_port);
- let mut port4 = sel.handle(&self.image_cache_port);
+ let mut script_port = sel.handle(&self.port);
+ let mut control_port = sel.handle(&self.control_port);
+ let mut timer_event_port = sel.handle(&self.timer_event_port);
+ let mut devtools_port = sel.handle(&self.devtools_port);
+ let mut image_cache_port = sel.handle(&self.image_cache_port);
unsafe {
- port1.add();
- port2.add();
+ script_port.add();
+ control_port.add();
+ timer_event_port.add();
if self.devtools_chan.is_some() {
- port3.add();
+ devtools_port.add();
}
- port4.add();
+ image_cache_port.add();
}
let ret = sel.wait();
- if ret == port1.id() {
+ if ret == script_port.id() {
MixedMessage::FromScript(self.port.recv().unwrap())
- } else if ret == port2.id() {
+ } else if ret == control_port.id() {
MixedMessage::FromConstellation(self.control_port.recv().unwrap())
- } else if ret == port3.id() {
+ } else if ret == timer_event_port.id() {
+ MixedMessage::FromScheduler(self.timer_event_port.recv().unwrap())
+ } else if ret == devtools_port.id() {
MixedMessage::FromDevtools(self.devtools_port.recv().unwrap())
- } else if ret == port4.id() {
+ } else if ret == image_cache_port.id() {
MixedMessage::FromImageCache(self.image_cache_port.recv().unwrap())
} else {
panic!("unexpected select result")
@@ -797,12 +817,15 @@ impl ScriptTask {
// on and execute the sequential non-resize events we've seen.
match self.control_port.try_recv() {
Err(_) => match self.port.try_recv() {
- Err(_) => match self.devtools_port.try_recv() {
- Err(_) => match self.image_cache_port.try_recv() {
- Err(_) => break,
- Ok(ev) => event = MixedMessage::FromImageCache(ev),
+ Err(_) => match self.timer_event_port.try_recv() {
+ Err(_) => match self.devtools_port.try_recv() {
+ Err(_) => match self.image_cache_port.try_recv() {
+ Err(_) => break,
+ Ok(ev) => event = MixedMessage::FromImageCache(ev),
+ },
+ Ok(ev) => event = MixedMessage::FromDevtools(ev),
},
- Ok(ev) => event = MixedMessage::FromDevtools(ev),
+ Ok(ev) => event = MixedMessage::FromScheduler(ev),
},
Ok(ev) => event = MixedMessage::FromScript(ev),
},
@@ -823,6 +846,7 @@ impl ScriptTask {
},
MixedMessage::FromConstellation(inner_msg) => self.handle_msg_from_constellation(inner_msg),
MixedMessage::FromScript(inner_msg) => self.handle_msg_from_script(inner_msg),
+ MixedMessage::FromScheduler(inner_msg) => self.handle_timer_event(inner_msg),
MixedMessage::FromDevtools(inner_msg) => self.handle_msg_from_devtools(inner_msg),
MixedMessage::FromImageCache(inner_msg) => self.handle_msg_from_image_cache(inner_msg),
}
@@ -871,7 +895,8 @@ impl ScriptTask {
*category,
_ => ScriptTaskEventCategory::ScriptEvent
}
- }
+ },
+ MixedMessage::FromScheduler(_) => ScriptTaskEventCategory::TimerEvent,
}
}
@@ -893,6 +918,7 @@ impl ScriptTask {
ScriptTaskEventCategory::ScriptEvent => ProfilerCategory::ScriptEvent,
ScriptTaskEventCategory::UpdateReplacedElement => ProfilerCategory::ScriptUpdateReplacedElement,
ScriptTaskEventCategory::SetViewport => ProfilerCategory::ScriptSetViewport,
+ ScriptTaskEventCategory::TimerEvent => ProfilerCategory::ScriptTimerEvent,
ScriptTaskEventCategory::WebSocketEvent => ProfilerCategory::ScriptWebSocketEvent,
ScriptTaskEventCategory::WorkerEvent => ProfilerCategory::ScriptWorkerEvent,
ScriptTaskEventCategory::XhrEvent => ProfilerCategory::ScriptXhrEvent,
@@ -966,12 +992,6 @@ impl ScriptTask {
runnable.handler(self),
MainThreadScriptMsg::DocumentLoadsComplete(id) =>
self.handle_loads_complete(id),
- MainThreadScriptMsg::Common(
- CommonScriptMsg::FireTimer(TimerSource::FromWindow(id), timer_id)) =>
- self.handle_fire_timer_msg(id, timer_id),
- MainThreadScriptMsg::Common(
- CommonScriptMsg::FireTimer(TimerSource::FromWorker, _)) =>
- panic!("Worker timeouts must not be sent to script task"),
MainThreadScriptMsg::Common(CommonScriptMsg::RunnableMsg(_, runnable)) =>
// The category of the runnable is ignored by the pattern, however
// it is still respected by profiling (see categorize_msg).
@@ -983,6 +1003,22 @@ impl ScriptTask {
}
}
+ fn handle_timer_event(&self, timer_event: TimerEvent) {
+ let TimerEvent(source, id) = timer_event;
+
+ let pipeline_id = match source {
+ TimerSource::FromWindow(pipeline_id) => pipeline_id,
+ TimerSource::FromWorker => panic!("Worker timeouts must not be sent to script task"),
+ };
+
+ let page = self.root_page();
+ let page = page.find(pipeline_id).expect("ScriptTask: received fire timer msg for a
+ pipeline ID not associated with this script task. This is a bug.");
+ let window = page.window();
+
+ window.r().handle_fire_timer(id);
+ }
+
fn handle_msg_from_devtools(&self, msg: DevtoolScriptControlMsg) {
let page = self.root_page();
match msg {
@@ -1272,15 +1308,6 @@ impl ScriptTask {
reports_chan.send(reports);
}
- /// Handles a timer that fired.
- fn handle_fire_timer_msg(&self, id: PipelineId, timer_id: TimerId) {
- let page = self.root_page();
- let page = page.find(id).expect("ScriptTask: received fire timer msg for a
- pipeline ID not associated with this script task. This is a bug.");
- let window = page.window();
- window.r().handle_fire_timer(timer_id);
- }
-
/// Handles freeze message
fn handle_freeze_msg(&self, id: PipelineId) {
// Workaround for a race condition when navigating before the initial page has
@@ -1587,6 +1614,8 @@ impl ScriptTask {
self.mem_profiler_chan.clone(),
self.devtools_chan.clone(),
self.constellation_chan.clone(),
+ self.scheduler_chan.clone(),
+ MainThreadTimerEventChan(self.timer_event_chan.clone()),
incomplete.layout_chan,
incomplete.pipeline_id,
incomplete.parent_info,
diff --git a/components/script/timers.rs b/components/script/timers.rs
index 1704ba3cbbf..6c982cb1ad4 100644
--- a/components/script/timers.rs
+++ b/components/script/timers.rs
@@ -8,259 +8,335 @@ use dom::bindings::codegen::Bindings::FunctionBinding::Function;
use dom::bindings::global::global_object_for_js_object;
use dom::bindings::utils::Reflectable;
use dom::window::ScriptHelpers;
-use horribly_inefficient_timers;
+use euclid::length::Length;
use js::jsapi::{HandleValue, Heap, RootedValue};
use js::jsval::{JSVal, UndefinedValue};
-use script_task::{CommonScriptMsg, ScriptChan, TimerSource};
-use std::borrow::ToOwned;
+use num::traits::Saturating;
+use script_traits::{MsDuration, precise_time_ms};
+use script_traits::{TimerEventChan, TimerEventId, TimerEventRequest, TimerSource};
use std::cell::Cell;
-use std::cmp;
-use std::collections::HashMap;
+use std::cmp::{self, Ord, Ordering};
use std::default::Default;
-use std::hash::{Hash, Hasher};
use std::rc::Rc;
-use std::sync::mpsc::Select;
-use std::sync::mpsc::{Sender, channel};
+use std::sync::mpsc::Sender;
use util::mem::HeapSizeOf;
use util::str::DOMString;
-use util::task::spawn_named;
-#[derive(JSTraceable, PartialEq, Eq, Copy, Clone, HeapSizeOf)]
-pub struct TimerId(i32);
+#[derive(JSTraceable, PartialEq, Eq, Copy, Clone, HeapSizeOf, Hash, PartialOrd, Ord)]
+pub struct TimerHandle(i32);
#[derive(JSTraceable, HeapSizeOf)]
#[privatize]
-struct TimerHandle {
- handle: TimerId,
- data: TimerData,
- #[ignore_heap_size_of = "channels are hard"]
- control_chan: Option<Sender<TimerControlMsg>>,
+pub struct ActiveTimers {
+ #[ignore_heap_size_of = "Defined in std"]
+ timer_event_chan: Box<TimerEventChan + Send>,
+ #[ignore_heap_size_of = "Defined in std"]
+ scheduler_chan: Sender<TimerEventRequest>,
+ next_timer_handle: Cell<TimerHandle>,
+ timers: DOMRefCell<Vec<Timer>>,
+ suspended_since: Cell<Option<MsDuration>>,
+ /// Initially 0, increased whenever the associated document is reactivated
+ /// by the amount of ms the document was inactive. The current time can be
+ /// offset back by this amount for a coherent time across document
+ /// activations.
+ suspension_offset: Cell<MsDuration>,
+ /// Calls to `fire_timer` with a different argument than this get ignored.
+ /// They were previously scheduled and got invalidated when
+ /// - timers were suspended,
+ /// - the timer it was scheduled for got canceled or
+ /// - a timer was added with an earlier callback time. In this case the
+ /// original timer is rescheduled when it is the next one to get called.
+ expected_event_id: Cell<TimerEventId>,
+ /// The nesting level of the currently executing timer task or 0.
+ nesting_level: Cell<u32>,
}
-#[derive(JSTraceable, Clone)]
-pub enum TimerCallback {
- StringTimerCallback(DOMString),
- FunctionTimerCallback(Rc<Function>)
+// Holder for the various JS values associated with setTimeout
+// (ie. function value to invoke and all arguments to pass
+// to the function when calling it)
+// TODO: Handle rooting during fire_timer when movable GC is turned on
+#[derive(JSTraceable, HeapSizeOf)]
+#[privatize]
+struct Timer {
+ handle: TimerHandle,
+ source: TimerSource,
+ callback: InternalTimerCallback,
+ is_interval: IsInterval,
+ nesting_level: u32,
+ duration: MsDuration,
+ next_call: MsDuration,
}
-impl HeapSizeOf for TimerCallback {
- fn heap_size_of_children(&self) -> usize {
- // FIXME: Rc<T> isn't HeapSizeOf and we can't ignore it due to #6870 and #6871
- 0
- }
+// Enum allowing more descriptive values for the is_interval field
+#[derive(JSTraceable, PartialEq, Copy, Clone, HeapSizeOf)]
+pub enum IsInterval {
+ Interval,
+ NonInterval,
}
-impl Hash for TimerId {
- fn hash<H: Hasher>(&self, state: &mut H) {
- let TimerId(id) = *self;
- id.hash(state);
+impl Ord for Timer {
+ fn cmp(&self, other: &Timer) -> Ordering {
+ match self.next_call.cmp(&other.next_call).reverse() {
+ Ordering::Equal => self.handle.cmp(&other.handle).reverse(),
+ res @ _ => res
+ }
}
}
-impl TimerHandle {
- fn cancel(&mut self) {
- self.control_chan.as_ref().map(|chan| chan.send(TimerControlMsg::Cancel).ok());
- }
- fn suspend(&mut self) {
- self.control_chan.as_ref().map(|chan| chan.send(TimerControlMsg::Suspend).ok());
- }
- fn resume(&mut self) {
- self.control_chan.as_ref().map(|chan| chan.send(TimerControlMsg::Resume).ok());
+impl PartialOrd for Timer {
+ fn partial_cmp(&self, other: &Timer) -> Option<Ordering> {
+ Some(self.cmp(other))
}
}
-#[derive(JSTraceable, HeapSizeOf)]
-#[privatize]
-pub struct TimerManager {
- active_timers: DOMRefCell<HashMap<TimerId, TimerHandle>>,
- next_timer_handle: Cell<i32>,
-}
-
-
-impl Drop for TimerManager {
- fn drop(&mut self) {
- for (_, timer_handle) in &mut *self.active_timers.borrow_mut() {
- timer_handle.cancel();
- }
+impl Eq for Timer {}
+impl PartialEq for Timer {
+ fn eq(&self, other: &Timer) -> bool {
+ self as *const Timer == other as *const Timer
}
}
-// Enum allowing more descriptive values for the is_interval field
-#[derive(JSTraceable, PartialEq, Copy, Clone, HeapSizeOf)]
-pub enum IsInterval {
- Interval,
- NonInterval,
-}
-
-// Messages sent control timers from script task
-#[derive(JSTraceable, PartialEq, Copy, Clone, Debug)]
-pub enum TimerControlMsg {
- Cancel,
- Suspend,
- Resume
+#[derive(Clone)]
+pub enum TimerCallback {
+ StringTimerCallback(DOMString),
+ FunctionTimerCallback(Rc<Function>),
}
-// Holder for the various JS values associated with setTimeout
-// (ie. function value to invoke and all arguments to pass
-// to the function when calling it)
-// TODO: Handle rooting during fire_timer when movable GC is turned on
-#[derive(JSTraceable, HeapSizeOf)]
-#[privatize]
-struct TimerData {
- is_interval: IsInterval,
- callback: TimerCallback,
- args: Vec<Heap<JSVal>>
+#[derive(JSTraceable, Clone)]
+enum InternalTimerCallback {
+ StringTimerCallback(DOMString),
+ FunctionTimerCallback(Rc<Function>, Rc<Vec<Heap<JSVal>>>),
}
-impl TimerManager {
- pub fn new() -> TimerManager {
- TimerManager {
- active_timers: DOMRefCell::new(HashMap::new()),
- next_timer_handle: Cell::new(0)
- }
+impl HeapSizeOf for InternalTimerCallback {
+ fn heap_size_of_children(&self) -> usize {
+ // FIXME: Rc<T> isn't HeapSizeOf and we can't ignore it due to #6870 and #6871
+ 0
}
+}
- pub fn suspend(&self) {
- for (_, timer_handle) in &mut *self.active_timers.borrow_mut() {
- timer_handle.suspend();
- }
- }
- pub fn resume(&self) {
- for (_, timer_handle) in &mut *self.active_timers.borrow_mut() {
- timer_handle.resume();
+impl ActiveTimers {
+ pub fn new(timer_event_chan: Box<TimerEventChan + Send>,
+ scheduler_chan: Sender<TimerEventRequest>)
+ -> ActiveTimers {
+ ActiveTimers {
+ timer_event_chan: timer_event_chan,
+ scheduler_chan: scheduler_chan,
+ next_timer_handle: Cell::new(TimerHandle(1)),
+ timers: DOMRefCell::new(Vec::new()),
+ suspended_since: Cell::new(None),
+ suspension_offset: Cell::new(Length::new(0)),
+ expected_event_id: Cell::new(TimerEventId(0)),
+ nesting_level: Cell::new(0),
}
}
- #[allow(unsafe_code)]
+ // see https://html.spec.whatwg.org/multipage/#timer-initialisation-steps
pub fn set_timeout_or_interval(&self,
- callback: TimerCallback,
- arguments: Vec<HandleValue>,
- timeout: i32,
- is_interval: IsInterval,
- source: TimerSource,
- script_chan: Box<ScriptChan + Send>)
- -> i32 {
- let duration_ms = cmp::max(0, timeout) as u32;
- let handle = self.next_timer_handle.get();
- self.next_timer_handle.set(handle + 1);
-
- // Spawn a new timer task; it will dispatch the `CommonScriptMsg::FireTimer`
- // to the relevant script handler that will deal with it.
- let (control_chan, control_port) = channel();
- let spawn_name = match source {
- TimerSource::FromWindow(_) if is_interval == IsInterval::Interval => "Window:SetInterval",
- TimerSource::FromWorker if is_interval == IsInterval::Interval => "Worker:SetInterval",
- TimerSource::FromWindow(_) => "Window:SetTimeout",
- TimerSource::FromWorker => "Worker:SetTimeout",
- }.to_owned();
- spawn_named(spawn_name, move || {
- let timeout_port = if is_interval == IsInterval::Interval {
- horribly_inefficient_timers::periodic(duration_ms)
- } else {
- horribly_inefficient_timers::oneshot(duration_ms)
- };
- let control_port = control_port;
-
- let select = Select::new();
- let mut timeout_handle = select.handle(&timeout_port);
- unsafe { timeout_handle.add() };
- let mut control_handle = select.handle(&control_port);
- unsafe { control_handle.add() };
-
- loop {
- let id = select.wait();
-
- if id == timeout_handle.id() {
- timeout_port.recv().unwrap();
- if script_chan.send(CommonScriptMsg::FireTimer(source, TimerId(handle))).is_err() {
- break;
- }
-
- if is_interval == IsInterval::NonInterval {
- break;
- }
- } else if id == control_handle.id() {
- match control_port.recv().unwrap() {
- TimerControlMsg::Suspend => {
- let msg = control_port.recv().unwrap();
- match msg {
- TimerControlMsg::Suspend => panic!("Nothing to suspend!"),
- TimerControlMsg::Resume => {},
- TimerControlMsg::Cancel => {
- break;
- },
- }
- },
- TimerControlMsg::Resume => panic!("Nothing to resume!"),
- TimerControlMsg::Cancel => {
- break;
- }
- }
+ callback: TimerCallback,
+ arguments: Vec<HandleValue>,
+ timeout: i32,
+ is_interval: IsInterval,
+ source: TimerSource)
+ -> i32 {
+ assert!(self.suspended_since.get().is_none());
+
+ // step 3
+ let TimerHandle(new_handle) = self.next_timer_handle.get();
+ self.next_timer_handle.set(TimerHandle(new_handle + 1));
+
+ let timeout = cmp::max(0, timeout);
+ // step 7
+ let duration = self.clamp_duration(Length::new(timeout as u64));
+ let next_call = self.base_time() + duration;
+
+ let callback = match callback {
+ TimerCallback::StringTimerCallback(code_str) =>
+ InternalTimerCallback::StringTimerCallback(code_str),
+ TimerCallback::FunctionTimerCallback(function) => {
+ // This is a bit complicated, but this ensures that the vector's
+ // buffer isn't reallocated (and moved) after setting the Heap values
+ let mut args = Vec::with_capacity(arguments.len());
+ for _ in 0..arguments.len() {
+ args.push(Heap::default());
}
- }
- });
- let timer_id = TimerId(handle);
- let timer = TimerHandle {
- handle: timer_id,
- control_chan: Some(control_chan),
- data: TimerData {
- is_interval: is_interval,
- callback: callback,
- args: Vec::with_capacity(arguments.len())
+ for (i, item) in arguments.iter().enumerate() {
+ args.get_mut(i).unwrap().set(item.get());
+ }
+ InternalTimerCallback::FunctionTimerCallback(function, Rc::new(args))
}
};
- self.active_timers.borrow_mut().insert(timer_id, timer);
-
- // This is a bit complicated, but this ensures that the vector's
- // buffer isn't reallocated (and moved) after setting the Heap values
- let mut timers = self.active_timers.borrow_mut();
- let mut timer = timers.get_mut(&timer_id).unwrap();
- for _ in 0..arguments.len() {
- timer.data.args.push(Heap::default());
- }
- for (i, item) in arguments.iter().enumerate() {
- timer.data.args.get_mut(i).unwrap().set(item.get());
+
+ let timer = Timer {
+ handle: TimerHandle(new_handle),
+ source: source,
+ callback: callback,
+ is_interval: is_interval,
+ duration: duration,
+ // step 6
+ nesting_level: self.nesting_level.get() + 1,
+ next_call: next_call,
+ };
+
+ self.insert_timer(timer);
+
+ let TimerHandle(max_handle) = self.timers.borrow().last().unwrap().handle;
+ if max_handle == new_handle {
+ self.schedule_timer_call();
}
- handle
+
+ // step 10
+ new_handle
}
pub fn clear_timeout_or_interval(&self, handle: i32) {
- let mut timer_handle = self.active_timers.borrow_mut().remove(&TimerId(handle));
- match timer_handle {
- Some(ref mut handle) => handle.cancel(),
- None => {}
+ let handle = TimerHandle(handle);
+ let was_next = self.is_next_timer(handle);
+
+ self.timers.borrow_mut().retain(|t| t.handle != handle);
+
+ if was_next {
+ self.invalidate_expected_event_id();
+ self.schedule_timer_call();
}
}
+ // see https://html.spec.whatwg.org/multipage/#timer-initialisation-steps
#[allow(unsafe_code)]
- pub fn fire_timer<T: Reflectable>(&self, timer_id: TimerId, this: &T) {
-
- let (is_interval, callback, args): (IsInterval, TimerCallback, Vec<JSVal>) =
- match self.active_timers.borrow().get(&timer_id) {
- Some(timer_handle) =>
- (timer_handle.data.is_interval,
- timer_handle.data.callback.clone(),
- timer_handle.data.args.iter().map(|arg| arg.get()).collect()),
- None => return,
+ pub fn fire_timer<T: Reflectable>(&self, id: TimerEventId, this: &T) {
+ let expected_id = self.expected_event_id.get();
+ if expected_id != id {
+ debug!("ignoring timer fire event {:?} (expected {:?}", id, expected_id);
+ return;
+ }
+
+ assert!(self.suspended_since.get().is_none());
+
+ let base_time = self.base_time();
+
+ // Since the event id was the expected one, at least one timer should be due.
+ assert!(base_time >= self.timers.borrow().last().unwrap().next_call);
+
+ loop {
+ let timer = {
+ let mut timers = self.timers.borrow_mut();
+
+ if timers.is_empty() || timers.last().unwrap().next_call > base_time {
+ break;
+ }
+
+ timers.pop().unwrap()
};
+ let callback = timer.callback.clone();
- match callback {
- TimerCallback::FunctionTimerCallback(function) => {
- let arg_handles = args.iter().by_ref().map(|arg| unsafe {
- HandleValue::from_marked_location(arg)
- }).collect();
- let _ = function.Call_(this, arg_handles, Report);
- }
- TimerCallback::StringTimerCallback(code_str) => {
- let proxy = this.reflector().get_jsobject();
- let cx = global_object_for_js_object(proxy.get()).r().get_cx();
- let mut rval = RootedValue::new(cx, UndefinedValue());
- this.evaluate_js_on_global_with_result(&code_str, rval.handle_mut());
+ // prep for step 6 in nested set_timeout_or_interval calls
+ self.nesting_level.set(timer.nesting_level);
+
+ // step 4.3
+ if timer.is_interval == IsInterval::Interval {
+ let mut timer = timer;
+
+ // step 7
+ timer.duration = self.clamp_duration(timer.duration);
+ // step 8, 9
+ timer.nesting_level += 1;
+ timer.next_call = base_time + timer.duration;
+ self.insert_timer(timer);
}
+
+ // step 14
+ match callback {
+ InternalTimerCallback::StringTimerCallback(code_str) => {
+ let proxy = this.reflector().get_jsobject();
+ let cx = global_object_for_js_object(proxy.get()).r().get_cx();
+ let mut rval = RootedValue::new(cx, UndefinedValue());
+
+ this.evaluate_js_on_global_with_result(&code_str, rval.handle_mut());
+ },
+ InternalTimerCallback::FunctionTimerCallback(function, arguments) => {
+ let arguments: Vec<JSVal> = arguments.iter().map(|arg| arg.get()).collect();
+ let arguments = arguments.iter().by_ref().map(|arg| unsafe {
+ HandleValue::from_marked_location(arg)
+ }).collect();
+
+ let _ = function.Call_(this, arguments, Report);
+ }
+ };
+
+ self.nesting_level.set(0);
}
- if is_interval == IsInterval::NonInterval {
- self.active_timers.borrow_mut().remove(&timer_id);
+ self.schedule_timer_call();
+ }
+
+ fn insert_timer(&self, timer: Timer) {
+ let mut timers = self.timers.borrow_mut();
+ let insertion_index = timers.binary_search(&timer).err().unwrap();
+ timers.insert(insertion_index, timer);
+ }
+
+ fn is_next_timer(&self, handle: TimerHandle) -> bool {
+ match self.timers.borrow().last() {
+ None => false,
+ Some(ref max_timer) => max_timer.handle == handle
}
}
+
+ fn schedule_timer_call(&self) {
+ assert!(self.suspended_since.get().is_none());
+
+ let timers = self.timers.borrow();
+
+ if let Some(timer) = timers.last() {
+ let expected_event_id = self.invalidate_expected_event_id();
+
+ let delay = Length::new(timer.next_call.get().saturating_sub(precise_time_ms().get()));
+ let request = TimerEventRequest(self.timer_event_chan.clone(), timer.source,
+ expected_event_id, delay);
+ self.scheduler_chan.send(request).unwrap();
+ }
+ }
+
+ pub fn suspend(&self) {
+ assert!(self.suspended_since.get().is_none());
+
+ self.suspended_since.set(Some(precise_time_ms()));
+ self.invalidate_expected_event_id();
+ }
+
+ pub fn resume(&self) {
+ assert!(self.suspended_since.get().is_some());
+
+ let additional_offset = match self.suspended_since.get() {
+ Some(suspended_since) => precise_time_ms() - suspended_since,
+ None => panic!("Timers are not suspended.")
+ };
+
+ self.suspension_offset.set(self.suspension_offset.get() + additional_offset);
+
+ self.schedule_timer_call();
+ }
+
+ fn base_time(&self) -> MsDuration {
+ precise_time_ms() - self.suspension_offset.get()
+ }
+
+ // see step 7 of https://html.spec.whatwg.org/multipage/#timer-initialisation-steps
+ fn clamp_duration(&self, unclamped: MsDuration) -> MsDuration {
+ let ms = if self.nesting_level.get() > 5 {
+ 4
+ } else {
+ 0
+ };
+
+ cmp::max(Length::new(ms), unclamped)
+ }
+
+ fn invalidate_expected_event_id(&self) -> TimerEventId {
+ let TimerEventId(currently_expected) = self.expected_event_id.get();
+ let next_id = TimerEventId(currently_expected + 1);
+ debug!("invalidating expected timer (was {:?}, now {:?}", currently_expected, next_id);
+ self.expected_event_id.set(next_id);
+ next_id
+ }
}
diff --git a/components/script_traits/Cargo.toml b/components/script_traits/Cargo.toml
index d038ce36f39..11e3b077096 100644
--- a/components/script_traits/Cargo.toml
+++ b/components/script_traits/Cargo.toml
@@ -38,3 +38,4 @@ libc = "0.1"
euclid = "0.2"
serde = "0.6"
serde_macros = "0.5"
+time = "0.1.12"
diff --git a/components/script_traits/lib.rs b/components/script_traits/lib.rs
index 7693529d4b1..3c04fa36dda 100644
--- a/components/script_traits/lib.rs
+++ b/components/script_traits/lib.rs
@@ -19,11 +19,13 @@ extern crate msg;
extern crate net_traits;
extern crate profile_traits;
extern crate serde;
+extern crate time;
extern crate url;
extern crate util;
use app_units::Au;
use devtools_traits::ScriptToDevtoolsControlMsg;
+use euclid::length::Length;
use euclid::point::Point2D;
use euclid::rect::Rect;
use ipc_channel::ipc::{IpcReceiver, IpcSender};
@@ -36,10 +38,11 @@ use msg::webdriver_msg::WebDriverScriptCommand;
use net_traits::ResourceTask;
use net_traits::image_cache_task::ImageCacheTask;
use net_traits::storage_task::StorageTask;
-use profile_traits::{mem, time};
+use profile_traits::mem;
use std::any::Any;
use std::sync::mpsc::{Receiver, Sender};
use url::Url;
+use util::mem::HeapSizeOf;
/// The address of a node. Layout sends these back. They must be validated via
/// `from_untrusted_node_address` before they can be used, because we do not trust layout.
@@ -177,6 +180,56 @@ pub enum CompositorEvent {
/// crates that don't need to know about them.
pub struct OpaqueScriptLayoutChannel(pub (Box<Any + Send>, Box<Any + Send>));
+/// Requests a TimerEvent-Message be sent after the given duration.
+pub struct TimerEventRequest(pub Box<TimerEventChan + Send>, pub TimerSource, pub TimerEventId, pub MsDuration);
+
+/// Notifies the script task to fire due timers.
+/// TimerSource must be FromWindow when dispatched to ScriptTask and
+/// must be FromWorker when dispatched to a DedicatedGlobalWorkerScope
+pub struct TimerEvent(pub TimerSource, pub TimerEventId);
+
+/// A cloneable interface for sending timer events.
+pub trait TimerEventChan {
+ /// Send a timer event to the associated event loop.
+ fn send(&self, msg: TimerEvent) -> Result<(), ()>;
+ /// Clone this handle.
+ fn clone(&self) -> Box<TimerEventChan + Send>;
+}
+
+/// Describes the task that requested the TimerEvent.
+#[derive(Copy, Clone, HeapSizeOf)]
+pub enum TimerSource {
+ /// The event was requested from a window (ScriptTask).
+ FromWindow(PipelineId),
+ /// The event was requested from a worker (DedicatedGlobalWorkerScope).
+ FromWorker
+}
+
+/// The id to be used for a TimerEvent is defined by the corresponding TimerEventRequest.
+#[derive(PartialEq, Eq, Copy, Clone, Debug, HeapSizeOf)]
+pub struct TimerEventId(pub u32);
+
+/// Unit of measurement.
+#[derive(Clone, Copy, HeapSizeOf)]
+pub enum Milliseconds {}
+/// Unit of measurement.
+#[derive(Clone, Copy, HeapSizeOf)]
+pub enum Nanoseconds {}
+
+/// Amount of milliseconds.
+pub type MsDuration = Length<Milliseconds, u64>;
+/// Amount of nanoseconds.
+pub type NsDuration = Length<Nanoseconds, u64>;
+
+/// Returns the duration since an unspecified epoch measured in ms.
+pub fn precise_time_ms() -> MsDuration {
+ Length::new(time::precise_time_ns() / (1000 * 1000))
+}
+/// Returns the duration since an unspecified epoch measured in ns.
+pub fn precise_time_ns() -> NsDuration {
+ Length::new(time::precise_time_ns())
+}
+
/// Data needed to construct a script thread.
pub struct InitialScriptState {
/// The ID of the pipeline with which this script thread is associated.
@@ -192,6 +245,8 @@ pub struct InitialScriptState {
pub control_port: Receiver<ConstellationControlMsg>,
/// A channel on which messages can be sent to the constellation from script.
pub constellation_chan: ConstellationChan,
+ /// A channel to schedule timer events.
+ pub scheduler_chan: Sender<TimerEventRequest>,
/// Information that script sends out when it panics.
pub failure_info: Failure,
/// A channel to the resource manager task.
@@ -201,7 +256,7 @@ pub struct InitialScriptState {
/// A channel to the image cache task.
pub image_cache_task: ImageCacheTask,
/// A channel to the time profiler thread.
- pub time_profiler_chan: time::ProfilerChan,
+ pub time_profiler_chan: profile_traits::time::ProfilerChan,
/// A channel to the memory profiler thread.
pub mem_profiler_chan: mem::ProfilerChan,
/// A channel to the developer tools, if applicable.
diff --git a/components/servo/Cargo.lock b/components/servo/Cargo.lock
index bf251e5fc02..513cbcaa38f 100644
--- a/components/servo/Cargo.lock
+++ b/components/servo/Cargo.lock
@@ -1567,6 +1567,7 @@ dependencies = [
"profile_traits 0.0.1",
"serde 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_macros 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "time 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)",
"url 0.2.37 (registry+https://github.com/rust-lang/crates.io-index)",
"util 0.0.1",
]
diff --git a/ports/cef/Cargo.lock b/ports/cef/Cargo.lock
index 12d7e6d39de..0d49024f8f8 100644
--- a/ports/cef/Cargo.lock
+++ b/ports/cef/Cargo.lock
@@ -1491,6 +1491,7 @@ dependencies = [
"profile_traits 0.0.1",
"serde 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_macros 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "time 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)",
"url 0.2.37 (registry+https://github.com/rust-lang/crates.io-index)",
"util 0.0.1",
]
diff --git a/ports/gonk/Cargo.lock b/ports/gonk/Cargo.lock
index deeeaf1a3d5..c3d7ac2ade3 100644
--- a/ports/gonk/Cargo.lock
+++ b/ports/gonk/Cargo.lock
@@ -1353,6 +1353,7 @@ dependencies = [
"profile_traits 0.0.1",
"serde 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_macros 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "time 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)",
"url 0.2.37 (registry+https://github.com/rust-lang/crates.io-index)",
"util 0.0.1",
]