MatchingContext by jryans.
draft
MatchingContext by jryans.
Just the patch from https://github.com/servo/servo/pull/16872 rebased.
MozReview-Commit-ID: Hw4KTjY4dCD
--- a/servo/components/script/dom/element.rs
+++ b/servo/components/script/dom/element.rs
@@ -80,17 +80,17 @@ use html5ever::serialize;
use html5ever::serialize::SerializeOpts;
use html5ever::serialize::TraversalScope;
use html5ever::serialize::TraversalScope::{ChildrenOnly, IncludeNode};
use js::jsapi::{HandleValue, JSAutoCompartment};
use net_traits::request::CorsSettings;
use ref_filter_map::ref_filter_map;
use script_layout_interface::message::ReflowQueryType;
use script_thread::Runnable;
-use selectors::matching::{ElementSelectorFlags, StyleRelations, matches_selector_list};
+use selectors::matching::{ElementSelectorFlags, MatchingContext, matches_selector_list};
use selectors::matching::{HAS_EDGE_CHILD_SELECTOR, HAS_SLOW_SELECTOR, HAS_SLOW_SELECTOR_LATER_SIBLINGS};
use selectors::parser::{AttrSelector, NamespaceConstraint};
use servo_atoms::Atom;
use std::ascii::AsciiExt;
use std::borrow::Cow;
use std::cell::{Cell, Ref};
use std::convert::TryFrom;
use std::default::Default;
@@ -2409,17 +2409,17 @@ impl<'a> ::selectors::Element for Root<E
}
fn get_namespace(&self) -> &Namespace {
self.namespace()
}
fn match_non_ts_pseudo_class<F>(&self,
pseudo_class: &NonTSPseudoClass,
- _: &mut StyleRelations,
+ _: &mut MatchingContext,
_: &mut F)
-> bool
where F: FnMut(&Self, ElementSelectorFlags),
{
match *pseudo_class {
// https://github.com/servo/servo/issues/8718
NonTSPseudoClass::Link |
NonTSPseudoClass::AnyLink => self.is_link(),
--- a/servo/components/script/layout_wrapper.rs
+++ b/servo/components/script/layout_wrapper.rs
@@ -45,17 +45,17 @@ use dom::text::Text;
use gfx_traits::ByteIndex;
use html5ever::{LocalName, Namespace};
use msg::constellation_msg::{FrameId, PipelineId};
use range::Range;
use script_layout_interface::{HTMLCanvasData, LayoutNodeType, SVGSVGData, TrustedNodeAddress};
use script_layout_interface::{OpaqueStyleAndLayoutData, PartialPersistentLayoutData};
use script_layout_interface::wrapper_traits::{DangerousThreadSafeLayoutNode, GetLayoutData, LayoutNode};
use script_layout_interface::wrapper_traits::{PseudoElementType, ThreadSafeLayoutElement, ThreadSafeLayoutNode};
-use selectors::matching::{ElementSelectorFlags, StyleRelations};
+use selectors::matching::{ElementSelectorFlags, MatchingContext};
use selectors::parser::{AttrSelector, NamespaceConstraint};
use servo_atoms::Atom;
use servo_url::ServoUrl;
use std::fmt;
use std::fmt::Debug;
use std::hash::{Hash, Hasher};
use std::marker::PhantomData;
use std::mem::transmute;
@@ -649,17 +649,17 @@ impl<'le> ::selectors::Element for Servo
#[inline]
fn get_namespace(&self) -> &Namespace {
self.element.namespace()
}
fn match_non_ts_pseudo_class<F>(&self,
pseudo_class: &NonTSPseudoClass,
- _: &mut StyleRelations,
+ _: &mut MatchingContext,
_: &mut F)
-> bool
where F: FnMut(&Self, ElementSelectorFlags),
{
match *pseudo_class {
// https://github.com/servo/servo/issues/8718
NonTSPseudoClass::Link |
NonTSPseudoClass::AnyLink => unsafe {
@@ -1147,17 +1147,17 @@ impl<'le> ::selectors::Element for Servo
#[inline]
fn get_namespace(&self) -> &Namespace {
self.element.get_namespace()
}
fn match_non_ts_pseudo_class<F>(&self,
_: &NonTSPseudoClass,
- _: &mut StyleRelations,
+ _: &mut MatchingContext,
_: &mut F)
-> bool
where F: FnMut(&Self, ElementSelectorFlags),
{
// NB: This could maybe be implemented
warn!("ServoThreadSafeLayoutElement::match_non_ts_pseudo_class called");
false
}
--- a/servo/components/selectors/matching.rs
+++ b/servo/components/selectors/matching.rs
@@ -12,16 +12,17 @@ use tree::Element;
// rapidly increase.
pub static RECOMMENDED_SELECTOR_BLOOM_FILTER_SIZE: usize = 4096;
bitflags! {
/// Set of flags that determine the different kind of elements affected by
/// the selector matching process.
///
/// This is used to implement efficient sharing.
+ #[derive(Default)]
pub flags StyleRelations: usize {
/// Whether this element is affected by an ID selector.
const AFFECTED_BY_ID_SELECTOR = 1 << 0,
/// Whether this element has a style attribute. Computed
/// externally.
const AFFECTED_BY_STYLE_ATTRIBUTE = 1 << 1,
/// Whether this element is affected by presentational hints. This is
/// computed externally (that is, in Servo).
@@ -63,28 +64,38 @@ impl ElementSelectorFlags {
}
/// Returns the subset of flags that apply to the parent.
pub fn for_parent(self) -> ElementSelectorFlags {
self & (HAS_SLOW_SELECTOR | HAS_SLOW_SELECTOR_LATER_SIBLINGS | HAS_EDGE_CHILD_SELECTOR)
}
}
+/// Data associated with the matching process for a element. This context is
+/// used across many selectors for an element, so it's not appropriate for
+/// transient data that applies to only a single selector.
+#[derive(Default)]
+pub struct MatchingContext {
+ /// Output that records certains relations between elements noticed during
+ /// matching (and also extended after matching).
+ pub relations: StyleRelations,
+}
+
pub fn matches_selector_list<E>(selector_list: &[Selector<E::Impl>],
element: &E,
parent_bf: Option<&BloomFilter>)
-> bool
where E: Element
{
selector_list.iter().any(|selector| {
selector.pseudo_element.is_none() &&
matches_selector(&selector.inner,
element,
parent_bf,
- &mut StyleRelations::empty(),
+ &mut MatchingContext::default(),
&mut |_, _| {})
})
}
fn may_match<E>(sel: &SelectorInner<E::Impl>,
bf: &BloomFilter)
-> bool
where E: Element,
@@ -103,31 +114,31 @@ fn may_match<E>(sel: &SelectorInner<E::I
true
}
/// Determines whether the given element matches the given complex selector.
pub fn matches_selector<E, F>(selector: &SelectorInner<E::Impl>,
element: &E,
parent_bf: Option<&BloomFilter>,
- relations: &mut StyleRelations,
+ context: &mut MatchingContext,
flags_setter: &mut F)
-> bool
where E: Element,
F: FnMut(&E, ElementSelectorFlags),
{
// Use the bloom filter to fast-reject.
if let Some(filter) = parent_bf {
if !may_match::<E>(selector, filter) {
return false;
}
}
// Match the selector.
- matches_complex_selector(&selector.complex, element, relations, flags_setter)
+ matches_complex_selector(&selector.complex, element, context, flags_setter)
}
/// A result of selector matching, includes 3 failure types,
///
/// NotMatchedAndRestartFromClosestLaterSibling
/// NotMatchedAndRestartFromClosestDescendant
/// NotMatchedGlobally
///
@@ -173,41 +184,41 @@ enum SelectorMatchingResult {
NotMatchedAndRestartFromClosestLaterSibling,
NotMatchedAndRestartFromClosestDescendant,
NotMatchedGlobally,
}
/// Matches a complex selector.
pub fn matches_complex_selector<E, F>(selector: &ComplexSelector<E::Impl>,
element: &E,
- relations: &mut StyleRelations,
+ context: &mut MatchingContext,
flags_setter: &mut F)
-> bool
where E: Element,
F: FnMut(&E, ElementSelectorFlags),
{
match matches_complex_selector_internal(selector.iter(),
element,
- relations,
+ context,
flags_setter) {
SelectorMatchingResult::Matched => true,
_ => false
}
}
fn matches_complex_selector_internal<E, F>(mut selector_iter: SelectorIter<E::Impl>,
element: &E,
- relations: &mut StyleRelations,
+ context: &mut MatchingContext,
flags_setter: &mut F)
-> SelectorMatchingResult
where E: Element,
F: FnMut(&E, ElementSelectorFlags),
{
let matches_all_simple_selectors = selector_iter.all(|simple| {
- matches_simple_selector(simple, element, relations, flags_setter)
+ matches_simple_selector(simple, element, context, flags_setter)
});
let combinator = selector_iter.next_sequence();
let siblings = combinator.map_or(false, |c| c.is_sibling());
if siblings {
flags_setter(element, HAS_SLOW_SELECTOR_LATER_SIBLINGS);
}
@@ -228,17 +239,17 @@ fn matches_complex_selector_internal<E,
loop {
let element = match next_element {
None => return candidate_not_found,
Some(next_element) => next_element,
};
let result = matches_complex_selector_internal(selector_iter.clone(),
&element,
- relations,
+ context,
flags_setter);
match (result, c) {
// Return the status immediately.
(SelectorMatchingResult::Matched, _) => return result,
(SelectorMatchingResult::NotMatchedGlobally, _) => return result,
// Upgrade the failure status to
// NotMatchedAndRestartFromClosestDescendant.
@@ -271,26 +282,26 @@ fn matches_complex_selector_internal<E,
}
}
/// Determines whether the given element matches the given single selector.
#[inline]
fn matches_simple_selector<E, F>(
selector: &Component<E::Impl>,
element: &E,
- relations: &mut StyleRelations,
+ context: &mut MatchingContext,
flags_setter: &mut F)
-> bool
where E: Element,
F: FnMut(&E, ElementSelectorFlags),
{
macro_rules! relation_if {
($ex:expr, $flag:ident) => {
if $ex {
- *relations |= $flag;
+ context.relations |= $flag;
true
} else {
false
}
}
}
match *selector {
@@ -336,17 +347,17 @@ fn matches_simple_selector<E, F>(
}
Component::AttrIncludesNeverMatch(..) |
Component::AttrPrefixNeverMatch(..) |
Component::AttrSubstringNeverMatch(..) |
Component::AttrSuffixNeverMatch(..) => {
false
}
Component::NonTSPseudoClass(ref pc) => {
- element.match_non_ts_pseudo_class(pc, relations, flags_setter)
+ element.match_non_ts_pseudo_class(pc, context, flags_setter)
}
Component::FirstChild => {
matches_first_child(element, flags_setter)
}
Component::LastChild => {
matches_last_child(element, flags_setter)
}
Component::OnlyChild => {
@@ -380,17 +391,17 @@ fn matches_simple_selector<E, F>(
Component::LastOfType => {
matches_generic_nth_child(element, 0, 1, true, true, flags_setter)
}
Component::OnlyOfType => {
matches_generic_nth_child(element, 0, 1, true, false, flags_setter) &&
matches_generic_nth_child(element, 0, 1, true, true, flags_setter)
}
Component::Negation(ref negated) => {
- !negated.iter().all(|ss| matches_simple_selector(ss, element, relations, flags_setter))
+ !negated.iter().all(|ss| matches_simple_selector(ss, element, context, flags_setter))
}
}
}
#[inline]
fn matches_generic_nth_child<E, F>(element: &E,
a: i32,
b: i32,
--- a/servo/components/selectors/tree.rs
+++ b/servo/components/selectors/tree.rs
@@ -1,16 +1,16 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
//! Traits that nodes must implement. Breaks the otherwise-cyclic dependency between layout and
//! style.
-use matching::{ElementSelectorFlags, StyleRelations};
+use matching::{ElementSelectorFlags, MatchingContext};
use parser::{AttrSelector, SelectorImpl};
use std::ascii::AsciiExt;
/// The definition of whitespace per CSS Selectors Level 3 ยง 4.
pub static SELECTOR_WHITESPACE: &'static [char] = &[' ', '\t', '\n', '\r', '\x0C'];
// Attribute matching routines. Consumers with simple implementations can implement
// MatchAttrGeneric instead.
@@ -136,17 +136,17 @@ pub trait Element: MatchAttr + Sized {
fn next_sibling_element(&self) -> Option<Self>;
fn is_html_element_in_html_document(&self) -> bool;
fn get_local_name(&self) -> &<Self::Impl as SelectorImpl>::BorrowedLocalName;
fn get_namespace(&self) -> &<Self::Impl as SelectorImpl>::BorrowedNamespaceUrl;
fn match_non_ts_pseudo_class<F>(&self,
pc: &<Self::Impl as SelectorImpl>::NonTSPseudoClass,
- relations: &mut StyleRelations,
+ context: &mut MatchingContext,
flags_setter: &mut F) -> bool
where F: FnMut(&Self, ElementSelectorFlags);
fn get_id(&self) -> Option<<Self::Impl as SelectorImpl>::Identifier>;
fn has_class(&self, name: &<Self::Impl as SelectorImpl>::ClassName) -> bool;
/// Returns whether this element matches `:empty`.
///
--- a/servo/components/style/gecko/wrapper.rs
+++ b/servo/components/style/gecko/wrapper.rs
@@ -59,17 +59,17 @@ use logical_geometry::WritingMode;
use media_queries::Device;
use properties::{ComputedValues, parse_style_attribute};
use properties::{Importance, PropertyDeclaration, PropertyDeclarationBlock};
use properties::animated_properties::{AnimationValue, AnimationValueMap, TransitionProperty};
use properties::style_structs::Font;
use rule_tree::CascadeLevel as ServoCascadeLevel;
use selector_parser::ElementExt;
use selectors::Element;
-use selectors::matching::{ElementSelectorFlags, StyleRelations};
+use selectors::matching::{ElementSelectorFlags, MatchingContext};
use selectors::parser::{AttrSelector, NamespaceConstraint};
use shared_lock::Locked;
use sink::Push;
use std::cell::RefCell;
use std::collections::HashMap;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::ptr;
@@ -1120,17 +1120,17 @@ impl<'le> ::selectors::Element for Gecko
fn get_namespace(&self) -> &WeakNamespace {
unsafe {
WeakNamespace::new(Gecko_Namespace(self.0))
}
}
fn match_non_ts_pseudo_class<F>(&self,
pseudo_class: &NonTSPseudoClass,
- relations: &mut StyleRelations,
+ context: &mut MatchingContext,
flags_setter: &mut F)
-> bool
where F: FnMut(&Self, ElementSelectorFlags),
{
use selectors::matching::*;
match *pseudo_class {
NonTSPseudoClass::AnyLink |
NonTSPseudoClass::Link |
@@ -1214,17 +1214,17 @@ impl<'le> ::selectors::Element for Gecko
}
NonTSPseudoClass::MozTableBorderNonzero |
NonTSPseudoClass::MozBrowserFrame |
NonTSPseudoClass::MozNativeAnonymous => unsafe {
Gecko_MatchesElement(pseudo_class.to_gecko_pseudoclasstype().unwrap(), self.0)
},
NonTSPseudoClass::MozAny(ref sels) => {
sels.iter().any(|s| {
- matches_complex_selector(s, self, relations, flags_setter)
+ matches_complex_selector(s, self, context, flags_setter)
})
}
NonTSPseudoClass::MozSystemMetric(ref s) |
NonTSPseudoClass::MozLocaleDir(ref s) |
NonTSPseudoClass::MozEmptyExceptChildrenWithLocalname(ref s) |
NonTSPseudoClass::Dir(ref s) |
NonTSPseudoClass::Lang(ref s) => {
unsafe {
@@ -1371,17 +1371,17 @@ impl<'le> ::selectors::MatchAttr for Gec
}
}
}
impl<'le> ElementExt for GeckoElement<'le> {
#[inline]
fn is_link(&self) -> bool {
self.match_non_ts_pseudo_class(&NonTSPseudoClass::AnyLink,
- &mut StyleRelations::empty(),
+ &mut MatchingContext::default(),
&mut |_, _| {})
}
#[inline]
fn matches_user_and_author_rules(&self) -> bool {
self.flags() & (NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE as u32) == 0
}
}
--- a/servo/components/style/matching.rs
+++ b/servo/components/style/matching.rs
@@ -19,17 +19,17 @@ 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;
use restyle_hints::{RESTYLE_CSS_ANIMATIONS, RESTYLE_CSS_TRANSITIONS, RestyleHint};
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, StyleRelations};
+use selectors::matching::{ElementSelectorFlags, MatchingContext, StyleRelations};
use selectors::matching::AFFECTED_BY_PSEUDO_ELEMENTS;
use shared_lock::StylesheetGuards;
use sink::ForgetfulSink;
use stylearc::Arc;
use stylist::ApplicableDeclarationBlock;
/// The way a style should be inherited.
enum InheritMode {
@@ -890,32 +890,34 @@ pub trait MatchMethods : TElement {
/// Performs selector matching and property cascading on an element and its
/// eager pseudos.
fn match_and_cascade(&self,
context: &mut StyleContext<Self>,
data: &mut ElementData,
sharing: StyleSharingBehavior)
{
// Perform selector matching for the primary style.
- let mut primary_relations = StyleRelations::empty();
- let _rule_node_changed = self.match_primary(context, data, &mut primary_relations);
+ let mut primary_matching_context = MatchingContext::default();
+ let _rule_node_changed = self.match_primary(context,
+ data,
+ &mut primary_matching_context);
// Cascade properties and compute primary values.
self.cascade_primary(context, data);
// Match and cascade eager pseudo-elements.
if !data.styles().is_display_none() {
let _pseudo_rule_nodes_changed =
self.match_pseudos(context, data);
self.cascade_pseudos(context, data);
}
// If we have any pseudo elements, indicate so in the primary StyleRelations.
if !data.styles().pseudos.is_empty() {
- primary_relations |= AFFECTED_BY_PSEUDO_ELEMENTS;
+ primary_matching_context.relations |= AFFECTED_BY_PSEUDO_ELEMENTS;
}
// If the style is shareable, add it to the LRU cache.
if sharing == StyleSharingBehavior::Allow {
// If we previously tried to match this element against the cache,
// the revalidation match results will already be cached. Otherwise
// we'll have None, and compute them later on-demand.
//
@@ -925,17 +927,17 @@ pub trait MatchMethods : TElement {
.current_element_info
.as_mut().unwrap()
.revalidation_match_results
.take();
context.thread_local
.style_sharing_candidate_cache
.insert_if_possible(self,
data.styles().primary.values(),
- primary_relations,
+ primary_matching_context.relations,
revalidation_match_results);
}
}
/// Performs the cascade, without matching.
fn cascade_primary_and_pseudos(&self,
context: &mut StyleContext<Self>,
mut data: &mut ElementData)
@@ -945,17 +947,17 @@ pub trait MatchMethods : TElement {
}
/// Runs selector matching to (re)compute the primary rule node for this element.
///
/// Returns whether the primary rule node changed.
fn match_primary(&self,
context: &mut StyleContext<Self>,
data: &mut ElementData,
- relations: &mut StyleRelations)
+ matching_context: &mut MatchingContext)
-> bool
{
let implemented_pseudo = self.implemented_pseudo_element();
if let Some(ref pseudo) = implemented_pseudo {
if pseudo.is_eager() {
// If it's an eager element-backed pseudo, just grab the matched
// rules from the parent, and update animations.
let parent = self.parent_element().unwrap();
@@ -1017,24 +1019,25 @@ pub trait MatchMethods : TElement {
};
let pseudo_and_state = match implemented_pseudo {
Some(ref pseudo) => Some((pseudo, self.get_state())),
None => None,
};
// Compute the primary rule node.
- *relations = stylist.push_applicable_declarations(&selector_matching_target,
- Some(bloom),
- style_attribute,
- smil_override,
- animation_rules,
- pseudo_and_state,
- &mut applicable_declarations,
- &mut set_selector_flags);
+ stylist.push_applicable_declarations(&selector_matching_target,
+ Some(bloom),
+ style_attribute,
+ smil_override,
+ animation_rules,
+ pseudo_and_state,
+ &mut applicable_declarations,
+ matching_context,
+ &mut set_selector_flags);
let primary_rule_node =
compute_rule_node::<Self>(&stylist.rule_tree,
&mut applicable_declarations,
&context.shared.guards);
return data.set_primary_rules(primary_rule_node);
}
@@ -1079,16 +1082,17 @@ pub trait MatchMethods : TElement {
// traversing them.
stylist.push_applicable_declarations(self,
Some(bloom_filter),
None,
None,
AnimationRules(None, None),
Some((&pseudo, ElementState::empty())),
&mut applicable_declarations,
+ &mut MatchingContext::default(),
&mut set_selector_flags);
if !applicable_declarations.is_empty() {
let new_rules =
compute_rule_node::<Self>(rule_tree,
&mut applicable_declarations,
&guards);
if pseudos.has(&pseudo) {
--- a/servo/components/style/restyle_hints.rs
+++ b/servo/components/style/restyle_hints.rs
@@ -11,17 +11,17 @@ 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, StyleRelations};
+use selectors::matching::{ElementSelectorFlags, MatchingContext};
use selectors::matching::matches_selector;
use selectors::parser::{AttrSelector, Combinator, Component, Selector};
use selectors::parser::{SelectorInner, SelectorMethods};
use selectors::visitor::SelectorVisitor;
use smallvec::SmallVec;
use std::borrow::Borrow;
use std::cell::Cell;
use std::clone::Clone;
@@ -343,29 +343,29 @@ fn dir_selector_to_state(s: &[u16]) -> E
}
}
impl<'a, E> Element for ElementWrapper<'a, E>
where E: TElement,
{
fn match_non_ts_pseudo_class<F>(&self,
pseudo_class: &NonTSPseudoClass,
- relations: &mut StyleRelations,
+ context: &mut MatchingContext,
_setter: &mut F)
-> bool
where F: FnMut(&Self, ElementSelectorFlags),
{
// :moz-any is quite special, because we need to keep matching as a
// snapshot.
#[cfg(feature = "gecko")]
{
use selectors::matching::matches_complex_selector;
if let NonTSPseudoClass::MozAny(ref selectors) = *pseudo_class {
return selectors.iter().any(|s| {
- matches_complex_selector(s, self, relations, _setter)
+ matches_complex_selector(s, self, context, _setter)
})
}
}
// :dir needs special handling. It's implemented in terms of state
// flags, but which state flag it maps to depends on the argument to
// :dir. That means we can't just add its state flags to the
// NonTSPseudoClass, because if we added all of them there, and tested
@@ -388,24 +388,24 @@ impl<'a, E> Element for ElementWrapper<'
};
return state.contains(selector_flag);
}
}
let flag = pseudo_class.state_flag();
if flag.is_empty() {
return self.element.match_non_ts_pseudo_class(pseudo_class,
- relations,
+ context,
&mut |_, _| {})
}
match self.snapshot().and_then(|s| s.state()) {
Some(snapshot_state) => snapshot_state.intersects(flag),
None => {
self.element.match_non_ts_pseudo_class(pseudo_class,
- relations,
+ context,
&mut |_, _| {})
}
}
}
fn parent_element(&self) -> Option<Self> {
self.element.parent_element()
.map(|e| ElementWrapper::new(e, self.snapshot_map))
@@ -852,21 +852,21 @@ impl DependencySet {
return true;
}
// We can ignore the selector flags, since they would have already
// 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 StyleRelations::empty(),
+ &mut MatchingContext::default(),
&mut |_, _| {});
let matches_now =
matches_selector(&dep.selector, el, None,
- &mut StyleRelations::empty(),
+ &mut MatchingContext::default(),
&mut |_, _| {});
if matched_then != matches_now {
hint.insert(dep.hint);
}
!hint.is_all()
});
--- a/servo/components/style/servo/selector_parser.rs
+++ b/servo/components/style/servo/selector_parser.rs
@@ -10,17 +10,17 @@ use {Atom, Prefix, Namespace, LocalName}
use attr::{AttrIdentifier, AttrValue};
use cssparser::{Parser as CssParser, ToCss, serialize_identifier};
use dom::{OpaqueNode, TElement, TNode};
use element_state::ElementState;
use fnv::FnvHashMap;
use restyle_hints::ElementSnapshot;
use selector_parser::{ElementExt, PseudoElementCascadeType, SelectorParser};
use selectors::{Element, MatchAttrGeneric};
-use selectors::matching::StyleRelations;
+use selectors::matching::MatchingContext;
use selectors::parser::{AttrSelector, SelectorMethods};
use selectors::visitor::SelectorVisitor;
use std::borrow::Cow;
use std::fmt;
use std::fmt::Debug;
use std::mem;
use std::ops::{Deref, DerefMut};
@@ -575,17 +575,17 @@ impl MatchAttrGeneric for ServoElementSn
NamespaceConstraint::Any => self.get_attr_ignore_ns(local_name),
}.map_or(false, |v| test(v))
}
}
impl<E: Element<Impl=SelectorImpl> + Debug> ElementExt for E {
fn is_link(&self) -> bool {
self.match_non_ts_pseudo_class(&NonTSPseudoClass::AnyLink,
- &mut StyleRelations::empty(),
+ &mut MatchingContext::default(),
&mut |_, _| {})
}
#[inline]
fn matches_user_and_author_rules(&self) -> bool {
true
}
}
--- a/servo/components/style/stylist.rs
+++ b/servo/components/style/stylist.rs
@@ -22,17 +22,17 @@ use properties::{self, CascadeFlags, Com
use properties::INHERIT_ALL;
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, StyleRelations, matches_selector};
+use selectors::matching::{ElementSelectorFlags, matches_selector, MatchingContext};
use selectors::parser::{AttrSelector, Combinator, Component, Selector, SelectorInner, SelectorIter};
use selectors::parser::{SelectorMethods, LocalName as LocalNameSelector};
use selectors::visitor::SelectorVisitor;
use shared_lock::{Locked, SharedRwLockReadGuard, StylesheetGuards};
use sink::Push;
use smallvec::VecLike;
use std::borrow::Borrow;
use std::collections::HashMap;
@@ -672,25 +672,25 @@ impl Stylist {
let parent_flags = flags.for_parent();
if !parent_flags.is_empty() {
if let Some(p) = element.parent_element() {
unsafe { p.set_selector_flags(parent_flags); }
}
}
};
-
let mut declarations = vec![];
self.push_applicable_declarations(element,
None,
None,
None,
AnimationRules(None, None),
Some((pseudo, pseudo_state)),
&mut declarations,
+ &mut MatchingContext::default(),
&mut set_selector_flags);
if declarations.is_empty() {
return None
}
let rule_node =
self.rule_tree.insert_ordered_rules_with_important(
declarations.into_iter().map(|a| (a.source, a.level)),
@@ -799,29 +799,29 @@ impl Stylist {
// mode info in the `SharedLayoutContext`.
self.quirks_mode = quirks_mode;
}
/// Returns the applicable CSS declarations for the given element.
///
/// This corresponds to `ElementRuleCollector` in WebKit.
///
- /// The returned `StyleRelations` indicate hints about which kind of rules
- /// have matched.
+ /// The `StyleRelations` recorded in `MatchingContext` indicate hints about
+ /// which kind of rules have matched.
pub fn push_applicable_declarations<E, V, F>(
&self,
element: &E,
parent_bf: Option<&BloomFilter>,
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)
- -> StyleRelations
where E: TElement,
V: Push<ApplicableDeclarationBlock> + VecLike<ApplicableDeclarationBlock>,
F: FnMut(&E, ElementSelectorFlags),
{
debug_assert!(!self.is_device_dirty);
// Gecko definitely has pseudo-elements with style attributes, like
// ::-moz-color-swatch.
debug_assert!(cfg!(feature = "gecko") ||
@@ -829,118 +829,114 @@ impl Stylist {
"Style attributes do not apply to pseudo-elements");
debug_assert!(pseudo_element.as_ref().map_or(true, |p| !p.0.is_precomputed()));
let map = match pseudo_element {
Some((ref pseudo, _)) => self.pseudos_map.get(pseudo).unwrap(),
None => &self.element_map,
};
- let mut relations = StyleRelations::empty();
-
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,
applicable_declarations,
- &mut relations,
+ context,
flags_setter,
CascadeLevel::UANormal);
- debug!("UA normal: {:?}", relations);
+ debug!("UA normal: {:?}", context.relations);
if pseudo_element.is_none() {
// Step 2: Presentational hints.
let length_before_preshints = applicable_declarations.len();
element.synthesize_presentational_hints_for_legacy_attributes(applicable_declarations);
if applicable_declarations.len() != length_before_preshints {
if cfg!(debug_assertions) {
for declaration in &applicable_declarations[length_before_preshints..] {
assert_eq!(declaration.level, CascadeLevel::PresHints);
}
}
// Never share style for elements with preshints
- relations |= AFFECTED_BY_PRESENTATIONAL_HINTS;
+ context.relations |= AFFECTED_BY_PRESENTATIONAL_HINTS;
}
- debug!("preshints: {:?}", relations);
+ debug!("preshints: {:?}", context.relations);
}
if element.matches_user_and_author_rules() {
// Step 3: User and author normal rules.
map.user.get_all_matching_rules(element,
pseudo_element,
parent_bf,
applicable_declarations,
- &mut relations,
+ context,
flags_setter,
CascadeLevel::UserNormal);
- debug!("user normal: {:?}", relations);
+ debug!("user normal: {:?}", context.relations);
map.author.get_all_matching_rules(element,
pseudo_element,
parent_bf,
applicable_declarations,
- &mut relations,
+ context,
flags_setter,
CascadeLevel::AuthorNormal);
- debug!("author normal: {:?}", relations);
+ debug!("author normal: {:?}", context.relations);
// Step 4: Normal style attributes.
if let Some(sa) = style_attribute {
- relations |= AFFECTED_BY_STYLE_ATTRIBUTE;
+ context.relations |= AFFECTED_BY_STYLE_ATTRIBUTE;
Push::push(
applicable_declarations,
ApplicableDeclarationBlock::from_declarations(sa.clone(),
CascadeLevel::StyleAttributeNormal));
}
- debug!("style attr: {:?}", relations);
+ debug!("style attr: {:?}", context.relations);
// Step 5: SMIL override.
// Declarations from SVG SMIL animation elements.
if let Some(so) = smil_override {
Push::push(
applicable_declarations,
ApplicableDeclarationBlock::from_declarations(so.clone(),
CascadeLevel::SMILOverride));
}
- debug!("SMIL: {:?}", relations);
+ debug!("SMIL: {:?}", context.relations);
// Step 6: Animations.
// The animations sheet (CSS animations, script-generated animations,
// and CSS transitions that are no longer tied to CSS markup)
if let Some(anim) = animation_rules.0 {
Push::push(
applicable_declarations,
ApplicableDeclarationBlock::from_declarations(anim,
CascadeLevel::Animations));
}
- debug!("animation: {:?}", relations);
+ debug!("animation: {:?}", context.relations);
} else {
debug!("skipping non-agent rules");
}
//
// Steps 7-10 correspond to !important rules, and are handled during
// rule tree insertion.
//
// Step 11: Transitions.
// The transitions sheet (CSS transitions that are tied to CSS markup)
if let Some(anim) = animation_rules.1 {
Push::push(
applicable_declarations,
ApplicableDeclarationBlock::from_declarations(anim, CascadeLevel::Transitions));
}
- debug!("transition: {:?}", relations);
+ debug!("transition: {:?}", context.relations);
- debug!("push_applicable_declarations: shareable: {:?}", relations);
-
- relations
+ debug!("push_applicable_declarations: shareable: {:?}", context.relations);
}
/// Return whether the device is dirty, that is, whether the screen size or
/// media type have changed (for now).
#[inline]
pub fn is_device_dirty(&self) -> bool {
self.is_device_dirty
}
@@ -962,30 +958,27 @@ impl Stylist {
pub fn match_revalidation_selectors<E, F>(&self,
element: &E,
bloom: &BloomFilter,
flags_setter: &mut F)
-> BitVec
where E: TElement,
F: FnMut(&E, ElementSelectorFlags),
{
- use selectors::matching::StyleRelations;
- use selectors::matching::matches_selector;
-
// 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
// the lookups, which means that the bitvecs are comparable. We verify
// this in the caller by asserting that the bitvecs are same-length.
let mut results = BitVec::new();
self.selectors_for_cache_revalidation.lookup(*element, &mut |selector| {
results.push(matches_selector(selector,
element,
Some(bloom),
- &mut StyleRelations::empty(),
+ &mut MatchingContext::default(),
flags_setter));
true
});
results
}
/// Given an element, and a snapshot table that represents a previous state
@@ -1258,17 +1251,17 @@ impl SelectorMap<Rule> {
///
/// Extract matching rules as per element's ID, classes, tag name, etc..
/// 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>,
matching_rules_list: &mut V,
- relations: &mut StyleRelations,
+ context: &mut MatchingContext,
flags_setter: &mut F,
cascade_level: CascadeLevel)
where E: Element<Impl=SelectorImpl>,
V: VecLike<ApplicableDeclarationBlock>,
F: FnMut(&E, ElementSelectorFlags),
{
if self.is_empty() {
return
@@ -1278,49 +1271,49 @@ impl SelectorMap<Rule> {
let init_len = matching_rules_list.len();
if let Some(id) = element.get_id() {
SelectorMap::get_matching_rules_from_hash(element,
pseudo_element,
parent_bf,
&self.id_hash,
&id,
matching_rules_list,
- relations,
+ context,
flags_setter,
cascade_level)
}
element.each_class(|class| {
SelectorMap::get_matching_rules_from_hash(element,
pseudo_element,
parent_bf,
&self.class_hash,
class,
matching_rules_list,
- relations,
+ context,
flags_setter,
cascade_level);
});
SelectorMap::get_matching_rules_from_hash(element,
pseudo_element,
parent_bf,
&self.local_name_hash,
element.get_local_name(),
matching_rules_list,
- relations,
+ context,
flags_setter,
cascade_level);
SelectorMap::get_matching_rules(element,
pseudo_element,
parent_bf,
&self.other,
matching_rules_list,
- relations,
+ context,
flags_setter,
cascade_level);
// Sort only the rules we just added.
sort_by_key(&mut matching_rules_list[init_len..],
|block| (block.specificity, block.source_order));
}
@@ -1349,44 +1342,44 @@ 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,
- relations: &mut StyleRelations,
+ context: &mut MatchingContext,
flags_setter: &mut F,
cascade_level: CascadeLevel)
where E: Element<Impl=SelectorImpl>,
Str: Borrow<BorrowedStr> + Eq + Hash,
BorrowedStr: Eq + Hash,
Vector: VecLike<ApplicableDeclarationBlock>,
F: FnMut(&E, ElementSelectorFlags),
{
if let Some(rules) = hash.get(key) {
SelectorMap::get_matching_rules(element,
pseudo_element,
parent_bf,
rules,
matching_rules,
- relations,
+ context,
flags_setter,
cascade_level)
}
}
/// 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,
- relations: &mut StyleRelations,
+ context: &mut MatchingContext,
flags_setter: &mut F,
cascade_level: CascadeLevel)
where E: Element<Impl=SelectorImpl>,
V: VecLike<ApplicableDeclarationBlock>,
F: FnMut(&E, ElementSelectorFlags),
{
for rule in rules.iter() {
debug_assert_eq!(rule.selector.pseudo_element.is_some(),
@@ -1408,17 +1401,17 @@ impl SelectorMap<Rule> {
if !state.is_empty() && !pseudo_state.contains(state) {
continue;
}
}
if matches_selector(&rule.selector.inner,
element,
parent_bf,
- relations,
+ context,
flags_setter) {
matching_rules.push(
rule.to_applicable_declaration_block(cascade_level));
}
}
}
}