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 list of CSS rules. 6 7 use crate::shared_lock::{DeepCloneParams, DeepCloneWithLock, Locked}; 8 use crate::shared_lock::{SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard}; 9 use crate::str::CssStringWriter; 10 use crate::stylesheets::loader::StylesheetLoader; 11 use crate::stylesheets::rule_parser::{InsertRuleContext, State}; 12 use crate::stylesheets::stylesheet::StylesheetContents; 13 use crate::stylesheets::{AllowImportRules, CssRule, RulesMutateError}; 14 #[cfg(feature = "gecko")] 15 use malloc_size_of::{MallocShallowSizeOf, MallocSizeOfOps}; 16 use servo_arc::{Arc, RawOffsetArc}; 17 use std::fmt::{self, Write}; 18 19 /// A list of CSS rules. 20 #[derive(Debug, ToShmem)] 21 pub struct CssRules(pub Vec<CssRule>); 22 23 impl CssRules { 24 /// Whether this CSS rules is empty. is_empty(&self) -> bool25 pub fn is_empty(&self) -> bool { 26 self.0.is_empty() 27 } 28 } 29 30 impl DeepCloneWithLock for CssRules { deep_clone_with_lock( &self, lock: &SharedRwLock, guard: &SharedRwLockReadGuard, params: &DeepCloneParams, ) -> Self31 fn deep_clone_with_lock( 32 &self, 33 lock: &SharedRwLock, 34 guard: &SharedRwLockReadGuard, 35 params: &DeepCloneParams, 36 ) -> Self { 37 CssRules( 38 self.0 39 .iter() 40 .map(|x| x.deep_clone_with_lock(lock, guard, params)) 41 .collect(), 42 ) 43 } 44 } 45 46 impl CssRules { 47 /// Measure heap usage. 48 #[cfg(feature = "gecko")] size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize49 pub fn size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize { 50 let mut n = self.0.shallow_size_of(ops); 51 for rule in self.0.iter() { 52 n += rule.size_of(guard, ops); 53 } 54 n 55 } 56 57 /// Trivially construct a new set of CSS rules. new(rules: Vec<CssRule>, shared_lock: &SharedRwLock) -> Arc<Locked<CssRules>>58 pub fn new(rules: Vec<CssRule>, shared_lock: &SharedRwLock) -> Arc<Locked<CssRules>> { 59 Arc::new(shared_lock.wrap(CssRules(rules))) 60 } 61 62 /// Returns whether all the rules in this list are namespace or import 63 /// rules. only_ns_or_import(&self) -> bool64 fn only_ns_or_import(&self) -> bool { 65 self.0.iter().all(|r| match *r { 66 CssRule::Namespace(..) | CssRule::Import(..) => true, 67 _ => false, 68 }) 69 } 70 71 /// <https://drafts.csswg.org/cssom/#remove-a-css-rule> remove_rule(&mut self, index: usize) -> Result<(), RulesMutateError>72 pub fn remove_rule(&mut self, index: usize) -> Result<(), RulesMutateError> { 73 // Step 1, 2 74 if index >= self.0.len() { 75 return Err(RulesMutateError::IndexSize); 76 } 77 78 { 79 // Step 3 80 let ref rule = self.0[index]; 81 82 // Step 4 83 if let CssRule::Namespace(..) = *rule { 84 if !self.only_ns_or_import() { 85 return Err(RulesMutateError::InvalidState); 86 } 87 } 88 } 89 90 // Step 5, 6 91 self.0.remove(index); 92 Ok(()) 93 } 94 95 /// Serializes this CSSRules to CSS text as a block of rules. 96 /// 97 /// This should be speced into CSSOM spec at some point. See 98 /// <https://github.com/w3c/csswg-drafts/issues/1985> to_css_block( &self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter, ) -> fmt::Result99 pub fn to_css_block( 100 &self, 101 guard: &SharedRwLockReadGuard, 102 dest: &mut CssStringWriter, 103 ) -> fmt::Result { 104 dest.write_str(" {")?; 105 for rule in self.0.iter() { 106 dest.write_str("\n ")?; 107 rule.to_css(guard, dest)?; 108 } 109 dest.write_str("\n}") 110 } 111 } 112 113 /// A trait to implement helpers for `Arc<Locked<CssRules>>`. 114 pub trait CssRulesHelpers { 115 /// <https://drafts.csswg.org/cssom/#insert-a-css-rule> 116 /// 117 /// Written in this funky way because parsing an @import rule may cause us 118 /// to clone a stylesheet from the same document due to caching in the CSS 119 /// loader. 120 /// 121 /// TODO(emilio): We could also pass the write guard down into the loader 122 /// instead, but that seems overkill. insert_rule( &self, lock: &SharedRwLock, rule: &str, parent_stylesheet_contents: &StylesheetContents, index: usize, nested: bool, loader: Option<&dyn StylesheetLoader>, allow_import_rules: AllowImportRules, ) -> Result<CssRule, RulesMutateError>123 fn insert_rule( 124 &self, 125 lock: &SharedRwLock, 126 rule: &str, 127 parent_stylesheet_contents: &StylesheetContents, 128 index: usize, 129 nested: bool, 130 loader: Option<&dyn StylesheetLoader>, 131 allow_import_rules: AllowImportRules, 132 ) -> Result<CssRule, RulesMutateError>; 133 } 134 135 impl CssRulesHelpers for RawOffsetArc<Locked<CssRules>> { insert_rule( &self, lock: &SharedRwLock, rule: &str, parent_stylesheet_contents: &StylesheetContents, index: usize, nested: bool, loader: Option<&dyn StylesheetLoader>, allow_import_rules: AllowImportRules, ) -> Result<CssRule, RulesMutateError>136 fn insert_rule( 137 &self, 138 lock: &SharedRwLock, 139 rule: &str, 140 parent_stylesheet_contents: &StylesheetContents, 141 index: usize, 142 nested: bool, 143 loader: Option<&dyn StylesheetLoader>, 144 allow_import_rules: AllowImportRules, 145 ) -> Result<CssRule, RulesMutateError> { 146 let new_rule = { 147 let read_guard = lock.read(); 148 let rules = self.read_with(&read_guard); 149 150 // Step 1, 2 151 if index > rules.0.len() { 152 return Err(RulesMutateError::IndexSize); 153 } 154 155 // Computes the parser state at the given index 156 let state = if nested { 157 State::Body 158 } else if index == 0 { 159 State::Start 160 } else { 161 rules 162 .0 163 .get(index - 1) 164 .map(CssRule::rule_state) 165 .unwrap_or(State::Body) 166 }; 167 168 let insert_rule_context = InsertRuleContext { 169 rule_list: &rules.0, 170 index, 171 }; 172 173 // Steps 3, 4, 5, 6 174 CssRule::parse( 175 &rule, 176 insert_rule_context, 177 parent_stylesheet_contents, 178 lock, 179 state, 180 loader, 181 allow_import_rules, 182 )? 183 }; 184 185 { 186 let mut write_guard = lock.write(); 187 let rules = self.write_with(&mut write_guard); 188 rules.0.insert(index, new_rule.clone()); 189 } 190 191 Ok(new_rule) 192 } 193 } 194