--- a/layout/style/test/stylo-failures.md
+++ b/layout/style/test/stylo-failures.md
@@ -68,17 +68,16 @@ to mochitest command.
* test_bug221428.html [1]
* test_css_eof_handling.html: also relies on \@-moz-document [1]
* @keyframes
* test_keyframes_rules.html [1]
* test_rules_out_of_sheets.html [1]
* @support
* test_supports_rules.html [1]
* test_box_size_keywords.html: moz-prefixed intrinsic size keyword value [64]
-* test_bug229915.html: sibling selector with dynamic change bug 1330885 [5]
* test_bug357614.html: case-insensitivity for old attrs in attr selector servo/servo#15006 [2]
* mapped attribute not supported
* test_bug363146.html [2]
* test_bug389464.html: also font-size computation [1]
* test_html_attribute_computed_values.html: also list-style-type [8]
* test_bug387615.html: servo/servo#15006 [1]
* test_bug397427.html: @import issue bug 1331291 and CSSOM support of @import [3]
* console support:
--- a/servo/components/selectors/matching.rs
+++ b/servo/components/selectors/matching.rs
@@ -102,18 +102,21 @@ impl ElementSelectorFlags {
pub fn matches<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_complex_selector(&*selector.complex_selector, element, parent_bf,
- &mut StyleRelations::empty(), &mut ElementSelectorFlags::empty())
+ matches_complex_selector(&*selector.complex_selector,
+ element,
+ parent_bf,
+ &mut StyleRelations::empty(),
+ &mut |_, _| {})
})
}
fn may_match<E>(mut selector: &ComplexSelector<E::Impl>,
bf: &BloomFilter)
-> bool
where E: Element,
{
@@ -157,31 +160,35 @@ fn may_match<E>(mut selector: &ComplexSe
}
}
// If we haven't proven otherwise, it may match.
true
}
/// Determines whether the given element matches the given complex selector.
-pub fn matches_complex_selector<E>(selector: &ComplexSelector<E::Impl>,
- element: &E,
- parent_bf: Option<&BloomFilter>,
- relations: &mut StyleRelations,
- flags: &mut ElementSelectorFlags)
- -> bool
- where E: Element
+pub fn matches_complex_selector<E, F>(selector: &ComplexSelector<E::Impl>,
+ element: &E,
+ parent_bf: Option<&BloomFilter>,
+ relations: &mut StyleRelations,
+ flags_setter: &mut F)
+ -> bool
+ where E: Element,
+ F: FnMut(&E, ElementSelectorFlags),
{
if let Some(filter) = parent_bf {
if !may_match::<E>(selector, filter) {
return false;
}
}
- match matches_complex_selector_internal(selector, element, relations, flags) {
+ match matches_complex_selector_internal(selector,
+ element,
+ relations,
+ flags_setter) {
SelectorMatchingResult::Matched => {
match selector.next {
Some((_, Combinator::NextSibling)) |
Some((_, Combinator::LaterSibling)) => *relations |= AFFECTED_BY_SIBLINGS,
_ => {}
}
true
@@ -235,54 +242,60 @@ pub fn matches_complex_selector<E>(selec
#[derive(PartialEq, Eq, Copy, Clone)]
enum SelectorMatchingResult {
Matched,
NotMatchedAndRestartFromClosestLaterSibling,
NotMatchedAndRestartFromClosestDescendant,
NotMatchedGlobally,
}
-fn matches_complex_selector_internal<E>(selector: &ComplexSelector<E::Impl>,
- element: &E,
- relations: &mut StyleRelations,
- flags: &mut ElementSelectorFlags)
- -> SelectorMatchingResult
- where E: Element
+fn matches_complex_selector_internal<E, F>(selector: &ComplexSelector<E::Impl>,
+ element: &E,
+ relations: &mut StyleRelations,
+ flags_setter: &mut F)
+ -> SelectorMatchingResult
+ where E: Element,
+ F: FnMut(&E, ElementSelectorFlags),
{
let matches_all_simple_selectors = selector.compound_selector.iter().all(|simple| {
- matches_simple_selector(simple, element, relations, flags)
+ matches_simple_selector(simple, element, relations, flags_setter)
});
+ let siblings = selector.next.as_ref().map_or(false, |&(_, combinator)| {
+ matches!(combinator, Combinator::NextSibling | Combinator::LaterSibling)
+ });
+
+ if siblings {
+ flags_setter(element, HAS_SLOW_SELECTOR_LATER_SIBLINGS);
+ }
+
if !matches_all_simple_selectors {
return SelectorMatchingResult::NotMatchedAndRestartFromClosestLaterSibling;
}
match selector.next {
None => SelectorMatchingResult::Matched,
Some((ref next_selector, combinator)) => {
- let (siblings, candidate_not_found) = match combinator {
- Combinator::Child => (false, SelectorMatchingResult::NotMatchedGlobally),
- Combinator::Descendant => (false, SelectorMatchingResult::NotMatchedGlobally),
- Combinator::NextSibling => (true, SelectorMatchingResult::NotMatchedAndRestartFromClosestDescendant),
- Combinator::LaterSibling => (true, SelectorMatchingResult::NotMatchedAndRestartFromClosestDescendant),
+ let (mut next_element, candidate_not_found) = if siblings {
+ (element.prev_sibling_element(),
+ SelectorMatchingResult::NotMatchedAndRestartFromClosestDescendant)
+ } else {
+ (element.parent_element(),
+ SelectorMatchingResult::NotMatchedGlobally)
};
- let mut next_element = if siblings {
- element.prev_sibling_element()
- } else {
- element.parent_element()
- };
+
loop {
let element = match next_element {
None => return candidate_not_found,
Some(next_element) => next_element,
};
let result = matches_complex_selector_internal(&**next_selector,
&element,
relations,
- flags);
+ flags_setter);
match (result, combinator) {
// Return the status immediately.
(SelectorMatchingResult::Matched, _) => return result,
(SelectorMatchingResult::NotMatchedGlobally, _) => return result,
// Upgrade the failure status to
// NotMatchedAndRestartFromClosestDescendant.
(_, Combinator::Child) => return SelectorMatchingResult::NotMatchedAndRestartFromClosestDescendant,
@@ -311,23 +324,24 @@ fn matches_complex_selector_internal<E>(
};
}
}
}
}
/// Determines whether the given element matches the given single selector.
#[inline]
-fn matches_simple_selector<E>(
+fn matches_simple_selector<E, F>(
selector: &SimpleSelector<E::Impl>,
element: &E,
relations: &mut StyleRelations,
- flags: &mut ElementSelectorFlags)
+ flags_setter: &mut F)
-> bool
- where E: Element
+ where E: Element,
+ F: FnMut(&E, ElementSelectorFlags),
{
macro_rules! relation_if {
($ex:expr, $flag:ident) => {
if $ex {
*relations |= $flag;
true
} else {
false
@@ -394,89 +408,96 @@ fn matches_simple_selector<E>(
}
SimpleSelector::AttrIncludesNeverMatch(..) |
SimpleSelector::AttrPrefixNeverMatch(..) |
SimpleSelector::AttrSubstringNeverMatch(..) |
SimpleSelector::AttrSuffixNeverMatch(..) => {
false
}
SimpleSelector::NonTSPseudoClass(ref pc) => {
- relation_if!(element.match_non_ts_pseudo_class(pc, relations, flags),
+ relation_if!(element.match_non_ts_pseudo_class(pc, relations, flags_setter),
AFFECTED_BY_STATE)
}
SimpleSelector::FirstChild => {
- relation_if!(matches_first_child(element, flags), AFFECTED_BY_CHILD_INDEX)
+ relation_if!(matches_first_child(element, flags_setter),
+ AFFECTED_BY_CHILD_INDEX)
}
SimpleSelector::LastChild => {
- relation_if!(matches_last_child(element, flags), AFFECTED_BY_CHILD_INDEX)
+ relation_if!(matches_last_child(element, flags_setter),
+ AFFECTED_BY_CHILD_INDEX)
}
SimpleSelector::OnlyChild => {
- relation_if!(matches_first_child(element, flags) &&
- matches_last_child(element, flags), AFFECTED_BY_CHILD_INDEX)
+ relation_if!(matches_first_child(element, flags_setter) &&
+ matches_last_child(element, flags_setter),
+ AFFECTED_BY_CHILD_INDEX)
}
SimpleSelector::Root => {
// We never share styles with an element with no parent, so no point
// in creating a new StyleRelation.
element.is_root()
}
SimpleSelector::Empty => {
- flags.insert(HAS_EMPTY_SELECTOR);
+ flags_setter(element, HAS_EMPTY_SELECTOR);
relation_if!(element.is_empty(), AFFECTED_BY_EMPTY)
}
SimpleSelector::NthChild(a, b) => {
- relation_if!(matches_generic_nth_child(element, a, b, false, false, flags),
+ relation_if!(matches_generic_nth_child(element, a, b, false, false, flags_setter),
AFFECTED_BY_CHILD_INDEX)
}
SimpleSelector::NthLastChild(a, b) => {
- relation_if!(matches_generic_nth_child(element, a, b, false, true, flags),
+ relation_if!(matches_generic_nth_child(element, a, b, false, true, flags_setter),
AFFECTED_BY_CHILD_INDEX)
}
SimpleSelector::NthOfType(a, b) => {
- relation_if!(matches_generic_nth_child(element, a, b, true, false, flags),
+ relation_if!(matches_generic_nth_child(element, a, b, true, false, flags_setter),
AFFECTED_BY_CHILD_INDEX)
}
SimpleSelector::NthLastOfType(a, b) => {
- relation_if!(matches_generic_nth_child(element, a, b, true, true, flags),
+ relation_if!(matches_generic_nth_child(element, a, b, true, true, flags_setter),
AFFECTED_BY_CHILD_INDEX)
}
SimpleSelector::FirstOfType => {
- relation_if!(matches_generic_nth_child(element, 0, 1, true, false, flags),
+ relation_if!(matches_generic_nth_child(element, 0, 1, true, false, flags_setter),
AFFECTED_BY_CHILD_INDEX)
}
SimpleSelector::LastOfType => {
- relation_if!(matches_generic_nth_child(element, 0, 1, true, true, flags),
+ relation_if!(matches_generic_nth_child(element, 0, 1, true, true, flags_setter),
AFFECTED_BY_CHILD_INDEX)
}
SimpleSelector::OnlyOfType => {
- relation_if!(matches_generic_nth_child(element, 0, 1, true, false, flags) &&
- matches_generic_nth_child(element, 0, 1, true, true, flags),
+ relation_if!(matches_generic_nth_child(element, 0, 1, true, false, flags_setter) &&
+ matches_generic_nth_child(element, 0, 1, true, true, flags_setter),
AFFECTED_BY_CHILD_INDEX)
}
SimpleSelector::Negation(ref negated) => {
!negated.iter().all(|s| {
- match matches_complex_selector_internal(s, element, relations, flags) {
+ match matches_complex_selector_internal(s,
+ element,
+ relations,
+ flags_setter) {
SelectorMatchingResult::Matched => true,
_ => false,
}
})
}
}
}
#[inline]
-fn matches_generic_nth_child<E>(element: &E,
- a: i32,
- b: i32,
- is_of_type: bool,
- is_from_end: bool,
- flags: &mut ElementSelectorFlags)
- -> bool
- where E: Element
+fn matches_generic_nth_child<E, F>(element: &E,
+ a: i32,
+ b: i32,
+ is_of_type: bool,
+ is_from_end: bool,
+ flags_setter: &mut F)
+ -> bool
+ where E: Element,
+ F: FnMut(&E, ElementSelectorFlags),
{
- flags.insert(if is_from_end {
+ flags_setter(element, if is_from_end {
HAS_SLOW_SELECTOR
} else {
HAS_SLOW_SELECTOR_LATER_SIBLINGS
});
let mut index = 1;
let mut next_sibling = if is_from_end {
element.next_sibling_element()
@@ -509,20 +530,24 @@ fn matches_generic_nth_child<E>(element:
b == index
} else {
(index - b) / a >= 0 &&
(index - b) % a == 0
}
}
#[inline]
-fn matches_first_child<E>(element: &E, flags: &mut ElementSelectorFlags)
- -> bool where E: Element {
- flags.insert(HAS_EDGE_CHILD_SELECTOR);
+fn matches_first_child<E, F>(element: &E, flags_setter: &mut F) -> bool
+ where E: Element,
+ F: FnMut(&E, ElementSelectorFlags),
+{
+ flags_setter(element, HAS_EDGE_CHILD_SELECTOR);
element.prev_sibling_element().is_none()
}
#[inline]
-fn matches_last_child<E>(element: &E, flags: &mut ElementSelectorFlags)
- -> bool where E: Element {
- flags.insert(HAS_EDGE_CHILD_SELECTOR);
+fn matches_last_child<E, F>(element: &E, flags_setter: &mut F) -> bool
+ where E: Element,
+ F: FnMut(&E, ElementSelectorFlags),
+{
+ flags_setter(element, HAS_EDGE_CHILD_SELECTOR);
element.next_sibling_element().is_none()
}
--- a/servo/components/selectors/tree.rs
+++ b/servo/components/selectors/tree.rs
@@ -134,20 +134,21 @@ pub trait Element: MatchAttr + Sized {
// Skips non-element nodes
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(&self,
- pc: &<Self::Impl as SelectorImpl>::NonTSPseudoClass,
- relations: &mut StyleRelations,
- flags: &mut ElementSelectorFlags) -> bool;
+ fn match_non_ts_pseudo_class<F>(&self,
+ pc: &<Self::Impl as SelectorImpl>::NonTSPseudoClass,
+ relations: &mut StyleRelations,
+ 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`.
///
/// That is, whether it does not contain any child element or any non-zero-length text node.
/// See http://dev.w3.org/csswg/selectors-3/#empty-pseudo
--- a/servo/components/style/gecko/wrapper.rs
+++ b/servo/components/style/gecko/wrapper.rs
@@ -657,20 +657,23 @@ impl<'le> ::selectors::Element for Gecko
}
fn get_namespace(&self) -> &WeakNamespace {
unsafe {
WeakNamespace::new(Gecko_Namespace(self.0))
}
}
- fn match_non_ts_pseudo_class(&self,
- pseudo_class: &NonTSPseudoClass,
- relations: &mut StyleRelations,
- flags: &mut ElementSelectorFlags) -> bool {
+ fn match_non_ts_pseudo_class<F>(&self,
+ pseudo_class: &NonTSPseudoClass,
+ relations: &mut StyleRelations,
+ flags_setter: &mut F)
+ -> bool
+ where F: FnMut(&Self, ElementSelectorFlags),
+ {
match *pseudo_class {
// https://github.com/servo/servo/issues/8718
NonTSPseudoClass::AnyLink => unsafe { Gecko_IsLink(self.0) },
NonTSPseudoClass::Link => unsafe { Gecko_IsUnvisitedLink(self.0) },
NonTSPseudoClass::Visited => unsafe { Gecko_IsVisitedLink(self.0) },
NonTSPseudoClass::Active |
NonTSPseudoClass::Focus |
NonTSPseudoClass::Hover |
@@ -699,17 +702,23 @@ impl<'le> ::selectors::Element for Gecko
}
NonTSPseudoClass::MozTableBorderNonzero |
NonTSPseudoClass::MozBrowserFrame => unsafe {
Gecko_MatchesElement(pseudo_class.to_gecko_pseudoclasstype().unwrap(), self.0)
},
NonTSPseudoClass::MozSystemMetric(_) => false,
NonTSPseudoClass::MozAny(ref sels) => {
- sels.iter().any(|s| matches_complex_selector(s, self, None, relations, flags))
+ sels.iter().any(|s| {
+ matches_complex_selector(s,
+ self,
+ None,
+ relations,
+ flags_setter)
+ })
}
}
}
fn get_id(&self) -> Option<Atom> {
let ptr = unsafe {
bindings::Gecko_AtomAttrValue(self.0,
atom!("id").as_ptr())
@@ -840,16 +849,16 @@ 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 ElementSelectorFlags::empty())
+ &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
@@ -13,17 +13,17 @@ use atomic_refcell::AtomicRefMut;
use cache::{LRUCache, LRUCacheMutIterator};
use cascade_info::CascadeInfo;
use context::{SequentialTask, SharedStyleContext, StyleContext};
use data::{ComputedStyle, ElementData, ElementStyles, RestyleData};
use dom::{AnimationRules, SendElement, TElement, TNode};
use properties::{CascadeFlags, ComputedValues, SHAREABLE, SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP, cascade};
use properties::longhands::display::computed_value as display;
use restyle_hints::{RESTYLE_STYLE_ATTRIBUTE, RestyleHint};
-use rule_tree::{CascadeLevel, StrongRuleNode};
+use rule_tree::{CascadeLevel, RuleTree, StrongRuleNode};
use selector_parser::{PseudoElement, RestyleDamage, SelectorImpl};
use selectors::MatchAttr;
use selectors::bloom::BloomFilter;
use selectors::matching::{ElementSelectorFlags, StyleRelations};
use selectors::matching::AFFECTED_BY_PSEUDO_ELEMENTS;
use servo_config::opts;
use sink::ForgetfulSink;
use std::collections::hash_map::Entry;
@@ -745,22 +745,22 @@ trait PrivateMatchMethods: TElement {
shared_context: &SharedStyleContext,
candidate: &mut StyleSharingCandidate<Self>)
-> Result<ComputedStyle, CacheMiss> {
let candidate_element = *candidate.element;
element_matches_candidate(self, candidate, &candidate_element, shared_context)
}
}
-fn compute_rule_node<E: TElement>(context: &StyleContext<E>,
+fn compute_rule_node<E: TElement>(rule_tree: &RuleTree,
applicable_declarations: &mut Vec<ApplicableDeclarationBlock>)
-> StrongRuleNode
{
let rules = applicable_declarations.drain(..).map(|d| (d.source, d.level));
- let rule_node = context.shared.stylist.rule_tree.insert_ordered_rules(rules);
+ let rule_node = rule_tree.insert_ordered_rules(rules);
rule_node
}
impl<E: TElement> PrivateMatchMethods for E {}
/// The public API that elements expose for selector matching.
pub trait MatchMethods : TElement {
/// Runs selector matching to (re)compute rule nodes for this element.
@@ -770,30 +770,85 @@ pub trait MatchMethods : TElement {
-> MatchResults
{
let mut applicable_declarations =
Vec::<ApplicableDeclarationBlock>::with_capacity(16);
let stylist = &context.shared.stylist;
let style_attribute = self.style_attribute();
let animation_rules = self.get_animation_rules(None);
- let mut flags = ElementSelectorFlags::empty();
let mut rule_nodes_changed = false;
+ // TODO(emilio): This is somewhat inefficient, because of a variety of
+ // reasons:
+ //
+ // * It doesn't coalesce flags.
+ // * It doesn't look at flags already sent in a task for the main
+ // thread to process.
+ // * It doesn't take advantage of us knowing that the traversal is
+ // sequential.
+ //
+ // I suspect (need to measure!) that we don't use to set flags on
+ // a lot of different elements, but we could end up posting the same
+ // flag over and over with this approach.
+ //
+ // If the number of elements is low, perhaps a small cache with the
+ // flags already sent would be appropriate.
+ //
+ // The sequential task business for this is kind of sad :(.
+ //
+ // Anyway, let's do the obvious thing for now.
+ let tasks = &mut context.thread_local.tasks;
+ let mut set_selector_flags = |element: &Self, flags: ElementSelectorFlags| {
+ // Apply the selector flags.
+ let self_flags = flags.for_self();
+ if !self_flags.is_empty() {
+ if element == self {
+ unsafe { element.set_selector_flags(self_flags); }
+ } else {
+ if !element.has_selector_flags(self_flags) {
+ let task =
+ SequentialTask::set_selector_flags(element.clone(),
+ self_flags);
+ tasks.push(task);
+ }
+ }
+ }
+ let parent_flags = flags.for_parent();
+ if !parent_flags.is_empty() {
+ if let Some(p) = element.parent_element() {
+ // Avoid the overhead of the SequentialTask if the flags are
+ // already set.
+ if !p.has_selector_flags(parent_flags) {
+ let task = SequentialTask::set_selector_flags(p, parent_flags);
+ tasks.push(task);
+ }
+ }
+ }
+ };
+
+ // Borrow the stuff we need here so the borrow checker doesn't get mad
+ // at us later in the closure.
+ let guards = &context.shared.guards;
+ let rule_tree = &context.shared.stylist.rule_tree;
+ let bloom_filter = context.thread_local.bloom_filter.filter();
+
// Compute the primary rule node.
let mut primary_relations =
stylist.push_applicable_declarations(self,
- Some(context.thread_local.bloom_filter.filter()),
+ Some(bloom_filter),
style_attribute,
animation_rules,
None,
- &context.shared.guards,
+ guards,
&mut applicable_declarations,
- &mut flags);
- let primary_rule_node = compute_rule_node(context, &mut applicable_declarations);
+ &mut set_selector_flags);
+
+ let primary_rule_node =
+ compute_rule_node::<Self>(rule_tree, &mut applicable_declarations);
if !data.has_styles() {
data.set_styles(ElementStyles::new(ComputedStyle::new_partial(primary_rule_node)));
rule_nodes_changed = true;
} else if data.styles().primary.rules != primary_rule_node {
data.styles_mut().primary.rules = primary_rule_node;
rule_nodes_changed = true;
}
@@ -803,25 +858,26 @@ pub trait MatchMethods : TElement {
let mut per_pseudo = &mut data.styles_mut().pseudos;
debug_assert!(applicable_declarations.is_empty());
let pseudo_animation_rules = if <Self as MatchAttr>::Impl::pseudo_is_before_or_after(&pseudo) {
self.get_animation_rules(Some(&pseudo))
} else {
AnimationRules(None, None)
};
stylist.push_applicable_declarations(self,
- Some(context.thread_local.bloom_filter.filter()),
+ Some(bloom_filter),
None, pseudo_animation_rules,
Some(&pseudo),
- &context.shared.guards,
+ &guards,
&mut applicable_declarations,
- &mut flags);
+ &mut set_selector_flags);
if !applicable_declarations.is_empty() {
- let new_rules = compute_rule_node(context, &mut applicable_declarations);
+ let new_rules =
+ compute_rule_node::<Self>(rule_tree, &mut applicable_declarations);
match per_pseudo.entry(pseudo) {
Entry::Occupied(mut e) => {
if e.get().rules != new_rules {
e.get_mut().rules = new_rules;
rule_nodes_changed = true;
}
},
Entry::Vacant(e) => {
@@ -843,32 +899,16 @@ pub trait MatchMethods : TElement {
}
}
// If we have any pseudo elements, indicate so in the primary StyleRelations.
if !data.styles().pseudos.is_empty() {
primary_relations |= AFFECTED_BY_PSEUDO_ELEMENTS;
}
- // Apply the selector flags.
- let self_flags = flags.for_self();
- if !self_flags.is_empty() {
- unsafe { self.set_selector_flags(self_flags); }
- }
- let parent_flags = flags.for_parent();
- if !parent_flags.is_empty() {
- if let Some(p) = self.parent_element() {
- // Avoid the overhead of the SequentialTask if the flags are already set.
- if !p.has_selector_flags(parent_flags) {
- let task = SequentialTask::set_selector_flags(p, parent_flags);
- context.thread_local.tasks.push(task);
- }
- }
- }
-
MatchResults {
primary_relations: Some(primary_relations),
rule_nodes_changed: rule_nodes_changed,
}
}
/// Updates the rule nodes without re-running selector matching, using just
/// the rule tree. Returns true if the rule nodes changed.
--- a/servo/components/style/restyle_hints.rs
+++ b/servo/components/style/restyle_hints.rs
@@ -264,27 +264,35 @@ impl<'a, E> MatchAttr for ElementWrapper
_ => self.element.match_attr_suffix(attr, value)
}
}
}
impl<'a, E> Element for ElementWrapper<'a, E>
where E: TElement,
{
- fn match_non_ts_pseudo_class(&self,
- pseudo_class: &NonTSPseudoClass,
- relations: &mut StyleRelations,
- flags: &mut ElementSelectorFlags) -> bool {
+ fn match_non_ts_pseudo_class<F>(&self,
+ pseudo_class: &NonTSPseudoClass,
+ relations: &mut StyleRelations,
+ _: &mut F)
+ -> bool
+ where F: FnMut(&Self, ElementSelectorFlags),
+ {
let flag = SelectorImpl::pseudo_class_state_flag(pseudo_class);
- if flag == ElementState::empty() {
- self.element.match_non_ts_pseudo_class(pseudo_class, relations, flags)
- } else {
- match self.snapshot.and_then(|s| s.state()) {
- Some(snapshot_state) => snapshot_state.contains(flag),
- _ => self.element.match_non_ts_pseudo_class(pseudo_class, relations, flags)
+ if flag.is_empty() {
+ return self.element.match_non_ts_pseudo_class(pseudo_class,
+ relations,
+ &mut |_, _| {})
+ }
+ match self.snapshot.and_then(|s| s.state()) {
+ Some(snapshot_state) => snapshot_state.contains(flag),
+ None => {
+ self.element.match_non_ts_pseudo_class(pseudo_class,
+ relations,
+ &mut |_, _| {})
}
}
}
fn parent_element(&self) -> Option<Self> {
self.element.parent_element().map(ElementWrapper::new)
}
@@ -576,21 +584,21 @@ impl DependencySet {
(attrs_changed && dep.sensitivities.attrs),
"Testing a known ineffective dependency?");
if (attrs_changed || state_changes.intersects(dep.sensitivities.states)) && !hint.intersects(dep.hint) {
// 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_complex_selector(&dep.selector, snapshot, None,
&mut StyleRelations::empty(),
- &mut ElementSelectorFlags::empty());
+ &mut |_, _| {});
let matches_now =
matches_complex_selector(&dep.selector, element, None,
&mut StyleRelations::empty(),
- &mut ElementSelectorFlags::empty());
+ &mut |_, _| {});
if matched_then != matches_now {
hint.insert(dep.hint);
}
if hint.is_all() {
break;
}
}
}
--- a/servo/components/style/stylist.rs
+++ b/servo/components/style/stylist.rs
@@ -393,25 +393,49 @@ impl Stylist {
{
debug_assert!(SelectorImpl::pseudo_element_cascade_type(pseudo).is_lazy());
if self.pseudos_map.get(pseudo).is_none() {
return None;
}
let mut declarations = vec![];
- let mut flags = ElementSelectorFlags::empty();
+ // Apply the selector flags. We should be in sequential mode
+ // already, so we can directly apply the parent flags.
+ let mut set_selector_flags = |element: &E, flags: ElementSelectorFlags| {
+ if cfg!(feature = "servo") {
+ // Servo calls this function from the worker, but only for internal
+ // pseudos, so we should never generate selector flags here.
+ unreachable!("internal pseudo generated slow selector flags?");
+ }
+
+ // Gecko calls this from sequential mode, so we can directly apply
+ // the flags.
+ debug_assert!(thread_state::get() == thread_state::LAYOUT);
+ let self_flags = flags.for_self();
+ if !self_flags.is_empty() {
+ unsafe { element.set_selector_flags(self_flags); }
+ }
+ 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); }
+ }
+ }
+ };
+
+
self.push_applicable_declarations(element,
None,
None,
AnimationRules(None, None),
Some(pseudo),
guards,
&mut declarations,
- &mut flags);
+ &mut set_selector_flags);
let rule_node =
self.rule_tree.insert_ordered_rules(
declarations.into_iter().map(|a| (a.source, a.level)));
// Read the comment on `precomputed_values_for_pseudo` to see why it's
// difficult to assert that display: contents nodes never arrive here
// (tl;dr: It doesn't apply for replaced elements and such, but the
@@ -421,38 +445,16 @@ impl Stylist {
&rule_node,
guards,
Some(&**parent),
Some(&**parent),
None,
&StdoutErrorReporter,
CascadeFlags::empty());
- // Apply the selector flags. We should be in sequential mode already,
- // so we can directly apply the parent flags.
- if cfg!(feature = "servo") {
- // Servo calls this function from the worker, but only for internal
- // pseudos, so we should never generate selector flags here.
- debug_assert!(flags.is_empty());
- } else {
- // Gecko calls this from sequential mode, so we can directly apply
- // the flags.
- debug_assert!(thread_state::get() == thread_state::LAYOUT);
- let self_flags = flags.for_self();
- if !self_flags.is_empty() {
- unsafe { element.set_selector_flags(self_flags); }
- }
- 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); }
- }
- }
- }
-
Some(ComputedStyle::new(rule_node, Arc::new(computed)))
}
/// Set a given device, which may change the styles that apply to the
/// document.
///
/// This means that we may need to rebuild style data even if the
/// stylesheets haven't changed.
@@ -532,30 +534,32 @@ impl Stylist {
}
/// 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.
- pub fn push_applicable_declarations<E, V>(
+ pub fn push_applicable_declarations<E, V, F>(
&self,
element: &E,
parent_bf: Option<&BloomFilter>,
style_attribute: Option<&Arc<Locked<PropertyDeclarationBlock>>>,
animation_rules: AnimationRules,
pseudo_element: Option<&PseudoElement>,
guards: &StylesheetGuards,
applicable_declarations: &mut V,
- flags: &mut ElementSelectorFlags) -> StyleRelations
+ flags_setter: &mut F)
+ -> StyleRelations
where E: TElement +
fmt::Debug +
PresentationalHintsSynthetizer,
- V: Push<ApplicableDeclarationBlock> + VecLike<ApplicableDeclarationBlock>
+ V: Push<ApplicableDeclarationBlock> + VecLike<ApplicableDeclarationBlock>,
+ F: FnMut(&E, ElementSelectorFlags),
{
debug_assert!(!self.is_device_dirty);
debug_assert!(style_attribute.is_none() || pseudo_element.is_none(),
"Style attributes do not apply to pseudo-elements");
debug_assert!(pseudo_element.is_none() ||
!SelectorImpl::pseudo_element_cascade_type(pseudo_element.as_ref().unwrap())
.is_precomputed());
@@ -568,17 +572,17 @@ impl Stylist {
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,
parent_bf,
guards.ua_or_user,
applicable_declarations,
&mut relations,
- flags,
+ flags_setter,
CascadeLevel::UANormal);
debug!("UA normal: {:?}", relations);
// 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) {
@@ -593,25 +597,25 @@ impl Stylist {
if element.matches_user_and_author_rules() {
// Step 3: User and author normal rules.
map.user.get_all_matching_rules(element,
parent_bf,
guards.ua_or_user,
applicable_declarations,
&mut relations,
- flags,
+ flags_setter,
CascadeLevel::UserNormal);
debug!("user normal: {:?}", relations);
map.author.get_all_matching_rules(element,
parent_bf,
guards.author,
applicable_declarations,
&mut relations,
- flags,
+ flags_setter,
CascadeLevel::AuthorNormal);
debug!("author normal: {:?}", relations);
// Step 4: Normal style attributes.
if let Some(sa) = style_attribute {
if sa.read_with(guards.author).any_normal() {
relations |= AFFECTED_BY_STYLE_ATTRIBUTE;
Push::push(
@@ -636,17 +640,17 @@ impl Stylist {
debug!("animation: {:?}", relations);
// Step 6: Author-supplied `!important` rules.
map.author.get_all_matching_rules(element,
parent_bf,
guards.author,
applicable_declarations,
&mut relations,
- flags,
+ flags_setter,
CascadeLevel::AuthorImportant);
debug!("author important: {:?}", relations);
// Step 7: `!important` style attributes.
if let Some(sa) = style_attribute {
if sa.read_with(guards.author).any_important() {
relations |= AFFECTED_BY_STYLE_ATTRIBUTE;
@@ -660,31 +664,31 @@ impl Stylist {
debug!("style attr important: {:?}", relations);
// Step 8: User `!important` rules.
map.user.get_all_matching_rules(element,
parent_bf,
guards.ua_or_user,
applicable_declarations,
&mut relations,
- flags,
+ flags_setter,
CascadeLevel::UserImportant);
debug!("user important: {:?}", relations);
} else {
debug!("skipping non-agent rules");
}
// Step 9: UA `!important` rules.
map.user_agent.get_all_matching_rules(element,
parent_bf,
guards.ua_or_user,
applicable_declarations,
&mut relations,
- flags,
+ flags_setter,
CascadeLevel::UAImportant);
debug!("UA important: {:?}", relations);
// Step 10: Transitions.
// The transitions sheet (CSS transitions that are tied to CSS markup)
if let Some(anim) = animation_rules.1 {
relations |= AFFECTED_BY_TRANSITIONS;
@@ -730,21 +734,21 @@ impl Stylist {
// the same.
//
// TODO(emilio): Use the bloom filter, since they contain the element's
// ancestor chain and it's correct for the candidate too.
for ref selector in self.non_common_style_affecting_attributes_selectors.iter() {
let element_matches =
matches_complex_selector(&selector.complex_selector, element,
None, &mut StyleRelations::empty(),
- &mut ElementSelectorFlags::empty());
+ &mut |_, _| {});
let candidate_matches =
matches_complex_selector(&selector.complex_selector, candidate,
None, &mut StyleRelations::empty(),
- &mut ElementSelectorFlags::empty());
+ &mut |_, _| {});
if element_matches != candidate_matches {
return false;
}
}
true
}
@@ -769,22 +773,22 @@ impl Stylist {
// know what rules it matches.
//
// TODO(emilio): Use the bloom filter, since they contain the element's
// ancestor chain and it's correct for the candidate too.
for ref selector in self.sibling_affecting_selectors.iter() {
let element_matches =
matches_complex_selector(&selector.complex_selector, element,
None, &mut StyleRelations::empty(),
- &mut ElementSelectorFlags::empty());
+ &mut |_, _| {});
let candidate_matches =
matches_complex_selector(&selector.complex_selector, candidate,
None, &mut StyleRelations::empty(),
- &mut ElementSelectorFlags::empty());
+ &mut |_, _| {});
if element_matches != candidate_matches {
debug!("match_same_sibling_affecting_rules: Failure due to {:?}",
selector.complex_selector);
return false;
}
}
@@ -907,79 +911,80 @@ impl SelectorMap {
empty: true,
}
}
/// Append to `rule_list` all Rules in `self` that match element.
///
/// 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>(&self,
- element: &E,
- parent_bf: Option<&BloomFilter>,
- guard: &SharedRwLockReadGuard,
- matching_rules_list: &mut V,
- relations: &mut StyleRelations,
- flags: &mut ElementSelectorFlags,
- cascade_level: CascadeLevel)
+ pub fn get_all_matching_rules<E, V, F>(&self,
+ element: &E,
+ parent_bf: Option<&BloomFilter>,
+ guard: &SharedRwLockReadGuard,
+ matching_rules_list: &mut V,
+ relations: &mut StyleRelations,
+ flags_setter: &mut F,
+ cascade_level: CascadeLevel)
where E: Element<Impl=SelectorImpl>,
- V: VecLike<ApplicableDeclarationBlock>
+ V: VecLike<ApplicableDeclarationBlock>,
+ F: FnMut(&E, ElementSelectorFlags),
{
if self.empty {
return
}
// 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() {
SelectorMap::get_matching_rules_from_hash(element,
parent_bf,
&self.id_hash,
&id,
guard,
matching_rules_list,
relations,
- flags,
+ flags_setter,
cascade_level)
}
element.each_class(|class| {
SelectorMap::get_matching_rules_from_hash(element,
parent_bf,
&self.class_hash,
class,
guard,
matching_rules_list,
relations,
- flags,
+ flags_setter,
cascade_level);
});
let local_name_hash = if element.is_html_element_in_html_document() {
&self.lower_local_name_hash
} else {
&self.local_name_hash
};
SelectorMap::get_matching_rules_from_hash(element,
parent_bf,
local_name_hash,
element.get_local_name(),
guard,
matching_rules_list,
relations,
- flags,
+ flags_setter,
cascade_level);
SelectorMap::get_matching_rules(element,
parent_bf,
&self.other_rules,
guard,
matching_rules_list,
relations,
- flags,
+ 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));
}
/// Append to `rule_list` all universal Rules (rules with selector `*|*`) in
@@ -1022,66 +1027,68 @@ impl SelectorMap {
sort_by_key(&mut matching_rules_list[0..normal_len],
|block| (block.specificity, block.source_order));
sort_by_key(&mut matching_rules_list[normal_len..],
|block| (block.specificity, block.source_order));
matching_rules_list
}
- fn get_matching_rules_from_hash<E, Str, BorrowedStr: ?Sized, Vector>(
+ fn get_matching_rules_from_hash<E, Str, BorrowedStr: ?Sized, Vector, F>(
element: &E,
parent_bf: Option<&BloomFilter>,
hash: &FnvHashMap<Str, Vec<Rule>>,
key: &BorrowedStr,
guard: &SharedRwLockReadGuard,
matching_rules: &mut Vector,
relations: &mut StyleRelations,
- flags: &mut ElementSelectorFlags,
+ flags_setter: &mut F,
cascade_level: CascadeLevel)
where E: Element<Impl=SelectorImpl>,
Str: Borrow<BorrowedStr> + Eq + Hash,
BorrowedStr: Eq + Hash,
- Vector: VecLike<ApplicableDeclarationBlock>
+ Vector: VecLike<ApplicableDeclarationBlock>,
+ F: FnMut(&E, ElementSelectorFlags),
{
if let Some(rules) = hash.get(key) {
SelectorMap::get_matching_rules(element,
parent_bf,
rules,
guard,
matching_rules,
relations,
- flags,
+ flags_setter,
cascade_level)
}
}
/// Adds rules in `rules` that match `element` to the `matching_rules` list.
- fn get_matching_rules<E, V>(element: &E,
- parent_bf: Option<&BloomFilter>,
- rules: &[Rule],
- guard: &SharedRwLockReadGuard,
- matching_rules: &mut V,
- relations: &mut StyleRelations,
- flags: &mut ElementSelectorFlags,
- cascade_level: CascadeLevel)
+ fn get_matching_rules<E, V, F>(element: &E,
+ parent_bf: Option<&BloomFilter>,
+ rules: &[Rule],
+ guard: &SharedRwLockReadGuard,
+ matching_rules: &mut V,
+ relations: &mut StyleRelations,
+ flags_setter: &mut F,
+ cascade_level: CascadeLevel)
where E: Element<Impl=SelectorImpl>,
- V: VecLike<ApplicableDeclarationBlock>
+ V: VecLike<ApplicableDeclarationBlock>,
+ F: FnMut(&E, ElementSelectorFlags),
{
for rule in rules.iter() {
let style_rule = rule.style_rule.read_with(guard);
let block = style_rule.block.read_with(guard);
let any_declaration_for_importance = if cascade_level.is_important() {
block.any_important()
} else {
block.any_normal()
};
if any_declaration_for_importance &&
matches_complex_selector(&*rule.selector, element, parent_bf,
- relations, flags) {
+ relations, flags_setter) {
matching_rules.push(
rule.to_applicable_declaration_block(cascade_level));
}
}
}
/// Insert rule into the correct hash.
/// Order in which to try: id_hash, class_hash, local_name_hash, other_rules.
--- a/servo/ports/geckolib/Cargo.toml
+++ b/servo/ports/geckolib/Cargo.toml
@@ -12,17 +12,16 @@ crate-type = ["staticlib", "rlib"]
[features]
bindgen = ["style/use_bindgen"]
testing = ["style/testing"]
[dependencies]
atomic_refcell = "0.1"
cssparser = "0.12"
env_logger = {version = "0.4", default-features = false} # disable `regex` to reduce code size
-lazy_static = "0.2"
libc = "0.2"
log = {version = "0.3.5", features = ["release_max_level_info"]}
parking_lot = "0.3"
selectors = {path = "../../components/selectors"}
servo_url = {path = "../../components/url"}
style = {path = "../../components/style", features = ["gecko"]}
style_traits = {path = "../../components/style_traits"}
--- a/servo/ports/geckolib/lib.rs
+++ b/servo/ports/geckolib/lib.rs
@@ -2,17 +2,16 @@
* 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/. */
#![deny(warnings)]
extern crate atomic_refcell;
extern crate cssparser;
extern crate env_logger;
-#[macro_use] extern crate lazy_static;
extern crate libc;
#[macro_use] extern crate log;
extern crate parking_lot;
extern crate selectors;
extern crate servo_url;
#[macro_use] extern crate style;
extern crate style_traits;
--- a/toolkit/library/gtest/rust/Cargo.lock
+++ b/toolkit/library/gtest/rust/Cargo.lock
@@ -297,17 +297,16 @@ dependencies = [
[[package]]
name = "geckoservo"
version = "0.0.1"
dependencies = [
"atomic_refcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cssparser 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "lazy_static 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
"selectors 0.18.0",
"servo_url 0.0.1",
"style 0.0.1",
"style_traits 0.0.1",
]
--- a/toolkit/library/rust/Cargo.lock
+++ b/toolkit/library/rust/Cargo.lock
@@ -295,17 +295,16 @@ dependencies = [
[[package]]
name = "geckoservo"
version = "0.0.1"
dependencies = [
"atomic_refcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cssparser 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "lazy_static 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
"selectors 0.18.0",
"servo_url 0.0.1",
"style 0.0.1",
"style_traits 0.0.1",
]