From 4f210eb8fe7607fdf991ff284943175b256b2c62 Mon Sep 17 00:00:00 2001 From: Adenilson Cavalcanti Date: Mon, 20 Apr 2015 15:44:16 -0700 Subject: [PATCH] Fixing inline elements filtering rendering. It required to move create_stacking_context() outside of BlockFlow, as a method of FragmentDisplayListBuilding. This allowed the reuse for inline fragments that need a stacking context. As we now create stacking contexts (SC) for inline elements, this stresses the code in Fragment that calculates the need for a SC. For while, we create a SC only if there are filters associated with the Inline element. Companion issue is #5812. --- components/layout/display_list_builder.rs | 145 +++++++++++----------- tests/ref/basic.list | 1 + tests/ref/filter_inline_a.html | 18 +++ tests/ref/filter_inline_ref.html | 18 +++ tests/ref/rust.png | Bin 0 -> 13361 bytes 5 files changed, 112 insertions(+), 70 deletions(-) create mode 100644 tests/ref/filter_inline_a.html create mode 100644 tests/ref/filter_inline_ref.html create mode 100644 tests/ref/rust.png diff --git a/components/layout/display_list_builder.rs b/components/layout/display_list_builder.rs index a1074a0a27e..153afb61ee0 100644 --- a/components/layout/display_list_builder.rs +++ b/components/layout/display_list_builder.rs @@ -230,6 +230,14 @@ pub trait FragmentDisplayListBuilding { display_list: &mut DisplayList, stacking_relative_border_box: &Rect, clip: &ClippingRegion); + + /// Creates a stacking context for associated fragment. + fn create_stacking_context(&self, + base_flow: &BaseFlow, + display_list: Box, + layer: Option>) + -> Arc; + } fn handle_overlapping_radii(size: &Size2D, radii: &BorderRadii) -> BorderRadii { @@ -1017,6 +1025,52 @@ impl FragmentDisplayListBuilding for Fragment { } } + fn create_stacking_context(&self, + base_flow: &BaseFlow, + display_list: Box, + layer: Option>) + -> Arc { + + let border_box = self.stacking_relative_border_box(&base_flow.stacking_relative_position, + &base_flow.absolute_position_info + .relative_containing_block_size, + base_flow.absolute_position_info + .relative_containing_block_mode, + CoordinateSystem::Parent); + + let transform_origin = self.style().get_effects().transform_origin; + let transform_origin = + Point2D(model::specified(transform_origin.horizontal, + border_box.size.width).to_frac32_px(), + model::specified(transform_origin.vertical, + border_box.size.height).to_frac32_px()); + let transform = self.style().get_effects().transform + .unwrap_or(ComputedMatrix::identity()).to_gfx_matrix(&border_box.size); + + let transform = Matrix2D::identity().translate(transform_origin.x, transform_origin.y) + .mul(&transform).translate(-transform_origin.x, -transform_origin.y); + + // FIXME(pcwalton): Is this vertical-writing-direction-safe? + let margin = self.margin.to_physical(base_flow.writing_mode); + let overflow = base_flow.overflow.translate(&-Point2D(margin.left, Au(0))); + + // Create the filter pipeline. + let effects = self.style().get_effects(); + let mut filters = effects.filter.clone(); + if effects.opacity != 1.0 { + filters.push(Filter::Opacity(effects.opacity)) + } + + Arc::new(StackingContext::new(display_list, + &border_box, + &overflow, + self.style().get_box().z_index.number_or_zero(), + &transform, + filters, + self.style().get_effects().mix_blend_mode, + layer)) + } + #[inline(never)] fn finalize_position_and_size_of_iframe(&self, iframe_fragment: &IframeFragmentInfo, @@ -1222,10 +1276,6 @@ pub trait BlockFlowDisplayListBuilding { fn build_display_list_for_block(&mut self, display_list: Box, layout_context: &LayoutContext); - fn create_stacking_context(&self, - display_list: Box, - layer: Option>) - -> Arc; } impl BlockFlowDisplayListBuilding for BlockFlow { @@ -1268,8 +1318,7 @@ impl BlockFlowDisplayListBuilding for BlockFlow { background_border_level); self.base.display_list_building_result = if self.fragment.establishes_stacking_context() { - DisplayListBuildingResult::StackingContext(self.create_stacking_context(display_list, - None)) + DisplayListBuildingResult::StackingContext(self.fragment.create_stacking_context(&self.base, display_list, None)) } else { DisplayListBuildingResult::Normal(display_list) } @@ -1286,9 +1335,8 @@ impl BlockFlowDisplayListBuilding for BlockFlow { !self.base.flags.contains(NEEDS_LAYER) { // We didn't need a layer. self.base.display_list_building_result = - DisplayListBuildingResult::StackingContext(self.create_stacking_context( - display_list, - None)); + DisplayListBuildingResult::StackingContext(self.fragment + .create_stacking_context(&self.base, display_list, None)); return } @@ -1299,12 +1347,12 @@ impl BlockFlowDisplayListBuilding for BlockFlow { ScrollPolicy::Scrollable }; + let transparent = color::transparent(); - let stacking_context = - self.create_stacking_context(display_list, - Some(Arc::new(PaintLayer::new(self.layer_id(0), - transparent, - scroll_policy)))); + let stacking_context = self.fragment.create_stacking_context(&self.base, display_list, + Some(Arc::new(PaintLayer::new(self.layer_id(0), + transparent, + scroll_policy)))); self.base.display_list_building_result = DisplayListBuildingResult::StackingContext(stacking_context) } @@ -1318,8 +1366,8 @@ impl BlockFlowDisplayListBuilding for BlockFlow { display_list.form_float_pseudo_stacking_context(); self.base.display_list_building_result = if self.fragment.establishes_stacking_context() { - DisplayListBuildingResult::StackingContext(self.create_stacking_context(display_list, - None)) + DisplayListBuildingResult::StackingContext(self.fragment + .create_stacking_context(&self.base, display_list, None)) } else { DisplayListBuildingResult::Normal(display_list) } @@ -1340,58 +1388,6 @@ impl BlockFlowDisplayListBuilding for BlockFlow { BackgroundAndBorderLevel::Block); } } - - fn create_stacking_context(&self, - display_list: Box, - layer: Option>) - -> Arc { - debug_assert!(self.fragment.establishes_stacking_context()); - let border_box = self.fragment - .stacking_relative_border_box(&self.base.stacking_relative_position, - &self.base - .absolute_position_info - .relative_containing_block_size, - self.base - .absolute_position_info - .relative_containing_block_mode, - CoordinateSystem::Parent); - - let transform_origin = self.fragment.style().get_effects().transform_origin; - let transform_origin = - Point2D(model::specified(transform_origin.horizontal, - border_box.size.width).to_frac32_px(), - model::specified(transform_origin.vertical, - border_box.size.height).to_frac32_px()); - let transform = self.fragment - .style() - .get_effects() - .transform - .unwrap_or(ComputedMatrix::identity()) - .to_gfx_matrix(&border_box.size); - let transform = Matrix2D::identity().translate(transform_origin.x, transform_origin.y) - .mul(&transform) - .translate(-transform_origin.x, -transform_origin.y); - - // FIXME(pcwalton): Is this vertical-writing-direction-safe? - let margin = self.fragment.margin.to_physical(self.base.writing_mode); - let overflow = self.base.overflow.translate(&-Point2D(margin.left, Au(0))); - - // Create the filter pipeline. - let effects = self.fragment.style().get_effects(); - let mut filters = effects.filter.clone(); - if effects.opacity != 1.0 { - filters.push(Filter::Opacity(effects.opacity)) - } - - Arc::new(StackingContext::new(display_list, - &border_box, - &overflow, - self.fragment.style().get_box().z_index.number_or_zero(), - &transform, - filters, - self.fragment.style().get_effects().mix_blend_mode, - layer)) - } } pub trait InlineFlowDisplayListBuilding { @@ -1405,7 +1401,7 @@ impl InlineFlowDisplayListBuilding for InlineFlow { debug!("Flow: building display list for {} inline fragments", self.fragments.len()); let mut display_list = box DisplayList::new(); - + let mut has_stacking_context = false; for fragment in self.fragments.fragments.iter_mut() { fragment.build_display_list(&mut *display_list, layout_context, @@ -1418,6 +1414,8 @@ impl InlineFlowDisplayListBuilding for InlineFlow { .relative_containing_block_mode, BackgroundAndBorderLevel::Content, &self.base.clip); + + has_stacking_context = fragment.establishes_stacking_context(); match fragment.specific { SpecificFragmentInfo::InlineBlock(ref mut block_flow) => { let block_flow = &mut *block_flow.flow_ref; @@ -1438,7 +1436,14 @@ impl InlineFlowDisplayListBuilding for InlineFlow { self.fragments.fragments[0].node); } - self.base.display_list_building_result = DisplayListBuildingResult::Normal(display_list); + // FIXME(Savago): fix Fragment::establishes_stacking_context() for absolute positioned item + // and remove the check for filter presence. Further details on #5812. + if has_stacking_context && !self.fragments.fragments[0].style().get_effects().filter.is_empty() { + self.base.display_list_building_result = + DisplayListBuildingResult::StackingContext(self.fragments.fragments[0].create_stacking_context(&self.base, display_list, None)); + } else { + self.base.display_list_building_result = DisplayListBuildingResult::Normal(display_list); + } if opts::get().validate_display_list_geometry { self.base.validate_display_list_geometry(); diff --git a/tests/ref/basic.list b/tests/ref/basic.list index 2f17b1e9e59..8d8a6b21add 100644 --- a/tests/ref/basic.list +++ b/tests/ref/basic.list @@ -108,6 +108,7 @@ flaky_cpu == append_style_a.html append_style_b.html == empty_cells_a.html empty_cells_ref.html == external_media_query_link.html external_media_query_ref.html == external_media_query_style.html external_media_query_ref.html +== filter_inline_a.html filter_inline_ref.html == filter_opacity_a.html filter_opacity_ref.html == filter_sepia_a.html filter_sepia_ref.html == first_child_pseudo_a.html first_child_pseudo_b.html diff --git a/tests/ref/filter_inline_a.html b/tests/ref/filter_inline_a.html new file mode 100644 index 00000000000..1190c6602ad --- /dev/null +++ b/tests/ref/filter_inline_a.html @@ -0,0 +1,18 @@ + + + + + + + + + diff --git a/tests/ref/filter_inline_ref.html b/tests/ref/filter_inline_ref.html new file mode 100644 index 00000000000..0bc34849029 --- /dev/null +++ b/tests/ref/filter_inline_ref.html @@ -0,0 +1,18 @@ + + + + + + + + + diff --git a/tests/ref/rust.png b/tests/ref/rust.png new file mode 100644 index 0000000000000000000000000000000000000000..68b5ed31e7b09382e38a963ff39dbe249e56ec36 GIT binary patch literal 13361 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