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