/* * Copyright (c) 2021, Sam Atkins * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include #include #include #include #include #include #include #include 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; }; // https://www.w3.org/TR/mediaqueries-4/#mq-features class MediaFeature { public: // Corresponds to `` grammar static MediaFeature boolean(String const& name) { return MediaFeature(Type::IsTrue, name); } // Corresponds to `` grammar static MediaFeature plain(String const& name, MediaFeatureValue value) { if (name.starts_with("min-", CaseSensitivity::CaseInsensitive)) return MediaFeature(Type::MinValue, name.substring_view(4), move(value)); if (name.starts_with("max-", CaseSensitivity::CaseInsensitive)) return MediaFeature(Type::MaxValue, name.substring_view(4), move(value)); return MediaFeature(Type::ExactValue, move(name), move(value)); } bool evaluate(DOM::Window const&) const; String to_string() const; private: // FIXME: Implement range syntax: https://www.w3.org/TR/mediaqueries-4/#mq-ranges enum class Type { IsTrue, ExactValue, MinValue, MaxValue, }; MediaFeature(Type type, FlyString name, Optional value = {}) : m_type(type) , m_name(move(name)) , m_value(move(value)) { } Type m_type; FlyString m_name; Optional m_value {}; }; // https://www.w3.org/TR/mediaqueries-4/#media-conditions struct MediaCondition { enum class Type { Single, And, Or, Not, GeneralEnclosed, }; static NonnullOwnPtr from_general_enclosed(GeneralEnclosed&&); static NonnullOwnPtr from_feature(MediaFeature&&); static NonnullOwnPtr from_not(NonnullOwnPtr&&); static NonnullOwnPtr from_and_list(NonnullOwnPtrVector&&); static NonnullOwnPtr from_or_list(NonnullOwnPtrVector&&); MatchResult evaluate(DOM::Window const&) const; String to_string() const; private: MediaCondition() { } Type type; Optional feature; NonnullOwnPtrVector conditions; Optional general_enclosed; }; class MediaQuery : public RefCounted { friend class Parser; public: ~MediaQuery() = default; // https://www.w3.org/TR/mediaqueries-4/#media-types enum class MediaType { All, Print, Screen, // Deprecated, must never match: TTY, TV, Projection, Handheld, Braille, Embossed, Aural, Speech, }; static NonnullRefPtr create_not_all(); static NonnullRefPtr create() { return adopt_ref(*new MediaQuery); } bool matches() const { return m_matches; } bool evaluate(DOM::Window const&); String to_string() const; private: MediaQuery() = default; // https://www.w3.org/TR/mediaqueries-4/#mq-not bool m_negated { false }; MediaType m_media_type { MediaType::All }; OwnPtr m_media_condition { nullptr }; // Cached value, updated by evaluate() bool m_matches { false }; }; String serialize_a_media_query_list(NonnullRefPtrVector const&); } namespace AK { template<> struct Formatter : Formatter { ErrorOr format(FormatBuilder& builder, Web::CSS::MediaFeature const& media_feature) { return Formatter::format(builder, media_feature.to_string()); } }; template<> struct Formatter : Formatter { ErrorOr format(FormatBuilder& builder, Web::CSS::MediaCondition const& media_condition) { return Formatter::format(builder, media_condition.to_string()); } }; template<> struct Formatter : Formatter { ErrorOr format(FormatBuilder& builder, Web::CSS::MediaQuery const& media_query) { return Formatter::format(builder, media_query.to_string()); } }; }