LibWeb: Respect root color scheme for loading canvases

Only use the loading-time top-level canvas fallback when the root
color-scheme value is still normal. An explicit light scheme on the
root element already gives the canvas a light used scheme, so replacing
it with the preferred dark scheme makes the viewport disagree with the
computed system colors until readiness advances.

Add internals coverage for the document canvas scheme and a text test
that exercises the loading state with a dark preferred color scheme.
This commit is contained in:
Andreas Kling 2026-05-27 11:56:19 +02:00 committed by Andreas Kling
parent 203885be59
commit 6ca9214b4c
8 changed files with 62 additions and 2 deletions

View file

@ -1390,12 +1390,14 @@ Color Document::canvas_background_color() const
CSS::PreferredColorScheme Document::canvas_color_scheme() const
{
auto color_scheme = CSS::PreferredColorScheme::Light;
auto root_color_scheme_is_normal = true;
if (auto* html_element = this->html_element(); html_element && html_element->layout_node()) {
auto const& computed_values = html_element->layout_node()->computed_values();
auto const& color_scheme_value = html_element->computed_properties()->property(CSS::PropertyID::ColorScheme).as_color_scheme();
root_color_scheme_is_normal = color_scheme_value.schemes().is_empty();
if (computed_values.color_scheme() == CSS::PreferredColorScheme::Dark) {
color_scheme = CSS::PreferredColorScheme::Dark;
} else if (auto const& color_scheme_value = html_element->computed_properties()->property(CSS::PropertyID::ColorScheme).as_color_scheme();
color_scheme_value.schemes().is_empty() && m_supported_color_schemes.has_value()) {
} else if (root_color_scheme_is_normal && m_supported_color_schemes.has_value()) {
auto preferred_color_scheme = page().preferred_color_scheme();
if (m_supported_color_schemes->contains_slow(CSS::preferred_color_scheme_to_string(preferred_color_scheme)))
color_scheme = preferred_color_scheme;
@ -1403,6 +1405,7 @@ CSS::PreferredColorScheme Document::canvas_color_scheme() const
}
if (color_scheme == CSS::PreferredColorScheme::Light
&& root_color_scheme_is_normal
&& !m_supported_color_schemes.has_value()
&& readiness() == HTML::DocumentReadyState::Loading) {
if (auto navigable = this->navigable(); navigable && navigable->is_top_level_traversable())

View file

@ -20,6 +20,7 @@
#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/Bindings/MainThreadVM.h>
#include <LibWeb/CSS/CSSStyleSheet.h>
#include <LibWeb/CSS/PreferredColorScheme.h>
#include <LibWeb/CSS/StyleValues/ImageStyleValue.h>
#include <LibWeb/Compositor/AsyncScrollTree.h>
#include <LibWeb/Compositor/AsyncScrollingState.h>
@ -714,6 +715,27 @@ void Internals::update_style()
window().associated_document().update_style();
}
void Internals::set_preferred_color_scheme(StringView color_scheme)
{
auto preferred_color_scheme = CSS::preferred_color_scheme_from_string(color_scheme);
Optional<CSS::PreferredColorScheme> preferred_color_scheme_override;
if (preferred_color_scheme != CSS::PreferredColorScheme::Auto)
preferred_color_scheme_override = preferred_color_scheme;
page().set_preferred_color_scheme_override_for_testing(preferred_color_scheme_override);
auto& document = window().associated_document();
document.invalidate_style(DOM::StyleInvalidationReason::SettingsChange);
document.set_needs_media_query_evaluation();
}
String Internals::canvas_color_scheme()
{
auto& document = window().associated_document();
document.update_layout(DOM::UpdateLayoutReason::Debugging);
return MUST(String::from_utf8(CSS::preferred_color_scheme_to_string(document.canvas_color_scheme())));
}
bool Internals::style_sheet_may_have_has_selectors(CSS::CSSStyleSheet& style_sheet)
{
return style_sheet.selector_insights().has_has_selectors;

View file

@ -122,6 +122,8 @@ public:
JS::Object* get_style_invalidation_counters();
void reset_style_invalidation_counters();
void update_style();
void set_preferred_color_scheme(StringView color_scheme);
String canvas_color_scheme();
bool style_sheet_may_have_has_selectors(CSS::CSSStyleSheet&);
WebIDL::UnsignedLongLong active_image_style_value_animation_count();
JS::Object* async_scrolling_state();

View file

@ -97,6 +97,8 @@ interface Internals {
undefined resetStyleInvalidationCounters();
// Flushes pending style work without forcing layout.
undefined updateStyle();
undefined setPreferredColorScheme(DOMString colorScheme);
DOMString canvasColorScheme();
// Returns the selector-insight cache state for stylesheet invalidation tests.
boolean styleSheetMayHaveHasSelectors(CSSStyleSheet sheet);
unsigned long long activeImageStyleValueAnimationCount();

View file

@ -216,6 +216,9 @@ CSSPixelRect Page::web_exposed_available_screen_area() const
CSS::PreferredColorScheme Page::preferred_color_scheme() const
{
if (m_preferred_color_scheme_override_for_testing.has_value())
return *m_preferred_color_scheme_override_for_testing;
auto preferred_color_scheme = m_client->preferred_color_scheme();
if (preferred_color_scheme == CSS::PreferredColorScheme::Auto)

View file

@ -132,6 +132,7 @@ public:
CSSPixelRect web_exposed_screen_area() const;
CSSPixelRect web_exposed_available_screen_area() const;
CSS::PreferredColorScheme preferred_color_scheme() const;
void set_preferred_color_scheme_override_for_testing(Optional<CSS::PreferredColorScheme> color_scheme) { m_preferred_color_scheme_override_for_testing = color_scheme; }
CSS::PreferredContrast preferred_contrast() const;
CSS::PreferredMotion preferred_motion() const;
@ -370,6 +371,7 @@ private:
URL::URL m_last_find_in_page_url;
bool m_listen_for_dom_mutations { false };
Optional<CSS::PreferredColorScheme> m_preferred_color_scheme_override_for_testing;
struct PendingFullscreenEnter {
GC::Ref<DOM::Element> element;

View file

@ -0,0 +1,3 @@
Ready state: loading
Computed root color-scheme: light
Canvas color-scheme: light

View file

@ -0,0 +1,23 @@
<!doctype html>
<style>
html {
color-scheme: light;
}
</style>
<body>
<pre id="out"></pre>
<script>
function println(line) {
out.appendChild(document.createTextNode(`${line}\n`));
}
internals.setPreferredColorScheme("dark");
internals.updateStyle();
println(`Ready state: ${document.readyState}`);
println(`Computed root color-scheme: ${getComputedStyle(document.documentElement).colorScheme}`);
println(`Canvas color-scheme: ${internals.canvasColorScheme()}`);
internals.signalTestIsDone(out.innerText);
</script>
</body>