aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--components/compositing/compositor.rs116
-rw-r--r--components/compositing/compositor_task.rs3
-rw-r--r--components/compositing/constellation.rs25
-rw-r--r--components/compositing/headless.rs1
-rw-r--r--components/compositing/lib.rs2
-rw-r--r--components/compositing/pipeline.rs20
-rw-r--r--components/gfx/paint_task.rs57
-rw-r--r--components/layout/layout_task.rs22
-rw-r--r--components/layout_traits/lib.rs5
-rw-r--r--components/msg/constellation_msg.rs7
-rw-r--r--components/script/layout_interface.rs4
-rw-r--r--components/script/script_task.rs18
-rw-r--r--components/script_traits/lib.rs3
13 files changed, 180 insertions, 103 deletions
diff --git a/components/compositing/compositor.rs b/components/compositing/compositor.rs
index 0e5294ae2f3..71a37ddbc4f 100644
--- a/components/compositing/compositor.rs
+++ b/components/compositing/compositor.rs
@@ -129,6 +129,9 @@ pub struct IOCompositor<Window: WindowMethods> {
/// Pending scroll events.
pending_scroll_events: Vec<ScrollEvent>,
+
+ /// PaintTask channels that we know about, used to return unused buffers.
+ paint_channels: HashMap<PipelineId, PaintChan>,
}
pub struct ScrollEvent {
@@ -198,6 +201,7 @@ impl<Window: WindowMethods> IOCompositor<Window> {
fragment_point: None,
outstanding_paint_msgs: 0,
last_composite_time: 0,
+ paint_channels: HashMap::new(),
}
}
@@ -341,6 +345,12 @@ impl<Window: WindowMethods> IOCompositor<Window> {
self.window.set_cursor(cursor)
}
+ (Msg::PaintTaskExited(pipeline_id), ShutdownState::NotShuttingDown) => {
+ if self.paint_channels.remove(&pipeline_id).is_none() {
+ panic!("Saw PaintTaskExited message from an unknown pipeline!");
+ }
+ }
+
// When we are shutting_down, we need to avoid performing operations
// such as Paint that may crash because we have begun tearing down
// the rest of our resources.
@@ -456,6 +466,38 @@ impl<Window: WindowMethods> IOCompositor<Window> {
self.composite_if_necessary();
}
+ fn create_root_layer_for_pipeline_and_rect(&mut self,
+ pipeline: &CompositionPipeline,
+ frame_rect: Option<TypedRect<PagePx, f32>>)
+ -> Rc<Layer<CompositorData>> {
+ let layer_properties = LayerProperties {
+ pipeline_id: pipeline.id,
+ epoch: Epoch(0),
+ id: LayerId::null(),
+ rect: Rect::zero(),
+ background_color: azure_hl::Color::new(0., 0., 0., 0.),
+ scroll_policy: Scrollable,
+ };
+
+ let root_layer = CompositorData::new_layer(pipeline.clone(),
+ layer_properties,
+ WantsScrollEventsFlag::WantsScrollEvents,
+ opts::get().tile_size);
+ if !self.paint_channels.contains_key(&pipeline.id) {
+ self.paint_channels.insert(pipeline.id, pipeline.paint_chan.clone());
+ }
+
+ // All root layers mask to bounds.
+ *root_layer.masks_to_bounds.borrow_mut() = true;
+
+ if let Some(ref frame_rect) = frame_rect {
+ let frame_rect = frame_rect.to_untyped();
+ *root_layer.bounds.borrow_mut() = Rect::from_untyped(&frame_rect);
+ }
+
+ return root_layer;
+ }
+
fn create_frame_tree_root_layers(&mut self,
frame_tree: &SendableFrameTree,
frame_rect: Option<TypedRect<PagePx, f32>>)
@@ -464,7 +506,8 @@ impl<Window: WindowMethods> IOCompositor<Window> {
self.ready_states.insert(frame_tree.pipeline.id, Blank);
self.paint_states.insert(frame_tree.pipeline.id, PaintingPaintState);
- let root_layer = create_root_layer_for_pipeline_and_rect(&frame_tree.pipeline, frame_rect);
+ let root_layer = self.create_root_layer_for_pipeline_and_rect(&frame_tree.pipeline,
+ frame_rect);
for kid in frame_tree.children.iter() {
root_layer.add_child(self.create_frame_tree_root_layers(&kid.frame_tree, kid.rect));
}
@@ -474,8 +517,8 @@ impl<Window: WindowMethods> IOCompositor<Window> {
fn update_frame_tree(&mut self, frame_tree_diff: &FrameTreeDiff) {
let parent_layer = self.find_pipeline_root_layer(frame_tree_diff.parent_pipeline.id);
parent_layer.add_child(
- create_root_layer_for_pipeline_and_rect(&frame_tree_diff.pipeline,
- frame_tree_diff.rect));
+ self.create_root_layer_for_pipeline_and_rect(&frame_tree_diff.pipeline,
+ frame_tree_diff.rect));
}
fn find_pipeline_root_layer(&self, pipeline_id: PipelineId) -> Rc<Layer<CompositorData>> {
@@ -614,6 +657,24 @@ impl<Window: WindowMethods> IOCompositor<Window> {
layer_id: LayerId,
new_layer_buffer_set: Box<LayerBufferSet>,
epoch: Epoch) {
+ match self.find_layer_with_pipeline_and_layer_id(pipeline_id, layer_id) {
+ Some(layer) => self.paint_to_layer(layer, new_layer_buffer_set, epoch),
+ None => {
+ match self.paint_channels.entry(pipeline_id) {
+ Occupied(entry) => {
+ let message = UnusedBufferMsg(new_layer_buffer_set.buffers);
+ let _ = entry.get().send_opt(message);
+ },
+ Vacant(_) => panic!("Received a buffer from an unknown pipeline!"),
+ }
+ }
+ }
+ }
+
+ fn paint_to_layer(&mut self,
+ layer: Rc<Layer<CompositorData>>,
+ new_layer_buffer_set: Box<LayerBufferSet>,
+ epoch: Epoch) {
debug!("compositor received new frame at size {}x{}",
self.window_size.width.get(),
self.window_size.height.get());
@@ -622,20 +683,10 @@ impl<Window: WindowMethods> IOCompositor<Window> {
let mut new_layer_buffer_set = new_layer_buffer_set;
new_layer_buffer_set.mark_will_leak();
- match self.find_layer_with_pipeline_and_layer_id(pipeline_id, layer_id) {
- Some(ref layer) => {
- // FIXME(pcwalton): This is going to cause problems with inconsistent frames since
- // we only composite one layer at a time.
- assert!(layer.add_buffers(new_layer_buffer_set, epoch));
- self.composite_if_necessary();
- }
- None => {
- // FIXME: This may potentially be triggered by a race condition where a
- // buffers are being painted but the layer is removed before painting
- // completes.
- panic!("compositor given paint command for non-existent layer");
- }
- }
+ // FIXME(pcwalton): This is going to cause problems with inconsistent frames since
+ // we only composite one layer at a time.
+ assert!(layer.add_buffers(new_layer_buffer_set, epoch));
+ self.composite_if_necessary();
}
fn scroll_fragment_to_point(&mut self,
@@ -1196,37 +1247,6 @@ fn find_layer_with_pipeline_and_layer_id_for_layer(layer: Rc<Layer<CompositorDat
return None;
}
-fn create_root_layer_for_pipeline_and_rect(pipeline: &CompositionPipeline,
- frame_rect: Option<TypedRect<PagePx, f32>>)
- -> Rc<Layer<CompositorData>> {
- let layer_properties = LayerProperties {
- pipeline_id: pipeline.id,
- epoch: Epoch(0),
- id: LayerId::null(),
- rect: Rect::zero(),
- background_color: azure_hl::Color::new(0., 0., 0., 0.),
- scroll_policy: Scrollable,
- };
-
- let root_layer = CompositorData::new_layer(pipeline.clone(),
- layer_properties,
- WantsScrollEventsFlag::WantsScrollEvents,
- opts::get().tile_size);
-
- // All root layers mask to bounds.
- *root_layer.masks_to_bounds.borrow_mut() = true;
-
- match frame_rect {
- Some(ref frame_rect) => {
- let frame_rect = frame_rect.to_untyped();
- *root_layer.bounds.borrow_mut() = Rect::from_untyped(&frame_rect);
- }
- None => {}
- }
-
- return root_layer;
-}
-
impl<Window> CompositorEventListener for IOCompositor<Window> where Window: WindowMethods {
fn handle_event(&mut self, msg: WindowEvent) -> bool {
// Check for new messages coming from the other tasks in the system.
diff --git a/components/compositing/compositor_task.rs b/components/compositing/compositor_task.rs
index c8e8932b956..147bbe4b211 100644
--- a/components/compositing/compositor_task.rs
+++ b/components/compositing/compositor_task.rs
@@ -216,6 +216,8 @@ pub enum Msg {
KeyEvent(Key, KeyModifiers),
/// Changes the cursor.
SetCursor(Cursor),
+ /// Informs the compositor that the paint task for the given pipeline has exited.
+ PaintTaskExited(PipelineId),
}
impl Show for Msg {
@@ -240,6 +242,7 @@ impl Show for Msg {
Msg::ScrollTimeout(..) => write!(f, "ScrollTimeout"),
Msg::KeyEvent(..) => write!(f, "KeyEvent"),
Msg::SetCursor(..) => write!(f, "SetCursor"),
+ Msg::PaintTaskExited(..) => write!(f, "PaintTaskExited"),
}
}
}
diff --git a/components/compositing/constellation.rs b/components/compositing/constellation.rs
index 118f33bc3f3..6ec526d1b90 100644
--- a/components/compositing/constellation.rs
+++ b/components/compositing/constellation.rs
@@ -11,11 +11,10 @@ use devtools_traits::DevtoolsControlChan;
use geom::rect::{Rect, TypedRect};
use geom::scale_factor::ScaleFactor;
use gfx::font_cache_task::FontCacheTask;
-use gfx::paint_task;
use layers::geometry::DevicePixel;
-use layout_traits::{LayoutControlChan, LayoutTaskFactory, ExitNowMsg};
+use layout_traits::LayoutTaskFactory;
use libc;
-use script_traits::{mod, GetTitleMsg, ResizeMsg, ResizeInactiveMsg, ExitPipelineMsg, SendEventMsg};
+use script_traits::{mod, GetTitleMsg, ResizeMsg, ResizeInactiveMsg, SendEventMsg};
use script_traits::{ScriptControlChan, ScriptTaskFactory};
use servo_msg::compositor_msg::LayerId;
use servo_msg::constellation_msg::{mod, ConstellationChan, ExitMsg, FailureMsg, Failure};
@@ -23,7 +22,7 @@ use servo_msg::constellation_msg::{FrameRectMsg, GetPipelineTitleMsg};
use servo_msg::constellation_msg::{IFrameSandboxState, IFrameUnsandboxed, InitLoadUrlMsg};
use servo_msg::constellation_msg::{KeyEvent, Key, KeyState, KeyModifiers, LoadCompleteMsg};
use servo_msg::constellation_msg::{LoadData, LoadUrlMsg, NavigateMsg, NavigationType};
-use servo_msg::constellation_msg::{PainterReadyMsg, PipelineId, ResizedWindowMsg};
+use servo_msg::constellation_msg::{PainterReadyMsg, PipelineExitType, PipelineId, ResizedWindowMsg};
use servo_msg::constellation_msg::{ScriptLoadedURLInIFrameMsg, SetCursorMsg, SubpageId};
use servo_msg::constellation_msg::{WindowSizeData};
use servo_msg::constellation_msg::Msg as ConstellationMsg;
@@ -515,7 +514,7 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
fn handle_exit(&mut self) {
for (_id, ref pipeline) in self.pipelines.iter() {
- pipeline.exit();
+ pipeline.exit(PipelineExitType::Complete);
}
self.image_cache_task.exit();
self.resource_task.send(resource_task::Exit);
@@ -547,14 +546,8 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
Some(pipeline) => pipeline.clone()
};
- fn force_pipeline_exit(old_pipeline: &Rc<Pipeline>) {
- let ScriptControlChan(ref old_script) = old_pipeline.script_chan;
- let _ = old_script.send_opt(ExitPipelineMsg(old_pipeline.id));
- let _ = old_pipeline.paint_chan.send_opt(paint_task::ExitMsg(None));
- let LayoutControlChan(ref old_layout) = old_pipeline.layout_chan;
- let _ = old_layout.send_opt(ExitNowMsg);
- }
- force_pipeline_exit(&old_pipeline);
+ old_pipeline.force_exit();
+ self.compositor_proxy.send(CompositorMsg::PaintTaskExited(old_pipeline.id));
self.pipelines.remove(&pipeline_id);
loop {
@@ -564,7 +557,8 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
match idx {
Some(idx) => {
debug!("removing pending frame change for failed pipeline");
- force_pipeline_exit(&self.pending_frames[idx].after.pipeline);
+ self.pending_frames[idx].after.pipeline.force_exit();
+ self.compositor_proxy.send(CompositorMsg::PaintTaskExited(old_pipeline.id));
self.pending_frames.remove(idx);
},
None => break,
@@ -987,7 +981,8 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
// TODO(tkuehn): should only exit once per unique script task,
// and then that script task will handle sub-exits
for frame_tree in frame_tree.iter() {
- frame_tree.pipeline.exit();
+ frame_tree.pipeline.exit(PipelineExitType::PipelineOnly);
+ self.compositor_proxy.send(CompositorMsg::PaintTaskExited(frame_tree.pipeline.id));
self.pipelines.remove(&frame_tree.pipeline.id);
}
}
diff --git a/components/compositing/headless.rs b/components/compositing/headless.rs
index 9651ec7de78..e2ed207b274 100644
--- a/components/compositing/headless.rs
+++ b/components/compositing/headless.rs
@@ -111,6 +111,7 @@ impl CompositorEventListener for NullCompositor {
Msg::ChangePageLoadData(..) |
Msg::KeyEvent(..) |
Msg::SetCursor(..) => {}
+ Msg::PaintTaskExited(..) => {}
}
true
}
diff --git a/components/compositing/lib.rs b/components/compositing/lib.rs
index 4e906b83133..ea0405a3045 100644
--- a/components/compositing/lib.rs
+++ b/components/compositing/lib.rs
@@ -5,7 +5,7 @@
#![comment = "The Servo Parallel Browser Project"]
#![license = "MPL"]
-#![feature(globs, phase, macro_rules)]
+#![feature(globs, phase, macro_rules, if_let)]
#![deny(unused_imports)]
#![deny(unused_variables)]
diff --git a/components/compositing/pipeline.rs b/components/compositing/pipeline.rs
index 6dd9dabb0bf..e90bf221e1d 100644
--- a/components/compositing/pipeline.rs
+++ b/components/compositing/pipeline.rs
@@ -3,15 +3,16 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use CompositorProxy;
-use layout_traits::{LayoutTaskFactory, LayoutControlChan};
+use layout_traits::{ExitNowMsg, LayoutTaskFactory, LayoutControlChan};
use script_traits::{ScriptControlChan, ScriptTaskFactory};
use script_traits::{AttachLayoutMsg, LoadMsg, NewLayoutInfo, ExitPipelineMsg};
use devtools_traits::DevtoolsControlChan;
+use gfx::paint_task;
use gfx::paint_task::{PaintPermissionGranted, PaintPermissionRevoked};
use gfx::paint_task::{PaintChan, PaintTask};
-use servo_msg::constellation_msg::{ConstellationChan, Failure, PipelineId, SubpageId};
-use servo_msg::constellation_msg::{LoadData, WindowSizeData};
+use servo_msg::constellation_msg::{ConstellationChan, ExitMsg, Failure, PipelineId, SubpageId};
+use servo_msg::constellation_msg::{LoadData, WindowSizeData, PipelineExitType};
use servo_net::image_cache_task::ImageCacheTask;
use gfx::font_cache_task::FontCacheTask;
use servo_net::resource_task::ResourceTask;
@@ -173,18 +174,27 @@ impl Pipeline {
let _ = self.paint_chan.send_opt(PaintPermissionRevoked);
}
- pub fn exit(&self) {
+ pub fn exit(&self, exit_type: PipelineExitType) {
debug!("pipeline {} exiting", self.id);
// Script task handles shutting down layout, and layout handles shutting down the painter.
// For now, if the script task has failed, we give up on clean shutdown.
let ScriptControlChan(ref chan) = self.script_chan;
- if chan.send_opt(ExitPipelineMsg(self.id)).is_ok() {
+ if chan.send_opt(ExitPipelineMsg(self.id, exit_type)).is_ok() {
// Wait until all slave tasks have terminated and run destructors
// NOTE: We don't wait for script task as we don't always own it
let _ = self.paint_shutdown_port.recv_opt();
let _ = self.layout_shutdown_port.recv_opt();
}
+
+ }
+
+ pub fn force_exit(&self) {
+ let ScriptControlChan(ref script_channel) = self.script_chan;
+ let _ = script_channel.send_opt( ExitPipelineMsg(self.id, PipelineExitType::PipelineOnly));
+ let _ = self.paint_chan.send_opt(paint_task::ExitMsg(None, PipelineExitType::PipelineOnly));
+ let LayoutControlChan(ref layout_channel) = self.layout_chan;
+ let _ = layout_channel.send_opt(ExitNowMsg(PipelineExitType::PipelineOnly));
}
pub fn to_sendable(&self) -> CompositionPipeline {
diff --git a/components/gfx/paint_task.rs b/components/gfx/paint_task.rs
index d5008bf355d..676e0ec5d6b 100644
--- a/components/gfx/paint_task.rs
+++ b/components/gfx/paint_task.rs
@@ -23,8 +23,8 @@ use layers;
use native::task::NativeTaskBuilder;
use servo_msg::compositor_msg::{Epoch, IdlePaintState, LayerId};
use servo_msg::compositor_msg::{LayerMetadata, PaintListener, PaintingPaintState, ScrollPolicy};
-use servo_msg::constellation_msg::{ConstellationChan, Failure, FailureMsg, PipelineId};
-use servo_msg::constellation_msg::{PainterReadyMsg};
+use servo_msg::constellation_msg::{ConstellationChan, Failure, FailureMsg, PipelineExitType};
+use servo_msg::constellation_msg::{PipelineId, PainterReadyMsg};
use servo_msg::platform::surface::NativeSurfaceAzureMethods;
use servo_util::geometry::{Au, ZERO_POINT};
use servo_util::opts;
@@ -73,7 +73,7 @@ pub enum Msg {
UnusedBufferMsg(Vec<Box<LayerBuffer>>),
PaintPermissionGranted,
PaintPermissionRevoked,
- ExitMsg(Option<Sender<()>>),
+ ExitMsg(Option<Sender<()>>, PipelineExitType),
}
#[deriving(Clone)]
@@ -122,6 +122,10 @@ pub struct PaintTask<C> {
/// Communication handles to each of the worker threads.
worker_threads: Vec<WorkerThreadProxy>,
+
+ /// Tracks the number of buffers that the compositor currently owns. The
+ /// PaintTask waits to exit until all buffers are returned.
+ used_buffer_count: uint,
}
// If we implement this as a function, we get borrowck errors from borrowing
@@ -201,6 +205,7 @@ impl<C> PaintTask<C> where C: PaintListener + Send {
epoch: Epoch(0),
buffer_map: BufferMap::new(10000000),
worker_threads: worker_threads,
+ used_buffer_count: 0,
};
paint_task.start();
@@ -223,8 +228,10 @@ impl<C> PaintTask<C> where C: PaintListener + Send {
}
fn start(&mut self) {
- debug!("paint_task: beginning painting loop");
+ debug!("PaintTask: beginning painting loop");
+ let mut exit_response_channel : Option<Sender<()>> = None;
+ let mut waiting_for_compositor_buffers_to_exit = false;
loop {
match self.port.recv() {
Msg::PaintInitMsg(stacking_context) => {
@@ -232,7 +239,7 @@ impl<C> PaintTask<C> where C: PaintListener + Send {
self.root_stacking_context = Some(stacking_context.clone());
if !self.paint_permission {
- debug!("paint_task: paint ready msg");
+ debug!("PaintTask: paint ready msg");
let ConstellationChan(ref mut c) = self.constellation_chan;
c.send(PainterReadyMsg(self.id));
continue;
@@ -245,7 +252,7 @@ impl<C> PaintTask<C> where C: PaintListener + Send {
}
Msg::PaintMsg(requests) => {
if !self.paint_permission {
- debug!("paint_task: paint ready msg");
+ debug!("PaintTask: paint ready msg");
let ConstellationChan(ref mut c) = self.constellation_chan;
c.send(PainterReadyMsg(self.id));
self.compositor.paint_msg_discarded();
@@ -265,13 +272,27 @@ impl<C> PaintTask<C> where C: PaintListener + Send {
self.compositor.set_paint_state(self.id, IdlePaintState);
- debug!("paint_task: returning surfaces");
+ for reply in replies.iter() {
+ let &(_, ref buffer_set) = reply;
+ self.used_buffer_count += (*buffer_set).buffers.len();
+ }
+
+ debug!("PaintTask: returning surfaces");
self.compositor.paint(self.id, self.epoch, replies);
}
Msg::UnusedBufferMsg(unused_buffers) => {
+ debug!("PaintTask: Received {} unused buffers", unused_buffers.len());
+ self.used_buffer_count -= unused_buffers.len();
+
for buffer in unused_buffers.into_iter().rev() {
self.buffer_map.insert(native_graphics_context!(self), buffer);
}
+
+ if waiting_for_compositor_buffers_to_exit && self.used_buffer_count == 0 {
+ debug!("PaintTask: Received all loaned buffers, exiting.");
+ exit_response_channel.map(|channel| channel.send(()));
+ break;
+ }
}
Msg::PaintPermissionGranted => {
self.paint_permission = true;
@@ -290,10 +311,24 @@ impl<C> PaintTask<C> where C: PaintListener + Send {
Msg::PaintPermissionRevoked => {
self.paint_permission = false;
}
- Msg::ExitMsg(response_ch) => {
- debug!("paint_task: exitmsg response send");
- response_ch.map(|ch| ch.send(()));
- break;
+ Msg::ExitMsg(response_channel, exit_type) => {
+ let should_wait_for_compositor_buffers = match exit_type {
+ PipelineExitType::Complete => false,
+ PipelineExitType::PipelineOnly => self.used_buffer_count != 0
+ };
+
+ if !should_wait_for_compositor_buffers {
+ debug!("PaintTask: Exiting without waiting for compositor buffers.");
+ response_channel.map(|channel| channel.send(()));
+ break;
+ }
+
+ // If we own buffers in the compositor and we are not exiting completely, wait
+ // for the compositor to return buffers, so that we can release them properly.
+ // When doing a complete exit, the compositor lets all buffers leak.
+ println!("PaintTask: Saw ExitMsg, {} buffers in use", self.used_buffer_count);
+ waiting_for_compositor_buffers_to_exit = true;
+ exit_response_channel = response_channel;
}
}
}
diff --git a/components/layout/layout_task.rs b/components/layout/layout_task.rs
index 2eeeb31409f..fae8dbb070a 100644
--- a/components/layout/layout_task.rs
+++ b/components/layout/layout_task.rs
@@ -42,8 +42,8 @@ use script::layout_interface::{Reflow, ReflowGoal, ScriptLayoutChan, TrustedNode
use script_traits::{SendEventMsg, ReflowEvent, ReflowCompleteMsg, OpaqueScriptLayoutChannel};
use script_traits::{ScriptControlChan, UntrustedNodeAddress};
use servo_msg::compositor_msg::Scrollable;
-use servo_msg::constellation_msg::{ConstellationChan, PipelineId, Failure, FailureMsg};
-use servo_msg::constellation_msg::{SetCursorMsg};
+use servo_msg::constellation_msg::{ConstellationChan, Failure, FailureMsg, PipelineExitType};
+use servo_msg::constellation_msg::{PipelineId, SetCursorMsg};
use servo_net::image_cache_task::{ImageCacheTask, ImageResponseMsg};
use servo_net::local_image_cache::{ImageResponder, LocalImageCache};
use servo_net::resource_task::{ResourceTask, load_bytes_iter};
@@ -343,8 +343,8 @@ impl LayoutTask {
match port_to_read {
PortToRead::Pipeline => {
match self.pipeline_port.recv() {
- layout_traits::ExitNowMsg => {
- self.handle_script_request(Msg::ExitNow, possibly_locked_rw_data)
+ layout_traits::ExitNowMsg(exit_type) => {
+ self.handle_script_request(Msg::ExitNow(exit_type), possibly_locked_rw_data)
}
}
},
@@ -411,9 +411,9 @@ impl LayoutTask {
self.prepare_to_exit(response_chan, possibly_locked_rw_data);
return false
},
- Msg::ExitNow => {
+ Msg::ExitNow(exit_type) => {
debug!("layout: ExitNowMsg received");
- self.exit_now(possibly_locked_rw_data);
+ self.exit_now(possibly_locked_rw_data, exit_type);
return false
}
}
@@ -435,9 +435,9 @@ impl LayoutTask {
LayoutTask::handle_reap_layout_data(dead_layout_data)
}
}
- Msg::ExitNow => {
+ Msg::ExitNow(exit_type) => {
debug!("layout task is exiting...");
- self.exit_now(possibly_locked_rw_data);
+ self.exit_now(possibly_locked_rw_data, exit_type);
break
}
_ => {
@@ -450,7 +450,9 @@ impl LayoutTask {
/// Shuts down the layout task now. If there are any DOM nodes left, layout will now (safely)
/// crash.
- fn exit_now<'a>(&'a self, possibly_locked_rw_data: &mut Option<MutexGuard<'a, LayoutTaskData>>) {
+ fn exit_now<'a>(&'a self,
+ possibly_locked_rw_data: &mut Option<MutexGuard<'a, LayoutTaskData>>,
+ exit_type: PipelineExitType) {
let (response_chan, response_port) = channel();
{
@@ -462,7 +464,7 @@ impl LayoutTask {
LayoutTask::return_rw_data(possibly_locked_rw_data, rw_data);
}
- self.paint_chan.send(paint_task::ExitMsg(Some(response_chan)));
+ self.paint_chan.send(paint_task::ExitMsg(Some(response_chan), exit_type));
response_port.recv()
}
diff --git a/components/layout_traits/lib.rs b/components/layout_traits/lib.rs
index 229791ff928..acc5587ab5a 100644
--- a/components/layout_traits/lib.rs
+++ b/components/layout_traits/lib.rs
@@ -21,8 +21,7 @@ extern crate "util" as servo_util;
use gfx::font_cache_task::FontCacheTask;
use gfx::paint_task::PaintChan;
-use servo_msg::constellation_msg::{ConstellationChan, PipelineId};
-use servo_msg::constellation_msg::Failure;
+use servo_msg::constellation_msg::{ConstellationChan, Failure, PipelineId, PipelineExitType};
use servo_net::image_cache_task::ImageCacheTask;
use servo_net::resource_task::ResourceTask;
use servo_util::time::TimeProfilerChan;
@@ -31,7 +30,7 @@ use std::comm::Sender;
/// Messages sent to the layout task from the constellation
pub enum LayoutControlMsg {
- ExitNowMsg,
+ ExitNowMsg(PipelineExitType),
}
/// A channel wrapper for constellation messages
diff --git a/components/msg/constellation_msg.rs b/components/msg/constellation_msg.rs
index 78cda641aef..e9e69c669c7 100644
--- a/components/msg/constellation_msg.rs
+++ b/components/msg/constellation_msg.rs
@@ -253,3 +253,10 @@ pub struct PipelineId(pub uint);
#[deriving(Clone, PartialEq, Eq, Hash, Show)]
pub struct SubpageId(pub uint);
+
+// The type of pipeline exit. During complete shutdowns, pipelines do not have to
+// release resources automatically released on process termination.
+pub enum PipelineExitType {
+ PipelineOnly,
+ Complete,
+}
diff --git a/components/script/layout_interface.rs b/components/script/layout_interface.rs
index b19e789a4ed..2856930b073 100644
--- a/components/script/layout_interface.rs
+++ b/components/script/layout_interface.rs
@@ -11,7 +11,7 @@ use dom::node::LayoutDataRef;
use geom::point::Point2D;
use geom::rect::Rect;
use script_traits::{ScriptControlChan, OpaqueScriptLayoutChannel, UntrustedNodeAddress};
-use servo_msg::constellation_msg::WindowSizeData;
+use servo_msg::constellation_msg::{PipelineExitType, WindowSizeData};
use servo_util::geometry::Au;
use std::any::{Any, AnyRefExt};
use std::comm::{channel, Receiver, Sender};
@@ -50,7 +50,7 @@ pub enum Msg {
/// Requests that the layout task immediately shut down. There must be no more nodes left after
/// this, or layout will crash.
- ExitNow,
+ ExitNow(PipelineExitType),
}
/// Synchronous messages that script can send to layout.
diff --git a/components/script/script_task.rs b/components/script/script_task.rs
index 918d776c59f..4840dab2a3b 100644
--- a/components/script/script_task.rs
+++ b/components/script/script_task.rs
@@ -50,6 +50,7 @@ use servo_msg::constellation_msg::{LoadData, LoadUrlMsg, NavigationDirection, Pi
use servo_msg::constellation_msg::{Failure, FailureMsg, WindowSizeData, Key, KeyState};
use servo_msg::constellation_msg::{KeyModifiers, SUPER, SHIFT, CONTROL, ALT, Repeated, Pressed};
use servo_msg::constellation_msg::{Released};
+use servo_msg::constellation_msg::{PipelineExitType};
use servo_msg::constellation_msg;
use servo_net::image_cache_task::ImageCacheTask;
use servo_net::resource_task::{ResourceTask, Load};
@@ -545,8 +546,11 @@ impl ScriptTask {
// Process the gathered events.
for msg in sequential.into_iter() {
match msg {
- MixedMessage::FromConstellation(ExitPipelineMsg(id)) =>
- if self.handle_exit_pipeline_msg(id) { return false },
+ MixedMessage::FromConstellation(ExitPipelineMsg(id, exit_type)) => {
+ if self.handle_exit_pipeline_msg(id, exit_type) {
+ return false
+ }
+ },
MixedMessage::FromConstellation(inner_msg) => self.handle_msg_from_constellation(inner_msg),
MixedMessage::FromScript(inner_msg) => self.handle_msg_from_script(inner_msg),
MixedMessage::FromDevtools(inner_msg) => self.handle_msg_from_devtools(inner_msg),
@@ -713,20 +717,20 @@ impl ScriptTask {
/// Handles a request to exit the script task and shut down layout.
/// Returns true if the script task should shut down and false otherwise.
- fn handle_exit_pipeline_msg(&self, id: PipelineId) -> bool {
+ fn handle_exit_pipeline_msg(&self, id: PipelineId, exit_type: PipelineExitType) -> bool {
// If root is being exited, shut down all pages
let page = self.page.borrow_mut();
if page.id == id {
debug!("shutting down layout for root page {}", id);
*self.js_context.borrow_mut() = None;
- shut_down_layout(&*page, (*self.js_runtime).ptr);
+ shut_down_layout(&*page, (*self.js_runtime).ptr, exit_type);
return true
}
// otherwise find just the matching page and exit all sub-pages
match page.remove(id) {
Some(ref mut page) => {
- shut_down_layout(&*page, (*self.js_runtime).ptr);
+ shut_down_layout(&*page, (*self.js_runtime).ptr, exit_type);
false
}
// TODO(tkuehn): pipeline closing is currently duplicated across
@@ -1261,7 +1265,7 @@ impl ScriptTask {
}
/// Shuts down layout for the given page tree.
-fn shut_down_layout(page_tree: &Rc<Page>, rt: *mut JSRuntime) {
+fn shut_down_layout(page_tree: &Rc<Page>, rt: *mut JSRuntime, exit_type: PipelineExitType) {
for page in page_tree.iter() {
// Tell the layout task to begin shutting down, and wait until it
// processed this message.
@@ -1290,7 +1294,7 @@ fn shut_down_layout(page_tree: &Rc<Page>, rt: *mut JSRuntime) {
// Destroy the layout task. If there were node leaks, layout will now crash safely.
for page in page_tree.iter() {
let LayoutChan(ref chan) = page.layout_chan;
- chan.send(layout_interface::Msg::ExitNow);
+ chan.send(layout_interface::Msg::ExitNow(exit_type));
}
}
diff --git a/components/script_traits/lib.rs b/components/script_traits/lib.rs
index 59a1447a75a..ada4ba7cfbf 100644
--- a/components/script_traits/lib.rs
+++ b/components/script_traits/lib.rs
@@ -26,6 +26,7 @@ use devtools_traits::DevtoolsControlChan;
use libc::c_void;
use servo_msg::constellation_msg::{ConstellationChan, PipelineId, Failure, WindowSizeData};
use servo_msg::constellation_msg::{LoadData, SubpageId, Key, KeyState, KeyModifiers};
+use servo_msg::constellation_msg::PipelineExitType;
use servo_msg::compositor_msg::ScriptListener;
use servo_net::image_cache_task::ImageCacheTask;
use servo_net::resource_task::ResourceTask;
@@ -60,7 +61,7 @@ pub enum ConstellationControlMsg {
/// Notifies script that window has been resized but to not take immediate action.
ResizeInactiveMsg(PipelineId, WindowSizeData),
/// Notifies the script that a pipeline should be closed.
- ExitPipelineMsg(PipelineId),
+ ExitPipelineMsg(PipelineId, PipelineExitType),
/// Sends a DOM event.
SendEventMsg(PipelineId, CompositorEvent),
/// Notifies script that reflow is finished.