mirror of
https://github.com/servo/servo.git
synced 2025-08-04 13:10:20 +01:00
Timers are scheduled by a dedicated per-constellation thread.
This commit is contained in:
parent
674589c370
commit
553a0dbefd
21 changed files with 786 additions and 334 deletions
|
@ -41,6 +41,7 @@ use profile_traits::mem;
|
||||||
use profile_traits::time;
|
use profile_traits::time;
|
||||||
use script_traits::{CompositorEvent, ConstellationControlMsg, LayoutControlMsg};
|
use script_traits::{CompositorEvent, ConstellationControlMsg, LayoutControlMsg};
|
||||||
use script_traits::{ScriptState, ScriptTaskFactory};
|
use script_traits::{ScriptState, ScriptTaskFactory};
|
||||||
|
use script_traits::{TimerEventRequest};
|
||||||
use std::borrow::ToOwned;
|
use std::borrow::ToOwned;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::io::{self, Write};
|
use std::io::{self, Write};
|
||||||
|
@ -49,6 +50,7 @@ use std::mem::replace;
|
||||||
use std::process;
|
use std::process;
|
||||||
use std::sync::mpsc::{Receiver, Sender, channel};
|
use std::sync::mpsc::{Receiver, Sender, channel};
|
||||||
use style_traits::viewport::ViewportConstraints;
|
use style_traits::viewport::ViewportConstraints;
|
||||||
|
use timer_scheduler::TimerScheduler;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
use util::cursor::Cursor;
|
use util::cursor::Cursor;
|
||||||
use util::geometry::PagePx;
|
use util::geometry::PagePx;
|
||||||
|
@ -135,6 +137,8 @@ pub struct Constellation<LTF, STF> {
|
||||||
|
|
||||||
/// A list of in-process senders to `WebGLPaintTask`s.
|
/// A list of in-process senders to `WebGLPaintTask`s.
|
||||||
webgl_paint_tasks: Vec<Sender<CanvasMsg>>,
|
webgl_paint_tasks: Vec<Sender<CanvasMsg>>,
|
||||||
|
|
||||||
|
scheduler_chan: Sender<TimerEventRequest>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// State needed to construct a constellation.
|
/// State needed to construct a constellation.
|
||||||
|
@ -280,6 +284,7 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
|
||||||
webdriver: WebDriverData::new(),
|
webdriver: WebDriverData::new(),
|
||||||
canvas_paint_tasks: Vec::new(),
|
canvas_paint_tasks: Vec::new(),
|
||||||
webgl_paint_tasks: Vec::new(),
|
webgl_paint_tasks: Vec::new(),
|
||||||
|
scheduler_chan: TimerScheduler::start(),
|
||||||
};
|
};
|
||||||
let namespace_id = constellation.next_pipeline_namespace_id();
|
let namespace_id = constellation.next_pipeline_namespace_id();
|
||||||
PipelineNamespace::install(namespace_id);
|
PipelineNamespace::install(namespace_id);
|
||||||
|
@ -317,6 +322,7 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
|
||||||
id: pipeline_id,
|
id: pipeline_id,
|
||||||
parent_info: parent_info,
|
parent_info: parent_info,
|
||||||
constellation_chan: self.chan.clone(),
|
constellation_chan: self.chan.clone(),
|
||||||
|
scheduler_chan: self.scheduler_chan.clone(),
|
||||||
compositor_proxy: self.compositor_proxy.clone_compositor_proxy(),
|
compositor_proxy: self.compositor_proxy.clone_compositor_proxy(),
|
||||||
devtools_chan: self.devtools_chan.clone(),
|
devtools_chan: self.devtools_chan.clone(),
|
||||||
image_cache_task: self.image_cache_task.clone(),
|
image_cache_task: self.image_cache_task.clone(),
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#![feature(iter_cmp)]
|
#![feature(iter_cmp)]
|
||||||
#![feature(slice_bytes)]
|
#![feature(slice_bytes)]
|
||||||
#![feature(vec_push_all)]
|
#![feature(vec_push_all)]
|
||||||
|
#![feature(mpsc_select)]
|
||||||
#![feature(plugin)]
|
#![feature(plugin)]
|
||||||
#![plugin(plugins)]
|
#![plugin(plugins)]
|
||||||
|
|
||||||
|
@ -54,6 +55,7 @@ mod compositor_layer;
|
||||||
mod headless;
|
mod headless;
|
||||||
mod scrolling;
|
mod scrolling;
|
||||||
mod surface_map;
|
mod surface_map;
|
||||||
|
mod timer_scheduler;
|
||||||
pub mod compositor_task;
|
pub mod compositor_task;
|
||||||
pub mod constellation;
|
pub mod constellation;
|
||||||
pub mod pipeline;
|
pub mod pipeline;
|
||||||
|
|
|
@ -24,6 +24,7 @@ use profile_traits::mem as profile_mem;
|
||||||
use profile_traits::time;
|
use profile_traits::time;
|
||||||
use script_traits::{ConstellationControlMsg, InitialScriptState};
|
use script_traits::{ConstellationControlMsg, InitialScriptState};
|
||||||
use script_traits::{LayoutControlMsg, NewLayoutInfo, ScriptTaskFactory};
|
use script_traits::{LayoutControlMsg, NewLayoutInfo, ScriptTaskFactory};
|
||||||
|
use script_traits::{TimerEventRequest};
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::sync::mpsc::{Receiver, Sender, channel};
|
use std::sync::mpsc::{Receiver, Sender, channel};
|
||||||
|
@ -75,6 +76,8 @@ pub struct InitialPipelineState {
|
||||||
pub parent_info: Option<(PipelineId, SubpageId)>,
|
pub parent_info: Option<(PipelineId, SubpageId)>,
|
||||||
/// A channel to the associated constellation.
|
/// A channel to the associated constellation.
|
||||||
pub constellation_chan: ConstellationChan,
|
pub constellation_chan: ConstellationChan,
|
||||||
|
/// A channel to schedule timer events.
|
||||||
|
pub scheduler_chan: Sender<TimerEventRequest>,
|
||||||
/// A channel to the compositor.
|
/// A channel to the compositor.
|
||||||
pub compositor_proxy: Box<CompositorProxy + 'static + Send>,
|
pub compositor_proxy: Box<CompositorProxy + 'static + Send>,
|
||||||
/// A channel to the developer tools, if applicable.
|
/// A channel to the developer tools, if applicable.
|
||||||
|
@ -181,6 +184,7 @@ impl Pipeline {
|
||||||
id: state.id,
|
id: state.id,
|
||||||
parent_info: state.parent_info,
|
parent_info: state.parent_info,
|
||||||
constellation_chan: state.constellation_chan,
|
constellation_chan: state.constellation_chan,
|
||||||
|
scheduler_chan: state.scheduler_chan,
|
||||||
compositor_proxy: state.compositor_proxy,
|
compositor_proxy: state.compositor_proxy,
|
||||||
devtools_chan: script_to_devtools_chan,
|
devtools_chan: script_to_devtools_chan,
|
||||||
image_cache_task: state.image_cache_task,
|
image_cache_task: state.image_cache_task,
|
||||||
|
@ -316,6 +320,7 @@ pub struct PipelineContent {
|
||||||
id: PipelineId,
|
id: PipelineId,
|
||||||
parent_info: Option<(PipelineId, SubpageId)>,
|
parent_info: Option<(PipelineId, SubpageId)>,
|
||||||
constellation_chan: ConstellationChan,
|
constellation_chan: ConstellationChan,
|
||||||
|
scheduler_chan: Sender<TimerEventRequest>,
|
||||||
compositor_proxy: Box<CompositorProxy + Send + 'static>,
|
compositor_proxy: Box<CompositorProxy + Send + 'static>,
|
||||||
devtools_chan: Option<IpcSender<ScriptToDevtoolsControlMsg>>,
|
devtools_chan: Option<IpcSender<ScriptToDevtoolsControlMsg>>,
|
||||||
image_cache_task: ImageCacheTask,
|
image_cache_task: ImageCacheTask,
|
||||||
|
@ -361,6 +366,7 @@ impl PipelineContent {
|
||||||
control_chan: self.script_chan.clone(),
|
control_chan: self.script_chan.clone(),
|
||||||
control_port: mem::replace(&mut self.script_port, None).unwrap(),
|
control_port: mem::replace(&mut self.script_port, None).unwrap(),
|
||||||
constellation_chan: self.constellation_chan.clone(),
|
constellation_chan: self.constellation_chan.clone(),
|
||||||
|
scheduler_chan: self.scheduler_chan.clone(),
|
||||||
failure_info: self.failure.clone(),
|
failure_info: self.failure.clone(),
|
||||||
resource_task: self.resource_task,
|
resource_task: self.resource_task,
|
||||||
storage_task: self.storage_task.clone(),
|
storage_task: self.storage_task.clone(),
|
||||||
|
|
221
components/compositing/timer_scheduler.rs
Normal file
221
components/compositing/timer_scheduler.rs
Normal file
|
@ -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)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -92,6 +92,7 @@ impl Formattable for ProfilerCategory {
|
||||||
ProfilerCategory::ScriptEvent => "Script Event",
|
ProfilerCategory::ScriptEvent => "Script Event",
|
||||||
ProfilerCategory::ScriptUpdateReplacedElement => "Script Update Replaced Element",
|
ProfilerCategory::ScriptUpdateReplacedElement => "Script Update Replaced Element",
|
||||||
ProfilerCategory::ScriptSetViewport => "Script Set Viewport",
|
ProfilerCategory::ScriptSetViewport => "Script Set Viewport",
|
||||||
|
ProfilerCategory::ScriptTimerEvent => "Script Timer Event",
|
||||||
ProfilerCategory::ScriptWebSocketEvent => "Script Web Socket Event",
|
ProfilerCategory::ScriptWebSocketEvent => "Script Web Socket Event",
|
||||||
ProfilerCategory::ScriptWorkerEvent => "Script Worker Event",
|
ProfilerCategory::ScriptWorkerEvent => "Script Worker Event",
|
||||||
ProfilerCategory::ScriptXhrEvent => "Script Xhr Event",
|
ProfilerCategory::ScriptXhrEvent => "Script Xhr Event",
|
||||||
|
|
|
@ -70,6 +70,7 @@ pub enum ProfilerCategory {
|
||||||
ScriptEvent,
|
ScriptEvent,
|
||||||
ScriptUpdateReplacedElement,
|
ScriptUpdateReplacedElement,
|
||||||
ScriptSetViewport,
|
ScriptSetViewport,
|
||||||
|
ScriptTimerEvent,
|
||||||
ScriptWebSocketEvent,
|
ScriptWebSocketEvent,
|
||||||
ScriptWorkerEvent,
|
ScriptWorkerEvent,
|
||||||
ScriptXhrEvent,
|
ScriptXhrEvent,
|
||||||
|
|
|
@ -22,6 +22,8 @@ use msg::constellation_msg::{ConstellationChan, PipelineId, WorkerId};
|
||||||
use net_traits::ResourceTask;
|
use net_traits::ResourceTask;
|
||||||
use profile_traits::mem;
|
use profile_traits::mem;
|
||||||
use script_task::{CommonScriptMsg, ScriptChan, ScriptPort, ScriptTask};
|
use script_task::{CommonScriptMsg, ScriptChan, ScriptPort, ScriptTask};
|
||||||
|
use script_traits::TimerEventRequest;
|
||||||
|
use std::sync::mpsc::Sender;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
use util::mem::HeapSizeOf;
|
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
|
/// Get an `IpcSender<ScriptToDevtoolsControlMsg>` to send messages to Devtools
|
||||||
/// task when available.
|
/// task when available.
|
||||||
pub fn devtools_chan(&self) -> Option<IpcSender<ScriptToDevtoolsControlMsg>> {
|
pub fn devtools_chan(&self) -> Option<IpcSender<ScriptToDevtoolsControlMsg>> {
|
||||||
|
|
|
@ -37,6 +37,7 @@ use dom::bindings::js::{JS, Root};
|
||||||
use dom::bindings::refcounted::Trusted;
|
use dom::bindings::refcounted::Trusted;
|
||||||
use dom::bindings::utils::{Reflectable, Reflector, WindowProxyHandler};
|
use dom::bindings::utils::{Reflectable, Reflector, WindowProxyHandler};
|
||||||
use encoding::types::EncodingRef;
|
use encoding::types::EncodingRef;
|
||||||
|
use euclid::length::Length as EuclidLength;
|
||||||
use euclid::matrix2d::Matrix2D;
|
use euclid::matrix2d::Matrix2D;
|
||||||
use euclid::rect::Rect;
|
use euclid::rect::Rect;
|
||||||
use euclid::size::Size2D;
|
use euclid::size::Size2D;
|
||||||
|
@ -58,7 +59,7 @@ use net_traits::storage_task::StorageType;
|
||||||
use profile_traits::mem::ProfilerChan as MemProfilerChan;
|
use profile_traits::mem::ProfilerChan as MemProfilerChan;
|
||||||
use profile_traits::time::ProfilerChan as TimeProfilerChan;
|
use profile_traits::time::ProfilerChan as TimeProfilerChan;
|
||||||
use script_task::ScriptChan;
|
use script_task::ScriptChan;
|
||||||
use script_traits::UntrustedNodeAddress;
|
use script_traits::{TimerEventChan, TimerEventId, TimerSource, UntrustedNodeAddress};
|
||||||
use selectors::parser::PseudoElement;
|
use selectors::parser::PseudoElement;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
@ -283,6 +284,7 @@ no_jsmanaged_fields!(HashSet<T>);
|
||||||
// These three are interdependent, if you plan to put jsmanaged data
|
// These three are interdependent, if you plan to put jsmanaged data
|
||||||
// in one of these make sure it is propagated properly to containing structs
|
// in one of these make sure it is propagated properly to containing structs
|
||||||
no_jsmanaged_fields!(SubpageId, WindowSizeData, PipelineId);
|
no_jsmanaged_fields!(SubpageId, WindowSizeData, PipelineId);
|
||||||
|
no_jsmanaged_fields!(TimerEventId, TimerSource);
|
||||||
no_jsmanaged_fields!(WorkerId);
|
no_jsmanaged_fields!(WorkerId);
|
||||||
no_jsmanaged_fields!(QuirksMode);
|
no_jsmanaged_fields!(QuirksMode);
|
||||||
no_jsmanaged_fields!(Runtime);
|
no_jsmanaged_fields!(Runtime);
|
||||||
|
@ -293,6 +295,7 @@ no_jsmanaged_fields!(WindowProxyHandler);
|
||||||
no_jsmanaged_fields!(UntrustedNodeAddress);
|
no_jsmanaged_fields!(UntrustedNodeAddress);
|
||||||
no_jsmanaged_fields!(LengthOrPercentageOrAuto);
|
no_jsmanaged_fields!(LengthOrPercentageOrAuto);
|
||||||
no_jsmanaged_fields!(RGBA);
|
no_jsmanaged_fields!(RGBA);
|
||||||
|
no_jsmanaged_fields!(EuclidLength<Unit, T>);
|
||||||
no_jsmanaged_fields!(Matrix2D<T>);
|
no_jsmanaged_fields!(Matrix2D<T>);
|
||||||
no_jsmanaged_fields!(StorageType);
|
no_jsmanaged_fields!(StorageType);
|
||||||
no_jsmanaged_fields!(CanvasGradientStop, LinearGradientStyle, RadialGradientStyle);
|
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, )> {
|
impl JSTraceable for Box<FnBox(f64, )> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn trace(&self, _trc: *mut JSTracer) {
|
fn trace(&self, _trc: *mut JSTracer) {
|
||||||
|
|
|
@ -29,7 +29,8 @@ use msg::constellation_msg::PipelineId;
|
||||||
use net_traits::load_whole_resource;
|
use net_traits::load_whole_resource;
|
||||||
use rand::random;
|
use rand::random;
|
||||||
use script_task::ScriptTaskEventCategory::WorkerEvent;
|
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::mem::replace;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::sync::mpsc::{Receiver, RecvError, Select, Sender, channel};
|
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
|
/// 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
|
/// 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
|
/// 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 {
|
enum MixedMessage {
|
||||||
FromWorker((TrustedWorkerAddress, WorkerScriptMsg)),
|
FromWorker((TrustedWorkerAddress, WorkerScriptMsg)),
|
||||||
|
FromScheduler((TrustedWorkerAddress, TimerEvent)),
|
||||||
FromDevtools(DevtoolScriptControlMsg),
|
FromDevtools(DevtoolScriptControlMsg),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,6 +164,8 @@ pub struct DedicatedWorkerGlobalScope {
|
||||||
receiver: Receiver<(TrustedWorkerAddress, WorkerScriptMsg)>,
|
receiver: Receiver<(TrustedWorkerAddress, WorkerScriptMsg)>,
|
||||||
#[ignore_heap_size_of = "Defined in std"]
|
#[ignore_heap_size_of = "Defined in std"]
|
||||||
own_sender: Sender<(TrustedWorkerAddress, WorkerScriptMsg)>,
|
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>"]
|
#[ignore_heap_size_of = "Trusted<T> has unclear ownership like JS<T>"]
|
||||||
worker: DOMRefCell<Option<TrustedWorkerAddress>>,
|
worker: DOMRefCell<Option<TrustedWorkerAddress>>,
|
||||||
#[ignore_heap_size_of = "Can't measure trait objects"]
|
#[ignore_heap_size_of = "Can't measure trait objects"]
|
||||||
|
@ -154,14 +181,18 @@ impl DedicatedWorkerGlobalScope {
|
||||||
runtime: Rc<Runtime>,
|
runtime: Rc<Runtime>,
|
||||||
parent_sender: Box<ScriptChan + Send>,
|
parent_sender: Box<ScriptChan + Send>,
|
||||||
own_sender: Sender<(TrustedWorkerAddress, WorkerScriptMsg)>,
|
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 {
|
||||||
|
|
||||||
DedicatedWorkerGlobalScope {
|
DedicatedWorkerGlobalScope {
|
||||||
workerglobalscope: WorkerGlobalScope::new_inherited(
|
workerglobalscope: WorkerGlobalScope::new_inherited(
|
||||||
init, worker_url, runtime, from_devtools_receiver),
|
init, worker_url, runtime, from_devtools_receiver, timer_event_chan),
|
||||||
id: id,
|
id: id,
|
||||||
receiver: receiver,
|
receiver: receiver,
|
||||||
own_sender: own_sender,
|
own_sender: own_sender,
|
||||||
|
timer_event_port: timer_event_port,
|
||||||
parent_sender: parent_sender,
|
parent_sender: parent_sender,
|
||||||
worker: DOMRefCell::new(None),
|
worker: DOMRefCell::new(None),
|
||||||
}
|
}
|
||||||
|
@ -174,11 +205,13 @@ impl DedicatedWorkerGlobalScope {
|
||||||
runtime: Rc<Runtime>,
|
runtime: Rc<Runtime>,
|
||||||
parent_sender: Box<ScriptChan + Send>,
|
parent_sender: Box<ScriptChan + Send>,
|
||||||
own_sender: Sender<(TrustedWorkerAddress, WorkerScriptMsg)>,
|
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> {
|
-> Root<DedicatedWorkerGlobalScope> {
|
||||||
let scope = box DedicatedWorkerGlobalScope::new_inherited(
|
let scope = box DedicatedWorkerGlobalScope::new_inherited(
|
||||||
init, worker_url, id, from_devtools_receiver, runtime.clone(), parent_sender,
|
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)
|
DedicatedWorkerGlobalScopeBinding::Wrap(runtime.cx(), scope)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,9 +247,16 @@ impl DedicatedWorkerGlobalScope {
|
||||||
let (devtools_mpsc_chan, devtools_mpsc_port) = channel();
|
let (devtools_mpsc_chan, devtools_mpsc_port) = channel();
|
||||||
ROUTER.route_ipc_receiver_to_mpsc_sender(from_devtools_receiver, devtools_mpsc_chan);
|
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(
|
let global = DedicatedWorkerGlobalScope::new(
|
||||||
init, url, id, devtools_mpsc_port, runtime.clone(),
|
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
|
// 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.
|
// registration (#6631), so we instead use a random number and cross our fingers.
|
||||||
let scope = global.upcast::<WorkerGlobalScope>();
|
let scope = global.upcast::<WorkerGlobalScope>();
|
||||||
|
@ -263,13 +303,16 @@ impl DedicatedWorkerGlobalScope {
|
||||||
fn receive_event(&self) -> Result<MixedMessage, RecvError> {
|
fn receive_event(&self) -> Result<MixedMessage, RecvError> {
|
||||||
let scope = self.upcast::<WorkerGlobalScope>();
|
let scope = self.upcast::<WorkerGlobalScope>();
|
||||||
let worker_port = &self.receiver;
|
let worker_port = &self.receiver;
|
||||||
|
let timer_event_port = &self.timer_event_port;
|
||||||
let devtools_port = scope.from_devtools_receiver();
|
let devtools_port = scope.from_devtools_receiver();
|
||||||
|
|
||||||
let sel = Select::new();
|
let sel = Select::new();
|
||||||
let mut worker_handle = sel.handle(worker_port);
|
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);
|
let mut devtools_handle = sel.handle(devtools_port);
|
||||||
unsafe {
|
unsafe {
|
||||||
worker_handle.add();
|
worker_handle.add();
|
||||||
|
timer_event_handle.add();
|
||||||
if scope.from_devtools_sender().is_some() {
|
if scope.from_devtools_sender().is_some() {
|
||||||
devtools_handle.add();
|
devtools_handle.add();
|
||||||
}
|
}
|
||||||
|
@ -277,6 +320,8 @@ impl DedicatedWorkerGlobalScope {
|
||||||
let ret = sel.wait();
|
let ret = sel.wait();
|
||||||
if ret == worker_handle.id() {
|
if ret == worker_handle.id() {
|
||||||
Ok(MixedMessage::FromWorker(try!(worker_port.recv())))
|
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() {
|
} else if ret == devtools_handle.id() {
|
||||||
Ok(MixedMessage::FromDevtools(try!(devtools_port.recv())))
|
Ok(MixedMessage::FromDevtools(try!(devtools_port.recv())))
|
||||||
} else {
|
} else {
|
||||||
|
@ -301,11 +346,6 @@ impl DedicatedWorkerGlobalScope {
|
||||||
WorkerScriptMsg::Common(CommonScriptMsg::RefcountCleanup(addr)) => {
|
WorkerScriptMsg::Common(CommonScriptMsg::RefcountCleanup(addr)) => {
|
||||||
LiveDOMReferences::cleanup(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)) => {
|
WorkerScriptMsg::Common(CommonScriptMsg::CollectReports(reports_chan)) => {
|
||||||
let scope = self.upcast::<WorkerGlobalScope>();
|
let scope = self.upcast::<WorkerGlobalScope>();
|
||||||
let cx = scope.get_cx();
|
let cx = scope.get_cx();
|
||||||
|
@ -313,9 +353,6 @@ impl DedicatedWorkerGlobalScope {
|
||||||
let reports = ScriptTask::get_reports(cx, path_seg);
|
let reports = ScriptTask::get_reports(cx, path_seg);
|
||||||
reports_chan.send(reports);
|
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!"),
|
_ => 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)) => {
|
MixedMessage::FromWorker((linked_worker, msg)) => {
|
||||||
let _ar = AutoWorkerReset::new(self, linked_worker);
|
let _ar = AutoWorkerReset::new(self, linked_worker);
|
||||||
self.handle_script_event(msg);
|
self.handle_script_event(msg);
|
||||||
|
|
|
@ -52,9 +52,9 @@ use num::traits::ToPrimitive;
|
||||||
use page::Page;
|
use page::Page;
|
||||||
use profile_traits::mem;
|
use profile_traits::mem;
|
||||||
use rustc_serialize::base64::{FromBase64, STANDARD, ToBase64};
|
use rustc_serialize::base64::{FromBase64, STANDARD, ToBase64};
|
||||||
use script_task::{MainThreadScriptChan, SendableMainThreadScriptChan};
|
use script_task::{ScriptChan, ScriptPort, MainThreadScriptMsg};
|
||||||
use script_task::{MainThreadScriptMsg, ScriptChan, ScriptPort, TimerSource};
|
use script_task::{SendableMainThreadScriptChan, MainThreadScriptChan, MainThreadTimerEventChan};
|
||||||
use script_traits::ConstellationControlMsg;
|
use script_traits::{ConstellationControlMsg, TimerEventChan, TimerEventId, TimerEventRequest, TimerSource};
|
||||||
use selectors::parser::PseudoElement;
|
use selectors::parser::PseudoElement;
|
||||||
use std::ascii::AsciiExt;
|
use std::ascii::AsciiExt;
|
||||||
use std::borrow::ToOwned;
|
use std::borrow::ToOwned;
|
||||||
|
@ -70,7 +70,7 @@ use std::sync::mpsc::TryRecvError::{Disconnected, Empty};
|
||||||
use std::sync::mpsc::{Receiver, Sender, channel};
|
use std::sync::mpsc::{Receiver, Sender, channel};
|
||||||
use string_cache::Atom;
|
use string_cache::Atom;
|
||||||
use time;
|
use time;
|
||||||
use timers::{IsInterval, TimerCallback, TimerId, TimerManager};
|
use timers::{ActiveTimers, IsInterval, TimerCallback};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
use util::geometry::{self, MAX_RECT};
|
use util::geometry::{self, MAX_RECT};
|
||||||
use util::str::{DOMString, HTML_SPACE_CHARACTERS};
|
use util::str::{DOMString, HTML_SPACE_CHARACTERS};
|
||||||
|
@ -129,7 +129,9 @@ pub struct Window {
|
||||||
screen: MutNullableHeap<JS<Screen>>,
|
screen: MutNullableHeap<JS<Screen>>,
|
||||||
session_storage: MutNullableHeap<JS<Storage>>,
|
session_storage: MutNullableHeap<JS<Storage>>,
|
||||||
local_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>,
|
next_worker_id: Cell<WorkerId>,
|
||||||
|
|
||||||
|
@ -425,8 +427,7 @@ impl WindowMethods for Window {
|
||||||
args,
|
args,
|
||||||
timeout,
|
timeout,
|
||||||
IsInterval::NonInterval,
|
IsInterval::NonInterval,
|
||||||
TimerSource::FromWindow(self.id.clone()),
|
TimerSource::FromWindow(self.id.clone()))
|
||||||
self.script_chan.clone())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/#dom-windowtimers-settimeout
|
// https://html.spec.whatwg.org/multipage/#dom-windowtimers-settimeout
|
||||||
|
@ -435,8 +436,7 @@ impl WindowMethods for Window {
|
||||||
args,
|
args,
|
||||||
timeout,
|
timeout,
|
||||||
IsInterval::NonInterval,
|
IsInterval::NonInterval,
|
||||||
TimerSource::FromWindow(self.id.clone()),
|
TimerSource::FromWindow(self.id.clone()))
|
||||||
self.script_chan.clone())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/#dom-windowtimers-cleartimeout
|
// https://html.spec.whatwg.org/multipage/#dom-windowtimers-cleartimeout
|
||||||
|
@ -450,8 +450,7 @@ impl WindowMethods for Window {
|
||||||
args,
|
args,
|
||||||
timeout,
|
timeout,
|
||||||
IsInterval::Interval,
|
IsInterval::Interval,
|
||||||
TimerSource::FromWindow(self.id.clone()),
|
TimerSource::FromWindow(self.id.clone()))
|
||||||
self.script_chan.clone())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/#dom-windowtimers-setinterval
|
// https://html.spec.whatwg.org/multipage/#dom-windowtimers-setinterval
|
||||||
|
@ -460,8 +459,7 @@ impl WindowMethods for Window {
|
||||||
args,
|
args,
|
||||||
timeout,
|
timeout,
|
||||||
IsInterval::Interval,
|
IsInterval::Interval,
|
||||||
TimerSource::FromWindow(self.id.clone()),
|
TimerSource::FromWindow(self.id.clone()))
|
||||||
self.script_chan.clone())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/#dom-windowtimers-clearinterval
|
// https://html.spec.whatwg.org/multipage/#dom-windowtimers-clearinterval
|
||||||
|
@ -1076,7 +1074,7 @@ impl Window {
|
||||||
MainThreadScriptMsg::Navigate(self.id, LoadData::new(url))).unwrap();
|
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.timers.fire_timer(timer_id, self);
|
||||||
self.reflow(ReflowGoal::ForDisplay, ReflowQueryType::NoQuery, ReflowReason::Timer);
|
self.reflow(ReflowGoal::ForDisplay, ReflowQueryType::NoQuery, ReflowReason::Timer);
|
||||||
}
|
}
|
||||||
|
@ -1122,6 +1120,10 @@ impl Window {
|
||||||
self.constellation_chan.clone()
|
self.constellation_chan.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn scheduler_chan(&self) -> Sender<TimerEventRequest> {
|
||||||
|
self.scheduler_chan.clone()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn windowproxy_handler(&self) -> WindowProxyHandler {
|
pub fn windowproxy_handler(&self) -> WindowProxyHandler {
|
||||||
WindowProxyHandler(self.dom_static.windowproxy_handler.0)
|
WindowProxyHandler(self.dom_static.windowproxy_handler.0)
|
||||||
}
|
}
|
||||||
|
@ -1267,6 +1269,8 @@ impl Window {
|
||||||
mem_profiler_chan: mem::ProfilerChan,
|
mem_profiler_chan: mem::ProfilerChan,
|
||||||
devtools_chan: Option<IpcSender<ScriptToDevtoolsControlMsg>>,
|
devtools_chan: Option<IpcSender<ScriptToDevtoolsControlMsg>>,
|
||||||
constellation_chan: ConstellationChan,
|
constellation_chan: ConstellationChan,
|
||||||
|
scheduler_chan: Sender<TimerEventRequest>,
|
||||||
|
timer_event_chan: MainThreadTimerEventChan,
|
||||||
layout_chan: LayoutChan,
|
layout_chan: LayoutChan,
|
||||||
id: PipelineId,
|
id: PipelineId,
|
||||||
parent_info: Option<(PipelineId, SubpageId)>,
|
parent_info: Option<(PipelineId, SubpageId)>,
|
||||||
|
@ -1299,7 +1303,8 @@ impl Window {
|
||||||
screen: Default::default(),
|
screen: Default::default(),
|
||||||
session_storage: Default::default(),
|
session_storage: Default::default(),
|
||||||
local_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)),
|
next_worker_id: Cell::new(WorkerId(0)),
|
||||||
id: id,
|
id: id,
|
||||||
parent_info: parent_info,
|
parent_info: parent_info,
|
||||||
|
|
|
@ -72,6 +72,7 @@ impl Worker {
|
||||||
|
|
||||||
let resource_task = global.resource_task();
|
let resource_task = global.resource_task();
|
||||||
let constellation_chan = global.constellation_chan();
|
let constellation_chan = global.constellation_chan();
|
||||||
|
let scheduler_chan = global.scheduler_chan();
|
||||||
|
|
||||||
let (sender, receiver) = channel();
|
let (sender, receiver) = channel();
|
||||||
let worker = Worker::new(global, sender.clone());
|
let worker = Worker::new(global, sender.clone());
|
||||||
|
@ -101,6 +102,7 @@ impl Worker {
|
||||||
to_devtools_sender: global.devtools_chan(),
|
to_devtools_sender: global.devtools_chan(),
|
||||||
from_devtools_sender: optional_sender,
|
from_devtools_sender: optional_sender,
|
||||||
constellation_chan: constellation_chan,
|
constellation_chan: constellation_chan,
|
||||||
|
scheduler_chan: scheduler_chan,
|
||||||
worker_id: worker_id,
|
worker_id: worker_id,
|
||||||
};
|
};
|
||||||
DedicatedWorkerGlobalScope::run_worker_scope(
|
DedicatedWorkerGlobalScope::run_worker_scope(
|
||||||
|
|
|
@ -23,12 +23,13 @@ use js::rust::Runtime;
|
||||||
use msg::constellation_msg::{ConstellationChan, PipelineId, WorkerId};
|
use msg::constellation_msg::{ConstellationChan, PipelineId, WorkerId};
|
||||||
use net_traits::{ResourceTask, load_whole_resource};
|
use net_traits::{ResourceTask, load_whole_resource};
|
||||||
use profile_traits::mem;
|
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::cell::Cell;
|
||||||
use std::default::Default;
|
use std::default::Default;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::sync::mpsc::Receiver;
|
use std::sync::mpsc::{Receiver, Sender};
|
||||||
use timers::{IsInterval, TimerCallback, TimerId, TimerManager};
|
use timers::{ActiveTimers, IsInterval, TimerCallback};
|
||||||
use url::{Url, UrlParser};
|
use url::{Url, UrlParser};
|
||||||
use util::str::DOMString;
|
use util::str::DOMString;
|
||||||
|
|
||||||
|
@ -43,6 +44,7 @@ pub struct WorkerGlobalScopeInit {
|
||||||
pub to_devtools_sender: Option<IpcSender<ScriptToDevtoolsControlMsg>>,
|
pub to_devtools_sender: Option<IpcSender<ScriptToDevtoolsControlMsg>>,
|
||||||
pub from_devtools_sender: Option<IpcSender<DevtoolScriptControlMsg>>,
|
pub from_devtools_sender: Option<IpcSender<DevtoolScriptControlMsg>>,
|
||||||
pub constellation_chan: ConstellationChan,
|
pub constellation_chan: ConstellationChan,
|
||||||
|
pub scheduler_chan: Sender<TimerEventRequest>,
|
||||||
pub worker_id: WorkerId,
|
pub worker_id: WorkerId,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,7 +63,7 @@ pub struct WorkerGlobalScope {
|
||||||
navigator: MutNullableHeap<JS<WorkerNavigator>>,
|
navigator: MutNullableHeap<JS<WorkerNavigator>>,
|
||||||
console: MutNullableHeap<JS<Console>>,
|
console: MutNullableHeap<JS<Console>>,
|
||||||
crypto: MutNullableHeap<JS<Crypto>>,
|
crypto: MutNullableHeap<JS<Crypto>>,
|
||||||
timers: TimerManager,
|
timers: ActiveTimers,
|
||||||
#[ignore_heap_size_of = "Defined in std"]
|
#[ignore_heap_size_of = "Defined in std"]
|
||||||
mem_profiler_chan: mem::ProfilerChan,
|
mem_profiler_chan: mem::ProfilerChan,
|
||||||
#[ignore_heap_size_of = "Defined in ipc-channel"]
|
#[ignore_heap_size_of = "Defined in ipc-channel"]
|
||||||
|
@ -83,13 +85,17 @@ pub struct WorkerGlobalScope {
|
||||||
|
|
||||||
#[ignore_heap_size_of = "Defined in std"]
|
#[ignore_heap_size_of = "Defined in std"]
|
||||||
constellation_chan: ConstellationChan,
|
constellation_chan: ConstellationChan,
|
||||||
|
|
||||||
|
#[ignore_heap_size_of = "Defined in std"]
|
||||||
|
scheduler_chan: Sender<TimerEventRequest>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WorkerGlobalScope {
|
impl WorkerGlobalScope {
|
||||||
pub fn new_inherited(init: WorkerGlobalScopeInit,
|
pub fn new_inherited(init: WorkerGlobalScopeInit,
|
||||||
worker_url: Url,
|
worker_url: Url,
|
||||||
runtime: Rc<Runtime>,
|
runtime: Rc<Runtime>,
|
||||||
from_devtools_receiver: Receiver<DevtoolScriptControlMsg>)
|
from_devtools_receiver: Receiver<DevtoolScriptControlMsg>,
|
||||||
|
timer_event_chan: Box<TimerEventChan + Send>)
|
||||||
-> WorkerGlobalScope {
|
-> WorkerGlobalScope {
|
||||||
WorkerGlobalScope {
|
WorkerGlobalScope {
|
||||||
eventtarget: EventTarget::new_inherited(),
|
eventtarget: EventTarget::new_inherited(),
|
||||||
|
@ -102,13 +108,14 @@ impl WorkerGlobalScope {
|
||||||
navigator: Default::default(),
|
navigator: Default::default(),
|
||||||
console: Default::default(),
|
console: Default::default(),
|
||||||
crypto: 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,
|
mem_profiler_chan: init.mem_profiler_chan,
|
||||||
to_devtools_sender: init.to_devtools_sender,
|
to_devtools_sender: init.to_devtools_sender,
|
||||||
from_devtools_sender: init.from_devtools_sender,
|
from_devtools_sender: init.from_devtools_sender,
|
||||||
from_devtools_receiver: from_devtools_receiver,
|
from_devtools_receiver: from_devtools_receiver,
|
||||||
devtools_wants_updates: Cell::new(false),
|
devtools_wants_updates: Cell::new(false),
|
||||||
constellation_chan: init.constellation_chan,
|
constellation_chan: init.constellation_chan,
|
||||||
|
scheduler_chan: init.scheduler_chan,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,6 +139,10 @@ impl WorkerGlobalScope {
|
||||||
self.constellation_chan.clone()
|
self.constellation_chan.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn scheduler_chan(&self) -> Sender<TimerEventRequest> {
|
||||||
|
self.scheduler_chan.clone()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_cx(&self) -> *mut JSContext {
|
pub fn get_cx(&self) -> *mut JSContext {
|
||||||
self.runtime.cx()
|
self.runtime.cx()
|
||||||
}
|
}
|
||||||
|
@ -233,8 +244,7 @@ impl WorkerGlobalScopeMethods for WorkerGlobalScope {
|
||||||
args,
|
args,
|
||||||
timeout,
|
timeout,
|
||||||
IsInterval::NonInterval,
|
IsInterval::NonInterval,
|
||||||
TimerSource::FromWorker,
|
TimerSource::FromWorker)
|
||||||
self.script_chan())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/#dom-windowtimers-setinterval
|
// https://html.spec.whatwg.org/multipage/#dom-windowtimers-setinterval
|
||||||
|
@ -243,8 +253,7 @@ impl WorkerGlobalScopeMethods for WorkerGlobalScope {
|
||||||
args,
|
args,
|
||||||
timeout,
|
timeout,
|
||||||
IsInterval::NonInterval,
|
IsInterval::NonInterval,
|
||||||
TimerSource::FromWorker,
|
TimerSource::FromWorker)
|
||||||
self.script_chan())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/#dom-windowtimers-clearinterval
|
// https://html.spec.whatwg.org/multipage/#dom-windowtimers-clearinterval
|
||||||
|
@ -258,8 +267,7 @@ impl WorkerGlobalScopeMethods for WorkerGlobalScope {
|
||||||
args,
|
args,
|
||||||
timeout,
|
timeout,
|
||||||
IsInterval::Interval,
|
IsInterval::Interval,
|
||||||
TimerSource::FromWorker,
|
TimerSource::FromWorker)
|
||||||
self.script_chan())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/#dom-windowtimers-setinterval
|
// https://html.spec.whatwg.org/multipage/#dom-windowtimers-setinterval
|
||||||
|
@ -268,8 +276,7 @@ impl WorkerGlobalScopeMethods for WorkerGlobalScope {
|
||||||
args,
|
args,
|
||||||
timeout,
|
timeout,
|
||||||
IsInterval::Interval,
|
IsInterval::Interval,
|
||||||
TimerSource::FromWorker,
|
TimerSource::FromWorker)
|
||||||
self.script_chan())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/#dom-windowtimers-clearinterval
|
// 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);
|
self.timers.fire_timer(timer_id, self);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -86,7 +86,6 @@ mod devtools;
|
||||||
pub mod document_loader;
|
pub mod document_loader;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
pub mod dom;
|
pub mod dom;
|
||||||
mod horribly_inefficient_timers;
|
|
||||||
pub mod layout_interface;
|
pub mod layout_interface;
|
||||||
mod mem;
|
mod mem;
|
||||||
mod network_listener;
|
mod network_listener;
|
||||||
|
|
|
@ -82,6 +82,7 @@ use script_traits::CompositorEvent::{MouseDownEvent, MouseUpEvent};
|
||||||
use script_traits::{CompositorEvent, ConstellationControlMsg};
|
use script_traits::{CompositorEvent, ConstellationControlMsg};
|
||||||
use script_traits::{InitialScriptState, MouseButton, NewLayoutInfo};
|
use script_traits::{InitialScriptState, MouseButton, NewLayoutInfo};
|
||||||
use script_traits::{OpaqueScriptLayoutChannel, ScriptState, ScriptTaskFactory};
|
use script_traits::{OpaqueScriptLayoutChannel, ScriptState, ScriptTaskFactory};
|
||||||
|
use script_traits::{TimerEvent, TimerEventChan, TimerEventRequest, TimerSource};
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use std::borrow::ToOwned;
|
use std::borrow::ToOwned;
|
||||||
use std::cell::{Cell, RefCell};
|
use std::cell::{Cell, RefCell};
|
||||||
|
@ -96,7 +97,6 @@ use std::sync::mpsc::{Receiver, Select, Sender, channel};
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use string_cache::Atom;
|
use string_cache::Atom;
|
||||||
use time::{Tm, now};
|
use time::{Tm, now};
|
||||||
use timers::TimerId;
|
|
||||||
use url::{Url, UrlParser};
|
use url::{Url, UrlParser};
|
||||||
use util::opts;
|
use util::opts;
|
||||||
use util::str::DOMString;
|
use util::str::DOMString;
|
||||||
|
@ -156,12 +156,6 @@ impl InProgressLoad {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub enum TimerSource {
|
|
||||||
FromWindow(PipelineId),
|
|
||||||
FromWorker
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait Runnable {
|
pub trait Runnable {
|
||||||
fn handler(self: Box<Self>);
|
fn handler(self: Box<Self>);
|
||||||
}
|
}
|
||||||
|
@ -175,6 +169,7 @@ enum MixedMessage {
|
||||||
FromScript(MainThreadScriptMsg),
|
FromScript(MainThreadScriptMsg),
|
||||||
FromDevtools(DevtoolScriptControlMsg),
|
FromDevtools(DevtoolScriptControlMsg),
|
||||||
FromImageCache(ImageCacheResult),
|
FromImageCache(ImageCacheResult),
|
||||||
|
FromScheduler(TimerEvent),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Common messages used to control the event loops in both the script and the worker
|
/// 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
|
/// Requests that the script task measure its memory usage. The results are sent back via the
|
||||||
/// supplied channel.
|
/// supplied channel.
|
||||||
CollectReports(ReportsChan),
|
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).
|
/// A DOM object's last pinned reference was removed (dispatched to all tasks).
|
||||||
RefcountCleanup(TrustedReference),
|
RefcountCleanup(TrustedReference),
|
||||||
/// Generic message that encapsulates event handling.
|
/// Generic message that encapsulates event handling.
|
||||||
|
@ -205,6 +196,7 @@ pub enum ScriptTaskEventCategory {
|
||||||
NetworkEvent,
|
NetworkEvent,
|
||||||
Resize,
|
Resize,
|
||||||
ScriptEvent,
|
ScriptEvent,
|
||||||
|
TimerEvent,
|
||||||
UpdateReplacedElement,
|
UpdateReplacedElement,
|
||||||
SetViewport,
|
SetViewport,
|
||||||
WebSocketEvent,
|
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;
|
pub struct StackRootTLS;
|
||||||
|
|
||||||
impl StackRootTLS {
|
impl StackRootTLS {
|
||||||
|
@ -408,6 +414,10 @@ pub struct ScriptTask {
|
||||||
|
|
||||||
/// List of pipelines that have been owned and closed by this script task.
|
/// List of pipelines that have been owned and closed by this script task.
|
||||||
closed_pipelines: RefCell<HashSet<PipelineId>>,
|
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
|
/// 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 =
|
let image_cache_port =
|
||||||
ROUTER.route_ipc_receiver_to_new_mpsc_receiver(ipc_image_cache_port);
|
ROUTER.route_ipc_receiver_to_new_mpsc_receiver(ipc_image_cache_port);
|
||||||
|
|
||||||
|
let (timer_event_chan, timer_event_port) = channel();
|
||||||
|
|
||||||
ScriptTask {
|
ScriptTask {
|
||||||
page: DOMRefCell::new(None),
|
page: DOMRefCell::new(None),
|
||||||
incomplete_loads: DOMRefCell::new(vec!()),
|
incomplete_loads: DOMRefCell::new(vec!()),
|
||||||
|
@ -631,6 +643,10 @@ impl ScriptTask {
|
||||||
js_runtime: Rc::new(runtime),
|
js_runtime: Rc::new(runtime),
|
||||||
mouse_over_targets: DOMRefCell::new(vec!()),
|
mouse_over_targets: DOMRefCell::new(vec!()),
|
||||||
closed_pipelines: RefCell::new(HashSet::new()),
|
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.
|
// Receive at least one message so we don't spinloop.
|
||||||
let mut event = {
|
let mut event = {
|
||||||
let sel = Select::new();
|
let sel = Select::new();
|
||||||
let mut port1 = sel.handle(&self.port);
|
let mut script_port = sel.handle(&self.port);
|
||||||
let mut port2 = sel.handle(&self.control_port);
|
let mut control_port = sel.handle(&self.control_port);
|
||||||
let mut port3 = sel.handle(&self.devtools_port);
|
let mut timer_event_port = sel.handle(&self.timer_event_port);
|
||||||
let mut port4 = sel.handle(&self.image_cache_port);
|
let mut devtools_port = sel.handle(&self.devtools_port);
|
||||||
|
let mut image_cache_port = sel.handle(&self.image_cache_port);
|
||||||
unsafe {
|
unsafe {
|
||||||
port1.add();
|
script_port.add();
|
||||||
port2.add();
|
control_port.add();
|
||||||
|
timer_event_port.add();
|
||||||
if self.devtools_chan.is_some() {
|
if self.devtools_chan.is_some() {
|
||||||
port3.add();
|
devtools_port.add();
|
||||||
}
|
}
|
||||||
port4.add();
|
image_cache_port.add();
|
||||||
}
|
}
|
||||||
let ret = sel.wait();
|
let ret = sel.wait();
|
||||||
if ret == port1.id() {
|
if ret == script_port.id() {
|
||||||
MixedMessage::FromScript(self.port.recv().unwrap())
|
MixedMessage::FromScript(self.port.recv().unwrap())
|
||||||
} else if ret == port2.id() {
|
} else if ret == control_port.id() {
|
||||||
MixedMessage::FromConstellation(self.control_port.recv().unwrap())
|
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())
|
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())
|
MixedMessage::FromImageCache(self.image_cache_port.recv().unwrap())
|
||||||
} else {
|
} else {
|
||||||
panic!("unexpected select result")
|
panic!("unexpected select result")
|
||||||
|
@ -797,6 +817,7 @@ impl ScriptTask {
|
||||||
// on and execute the sequential non-resize events we've seen.
|
// on and execute the sequential non-resize events we've seen.
|
||||||
match self.control_port.try_recv() {
|
match self.control_port.try_recv() {
|
||||||
Err(_) => match self.port.try_recv() {
|
Err(_) => match self.port.try_recv() {
|
||||||
|
Err(_) => match self.timer_event_port.try_recv() {
|
||||||
Err(_) => match self.devtools_port.try_recv() {
|
Err(_) => match self.devtools_port.try_recv() {
|
||||||
Err(_) => match self.image_cache_port.try_recv() {
|
Err(_) => match self.image_cache_port.try_recv() {
|
||||||
Err(_) => break,
|
Err(_) => break,
|
||||||
|
@ -804,6 +825,8 @@ impl ScriptTask {
|
||||||
},
|
},
|
||||||
Ok(ev) => event = MixedMessage::FromDevtools(ev),
|
Ok(ev) => event = MixedMessage::FromDevtools(ev),
|
||||||
},
|
},
|
||||||
|
Ok(ev) => event = MixedMessage::FromScheduler(ev),
|
||||||
|
},
|
||||||
Ok(ev) => event = MixedMessage::FromScript(ev),
|
Ok(ev) => event = MixedMessage::FromScript(ev),
|
||||||
},
|
},
|
||||||
Ok(ev) => event = MixedMessage::FromConstellation(ev),
|
Ok(ev) => event = MixedMessage::FromConstellation(ev),
|
||||||
|
@ -823,6 +846,7 @@ impl ScriptTask {
|
||||||
},
|
},
|
||||||
MixedMessage::FromConstellation(inner_msg) => self.handle_msg_from_constellation(inner_msg),
|
MixedMessage::FromConstellation(inner_msg) => self.handle_msg_from_constellation(inner_msg),
|
||||||
MixedMessage::FromScript(inner_msg) => self.handle_msg_from_script(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::FromDevtools(inner_msg) => self.handle_msg_from_devtools(inner_msg),
|
||||||
MixedMessage::FromImageCache(inner_msg) => self.handle_msg_from_image_cache(inner_msg),
|
MixedMessage::FromImageCache(inner_msg) => self.handle_msg_from_image_cache(inner_msg),
|
||||||
}
|
}
|
||||||
|
@ -871,7 +895,8 @@ impl ScriptTask {
|
||||||
*category,
|
*category,
|
||||||
_ => ScriptTaskEventCategory::ScriptEvent
|
_ => ScriptTaskEventCategory::ScriptEvent
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
MixedMessage::FromScheduler(_) => ScriptTaskEventCategory::TimerEvent,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -893,6 +918,7 @@ impl ScriptTask {
|
||||||
ScriptTaskEventCategory::ScriptEvent => ProfilerCategory::ScriptEvent,
|
ScriptTaskEventCategory::ScriptEvent => ProfilerCategory::ScriptEvent,
|
||||||
ScriptTaskEventCategory::UpdateReplacedElement => ProfilerCategory::ScriptUpdateReplacedElement,
|
ScriptTaskEventCategory::UpdateReplacedElement => ProfilerCategory::ScriptUpdateReplacedElement,
|
||||||
ScriptTaskEventCategory::SetViewport => ProfilerCategory::ScriptSetViewport,
|
ScriptTaskEventCategory::SetViewport => ProfilerCategory::ScriptSetViewport,
|
||||||
|
ScriptTaskEventCategory::TimerEvent => ProfilerCategory::ScriptTimerEvent,
|
||||||
ScriptTaskEventCategory::WebSocketEvent => ProfilerCategory::ScriptWebSocketEvent,
|
ScriptTaskEventCategory::WebSocketEvent => ProfilerCategory::ScriptWebSocketEvent,
|
||||||
ScriptTaskEventCategory::WorkerEvent => ProfilerCategory::ScriptWorkerEvent,
|
ScriptTaskEventCategory::WorkerEvent => ProfilerCategory::ScriptWorkerEvent,
|
||||||
ScriptTaskEventCategory::XhrEvent => ProfilerCategory::ScriptXhrEvent,
|
ScriptTaskEventCategory::XhrEvent => ProfilerCategory::ScriptXhrEvent,
|
||||||
|
@ -966,12 +992,6 @@ impl ScriptTask {
|
||||||
runnable.handler(self),
|
runnable.handler(self),
|
||||||
MainThreadScriptMsg::DocumentLoadsComplete(id) =>
|
MainThreadScriptMsg::DocumentLoadsComplete(id) =>
|
||||||
self.handle_loads_complete(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)) =>
|
MainThreadScriptMsg::Common(CommonScriptMsg::RunnableMsg(_, runnable)) =>
|
||||||
// The category of the runnable is ignored by the pattern, however
|
// The category of the runnable is ignored by the pattern, however
|
||||||
// it is still respected by profiling (see categorize_msg).
|
// 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) {
|
fn handle_msg_from_devtools(&self, msg: DevtoolScriptControlMsg) {
|
||||||
let page = self.root_page();
|
let page = self.root_page();
|
||||||
match msg {
|
match msg {
|
||||||
|
@ -1272,15 +1308,6 @@ impl ScriptTask {
|
||||||
reports_chan.send(reports);
|
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
|
/// Handles freeze message
|
||||||
fn handle_freeze_msg(&self, id: PipelineId) {
|
fn handle_freeze_msg(&self, id: PipelineId) {
|
||||||
// Workaround for a race condition when navigating before the initial page has
|
// Workaround for a race condition when navigating before the initial page has
|
||||||
|
@ -1587,6 +1614,8 @@ impl ScriptTask {
|
||||||
self.mem_profiler_chan.clone(),
|
self.mem_profiler_chan.clone(),
|
||||||
self.devtools_chan.clone(),
|
self.devtools_chan.clone(),
|
||||||
self.constellation_chan.clone(),
|
self.constellation_chan.clone(),
|
||||||
|
self.scheduler_chan.clone(),
|
||||||
|
MainThreadTimerEventChan(self.timer_event_chan.clone()),
|
||||||
incomplete.layout_chan,
|
incomplete.layout_chan,
|
||||||
incomplete.pipeline_id,
|
incomplete.pipeline_id,
|
||||||
incomplete.parent_info,
|
incomplete.parent_info,
|
||||||
|
|
|
@ -8,81 +8,63 @@ use dom::bindings::codegen::Bindings::FunctionBinding::Function;
|
||||||
use dom::bindings::global::global_object_for_js_object;
|
use dom::bindings::global::global_object_for_js_object;
|
||||||
use dom::bindings::utils::Reflectable;
|
use dom::bindings::utils::Reflectable;
|
||||||
use dom::window::ScriptHelpers;
|
use dom::window::ScriptHelpers;
|
||||||
use horribly_inefficient_timers;
|
use euclid::length::Length;
|
||||||
use js::jsapi::{HandleValue, Heap, RootedValue};
|
use js::jsapi::{HandleValue, Heap, RootedValue};
|
||||||
use js::jsval::{JSVal, UndefinedValue};
|
use js::jsval::{JSVal, UndefinedValue};
|
||||||
use script_task::{CommonScriptMsg, ScriptChan, TimerSource};
|
use num::traits::Saturating;
|
||||||
use std::borrow::ToOwned;
|
use script_traits::{MsDuration, precise_time_ms};
|
||||||
|
use script_traits::{TimerEventChan, TimerEventId, TimerEventRequest, TimerSource};
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
use std::cmp;
|
use std::cmp::{self, Ord, Ordering};
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::default::Default;
|
use std::default::Default;
|
||||||
use std::hash::{Hash, Hasher};
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::sync::mpsc::Select;
|
use std::sync::mpsc::Sender;
|
||||||
use std::sync::mpsc::{Sender, channel};
|
|
||||||
use util::mem::HeapSizeOf;
|
use util::mem::HeapSizeOf;
|
||||||
use util::str::DOMString;
|
use util::str::DOMString;
|
||||||
use util::task::spawn_named;
|
|
||||||
|
|
||||||
#[derive(JSTraceable, PartialEq, Eq, Copy, Clone, HeapSizeOf)]
|
#[derive(JSTraceable, PartialEq, Eq, Copy, Clone, HeapSizeOf, Hash, PartialOrd, Ord)]
|
||||||
pub struct TimerId(i32);
|
pub struct TimerHandle(i32);
|
||||||
|
|
||||||
#[derive(JSTraceable, HeapSizeOf)]
|
#[derive(JSTraceable, HeapSizeOf)]
|
||||||
#[privatize]
|
#[privatize]
|
||||||
struct TimerHandle {
|
pub struct ActiveTimers {
|
||||||
handle: TimerId,
|
#[ignore_heap_size_of = "Defined in std"]
|
||||||
data: TimerData,
|
timer_event_chan: Box<TimerEventChan + Send>,
|
||||||
#[ignore_heap_size_of = "channels are hard"]
|
#[ignore_heap_size_of = "Defined in std"]
|
||||||
control_chan: Option<Sender<TimerControlMsg>>,
|
scheduler_chan: Sender<TimerEventRequest>,
|
||||||
}
|
next_timer_handle: Cell<TimerHandle>,
|
||||||
|
timers: DOMRefCell<Vec<Timer>>,
|
||||||
#[derive(JSTraceable, Clone)]
|
suspended_since: Cell<Option<MsDuration>>,
|
||||||
pub enum TimerCallback {
|
/// Initially 0, increased whenever the associated document is reactivated
|
||||||
StringTimerCallback(DOMString),
|
/// by the amount of ms the document was inactive. The current time can be
|
||||||
FunctionTimerCallback(Rc<Function>)
|
/// offset back by this amount for a coherent time across document
|
||||||
}
|
/// activations.
|
||||||
|
suspension_offset: Cell<MsDuration>,
|
||||||
impl HeapSizeOf for TimerCallback {
|
/// Calls to `fire_timer` with a different argument than this get ignored.
|
||||||
fn heap_size_of_children(&self) -> usize {
|
/// They were previously scheduled and got invalidated when
|
||||||
// FIXME: Rc<T> isn't HeapSizeOf and we can't ignore it due to #6870 and #6871
|
/// - timers were suspended,
|
||||||
0
|
/// - 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>,
|
||||||
impl Hash for TimerId {
|
/// The nesting level of the currently executing timer task or 0.
|
||||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
nesting_level: Cell<u32>,
|
||||||
let TimerId(id) = *self;
|
|
||||||
id.hash(state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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)]
|
#[derive(JSTraceable, HeapSizeOf)]
|
||||||
#[privatize]
|
#[privatize]
|
||||||
pub struct TimerManager {
|
struct Timer {
|
||||||
active_timers: DOMRefCell<HashMap<TimerId, TimerHandle>>,
|
handle: TimerHandle,
|
||||||
next_timer_handle: Cell<i32>,
|
source: TimerSource,
|
||||||
}
|
callback: InternalTimerCallback,
|
||||||
|
is_interval: IsInterval,
|
||||||
|
nesting_level: u32,
|
||||||
impl Drop for TimerManager {
|
duration: MsDuration,
|
||||||
fn drop(&mut self) {
|
next_call: MsDuration,
|
||||||
for (_, timer_handle) in &mut *self.active_timers.borrow_mut() {
|
|
||||||
timer_handle.cancel();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enum allowing more descriptive values for the is_interval field
|
// Enum allowing more descriptive values for the is_interval field
|
||||||
|
@ -92,175 +74,269 @@ pub enum IsInterval {
|
||||||
NonInterval,
|
NonInterval,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Messages sent control timers from script task
|
impl Ord for Timer {
|
||||||
#[derive(JSTraceable, PartialEq, Copy, Clone, Debug)]
|
fn cmp(&self, other: &Timer) -> Ordering {
|
||||||
pub enum TimerControlMsg {
|
match self.next_call.cmp(&other.next_call).reverse() {
|
||||||
Cancel,
|
Ordering::Equal => self.handle.cmp(&other.handle).reverse(),
|
||||||
Suspend,
|
res @ _ => res
|
||||||
Resume
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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>>
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TimerManager {
|
|
||||||
pub fn new() -> TimerManager {
|
|
||||||
TimerManager {
|
|
||||||
active_timers: DOMRefCell::new(HashMap::new()),
|
|
||||||
next_timer_handle: Cell::new(0)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn suspend(&self) {
|
impl PartialOrd for Timer {
|
||||||
for (_, timer_handle) in &mut *self.active_timers.borrow_mut() {
|
fn partial_cmp(&self, other: &Timer) -> Option<Ordering> {
|
||||||
timer_handle.suspend();
|
Some(self.cmp(other))
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn resume(&self) {
|
|
||||||
for (_, timer_handle) in &mut *self.active_timers.borrow_mut() {
|
|
||||||
timer_handle.resume();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unsafe_code)]
|
impl Eq for Timer {}
|
||||||
|
impl PartialEq for Timer {
|
||||||
|
fn eq(&self, other: &Timer) -> bool {
|
||||||
|
self as *const Timer == other as *const Timer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub enum TimerCallback {
|
||||||
|
StringTimerCallback(DOMString),
|
||||||
|
FunctionTimerCallback(Rc<Function>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(JSTraceable, Clone)]
|
||||||
|
enum InternalTimerCallback {
|
||||||
|
StringTimerCallback(DOMString),
|
||||||
|
FunctionTimerCallback(Rc<Function>, Rc<Vec<Heap<JSVal>>>),
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// see https://html.spec.whatwg.org/multipage/#timer-initialisation-steps
|
||||||
pub fn set_timeout_or_interval(&self,
|
pub fn set_timeout_or_interval(&self,
|
||||||
callback: TimerCallback,
|
callback: TimerCallback,
|
||||||
arguments: Vec<HandleValue>,
|
arguments: Vec<HandleValue>,
|
||||||
timeout: i32,
|
timeout: i32,
|
||||||
is_interval: IsInterval,
|
is_interval: IsInterval,
|
||||||
source: TimerSource,
|
source: TimerSource)
|
||||||
script_chan: Box<ScriptChan + Send>)
|
|
||||||
-> i32 {
|
-> i32 {
|
||||||
let duration_ms = cmp::max(0, timeout) as u32;
|
assert!(self.suspended_since.get().is_none());
|
||||||
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`
|
// step 3
|
||||||
// to the relevant script handler that will deal with it.
|
let TimerHandle(new_handle) = self.next_timer_handle.get();
|
||||||
let (control_chan, control_port) = channel();
|
self.next_timer_handle.set(TimerHandle(new_handle + 1));
|
||||||
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 timeout = cmp::max(0, timeout);
|
||||||
let mut timeout_handle = select.handle(&timeout_port);
|
// step 7
|
||||||
unsafe { timeout_handle.add() };
|
let duration = self.clamp_duration(Length::new(timeout as u64));
|
||||||
let mut control_handle = select.handle(&control_port);
|
let next_call = self.base_time() + duration;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
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())
|
|
||||||
}
|
|
||||||
};
|
|
||||||
self.active_timers.borrow_mut().insert(timer_id, timer);
|
|
||||||
|
|
||||||
|
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
|
// This is a bit complicated, but this ensures that the vector's
|
||||||
// buffer isn't reallocated (and moved) after setting the Heap values
|
// buffer isn't reallocated (and moved) after setting the Heap values
|
||||||
let mut timers = self.active_timers.borrow_mut();
|
let mut args = Vec::with_capacity(arguments.len());
|
||||||
let mut timer = timers.get_mut(&timer_id).unwrap();
|
|
||||||
for _ in 0..arguments.len() {
|
for _ in 0..arguments.len() {
|
||||||
timer.data.args.push(Heap::default());
|
args.push(Heap::default());
|
||||||
}
|
}
|
||||||
for (i, item) in arguments.iter().enumerate() {
|
for (i, item) in arguments.iter().enumerate() {
|
||||||
timer.data.args.get_mut(i).unwrap().set(item.get());
|
args.get_mut(i).unwrap().set(item.get());
|
||||||
}
|
}
|
||||||
handle
|
InternalTimerCallback::FunctionTimerCallback(function, Rc::new(args))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
// step 10
|
||||||
|
new_handle
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear_timeout_or_interval(&self, handle: i32) {
|
pub fn clear_timeout_or_interval(&self, handle: i32) {
|
||||||
let mut timer_handle = self.active_timers.borrow_mut().remove(&TimerId(handle));
|
let handle = TimerHandle(handle);
|
||||||
match timer_handle {
|
let was_next = self.is_next_timer(handle);
|
||||||
Some(ref mut handle) => handle.cancel(),
|
|
||||||
None => {}
|
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)]
|
#[allow(unsafe_code)]
|
||||||
pub fn fire_timer<T: Reflectable>(&self, timer_id: TimerId, this: &T) {
|
pub fn fire_timer<T: Reflectable>(&self, id: TimerEventId, this: &T) {
|
||||||
|
let expected_id = self.expected_event_id.get();
|
||||||
let (is_interval, callback, args): (IsInterval, TimerCallback, Vec<JSVal>) =
|
if expected_id != id {
|
||||||
match self.active_timers.borrow().get(&timer_id) {
|
debug!("ignoring timer fire event {:?} (expected {:?}", id, expected_id);
|
||||||
Some(timer_handle) =>
|
return;
|
||||||
(timer_handle.data.is_interval,
|
|
||||||
timer_handle.data.callback.clone(),
|
|
||||||
timer_handle.data.args.iter().map(|arg| arg.get()).collect()),
|
|
||||||
None => return,
|
|
||||||
};
|
|
||||||
|
|
||||||
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) => {
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
// 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 proxy = this.reflector().get_jsobject();
|
||||||
let cx = global_object_for_js_object(proxy.get()).r().get_cx();
|
let cx = global_object_for_js_object(proxy.get()).r().get_cx();
|
||||||
let mut rval = RootedValue::new(cx, UndefinedValue());
|
let mut rval = RootedValue::new(cx, UndefinedValue());
|
||||||
|
|
||||||
this.evaluate_js_on_global_with_result(&code_str, rval.handle_mut());
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if is_interval == IsInterval::NonInterval {
|
fn schedule_timer_call(&self) {
|
||||||
self.active_timers.borrow_mut().remove(&timer_id);
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,3 +38,4 @@ libc = "0.1"
|
||||||
euclid = "0.2"
|
euclid = "0.2"
|
||||||
serde = "0.6"
|
serde = "0.6"
|
||||||
serde_macros = "0.5"
|
serde_macros = "0.5"
|
||||||
|
time = "0.1.12"
|
||||||
|
|
|
@ -19,11 +19,13 @@ extern crate msg;
|
||||||
extern crate net_traits;
|
extern crate net_traits;
|
||||||
extern crate profile_traits;
|
extern crate profile_traits;
|
||||||
extern crate serde;
|
extern crate serde;
|
||||||
|
extern crate time;
|
||||||
extern crate url;
|
extern crate url;
|
||||||
extern crate util;
|
extern crate util;
|
||||||
|
|
||||||
use app_units::Au;
|
use app_units::Au;
|
||||||
use devtools_traits::ScriptToDevtoolsControlMsg;
|
use devtools_traits::ScriptToDevtoolsControlMsg;
|
||||||
|
use euclid::length::Length;
|
||||||
use euclid::point::Point2D;
|
use euclid::point::Point2D;
|
||||||
use euclid::rect::Rect;
|
use euclid::rect::Rect;
|
||||||
use ipc_channel::ipc::{IpcReceiver, IpcSender};
|
use ipc_channel::ipc::{IpcReceiver, IpcSender};
|
||||||
|
@ -36,10 +38,11 @@ use msg::webdriver_msg::WebDriverScriptCommand;
|
||||||
use net_traits::ResourceTask;
|
use net_traits::ResourceTask;
|
||||||
use net_traits::image_cache_task::ImageCacheTask;
|
use net_traits::image_cache_task::ImageCacheTask;
|
||||||
use net_traits::storage_task::StorageTask;
|
use net_traits::storage_task::StorageTask;
|
||||||
use profile_traits::{mem, time};
|
use profile_traits::mem;
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use std::sync::mpsc::{Receiver, Sender};
|
use std::sync::mpsc::{Receiver, Sender};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
use util::mem::HeapSizeOf;
|
||||||
|
|
||||||
/// The address of a node. Layout sends these back. They must be validated via
|
/// 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.
|
/// `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.
|
/// crates that don't need to know about them.
|
||||||
pub struct OpaqueScriptLayoutChannel(pub (Box<Any + Send>, Box<Any + Send>));
|
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.
|
/// Data needed to construct a script thread.
|
||||||
pub struct InitialScriptState {
|
pub struct InitialScriptState {
|
||||||
/// The ID of the pipeline with which this script thread is associated.
|
/// The ID of the pipeline with which this script thread is associated.
|
||||||
|
@ -192,6 +245,8 @@ pub struct InitialScriptState {
|
||||||
pub control_port: Receiver<ConstellationControlMsg>,
|
pub control_port: Receiver<ConstellationControlMsg>,
|
||||||
/// A channel on which messages can be sent to the constellation from script.
|
/// A channel on which messages can be sent to the constellation from script.
|
||||||
pub constellation_chan: ConstellationChan,
|
pub constellation_chan: ConstellationChan,
|
||||||
|
/// A channel to schedule timer events.
|
||||||
|
pub scheduler_chan: Sender<TimerEventRequest>,
|
||||||
/// Information that script sends out when it panics.
|
/// Information that script sends out when it panics.
|
||||||
pub failure_info: Failure,
|
pub failure_info: Failure,
|
||||||
/// A channel to the resource manager task.
|
/// A channel to the resource manager task.
|
||||||
|
@ -201,7 +256,7 @@ pub struct InitialScriptState {
|
||||||
/// A channel to the image cache task.
|
/// A channel to the image cache task.
|
||||||
pub image_cache_task: ImageCacheTask,
|
pub image_cache_task: ImageCacheTask,
|
||||||
/// A channel to the time profiler thread.
|
/// 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.
|
/// A channel to the memory profiler thread.
|
||||||
pub mem_profiler_chan: mem::ProfilerChan,
|
pub mem_profiler_chan: mem::ProfilerChan,
|
||||||
/// A channel to the developer tools, if applicable.
|
/// A channel to the developer tools, if applicable.
|
||||||
|
|
1
components/servo/Cargo.lock
generated
1
components/servo/Cargo.lock
generated
|
@ -1567,6 +1567,7 @@ dependencies = [
|
||||||
"profile_traits 0.0.1",
|
"profile_traits 0.0.1",
|
||||||
"serde 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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)",
|
"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)",
|
"url 0.2.37 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"util 0.0.1",
|
"util 0.0.1",
|
||||||
]
|
]
|
||||||
|
|
1
ports/cef/Cargo.lock
generated
1
ports/cef/Cargo.lock
generated
|
@ -1491,6 +1491,7 @@ dependencies = [
|
||||||
"profile_traits 0.0.1",
|
"profile_traits 0.0.1",
|
||||||
"serde 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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)",
|
"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)",
|
"url 0.2.37 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"util 0.0.1",
|
"util 0.0.1",
|
||||||
]
|
]
|
||||||
|
|
1
ports/gonk/Cargo.lock
generated
1
ports/gonk/Cargo.lock
generated
|
@ -1353,6 +1353,7 @@ dependencies = [
|
||||||
"profile_traits 0.0.1",
|
"profile_traits 0.0.1",
|
||||||
"serde 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"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)",
|
"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)",
|
"url 0.2.37 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"util 0.0.1",
|
"util 0.0.1",
|
||||||
]
|
]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue