diff options
Diffstat (limited to 'components/layout/display_list/stacking_context.rs')
-rw-r--r-- | components/layout/display_list/stacking_context.rs | 135 |
1 files changed, 40 insertions, 95 deletions
diff --git a/components/layout/display_list/stacking_context.rs b/components/layout/display_list/stacking_context.rs index b044b713260..27fa73a680c 100644 --- a/components/layout/display_list/stacking_context.rs +++ b/components/layout/display_list/stacking_context.rs @@ -582,15 +582,42 @@ impl StackingContext { &self, builder: &mut DisplayListBuilder, fragment_tree: &crate::FragmentTree, - containing_block_rect: &PhysicalRect<Au>, ) { - let style = if let Some(style) = &fragment_tree.canvas_background.style { - style - } else { - // The root element has `display: none`, - // or the canvas background is taken from `<body>` which has `display: none` + let Some(root_fragment) = fragment_tree.root_fragments.iter().find(|fragment| { + fragment + .base() + .is_some_and(|base| base.flags.intersects(FragmentFlags::IS_ROOT_ELEMENT)) + }) else { return; }; + let root_fragment = match root_fragment { + Fragment::Box(box_fragment) | Fragment::Float(box_fragment) => box_fragment, + _ => return, + } + .borrow(); + + let source_style = { + // > For documents whose root element is an HTML HTML element or an XHTML html element + // > [HTML]: if the computed value of background-image on the root element is none and its + // > background-color is transparent, user agents must instead propagate the computed + // > values of the background properties from that element’s first HTML BODY or XHTML body + // > child element. + if root_fragment.style.background_is_transparent() { + let body_fragment = fragment_tree.body_fragment(); + builder.paint_body_background = body_fragment.is_none(); + body_fragment + .map(|body_fragment| body_fragment.borrow().style.clone()) + .unwrap_or(root_fragment.style.clone()) + } else { + root_fragment.style.clone() + } + }; + + // This can happen if the root fragment does not have a `<body>` child (either because it is + // `display: none` or `display: contents`) or if the `<body>`'s background is transparent. + if source_style.background_is_transparent() { + return; + } // The painting area is theoretically the infinite 2D plane, // but we need a rectangle with finite coordinates. @@ -598,14 +625,15 @@ impl StackingContext { // If the document is smaller than the viewport (and doesn’t scroll), // we still want to paint the rest of the viewport. // If it’s larger, we also want to paint areas reachable after scrolling. - let mut painting_area = fragment_tree + let painting_area = fragment_tree .initial_containing_block .union(&fragment_tree.scrollable_overflow) .to_webrender(); - let background_color = style.resolve_color(&style.get_background().background_color); + let background_color = + source_style.resolve_color(&source_style.get_background().background_color); if background_color.alpha > 0.0 { - let common = builder.common_properties(painting_area, style); + let common = builder.common_properties(painting_area, &source_style); let color = super::rgba(background_color); builder .display_list @@ -613,97 +641,14 @@ impl StackingContext { .push_rect(&common, painting_area, color) } - // `background-color` was comparatively easy, - // but `background-image` needs a positioning area based on the root element. - // Let’s find the corresponding fragment. - - // The fragment generated by the root element is the first one here, unless… - let first_if_any = self.contents.first().or_else(|| { - // There wasn’t any `StackingContextFragment` in the root `StackingContext`, - // because the root element generates a stacking context. Let’s find that one. - self.real_stacking_contexts_and_positioned_stacking_containers - .first() - .and_then(|first_child_stacking_context| { - first_child_stacking_context.contents.first() - }) - }); - - macro_rules! debug_panic { - ($msg: expr) => { - if cfg!(debug_assertions) { - panic!($msg); - } else { - warn!($msg); - return; - } - }; - } - - let first_stacking_context_fragment = if let Some(first) = first_if_any { - first - } else { - // This should only happen if the root element has `display: none` - // TODO(servo#30569) revert to debug_panic!() once underlying bug is fixed - log::warn!( - "debug assertion failed! `CanvasBackground::for_root_element` should have returned `style: None`", - ); - return; - }; - - let StackingContextContent::Fragment { - fragment, - scroll_node_id, - containing_block, - .. - } = first_stacking_context_fragment - else { - debug_panic!("Expected a fragment, not a stacking container"); - }; - let box_fragment = match fragment { - Fragment::Box(box_fragment) | Fragment::Float(box_fragment) => box_fragment, - _ => debug_panic!("Expected a box-generated fragment"), - }; - let box_fragment = &*box_fragment.borrow(); - - // The `StackingContextFragment` we found is for the root DOM element: - debug_assert_eq!( - fragment.tag().map(|tag| tag.node), - Some(fragment_tree.canvas_background.root_element), - ); - - // The root element may have a CSS transform, and we want the canvas’ - // background image to be transformed. To do so, take its `SpatialId` - // (but not its `ClipId`) - builder.current_scroll_node_id = *scroll_node_id; - - // Now we need express the painting area rectangle in the local coordinate system, - // which differs from the top-level coordinate system based on… - - // Convert the painting area rectangle to the local coordinate system of this `SpatialId` - if let Some(reference_frame_data) = - box_fragment.reference_frame_data_if_necessary(containing_block_rect) - { - painting_area.min -= reference_frame_data.origin.to_webrender().to_vector(); - if let Some(transformed) = reference_frame_data - .transform - .inverse() - .and_then(|inversed| inversed.outer_transformed_rect(&painting_area.to_rect())) - { - painting_area = transformed.to_box2d(); - } else { - // The desired rect cannot be represented, so skip painting this background-image - return; - } - } - let mut fragment_builder = BuilderForBoxFragment::new( - box_fragment, - containing_block, + &root_fragment, + &fragment_tree.initial_containing_block, false, /* is_hit_test_for_scrollable_overflow */ false, /* is_collapsed_table_borders */ ); let painter = super::background::BackgroundPainter { - style, + style: &source_style, painting_area_override: Some(painting_area), positioning_area_override: None, }; |