/* 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 [`@margin`][margin] rule. //! //! [margin]: https://drafts.csswg.org/css-page-3/#margin-boxes use crate::properties::PropertyDeclarationBlock; use crate::shared_lock::{DeepCloneWithLock, Locked}; use crate::shared_lock::{SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard}; use crate::str::CssStringWriter; use cssparser::SourceLocation; #[cfg(feature = "gecko")] use malloc_size_of::{MallocSizeOf, MallocSizeOfOps, MallocUnconditionalShallowSizeOf}; use servo_arc::Arc; use std::fmt::{self, Write}; macro_rules! margin_rule_types { ($($(#[$($meta:tt)+])* $id:ident => $val:literal,)+) => { /// [`@margin`][margin] rule names. /// /// https://drafts.csswg.org/css-page-3/#margin-at-rules #[derive(Clone, Copy, Eq, MallocSizeOf, PartialEq, ToShmem)] #[repr(u8)] pub enum MarginRuleType { $($(#[$($meta)+])* $id,)+ } /// All [`@margin`][margin] rule names, with a preceding '@'. /// /// This array lets us have just one single memory region used for /// to_str, name, and the Debug implementation. const MARGIN_RULE_AT_NAMES:&[&'static str] = &[ $( concat!('@', $val), )+ ]; impl MarginRuleType { /// Matches the rule type for this name. This does not expect a /// leading '@'. pub fn match_name(name: &str) -> Option { Some(match_ignore_ascii_case! { name, $( $val => MarginRuleType::$id, )+ _ => return None, }) } } } } margin_rule_types! { /// [`@top-left-corner`][top-left-corner] margin rule /// /// [top-left-corner] https://drafts.csswg.org/css-page-3/#top-left-corner-box-def TopLeftCorner => "top-left-corner", /// [`@top-left`][top-left] margin rule /// /// [top-left] https://drafts.csswg.org/css-page-3/#top-left-box-def TopLeft => "top-left", /// [`@top-center`][top-center] margin rule /// /// [top-center] https://drafts.csswg.org/css-page-3/#top-center-box-def TopCenter => "top-center", /// [`@top-right`][top-right] margin rule /// /// [top-right] https://drafts.csswg.org/css-page-3/#top-right-box-def TopRight => "top-right", /// [`@top-right-corner`][top-right-corner] margin rule /// /// [top-right-corner] https://drafts.csswg.org/css-page-3/#top-right-corner-box-def TopRightCorner => "top-right-corner", /// [`@bottom-left-corner`][bottom-left-corner] margin rule /// /// [bottom-left-corner] https://drafts.csswg.org/css-page-3/#bottom-left-corner-box-def BottomLeftCorner => "bottom-left-corner", /// [`@bottom-left`][bottom-left] margin rule /// /// [bottom-left] https://drafts.csswg.org/css-page-3/#bottom-left-box-def BottomLeft => "bottom-left", /// [`@bottom-center`][bottom-center] margin rule /// /// [bottom-center] https://drafts.csswg.org/css-page-3/#bottom-center-box-def BottomCenter => "bottom-center", /// [`@bottom-right`][bottom-right] margin rule /// /// [bottom-right] https://drafts.csswg.org/css-page-3/#bottom-right-box-def BottomRight => "bottom-right", /// [`@bottom-right-corner`][bottom-right-corner] margin rule /// /// [bottom-right-corner] https://drafts.csswg.org/css-page-3/#bottom-right-corner-box-def BottomRightCorner => "bottom-right-corner", /// [`@left-top`][left-top] margin rule /// /// [left-top] https://drafts.csswg.org/css-page-3/#left-top-box-def LeftTop => "left-top", /// [`@left-middle`][left-middle] margin rule /// /// [left-middle] https://drafts.csswg.org/css-page-3/#left-middle-box-def LeftMiddle => "left-middle", /// [`@left-bottom`][left-bottom] margin rule /// /// [left-bottom] https://drafts.csswg.org/css-page-3/#left-bottom-box-def LeftBottom => "left-bottom", /// [`@right-top`][right-top] margin rule /// /// [right-top] https://drafts.csswg.org/css-page-3/#right-top-box-def RightTop => "right-top", /// [`@right-middle`][right-middle] margin rule /// /// [right-middle] https://drafts.csswg.org/css-page-3/#right-middle-box-def RightMiddle => "right-middle", /// [`@right-bottom`][right-bottom] margin rule /// /// [right-bottom] https://drafts.csswg.org/css-page-3/#right-bottom-box-def RightBottom => "right-bottom", } impl MarginRuleType { #[inline] fn to_str(&self) -> &'static str { &MARGIN_RULE_AT_NAMES[*self as usize] } #[inline] fn name(&self) -> &'static str { // Use the at-name array, skipping the first character to get // the name without the @ sign. &MARGIN_RULE_AT_NAMES[*self as usize][1..] } } // Implement Debug manually so that it will share the same string memory as // MarginRuleType::name and MarginRuleType::to_str. impl fmt::Debug for MarginRuleType { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { f.write_str(self.name()) } } /// A [`@margin`][margin] rule. /// /// [margin]: https://drafts.csswg.org/css-page-3/#margin-at-rules #[derive(Clone, Debug, ToShmem)] pub struct MarginRule { /// Type of this margin rule. pub rule_type: MarginRuleType, /// The declaration block this margin rule contains. pub block: Arc>, /// The source position this rule was found at. pub source_location: SourceLocation, } impl MarginRule { /// 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.block.unconditional_shallow_size_of(ops) + self.block.read_with(guard).size_of(ops) } /// Gets the name for this margin rule. #[inline] pub fn name(&self) -> &'static str { self.rule_type.name() } } impl ToCssWithGuard for MarginRule { /// Serialization of a margin-rule is not specced, this is adapted from how /// page-rules and style-rules are serialized. fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result { dest.write_str(self.rule_type.to_str())?; dest.write_str(" { ")?; let declaration_block = self.block.read_with(guard); declaration_block.to_css(dest)?; if !declaration_block.declarations().is_empty() { dest.write_char(' ')?; } dest.write_char('}') } } impl DeepCloneWithLock for MarginRule { fn deep_clone_with_lock( &self, lock: &SharedRwLock, guard: &SharedRwLockReadGuard, ) -> Self { MarginRule { rule_type: self.rule_type, block: Arc::new(lock.wrap(self.block.read_with(&guard).clone())), source_location: self.source_location.clone(), } } }