diff --git a/Userland/Libraries/LibWeb/CSS/MediaQuery.cpp b/Userland/Libraries/LibWeb/CSS/MediaQuery.cpp index 225af000d9..b273a7a39c 100644 --- a/Userland/Libraries/LibWeb/CSS/MediaQuery.cpp +++ b/Userland/Libraries/LibWeb/CSS/MediaQuery.cpp @@ -5,6 +5,7 @@ */ #include +#include #include #include @@ -19,6 +20,45 @@ NonnullRefPtr MediaQuery::create_not_all() return adopt_ref(*media_query); } +String MediaFeatureValue::to_string() const +{ + return m_value.visit( + [](String& ident) { return serialize_an_identifier(ident); }, + [](Length& length) { return length.to_string(); }, + [](double number) { return String::number(number); }); +} + +bool MediaFeatureValue::is_same_type(MediaFeatureValue const& other) const +{ + return m_value.visit( + [&](String&) { return other.is_ident(); }, + [&](Length&) { return other.is_length(); }, + [&](double) { return other.is_number(); }); +} + +bool MediaFeatureValue::equals(MediaFeatureValue const& other) const +{ + if (!is_same_type(other)) + return false; + + if (is_ident() && other.is_ident()) + return m_value.get().equals_ignoring_case(other.m_value.get()); + if (is_length() && other.is_length()) { + // FIXME: Handle relative lengths. https://www.w3.org/TR/mediaqueries-4/#ref-for-relative-length + auto& my_length = m_value.get(); + auto& other_length = other.m_value.get(); + if (!my_length.is_absolute() || !other_length.is_absolute()) { + dbgln("TODO: Support relative lengths in media queries!"); + return false; + } + return my_length.absolute_length_to_px() == other_length.absolute_length_to_px(); + } + if (is_number() && other.is_number()) + return m_value.get() == other.m_value.get(); + + VERIFY_NOT_REACHED(); +} + String MediaQuery::MediaFeature::to_string() const { switch (type) { @@ -37,51 +77,62 @@ String MediaQuery::MediaFeature::to_string() const bool MediaQuery::MediaFeature::evaluate(DOM::Window const& window) const { - auto queried_value = window.query_media_feature(name); - if (!queried_value) + auto maybe_queried_value = window.query_media_feature(name); + if (!maybe_queried_value.has_value()) return false; + auto queried_value = maybe_queried_value.release_value(); switch (type) { case Type::IsTrue: - if (queried_value->has_number()) - return queried_value->to_number() != 0; - if (queried_value->has_length()) - return queried_value->to_length().raw_value() != 0; - if (queried_value->has_identifier()) - return queried_value->to_identifier() != ValueID::None; + if (queried_value.is_number()) + return queried_value.number() != 0; + if (queried_value.is_length()) + return queried_value.length().raw_value() != 0; + if (queried_value.is_ident()) + return queried_value.ident() != "none"; return false; case Type::ExactValue: - return queried_value->equals(*value); + return queried_value.equals(*value); case Type::MinValue: - if (queried_value->has_number() && value->has_number()) - return queried_value->to_number() >= value->to_number(); - if (queried_value->has_length() && value->has_length()) { - auto queried_length = queried_value->to_length(); - auto value_length = value->to_length(); - // FIXME: We should be checking that lengths are valid during parsing + if (!value->is_same_type(queried_value)) + return false; + + if (value->is_number()) + return queried_value.number() >= value->number(); + + if (value->is_length()) { + auto& queried_length = queried_value.length(); + auto& value_length = value->length(); + // FIXME: Handle relative lengths. https://www.w3.org/TR/mediaqueries-4/#ref-for-relative-length if (!value_length.is_absolute()) { - dbgln("Media feature was given a non-absolute length, which is invalid! {}", value_length.to_string()); + dbgln("Media feature was given a non-absolute length! {}", value_length.to_string()); return false; } return queried_length.absolute_length_to_px() >= value_length.absolute_length_to_px(); } + return false; case Type::MaxValue: - if (queried_value->has_number() && value->has_number()) - return queried_value->to_number() <= value->to_number(); - if (queried_value->has_length() && value->has_length()) { - auto queried_length = queried_value->to_length(); - auto value_length = value->to_length(); - // FIXME: We should be checking that lengths are valid during parsing + if (!value->is_same_type(queried_value)) + return false; + + if (value->is_number()) + return queried_value.number() <= value->number(); + + if (value->is_length()) { + auto& queried_length = queried_value.length(); + auto& value_length = value->length(); + // FIXME: Handle relative lengths. https://www.w3.org/TR/mediaqueries-4/#ref-for-relative-length if (!value_length.is_absolute()) { - dbgln("Media feature was given a non-absolute length, which is invalid! {}", value_length.to_string()); + dbgln("Media feature was given a non-absolute length! {}", value_length.to_string()); return false; } return queried_length.absolute_length_to_px() <= value_length.absolute_length_to_px(); } + return false; } diff --git a/Userland/Libraries/LibWeb/CSS/MediaQuery.h b/Userland/Libraries/LibWeb/CSS/MediaQuery.h index eb2b7598f8..b7ef4079b1 100644 --- a/Userland/Libraries/LibWeb/CSS/MediaQuery.h +++ b/Userland/Libraries/LibWeb/CSS/MediaQuery.h @@ -17,6 +17,58 @@ namespace Web::CSS { +// https://www.w3.org/TR/mediaqueries-4/#typedef-mf-value +class MediaFeatureValue { +public: + explicit MediaFeatureValue(String ident) + : m_value(move(ident)) + { + } + + explicit MediaFeatureValue(Length length) + : m_value(move(length)) + { + } + + explicit MediaFeatureValue(double number) + : m_value(number) + { + } + + String to_string() const; + + bool is_ident() const { return m_value.has(); } + bool is_length() const { return m_value.has(); } + bool is_number() const { return m_value.has(); } + bool is_same_type(MediaFeatureValue const& other) const; + + String const& ident() const + { + VERIFY(is_ident()); + return m_value.get(); + } + + Length const& length() const + { + VERIFY(is_length()); + return m_value.get(); + } + + double number() const + { + VERIFY(is_number()); + return m_value.get(); + } + + bool operator==(MediaFeatureValue const& other) const { return equals(other); } + bool operator!=(MediaFeatureValue const& other) const { return !(*this == other); } + bool equals(MediaFeatureValue const& other) const; + +private: + // TODO: Support once we have that. + Variant m_value; +}; + class MediaQuery : public RefCounted { friend class Parser; @@ -52,7 +104,7 @@ public: Type type; FlyString name; - RefPtr value { nullptr }; + Optional value {}; bool evaluate(DOM::Window const&) const; String to_string() const; diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp index ed3f8440fa..972116b29b 100644 --- a/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp +++ b/Userland/Libraries/LibWeb/CSS/Parser/Parser.cpp @@ -895,8 +895,8 @@ Optional Parser::consume_media_feature(TokenStream Parser::consume_media_type(TokenStream`, https://www.w3.org/TR/mediaqueries-4/#typedef-mf-value +Optional Parser::parse_media_feature_value(TokenStream& tokens) +{ + // ` | | | ` + auto position = tokens.position(); + tokens.skip_whitespace(); + auto& first = tokens.next_token(); + + // `` + if (first.is(Token::Type::Number)) + return MediaFeatureValue(first.token().number_value()); + + // `` + if (auto length = parse_length(first); length.has_value()) + return MediaFeatureValue(length.release_value()); + + // `` + if (first.is(Token::Type::Ident)) + return MediaFeatureValue(first.token().ident()); + + // FIXME: ``, once we have ratios. + + tokens.rewind_to_position(position); + return {}; +} + RefPtr Parser::parse_as_supports() { return parse_a_supports(m_token_stream); diff --git a/Userland/Libraries/LibWeb/CSS/Parser/Parser.h b/Userland/Libraries/LibWeb/CSS/Parser/Parser.h index 5842370f7b..28640ce686 100644 --- a/Userland/Libraries/LibWeb/CSS/Parser/Parser.h +++ b/Userland/Libraries/LibWeb/CSS/Parser/Parser.h @@ -238,6 +238,7 @@ private: OwnPtr consume_media_condition(TokenStream&); Optional consume_media_feature(TokenStream&); Optional consume_media_type(TokenStream&); + Optional parse_media_feature_value(TokenStream&); OwnPtr parse_supports_condition(TokenStream&); Optional parse_supports_in_parens(TokenStream&); diff --git a/Userland/Libraries/LibWeb/DOM/Window.cpp b/Userland/Libraries/LibWeb/DOM/Window.cpp index 4453b9bb12..0ddac4e754 100644 --- a/Userland/Libraries/LibWeb/DOM/Window.cpp +++ b/Userland/Libraries/LibWeb/DOM/Window.cpp @@ -291,59 +291,59 @@ NonnullRefPtr Window::match_media(String media) return media_query_list; } -RefPtr Window::query_media_feature(FlyString const& name) const +Optional Window::query_media_feature(FlyString const& name) const { // FIXME: Many of these should be dependent on the hardware // MEDIAQUERIES-4 properties - https://www.w3.org/TR/mediaqueries-4/#media-descriptor-table if (name.equals_ignoring_case("any-hover"sv)) - return CSS::IdentifierStyleValue::create(CSS::ValueID::Hover); + return CSS::MediaFeatureValue("hover"); if (name.equals_ignoring_case("any-pointer"sv)) - return CSS::IdentifierStyleValue::create(CSS::ValueID::Fine); + return CSS::MediaFeatureValue("fine"); // FIXME: aspect-ratio if (name.equals_ignoring_case("color"sv)) - return CSS::NumericStyleValue::create_integer(32); + return CSS::MediaFeatureValue(32); if (name.equals_ignoring_case("color-gamut"sv)) - return CSS::IdentifierStyleValue::create(CSS::ValueID::Srgb); + return CSS::MediaFeatureValue("srgb"); if (name.equals_ignoring_case("color-index"sv)) - return CSS::NumericStyleValue::create_integer(0); + return CSS::MediaFeatureValue(0); // FIXME: device-aspect-ratio // FIXME: device-height // FIXME: device-width if (name.equals_ignoring_case("grid"sv)) - return CSS::NumericStyleValue::create_integer(0); + return CSS::MediaFeatureValue(0); if (name.equals_ignoring_case("height"sv)) - return CSS::LengthStyleValue::create(CSS::Length::make_px(inner_height())); + return CSS::MediaFeatureValue(CSS::Length::make_px(inner_height())); if (name.equals_ignoring_case("hover"sv)) - return CSS::IdentifierStyleValue::create(CSS::ValueID::Hover); + return CSS::MediaFeatureValue("hover"); if (name.equals_ignoring_case("monochrome"sv)) - return CSS::NumericStyleValue::create_integer(0); + return CSS::MediaFeatureValue(0); if (name.equals_ignoring_case("hover"sv)) - return CSS::IdentifierStyleValue::create(inner_height() >= inner_width() ? CSS::ValueID::Portrait : CSS::ValueID::Landscape); + return CSS::MediaFeatureValue(inner_height() >= inner_width() ? "portrait" : "landscape"); if (name.equals_ignoring_case("overflow-block"sv)) - return CSS::IdentifierStyleValue::create(CSS::ValueID::Scroll); + return CSS::MediaFeatureValue("scroll"); // FIXME: overflow-inline if (name.equals_ignoring_case("pointer"sv)) - return CSS::IdentifierStyleValue::create(CSS::ValueID::Fine); + return CSS::MediaFeatureValue("fine"); // FIXME: resolution if (name.equals_ignoring_case("scan"sv)) - return CSS::IdentifierStyleValue::create(CSS::ValueID::Progressive); + return CSS::MediaFeatureValue("progressive"); if (name.equals_ignoring_case("update"sv)) - return CSS::IdentifierStyleValue::create(CSS::ValueID::Fast); + return CSS::MediaFeatureValue("fast"); if (name.equals_ignoring_case("width"sv)) - return CSS::LengthStyleValue::create(CSS::Length::make_px(inner_width())); + return CSS::MediaFeatureValue(CSS::Length::make_px(inner_width())); // MEDIAQUERIES-5 properties - https://www.w3.org/TR/mediaqueries-5/#media-descriptor-table if (name.equals_ignoring_case("prefers-color-scheme")) { if (auto* page = this->page()) { switch (page->preferred_color_scheme()) { case CSS::PreferredColorScheme::Light: - return CSS::IdentifierStyleValue::create(CSS::ValueID::Light); + return CSS::MediaFeatureValue("light"); case CSS::PreferredColorScheme::Dark: - return CSS::IdentifierStyleValue::create(CSS::ValueID::Dark); + return CSS::MediaFeatureValue("dark"); case CSS::PreferredColorScheme::Auto: default: - return CSS::IdentifierStyleValue::create(page->palette().is_dark() ? CSS::ValueID::Dark : CSS::ValueID::Light); + return CSS::MediaFeatureValue(page->palette().is_dark() ? "dark" : "light"); } } } diff --git a/Userland/Libraries/LibWeb/DOM/Window.h b/Userland/Libraries/LibWeb/DOM/Window.h index 19394449a2..93f7d1c6ba 100644 --- a/Userland/Libraries/LibWeb/DOM/Window.h +++ b/Userland/Libraries/LibWeb/DOM/Window.h @@ -84,7 +84,7 @@ public: NonnullRefPtr get_computed_style(DOM::Element&) const; NonnullRefPtr match_media(String); - RefPtr query_media_feature(FlyString const&) const; + Optional query_media_feature(FlyString const&) const; float scroll_x() const; float scroll_y() const;