From fd3a99ead3093c824b823863daee02970bb8f79d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Sat, 13 Aug 2016 02:36:17 -0700 Subject: [PATCH] layout: Make the stacking context take into account the children transform when calculating overflow areas. This only works for simple translations and similar, but I want Patrick to validate my approach. --- components/gfx/display_list/mod.rs | 101 ++++++++++++++++------ components/gfx/paint_thread.rs | 2 +- components/layout/display_list_builder.rs | 8 +- components/layout/sequential.rs | 4 +- components/layout/webrender_helpers.rs | 2 +- 5 files changed, 85 insertions(+), 32 deletions(-) diff --git a/components/gfx/display_list/mod.rs b/components/gfx/display_list/mod.rs index 17f9be5b4852..d9befc61f00f 100644 --- a/components/gfx/display_list/mod.rs +++ b/components/gfx/display_list/mod.rs @@ -360,7 +360,7 @@ impl DisplayList { return Some(stacking_context); } - for kid in stacking_context.children.iter() { + for kid in stacking_context.children() { let result = find_stacking_context_in_stacking_context(kid, stacking_context_id); if result.is_some() { return result; @@ -449,8 +449,9 @@ impl DisplayList { let old_transform = paint_context.draw_target.get_transform(); let pixels_per_px = paint_context.screen_pixels_per_px(); let (transform, subpixel_offset) = match stacking_context.layer_info { - // If this stacking context starts a layer, the offset and transformation are handled - // by layer position within the compositor. + // If this stacking context starts a layer, the offset and + // transformation are handled by layer position within the + // compositor. Some(..) => (*transform, *subpixel_offset), None => { let origin = stacking_context.bounds.origin + *subpixel_offset; @@ -606,7 +607,7 @@ pub struct StackingContext { pub layer_info: Option, /// Children of this StackingContext. - pub children: Vec>, + children: Vec>, } impl StackingContext { @@ -642,6 +643,73 @@ impl StackingContext { } } + pub fn set_children(&mut self, children: Vec>) { + debug_assert!(self.children.is_empty()); + // We need to take into account the possible transformations of the + // child stacking contexts. + for child in &children { + self.update_overflow_for_new_child(&child); + } + + self.children = children; + } + + pub fn add_child(&mut self, child: Box) { + self.update_overflow_for_new_child(&child); + self.children.push(child); + } + + pub fn add_children(&mut self, children: Vec>) { + if self.children.is_empty() { + return self.set_children(children); + } + + for child in children { + self.add_child(child); + } + } + + pub fn child_at_mut(&mut self, index: usize) -> &mut StackingContext { + &mut *self.children[index] + } + + pub fn children(&self) -> &[Box] { + &self.children + } + + fn update_overflow_for_new_child(&mut self, child: &StackingContext) { + if self.context_type == StackingContextType::Real && + child.context_type == StackingContextType::Real && + !self.scrolls_overflow_area { + // This child might be transformed, so we need to take into account + // its transformed overflow rect too, but at the correct position. + let overflow = + child.overflow_rect_in_parent_space(); + + self.overflow = self.overflow.union(&overflow); + } + } + + fn overflow_rect_in_parent_space(&self) -> Rect { + // Transform this stacking context to get it into the same space as + // the parent stacking context. + // + // TODO: Take into account 3d transforms, even though it's a fairly + // uncommon case. + let origin_x = self.bounds.origin.x.to_f32_px(); + let origin_y = self.bounds.origin.y.to_f32_px(); + + let transform = Matrix4D::identity().translate(origin_x, origin_y, 0.0) + .mul(&self.transform); + let transform_2d = Matrix2D::new(transform.m11, transform.m12, + transform.m21, transform.m22, + transform.m41, transform.m42); + + let overflow = geometry::au_rect_to_f32_rect(self.overflow); + let overflow = transform_2d.transform_rect(&overflow); + geometry::f32_rect_to_au_rect(overflow) + } + pub fn hit_test<'a>(&self, traversal: &mut DisplayListTraversal<'a>, translated_point: &Point2D, @@ -679,7 +747,7 @@ impl StackingContext { } } - for child in self.children.iter() { + for child in self.children() { while let Some(item) = traversal.advance(self) { if let Some(meta) = item.hit_test(point) { result.push(meta); @@ -697,13 +765,13 @@ impl StackingContext { pub fn print_with_tree(&self, print_tree: &mut PrintTree) { print_tree.new_level(format!("{:?}", self)); - for kid in self.children.iter() { + for kid in self.children() { kid.print_with_tree(print_tree); } print_tree.end_level(); } - pub fn intersects_rect_in_parent_context(&self, rect: Option>) -> bool { + fn intersects_rect_in_parent_context(&self, rect: Option>) -> bool { // We only do intersection checks for real stacking contexts, since // pseudo stacking contexts might not have proper position information. if self.context_type != StackingContextType::Real { @@ -715,24 +783,7 @@ impl StackingContext { None => return true, }; - // Transform this stacking context to get it into the same space as - // the parent stacking context. - let origin_x = self.bounds.origin.x.to_f32_px(); - let origin_y = self.bounds.origin.y.to_f32_px(); - - let transform = Matrix4D::identity().translate(origin_x, - origin_y, - 0.0) - .mul(&self.transform); - let transform_2d = Matrix2D::new(transform.m11, transform.m12, - transform.m21, transform.m22, - transform.m41, transform.m42); - - let overflow = geometry::au_rect_to_f32_rect(self.overflow); - let overflow = transform_2d.transform_rect(&overflow); - let overflow = geometry::f32_rect_to_au_rect(overflow); - - rect.intersects(&overflow) + self.overflow_rect_in_parent_space().intersects(rect) } } diff --git a/components/gfx/paint_thread.rs b/components/gfx/paint_thread.rs index f1c712b60fe8..5b79203d4a7d 100644 --- a/components/gfx/paint_thread.rs +++ b/components/gfx/paint_thread.rs @@ -254,7 +254,7 @@ impl LayerCreator { parent_origin: &Point2D, transform: &Matrix4D, perspective: &Matrix4D) { - for kid in stacking_context.children.iter() { + for kid in stacking_context.children() { while let Some(item) = traversal.advance(stacking_context) { self.create_layers_for_item(item, parent_origin, diff --git a/components/layout/display_list_builder.rs b/components/layout/display_list_builder.rs index 229f826b4e33..7f00ccd1eb9c 100644 --- a/components/layout/display_list_builder.rs +++ b/components/layout/display_list_builder.rs @@ -1729,7 +1729,7 @@ impl BlockFlowDisplayListBuilding for BlockFlow { } } - contexts[stacking_context_index].children = floating; + contexts[stacking_context_index].set_children(floating); return stacking_context_id; } @@ -1745,14 +1745,14 @@ impl BlockFlowDisplayListBuilding for BlockFlow { &self.base, scroll_policy, StackingContextCreationMode::InnerScrollWrapper); - inner_stacking_context.children = child_contexts; + inner_stacking_context.set_children(child_contexts); let mut outer_stacking_context = self.fragment.create_stacking_context( stacking_context_id, &self.base, scroll_policy, StackingContextCreationMode::OuterScrollWrapper); - outer_stacking_context.children.push(inner_stacking_context); + outer_stacking_context.add_child(inner_stacking_context); outer_stacking_context } else { let mut stacking_context = self.fragment.create_stacking_context( @@ -1760,7 +1760,7 @@ impl BlockFlowDisplayListBuilding for BlockFlow { &self.base, scroll_policy, StackingContextCreationMode::Normal); - stacking_context.children = child_contexts; + stacking_context.set_children(child_contexts); stacking_context }; diff --git a/components/layout/sequential.rs b/components/layout/sequential.rs index 9d51df51a802..1862086b1639 100644 --- a/components/layout/sequential.rs +++ b/components/layout/sequential.rs @@ -84,8 +84,10 @@ pub fn build_display_list_for_subtree(root: &mut FlowRef, let flow_root = flow_ref::deref_mut(root); let layout_context = LayoutContext::new(shared_layout_context); flow_root.traverse_preorder(&ComputeAbsolutePositions { layout_context: &layout_context }); + let mut children = vec![]; flow_root.collect_stacking_contexts(root_stacking_context.id, - &mut root_stacking_context.children); + &mut children); + root_stacking_context.add_children(children); let mut build_display_list = BuildDisplayList { state: DisplayListBuildState::new(&layout_context, flow::base(&*flow_root).stacking_context_id), diff --git a/components/layout/webrender_helpers.rs b/components/layout/webrender_helpers.rs index d7fd26513e5d..0c95499f8801 100644 --- a/components/layout/webrender_helpers.rs +++ b/components/layout/webrender_helpers.rs @@ -260,7 +260,7 @@ impl WebRenderStackingContextConverter for StackingContext { builder: &mut webrender_traits::DisplayListBuilder, frame_builder: &mut WebRenderFrameBuilder, _force_positioned_stacking_level: bool) { - for child in self.children.iter() { + for child in self.children() { while let Some(item) = traversal.advance(self) { item.convert_to_webrender(builder, frame_builder); }