mirror of
https://github.com/servo/servo.git
synced 2025-06-10 09:33:13 +00:00
Add thaw/freeze messages that can suspend/resume webcontent timers #4907
This commit is contained in:
parent
dc31d96f65
commit
c2961c94b4
6 changed files with 105 additions and 9 deletions
|
@ -834,6 +834,8 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
|
|||
pipeline.clone(),
|
||||
parent.borrow().clone())),
|
||||
NavigationType::Load);
|
||||
// Send message to ScriptTask that will suspend all timers
|
||||
source_frame.pipeline.borrow().freeze();
|
||||
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();
|
||||
for frame in old.iter() {
|
||||
frame.pipeline.borrow().revoke_paint_permission();
|
||||
frame.pipeline.borrow().freeze();
|
||||
}
|
||||
}
|
||||
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();
|
||||
for frame in old.iter() {
|
||||
frame.pipeline.borrow().revoke_paint_permission();
|
||||
frame.pipeline.borrow().freeze();
|
||||
}
|
||||
}
|
||||
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() {
|
||||
frame.pipeline.borrow().load();
|
||||
frame.pipeline.borrow().thaw();
|
||||
}
|
||||
self.send_frame_tree_and_grant_paint_permission(destination_frame);
|
||||
|
||||
|
|
|
@ -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) {
|
||||
let ScriptControlChan(ref script_channel) = self.script_chan;
|
||||
let _ = script_channel.send(
|
||||
|
|
|
@ -335,6 +335,8 @@ pub trait WindowHelpers {
|
|||
fn load_url(self, href: DOMString);
|
||||
fn handle_fire_timer(self, timer_id: TimerId);
|
||||
fn IndexedGetter(self, _index: u32, _found: &mut bool) -> Option<Temporary<Window>>;
|
||||
fn thaw(self);
|
||||
fn freeze(self);
|
||||
}
|
||||
|
||||
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>> {
|
||||
None
|
||||
}
|
||||
|
||||
fn thaw(self) {
|
||||
self.timers.resume();
|
||||
}
|
||||
|
||||
fn freeze(self) {
|
||||
self.timers.suspend();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl Window {
|
||||
|
|
|
@ -602,6 +602,10 @@ impl ScriptTask {
|
|||
panic!("should have handled ExitPipeline already"),
|
||||
ConstellationControlMsg::GetTitle(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);
|
||||
}
|
||||
|
||||
/// 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.
|
||||
fn handle_reflow_complete_msg(&self, pipeline_id: PipelineId, reflow_id: uint) {
|
||||
debug!("Script: Reflow {:?} complete for {:?}", reflow_id, pipeline_id);
|
||||
|
|
|
@ -37,7 +37,7 @@ pub struct TimerId(i32);
|
|||
struct TimerHandle {
|
||||
handle: TimerId,
|
||||
data: TimerData,
|
||||
cancel_chan: Option<Sender<()>>,
|
||||
control_chan: Option<Sender<TimerControlMsg>>,
|
||||
}
|
||||
|
||||
#[jstraceable]
|
||||
|
@ -56,7 +56,13 @@ impl<H: Writer + Hasher> Hash<H> for TimerId {
|
|||
|
||||
impl TimerHandle {
|
||||
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,
|
||||
}
|
||||
|
||||
// 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
|
||||
// (ie. function value to invoke and all arguments to pass
|
||||
// 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)]
|
||||
pub fn set_timeout_or_interval(&self,
|
||||
callback: TimerCallback,
|
||||
|
@ -122,7 +148,7 @@ impl TimerManager {
|
|||
// Spawn a new timer task; it will dispatch the `ScriptMsg::FireTimer`
|
||||
// to the relevant script handler that will deal with it.
|
||||
let tm = Timer::new().unwrap();
|
||||
let (cancel_chan, cancel_port) = channel();
|
||||
let (control_chan, control_port) = channel();
|
||||
let spawn_name = match source {
|
||||
TimerSource::FromWindow(_) if is_interval == IsInterval::Interval => "Window:SetInterval",
|
||||
TimerSource::FromWorker if is_interval == IsInterval::Interval => "Worker:SetInterval",
|
||||
|
@ -137,31 +163,47 @@ impl TimerManager {
|
|||
} else {
|
||||
tm.oneshot(duration)
|
||||
};
|
||||
let cancel_port = cancel_port;
|
||||
let control_port = control_port;
|
||||
|
||||
let select = Select::new();
|
||||
let mut timeout_handle = select.handle(&timeout_port);
|
||||
unsafe { timeout_handle.add() };
|
||||
let mut cancel_handle = select.handle(&cancel_port);
|
||||
unsafe { cancel_handle.add() };
|
||||
let mut control_handle = select.handle(&control_port);
|
||||
unsafe { control_handle.add() };
|
||||
|
||||
loop {
|
||||
let id = select.wait();
|
||||
|
||||
if id == timeout_handle.id() {
|
||||
timeout_port.recv().unwrap();
|
||||
script_chan.send(ScriptMsg::FireTimer(source, TimerId(handle)));
|
||||
if is_interval == IsInterval::NonInterval {
|
||||
break;
|
||||
}
|
||||
} else if id == cancel_handle.id() {
|
||||
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,
|
||||
cancel_chan: Some(cancel_chan),
|
||||
control_chan: Some(control_chan),
|
||||
data: TimerData {
|
||||
is_interval: is_interval,
|
||||
callback: callback,
|
||||
|
|
|
@ -71,6 +71,10 @@ pub enum ConstellationControlMsg {
|
|||
Viewport(PipelineId, Rect<f32>),
|
||||
/// Requests that the script task immediately send the constellation the title of a pipeline.
|
||||
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 {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue