Browse Source

LibWeb: Implement `@supports` rule :^)

The main thing missing is that we don't serialize the supports clause,
but for actually using a `@supports (something: cool) {}` rule in CSS,
it works!
Sam Atkins 3 years ago
parent
commit
57a25139a5

+ 1 - 0
Userland/Libraries/LibWeb/CMakeLists.txt

@@ -25,6 +25,7 @@ set(SOURCES
     CSS/CSSStyleDeclaration.cpp
     CSS/CSSStyleRule.cpp
     CSS/CSSStyleSheet.cpp
+    CSS/CSSSupportsRule.cpp
     CSS/ResolvedCSSStyleDeclaration.cpp
     CSS/DefaultStyleSheetSource.cpp
     CSS/Length.cpp

+ 1 - 0
Userland/Libraries/LibWeb/CSS/CSSRule.h

@@ -26,6 +26,7 @@ public:
         Style,
         Import,
         Media,
+        Supports,
         __Count,
     };
 

+ 6 - 0
Userland/Libraries/LibWeb/CSS/CSSRuleList.cpp

@@ -8,6 +8,7 @@
 #include <LibWeb/CSS/CSSImportRule.h>
 #include <LibWeb/CSS/CSSMediaRule.h>
 #include <LibWeb/CSS/CSSRuleList.h>
+#include <LibWeb/CSS/CSSSupportsRule.h>
 #include <LibWeb/DOM/ExceptionOr.h>
 
 namespace Web::CSS {
@@ -93,6 +94,9 @@ void CSSRuleList::for_each_effective_style_rule(Function<void(CSSStyleRule const
         case CSSRule::Type::Media:
             verify_cast<CSSMediaRule>(rule).for_each_effective_style_rule(callback);
             break;
+        case CSSRule::Type::Supports:
+            verify_cast<CSSSupportsRule>(rule).for_each_effective_style_rule(callback);
+            break;
         case CSSRule::Type::__Count:
             VERIFY_NOT_REACHED();
         }
@@ -114,6 +118,8 @@ bool CSSRuleList::for_first_not_loaded_import_rule(Function<void(CSSImportRule&)
             }
         } else if (rule.type() == CSSRule::Type::Media) {
             return verify_cast<CSSMediaRule>(rule).for_first_not_loaded_import_rule(callback);
+        } else if (rule.type() == CSSRule::Type::Supports) {
+            return verify_cast<CSSSupportsRule>(rule).for_first_not_loaded_import_rule(callback);
         }
     }
 

+ 34 - 0
Userland/Libraries/LibWeb/CSS/CSSSupportsRule.cpp

@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2021, Sam Atkins <atkinssj@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <LibWeb/CSS/CSSSupportsRule.h>
+#include <LibWeb/CSS/Parser/Parser.h>
+
+namespace Web::CSS {
+
+CSSSupportsRule::CSSSupportsRule(NonnullRefPtr<Supports>&& supports, NonnullRefPtrVector<CSSRule>&& rules)
+    : CSSConditionRule(move(rules))
+    , m_supports(move(supports))
+{
+}
+
+CSSSupportsRule::~CSSSupportsRule()
+{
+}
+
+String CSSSupportsRule::condition_text() const
+{
+    // FIXME: Serializing supports rules!
+    return "<supports-condition>";
+}
+
+void CSSSupportsRule::set_condition_text(String text)
+{
+    if (auto new_supports = parse_css_supports({}, text))
+        m_supports = new_supports.release_nonnull();
+}
+
+}

+ 47 - 0
Userland/Libraries/LibWeb/CSS/CSSSupportsRule.h

@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2021, Sam Atkins <atkinssj@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <AK/NonnullRefPtr.h>
+#include <AK/NonnullRefPtrVector.h>
+#include <LibWeb/CSS/CSSConditionRule.h>
+#include <LibWeb/CSS/CSSRule.h>
+#include <LibWeb/CSS/Supports.h>
+#include <LibWeb/Forward.h>
+
+namespace Web::CSS {
+
+// https://www.w3.org/TR/css-conditional-3/#the-csssupportsrule-interface
+class CSSSupportsRule final : public CSSConditionRule {
+    AK_MAKE_NONCOPYABLE(CSSSupportsRule);
+    AK_MAKE_NONMOVABLE(CSSSupportsRule);
+
+public:
+    static NonnullRefPtr<CSSSupportsRule> create(NonnullRefPtr<Supports>&& supports, NonnullRefPtrVector<CSSRule>&& rules)
+    {
+        return adopt_ref(*new CSSSupportsRule(move(supports), move(rules)));
+    }
+
+    ~CSSSupportsRule();
+
+    virtual StringView class_name() const override { return "CSSSupportsRule"; };
+    virtual Type type() const override { return Type::Supports; };
+
+    String condition_text() const override;
+    void set_condition_text(String) override;
+    virtual bool condition_matches() const override { return m_supports->matches(); }
+
+private:
+    explicit CSSSupportsRule(NonnullRefPtr<Supports>&&, NonnullRefPtrVector<CSSRule>&&);
+
+    NonnullRefPtr<Supports> m_supports;
+};
+
+template<>
+inline bool CSSRule::fast_is<CSSSupportsRule>() const { return type() == CSSRule::Type::Supports; }
+
+}

+ 24 - 0
Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp

@@ -16,6 +16,7 @@
 #include <LibWeb/CSS/CSSStyleDeclaration.h>
 #include <LibWeb/CSS/CSSStyleRule.h>
 #include <LibWeb/CSS/CSSStyleSheet.h>
+#include <LibWeb/CSS/CSSSupportsRule.h>
 #include <LibWeb/CSS/Parser/DeclarationOrAtRule.h>
 #include <LibWeb/CSS/Parser/Parser.h>
 #include <LibWeb/CSS/Parser/StyleBlockRule.h>
@@ -1735,6 +1736,29 @@ RefPtr<CSSRule> Parser::convert_to_rule(NonnullRefPtr<StyleRule> rule)
                 return CSSImportRule::create(url.value());
             else
                 dbgln("Unable to parse url from @import rule");
+
+        } else if (rule->m_name.equals_ignoring_case("supports"sv)) {
+
+            auto supports_tokens = TokenStream { rule->prelude() };
+            auto supports = parse_a_supports(supports_tokens);
+            if (!supports) {
+                if constexpr (CSS_PARSER_DEBUG) {
+                    dbgln("CSSParser: @supports rule invalid; discarding.");
+                    supports_tokens.dump_all_tokens();
+                }
+                return {};
+            }
+
+            auto child_tokens = TokenStream { rule->block().values() };
+            auto parser_rules = consume_a_list_of_rules(child_tokens, false);
+            NonnullRefPtrVector<CSSRule> child_rules;
+            for (auto& raw_rule : parser_rules) {
+                if (auto child_rule = convert_to_rule(raw_rule))
+                    child_rules.append(*child_rule);
+            }
+
+            return CSSSupportsRule::create(supports.release_nonnull(), move(child_rules));
+
         } else {
             dbgln("Unrecognized CSS at-rule: @{}", rule->m_name);
         }

+ 14 - 0
Userland/Libraries/LibWeb/Dump.cpp

@@ -13,6 +13,7 @@
 #include <LibWeb/CSS/CSSRule.h>
 #include <LibWeb/CSS/CSSStyleRule.h>
 #include <LibWeb/CSS/CSSStyleSheet.h>
+#include <LibWeb/CSS/CSSSupportsRule.h>
 #include <LibWeb/CSS/PropertyID.h>
 #include <LibWeb/DOM/Comment.h>
 #include <LibWeb/DOM/Document.h>
@@ -500,6 +501,9 @@ void dump_rule(StringBuilder& builder, CSS::CSSRule const& rule, int indent_leve
     case CSS::CSSRule::Type::Media:
         dump_media_rule(builder, verify_cast<CSS::CSSMediaRule const>(rule), indent_levels);
         break;
+    case CSS::CSSRule::Type::Supports:
+        dump_supports_rule(builder, verify_cast<CSS::CSSSupportsRule const>(rule), indent_levels);
+        break;
     case CSS::CSSRule::Type::__Count:
         VERIFY_NOT_REACHED();
     }
@@ -521,6 +525,16 @@ void dump_media_rule(StringBuilder& builder, CSS::CSSMediaRule const& media, int
     }
 }
 
+void dump_supports_rule(StringBuilder& builder, CSS::CSSSupportsRule const& supports, int indent_levels)
+{
+    indent(builder, indent_levels);
+    builder.appendff("  Supports: {}\n  Rules ({}):\n", supports.condition_text(), supports.css_rules().length());
+
+    for (auto& rule : supports.css_rules()) {
+        dump_rule(builder, rule, indent_levels + 1);
+    }
+}
+
 void dump_style_rule(StringBuilder& builder, CSS::CSSStyleRule const& rule, int indent_levels)
 {
     for (auto& selector : rule.selectors()) {

+ 1 - 0
Userland/Libraries/LibWeb/Dump.h

@@ -23,6 +23,7 @@ void dump_rule(CSS::CSSRule const&);
 void dump_style_rule(StringBuilder&, CSS::CSSStyleRule const&, int indent_levels = 0);
 void dump_import_rule(StringBuilder&, CSS::CSSImportRule const&, int indent_levels = 0);
 void dump_media_rule(StringBuilder&, CSS::CSSMediaRule const&, int indent_levels = 0);
+void dump_supports_rule(StringBuilder&, CSS::CSSSupportsRule const&, int indent_levels = 0);
 void dump_selector(StringBuilder&, CSS::Selector const&);
 void dump_selector(CSS::Selector const&);
 

+ 1 - 0
Userland/Libraries/LibWeb/Forward.h

@@ -26,6 +26,7 @@ class CSSRuleList;
 class CSSStyleDeclaration;
 class CSSStyleRule;
 class CSSStyleSheet;
+class CSSSupportsRule;
 class Display;
 class ElementInlineCSSStyleDeclaration;
 class Length;