| //! CSS declarations. |
| |
| use std::borrow::Cow; |
| use std::ops::Range; |
| |
| use crate::context::{DeclarationContext, PropertyHandlerContext}; |
| use crate::error::{ParserError, PrinterError, PrinterErrorKind}; |
| use crate::parser::ParserOptions; |
| use crate::printer::Printer; |
| use crate::properties::box_shadow::BoxShadowHandler; |
| use crate::properties::custom::{CustomProperty, CustomPropertyName}; |
| use crate::properties::masking::MaskHandler; |
| use crate::properties::text::{Direction, UnicodeBidi}; |
| use crate::properties::{ |
| align::AlignHandler, |
| animation::AnimationHandler, |
| background::BackgroundHandler, |
| border::BorderHandler, |
| contain::ContainerHandler, |
| display::DisplayHandler, |
| flex::FlexHandler, |
| font::FontHandler, |
| grid::GridHandler, |
| list::ListStyleHandler, |
| margin_padding::*, |
| outline::OutlineHandler, |
| overflow::OverflowHandler, |
| position::PositionHandler, |
| prefix_handler::{FallbackHandler, PrefixHandler}, |
| size::SizeHandler, |
| text::TextDecorationHandler, |
| transform::TransformHandler, |
| transition::TransitionHandler, |
| ui::ColorSchemeHandler, |
| }; |
| use crate::properties::{Property, PropertyId}; |
| use crate::selector::SelectorList; |
| use crate::traits::{PropertyHandler, ToCss}; |
| use crate::values::ident::DashedIdent; |
| use crate::values::string::CowArcStr; |
| #[cfg(feature = "visitor")] |
| use crate::visitor::Visit; |
| use cssparser::*; |
| use indexmap::IndexMap; |
| use smallvec::SmallVec; |
| |
| /// A CSS declaration block. |
| /// |
| /// Properties are separated into a list of `!important` declararations, |
| /// and a list of normal declarations. This reduces memory usage compared |
| /// with storing a boolean along with each property. |
| #[derive(Debug, PartialEq, Clone, Default)] |
| #[cfg_attr(feature = "visitor", derive(Visit), visit(visit_declaration_block, PROPERTIES))] |
| #[cfg_attr(feature = "into_owned", derive(static_self::IntoOwned))] |
| #[cfg_attr( |
| feature = "serde", |
| derive(serde::Serialize, serde::Deserialize), |
| serde(rename_all = "camelCase") |
| )] |
| #[cfg_attr(feature = "jsonschema", derive(schemars::JsonSchema))] |
| pub struct DeclarationBlock<'i> { |
| /// A list of `!important` declarations in the block. |
| #[cfg_attr(feature = "serde", serde(borrow, default))] |
| pub important_declarations: Vec<Property<'i>>, |
| /// A list of normal declarations in the block. |
| #[cfg_attr(feature = "serde", serde(default))] |
| pub declarations: Vec<Property<'i>>, |
| } |
| |
| impl<'i> DeclarationBlock<'i> { |
| /// Parses a declaration block from CSS syntax. |
| pub fn parse<'a, 'o, 't>( |
| input: &mut Parser<'i, 't>, |
| options: &'a ParserOptions<'o, 'i>, |
| ) -> Result<Self, ParseError<'i, ParserError<'i>>> { |
| let mut important_declarations = DeclarationList::new(); |
| let mut declarations = DeclarationList::new(); |
| let mut decl_parser = PropertyDeclarationParser { |
| important_declarations: &mut important_declarations, |
| declarations: &mut declarations, |
| options, |
| }; |
| let mut parser = RuleBodyParser::new(input, &mut decl_parser); |
| while let Some(res) = parser.next() { |
| if let Err((err, _)) = res { |
| if options.error_recovery { |
| options.warn(err); |
| continue; |
| } |
| return Err(err); |
| } |
| } |
| |
| Ok(DeclarationBlock { |
| important_declarations, |
| declarations, |
| }) |
| } |
| |
| /// Parses a declaration block from a string. |
| pub fn parse_string<'o>( |
| input: &'i str, |
| options: ParserOptions<'o, 'i>, |
| ) -> Result<Self, ParseError<'i, ParserError<'i>>> { |
| let mut input = ParserInput::new(input); |
| let mut parser = Parser::new(&mut input); |
| let result = Self::parse(&mut parser, &options)?; |
| parser.expect_exhausted()?; |
| Ok(result) |
| } |
| |
| /// Returns an empty declaration block. |
| pub fn new() -> Self { |
| Self { |
| declarations: vec![], |
| important_declarations: vec![], |
| } |
| } |
| |
| /// Returns the total number of declarations in the block. |
| pub fn len(&self) -> usize { |
| self.declarations.len() + self.important_declarations.len() |
| } |
| } |
| |
| impl<'i> ToCss for DeclarationBlock<'i> { |
| fn to_css<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError> |
| where |
| W: std::fmt::Write, |
| { |
| let len = self.declarations.len() + self.important_declarations.len(); |
| let mut i = 0; |
| |
| macro_rules! write { |
| ($decls: expr, $important: literal) => { |
| for decl in &$decls { |
| decl.to_css(dest, $important)?; |
| if i != len - 1 { |
| dest.write_char(';')?; |
| dest.whitespace()?; |
| } |
| i += 1; |
| } |
| }; |
| } |
| |
| write!(self.declarations, false); |
| write!(self.important_declarations, true); |
| Ok(()) |
| } |
| } |
| |
| impl<'i> DeclarationBlock<'i> { |
| /// Writes the declarations to a CSS block, including starting and ending braces. |
| pub fn to_css_block<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError> |
| where |
| W: std::fmt::Write, |
| { |
| dest.whitespace()?; |
| dest.write_char('{')?; |
| dest.indent(); |
| dest.newline()?; |
| |
| self.to_css_declarations(dest, false, &parcel_selectors::SelectorList(SmallVec::new()), 0)?; |
| |
| dest.dedent(); |
| dest.newline()?; |
| dest.write_char('}') |
| } |
| |
| pub(crate) fn has_printable_declarations(&self) -> bool { |
| if self.len() > 1 { |
| return true; |
| } |
| |
| if self.declarations.len() == 1 { |
| !matches!(self.declarations[0], crate::properties::Property::Composes(_)) |
| } else if self.important_declarations.len() == 1 { |
| !matches!(self.important_declarations[0], crate::properties::Property::Composes(_)) |
| } else { |
| false |
| } |
| } |
| |
| /// Writes the declarations to a CSS declaration block. |
| pub fn to_css_declarations<W>( |
| &self, |
| dest: &mut Printer<W>, |
| has_nested_rules: bool, |
| selectors: &SelectorList, |
| source_index: u32, |
| ) -> Result<(), PrinterError> |
| where |
| W: std::fmt::Write, |
| { |
| let mut i = 0; |
| let len = self.len(); |
| |
| macro_rules! write { |
| ($decls: expr, $important: literal) => { |
| for decl in &$decls { |
| // The CSS modules `composes` property is handled specially, and omitted during printing. |
| // We need to add the classes it references to the list for the selectors in this rule. |
| if let crate::properties::Property::Composes(composes) = &decl { |
| if dest.is_nested() && dest.css_module.is_some() { |
| return Err(dest.error(PrinterErrorKind::InvalidComposesNesting, composes.loc)); |
| } |
| |
| if let Some(css_module) = &mut dest.css_module { |
| css_module |
| .handle_composes(&selectors, &composes, source_index) |
| .map_err(|e| dest.error(e, composes.loc))?; |
| continue; |
| } |
| } |
| |
| if i > 0 { |
| dest.newline()?; |
| } |
| |
| decl.to_css(dest, $important)?; |
| if i != len - 1 || !dest.minify || has_nested_rules { |
| dest.write_char(';')?; |
| } |
| |
| i += 1; |
| } |
| }; |
| } |
| |
| write!(self.declarations, false); |
| write!(self.important_declarations, true); |
| Ok(()) |
| } |
| } |
| |
| impl<'i> DeclarationBlock<'i> { |
| pub(crate) fn minify( |
| &mut self, |
| handler: &mut DeclarationHandler<'i>, |
| important_handler: &mut DeclarationHandler<'i>, |
| context: &mut PropertyHandlerContext<'i, '_>, |
| ) { |
| macro_rules! handle { |
| ($decls: expr, $handler: expr, $important: literal) => { |
| for decl in $decls.iter() { |
| context.is_important = $important; |
| let handled = $handler.handle_property(decl, context); |
| |
| if !handled { |
| $handler.decls.push(decl.clone()); |
| } |
| } |
| }; |
| } |
| |
| handle!(self.important_declarations, important_handler, true); |
| handle!(self.declarations, handler, false); |
| |
| handler.finalize(context); |
| important_handler.finalize(context); |
| self.important_declarations = std::mem::take(&mut important_handler.decls); |
| self.declarations = std::mem::take(&mut handler.decls); |
| } |
| |
| /// Returns whether the declaration block is empty. |
| pub fn is_empty(&self) -> bool { |
| return self.declarations.is_empty() && self.important_declarations.is_empty(); |
| } |
| |
| pub(crate) fn property_location<'t>( |
| &self, |
| input: &mut Parser<'i, 't>, |
| index: usize, |
| ) -> Result<(Range<SourceLocation>, Range<SourceLocation>), ParseError<'i, ParserError<'i>>> { |
| // Skip to the requested property index. |
| for _ in 0..index { |
| input.expect_ident()?; |
| input.expect_colon()?; |
| input.parse_until_after(Delimiter::Semicolon, |parser| { |
| while parser.next().is_ok() {} |
| Ok(()) |
| })?; |
| } |
| |
| // Get property name range. |
| input.skip_whitespace(); |
| let key_start = input.current_source_location(); |
| input.expect_ident()?; |
| let key_end = input.current_source_location(); |
| let key_range = key_start..key_end; |
| |
| input.expect_colon()?; |
| input.skip_whitespace(); |
| |
| // Get value range. |
| let val_start = input.current_source_location(); |
| input.parse_until_before(Delimiter::Semicolon, |parser| { |
| while parser.next().is_ok() {} |
| Ok(()) |
| })?; |
| let val_end = input.current_source_location(); |
| let val_range = val_start..val_end; |
| |
| Ok((key_range, val_range)) |
| } |
| } |
| |
| impl<'i> DeclarationBlock<'i> { |
| /// Returns an iterator over all properties in the declaration. |
| pub fn iter(&self) -> impl std::iter::DoubleEndedIterator<Item = (&Property<'i>, bool)> { |
| self |
| .declarations |
| .iter() |
| .map(|property| (property, false)) |
| .chain(self.important_declarations.iter().map(|property| (property, true))) |
| } |
| |
| /// Returns a mutable iterator over all properties in the declaration. |
| pub fn iter_mut(&mut self) -> impl std::iter::DoubleEndedIterator<Item = &mut Property<'i>> { |
| self.declarations.iter_mut().chain(self.important_declarations.iter_mut()) |
| } |
| |
| /// Returns the value for a given property id based on the properties in this declaration block. |
| /// |
| /// If the property is a shorthand, the result will be a combined value of all of the included |
| /// longhands, or `None` if some of the longhands are not declared. Otherwise, the value will be |
| /// either an explicitly declared longhand, or a value extracted from a shorthand property. |
| pub fn get<'a>(&'a self, property_id: &PropertyId) -> Option<(Cow<'a, Property<'i>>, bool)> { |
| if property_id.is_shorthand() { |
| if let Some((shorthand, important)) = property_id.shorthand_value(&self) { |
| return Some((Cow::Owned(shorthand), important)); |
| } |
| } else { |
| for (property, important) in self.iter().rev() { |
| if property.property_id() == *property_id { |
| return Some((Cow::Borrowed(property), important)); |
| } |
| |
| if let Some(val) = property.longhand(&property_id) { |
| return Some((Cow::Owned(val), important)); |
| } |
| } |
| } |
| |
| None |
| } |
| |
| /// Sets the value and importance for a given property, replacing any existing declarations. |
| /// |
| /// If the property already exists within the declaration block, it is updated in place. Otherwise, |
| /// a new declaration is appended. When updating a longhand property and a shorthand is defined which |
| /// includes the longhand, the shorthand will be updated rather than appending a new declaration. |
| pub fn set(&mut self, property: Property<'i>, important: bool) { |
| let property_id = property.property_id(); |
| let declarations = if important { |
| // Remove any non-important properties with this id. |
| self.declarations.retain(|decl| decl.property_id() != property_id); |
| &mut self.important_declarations |
| } else { |
| // Remove any important properties with this id. |
| self.important_declarations.retain(|decl| decl.property_id() != property_id); |
| &mut self.declarations |
| }; |
| |
| let longhands = property_id.longhands().unwrap_or_else(|| vec![property.property_id()]); |
| |
| for decl in declarations.iter_mut().rev() { |
| { |
| // If any of the longhands being set are in the same logical property group as any of the |
| // longhands in this property, but in a different category (i.e. logical or physical), |
| // then we cannot modify in place, and need to append a new property. |
| let id = decl.property_id(); |
| let id_longhands = id.longhands().unwrap_or_else(|| vec![id]); |
| if longhands.iter().any(|longhand| { |
| let logical_group = longhand.logical_group(); |
| let category = longhand.category(); |
| |
| logical_group.is_some() |
| && id_longhands.iter().any(|id_longhand| { |
| logical_group == id_longhand.logical_group() && category != id_longhand.category() |
| }) |
| }) { |
| break; |
| } |
| } |
| |
| if decl.property_id() == property_id { |
| *decl = property; |
| return; |
| } |
| |
| // Update shorthand. |
| if decl.set_longhand(&property).is_ok() { |
| return; |
| } |
| } |
| |
| declarations.push(property) |
| } |
| |
| /// Removes all declarations of the given property id from the declaration block. |
| /// |
| /// When removing a longhand property and a shorthand is defined which includes the longhand, |
| /// the shorthand will be split apart into its component longhand properties, minus the property |
| /// to remove. When removing a shorthand, all included longhand properties are also removed. |
| pub fn remove(&mut self, property_id: &PropertyId) { |
| fn remove<'i, 'a>(declarations: &mut Vec<Property<'i>>, property_id: &PropertyId<'a>) { |
| let longhands = property_id.longhands().unwrap_or(vec![]); |
| let mut i = 0; |
| while i < declarations.len() { |
| let replacement = { |
| let property = &declarations[i]; |
| let id = property.property_id(); |
| if id == *property_id || longhands.contains(&id) { |
| // If the property matches the requested property id, or is a longhand |
| // property that is included in the requested shorthand, remove it. |
| None |
| } else if longhands.is_empty() && id.longhands().unwrap_or(vec![]).contains(&property_id) { |
| // If this is a shorthand property that includes the requested longhand, |
| // split it apart into its component longhands, excluding the requested one. |
| Some( |
| id.longhands() |
| .unwrap() |
| .iter() |
| .filter_map(|longhand| { |
| if *longhand == *property_id { |
| None |
| } else { |
| property.longhand(longhand) |
| } |
| }) |
| .collect::<Vec<Property>>(), |
| ) |
| } else { |
| i += 1; |
| continue; |
| } |
| }; |
| |
| match replacement { |
| Some(properties) => { |
| let count = properties.len(); |
| declarations.splice(i..i + 1, properties); |
| i += count; |
| } |
| None => { |
| declarations.remove(i); |
| } |
| } |
| } |
| } |
| |
| remove(&mut self.declarations, property_id); |
| remove(&mut self.important_declarations, property_id); |
| } |
| } |
| |
| struct PropertyDeclarationParser<'a, 'o, 'i> { |
| important_declarations: &'a mut Vec<Property<'i>>, |
| declarations: &'a mut Vec<Property<'i>>, |
| options: &'a ParserOptions<'o, 'i>, |
| } |
| |
| /// Parse a declaration within {} block: `color: blue` |
| impl<'a, 'o, 'i> cssparser::DeclarationParser<'i> for PropertyDeclarationParser<'a, 'o, 'i> { |
| type Declaration = (); |
| type Error = ParserError<'i>; |
| |
| fn parse_value<'t>( |
| &mut self, |
| name: CowRcStr<'i>, |
| input: &mut cssparser::Parser<'i, 't>, |
| ) -> Result<Self::Declaration, cssparser::ParseError<'i, Self::Error>> { |
| parse_declaration( |
| name, |
| input, |
| &mut self.declarations, |
| &mut self.important_declarations, |
| &self.options, |
| ) |
| } |
| } |
| |
| /// Default methods reject all at rules. |
| impl<'a, 'o, 'i> AtRuleParser<'i> for PropertyDeclarationParser<'a, 'o, 'i> { |
| type Prelude = (); |
| type AtRule = (); |
| type Error = ParserError<'i>; |
| } |
| |
| impl<'a, 'o, 'i> QualifiedRuleParser<'i> for PropertyDeclarationParser<'a, 'o, 'i> { |
| type Prelude = (); |
| type QualifiedRule = (); |
| type Error = ParserError<'i>; |
| } |
| |
| impl<'a, 'o, 'i> RuleBodyItemParser<'i, (), ParserError<'i>> for PropertyDeclarationParser<'a, 'o, 'i> { |
| fn parse_qualified(&self) -> bool { |
| false |
| } |
| |
| fn parse_declarations(&self) -> bool { |
| true |
| } |
| } |
| |
| pub(crate) fn parse_declaration<'i, 't>( |
| name: CowRcStr<'i>, |
| input: &mut cssparser::Parser<'i, 't>, |
| declarations: &mut DeclarationList<'i>, |
| important_declarations: &mut DeclarationList<'i>, |
| options: &ParserOptions<'_, 'i>, |
| ) -> Result<(), cssparser::ParseError<'i, ParserError<'i>>> { |
| // Stop if we hit a `{` token in a non-custom property to |
| // avoid ambiguity between nested rules and declarations. |
| // https://github.com/w3c/csswg-drafts/issues/9317 |
| let property_id = PropertyId::from(CowArcStr::from(name)); |
| let mut delimiters = Delimiter::Bang; |
| if !matches!(property_id, PropertyId::Custom(CustomPropertyName::Custom(..))) { |
| delimiters = delimiters | Delimiter::CurlyBracketBlock; |
| } |
| let property = input.parse_until_before(delimiters, |input| Property::parse(property_id, input, options))?; |
| let important = input |
| .try_parse(|input| { |
| input.expect_delim('!')?; |
| input.expect_ident_matching("important") |
| }) |
| .is_ok(); |
| input.expect_exhausted()?; |
| if important { |
| important_declarations.push(property); |
| } else { |
| declarations.push(property); |
| } |
| Ok(()) |
| } |
| |
| pub(crate) type DeclarationList<'i> = Vec<Property<'i>>; |
| |
| #[derive(Default)] |
| pub(crate) struct DeclarationHandler<'i> { |
| background: BackgroundHandler<'i>, |
| border: BorderHandler<'i>, |
| outline: OutlineHandler, |
| flex: FlexHandler, |
| grid: GridHandler<'i>, |
| align: AlignHandler, |
| size: SizeHandler, |
| margin: MarginHandler<'i>, |
| padding: PaddingHandler<'i>, |
| scroll_margin: ScrollMarginHandler<'i>, |
| scroll_padding: ScrollPaddingHandler<'i>, |
| font: FontHandler<'i>, |
| text: TextDecorationHandler<'i>, |
| list: ListStyleHandler<'i>, |
| transition: TransitionHandler<'i>, |
| animation: AnimationHandler<'i>, |
| display: DisplayHandler<'i>, |
| position: PositionHandler, |
| inset: InsetHandler<'i>, |
| overflow: OverflowHandler, |
| transform: TransformHandler, |
| box_shadow: BoxShadowHandler, |
| mask: MaskHandler<'i>, |
| container: ContainerHandler<'i>, |
| color_scheme: ColorSchemeHandler, |
| fallback: FallbackHandler, |
| prefix: PrefixHandler, |
| direction: Option<Direction>, |
| unicode_bidi: Option<UnicodeBidi>, |
| custom_properties: IndexMap<DashedIdent<'i>, usize>, |
| decls: DeclarationList<'i>, |
| } |
| |
| impl<'i> DeclarationHandler<'i> { |
| pub fn handle_property( |
| &mut self, |
| property: &Property<'i>, |
| context: &mut PropertyHandlerContext<'i, '_>, |
| ) -> bool { |
| self.background.handle_property(property, &mut self.decls, context) |
| || self.border.handle_property(property, &mut self.decls, context) |
| || self.outline.handle_property(property, &mut self.decls, context) |
| || self.flex.handle_property(property, &mut self.decls, context) |
| || self.grid.handle_property(property, &mut self.decls, context) |
| || self.align.handle_property(property, &mut self.decls, context) |
| || self.size.handle_property(property, &mut self.decls, context) |
| || self.margin.handle_property(property, &mut self.decls, context) |
| || self.padding.handle_property(property, &mut self.decls, context) |
| || self.scroll_margin.handle_property(property, &mut self.decls, context) |
| || self.scroll_padding.handle_property(property, &mut self.decls, context) |
| || self.font.handle_property(property, &mut self.decls, context) |
| || self.text.handle_property(property, &mut self.decls, context) |
| || self.list.handle_property(property, &mut self.decls, context) |
| || self.transition.handle_property(property, &mut self.decls, context) |
| || self.animation.handle_property(property, &mut self.decls, context) |
| || self.display.handle_property(property, &mut self.decls, context) |
| || self.position.handle_property(property, &mut self.decls, context) |
| || self.inset.handle_property(property, &mut self.decls, context) |
| || self.overflow.handle_property(property, &mut self.decls, context) |
| || self.transform.handle_property(property, &mut self.decls, context) |
| || self.box_shadow.handle_property(property, &mut self.decls, context) |
| || self.mask.handle_property(property, &mut self.decls, context) |
| || self.container.handle_property(property, &mut self.decls, context) |
| || self.color_scheme.handle_property(property, &mut self.decls, context) |
| || self.fallback.handle_property(property, &mut self.decls, context) |
| || self.prefix.handle_property(property, &mut self.decls, context) |
| || self.handle_all(property) |
| || self.handle_custom_property(property, context) |
| } |
| |
| fn handle_custom_property( |
| &mut self, |
| property: &Property<'i>, |
| context: &mut PropertyHandlerContext<'i, '_>, |
| ) -> bool { |
| if let Property::Custom(custom) = property { |
| if context.unused_symbols.contains(custom.name.as_ref()) { |
| return true; |
| } |
| |
| if let CustomPropertyName::Custom(name) = &custom.name { |
| if let Some(index) = self.custom_properties.get(name) { |
| if self.decls[*index] == *property { |
| return true; |
| } |
| let mut custom = custom.clone(); |
| self.add_conditional_fallbacks(&mut custom, context); |
| self.decls[*index] = Property::Custom(custom); |
| } else { |
| self.custom_properties.insert(name.clone(), self.decls.len()); |
| let mut custom = custom.clone(); |
| self.add_conditional_fallbacks(&mut custom, context); |
| self.decls.push(Property::Custom(custom)); |
| } |
| |
| return true; |
| } |
| } |
| |
| false |
| } |
| |
| fn handle_all(&mut self, property: &Property<'i>) -> bool { |
| // The `all` property resets all properies except `unicode-bidi`, `direction`, and custom properties. |
| // https://drafts.csswg.org/css-cascade-5/#all-shorthand |
| match property { |
| Property::UnicodeBidi(bidi) => { |
| self.unicode_bidi = Some(*bidi); |
| true |
| } |
| Property::Direction(direction) => { |
| self.direction = Some(*direction); |
| true |
| } |
| Property::All(keyword) => { |
| let mut handler = DeclarationHandler { |
| unicode_bidi: self.unicode_bidi.clone(), |
| direction: self.direction.clone(), |
| ..Default::default() |
| }; |
| for (key, index) in self.custom_properties.drain(..) { |
| handler.custom_properties.insert(key, handler.decls.len()); |
| handler.decls.push(self.decls[index].clone()); |
| } |
| handler.decls.push(Property::All(keyword.clone())); |
| *self = handler; |
| true |
| } |
| _ => false, |
| } |
| } |
| |
| fn add_conditional_fallbacks( |
| &self, |
| custom: &mut CustomProperty<'i>, |
| context: &mut PropertyHandlerContext<'i, '_>, |
| ) { |
| if context.context != DeclarationContext::Keyframes { |
| let fallbacks = custom.value.get_fallbacks(context.targets); |
| for (condition, fallback) in fallbacks { |
| context.add_conditional_property( |
| condition, |
| Property::Custom(CustomProperty { |
| name: custom.name.clone(), |
| value: fallback, |
| }), |
| ); |
| } |
| } |
| } |
| |
| pub fn finalize(&mut self, context: &mut PropertyHandlerContext<'i, '_>) { |
| if let Some(direction) = std::mem::take(&mut self.direction) { |
| self.decls.push(Property::Direction(direction)); |
| } |
| if let Some(unicode_bidi) = std::mem::take(&mut self.unicode_bidi) { |
| self.decls.push(Property::UnicodeBidi(unicode_bidi)); |
| } |
| |
| self.background.finalize(&mut self.decls, context); |
| self.border.finalize(&mut self.decls, context); |
| self.outline.finalize(&mut self.decls, context); |
| self.flex.finalize(&mut self.decls, context); |
| self.grid.finalize(&mut self.decls, context); |
| self.align.finalize(&mut self.decls, context); |
| self.size.finalize(&mut self.decls, context); |
| self.margin.finalize(&mut self.decls, context); |
| self.padding.finalize(&mut self.decls, context); |
| self.scroll_margin.finalize(&mut self.decls, context); |
| self.scroll_padding.finalize(&mut self.decls, context); |
| self.font.finalize(&mut self.decls, context); |
| self.text.finalize(&mut self.decls, context); |
| self.list.finalize(&mut self.decls, context); |
| self.transition.finalize(&mut self.decls, context); |
| self.animation.finalize(&mut self.decls, context); |
| self.display.finalize(&mut self.decls, context); |
| self.position.finalize(&mut self.decls, context); |
| self.inset.finalize(&mut self.decls, context); |
| self.overflow.finalize(&mut self.decls, context); |
| self.transform.finalize(&mut self.decls, context); |
| self.box_shadow.finalize(&mut self.decls, context); |
| self.mask.finalize(&mut self.decls, context); |
| self.container.finalize(&mut self.decls, context); |
| self.color_scheme.finalize(&mut self.decls, context); |
| self.fallback.finalize(&mut self.decls, context); |
| self.prefix.finalize(&mut self.decls, context); |
| self.custom_properties.clear(); |
| } |
| } |