mirror of
https://github.com/servo/servo.git
synced 2025-08-03 12:40:06 +01:00
style: Add support for parsing container-query-specific features
There are some mediaqueries-5 features that we still don't support and explain the remaining failures in at-container-{parsing,serialization}. Differential Revision: https://phabricator.services.mozilla.com/D144446
This commit is contained in:
parent
989f8d89c4
commit
1003d644aa
16 changed files with 205 additions and 79 deletions
|
@ -7,7 +7,7 @@
|
|||
//! https://drafts.csswg.org/mediaqueries-4/#typedef-media-condition
|
||||
//! https://drafts.csswg.org/css-contain-3/#typedef-container-condition
|
||||
|
||||
use super::QueryFeatureExpression;
|
||||
use super::{QueryFeatureExpression, FeatureType};
|
||||
use crate::parser::ParserContext;
|
||||
use crate::values::computed;
|
||||
use cssparser::{Parser, Token};
|
||||
|
@ -80,8 +80,9 @@ impl QueryCondition {
|
|||
pub fn parse<'i, 't>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
feature_type: FeatureType,
|
||||
) -> Result<Self, ParseError<'i>> {
|
||||
Self::parse_internal(context, input, AllowOr::Yes)
|
||||
Self::parse_internal(context, input, feature_type, AllowOr::Yes)
|
||||
}
|
||||
|
||||
/// Parse a single condition, disallowing `or` expressions.
|
||||
|
@ -90,13 +91,15 @@ impl QueryCondition {
|
|||
pub fn parse_disallow_or<'i, 't>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
feature_type: FeatureType,
|
||||
) -> Result<Self, ParseError<'i>> {
|
||||
Self::parse_internal(context, input, AllowOr::No)
|
||||
Self::parse_internal(context, input, feature_type, AllowOr::No)
|
||||
}
|
||||
|
||||
fn parse_internal<'i, 't>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
feature_type: FeatureType,
|
||||
allow_or: AllowOr,
|
||||
) -> Result<Self, ParseError<'i>> {
|
||||
let location = input.current_source_location();
|
||||
|
@ -109,12 +112,12 @@ impl QueryCondition {
|
|||
};
|
||||
|
||||
if is_negation {
|
||||
let inner_condition = Self::parse_in_parens(context, input)?;
|
||||
let inner_condition = Self::parse_in_parens(context, input, feature_type)?;
|
||||
return Ok(QueryCondition::Not(Box::new(inner_condition)));
|
||||
}
|
||||
|
||||
// ParenthesisBlock.
|
||||
let first_condition = Self::parse_paren_block(context, input)?;
|
||||
let first_condition = Self::parse_paren_block(context, input, feature_type)?;
|
||||
let operator = match input.try_parse(Operator::parse) {
|
||||
Ok(op) => op,
|
||||
Err(..) => return Ok(first_condition),
|
||||
|
@ -126,7 +129,7 @@ impl QueryCondition {
|
|||
|
||||
let mut conditions = vec![];
|
||||
conditions.push(first_condition);
|
||||
conditions.push(Self::parse_in_parens(context, input)?);
|
||||
conditions.push(Self::parse_in_parens(context, input, feature_type)?);
|
||||
|
||||
let delim = match operator {
|
||||
Operator::And => "and",
|
||||
|
@ -141,7 +144,7 @@ impl QueryCondition {
|
|||
));
|
||||
}
|
||||
|
||||
conditions.push(Self::parse_in_parens(context, input)?);
|
||||
conditions.push(Self::parse_in_parens(context, input, feature_type)?);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -149,21 +152,23 @@ impl QueryCondition {
|
|||
pub fn parse_in_parens<'i, 't>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
feature_type: FeatureType,
|
||||
) -> Result<Self, ParseError<'i>> {
|
||||
input.expect_parenthesis_block()?;
|
||||
Self::parse_paren_block(context, input)
|
||||
Self::parse_paren_block(context, input, feature_type)
|
||||
}
|
||||
|
||||
fn parse_paren_block<'i, 't>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
feature_type: FeatureType,
|
||||
) -> Result<Self, ParseError<'i>> {
|
||||
input.parse_nested_block(|input| {
|
||||
// Base case.
|
||||
if let Ok(inner) = input.try_parse(|i| Self::parse(context, i)) {
|
||||
if let Ok(inner) = input.try_parse(|i| Self::parse(context, i, feature_type)) {
|
||||
return Ok(QueryCondition::InParens(Box::new(inner)));
|
||||
}
|
||||
let expr = QueryFeatureExpression::parse_in_parenthesis_block(context, input)?;
|
||||
let expr = QueryFeatureExpression::parse_in_parenthesis_block(context, input, feature_type)?;
|
||||
Ok(QueryCondition::Feature(expr))
|
||||
})
|
||||
}
|
||||
|
|
|
@ -7,11 +7,7 @@
|
|||
|
||||
use super::feature::{Evaluator, QueryFeatureDescription};
|
||||
use super::feature::{KeywordDiscriminant, ParsingRequirements};
|
||||
#[cfg(feature = "gecko")]
|
||||
use crate::gecko::media_features::MEDIA_FEATURES;
|
||||
use crate::parser::{Parse, ParserContext};
|
||||
#[cfg(feature = "servo")]
|
||||
use crate::servo::media_queries::MEDIA_FEATURES;
|
||||
use crate::str::{starts_with_ignore_ascii_case, string_as_ascii_lowercase};
|
||||
use crate::values::computed::{self, Ratio, ToComputedValue};
|
||||
use crate::values::specified::{Integer, Length, Number, Resolution};
|
||||
|
@ -22,6 +18,36 @@ use std::cmp::{Ordering, PartialOrd};
|
|||
use std::fmt::{self, Write};
|
||||
use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
|
||||
|
||||
/// Whether we're parsing a media or container query feature.
|
||||
#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToShmem)]
|
||||
pub enum FeatureType {
|
||||
/// We're parsing a media feature.
|
||||
Media,
|
||||
/// We're parsing a container feature.
|
||||
Container,
|
||||
}
|
||||
|
||||
impl FeatureType {
|
||||
fn features(&self) -> &'static [QueryFeatureDescription] {
|
||||
#[cfg(feature = "gecko")]
|
||||
use crate::gecko::media_features::MEDIA_FEATURES;
|
||||
#[cfg(feature = "servo")]
|
||||
use crate::servo::media_queries::MEDIA_FEATURES;
|
||||
|
||||
use crate::stylesheets::container_rule::CONTAINER_FEATURES;
|
||||
|
||||
match *self {
|
||||
FeatureType::Media => &MEDIA_FEATURES,
|
||||
FeatureType::Container => &CONTAINER_FEATURES,
|
||||
}
|
||||
}
|
||||
|
||||
fn find_feature(&self, name: &Atom) -> Option<(usize, &'static QueryFeatureDescription)> {
|
||||
self.features().iter().enumerate().find(|(_, f)| f.name == *name)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// The kind of matching that should be performed on a feature value.
|
||||
#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToShmem)]
|
||||
pub enum Range {
|
||||
|
@ -124,6 +150,7 @@ impl RangeOrOperator {
|
|||
/// query contained, and the range to evaluate.
|
||||
#[derive(Clone, Debug, MallocSizeOf, ToShmem, PartialEq)]
|
||||
pub struct QueryFeatureExpression {
|
||||
feature_type: FeatureType,
|
||||
feature_index: usize,
|
||||
value: Option<QueryExpressionValue>,
|
||||
range_or_operator: Option<RangeOrOperator>,
|
||||
|
@ -244,12 +271,14 @@ fn disabled_by_pref(feature: &Atom, context: &ParserContext) -> bool {
|
|||
|
||||
impl QueryFeatureExpression {
|
||||
fn new(
|
||||
feature_type: FeatureType,
|
||||
feature_index: usize,
|
||||
value: Option<QueryExpressionValue>,
|
||||
range_or_operator: Option<RangeOrOperator>,
|
||||
) -> Self {
|
||||
debug_assert!(feature_index < MEDIA_FEATURES.len());
|
||||
debug_assert!(feature_index < feature_type.features().len());
|
||||
Self {
|
||||
feature_type,
|
||||
feature_index,
|
||||
value,
|
||||
range_or_operator,
|
||||
|
@ -257,7 +286,7 @@ impl QueryFeatureExpression {
|
|||
}
|
||||
|
||||
fn feature(&self) -> &'static QueryFeatureDescription {
|
||||
&MEDIA_FEATURES[self.feature_index]
|
||||
&self.feature_type.features()[self.feature_index]
|
||||
}
|
||||
|
||||
/// Parse a feature expression of the form:
|
||||
|
@ -268,15 +297,17 @@ impl QueryFeatureExpression {
|
|||
pub fn parse<'i, 't>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
feature_type: FeatureType,
|
||||
) -> Result<Self, ParseError<'i>> {
|
||||
input.expect_parenthesis_block()?;
|
||||
input.parse_nested_block(|input| Self::parse_in_parenthesis_block(context, input))
|
||||
input.parse_nested_block(|input| Self::parse_in_parenthesis_block(context, input, feature_type))
|
||||
}
|
||||
|
||||
/// Parse a feature expression where we've already consumed the parenthesis.
|
||||
pub fn parse_in_parenthesis_block<'i, 't>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
feature_type: FeatureType,
|
||||
) -> Result<Self, ParseError<'i>> {
|
||||
let mut requirements = ParsingRequirements::empty();
|
||||
let location = input.current_source_location();
|
||||
|
@ -305,11 +336,7 @@ impl QueryFeatureExpression {
|
|||
|
||||
let atom = Atom::from(string_as_ascii_lowercase(feature_name));
|
||||
|
||||
let (feature_index, feature) = match MEDIA_FEATURES
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find(|(_, f)| f.name == atom)
|
||||
{
|
||||
let (feature_index, feature) = match feature_type.find_feature(&atom) {
|
||||
Some((i, f)) => (i, f),
|
||||
None => {
|
||||
return Err(location.new_custom_error(
|
||||
|
@ -341,7 +368,7 @@ impl QueryFeatureExpression {
|
|||
);
|
||||
}
|
||||
|
||||
return Ok(Self::new(feature_index, None, None));
|
||||
return Ok(Self::new(feature_type, feature_index, None, None));
|
||||
},
|
||||
Ok(operator) => operator,
|
||||
};
|
||||
|
@ -372,7 +399,7 @@ impl QueryFeatureExpression {
|
|||
.new_custom_error(StyleParseErrorKind::MediaQueryExpectedFeatureValue)
|
||||
})?;
|
||||
|
||||
Ok(Self::new(feature_index, Some(value), range_or_operator))
|
||||
Ok(Self::new(feature_type, feature_index, Some(value), range_or_operator))
|
||||
}
|
||||
|
||||
/// Returns whether this query evaluates to true for the given device.
|
||||
|
|
|
@ -12,6 +12,7 @@ mod condition;
|
|||
#[macro_use]
|
||||
pub mod feature;
|
||||
pub mod feature_expression;
|
||||
pub mod values;
|
||||
|
||||
pub use self::condition::QueryCondition;
|
||||
pub use self::feature_expression::QueryFeatureExpression;
|
||||
pub use self::feature_expression::{QueryFeatureExpression, FeatureType};
|
||||
|
|
36
components/style/queries/values.rs
Normal file
36
components/style/queries/values.rs
Normal file
|
@ -0,0 +1,36 @@
|
|||
/* 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/. */
|
||||
|
||||
//! Common feature values between media and container features.
|
||||
|
||||
/// The orientation media / container feature.
|
||||
/// https://drafts.csswg.org/mediaqueries-5/#orientation
|
||||
/// https://drafts.csswg.org/css-contain-3/#orientation
|
||||
#[derive(Clone, Copy, Debug, FromPrimitive, Parse, ToCss)]
|
||||
#[repr(u8)]
|
||||
#[allow(missing_docs)]
|
||||
pub enum Orientation {
|
||||
Portrait,
|
||||
Landscape,
|
||||
}
|
||||
|
||||
impl Orientation {
|
||||
/// A helper to evaluate a orientation query given a generic size getter.
|
||||
pub fn eval<T>(size: euclid::default::Size2D<T>, value: Option<Self>) -> bool
|
||||
where
|
||||
T: PartialOrd,
|
||||
{
|
||||
let query_orientation = match value {
|
||||
Some(v) => v,
|
||||
None => return true,
|
||||
};
|
||||
|
||||
// Per spec, square viewports should be 'portrait'
|
||||
let is_landscape = size.width > size.height;
|
||||
match query_orientation {
|
||||
Self::Landscape => is_landscape,
|
||||
Self::Portrait => !is_landscape,
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue