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,6 +7,7 @@
|
||||||
use crate::gecko_bindings::bindings;
|
use crate::gecko_bindings::bindings;
|
||||||
use crate::gecko_bindings::structs;
|
use crate::gecko_bindings::structs;
|
||||||
use crate::queries::feature::{AllowsRanges, Evaluator, ParsingRequirements, QueryFeatureDescription};
|
use crate::queries::feature::{AllowsRanges, Evaluator, ParsingRequirements, QueryFeatureDescription};
|
||||||
|
use crate::queries::values::Orientation;
|
||||||
use crate::media_queries::{Device, MediaType};
|
use crate::media_queries::{Device, MediaType};
|
||||||
use crate::values::computed::{Context, CSSPixelLength, Ratio, Resolution};
|
use crate::values::computed::{Context, CSSPixelLength, Ratio, Resolution};
|
||||||
use app_units::Au;
|
use app_units::Au;
|
||||||
|
@ -64,40 +65,14 @@ fn eval_device_pixel_ratio(context: &Context) -> f32 {
|
||||||
eval_resolution(context).dppx()
|
eval_resolution(context).dppx()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, FromPrimitive, Parse, ToCss)]
|
|
||||||
#[repr(u8)]
|
|
||||||
enum Orientation {
|
|
||||||
Landscape,
|
|
||||||
Portrait,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn eval_orientation_for<F>(context: &Context, value: Option<Orientation>, get_size: F) -> bool
|
|
||||||
where
|
|
||||||
F: FnOnce(&Device) -> Size2D<Au>,
|
|
||||||
{
|
|
||||||
let query_orientation = match value {
|
|
||||||
Some(v) => v,
|
|
||||||
None => return true,
|
|
||||||
};
|
|
||||||
|
|
||||||
let size = get_size(context.device());
|
|
||||||
|
|
||||||
// Per spec, square viewports should be 'portrait'
|
|
||||||
let is_landscape = size.width > size.height;
|
|
||||||
match query_orientation {
|
|
||||||
Orientation::Landscape => is_landscape,
|
|
||||||
Orientation::Portrait => !is_landscape,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// https://drafts.csswg.org/mediaqueries-4/#orientation
|
/// https://drafts.csswg.org/mediaqueries-4/#orientation
|
||||||
fn eval_orientation(context: &Context, value: Option<Orientation>) -> bool {
|
fn eval_orientation(context: &Context, value: Option<Orientation>) -> bool {
|
||||||
eval_orientation_for(context, value, Device::au_viewport_size)
|
Orientation::eval(context.device().au_viewport_size(), value)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// FIXME: There's no spec for `-moz-device-orientation`.
|
/// FIXME: There's no spec for `-moz-device-orientation`.
|
||||||
fn eval_device_orientation(context: &Context, value: Option<Orientation>) -> bool {
|
fn eval_device_orientation(context: &Context, value: Option<Orientation>) -> bool {
|
||||||
eval_orientation_for(context, value, device_size)
|
Orientation::eval(device_size(context.device()), value)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Values for the display-mode media feature.
|
/// Values for the display-mode media feature.
|
||||||
|
|
|
@ -872,10 +872,10 @@ impl<T> LogicalMargin<T> {
|
||||||
inline_start: T,
|
inline_start: T,
|
||||||
) -> LogicalMargin<T> {
|
) -> LogicalMargin<T> {
|
||||||
LogicalMargin {
|
LogicalMargin {
|
||||||
block_start: block_start,
|
block_start,
|
||||||
inline_end: inline_end,
|
inline_end,
|
||||||
block_end: block_end,
|
block_end,
|
||||||
inline_start: inline_start,
|
inline_start,
|
||||||
debug_writing_mode: DebugWritingMode::new(mode),
|
debug_writing_mode: DebugWritingMode::new(mode),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
//!
|
//!
|
||||||
//! https://drafts.csswg.org/mediaqueries/#typedef-media-query
|
//! https://drafts.csswg.org/mediaqueries/#typedef-media-query
|
||||||
|
|
||||||
use crate::queries::QueryCondition;
|
use crate::queries::{QueryCondition, FeatureType};
|
||||||
use crate::parser::ParserContext;
|
use crate::parser::ParserContext;
|
||||||
use crate::str::string_as_ascii_lowercase;
|
use crate::str::string_as_ascii_lowercase;
|
||||||
use crate::values::CustomIdent;
|
use crate::values::CustomIdent;
|
||||||
|
@ -134,9 +134,9 @@ impl MediaQuery {
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
let condition = if explicit_media_type.is_none() {
|
let condition = if explicit_media_type.is_none() {
|
||||||
Some(QueryCondition::parse(context, input)?)
|
Some(QueryCondition::parse(context, input, FeatureType::Media)?)
|
||||||
} else if input.try_parse(|i| i.expect_ident_matching("and")).is_ok() {
|
} else if input.try_parse(|i| i.expect_ident_matching("and")).is_ok() {
|
||||||
Some(QueryCondition::parse_disallow_or(context, input)?)
|
Some(QueryCondition::parse_disallow_or(context, input, FeatureType::Media)?)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
//! https://drafts.csswg.org/mediaqueries-4/#typedef-media-condition
|
//! https://drafts.csswg.org/mediaqueries-4/#typedef-media-condition
|
||||||
//! https://drafts.csswg.org/css-contain-3/#typedef-container-condition
|
//! https://drafts.csswg.org/css-contain-3/#typedef-container-condition
|
||||||
|
|
||||||
use super::QueryFeatureExpression;
|
use super::{QueryFeatureExpression, FeatureType};
|
||||||
use crate::parser::ParserContext;
|
use crate::parser::ParserContext;
|
||||||
use crate::values::computed;
|
use crate::values::computed;
|
||||||
use cssparser::{Parser, Token};
|
use cssparser::{Parser, Token};
|
||||||
|
@ -80,8 +80,9 @@ impl QueryCondition {
|
||||||
pub fn parse<'i, 't>(
|
pub fn parse<'i, 't>(
|
||||||
context: &ParserContext,
|
context: &ParserContext,
|
||||||
input: &mut Parser<'i, 't>,
|
input: &mut Parser<'i, 't>,
|
||||||
|
feature_type: FeatureType,
|
||||||
) -> Result<Self, ParseError<'i>> {
|
) -> 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.
|
/// Parse a single condition, disallowing `or` expressions.
|
||||||
|
@ -90,13 +91,15 @@ impl QueryCondition {
|
||||||
pub fn parse_disallow_or<'i, 't>(
|
pub fn parse_disallow_or<'i, 't>(
|
||||||
context: &ParserContext,
|
context: &ParserContext,
|
||||||
input: &mut Parser<'i, 't>,
|
input: &mut Parser<'i, 't>,
|
||||||
|
feature_type: FeatureType,
|
||||||
) -> Result<Self, ParseError<'i>> {
|
) -> 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>(
|
fn parse_internal<'i, 't>(
|
||||||
context: &ParserContext,
|
context: &ParserContext,
|
||||||
input: &mut Parser<'i, 't>,
|
input: &mut Parser<'i, 't>,
|
||||||
|
feature_type: FeatureType,
|
||||||
allow_or: AllowOr,
|
allow_or: AllowOr,
|
||||||
) -> Result<Self, ParseError<'i>> {
|
) -> Result<Self, ParseError<'i>> {
|
||||||
let location = input.current_source_location();
|
let location = input.current_source_location();
|
||||||
|
@ -109,12 +112,12 @@ impl QueryCondition {
|
||||||
};
|
};
|
||||||
|
|
||||||
if is_negation {
|
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)));
|
return Ok(QueryCondition::Not(Box::new(inner_condition)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParenthesisBlock.
|
// 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) {
|
let operator = match input.try_parse(Operator::parse) {
|
||||||
Ok(op) => op,
|
Ok(op) => op,
|
||||||
Err(..) => return Ok(first_condition),
|
Err(..) => return Ok(first_condition),
|
||||||
|
@ -126,7 +129,7 @@ impl QueryCondition {
|
||||||
|
|
||||||
let mut conditions = vec![];
|
let mut conditions = vec![];
|
||||||
conditions.push(first_condition);
|
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 {
|
let delim = match operator {
|
||||||
Operator::And => "and",
|
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>(
|
pub fn parse_in_parens<'i, 't>(
|
||||||
context: &ParserContext,
|
context: &ParserContext,
|
||||||
input: &mut Parser<'i, 't>,
|
input: &mut Parser<'i, 't>,
|
||||||
|
feature_type: FeatureType,
|
||||||
) -> Result<Self, ParseError<'i>> {
|
) -> Result<Self, ParseError<'i>> {
|
||||||
input.expect_parenthesis_block()?;
|
input.expect_parenthesis_block()?;
|
||||||
Self::parse_paren_block(context, input)
|
Self::parse_paren_block(context, input, feature_type)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_paren_block<'i, 't>(
|
fn parse_paren_block<'i, 't>(
|
||||||
context: &ParserContext,
|
context: &ParserContext,
|
||||||
input: &mut Parser<'i, 't>,
|
input: &mut Parser<'i, 't>,
|
||||||
|
feature_type: FeatureType,
|
||||||
) -> Result<Self, ParseError<'i>> {
|
) -> Result<Self, ParseError<'i>> {
|
||||||
input.parse_nested_block(|input| {
|
input.parse_nested_block(|input| {
|
||||||
// Base case.
|
// 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)));
|
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))
|
Ok(QueryCondition::Feature(expr))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,11 +7,7 @@
|
||||||
|
|
||||||
use super::feature::{Evaluator, QueryFeatureDescription};
|
use super::feature::{Evaluator, QueryFeatureDescription};
|
||||||
use super::feature::{KeywordDiscriminant, ParsingRequirements};
|
use super::feature::{KeywordDiscriminant, ParsingRequirements};
|
||||||
#[cfg(feature = "gecko")]
|
|
||||||
use crate::gecko::media_features::MEDIA_FEATURES;
|
|
||||||
use crate::parser::{Parse, ParserContext};
|
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::str::{starts_with_ignore_ascii_case, string_as_ascii_lowercase};
|
||||||
use crate::values::computed::{self, Ratio, ToComputedValue};
|
use crate::values::computed::{self, Ratio, ToComputedValue};
|
||||||
use crate::values::specified::{Integer, Length, Number, Resolution};
|
use crate::values::specified::{Integer, Length, Number, Resolution};
|
||||||
|
@ -22,6 +18,36 @@ use std::cmp::{Ordering, PartialOrd};
|
||||||
use std::fmt::{self, Write};
|
use std::fmt::{self, Write};
|
||||||
use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
|
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.
|
/// The kind of matching that should be performed on a feature value.
|
||||||
#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToShmem)]
|
#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq, ToShmem)]
|
||||||
pub enum Range {
|
pub enum Range {
|
||||||
|
@ -124,6 +150,7 @@ impl RangeOrOperator {
|
||||||
/// query contained, and the range to evaluate.
|
/// query contained, and the range to evaluate.
|
||||||
#[derive(Clone, Debug, MallocSizeOf, ToShmem, PartialEq)]
|
#[derive(Clone, Debug, MallocSizeOf, ToShmem, PartialEq)]
|
||||||
pub struct QueryFeatureExpression {
|
pub struct QueryFeatureExpression {
|
||||||
|
feature_type: FeatureType,
|
||||||
feature_index: usize,
|
feature_index: usize,
|
||||||
value: Option<QueryExpressionValue>,
|
value: Option<QueryExpressionValue>,
|
||||||
range_or_operator: Option<RangeOrOperator>,
|
range_or_operator: Option<RangeOrOperator>,
|
||||||
|
@ -244,12 +271,14 @@ fn disabled_by_pref(feature: &Atom, context: &ParserContext) -> bool {
|
||||||
|
|
||||||
impl QueryFeatureExpression {
|
impl QueryFeatureExpression {
|
||||||
fn new(
|
fn new(
|
||||||
|
feature_type: FeatureType,
|
||||||
feature_index: usize,
|
feature_index: usize,
|
||||||
value: Option<QueryExpressionValue>,
|
value: Option<QueryExpressionValue>,
|
||||||
range_or_operator: Option<RangeOrOperator>,
|
range_or_operator: Option<RangeOrOperator>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
debug_assert!(feature_index < MEDIA_FEATURES.len());
|
debug_assert!(feature_index < feature_type.features().len());
|
||||||
Self {
|
Self {
|
||||||
|
feature_type,
|
||||||
feature_index,
|
feature_index,
|
||||||
value,
|
value,
|
||||||
range_or_operator,
|
range_or_operator,
|
||||||
|
@ -257,7 +286,7 @@ impl QueryFeatureExpression {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn feature(&self) -> &'static QueryFeatureDescription {
|
fn feature(&self) -> &'static QueryFeatureDescription {
|
||||||
&MEDIA_FEATURES[self.feature_index]
|
&self.feature_type.features()[self.feature_index]
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a feature expression of the form:
|
/// Parse a feature expression of the form:
|
||||||
|
@ -268,15 +297,17 @@ impl QueryFeatureExpression {
|
||||||
pub fn parse<'i, 't>(
|
pub fn parse<'i, 't>(
|
||||||
context: &ParserContext,
|
context: &ParserContext,
|
||||||
input: &mut Parser<'i, 't>,
|
input: &mut Parser<'i, 't>,
|
||||||
|
feature_type: FeatureType,
|
||||||
) -> Result<Self, ParseError<'i>> {
|
) -> Result<Self, ParseError<'i>> {
|
||||||
input.expect_parenthesis_block()?;
|
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.
|
/// Parse a feature expression where we've already consumed the parenthesis.
|
||||||
pub fn parse_in_parenthesis_block<'i, 't>(
|
pub fn parse_in_parenthesis_block<'i, 't>(
|
||||||
context: &ParserContext,
|
context: &ParserContext,
|
||||||
input: &mut Parser<'i, 't>,
|
input: &mut Parser<'i, 't>,
|
||||||
|
feature_type: FeatureType,
|
||||||
) -> Result<Self, ParseError<'i>> {
|
) -> Result<Self, ParseError<'i>> {
|
||||||
let mut requirements = ParsingRequirements::empty();
|
let mut requirements = ParsingRequirements::empty();
|
||||||
let location = input.current_source_location();
|
let location = input.current_source_location();
|
||||||
|
@ -305,11 +336,7 @@ impl QueryFeatureExpression {
|
||||||
|
|
||||||
let atom = Atom::from(string_as_ascii_lowercase(feature_name));
|
let atom = Atom::from(string_as_ascii_lowercase(feature_name));
|
||||||
|
|
||||||
let (feature_index, feature) = match MEDIA_FEATURES
|
let (feature_index, feature) = match feature_type.find_feature(&atom) {
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.find(|(_, f)| f.name == atom)
|
|
||||||
{
|
|
||||||
Some((i, f)) => (i, f),
|
Some((i, f)) => (i, f),
|
||||||
None => {
|
None => {
|
||||||
return Err(location.new_custom_error(
|
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,
|
Ok(operator) => operator,
|
||||||
};
|
};
|
||||||
|
@ -372,7 +399,7 @@ impl QueryFeatureExpression {
|
||||||
.new_custom_error(StyleParseErrorKind::MediaQueryExpectedFeatureValue)
|
.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.
|
/// Returns whether this query evaluates to true for the given device.
|
||||||
|
|
|
@ -12,6 +12,7 @@ mod condition;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
pub mod feature;
|
pub mod feature;
|
||||||
pub mod feature_expression;
|
pub mod feature_expression;
|
||||||
|
pub mod values;
|
||||||
|
|
||||||
pub use self::condition::QueryCondition;
|
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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,14 +6,20 @@
|
||||||
//!
|
//!
|
||||||
//! [container]: https://drafts.csswg.org/css-contain-3/#container-rule
|
//! [container]: https://drafts.csswg.org/css-contain-3/#container-rule
|
||||||
|
|
||||||
|
use crate::logical_geometry::{WritingMode, LogicalSize};
|
||||||
use crate::queries::QueryCondition;
|
use crate::queries::QueryCondition;
|
||||||
use crate::shared_lock::{
|
use crate::shared_lock::{
|
||||||
DeepCloneParams, DeepCloneWithLock, Locked, SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard,
|
DeepCloneParams, DeepCloneWithLock, Locked, SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard,
|
||||||
};
|
};
|
||||||
use crate::values::specified::ContainerName;
|
use crate::values::specified::ContainerName;
|
||||||
|
use crate::values::computed::{Context, CSSPixelLength, Ratio};
|
||||||
use crate::str::CssStringWriter;
|
use crate::str::CssStringWriter;
|
||||||
use crate::stylesheets::CssRules;
|
use crate::stylesheets::CssRules;
|
||||||
|
use crate::queries::feature::{AllowsRanges, Evaluator, ParsingRequirements, QueryFeatureDescription};
|
||||||
|
use crate::queries::values::Orientation;
|
||||||
|
use app_units::Au;
|
||||||
use cssparser::SourceLocation;
|
use cssparser::SourceLocation;
|
||||||
|
use euclid::default::Size2D;
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
use malloc_size_of::{MallocSizeOfOps, MallocUnconditionalShallowSizeOf};
|
use malloc_size_of::{MallocSizeOfOps, MallocUnconditionalShallowSizeOf};
|
||||||
use servo_arc::Arc;
|
use servo_arc::Arc;
|
||||||
|
@ -77,3 +83,79 @@ impl ToCssWithGuard for ContainerRule {
|
||||||
|
|
||||||
/// TODO: Factor out the media query code to work with containers.
|
/// TODO: Factor out the media query code to work with containers.
|
||||||
pub type ContainerCondition = QueryCondition;
|
pub type ContainerCondition = QueryCondition;
|
||||||
|
|
||||||
|
fn get_container(_context: &Context) -> (Size2D<Au>, WritingMode) {
|
||||||
|
unimplemented!("TODO: implement container matching");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eval_width(context: &Context) -> CSSPixelLength {
|
||||||
|
let (size, _wm) = get_container(context);
|
||||||
|
CSSPixelLength::new(size.width.to_f32_px())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eval_height(context: &Context) -> CSSPixelLength {
|
||||||
|
let (size, _wm) = get_container(context);
|
||||||
|
CSSPixelLength::new(size.height.to_f32_px())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eval_inline_size(context: &Context) -> CSSPixelLength {
|
||||||
|
let (size, wm) = get_container(context);
|
||||||
|
CSSPixelLength::new(LogicalSize::from_physical(wm, size).inline.to_f32_px())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eval_block_size(context: &Context) -> CSSPixelLength {
|
||||||
|
let (size, wm) = get_container(context);
|
||||||
|
CSSPixelLength::new(LogicalSize::from_physical(wm, size).block.to_f32_px())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eval_aspect_ratio(context: &Context) -> Ratio {
|
||||||
|
let (size, _wm) = get_container(context);
|
||||||
|
Ratio::new(size.width.0 as f32, size.height.0 as f32)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eval_orientation(context: &Context, value: Option<Orientation>) -> bool {
|
||||||
|
let (size, _wm) = get_container(context);
|
||||||
|
Orientation::eval(size, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// https://drafts.csswg.org/css-contain-3/#container-features
|
||||||
|
///
|
||||||
|
/// TODO: Support style queries, perhaps.
|
||||||
|
pub static CONTAINER_FEATURES: [QueryFeatureDescription; 6] = [
|
||||||
|
feature!(
|
||||||
|
atom!("width"),
|
||||||
|
AllowsRanges::Yes,
|
||||||
|
Evaluator::Length(eval_width),
|
||||||
|
ParsingRequirements::empty(),
|
||||||
|
),
|
||||||
|
feature!(
|
||||||
|
atom!("height"),
|
||||||
|
AllowsRanges::Yes,
|
||||||
|
Evaluator::Length(eval_height),
|
||||||
|
ParsingRequirements::empty(),
|
||||||
|
),
|
||||||
|
feature!(
|
||||||
|
atom!("inline-size"),
|
||||||
|
AllowsRanges::Yes,
|
||||||
|
Evaluator::Length(eval_inline_size),
|
||||||
|
ParsingRequirements::empty(),
|
||||||
|
),
|
||||||
|
feature!(
|
||||||
|
atom!("block-size"),
|
||||||
|
AllowsRanges::Yes,
|
||||||
|
Evaluator::Length(eval_block_size),
|
||||||
|
ParsingRequirements::empty(),
|
||||||
|
),
|
||||||
|
feature!(
|
||||||
|
atom!("aspect-ratio"),
|
||||||
|
AllowsRanges::Yes,
|
||||||
|
Evaluator::NumberRatio(eval_aspect_ratio),
|
||||||
|
ParsingRequirements::empty(),
|
||||||
|
),
|
||||||
|
feature!(
|
||||||
|
atom!("orientation"),
|
||||||
|
AllowsRanges::No,
|
||||||
|
keyword_evaluator!(eval_orientation, Orientation),
|
||||||
|
ParsingRequirements::empty(),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
|
@ -10,6 +10,7 @@ use crate::font_face::parse_font_face_block;
|
||||||
use crate::media_queries::MediaList;
|
use crate::media_queries::MediaList;
|
||||||
use crate::parser::{Parse, ParserContext};
|
use crate::parser::{Parse, ParserContext};
|
||||||
use crate::properties::parse_property_declaration_list;
|
use crate::properties::parse_property_declaration_list;
|
||||||
|
use crate::queries::FeatureType;
|
||||||
use crate::selector_parser::{SelectorImpl, SelectorParser};
|
use crate::selector_parser::{SelectorImpl, SelectorParser};
|
||||||
use crate::shared_lock::{Locked, SharedRwLock};
|
use crate::shared_lock::{Locked, SharedRwLock};
|
||||||
use crate::str::starts_with_ignore_ascii_case;
|
use crate::str::starts_with_ignore_ascii_case;
|
||||||
|
@ -450,7 +451,7 @@ impl<'a, 'b, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'b> {
|
||||||
let name = input.try_parse(|input| {
|
let name = input.try_parse(|input| {
|
||||||
ContainerName::parse(self.context, input)
|
ContainerName::parse(self.context, input)
|
||||||
}).ok().unwrap_or_else(ContainerName::none);
|
}).ok().unwrap_or_else(ContainerName::none);
|
||||||
let condition = ContainerCondition::parse(self.context, input)?;
|
let condition = ContainerCondition::parse(self.context, input, FeatureType::Container)?;
|
||||||
AtRulePrelude::Container(name, condition)
|
AtRulePrelude::Container(name, condition)
|
||||||
},
|
},
|
||||||
"layer" => {
|
"layer" => {
|
||||||
|
|
|
@ -206,7 +206,6 @@ impl Default for ScrollDirection {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Avoid name collision in cbindgen with StyleOrientation.
|
|
||||||
pub use self::ScrollDirection as Orientation;
|
pub use self::ScrollDirection as Orientation;
|
||||||
|
|
||||||
/// Scroll-timeline offsets. We treat None as an empty vector.
|
/// Scroll-timeline offsets. We treat None as an empty vector.
|
||||||
|
|
|
@ -72,7 +72,7 @@ pub use self::list::ListStyleType;
|
||||||
pub use self::list::Quotes;
|
pub use self::list::Quotes;
|
||||||
pub use self::motion::{OffsetPath, OffsetRotate};
|
pub use self::motion::{OffsetPath, OffsetRotate};
|
||||||
pub use self::outline::OutlineStyle;
|
pub use self::outline::OutlineStyle;
|
||||||
pub use self::page::{Orientation, PageName, PageSize, PaperSize};
|
pub use self::page::{PageOrientation, PageName, PageSize, PaperSize};
|
||||||
pub use self::percentage::{NonNegativePercentage, Percentage};
|
pub use self::percentage::{NonNegativePercentage, Percentage};
|
||||||
pub use self::position::AspectRatio;
|
pub use self::position::AspectRatio;
|
||||||
pub use self::position::{
|
pub use self::position::{
|
||||||
|
|
|
@ -11,7 +11,7 @@ use crate::values::generics::size::Size2D;
|
||||||
|
|
||||||
use crate::values::specified::page as specified;
|
use crate::values::specified::page as specified;
|
||||||
pub use generics::page::GenericPageSize;
|
pub use generics::page::GenericPageSize;
|
||||||
pub use generics::page::Orientation;
|
pub use generics::page::PageOrientation;
|
||||||
pub use generics::page::PaperSize;
|
pub use generics::page::PaperSize;
|
||||||
pub use specified::PageName;
|
pub use specified::PageName;
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ pub enum PageSize {
|
||||||
/// Specified size, paper size, or paper size and orientation.
|
/// Specified size, paper size, or paper size and orientation.
|
||||||
Size(Size2D<NonNegativeLength>),
|
Size(Size2D<NonNegativeLength>),
|
||||||
/// `landscape` or `portrait` value, no specified size.
|
/// `landscape` or `portrait` value, no specified size.
|
||||||
Orientation(Orientation),
|
Orientation(PageOrientation),
|
||||||
/// `auto` value
|
/// `auto` value
|
||||||
Auto,
|
Auto,
|
||||||
}
|
}
|
||||||
|
@ -37,11 +37,11 @@ impl ToComputedValue for specified::PageSize {
|
||||||
fn to_computed_value(&self, ctx: &Context) -> Self::ComputedValue {
|
fn to_computed_value(&self, ctx: &Context) -> Self::ComputedValue {
|
||||||
match &*self {
|
match &*self {
|
||||||
Self::Size(s) => PageSize::Size(s.to_computed_value(ctx)),
|
Self::Size(s) => PageSize::Size(s.to_computed_value(ctx)),
|
||||||
Self::PaperSize(p, Orientation::Landscape) => PageSize::Size(Size2D {
|
Self::PaperSize(p, PageOrientation::Landscape) => PageSize::Size(Size2D {
|
||||||
width: p.long_edge().to_computed_value(ctx),
|
width: p.long_edge().to_computed_value(ctx),
|
||||||
height: p.short_edge().to_computed_value(ctx),
|
height: p.short_edge().to_computed_value(ctx),
|
||||||
}),
|
}),
|
||||||
Self::PaperSize(p, Orientation::Portrait) => PageSize::Size(Size2D {
|
Self::PaperSize(p, PageOrientation::Portrait) => PageSize::Size(Size2D {
|
||||||
width: p.short_edge().to_computed_value(ctx),
|
width: p.short_edge().to_computed_value(ctx),
|
||||||
height: p.long_edge().to_computed_value(ctx),
|
height: p.long_edge().to_computed_value(ctx),
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -87,7 +87,7 @@ impl PaperSize {
|
||||||
ToShmem,
|
ToShmem,
|
||||||
)]
|
)]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
pub enum Orientation {
|
pub enum PageOrientation {
|
||||||
/// Portrait orientation
|
/// Portrait orientation
|
||||||
Portrait,
|
Portrait,
|
||||||
/// Landscape orientation
|
/// Landscape orientation
|
||||||
|
@ -95,8 +95,8 @@ pub enum Orientation {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn is_portrait(orientation: &Orientation) -> bool {
|
fn is_portrait(orientation: &PageOrientation) -> bool {
|
||||||
*orientation == Orientation::Portrait
|
*orientation == PageOrientation::Portrait
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Page size property
|
/// Page size property
|
||||||
|
@ -110,9 +110,9 @@ pub enum GenericPageSize<S> {
|
||||||
/// Page dimensions.
|
/// Page dimensions.
|
||||||
Size(S),
|
Size(S),
|
||||||
/// An orientation with no size.
|
/// An orientation with no size.
|
||||||
Orientation(Orientation),
|
Orientation(PageOrientation),
|
||||||
/// Paper size by name
|
/// Paper size by name
|
||||||
PaperSize(PaperSize, #[css(skip_if = "is_portrait")] Orientation),
|
PaperSize(PaperSize, #[css(skip_if = "is_portrait")] PageOrientation),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub use self::GenericPageSize as PageSize;
|
pub use self::GenericPageSize as PageSize;
|
||||||
|
|
|
@ -69,7 +69,7 @@ pub use self::list::ListStyleType;
|
||||||
pub use self::list::Quotes;
|
pub use self::list::Quotes;
|
||||||
pub use self::motion::{OffsetPath, OffsetRotate};
|
pub use self::motion::{OffsetPath, OffsetRotate};
|
||||||
pub use self::outline::OutlineStyle;
|
pub use self::outline::OutlineStyle;
|
||||||
pub use self::page::{Orientation, PageName, PageSize, PaperSize};
|
pub use self::page::{PageOrientation, PageName, PageSize, PaperSize};
|
||||||
pub use self::percentage::{NonNegativePercentage, Percentage};
|
pub use self::percentage::{NonNegativePercentage, Percentage};
|
||||||
pub use self::position::AspectRatio;
|
pub use self::position::AspectRatio;
|
||||||
pub use self::position::{
|
pub use self::position::{
|
||||||
|
|
|
@ -11,7 +11,7 @@ use crate::values::{generics, CustomIdent};
|
||||||
use cssparser::Parser;
|
use cssparser::Parser;
|
||||||
use style_traits::ParseError;
|
use style_traits::ParseError;
|
||||||
|
|
||||||
pub use generics::page::Orientation;
|
pub use generics::page::PageOrientation;
|
||||||
pub use generics::page::PaperSize;
|
pub use generics::page::PaperSize;
|
||||||
/// Specified value of the @page size descriptor
|
/// Specified value of the @page size descriptor
|
||||||
pub type PageSize = generics::page::PageSize<Size2D<NonNegativeLength>>;
|
pub type PageSize = generics::page::PageSize<Size2D<NonNegativeLength>>;
|
||||||
|
@ -24,12 +24,12 @@ impl Parse for PageSize {
|
||||||
// Try to parse as <page-size> [ <orientation> ]
|
// Try to parse as <page-size> [ <orientation> ]
|
||||||
if let Ok(paper_size) = input.try_parse(PaperSize::parse) {
|
if let Ok(paper_size) = input.try_parse(PaperSize::parse) {
|
||||||
let orientation = input
|
let orientation = input
|
||||||
.try_parse(Orientation::parse)
|
.try_parse(PageOrientation::parse)
|
||||||
.unwrap_or(Orientation::Portrait);
|
.unwrap_or(PageOrientation::Portrait);
|
||||||
return Ok(PageSize::PaperSize(paper_size, orientation));
|
return Ok(PageSize::PaperSize(paper_size, orientation));
|
||||||
}
|
}
|
||||||
// Try to parse as <orientation> [ <page-size> ]
|
// Try to parse as <orientation> [ <page-size> ]
|
||||||
if let Ok(orientation) = input.try_parse(Orientation::parse) {
|
if let Ok(orientation) = input.try_parse(PageOrientation::parse) {
|
||||||
if let Ok(paper_size) = input.try_parse(PaperSize::parse) {
|
if let Ok(paper_size) = input.try_parse(PaperSize::parse) {
|
||||||
return Ok(PageSize::PaperSize(paper_size, orientation));
|
return Ok(PageSize::PaperSize(paper_size, orientation));
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
use crate::gecko_bindings::sugar::ownership::{HasBoxFFI, HasFFI, HasSimpleFFI};
|
use crate::gecko_bindings::sugar::ownership::{HasBoxFFI, HasFFI, HasSimpleFFI};
|
||||||
use crate::media_queries::Device;
|
use crate::media_queries::Device;
|
||||||
use crate::parser::{Parse, ParserContext};
|
use crate::parser::{Parse, ParserContext};
|
||||||
use crate::queries::QueryCondition;
|
use crate::queries::{QueryCondition, FeatureType};
|
||||||
use crate::values::computed::{self, ToComputedValue};
|
use crate::values::computed::{self, ToComputedValue};
|
||||||
use crate::values::specified::{Length, NoCalcLength, ViewportPercentageLength};
|
use crate::values::specified::{Length, NoCalcLength, ViewportPercentageLength};
|
||||||
use app_units::Au;
|
use app_units::Au;
|
||||||
|
@ -30,7 +30,7 @@ impl Parse for SourceSize {
|
||||||
context: &ParserContext,
|
context: &ParserContext,
|
||||||
input: &mut Parser<'i, 't>,
|
input: &mut Parser<'i, 't>,
|
||||||
) -> Result<Self, ParseError<'i>> {
|
) -> Result<Self, ParseError<'i>> {
|
||||||
let condition = QueryCondition::parse(context, input)?;
|
let condition = QueryCondition::parse(context, input, FeatureType::Media)?;
|
||||||
let value = Length::parse_non_negative(context, input)?;
|
let value = Length::parse_non_negative(context, input)?;
|
||||||
Ok(Self { condition, value })
|
Ok(Self { condition, value })
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue