1use crate::parser::ParserContext;
8use crate::values::computed::{self, CSSPixelLength, Ratio, Resolution};
9use crate::Atom;
10use cssparser::Parser;
11use selectors::kleene_value::KleeneValue;
12use std::fmt;
13use style_traits::ParseError;
14
15pub type KeywordDiscriminant = u8;
17
18type QueryFeatureGetter<T> = fn(device: &computed::Context) -> T;
19
20pub type KeywordSerializer = fn(KeywordDiscriminant) -> String;
25
26pub type KeywordParser = for<'a, 'i, 't> fn(
28 context: &'a ParserContext,
29 input: &'a mut Parser<'i, 't>,
30) -> Result<KeywordDiscriminant, ParseError<'i>>;
31
32#[allow(missing_docs)]
36pub enum Evaluator {
37 Length(QueryFeatureGetter<CSSPixelLength>),
38 OptionalLength(QueryFeatureGetter<Option<CSSPixelLength>>),
39 Integer(QueryFeatureGetter<i32>),
40 Float(QueryFeatureGetter<f32>),
41 BoolInteger(QueryFeatureGetter<bool>),
42 NumberRatio(QueryFeatureGetter<Ratio>),
44 OptionalNumberRatio(QueryFeatureGetter<Option<Ratio>>),
45 Resolution(QueryFeatureGetter<Resolution>),
47 Enumerated {
49 parser: KeywordParser,
51 serializer: KeywordSerializer,
56 evaluator: fn(&computed::Context, Option<KeywordDiscriminant>) -> KleeneValue,
59 },
60}
61
62macro_rules! keyword_evaluator {
68 ($actual_evaluator:ident, $keyword_type:ty) => {{
69 fn __parse<'i, 't>(
70 context: &$crate::parser::ParserContext,
71 input: &mut $crate::cssparser::Parser<'i, 't>,
72 ) -> Result<$crate::queries::feature::KeywordDiscriminant, ::style_traits::ParseError<'i>>
73 {
74 let kw = <$keyword_type as $crate::parser::Parse>::parse(context, input)?;
75 Ok(kw as $crate::queries::feature::KeywordDiscriminant)
76 }
77
78 fn __serialize(kw: $crate::queries::feature::KeywordDiscriminant) -> String {
79 let value: $keyword_type = ::num_traits::cast::FromPrimitive::from_u8(kw).unwrap();
82 <$keyword_type as ::style_traits::ToCss>::to_css_string(&value)
83 }
84
85 fn __evaluate(
86 context: &$crate::values::computed::Context,
87 value: Option<$crate::queries::feature::KeywordDiscriminant>,
88 ) -> selectors::kleene_value::KleeneValue {
89 let value: Option<$keyword_type> =
92 value.map(|kw| ::num_traits::cast::FromPrimitive::from_u8(kw).unwrap());
93 selectors::kleene_value::KleeneValue::from($actual_evaluator(context, value))
94 }
95
96 $crate::queries::feature::Evaluator::Enumerated {
97 parser: __parse,
98 serializer: __serialize,
99 evaluator: __evaluate,
100 }
101 }};
102}
103
104#[derive(Clone, Copy, Debug, ToShmem)]
107pub struct FeatureFlags(u8);
108bitflags! {
109 impl FeatureFlags : u8 {
110 const CHROME_AND_UA_ONLY = 1 << 0;
112 const WEBKIT_PREFIX = 1 << 1;
114 const CONTAINER_REQUIRES_INLINE_AXIS = 1 << 2;
116 const CONTAINER_REQUIRES_BLOCK_AXIS = 1 << 3;
118 const CONTAINER_REQUIRES_WIDTH_AXIS = 1 << 4;
120 const CONTAINER_REQUIRES_HEIGHT_AXIS = 1 << 5;
122 const VIEWPORT_DEPENDENT = 1 << 6;
124 const STYLE = 1 << 7;
126 }
127}
128
129impl FeatureFlags {
130 pub fn parsing_requirements(self) -> Self {
132 self.intersection(Self::CHROME_AND_UA_ONLY | Self::WEBKIT_PREFIX)
133 }
134
135 pub fn all_container_axes() -> Self {
137 Self::CONTAINER_REQUIRES_INLINE_AXIS
138 | Self::CONTAINER_REQUIRES_BLOCK_AXIS
139 | Self::CONTAINER_REQUIRES_WIDTH_AXIS
140 | Self::CONTAINER_REQUIRES_HEIGHT_AXIS
141 }
142
143 pub fn container_axes(self) -> Self {
145 self.intersection(Self::all_container_axes())
146 }
147}
148
149#[derive(Clone, Copy, Debug, Eq, PartialEq)]
151#[allow(missing_docs)]
152pub enum AllowsRanges {
153 Yes,
154 No,
155}
156
157pub struct QueryFeatureDescription {
159 pub name: Atom,
161 pub allows_ranges: AllowsRanges,
163 pub evaluator: Evaluator,
166 pub flags: FeatureFlags,
168}
169
170impl QueryFeatureDescription {
171 #[inline]
173 pub fn allows_ranges(&self) -> bool {
174 self.allows_ranges == AllowsRanges::Yes
175 }
176}
177
178macro_rules! feature {
180 ($name:expr, $allows_ranges:expr, $evaluator:expr, $flags:expr,) => {
181 $crate::queries::feature::QueryFeatureDescription {
182 name: $name,
183 allows_ranges: $allows_ranges,
184 evaluator: $evaluator,
185 flags: $flags,
186 }
187 };
188}
189
190impl fmt::Debug for QueryFeatureDescription {
191 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
192 f.debug_struct("QueryFeatureDescription")
193 .field("name", &self.name)
194 .field("allows_ranges", &self.allows_ranges)
195 .field("flags", &self.flags)
196 .finish()
197 }
198}