diff --git a/components/gfx/display_list/mod.rs b/components/gfx/display_list/mod.rs index 17f9be5b485..d9befc61f00 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 f1c712b60fe..5b79203d4a7 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 229f826b4e3..7f00ccd1eb9 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 9d51df51a80..1862086b163 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 d7fd26513e5..0c95499f880 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); } diff --git a/tests/wpt/mozilla/meta/MANIFEST.json b/tests/wpt/mozilla/meta/MANIFEST.json index cfe571f4892..84e624262c3 100644 --- a/tests/wpt/mozilla/meta/MANIFEST.json +++ b/tests/wpt/mozilla/meta/MANIFEST.json @@ -3960,6 +3960,30 @@ "url": "/_mozilla/css/overflow_simple_a.html" } ], + "css/overflow_transformed_sc.html": [ + { + "path": "css/overflow_transformed_sc.html", + "references": [ + [ + "/_mozilla/css/overflow_transformed_sc_ref.html", + "==" + ] + ], + "url": "/_mozilla/css/overflow_transformed_sc.html" + } + ], + "css/overflow_transformed_sc_rotate.html": [ + { + "path": "css/overflow_transformed_sc_rotate.html", + "references": [ + [ + "/_mozilla/css/overflow_transformed_sc_rotate_ref.html", + "==" + ] + ], + "url": "/_mozilla/css/overflow_transformed_sc_rotate.html" + } + ], "css/overflow_wrap_a.html": [ { "path": "css/overflow_wrap_a.html", @@ -13192,6 +13216,30 @@ "url": "/_mozilla/css/overflow_simple_a.html" } ], + "css/overflow_transformed_sc.html": [ + { + "path": "css/overflow_transformed_sc.html", + "references": [ + [ + "/_mozilla/css/overflow_transformed_sc_ref.html", + "==" + ] + ], + "url": "/_mozilla/css/overflow_transformed_sc.html" + } + ], + "css/overflow_transformed_sc_rotate.html": [ + { + "path": "css/overflow_transformed_sc_rotate.html", + "references": [ + [ + "/_mozilla/css/overflow_transformed_sc_rotate_ref.html", + "==" + ] + ], + "url": "/_mozilla/css/overflow_transformed_sc_rotate.html" + } + ], "css/overflow_wrap_a.html": [ { "path": "css/overflow_wrap_a.html", diff --git a/tests/wpt/mozilla/tests/css/overflow_transformed_sc.html b/tests/wpt/mozilla/tests/css/overflow_transformed_sc.html new file mode 100644 index 00000000000..6fbd5e1ef0c --- /dev/null +++ b/tests/wpt/mozilla/tests/css/overflow_transformed_sc.html @@ -0,0 +1,31 @@ + + +Inner stacking contexts' transforms are taken into account for overflow computation + + +
+
+
+
diff --git a/tests/wpt/mozilla/tests/css/overflow_transformed_sc_ref.html b/tests/wpt/mozilla/tests/css/overflow_transformed_sc_ref.html new file mode 100644 index 00000000000..ca67f4ecb13 --- /dev/null +++ b/tests/wpt/mozilla/tests/css/overflow_transformed_sc_ref.html @@ -0,0 +1,28 @@ + + +CSS test reference + +
+
+
+
diff --git a/tests/wpt/mozilla/tests/css/overflow_transformed_sc_rotate.html b/tests/wpt/mozilla/tests/css/overflow_transformed_sc_rotate.html new file mode 100644 index 00000000000..5724bb3f1a6 --- /dev/null +++ b/tests/wpt/mozilla/tests/css/overflow_transformed_sc_rotate.html @@ -0,0 +1,31 @@ + + +Inner stacking contexts' transforms are taken into account for overflow computation + + +
+
+
+
diff --git a/tests/wpt/mozilla/tests/css/overflow_transformed_sc_rotate_ref.html b/tests/wpt/mozilla/tests/css/overflow_transformed_sc_rotate_ref.html new file mode 100644 index 00000000000..0035ffcf217 --- /dev/null +++ b/tests/wpt/mozilla/tests/css/overflow_transformed_sc_rotate_ref.html @@ -0,0 +1,30 @@ + + +CSS test reference. + +
+
+
+