diff --git a/components/compositing/compositor.rs b/components/compositing/compositor.rs index 087e9b4773c..e098de519bb 100644 --- a/components/compositing/compositor.rs +++ b/components/compositing/compositor.rs @@ -21,6 +21,7 @@ use windowing::PinchZoomWindowEvent; use azure::azure_hl::SourceSurfaceMethods; use azure::azure_hl; +use std::cmp; use geom::matrix::identity; use geom::point::{Point2D, TypedPoint2D}; use geom::rect::Rect; @@ -100,8 +101,8 @@ pub struct IOCompositor { /// The time of the last zoom action has started. zoom_time: f64, - /// Current display/reflow status of the page - ready_state: ReadyState, + /// Current display/reflow status of each pipeline. + ready_states: HashMap, /// Whether the page being rendered has loaded completely. /// Differs from ReadyState because we can finish loading (ready) @@ -165,7 +166,7 @@ impl IOCompositor { viewport_zoom: ScaleFactor(1.0), zoom_action: false, zoom_time: 0f64, - ready_state: Blank, + ready_states: HashMap::new(), load_complete: false, constellation_chan: constellation_chan, time_profiler_chan: time_profiler_chan, @@ -275,9 +276,8 @@ impl IOCompositor { break; } - (Ok(ChangeReadyState(ready_state)), NotShuttingDown) => { - self.window.set_ready_state(ready_state); - self.ready_state = ready_state; + (Ok(ChangeReadyState(pipeline_id, ready_state)), NotShuttingDown) => { + self.change_ready_state(pipeline_id, ready_state); } (Ok(ChangeRenderState(render_state)), NotShuttingDown) => { @@ -333,6 +333,21 @@ impl IOCompositor { } } + fn change_ready_state(&mut self, pipeline_id: PipelineId, ready_state: ReadyState) { + self.ready_states.insert_or_update_with(pipeline_id, + ready_state, + |_key, value| *value = ready_state); + self.window.set_ready_state(self.get_earliest_pipeline_ready_state()); + } + + fn get_earliest_pipeline_ready_state(&self) -> ReadyState { + if self.ready_states.len() == 0 { + return Blank; + } + return self.ready_states.values().fold(FinishedLoading, |a, &b| cmp::min(a, b)); + + } + fn change_render_state(&mut self, render_state: RenderState) { self.window.set_render_state(render_state); if render_state == IdleRenderState { @@ -873,7 +888,7 @@ impl IOCompositor { // Render to PNG. We must read from the back buffer (ie, before // self.window.present()) as OpenGL ES 2 does not have glReadBuffer(). - if self.load_complete && self.ready_state == FinishedLoading + if self.load_complete && self.get_earliest_pipeline_ready_state() == FinishedLoading && self.opts.output_file.is_some() && !self.has_outstanding_render_msgs() { let (width, height) = (self.window_size.width.get(), self.window_size.height.get()); let path = from_str::(self.opts.output_file.get_ref().as_slice()).unwrap(); diff --git a/components/compositing/compositor_task.rs b/components/compositing/compositor_task.rs index be87ce9462e..dd98381ee3b 100644 --- a/components/compositing/compositor_task.rs +++ b/components/compositing/compositor_task.rs @@ -38,8 +38,8 @@ pub struct CompositorChan { /// Implementation of the abstract `ScriptListener` interface. impl ScriptListener for CompositorChan { - fn set_ready_state(&self, ready_state: ReadyState) { - let msg = ChangeReadyState(ready_state); + fn set_ready_state(&self, pipeline_id: PipelineId, ready_state: ReadyState) { + let msg = ChangeReadyState(pipeline_id, ready_state); self.chan.send(msg); } @@ -174,7 +174,7 @@ pub enum Msg { /// Requests that the compositor paint the given layer buffer set for the given page size. Paint(PipelineId, Epoch, Vec<(LayerId, Box)>), /// Alerts the compositor to the current status of page loading. - ChangeReadyState(ReadyState), + ChangeReadyState(PipelineId, ReadyState), /// Alerts the compositor to the current status of rendering. ChangeRenderState(RenderState), /// Alerts the compositor that the RenderMsg has been discarded. diff --git a/components/msg/compositor_msg.rs b/components/msg/compositor_msg.rs index 6008e4fca23..650c7fae71a 100644 --- a/components/msg/compositor_msg.rs +++ b/components/msg/compositor_msg.rs @@ -20,7 +20,7 @@ pub enum RenderState { RenderingRenderState, } -#[deriving(PartialEq, Clone)] +#[deriving(Eq, Ord, PartialEq, PartialOrd, Clone)] pub enum ReadyState { /// Informs the compositor that nothing has been done yet. Used for setting status Blank, @@ -107,7 +107,7 @@ pub trait RenderListener { /// The interface used by the script task to tell the compositor to update its ready state, /// which is used in displaying the appropriate message in the window's title. pub trait ScriptListener : Clone { - fn set_ready_state(&self, ReadyState); + fn set_ready_state(&self, PipelineId, ReadyState); fn scroll_fragment_point(&self, pipeline_id: PipelineId, layer_id: LayerId, diff --git a/components/script/page.rs b/components/script/page.rs index e61c3cd1a58..cf532783c8b 100644 --- a/components/script/page.rs +++ b/components/script/page.rs @@ -328,7 +328,7 @@ impl Page { self.join_layout(); // Tell the user that we're performing layout. - compositor.set_ready_state(PerformingLayout); + compositor.set_ready_state(self.id, PerformingLayout); // Layout will let us know when it's done. let (join_chan, join_port) = channel(); diff --git a/components/script/script_task.rs b/components/script/script_task.rs index e49dc600211..e743b9a098e 100644 --- a/components/script/script_task.rs +++ b/components/script/script_task.rs @@ -512,7 +512,8 @@ impl ScriptTask { let mut layout_join_port = page.layout_join_port.deref().borrow_mut(); *layout_join_port = None; } - self.compositor.set_ready_state(FinishedLoading); + + self.compositor.set_ready_state(pipeline_id, FinishedLoading); if page.pending_reflows.get() > 0 { page.pending_reflows.set(0); @@ -616,7 +617,7 @@ impl ScriptTask { let document = Document::new(&*window, Some(url.clone()), HTMLDocument, None).root(); window.deref().init_browser_context(&*document); - self.compositor.set_ready_state(Loading); + self.compositor.set_ready_state(pipeline_id, Loading); // Parse HTML. // // Note: We can parse the next document in parallel with any previous documents.