Bug 1364412: Use the pseudo selector to reject state selectors. r=bholley

MozReview-Commit-ID: 73dnp6nZpdU
Signed-off-by: Emilio Cobos Álvarez <emilio@crisal.io>
This commit is contained in:
Emilio Cobos Álvarez 2017-05-12 17:35:28 +02:00
parent dd38740ece
commit 84f5a90668
No known key found for this signature in database
GPG key ID: 056B727BB9C1027C
5 changed files with 73 additions and 13 deletions

View file

@ -20,6 +20,7 @@ use style::context::SharedStyleContext;
use style::data::ElementData; use style::data::ElementData;
use style::dom::{LayoutIterator, NodeInfo, PresentationalHintsSynthesizer, TNode}; use style::dom::{LayoutIterator, NodeInfo, PresentationalHintsSynthesizer, TNode};
use style::dom::OpaqueNode; use style::dom::OpaqueNode;
use style::element_state::ElementState;
use style::font_metrics::ServoMetricsProvider; use style::font_metrics::ServoMetricsProvider;
use style::properties::{CascadeFlags, ServoComputedValues}; use style::properties::{CascadeFlags, ServoComputedValues};
use style::selector_parser::{PseudoElement, PseudoElementCascadeType, SelectorImpl}; use style::selector_parser::{PseudoElement, PseudoElementCascadeType, SelectorImpl};
@ -434,6 +435,7 @@ pub trait ThreadSafeLayoutElement: Clone + Copy + Sized + Debug +
&context.guards, &context.guards,
unsafe { &self.unsafe_get() }, unsafe { &self.unsafe_get() },
&style_pseudo, &style_pseudo,
ElementState::empty(),
data.styles().primary.values(), data.styles().primary.values(),
&ServoMetricsProvider); &ServoMetricsProvider);
data.styles_mut().cached_pseudos data.styles_mut().cached_pseudos

View file

@ -401,6 +401,11 @@ impl PseudoElementSelector {
pub fn pseudo_element(&self) -> &PseudoElement { pub fn pseudo_element(&self) -> &PseudoElement {
&self.pseudo &self.pseudo
} }
/// Returns the pseudo-element selector state.
pub fn state(&self) -> ElementState {
self.state
}
} }
impl ToCss for PseudoElementSelector { impl ToCss for PseudoElementSelector {

View file

@ -15,6 +15,7 @@ use cascade_info::CascadeInfo;
use context::{CurrentElementInfo, SelectorFlagsMap, SharedStyleContext, StyleContext}; use context::{CurrentElementInfo, SelectorFlagsMap, SharedStyleContext, StyleContext};
use data::{ComputedStyle, ElementData, ElementStyles, RestyleData}; use data::{ComputedStyle, ElementData, ElementStyles, RestyleData};
use dom::{AnimationRules, SendElement, TElement, TNode}; use dom::{AnimationRules, SendElement, TElement, TNode};
use element_state::ElementState;
use font_metrics::FontMetricsProvider; use font_metrics::FontMetricsProvider;
use properties::{CascadeFlags, ComputedValues, SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP, cascade}; use properties::{CascadeFlags, ComputedValues, SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP, cascade};
use properties::longhands::display::computed_value as display; use properties::longhands::display::computed_value as display;
@ -1015,13 +1016,18 @@ pub trait MatchMethods : TElement {
None => *self, None => *self,
}; };
let pseudo_and_state = match implemented_pseudo {
Some(ref pseudo) => Some((pseudo, self.get_state())),
None => None,
};
// Compute the primary rule node. // Compute the primary rule node.
*relations = stylist.push_applicable_declarations(&selector_matching_target, *relations = stylist.push_applicable_declarations(&selector_matching_target,
Some(bloom), Some(bloom),
style_attribute, style_attribute,
smil_override, smil_override,
animation_rules, animation_rules,
implemented_pseudo.as_ref(), pseudo_and_state,
&mut applicable_declarations, &mut applicable_declarations,
&mut set_selector_flags); &mut set_selector_flags);
@ -1076,7 +1082,7 @@ pub trait MatchMethods : TElement {
None, None,
None, None,
AnimationRules(None, None), AnimationRules(None, None),
Some(&pseudo), Some((&pseudo, ElementState::empty())),
&mut applicable_declarations, &mut applicable_declarations,
&mut set_selector_flags); &mut set_selector_flags);

View file

@ -602,15 +602,17 @@ impl Stylist {
guards: &StylesheetGuards, guards: &StylesheetGuards,
element: &E, element: &E,
pseudo: &PseudoElement, pseudo: &PseudoElement,
pseudo_state: ElementState,
parent: &Arc<ComputedValues>, parent: &Arc<ComputedValues>,
font_metrics: &FontMetricsProvider) font_metrics: &FontMetricsProvider)
-> Option<ComputedStyle> -> Option<ComputedStyle>
where E: TElement, where E: TElement,
{ {
let rule_node = match self.lazy_pseudo_rules(guards, element, pseudo) { let rule_node =
Some(rule_node) => rule_node, match self.lazy_pseudo_rules(guards, element, pseudo, pseudo_state) {
None => return None Some(rule_node) => rule_node,
}; None => return None
};
// Read the comment on `precomputed_values_for_pseudo` to see why it's // Read the comment on `precomputed_values_for_pseudo` to see why it's
// difficult to assert that display: contents nodes never arrive here // difficult to assert that display: contents nodes never arrive here
@ -638,7 +640,8 @@ impl Stylist {
pub fn lazy_pseudo_rules<E>(&self, pub fn lazy_pseudo_rules<E>(&self,
guards: &StylesheetGuards, guards: &StylesheetGuards,
element: &E, element: &E,
pseudo: &PseudoElement) pseudo: &PseudoElement,
pseudo_state: ElementState)
-> Option<StrongRuleNode> -> Option<StrongRuleNode>
where E: TElement where E: TElement
{ {
@ -678,7 +681,7 @@ impl Stylist {
None, None,
None, None,
AnimationRules(None, None), AnimationRules(None, None),
Some(pseudo), Some((pseudo, pseudo_state)),
&mut declarations, &mut declarations,
&mut set_selector_flags); &mut set_selector_flags);
if declarations.is_empty() { if declarations.is_empty() {
@ -807,7 +810,7 @@ impl Stylist {
style_attribute: Option<&Arc<Locked<PropertyDeclarationBlock>>>, style_attribute: Option<&Arc<Locked<PropertyDeclarationBlock>>>,
smil_override: Option<&Arc<Locked<PropertyDeclarationBlock>>>, smil_override: Option<&Arc<Locked<PropertyDeclarationBlock>>>,
animation_rules: AnimationRules, animation_rules: AnimationRules,
pseudo_element: Option<&PseudoElement>, pseudo_element: Option<(&PseudoElement, ElementState)>,
applicable_declarations: &mut V, applicable_declarations: &mut V,
flags_setter: &mut F) flags_setter: &mut F)
-> StyleRelations -> StyleRelations
@ -821,18 +824,21 @@ impl Stylist {
debug_assert!(cfg!(feature = "gecko") || debug_assert!(cfg!(feature = "gecko") ||
style_attribute.is_none() || pseudo_element.is_none(), style_attribute.is_none() || pseudo_element.is_none(),
"Style attributes do not apply to pseudo-elements"); "Style attributes do not apply to pseudo-elements");
debug_assert!(pseudo_element.as_ref().map_or(true, |p| !p.is_precomputed())); debug_assert!(pseudo_element.as_ref().map_or(true, |p| !p.0.is_precomputed()));
let map = match pseudo_element { let map = match pseudo_element {
Some(ref pseudo) => self.pseudos_map.get(pseudo).unwrap(), Some((ref pseudo, _)) => self.pseudos_map.get(pseudo).unwrap(),
None => &self.element_map, None => &self.element_map,
}; };
let mut relations = StyleRelations::empty(); let mut relations = StyleRelations::empty();
debug!("Determining if style is shareable: pseudo: {}", pseudo_element.is_some()); debug!("Determining if style is shareable: pseudo: {}",
pseudo_element.is_some());
// Step 1: Normal user-agent rules. // Step 1: Normal user-agent rules.
map.user_agent.get_all_matching_rules(element, map.user_agent.get_all_matching_rules(element,
pseudo_element,
parent_bf, parent_bf,
applicable_declarations, applicable_declarations,
&mut relations, &mut relations,
@ -859,6 +865,7 @@ impl Stylist {
if element.matches_user_and_author_rules() { if element.matches_user_and_author_rules() {
// Step 3: User and author normal rules. // Step 3: User and author normal rules.
map.user.get_all_matching_rules(element, map.user.get_all_matching_rules(element,
pseudo_element,
parent_bf, parent_bf,
applicable_declarations, applicable_declarations,
&mut relations, &mut relations,
@ -866,6 +873,7 @@ impl Stylist {
CascadeLevel::UserNormal); CascadeLevel::UserNormal);
debug!("user normal: {:?}", relations); debug!("user normal: {:?}", relations);
map.author.get_all_matching_rules(element, map.author.get_all_matching_rules(element,
pseudo_element,
parent_bf, parent_bf,
applicable_declarations, applicable_declarations,
&mut relations, &mut relations,
@ -1249,6 +1257,7 @@ impl SelectorMap<Rule> {
/// Sort the Rules at the end to maintain cascading order. /// Sort the Rules at the end to maintain cascading order.
pub fn get_all_matching_rules<E, V, F>(&self, pub fn get_all_matching_rules<E, V, F>(&self,
element: &E, element: &E,
pseudo_element: Option<(&PseudoElement, ElementState)>,
parent_bf: Option<&BloomFilter>, parent_bf: Option<&BloomFilter>,
matching_rules_list: &mut V, matching_rules_list: &mut V,
relations: &mut StyleRelations, relations: &mut StyleRelations,
@ -1266,6 +1275,7 @@ impl SelectorMap<Rule> {
let init_len = matching_rules_list.len(); let init_len = matching_rules_list.len();
if let Some(id) = element.get_id() { if let Some(id) = element.get_id() {
SelectorMap::get_matching_rules_from_hash(element, SelectorMap::get_matching_rules_from_hash(element,
pseudo_element,
parent_bf, parent_bf,
&self.id_hash, &self.id_hash,
&id, &id,
@ -1277,6 +1287,7 @@ impl SelectorMap<Rule> {
element.each_class(|class| { element.each_class(|class| {
SelectorMap::get_matching_rules_from_hash(element, SelectorMap::get_matching_rules_from_hash(element,
pseudo_element,
parent_bf, parent_bf,
&self.class_hash, &self.class_hash,
class, class,
@ -1287,6 +1298,7 @@ impl SelectorMap<Rule> {
}); });
SelectorMap::get_matching_rules_from_hash(element, SelectorMap::get_matching_rules_from_hash(element,
pseudo_element,
parent_bf, parent_bf,
&self.local_name_hash, &self.local_name_hash,
element.get_local_name(), element.get_local_name(),
@ -1296,6 +1308,7 @@ impl SelectorMap<Rule> {
cascade_level); cascade_level);
SelectorMap::get_matching_rules(element, SelectorMap::get_matching_rules(element,
pseudo_element,
parent_bf, parent_bf,
&self.other, &self.other,
matching_rules_list, matching_rules_list,
@ -1333,6 +1346,7 @@ impl SelectorMap<Rule> {
fn get_matching_rules_from_hash<E, Str, BorrowedStr: ?Sized, Vector, F>( fn get_matching_rules_from_hash<E, Str, BorrowedStr: ?Sized, Vector, F>(
element: &E, element: &E,
pseudo_element: Option<(&PseudoElement, ElementState)>,
parent_bf: Option<&BloomFilter>, parent_bf: Option<&BloomFilter>,
hash: &FnvHashMap<Str, Vec<Rule>>, hash: &FnvHashMap<Str, Vec<Rule>>,
key: &BorrowedStr, key: &BorrowedStr,
@ -1348,6 +1362,7 @@ impl SelectorMap<Rule> {
{ {
if let Some(rules) = hash.get(key) { if let Some(rules) = hash.get(key) {
SelectorMap::get_matching_rules(element, SelectorMap::get_matching_rules(element,
pseudo_element,
parent_bf, parent_bf,
rules, rules,
matching_rules, matching_rules,
@ -1359,6 +1374,7 @@ impl SelectorMap<Rule> {
/// Adds rules in `rules` that match `element` to the `matching_rules` list. /// Adds rules in `rules` that match `element` to the `matching_rules` list.
fn get_matching_rules<E, V, F>(element: &E, fn get_matching_rules<E, V, F>(element: &E,
pseudo_element: Option<(&PseudoElement, ElementState)>,
parent_bf: Option<&BloomFilter>, parent_bf: Option<&BloomFilter>,
rules: &[Rule], rules: &[Rule],
matching_rules: &mut V, matching_rules: &mut V,
@ -1370,6 +1386,27 @@ impl SelectorMap<Rule> {
F: FnMut(&E, ElementSelectorFlags), F: FnMut(&E, ElementSelectorFlags),
{ {
for rule in rules.iter() { 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;
}
}
if matches_selector(&rule.selector.inner, if matches_selector(&rule.selector.inner,
element, element,
parent_bf, parent_bf,

View file

@ -990,6 +990,8 @@ pub extern "C" fn Servo_ComputedValues_GetForAnonymousBox(parent_style_or_null:
.into_strong() .into_strong()
} }
// FIXME(emilio): Don't use pseudo_tag here, and pass the pseudo-element
// directly.
#[no_mangle] #[no_mangle]
pub extern "C" fn Servo_ResolveRuleNode(element: RawGeckoElementBorrowed, pub extern "C" fn Servo_ResolveRuleNode(element: RawGeckoElementBorrowed,
pseudo_tag: *mut nsIAtom, pseudo_tag: *mut nsIAtom,
@ -1045,6 +1047,8 @@ pub extern "C" fn Servo_ResolvePseudoStyle(element: RawGeckoElementBorrowed,
let guard = global_style_data.shared_lock.read(); let guard = global_style_data.shared_lock.read();
match get_pseudo_style(&guard, element, pseudo_tag, data.styles(), doc_data) { match get_pseudo_style(&guard, element, pseudo_tag, data.styles(), doc_data) {
Some(values) => values.into_strong(), Some(values) => values.into_strong(),
// FIXME(emilio): This looks pretty wrong! Shouldn't it be at least an
// empty style inheriting from the element?
None if !is_probe => data.styles().primary.values().clone().into_strong(), None if !is_probe => data.styles().primary.values().clone().into_strong(),
None => Strong::null(), None => Strong::null(),
} }
@ -1081,7 +1085,12 @@ fn get_pseudo_rule_node(guard: &SharedRwLockReadGuard,
PseudoElementCascadeType::Lazy => { PseudoElementCascadeType::Lazy => {
let d = doc_data.borrow_mut(); let d = doc_data.borrow_mut();
let guards = StylesheetGuards::same(guard); let guards = StylesheetGuards::same(guard);
d.stylist.lazy_pseudo_rules(&guards, &element, &pseudo)
// FIXME(emilio): We should get the pseudo state here!
d.stylist.lazy_pseudo_rules(&guards,
&element,
&pseudo,
ElementState::empty())
}, },
} }
} }
@ -1105,6 +1114,7 @@ fn get_pseudo_style(guard: &SharedRwLockReadGuard,
d.stylist.lazily_compute_pseudo_element_style(&guards, d.stylist.lazily_compute_pseudo_element_style(&guards,
&element, &element,
&pseudo, &pseudo,
ElementState::empty(),
base, base,
&metrics) &metrics)
.map(|s| s.values().clone()) .map(|s| s.values().clone())