layout: Resolve canvas background properties during painting (#36917)

Instead of resolving the canvas background properties (essentially
keeping a possible reference to the `<body>`'s style) during fragment
tree construction, wait until painting to possibly find the style on an
appropriate `<body>` fragment. This is possible now because `Fragment`
keeps a list of flags with relevant information about the root and
`<body>` elements.

A benefit of this approach is that styles aren't cached in the fragment
tree, which would be problematic for incremental layout. In addition,
the old code was making an effort to transform the `<body>`'s background
by the root element's transform. Only Safari does this and there was
a resolution the WG that this should not happen in
https://github.com/w3c/csswg-drafts/issues/6683.

Testing:
 - `/css/css-transforms/transform-translate-background-001.html`
 - `/css/css-transforms/transform-translate-background-002.html`
 - `/css/CSS2/floats/float-root.html`
 
Fixes: #30475.
Closes: #30569.

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
Co-authored-by: Oriol Brufau <obrufau@igalia.com>
This commit is contained in:
Martin Robinson 2025-05-09 12:36:53 +02:00 committed by GitHub
parent c6f61e6b6e
commit 53be79a5b5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 116 additions and 195 deletions

View file

@ -153,10 +153,6 @@ pub(crate) struct DisplayListBuilder<'a> {
/// list building functions.
current_clip_chain_id: ClipChainId,
/// The [OpaqueNode] handle to the node used to paint the page background
/// if the background was a canvas.
element_for_canvas_background: OpaqueNode,
/// A [LayoutContext] used to get information about the device pixel ratio
/// and get handles to WebRender images.
pub context: &'a LayoutContext<'a>,
@ -169,6 +165,11 @@ pub(crate) struct DisplayListBuilder<'a> {
/// This data is collected during the traversal of the fragment tree and used
/// to paint the highlight at the very end.
inspector_highlight: Option<InspectorHighlight>,
/// Whether or not the `<body>` element should be painted. This is false if the root `<html>`
/// element inherits the `<body>`'s background to paint the page canvas background.
/// See <https://drafts.csswg.org/css-backgrounds/#body-background>.
paint_body_background: bool,
}
struct InspectorHighlight {
@ -218,12 +219,12 @@ impl DisplayList {
current_scroll_node_id: self.compositor_info.root_reference_frame_id,
current_reference_frame_scroll_node_id: self.compositor_info.root_reference_frame_id,
current_clip_chain_id: ClipChainId::INVALID,
element_for_canvas_background: fragment_tree.canvas_background.from_element,
context,
display_list: self,
inspector_highlight: context
.highlighted_dom_node
.map(InspectorHighlight::for_node),
paint_body_background: true,
};
fragment_tree.build_display_list(&mut builder, root_stacking_context);
@ -999,12 +1000,17 @@ impl<'a> BuilderForBoxFragment<'a> {
}
fn build_background(&mut self, builder: &mut DisplayListBuilder) {
if self
.fragment
.base
.is_for_node(builder.element_for_canvas_background)
let flags = self.fragment.base.flags;
// The root element's background is painted separately as it might inherit the `<body>`'s
// background.
if flags.intersects(FragmentFlags::IS_ROOT_ELEMENT) {
return;
}
// If the `<body>` background was inherited by the root element, don't paint it again here.
if !builder.paint_body_background &&
flags.intersects(FragmentFlags::IS_BODY_ELEMENT_OF_HTML_ELEMENT_ROOT)
{
// This background is already painted for the canvas, dont paint it again here.
return;
}