ladybird/Userland/Libraries/LibWeb/CSS/MediaQuery.h
Sam Atkins 0c14103025 LibWeb: Move PercentageOr and subclasses into PercentageOr.{h,cpp}
This solves an awkward dependency cycle, where CalculatedStyleValue
needs the definition of Percentage, but including that would also pull
in PercentageOr, which in turn needs CalculatedStyleValue.

Many places that previously included StyleValue.h no longer need to. :^)
2023-03-30 21:29:50 +02:00

292 lines
7.6 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(ValueID 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)
{
}
ErrorOr<String> to_string() const;
bool is_ident() const { return m_value.has<ValueID>(); }
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;
ValueID const& ident() const
{
VERIFY(is_ident());
return m_value.get<ValueID>();
}
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<ValueID, 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;
ErrorOr<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;
ErrorOr<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&);
ErrorOr<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 };
};
ErrorOr<String> serialize_a_media_query_list(Vector<NonnullRefPtr<MediaQuery>> const&);
bool is_media_feature_name(StringView name);
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, TRY(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, TRY(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, TRY(media_query.to_string()));
}
};
}