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}