Various fixes for cleaning up iframes, compositor layers, pipelines and threads.

This allows most of the jquery test suite to run without exhausting thread resources.
This commit is contained in:
Glenn Watson 2015-04-29 09:13:46 +10:00
parent 5e61ebaa05
commit 2b3737d34e
11 changed files with 191 additions and 14 deletions

View file

@ -431,9 +431,7 @@ impl<Window: WindowMethods> IOCompositor<Window> {
}
(Msg::PaintTaskExited(pipeline_id), ShutdownState::NotShuttingDown) => {
if self.pipeline_details.remove(&pipeline_id).is_none() {
panic!("Saw PaintTaskExited message from an unknown pipeline!");
}
self.remove_pipeline_root_layer(pipeline_id);
}
(Msg::ViewportConstrained(pipeline_id, constraints), ShutdownState::NotShuttingDown) => {
@ -591,6 +589,16 @@ impl<Window: WindowMethods> IOCompositor<Window> {
self.find_layer_with_pipeline_and_layer_id(pipeline_id, LayerId::null())
}
fn remove_pipeline_root_layer(&mut self, pipeline_id: PipelineId) {
if let Some(ref root_layer) = self.scene.root {
// Remove all the compositor layers for this pipeline
// and send any owned buffers back to the paint task.
root_layer.remove_root_layer_with_pipeline_id(self, pipeline_id);
self.pipeline_details.remove(&pipeline_id);
}
}
fn update_layer_if_exists(&mut self, pipeline_id: PipelineId, properties: LayerProperties) -> bool {
match self.find_layer_with_pipeline_and_layer_id(pipeline_id, properties.id) {
Some(existing_layer) => {

View file

@ -89,6 +89,14 @@ pub trait CompositorLayer {
/// painter to be destroyed or reused.
fn clear_all_tiles<Window>(&self, compositor: &IOCompositor<Window>) where Window: WindowMethods;
/// Removes the root layer (and any children) for a given pipeline from the
/// compositor. Buffers that the compositor is holding are returned to the
/// owning paint task.
fn remove_root_layer_with_pipeline_id<Window>(&self,
compositor: &IOCompositor<Window>,
pipeline_id: PipelineId)
where Window: WindowMethods;
/// Destroys all tiles of all layers, including children, *without* sending them back to the
/// painter. You must call this only when the paint task is destined to be going down;
/// otherwise, you will leak tiles.
@ -248,6 +256,31 @@ impl CompositorLayer for Layer<CompositorData> {
}
}
fn remove_root_layer_with_pipeline_id<Window>(&self,
compositor: &IOCompositor<Window>,
pipeline_id: PipelineId)
where Window: WindowMethods {
// Find the child that is the root layer for this pipeline.
let index = self.children().iter().position(|kid| {
let extra_data = kid.extra_data.borrow();
extra_data.pipeline_id == pipeline_id && extra_data.id == LayerId::null()
});
match index {
Some(index) => {
// Remove the root layer, and return buffers to the paint task
let child = self.children().remove(index);
child.clear_all_tiles(compositor);
}
None => {
// Wasn't found, recurse into child layers
for kid in self.children().iter() {
kid.remove_root_layer_with_pipeline_id(compositor, pipeline_id);
}
}
}
}
/// Destroys all tiles of all layers, including children, *without* sending them back to the
/// painter. You must call this only when the paint task is destined to be going down;
/// otherwise, you will leak tiles.

View file

@ -114,6 +114,10 @@ impl PaintListener for Box<CompositorProxy+'static+Send> {
// `position: fixed` but will not be sufficient to handle `overflow: scroll` or transforms.
self.send(Msg::InitializeLayersForPipeline(pipeline_id, epoch, properties));
}
fn notify_paint_task_exiting(&mut self, pipeline_id: PipelineId) {
self.send(Msg::PaintTaskExited(pipeline_id))
}
}
/// Messages from the painting task and the constellation task to the compositor task.

View file

@ -443,6 +443,10 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
let is_ready = self.handle_is_ready_to_save_image(pipeline_states);
self.compositor_proxy.send(CompositorMsg::IsReadyToSaveImageReply(is_ready));
}
ConstellationMsg::RemoveIFrame(containing_pipeline_id, subpage_id) => {
debug!("constellation got remove iframe message");
self.handle_remove_iframe_msg(containing_pipeline_id, subpage_id);
}
}
true
}
@ -511,7 +515,14 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
rect: TypedRect<PagePx, f32>) {
// Store the new rect inside the pipeline
let (pipeline_id, script_chan) = {
let pipeline = self.find_subpage(containing_pipeline_id, subpage_id);
// Find the pipeline that corresponds to this rectangle. It's possible that this
// pipeline may have already exited before we process this message, so just
// early exit if that occurs.
let pipeline_id = self.subpage_map.get(&(containing_pipeline_id, subpage_id)).map(|id| *id);
let pipeline = match pipeline_id {
Some(pipeline_id) => self.mut_pipeline(pipeline_id),
None => return,
};
pipeline.rect = Some(rect);
(pipeline.id, pipeline.script_chan.clone())
};
@ -786,6 +797,23 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
self.focus_parent_pipeline(pipeline_id);
}
fn handle_remove_iframe_msg(&mut self, containing_pipeline_id: PipelineId, subpage_id: SubpageId) {
let pipeline_id = self.find_subpage(containing_pipeline_id, subpage_id).id;
let frame_id = self.pipeline_to_frame_map.get(&pipeline_id).map(|id| *id);
match frame_id {
Some(frame_id) => {
// This iframe has already loaded and been added to the frame tree.
self.close_frame(frame_id, ExitPipelineMode::Normal);
}
None => {
// This iframe is currently loading / painting for the first time.
// In this case, it doesn't exist in the frame tree, but the pipeline
// still needs to be shut down.
self.close_pipeline(pipeline_id, ExitPipelineMode::Normal);
}
}
}
fn handle_webdriver_command_msg(&mut self,
pipeline_id: PipelineId,
msg: WebDriverScriptCommand) {
@ -1026,24 +1054,56 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
// Close a frame (and all children)
fn close_frame(&mut self, frame_id: FrameId, exit_mode: ExitPipelineMode) {
let frame = self.frames.remove(&frame_id).unwrap();
// Store information about the pipelines to be closed. Then close the
// pipelines, before removing ourself from the frames hash map. This
// ordering is vital - so that if close_pipeline() ends up closing
// any child frames, they can be removed from the parent frame correctly.
let parent_info = self.pipeline(self.frame(frame_id).current).parent_info;
let pipelines_to_close = {
let mut pipelines_to_close = vec!();
self.close_pipeline(frame.current, exit_mode);
let frame = self.frame(frame_id);
pipelines_to_close.push_all(&frame.next);
pipelines_to_close.push(frame.current);
pipelines_to_close.push_all(&frame.prev);
for pipeline_id in frame.prev.iter().chain(frame.next.iter()) {
pipelines_to_close
};
for pipeline_id in &pipelines_to_close {
self.close_pipeline(*pipeline_id, exit_mode);
}
self.frames.remove(&frame_id).unwrap();
if let Some((parent_pipeline_id, _)) = parent_info {
let parent_pipeline = self.mut_pipeline(parent_pipeline_id);
parent_pipeline.remove_child(frame_id);
}
}
// Close all pipelines at and beneath a given frame
fn close_pipeline(&mut self, pipeline_id: PipelineId, exit_mode: ExitPipelineMode) {
let pipeline = self.pipelines.remove(&pipeline_id).unwrap();
// Store information about the frames to be closed. Then close the
// frames, before removing ourself from the pipelines hash map. This
// ordering is vital - so that if close_frames() ends up closing
// any child pipelines, they can be removed from the parent pipeline correctly.
let frames_to_close = {
let mut frames_to_close = vec!();
let pipeline = self.pipeline(pipeline_id);
frames_to_close.push_all(&pipeline.children);
frames_to_close
};
// Remove any child frames
for child in &pipeline.children {
self.close_frame(*child, exit_mode);
for child_frame in &frames_to_close {
self.close_frame(*child_frame, exit_mode);
}
let pipeline = self.pipelines.remove(&pipeline_id).unwrap();
// If a child pipeline, remove from subpage map
if let Some(info) = pipeline.parent_info {
self.subpage_map.remove(&info);
@ -1052,12 +1112,19 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
// Remove assocation between this pipeline and its holding frame
self.pipeline_to_frame_map.remove(&pipeline_id);
// Remove this pipeline from pending frames if it hasn't loaded yet.
let pending_index = self.pending_frames.iter().position(|frame_change| {
frame_change.new_pipeline_id == pipeline_id
});
if let Some(pending_index) = pending_index {
self.pending_frames.remove(pending_index);
}
// Inform script, compositor that this pipeline has exited.
match exit_mode {
ExitPipelineMode::Normal => pipeline.exit(PipelineExitType::PipelineOnly),
ExitPipelineMode::Force => pipeline.force_exit(),
}
self.compositor_proxy.send(CompositorMsg::PaintTaskExited(pipeline_id));
}
// Convert a frame to a sendable form to pass to the compositor

View file

@ -3,6 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#![feature(box_syntax)]
#![feature(collections)]
#![feature(core)]
#![feature(rustc_private)]

View file

@ -253,6 +253,11 @@ impl Pipeline {
self.children.push(frame_id);
}
pub fn remove_child(&mut self, frame_id: FrameId) {
let index = self.children.iter().position(|id| *id == frame_id).unwrap();
self.children.remove(index);
}
pub fn trigger_mozbrowser_event(&self,
subpage_id: SubpageId,
event: MozBrowserEvent) {