mirror of
https://github.com/servo/servo.git
synced 2025-08-07 06:25:32 +01:00
Bug 1364850: Move PseudoElement to be just another combinator in selectors. r=bholley
MozReview-Commit-ID: 8OoOIodkKJ5 Signed-off-by: Emilio Cobos Álvarez <emilio@crisal.io>
This commit is contained in:
parent
8375319928
commit
522f8489d6
19 changed files with 675 additions and 541 deletions
|
@ -10,12 +10,24 @@
|
|||
|
||||
use cssparser::ToCss;
|
||||
use gecko_bindings::structs::{self, CSSPseudoElementType};
|
||||
use selector_parser::PseudoElementCascadeType;
|
||||
use selector_parser::{NonTSPseudoClass, PseudoElementCascadeType, SelectorImpl};
|
||||
use std::fmt;
|
||||
use string_cache::Atom;
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/gecko/pseudo_element_definition.rs"));
|
||||
|
||||
impl ::selectors::parser::PseudoElement for PseudoElement {
|
||||
type Impl = SelectorImpl;
|
||||
|
||||
fn supports_pseudo_class(&self, pseudo_class: &NonTSPseudoClass) -> bool {
|
||||
if !self.supports_user_action_state() {
|
||||
return false;
|
||||
}
|
||||
|
||||
return pseudo_class.is_safe_user_action_state();
|
||||
}
|
||||
}
|
||||
|
||||
impl PseudoElement {
|
||||
/// Returns the kind of cascade type that a given pseudo is going to use.
|
||||
///
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
//! Gecko-specific bits for selector-parsing.
|
||||
|
||||
use cssparser::{Parser, ToCss};
|
||||
use element_state::{IN_ACTIVE_STATE, IN_FOCUS_STATE, IN_HOVER_STATE};
|
||||
use element_state::ElementState;
|
||||
use gecko_bindings::structs::CSSPseudoClassType;
|
||||
use selector_parser::{SelectorParser, PseudoElementCascadeType};
|
||||
|
@ -132,7 +131,7 @@ impl NonTSPseudoClass {
|
|||
/// https://drafts.csswg.org/selectors-4/#useraction-pseudos
|
||||
///
|
||||
/// We intentionally skip the link-related ones.
|
||||
fn is_safe_user_action_state(&self) -> bool {
|
||||
pub fn is_safe_user_action_state(&self) -> bool {
|
||||
matches!(*self, NonTSPseudoClass::Hover |
|
||||
NonTSPseudoClass::Active |
|
||||
NonTSPseudoClass::Focus)
|
||||
|
@ -195,58 +194,6 @@ impl NonTSPseudoClass {
|
|||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct SelectorImpl;
|
||||
|
||||
/// Some subset of pseudo-elements in Gecko are sensitive to some state
|
||||
/// selectors.
|
||||
///
|
||||
/// We store the sensitive states in this struct in order to properly handle
|
||||
/// these.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct PseudoElementSelector {
|
||||
pseudo: PseudoElement,
|
||||
state: ElementState,
|
||||
}
|
||||
|
||||
impl PseudoElementSelector {
|
||||
/// Returns the pseudo-element this selector represents.
|
||||
pub fn pseudo_element(&self) -> &PseudoElement {
|
||||
&self.pseudo
|
||||
}
|
||||
|
||||
/// Returns the pseudo-element selector state.
|
||||
pub fn state(&self) -> ElementState {
|
||||
self.state
|
||||
}
|
||||
}
|
||||
|
||||
impl ToCss for PseudoElementSelector {
|
||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result
|
||||
where W: fmt::Write,
|
||||
{
|
||||
if cfg!(debug_assertions) {
|
||||
let mut state = self.state;
|
||||
state.remove(IN_HOVER_STATE | IN_ACTIVE_STATE | IN_FOCUS_STATE);
|
||||
assert_eq!(state, ElementState::empty(),
|
||||
"Unhandled pseudo-element state selector?");
|
||||
}
|
||||
|
||||
self.pseudo.to_css(dest)?;
|
||||
|
||||
if self.state.contains(IN_HOVER_STATE) {
|
||||
dest.write_str(":hover")?
|
||||
}
|
||||
|
||||
if self.state.contains(IN_ACTIVE_STATE) {
|
||||
dest.write_str(":active")?
|
||||
}
|
||||
|
||||
if self.state.contains(IN_FOCUS_STATE) {
|
||||
dest.write_str(":focus")?
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl ::selectors::SelectorImpl for SelectorImpl {
|
||||
type AttrValue = Atom;
|
||||
type Identifier = Atom;
|
||||
|
@ -257,7 +204,7 @@ impl ::selectors::SelectorImpl for SelectorImpl {
|
|||
type BorrowedNamespaceUrl = WeakNamespace;
|
||||
type BorrowedLocalName = WeakAtom;
|
||||
|
||||
type PseudoElementSelector = PseudoElementSelector;
|
||||
type PseudoElement = PseudoElement;
|
||||
type NonTSPseudoClass = NonTSPseudoClass;
|
||||
}
|
||||
|
||||
|
@ -319,38 +266,9 @@ impl<'a> ::selectors::Parser for SelectorParser<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn parse_pseudo_element(&self, name: Cow<str>, input: &mut Parser) -> Result<PseudoElementSelector, ()> {
|
||||
let pseudo =
|
||||
match PseudoElement::from_slice(&name, self.in_user_agent_stylesheet()) {
|
||||
Some(pseudo) => pseudo,
|
||||
None => return Err(()),
|
||||
};
|
||||
|
||||
let state = if pseudo.supports_user_action_state() {
|
||||
input.try(|input| {
|
||||
let mut state = ElementState::empty();
|
||||
|
||||
while !input.is_exhausted() {
|
||||
input.expect_colon()?;
|
||||
let ident = input.expect_ident()?;
|
||||
let pseudo_class = self.parse_non_ts_pseudo_class(ident)?;
|
||||
|
||||
if !pseudo_class.is_safe_user_action_state() {
|
||||
return Err(())
|
||||
}
|
||||
state.insert(pseudo_class.state_flag());
|
||||
}
|
||||
|
||||
Ok(state)
|
||||
}).ok()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Ok(PseudoElementSelector {
|
||||
pseudo: pseudo,
|
||||
state: state.unwrap_or(ElementState::empty()),
|
||||
})
|
||||
fn parse_pseudo_element(&self, name: Cow<str>) -> Result<PseudoElement, ()> {
|
||||
PseudoElement::from_slice(&name, self.in_user_agent_stylesheet())
|
||||
.ok_or(())
|
||||
}
|
||||
|
||||
fn default_namespace(&self) -> Option<Namespace> {
|
||||
|
|
|
@ -64,7 +64,7 @@ use properties::style_structs::Font;
|
|||
use rule_tree::CascadeLevel as ServoCascadeLevel;
|
||||
use selector_parser::ElementExt;
|
||||
use selectors::Element;
|
||||
use selectors::matching::{ElementSelectorFlags, MatchingContext};
|
||||
use selectors::matching::{ElementSelectorFlags, MatchingContext, MatchingMode};
|
||||
use selectors::parser::{AttrSelector, NamespaceConstraint};
|
||||
use shared_lock::Locked;
|
||||
use sink::Push;
|
||||
|
@ -1055,6 +1055,11 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
|
|||
parent_node.and_then(|n| n.as_element())
|
||||
}
|
||||
|
||||
fn pseudo_element_originating_element(&self) -> Option<Self> {
|
||||
debug_assert!(self.implemented_pseudo_element().is_some());
|
||||
self.closest_non_native_anonymous_ancestor()
|
||||
}
|
||||
|
||||
fn first_child_element(&self) -> Option<Self> {
|
||||
let mut child = self.as_node().first_child();
|
||||
while let Some(child_node) = child {
|
||||
|
@ -1244,6 +1249,20 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
|
|||
}
|
||||
}
|
||||
|
||||
fn match_pseudo_element(&self,
|
||||
pseudo_element: &PseudoElement,
|
||||
_context: &mut MatchingContext)
|
||||
-> bool
|
||||
{
|
||||
// TODO(emilio): I believe we could assert we are a pseudo-element and
|
||||
// match the proper pseudo-element, given how we rulehash the stuff
|
||||
// based on the pseudo.
|
||||
match self.implemented_pseudo_element() {
|
||||
Some(ref pseudo) => pseudo == pseudo_element,
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_id(&self) -> Option<Atom> {
|
||||
let ptr = unsafe {
|
||||
bindings::Gecko_AtomAttrValue(self.0,
|
||||
|
@ -1378,8 +1397,9 @@ impl<'le> ::selectors::MatchAttr for GeckoElement<'le> {
|
|||
impl<'le> ElementExt for GeckoElement<'le> {
|
||||
#[inline]
|
||||
fn is_link(&self) -> bool {
|
||||
let mut context = MatchingContext::new(MatchingMode::Normal, None);
|
||||
self.match_non_ts_pseudo_class(&NonTSPseudoClass::AnyLink,
|
||||
&mut MatchingContext::default(),
|
||||
&mut context,
|
||||
&mut |_, _| {})
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,6 @@ use cascade_info::CascadeInfo;
|
|||
use context::{CurrentElementInfo, SelectorFlagsMap, SharedStyleContext, StyleContext};
|
||||
use data::{ComputedStyle, ElementData, ElementStyles, RestyleData};
|
||||
use dom::{AnimationRules, SendElement, TElement, TNode};
|
||||
use element_state::ElementState;
|
||||
use font_metrics::FontMetricsProvider;
|
||||
use properties::{CascadeFlags, ComputedValues, SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP, cascade};
|
||||
use properties::longhands::display::computed_value as display;
|
||||
|
@ -24,7 +23,7 @@ use restyle_hints::{RESTYLE_STYLE_ATTRIBUTE, RESTYLE_SMIL};
|
|||
use rule_tree::{CascadeLevel, RuleTree, StrongRuleNode};
|
||||
use selector_parser::{PseudoElement, RestyleDamage, SelectorImpl};
|
||||
use selectors::bloom::BloomFilter;
|
||||
use selectors::matching::{ElementSelectorFlags, MatchingContext, StyleRelations};
|
||||
use selectors::matching::{ElementSelectorFlags, MatchingContext, MatchingMode, StyleRelations};
|
||||
use selectors::matching::AFFECTED_BY_PSEUDO_ELEMENTS;
|
||||
use shared_lock::StylesheetGuards;
|
||||
use sink::ForgetfulSink;
|
||||
|
@ -895,10 +894,10 @@ pub trait MatchMethods : TElement {
|
|||
sharing: StyleSharingBehavior)
|
||||
{
|
||||
// Perform selector matching for the primary style.
|
||||
let mut primary_matching_context = MatchingContext::default();
|
||||
let mut relations = StyleRelations::empty();
|
||||
let _rule_node_changed = self.match_primary(context,
|
||||
data,
|
||||
&mut primary_matching_context);
|
||||
&mut relations);
|
||||
|
||||
// Cascade properties and compute primary values.
|
||||
self.cascade_primary(context, data);
|
||||
|
@ -912,7 +911,7 @@ pub trait MatchMethods : TElement {
|
|||
|
||||
// If we have any pseudo elements, indicate so in the primary StyleRelations.
|
||||
if !data.styles().pseudos.is_empty() {
|
||||
primary_matching_context.relations |= AFFECTED_BY_PSEUDO_ELEMENTS;
|
||||
relations |= AFFECTED_BY_PSEUDO_ELEMENTS;
|
||||
}
|
||||
|
||||
// If the style is shareable, add it to the LRU cache.
|
||||
|
@ -932,7 +931,7 @@ pub trait MatchMethods : TElement {
|
|||
.style_sharing_candidate_cache
|
||||
.insert_if_possible(self,
|
||||
data.styles().primary.values(),
|
||||
primary_matching_context.relations,
|
||||
relations,
|
||||
revalidation_match_results);
|
||||
}
|
||||
}
|
||||
|
@ -952,7 +951,7 @@ pub trait MatchMethods : TElement {
|
|||
fn match_primary(&self,
|
||||
context: &mut StyleContext<Self>,
|
||||
data: &mut ElementData,
|
||||
matching_context: &mut MatchingContext)
|
||||
relations: &mut StyleRelations)
|
||||
-> bool
|
||||
{
|
||||
let implemented_pseudo = self.implemented_pseudo_element();
|
||||
|
@ -1004,35 +1003,27 @@ pub trait MatchMethods : TElement {
|
|||
let animation_rules = self.get_animation_rules();
|
||||
let bloom = context.thread_local.bloom_filter.filter();
|
||||
|
||||
|
||||
let map = &mut context.thread_local.selector_flags;
|
||||
let mut set_selector_flags = |element: &Self, flags: ElementSelectorFlags| {
|
||||
self.apply_selector_flags(map, element, flags);
|
||||
};
|
||||
|
||||
let selector_matching_target = match implemented_pseudo {
|
||||
Some(..) => {
|
||||
self.closest_non_native_anonymous_ancestor()
|
||||
.expect("Pseudo-element without non-NAC parent?")
|
||||
},
|
||||
None => *self,
|
||||
};
|
||||
|
||||
let pseudo_and_state = match implemented_pseudo {
|
||||
Some(ref pseudo) => Some((pseudo, self.get_state())),
|
||||
None => None,
|
||||
};
|
||||
let mut matching_context =
|
||||
MatchingContext::new(MatchingMode::Normal, Some(bloom));
|
||||
|
||||
// Compute the primary rule node.
|
||||
stylist.push_applicable_declarations(&selector_matching_target,
|
||||
Some(bloom),
|
||||
stylist.push_applicable_declarations(self,
|
||||
implemented_pseudo.as_ref(),
|
||||
style_attribute,
|
||||
smil_override,
|
||||
animation_rules,
|
||||
pseudo_and_state,
|
||||
&mut applicable_declarations,
|
||||
matching_context,
|
||||
&mut matching_context,
|
||||
&mut set_selector_flags);
|
||||
|
||||
*relations = matching_context.relations;
|
||||
|
||||
let primary_rule_node =
|
||||
compute_rule_node::<Self>(&stylist.rule_tree,
|
||||
&mut applicable_declarations,
|
||||
|
@ -1041,8 +1032,8 @@ pub trait MatchMethods : TElement {
|
|||
return data.set_primary_rules(primary_rule_node);
|
||||
}
|
||||
|
||||
/// Runs selector matching to (re)compute eager pseudo-element rule nodes for this
|
||||
/// element.
|
||||
/// Runs selector matching to (re)compute eager pseudo-element rule nodes
|
||||
/// for this element.
|
||||
///
|
||||
/// Returns whether any of the pseudo rule nodes changed (including, but not
|
||||
/// limited to, cases where we match different pseudos altogether).
|
||||
|
@ -1070,6 +1061,10 @@ pub trait MatchMethods : TElement {
|
|||
let rule_tree = &stylist.rule_tree;
|
||||
let bloom_filter = context.thread_local.bloom_filter.filter();
|
||||
|
||||
let mut matching_context =
|
||||
MatchingContext::new(MatchingMode::ForStatelessPseudoElement,
|
||||
Some(bloom_filter));
|
||||
|
||||
// Compute rule nodes for eagerly-cascaded pseudo-elements.
|
||||
let mut matches_different_pseudos = false;
|
||||
let mut rule_nodes_changed = false;
|
||||
|
@ -1079,13 +1074,12 @@ pub trait MatchMethods : TElement {
|
|||
// NB: We handle animation rules for ::before and ::after when
|
||||
// traversing them.
|
||||
stylist.push_applicable_declarations(self,
|
||||
Some(bloom_filter),
|
||||
Some(&pseudo),
|
||||
None,
|
||||
None,
|
||||
AnimationRules(None, None),
|
||||
Some((&pseudo, ElementState::empty())),
|
||||
&mut applicable_declarations,
|
||||
&mut MatchingContext::default(),
|
||||
&mut matching_context,
|
||||
&mut set_selector_flags);
|
||||
|
||||
if !applicable_declarations.is_empty() {
|
||||
|
|
|
@ -9,14 +9,13 @@
|
|||
use Atom;
|
||||
use dom::TElement;
|
||||
use element_state::*;
|
||||
use fnv::FnvHashMap;
|
||||
#[cfg(feature = "gecko")]
|
||||
use gecko_bindings::structs::nsRestyleHint;
|
||||
#[cfg(feature = "servo")]
|
||||
use heapsize::HeapSizeOf;
|
||||
use selector_parser::{AttrValue, NonTSPseudoClass, PseudoElement, SelectorImpl, Snapshot, SnapshotMap};
|
||||
use selectors::{Element, MatchAttr};
|
||||
use selectors::matching::{ElementSelectorFlags, MatchingContext};
|
||||
use selectors::matching::{ElementSelectorFlags, MatchingContext, MatchingMode};
|
||||
use selectors::matching::matches_selector;
|
||||
use selectors::parser::{AttrSelector, Combinator, Component, Selector};
|
||||
use selectors::parser::{SelectorInner, SelectorMethods};
|
||||
|
@ -406,6 +405,14 @@ impl<'a, E> Element for ElementWrapper<'a, E>
|
|||
}
|
||||
}
|
||||
|
||||
fn match_pseudo_element(&self,
|
||||
pseudo_element: &PseudoElement,
|
||||
context: &mut MatchingContext)
|
||||
-> bool
|
||||
{
|
||||
self.element.match_pseudo_element(pseudo_element, context)
|
||||
}
|
||||
|
||||
fn parent_element(&self) -> Option<Self> {
|
||||
self.element.parent_element()
|
||||
.map(|e| ElementWrapper::new(e, self.snapshot_map))
|
||||
|
@ -475,6 +482,11 @@ impl<'a, E> Element for ElementWrapper<'a, E>
|
|||
_ => self.element.each_class(callback)
|
||||
}
|
||||
}
|
||||
|
||||
fn pseudo_element_originating_element(&self) -> Option<Self> {
|
||||
self.element.closest_non_native_anonymous_ancestor()
|
||||
.map(|e| ElementWrapper::new(e, self.snapshot_map))
|
||||
}
|
||||
}
|
||||
|
||||
fn selector_to_state(sel: &Component<SelectorImpl>) -> ElementState {
|
||||
|
@ -507,6 +519,9 @@ fn combinator_to_restyle_hint(combinator: Option<Combinator>) -> RestyleHint {
|
|||
match combinator {
|
||||
None => RESTYLE_SELF,
|
||||
Some(c) => match c {
|
||||
// NB: RESTYLE_SELF is needed to handle properly eager pseudos,
|
||||
// otherwise we may leave a stale style on the parent.
|
||||
Combinator::PseudoElement => RESTYLE_SELF | RESTYLE_DESCENDANTS,
|
||||
Combinator::Child => RESTYLE_DESCENDANTS,
|
||||
Combinator::Descendant => RESTYLE_DESCENDANTS,
|
||||
Combinator::NextSibling => RESTYLE_LATER_SIBLINGS,
|
||||
|
@ -634,13 +649,6 @@ impl SelectorVisitor for SensitivitiesVisitor {
|
|||
#[derive(Debug)]
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
pub struct DependencySet {
|
||||
/// A map used for pseudo-element's dependencies.
|
||||
///
|
||||
/// Note that pseudo-elements are somewhat special, because some of them in
|
||||
/// Gecko track state, and also because they don't do selector-matching as
|
||||
/// normal, but against their parent element.
|
||||
pseudo_dependencies: FnvHashMap<PseudoElement, SelectorMap<PseudoElementDependency>>,
|
||||
|
||||
/// This is for all other normal element's selectors/selector parts.
|
||||
dependencies: SelectorMap<Dependency>,
|
||||
}
|
||||
|
@ -668,34 +676,9 @@ impl DependencySet {
|
|||
index += 1; // Account for the simple selector.
|
||||
}
|
||||
|
||||
|
||||
let pseudo_selector_is_state_dependent =
|
||||
sequence_start == 0 &&
|
||||
selector.pseudo_element.as_ref().map_or(false, |pseudo_selector| {
|
||||
!pseudo_selector.state().is_empty()
|
||||
});
|
||||
|
||||
if pseudo_selector_is_state_dependent {
|
||||
let pseudo_selector = selector.pseudo_element.as_ref().unwrap();
|
||||
self.pseudo_dependencies
|
||||
.entry(pseudo_selector.pseudo_element().clone())
|
||||
.or_insert_with(SelectorMap::new)
|
||||
.insert(PseudoElementDependency {
|
||||
selector: selector.clone(),
|
||||
});
|
||||
}
|
||||
|
||||
// If we found a sensitivity, add an entry in the dependency set.
|
||||
if !visitor.sensitivities.is_empty() {
|
||||
let mut hint = combinator_to_restyle_hint(combinator);
|
||||
|
||||
if sequence_start == 0 && selector.pseudo_element.is_some() {
|
||||
// FIXME(emilio): Be more granular about this. See the
|
||||
// comment in `PseudoElementDependency` about how could this
|
||||
// be modified in order to be more efficient and restyle
|
||||
// less.
|
||||
hint |= RESTYLE_DESCENDANTS;
|
||||
}
|
||||
let hint = combinator_to_restyle_hint(combinator);
|
||||
|
||||
let dep_selector = if sequence_start == 0 {
|
||||
// Reuse the bloom hashes if this is the base selector.
|
||||
|
@ -724,82 +707,22 @@ impl DependencySet {
|
|||
pub fn new() -> Self {
|
||||
DependencySet {
|
||||
dependencies: SelectorMap::new(),
|
||||
pseudo_dependencies: FnvHashMap::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the total number of dependencies that this set contains.
|
||||
pub fn len(&self) -> usize {
|
||||
self.dependencies.len() +
|
||||
self.pseudo_dependencies.values().fold(0, |acc, val| acc + val.len())
|
||||
self.dependencies.len()
|
||||
}
|
||||
|
||||
/// Clear this dependency set.
|
||||
pub fn clear(&mut self) {
|
||||
self.dependencies = SelectorMap::new();
|
||||
self.pseudo_dependencies.clear()
|
||||
}
|
||||
|
||||
fn compute_pseudo_hint<E>(
|
||||
&self,
|
||||
pseudo: &E,
|
||||
pseudo_element: PseudoElement,
|
||||
snapshots: &SnapshotMap)
|
||||
-> RestyleHint
|
||||
where E: TElement,
|
||||
{
|
||||
debug!("compute_pseudo_hint: {:?}, {:?}", pseudo, pseudo_element);
|
||||
debug_assert!(pseudo.has_snapshot());
|
||||
|
||||
let map = match self.pseudo_dependencies.get(&pseudo_element) {
|
||||
Some(map) => map,
|
||||
None => return RestyleHint::empty(),
|
||||
};
|
||||
|
||||
// Only pseudo-element's state is relevant.
|
||||
let pseudo_state_changes =
|
||||
ElementWrapper::new(*pseudo, snapshots).state_changes();
|
||||
|
||||
debug!("pseudo_state_changes: {:?}", pseudo_state_changes);
|
||||
if pseudo_state_changes.is_empty() {
|
||||
return RestyleHint::empty();
|
||||
}
|
||||
|
||||
let selector_matching_target =
|
||||
pseudo.closest_non_native_anonymous_ancestor().unwrap();
|
||||
|
||||
// Note that we rely on that, if the originating element changes, it'll
|
||||
// post a restyle hint that would make us redo selector matching, so we
|
||||
// don't need to care about that.
|
||||
//
|
||||
// If that ever changes, we'd need to share more code with
|
||||
// `compute_element_hint`.
|
||||
let mut hint = RestyleHint::empty();
|
||||
map.lookup(selector_matching_target, &mut |dep| {
|
||||
// If the selector didn't match before, it either doesn't match now
|
||||
// either (or it doesn't matter because our parent posted a restyle
|
||||
// for us above).
|
||||
if !matches_selector(&dep.selector.inner, &selector_matching_target,
|
||||
None, &mut MatchingContext::default(),
|
||||
&mut |_, _| {}) {
|
||||
return true;
|
||||
}
|
||||
|
||||
let pseudo_selector = dep.selector.pseudo_element.as_ref().unwrap();
|
||||
debug_assert!(!pseudo_selector.state().is_empty());
|
||||
|
||||
if pseudo_selector.state().intersects(pseudo_state_changes) {
|
||||
hint = RESTYLE_SELF;
|
||||
return false;
|
||||
}
|
||||
|
||||
true
|
||||
});
|
||||
|
||||
hint
|
||||
}
|
||||
|
||||
fn compute_element_hint<E>(
|
||||
/// Compute a restyle hint given an element and a snapshot, per the rules
|
||||
/// explained in the rest of the documentation.
|
||||
pub fn compute_hint<E>(
|
||||
&self,
|
||||
el: &E,
|
||||
snapshots: &SnapshotMap)
|
||||
|
@ -838,8 +761,18 @@ impl DependencySet {
|
|||
});
|
||||
}
|
||||
|
||||
// FIXME(emilio): A bloom filter here would be neat.
|
||||
let mut matching_context =
|
||||
MatchingContext::new(MatchingMode::Normal, None);
|
||||
|
||||
let lookup_element = if el.implemented_pseudo_element().is_some() {
|
||||
el.closest_non_native_anonymous_ancestor().unwrap()
|
||||
} else {
|
||||
*el
|
||||
};
|
||||
|
||||
self.dependencies
|
||||
.lookup_with_additional(*el, additional_id, &additional_classes, &mut |dep| {
|
||||
.lookup_with_additional(lookup_element, additional_id, &additional_classes, &mut |dep| {
|
||||
trace!("scanning dependency: {:?}", dep);
|
||||
if !dep.sensitivities.sensitive_to(attrs_changed,
|
||||
state_changes) {
|
||||
|
@ -856,12 +789,12 @@ impl DependencySet {
|
|||
// been set during original matching for any element that might
|
||||
// change its matching behavior here.
|
||||
let matched_then =
|
||||
matches_selector(&dep.selector, &snapshot_el, None,
|
||||
&mut MatchingContext::default(),
|
||||
matches_selector(&dep.selector, &snapshot_el,
|
||||
&mut matching_context,
|
||||
&mut |_, _| {});
|
||||
let matches_now =
|
||||
matches_selector(&dep.selector, el, None,
|
||||
&mut MatchingContext::default(),
|
||||
matches_selector(&dep.selector, el,
|
||||
&mut matching_context,
|
||||
&mut |_, _| {});
|
||||
if matched_then != matches_now {
|
||||
hint.insert(dep.hint);
|
||||
|
@ -875,21 +808,4 @@ impl DependencySet {
|
|||
|
||||
hint
|
||||
}
|
||||
|
||||
|
||||
/// Compute a restyle hint given an element and a snapshot, per the rules
|
||||
/// explained in the rest of the documentation.
|
||||
pub fn compute_hint<E>(&self,
|
||||
el: &E,
|
||||
snapshots: &SnapshotMap)
|
||||
-> RestyleHint
|
||||
where E: TElement + Clone,
|
||||
{
|
||||
debug!("DependencySet::compute_hint({:?})", el);
|
||||
if let Some(pseudo) = el.implemented_pseudo_element() {
|
||||
return self.compute_pseudo_hint(el, pseudo, snapshots);
|
||||
}
|
||||
|
||||
self.compute_element_hint(el, snapshots)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ use fnv::FnvHashMap;
|
|||
use restyle_hints::ElementSnapshot;
|
||||
use selector_parser::{ElementExt, PseudoElementCascadeType, SelectorParser};
|
||||
use selectors::{Element, MatchAttrGeneric};
|
||||
use selectors::matching::MatchingContext;
|
||||
use selectors::matching::{MatchingContext, MatchingMode};
|
||||
use selectors::parser::{AttrSelector, SelectorMethods};
|
||||
use selectors::visitor::SelectorVisitor;
|
||||
use std::borrow::Cow;
|
||||
|
@ -51,6 +51,14 @@ pub enum PseudoElement {
|
|||
ServoInlineAbsolute,
|
||||
}
|
||||
|
||||
impl ::selectors::parser::PseudoElement for PseudoElement {
|
||||
type Impl = SelectorImpl;
|
||||
|
||||
fn supports_pseudo_class(&self, _: &NonTSPseudoClass) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl ToCss for PseudoElement {
|
||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||
use self::PseudoElement::*;
|
||||
|
@ -78,18 +86,6 @@ impl ToCss for PseudoElement {
|
|||
pub const EAGER_PSEUDO_COUNT: usize = 3;
|
||||
|
||||
impl PseudoElement {
|
||||
/// The pseudo-element, used for compatibility with Gecko's
|
||||
/// `PseudoElementSelector`.
|
||||
pub fn pseudo_element(&self) -> &Self {
|
||||
self
|
||||
}
|
||||
|
||||
/// The pseudo-element selector's state, used for compatibility with Gecko's
|
||||
/// `PseudoElementSelector`.
|
||||
pub fn state(&self) -> ElementState {
|
||||
ElementState::empty()
|
||||
}
|
||||
|
||||
/// Gets the canonical index of this eagerly-cascaded pseudo-element.
|
||||
#[inline]
|
||||
pub fn eager_index(&self) -> usize {
|
||||
|
@ -264,7 +260,7 @@ impl NonTSPseudoClass {
|
|||
pub struct SelectorImpl;
|
||||
|
||||
impl ::selectors::SelectorImpl for SelectorImpl {
|
||||
type PseudoElementSelector = PseudoElement;
|
||||
type PseudoElement = PseudoElement;
|
||||
type NonTSPseudoClass = NonTSPseudoClass;
|
||||
|
||||
type AttrValue = String;
|
||||
|
@ -323,9 +319,7 @@ impl<'a> ::selectors::Parser for SelectorParser<'a> {
|
|||
Ok(pseudo_class)
|
||||
}
|
||||
|
||||
fn parse_pseudo_element(&self,
|
||||
name: Cow<str>,
|
||||
_input: &mut CssParser)
|
||||
fn parse_pseudo_element(&self, name: Cow<str>)
|
||||
-> Result<PseudoElement, ()> {
|
||||
use self::PseudoElement::*;
|
||||
let pseudo_element = match_ignore_ascii_case! { &name,
|
||||
|
@ -579,8 +573,9 @@ impl MatchAttrGeneric for ServoElementSnapshot {
|
|||
|
||||
impl<E: Element<Impl=SelectorImpl> + Debug> ElementExt for E {
|
||||
fn is_link(&self) -> bool {
|
||||
let mut context = MatchingContext::new(MatchingMode::Normal, None);
|
||||
self.match_non_ts_pseudo_class(&NonTSPseudoClass::AnyLink,
|
||||
&mut MatchingContext::default(),
|
||||
&mut context,
|
||||
&mut |_, _| {})
|
||||
}
|
||||
|
||||
|
|
|
@ -26,10 +26,9 @@ use properties::PropertyDeclarationBlock;
|
|||
use restyle_hints::{RestyleHint, DependencySet};
|
||||
use rule_tree::{CascadeLevel, RuleTree, StrongRuleNode, StyleSource};
|
||||
use selector_parser::{SelectorImpl, PseudoElement, SnapshotMap};
|
||||
use selectors::Element;
|
||||
use selectors::bloom::BloomFilter;
|
||||
use selectors::matching::{AFFECTED_BY_STYLE_ATTRIBUTE, AFFECTED_BY_PRESENTATIONAL_HINTS};
|
||||
use selectors::matching::{ElementSelectorFlags, matches_selector, MatchingContext};
|
||||
use selectors::matching::{ElementSelectorFlags, matches_selector, MatchingContext, MatchingMode};
|
||||
use selectors::parser::{AttrSelector, Combinator, Component, Selector, SelectorInner, SelectorIter};
|
||||
use selectors::parser::{SelectorMethods, LocalName as LocalNameSelector};
|
||||
use selectors::visitor::SelectorVisitor;
|
||||
|
@ -479,9 +478,9 @@ impl Stylist {
|
|||
rule: &Arc<Locked<StyleRule>>,
|
||||
stylesheet: &Stylesheet)
|
||||
{
|
||||
let map = if let Some(ref pseudo_selector) = selector.pseudo_element {
|
||||
let map = if let Some(pseudo) = selector.pseudo_element() {
|
||||
self.pseudos_map
|
||||
.entry(pseudo_selector.pseudo_element().clone())
|
||||
.entry(pseudo.clone())
|
||||
.or_insert_with(PerPseudoElementSelectorMap::new)
|
||||
.borrow_for_origin(&stylesheet.origin)
|
||||
} else {
|
||||
|
@ -525,9 +524,6 @@ impl Stylist {
|
|||
|
||||
#[inline]
|
||||
fn note_attribute_and_state_dependencies(&mut self, selector: &Selector<SelectorImpl>) {
|
||||
if let Some(ref pseudo_selector) = selector.pseudo_element {
|
||||
self.state_dependencies.insert(pseudo_selector.state());
|
||||
}
|
||||
selector.visit(&mut AttributeAndStateDependencyVisitor(self));
|
||||
}
|
||||
|
||||
|
@ -635,14 +631,13 @@ impl Stylist {
|
|||
guards: &StylesheetGuards,
|
||||
element: &E,
|
||||
pseudo: &PseudoElement,
|
||||
pseudo_state: ElementState,
|
||||
parent: &Arc<ComputedValues>,
|
||||
font_metrics: &FontMetricsProvider)
|
||||
-> Option<ComputedStyle>
|
||||
where E: TElement,
|
||||
{
|
||||
let rule_node =
|
||||
match self.lazy_pseudo_rules(guards, element, pseudo, pseudo_state) {
|
||||
match self.lazy_pseudo_rules(guards, element, pseudo) {
|
||||
Some(rule_node) => rule_node,
|
||||
None => return None
|
||||
};
|
||||
|
@ -673,8 +668,7 @@ impl Stylist {
|
|||
pub fn lazy_pseudo_rules<E>(&self,
|
||||
guards: &StylesheetGuards,
|
||||
element: &E,
|
||||
pseudo: &PseudoElement,
|
||||
pseudo_state: ElementState)
|
||||
pseudo: &PseudoElement)
|
||||
-> Option<StrongRuleNode>
|
||||
where E: TElement
|
||||
{
|
||||
|
@ -708,14 +702,15 @@ impl Stylist {
|
|||
};
|
||||
|
||||
let mut declarations = ApplicableDeclarationList::new();
|
||||
let mut matching_context =
|
||||
MatchingContext::new(MatchingMode::ForStatelessPseudoElement, None);
|
||||
self.push_applicable_declarations(element,
|
||||
None,
|
||||
Some(pseudo),
|
||||
None,
|
||||
None,
|
||||
AnimationRules(None, None),
|
||||
Some((pseudo, pseudo_state)),
|
||||
&mut declarations,
|
||||
&mut MatchingContext::default(),
|
||||
&mut matching_context,
|
||||
&mut set_selector_flags);
|
||||
if declarations.is_empty() {
|
||||
return None
|
||||
|
@ -839,16 +834,15 @@ impl Stylist {
|
|||
pub fn push_applicable_declarations<E, V, F>(
|
||||
&self,
|
||||
element: &E,
|
||||
parent_bf: Option<&BloomFilter>,
|
||||
pseudo_element: Option<&PseudoElement>,
|
||||
style_attribute: Option<&Arc<Locked<PropertyDeclarationBlock>>>,
|
||||
smil_override: Option<&Arc<Locked<PropertyDeclarationBlock>>>,
|
||||
animation_rules: AnimationRules,
|
||||
pseudo_element: Option<(&PseudoElement, ElementState)>,
|
||||
applicable_declarations: &mut V,
|
||||
context: &mut MatchingContext,
|
||||
flags_setter: &mut F)
|
||||
where E: TElement,
|
||||
V: Push<ApplicableDeclarationBlock> + VecLike<ApplicableDeclarationBlock>,
|
||||
V: Push<ApplicableDeclarationBlock> + VecLike<ApplicableDeclarationBlock> + ::std::fmt::Debug,
|
||||
F: FnMut(&E, ElementSelectorFlags),
|
||||
{
|
||||
debug_assert!(!self.is_device_dirty);
|
||||
|
@ -857,20 +851,31 @@ impl Stylist {
|
|||
debug_assert!(cfg!(feature = "gecko") ||
|
||||
style_attribute.is_none() || pseudo_element.is_none(),
|
||||
"Style attributes do not apply to pseudo-elements");
|
||||
debug_assert!(pseudo_element.as_ref().map_or(true, |p| !p.0.is_precomputed()));
|
||||
debug_assert!(pseudo_element.map_or(true, |p| !p.is_precomputed()));
|
||||
|
||||
let map = match pseudo_element {
|
||||
Some((ref pseudo, _)) => self.pseudos_map.get(pseudo).unwrap(),
|
||||
Some(pseudo) => self.pseudos_map.get(pseudo).unwrap(),
|
||||
None => &self.element_map,
|
||||
};
|
||||
|
||||
let is_implemented_pseudo =
|
||||
element.implemented_pseudo_element().is_some();
|
||||
|
||||
// NB: This causes use to rule has pseudo selectors based on the
|
||||
// properties of the originating element (which is fine, given the
|
||||
// find_first_from_right usage).
|
||||
let rule_hash_target = if is_implemented_pseudo {
|
||||
element.closest_non_native_anonymous_ancestor().unwrap()
|
||||
} else {
|
||||
*element
|
||||
};
|
||||
|
||||
debug!("Determining if style is shareable: pseudo: {}",
|
||||
pseudo_element.is_some());
|
||||
|
||||
// Step 1: Normal user-agent rules.
|
||||
map.user_agent.get_all_matching_rules(element,
|
||||
pseudo_element,
|
||||
parent_bf,
|
||||
&rule_hash_target,
|
||||
applicable_declarations,
|
||||
context,
|
||||
flags_setter,
|
||||
|
@ -893,19 +898,25 @@ impl Stylist {
|
|||
debug!("preshints: {:?}", context.relations);
|
||||
}
|
||||
|
||||
if element.matches_user_and_author_rules() {
|
||||
// NB: the following condition, although it may look somewhat
|
||||
// inaccurate, would be equivalent to something like:
|
||||
//
|
||||
// element.matches_user_and_author_rules() ||
|
||||
// (is_implemented_pseudo &&
|
||||
// rule_hash_target.matches_user_and_author_rules())
|
||||
//
|
||||
// Which may be more what you would probably expect.
|
||||
if rule_hash_target.matches_user_and_author_rules() {
|
||||
// Step 3: User and author normal rules.
|
||||
map.user.get_all_matching_rules(element,
|
||||
pseudo_element,
|
||||
parent_bf,
|
||||
&rule_hash_target,
|
||||
applicable_declarations,
|
||||
context,
|
||||
flags_setter,
|
||||
CascadeLevel::UserNormal);
|
||||
debug!("user normal: {:?}", context.relations);
|
||||
map.author.get_all_matching_rules(element,
|
||||
pseudo_element,
|
||||
parent_bf,
|
||||
&rule_hash_target,
|
||||
applicable_declarations,
|
||||
context,
|
||||
flags_setter,
|
||||
|
@ -960,7 +971,6 @@ impl Stylist {
|
|||
ApplicableDeclarationBlock::from_declarations(anim, CascadeLevel::Transitions));
|
||||
}
|
||||
debug!("transition: {:?}", context.relations);
|
||||
|
||||
debug!("push_applicable_declarations: shareable: {:?}", context.relations);
|
||||
}
|
||||
|
||||
|
@ -993,6 +1003,11 @@ impl Stylist {
|
|||
where E: TElement,
|
||||
F: FnMut(&E, ElementSelectorFlags),
|
||||
{
|
||||
// NB: `MatchingMode` doesn't really matter, given we don't share style
|
||||
// between pseudos.
|
||||
let mut matching_context =
|
||||
MatchingContext::new(MatchingMode::Normal, Some(bloom));
|
||||
|
||||
// Note that, by the time we're revalidating, we're guaranteed that the
|
||||
// candidate and the entry have the same id, classes, and local name.
|
||||
// This means we're guaranteed to get the same rulehash buckets for all
|
||||
|
@ -1002,8 +1017,7 @@ impl Stylist {
|
|||
self.selectors_for_cache_revalidation.lookup(*element, &mut |selector| {
|
||||
results.push(matches_selector(selector,
|
||||
element,
|
||||
Some(bloom),
|
||||
&mut MatchingContext::default(),
|
||||
&mut matching_context,
|
||||
flags_setter));
|
||||
true
|
||||
});
|
||||
|
@ -1182,6 +1196,7 @@ pub fn needs_revalidation(selector: &Selector<SelectorImpl>) -> bool {
|
|||
/// Map that contains the CSS rules for a specific PseudoElement
|
||||
/// (or lack of PseudoElement).
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
#[derive(Debug)]
|
||||
struct PerPseudoElementSelectorMap {
|
||||
/// Rules from user agent stylesheets
|
||||
user_agent: SelectorMap<Rule>,
|
||||
|
@ -1283,13 +1298,12 @@ impl SelectorMap<Rule> {
|
|||
/// Sort the Rules at the end to maintain cascading order.
|
||||
pub fn get_all_matching_rules<E, V, F>(&self,
|
||||
element: &E,
|
||||
pseudo_element: Option<(&PseudoElement, ElementState)>,
|
||||
parent_bf: Option<&BloomFilter>,
|
||||
rule_hash_target: &E,
|
||||
matching_rules_list: &mut V,
|
||||
context: &mut MatchingContext,
|
||||
flags_setter: &mut F,
|
||||
cascade_level: CascadeLevel)
|
||||
where E: Element<Impl=SelectorImpl>,
|
||||
where E: TElement,
|
||||
V: VecLike<ApplicableDeclarationBlock>,
|
||||
F: FnMut(&E, ElementSelectorFlags),
|
||||
{
|
||||
|
@ -1299,10 +1313,8 @@ impl SelectorMap<Rule> {
|
|||
|
||||
// At the end, we're going to sort the rules that we added, so remember where we began.
|
||||
let init_len = matching_rules_list.len();
|
||||
if let Some(id) = element.get_id() {
|
||||
if let Some(id) = rule_hash_target.get_id() {
|
||||
SelectorMap::get_matching_rules_from_hash(element,
|
||||
pseudo_element,
|
||||
parent_bf,
|
||||
&self.id_hash,
|
||||
&id,
|
||||
matching_rules_list,
|
||||
|
@ -1311,10 +1323,8 @@ impl SelectorMap<Rule> {
|
|||
cascade_level)
|
||||
}
|
||||
|
||||
element.each_class(|class| {
|
||||
rule_hash_target.each_class(|class| {
|
||||
SelectorMap::get_matching_rules_from_hash(element,
|
||||
pseudo_element,
|
||||
parent_bf,
|
||||
&self.class_hash,
|
||||
class,
|
||||
matching_rules_list,
|
||||
|
@ -1324,18 +1334,14 @@ impl SelectorMap<Rule> {
|
|||
});
|
||||
|
||||
SelectorMap::get_matching_rules_from_hash(element,
|
||||
pseudo_element,
|
||||
parent_bf,
|
||||
&self.local_name_hash,
|
||||
element.get_local_name(),
|
||||
rule_hash_target.get_local_name(),
|
||||
matching_rules_list,
|
||||
context,
|
||||
flags_setter,
|
||||
cascade_level);
|
||||
|
||||
SelectorMap::get_matching_rules(element,
|
||||
pseudo_element,
|
||||
parent_bf,
|
||||
&self.other,
|
||||
matching_rules_list,
|
||||
context,
|
||||
|
@ -1372,15 +1378,13 @@ impl SelectorMap<Rule> {
|
|||
|
||||
fn get_matching_rules_from_hash<E, Str, BorrowedStr: ?Sized, Vector, F>(
|
||||
element: &E,
|
||||
pseudo_element: Option<(&PseudoElement, ElementState)>,
|
||||
parent_bf: Option<&BloomFilter>,
|
||||
hash: &FnvHashMap<Str, Vec<Rule>>,
|
||||
key: &BorrowedStr,
|
||||
matching_rules: &mut Vector,
|
||||
context: &mut MatchingContext,
|
||||
flags_setter: &mut F,
|
||||
cascade_level: CascadeLevel)
|
||||
where E: Element<Impl=SelectorImpl>,
|
||||
where E: TElement,
|
||||
Str: Borrow<BorrowedStr> + Eq + Hash,
|
||||
BorrowedStr: Eq + Hash,
|
||||
Vector: VecLike<ApplicableDeclarationBlock>,
|
||||
|
@ -1388,8 +1392,6 @@ impl SelectorMap<Rule> {
|
|||
{
|
||||
if let Some(rules) = hash.get(key) {
|
||||
SelectorMap::get_matching_rules(element,
|
||||
pseudo_element,
|
||||
parent_bf,
|
||||
rules,
|
||||
matching_rules,
|
||||
context,
|
||||
|
@ -1400,42 +1402,18 @@ impl SelectorMap<Rule> {
|
|||
|
||||
/// Adds rules in `rules` that match `element` to the `matching_rules` list.
|
||||
fn get_matching_rules<E, V, F>(element: &E,
|
||||
pseudo_element: Option<(&PseudoElement, ElementState)>,
|
||||
parent_bf: Option<&BloomFilter>,
|
||||
rules: &[Rule],
|
||||
matching_rules: &mut V,
|
||||
context: &mut MatchingContext,
|
||||
flags_setter: &mut F,
|
||||
cascade_level: CascadeLevel)
|
||||
where E: Element<Impl=SelectorImpl>,
|
||||
where E: TElement,
|
||||
V: VecLike<ApplicableDeclarationBlock>,
|
||||
F: FnMut(&E, ElementSelectorFlags),
|
||||
{
|
||||
for rule in rules.iter() {
|
||||
debug_assert_eq!(rule.selector.pseudo_element.is_some(),
|
||||
pseudo_element.is_some(),
|
||||
"Testing pseudo-elements against the wrong map");
|
||||
|
||||
if let Some((pseudo, pseudo_state)) = pseudo_element {
|
||||
let pseudo_selector =
|
||||
rule.selector.pseudo_element.as_ref().unwrap();
|
||||
|
||||
debug_assert_eq!(pseudo_selector.pseudo_element(), pseudo,
|
||||
"Testing pseudo-element against the wrong entry");
|
||||
|
||||
let state = pseudo_selector.state();
|
||||
|
||||
// NB: We only allow a subset of the flags here, so using
|
||||
// contains for them is fine, (and it's necessary, to handle
|
||||
// multiple state flags properly).
|
||||
if !state.is_empty() && !pseudo_state.contains(state) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
for rule in rules {
|
||||
if matches_selector(&rule.selector.inner,
|
||||
element,
|
||||
parent_bf,
|
||||
context,
|
||||
flags_setter) {
|
||||
matching_rules.push(
|
||||
|
@ -1593,45 +1571,70 @@ impl<T> SelectorMap<T> where T: Clone + Borrow<SelectorInner<SelectorImpl>> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Searches the selector from right to left, beginning to the left of the
|
||||
/// ::pseudo-element (if any), and ending at the first combinator.
|
||||
///
|
||||
/// The first non-None value returned from |f| is returned.
|
||||
///
|
||||
/// Effectively, pseudo-elements are ignored, given only state pseudo-classes
|
||||
/// may appear before them.
|
||||
fn find_from_right<F, R>(selector: &SelectorInner<SelectorImpl>, mut f: F) -> Option<R>
|
||||
where F: FnMut(&Component<SelectorImpl>) -> Option<R>,
|
||||
{
|
||||
let mut iter = selector.complex.iter();
|
||||
for ss in &mut iter {
|
||||
if let Some(r) = f(ss) {
|
||||
return Some(r)
|
||||
}
|
||||
}
|
||||
|
||||
if iter.next_sequence() == Some(Combinator::PseudoElement) {
|
||||
for ss in &mut iter {
|
||||
if let Some(r) = f(ss) {
|
||||
return Some(r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
/// Retrieve the first ID name in the selector, or None otherwise.
|
||||
pub fn get_id_name(selector: &SelectorInner<SelectorImpl>) -> Option<Atom> {
|
||||
for ss in selector.complex.iter() {
|
||||
find_from_right(selector, |ss| {
|
||||
// TODO(pradeep): Implement case-sensitivity based on the
|
||||
// document type and quirks mode.
|
||||
if let Component::ID(ref id) = *ss {
|
||||
return Some(id.clone());
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
None
|
||||
})
|
||||
}
|
||||
|
||||
/// Retrieve the FIRST class name in the selector, or None otherwise.
|
||||
pub fn get_class_name(selector: &SelectorInner<SelectorImpl>) -> Option<Atom> {
|
||||
for ss in selector.complex.iter() {
|
||||
find_from_right(selector, |ss| {
|
||||
// TODO(pradeep): Implement case-sensitivity based on the
|
||||
// document type and quirks mode.
|
||||
if let Component::Class(ref class) = *ss {
|
||||
return Some(class.clone());
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
None
|
||||
})
|
||||
}
|
||||
|
||||
/// Retrieve the name if it is a type selector, or None otherwise.
|
||||
pub fn get_local_name(selector: &SelectorInner<SelectorImpl>)
|
||||
-> Option<LocalNameSelector<SelectorImpl>> {
|
||||
for ss in selector.complex.iter() {
|
||||
find_from_right(selector, |ss| {
|
||||
if let Component::LocalName(ref n) = *ss {
|
||||
return Some(LocalNameSelector {
|
||||
name: n.name.clone(),
|
||||
lower_name: n.lower_name.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
None
|
||||
})
|
||||
}
|
||||
|
||||
/// A rule, that wraps a style rule, but represents a single selector of the
|
||||
|
@ -1661,7 +1664,7 @@ impl Borrow<SelectorInner<SelectorImpl>> for Rule {
|
|||
impl Rule {
|
||||
/// Returns the specificity of the rule.
|
||||
pub fn specificity(&self) -> u32 {
|
||||
self.selector.specificity
|
||||
self.selector.specificity()
|
||||
}
|
||||
|
||||
fn to_applicable_declaration_block(&self, level: CascadeLevel) -> ApplicableDeclarationBlock {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue