From 87a30418bfff4d1a8481eec0a11a947f4c3b2485 Mon Sep 17 00:00:00 2001 From: Sam Atkins Date: Fri, 8 Oct 2021 15:40:19 +0100 Subject: [PATCH] LibWeb: Add CSS 'Supports' class The name is a little awkward, but this corresponds to the condition of a `@supports` rule or the `CSS.supports("")` function. A supports query only gets evaluated once, since its condition cannot change during runtime. (We either support something or we don't, and the spec specifically mentions that user preferences that disable features do not affect the result here.) We keep a representation of it around though, so that it can be serialized if needed. This is a little awkward since we hold onto a `StyleDeclarationRule` which should be an internal Parser class. This means making some Parser functions more public. Potentially we could evaluate the Supports inside the Parser, and have it only store a String representation of itself. But this works for now. :^) --- Userland/Libraries/LibWeb/CMakeLists.txt | 1 + .../Libraries/LibWeb/CSS/Parser/Parser.cpp | 2 +- Userland/Libraries/LibWeb/CSS/Parser/Parser.h | 4 +- Userland/Libraries/LibWeb/CSS/Supports.cpp | 79 +++++++++++++++++ Userland/Libraries/LibWeb/CSS/Supports.h | 84 +++++++++++++++++++ Userland/Libraries/LibWeb/Forward.h | 1 + 6 files changed, 169 insertions(+), 2 deletions(-) create mode 100644 Userland/Libraries/LibWeb/CSS/Supports.cpp create mode 100644 Userland/Libraries/LibWeb/CSS/Supports.h diff --git a/Userland/Libraries/LibWeb/CMakeLists.txt b/Userland/Libraries/LibWeb/CMakeLists.txt index d7cce14091..9a6f035b4a 100644 --- a/Userland/Libraries/LibWeb/CMakeLists.txt +++ b/Userland/Libraries/LibWeb/CMakeLists.txt @@ -47,6 +47,7 @@ set(SOURCES CSS/StyleSheet.cpp CSS/StyleSheetList.cpp CSS/StyleValue.cpp + CSS/Supports.cpp CSS/ValueID.cpp CSS/ValueID.h Cookie/ParsedCookie.cpp diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp index cebf6816e3..a1177b8585 100644 --- a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp +++ b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp @@ -1604,7 +1604,7 @@ RefPtr Parser::convert_to_declaration(Nonnull return parse_a_list_of_declarations(stream); } -Optional Parser::convert_to_style_property(StyleDeclarationRule& declaration) +Optional Parser::convert_to_style_property(StyleDeclarationRule const& declaration) { auto& property_name = declaration.m_name; auto property_id = property_id_from_string(property_name); diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Parser.h b/Userland/Libraries/LibWeb/CSS/Parser/Parser.h index d3b55de014..e52575f8a4 100644 --- a/Userland/Libraries/LibWeb/CSS/Parser/Parser.h +++ b/Userland/Libraries/LibWeb/CSS/Parser/Parser.h @@ -106,6 +106,9 @@ public: RefPtr parse_as_css_value(PropertyID); + // FIXME: This is a hack, while CSS::Supports is using a StyleDeclarationRule + [[nodiscard]] Optional convert_to_style_property(StyleDeclarationRule const&); + private: enum class ParsingResult { Done, @@ -176,7 +179,6 @@ private: [[nodiscard]] RefPtr convert_to_rule(NonnullRefPtr); [[nodiscard]] RefPtr convert_to_declaration(NonnullRefPtr); - [[nodiscard]] Optional convert_to_style_property(StyleDeclarationRule&); static Optional try_parse_float(StringView string); static Optional parse_color(ParsingContext const&, StyleComponentValueRule const&); diff --git a/Userland/Libraries/LibWeb/CSS/Supports.cpp b/Userland/Libraries/LibWeb/CSS/Supports.cpp new file mode 100644 index 0000000000..bb96088e10 --- /dev/null +++ b/Userland/Libraries/LibWeb/CSS/Supports.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2021, Sam Atkins + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include + +namespace Web::CSS { + +Supports::Supports(NonnullOwnPtr&& condition) + : m_condition(move(condition)) +{ + auto result = m_condition->evaluate(); + if (result == MatchResult::Unknown) { + dbgln("!!! Evaluation of CSS Supports returned 'Unknown'!"); + } + m_matches = result == MatchResult::True; +} + +Supports::MatchResult Supports::Condition::evaluate() const +{ + switch (type) { + case Type::Not: + return negate(children.first().evaluate()); + case Type::And: { + size_t true_results = 0; + for (auto& child : children) { + auto child_match = child.evaluate(); + if (child_match == MatchResult::False) + return MatchResult::False; + if (child_match == MatchResult::True) + true_results++; + } + if (true_results == children.size()) + return MatchResult::True; + return MatchResult::Unknown; + } + case Type::Or: { + size_t false_results = 0; + for (auto& child : children) { + auto child_match = child.evaluate(); + if (child_match == MatchResult::True) + return MatchResult::True; + if (child_match == MatchResult::False) + false_results++; + } + if (false_results == children.size()) + return MatchResult::False; + return MatchResult::Unknown; + } + } + VERIFY_NOT_REACHED(); +} + +Supports::MatchResult Supports::InParens::evaluate() const +{ + return value.visit( + [&](NonnullOwnPtr& condition) { + return condition->evaluate(); + }, + [&](Feature& feature) { + return feature.evaluate(); + }, + [&](GeneralEnclosed&) { + return MatchResult::Unknown; + }); +} + +Supports::MatchResult Supports::Feature::evaluate() const +{ + auto style_property = Parser({}, "").convert_to_style_property(declaration); + if (style_property.has_value()) + return MatchResult::True; + return MatchResult::False; +} + +} diff --git a/Userland/Libraries/LibWeb/CSS/Supports.h b/Userland/Libraries/LibWeb/CSS/Supports.h new file mode 100644 index 0000000000..6fe96351ce --- /dev/null +++ b/Userland/Libraries/LibWeb/CSS/Supports.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2021, Sam Atkins + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace Web::CSS { + +class Supports final : public RefCounted { + friend class Parser; + +private: + enum class MatchResult { + False, + True, + Unknown, + }; + + static MatchResult negate(MatchResult value) + { + switch (value) { + case MatchResult::False: + return MatchResult::True; + case MatchResult::True: + return MatchResult::False; + case MatchResult::Unknown: + return MatchResult::Unknown; + } + VERIFY_NOT_REACHED(); + } + +public: + struct GeneralEnclosed { + }; + + struct Feature { + // FIXME: Using this internal parser class is a bit of a hack. + StyleDeclarationRule declaration; + MatchResult evaluate() const; + }; + + struct Condition; + struct InParens { + Variant, Feature, GeneralEnclosed> value; + + MatchResult evaluate() const; + }; + + struct Condition { + enum class Type { + Not, + And, + Or, + }; + Type type; + Vector children; + + MatchResult evaluate() const; + }; + + static NonnullRefPtr create(NonnullOwnPtr&& condition) + { + return adopt_ref(*new Supports(move(condition))); + } + + bool matches() const { return m_matches; } + +private: + Supports(NonnullOwnPtr&&); + + NonnullOwnPtr m_condition; + bool m_matches { false }; +}; + +} diff --git a/Userland/Libraries/LibWeb/Forward.h b/Userland/Libraries/LibWeb/Forward.h index c6f3506a80..cdc6d6a33b 100644 --- a/Userland/Libraries/LibWeb/Forward.h +++ b/Userland/Libraries/LibWeb/Forward.h @@ -39,6 +39,7 @@ class Selector; class StyleProperties; class StyleComputer; class StyleSheet; +class Supports; class StyleValue; class BackgroundRepeatStyleValue;