mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 06:12:43 +00:00 
			
		
		
		
	LibWeb: Make MediaFeature a top-level class and add factory methods
Web::CSS::MediaQuery::MediaFeature::Type was getting a bit ridiculous! Also, this moves the detection of "min-" and "max-" media-features into the MediaFeature itself, since this is an implementation detail, not part of the spec.
This commit is contained in:
		
							parent
							
								
									d470e7e817
								
							
						
					
					
						commit
						ae4f0000c8
					
				
					 4 changed files with 74 additions and 71 deletions
				
			
		|  | @ -59,30 +59,30 @@ bool MediaFeatureValue::equals(MediaFeatureValue const& other) const | |||
|     VERIFY_NOT_REACHED(); | ||||
| } | ||||
| 
 | ||||
| String MediaQuery::MediaFeature::to_string() const | ||||
| String MediaFeature::to_string() const | ||||
| { | ||||
|     switch (type) { | ||||
|     switch (m_type) { | ||||
|     case Type::IsTrue: | ||||
|         return name; | ||||
|         return m_name; | ||||
|     case Type::ExactValue: | ||||
|         return String::formatted("{}:{}", name, value->to_string()); | ||||
|         return String::formatted("{}:{}", m_name, m_value->to_string()); | ||||
|     case Type::MinValue: | ||||
|         return String::formatted("min-{}:{}", name, value->to_string()); | ||||
|         return String::formatted("min-{}:{}", m_name, m_value->to_string()); | ||||
|     case Type::MaxValue: | ||||
|         return String::formatted("max-{}:{}", name, value->to_string()); | ||||
|         return String::formatted("max-{}:{}", m_name, m_value->to_string()); | ||||
|     } | ||||
| 
 | ||||
|     VERIFY_NOT_REACHED(); | ||||
| } | ||||
| 
 | ||||
| bool MediaQuery::MediaFeature::evaluate(DOM::Window const& window) const | ||||
| bool MediaFeature::evaluate(DOM::Window const& window) const | ||||
| { | ||||
|     auto maybe_queried_value = window.query_media_feature(name); | ||||
|     auto maybe_queried_value = window.query_media_feature(m_name); | ||||
|     if (!maybe_queried_value.has_value()) | ||||
|         return false; | ||||
|     auto queried_value = maybe_queried_value.release_value(); | ||||
| 
 | ||||
|     switch (type) { | ||||
|     switch (m_type) { | ||||
|     case Type::IsTrue: | ||||
|         if (queried_value.is_number()) | ||||
|             return queried_value.number() != 0; | ||||
|  | @ -93,18 +93,18 @@ bool MediaQuery::MediaFeature::evaluate(DOM::Window const& window) const | |||
|         return false; | ||||
| 
 | ||||
|     case Type::ExactValue: | ||||
|         return queried_value.equals(*value); | ||||
|         return queried_value.equals(*m_value); | ||||
| 
 | ||||
|     case Type::MinValue: | ||||
|         if (!value->is_same_type(queried_value)) | ||||
|         if (!m_value->is_same_type(queried_value)) | ||||
|             return false; | ||||
| 
 | ||||
|         if (value->is_number()) | ||||
|             return queried_value.number() >= value->number(); | ||||
|         if (m_value->is_number()) | ||||
|             return queried_value.number() >= m_value->number(); | ||||
| 
 | ||||
|         if (value->is_length()) { | ||||
|         if (m_value->is_length()) { | ||||
|             auto& queried_length = queried_value.length(); | ||||
|             auto& value_length = value->length(); | ||||
|             auto& value_length = m_value->length(); | ||||
|             // FIXME: Handle relative lengths. https://www.w3.org/TR/mediaqueries-4/#ref-for-relative-length
 | ||||
|             if (!value_length.is_absolute()) { | ||||
|                 dbgln("Media feature was given a non-absolute length! {}", value_length.to_string()); | ||||
|  | @ -116,15 +116,15 @@ bool MediaQuery::MediaFeature::evaluate(DOM::Window const& window) const | |||
|         return false; | ||||
| 
 | ||||
|     case Type::MaxValue: | ||||
|         if (!value->is_same_type(queried_value)) | ||||
|         if (!m_value->is_same_type(queried_value)) | ||||
|             return false; | ||||
| 
 | ||||
|         if (value->is_number()) | ||||
|             return queried_value.number() <= value->number(); | ||||
|         if (m_value->is_number()) | ||||
|             return queried_value.number() <= m_value->number(); | ||||
| 
 | ||||
|         if (value->is_length()) { | ||||
|         if (m_value->is_length()) { | ||||
|             auto& queried_length = queried_value.length(); | ||||
|             auto& value_length = value->length(); | ||||
|             auto& value_length = m_value->length(); | ||||
|             // FIXME: Handle relative lengths. https://www.w3.org/TR/mediaqueries-4/#ref-for-relative-length
 | ||||
|             if (!value_length.is_absolute()) { | ||||
|                 dbgln("Media feature was given a non-absolute length! {}", value_length.to_string()); | ||||
|  | @ -145,7 +145,7 @@ String MediaQuery::MediaCondition::to_string() const | |||
|     builder.append('('); | ||||
|     switch (type) { | ||||
|     case Type::Single: | ||||
|         builder.append(feature.to_string()); | ||||
|         builder.append(feature->to_string()); | ||||
|         break; | ||||
|     case Type::Not: | ||||
|         builder.append("not "); | ||||
|  | @ -169,7 +169,7 @@ MatchResult MediaQuery::MediaCondition::evaluate(DOM::Window const& window) cons | |||
| { | ||||
|     switch (type) { | ||||
|     case Type::Single: | ||||
|         return as_match_result(feature.evaluate(window)); | ||||
|         return as_match_result(feature->evaluate(window)); | ||||
|     case Type::Not: | ||||
|         return negate(conditions.first().evaluate(window)); | ||||
|     case Type::And: | ||||
|  |  | |||
|  | @ -69,6 +69,49 @@ private: | |||
|     Variant<String, Length, double> m_value; | ||||
| }; | ||||
| 
 | ||||
| // https://www.w3.org/TR/mediaqueries-4/#mq-features
 | ||||
| class MediaFeature { | ||||
| public: | ||||
|     // Corresponds to `<mf-boolean>` grammar
 | ||||
|     static MediaFeature boolean(String const& name) | ||||
|     { | ||||
|         return MediaFeature(Type::IsTrue, name); | ||||
|     } | ||||
| 
 | ||||
|     // Corresponds to `<mf-plain>` 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<MediaFeatureValue> value = {}) | ||||
|         : m_type(type) | ||||
|         , m_name(move(name)) | ||||
|         , m_value(move(value)) | ||||
|     { | ||||
|     } | ||||
| 
 | ||||
|     Type m_type; | ||||
|     FlyString m_name; | ||||
|     Optional<MediaFeatureValue> m_value {}; | ||||
| }; | ||||
| 
 | ||||
| class MediaQuery : public RefCounted<MediaQuery> { | ||||
|     friend class Parser; | ||||
| 
 | ||||
|  | @ -92,24 +135,6 @@ public: | |||
|         Speech, | ||||
|     }; | ||||
| 
 | ||||
|     // https://www.w3.org/TR/mediaqueries-4/#mq-features
 | ||||
|     struct MediaFeature { | ||||
|         // FIXME: Implement range syntax: https://www.w3.org/TR/mediaqueries-4/#mq-ranges
 | ||||
|         enum class Type { | ||||
|             IsTrue, | ||||
|             ExactValue, | ||||
|             MinValue, | ||||
|             MaxValue, | ||||
|         }; | ||||
| 
 | ||||
|         Type type; | ||||
|         FlyString name; | ||||
|         Optional<MediaFeatureValue> value {}; | ||||
| 
 | ||||
|         bool evaluate(DOM::Window const&) const; | ||||
|         String to_string() const; | ||||
|     }; | ||||
| 
 | ||||
|     // https://www.w3.org/TR/mediaqueries-4/#media-conditions
 | ||||
|     struct MediaCondition { | ||||
|         enum class Type { | ||||
|  | @ -121,7 +146,7 @@ public: | |||
|         }; | ||||
| 
 | ||||
|         Type type; | ||||
|         MediaFeature feature; | ||||
|         Optional<MediaFeature> feature; | ||||
|         NonnullOwnPtrVector<MediaCondition> conditions; | ||||
|         Optional<GeneralEnclosed> general_enclosed; | ||||
| 
 | ||||
|  | @ -155,8 +180,8 @@ String serialize_a_media_query_list(NonnullRefPtrVector<MediaQuery> const&); | |||
| namespace AK { | ||||
| 
 | ||||
| template<> | ||||
| struct Formatter<Web::CSS::MediaQuery::MediaFeature> : Formatter<StringView> { | ||||
|     ErrorOr<void> format(FormatBuilder& builder, Web::CSS::MediaQuery::MediaFeature const& media_feature) | ||||
| 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()); | ||||
|     } | ||||
|  |  | |||
|  | @ -860,11 +860,11 @@ OwnPtr<MediaQuery::MediaCondition> Parser::consume_media_condition(TokenStream<S | |||
|     return {}; | ||||
| } | ||||
| 
 | ||||
| Optional<MediaQuery::MediaFeature> Parser::consume_media_feature(TokenStream<StyleComponentValueRule>& outer_tokens) | ||||
| Optional<MediaFeature> Parser::consume_media_feature(TokenStream<StyleComponentValueRule>& outer_tokens) | ||||
| { | ||||
|     outer_tokens.skip_whitespace(); | ||||
| 
 | ||||
|     auto invalid_feature = [&]() -> Optional<MediaQuery::MediaFeature> { | ||||
|     auto invalid_feature = [&]() -> Optional<MediaFeature> { | ||||
|         outer_tokens.reconsume_current_input_token(); | ||||
|         return {}; | ||||
|     }; | ||||
|  | @ -884,12 +884,8 @@ Optional<MediaQuery::MediaFeature> Parser::consume_media_feature(TokenStream<Sty | |||
|         auto feature_name = name_token.token().ident(); | ||||
|         tokens.skip_whitespace(); | ||||
| 
 | ||||
|         if (!tokens.has_next_token()) { | ||||
|             return MediaQuery::MediaFeature { | ||||
|                 .type = MediaQuery::MediaFeature::Type::IsTrue, | ||||
|                 .name = feature_name, | ||||
|             }; | ||||
|         } | ||||
|         if (!tokens.has_next_token()) | ||||
|             return MediaFeature::boolean(feature_name); | ||||
| 
 | ||||
|         if (!tokens.next_token().is(Token::Type::Colon)) | ||||
|             return invalid_feature(); | ||||
|  | @ -902,25 +898,7 @@ Optional<MediaQuery::MediaFeature> Parser::consume_media_feature(TokenStream<Sty | |||
|         if (tokens.has_next_token()) | ||||
|             return invalid_feature(); | ||||
| 
 | ||||
|         if (feature_name.starts_with("min-", CaseSensitivity::CaseInsensitive)) { | ||||
|             return MediaQuery::MediaFeature { | ||||
|                 .type = MediaQuery::MediaFeature::Type::MinValue, | ||||
|                 .name = feature_name.substring_view(4), | ||||
|                 .value = value.release_value(), | ||||
|             }; | ||||
|         } else if (feature_name.starts_with("max-", CaseSensitivity::CaseInsensitive)) { | ||||
|             return MediaQuery::MediaFeature { | ||||
|                 .type = MediaQuery::MediaFeature::Type::MaxValue, | ||||
|                 .name = feature_name.substring_view(4), | ||||
|                 .value = value.release_value(), | ||||
|             }; | ||||
|         } | ||||
| 
 | ||||
|         return MediaQuery::MediaFeature { | ||||
|             .type = MediaQuery::MediaFeature::Type::ExactValue, | ||||
|             .name = feature_name, | ||||
|             .value = value.release_value(), | ||||
|         }; | ||||
|         return MediaFeature::plain(feature_name, value.release_value()); | ||||
|     } | ||||
| 
 | ||||
|     return invalid_feature(); | ||||
|  |  | |||
|  | @ -236,7 +236,7 @@ private: | |||
| 
 | ||||
|     NonnullRefPtr<MediaQuery> parse_media_query(TokenStream<StyleComponentValueRule>&); | ||||
|     OwnPtr<MediaQuery::MediaCondition> consume_media_condition(TokenStream<StyleComponentValueRule>&); | ||||
|     Optional<MediaQuery::MediaFeature> consume_media_feature(TokenStream<StyleComponentValueRule>&); | ||||
|     Optional<MediaFeature> consume_media_feature(TokenStream<StyleComponentValueRule>&); | ||||
|     Optional<MediaQuery::MediaType> consume_media_type(TokenStream<StyleComponentValueRule>&); | ||||
|     Optional<MediaFeatureValue> parse_media_feature_value(TokenStream<StyleComponentValueRule>&); | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Sam Atkins
						Sam Atkins