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>,
|
where V: SelectorVisitor<Impl = Impl>,
|
||||||
{
|
{
|
||||||
use self::SimpleSelector::*;
|
use self::SimpleSelector::*;
|
||||||
|
if !visitor.visit_simple_selector(self) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
match *self {
|
match *self {
|
||||||
Negation(ref negated) => {
|
Negation(ref negated) => {
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
#![deny(missing_docs)]
|
#![deny(missing_docs)]
|
||||||
|
|
||||||
use parser::{AttrSelector, Combinator, ComplexSelector, SelectorImpl};
|
use parser::{AttrSelector, Combinator, ComplexSelector, SelectorImpl, SimpleSelector};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
/// A trait to visit selector properties.
|
/// A trait to visit selector properties.
|
||||||
|
@ -24,6 +24,11 @@ pub trait SelectorVisitor {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Visit a simple selector.
|
||||||
|
fn visit_simple_selector(&mut self, _: &SimpleSelector<Self::Impl>) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
/// Visits a complex selector.
|
/// Visits a complex selector.
|
||||||
///
|
///
|
||||||
/// Gets the combinator to the right of the selector, or `None` if the
|
/// 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 element_state::ElementState;
|
||||||
use gecko_bindings::structs::CSSPseudoClassType;
|
use gecko_bindings::structs::CSSPseudoClassType;
|
||||||
use gecko_bindings::structs::nsIAtom;
|
use gecko_bindings::structs::nsIAtom;
|
||||||
use restyle_hints::complex_selector_to_state;
|
|
||||||
use selector_parser::{SelectorParser, PseudoElementCascadeType};
|
use selector_parser::{SelectorParser, PseudoElementCascadeType};
|
||||||
use selectors::parser::{ComplexSelector, SelectorMethods};
|
use selectors::parser::{ComplexSelector, SelectorMethods};
|
||||||
use selectors::visitor::SelectorVisitor;
|
use selectors::visitor::SelectorVisitor;
|
||||||
|
@ -313,11 +312,7 @@ impl NonTSPseudoClass {
|
||||||
match *self {
|
match *self {
|
||||||
$(NonTSPseudoClass::$name => flag!($state),)*
|
$(NonTSPseudoClass::$name => flag!($state),)*
|
||||||
$(NonTSPseudoClass::$s_name(..) => flag!($s_state),)*
|
$(NonTSPseudoClass::$s_name(..) => flag!($s_state),)*
|
||||||
NonTSPseudoClass::MozAny(ref selectors) => {
|
NonTSPseudoClass::MozAny(..) => ElementState::empty(),
|
||||||
selectors.iter().fold(ElementState::empty(), |state, s| {
|
|
||||||
state | complex_selector_to_state(s)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ use selector_parser::{AttrValue, NonTSPseudoClass, Snapshot, SelectorImpl};
|
||||||
use selectors::{Element, MatchAttr};
|
use selectors::{Element, MatchAttr};
|
||||||
use selectors::matching::{ElementSelectorFlags, StyleRelations};
|
use selectors::matching::{ElementSelectorFlags, StyleRelations};
|
||||||
use selectors::matching::matches_complex_selector;
|
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 selectors::visitor::SelectorVisitor;
|
||||||
use std::clone::Clone;
|
use std::clone::Clone;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
@ -283,6 +283,21 @@ impl<'a, E> Element for ElementWrapper<'a, E>
|
||||||
-> bool
|
-> bool
|
||||||
where F: FnMut(&Self, ElementSelectorFlags),
|
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);
|
let flag = SelectorImpl::pseudo_class_state_flag(pseudo_class);
|
||||||
if flag.is_empty() {
|
if flag.is_empty() {
|
||||||
return self.element.match_non_ts_pseudo_class(pseudo_class,
|
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 {
|
fn selector_to_state(sel: &SimpleSelector<SelectorImpl>) -> ElementState {
|
||||||
match *sel {
|
match *sel {
|
||||||
SimpleSelector::NonTSPseudoClass(ref pc) => SelectorImpl::pseudo_class_state_flag(pc),
|
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(),
|
_ => ElementState::empty(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -483,10 +485,35 @@ struct Dependency {
|
||||||
sensitivities: Sensitivities,
|
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.
|
/// A visitor struct that collects information for a given selector.
|
||||||
///
|
///
|
||||||
/// This is the struct responsible of adding dependencies for a given complex
|
/// 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> {
|
pub struct SelectorDependencyVisitor<'a> {
|
||||||
dependency_set: &'a mut DependencySet,
|
dependency_set: &'a mut DependencySet,
|
||||||
needs_cache_revalidation: bool,
|
needs_cache_revalidation: bool,
|
||||||
|
@ -511,32 +538,37 @@ impl<'a> SelectorDependencyVisitor<'a> {
|
||||||
impl<'a> SelectorVisitor for SelectorDependencyVisitor<'a> {
|
impl<'a> SelectorVisitor for SelectorDependencyVisitor<'a> {
|
||||||
type Impl = SelectorImpl;
|
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,
|
fn visit_complex_selector(&mut self,
|
||||||
selector: &Arc<ComplexSelector<SelectorImpl>>,
|
selector: &Arc<ComplexSelector<SelectorImpl>>,
|
||||||
combinator: Option<Combinator>)
|
combinator: Option<Combinator>)
|
||||||
-> bool
|
-> bool
|
||||||
{
|
{
|
||||||
let mut sensitivities = Sensitivities::new();
|
let mut sensitivity_visitor = SensitivitiesVisitor {
|
||||||
|
sensitivities: Sensitivities::new(),
|
||||||
|
};
|
||||||
|
|
||||||
for s in &selector.compound_selector {
|
for s in &selector.compound_selector {
|
||||||
sensitivities.states.insert(selector_to_state(s));
|
s.visit(&mut sensitivity_visitor);
|
||||||
if !self.needs_cache_revalidation {
|
|
||||||
self.needs_cache_revalidation = needs_cache_revalidation(s);
|
|
||||||
}
|
|
||||||
if !sensitivities.attrs {
|
|
||||||
sensitivities.attrs = is_attr_selector(s);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let hint = combinator_to_restyle_hint(combinator);
|
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);
|
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 {
|
self.dependency_set.add_dependency(Dependency {
|
||||||
selector: selector.clone(),
|
selector: selector.clone(),
|
||||||
hint: hint,
|
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": [
|
"css/negative-calc-cv.html": [
|
||||||
[
|
[
|
||||||
"/_mozilla/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": [
|
"css/negative-calc-cv-ref.html": [
|
||||||
[
|
[
|
||||||
{}
|
{}
|
||||||
|
@ -22501,6 +22518,14 @@
|
||||||
"a82c94a9a4a18cda6009e467993cace8babe7591",
|
"a82c94a9a4a18cda6009e467993cace8babe7591",
|
||||||
"support"
|
"support"
|
||||||
],
|
],
|
||||||
|
"css/negation-attr-dependence-ref.html": [
|
||||||
|
"560c5d8b523f6d4cad6e15e5604f51646647d487",
|
||||||
|
"support"
|
||||||
|
],
|
||||||
|
"css/negation-attr-dependence.html": [
|
||||||
|
"27bec4af590a2e6d5e88b54ed37fd254ef0f0a99",
|
||||||
|
"reftest"
|
||||||
|
],
|
||||||
"css/negative-calc-cv-ref.html": [
|
"css/negative-calc-cv-ref.html": [
|
||||||
"a42f34470ead45d5865562618f6538fde263a359",
|
"a42f34470ead45d5865562618f6538fde263a359",
|
||||||
"support"
|
"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