diff --git a/components/compositing/compositor.rs b/components/compositing/compositor.rs index a45dafd54ea..20f39eba587 100644 --- a/components/compositing/compositor.rs +++ b/components/compositing/compositor.rs @@ -2,7 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use compositor_layer::{CompositorData, CompositorLayer, WantsScrollEventsFlag}; +use compositor_layer::{CompositorData, CompositorLayer, RcCompositorLayer, WantsScrollEventsFlag}; use compositor_task::{CompositorEventListener, CompositorProxy}; use compositor_task::{CompositorReceiver, InitialCompositorState, Msg}; use constellation::SendableFrameTree; @@ -28,17 +28,16 @@ use msg::compositor_msg::{Epoch, FrameTreeId, LayerId, LayerKind}; use msg::compositor_msg::{LayerProperties, ScrollPolicy}; use msg::constellation_msg::AnimationState; use msg::constellation_msg::Msg as ConstellationMsg; -use msg::constellation_msg::{ConstellationChan, NavigationDirection}; -use msg::constellation_msg::{Key, KeyModifiers, KeyState, LoadData}; -use msg::constellation_msg::{PipelineId, WindowSizeData}; +use msg::constellation_msg::{ConstellationChan, Key, KeyModifiers, KeyState, LoadData}; +use msg::constellation_msg::{NavigationDirection, PipelineId, SubpageId, WindowSizeData}; use pipeline::CompositionPipeline; use png; use profile_traits::mem::{self, ReportKind, Reporter, ReporterRequest}; use profile_traits::time::{self, ProfilerCategory, profile}; use script_traits::{ConstellationControlMsg, LayoutControlMsg}; use scrolling::ScrollingTimerProxy; -use std::collections::HashMap; use std::collections::hash_map::Entry::{Occupied, Vacant}; +use std::collections::{HashMap, HashSet}; use std::mem as std_mem; use std::rc::Rc; use std::slice::bytes::copy_memory; @@ -49,8 +48,7 @@ use time::{precise_time_ns, precise_time_s}; use url::Url; use util::geometry::{Au, PagePx, ScreenPx, ViewportPx}; use util::opts; -use windowing; -use windowing::{MouseWindowEvent, WindowEvent, WindowMethods, WindowNavigateMsg}; +use windowing::{self, MouseWindowEvent, WindowEvent, WindowMethods, WindowNavigateMsg}; const BUFFER_MAP_SIZE: usize = 10000000; @@ -164,6 +162,14 @@ pub struct IOCompositor { /// A data structure to cache unused NativeSurfaces. surface_map: SurfaceMap, + + /// Information about subpage layers that are pending. The keys in this map are the + /// pipeline/subpage IDs of the parent. + pending_subpage_layers: HashMap<(PipelineId, SubpageId), PendingSubpageLayerInfo>, + + /// Pipeline IDs of subpages that the compositor has seen in a layer tree but which have not + /// yet been painted. + pending_subpages: HashSet, } pub struct ScrollEvent { @@ -308,6 +314,8 @@ impl IOCompositor { has_seen_quit_event: false, ready_to_save_state: ReadyState::Unknown, surface_map: SurfaceMap::new(BUFFER_MAP_SIZE), + pending_subpage_layers: HashMap::new(), + pending_subpages: HashSet::new(), } } @@ -369,6 +377,7 @@ impl IOCompositor { (Msg::InitializeLayersForPipeline(pipeline_id, epoch, properties), ShutdownState::NotShuttingDown) => { self.pipeline_details(pipeline_id).current_epoch = epoch; + self.collect_old_layers(pipeline_id, &properties); for (index, layer_properties) in properties.iter().enumerate() { if index == 0 { @@ -377,6 +386,7 @@ impl IOCompositor { self.create_or_update_descendant_layer(pipeline_id, *layer_properties); } } + self.send_buffer_requests_for_all_layers(); } @@ -385,13 +395,10 @@ impl IOCompositor { chan.send(Some(self.native_display.clone())).unwrap(); } - (Msg::SetLayerRect(pipeline_id, layer_id, rect), - ShutdownState::NotShuttingDown) => { - self.set_layer_rect(pipeline_id, layer_id, &rect); - } - (Msg::AssignPaintedBuffers(pipeline_id, epoch, replies, frame_tree_id), ShutdownState::NotShuttingDown) => { + self.pending_subpages.remove(&pipeline_id); + for (layer_id, new_layer_buffer_set) in replies { self.assign_painted_buffers(pipeline_id, layer_id, @@ -509,6 +516,15 @@ impl IOCompositor { self.window.head_parsed(); } + (Msg::CreateLayerForSubpage(parent_pipeline_id, + parent_subpage_id, + subpage_pipeline_id), + ShutdownState::NotShuttingDown) => { + self.create_layer_for_subpage(parent_pipeline_id, + parent_subpage_id, + subpage_pipeline_id); + } + (Msg::CollectMemoryReports(reports_chan), ShutdownState::NotShuttingDown) => { let mut reports = vec![]; let name = "compositor-task"; @@ -527,6 +543,15 @@ impl IOCompositor { reports_chan.send(reports); } + (Msg::PipelineExited(pipeline_id), _) => { + self.pending_subpages.remove(&pipeline_id); + + // FIXME(pcwalton): This is a total hack. But it'll get complicated to do this + // properly, since we need to get rid of the pending subpage layers if either the + // parent or the child layer goes away. + self.pending_subpage_layers.clear(); + } + // 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. @@ -573,16 +598,9 @@ impl IOCompositor { return self.pipeline_details.get_mut(&pipeline_id).unwrap(); } - pub fn pipeline<'a>(&'a self, pipeline_id: PipelineId) -> &'a CompositionPipeline { + pub fn pipeline<'a>(&'a self, pipeline_id: PipelineId) -> Option<&'a CompositionPipeline> { match self.pipeline_details.get(&pipeline_id) { - Some(ref details) => { - match details.pipeline { - Some(ref pipeline) => pipeline, - None => panic!("Compositor layer has an unitialized pipeline ({:?}).", - pipeline_id), - - } - } + Some(ref details) => details.pipeline.as_ref(), None => panic!("Compositor layer has an unknown pipeline ({:?}).", pipeline_id), } } @@ -606,6 +624,9 @@ impl IOCompositor { new_constellation_chan: ConstellationChan) { response_chan.send(()).unwrap(); + // There are now no more pending iframes. + self.pending_subpages.clear(); + self.root_pipeline = Some(frame_tree.pipeline.clone()); // If we have an old root layer, release all old tiles before replacing it. @@ -614,9 +635,12 @@ impl IOCompositor { old_root_layer.clear_all_tiles(self) } - self.scene.root = Some(self.create_frame_tree_root_layers(frame_tree, None)); + self.scene.root = Some(self.create_root_layer_for_pipeline_and_size(&frame_tree.pipeline, + None)); self.scene.set_root_layer_size(self.window_size.as_f32()); + self.create_pipeline_details_for_frame_tree(&frame_tree); + // Initialize the new constellation channel by sending it the root window size. self.constellation_chan = new_constellation_chan; self.send_window_size(); @@ -625,9 +649,9 @@ impl IOCompositor { self.composite_if_necessary(CompositingReason::NewFrameTree); } - fn create_root_layer_for_pipeline_and_rect(&mut self, + fn create_root_layer_for_pipeline_and_size(&mut self, pipeline: &CompositionPipeline, - frame_rect: Option>) + frame_size: Option>) -> Rc> { let layer_properties = LayerProperties { id: LayerId::null(), @@ -637,6 +661,7 @@ impl IOCompositor { scroll_policy: ScrollPolicy::Scrollable, transform: Matrix4::identity(), perspective: Matrix4::identity(), + subpage_layer_info: None, establishes_3d_context: true, scrolls_overflow_area: false, }; @@ -651,24 +676,20 @@ impl IOCompositor { // 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); + if let Some(ref frame_size) = frame_size { + let frame_size = frame_size.to_untyped(); + root_layer.bounds.borrow_mut().size = Size2D::from_untyped(&frame_size); } return root_layer; } - fn create_frame_tree_root_layers(&mut self, - frame_tree: &SendableFrameTree, - frame_rect: Option>) - -> Rc> { - let root_layer = self.create_root_layer_for_pipeline_and_rect(&frame_tree.pipeline, - frame_rect); + fn create_pipeline_details_for_frame_tree(&mut self, frame_tree: &SendableFrameTree) { + self.pipeline_details(frame_tree.pipeline.id).pipeline = Some(frame_tree.pipeline.clone()); + for kid in &frame_tree.children { - root_layer.add_child(self.create_frame_tree_root_layers(kid, kid.rect)); + self.create_pipeline_details_for_frame_tree(kid); } - return root_layer; } fn find_pipeline_root_layer(&self, pipeline_id: PipelineId) @@ -687,7 +708,14 @@ impl IOCompositor { None => return, }; - root_layer.collect_old_layers(self, pipeline_id, new_layers); + let mut pipelines_removed = Vec::new(); + root_layer.collect_old_layers(self, pipeline_id, new_layers, &mut pipelines_removed); + + for pipeline_removed in pipelines_removed.into_iter() { + self.pending_subpage_layers.remove(&(pipeline_removed.parent_pipeline_id, + pipeline_removed.parent_subpage_id)); + self.pending_subpages.remove(&pipeline_removed.child_pipeline_id); + } } fn remove_pipeline_root_layer(&mut self, pipeline_id: PipelineId) { @@ -702,7 +730,8 @@ impl IOCompositor { self.pipeline_details.remove(&pipeline_id); } - fn update_layer_if_exists(&mut self, pipeline_id: PipelineId, properties: LayerProperties) -> bool { + 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) => { existing_layer.update_layer(properties); @@ -748,19 +777,23 @@ impl IOCompositor { layer_properties.id); } - fn create_or_update_descendant_layer(&mut self, pipeline_id: PipelineId, layer_properties: LayerProperties) { + fn create_or_update_descendant_layer(&mut self, + pipeline_id: PipelineId, + layer_properties: LayerProperties) { debug_assert!(layer_properties.parent_id.is_some()); if !self.update_layer_if_exists(pipeline_id, layer_properties) { self.create_descendant_layer(pipeline_id, layer_properties); } + self.update_subpage_size_if_necessary(&layer_properties); self.scroll_layer_to_fragment_point_if_necessary(pipeline_id, layer_properties.id); } - fn create_descendant_layer(&self, pipeline_id: PipelineId, layer_properties: LayerProperties) { + fn create_descendant_layer(&mut self, + pipeline_id: PipelineId, + layer_properties: LayerProperties) { let parent_id = layer_properties.parent_id.unwrap(); - if let Some(parent_layer) = self.find_layer_with_pipeline_and_layer_id(pipeline_id, parent_id) { let wants_scroll_events = if layer_properties.scrolls_overflow_area { @@ -778,7 +811,69 @@ impl IOCompositor { *new_layer.masks_to_bounds.borrow_mut() = true } - parent_layer.add_child(new_layer); + // If this layer contains a subpage, then create the root layer for that subpage now. + if let Some(ref subpage_layer_info) = layer_properties.subpage_layer_info { + let subpage_layer_properties = LayerProperties { + id: LayerId::null(), + parent_id: None, + rect: Rect::new(Point2D::new(subpage_layer_info.origin.x.to_f32_px(), + subpage_layer_info.origin.y.to_f32_px()), + layer_properties.rect.size), + background_color: layer_properties.background_color, + scroll_policy: ScrollPolicy::Scrollable, + transform: Matrix4::identity(), + perspective: Matrix4::identity(), + subpage_layer_info: layer_properties.subpage_layer_info, + establishes_3d_context: true, + scrolls_overflow_area: true, + }; + + // We need to map from the (pipeline ID, subpage ID) pair to the pipeline ID of + // the subpage itself. The constellation is the source of truth for that + // information, so go ask it. In the meantime, store the information in the list of + // pending subpage layers. + self.pending_subpage_layers.insert((pipeline_id, subpage_layer_info.subpage_id), + PendingSubpageLayerInfo { + subpage_layer_properties: subpage_layer_properties, + container_layer_id: layer_properties.id, + }); + self.constellation_chan.0.send(ConstellationMsg::PrepareForSubpageLayerCreation( + pipeline_id, + subpage_layer_info.subpage_id)).unwrap(); + } + + parent_layer.add_child(new_layer.clone()); + } + } + + fn create_layer_for_subpage(&mut self, + parent_pipeline_id: PipelineId, + parent_subpage_id: SubpageId, + subpage_pipeline_id: Option) { + let subpage_pipeline_id = match subpage_pipeline_id { + Some(subpage_pipeline_id) => subpage_pipeline_id, + None => return, + }; + if let Some(PendingSubpageLayerInfo { + subpage_layer_properties, + container_layer_id + }) = self.pending_subpage_layers.remove(&(parent_pipeline_id, parent_subpage_id)) { + if let Some(container_layer) = + self.find_layer_with_pipeline_and_layer_id(parent_pipeline_id, + container_layer_id) { + let wants_scroll_events = if subpage_layer_properties.scrolls_overflow_area { + WantsScrollEventsFlag::WantsScrollEvents + } else { + WantsScrollEventsFlag::DoesntWantScrollEvents + }; + let subpage_layer = CompositorData::new_layer(subpage_pipeline_id, + subpage_layer_properties, + wants_scroll_events, + container_layer.tile_size); + *subpage_layer.masks_to_bounds.borrow_mut() = true; + container_layer.add_child(subpage_layer); + self.pending_subpages.insert(subpage_pipeline_id); + } } } @@ -795,6 +890,20 @@ impl IOCompositor { })).unwrap() } + /// Sends the size of the given subpage up to the constellation. This will often trigger a + /// reflow of that subpage. + fn update_subpage_size_if_necessary(&self, layer_properties: &LayerProperties) { + let subpage_layer_info = match layer_properties.subpage_layer_info { + Some(ref subpage_layer_info) => *subpage_layer_info, + None => return, + }; + + let ConstellationChan(ref chan) = self.constellation_chan; + chan.send(ConstellationMsg::FrameSize(subpage_layer_info.pipeline_id, + subpage_layer_info.subpage_id, + layer_properties.rect.size)).unwrap(); + } + pub fn move_layer(&self, pipeline_id: PipelineId, layer_id: LayerId, @@ -835,21 +944,6 @@ impl IOCompositor { self.composition_request = CompositionRequest::CompositeOnScrollTimeout(timestamp); } - fn set_layer_rect(&mut self, - pipeline_id: PipelineId, - layer_id: LayerId, - new_rect: &Rect) { - match self.find_layer_with_pipeline_and_layer_id(pipeline_id, layer_id) { - Some(ref layer) => { - *layer.bounds.borrow_mut() = Rect::from_untyped(new_rect) - } - None => panic!("Compositor received SetLayerRect for nonexistent \ - layer: {:?}", pipeline_id), - }; - - self.send_buffer_requests_for_all_layers(); - } - fn assign_painted_buffers(&mut self, pipeline_id: PipelineId, layer_id: LayerId, @@ -862,7 +956,8 @@ impl IOCompositor { // loaded and the frame tree changes. This can result in the compositor thinking it // has already drawn the most recently painted buffer, and missing a frame. if frame_tree_id == self.frame_tree_id { - if let Some(layer) = self.find_layer_with_pipeline_and_layer_id(pipeline_id, layer_id) { + if let Some(layer) = self.find_layer_with_pipeline_and_layer_id(pipeline_id, + layer_id) { let requested_epoch = layer.extra_data.borrow().requested_epoch; if requested_epoch == epoch { self.assign_painted_buffers_to_layer(layer, new_layer_buffer_set, epoch); @@ -1250,7 +1345,7 @@ impl IOCompositor { for (layer, mut layer_requests) in requests { let pipeline_id = layer.pipeline_id(); - let current_epoch = self.pipeline_details.get(&pipeline_id).unwrap().current_epoch; + let current_epoch = self.pipeline_details(pipeline_id).current_epoch; layer.extra_data.borrow_mut().requested_epoch = current_epoch; let vec = match results.entry(pipeline_id) { Occupied(entry) => { @@ -1291,8 +1386,10 @@ impl IOCompositor { if layer.extra_data.borrow().id == LayerId::null() { let layer_rect = Rect::new(-layer.extra_data.borrow().scroll_offset.to_untyped(), layer.bounds.borrow().size.to_untyped()); - let pipeline = self.pipeline(layer.pipeline_id()); - pipeline.script_chan.send(ConstellationControlMsg::Viewport(pipeline.id.clone(), layer_rect)).unwrap(); + if let Some(pipeline) = self.pipeline(layer.pipeline_id()) { + pipeline.script_chan.send(ConstellationControlMsg::Viewport(pipeline.id.clone(), + layer_rect)).unwrap(); + } } for kid in &*layer.children() { @@ -1333,7 +1430,9 @@ impl IOCompositor { for (pipeline_id, requests) in pipeline_requests { let msg = ChromeToPaintMsg::Paint(requests, self.frame_tree_id); - let _ = self.pipeline(pipeline_id).chrome_to_paint_chan.send(msg); + if let Some(pipeline) = self.pipeline(pipeline_id) { + pipeline.chrome_to_paint_chan.send(msg).unwrap(); + } } true @@ -1341,9 +1440,13 @@ impl IOCompositor { /// Check if a layer (or its children) have any outstanding paint /// results to arrive yet. - fn does_layer_have_outstanding_paint_messages(&self, layer: &Rc>) -> bool { + fn does_layer_have_outstanding_paint_messages(&self, layer: &Rc>) + -> bool { let layer_data = layer.extra_data.borrow(); - let current_epoch = self.pipeline_details.get(&layer_data.pipeline_id).unwrap().current_epoch; + let current_epoch = match self.pipeline_details.get(&layer_data.pipeline_id) { + None => return false, + Some(ref details) => details.current_epoch, + }; // Only check layers that have requested the current epoch, as there may be // layers that are not visible in the current viewport, and therefore @@ -1390,6 +1493,11 @@ impl IOCompositor { } } + // Check if there are any pending frames. If so, the image is not stable yet. + if self.pending_subpage_layers.len() > 0 || self.pending_subpages.len() > 0 { + return false + } + // Collect the currently painted epoch of each pipeline that is // complete (i.e. has *all* layers painted to the requested epoch). // This gets sent to the constellation for comparison with the current @@ -1805,3 +1913,16 @@ pub enum CompositingReason { /// The window has been zoomed. Zoom, } + +struct PendingSubpageLayerInfo { + subpage_layer_properties: LayerProperties, + container_layer_id: LayerId, +} + +#[derive(Copy, Clone)] +pub struct RemovedPipelineInfo { + pub parent_pipeline_id: PipelineId, + pub parent_subpage_id: SubpageId, + pub child_pipeline_id: PipelineId, +} + diff --git a/components/compositing/compositor_layer.rs b/components/compositing/compositor_layer.rs index 1902849e97f..261802c9a81 100644 --- a/components/compositing/compositor_layer.rs +++ b/components/compositing/compositor_layer.rs @@ -3,7 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use azure::azure_hl; -use compositor::IOCompositor; +use compositor::{IOCompositor, RemovedPipelineInfo}; use euclid::length::Length; use euclid::point::{Point2D, TypedPoint2D}; use euclid::rect::Rect; @@ -12,7 +12,7 @@ use layers::color::Color; use layers::geometry::LayerPixel; use layers::layers::{Layer, LayerBufferSet}; use msg::compositor_msg::{Epoch, LayerId, LayerProperties, ScrollPolicy}; -use msg::constellation_msg::PipelineId; +use msg::constellation_msg::{PipelineId, SubpageId}; use script_traits::CompositorEvent::{ClickEvent, MouseDownEvent, MouseMoveEvent, MouseUpEvent}; use script_traits::ConstellationControlMsg; use std::rc::Rc; @@ -42,6 +42,9 @@ pub struct CompositorData { /// The scroll offset originating from this scrolling root. This allows scrolling roots /// to track their current scroll position even while their content_offset does not change. pub scroll_offset: TypedPoint2D, + + /// The pipeline ID and subpage ID of this layer, if it represents a subpage. + pub subpage_info: Option<(PipelineId, SubpageId)>, } impl CompositorData { @@ -58,6 +61,9 @@ impl CompositorData { requested_epoch: Epoch(0), painted_epoch: Epoch(0), scroll_offset: Point2D::typed(0., 0.), + subpage_info: layer_properties.subpage_layer_info.map(|subpage_layer_info| { + (subpage_layer_info.pipeline_id, subpage_layer_info.subpage_id) + }), }; Rc::new(Layer::new(Rect::from_untyped(&layer_properties.rect), @@ -97,14 +103,6 @@ pub trait CompositorLayer { pipeline_id: PipelineId) where Window: WindowMethods; - /// Traverses the existing layer hierarchy and removes any layers that - /// currently exist but which are no longer required. - fn collect_old_layers(&self, - compositor: &mut IOCompositor, - pipeline_id: PipelineId, - new_layers: &[LayerProperties]) - 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. @@ -151,6 +149,17 @@ pub trait CompositorLayer { fn pipeline_id(&self) -> PipelineId; } +pub trait RcCompositorLayer { + /// Traverses the existing layer hierarchy and removes any layers that + /// currently exist but which are no longer required. + fn collect_old_layers(&self, + compositor: &mut IOCompositor, + pipeline_id: PipelineId, + new_layers: &[LayerProperties], + pipelines_removed: &mut Vec) + where Window: WindowMethods; +} + #[derive(Copy, PartialEq, Clone, Debug)] pub enum WantsScrollEventsFlag { WantsScrollEvents, @@ -279,43 +288,6 @@ impl CompositorLayer for Layer { } } - fn collect_old_layers(&self, - compositor: &mut IOCompositor, - pipeline_id: PipelineId, - new_layers: &[LayerProperties]) - where Window: WindowMethods { - // Traverse children first so that layers are removed - // bottom up - allowing each layer being removed to properly - // clean up any tiles it owns. - for kid in &*self.children() { - kid.collect_old_layers(compositor, pipeline_id, new_layers); - } - - // Retain child layers that also exist in the new layer list. - self.children().retain(|child| { - let extra_data = child.extra_data.borrow(); - - // Never remove root layers or layers from other pipelines. - if pipeline_id != extra_data.pipeline_id || - extra_data.id == LayerId::null() { - true - } else { - // Keep this layer if it exists in the new layer list. - let keep_layer = new_layers.iter().any(|properties| { - properties.id == extra_data.id - }); - - // When removing a layer, clear any tiles and surfaces - // associated with the layer. - if !keep_layer { - child.clear_all_tiles(compositor); - } - - keep_layer - } - }); - } - /// 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. @@ -403,8 +375,11 @@ impl CompositorLayer for Layer { MouseUpEvent(button, event_point), }; - let pipeline = compositor.pipeline(self.pipeline_id()); - let _ = pipeline.script_chan.send(ConstellationControlMsg::SendEvent(pipeline.id.clone(), message)); + if let Some(pipeline) = compositor.pipeline(self.pipeline_id()) { + pipeline.script_chan + .send(ConstellationControlMsg::SendEvent(pipeline.id.clone(), message)) + .unwrap(); + } } fn send_mouse_move_event(&self, @@ -412,8 +387,11 @@ impl CompositorLayer for Layer { cursor: TypedPoint2D) where Window: WindowMethods { let message = MouseMoveEvent(cursor.to_untyped()); - let pipeline = compositor.pipeline(self.pipeline_id()); - let _ = pipeline.script_chan.send(ConstellationControlMsg::SendEvent(pipeline.id.clone(), message)); + if let Some(pipeline) = compositor.pipeline(self.pipeline_id()) { + pipeline.script_chan + .send(ConstellationControlMsg::SendEvent(pipeline.id.clone(), message)) + .unwrap(); + } } fn scroll_layer_and_all_child_layers(&self, new_offset: TypedPoint2D) @@ -443,3 +421,97 @@ impl CompositorLayer for Layer { self.extra_data.borrow().pipeline_id } } + +impl RcCompositorLayer for Rc> { + fn collect_old_layers(&self, + compositor: &mut IOCompositor, + pipeline_id: PipelineId, + new_layers: &[LayerProperties], + pipelines_removed: &mut Vec) + where Window: WindowMethods { + fn find_root_layer_for_pipeline(layer: &Rc>, pipeline_id: PipelineId) + -> Option>> { + let extra_data = layer.extra_data.borrow(); + if extra_data.pipeline_id == pipeline_id { + return Some((*layer).clone()) + } + + for kid in &*layer.children() { + if let Some(layer) = find_root_layer_for_pipeline(kid, pipeline_id) { + return Some(layer.clone()) + } + } + None + } + + fn collect_old_layers_for_pipeline( + layer: &Layer, + compositor: &mut IOCompositor, + pipeline_id: PipelineId, + new_layers: &[LayerProperties], + pipelines_removed: &mut Vec) + where Window: WindowMethods { + // Traverse children first so that layers are removed + // bottom up - allowing each layer being removed to properly + // clean up any tiles it owns. + for kid in &*layer.children() { + collect_old_layers_for_pipeline(kid, + compositor, + pipeline_id, + new_layers, + pipelines_removed); + } + + // Retain child layers that also exist in the new layer list. + layer.children().retain(|child| { + let extra_data = child.extra_data.borrow(); + if pipeline_id == extra_data.pipeline_id { + // Never remove our own root layer. + if extra_data.id == LayerId::null() { + return true + } + + // Keep this layer if it exists in the new layer list. + return new_layers.iter().any(|properties| properties.id == extra_data.id); + } + + if let Some(ref subpage_info_for_this_layer) = extra_data.subpage_info { + for layer_properties in new_layers.iter() { + // Keep this layer if a reference to it exists. + if let Some(ref subpage_layer_info) = layer_properties.subpage_layer_info { + if subpage_layer_info.pipeline_id == subpage_info_for_this_layer.0 && + subpage_layer_info.subpage_id == + subpage_info_for_this_layer.1 { + return true + } + } + } + + pipelines_removed.push(RemovedPipelineInfo { + parent_pipeline_id: subpage_info_for_this_layer.0, + parent_subpage_id: subpage_info_for_this_layer.1, + child_pipeline_id: extra_data.pipeline_id, + }); + } + + // When removing a layer, clear any tiles and surfaces associated with the layer. + child.clear_all_tiles(compositor); + false + }); + } + + // First, find the root layer with the given pipeline ID. + let root_layer = match find_root_layer_for_pipeline(self, pipeline_id) { + Some(root_layer) => root_layer, + None => return, + }; + + // Then collect all old layers underneath that layer. + collect_old_layers_for_pipeline(&root_layer, + compositor, + pipeline_id, + new_layers, + pipelines_removed); + } +} + diff --git a/components/compositing/compositor_task.rs b/components/compositing/compositor_task.rs index 7026a0a7035..f74813cdf33 100644 --- a/components/compositing/compositor_task.rs +++ b/components/compositing/compositor_task.rs @@ -5,14 +5,14 @@ //! Communication with the compositor task. use compositor; -use euclid::{Point2D, Rect, Size2D}; +use euclid::{Point2D, Size2D}; use headless; use ipc_channel::ipc::{IpcReceiver, IpcSender}; use layers::layers::{BufferRequest, LayerBufferSet}; use layers::platform::surface::{NativeDisplay, NativeSurface}; use msg::compositor_msg::{Epoch, FrameTreeId, LayerId, LayerProperties}; use msg::compositor_msg::{PaintListener, ScriptToCompositorMsg}; -use msg::constellation_msg::{AnimationState, ConstellationChan, PipelineId}; +use msg::constellation_msg::{AnimationState, ConstellationChan, PipelineId, SubpageId}; use msg::constellation_msg::{Key, KeyModifiers, KeyState}; use png; use profile_traits::mem; @@ -64,7 +64,10 @@ pub fn run_script_listener_thread(compositor_proxy: Box { - compositor_proxy.send(Msg::ScrollFragmentPoint(pipeline_id, layer_id, point, _smooth)); + compositor_proxy.send(Msg::ScrollFragmentPoint(pipeline_id, + layer_id, + point, + _smooth)); } ScriptToCompositorMsg::GetClientWindow(send) => { @@ -165,8 +168,6 @@ pub enum Msg { /// Tells the compositor to create or update the layers for a pipeline if necessary /// (i.e. if no layer with that ID exists). InitializeLayersForPipeline(PipelineId, Epoch, Vec), - /// Alerts the compositor that the specified layer's rect has changed. - SetLayerRect(PipelineId, LayerId, Rect), /// Scroll a page in a window ScrollFragmentPoint(PipelineId, LayerId, Point2D, bool), /// Requests that the compositor assign the painted buffers to the given layers. @@ -216,6 +217,12 @@ pub enum Msg { MoveTo(Point2D), /// Resize the window to size ResizeTo(Size2D), + /// A pipeline was shut down. + PipelineExited(PipelineId), + /// The layer for a subpage should be created. The first two IDs are the IDs of the *parent* + /// pipeline and subpage, respectively, while the last ID is the pipeline ID of the subpage + /// itself (or `None` if it has shut down). + CreateLayerForSubpage(PipelineId, SubpageId, Option), } impl Debug for Msg { @@ -225,7 +232,6 @@ impl Debug for Msg { Msg::ShutdownComplete(..) => write!(f, "ShutdownComplete"), Msg::GetNativeDisplay(..) => write!(f, "GetNativeDisplay"), Msg::InitializeLayersForPipeline(..) => write!(f, "InitializeLayersForPipeline"), - Msg::SetLayerRect(..) => write!(f, "SetLayerRect"), Msg::ScrollFragmentPoint(..) => write!(f, "ScrollFragmentPoint"), Msg::AssignPaintedBuffers(..) => write!(f, "AssignPaintedBuffers"), Msg::ChangeRunningAnimationsState(..) => write!(f, "ChangeRunningAnimationsState"), @@ -250,6 +256,8 @@ impl Debug for Msg { Msg::GetClientWindow(..) => write!(f, "GetClientWindow"), Msg::MoveTo(..) => write!(f, "MoveTo"), Msg::ResizeTo(..) => write!(f, "ResizeTo"), + Msg::PipelineExited(..) => write!(f, "PipelineExited"), + Msg::CreateLayerForSubpage(..) => write!(f, "CreateLayerForSubpage"), } } } diff --git a/components/compositing/constellation.rs b/components/compositing/constellation.rs index 656a254d40e..ffd898934cc 100644 --- a/components/compositing/constellation.rs +++ b/components/compositing/constellation.rs @@ -16,14 +16,12 @@ use clipboard::ClipboardContext; use compositor_task::CompositorProxy; use compositor_task::Msg as CompositorMsg; use devtools_traits::{ChromeToDevtoolsControlMsg, DevtoolsControlMsg}; -use euclid::point::Point2D; -use euclid::rect::{Rect, TypedRect}; use euclid::scale_factor::ScaleFactor; -use euclid::size::Size2D; +use euclid::size::{Size2D, TypedSize2D}; use gfx::font_cache_task::FontCacheTask; use ipc_channel::ipc::{self, IpcSender}; use layout_traits::{LayoutControlChan, LayoutTaskFactory}; -use msg::compositor_msg::{Epoch, LayerId}; +use msg::compositor_msg::Epoch; use msg::constellation_msg::AnimationState; use msg::constellation_msg::Msg as ConstellationMsg; use msg::constellation_msg::WebDriverCommandMsg; @@ -136,6 +134,10 @@ pub struct Constellation { /// A list of in-process senders to `WebGLPaintTask`s. webgl_paint_tasks: Vec>, + + /// A list of senders that are waiting to be notified whenever a pipeline or subpage ID comes + /// in. + subpage_id_senders: HashMap<(PipelineId, SubpageId), Vec>>, } /// State needed to construct a constellation. @@ -217,7 +219,7 @@ impl<'a> Iterator for FrameTreeIterator<'a> { pub struct SendableFrameTree { pub pipeline: CompositionPipeline, - pub rect: Option>, + pub size: Option>, pub children: Vec, } @@ -280,6 +282,7 @@ impl Constellation { webdriver: WebDriverData::new(), canvas_paint_tasks: Vec::new(), webgl_paint_tasks: Vec::new(), + subpage_id_senders: HashMap::new(), }; constellation.run(); }); @@ -298,7 +301,7 @@ impl Constellation { /// Helper function for creating a pipeline fn new_pipeline(&mut self, parent_info: Option<(PipelineId, SubpageId)>, - initial_window_rect: Option>, + initial_window_size: Option>, script_channel: Option>, load_data: LoadData) -> PipelineId { @@ -320,7 +323,7 @@ impl Constellation { storage_task: self.storage_task.clone(), time_profiler_chan: self.time_profiler_chan.clone(), mem_profiler_chan: self.mem_profiler_chan.clone(), - window_rect: initial_window_rect, + window_size: initial_window_size, script_chan: script_channel, load_data: load_data, device_pixel_ratio: self.window_size.device_pixel_ratio, @@ -396,11 +399,11 @@ impl Constellation { debug!("constellation got init load URL message"); self.handle_init_load(url); } - // A layout assigned a size and position to a subframe. This needs to be reflected by - // all frame trees in the navigation context containing the subframe. - ConstellationMsg::FrameRect(pipeline_id, subpage_id, rect) => { - debug!("constellation got frame rect message"); - self.handle_frame_rect_msg(pipeline_id, subpage_id, Rect::from_untyped(&rect)); + // The compositor discovered the size of a subframe. This needs to be reflected by all + // frame trees in the navigation context containing the subframe. + ConstellationMsg::FrameSize(pipeline_id, subpage_id, size) => { + debug!("constellation got frame size message"); + self.handle_frame_size_msg(pipeline_id, subpage_id, &Size2D::from_untyped(&size)); } ConstellationMsg::ScriptLoadedURLInIFrame(url, source_pipeline_id, @@ -417,7 +420,9 @@ impl Constellation { old_subpage_id, sandbox); } - ConstellationMsg::SetCursor(cursor) => self.handle_set_cursor_msg(cursor), + ConstellationMsg::SetCursor(cursor) => { + self.handle_set_cursor_msg(cursor) + } ConstellationMsg::ChangeRunningAnimationsState(pipeline_id, animation_state) => { self.handle_change_running_animations_state(pipeline_id, animation_state) } @@ -537,6 +542,12 @@ impl Constellation { debug!("constellation got NodeStatus message"); self.compositor_proxy.send(CompositorMsg::Status(message)); } + ConstellationMsg::PrepareForSubpageLayerCreation(pipeline_id, subpage_id) => { + self.compositor_proxy.send(CompositorMsg::CreateLayerForSubpage( + pipeline_id, + subpage_id, + self.subpage_map.get(&(pipeline_id, subpage_id)).map(|x| *x))) + } } true } @@ -585,10 +596,10 @@ impl Constellation { } debug!("creating replacement pipeline for about:failure"); - let window_rect = self.pipeline(pipeline_id).rect; + let window_size = self.pipeline(pipeline_id).size; let new_pipeline_id = self.new_pipeline(parent_info, - window_rect, + window_size, None, LoadData::new(Url::parse("about:failure").unwrap())); @@ -596,44 +607,39 @@ impl Constellation { } fn handle_init_load(&mut self, url: Url) { - let window_rect = Rect::new(Point2D::zero(), self.window_size.visible_viewport); + let window_size = self.window_size.visible_viewport; let root_pipeline_id = - self.new_pipeline(None, Some(window_rect), None, LoadData::new(url.clone())); + self.new_pipeline(None, Some(window_size), None, LoadData::new(url.clone())); self.handle_load_start_msg(&root_pipeline_id); self.push_pending_frame(root_pipeline_id, None); self.compositor_proxy.send(CompositorMsg::ChangePageUrl(root_pipeline_id, url)); } - fn handle_frame_rect_msg(&mut self, containing_pipeline_id: PipelineId, subpage_id: SubpageId, - rect: TypedRect) { + fn handle_frame_size_msg(&mut self, + containing_pipeline_id: PipelineId, + subpage_id: SubpageId, + size: &TypedSize2D) { // Store the new rect inside the pipeline let (pipeline_id, script_chan) = { // 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_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.size = Some(*size); (pipeline.id, pipeline.script_chan.clone()) }; script_chan.send(ConstellationControlMsg::Resize(pipeline_id, WindowSizeData { - visible_viewport: rect.size, - initial_viewport: rect.size * ScaleFactor::new(1.0), + visible_viewport: *size, + initial_viewport: *size * ScaleFactor::new(1.0), device_pixel_ratio: self.window_size.device_pixel_ratio, })).unwrap(); - - // If this pipeline is in the current frame tree, - // send the updated rect to the script and compositor tasks - if self.pipeline_is_in_current_frame(pipeline_id) { - self.compositor_proxy.send(CompositorMsg::SetLayerRect( - pipeline_id, - LayerId::null(), - rect.to_untyped())); - } } // The script task associated with pipeline_id has loaded a URL in an iframe via script. This @@ -674,14 +680,24 @@ impl Constellation { let old_pipeline_id = old_subpage_id.map(|old_subpage_id| { self.find_subpage(containing_pipeline_id, old_subpage_id).id }); - let window_rect = old_pipeline_id.and_then(|old_pipeline_id| { - self.pipeline(old_pipeline_id).rect + let window_size = old_pipeline_id.and_then(|old_pipeline_id| { + self.pipeline(old_pipeline_id).size }); let new_pipeline_id = self.new_pipeline(Some((containing_pipeline_id, new_subpage_id)), - window_rect, + window_size, script_chan, LoadData::new(url)); + self.subpage_map.insert((containing_pipeline_id, new_subpage_id), new_pipeline_id); + + // If anyone is waiting to know the pipeline ID, send that information now. + if let Some(subpage_id_senders) = self.subpage_id_senders.remove(&(containing_pipeline_id, + new_subpage_id)) { + for subpage_id_sender in subpage_id_senders.into_iter() { + subpage_id_sender.send(new_pipeline_id).unwrap(); + } + } + self.push_pending_frame(new_pipeline_id, old_pipeline_id); } @@ -739,8 +755,8 @@ impl Constellation { // changes would be overridden by changing the subframe associated with source_id. // Create the new pipeline - let window_rect = self.pipeline(source_id).rect; - let new_pipeline_id = self.new_pipeline(None, window_rect, None, load_data); + let window_size = self.pipeline(source_id).size; + let new_pipeline_id = self.new_pipeline(None, window_size, None, load_data); self.push_pending_frame(new_pipeline_id, Some(source_id)); // Send message to ScriptTask that will suspend all timers @@ -954,7 +970,9 @@ impl Constellation { self.focus_parent_pipeline(pipeline_id); } - fn handle_remove_iframe_msg(&mut self, containing_pipeline_id: PipelineId, subpage_id: SubpageId) { + 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 { @@ -1172,7 +1190,8 @@ impl Constellation { for pending_frame in &self.pending_frames { let pipeline = self.pipelines.get(&pending_frame.new_pipeline_id).unwrap(); if pipeline.parent_info.is_none() { - let _ = pipeline.script_chan.send(ConstellationControlMsg::Resize(pipeline.id, new_size)); + let _ = pipeline.script_chan.send(ConstellationControlMsg::Resize(pipeline.id, + new_size)); } } @@ -1180,7 +1199,9 @@ impl Constellation { } /// Handle updating actual viewport / zoom due to @viewport rules - fn handle_viewport_constrained_msg(&mut self, pipeline_id: PipelineId, constraints: ViewportConstraints) { + fn handle_viewport_constrained_msg(&mut self, + pipeline_id: PipelineId, + constraints: ViewportConstraints) { self.compositor_proxy.send(CompositorMsg::ViewportConstrained(pipeline_id, constraints)); } @@ -1196,12 +1217,6 @@ impl Constellation { return false; } - // If there are pending changes to the current frame - // tree, the image is not stable yet. - if self.pending_frames.len() > 0 { - return false; - } - // Step through the current frame tree, checking that the script // task is idle, and that the current epoch of the layout task // matches what the compositor has painted. If all these conditions @@ -1215,12 +1230,11 @@ impl Constellation { let (sender, receiver) = channel(); let msg = ConstellationControlMsg::GetCurrentState(sender, frame.current); pipeline.script_chan.send(msg).unwrap(); - if receiver.recv().unwrap() == ScriptState::DocumentLoading { + let result = receiver.recv().unwrap(); + if result == ScriptState::DocumentLoading { return false; } - // Synchronously query the layout task for this pipeline - // to see if it is idle. let (sender, receiver) = ipc::channel().unwrap(); let msg = LayoutControlMsg::GetWebFontLoadState(sender); pipeline.layout_chan.0.send(msg).unwrap(); @@ -1228,43 +1242,39 @@ impl Constellation { return false; } - // Check the visible rectangle for this pipeline. If the constellation - // hasn't received a rectangle for this pipeline yet, then assume - // that the output image isn't stable yet. - match pipeline.rect { - Some(rect) => { - // If the rectangle for this pipeline is zero sized, it will - // never be painted. In this case, don't query the layout - // task as it won't contribute to the final output image. - if rect.size == Size2D::zero() { - continue; - } + // Check the visible rectangle for this pipeline. If the constellation has received a + // size for the pipeline, then its painting should be up to date. If the constellation + // *hasn't* received a size, it could be that the layer was hidden by script before the + // compositor discovered it, so we just don't check the layer. + if let Some(size) = pipeline.size { + // If the rectangle for this pipeline is zero sized, it will + // never be painted. In this case, don't query the layout + // task as it won't contribute to the final output image. + if size == Size2D::zero() { + continue; + } - // Get the epoch that the compositor has drawn for this pipeline. - let compositor_epoch = pipeline_states.get(&frame.current); - match compositor_epoch { - Some(compositor_epoch) => { - // Synchronously query the layout task to see if the current - // epoch matches what the compositor has drawn. If they match - // (and script is idle) then this pipeline won't change again - // and can be considered stable. - let (sender, receiver) = ipc::channel().unwrap(); - let LayoutControlChan(ref layout_chan) = pipeline.layout_chan; - layout_chan.send(LayoutControlMsg::GetCurrentEpoch(sender)).unwrap(); - let layout_task_epoch = receiver.recv().unwrap(); - if layout_task_epoch != *compositor_epoch { - return false; - } - } - None => { - // The compositor doesn't know about this pipeline yet. - // Assume it hasn't rendered yet. + // Get the epoch that the compositor has drawn for this pipeline. + let compositor_epoch = pipeline_states.get(&frame.current); + match compositor_epoch { + Some(compositor_epoch) => { + // Synchronously query the layout task to see if the current + // epoch matches what the compositor has drawn. If they match + // (and script is idle) then this pipeline won't change again + // and can be considered stable. + let (sender, receiver) = ipc::channel().unwrap(); + let LayoutControlChan(ref layout_chan) = pipeline.layout_chan; + layout_chan.send(LayoutControlMsg::GetCurrentEpoch(sender)).unwrap(); + let layout_task_epoch = receiver.recv().unwrap(); + if layout_task_epoch != *compositor_epoch { return false; } } - } - None => { - return false; + None => { + // The compositor doesn't know about this pipeline yet. + // Assume it hasn't rendered yet. + return false; + } } } } @@ -1354,7 +1364,7 @@ impl Constellation { let mut frame_tree = SendableFrameTree { pipeline: pipeline.to_sendable(), - rect: pipeline.rect, + size: pipeline.size, children: vec!(), }; @@ -1409,7 +1419,8 @@ impl Constellation { // If this is an iframe, then send the event with new url if let Some((containing_pipeline_id, subpage_id, url)) = event_info { let parent_pipeline = self.pipeline(containing_pipeline_id); - parent_pipeline.trigger_mozbrowser_event(subpage_id, MozBrowserEvent::LocationChange(url)); + parent_pipeline.trigger_mozbrowser_event(subpage_id, + MozBrowserEvent::LocationChange(url)); } } } @@ -1451,7 +1462,8 @@ impl Constellation { self.pipelines.get_mut(&pipeline_id).expect("unable to find pipeline - this is a bug") } - fn find_subpage(&mut self, containing_pipeline_id: PipelineId, subpage_id: SubpageId) -> &mut Pipeline { + fn find_subpage(&mut self, containing_pipeline_id: PipelineId, subpage_id: SubpageId) + -> &mut Pipeline { let pipeline_id = *self.subpage_map .get(&(containing_pipeline_id, subpage_id)) .expect("no subpage pipeline_id"); diff --git a/components/compositing/headless.rs b/components/compositing/headless.rs index 050cd32d9e8..92b445a4476 100644 --- a/components/compositing/headless.rs +++ b/components/compositing/headless.rs @@ -100,7 +100,6 @@ impl CompositorEventListener for NullCompositor { // SetFrameTree. Msg::InitializeLayersForPipeline(..) | - Msg::SetLayerRect(..) | Msg::AssignPaintedBuffers(..) | Msg::ScrollFragmentPoint(..) | Msg::Status(..) | @@ -122,6 +121,8 @@ impl CompositorEventListener for NullCompositor { Msg::HeadParsed => {} Msg::ReturnUnusedNativeSurfaces(..) => {} Msg::CollectMemoryReports(..) => {} + Msg::PipelineExited(..) => {} + Msg::CreateLayerForSubpage(..) => {} } true } diff --git a/components/compositing/pipeline.rs b/components/compositing/pipeline.rs index 73cabc1d35e..59870d23ab8 100644 --- a/components/compositing/pipeline.rs +++ b/components/compositing/pipeline.rs @@ -4,9 +4,10 @@ use CompositorProxy; use compositor_task; +use compositor_task::Msg as CompositorMsg; use devtools_traits::{DevtoolsControlMsg, ScriptToDevtoolsControlMsg}; -use euclid::rect::{TypedRect}; use euclid::scale_factor::ScaleFactor; +use euclid::size::TypedSize2D; use gfx::font_cache_task::FontCacheTask; use gfx::paint_task::{ChromeToPaintMsg, LayoutToPaintMsg, PaintTask}; use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; @@ -39,6 +40,8 @@ pub struct Pipeline { pub script_chan: Sender, /// A channel to layout, for performing reflows and shutdown. pub layout_chan: LayoutControlChan, + /// A channel to the compositor. + pub compositor_proxy: Box, pub chrome_to_paint_chan: Sender, pub layout_shutdown_port: Receiver<()>, pub paint_shutdown_port: Receiver<()>, @@ -46,7 +49,7 @@ pub struct Pipeline { pub url: Url, /// The title of the most recently-loaded page. pub title: Option, - pub rect: Option>, + pub size: Option>, /// Whether this pipeline is currently running animations. Pipelines that are running /// animations cause composites to be continually scheduled. pub running_animations: bool, @@ -88,7 +91,7 @@ pub struct InitialPipelineState { /// A channel to the memory profiler thread. pub mem_profiler_chan: profile_mem::ProfilerChan, /// Information about the initial window size. - pub window_rect: Option>, + pub window_size: Option>, /// Information about the device pixel ratio. pub device_pixel_ratio: ScaleFactor, /// A channel to the script thread, if applicable. If this is `Some`, @@ -116,10 +119,10 @@ impl Pipeline { parent_info: state.parent_info, }; - let window_size = state.window_rect.map(|rect| { + let window_size = state.window_size.map(|size| { WindowSizeData { - visible_viewport: rect.size, - initial_viewport: rect.size * ScaleFactor::new(1.0), + visible_viewport: size, + initial_viewport: size * ScaleFactor::new(1.0), device_pixel_ratio: state.device_pixel_ratio, } }); @@ -164,11 +167,12 @@ impl Pipeline { state.parent_info, script_chan.clone(), LayoutControlChan(pipeline_chan), + state.compositor_proxy.clone_compositor_proxy(), chrome_to_paint_chan.clone(), layout_shutdown_port, paint_shutdown_port, state.load_data.url.clone(), - state.window_rect); + state.window_size); let pipeline_content = PipelineContent { id: state.id, @@ -203,24 +207,26 @@ impl Pipeline { parent_info: Option<(PipelineId, SubpageId)>, script_chan: Sender, layout_chan: LayoutControlChan, + compositor_proxy: Box, chrome_to_paint_chan: Sender, layout_shutdown_port: Receiver<()>, paint_shutdown_port: Receiver<()>, url: Url, - rect: Option>) + size: Option>) -> Pipeline { Pipeline { id: id, parent_info: parent_info, script_chan: script_chan, layout_chan: layout_chan, + compositor_proxy: compositor_proxy, chrome_to_paint_chan: chrome_to_paint_chan, layout_shutdown_port: layout_shutdown_port, paint_shutdown_port: paint_shutdown_port, url: url, title: None, children: vec!(), - rect: rect, + size: size, running_animations: false, } } @@ -239,13 +245,17 @@ impl Pipeline { // 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. - if self.script_chan.send(ConstellationControlMsg::ExitPipeline(self.id, exit_type)).is_ok() { + if self.script_chan + .send(ConstellationControlMsg::ExitPipeline(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(); let _ = self.layout_shutdown_port.recv(); } + // The compositor wants to know when pipelines shut down too. + self.compositor_proxy.send(CompositorMsg::PipelineExited(self.id)) } pub fn freeze(&self) { diff --git a/components/gfx/display_list/mod.rs b/components/gfx/display_list/mod.rs index 6c001ab310f..2df3a7bf85c 100644 --- a/components/gfx/display_list/mod.rs +++ b/components/gfx/display_list/mod.rs @@ -22,7 +22,7 @@ use euclid::num::Zero; use euclid::{Matrix2D, Matrix4, Point2D, Rect, SideOffsets2D, Size2D}; use gfx_traits::color; use libc::uintptr_t; -use msg::compositor_msg::{LayerId, LayerKind, ScrollPolicy}; +use msg::compositor_msg::{LayerId, LayerKind, ScrollPolicy, SubpageLayerInfo}; use net_traits::image::base::Image; use paint_context::PaintContext; use paint_task::PaintLayer; @@ -364,6 +364,9 @@ pub struct StackingContext { /// The layer id for this stacking context, if there is one. pub layer_id: Option, + + /// The subpage that this stacking context represents, if there is one. + pub subpage_layer_info: Option, } impl StackingContext { @@ -380,7 +383,8 @@ impl StackingContext { establishes_3d_context: bool, scrolls_overflow_area: bool, scroll_policy: ScrollPolicy, - layer_id: Option) + layer_id: Option, + subpage_layer_info: Option) -> StackingContext { let mut stacking_context = StackingContext { display_list: display_list, @@ -395,6 +399,7 @@ impl StackingContext { scrolls_overflow_area: scrolls_overflow_area, scroll_policy: scroll_policy, layer_id: layer_id, + subpage_layer_info: subpage_layer_info, }; StackingContextLayerCreator::add_layers_to_preserve_drawing_order(&mut stacking_context); stacking_context @@ -416,6 +421,7 @@ impl StackingContext { scrolls_overflow_area: self.scrolls_overflow_area, scroll_policy: self.scroll_policy, layer_id: Some(layer_id), + subpage_layer_info: self.subpage_layer_info, } } @@ -679,11 +685,19 @@ impl StackingContextLayerCreator { fn finish_building_current_layer(&mut self, stacking_context: &mut StackingContext) { if let Some(display_list) = self.display_list_for_next_layer.take() { let next_layer_id = - stacking_context.display_list.layered_children.back().unwrap().id.companion_layer_id(); + stacking_context.display_list + .layered_children + .back() + .unwrap() + .id + .companion_layer_id(); let child_stacking_context = Arc::new(stacking_context.create_layered_child(next_layer_id, display_list)); - stacking_context.display_list.layered_children.push_back(Arc::new( - PaintLayer::new(next_layer_id, color::transparent(), child_stacking_context))); + stacking_context.display_list.layered_children.push_back( + Arc::new(PaintLayer::new(next_layer_id, + color::transparent(), + child_stacking_context, + ScrollPolicy::Scrollable))); self.all_following_children_need_layers = true; } } @@ -694,7 +708,10 @@ impl StackingContextLayerCreator { if let Some(layer_id) = stacking_context.layer_id { self.finish_building_current_layer(parent_stacking_context); parent_stacking_context.display_list.layered_children.push_back( - Arc::new(PaintLayer::new(layer_id, color::transparent(), stacking_context))); + Arc::new(PaintLayer::new(layer_id, + color::transparent(), + stacking_context, + ScrollPolicy::Scrollable))); // We have started processing layered stacking contexts, so any stacking context that // we process from now on needs its own layer to ensure proper rendering order. diff --git a/components/gfx/paint_task.rs b/components/gfx/paint_task.rs index 3c139fafaaa..683d96d2e71 100644 --- a/components/gfx/paint_task.rs +++ b/components/gfx/paint_task.rs @@ -17,8 +17,8 @@ use font_context::FontContext; use ipc_channel::ipc::IpcSender; use layers::layers::{BufferRequest, LayerBuffer, LayerBufferSet}; use layers::platform::surface::{NativeDisplay, NativeSurface}; -use msg::compositor_msg::{Epoch, FrameTreeId, LayerId, LayerKind}; -use msg::compositor_msg::{LayerProperties, PaintListener}; +use msg::compositor_msg::{Epoch, FrameTreeId, LayerId, LayerKind, LayerProperties, PaintListener}; +use msg::compositor_msg::{ScrollPolicy}; use msg::constellation_msg::Msg as ConstellationMsg; use msg::constellation_msg::PipelineExitType; use msg::constellation_msg::{ConstellationChan, Failure, PipelineId}; @@ -48,15 +48,22 @@ pub struct PaintLayer { pub background_color: Color, /// The stacking context that represents the content of this layer. pub stacking_context: Arc, + /// The scrolling policy of this layer. + pub scroll_policy: ScrollPolicy, } impl PaintLayer { /// Creates a new `PaintLayer`. - pub fn new(id: LayerId, background_color: Color, stacking_context: Arc) -> PaintLayer { + pub fn new(id: LayerId, + background_color: Color, + stacking_context: Arc, + scroll_policy: ScrollPolicy) + -> PaintLayer { PaintLayer { id: id, background_color: background_color, stacking_context: stacking_context, + scroll_policy: scroll_policy, } } @@ -355,9 +362,9 @@ impl PaintTask where C: PaintListener + Send + 'static { let transform = transform.mul(&paint_layer.stacking_context.transform); let perspective = perspective.mul(&paint_layer.stacking_context.perspective); - let overflow_size = - Size2D::new(paint_layer.stacking_context.overflow.size.width.to_nearest_px() as f32, - paint_layer.stacking_context.overflow.size.height.to_nearest_px() as f32); + let overflow_size = Size2D::new( + paint_layer.stacking_context.overflow.size.width.to_nearest_px() as f32, + paint_layer.stacking_context.overflow.size.height.to_nearest_px() as f32); // Layers start at the top left of their overflow rect, as far as the info // we give to the compositor is concerned. @@ -379,6 +386,7 @@ impl PaintTask where C: PaintListener + Send + 'static { perspective: perspective, establishes_3d_context: paint_layer.stacking_context.establishes_3d_context, scrolls_overflow_area: paint_layer.stacking_context.scrolls_overflow_area, + subpage_layer_info: paint_layer.stacking_context.subpage_layer_info, }); // When there is a new layer, the transforms and origin are handled by the compositor, diff --git a/components/layout/block.rs b/components/layout/block.rs index 890353b7a10..970d6a1c315 100644 --- a/components/layout/block.rs +++ b/components/layout/block.rs @@ -2126,10 +2126,11 @@ impl Flow for BlockFlow { } fn compute_overflow(&self) -> Rect { - self.fragment.compute_overflow(&self.base.early_absolute_position_info - .relative_containing_block_size, - self.base.early_absolute_position_info - .relative_containing_block_mode) + let flow_size = self.base.position.size.to_physical(self.base.writing_mode); + self.fragment.compute_overflow(&flow_size, + &self.base + .early_absolute_position_info + .relative_containing_block_size) } fn iterate_through_fragment_border_boxes(&self, diff --git a/components/layout/construct.rs b/components/layout/construct.rs index b7eb8a4ef84..2421a797b43 100644 --- a/components/layout/construct.rs +++ b/components/layout/construct.rs @@ -285,15 +285,6 @@ impl<'a> FlowConstructor<'a> { fn set_flow_construction_result(&self, node: &ThreadSafeLayoutNode, result: ConstructionResult) { - if let ConstructionResult::None = result { - let mut layout_data_ref = node.mutate_layout_data(); - let layout_data = layout_data_ref.as_mut().expect("no layout data"); - layout_data.remove_compositor_layers(self.layout_context - .shared - .constellation_chan - .clone()); - } - node.set_flow_construction_result(result); } diff --git a/components/layout/data.rs b/components/layout/data.rs index e7a21be38a9..5534773834a 100644 --- a/components/layout/data.rs +++ b/components/layout/data.rs @@ -2,9 +2,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use construct::{ConstructionItem, ConstructionResult}; +use construct::ConstructionResult; use incremental::RestyleDamage; -use msg::constellation_msg::ConstellationChan; use parallel::DomParallelInfo; use script::dom::node::SharedLayoutData; use std::sync::Arc; @@ -64,30 +63,6 @@ pub struct LayoutDataWrapper { pub data: Box, } -impl LayoutDataWrapper { - pub fn remove_compositor_layers(&self, constellation_chan: ConstellationChan) { - match self.data.flow_construction_result { - ConstructionResult::None => {} - ConstructionResult::Flow(ref flow_ref, _) => { - flow_ref.remove_compositor_layers(constellation_chan); - } - ConstructionResult::ConstructionItem(ref construction_item) => { - match *construction_item { - ConstructionItem::InlineFragments(ref inline_fragments) => { - for fragment in &inline_fragments.fragments.fragments { - fragment.remove_compositor_layers(constellation_chan.clone()); - } - } - ConstructionItem::Whitespace(..) => {} - ConstructionItem::TableColumnFragment(ref fragment) => { - fragment.remove_compositor_layers(constellation_chan.clone()); - } - } - } - } - } -} - #[allow(dead_code, unsafe_code)] fn static_assertion(x: Option) { unsafe { diff --git a/components/layout/display_list_builder.rs b/components/layout/display_list_builder.rs index 4bc788c0ce7..9d4e7a5f979 100644 --- a/components/layout/display_list_builder.rs +++ b/components/layout/display_list_builder.rs @@ -14,13 +14,12 @@ use azure::azure_hl::Color; use block::BlockFlow; use canvas_traits::{CanvasMsg, FromLayoutMsg}; use context::LayoutContext; -use euclid::Matrix4; -use euclid::{Point2D, Point3D, Rect, SideOffsets2D, Size2D}; +use euclid::{Matrix4, Point2D, Point3D, Rect, SideOffsets2D, Size2D}; use flex::FlexFlow; use flow::{self, BaseFlow, Flow, IS_ABSOLUTELY_POSITIONED}; use flow_ref; -use fragment::{CoordinateSystem, Fragment, HAS_LAYER, IframeFragmentInfo, ImageFragmentInfo}; -use fragment::{ScannedTextFragmentInfo, SpecificFragmentInfo}; +use fragment::{CoordinateSystem, Fragment, HAS_LAYER, ImageFragmentInfo, ScannedTextFragmentInfo}; +use fragment::{SpecificFragmentInfo}; use gfx::display_list::{BLUR_INFLATION_FACTOR, BaseDisplayItem, BorderDisplayItem}; use gfx::display_list::{BorderRadii, BoxShadowClipMode, BoxShadowDisplayItem, ClippingRegion}; use gfx::display_list::{DisplayItem, DisplayItemMetadata, DisplayList}; @@ -35,9 +34,7 @@ use inline::{FIRST_FRAGMENT_OF_ELEMENT, InlineFlow, LAST_FRAGMENT_OF_ELEMENT}; use ipc_channel::ipc::{self, IpcSharedMemory}; use list_item::ListItemFlow; use model::{self, MaybeAuto, ToGfxMatrix}; -use msg::compositor_msg::{LayerId, ScrollPolicy}; -use msg::constellation_msg::ConstellationChan; -use msg::constellation_msg::Msg as ConstellationMsg; +use msg::compositor_msg::{LayerId, ScrollPolicy, SubpageLayerInfo}; use net_traits::image::base::{Image, PixelFormat}; use net_traits::image_cache_task::UsePlaceholder; use std::default::Default; @@ -70,7 +67,7 @@ const INSERTION_POINT_LOGICAL_WIDTH: Au = Au(1 * AU_PER_PX); /// Whether a stacking context needs a layer or not. pub enum StackingContextLayerNecessity { Always(LayerId, ScrollPolicy), - IfCanvas(LayerId), + IfCanvasOrIframe(LayerId), } /// The results of display list building for a single flow. @@ -206,13 +203,6 @@ pub trait FragmentDisplayListBuilding { clip: &ClippingRegion, stacking_relative_display_port: &Rect); - /// Sends the size and position of this iframe fragment to the constellation. This is out of - /// line to guide inlining. - fn finalize_position_and_size_of_iframe(&self, - iframe_fragment: &IframeFragmentInfo, - offset: Point2D, - layout_context: &LayoutContext); - /// Returns the appropriate clipping region for descendants of this fragment. fn clipping_region_for_children(&self, current_clip: &ClippingRegion, @@ -270,7 +260,6 @@ pub trait FragmentDisplayListBuilding { needs_layer: StackingContextLayerNecessity, mode: StackingContextCreationMode) -> Arc; - } fn handle_overlapping_radii(size: &Size2D, radii: &BorderRadii) -> BorderRadii { @@ -1060,28 +1049,6 @@ impl FragmentDisplayListBuilding for Fragment { &stacking_relative_border_box, &clip) } - - // If this is an iframe, then send its position and size up to the constellation. - // - // FIXME(pcwalton): Doing this during display list construction seems potentially - // problematic if iframes are outside the area we're computing the display list for, since - // they won't be able to reflow at all until the user scrolls to them. Perhaps we should - // separate this into two parts: first we should send the size only to the constellation - // once that's computed during assign-block-sizes, and second we should should send the - // origin to the constellation here during display list construction. This should work - // because layout for the iframe only needs to know size, and origin is only relevant if - // the iframe is actually going to be displayed. - if let SpecificFragmentInfo::Iframe(ref iframe_fragment) = self.specific { - let stacking_relative_border_box_in_parent_coordinate_system = - self.stacking_relative_border_box(stacking_relative_flow_origin, - relative_containing_block_size, - relative_containing_block_mode, - CoordinateSystem::Parent); - self.finalize_position_and_size_of_iframe( - &**iframe_fragment, - stacking_relative_border_box_in_parent_coordinate_system.origin, - layout_context) - } } fn build_fragment_type_specific_display_items(&mut self, @@ -1317,15 +1284,18 @@ impl FragmentDisplayListBuilding for Fragment { filters.push(Filter::Opacity(effects.opacity)) } - // Ensure every canvas has a layer + // Ensure every canvas or iframe has a layer. let (scroll_policy, layer_id) = match needs_layer { - StackingContextLayerNecessity::Always(layer_id, scroll_policy) => - (scroll_policy, Some(layer_id)), - StackingContextLayerNecessity::IfCanvas(layer_id) => { - if let SpecificFragmentInfo::Canvas(_) = self.specific { - (ScrollPolicy::Scrollable, Some(layer_id)) - } else { - (ScrollPolicy::Scrollable, None) + StackingContextLayerNecessity::Always(layer_id, scroll_policy) => { + (scroll_policy, Some(layer_id)) + } + StackingContextLayerNecessity::IfCanvasOrIframe(layer_id) => { + // FIXME(pcwalton): So so bogus :( + match self.specific { + SpecificFragmentInfo::Canvas(_) | SpecificFragmentInfo::Iframe(_) => { + (ScrollPolicy::Scrollable, Some(layer_id)) + } + _ => (ScrollPolicy::Scrollable, None), } } }; @@ -1341,6 +1311,18 @@ impl FragmentDisplayListBuilding for Fragment { } } + let subpage_layer_info = match self.specific { + SpecificFragmentInfo::Iframe(ref iframe_fragment_info) => { + let border_padding = self.border_padding.to_physical(self.style().writing_mode); + Some(SubpageLayerInfo { + pipeline_id: iframe_fragment_info.pipeline_id, + subpage_id: iframe_fragment_info.subpage_id, + origin: Point2D::new(border_padding.left, border_padding.top), + }) + } + _ => None, + }; + let scrolls_overflow_area = mode == StackingContextCreationMode::OuterScrollWrapper; let transform_style = self.style().get_used_transform_style(); let establishes_3d_context = scrolls_overflow_area || @@ -1357,28 +1339,8 @@ impl FragmentDisplayListBuilding for Fragment { establishes_3d_context, scrolls_overflow_area, scroll_policy, - layer_id)) - } - - #[inline(never)] - fn finalize_position_and_size_of_iframe(&self, - iframe_fragment: &IframeFragmentInfo, - offset: Point2D, - layout_context: &LayoutContext) { - let border_padding = (self.border_padding).to_physical(self.style.writing_mode); - let content_size = self.content_box().size.to_physical(self.style.writing_mode); - let iframe_rect = Rect::new(Point2D::new((offset.x + border_padding.left).to_f32_px(), - (offset.y + border_padding.top).to_f32_px()), - Size2D::new(content_size.width.to_f32_px(), - content_size.height.to_f32_px())); - - debug!("finalizing position and size of iframe for {:?},{:?}", - iframe_fragment.pipeline_id, - iframe_fragment.subpage_id); - let ConstellationChan(ref chan) = layout_context.shared.constellation_chan; - chan.send(ConstellationMsg::FrameRect(iframe_fragment.pipeline_id, - iframe_fragment.subpage_id, - iframe_rect)).unwrap(); + layer_id, + subpage_layer_info)) } fn clipping_region_for_children(&self, @@ -1643,7 +1605,7 @@ impl BlockFlowDisplayListBuilding for BlockFlow { &self.base, display_list, layout_context, - StackingContextLayerNecessity::IfCanvas(self.layer_id()), + StackingContextLayerNecessity::IfCanvasOrIframe(self.layer_id()), StackingContextCreationMode::Normal)) } else { match self.fragment.style.get_box().position { @@ -1720,7 +1682,7 @@ impl BlockFlowDisplayListBuilding for BlockFlow { &self.base, display_list, layout_context, - StackingContextLayerNecessity::IfCanvas(self.layer_id()), + StackingContextLayerNecessity::IfCanvasOrIframe(self.layer_id()), StackingContextCreationMode::Normal)); } return @@ -1785,7 +1747,7 @@ impl BlockFlowDisplayListBuilding for BlockFlow { &self.base, display_list, layout_context, - StackingContextLayerNecessity::IfCanvas(self.layer_id()), + StackingContextLayerNecessity::IfCanvasOrIframe(self.layer_id()), StackingContextCreationMode::Normal)) } else { DisplayListBuildingResult::Normal(display_list) @@ -1871,13 +1833,17 @@ impl InlineFlowDisplayListBuilding for InlineFlow { // FIXME(Savago): fix Fragment::establishes_stacking_context() for absolute positioned item // and remove the check for filter presence. Further details on #5812. - has_stacking_context = has_stacking_context && { - if let SpecificFragmentInfo::Canvas(_) = self.fragments.fragments[0].specific { - true - } else { - !self.fragments.fragments[0].style().get_effects().filter.is_empty() + // + // FIXME(#7424, pcwalton): This is terribly bogus! What is even going on here? + if has_stacking_context { + match self.fragments.fragments[0].specific { + SpecificFragmentInfo::Canvas(_) | SpecificFragmentInfo::Iframe(_) => {} + _ => { + has_stacking_context = + !self.fragments.fragments[0].style().get_effects().filter.is_empty() + } } - }; + } self.base.display_list_building_result = if has_stacking_context { DisplayListBuildingResult::StackingContext( @@ -1885,7 +1851,8 @@ impl InlineFlowDisplayListBuilding for InlineFlow { &self.base, display_list, layout_context, - StackingContextLayerNecessity::IfCanvas(self.layer_id()), + StackingContextLayerNecessity::Always(self.layer_id(), + ScrollPolicy::Scrollable), StackingContextCreationMode::Normal)) } else { DisplayListBuildingResult::Normal(display_list) diff --git a/components/layout/fragment.rs b/components/layout/fragment.rs index c6cea07b77b..00f264fb83c 100644 --- a/components/layout/fragment.rs +++ b/components/layout/fragment.rs @@ -22,7 +22,7 @@ use inline::{InlineMetrics, LAST_FRAGMENT_OF_ELEMENT}; use ipc_channel::ipc::IpcSender; use layout_debug; use model::{self, IntrinsicISizes, IntrinsicISizesContribution, MaybeAuto, specified}; -use msg::constellation_msg::{ConstellationChan, Msg, PipelineId, SubpageId}; +use msg::constellation_msg::{PipelineId, SubpageId}; use net_traits::image::base::Image; use net_traits::image_cache_task::UsePlaceholder; use rustc_serialize::{Encodable, Encoder}; @@ -2102,6 +2102,16 @@ impl Fragment { stacking_relative_border_box.size.height - border_padding.vertical())) } + /// Returns true if this fragment unconditionally layerizes. + pub fn needs_layer(&self) -> bool { + // Canvas and iframes always layerize, as an special case + // FIXME(pcwalton): Don't unconditionally form stacking contexts for each canvas. + match self.specific { + SpecificFragmentInfo::Canvas(_) | SpecificFragmentInfo::Iframe(_) => true, + _ => false, + } + } + /// Returns true if this fragment establishes a new stacking context and false otherwise. pub fn establishes_stacking_context(&self) -> bool { if self.flags.contains(HAS_LAYER) { @@ -2126,9 +2136,7 @@ impl Fragment { transform_style::T::auto => {} } - // Canvas always layerizes, as an special case - // FIXME(pcwalton): Don't unconditionally form stacking contexts for each canvas. - if let SpecificFragmentInfo::Canvas(_) = self.specific { + if self.needs_layer() { return true } @@ -2161,12 +2169,10 @@ impl Fragment { /// Computes the overflow rect of this fragment relative to the start of the flow. pub fn compute_overflow(&self, - relative_containing_block_size: &LogicalSize, - relative_containing_block_mode: WritingMode) + flow_size: &Size2D, + relative_containing_block_size: &LogicalSize) -> Rect { - let container_size = - relative_containing_block_size.to_physical(relative_containing_block_mode); - let mut border_box = self.border_box.to_physical(self.style.writing_mode, container_size); + let mut border_box = self.border_box.to_physical(self.style.writing_mode, *flow_size); // Relative position can cause us to draw outside our border box. // @@ -2209,23 +2215,6 @@ impl Fragment { overflow } - /// Remove any compositor layers associated with this fragment - it is being - /// removed from the tree or had its display property set to none. - /// TODO(gw): This just hides the compositor layer for now. In the future - /// it probably makes sense to provide a hint to the compositor whether - /// the layers should be destroyed to free memory. - pub fn remove_compositor_layers(&self, constellation_chan: ConstellationChan) { - match self.specific { - SpecificFragmentInfo::Iframe(ref iframe_info) => { - let ConstellationChan(ref chan) = constellation_chan; - chan.send(Msg::FrameRect(iframe_info.pipeline_id, - iframe_info.subpage_id, - Rect::zero())).unwrap(); - } - _ => {} - } - } - pub fn requires_line_break_afterward_if_wrapping_on_newlines(&self) -> bool { match self.specific { SpecificFragmentInfo::ScannedText(ref scanned_text) => { diff --git a/components/layout/inline.rs b/components/layout/inline.rs index 5692ffa93df..e2b028a6880 100644 --- a/components/layout/inline.rs +++ b/components/layout/inline.rs @@ -9,7 +9,7 @@ use context::LayoutContext; use display_list_builder::{FragmentDisplayListBuilding, InlineFlowDisplayListBuilding}; use euclid::{Point2D, Rect, Size2D}; use floats::{FloatKind, Floats, PlacementInfo}; -use flow::{EarlyAbsolutePositionInfo, MutableFlowUtils, OpaqueFlow}; +use flow::{EarlyAbsolutePositionInfo, LAYERS_NEEDED_FOR_DESCENDANTS, MutableFlowUtils, OpaqueFlow}; use flow::{self, BaseFlow, Flow, FlowClass, ForceNonfloatedFlag, IS_ABSOLUTELY_POSITIONED}; use flow_ref; use fragment::{CoordinateSystem, Fragment, FragmentBorderBoxIterator, SpecificFragmentInfo}; @@ -1465,6 +1465,7 @@ impl Flow for InlineFlow { // Now, go through each line and lay out the fragments inside. let mut line_distance_from_flow_block_start = Au(0); + let mut layers_needed_for_descendants = false; let line_count = self.lines.len(); for line_index in 0..line_count { let line = &mut self.lines[line_index]; @@ -1493,6 +1494,10 @@ impl Flow for InlineFlow { for fragment_index in line.range.each_index() { let fragment = &mut self.fragments.fragments[fragment_index.to_usize()]; + if fragment.needs_layer() && !fragment.is_positioned() { + layers_needed_for_descendants = true + } + let InlineMetrics { mut block_size_above_baseline, mut depth_below_baseline, @@ -1589,6 +1594,10 @@ impl Flow for InlineFlow { kid.assign_block_size_for_inorder_child_if_necessary(layout_context, thread_id); } + // Mark ourselves for layerization if that will be necessary to paint in the proper + // order (CSS 2.1, Appendix E). + self.base.flags.set(LAYERS_NEEDED_FOR_DESCENDANTS, layers_needed_for_descendants); + if self.contains_positioned_fragments() { // Assign block-sizes for all flows in this absolute flow tree. // This is preorder because the block-size of an absolute flow may depend on @@ -1773,10 +1782,12 @@ impl Flow for InlineFlow { fn compute_overflow(&self) -> Rect { let mut overflow = ZERO_RECT; + let flow_size = self.base.position.size.to_physical(self.base.writing_mode); + let relative_containing_block_size = + &self.base.early_absolute_position_info.relative_containing_block_size; for fragment in &self.fragments.fragments { - overflow = overflow.union(&fragment.compute_overflow( - &self.base.early_absolute_position_info.relative_containing_block_size, - self.base.early_absolute_position_info.relative_containing_block_mode)); + overflow = overflow.union(&fragment.compute_overflow(&flow_size, + &relative_containing_block_size)) } overflow } diff --git a/components/layout/layout_task.rs b/components/layout/layout_task.rs index c5f9c371035..3158753d5c1 100644 --- a/components/layout/layout_task.rs +++ b/components/layout/layout_task.rs @@ -637,7 +637,7 @@ impl LayoutTask { unsafe { self.handle_reap_layout_data(dead_layout_data) } - }, + } Msg::CollectReports(reports_chan) => { self.collect_reports(reports_chan, possibly_locked_rw_data); }, @@ -724,8 +724,7 @@ impl LayoutTask { info.layout_shutdown_chan); } - /// Enters a quiescent state in which no new messages except for - /// `layout_interface::Msg::ReapLayoutData` will be processed until an `ExitNow` is + /// Enters a quiescent state in which no new messages will be processed until an `ExitNow` is /// received. A pong is immediately sent on the given response channel. fn prepare_to_exit<'a>(&'a self, response_chan: Sender<()>, @@ -1099,6 +1098,7 @@ impl LayoutTask { flow::mut_base(flow_ref::deref_mut(layout_root)) .display_list_building_result .add_to(&mut *display_list); + let origin = Rect::new(Point2D::new(Au(0), Au(0)), root_size); let layer_id = layout_root.layer_id(); let stacking_context = Arc::new(StackingContext::new(display_list, @@ -1112,11 +1112,8 @@ impl LayoutTask { true, false, ScrollPolicy::Scrollable, - Some(layer_id))); - let paint_layer = PaintLayer::new(layer_id, - root_background_color, - stacking_context.clone()); - + Some(layer_id), + None)); if opts::get().dump_display_list { stacking_context.print("DisplayList".to_owned()); } @@ -1124,7 +1121,12 @@ impl LayoutTask { println!("{}", serde_json::to_string_pretty(&stacking_context).unwrap()); } - rw_data.stacking_context = Some(stacking_context); + rw_data.stacking_context = Some(stacking_context.clone()); + + let paint_layer = PaintLayer::new(layout_root.layer_id(), + root_background_color, + stacking_context, + ScrollPolicy::Scrollable); debug!("Layout done!"); @@ -1489,8 +1491,7 @@ impl LayoutTask { /// Handles a message to destroy layout data. Layout data must be destroyed on *this* task /// because the struct type is transmuted to a different type on the script side. unsafe fn handle_reap_layout_data(&self, layout_data: LayoutData) { - let layout_data_wrapper: LayoutDataWrapper = transmute(layout_data); - layout_data_wrapper.remove_compositor_layers(self.constellation_chan.clone()); + let _: LayoutDataWrapper = transmute(layout_data); } /// Returns profiling information which is passed to the time profiler. diff --git a/components/msg/compositor_msg.rs b/components/msg/compositor_msg.rs index ed66bbebff1..7290a4b9e09 100644 --- a/components/msg/compositor_msg.rs +++ b/components/msg/compositor_msg.rs @@ -3,12 +3,13 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use azure::azure_hl::Color; -use constellation_msg::{Key, KeyModifiers, KeyState, PipelineId}; +use constellation_msg::{Key, KeyModifiers, KeyState, PipelineId, SubpageId}; use euclid::{Matrix4, Point2D, Rect, Size2D}; use ipc_channel::ipc::IpcSender; use layers::layers::{BufferRequest, LayerBufferSet}; use layers::platform::surface::NativeDisplay; use std::fmt::{self, Debug, Formatter}; +use util::geometry::Au; /// A newtype struct for denoting the age of messages; prevents race conditions. #[derive(PartialEq, Eq, Debug, Copy, Clone, PartialOrd, Ord, Deserialize, Serialize)] @@ -124,6 +125,9 @@ pub struct LayerProperties { pub transform: Matrix4, /// The perspective transform for this layer pub perspective: Matrix4, + /// The subpage that this layer represents. If this is `Some`, this layer represents an + /// iframe. + pub subpage_layer_info: Option, /// Whether this layer establishes a new 3d rendering context. pub establishes_3d_context: bool, /// Whether this layer scrolls its overflow area. @@ -166,3 +170,15 @@ pub enum ScriptToCompositorMsg { ResizeTo(Size2D), Exit, } + +/// Subpage (i.e. iframe)-specific information about each layer. +#[derive(Clone, Copy, Deserialize, Serialize, HeapSizeOf)] +pub struct SubpageLayerInfo { + /// The ID of the pipeline. + pub pipeline_id: PipelineId, + /// The ID of the subpage. + pub subpage_id: SubpageId, + /// The offset of the subpage within this layer (to account for borders). + pub origin: Point2D, +} + diff --git a/components/msg/constellation_msg.rs b/components/msg/constellation_msg.rs index ab244707c28..49d673b1097 100644 --- a/components/msg/constellation_msg.rs +++ b/components/msg/constellation_msg.rs @@ -7,7 +7,6 @@ use canvas_traits::CanvasMsg; use compositor_msg::Epoch; -use euclid::rect::Rect; use euclid::scale_factor::ScaleFactor; use euclid::size::{Size2D, TypedSize2D}; use hyper::header::Headers; @@ -221,7 +220,7 @@ pub enum Msg { LoadComplete(PipelineId), /// Dispatched after the DOM load event has fired on a document DOMLoad(PipelineId), - FrameRect(PipelineId, SubpageId, Rect), + FrameSize(PipelineId, SubpageId, Size2D), LoadUrl(PipelineId, LoadData), ScriptLoadedURLInIFrame(Url, PipelineId, SubpageId, Option, IFrameSandboxState), Navigate(Option<(PipelineId, SubpageId)>, NavigationDirection), @@ -273,6 +272,9 @@ pub enum Msg { IpcSender, usize), String>>), /// Status message to be displayed in the chrome, eg. a link URL on mouseover. NodeStatus(Option), + /// Requests that the pipeline ID of the subpage identified by a (pipeline ID, subpage ID) + /// pair be sent to the compositor via a `CreateLayerForSubpage` message. + PrepareForSubpageLayerCreation(PipelineId, SubpageId), } #[derive(Clone, Eq, PartialEq, Deserialize, Serialize, Debug)] diff --git a/tests/ref/basic.list b/tests/ref/basic.list index 7d3ff35c43a..d62ba854302 100644 --- a/tests/ref/basic.list +++ b/tests/ref/basic.list @@ -149,6 +149,7 @@ prefs:"layout.flex.enabled" == flex_row_direction.html flex_row_direction_ref.ht == iframe/size_attributes.html iframe/size_attributes_ref.html prefs:"layout.writing-mode.enabled" == iframe/size_attributes_vertical_writing_mode.html iframe/size_attributes_vertical_writing_mode_ref.html == iframe/stacking_context.html iframe/stacking_context_ref.html +== iframe/stacking_context_position_a.html iframe/stacking_context_position_ref.html != image_rendering_auto_a.html image_rendering_pixelated_a.html == image_rendering_pixelated_a.html image_rendering_pixelated_ref.html diff --git a/tests/ref/iframe/stacking_context_position_a.html b/tests/ref/iframe/stacking_context_position_a.html new file mode 100644 index 00000000000..be4b870407f --- /dev/null +++ b/tests/ref/iframe/stacking_context_position_a.html @@ -0,0 +1,5 @@ + +
+ +
+ diff --git a/tests/ref/iframe/stacking_context_position_ref.html b/tests/ref/iframe/stacking_context_position_ref.html new file mode 100644 index 00000000000..85616ddea22 --- /dev/null +++ b/tests/ref/iframe/stacking_context_position_ref.html @@ -0,0 +1,5 @@ + +
+ +
+