mirror of
https://github.com/servo/servo.git
synced 2025-08-06 14:10:11 +01:00
style: Fix dynamic changes of attributes when combined with :not.
This commit is contained in:
parent
8298ee75c7
commit
568fa4cc0d
7 changed files with 116 additions and 33 deletions
|
@ -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) => {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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>
|
15
tests/wpt/mozilla/tests/css/negation-attr-dependence.html
Normal file
15
tests/wpt/mozilla/tests/css/negation-attr-dependence.html
Normal 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>
|
Loading…
Add table
Add a link
Reference in a new issue