style/stylesheets/
margin_rule.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5//! A [`@margin`][margin] rule.
6//!
7//! [margin]: https://drafts.csswg.org/css-page-3/#margin-boxes
8
9use crate::properties::PropertyDeclarationBlock;
10use crate::shared_lock::{DeepCloneWithLock, Locked};
11use crate::shared_lock::{SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard};
12use crate::str::CssStringWriter;
13use cssparser::SourceLocation;
14#[cfg(feature = "gecko")]
15use malloc_size_of::{MallocSizeOf, MallocSizeOfOps, MallocUnconditionalShallowSizeOf};
16use servo_arc::Arc;
17use std::fmt::{self, Write};
18
19macro_rules! margin_rule_types {
20    ($($(#[$($meta:tt)+])* $id:ident => $val:literal,)+) => {
21        /// [`@margin`][margin] rule names.
22        ///
23        /// https://drafts.csswg.org/css-page-3/#margin-at-rules
24        #[derive(Clone, Copy, Eq, MallocSizeOf, PartialEq, ToShmem)]
25        #[repr(u8)]
26        pub enum MarginRuleType {
27            $($(#[$($meta)+])* $id,)+
28        }
29
30        /// All [`@margin`][margin] rule names, with a preceding '@'.
31        ///
32        /// This array lets us have just one single memory region used for
33        /// to_str, name, and the Debug implementation.
34        const MARGIN_RULE_AT_NAMES:&[&'static str] = &[
35            $( concat!('@', $val), )+
36        ];
37
38        impl MarginRuleType {
39            /// Matches the rule type for this name. This does not expect a
40            /// leading '@'.
41            pub fn match_name(name: &str) -> Option<Self> {
42                Some(match_ignore_ascii_case! { name,
43                    $( $val => MarginRuleType::$id, )+
44                    _ => return None,
45                })
46            }
47        }
48    }
49}
50
51margin_rule_types! {
52    /// [`@top-left-corner`][top-left-corner] margin rule
53    ///
54    /// [top-left-corner] https://drafts.csswg.org/css-page-3/#top-left-corner-box-def
55    TopLeftCorner => "top-left-corner",
56    /// [`@top-left`][top-left] margin rule
57    ///
58    /// [top-left] https://drafts.csswg.org/css-page-3/#top-left-box-def
59    TopLeft => "top-left",
60    /// [`@top-center`][top-center] margin rule
61    ///
62    /// [top-center] https://drafts.csswg.org/css-page-3/#top-center-box-def
63    TopCenter => "top-center",
64    /// [`@top-right`][top-right] margin rule
65    ///
66    /// [top-right] https://drafts.csswg.org/css-page-3/#top-right-box-def
67    TopRight => "top-right",
68    /// [`@top-right-corner`][top-right-corner] margin rule
69    ///
70    /// [top-right-corner] https://drafts.csswg.org/css-page-3/#top-right-corner-box-def
71    TopRightCorner => "top-right-corner",
72    /// [`@bottom-left-corner`][bottom-left-corner] margin rule
73    ///
74    /// [bottom-left-corner] https://drafts.csswg.org/css-page-3/#bottom-left-corner-box-def
75    BottomLeftCorner => "bottom-left-corner",
76    /// [`@bottom-left`][bottom-left] margin rule
77    ///
78    /// [bottom-left] https://drafts.csswg.org/css-page-3/#bottom-left-box-def
79    BottomLeft => "bottom-left",
80    /// [`@bottom-center`][bottom-center] margin rule
81    ///
82    /// [bottom-center] https://drafts.csswg.org/css-page-3/#bottom-center-box-def
83    BottomCenter => "bottom-center",
84    /// [`@bottom-right`][bottom-right] margin rule
85    ///
86    /// [bottom-right] https://drafts.csswg.org/css-page-3/#bottom-right-box-def
87    BottomRight => "bottom-right",
88    /// [`@bottom-right-corner`][bottom-right-corner] margin rule
89    ///
90    /// [bottom-right-corner] https://drafts.csswg.org/css-page-3/#bottom-right-corner-box-def
91    BottomRightCorner => "bottom-right-corner",
92    /// [`@left-top`][left-top] margin rule
93    ///
94    /// [left-top] https://drafts.csswg.org/css-page-3/#left-top-box-def
95    LeftTop => "left-top",
96    /// [`@left-middle`][left-middle] margin rule
97    ///
98    /// [left-middle] https://drafts.csswg.org/css-page-3/#left-middle-box-def
99    LeftMiddle => "left-middle",
100    /// [`@left-bottom`][left-bottom] margin rule
101    ///
102    /// [left-bottom] https://drafts.csswg.org/css-page-3/#left-bottom-box-def
103    LeftBottom => "left-bottom",
104    /// [`@right-top`][right-top] margin rule
105    ///
106    /// [right-top] https://drafts.csswg.org/css-page-3/#right-top-box-def
107    RightTop => "right-top",
108    /// [`@right-middle`][right-middle] margin rule
109    ///
110    /// [right-middle] https://drafts.csswg.org/css-page-3/#right-middle-box-def
111    RightMiddle => "right-middle",
112    /// [`@right-bottom`][right-bottom] margin rule
113    ///
114    /// [right-bottom] https://drafts.csswg.org/css-page-3/#right-bottom-box-def
115    RightBottom => "right-bottom",
116}
117
118impl MarginRuleType {
119    #[inline]
120    fn to_str(&self) -> &'static str {
121        &MARGIN_RULE_AT_NAMES[*self as usize]
122    }
123    #[inline]
124    fn name(&self) -> &'static str {
125        // Use the at-name array, skipping the first character to get
126        // the name without the @ sign.
127        &MARGIN_RULE_AT_NAMES[*self as usize][1..]
128    }
129}
130
131// Implement Debug manually so that it will share the same string memory as
132// MarginRuleType::name and MarginRuleType::to_str.
133impl fmt::Debug for MarginRuleType {
134    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
135        f.write_str(self.name())
136    }
137}
138
139/// A [`@margin`][margin] rule.
140///
141/// [margin]: https://drafts.csswg.org/css-page-3/#margin-at-rules
142#[derive(Clone, Debug, ToShmem)]
143pub struct MarginRule {
144    /// Type of this margin rule.
145    pub rule_type: MarginRuleType,
146    /// The declaration block this margin rule contains.
147    pub block: Arc<Locked<PropertyDeclarationBlock>>,
148    /// The source position this rule was found at.
149    pub source_location: SourceLocation,
150}
151
152impl MarginRule {
153    /// Measure heap usage.
154    #[cfg(feature = "gecko")]
155    pub fn size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize {
156        // Measurement of other fields may be added later.
157        self.block.unconditional_shallow_size_of(ops) + self.block.read_with(guard).size_of(ops)
158    }
159    /// Gets the name for this margin rule.
160    #[inline]
161    pub fn name(&self) -> &'static str {
162        self.rule_type.name()
163    }
164}
165
166impl ToCssWithGuard for MarginRule {
167    /// Serialization of a margin-rule is not specced, this is adapted from how
168    /// page-rules and style-rules are serialized.
169    fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {
170        dest.write_str(self.rule_type.to_str())?;
171        dest.write_str(" { ")?;
172        let declaration_block = self.block.read_with(guard);
173        declaration_block.to_css(dest)?;
174        if !declaration_block.declarations().is_empty() {
175            dest.write_char(' ')?;
176        }
177        dest.write_char('}')
178    }
179}
180
181impl DeepCloneWithLock for MarginRule {
182    fn deep_clone_with_lock(&self, lock: &SharedRwLock, guard: &SharedRwLockReadGuard) -> Self {
183        MarginRule {
184            rule_type: self.rule_type,
185            block: Arc::new(lock.wrap(self.block.read_with(&guard).clone())),
186            source_location: self.source_location.clone(),
187        }
188    }
189}