Add thaw/freeze messages that can suspend/resume webcontent timers #4907

This commit is contained in:
Pawel Kondzior 2015-02-19 11:20:55 +07:00
parent dc31d96f65
commit c2961c94b4
6 changed files with 105 additions and 9 deletions

View file

@ -834,6 +834,8 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
pipeline.clone(), pipeline.clone(),
parent.borrow().clone())), parent.borrow().clone())),
NavigationType::Load); NavigationType::Load);
// Send message to ScriptTask that will suspend all timers
source_frame.pipeline.borrow().freeze();
self.pipelines.insert(pipeline.id, pipeline); self.pipelines.insert(pipeline.id, pipeline);
} }
@ -853,6 +855,7 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
let old = self.current_frame().as_ref().unwrap(); let old = self.current_frame().as_ref().unwrap();
for frame in old.iter() { for frame in old.iter() {
frame.pipeline.borrow().revoke_paint_permission(); frame.pipeline.borrow().revoke_paint_permission();
frame.pipeline.borrow().freeze();
} }
} }
self.navigation_context.forward(&mut *self.compositor_proxy) self.navigation_context.forward(&mut *self.compositor_proxy)
@ -865,6 +868,7 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
let old = self.current_frame().as_ref().unwrap(); let old = self.current_frame().as_ref().unwrap();
for frame in old.iter() { for frame in old.iter() {
frame.pipeline.borrow().revoke_paint_permission(); frame.pipeline.borrow().revoke_paint_permission();
frame.pipeline.borrow().freeze();
} }
} }
self.navigation_context.back(&mut *self.compositor_proxy) self.navigation_context.back(&mut *self.compositor_proxy)
@ -873,6 +877,7 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
for frame in destination_frame.iter() { for frame in destination_frame.iter() {
frame.pipeline.borrow().load(); frame.pipeline.borrow().load();
frame.pipeline.borrow().thaw();
} }
self.send_frame_tree_and_grant_paint_permission(destination_frame); self.send_frame_tree_and_grant_paint_permission(destination_frame);

View file

@ -191,6 +191,16 @@ impl Pipeline {
} }
pub fn freeze(&self) {
let ScriptControlChan(ref script_channel) = self.script_chan;
let _ = script_channel.send(ConstellationControlMsg::Freeze(self.id)).unwrap();
}
pub fn thaw(&self) {
let ScriptControlChan(ref script_channel) = self.script_chan;
let _ = script_channel.send(ConstellationControlMsg::Thaw(self.id)).unwrap();
}
pub fn force_exit(&self) { pub fn force_exit(&self) {
let ScriptControlChan(ref script_channel) = self.script_chan; let ScriptControlChan(ref script_channel) = self.script_chan;
let _ = script_channel.send( let _ = script_channel.send(

View file

@ -335,6 +335,8 @@ pub trait WindowHelpers {
fn load_url(self, href: DOMString); fn load_url(self, href: DOMString);
fn handle_fire_timer(self, timer_id: TimerId); fn handle_fire_timer(self, timer_id: TimerId);
fn IndexedGetter(self, _index: u32, _found: &mut bool) -> Option<Temporary<Window>>; fn IndexedGetter(self, _index: u32, _found: &mut bool) -> Option<Temporary<Window>>;
fn thaw(self);
fn freeze(self);
} }
pub trait ScriptHelpers { pub trait ScriptHelpers {
@ -404,6 +406,15 @@ impl<'a> WindowHelpers for JSRef<'a, Window> {
fn IndexedGetter(self, _index: u32, _found: &mut bool) -> Option<Temporary<Window>> { fn IndexedGetter(self, _index: u32, _found: &mut bool) -> Option<Temporary<Window>> {
None None
} }
fn thaw(self) {
self.timers.resume();
}
fn freeze(self) {
self.timers.suspend();
}
} }
impl Window { impl Window {

View file

@ -602,6 +602,10 @@ impl ScriptTask {
panic!("should have handled ExitPipeline already"), panic!("should have handled ExitPipeline already"),
ConstellationControlMsg::GetTitle(pipeline_id) => ConstellationControlMsg::GetTitle(pipeline_id) =>
self.handle_get_title_msg(pipeline_id), self.handle_get_title_msg(pipeline_id),
ConstellationControlMsg::Freeze(pipeline_id) =>
self.handle_freeze_msg(pipeline_id),
ConstellationControlMsg::Thaw(pipeline_id) =>
self.handle_thaw_msg(pipeline_id)
} }
} }
@ -685,6 +689,26 @@ impl ScriptTask {
window.r().handle_fire_timer(timer_id); window.r().handle_fire_timer(timer_id);
} }
/// Handles freeze message
fn handle_freeze_msg(&self, id: PipelineId) {
let page = self.page.borrow_mut();
let page = page.find(id).expect("ScriptTask: received freeze msg for a
pipeline ID not associated with this script task. This is a bug.");
let frame = page.frame();
let window = frame.as_ref().unwrap().window.root();
window.r().freeze();
}
/// Handles thaw message
fn handle_thaw_msg(&self, id: PipelineId) {
let page = self.page.borrow_mut();
let page = page.find(id).expect("ScriptTask: received thaw msg for a
pipeline ID not associated with this script task. This is a bug.");
let frame = page.frame();
let window = frame.as_ref().unwrap().window.root();
window.r().thaw();
}
/// Handles a notification that reflow completed. /// Handles a notification that reflow completed.
fn handle_reflow_complete_msg(&self, pipeline_id: PipelineId, reflow_id: uint) { fn handle_reflow_complete_msg(&self, pipeline_id: PipelineId, reflow_id: uint) {
debug!("Script: Reflow {:?} complete for {:?}", reflow_id, pipeline_id); debug!("Script: Reflow {:?} complete for {:?}", reflow_id, pipeline_id);

View file

@ -37,7 +37,7 @@ pub struct TimerId(i32);
struct TimerHandle { struct TimerHandle {
handle: TimerId, handle: TimerId,
data: TimerData, data: TimerData,
cancel_chan: Option<Sender<()>>, control_chan: Option<Sender<TimerControlMsg>>,
} }
#[jstraceable] #[jstraceable]
@ -56,7 +56,13 @@ impl<H: Writer + Hasher> Hash<H> for TimerId {
impl TimerHandle { impl TimerHandle {
fn cancel(&mut self) { fn cancel(&mut self) {
self.cancel_chan.as_ref().map(|chan| chan.send(()).ok()); 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());
} }
} }
@ -85,6 +91,15 @@ pub enum IsInterval {
NonInterval, NonInterval,
} }
// Messages sent control timers from script task
#[jstraceable]
#[derive(PartialEq, Copy, Clone, Show)]
pub enum TimerControlMsg {
Cancel,
Suspend,
Resume
}
// Holder for the various JS values associated with setTimeout // Holder for the various JS values associated with setTimeout
// (ie. function value to invoke and all arguments to pass // (ie. function value to invoke and all arguments to pass
// to the function when calling it) // to the function when calling it)
@ -106,6 +121,17 @@ impl TimerManager {
} }
} }
pub fn suspend(&self) {
for (_, timer_handle) in self.active_timers.borrow_mut().iter_mut() {
timer_handle.suspend();
}
}
pub fn resume(&self) {
for (_, timer_handle) in self.active_timers.borrow_mut().iter_mut() {
timer_handle.resume();
}
}
#[allow(unsafe_blocks)] #[allow(unsafe_blocks)]
pub fn set_timeout_or_interval(&self, pub fn set_timeout_or_interval(&self,
callback: TimerCallback, callback: TimerCallback,
@ -122,7 +148,7 @@ impl TimerManager {
// Spawn a new timer task; it will dispatch the `ScriptMsg::FireTimer` // Spawn a new timer task; it will dispatch the `ScriptMsg::FireTimer`
// to the relevant script handler that will deal with it. // to the relevant script handler that will deal with it.
let tm = Timer::new().unwrap(); let tm = Timer::new().unwrap();
let (cancel_chan, cancel_port) = channel(); let (control_chan, control_port) = channel();
let spawn_name = match source { let spawn_name = match source {
TimerSource::FromWindow(_) if is_interval == IsInterval::Interval => "Window:SetInterval", TimerSource::FromWindow(_) if is_interval == IsInterval::Interval => "Window:SetInterval",
TimerSource::FromWorker if is_interval == IsInterval::Interval => "Worker:SetInterval", TimerSource::FromWorker if is_interval == IsInterval::Interval => "Worker:SetInterval",
@ -137,31 +163,47 @@ impl TimerManager {
} else { } else {
tm.oneshot(duration) tm.oneshot(duration)
}; };
let cancel_port = cancel_port; let control_port = control_port;
let select = Select::new(); let select = Select::new();
let mut timeout_handle = select.handle(&timeout_port); let mut timeout_handle = select.handle(&timeout_port);
unsafe { timeout_handle.add() }; unsafe { timeout_handle.add() };
let mut cancel_handle = select.handle(&cancel_port); let mut control_handle = select.handle(&control_port);
unsafe { cancel_handle.add() }; unsafe { control_handle.add() };
loop { loop {
let id = select.wait(); let id = select.wait();
if id == timeout_handle.id() { if id == timeout_handle.id() {
timeout_port.recv().unwrap(); timeout_port.recv().unwrap();
script_chan.send(ScriptMsg::FireTimer(source, TimerId(handle))); script_chan.send(ScriptMsg::FireTimer(source, TimerId(handle)));
if is_interval == IsInterval::NonInterval { if is_interval == IsInterval::NonInterval {
break; break;
} }
} else if id == cancel_handle.id() { } else if id == control_handle.id() {;
break; 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_id = TimerId(handle);
let timer = TimerHandle { let timer = TimerHandle {
handle: timer_id, handle: timer_id,
cancel_chan: Some(cancel_chan), control_chan: Some(control_chan),
data: TimerData { data: TimerData {
is_interval: is_interval, is_interval: is_interval,
callback: callback, callback: callback,

View file

@ -71,6 +71,10 @@ pub enum ConstellationControlMsg {
Viewport(PipelineId, Rect<f32>), Viewport(PipelineId, Rect<f32>),
/// Requests that the script task immediately send the constellation the title of a pipeline. /// Requests that the script task immediately send the constellation the title of a pipeline.
GetTitle(PipelineId), GetTitle(PipelineId),
/// Notifies script task to suspend all its timers
Freeze(PipelineId),
/// Notifies script task to resume all its timers
Thaw(PipelineId)
} }
unsafe impl Send for ConstellationControlMsg { unsafe impl Send for ConstellationControlMsg {