diff --git a/components/style/applicable_declarations.rs b/components/style/applicable_declarations.rs index 3a55060cc87..387dcdab07d 100644 --- a/components/style/applicable_declarations.rs +++ b/components/style/applicable_declarations.rs @@ -42,13 +42,7 @@ pub struct CascadePriority { layer_order: LayerOrder, } -#[allow(dead_code)] -fn size_assert() { - #[allow(unsafe_code)] - unsafe { - std::mem::transmute::(0u32) - }; -} +const_assert_eq!(std::mem::size_of::(), std::mem::size_of::()); impl PartialOrd for CascadePriority { #[inline] diff --git a/components/style/dom.rs b/components/style/dom.rs index 455acfd7863..83466eb21e6 100644 --- a/components/style/dom.rs +++ b/components/style/dom.rs @@ -928,6 +928,9 @@ pub trait TElement: /// Returns element's namespace. fn namespace(&self) -> &::BorrowedNamespaceUrl; + + /// Returns the size of the primary box of the element. + fn primary_box_size(&self) -> euclid::default::Size2D; } /// TNode and TElement aren't Send because we want to be careful and explicit diff --git a/components/style/gecko/media_features.rs b/components/style/gecko/media_features.rs index 1c84a7ebacc..a559adbcb1a 100644 --- a/components/style/gecko/media_features.rs +++ b/components/style/gecko/media_features.rs @@ -6,7 +6,7 @@ use crate::gecko_bindings::bindings; use crate::gecko_bindings::structs; -use crate::queries::feature::{AllowsRanges, Evaluator, ParsingRequirements, QueryFeatureDescription}; +use crate::queries::feature::{AllowsRanges, Evaluator, FeatureFlags, QueryFeatureDescription}; use crate::queries::values::Orientation; use crate::media_queries::{Device, MediaType}; use crate::values::computed::{Context, CSSPixelLength, Ratio, Resolution}; @@ -548,7 +548,7 @@ macro_rules! lnf_int_feature { $feature_name, AllowsRanges::No, Evaluator::BoolInteger(__eval), - ParsingRequirements::CHROME_AND_UA_ONLY, + FeatureFlags::CHROME_AND_UA_ONLY, ) }}; ($feature_name:expr, $int_id:ident) => {{ @@ -576,7 +576,7 @@ macro_rules! bool_pref_feature { $feature_name, AllowsRanges::No, Evaluator::BoolInteger(__eval), - ParsingRequirements::CHROME_AND_UA_ONLY, + FeatureFlags::CHROME_AND_UA_ONLY, ) }}; } @@ -591,49 +591,49 @@ pub static MEDIA_FEATURES: [QueryFeatureDescription; 60] = [ atom!("width"), AllowsRanges::Yes, Evaluator::Length(eval_width), - ParsingRequirements::empty(), + FeatureFlags::empty(), ), feature!( atom!("height"), AllowsRanges::Yes, Evaluator::Length(eval_height), - ParsingRequirements::empty(), + FeatureFlags::empty(), ), feature!( atom!("aspect-ratio"), AllowsRanges::Yes, Evaluator::NumberRatio(eval_aspect_ratio), - ParsingRequirements::empty(), + FeatureFlags::empty(), ), feature!( atom!("orientation"), AllowsRanges::No, keyword_evaluator!(eval_orientation, Orientation), - ParsingRequirements::empty(), + FeatureFlags::empty(), ), feature!( atom!("device-width"), AllowsRanges::Yes, Evaluator::Length(eval_device_width), - ParsingRequirements::empty(), + FeatureFlags::empty(), ), feature!( atom!("device-height"), AllowsRanges::Yes, Evaluator::Length(eval_device_height), - ParsingRequirements::empty(), + FeatureFlags::empty(), ), feature!( atom!("device-aspect-ratio"), AllowsRanges::Yes, Evaluator::NumberRatio(eval_device_aspect_ratio), - ParsingRequirements::empty(), + FeatureFlags::empty(), ), feature!( atom!("-moz-device-orientation"), AllowsRanges::No, keyword_evaluator!(eval_device_orientation, Orientation), - ParsingRequirements::empty(), + FeatureFlags::empty(), ), // Webkit extensions that we support for de-facto web compatibility. // -webkit-{min|max}-device-pixel-ratio (controlled with its own pref): @@ -641,68 +641,68 @@ pub static MEDIA_FEATURES: [QueryFeatureDescription; 60] = [ atom!("device-pixel-ratio"), AllowsRanges::Yes, Evaluator::Float(eval_device_pixel_ratio), - ParsingRequirements::WEBKIT_PREFIX, + FeatureFlags::WEBKIT_PREFIX, ), // -webkit-transform-3d. feature!( atom!("transform-3d"), AllowsRanges::No, Evaluator::BoolInteger(eval_transform_3d), - ParsingRequirements::WEBKIT_PREFIX, + FeatureFlags::WEBKIT_PREFIX, ), feature!( atom!("-moz-device-pixel-ratio"), AllowsRanges::Yes, Evaluator::Float(eval_device_pixel_ratio), - ParsingRequirements::empty(), + FeatureFlags::empty(), ), feature!( atom!("resolution"), AllowsRanges::Yes, Evaluator::Resolution(eval_resolution), - ParsingRequirements::empty(), + FeatureFlags::empty(), ), feature!( atom!("display-mode"), AllowsRanges::No, keyword_evaluator!(eval_display_mode, DisplayMode), - ParsingRequirements::empty(), + FeatureFlags::empty(), ), feature!( atom!("grid"), AllowsRanges::No, Evaluator::BoolInteger(eval_grid), - ParsingRequirements::empty(), + FeatureFlags::empty(), ), feature!( atom!("scan"), AllowsRanges::No, keyword_evaluator!(eval_scan, Scan), - ParsingRequirements::empty(), + FeatureFlags::empty(), ), feature!( atom!("color"), AllowsRanges::Yes, Evaluator::Integer(eval_color), - ParsingRequirements::empty(), + FeatureFlags::empty(), ), feature!( atom!("color-index"), AllowsRanges::Yes, Evaluator::Integer(eval_color_index), - ParsingRequirements::empty(), + FeatureFlags::empty(), ), feature!( atom!("monochrome"), AllowsRanges::Yes, Evaluator::Integer(eval_monochrome), - ParsingRequirements::empty(), + FeatureFlags::empty(), ), feature!( atom!("prefers-reduced-motion"), AllowsRanges::No, keyword_evaluator!(eval_prefers_reduced_motion, PrefersReducedMotion), - ParsingRequirements::empty(), + FeatureFlags::empty(), ), feature!( atom!("prefers-contrast"), @@ -713,49 +713,49 @@ pub static MEDIA_FEATURES: [QueryFeatureDescription; 60] = [ // layout.css.prefers-contrast.enabled preference. See // disabed_by_pref in media_feature_expression.rs for how that // is done. - ParsingRequirements::empty(), + FeatureFlags::empty(), ), feature!( atom!("forced-colors"), AllowsRanges::No, keyword_evaluator!(eval_forced_colors, ForcedColors), - ParsingRequirements::empty(), + FeatureFlags::empty(), ), feature!( atom!("overflow-block"), AllowsRanges::No, keyword_evaluator!(eval_overflow_block, OverflowBlock), - ParsingRequirements::empty(), + FeatureFlags::empty(), ), feature!( atom!("overflow-inline"), AllowsRanges::No, keyword_evaluator!(eval_overflow_inline, OverflowInline), - ParsingRequirements::empty(), + FeatureFlags::empty(), ), feature!( atom!("update"), AllowsRanges::No, keyword_evaluator!(eval_update, Update), - ParsingRequirements::empty(), + FeatureFlags::empty(), ), feature!( atom!("prefers-color-scheme"), AllowsRanges::No, keyword_evaluator!(eval_prefers_color_scheme, PrefersColorScheme), - ParsingRequirements::empty(), + FeatureFlags::empty(), ), feature!( atom!("dynamic-range"), AllowsRanges::No, keyword_evaluator!(eval_dynamic_range, DynamicRange), - ParsingRequirements::empty(), + FeatureFlags::empty(), ), feature!( atom!("video-dynamic-range"), AllowsRanges::No, keyword_evaluator!(eval_video_dynamic_range, DynamicRange), - ParsingRequirements::empty(), + FeatureFlags::empty(), ), // Evaluates to the preferred color scheme for content. Only useful in // chrome context, where the chrome color-scheme and the content @@ -764,31 +764,31 @@ pub static MEDIA_FEATURES: [QueryFeatureDescription; 60] = [ atom!("-moz-content-prefers-color-scheme"), AllowsRanges::No, keyword_evaluator!(eval_content_prefers_color_scheme, PrefersColorScheme), - ParsingRequirements::CHROME_AND_UA_ONLY, + FeatureFlags::CHROME_AND_UA_ONLY, ), feature!( atom!("pointer"), AllowsRanges::No, keyword_evaluator!(eval_pointer, Pointer), - ParsingRequirements::empty(), + FeatureFlags::empty(), ), feature!( atom!("any-pointer"), AllowsRanges::No, keyword_evaluator!(eval_any_pointer, Pointer), - ParsingRequirements::empty(), + FeatureFlags::empty(), ), feature!( atom!("hover"), AllowsRanges::No, keyword_evaluator!(eval_hover, Hover), - ParsingRequirements::empty(), + FeatureFlags::empty(), ), feature!( atom!("any-hover"), AllowsRanges::No, keyword_evaluator!(eval_any_hover, Hover), - ParsingRequirements::empty(), + FeatureFlags::empty(), ), // Internal -moz-is-glyph media feature: applies only inside SVG glyphs. // Internal because it is really only useful in the user agent anyway @@ -797,43 +797,43 @@ pub static MEDIA_FEATURES: [QueryFeatureDescription; 60] = [ atom!("-moz-is-glyph"), AllowsRanges::No, Evaluator::BoolInteger(eval_moz_is_glyph), - ParsingRequirements::CHROME_AND_UA_ONLY, + FeatureFlags::CHROME_AND_UA_ONLY, ), feature!( atom!("-moz-is-resource-document"), AllowsRanges::No, Evaluator::BoolInteger(eval_moz_is_resource_document), - ParsingRequirements::CHROME_AND_UA_ONLY, + FeatureFlags::CHROME_AND_UA_ONLY, ), feature!( atom!("-moz-platform"), AllowsRanges::No, keyword_evaluator!(eval_moz_platform, Platform), - ParsingRequirements::CHROME_AND_UA_ONLY, + FeatureFlags::CHROME_AND_UA_ONLY, ), feature!( atom!("-moz-print-preview"), AllowsRanges::No, Evaluator::BoolInteger(eval_moz_print_preview), - ParsingRequirements::CHROME_AND_UA_ONLY, + FeatureFlags::CHROME_AND_UA_ONLY, ), feature!( atom!("-moz-non-native-content-theme"), AllowsRanges::No, Evaluator::BoolInteger(eval_moz_non_native_content_theme), - ParsingRequirements::CHROME_AND_UA_ONLY, + FeatureFlags::CHROME_AND_UA_ONLY, ), feature!( atom!("-moz-windows-non-native-menus"), AllowsRanges::No, Evaluator::BoolInteger(eval_moz_windows_non_native_menus), - ParsingRequirements::CHROME_AND_UA_ONLY, + FeatureFlags::CHROME_AND_UA_ONLY, ), feature!( atom!("-moz-overlay-scrollbars"), AllowsRanges::No, Evaluator::BoolInteger(eval_moz_overlay_scrollbars), - ParsingRequirements::CHROME_AND_UA_ONLY, + FeatureFlags::CHROME_AND_UA_ONLY, ), lnf_int_feature!( atom!("-moz-scrollbar-start-backward"), diff --git a/components/style/gecko/wrapper.rs b/components/style/gecko/wrapper.rs index 91d713fa98a..6053038c0c7 100644 --- a/components/style/gecko/wrapper.rs +++ b/components/style/gecko/wrapper.rs @@ -66,6 +66,8 @@ use crate::stylist::CascadeData; use crate::values::{AtomIdent, AtomString}; use crate::CaseSensitivityExt; use crate::LocalName; +use app_units::Au; +use euclid::default::Size2D; use atomic_refcell::{AtomicRef, AtomicRefCell, AtomicRefMut}; use fxhash::FxHashMap; use selectors::attr::{AttrSelectorOperation, AttrSelectorOperator}; @@ -321,6 +323,11 @@ impl<'ln> GeckoNode<'ln> { self.flags() & (NODE_IS_IN_SHADOW_TREE as u32) != 0 } + #[inline] + fn is_connected(&self) -> bool { + self.get_bool_flag(nsINode_BooleanFlag::IsConnected) + } + /// WARNING: This logic is duplicated in Gecko's FlattenedTreeParentIsParent. /// Make sure to mirror any modifications in both places. #[inline] @@ -1030,6 +1037,21 @@ impl<'le> TElement for GeckoElement<'le> { } } + #[inline] + fn primary_box_size(&self) -> Size2D { + if !self.as_node().is_connected() { + return Size2D::zero(); + } + + unsafe { + let frame = self.0._base._base._base.__bindgen_anon_1.mPrimaryFrame.as_ref(); + if frame.is_null() { + return Size2D::zero(); + } + Size2D::new(Au((**frame).mRect.width), Au((**frame).mRect.height)) + } + } + /// Return the list of slotted nodes of this node. #[inline] fn slotted_nodes(&self) -> &[Self::ConcreteNode] { diff --git a/components/style/lib.rs b/components/style/lib.rs index 2f0cbc978a7..b1cec716fcf 100644 --- a/components/style/lib.rs +++ b/components/style/lib.rs @@ -62,7 +62,6 @@ pub use servo_arc; #[cfg(feature = "servo")] #[macro_use] extern crate servo_atoms; -#[cfg(target_pointer_width = "64")] #[macro_use] extern crate static_assertions; #[macro_use] diff --git a/components/style/logical_geometry.rs b/components/style/logical_geometry.rs index 9113185cf69..d475e9bdfc4 100644 --- a/components/style/logical_geometry.rs +++ b/components/style/logical_geometry.rs @@ -168,6 +168,11 @@ impl WritingMode { flags } + /// Returns the `horizontal-tb` value. + pub fn horizontal_tb() -> Self { + Self::from_bits_truncate(0) + } + #[inline] pub fn is_vertical(&self) -> bool { self.intersects(WritingMode::VERTICAL) diff --git a/components/style/properties/cascade.rs b/components/style/properties/cascade.rs index e7d605dfc19..e3383118709 100644 --- a/components/style/properties/cascade.rs +++ b/components/style/properties/cascade.rs @@ -292,6 +292,7 @@ where in_media_query: false, for_smil_animation: false, for_non_inherited_property: None, + container_info: None, quirks_mode, rule_cache_conditions: RefCell::new(rule_cache_conditions), }; diff --git a/components/style/queries/condition.rs b/components/style/queries/condition.rs index 71f4e2fd0d6..da8ad02d595 100644 --- a/components/style/queries/condition.rs +++ b/components/style/queries/condition.rs @@ -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(&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. diff --git a/components/style/queries/feature.rs b/components/style/queries/feature.rs index 3390127887b..9efd64f8c23 100644 --- a/components/style/queries/feature.rs +++ b/components/style/queries/feature.rs @@ -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() } } diff --git a/components/style/queries/feature_expression.rs b/components/style/queries/feature_expression.rs index 978c0e6e1ea..aa2e4a04582 100644 --- a/components/style/queries/feature_expression.rs +++ b/components/style/queries/feature_expression.rs @@ -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), 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( diff --git a/components/style/queries/mod.rs b/components/style/queries/mod.rs index 69df6e37ad8..6bbf197c43b 100644 --- a/components/style/queries/mod.rs +++ b/components/style/queries/mod.rs @@ -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}; diff --git a/components/style/rule_collector.rs b/components/style/rule_collector.rs index e3f3725f8b5..0e18383455c 100644 --- a/components/style/rule_collector.rs +++ b/components/style/rule_collector.rs @@ -225,6 +225,7 @@ where &mut self.context, cascade_level, cascade_data, + &self.stylist, ); } @@ -243,6 +244,7 @@ where &mut self.context, cascade_level, cascade_data, + &self.stylist, ); } diff --git a/components/style/selector_map.rs b/components/style/selector_map.rs index 82bff0fb63d..b2cd8d918ca 100644 --- a/components/style/selector_map.rs +++ b/components/style/selector_map.rs @@ -10,7 +10,7 @@ use crate::context::QuirksMode; use crate::dom::TElement; use crate::rule_tree::CascadeLevel; use crate::selector_parser::SelectorImpl; -use crate::stylist::{CascadeData, Rule}; +use crate::stylist::{Stylist, CascadeData, Rule, ContainerConditionId}; use crate::AllocErr; use crate::{Atom, LocalName, Namespace, ShrinkIfNeeded, WeakAtom}; use precomputed_hash::PrecomputedHash; @@ -191,9 +191,10 @@ impl SelectorMap { element: E, rule_hash_target: E, matching_rules_list: &mut ApplicableDeclarationList, - context: &mut MatchingContext, + matching_context: &mut MatchingContext, cascade_level: CascadeLevel, cascade_data: &CascadeData, + stylist: &Stylist, ) where E: TElement, { @@ -201,16 +202,17 @@ impl SelectorMap { return; } - let quirks_mode = context.quirks_mode(); + let quirks_mode = matching_context.quirks_mode(); if rule_hash_target.is_root() { SelectorMap::get_matching_rules( element, &self.root, matching_rules_list, - context, + matching_context, cascade_level, cascade_data, + stylist, ); } @@ -220,9 +222,10 @@ impl SelectorMap { element, rules, matching_rules_list, - context, + matching_context, cascade_level, cascade_data, + stylist, ) } } @@ -233,9 +236,10 @@ impl SelectorMap { element, rules, matching_rules_list, - context, + matching_context, cascade_level, cascade_data, + stylist, ) } }); @@ -247,9 +251,10 @@ impl SelectorMap { element, rules, matching_rules_list, - context, + matching_context, cascade_level, cascade_data, + stylist, ) } }); @@ -260,9 +265,10 @@ impl SelectorMap { element, rules, matching_rules_list, - context, + matching_context, cascade_level, cascade_data, + stylist, ) } @@ -271,9 +277,10 @@ impl SelectorMap { element, rules, matching_rules_list, - context, + matching_context, cascade_level, cascade_data, + stylist, ) } @@ -281,9 +288,10 @@ impl SelectorMap { element, &self.other, matching_rules_list, - context, + matching_context, cascade_level, cascade_data, + stylist, ); } @@ -292,23 +300,32 @@ impl SelectorMap { element: E, rules: &[Rule], matching_rules: &mut ApplicableDeclarationList, - context: &mut MatchingContext, + matching_context: &mut MatchingContext, cascade_level: CascadeLevel, cascade_data: &CascadeData, + stylist: &Stylist, ) where E: TElement, { for rule in rules { - if matches_selector( + if !matches_selector( &rule.selector, 0, Some(&rule.hashes), &element, - context, + matching_context, ) { - matching_rules - .push(rule.to_applicable_declaration_block(cascade_level, cascade_data)); + continue; } + + if rule.container_condition_id != ContainerConditionId::none() { + if !cascade_data.container_condition_matches(rule.container_condition_id, stylist, element) { + continue; + } + } + + matching_rules + .push(rule.to_applicable_declaration_block(cascade_level, cascade_data)); } } } diff --git a/components/style/stylesheets/container_rule.rs b/components/style/stylesheets/container_rule.rs index b88a41db622..fe103d8986d 100644 --- a/components/style/stylesheets/container_rule.rs +++ b/components/style/stylesheets/container_rule.rs @@ -7,32 +7,34 @@ //! [container]: https://drafts.csswg.org/css-contain-3/#container-rule use crate::logical_geometry::{WritingMode, LogicalSize}; -use crate::queries::QueryCondition; +use crate::dom::TElement; +use crate::media_queries::Device; +use crate::parser::ParserContext; +use crate::queries::{QueryCondition, FeatureType}; +use crate::queries::feature::{AllowsRanges, Evaluator, FeatureFlags, QueryFeatureDescription}; +use crate::queries::values::Orientation; +use crate::str::CssStringWriter; use crate::shared_lock::{ DeepCloneParams, DeepCloneWithLock, Locked, SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard, }; use crate::values::specified::ContainerName; use crate::values::computed::{Context, CSSPixelLength, Ratio}; -use crate::str::CssStringWriter; +use crate::properties::ComputedValues; 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, Parser}; use euclid::default::Size2D; #[cfg(feature = "gecko")] use malloc_size_of::{MallocSizeOfOps, MallocUnconditionalShallowSizeOf}; use servo_arc::Arc; use std::fmt::{self, Write}; -use style_traits::{CssWriter, ToCss}; +use style_traits::{CssWriter, ToCss, ParseError}; /// A container rule. #[derive(Debug, ToShmem)] pub struct ContainerRule { - /// The container name. - pub name: ContainerName, - /// The container query. - pub condition: ContainerCondition, + /// The container query and name. + pub condition: Arc, /// The nested rules inside the block. pub rules: Arc>, /// The source position where this rule was found. @@ -40,6 +42,11 @@ pub struct ContainerRule { } impl ContainerRule { + /// Returns the query condition. + pub fn query_condition(&self) -> &QueryCondition { + &self.condition.condition + } + /// Measure heap usage. #[cfg(feature = "gecko")] pub fn size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize { @@ -58,7 +65,6 @@ impl DeepCloneWithLock for ContainerRule { ) -> Self { let rules = self.rules.read_with(guard); Self { - name: self.name.clone(), condition: self.condition.clone(), rules: Arc::new(lock.wrap(rules.deep_clone_with_lock(lock, guard, params))), source_location: self.source_location.clone(), @@ -71,51 +77,163 @@ impl ToCssWithGuard for ContainerRule { dest.write_str("@container ")?; { let mut writer = CssWriter::new(dest); - if !self.name.is_none() { - self.name.to_css(&mut writer)?; + if !self.condition.name.is_none() { + self.condition.name.to_css(&mut writer)?; writer.write_char(' ')?; } - self.condition.to_css(&mut writer)?; + self.condition.condition.to_css(&mut writer)?; } self.rules.read_with(guard).to_css_block(guard, dest) } } -/// TODO: Factor out the media query code to work with containers. -pub type ContainerCondition = QueryCondition; +/// A container condition and filter, combined. +#[derive(Debug, ToShmem)] +pub struct ContainerCondition { + name: ContainerName, + condition: QueryCondition, + flags: FeatureFlags, +} -fn get_container(_context: &Context) -> (Size2D, WritingMode) { - unimplemented!("TODO: implement container matching"); +impl ContainerCondition { + /// Parse a container condition. + pub fn parse<'a>( + context: &ParserContext, + input: &mut Parser<'a, '_>, + ) -> Result> { + use crate::parser::Parse; + + // FIXME: This is a bit ambiguous: + // https://github.com/w3c/csswg-drafts/issues/7203 + let name = input.try_parse(|input| { + ContainerName::parse(context, input) + }).ok().unwrap_or_else(ContainerName::none); + let condition = QueryCondition::parse(context, input, FeatureType::Container)?; + let flags = condition.cumulative_flags(); + Ok(Self { name, condition, flags }) + } + + fn valid_container_info(&self, potential_container: E) -> Option<(ContainerInfo, Arc)> + where + E: TElement, + { + use crate::values::computed::ContainerType; + + fn container_type_axes(ty_: ContainerType, wm: WritingMode) -> FeatureFlags { + if ty_.intersects(ContainerType::SIZE) { + return FeatureFlags::all_container_axes() + } + if ty_.intersects(ContainerType::INLINE_SIZE) { + let physical_axis = if wm.is_vertical() { + FeatureFlags::CONTAINER_REQUIRES_HEIGHT_AXIS + } else { + FeatureFlags::CONTAINER_REQUIRES_WIDTH_AXIS + }; + return FeatureFlags::CONTAINER_REQUIRES_INLINE_AXIS | physical_axis + } + FeatureFlags::empty() + } + + let data = match potential_container.borrow_data() { + Some(data) => data, + None => return None, + }; + let style = data.styles.primary(); + let wm = style.writing_mode; + let box_style = style.get_box(); + + // Filter by container-type. + let container_type = box_style.clone_container_type(); + let available_axes = container_type_axes(container_type, wm); + if !available_axes.contains(self.flags.container_axes()) { + return None; + } + + // Filter by container-name. + let container_name = box_style.clone_container_name(); + for filter_name in self.name.0.iter() { + if !container_name.0.contains(filter_name) { + return None; + } + } + + let size = potential_container.primary_box_size(); + let style = style.clone(); + Some((ContainerInfo { size, wm }, style)) + } + + fn find_container(&self, mut e: E) -> Option<(ContainerInfo, Arc)> + where + E: TElement, + { + while let Some(element) = e.traversal_parent() { + if let Some(info) = self.valid_container_info(element) { + return Some(info); + } + e = element; + } + + None + } + + /// Tries to match a container query condition for a given element. + pub(crate) fn matches(&self, device: &Device, element: E) -> bool + where + E: TElement, + { + let info = self.find_container(element); + Context::for_container_query_evaluation(device, info, |context| { + self.condition.matches(context) + }) + } +} + + +/// Information needed to evaluate an individual container query. +#[derive(Copy, Clone)] +pub struct ContainerInfo { + size: Size2D, + wm: WritingMode, +} + +fn get_container(context: &Context) -> ContainerInfo { + if let Some(ref info) = context.container_info { + return info.clone() + } + ContainerInfo { + size: context.device().au_viewport_size(), + wm: WritingMode::horizontal_tb(), + } } fn eval_width(context: &Context) -> CSSPixelLength { - let (size, _wm) = get_container(context); - CSSPixelLength::new(size.width.to_f32_px()) + let info = get_container(context); + CSSPixelLength::new(info.size.width.to_f32_px()) } fn eval_height(context: &Context) -> CSSPixelLength { - let (size, _wm) = get_container(context); - CSSPixelLength::new(size.height.to_f32_px()) + let info = get_container(context); + CSSPixelLength::new(info.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()) + let info = get_container(context); + CSSPixelLength::new(LogicalSize::from_physical(info.wm, info.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()) + let info = get_container(context); + CSSPixelLength::new(LogicalSize::from_physical(info.wm, info.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) + let info = get_container(context); + Ratio::new(info.size.width.0 as f32, info.size.height.0 as f32) } fn eval_orientation(context: &Context, value: Option) -> bool { - let (size, _wm) = get_container(context); - Orientation::eval(size, value) + let info = get_container(context); + Orientation::eval(info.size, value) } /// https://drafts.csswg.org/css-contain-3/#container-features @@ -126,36 +244,38 @@ pub static CONTAINER_FEATURES: [QueryFeatureDescription; 6] = [ atom!("width"), AllowsRanges::Yes, Evaluator::Length(eval_width), - ParsingRequirements::empty(), + FeatureFlags::CONTAINER_REQUIRES_WIDTH_AXIS, ), feature!( atom!("height"), AllowsRanges::Yes, Evaluator::Length(eval_height), - ParsingRequirements::empty(), + FeatureFlags::CONTAINER_REQUIRES_HEIGHT_AXIS, ), feature!( atom!("inline-size"), AllowsRanges::Yes, Evaluator::Length(eval_inline_size), - ParsingRequirements::empty(), + FeatureFlags::CONTAINER_REQUIRES_INLINE_AXIS, ), feature!( atom!("block-size"), AllowsRanges::Yes, Evaluator::Length(eval_block_size), - ParsingRequirements::empty(), + FeatureFlags::CONTAINER_REQUIRES_BLOCK_AXIS, ), feature!( atom!("aspect-ratio"), AllowsRanges::Yes, Evaluator::NumberRatio(eval_aspect_ratio), - ParsingRequirements::empty(), + // XXX from_bits_truncate is const, but the pipe operator isn't, so this + // works around it. + FeatureFlags::from_bits_truncate(FeatureFlags::CONTAINER_REQUIRES_BLOCK_AXIS.bits() | FeatureFlags::CONTAINER_REQUIRES_INLINE_AXIS.bits()), ), feature!( atom!("orientation"), AllowsRanges::No, keyword_evaluator!(eval_orientation, Orientation), - ParsingRequirements::empty(), + FeatureFlags::from_bits_truncate(FeatureFlags::CONTAINER_REQUIRES_BLOCK_AXIS.bits() | FeatureFlags::CONTAINER_REQUIRES_INLINE_AXIS.bits()), ), ]; diff --git a/components/style/stylesheets/rule_parser.rs b/components/style/stylesheets/rule_parser.rs index 08c2ab6e4d1..b0203338e56 100644 --- a/components/style/stylesheets/rule_parser.rs +++ b/components/style/stylesheets/rule_parser.rs @@ -10,7 +10,6 @@ use crate::font_face::parse_font_face_block; use crate::media_queries::MediaList; use crate::parser::{Parse, ParserContext}; use crate::properties::parse_property_declaration_list; -use crate::queries::FeatureType; use crate::selector_parser::{SelectorImpl, SelectorParser}; use crate::shared_lock::{Locked, SharedRwLock}; use crate::str::starts_with_ignore_ascii_case; @@ -30,7 +29,6 @@ use crate::stylesheets::{ }; use crate::values::computed::font::FamilyName; use crate::values::{CssUrl, CustomIdent, KeyframesName, TimelineName}; -use crate::values::specified::ContainerName; use crate::{Namespace, Prefix}; use cssparser::{ AtRuleParser, BasicParseError, BasicParseErrorKind, CowRcStr, Parser, ParserState, @@ -192,7 +190,7 @@ pub enum AtRulePrelude { /// A @media rule prelude, with its media queries. Media(Arc>), /// A @container rule prelude. - Container(ContainerName, ContainerCondition), + Container(Arc), /// An @supports rule, with its conditional Supports(SupportsCondition), /// A @viewport rule prelude. @@ -487,13 +485,8 @@ impl<'a, 'b, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'b> { AtRulePrelude::FontFace }, "container" if container_queries_enabled() => { - // FIXME: This is a bit ambiguous: - // https://github.com/w3c/csswg-drafts/issues/7203 - let name = input.try_parse(|input| { - ContainerName::parse(self.context, input) - }).ok().unwrap_or_else(ContainerName::none); - let condition = ContainerCondition::parse(self.context, input, FeatureType::Container)?; - AtRulePrelude::Container(name, condition) + let condition = Arc::new(ContainerCondition::parse(self.context, input)?); + AtRulePrelude::Container(condition) }, "layer" => { let names = input.try_parse(|input| { @@ -676,10 +669,9 @@ impl<'a, 'b, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'b> { }, )))) }, - AtRulePrelude::Container(name, condition) => { + AtRulePrelude::Container(condition) => { Ok(CssRule::Container(Arc::new(self.shared_lock.wrap( ContainerRule { - name, condition, rules: self.parse_nested_rules(input, CssRuleType::Container), source_location: start.source_location(), diff --git a/components/style/stylesheets/viewport_rule.rs b/components/style/stylesheets/viewport_rule.rs index 89f604be01b..10e6af5715b 100644 --- a/components/style/stylesheets/viewport_rule.rs +++ b/components/style/stylesheets/viewport_rule.rs @@ -671,7 +671,8 @@ impl MaybeNew for ViewportConstraints { builder: StyleBuilder::for_inheritance(device, None, None), cached_system_font: None, in_media_query: false, - quirks_mode: quirks_mode, + quirks_mode, + container_info: None, for_smil_animation: false, for_non_inherited_property: None, rule_cache_conditions: RefCell::new(&mut conditions), diff --git a/components/style/stylist.rs b/components/style/stylist.rs index b420e902ebc..21b975c9e92 100644 --- a/components/style/stylist.rs +++ b/components/style/stylist.rs @@ -28,6 +28,7 @@ use crate::selector_parser::{PerPseudoElementMap, PseudoElement, SelectorImpl, S use crate::shared_lock::{Locked, SharedRwLockReadGuard, StylesheetGuards}; use crate::stylesheet_set::{DataValidity, DocumentStylesheetSet, SheetRebuildKind}; use crate::stylesheet_set::{DocumentStylesheetFlusher, SheetCollectionFlusher}; +use crate::stylesheets::container_rule::ContainerCondition; use crate::stylesheets::keyframes_rule::KeyframesAnimation; use crate::stylesheets::layer_rule::{LayerName, LayerOrder}; use crate::stylesheets::viewport_rule::{self, MaybeNew, ViewportRule}; @@ -2112,15 +2113,17 @@ impl ContainerConditionId { #[derive(Clone, Debug, MallocSizeOf)] -struct ContainerCondition { +struct ContainerConditionReference { parent: ContainerConditionId, - // TODO: condition: Option> (or so). + #[ignore_malloc_size_of = "Arc"] + condition: Option>, } -impl ContainerCondition { +impl ContainerConditionReference { const fn none() -> Self { Self { parent: ContainerConditionId::none(), + condition: None, } } } @@ -2197,7 +2200,7 @@ pub struct CascadeData { layers: SmallVec<[CascadeLayer; 1]>, /// The list of container conditions, indexed by their id. - container_conditions: SmallVec<[ContainerCondition; 1]>, + container_conditions: SmallVec<[ContainerConditionReference; 1]>, /// Effective media query results cached from the last rebuild. effective_media_query_results: EffectiveMediaQueryResults, @@ -2241,7 +2244,7 @@ impl CascadeData { animations: Default::default(), layer_id: Default::default(), layers: smallvec::smallvec![CascadeLayer::root()], - container_conditions: smallvec::smallvec![ContainerCondition::none()], + container_conditions: smallvec::smallvec![ContainerConditionReference::none()], extra_data: ExtraStyleData::default(), effective_media_query_results: EffectiveMediaQueryResults::new(), rules_source_order: 0, @@ -2356,6 +2359,23 @@ impl CascadeData { self.layers[id.0 as usize].order } + pub(crate) fn container_condition_matches(&self, mut id: ContainerConditionId, stylist: &Stylist, element: E) -> bool + where + E: TElement, + { + loop { + let condition_ref = &self.container_conditions[id.0 as usize]; + let condition = match condition_ref.condition { + None => return true, + Some(ref c) => c, + }; + if !condition.matches(stylist.device(), element) { + return false; + } + id = condition_ref.parent; + } + } + fn did_finish_rebuild(&mut self) { self.shrink_maps_if_needed(); self.compute_layer_order(); @@ -2771,10 +2791,11 @@ impl CascadeData { } }, CssRule::Container(ref lock) => { - let _container_rule = lock.read_with(guard); + let container_rule = lock.read_with(guard); let id = ContainerConditionId(self.container_conditions.len() as u16); - self.container_conditions.push(ContainerCondition { + self.container_conditions.push(ContainerConditionReference { parent: containing_rule_state.container_condition_id, + condition: Some(container_rule.condition.clone()), }); containing_rule_state.container_condition_id = id; }, @@ -2959,7 +2980,7 @@ impl CascadeData { self.layers.clear(); self.layers.push(CascadeLayer::root()); self.container_conditions.clear(); - self.container_conditions.push(ContainerCondition::none()); + self.container_conditions.push(ContainerConditionReference::none()); #[cfg(feature = "gecko")] { self.extra_data.clear(); diff --git a/components/style/values/computed/mod.rs b/components/style/values/computed/mod.rs index ed69ca4671f..78dfd1ba1cb 100644 --- a/components/style/values/computed/mod.rs +++ b/components/style/values/computed/mod.rs @@ -16,6 +16,7 @@ use super::specified; use super::{CSSFloat, CSSInteger}; use crate::computed_value_flags::ComputedValueFlags; use crate::context::QuirksMode; +use crate::stylesheets::container_rule::ContainerInfo; use crate::font_metrics::{FontMetrics, FontMetricsOrientation}; use crate::media_queries::Device; #[cfg(feature = "gecko")] @@ -170,6 +171,9 @@ pub struct Context<'a> { /// values, which SMIL allows. pub for_smil_animation: bool, + /// Returns the container information to evaluate a given container query. + pub container_info: Option, + /// The property we are computing a value for, if it is a non-inherited /// property. None if we are computed a value for an inherited property /// or not computing for a property at all (e.g. in a media query @@ -198,6 +202,42 @@ impl<'a> Context<'a> { in_media_query: true, quirks_mode, for_smil_animation: false, + container_info: None, + for_non_inherited_property: None, + rule_cache_conditions: RefCell::new(&mut conditions), + }; + + f(&context) + } + + /// Creates a suitable context for container query evaluation for the style + /// specified. + pub fn for_container_query_evaluation( + device: &Device, + container_info_and_style: Option<(ContainerInfo, Arc)>, + f: F, + ) -> R + where + F: FnOnce(&Context) -> R, + { + let mut conditions = RuleCacheConditions::default(); + let provider = get_metrics_provider_for_product(); + + let (container_info, style) = match container_info_and_style { + Some((ci, s)) => (Some(ci), Some(s)), + None => (None, None), + }; + + let style = style.as_ref().map(|s| &**s); + let quirks_mode = device.quirks_mode(); + let context = Context { + builder: StyleBuilder::for_inheritance(device, style, None), + font_metrics_provider: &provider, + cached_system_font: None, + in_media_query: true, + quirks_mode, + for_smil_animation: false, + container_info, for_non_inherited_property: None, rule_cache_conditions: RefCell::new(&mut conditions), }; @@ -304,8 +344,8 @@ impl<'a, 'cx, 'cx_a: 'cx, S: ToComputedValue + 'a> ComputedVecIter<'a, 'cx, 'cx_ /// Construct an iterator from a slice of specified values and a context pub fn new(cx: &'cx Context<'cx_a>, values: &'a [S]) -> Self { ComputedVecIter { - cx: cx, - values: values, + cx, + values, } } }