diff --git a/Libraries/LibWeb/CSS/ComputedProperties.cpp b/Libraries/LibWeb/CSS/ComputedProperties.cpp index 451343115a0..3a471c5bd55 100644 --- a/Libraries/LibWeb/CSS/ComputedProperties.cpp +++ b/Libraries/LibWeb/CSS/ComputedProperties.cpp @@ -230,7 +230,7 @@ Color ComputedProperties::color_or_fallback(CSS::PropertyID id, Layout::NodeWith } // https://drafts.csswg.org/css-color-adjust-1/#determine-the-used-color-scheme -CSS::PreferredColorScheme ComputedProperties::color_scheme(CSS::PreferredColorScheme preferred_scheme) const +CSS::PreferredColorScheme ComputedProperties::color_scheme(CSS::PreferredColorScheme preferred_scheme, Optional const&> document_supported_schemes) const { // To determine the used color scheme of an element: auto const& scheme_value = property(CSS::PropertyID::ColorScheme).as_color_scheme(); @@ -254,6 +254,16 @@ CSS::PreferredColorScheme ComputedProperties::color_scheme(CSS::PreferredColorSc return preferred_color_scheme_from_string(first_supported.value()); // 4. Otherwise, the used color scheme is the browser default. (Same as normal.) + // `normal` indicates that the element supports the page’s supported color schemes, if they are set + if (document_supported_schemes.has_value()) { + if (preferred_scheme != CSS::PreferredColorScheme::Auto && document_supported_schemes->contains_slow(preferred_color_scheme_to_string(preferred_scheme))) + return preferred_scheme; + + auto document_first_supported = document_supported_schemes->first_matching([](auto scheme) { return preferred_color_scheme_from_string(scheme) != CSS::PreferredColorScheme::Auto; }); + if (document_first_supported.has_value()) + return preferred_color_scheme_from_string(document_first_supported.value()); + } + return CSS::PreferredColorScheme::Light; } diff --git a/Libraries/LibWeb/CSS/ComputedProperties.h b/Libraries/LibWeb/CSS/ComputedProperties.h index 374686eb3f5..2ebbdab8809 100644 --- a/Libraries/LibWeb/CSS/ComputedProperties.h +++ b/Libraries/LibWeb/CSS/ComputedProperties.h @@ -74,7 +74,7 @@ public: Optional length_percentage(CSS::PropertyID) const; LengthBox length_box(CSS::PropertyID left_id, CSS::PropertyID top_id, CSS::PropertyID right_id, CSS::PropertyID bottom_id, const CSS::Length& default_value) const; Color color_or_fallback(CSS::PropertyID, Layout::NodeWithStyle const&, Color fallback) const; - CSS::PreferredColorScheme color_scheme(CSS::PreferredColorScheme) const; + CSS::PreferredColorScheme color_scheme(CSS::PreferredColorScheme, Optional const&> document_supported_schemes) const; Optional text_anchor() const; Optional text_align() const; Optional text_justify() const; diff --git a/Libraries/LibWeb/DOM/Document.cpp b/Libraries/LibWeb/DOM/Document.cpp index 4ca6220129e..233dd960a76 100644 --- a/Libraries/LibWeb/DOM/Document.cpp +++ b/Libraries/LibWeb/DOM/Document.cpp @@ -37,9 +37,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -98,6 +100,7 @@ #include #include #include +#include #include #include #include @@ -1432,6 +1435,42 @@ void Document::set_visited_link_color(Color color) m_visited_link_color = color; } +Optional const&> Document::supported_color_schemes() const +{ + return m_supported_color_schemes; +} + +// https://html.spec.whatwg.org/multipage/semantics.html#meta-color-scheme +void Document::obtain_supported_color_schemes() +{ + m_supported_color_schemes = {}; + + // 1. Let candidate elements be the list of all meta elements that meet the following criteria, in tree order: + for_each_in_subtree_of_type([&](HTML::HTMLMetaElement& element) { + // * the element is in a document tree; + // * the element has a name attribute, whose value is an ASCII case-insensitive match for color-scheme; and + // * the element has a content attribute. + + // 2. For each element in candidate elements: + auto content = element.attribute(HTML::AttributeNames::content); + if (element.name().has_value() && element.name()->equals_ignoring_ascii_case("color-scheme"sv) && content.has_value()) { + // 1. Let parsed be the result of parsing a list of component values given the value of element's content attribute. + auto context = CSS::Parser::ParsingContext { document() }; + auto parsed = parse_css_value(context, content.value(), CSS::PropertyID::ColorScheme); + + // 2. If parsed is a valid CSS 'color-scheme' property value, then return parsed. + if (!parsed.is_null() && parsed->is_color_scheme()) { + m_supported_color_schemes = parsed->as_color_scheme().schemes(); + return TraversalDecision::Break; + } + } + + return TraversalDecision::Continue; + }); + + // 3. Return null. +} + Layout::Viewport const* Document::layout_node() const { return static_cast(Node::layout_node()); diff --git a/Libraries/LibWeb/DOM/Document.h b/Libraries/LibWeb/DOM/Document.h index d93445c7ef2..22fbec3fc26 100644 --- a/Libraries/LibWeb/DOM/Document.h +++ b/Libraries/LibWeb/DOM/Document.h @@ -242,6 +242,9 @@ public: Optional visited_link_color() const; void set_visited_link_color(Color); + Optional const&> supported_color_schemes() const; + void obtain_supported_color_schemes(); + void update_style(); void update_layout(); void update_paint_and_hit_testing_properties_if_needed(); @@ -829,6 +832,8 @@ private: Optional m_active_link_color; Optional m_visited_link_color; + Optional> m_supported_color_schemes; + GC::Ptr m_parser; bool m_active_parser_was_aborted { false }; diff --git a/Libraries/LibWeb/HTML/HTMLMetaElement.cpp b/Libraries/LibWeb/HTML/HTMLMetaElement.cpp index 8f86575208d..e68d4c2ecb6 100644 --- a/Libraries/LibWeb/HTML/HTMLMetaElement.cpp +++ b/Libraries/LibWeb/HTML/HTMLMetaElement.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -47,6 +48,23 @@ Optional HTMLMetaElement::http_equiv_s return OptionalNone {}; } +void HTMLMetaElement::update_metadata(Optional const& old_name) +{ + if (name().has_value()) { + if (name()->equals_ignoring_ascii_case("color-scheme"sv)) { + document().obtain_supported_color_schemes(); + return; + } + } + + if (old_name.has_value()) { + if (old_name->equals_ignoring_ascii_case("color-scheme"sv)) { + document().obtain_supported_color_schemes(); + return; + } + } +} + void HTMLMetaElement::inserted() { Base::inserted(); @@ -84,6 +102,8 @@ void HTMLMetaElement::inserted() return; } + update_metadata(); + // https://html.spec.whatwg.org/multipage/semantics.html#pragma-directives // When a meta element is inserted into the document, if its http-equiv attribute is present and represents one of // the above states, then the user agent must run the algorithm appropriate for that state, as described in the @@ -160,4 +180,20 @@ void HTMLMetaElement::inserted() } } +void HTMLMetaElement::removed_from(Node* parent) +{ + Base::removed_from(parent); + update_metadata(); +} + +void HTMLMetaElement::attribute_changed(FlyString const& local_name, Optional const& old_value, Optional const& value, Optional const& namespace_) +{ + Base::attribute_changed(local_name, old_value, value, namespace_); + if (local_name == HTML::AttributeNames::name) { + update_metadata(old_value); + } else { + update_metadata(); + } +} + } diff --git a/Libraries/LibWeb/HTML/HTMLMetaElement.h b/Libraries/LibWeb/HTML/HTMLMetaElement.h index ba2a41a0a98..f844bd4ceb4 100644 --- a/Libraries/LibWeb/HTML/HTMLMetaElement.h +++ b/Libraries/LibWeb/HTML/HTMLMetaElement.h @@ -41,8 +41,12 @@ private: virtual void initialize(JS::Realm&) override; + void update_metadata(Optional const& old_name = {}); + // ^DOM::Element virtual void inserted() override; + virtual void removed_from(Node*) override; + virtual void attribute_changed(FlyString const& local_name, Optional const& old_value, Optional const& value, Optional const& namespace_) override; }; } diff --git a/Libraries/LibWeb/Layout/Node.cpp b/Libraries/LibWeb/Layout/Node.cpp index 6930e3bdfb8..7c750e82923 100644 --- a/Libraries/LibWeb/Layout/Node.cpp +++ b/Libraries/LibWeb/Layout/Node.cpp @@ -334,7 +334,7 @@ void NodeWithStyle::apply_style(const CSS::ComputedProperties& computed_style) if (preferred_color_scheme != CSS::PreferredColorScheme::Dark && preferred_color_scheme != CSS::PreferredColorScheme::Light) { preferred_color_scheme = document().page().palette().is_dark() ? CSS::PreferredColorScheme::Dark : CSS::PreferredColorScheme::Light; } - computed_values.set_color_scheme(computed_style.color_scheme(preferred_color_scheme)); + computed_values.set_color_scheme(computed_style.color_scheme(preferred_color_scheme, document().supported_color_schemes())); // NOTE: color must be set second to ensure currentColor can be resolved in other properties (e.g. background-color). computed_values.set_color(computed_style.color_or_fallback(CSS::PropertyID::Color, *this, CSS::InitialValues::color()));