Serenity Operating System
at master 291 lines 7.8 kB view raw
1/* 2 * Copyright (c) 2021-2023, Sam Atkins <atkinssj@serenityos.org> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#pragma once 8 9#include <AK/NonnullRefPtr.h> 10#include <AK/Optional.h> 11#include <AK/OwnPtr.h> 12#include <AK/RefCounted.h> 13#include <LibWeb/CSS/GeneralEnclosed.h> 14#include <LibWeb/CSS/MediaFeatureID.h> 15#include <LibWeb/CSS/Ratio.h> 16#include <LibWeb/CSS/StyleValue.h> 17 18namespace Web::CSS { 19 20// https://www.w3.org/TR/mediaqueries-4/#typedef-mf-value 21class MediaFeatureValue { 22public: 23 explicit MediaFeatureValue(ValueID ident) 24 : m_value(move(ident)) 25 { 26 } 27 28 explicit MediaFeatureValue(Length length) 29 : m_value(move(length)) 30 { 31 } 32 33 explicit MediaFeatureValue(Ratio ratio) 34 : m_value(move(ratio)) 35 { 36 } 37 38 explicit MediaFeatureValue(Resolution resolution) 39 : m_value(move(resolution)) 40 { 41 } 42 43 explicit MediaFeatureValue(float number) 44 : m_value(number) 45 { 46 } 47 48 ErrorOr<String> to_string() const; 49 50 bool is_ident() const { return m_value.has<ValueID>(); } 51 bool is_length() const { return m_value.has<Length>(); } 52 bool is_number() const { return m_value.has<float>(); } 53 bool is_ratio() const { return m_value.has<Ratio>(); } 54 bool is_resolution() const { return m_value.has<Resolution>(); } 55 bool is_same_type(MediaFeatureValue const& other) const; 56 57 ValueID const& ident() const 58 { 59 VERIFY(is_ident()); 60 return m_value.get<ValueID>(); 61 } 62 63 Length const& length() const 64 { 65 VERIFY(is_length()); 66 return m_value.get<Length>(); 67 } 68 69 Ratio const& ratio() const 70 { 71 VERIFY(is_ratio()); 72 return m_value.get<Ratio>(); 73 } 74 75 Resolution const& resolution() const 76 { 77 VERIFY(is_resolution()); 78 return m_value.get<Resolution>(); 79 } 80 81 float number() const 82 { 83 VERIFY(is_number()); 84 return m_value.get<float>(); 85 } 86 87private: 88 Variant<ValueID, Length, Ratio, Resolution, float> m_value; 89}; 90 91// https://www.w3.org/TR/mediaqueries-4/#mq-features 92class MediaFeature { 93public: 94 enum class Comparison { 95 Equal, 96 LessThan, 97 LessThanOrEqual, 98 GreaterThan, 99 GreaterThanOrEqual, 100 }; 101 102 // Corresponds to `<mf-boolean>` grammar 103 static MediaFeature boolean(MediaFeatureID id) 104 { 105 return MediaFeature(Type::IsTrue, id); 106 } 107 108 // Corresponds to `<mf-plain>` grammar 109 static MediaFeature plain(MediaFeatureID id, MediaFeatureValue value) 110 { 111 return MediaFeature(Type::ExactValue, move(id), move(value)); 112 } 113 static MediaFeature min(MediaFeatureID id, MediaFeatureValue value) 114 { 115 return MediaFeature(Type::MinValue, id, move(value)); 116 } 117 static MediaFeature max(MediaFeatureID id, MediaFeatureValue value) 118 { 119 return MediaFeature(Type::MaxValue, id, move(value)); 120 } 121 122 // Corresponds to `<mf-range>` grammar, with a single comparison 123 static MediaFeature half_range(MediaFeatureValue value, Comparison comparison, MediaFeatureID id) 124 { 125 MediaFeature feature { Type::Range, id }; 126 feature.m_range = Range { 127 .left_value = value, 128 .left_comparison = comparison, 129 }; 130 return feature; 131 } 132 133 // Corresponds to `<mf-range>` grammar, with two comparisons 134 static MediaFeature range(MediaFeatureValue left_value, Comparison left_comparison, MediaFeatureID id, Comparison right_comparison, MediaFeatureValue right_value) 135 { 136 MediaFeature feature { Type::Range, id }; 137 feature.m_range = Range { 138 .left_value = left_value, 139 .left_comparison = left_comparison, 140 .right_comparison = right_comparison, 141 .right_value = right_value, 142 }; 143 return feature; 144 } 145 146 bool evaluate(HTML::Window const&) const; 147 ErrorOr<String> to_string() const; 148 149private: 150 enum class Type { 151 IsTrue, 152 ExactValue, 153 MinValue, 154 MaxValue, 155 Range, 156 }; 157 158 MediaFeature(Type type, MediaFeatureID id, Optional<MediaFeatureValue> value = {}) 159 : m_type(type) 160 , m_id(move(id)) 161 , m_value(move(value)) 162 { 163 } 164 165 static bool compare(HTML::Window const& window, MediaFeatureValue left, Comparison comparison, MediaFeatureValue right); 166 167 struct Range { 168 MediaFeatureValue left_value; 169 Comparison left_comparison; 170 Optional<Comparison> right_comparison {}; 171 Optional<MediaFeatureValue> right_value {}; 172 }; 173 174 Type m_type; 175 MediaFeatureID m_id; 176 Optional<MediaFeatureValue> m_value {}; 177 Optional<Range> m_range {}; 178}; 179 180// https://www.w3.org/TR/mediaqueries-4/#media-conditions 181struct MediaCondition { 182 enum class Type { 183 Single, 184 And, 185 Or, 186 Not, 187 GeneralEnclosed, 188 }; 189 190 // Only used in parsing 191 enum class AllowOr { 192 No = 0, 193 Yes = 1, 194 }; 195 196 static NonnullOwnPtr<MediaCondition> from_general_enclosed(GeneralEnclosed&&); 197 static NonnullOwnPtr<MediaCondition> from_feature(MediaFeature&&); 198 static NonnullOwnPtr<MediaCondition> from_not(NonnullOwnPtr<MediaCondition>&&); 199 static NonnullOwnPtr<MediaCondition> from_and_list(Vector<NonnullOwnPtr<MediaCondition>>&&); 200 static NonnullOwnPtr<MediaCondition> from_or_list(Vector<NonnullOwnPtr<MediaCondition>>&&); 201 202 MatchResult evaluate(HTML::Window const&) const; 203 ErrorOr<String> to_string() const; 204 205private: 206 MediaCondition() = default; 207 Type type; 208 Optional<MediaFeature> feature; 209 Vector<NonnullOwnPtr<MediaCondition>> conditions; 210 Optional<GeneralEnclosed> general_enclosed; 211}; 212 213class MediaQuery : public RefCounted<MediaQuery> { 214 friend class Parser::Parser; 215 216public: 217 ~MediaQuery() = default; 218 219 // https://www.w3.org/TR/mediaqueries-4/#media-types 220 enum class MediaType { 221 All, 222 Print, 223 Screen, 224 Unknown, 225 226 // Deprecated, must never match: 227 TTY, 228 TV, 229 Projection, 230 Handheld, 231 Braille, 232 Embossed, 233 Aural, 234 Speech, 235 }; 236 237 static NonnullRefPtr<MediaQuery> create_not_all(); 238 static NonnullRefPtr<MediaQuery> create() { return adopt_ref(*new MediaQuery); } 239 240 bool matches() const { return m_matches; } 241 bool evaluate(HTML::Window const&); 242 ErrorOr<String> to_string() const; 243 244private: 245 MediaQuery() = default; 246 247 // https://www.w3.org/TR/mediaqueries-4/#mq-not 248 bool m_negated { false }; 249 MediaType m_media_type { MediaType::All }; 250 OwnPtr<MediaCondition> m_media_condition { nullptr }; 251 252 // Cached value, updated by evaluate() 253 bool m_matches { false }; 254}; 255 256ErrorOr<String> serialize_a_media_query_list(Vector<NonnullRefPtr<MediaQuery>> const&); 257 258bool is_media_feature_name(StringView name); 259 260MediaQuery::MediaType media_type_from_string(StringView); 261StringView to_string(MediaQuery::MediaType); 262 263} 264 265namespace AK { 266 267template<> 268struct Formatter<Web::CSS::MediaFeature> : Formatter<StringView> { 269 ErrorOr<void> format(FormatBuilder& builder, Web::CSS::MediaFeature const& media_feature) 270 { 271 return Formatter<StringView>::format(builder, TRY(media_feature.to_string())); 272 } 273}; 274 275template<> 276struct Formatter<Web::CSS::MediaCondition> : Formatter<StringView> { 277 ErrorOr<void> format(FormatBuilder& builder, Web::CSS::MediaCondition const& media_condition) 278 { 279 return Formatter<StringView>::format(builder, TRY(media_condition.to_string())); 280 } 281}; 282 283template<> 284struct Formatter<Web::CSS::MediaQuery> : Formatter<StringView> { 285 ErrorOr<void> format(FormatBuilder& builder, Web::CSS::MediaQuery const& media_query) 286 { 287 return Formatter<StringView>::format(builder, TRY(media_query.to_string())); 288 } 289}; 290 291}