LibWeb: Propagate body background properties to root HTML element

The Acid1 test has a bit of an unusual background - the html and body
tags have different background colors. Our painting order of the DOM was
such that the body background was painted first, then all other elements
were painted in-phase according to Appendix E of CSS 2.1. So the html
element's background color was painted over the body background.

This removes the special handling of the body background from
InitialContainingBlockBox and now all boxes are painted in-phase. Doing
this also exposed that we weren't handling Section 2.11.2 of the spec;
when the html background is unset, the body's background should be
propagated to the html element.
This commit is contained in:
Timothy Flynn 2021-05-13 09:39:30 -04:00 committed by Andreas Kling
parent d1ed6bce5d
commit dba261f79b
Notes: sideshowbarker 2024-07-18 18:15:02 +09:00
5 changed files with 41 additions and 19 deletions

View file

@ -17,4 +17,12 @@ HTMLHtmlElement::~HTMLHtmlElement()
{
}
bool HTMLHtmlElement::should_use_body_background_properties() const
{
auto background_color = layout_node()->computed_values().background_color();
const auto* background_image = layout_node()->background_image();
return (background_color == Color::Transparent) && !background_image;
}
}

View file

@ -16,6 +16,8 @@ public:
HTMLHtmlElement(DOM::Document&, QualifiedName);
virtual ~HTMLHtmlElement() override;
bool should_use_body_background_properties() const;
};
}

View file

@ -7,6 +7,7 @@
#include <LibGfx/Painter.h>
#include <LibWeb/DOM/Document.h>
#include <LibWeb/HTML/HTMLBodyElement.h>
#include <LibWeb/HTML/HTMLHtmlElement.h>
#include <LibWeb/Layout/BlockBox.h>
#include <LibWeb/Layout/Box.h>
#include <LibWeb/Page/Frame.h>
@ -25,13 +26,37 @@ void Box::paint(PaintContext& context, PaintPhase phase)
auto padded_rect = this->padded_rect();
if (phase == PaintPhase::Background && !is_body()) {
auto background_rect = enclosing_int_rect(padded_rect);
context.painter().fill_rect(background_rect, computed_values().background_color());
if (phase == PaintPhase::Background) {
// If the body's background properties were propagated to the root element, do no re-paint the body's background.
if (is_body() && document().html_element()->should_use_body_background_properties())
return;
if (background_image() && background_image()->bitmap()) {
paint_background_image(context, *background_image()->bitmap(), computed_values().background_repeat_x(), computed_values().background_repeat_y(), move(background_rect));
Gfx::IntRect background_rect;
Color background_color = computed_values().background_color();
const Gfx::Bitmap* background_image = this->background_image() ? this->background_image()->bitmap() : nullptr;
CSS::Repeat background_repeat_x = computed_values().background_repeat_x();
CSS::Repeat background_repeat_y = computed_values().background_repeat_y();
if (is_root_element()) {
// CSS 2.1 Appendix E.2: If the element is a root element, paint the background over the entire canvas.
background_rect = context.viewport_rect();
// Section 2.11.2: 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 elements first HTML BODY child element.
if (document().html_element()->should_use_body_background_properties()) {
background_color = document().background_color(context.palette());
background_image = document().background_image();
background_repeat_x = document().background_repeat_x();
background_repeat_y = document().background_repeat_y();
}
} else {
background_rect = enclosing_int_rect(padded_rect);
}
context.painter().fill_rect(background_rect, move(background_color));
if (background_image)
paint_background_image(context, *background_image, background_repeat_x, background_repeat_y, move(background_rect));
}
if (phase == PaintPhase::Border) {

View file

@ -42,20 +42,9 @@ void InitialContainingBlockBox::build_stacking_context_tree()
});
}
void InitialContainingBlockBox::paint_document_background(PaintContext& context)
{
context.painter().fill_rect(Gfx::IntRect { {}, context.viewport_rect().size() }, document().background_color(context.palette()));
context.painter().translate(-context.viewport_rect().location());
if (auto background_bitmap = document().background_image()) {
Gfx::IntRect background_rect = { 0, 0, context.viewport_rect().x() + context.viewport_rect().width(), context.viewport_rect().y() + context.viewport_rect().height() };
paint_background_image(context, *background_bitmap, document().background_repeat_x(), document().background_repeat_y(), move(background_rect));
}
}
void InitialContainingBlockBox::paint_all_phases(PaintContext& context)
{
paint_document_background(context);
context.painter().translate(-context.viewport_rect().location());
stacking_context()->paint(context);
}

View file

@ -20,8 +20,6 @@ public:
void paint_all_phases(PaintContext&);
void paint_document_background(PaintContext&);
virtual HitTestResult hit_test(const Gfx::IntPoint&, HitTestType) const override;
const LayoutRange& selection() const { return m_selection; }