
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.
290 lines
7.5 KiB
C++
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());
|
|
}
|
|
};
|
|
|
|
}
|