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