style: Fix dynamic changes of attributes when combined with :not.

This commit is contained in:
Emilio Cobos Álvarez 2017-04-12 12:45:41 +08:00
parent 8298ee75c7
commit 568fa4cc0d
No known key found for this signature in database
GPG key ID: 056B727BB9C1027C
7 changed files with 116 additions and 33 deletions

View file

@ -184,6 +184,9 @@ impl<Impl: SelectorImpl> SelectorMethods for SimpleSelector<Impl> {
where V: SelectorVisitor<Impl = Impl>,
{
use self::SimpleSelector::*;
if !visitor.visit_simple_selector(self) {
return false;
}
match *self {
Negation(ref negated) => {

View file

@ -6,7 +6,7 @@
#![deny(missing_docs)]
use parser::{AttrSelector, Combinator, ComplexSelector, SelectorImpl};
use parser::{AttrSelector, Combinator, ComplexSelector, SelectorImpl, SimpleSelector};
use std::sync::Arc;
/// A trait to visit selector properties.
@ -24,6 +24,11 @@ pub trait SelectorVisitor {
true
}
/// Visit a simple selector.
fn visit_simple_selector(&mut self, _: &SimpleSelector<Self::Impl>) -> bool {
true
}
/// Visits a complex selector.
///
/// Gets the combinator to the right of the selector, or `None` if the

View file

@ -8,7 +8,6 @@ use cssparser::{Parser, ToCss};
use element_state::ElementState;
use gecko_bindings::structs::CSSPseudoClassType;
use gecko_bindings::structs::nsIAtom;
use restyle_hints::complex_selector_to_state;
use selector_parser::{SelectorParser, PseudoElementCascadeType};
use selectors::parser::{ComplexSelector, SelectorMethods};
use selectors::visitor::SelectorVisitor;
@ -313,11 +312,7 @@ impl NonTSPseudoClass {
match *self {
$(NonTSPseudoClass::$name => flag!($state),)*
$(NonTSPseudoClass::$s_name(..) => flag!($s_state),)*
NonTSPseudoClass::MozAny(ref selectors) => {
selectors.iter().fold(ElementState::empty(), |state, s| {
state | complex_selector_to_state(s)
})
}
NonTSPseudoClass::MozAny(..) => ElementState::empty(),
}
}
}

View file

@ -17,7 +17,7 @@ use selector_parser::{AttrValue, NonTSPseudoClass, Snapshot, SelectorImpl};
use selectors::{Element, MatchAttr};
use selectors::matching::{ElementSelectorFlags, StyleRelations};
use selectors::matching::matches_complex_selector;
use selectors::parser::{AttrSelector, Combinator, ComplexSelector, SimpleSelector};
use selectors::parser::{AttrSelector, Combinator, ComplexSelector, SelectorMethods, SimpleSelector};
use selectors::visitor::SelectorVisitor;
use std::clone::Clone;
use std::sync::Arc;
@ -283,6 +283,21 @@ impl<'a, E> Element for ElementWrapper<'a, E>
-> bool
where F: FnMut(&Self, ElementSelectorFlags),
{
// :moz-any is quite special, because we need to keep matching as a
// snapshot.
#[cfg(feature = "gecko")]
{
if let NonTSPseudoClass::MozAny(ref selectors) = *pseudo_class {
return selectors.iter().any(|s| {
matches_complex_selector(s,
self,
None,
relations,
setter)
})
}
}
let flag = SelectorImpl::pseudo_class_state_flag(pseudo_class);
if flag.is_empty() {
return self.element.match_non_ts_pseudo_class(pseudo_class,
@ -365,22 +380,9 @@ impl<'a, E> Element for ElementWrapper<'a, E>
}
}
/// Returns the union of any `ElementState` flags for components of a
/// `ComplexSelector`.
pub fn complex_selector_to_state(sel: &ComplexSelector<SelectorImpl>) -> ElementState {
sel.compound_selector.iter().fold(ElementState::empty(), |state, s| {
state | selector_to_state(s)
})
}
fn selector_to_state(sel: &SimpleSelector<SelectorImpl>) -> ElementState {
match *sel {
SimpleSelector::NonTSPseudoClass(ref pc) => SelectorImpl::pseudo_class_state_flag(pc),
SimpleSelector::Negation(ref negated) => {
negated.iter().fold(ElementState::empty(), |state, s| {
state | complex_selector_to_state(s)
})
}
_ => ElementState::empty(),
}
}
@ -483,10 +485,35 @@ struct Dependency {
sensitivities: Sensitivities,
}
/// The following visitor visits all the simple selectors for a given complex
/// selector, taking care of :not and :any combinators, collecting whether any
/// of them is sensitive to attribute or state changes.
struct SensitivitiesVisitor {
sensitivities: Sensitivities,
}
impl SelectorVisitor for SensitivitiesVisitor {
type Impl = SelectorImpl;
fn visit_simple_selector(&mut self, s: &SimpleSelector<SelectorImpl>) -> bool {
self.sensitivities.states.insert(selector_to_state(s));
if !self.sensitivities.attrs {
self.sensitivities.attrs = is_attr_selector(s);
}
true
}
}
/// A visitor struct that collects information for a given selector.
///
/// This is the struct responsible of adding dependencies for a given complex
/// selector.
/// selector and all the selectors to its left.
///
/// This uses a `SensitivitiesVisitor` internally to collect all the
/// dependencies inside the given complex selector.
pub struct SelectorDependencyVisitor<'a> {
dependency_set: &'a mut DependencySet,
needs_cache_revalidation: bool,
@ -511,32 +538,37 @@ impl<'a> SelectorDependencyVisitor<'a> {
impl<'a> SelectorVisitor for SelectorDependencyVisitor<'a> {
type Impl = SelectorImpl;
fn visit_simple_selector(&mut self, s: &SimpleSelector<SelectorImpl>) -> bool {
if !self.needs_cache_revalidation {
self.needs_cache_revalidation = needs_cache_revalidation(s);
}
true
}
fn visit_complex_selector(&mut self,
selector: &Arc<ComplexSelector<SelectorImpl>>,
combinator: Option<Combinator>)
-> bool
{
let mut sensitivities = Sensitivities::new();
let mut sensitivity_visitor = SensitivitiesVisitor {
sensitivities: Sensitivities::new(),
};
for s in &selector.compound_selector {
sensitivities.states.insert(selector_to_state(s));
if !self.needs_cache_revalidation {
self.needs_cache_revalidation = needs_cache_revalidation(s);
}
if !sensitivities.attrs {
sensitivities.attrs = is_attr_selector(s);
}
s.visit(&mut sensitivity_visitor);
}
let hint = combinator_to_restyle_hint(combinator);
self.needs_cache_revalidation |= sensitivities.attrs;
self.needs_cache_revalidation |= sensitivity_visitor.sensitivities.attrs;
self.needs_cache_revalidation |= hint.intersects(RESTYLE_LATER_SIBLINGS);
if !sensitivities.is_empty() {
if !sensitivity_visitor.sensitivities.is_empty() {
self.dependency_set.add_dependency(Dependency {
selector: selector.clone(),
hint: hint,
sensitivities: sensitivities,
sensitivities: sensitivity_visitor.sensitivities,
});
}

View file

@ -3743,6 +3743,18 @@
{}
]
],
"css/negation-attr-dependence.html": [
[
"/_mozilla/css/negation-attr-dependence.html",
[
[
"/_mozilla/css/negation-attr-dependence-ref.html",
"=="
]
],
{}
]
],
"css/negative-calc-cv.html": [
[
"/_mozilla/css/negative-calc-cv.html",
@ -8299,6 +8311,11 @@
{}
]
],
"css/negation-attr-dependence-ref.html": [
[
{}
]
],
"css/negative-calc-cv-ref.html": [
[
{}
@ -22501,6 +22518,14 @@
"a82c94a9a4a18cda6009e467993cace8babe7591",
"support"
],
"css/negation-attr-dependence-ref.html": [
"560c5d8b523f6d4cad6e15e5604f51646647d487",
"support"
],
"css/negation-attr-dependence.html": [
"27bec4af590a2e6d5e88b54ed37fd254ef0f0a99",
"reftest"
],
"css/negative-calc-cv-ref.html": [
"a42f34470ead45d5865562618f6538fde263a359",
"support"

View file

@ -0,0 +1,8 @@
<!doctype html>
<meta charset="utf-8">
<title>CSS Test Reference</title>
<style>
.foo + div { background: green; width: 100px; height: 100px; }
</style>
<div class="foo"></div>
<div>Should be green</div>

View file

@ -0,0 +1,15 @@
<!doctype html>
<meta charset="utf-8">
<title>CSS Test: Negation with attribute dependence correctly restyle siblings.</title>
<link rel="match" href="negation-attr-dependence-ref.html">
<style>
:not(.foo) + div { background: green; width: 100px; height: 100px; }
</style>
<link rel="matches" href="negation-attr-dependence-ref.html">
<div class="foo"></div>
<div>Should be green</div>
<script>
window.onload = function() {
document.querySelector('div').className = "";
}
</script>