diff --git a/components/style/gecko/arc_types.rs b/components/style/gecko/arc_types.rs index 4fb08c3a309..893812ea710 100644 --- a/components/style/gecko/arc_types.rs +++ b/components/style/gecko/arc_types.rs @@ -15,7 +15,7 @@ use crate::gecko_bindings::structs::{ RawServoKeyframesRule, RawServoLayerBlockRule, RawServoLayerStatementRule, RawServoMediaList, RawServoMediaRule, RawServoMozDocumentRule, RawServoNamespaceRule, RawServoPageRule, RawServoScrollTimelineRule, RawServoStyleRule, RawServoStyleSheetContents, - RawServoSupportsRule, ServoCssRules, + RawServoSupportsRule, RawServoContainerRule, ServoCssRules, }; use crate::gecko_bindings::sugar::ownership::{HasArcFFI, HasFFI, Strong}; use crate::media_queries::MediaList; @@ -26,7 +26,7 @@ use crate::stylesheets::keyframes_rule::Keyframe; use crate::stylesheets::{ CounterStyleRule, CssRules, DocumentRule, FontFaceRule, FontFeatureValuesRule, ImportRule, KeyframesRule, LayerBlockRule, LayerStatementRule, MediaRule, NamespaceRule, PageRule, - ScrollTimelineRule, StyleRule, StylesheetContents, SupportsRule, + ScrollTimelineRule, StyleRule, StylesheetContents, SupportsRule, ContainerRule, }; use servo_arc::{Arc, ArcBorrow}; use std::{mem, ptr}; @@ -98,6 +98,9 @@ impl_arc_ffi!(Locked => RawServoScrollTimelineRule impl_arc_ffi!(Locked => RawServoSupportsRule [Servo_SupportsRule_AddRef, Servo_SupportsRule_Release]); +impl_arc_ffi!(Locked => RawServoContainerRule + [Servo_ContainerRule_AddRef, Servo_ContainerRule_Release]); + impl_arc_ffi!(Locked => RawServoMozDocumentRule [Servo_DocumentRule_AddRef, Servo_DocumentRule_Release]); diff --git a/components/style/invalidation/stylesheets.rs b/components/style/invalidation/stylesheets.rs index 53130de7a2e..3c4ee845055 100644 --- a/components/style/invalidation/stylesheets.rs +++ b/components/style/invalidation/stylesheets.rs @@ -556,6 +556,7 @@ impl StylesheetInvalidationSet { FontFace(..) | Keyframes(..) | ScrollTimeline(..) | + Container(..) | Style(..) => { if is_generic_change { // TODO(emilio): We need to do this for selector / keyframe @@ -610,7 +611,7 @@ impl StylesheetInvalidationSet { } }, Document(..) | Namespace(..) | Import(..) | Media(..) | Supports(..) | - LayerStatement(..) | LayerBlock(..) => { + Container(..) | LayerStatement(..) | LayerBlock(..) => { // Do nothing, relevant nested rules are visited as part of the // iteration. }, diff --git a/components/style/stylesheets/container_rule.rs b/components/style/stylesheets/container_rule.rs new file mode 100644 index 00000000000..e2e21735b3b --- /dev/null +++ b/components/style/stylesheets/container_rule.rs @@ -0,0 +1,79 @@ +/* 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/. */ + +//! A [`@container`][container] rule. +//! +//! [container]: https://drafts.csswg.org/css-contain-3/#container-rule + +use crate::media_queries::MediaCondition; +use crate::shared_lock::{ + DeepCloneParams, DeepCloneWithLock, Locked, SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard, +}; +use crate::values::specified::ContainerName; +use crate::str::CssStringWriter; +use crate::stylesheets::CssRules; +use cssparser::SourceLocation; +#[cfg(feature = "gecko")] +use malloc_size_of::{MallocSizeOfOps, MallocUnconditionalShallowSizeOf}; +use servo_arc::Arc; +use std::fmt::{self, Write}; +use style_traits::{CssWriter, ToCss}; + +/// A container rule. +#[derive(Debug, ToShmem)] +pub struct ContainerRule { + /// The container name. + pub name: ContainerName, + /// The container query. + pub condition: ContainerCondition, + /// The nested rules inside the block. + pub rules: Arc>, + /// The source position where this rule was found. + pub source_location: SourceLocation, +} + +impl ContainerRule { + /// Measure heap usage. + #[cfg(feature = "gecko")] + pub fn size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize { + // Measurement of other fields may be added later. + self.rules.unconditional_shallow_size_of(ops) + + self.rules.read_with(guard).size_of(guard, ops) + } +} + +impl DeepCloneWithLock for ContainerRule { + fn deep_clone_with_lock( + &self, + lock: &SharedRwLock, + guard: &SharedRwLockReadGuard, + params: &DeepCloneParams, + ) -> 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(), + } + } +} + +impl ToCssWithGuard for ContainerRule { + fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result { + dest.write_str("@container ")?; + { + let mut writer = CssWriter::new(dest); + if !self.name.is_none() { + self.name.to_css(&mut writer)?; + writer.write_char(' ')?; + } + self.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 = MediaCondition; diff --git a/components/style/stylesheets/layer_rule.rs b/components/style/stylesheets/layer_rule.rs index ebff5bb9add..6a91f406f62 100644 --- a/components/style/stylesheets/layer_rule.rs +++ b/components/style/stylesheets/layer_rule.rs @@ -2,7 +2,7 @@ * 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/. */ -//! A [`@layer`][layer] urle. +//! A [`@layer`][layer] rule. //! //! [layer]: https://drafts.csswg.org/css-cascade-5/#layering diff --git a/components/style/stylesheets/media_rule.rs b/components/style/stylesheets/media_rule.rs index ea7441a5c09..cde60a16bf7 100644 --- a/components/style/stylesheets/media_rule.rs +++ b/components/style/stylesheets/media_rule.rs @@ -2,7 +2,7 @@ * 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/. */ -//! An [`@media`][media] urle. +//! An [`@media`][media] rule. //! //! [media]: https://drafts.csswg.org/css-conditional/#at-ruledef-media @@ -18,7 +18,7 @@ use servo_arc::Arc; use std::fmt::{self, Write}; use style_traits::{CssWriter, ToCss}; -/// An [`@media`][media] urle. +/// An [`@media`][media] rule. /// /// [media]: https://drafts.csswg.org/css-conditional/#at-ruledef-media #[derive(Debug, ToShmem)] diff --git a/components/style/stylesheets/mod.rs b/components/style/stylesheets/mod.rs index d48b7504797..c72f182ebf0 100644 --- a/components/style/stylesheets/mod.rs +++ b/components/style/stylesheets/mod.rs @@ -12,6 +12,7 @@ pub mod font_feature_values_rule; pub mod import_rule; pub mod keyframes_rule; pub mod layer_rule; +pub mod container_rule; mod loader; mod media_rule; mod namespace_rule; @@ -53,6 +54,7 @@ pub use self::import_rule::ImportRule; pub use self::keyframes_rule::KeyframesRule; pub use self::layer_rule::{LayerBlockRule, LayerStatementRule}; pub use self::loader::StylesheetLoader; +pub use self::container_rule::ContainerRule; pub use self::media_rule::MediaRule; pub use self::namespace_rule::NamespaceRule; pub use self::origin::{Origin, OriginSet, OriginSetIterator, PerOrigin, PerOriginIter}; @@ -253,6 +255,7 @@ pub enum CssRule { Import(Arc>), Style(Arc>), Media(Arc>), + Container(Arc>), FontFace(Arc>), FontFeatureValues(Arc>), CounterStyle(Arc>), @@ -287,6 +290,10 @@ impl CssRule { lock.unconditional_shallow_size_of(ops) + lock.read_with(guard).size_of(guard, ops) }, + CssRule::Container(ref lock) => { + lock.unconditional_shallow_size_of(ops) + lock.read_with(guard).size_of(guard, ops) + }, + CssRule::FontFace(_) => 0, CssRule::FontFeatureValues(_) => 0, CssRule::CounterStyle(_) => 0, @@ -344,6 +351,7 @@ pub enum CssRuleType { LayerBlock = 16, LayerStatement = 17, ScrollTimeline = 18, + Container = 19, } #[allow(missing_docs)] @@ -373,6 +381,7 @@ impl CssRule { CssRule::LayerBlock(_) => CssRuleType::LayerBlock, CssRule::LayerStatement(_) => CssRuleType::LayerStatement, CssRule::ScrollTimeline(_) => CssRuleType::ScrollTimeline, + CssRule::Container(_) => CssRuleType::Container, } } @@ -460,6 +469,12 @@ impl DeepCloneWithLock for CssRule { lock.wrap(rule.deep_clone_with_lock(lock, guard, params)), )) }, + CssRule::Container(ref arc) => { + let rule = arc.read_with(guard); + CssRule::Container(Arc::new( + lock.wrap(rule.deep_clone_with_lock(lock, guard, params)), + )) + }, CssRule::Media(ref arc) => { let rule = arc.read_with(guard); CssRule::Media(Arc::new( @@ -545,6 +560,7 @@ impl ToCssWithGuard for CssRule { CssRule::LayerBlock(ref lock) => lock.read_with(guard).to_css(guard, dest), CssRule::LayerStatement(ref lock) => lock.read_with(guard).to_css(guard, dest), CssRule::ScrollTimeline(ref lock) => lock.read_with(guard).to_css(guard, dest), + CssRule::Container(ref lock) => lock.read_with(guard).to_css(guard, dest), } } } diff --git a/components/style/stylesheets/rule_parser.rs b/components/style/stylesheets/rule_parser.rs index 9c0095bc32d..72fcdefd6f4 100644 --- a/components/style/stylesheets/rule_parser.rs +++ b/components/style/stylesheets/rule_parser.rs @@ -13,6 +13,7 @@ use crate::properties::parse_property_declaration_list; use crate::selector_parser::{SelectorImpl, SelectorParser}; use crate::shared_lock::{Locked, SharedRwLock}; use crate::str::starts_with_ignore_ascii_case; +use crate::stylesheets::container_rule::{ContainerRule, ContainerCondition}; use crate::stylesheets::document_rule::DocumentCondition; use crate::stylesheets::font_feature_values_rule::parse_family_name_list; use crate::stylesheets::import_rule::ImportLayer; @@ -28,6 +29,7 @@ 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, @@ -162,6 +164,8 @@ pub enum AtRulePrelude { CounterStyle(CustomIdent), /// A @media rule prelude, with its media queries. Media(Arc>), + /// A @container rule prelude. + Container(ContainerName, ContainerCondition), /// An @supports rule, with its conditional Supports(SupportsCondition), /// A @viewport rule prelude. @@ -433,6 +437,15 @@ impl<'a, 'b, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'b> { "font-face" => { AtRulePrelude::FontFace }, + "container" if static_prefs::pref!("layout.css.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)?; + AtRulePrelude::Container(name, condition) + }, "layer" => { let names = input.try_parse(|input| { input.parse_comma_separated(|input| { @@ -614,6 +627,16 @@ impl<'a, 'b, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'b> { }, )))) }, + AtRulePrelude::Container(name, 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(), + }, + )))) + }, AtRulePrelude::Layer(names) => { let name = match names.len() { 0 | 1 => names.into_iter().next(), diff --git a/components/style/stylesheets/rules_iterator.rs b/components/style/stylesheets/rules_iterator.rs index 417185953a0..c86d4dc96c3 100644 --- a/components/style/stylesheets/rules_iterator.rs +++ b/components/style/stylesheets/rules_iterator.rs @@ -88,6 +88,10 @@ where } Some(doc_rule.rules.read_with(guard).0.iter()) }, + CssRule::Container(ref lock) => { + let container_rule = lock.read_with(guard); + Some(container_rule.rules.read_with(guard).0.iter()) + }, CssRule::Media(ref lock) => { let media_rule = lock.read_with(guard); if !C::process_media(guard, device, quirks_mode, media_rule) { diff --git a/components/style/stylesheets/stylesheet.rs b/components/style/stylesheets/stylesheet.rs index aaf350b684d..1410634c281 100644 --- a/components/style/stylesheets/stylesheet.rs +++ b/components/style/stylesheets/stylesheet.rs @@ -370,6 +370,7 @@ impl SanitizationKind { CssRule::Media(..) | CssRule::Supports(..) | CssRule::Import(..) | + CssRule::Container(..) | // TODO(emilio): Perhaps Layer should not be always sanitized? But // we sanitize @media and co, so this seems safer for now. CssRule::LayerStatement(..) | diff --git a/components/style/stylist.rs b/components/style/stylist.rs index 4c19a41bcd6..f3d0e2bf4cb 100644 --- a/components/style/stylist.rs +++ b/components/style/stylist.rs @@ -2810,6 +2810,7 @@ impl CascadeData { CssRule::Style(..) | CssRule::Namespace(..) | CssRule::FontFace(..) | + CssRule::Container(..) | CssRule::CounterStyle(..) | CssRule::Supports(..) | CssRule::Keyframes(..) |