scope_rule.rs (17221B)
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 [`@scope`][scope] rule. 6 //! 7 //! [scope]: https://drafts.csswg.org/css-cascade-6/#scoped-styles 8 9 use crate::applicable_declarations::ScopeProximity; 10 use crate::derives::*; 11 use crate::dom::TElement; 12 use crate::parser::ParserContext; 13 use crate::selector_parser::{SelectorImpl, SelectorParser}; 14 use crate::shared_lock::{ 15 DeepCloneWithLock, Locked, SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard, 16 }; 17 use crate::simple_buckets_map::SimpleBucketsMap; 18 use crate::stylesheets::CssRules; 19 use cssparser::{Parser, SourceLocation, ToCss}; 20 #[cfg(feature = "gecko")] 21 use malloc_size_of::{ 22 MallocSizeOfOps, MallocUnconditionalShallowSizeOf, MallocUnconditionalSizeOf, 23 }; 24 use selectors::context::{MatchingContext, QuirksMode}; 25 use selectors::matching::matches_selector; 26 use selectors::parser::{Component, ParseRelative, Selector, SelectorList}; 27 use selectors::OpaqueElement; 28 use servo_arc::Arc; 29 use std::fmt::{self, Write}; 30 use style_traits::{CssStringWriter, CssWriter, ParseError}; 31 32 /// A scoped rule. 33 #[derive(Debug, ToShmem)] 34 pub struct ScopeRule { 35 /// Bounds at which this rule applies. 36 pub bounds: ScopeBounds, 37 /// The nested rules inside the block. 38 pub rules: Arc<Locked<CssRules>>, 39 /// The source position where this rule was found. 40 pub source_location: SourceLocation, 41 } 42 43 impl DeepCloneWithLock for ScopeRule { 44 fn deep_clone_with_lock(&self, lock: &SharedRwLock, guard: &SharedRwLockReadGuard) -> Self { 45 let rules = self.rules.read_with(guard); 46 Self { 47 bounds: self.bounds.clone(), 48 rules: Arc::new(lock.wrap(rules.deep_clone_with_lock(lock, guard))), 49 source_location: self.source_location.clone(), 50 } 51 } 52 } 53 54 impl ToCssWithGuard for ScopeRule { 55 fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result { 56 dest.write_str("@scope")?; 57 { 58 let mut writer = CssWriter::new(dest); 59 if let Some(start) = self.bounds.start.as_ref() { 60 writer.write_str(" (")?; 61 start.to_css(&mut writer)?; 62 writer.write_char(')')?; 63 } 64 if let Some(end) = self.bounds.end.as_ref() { 65 writer.write_str(" to (")?; 66 end.to_css(&mut writer)?; 67 writer.write_char(')')?; 68 } 69 } 70 self.rules.read_with(guard).to_css_block(guard, dest) 71 } 72 } 73 74 impl ScopeRule { 75 /// Measure heap usage. 76 #[cfg(feature = "gecko")] 77 pub fn size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize { 78 self.rules.unconditional_shallow_size_of(ops) 79 + self.rules.read_with(guard).size_of(guard, ops) 80 + self.bounds.size_of(ops) 81 } 82 } 83 84 /// Bounds of the scope. 85 #[derive(Debug, Clone, ToShmem)] 86 pub struct ScopeBounds { 87 /// Start of the scope. 88 pub start: Option<SelectorList<SelectorImpl>>, 89 /// End of the scope. 90 pub end: Option<SelectorList<SelectorImpl>>, 91 } 92 93 impl ScopeBounds { 94 #[cfg(feature = "gecko")] 95 fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { 96 fn bound_size_of( 97 bound: &Option<SelectorList<SelectorImpl>>, 98 ops: &mut MallocSizeOfOps, 99 ) -> usize { 100 bound 101 .as_ref() 102 .map(|list| list.unconditional_size_of(ops)) 103 .unwrap_or(0) 104 } 105 bound_size_of(&self.start, ops) + bound_size_of(&self.end, ops) 106 } 107 } 108 109 fn parse_scope<'a>( 110 context: &ParserContext, 111 input: &mut Parser<'a, '_>, 112 parse_relative: ParseRelative, 113 for_end: bool, 114 ) -> Result<Option<SelectorList<SelectorImpl>>, ParseError<'a>> { 115 input.try_parse(|input| { 116 if for_end { 117 // scope-end not existing is valid. 118 if input.try_parse(|i| i.expect_ident_matching("to")).is_err() { 119 return Ok(None); 120 } 121 } 122 let parens = input.try_parse(|i| i.expect_parenthesis_block()); 123 if for_end { 124 // `@scope to {}` is NOT valid. 125 parens?; 126 } else if parens.is_err() { 127 // `@scope {}` is valid. 128 return Ok(None); 129 } 130 input.parse_nested_block(|input| { 131 let selector_parser = SelectorParser { 132 stylesheet_origin: context.stylesheet_origin, 133 namespaces: &context.namespaces, 134 url_data: context.url_data, 135 for_supports_rule: false, 136 }; 137 let parse_relative = if for_end { 138 ParseRelative::ForScope 139 } else { 140 parse_relative 141 }; 142 Ok(Some(SelectorList::parse_disallow_pseudo( 143 &selector_parser, 144 input, 145 parse_relative, 146 )?)) 147 }) 148 }) 149 } 150 151 impl ScopeBounds { 152 /// Parse a container condition. 153 pub fn parse<'a>( 154 context: &ParserContext, 155 input: &mut Parser<'a, '_>, 156 parse_relative: ParseRelative, 157 ) -> Result<Self, ParseError<'a>> { 158 let start = parse_scope(context, input, parse_relative, false)?; 159 let end = parse_scope(context, input, parse_relative, true)?; 160 Ok(Self { start, end }) 161 } 162 } 163 164 /// Types of implicit scope root. 165 #[derive(Debug, Copy, Clone, MallocSizeOf)] 166 pub enum ImplicitScopeRoot { 167 /// This implicit scope root is in the light tree. 168 InLightTree(OpaqueElement), 169 /// This implicit scope root is the document element, regardless of which (light|shadow) tree 170 /// the element being matched is. This is the case for e.g. if you specified an implicit scope 171 /// within a user stylesheet. 172 DocumentElement, 173 /// The implicit scope root is in a constructed stylesheet - the scope root the element 174 /// under consideration's shadow root (If one exists). 175 Constructed, 176 /// This implicit scope root is in the shadow tree. 177 InShadowTree(OpaqueElement), 178 /// This implicit scope root is the shadow host of the stylesheet-containing shadow tree. 179 ShadowHost(OpaqueElement), 180 } 181 182 impl ImplicitScopeRoot { 183 /// Return true if this matches the shadow host. 184 pub fn matches_shadow_host(&self) -> bool { 185 match self { 186 Self::InLightTree(..) | Self::InShadowTree(..) | Self::DocumentElement => false, 187 Self::ShadowHost(..) | Self::Constructed => true, 188 } 189 } 190 191 /// Return the implicit scope root element. 192 pub fn element(&self, current_host: Option<OpaqueElement>) -> ImplicitScopeTarget { 193 match self { 194 Self::InLightTree(e) | Self::InShadowTree(e) | Self::ShadowHost(e) => { 195 ImplicitScopeTarget::Element(*e) 196 }, 197 Self::Constructed | Self::DocumentElement => { 198 if matches!(self, Self::Constructed) { 199 if let Some(host) = current_host { 200 return ImplicitScopeTarget::Element(host); 201 } 202 } 203 ImplicitScopeTarget::DocumentElement 204 }, 205 } 206 } 207 } 208 209 /// Target of this implicit scope. 210 pub enum ImplicitScopeTarget { 211 /// Target matches only the specified element. 212 Element(OpaqueElement), 213 /// Implicit scope whose target is the document element. 214 DocumentElement, 215 } 216 217 impl ImplicitScopeTarget { 218 /// Check if this element is the implicit scope. 219 fn check<E: TElement>(&self, element: E) -> bool { 220 match self { 221 Self::Element(e) => element.opaque() == *e, 222 Self::DocumentElement => element.is_root(), 223 } 224 } 225 } 226 227 /// Target of this scope. 228 pub enum ScopeTarget<'a> { 229 /// Target matches an element matching the specified selector list. 230 Selector(&'a SelectorList<SelectorImpl>), 231 /// Target matches an implicit scope target. 232 Implicit(ImplicitScopeTarget), 233 } 234 235 impl<'a> ScopeTarget<'a> { 236 /// Check if the given element is the scope. 237 fn check<E: TElement>( 238 &self, 239 element: E, 240 scope: Option<OpaqueElement>, 241 scope_subject_map: &ScopeSubjectMap, 242 context: &mut MatchingContext<E::Impl>, 243 ) -> bool { 244 match self { 245 Self::Selector(list) => context.nest_for_scope_condition(scope, |context| { 246 if scope_subject_map.early_reject(element, context.quirks_mode()) { 247 return false; 248 } 249 for selector in list.slice().iter() { 250 if matches_selector(selector, 0, None, &element, context) { 251 return true; 252 } 253 } 254 false 255 }), 256 Self::Implicit(t) => t.check(element), 257 } 258 } 259 } 260 261 /// A scope root candidate. 262 #[derive(Clone, Copy, Debug)] 263 pub struct ScopeRootCandidate { 264 /// This candidate's scope root. 265 pub root: OpaqueElement, 266 /// Ancestor hop from the element under consideration to this scope root. 267 pub proximity: ScopeProximity, 268 } 269 270 impl ScopeRootCandidate { 271 /// Get the element corresponding to this scope root candidate. 272 pub fn get_scope_root_element<E>(&self, originating_element: E) -> Option<E> 273 where 274 E: TElement, 275 { 276 // Could just unsafe-convert from opaque element - technically 277 // faster as well, but it doesn't seem worth having to manually 278 // assure safety every time. 279 let mut e = originating_element; 280 let hops = self.proximity.get()?; 281 for _ in 0..hops { 282 e = e.parent_element()?; 283 } 284 debug_assert_eq!(e.opaque(), self.root); 285 Some(e) 286 } 287 } 288 289 /// Collect potential scope roots for a given element and its scope target. 290 /// The check may not pass the ceiling, if specified. 291 pub fn collect_scope_roots<E>( 292 element: E, 293 ceiling: Option<OpaqueElement>, 294 context: &mut MatchingContext<E::Impl>, 295 target: &ScopeTarget, 296 matches_shadow_host: bool, 297 scope_subject_map: &ScopeSubjectMap, 298 ) -> Vec<ScopeRootCandidate> 299 where 300 E: TElement, 301 { 302 let mut result = vec![]; 303 let mut parent = Some(element); 304 let mut proximity = 0usize; 305 while let Some(p) = parent { 306 if target.check(p, ceiling, scope_subject_map, context) { 307 result.push(ScopeRootCandidate { 308 root: p.opaque(), 309 proximity: ScopeProximity::new(proximity), 310 }); 311 // Note that we can't really break here - we need to consider 312 // ALL scope roots to figure out whch one didn't end. 313 } 314 if ceiling == Some(p.opaque()) { 315 break; 316 } 317 parent = p.parent_element(); 318 proximity += 1; 319 // We we got to the top of the shadow tree - keep going 320 // if we may match the shadow host. 321 if parent.is_none() && matches_shadow_host { 322 parent = p.containing_shadow_host(); 323 } 324 } 325 result 326 } 327 328 /// Given the scope-end selector, check if the element is outside of the scope. 329 /// That is, check if any ancestor to the root matches the scope-end selector. 330 pub fn element_is_outside_of_scope<E>( 331 selector: &Selector<E::Impl>, 332 element: E, 333 root: OpaqueElement, 334 context: &mut MatchingContext<E::Impl>, 335 root_may_be_shadow_host: bool, 336 ) -> bool 337 where 338 E: TElement, 339 { 340 let mut parent = Some(element); 341 context.nest_for_scope_condition(Some(root), |context| { 342 while let Some(p) = parent { 343 if matches_selector(selector, 0, None, &p, context) { 344 return true; 345 } 346 if p.opaque() == root { 347 // Reached the top, not lying outside of scope. 348 break; 349 } 350 parent = p.parent_element(); 351 if parent.is_none() && root_may_be_shadow_host { 352 if let Some(host) = p.containing_shadow_host() { 353 // Pretty much an edge case where user specified scope-start and -end of :host 354 return host.opaque() == root; 355 } 356 } 357 } 358 return false; 359 }) 360 } 361 362 /// A map containing simple selectors in subjects of scope selectors. 363 /// This allows fast-rejecting scopes before running the full match. 364 #[derive(Clone, Debug, Default, MallocSizeOf)] 365 pub struct ScopeSubjectMap { 366 buckets: SimpleBucketsMap<()>, 367 any: bool, 368 } 369 370 impl ScopeSubjectMap { 371 /// Add the `<scope-start>` of a scope. 372 pub fn add_bound_start( 373 &mut self, 374 selectors: &SelectorList<SelectorImpl>, 375 quirks_mode: QuirksMode, 376 ) { 377 if self.add_selector_list(selectors, quirks_mode) { 378 self.any = true; 379 } 380 } 381 382 fn add_selector_list( 383 &mut self, 384 selectors: &SelectorList<SelectorImpl>, 385 quirks_mode: QuirksMode, 386 ) -> bool { 387 let mut is_any = false; 388 for selector in selectors.slice().iter() { 389 is_any = is_any || self.add_selector(selector, quirks_mode); 390 } 391 is_any 392 } 393 394 fn add_selector(&mut self, selector: &Selector<SelectorImpl>, quirks_mode: QuirksMode) -> bool { 395 let mut is_any = true; 396 let mut iter = selector.iter(); 397 while let Some(c) = iter.next() { 398 let component_any = match c { 399 Component::Class(cls) => { 400 match self.buckets.classes.try_entry(cls.0.clone(), quirks_mode) { 401 Ok(e) => { 402 e.or_insert(()); 403 false 404 }, 405 Err(_) => true, 406 } 407 }, 408 Component::ID(id) => match self.buckets.ids.try_entry(id.0.clone(), quirks_mode) { 409 Ok(e) => { 410 e.or_insert(()); 411 false 412 }, 413 Err(_) => true, 414 }, 415 Component::LocalName(local_name) => { 416 self.buckets 417 .local_names 418 .insert(local_name.lower_name.clone(), ()); 419 false 420 }, 421 Component::Is(ref list) | Component::Where(ref list) => { 422 self.add_selector_list(list, quirks_mode) 423 }, 424 _ => true, 425 }; 426 427 is_any = is_any && component_any; 428 } 429 is_any 430 } 431 432 /// Shrink the map as much as possible. 433 pub fn shrink_if_needed(&mut self) { 434 self.buckets.shrink_if_needed(); 435 } 436 437 /// Clear the map. 438 pub fn clear(&mut self) { 439 self.buckets.clear(); 440 self.any = false; 441 } 442 443 /// Could a given element possibly be a scope root? 444 fn early_reject<E: TElement>(&self, element: E, quirks_mode: QuirksMode) -> bool { 445 if self.any { 446 return false; 447 } 448 449 if let Some(id) = element.id() { 450 if self.buckets.ids.get(id, quirks_mode).is_some() { 451 return false; 452 } 453 } 454 455 let mut found = false; 456 element.each_class(|cls| { 457 if self.buckets.classes.get(cls, quirks_mode).is_some() { 458 found = true; 459 } 460 }); 461 if found { 462 return false; 463 } 464 465 if self.buckets.local_names.get(element.local_name()).is_some() { 466 return false; 467 } 468 469 true 470 } 471 } 472 473 /// Determine if this selector list, when used as a scope bound selector, is considered trivial. 474 pub fn scope_selector_list_is_trivial(list: &SelectorList<SelectorImpl>) -> bool { 475 fn scope_selector_is_trivial(selector: &Selector<SelectorImpl>) -> bool { 476 // A selector is trivial if: 477 // * There is no selector conditional on its siblings and/or descendant to match, and 478 // * There is no dependency on sibling relations, and 479 // * There's no ID selector in the selector. A more correct approach may be to ensure that 480 // scoping roots of the style sharing candidates and targets have matching IDs, but that 481 // requires re-plumbing what we pass around for scope roots. 482 let mut iter = selector.iter(); 483 loop { 484 while let Some(c) = iter.next() { 485 match c { 486 Component::ID(_) 487 | Component::Nth(_) 488 | Component::NthOf(_) 489 | Component::Has(_) => return false, 490 Component::Is(ref list) 491 | Component::Where(ref list) 492 | Component::Negation(ref list) => { 493 if !scope_selector_list_is_trivial(list) { 494 return false; 495 } 496 }, 497 _ => (), 498 } 499 } 500 501 match iter.next_sequence() { 502 Some(c) => { 503 if c.is_sibling() { 504 return false; 505 } 506 }, 507 None => return true, 508 } 509 } 510 } 511 512 list.slice().iter().all(|s| scope_selector_is_trivial(s)) 513 }