feature.rs (7376B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 4 5 //! Query features. 6 7 use crate::derives::*; 8 use crate::parser::ParserContext; 9 use crate::values::computed::{self, CSSPixelLength, Ratio, Resolution}; 10 use crate::Atom; 11 use cssparser::Parser; 12 use selectors::kleene_value::KleeneValue; 13 use std::fmt; 14 use style_traits::ParseError; 15 16 /// A generic discriminant for an enum value. 17 pub type KeywordDiscriminant = u8; 18 19 type QueryFeatureGetter<T> = fn(device: &computed::Context) -> T; 20 21 /// Serializes a given discriminant. 22 /// 23 /// FIXME(emilio): we could prevent this allocation if the ToCss code would 24 /// generate a method for keywords to get the static string or something. 25 pub type KeywordSerializer = fn(KeywordDiscriminant) -> String; 26 27 /// Parses a given identifier. 28 pub type KeywordParser = for<'a, 'i, 't> fn( 29 context: &'a ParserContext, 30 input: &'a mut Parser<'i, 't>, 31 ) -> Result<KeywordDiscriminant, ParseError<'i>>; 32 33 /// An evaluator for a given feature. 34 /// 35 /// This determines the kind of values that get parsed, too. 36 #[allow(missing_docs)] 37 pub enum Evaluator { 38 Length(QueryFeatureGetter<CSSPixelLength>), 39 OptionalLength(QueryFeatureGetter<Option<CSSPixelLength>>), 40 Integer(QueryFeatureGetter<i32>), 41 Float(QueryFeatureGetter<f32>), 42 BoolInteger(QueryFeatureGetter<bool>), 43 /// A non-negative number ratio, such as the one from device-pixel-ratio. 44 NumberRatio(QueryFeatureGetter<Ratio>), 45 OptionalNumberRatio(QueryFeatureGetter<Option<Ratio>>), 46 /// A resolution. 47 Resolution(QueryFeatureGetter<Resolution>), 48 /// A keyword value. 49 Enumerated { 50 /// The parser to get a discriminant given a string. 51 parser: KeywordParser, 52 /// The serializer to get a string from a discriminant. 53 /// 54 /// This is guaranteed to be called with a keyword that `parser` has 55 /// produced. 56 serializer: KeywordSerializer, 57 /// The evaluator itself. This is guaranteed to be called with a 58 /// keyword that `parser` has produced. 59 evaluator: fn(&computed::Context, Option<KeywordDiscriminant>) -> KleeneValue, 60 }, 61 } 62 63 /// A simple helper macro to create a keyword evaluator. 64 /// 65 /// This assumes that keyword feature expressions don't accept ranges, and 66 /// asserts if that's not true. As of today there's nothing like that (does that 67 /// even make sense?). 68 macro_rules! keyword_evaluator { 69 ($actual_evaluator:ident, $keyword_type:ty) => {{ 70 fn __parse<'i, 't>( 71 context: &$crate::parser::ParserContext, 72 input: &mut $crate::cssparser::Parser<'i, 't>, 73 ) -> Result<$crate::queries::feature::KeywordDiscriminant, ::style_traits::ParseError<'i>> 74 { 75 let kw = <$keyword_type as $crate::parser::Parse>::parse(context, input)?; 76 Ok(kw as $crate::queries::feature::KeywordDiscriminant) 77 } 78 79 fn __serialize(kw: $crate::queries::feature::KeywordDiscriminant) -> String { 80 // This unwrap is ok because the only discriminants that get 81 // back to us is the ones that `parse` produces. 82 let value: $keyword_type = ::num_traits::cast::FromPrimitive::from_u8(kw).unwrap(); 83 <$keyword_type as ::style_traits::ToCss>::to_css_string(&value) 84 } 85 86 fn __evaluate( 87 context: &$crate::values::computed::Context, 88 value: Option<$crate::queries::feature::KeywordDiscriminant>, 89 ) -> selectors::kleene_value::KleeneValue { 90 // This unwrap is ok because the only discriminants that get 91 // back to us is the ones that `parse` produces. 92 let value: Option<$keyword_type> = 93 value.map(|kw| ::num_traits::cast::FromPrimitive::from_u8(kw).unwrap()); 94 selectors::kleene_value::KleeneValue::from($actual_evaluator(context, value)) 95 } 96 97 $crate::queries::feature::Evaluator::Enumerated { 98 parser: __parse, 99 serializer: __serialize, 100 evaluator: __evaluate, 101 } 102 }}; 103 } 104 105 /// Different flags or toggles that change how a expression is parsed or 106 /// evaluated. 107 #[derive(Clone, Copy, Debug, ToShmem)] 108 pub struct FeatureFlags(u8); 109 bitflags! { 110 impl FeatureFlags : u8 { 111 /// The feature should only be parsed in chrome and ua sheets. 112 const CHROME_AND_UA_ONLY = 1 << 0; 113 /// The feature requires a -webkit- prefix. 114 const WEBKIT_PREFIX = 1 << 1; 115 /// The feature requires the inline-axis containment. 116 const CONTAINER_REQUIRES_INLINE_AXIS = 1 << 2; 117 /// The feature requires the block-axis containment. 118 const CONTAINER_REQUIRES_BLOCK_AXIS = 1 << 3; 119 /// The feature requires containment in the physical width axis. 120 const CONTAINER_REQUIRES_WIDTH_AXIS = 1 << 4; 121 /// The feature requires containment in the physical height axis. 122 const CONTAINER_REQUIRES_HEIGHT_AXIS = 1 << 5; 123 /// The feature evaluation depends on the viewport size. 124 const VIEWPORT_DEPENDENT = 1 << 6; 125 /// The feature evaluation depends on style queries. 126 const STYLE = 1 << 7; 127 } 128 } 129 130 impl FeatureFlags { 131 /// Returns parsing requirement flags. 132 pub fn parsing_requirements(self) -> Self { 133 self.intersection(Self::CHROME_AND_UA_ONLY | Self::WEBKIT_PREFIX) 134 } 135 136 /// Returns all the container axis flags. 137 pub fn all_container_axes() -> Self { 138 Self::CONTAINER_REQUIRES_INLINE_AXIS 139 | Self::CONTAINER_REQUIRES_BLOCK_AXIS 140 | Self::CONTAINER_REQUIRES_WIDTH_AXIS 141 | Self::CONTAINER_REQUIRES_HEIGHT_AXIS 142 } 143 144 /// Returns our subset of container axis flags. 145 pub fn container_axes(self) -> Self { 146 self.intersection(Self::all_container_axes()) 147 } 148 } 149 150 /// Whether a feature allows ranges or not. 151 #[derive(Clone, Copy, Debug, Eq, PartialEq)] 152 #[allow(missing_docs)] 153 pub enum AllowsRanges { 154 Yes, 155 No, 156 } 157 158 /// A description of a feature. 159 pub struct QueryFeatureDescription { 160 /// The feature name, in ascii lowercase. 161 pub name: Atom, 162 /// Whether min- / max- prefixes are allowed or not. 163 pub allows_ranges: AllowsRanges, 164 /// The evaluator, which we also use to determine which kind of value to 165 /// parse. 166 pub evaluator: Evaluator, 167 /// Different feature-specific flags. 168 pub flags: FeatureFlags, 169 } 170 171 impl QueryFeatureDescription { 172 /// Whether this feature allows ranges. 173 #[inline] 174 pub fn allows_ranges(&self) -> bool { 175 self.allows_ranges == AllowsRanges::Yes 176 } 177 } 178 179 /// A simple helper to construct a `QueryFeatureDescription`. 180 macro_rules! feature { 181 ($name:expr, $allows_ranges:expr, $evaluator:expr, $flags:expr,) => { 182 $crate::queries::feature::QueryFeatureDescription { 183 name: $name, 184 allows_ranges: $allows_ranges, 185 evaluator: $evaluator, 186 flags: $flags, 187 } 188 }; 189 } 190 191 impl fmt::Debug for QueryFeatureDescription { 192 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 193 f.debug_struct("QueryFeatureDescription") 194 .field("name", &self.name) 195 .field("allows_ranges", &self.allows_ranges) 196 .field("flags", &self.flags) 197 .finish() 198 } 199 }