/* This Source Code Form is subject to the terms of the Mozilla Public *License,v.2.0.IfacopyoftheMPLwasnotdistributedwiththis
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
usecrate::applicable_declarations::ScopeProximity; usecrate::dom::TElement; usecrate::parser::ParserContext; usecrate::selector_parser::{SelectorImpl, SelectorParser}; usecrate::shared_lock::{
DeepCloneWithLock, Locked, SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard,
}; usecrate::str::CssStringWriter; usecrate::stylesheets::CssRules; usecrate::simple_buckets_map::SimpleBucketsMap; use cssparser::{Parser, SourceLocation, ToCss}; #[cfg(feature = "gecko")] use malloc_size_of::{
MallocSizeOfOps, MallocUnconditionalShallowSizeOf, MallocUnconditionalSizeOf,
}; use selectors::context::{MatchingContext, QuirksMode}; use selectors::matching::matches_selector; use selectors::parser::{Component, ParseRelative, Selector, SelectorList}; use selectors::OpaqueElement; use servo_arc::Arc; use std::fmt::{self, Write}; use style_traits::{CssWriter, ParseError};
/// A scoped rule. #[derive(Debug, ToShmem)] pubstruct ScopeRule { /// Bounds at which this rule applies. pub bounds: ScopeBounds, /// The nested rules inside the block. pub rules: Arc<Locked<CssRules>>, /// The source position where this rule was found. pub source_location: SourceLocation,
}
/// Bounds of the scope. #[derive(Debug, Clone, ToShmem)] pubstruct ScopeBounds { /// Start of the scope. pub start: Option<SelectorList<SelectorImpl>>, /// End of the scope. pub end: Option<SelectorList<SelectorImpl>>,
}
fn parse_scope<'a>(
context: &ParserContext,
input: &mut Parser<'a, '_>,
parse_relative: ParseRelative,
for_end: bool,
) -> Result<Option<SelectorList<SelectorImpl>>, ParseError<'a>> {
input
.try_parse(|input| { if for_end { // scope-end not existing is valid. if input.try_parse(|i| i.expect_ident_matching("to")).is_err() { return Ok(None);
}
} let parens = input.try_parse(|i| i.expect_parenthesis_block()); if for_end { // `@scope to {}` is NOT valid.
parens?;
} elseif parens.is_err() { // `@scope {}` is valid. return Ok(None);
}
input.parse_nested_block(|input| { if input.is_exhausted() { // `@scope () {}` is valid. return Ok(None);
} let selector_parser = SelectorParser {
stylesheet_origin: context.stylesheet_origin,
namespaces: &context.namespaces,
url_data: context.url_data,
for_supports_rule: false,
}; let parse_relative = if for_end {
ParseRelative::ForScope
} else {
parse_relative
};
Ok(Some(SelectorList::parse_disallow_pseudo(
&selector_parser,
input,
parse_relative,
)?))
})
})
}
impl ScopeBounds { /// Parse a container condition. pubfn parse<'a>(
context: &ParserContext,
input: &mut Parser<'a, '_>,
parse_relative: ParseRelative,
) -> Result<Self, ParseError<'a>> { let start = parse_scope(context, input, parse_relative, false)?; let end = parse_scope(context, input, parse_relative, true)?;
Ok(Self { start, end })
}
}
/// Types of implicit scope root. #[derive(Debug, Copy, Clone, MallocSizeOf)] pubenum ImplicitScopeRoot { /// This implicit scope root is in the light tree.
InLightTree(OpaqueElement), /// This implicit scope root is the document element, regardless of which (light|shadow) tree /// the element being matched is. This is the case for e.g. if you specified an implicit scope /// within a user stylesheet.
DocumentElement, /// The implicit scope root is in a constructed stylesheet - the scope root the element /// under consideration's shadow root (If one exists).
Constructed, /// This implicit scope root is in the shadow tree.
InShadowTree(OpaqueElement), /// This implicit scope root is the shadow host of the stylesheet-containing shadow tree.
ShadowHost(OpaqueElement),
}
/// Target of this implicit scope. pubenum ImplicitScopeTarget { /// Target matches only the specified element.
Element(OpaqueElement), /// Implicit scope whose target is the document element.
DocumentElement,
}
impl ImplicitScopeTarget { /// Check if this element is the implicit scope. fn check<E: TElement>(&self, element: E) -> bool { matchself { Self::Element(e) => element.opaque() == *e, Self::DocumentElement => element.is_root(),
}
}
}
/// Target of this scope. pubenum ScopeTarget<'a> { /// Target matches an element matching the specified selector list.
Selector(&'a SelectorList<SelectorImpl>), /// Target matches an implicit scope target.
Implicit(ImplicitScopeTarget),
}
impl<'a> ScopeTarget<'a> { /// Check if the given element is the scope. fn check<E: TElement>(
&self,
element: E,
scope: Option<OpaqueElement>,
scope_subject_map: &ScopeSubjectMap,
context: &mut MatchingContext<E::Impl>,
) -> bool { matchself { Self::Selector(list) => context.nest_for_scope_condition(scope, |context| { if scope_subject_map.early_reject(element, context.quirks_mode()) { returnfalse;
} for selector in list.slice().iter() { if matches_selector(selector, 0, None, &element, context) { returntrue;
}
} false
}), Self::Implicit(t) => t.check(element),
}
}
}
/// A scope root candidate. #[derive(Clone, Copy, Debug)] pubstruct ScopeRootCandidate { /// This candidate's scope root. pub root: OpaqueElement, /// Ancestor hop from the element under consideration to this scope root. pub proximity: ScopeProximity,
}
/// Collect potential scope roots for a given element and its scope target. /// The check may not pass the ceiling, if specified. pubfn collect_scope_roots<E>(
element: E,
ceiling: Option<OpaqueElement>,
context: &mut MatchingContext<E::Impl>,
target: &ScopeTarget,
matches_shadow_host: bool,
scope_subject_map: &ScopeSubjectMap,
) -> Vec<ScopeRootCandidate> where
E: TElement,
{ letmut result = vec![]; letmut parent = Some(element); letmut proximity = 0usize; whilelet Some(p) = parent { if ceiling == Some(p.opaque()) { break;
} if target.check(p, ceiling, scope_subject_map, context) {
result.push(ScopeRootCandidate {
root: p.opaque(),
proximity: ScopeProximity::new(proximity),
}); // Note that we can't really break here - we need to consider // ALL scope roots to figure out whch one didn't end.
}
parent = p.parent_element();
proximity += 1; // We we got to the top of the shadow tree - keep going // if we may match the shadow host. if parent.is_none() && matches_shadow_host {
parent = p.containing_shadow_host();
}
}
result
}
/// Given the scope-end selector, check if the element is outside of the scope. /// That is, check if any ancestor to the root matches the scope-end selector. pubfn element_is_outside_of_scope<E>(
selector: &Selector<E::Impl>,
element: E,
root: OpaqueElement,
context: &mut MatchingContext<E::Impl>,
root_may_be_shadow_host: bool,
) -> bool where
E: TElement,
{ letmut parent = Some(element);
context.nest_for_scope_condition(Some(root), |context| { whilelet Some(p) = parent { if matches_selector(selector, 0, None, &p, context) { returntrue;
} if p.opaque() == root { // Reached the top, not lying outside of scope. break;
}
parent = p.parent_element(); if parent.is_none() && root_may_be_shadow_host { iflet Some(host) = p.containing_shadow_host() { // Pretty much an edge case where user specified scope-start and -end of :host return host.opaque() == root;
}
}
} returnfalse;
})
}
/// A map containing simple selectors in subjects of scope selectors. /// This allows fast-rejecting scopes before running the full match. #[derive(Clone, Debug, Default, MallocSizeOf)] pubstruct ScopeSubjectMap {
buckets: SimpleBucketsMap<()>,
any: bool,
}
impl ScopeSubjectMap { /// Add the `<scope-start>` of a scope. pubfn add_bound_start(&mutself, selectors: &SelectorList<SelectorImpl>, quirks_mode: QuirksMode) { ifself.add_selector_list(selectors, quirks_mode) { self.any = true;
}
}
/// Could a given element possibly be a scope root? fn early_reject<E: TElement>(&self, element: E, quirks_mode: QuirksMode) -> bool { ifself.any { returnfalse;
}
letmut found = false;
element.each_class(|cls| { ifself.buckets.classes.get(cls, quirks_mode).is_some() {
found = true;
}
}); if found { returnfalse;
}
/// Determine if this selector list, when used as a scope bound selector, is considered trivial. pubfn scope_selector_list_is_trivial(list: &SelectorList<SelectorImpl>) -> bool { fn scope_selector_is_trivial(selector: &Selector<SelectorImpl>) -> bool { // A selector is trivial if: // * There is no selector conditional on its siblings and/or descendant to match, and // * There is no dependency on sibling relations, and // * There's no ID selector in the selector. A more correct approach may be to ensure that // scoping roots of the style sharing candidates and targets have matching IDs, but that // requires re-plumbing what we pass around for scope roots. letmut iter = selector.iter(); loop { whilelet Some(c) = iter.next() { match c {
Component::ID(_) | Component::Nth(_) | Component::NthOf(_) | Component::Has(_) => returnfalse,
Component::Is(ref list) | Component::Where(ref list) | Component::Negation(ref list) => if !scope_selector_list_is_trivial(list) { returnfalse;
}
_ => (),
}
}
match iter.next_sequence() {
Some(c) => if c.is_sibling() { returnfalse;
},
None => returntrue,
}
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.