summaryrefslogtreecommitdiffstats
path: root/servo/components/style/queries/feature.rs
diff options
context:
space:
mode:
Diffstat (limited to 'servo/components/style/queries/feature.rs')
-rw-r--r--servo/components/style/queries/feature.rs195
1 files changed, 195 insertions, 0 deletions
diff --git a/servo/components/style/queries/feature.rs b/servo/components/style/queries/feature.rs
new file mode 100644
index 0000000000..e394d462df
--- /dev/null
+++ b/servo/components/style/queries/feature.rs
@@ -0,0 +1,195 @@
+/* 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 super::condition::KleeneValue;
+use crate::parser::ParserContext;
+use crate::values::computed::{self, CSSPixelLength, Ratio, Resolution};
+use crate::Atom;
+use cssparser::Parser;
+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>),
+ /// 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>,
+ ) -> $crate::queries::condition::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());
+ $crate::queries::condition::KleeneValue::from($actual_evaluator(context, value))
+ }
+
+ $crate::queries::feature::Evaluator::Enumerated {
+ parser: __parse,
+ serializer: __serialize,
+ evaluator: __evaluate,
+ }
+ }};
+}
+
+bitflags! {
+ /// Different flags or toggles that change how a expression is parsed or
+ /// evaluated.
+ #[derive(Clone, Copy, ToShmem)]
+ pub struct 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()
+ }
+}