ladybird/Userland/Libraries/LibWeb/CSS/MediaQuery.h
Sam Atkins 6a74b01644 LibWeb: Rename "identifier" and "ValueID" to "Keyword" where correct
For a long time, we've used two terms, inconsistently:
- "Identifier" is a spec term, but refers to a sequence of alphanumeric
  characters, which may or may not be a keyword. (Keywords are a
  subset of all identifiers.)
- "ValueID" is entirely non-spec, and is directly called a "keyword" in
  the CSS specs.

So to avoid confusion as much as possible, let's align with the spec
terminology. I've attempted to change variable names as well, but
obviously we use Keywords in a lot of places in LibWeb and so I may
have missed some.

One exception is that I've not renamed "valid-identifiers" in
Properties.json... I'd like to combine that and the "valid-types" array
together eventually, so there's no benefit to doing an extra rename
now.
2024-08-15 13:58:38 +01:00

290 lines
7.5 KiB
C++

/*
* Copyright (c) 2021-2023, Sam Atkins <atkinssj@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/NonnullRefPtr.h>
#include <AK/Optional.h>
#include <AK/OwnPtr.h>
#include <AK/RefCounted.h>
#include <LibWeb/CSS/GeneralEnclosed.h>
#include <LibWeb/CSS/Length.h>
#include <LibWeb/CSS/MediaFeatureID.h>
#include <LibWeb/CSS/Ratio.h>
#include <LibWeb/CSS/Resolution.h>
namespace Web::CSS {
// https://www.w3.org/TR/mediaqueries-4/#typedef-mf-value
class MediaFeatureValue {
public:
explicit MediaFeatureValue(Keyword ident)
: m_value(move(ident))
{
}
explicit MediaFeatureValue(Length length)
: m_value(move(length))
{
}
explicit MediaFeatureValue(Ratio ratio)
: m_value(move(ratio))
{
}
explicit MediaFeatureValue(Resolution resolution)
: m_value(move(resolution))
{
}
explicit MediaFeatureValue(float number)
: m_value(number)
{
}
String to_string() const;
bool is_ident() const { return m_value.has<Keyword>(); }
bool is_length() const { return m_value.has<Length>(); }
bool is_number() const { return m_value.has<float>(); }
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;
Keyword const& ident() const
{
VERIFY(is_ident());
return m_value.get<Keyword>();
}
Length const& length() const
{
VERIFY(is_length());
return m_value.get<Length>();
}
Ratio const& ratio() const
{
VERIFY(is_ratio());
return m_value.get<Ratio>();
}
Resolution const& resolution() const
{
VERIFY(is_resolution());
return m_value.get<Resolution>();
}
float number() const
{
VERIFY(is_number());
return m_value.get<float>();
}
private:
Variant<Keyword, Length, Ratio, Resolution, float> m_value;
};
// https://www.w3.org/TR/mediaqueries-4/#mq-features
class MediaFeature {
public:
enum class Comparison {
Equal,
LessThan,
LessThanOrEqual,
GreaterThan,
GreaterThanOrEqual,
};
// Corresponds to `<mf-boolean>` grammar
static MediaFeature boolean(MediaFeatureID id)
{
return MediaFeature(Type::IsTrue, id);
}
// Corresponds to `<mf-plain>` grammar
static MediaFeature plain(MediaFeatureID id, MediaFeatureValue value)
{
return MediaFeature(Type::ExactValue, move(id), move(value));
}
static MediaFeature min(MediaFeatureID id, MediaFeatureValue value)
{
return MediaFeature(Type::MinValue, id, move(value));
}
static MediaFeature max(MediaFeatureID id, MediaFeatureValue value)
{
return MediaFeature(Type::MaxValue, id, move(value));
}
// Corresponds to `<mf-range>` grammar, with a single comparison
static MediaFeature half_range(MediaFeatureValue value, Comparison comparison, MediaFeatureID id)
{
MediaFeature feature { Type::Range, id };
feature.m_range = Range {
.left_value = value,
.left_comparison = comparison,
};
return feature;
}
// Corresponds to `<mf-range>` grammar, with two comparisons
static MediaFeature range(MediaFeatureValue left_value, Comparison left_comparison, MediaFeatureID id, Comparison right_comparison, MediaFeatureValue right_value)
{
MediaFeature feature { Type::Range, id };
feature.m_range = Range {
.left_value = left_value,
.left_comparison = left_comparison,
.right_comparison = right_comparison,
.right_value = right_value,
};
return feature;
}
bool evaluate(HTML::Window const&) const;
String to_string() const;
private:
enum class Type {
IsTrue,
ExactValue,
MinValue,
MaxValue,
Range,
};
MediaFeature(Type type, MediaFeatureID id, Optional<MediaFeatureValue> value = {})
: m_type(type)
, m_id(move(id))
, m_value(move(value))
{
}
static bool compare(HTML::Window const& window, MediaFeatureValue left, Comparison comparison, MediaFeatureValue right);
struct Range {
MediaFeatureValue left_value;
Comparison left_comparison;
Optional<Comparison> right_comparison {};
Optional<MediaFeatureValue> right_value {};
};
Type m_type;
MediaFeatureID m_id;
Optional<MediaFeatureValue> m_value {};
Optional<Range> m_range {};
};
// https://www.w3.org/TR/mediaqueries-4/#media-conditions
struct MediaCondition {
enum class Type {
Single,
And,
Or,
Not,
GeneralEnclosed,
};
// Only used in parsing
enum class AllowOr {
No = 0,
Yes = 1,
};
static NonnullOwnPtr<MediaCondition> from_general_enclosed(GeneralEnclosed&&);
static NonnullOwnPtr<MediaCondition> from_feature(MediaFeature&&);
static NonnullOwnPtr<MediaCondition> from_not(NonnullOwnPtr<MediaCondition>&&);
static NonnullOwnPtr<MediaCondition> from_and_list(Vector<NonnullOwnPtr<MediaCondition>>&&);
static NonnullOwnPtr<MediaCondition> from_or_list(Vector<NonnullOwnPtr<MediaCondition>>&&);
MatchResult evaluate(HTML::Window const&) const;
String to_string() const;
private:
MediaCondition() = default;
Type type;
Optional<MediaFeature> feature;
Vector<NonnullOwnPtr<MediaCondition>> conditions;
Optional<GeneralEnclosed> general_enclosed;
};
class MediaQuery : public RefCounted<MediaQuery> {
friend class Parser::Parser;
public:
~MediaQuery() = default;
// https://www.w3.org/TR/mediaqueries-4/#media-types
enum class MediaType {
All,
Print,
Screen,
Unknown,
// Deprecated, must never match:
TTY,
TV,
Projection,
Handheld,
Braille,
Embossed,
Aural,
Speech,
};
static NonnullRefPtr<MediaQuery> create_not_all();
static NonnullRefPtr<MediaQuery> create() { return adopt_ref(*new MediaQuery); }
bool matches() const { return m_matches; }
bool evaluate(HTML::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<MediaCondition> m_media_condition { nullptr };
// Cached value, updated by evaluate()
bool m_matches { false };
};
String serialize_a_media_query_list(Vector<NonnullRefPtr<MediaQuery>> const&);
MediaQuery::MediaType media_type_from_string(StringView);
StringView to_string(MediaQuery::MediaType);
}
namespace AK {
template<>
struct Formatter<Web::CSS::MediaFeature> : Formatter<StringView> {
ErrorOr<void> format(FormatBuilder& builder, Web::CSS::MediaFeature const& media_feature)
{
return Formatter<StringView>::format(builder, media_feature.to_string());
}
};
template<>
struct Formatter<Web::CSS::MediaCondition> : Formatter<StringView> {
ErrorOr<void> format(FormatBuilder& builder, Web::CSS::MediaCondition const& media_condition)
{
return Formatter<StringView>::format(builder, media_condition.to_string());
}
};
template<>
struct Formatter<Web::CSS::MediaQuery> : Formatter<StringView> {
ErrorOr<void> format(FormatBuilder& builder, Web::CSS::MediaQuery const& media_query)
{
return Formatter<StringView>::format(builder, media_query.to_string());
}
};
}