diff options
Diffstat (limited to 'components/style')
-rw-r--r-- | components/style/error_reporting.rs | 10 | ||||
-rw-r--r-- | components/style/gecko/arc_types.rs | 8 | ||||
-rw-r--r-- | components/style/invalidation/stylesheets.rs | 7 | ||||
-rw-r--r-- | components/style/properties_and_values/mod.rs | 1 | ||||
-rw-r--r-- | components/style/properties_and_values/rule.rs | 245 | ||||
-rw-r--r-- | components/style/stylesheets/mod.rs | 15 | ||||
-rw-r--r-- | components/style/stylesheets/property_rule.rs | 5 | ||||
-rw-r--r-- | components/style/stylesheets/rule_parser.rs | 18 | ||||
-rw-r--r-- | components/style/stylesheets/rules_iterator.rs | 1 | ||||
-rw-r--r-- | components/style/stylesheets/stylesheet.rs | 1 | ||||
-rw-r--r-- | components/style/stylist.rs | 2 |
11 files changed, 310 insertions, 3 deletions
diff --git a/components/style/error_reporting.rs b/components/style/error_reporting.rs index 042838f3394..258c7c44ef4 100644 --- a/components/style/error_reporting.rs +++ b/components/style/error_reporting.rs @@ -22,6 +22,8 @@ pub enum ContextualParseError<'a> { ParseError<'a>, Option<&'a SelectorList<SelectorImpl>>, ), + /// A property descriptor was not recognized. + UnsupportedPropertyDescriptor(&'a str, ParseError<'a>), /// A font face descriptor was not recognized. UnsupportedFontFaceDescriptor(&'a str, ParseError<'a>), /// A font feature values descriptor was not recognized. @@ -135,6 +137,14 @@ impl<'a> fmt::Display for ContextualParseError<'a> { write!(f, "Unsupported property declaration: '{}', ", decl)?; parse_error_to_str(err, f) }, + ContextualParseError::UnsupportedPropertyDescriptor(decl, ref err) => { + write!( + f, + "Unsupported @property descriptor declaration: '{}', ", + decl + )?; + parse_error_to_str(err, f) + }, ContextualParseError::UnsupportedFontFaceDescriptor(decl, ref err) => { write!( f, diff --git a/components/style/gecko/arc_types.rs b/components/style/gecko/arc_types.rs index b2835abdfd0..707266d2bec 100644 --- a/components/style/gecko/arc_types.rs +++ b/components/style/gecko/arc_types.rs @@ -16,7 +16,7 @@ use crate::stylesheets::keyframes_rule::Keyframe; use crate::stylesheets::{ ContainerRule, CounterStyleRule, CssRules, DocumentRule, FontFaceRule, FontFeatureValuesRule, FontPaletteValuesRule, ImportRule, KeyframesRule, LayerBlockRule, LayerStatementRule, - MediaRule, NamespaceRule, PageRule, StyleRule, StylesheetContents, SupportsRule, + MediaRule, NamespaceRule, PageRule, PropertyRule, StyleRule, StylesheetContents, SupportsRule, }; use servo_arc::Arc; @@ -115,6 +115,12 @@ impl_locked_arc_ffi!( Servo_PageRule_Release ); impl_locked_arc_ffi!( + PropertyRule, + LockedPropertyRule, + Servo_PropertyRule_AddRef, + Servo_PropertyRule_Release +); +impl_locked_arc_ffi!( SupportsRule, LockedSupportsRule, Servo_SupportsRule_AddRef, diff --git a/components/style/invalidation/stylesheets.rs b/components/style/invalidation/stylesheets.rs index f18fe7942c5..5fc8ec4b764 100644 --- a/components/style/invalidation/stylesheets.rs +++ b/components/style/invalidation/stylesheets.rs @@ -550,6 +550,7 @@ impl StylesheetInvalidationSet { }, CounterStyle(..) | Page(..) | + Property(..) | Viewport(..) | FontFeatureValues(..) | FontPaletteValues(..) | @@ -633,7 +634,11 @@ impl StylesheetInvalidationSet { // existing elements. } }, - CounterStyle(..) | Page(..) | Viewport(..) | FontFeatureValues(..) | + CounterStyle(..) | + Page(..) | + Property(..) | + Viewport(..) | + FontFeatureValues(..) | FontPaletteValues(..) => { debug!( " > Found unsupported rule, marking the whole subtree \ diff --git a/components/style/properties_and_values/mod.rs b/components/style/properties_and_values/mod.rs index 3590d1279cc..00f7ac98f75 100644 --- a/components/style/properties_and_values/mod.rs +++ b/components/style/properties_and_values/mod.rs @@ -6,4 +6,5 @@ //! //! https://drafts.css-houdini.org/css-properties-values-api-1/ +pub mod rule; pub mod syntax; diff --git a/components/style/properties_and_values/rule.rs b/components/style/properties_and_values/rule.rs new file mode 100644 index 00000000000..3392e68124b --- /dev/null +++ b/components/style/properties_and_values/rule.rs @@ -0,0 +1,245 @@ +/* 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/. */ + +//! The [`@property`] at-rule. +//! +//! https://drafts.css-houdini.org/css-properties-values-api-1/#at-property-rule + +use crate::custom_properties::{Name as CustomPropertyName, SpecifiedValue}; +use crate::error_reporting::ContextualParseError; +use crate::parser::{Parse, ParserContext}; +use crate::properties_and_values::syntax::Descriptor as SyntaxDescriptor; +use crate::shared_lock::{SharedRwLockReadGuard, ToCssWithGuard}; +use crate::str::CssStringWriter; +use crate::values::serialize_atom_name; +use cssparser::{ + AtRuleParser, CowRcStr, DeclarationParser, ParseErrorKind, Parser, QualifiedRuleParser, + RuleBodyItemParser, RuleBodyParser, SourceLocation, +}; +use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; +use selectors::parser::SelectorParseErrorKind; +use servo_arc::Arc; +use std::fmt::{self, Write}; +use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss}; +use to_shmem::{SharedMemoryBuilder, ToShmem}; + +/// Parse the block inside a `@property` rule. +/// +/// Valid `@property` rules result in a registered custom property, as if `registerProperty()` had +/// been called with equivalent parameters. +pub fn parse_property_block( + context: &ParserContext, + input: &mut Parser, + name: PropertyRuleName, + location: SourceLocation, +) -> PropertyRuleData { + let mut rule = PropertyRuleData::empty(name, location); + let mut parser = PropertyRuleParser { + context, + rule: &mut rule, + }; + let mut iter = RuleBodyParser::new(input, &mut parser); + while let Some(declaration) = iter.next() { + if !context.error_reporting_enabled() { + continue; + } + if let Err((error, slice)) = declaration { + let location = error.location; + let error = if matches!( + error.kind, + ParseErrorKind::Custom(StyleParseErrorKind::PropertySyntaxField(_)) + ) { + ContextualParseError::UnsupportedValue(slice, error) + } else { + ContextualParseError::UnsupportedPropertyDescriptor(slice, error) + }; + context.log_css_error(location, error); + } + } + rule +} + +struct PropertyRuleParser<'a, 'b: 'a> { + context: &'a ParserContext<'b>, + rule: &'a mut PropertyRuleData, +} + +/// Default methods reject all at rules. +impl<'a, 'b, 'i> AtRuleParser<'i> for PropertyRuleParser<'a, 'b> { + type Prelude = (); + type AtRule = (); + type Error = StyleParseErrorKind<'i>; +} + +impl<'a, 'b, 'i> QualifiedRuleParser<'i> for PropertyRuleParser<'a, 'b> { + type Prelude = (); + type QualifiedRule = (); + type Error = StyleParseErrorKind<'i>; +} + +impl<'a, 'b, 'i> RuleBodyItemParser<'i, (), StyleParseErrorKind<'i>> + for PropertyRuleParser<'a, 'b> +{ + fn parse_qualified(&self) -> bool { + false + } + fn parse_declarations(&self) -> bool { + true + } +} + +macro_rules! property_descriptors { + ( + $( #[$doc: meta] $name: tt $ident: ident: $ty: ty, )* + ) => { + /// Data inside a `@property` rule. + /// + /// <https://drafts.css-houdini.org/css-properties-values-api-1/#at-property-rule> + #[derive(Clone, Debug, PartialEq)] + pub struct PropertyRuleData { + /// The custom property name. + pub name: PropertyRuleName, + + $( + #[$doc] + pub $ident: Option<$ty>, + )* + + /// Line and column of the @property rule source code. + pub source_location: SourceLocation, + } + + impl PropertyRuleData { + /// Create an empty property rule + pub fn empty(name: PropertyRuleName, source_location: SourceLocation) -> Self { + PropertyRuleData { + name, + $( + $ident: None, + )* + source_location, + } + } + + /// Serialization of declarations in PropertyRuleData + pub fn decl_to_css(&self, dest: &mut CssStringWriter) -> fmt::Result { + $( + if let Some(ref value) = self.$ident { + dest.write_str(concat!($name, ": "))?; + value.to_css(&mut CssWriter::new(dest))?; + dest.write_str("; ")?; + } + )* + Ok(()) + } + } + + impl<'a, 'b, 'i> DeclarationParser<'i> for PropertyRuleParser<'a, 'b> { + type Declaration = (); + type Error = StyleParseErrorKind<'i>; + + fn parse_value<'t>( + &mut self, + name: CowRcStr<'i>, + input: &mut Parser<'i, 't>, + ) -> Result<(), ParseError<'i>> { + match_ignore_ascii_case! { &*name, + $( + $name => { + // DeclarationParser also calls parse_entirely so we’d normally not need + // to, but in this case we do because we set the value as a side effect + // rather than returning it. + let value = input.parse_entirely(|i| Parse::parse(self.context, i))?; + self.rule.$ident = Some(value) + }, + )* + _ => return Err(input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone()))), + } + Ok(()) + } + } + } +} + +#[cfg(feature = "gecko")] +property_descriptors! { + /// <https://drafts.css-houdini.org/css-properties-values-api-1/#the-syntax-descriptor> + "syntax" syntax: SyntaxDescriptor, + + /// <https://drafts.css-houdini.org/css-properties-values-api-1/#inherits-descriptor> + "inherits" inherits: Inherits, + + /// <https://drafts.css-houdini.org/css-properties-values-api-1/#initial-value-descriptor> + "initial-value" initial_value: InitialValue, +} + +impl PropertyRuleData { + /// Measure heap usage. + #[cfg(feature = "gecko")] + pub fn size_of(&self, _guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize { + self.name.0.size_of(ops) + + self.syntax.size_of(ops) + + self.inherits.size_of(ops) + + if let Some(ref initial_value) = self.initial_value { + initial_value.size_of(ops) + } else { + 0 + } + } +} + +impl ToCssWithGuard for PropertyRuleData { + /// <https://drafts.css-houdini.org/css-properties-values-api-1/#serialize-a-csspropertyrule> + fn to_css(&self, _guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result { + dest.write_str("@property ")?; + self.name.to_css(&mut CssWriter::new(dest))?; + dest.write_str(" { ")?; + self.decl_to_css(dest)?; + dest.write_char('}') + } +} + +impl ToShmem for PropertyRuleData { + fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> to_shmem::Result<Self> { + Err(String::from( + "ToShmem failed for PropertyRule: cannot handle @property rules", + )) + } +} + +/// A custom property name wrapper that includes the `--` prefix in its serialization +#[derive(Clone, Debug, PartialEq)] +pub struct PropertyRuleName(pub Arc<CustomPropertyName>); + +impl ToCss for PropertyRuleName { + fn to_css<W: Write>(&self, dest: &mut CssWriter<W>) -> fmt::Result { + dest.write_str("--")?; + serialize_atom_name(&self.0, dest) + } +} + +/// <https://drafts.css-houdini.org/css-properties-values-api-1/#inherits-descriptor> +#[derive(Clone, Debug, MallocSizeOf, Parse, PartialEq, ToCss)] +pub enum Inherits { + /// `true` value for the `inherits` descriptor + True, + /// `false` value for the `inherits` descriptor + False, +} + +/// Specifies the initial value of the custom property registration represented by the @property +/// rule, controlling the property’s initial value. +/// +/// The SpecifiedValue is wrapped in an Arc to avoid copying when using it. +pub type InitialValue = Arc<SpecifiedValue>; + +impl Parse for InitialValue { + fn parse<'i, 't>( + _context: &ParserContext, + input: &mut Parser<'i, 't>, + ) -> Result<Self, ParseError<'i>> { + input.skip_whitespace(); + SpecifiedValue::parse(input) + } +} diff --git a/components/style/stylesheets/mod.rs b/components/style/stylesheets/mod.rs index 4c6027219ba..c5d2ec8b8e8 100644 --- a/components/style/stylesheets/mod.rs +++ b/components/style/stylesheets/mod.rs @@ -19,6 +19,7 @@ mod media_rule; mod namespace_rule; pub mod origin; mod page_rule; +mod property_rule; mod rule_list; mod rule_parser; mod rules_iterator; @@ -61,6 +62,7 @@ pub use self::media_rule::MediaRule; pub use self::namespace_rule::NamespaceRule; pub use self::origin::{Origin, OriginSet, OriginSetIterator, PerOrigin, PerOriginIter}; pub use self::page_rule::{PageRule, PageSelector, PageSelectors}; +pub use self::property_rule::PropertyRule; pub use self::rule_list::{CssRules, CssRulesHelpers}; pub use self::rule_parser::{InsertRuleContext, State, TopLevelRuleParser}; pub use self::rules_iterator::{AllRules, EffectiveRules}; @@ -261,6 +263,7 @@ pub enum CssRule { Keyframes(Arc<Locked<KeyframesRule>>), Supports(Arc<Locked<SupportsRule>>), Page(Arc<Locked<PageRule>>), + Property(Arc<Locked<PropertyRule>>), Document(Arc<Locked<DocumentRule>>), LayerBlock(Arc<Locked<LayerBlockRule>>), LayerStatement(Arc<Locked<LayerStatementRule>>), @@ -306,6 +309,10 @@ impl CssRule { lock.unconditional_shallow_size_of(ops) + lock.read_with(guard).size_of(guard, ops) }, + CssRule::Property(ref lock) => { + lock.unconditional_shallow_size_of(ops) + lock.read_with(guard).size_of(guard, ops) + }, + CssRule::Document(ref lock) => { lock.unconditional_shallow_size_of(ops) + lock.read_with(guard).size_of(guard, ops) }, @@ -350,6 +357,8 @@ pub enum CssRuleType { LayerStatement = 17, Container = 18, FontPaletteValues = 19, + // 20 is an arbitrary number to use for Property. + Property = 20, } impl CssRuleType { @@ -413,6 +422,7 @@ impl CssRule { CssRule::Viewport(_) => CssRuleType::Viewport, CssRule::Supports(_) => CssRuleType::Supports, CssRule::Page(_) => CssRuleType::Page, + CssRule::Property(_) => CssRuleType::Property, CssRule::Document(_) => CssRuleType::Document, CssRule::LayerBlock(_) => CssRuleType::LayerBlock, CssRule::LayerStatement(_) => CssRuleType::LayerStatement, @@ -545,6 +555,10 @@ impl DeepCloneWithLock for CssRule { lock.wrap(rule.deep_clone_with_lock(lock, guard, params)), )) }, + CssRule::Property(ref arc) => { + let rule = arc.read_with(guard); + CssRule::Property(Arc::new(lock.wrap(rule.clone()))) + }, CssRule::Document(ref arc) => { let rule = arc.read_with(guard); CssRule::Document(Arc::new( @@ -583,6 +597,7 @@ impl ToCssWithGuard for CssRule { CssRule::Media(ref lock) => lock.read_with(guard).to_css(guard, dest), CssRule::Supports(ref lock) => lock.read_with(guard).to_css(guard, dest), CssRule::Page(ref lock) => lock.read_with(guard).to_css(guard, dest), + CssRule::Property(ref lock) => lock.read_with(guard).to_css(guard, dest), CssRule::Document(ref lock) => lock.read_with(guard).to_css(guard, dest), CssRule::LayerBlock(ref lock) => lock.read_with(guard).to_css(guard, dest), CssRule::LayerStatement(ref lock) => lock.read_with(guard).to_css(guard, dest), diff --git a/components/style/stylesheets/property_rule.rs b/components/style/stylesheets/property_rule.rs new file mode 100644 index 00000000000..1d1c1c982e9 --- /dev/null +++ b/components/style/stylesheets/property_rule.rs @@ -0,0 +1,5 @@ +/* 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/. */ + +pub use crate::properties_and_values::rule::PropertyRuleData as PropertyRule; diff --git a/components/style/stylesheets/rule_parser.rs b/components/style/stylesheets/rule_parser.rs index 1e51e2b65b9..f1bd4c13fe0 100644 --- a/components/style/stylesheets/rule_parser.rs +++ b/components/style/stylesheets/rule_parser.rs @@ -5,6 +5,7 @@ //! Parsing of the stylesheet contents. use crate::counter_style::{parse_counter_style_body, parse_counter_style_name_definition}; +use crate::custom_properties::parse_name as parse_custom_property_name; use crate::error_reporting::ContextualParseError; use crate::font_face::parse_font_face_block; use crate::media_queries::MediaList; @@ -12,6 +13,7 @@ use crate::parser::{Parse, ParserContext}; use crate::properties::declaration_block::{ parse_property_declaration_list, DeclarationParserState, PropertyDeclarationBlock, }; +use crate::properties_and_values::rule::{parse_property_block, PropertyRuleName}; use crate::selector_parser::{SelectorImpl, SelectorParser}; use crate::shared_lock::{Locked, SharedRwLock}; use crate::str::starts_with_ignore_ascii_case; @@ -30,7 +32,7 @@ use crate::stylesheets::{ }; use crate::values::computed::font::FamilyName; use crate::values::{CssUrl, CustomIdent, DashedIdent, KeyframesName}; -use crate::{Namespace, Prefix}; +use crate::{Atom, Namespace, Prefix}; use cssparser::{ AtRuleParser, BasicParseError, BasicParseErrorKind, CowRcStr, DeclarationParser, Parser, ParserState, QualifiedRuleParser, RuleBodyItemParser, RuleBodyParser, SourceLocation, @@ -201,6 +203,8 @@ pub enum AtRulePrelude { Keyframes(KeyframesName, Option<VendorPrefix>), /// A @page rule prelude, with its page name if it exists. Page(PageSelectors), + /// A @property rule prelude. + Property(PropertyRuleName), /// A @document rule, with its conditional. Document(DocumentCondition), /// A @import rule prelude. @@ -600,6 +604,13 @@ impl<'a, 'b, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'b, 'i> { input.try_parse(|i| PageSelectors::parse(self.context, i)).unwrap_or_default() ) }, + "property" if static_prefs::pref!("layout.css.properties-and-values.enabled") => { + let name = input.expect_ident_cloned()?; + let name = parse_custom_property_name(&name).map_err(|_| { + input.new_custom_error(StyleParseErrorKind::UnexpectedIdent(name.clone())) + })?; + AtRulePrelude::Property(PropertyRuleName(Arc::new(Atom::from(name)))) + }, "-moz-document" if cfg!(feature = "gecko") => { let cond = DocumentCondition::parse(self.context, input)?; AtRulePrelude::Document(cond) @@ -704,6 +715,11 @@ impl<'a, 'b, 'i> AtRuleParser<'i> for NestedRuleParser<'a, 'b, 'i> { source_location: start.source_location(), }))) }, + AtRulePrelude::Property(name) => self.nest_for_rule(CssRuleType::Property, |p| { + CssRule::Property(Arc::new(p.shared_lock.wrap( + parse_property_block(&p.context, input, name, start.source_location()), + ))) + }), AtRulePrelude::Document(condition) => { if !cfg!(feature = "gecko") { unreachable!() diff --git a/components/style/stylesheets/rules_iterator.rs b/components/style/stylesheets/rules_iterator.rs index 59736ab09e0..ec0ceb7398e 100644 --- a/components/style/stylesheets/rules_iterator.rs +++ b/components/style/stylesheets/rules_iterator.rs @@ -68,6 +68,7 @@ where CssRule::Viewport(_) | CssRule::Keyframes(_) | CssRule::Page(_) | + CssRule::Property(_) | CssRule::LayerStatement(_) | CssRule::FontFeatureValues(_) | CssRule::FontPaletteValues(_) => None, diff --git a/components/style/stylesheets/stylesheet.rs b/components/style/stylesheets/stylesheet.rs index 2089468c3de..2ca75f3c86d 100644 --- a/components/style/stylesheets/stylesheet.rs +++ b/components/style/stylesheets/stylesheet.rs @@ -377,6 +377,7 @@ impl SanitizationKind { CssRule::Keyframes(..) | CssRule::Page(..) | + CssRule::Property(..) | CssRule::FontFeatureValues(..) | CssRule::FontPaletteValues(..) | CssRule::Viewport(..) | diff --git a/components/style/stylist.rs b/components/style/stylist.rs index 8f90c09925d..04a6a27e920 100644 --- a/components/style/stylist.rs +++ b/components/style/stylist.rs @@ -2849,6 +2849,7 @@ impl CascadeData { self.extra_data .add_page(guard, rule, containing_rule_state.layer_id)?; }, + // TODO: Handle CssRule::Property CssRule::Viewport(..) => {}, _ => { handled = false; @@ -3090,6 +3091,7 @@ impl CascadeData { CssRule::Supports(..) | CssRule::Keyframes(..) | CssRule::Page(..) | + CssRule::Property(..) | CssRule::Viewport(..) | CssRule::Document(..) | CssRule::LayerBlock(..) | |