rules_iterator.rs (11770B)
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 //! An iterator over a list of rules. 6 7 use crate::context::QuirksMode; 8 use crate::media_queries::Device; 9 use crate::shared_lock::SharedRwLockReadGuard; 10 use crate::stylesheets::{ 11 CssRule, CssRuleRef, CustomMediaEvaluator, CustomMediaMap, DocumentRule, ImportRule, MediaRule, 12 SupportsRule, 13 }; 14 use smallvec::SmallVec; 15 use std::ops::Deref; 16 use std::slice; 17 18 /// An iterator over a list of rules. 19 pub struct RulesIterator<'a, 'b, C, CMM> 20 where 21 'b: 'a, 22 C: NestedRuleIterationCondition + 'static, 23 CMM: Deref<Target = CustomMediaMap>, 24 { 25 device: &'a Device, 26 quirks_mode: QuirksMode, 27 custom_media: CMM, 28 guard: &'a SharedRwLockReadGuard<'b>, 29 stack: SmallVec<[slice::Iter<'a, CssRule>; 3]>, 30 _phantom: ::std::marker::PhantomData<C>, 31 } 32 33 impl<'a, 'b, C, CMM> RulesIterator<'a, 'b, C, CMM> 34 where 35 'b: 'a, 36 C: NestedRuleIterationCondition + 'static, 37 CMM: Deref<Target = CustomMediaMap>, 38 { 39 /// Returns the custom media map passed at construction. 40 pub fn custom_media(&mut self) -> &mut CMM { 41 &mut self.custom_media 42 } 43 44 /// Creates a new `RulesIterator` to iterate over `rules`. 45 pub fn new( 46 device: &'a Device, 47 quirks_mode: QuirksMode, 48 custom_media: CMM, 49 guard: &'a SharedRwLockReadGuard<'b>, 50 rules: slice::Iter<'a, CssRule>, 51 ) -> Self { 52 let mut stack = SmallVec::new(); 53 stack.push(rules); 54 Self { 55 device, 56 quirks_mode, 57 custom_media, 58 guard, 59 stack, 60 _phantom: ::std::marker::PhantomData, 61 } 62 } 63 64 /// Skips all the remaining children of the last nested rule processed. 65 pub fn skip_children(&mut self) { 66 self.stack.pop(); 67 } 68 69 /// Returns the children of `rule`, and whether `rule` is effective. 70 pub fn children( 71 rule: &'a CssRule, 72 device: &'a Device, 73 quirks_mode: QuirksMode, 74 custom_media_map: &CustomMediaMap, 75 guard: &'a SharedRwLockReadGuard<'_>, 76 effective: &mut bool, 77 ) -> Option<slice::Iter<'a, CssRule>> { 78 *effective = true; 79 match *rule { 80 CssRule::Namespace(_) 81 | CssRule::FontFace(_) 82 | CssRule::CounterStyle(_) 83 | CssRule::CustomMedia(_) 84 | CssRule::Keyframes(_) 85 | CssRule::Margin(_) 86 | CssRule::Property(_) 87 | CssRule::LayerStatement(_) 88 | CssRule::FontFeatureValues(_) 89 | CssRule::FontPaletteValues(_) 90 | CssRule::NestedDeclarations(_) 91 | CssRule::PositionTry(_) => None, 92 CssRule::Page(ref page_rule) => { 93 let page_rule = page_rule.read_with(guard); 94 let rules = page_rule.rules.read_with(guard); 95 Some(rules.0.iter()) 96 }, 97 CssRule::Style(ref style_rule) => { 98 let style_rule = style_rule.read_with(guard); 99 style_rule 100 .rules 101 .as_ref() 102 .map(|r| r.read_with(guard).0.iter()) 103 }, 104 CssRule::Import(ref import_rule) => { 105 let import_rule = import_rule.read_with(guard); 106 if !C::process_import(guard, device, quirks_mode, custom_media_map, import_rule) { 107 *effective = false; 108 return None; 109 } 110 Some(import_rule.stylesheet.rules(guard).iter()) 111 }, 112 CssRule::Document(ref doc_rule) => { 113 if !C::process_document(guard, device, quirks_mode, doc_rule) { 114 *effective = false; 115 return None; 116 } 117 Some(doc_rule.rules.read_with(guard).0.iter()) 118 }, 119 CssRule::Container(ref container_rule) => { 120 Some(container_rule.rules.read_with(guard).0.iter()) 121 }, 122 CssRule::Media(ref media_rule) => { 123 if !C::process_media(guard, device, quirks_mode, custom_media_map, media_rule) { 124 *effective = false; 125 return None; 126 } 127 Some(media_rule.rules.read_with(guard).0.iter()) 128 }, 129 CssRule::Supports(ref supports_rule) => { 130 if !C::process_supports(guard, device, quirks_mode, supports_rule) { 131 *effective = false; 132 return None; 133 } 134 Some(supports_rule.rules.read_with(guard).0.iter()) 135 }, 136 CssRule::LayerBlock(ref layer_rule) => Some(layer_rule.rules.read_with(guard).0.iter()), 137 CssRule::Scope(ref rule) => Some(rule.rules.read_with(guard).0.iter()), 138 CssRule::StartingStyle(ref rule) => Some(rule.rules.read_with(guard).0.iter()), 139 } 140 } 141 } 142 143 impl<'a, 'b, C, CMM> Iterator for RulesIterator<'a, 'b, C, CMM> 144 where 145 'b: 'a, 146 C: NestedRuleIterationCondition + 'static, 147 CMM: Deref<Target = CustomMediaMap>, 148 { 149 type Item = &'a CssRule; 150 151 fn next(&mut self) -> Option<Self::Item> { 152 while !self.stack.is_empty() { 153 let rule = { 154 let nested_iter = self.stack.last_mut().unwrap(); 155 match nested_iter.next() { 156 Some(r) => r, 157 None => { 158 self.stack.pop(); 159 continue; 160 }, 161 } 162 }; 163 164 let mut effective = true; 165 let children = Self::children( 166 rule, 167 self.device, 168 self.quirks_mode, 169 &self.custom_media, 170 self.guard, 171 &mut effective, 172 ); 173 if !effective { 174 continue; 175 } 176 177 if let Some(children) = children { 178 // NOTE: It's important that `children` gets pushed even if 179 // empty, so that `skip_children()` works as expected. 180 self.stack.push(children); 181 } 182 183 return Some(rule); 184 } 185 186 None 187 } 188 } 189 190 /// RulesIterator. 191 pub trait NestedRuleIterationCondition { 192 /// Whether we should process the nested rules in a given `@import` rule. 193 fn process_import( 194 guard: &SharedRwLockReadGuard, 195 device: &Device, 196 quirks_mode: QuirksMode, 197 custom_media_map: &CustomMediaMap, 198 rule: &ImportRule, 199 ) -> bool; 200 201 /// Whether we should process the nested rules in a given `@media` rule. 202 fn process_media( 203 guard: &SharedRwLockReadGuard, 204 device: &Device, 205 quirks_mode: QuirksMode, 206 custom_media_map: &CustomMediaMap, 207 rule: &MediaRule, 208 ) -> bool; 209 210 /// Whether we should process the nested rules in a given `@-moz-document` 211 /// rule. 212 fn process_document( 213 guard: &SharedRwLockReadGuard, 214 device: &Device, 215 quirks_mode: QuirksMode, 216 rule: &DocumentRule, 217 ) -> bool; 218 219 /// Whether we should process the nested rules in a given `@supports` rule. 220 fn process_supports( 221 guard: &SharedRwLockReadGuard, 222 device: &Device, 223 quirks_mode: QuirksMode, 224 rule: &SupportsRule, 225 ) -> bool; 226 } 227 228 /// A struct that represents the condition that a rule applies to the document. 229 pub struct EffectiveRules; 230 231 impl EffectiveRules { 232 /// Returns whether a given rule is effective. 233 pub fn is_effective( 234 guard: &SharedRwLockReadGuard, 235 device: &Device, 236 quirks_mode: QuirksMode, 237 custom_media_map: &CustomMediaMap, 238 rule: &CssRuleRef, 239 ) -> bool { 240 match *rule { 241 CssRuleRef::Import(import_rule) => { 242 let import_rule = import_rule.read_with(guard); 243 Self::process_import(guard, device, quirks_mode, custom_media_map, import_rule) 244 }, 245 CssRuleRef::Document(doc_rule) => { 246 Self::process_document(guard, device, quirks_mode, doc_rule) 247 }, 248 CssRuleRef::Media(media_rule) => { 249 Self::process_media(guard, device, quirks_mode, custom_media_map, media_rule) 250 }, 251 CssRuleRef::Supports(supports_rule) => { 252 Self::process_supports(guard, device, quirks_mode, supports_rule) 253 }, 254 _ => true, 255 } 256 } 257 } 258 259 impl NestedRuleIterationCondition for EffectiveRules { 260 fn process_import( 261 guard: &SharedRwLockReadGuard, 262 device: &Device, 263 quirks_mode: QuirksMode, 264 custom_media_map: &CustomMediaMap, 265 rule: &ImportRule, 266 ) -> bool { 267 match rule.stylesheet.media(guard) { 268 Some(m) => m.evaluate( 269 device, 270 quirks_mode, 271 &mut CustomMediaEvaluator::new(custom_media_map, guard), 272 ), 273 None => true, 274 } 275 } 276 277 fn process_media( 278 guard: &SharedRwLockReadGuard, 279 device: &Device, 280 quirks_mode: QuirksMode, 281 custom_media_map: &CustomMediaMap, 282 rule: &MediaRule, 283 ) -> bool { 284 rule.media_queries.read_with(guard).evaluate( 285 device, 286 quirks_mode, 287 &mut CustomMediaEvaluator::new(custom_media_map, guard), 288 ) 289 } 290 291 fn process_document( 292 _: &SharedRwLockReadGuard, 293 device: &Device, 294 _: QuirksMode, 295 rule: &DocumentRule, 296 ) -> bool { 297 rule.condition.evaluate(device) 298 } 299 300 fn process_supports( 301 _: &SharedRwLockReadGuard, 302 _: &Device, 303 _: QuirksMode, 304 rule: &SupportsRule, 305 ) -> bool { 306 rule.enabled 307 } 308 } 309 310 /// A filter that processes all the rules in a rule list. 311 pub struct AllRules; 312 313 impl NestedRuleIterationCondition for AllRules { 314 fn process_import( 315 _: &SharedRwLockReadGuard, 316 _: &Device, 317 _: QuirksMode, 318 _: &CustomMediaMap, 319 _: &ImportRule, 320 ) -> bool { 321 true 322 } 323 324 fn process_media( 325 _: &SharedRwLockReadGuard, 326 _: &Device, 327 _: QuirksMode, 328 _: &CustomMediaMap, 329 _: &MediaRule, 330 ) -> bool { 331 true 332 } 333 334 fn process_document( 335 _: &SharedRwLockReadGuard, 336 _: &Device, 337 _: QuirksMode, 338 _: &DocumentRule, 339 ) -> bool { 340 true 341 } 342 343 fn process_supports( 344 _: &SharedRwLockReadGuard, 345 _: &Device, 346 _: QuirksMode, 347 _: &SupportsRule, 348 ) -> bool { 349 true 350 } 351 } 352 353 /// An iterator over all the effective rules of a stylesheet. 354 /// 355 /// NOTE: This iterator recurses into `@import` rules. 356 pub type EffectiveRulesIterator<'a, 'b, CMM> = RulesIterator<'a, 'b, EffectiveRules, CMM>; 357 358 impl<'a, 'b, CMM> EffectiveRulesIterator<'a, 'b, CMM> 359 where 360 CMM: Deref<Target = CustomMediaMap>, 361 { 362 /// Returns an iterator over the effective children of a rule, even if 363 /// `rule` itself is not effective. 364 pub fn effective_children( 365 device: &'a Device, 366 quirks_mode: QuirksMode, 367 custom_media_map: CMM, 368 guard: &'a SharedRwLockReadGuard<'b>, 369 rule: &'a CssRule, 370 ) -> Self { 371 let children = RulesIterator::<AllRules, CMM>::children( 372 rule, 373 device, 374 quirks_mode, 375 &custom_media_map, 376 guard, 377 &mut false, 378 ); 379 EffectiveRulesIterator::new( 380 device, 381 quirks_mode, 382 custom_media_map, 383 guard, 384 children.unwrap_or([].iter()), 385 ) 386 } 387 }