style: More container queries plumbing

Provide container information in computed::Context and use it to resolve
the container queries.

This still fails a lot of tests because we are not ensuring that layout
is up-to-date when we style the container descendants, but that's
expected.

Differential Revision: https://phabricator.services.mozilla.com/D146478
This commit is contained in:
Emilio Cobos Álvarez 2023-08-14 23:29:47 +02:00 committed by Martin Robinson
parent 5c2fac087f
commit bbf10a43b8
18 changed files with 420 additions and 143 deletions

View file

@ -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, FeatureType};
use super::{QueryFeatureExpression, FeatureType, FeatureFlags};
use crate::parser::ParserContext;
use crate::values::computed;
use cssparser::{Parser, Token};
@ -85,6 +85,35 @@ impl QueryCondition {
Self::parse_internal(context, input, feature_type, AllowOr::Yes)
}
fn visit<F>(&self, visitor: &mut F)
where
F: FnMut(&Self),
{
visitor(self);
match *self {
Self::Feature(..) => {},
Self::Not(ref cond) => cond.visit(visitor),
Self::Operation(ref conds, _op) => {
for cond in conds.iter() {
cond.visit(visitor);
}
},
Self::InParens(ref cond) => cond.visit(visitor),
}
}
/// Returns the union of all flags in the expression. This is useful for
/// container queries.
pub fn cumulative_flags(&self) -> FeatureFlags {
let mut result = FeatureFlags::empty();
self.visit(&mut |condition| {
if let Self::Feature(ref f) = condition {
result.insert(f.feature_flags())
}
});
result
}
/// Parse a single condition, disallowing `or` expressions.
///
/// To be used from the legacy query syntax.

View file

@ -101,13 +101,42 @@ macro_rules! keyword_evaluator {
}
bitflags! {
/// Different requirements or toggles that change how a expression is
/// parsed.
pub struct ParsingRequirements: u8 {
/// Different flags or toggles that change how a expression is parsed or
/// evaluated.
#[derive(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;
}
}
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())
}
}
@ -128,9 +157,8 @@ pub struct QueryFeatureDescription {
/// The evaluator, which we also use to determine which kind of value to
/// parse.
pub evaluator: Evaluator,
/// Different requirements that need to hold for the feature to be
/// successfully parsed.
pub requirements: ParsingRequirements,
/// Different feature-specific flags.
pub flags: FeatureFlags,
}
impl QueryFeatureDescription {
@ -143,12 +171,12 @@ impl QueryFeatureDescription {
/// A simple helper to construct a `QueryFeatureDescription`.
macro_rules! feature {
($name:expr, $allows_ranges:expr, $evaluator:expr, $reqs:expr,) => {
($name:expr, $allows_ranges:expr, $evaluator:expr, $flags:expr,) => {
$crate::queries::feature::QueryFeatureDescription {
name: $name,
allows_ranges: $allows_ranges,
evaluator: $evaluator,
requirements: $reqs,
flags: $flags,
}
};
}
@ -158,7 +186,7 @@ impl fmt::Debug for QueryFeatureDescription {
f.debug_struct("QueryFeatureDescription")
.field("name", &self.name)
.field("allows_ranges", &self.allows_ranges)
.field("requirements", &self.requirements)
.field("flags", &self.flags)
.finish()
}
}

View file

@ -6,7 +6,7 @@
//! `(width >= 400px)`.
use super::feature::{Evaluator, QueryFeatureDescription};
use super::feature::{KeywordDiscriminant, ParsingRequirements};
use super::feature::{KeywordDiscriminant, FeatureFlags};
use crate::parser::{Parse, ParserContext};
use crate::str::{starts_with_ignore_ascii_case, string_as_ascii_lowercase};
use crate::values::computed::{self, Ratio, ToComputedValue};
@ -337,10 +337,7 @@ impl QueryFeatureExpression {
W: fmt::Write,
{
let feature = self.feature();
if feature
.requirements
.contains(ParsingRequirements::WEBKIT_PREFIX)
{
if feature.flags.contains(FeatureFlags::WEBKIT_PREFIX) {
dest.write_str("-webkit-")?;
}
@ -361,6 +358,11 @@ impl QueryFeatureExpression {
&self.feature_type.features()[self.feature_index]
}
/// Returns the feature flags for our feature.
pub fn feature_flags(&self) -> FeatureFlags {
self.feature().flags
}
/// Parse a feature expression of the form:
///
/// ```
@ -382,18 +384,18 @@ impl QueryFeatureExpression {
input: &mut Parser<'i, 't>,
feature_type: FeatureType,
) -> Result<(usize, Option<LegacyRange>), ParseError<'i>> {
let mut requirements = ParsingRequirements::empty();
let mut flags = FeatureFlags::empty();
let location = input.current_source_location();
let ident = input.expect_ident()?;
if context.in_ua_or_chrome_sheet() {
requirements.insert(ParsingRequirements::CHROME_AND_UA_ONLY);
flags.insert(FeatureFlags::CHROME_AND_UA_ONLY);
}
let mut feature_name = &**ident;
if starts_with_ignore_ascii_case(feature_name, "-webkit-") {
feature_name = &feature_name[8..];
requirements.insert(ParsingRequirements::WEBKIT_PREFIX);
flags.insert(FeatureFlags::WEBKIT_PREFIX);
}
let range = if starts_with_ignore_ascii_case(feature_name, "min-") {
@ -417,7 +419,7 @@ impl QueryFeatureExpression {
};
if disabled_by_pref(&feature.name, context) ||
!requirements.contains(feature.requirements) ||
!flags.contains(feature.flags.parsing_requirements()) ||
(range.is_some() && !feature.allows_ranges())
{
return Err(location.new_custom_error(

View file

@ -15,4 +15,5 @@ pub mod feature_expression;
pub mod values;
pub use self::condition::QueryCondition;
pub use self::feature::FeatureFlags;
pub use self::feature_expression::{QueryFeatureExpression, FeatureType};