Ver Fonte

LibWeb: Add Ratio type to MediaFeatureValue

As noted, the Parser can't handle the `<number>` syntax for this - it
gets parsed instead by the `<number>` branch. We can't actually resolve
the ambiguity without making the Parser aware of what type each
media-feature is, but I will get to that soon. :^)
Sam Atkins há 3 anos atrás
pai
commit
deea129b8c

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

@@ -25,6 +25,7 @@ String MediaFeatureValue::to_string() const
     return m_value.visit(
         [](String const& ident) { return serialize_an_identifier(ident); },
         [](Length const& length) { return length.to_string(); },
+        [](Ratio const& ratio) { return ratio.to_string(); },
         [](Resolution const& resolution) { return resolution.to_string(); },
         [](double number) { return String::number(number); });
 }
@@ -34,6 +35,7 @@ bool MediaFeatureValue::is_same_type(MediaFeatureValue const& other) const
     return m_value.visit(
         [&](String const&) { return other.is_ident(); },
         [&](Length const&) { return other.is_length(); },
+        [&](Ratio const&) { return other.is_ratio(); },
         [&](Resolution const&) { return other.is_resolution(); },
         [&](double) { return other.is_number(); });
 }
@@ -88,6 +90,9 @@ bool MediaFeature::evaluate(DOM::Window const& window) const
             return queried_value.number() != 0;
         if (queried_value.is_length())
             return queried_value.length().raw_value() != 0;
+        // FIXME: I couldn't figure out from the spec how ratios should be evaluated in a boolean context.
+        if (queried_value.is_ratio())
+            return !queried_value.ratio().is_degenerate();
         if (queried_value.is_resolution())
             return queried_value.resolution().to_dots_per_pixel() != 0;
         if (queried_value.is_ident())
@@ -181,6 +186,25 @@ bool MediaFeature::compare(DOM::Window const& window, MediaFeatureValue left, Co
         VERIFY_NOT_REACHED();
     }
 
+    if (left.is_ratio()) {
+        auto left_decimal = left.ratio().value();
+        auto right_decimal = right.ratio().value();
+
+        switch (comparison) {
+        case Comparison::Equal:
+            return left_decimal == right_decimal;
+        case Comparison::LessThan:
+            return left_decimal < right_decimal;
+        case Comparison::LessThanOrEqual:
+            return left_decimal <= right_decimal;
+        case Comparison::GreaterThan:
+            return left_decimal > right_decimal;
+        case Comparison::GreaterThanOrEqual:
+            return left_decimal >= right_decimal;
+        }
+        VERIFY_NOT_REACHED();
+    }
+
     if (left.is_resolution()) {
         auto left_dppx = left.resolution().to_dots_per_pixel();
         auto right_dppx = right.resolution().to_dots_per_pixel();

+ 14 - 2
Userland/Libraries/LibWeb/CSS/MediaQuery.h

@@ -13,6 +13,7 @@
 #include <AK/OwnPtr.h>
 #include <AK/RefCounted.h>
 #include <LibWeb/CSS/GeneralEnclosed.h>
+#include <LibWeb/CSS/Ratio.h>
 #include <LibWeb/CSS/StyleValue.h>
 
 namespace Web::CSS {
@@ -30,6 +31,11 @@ public:
     {
     }
 
+    explicit MediaFeatureValue(Ratio ratio)
+        : m_value(move(ratio))
+    {
+    }
+
     explicit MediaFeatureValue(Resolution resolution)
         : m_value(move(resolution))
     {
@@ -45,6 +51,7 @@ public:
     bool is_ident() const { return m_value.has<String>(); }
     bool is_length() const { return m_value.has<Length>(); }
     bool is_number() const { return m_value.has<double>(); }
+    bool is_ratio() const { return m_value.has<Ratio>(); }
     bool is_resolution() const { return m_value.has<Resolution>(); }
     bool is_same_type(MediaFeatureValue const& other) const;
 
@@ -60,6 +67,12 @@ public:
         return m_value.get<Length>();
     }
 
+    Ratio const& ratio() const
+    {
+        VERIFY(is_ratio());
+        return m_value.get<Ratio>();
+    }
+
     Resolution const& resolution() const
     {
         VERIFY(is_resolution());
@@ -73,8 +86,7 @@ public:
     }
 
 private:
-    // TODO: Support <ratio> once we have that.
-    Variant<String, Length, Resolution, double> m_value;
+    Variant<String, Length, Ratio, Resolution, double> m_value;
 };
 
 // https://www.w3.org/TR/mediaqueries-4/#mq-features

+ 10 - 4
Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp

@@ -1153,13 +1153,14 @@ Optional<MediaFeatureValue> Parser::parse_media_feature_value(TokenStream<StyleC
     auto position = tokens.position();
     tokens.skip_whitespace();
     auto& first = tokens.next_token();
+    tokens.skip_whitespace();
 
     // `<number>`
-    if (first.is(Token::Type::Number))
+    if (first.is(Token::Type::Number) && !tokens.has_next_token())
         return MediaFeatureValue(first.token().number_value());
 
     // `<dimension>`
-    if (auto dimension = parse_dimension(first); dimension.has_value()) {
+    if (auto dimension = parse_dimension(first); dimension.has_value() && !tokens.has_next_token()) {
         if (dimension->is_length())
             return MediaFeatureValue(dimension->length());
         if (dimension->is_resolution())
@@ -1167,10 +1168,15 @@ Optional<MediaFeatureValue> Parser::parse_media_feature_value(TokenStream<StyleC
     }
 
     // `<ident>`
-    if (first.is(Token::Type::Ident))
+    if (first.is(Token::Type::Ident) && !tokens.has_next_token())
         return MediaFeatureValue(first.token().ident());
 
-    // FIXME: `<ratio>`, once we have ratios.
+    // `<ratio>`
+    // Note that a single <number> is a valid <ratio>, but it gets parsed above as a <number>.
+    // This will get solved with directed parsing of values based on the media-feature.
+    tokens.rewind_to_position(position);
+    if (auto ratio = parse_ratio(tokens); ratio.has_value() && !tokens.has_next_token())
+        return MediaFeatureValue(ratio.release_value());
 
     tokens.rewind_to_position(position);
     return {};

+ 2 - 1
Userland/Libraries/LibWeb/DOM/Window.cpp

@@ -381,7 +381,8 @@ Optional<CSS::MediaFeatureValue> Window::query_media_feature(FlyString const& na
         return CSS::MediaFeatureValue("hover");
     if (name.equals_ignoring_case("any-pointer"sv))
         return CSS::MediaFeatureValue("fine");
-    // FIXME: aspect-ratio
+    if (name.equals_ignoring_case("aspect-ratio"sv))
+        return CSS::MediaFeatureValue(CSS::Ratio(inner_width(), inner_height()));
     if (name.equals_ignoring_case("color"sv))
         return CSS::MediaFeatureValue(8);
     if (name.equals_ignoring_case("color-gamut"sv))