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}