aboutsummaryrefslogtreecommitdiffstats
path: root/components/layout/display_list/stacking_context.rs
diff options
context:
space:
mode:
Diffstat (limited to 'components/layout/display_list/stacking_context.rs')
-rw-r--r--components/layout/display_list/stacking_context.rs135
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,
};