layer_rule.rs (7033B)
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 [`@layer`][layer] rule. 6 //! 7 //! [layer]: https://drafts.csswg.org/css-cascade-5/#layering 8 9 use crate::derives::*; 10 use crate::parser::{Parse, ParserContext}; 11 use crate::shared_lock::{DeepCloneWithLock, Locked}; 12 use crate::shared_lock::{SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard}; 13 use crate::values::AtomIdent; 14 15 use super::CssRules; 16 17 use cssparser::{Parser, SourceLocation, Token}; 18 use servo_arc::Arc; 19 use smallvec::SmallVec; 20 use std::fmt::{self, Write}; 21 use style_traits::{CssWriter, ParseError, ToCss}; 22 23 /// The order of a given layer. We use 16 bits so that we can pack LayerOrder 24 /// and CascadeLevel in a single 32-bit struct. If we need more bits we can go 25 /// back to packing CascadeLevel in a single byte as we did before. 26 #[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq, PartialOrd, Ord)] 27 pub struct LayerOrder(u16); 28 29 impl LayerOrder { 30 /// The order of the root layer. 31 pub const fn root() -> Self { 32 Self(std::u16::MAX - 1) 33 } 34 35 /// The order of the style attribute layer. 36 pub const fn style_attribute() -> Self { 37 Self(std::u16::MAX) 38 } 39 40 /// Returns whether this layer is for the style attribute, which behaves 41 /// differently in terms of !important, see 42 /// https://github.com/w3c/csswg-drafts/issues/6872 43 /// 44 /// (This is a bit silly, mind-you, but it's needed so that revert-layer 45 /// behaves correctly). 46 #[inline] 47 pub fn is_style_attribute_layer(&self) -> bool { 48 *self == Self::style_attribute() 49 } 50 51 /// The first cascade layer order. 52 pub const fn first() -> Self { 53 Self(0) 54 } 55 56 /// Increment the cascade layer order. 57 #[inline] 58 pub fn inc(&mut self) { 59 if self.0 != std::u16::MAX - 1 { 60 self.0 += 1; 61 } 62 } 63 } 64 65 /// A `<layer-name>`: https://drafts.csswg.org/css-cascade-5/#typedef-layer-name 66 #[derive(Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToShmem)] 67 pub struct LayerName(pub SmallVec<[AtomIdent; 1]>); 68 69 impl LayerName { 70 /// Returns an empty layer name (which isn't a valid final state, so caller 71 /// is responsible to fill up the name before use). 72 pub fn new_empty() -> Self { 73 Self(Default::default()) 74 } 75 76 /// Returns a synthesized name for an anonymous layer. 77 pub fn new_anonymous() -> Self { 78 use std::sync::atomic::{AtomicUsize, Ordering}; 79 static NEXT_ANONYMOUS_LAYER_NAME: AtomicUsize = AtomicUsize::new(0); 80 81 let mut name = SmallVec::new(); 82 let next_id = NEXT_ANONYMOUS_LAYER_NAME.fetch_add(1, Ordering::Relaxed); 83 // The parens don't _technically_ prevent conflicts with authors, as 84 // authors could write escaped parens as part of the identifier, I 85 // think, but highly reduces the possibility. 86 name.push(AtomIdent::from(&*format!("-moz-anon-layer({})", next_id))); 87 88 LayerName(name) 89 } 90 91 /// Returns the names of the layers. That is, for a layer like `foo.bar`, 92 /// it'd return [foo, bar]. 93 pub fn layer_names(&self) -> &[AtomIdent] { 94 &self.0 95 } 96 } 97 98 impl Parse for LayerName { 99 fn parse<'i, 't>( 100 _: &ParserContext, 101 input: &mut Parser<'i, 't>, 102 ) -> Result<Self, ParseError<'i>> { 103 let mut result = SmallVec::new(); 104 result.push(AtomIdent::from(&**input.expect_ident()?)); 105 loop { 106 let next_name = input.try_parse(|input| -> Result<AtomIdent, ParseError<'i>> { 107 match input.next_including_whitespace()? { 108 Token::Delim('.') => {}, 109 other => { 110 let t = other.clone(); 111 return Err(input.new_unexpected_token_error(t)); 112 }, 113 } 114 115 let name = match input.next_including_whitespace()? { 116 Token::Ident(ref ident) => ident, 117 other => { 118 let t = other.clone(); 119 return Err(input.new_unexpected_token_error(t)); 120 }, 121 }; 122 123 Ok(AtomIdent::from(&**name)) 124 }); 125 126 match next_name { 127 Ok(name) => result.push(name), 128 Err(..) => break, 129 } 130 } 131 Ok(LayerName(result)) 132 } 133 } 134 135 impl ToCss for LayerName { 136 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result 137 where 138 W: Write, 139 { 140 let mut first = true; 141 for name in self.0.iter() { 142 if !first { 143 dest.write_char('.')?; 144 } 145 first = false; 146 name.to_css(dest)?; 147 } 148 Ok(()) 149 } 150 } 151 152 #[derive(Debug, ToShmem)] 153 /// A block `@layer <name>? { ... }` 154 /// https://drafts.csswg.org/css-cascade-5/#layer-block 155 pub struct LayerBlockRule { 156 /// The layer name, or `None` if anonymous. 157 pub name: Option<LayerName>, 158 /// The nested rules. 159 pub rules: Arc<Locked<CssRules>>, 160 /// The source position where this rule was found. 161 pub source_location: SourceLocation, 162 } 163 164 impl ToCssWithGuard for LayerBlockRule { 165 fn to_css( 166 &self, 167 guard: &SharedRwLockReadGuard, 168 dest: &mut style_traits::CssStringWriter, 169 ) -> fmt::Result { 170 dest.write_str("@layer")?; 171 if let Some(ref name) = self.name { 172 dest.write_char(' ')?; 173 name.to_css(&mut CssWriter::new(dest))?; 174 } 175 self.rules.read_with(guard).to_css_block(guard, dest) 176 } 177 } 178 179 impl DeepCloneWithLock for LayerBlockRule { 180 fn deep_clone_with_lock(&self, lock: &SharedRwLock, guard: &SharedRwLockReadGuard) -> Self { 181 Self { 182 name: self.name.clone(), 183 rules: Arc::new( 184 lock.wrap( 185 self.rules 186 .read_with(guard) 187 .deep_clone_with_lock(lock, guard), 188 ), 189 ), 190 source_location: self.source_location.clone(), 191 } 192 } 193 } 194 195 /// A statement `@layer <name>, <name>, <name>;` 196 /// 197 /// https://drafts.csswg.org/css-cascade-5/#layer-empty 198 #[derive(Clone, Debug, ToShmem)] 199 pub struct LayerStatementRule { 200 /// The list of layers to sort. 201 pub names: Vec<LayerName>, 202 /// The source position where this rule was found. 203 pub source_location: SourceLocation, 204 } 205 206 impl ToCssWithGuard for LayerStatementRule { 207 fn to_css( 208 &self, 209 _: &SharedRwLockReadGuard, 210 dest: &mut style_traits::CssStringWriter, 211 ) -> fmt::Result { 212 let mut writer = CssWriter::new(dest); 213 writer.write_str("@layer ")?; 214 let mut first = true; 215 for name in &*self.names { 216 if !first { 217 writer.write_str(", ")?; 218 } 219 first = false; 220 name.to_css(&mut writer)?; 221 } 222 writer.write_char(';') 223 } 224 }