rule_list.rs (5222B)
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::derives::*; 8 use crate::shared_lock::{DeepCloneWithLock, Locked}; 9 use crate::shared_lock::{SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard}; 10 use crate::stylesheets::loader::StylesheetLoader; 11 use crate::stylesheets::rule_parser::InsertRuleContext; 12 use crate::stylesheets::stylesheet::StylesheetContents; 13 use crate::stylesheets::{AllowImportRules, CssRule, CssRuleTypes, RulesMutateError}; 14 #[cfg(feature = "gecko")] 15 use malloc_size_of::{MallocShallowSizeOf, MallocSizeOfOps}; 16 use servo_arc::Arc; 17 use std::fmt::{self, Write}; 18 use style_traits::CssStringWriter; 19 20 use super::CssRuleType; 21 22 /// A list of CSS rules. 23 #[derive(Debug, ToShmem)] 24 pub struct CssRules(pub Vec<CssRule>); 25 26 impl CssRules { 27 /// Whether this CSS rules is empty. 28 pub fn is_empty(&self) -> bool { 29 self.0.is_empty() 30 } 31 } 32 33 impl DeepCloneWithLock for CssRules { 34 fn deep_clone_with_lock(&self, lock: &SharedRwLock, guard: &SharedRwLockReadGuard) -> Self { 35 CssRules( 36 self.0 37 .iter() 38 .map(|x| x.deep_clone_with_lock(lock, guard)) 39 .collect(), 40 ) 41 } 42 } 43 44 impl CssRules { 45 /// Measure heap usage. 46 #[cfg(feature = "gecko")] 47 pub fn size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize { 48 let mut n = self.0.shallow_size_of(ops); 49 for rule in self.0.iter() { 50 n += rule.size_of(guard, ops); 51 } 52 n 53 } 54 55 /// Trivially construct a new set of CSS rules. 56 pub fn new(rules: Vec<CssRule>, shared_lock: &SharedRwLock) -> Arc<Locked<CssRules>> { 57 Arc::new(shared_lock.wrap(CssRules(rules))) 58 } 59 60 /// Returns whether all the rules in this list are namespace or import 61 /// rules. 62 fn only_ns_or_import(&self) -> bool { 63 self.0.iter().all(|r| match *r { 64 CssRule::Namespace(..) | CssRule::Import(..) => true, 65 _ => false, 66 }) 67 } 68 69 /// <https://drafts.csswg.org/cssom/#remove-a-css-rule> 70 pub fn remove_rule(&mut self, index: usize) -> Result<(), RulesMutateError> { 71 // Step 1, 2 72 if index >= self.0.len() { 73 return Err(RulesMutateError::IndexSize); 74 } 75 76 { 77 // Step 3 78 let ref rule = self.0[index]; 79 80 // Step 4 81 if let CssRule::Namespace(..) = *rule { 82 if !self.only_ns_or_import() { 83 return Err(RulesMutateError::InvalidState); 84 } 85 } 86 } 87 88 // Step 5, 6 89 self.0.remove(index); 90 Ok(()) 91 } 92 93 /// Serializes this CSSRules to CSS text as a block of rules. 94 /// 95 /// This should be speced into CSSOM spec at some point. See 96 /// <https://github.com/w3c/csswg-drafts/issues/1985> 97 pub fn to_css_block( 98 &self, 99 guard: &SharedRwLockReadGuard, 100 dest: &mut CssStringWriter, 101 ) -> fmt::Result { 102 dest.write_str(" {")?; 103 self.to_css_block_without_opening(guard, dest) 104 } 105 106 /// As above, but without the opening curly bracket. That's needed for nesting. 107 pub fn to_css_block_without_opening( 108 &self, 109 guard: &SharedRwLockReadGuard, 110 dest: &mut CssStringWriter, 111 ) -> fmt::Result { 112 for rule in self.0.iter() { 113 if rule.is_empty_nested_declarations(guard) { 114 continue; 115 } 116 117 dest.write_str("\n ")?; 118 let old_len = dest.len(); 119 rule.to_css(guard, dest)?; 120 debug_assert_ne!(old_len, dest.len()); 121 } 122 dest.write_str("\n}") 123 } 124 125 /// Parses a rule for <https://drafts.csswg.org/cssom/#insert-a-css-rule>. Caller is 126 /// responsible for calling insert() afterwards. 127 /// 128 /// Written in this funky way because parsing an @import rule may cause us 129 /// to clone a stylesheet from the same document due to caching in the CSS 130 /// loader. 131 /// 132 /// TODO(emilio): We could also pass the write guard down into the loader 133 /// instead, but that seems overkill. 134 pub fn parse_rule_for_insert( 135 &self, 136 lock: &SharedRwLock, 137 rule: &str, 138 parent_stylesheet_contents: &StylesheetContents, 139 index: usize, 140 containing_rule_types: CssRuleTypes, 141 parse_relative_rule_type: Option<CssRuleType>, 142 loader: Option<&dyn StylesheetLoader>, 143 allow_import_rules: AllowImportRules, 144 ) -> Result<CssRule, RulesMutateError> { 145 // Step 1, 2 146 if index > self.0.len() { 147 return Err(RulesMutateError::IndexSize); 148 } 149 150 let insert_rule_context = InsertRuleContext { 151 rule_list: &self.0, 152 index, 153 containing_rule_types, 154 parse_relative_rule_type, 155 }; 156 157 // Steps 3, 4, 5, 6 158 CssRule::parse( 159 &rule, 160 insert_rule_context, 161 parent_stylesheet_contents, 162 lock, 163 loader, 164 allow_import_rules, 165 ) 166 } 167 }