mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 05:11:16 +00:00
Bug 933562 - Implement complex :not(). r=jfkthame
This fixes the failures in bug 1671573
and just works thanks to the
invalidation improvements I did for :is / :where.
Added a couple tests for invalidation which is the tricky bit. 001 is a
very straight-forward test, 002 is the :is test but with :is() replaced
by double-:not().
This also fixes default namespaces inside :is() / :where(), which are
supposed to get ignored, but aren't. Added tests for that and for the
pre-existing :not() behavior which Chrome doesn't quite get right.
Differential Revision: https://phabricator.services.mozilla.com/D94142
This commit is contained in:
parent
e685a3df8c
commit
82e837eb83
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -4429,7 +4429,6 @@ dependencies = [
|
||||
"precomputed-hash",
|
||||
"servo_arc",
|
||||
"smallvec",
|
||||
"thin-slice",
|
||||
"to_shmem",
|
||||
"to_shmem_derive",
|
||||
]
|
||||
|
@ -196,10 +196,7 @@
|
||||
.plus .t1:hover + .unitTest + .unitTest { background-color: red; }
|
||||
]]></span>
|
||||
<span type="text/test" id="error">
|
||||
.blox16:not(.blox15[foo="blox14"]) { background-color: red; }
|
||||
|
||||
/* Tests from http://www.w3.org/Style/CSS/Test/CSS3/Selectors/20060307/html/index.html */
|
||||
div:not(:not(div)) { background-color : red }
|
||||
|
||||
div, { background: red; }
|
||||
.5cm { background: red; }
|
||||
|
@ -29,7 +29,6 @@ PEAttSelBadValue=Expected identifier or string for value in attribute selector b
|
||||
PEPseudoSelBadName=Expected identifier for pseudo-class or pseudo-element but found ‘%1$S’.
|
||||
PEPseudoSelEndOrUserActionPC=Expected end of selector or a user action pseudo-class after pseudo-element but found ‘%1$S’.
|
||||
PEPseudoSelUnknown=Unknown pseudo-class or pseudo-element ‘%1$S’.
|
||||
PENegationBadArg=Missing argument in negation pseudo-class ‘%1$S’.
|
||||
PEPseudoClassArgNotIdent=Expected identifier for pseudo-class parameter but found ‘%1$S’.
|
||||
PEColorNotColor=Expected color but found ‘%1$S’.
|
||||
PEParseDeclarationDeclExpected=Expected declaration but found ‘%1$S’.
|
||||
|
@ -78,7 +78,7 @@
|
||||
error: "Expected identifier for class selector but found ‘ ’. Ruleset ignored due to bad selector.",
|
||||
}, {
|
||||
css: ":not() {}",
|
||||
error: "Missing argument in negation pseudo-class ‘)’. Ruleset ignored due to bad selector.",
|
||||
error: "Selector expected. Ruleset ignored due to bad selector.",
|
||||
}, {
|
||||
css: "* { -webkit-text-size-adjust: 100% }",
|
||||
error: "Error in parsing value for ‘-webkit-text-size-adjust’. Declaration dropped.",
|
||||
|
@ -28,7 +28,6 @@ phf = "0.8"
|
||||
precomputed-hash = "0.1"
|
||||
servo_arc = { version = "0.1", path = "../servo_arc" }
|
||||
smallvec = "1.0"
|
||||
thin-slice = "0.1.0"
|
||||
to_shmem = { path = "../to_shmem" }
|
||||
to_shmem_derive = { path = "../to_shmem_derive" }
|
||||
|
||||
|
@ -326,7 +326,7 @@ where
|
||||
Component::NonTSPseudoClass(..) => {
|
||||
specificity.class_like_selectors += 1;
|
||||
},
|
||||
Component::Is(ref list) => {
|
||||
Component::Negation(ref list) | Component::Is(ref list) => {
|
||||
// https://drafts.csswg.org/selectors/#specificity-rules:
|
||||
//
|
||||
// The specificity of an :is() pseudo-class is replaced by the
|
||||
@ -346,11 +346,6 @@ where
|
||||
Component::Namespace(..) => {
|
||||
// Does not affect specificity
|
||||
},
|
||||
Component::Negation(ref negated) => {
|
||||
for ss in negated.iter() {
|
||||
simple_selector_specificity(&ss, specificity);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -238,10 +238,10 @@ where
|
||||
where
|
||||
F: FnOnce(&mut Self) -> R,
|
||||
{
|
||||
debug_assert!(!self.in_negation, "Someone messed up parsing?");
|
||||
let old_in_negation = self.in_negation;
|
||||
self.in_negation = true;
|
||||
let result = self.nest(f);
|
||||
self.in_negation = false;
|
||||
self.in_negation = old_in_negation;
|
||||
result
|
||||
}
|
||||
|
||||
|
@ -20,7 +20,6 @@ extern crate phf;
|
||||
extern crate precomputed_hash;
|
||||
extern crate servo_arc;
|
||||
extern crate smallvec;
|
||||
extern crate thin_slice;
|
||||
extern crate to_shmem;
|
||||
#[macro_use]
|
||||
extern crate to_shmem_derive;
|
||||
|
@ -853,14 +853,13 @@ where
|
||||
}
|
||||
false
|
||||
}),
|
||||
Component::Negation(ref negated) => context.shared.nest_for_negation(|context| {
|
||||
let mut local_context = LocalMatchingContext {
|
||||
matches_hover_and_active_quirk: MatchesHoverAndActiveQuirk::No,
|
||||
shared: context,
|
||||
};
|
||||
!negated
|
||||
.iter()
|
||||
.all(|ss| matches_simple_selector(ss, element, &mut local_context, flags_setter))
|
||||
Component::Negation(ref list) => context.shared.nest_for_negation(|context| {
|
||||
for selector in &**list {
|
||||
if matches_complex_selector(selector.iter(), element, context, flags_setter) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
@ -21,7 +21,6 @@ use std::borrow::{Borrow, Cow};
|
||||
use std::fmt::{self, Debug, Display, Write};
|
||||
use std::iter::Rev;
|
||||
use std::slice;
|
||||
use thin_slice::ThinBoxedSlice;
|
||||
|
||||
/// A trait that represents a pseudo-element.
|
||||
pub trait PseudoElement: Sized + ToCss {
|
||||
@ -76,9 +75,10 @@ fn to_ascii_lowercase(s: &str) -> Cow<str> {
|
||||
bitflags! {
|
||||
/// Flags that indicate at which point of parsing a selector are we.
|
||||
struct SelectorParsingState: u8 {
|
||||
/// Whether we're inside a negation. If we're inside a negation, we're
|
||||
/// not allowed to add another negation or such, for example.
|
||||
const INSIDE_NEGATION = 1 << 0;
|
||||
/// Whether we should avoid adding default namespaces to selectors that
|
||||
/// aren't type or universal selectors.
|
||||
const SKIP_DEFAULT_NAMESPACE = 1 << 0;
|
||||
|
||||
/// Whether we've parsed a ::slotted() pseudo-element already.
|
||||
///
|
||||
/// If so, then we can only parse a subset of pseudo-elements, and
|
||||
@ -162,7 +162,6 @@ pub enum SelectorParseErrorKind<'i> {
|
||||
NoQualifiedNameInAttributeSelector(Token<'i>),
|
||||
EmptySelector,
|
||||
DanglingCombinator,
|
||||
NonSimpleSelectorInNegation,
|
||||
NonCompoundSelector,
|
||||
NonPseudoElementAfterSlotted,
|
||||
InvalidPseudoElementAfterSlotted,
|
||||
@ -180,7 +179,6 @@ pub enum SelectorParseErrorKind<'i> {
|
||||
InvalidQualNameInAttr(Token<'i>),
|
||||
ExplicitNamespaceUnexpectedToken(Token<'i>),
|
||||
ClassNeedsIdent(Token<'i>),
|
||||
EmptyNegation,
|
||||
}
|
||||
|
||||
macro_rules! with_all_bounds {
|
||||
@ -1036,16 +1034,7 @@ pub enum Component<Impl: SelectorImpl> {
|
||||
AttributeOther(Box<AttrSelectorWithOptionalNamespace<Impl>>),
|
||||
|
||||
/// Pseudo-classes
|
||||
///
|
||||
/// CSS3 Negation only takes a simple simple selector, but we still need to
|
||||
/// treat it as a compound selector because it might be a type selector
|
||||
/// which we represent as a namespace and a localname.
|
||||
///
|
||||
/// Note: if/when we upgrade this to CSS4, which supports combinators, we
|
||||
/// need to think about how this should interact with
|
||||
/// visit_complex_selector, and what the consumers of those APIs should do
|
||||
/// about the presence of combinators in negation.
|
||||
Negation(ThinBoxedSlice<Component<Impl>>),
|
||||
Negation(Box<[Selector<Impl>]>),
|
||||
FirstChild,
|
||||
LastChild,
|
||||
OnlyChild,
|
||||
@ -1121,10 +1110,9 @@ impl<Impl: SelectorImpl> Component<Impl> {
|
||||
pub fn maybe_allowed_after_pseudo_element(&self) -> bool {
|
||||
match *self {
|
||||
Component::NonTSPseudoClass(..) => true,
|
||||
Component::Negation(ref components) => components
|
||||
.iter()
|
||||
.all(|c| c.maybe_allowed_after_pseudo_element()),
|
||||
Component::Is(ref selectors) | Component::Where(ref selectors) => {
|
||||
Component::Negation(ref selectors) |
|
||||
Component::Is(ref selectors) |
|
||||
Component::Where(ref selectors) => {
|
||||
selectors.iter().all(|selector| {
|
||||
selector
|
||||
.iter_raw_match_order()
|
||||
@ -1148,9 +1136,13 @@ impl<Impl: SelectorImpl> Component<Impl> {
|
||||
*self
|
||||
);
|
||||
match *self {
|
||||
Component::Negation(ref components) => !components
|
||||
.iter()
|
||||
.all(|c| c.matches_for_stateless_pseudo_element()),
|
||||
Component::Negation(ref selectors) => {
|
||||
!selectors.iter().all(|selector| {
|
||||
selector
|
||||
.iter_raw_match_order()
|
||||
.all(|c| c.matches_for_stateless_pseudo_element())
|
||||
})
|
||||
},
|
||||
Component::Is(ref selectors) | Component::Where(ref selectors) => {
|
||||
selectors.iter().any(|selector| {
|
||||
selector
|
||||
@ -1182,14 +1174,6 @@ impl<Impl: SelectorImpl> Component<Impl> {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
Negation(ref negated) => {
|
||||
for component in negated.iter() {
|
||||
if !component.visit(visitor) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
AttributeInNoNamespaceExists {
|
||||
ref local_name,
|
||||
ref local_name_lower,
|
||||
@ -1239,7 +1223,7 @@ impl<Impl: SelectorImpl> Component<Impl> {
|
||||
}
|
||||
},
|
||||
|
||||
Is(ref list) | Where(ref list) => {
|
||||
Negation(ref list) | Is(ref list) | Where(ref list) => {
|
||||
if !visitor.visit_selector_list(&list) {
|
||||
return false;
|
||||
}
|
||||
@ -2152,44 +2136,14 @@ where
|
||||
P: Parser<'i, Impl = Impl>,
|
||||
Impl: SelectorImpl,
|
||||
{
|
||||
let state = state | SelectorParsingState::INSIDE_NEGATION;
|
||||
let list = SelectorList::parse_with_state(
|
||||
parser,
|
||||
input,
|
||||
state | SelectorParsingState::SKIP_DEFAULT_NAMESPACE | SelectorParsingState::DISALLOW_PSEUDOS,
|
||||
ParseErrorRecovery::DiscardList
|
||||
)?;
|
||||
|
||||
// We use a sequence because a type selector may be represented as two Components.
|
||||
let mut sequence = SmallVec::<[Component<Impl>; 2]>::new();
|
||||
|
||||
input.skip_whitespace();
|
||||
|
||||
// Get exactly one simple selector. The parse logic in the caller will verify
|
||||
// that there are no trailing tokens after we're done.
|
||||
let is_type_sel = match parse_type_selector(parser, input, state, &mut sequence) {
|
||||
Ok(result) => result,
|
||||
Err(ParseError {
|
||||
kind: ParseErrorKind::Basic(BasicParseErrorKind::EndOfInput),
|
||||
..
|
||||
}) => return Err(input.new_custom_error(SelectorParseErrorKind::EmptyNegation)),
|
||||
Err(e) => return Err(e.into()),
|
||||
};
|
||||
if !is_type_sel {
|
||||
match parse_one_simple_selector(parser, input, state)? {
|
||||
Some(SimpleSelectorParseResult::SimpleSelector(s)) => {
|
||||
sequence.push(s);
|
||||
},
|
||||
None => {
|
||||
return Err(input.new_custom_error(SelectorParseErrorKind::EmptyNegation));
|
||||
},
|
||||
Some(SimpleSelectorParseResult::PseudoElement(_)) |
|
||||
Some(SimpleSelectorParseResult::PartPseudo(_)) |
|
||||
Some(SimpleSelectorParseResult::SlottedPseudo(_)) => {
|
||||
let e = SelectorParseErrorKind::NonSimpleSelectorInNegation;
|
||||
return Err(input.new_custom_error(e));
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Success.
|
||||
Ok(Component::Negation(
|
||||
sequence.into_vec().into_boxed_slice().into(),
|
||||
))
|
||||
Ok(Component::Negation(list.0.into_vec().into_boxed_slice()))
|
||||
}
|
||||
|
||||
/// simple_selector_sequence
|
||||
@ -2225,7 +2179,8 @@ where
|
||||
if let Some(url) = parser.default_namespace() {
|
||||
// If there was no explicit type selector, but there is a
|
||||
// default namespace, there is an implicit "<defaultns>|*" type
|
||||
// selector. Except for :host, where we ignore it.
|
||||
// selector. Except for :host() or :not() / :is() / :where(),
|
||||
// where we ignore it.
|
||||
//
|
||||
// https://drafts.csswg.org/css-scoping/#host-element-in-tree:
|
||||
//
|
||||
@ -2240,10 +2195,22 @@ where
|
||||
// given selector is allowed to match a featureless element,
|
||||
// it must do so while ignoring the default namespace.
|
||||
//
|
||||
if !matches!(
|
||||
result,
|
||||
SimpleSelectorParseResult::SimpleSelector(Component::Host(..))
|
||||
) {
|
||||
// https://drafts.csswg.org/selectors-4/#matches
|
||||
//
|
||||
// Default namespace declarations do not affect the compound
|
||||
// selector representing the subject of any selector within
|
||||
// a :is() pseudo-class, unless that compound selector
|
||||
// contains an explicit universal selector or type selector.
|
||||
//
|
||||
// (Similar quotes for :where() / :not())
|
||||
//
|
||||
let ignore_default_ns =
|
||||
state.intersects(SelectorParsingState::SKIP_DEFAULT_NAMESPACE) ||
|
||||
matches!(
|
||||
result,
|
||||
SimpleSelectorParseResult::SimpleSelector(Component::Host(..))
|
||||
);
|
||||
if !ignore_default_ns {
|
||||
builder.push_simple_selector(Component::DefaultNamespace(url));
|
||||
}
|
||||
}
|
||||
@ -2297,7 +2264,7 @@ where
|
||||
let inner = SelectorList::parse_with_state(
|
||||
parser,
|
||||
input,
|
||||
state | SelectorParsingState::DISALLOW_PSEUDOS,
|
||||
state | SelectorParsingState::SKIP_DEFAULT_NAMESPACE | SelectorParsingState::DISALLOW_PSEUDOS,
|
||||
parser.is_and_where_error_recovery(),
|
||||
)?;
|
||||
Ok(component(inner.0.into_vec().into_boxed_slice()))
|
||||
@ -2327,11 +2294,6 @@ where
|
||||
return Ok(Component::Host(Some(parse_inner_compound_selector(parser, input, state)?)));
|
||||
},
|
||||
"not" => {
|
||||
if state.intersects(SelectorParsingState::INSIDE_NEGATION) {
|
||||
return Err(input.new_custom_error(
|
||||
SelectorParseErrorKind::UnexpectedIdent("not".into())
|
||||
));
|
||||
}
|
||||
return parse_negation(parser, input, state)
|
||||
},
|
||||
_ => {}
|
||||
@ -3080,9 +3042,13 @@ pub mod tests {
|
||||
vec![
|
||||
Component::DefaultNamespace(MATHML.into()),
|
||||
Component::Negation(
|
||||
vec![Component::Class(DummyAtom::from("cl"))]
|
||||
.into_boxed_slice()
|
||||
.into(),
|
||||
vec![
|
||||
Selector::from_vec(
|
||||
vec![Component::Class(DummyAtom::from("cl"))],
|
||||
specificity(0, 1, 0),
|
||||
Default::default(),
|
||||
)
|
||||
].into_boxed_slice()
|
||||
),
|
||||
],
|
||||
specificity(0, 1, 0),
|
||||
@ -3096,11 +3062,16 @@ pub mod tests {
|
||||
Component::DefaultNamespace(MATHML.into()),
|
||||
Component::Negation(
|
||||
vec![
|
||||
Component::DefaultNamespace(MATHML.into()),
|
||||
Component::ExplicitUniversalType,
|
||||
Selector::from_vec(
|
||||
vec![
|
||||
Component::DefaultNamespace(MATHML.into()),
|
||||
Component::ExplicitUniversalType,
|
||||
],
|
||||
specificity(0, 0, 0),
|
||||
Default::default(),
|
||||
)
|
||||
]
|
||||
.into_boxed_slice()
|
||||
.into(),
|
||||
.into_boxed_slice(),
|
||||
),
|
||||
],
|
||||
specificity(0, 0, 0),
|
||||
@ -3114,14 +3085,19 @@ pub mod tests {
|
||||
Component::DefaultNamespace(MATHML.into()),
|
||||
Component::Negation(
|
||||
vec![
|
||||
Component::DefaultNamespace(MATHML.into()),
|
||||
Component::LocalName(LocalName {
|
||||
name: DummyAtom::from("e"),
|
||||
lower_name: DummyAtom::from("e"),
|
||||
}),
|
||||
Selector::from_vec(
|
||||
vec![
|
||||
Component::DefaultNamespace(MATHML.into()),
|
||||
Component::LocalName(LocalName {
|
||||
name: DummyAtom::from("e"),
|
||||
lower_name: DummyAtom::from("e"),
|
||||
}),
|
||||
],
|
||||
specificity(0, 0, 1),
|
||||
Default::default(),
|
||||
),
|
||||
]
|
||||
.into_boxed_slice()
|
||||
.into(),
|
||||
),
|
||||
],
|
||||
specificity(0, 0, 1),
|
||||
@ -3216,47 +3192,23 @@ pub mod tests {
|
||||
)]))
|
||||
);
|
||||
parser.default_ns = None;
|
||||
assert!(parse(":not(#provel.old)").is_err());
|
||||
assert!(parse(":not(#provel > old)").is_err());
|
||||
assert!(parse(":not(#provel.old)").is_ok());
|
||||
assert!(parse(":not(#provel > old)").is_ok());
|
||||
assert!(parse("table[rules]:not([rules=\"none\"]):not([rules=\"\"])").is_ok());
|
||||
assert_eq!(
|
||||
parse(":not(#provel)"),
|
||||
Ok(SelectorList::from_vec(vec![Selector::from_vec(
|
||||
vec![Component::Negation(
|
||||
vec![Component::ID(DummyAtom::from("provel"))]
|
||||
.into_boxed_slice()
|
||||
.into(),
|
||||
)],
|
||||
specificity(1, 0, 0),
|
||||
Default::default(),
|
||||
)]))
|
||||
);
|
||||
assert_eq!(
|
||||
parse_ns(":not(svg|circle)", &parser),
|
||||
Ok(SelectorList::from_vec(vec![Selector::from_vec(
|
||||
vec![Component::Negation(
|
||||
vec![
|
||||
Component::Namespace(DummyAtom("svg".into()), SVG.into()),
|
||||
Component::LocalName(LocalName {
|
||||
name: DummyAtom::from("circle"),
|
||||
lower_name: DummyAtom::from("circle"),
|
||||
}),
|
||||
]
|
||||
.into_boxed_slice()
|
||||
.into(),
|
||||
)],
|
||||
specificity(0, 0, 1),
|
||||
Default::default(),
|
||||
)]))
|
||||
);
|
||||
// https://github.com/servo/servo/issues/16017
|
||||
assert_eq!(
|
||||
parse_ns(":not(*)", &parser),
|
||||
Ok(SelectorList::from_vec(vec![Selector::from_vec(
|
||||
vec![Component::Negation(
|
||||
vec![Component::ExplicitUniversalType]
|
||||
.into_boxed_slice()
|
||||
.into(),
|
||||
vec![
|
||||
Selector::from_vec(
|
||||
vec![
|
||||
Component::ExplicitUniversalType
|
||||
],
|
||||
specificity(0, 0, 0),
|
||||
Default::default(),
|
||||
)
|
||||
].into_boxed_slice()
|
||||
)],
|
||||
specificity(0, 0, 0),
|
||||
Default::default(),
|
||||
@ -3267,11 +3219,16 @@ pub mod tests {
|
||||
Ok(SelectorList::from_vec(vec![Selector::from_vec(
|
||||
vec![Component::Negation(
|
||||
vec![
|
||||
Component::ExplicitNoNamespace,
|
||||
Component::ExplicitUniversalType,
|
||||
Selector::from_vec(
|
||||
vec![
|
||||
Component::ExplicitNoNamespace,
|
||||
Component::ExplicitUniversalType,
|
||||
],
|
||||
specificity(0, 0, 0),
|
||||
Default::default(),
|
||||
)
|
||||
]
|
||||
.into_boxed_slice()
|
||||
.into(),
|
||||
.into_boxed_slice(),
|
||||
)],
|
||||
specificity(0, 0, 0),
|
||||
Default::default(),
|
||||
@ -3281,27 +3238,17 @@ pub mod tests {
|
||||
// https://github.com/servo/servo/pull/17537
|
||||
assert_eq!(
|
||||
parse_ns_expected(":not(*|*)", &parser, Some(":not(*)")),
|
||||
Ok(SelectorList::from_vec(vec![Selector::from_vec(
|
||||
vec![Component::Negation(
|
||||
vec![Component::ExplicitUniversalType]
|
||||
.into_boxed_slice()
|
||||
.into(),
|
||||
)],
|
||||
specificity(0, 0, 0),
|
||||
Default::default(),
|
||||
)]))
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
parse_ns(":not(svg|*)", &parser),
|
||||
Ok(SelectorList::from_vec(vec![Selector::from_vec(
|
||||
vec![Component::Negation(
|
||||
vec![
|
||||
Component::Namespace(DummyAtom("svg".into()), SVG.into()),
|
||||
Component::ExplicitUniversalType,
|
||||
]
|
||||
.into_boxed_slice()
|
||||
.into(),
|
||||
Selector::from_vec(
|
||||
vec![
|
||||
Component::ExplicitUniversalType
|
||||
],
|
||||
specificity(0, 0, 0),
|
||||
Default::default()
|
||||
)
|
||||
].into_boxed_slice()
|
||||
)],
|
||||
specificity(0, 0, 0),
|
||||
Default::default(),
|
||||
|
@ -170,7 +170,6 @@ fn extract_error_params<'a>(err: ErrorKind<'a>) -> Option<ErrorParams<'a>> {
|
||||
SelectorParseErrorKind::EmptySelector | SelectorParseErrorKind::DanglingCombinator => {
|
||||
(None, None)
|
||||
},
|
||||
SelectorParseErrorKind::EmptyNegation => (None, Some(ErrorString::Snippet(")".into()))),
|
||||
err => match extract_error_param(ParseErrorKind::Custom(
|
||||
StyleParseErrorKind::SelectorError(err),
|
||||
)) {
|
||||
@ -365,9 +364,6 @@ impl<'a> ErrorHelpers<'a> for ContextualParseError<'a> {
|
||||
SelectorParseErrorKind::ClassNeedsIdent(_) => {
|
||||
Some(cstr!("PEClassSelNotIdent"))
|
||||
},
|
||||
SelectorParseErrorKind::EmptyNegation => {
|
||||
Some(cstr!("PENegationBadArg"))
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
},
|
||||
|
@ -1,2 +0,0 @@
|
||||
[css3-modsel-83.xml]
|
||||
expected: FAIL
|
@ -0,0 +1,41 @@
|
||||
<!DOCTYPE html>
|
||||
<title>CSS Selectors Invalidation: complex :not()</title>
|
||||
<link rel="help" href="https://drafts.csswg.org/selectors-4/#negation">
|
||||
<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
|
||||
<link rel="author" title="Mozilla" href="https://mozilla.org">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<style>
|
||||
* {
|
||||
color: yellow;
|
||||
}
|
||||
:not(.b ~ *) {
|
||||
color: green;
|
||||
}
|
||||
</style>
|
||||
<div id="b">
|
||||
</div>
|
||||
<div class="c">
|
||||
</div>
|
||||
<div class="d">
|
||||
</div>
|
||||
<script>
|
||||
var black = "rgb(0, 0, 0)";
|
||||
var blue = "rgb(0, 0, 255)";
|
||||
var green = "rgb(0, 128, 0)";
|
||||
var red = "rgb(255, 0, 0)";
|
||||
var yellow = "rgb(255, 255, 0)";
|
||||
|
||||
test(() => {
|
||||
assert_equals(getComputedStyle(document.querySelector("#b")).color, green);
|
||||
assert_equals(getComputedStyle(document.querySelector(".c")).color, green);
|
||||
assert_equals(getComputedStyle(document.querySelector(".d")).color, green);
|
||||
}, "precondition");
|
||||
|
||||
test(() => {
|
||||
document.getElementById("b").className = "b";
|
||||
assert_equals(getComputedStyle(document.querySelector("#b")).color, green);
|
||||
assert_equals(getComputedStyle(document.querySelector(".c")).color, yellow);
|
||||
assert_equals(getComputedStyle(document.querySelector(".d")).color, yellow);
|
||||
}, "Invalidation of sibling combinators in :not()");
|
||||
</script>
|
@ -0,0 +1,133 @@
|
||||
<!DOCTYPE html>
|
||||
<title>CSS Selectors Invalidation: complex :not()</title>
|
||||
<link rel="help" href="https://drafts.csswg.org/selectors-4/#negation">
|
||||
<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
|
||||
<link rel="author" title="Mozilla" href="https://mozilla.org">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<style>
|
||||
.b {
|
||||
color: yellow;
|
||||
}
|
||||
/*Simple selector arguments */
|
||||
.a :not(:not(.b, .c)) {
|
||||
color: red;
|
||||
}
|
||||
/*Compound selector arguments */
|
||||
.a :not(:not(.c#d, .e)) {
|
||||
color: green;
|
||||
}
|
||||
/* Complex selector arguments */
|
||||
.a .g>.b {
|
||||
color: black;
|
||||
}
|
||||
.a :not(:not(.e+.f, .g>.b, .h)) {
|
||||
color: blue;
|
||||
}
|
||||
.g>.b {
|
||||
color: black;
|
||||
}
|
||||
.a .h {
|
||||
color: black;
|
||||
}
|
||||
/* Nested */
|
||||
.a+.c>.e {
|
||||
color: black;
|
||||
}
|
||||
.c>.a+.e {
|
||||
color: black;
|
||||
}
|
||||
.a+:not(:not(.b+.f, :is(.c>.e, .g))) {
|
||||
color: red;
|
||||
}
|
||||
.c>.e {
|
||||
color: black;
|
||||
}
|
||||
</style>
|
||||
<div id="a1">
|
||||
<div class="b" id="b1">
|
||||
Red
|
||||
</div>
|
||||
<div class="c" id="c1">
|
||||
Red
|
||||
</div>
|
||||
<div class="c" id="d">
|
||||
Green
|
||||
</div>
|
||||
<div class="e" id="e1">
|
||||
Green
|
||||
</div>
|
||||
<div class="f" id="f1">
|
||||
Blue
|
||||
</div>
|
||||
<div class="g">
|
||||
<div class="b" id="b2">
|
||||
Blue
|
||||
<div class="b" id="b3">
|
||||
Red
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="h" id="h1">
|
||||
Blue
|
||||
</div>
|
||||
</div>
|
||||
<div class="c" id="c2">
|
||||
<div id="a2"></div>
|
||||
<div class="e" id="e2">
|
||||
Red
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
document.body.offsetTop;
|
||||
|
||||
var black = "rgb(0, 0, 0)";
|
||||
var blue = "rgb(0, 0, 255)";
|
||||
var green = "rgb(0, 128, 0)";
|
||||
var red = "rgb(255, 0, 0)";
|
||||
var yellow = "rgb(255, 255, 0)";
|
||||
|
||||
test(() => {
|
||||
assert_equals(getComputedStyle(b1).color, yellow);
|
||||
assert_equals(getComputedStyle(b2).color, black);
|
||||
assert_equals(getComputedStyle(b3).color, yellow);
|
||||
assert_equals(getComputedStyle(c1).color, black);
|
||||
assert_equals(getComputedStyle(d).color, black);
|
||||
assert_equals(getComputedStyle(e1).color, black);
|
||||
assert_equals(getComputedStyle(e2).color, black);
|
||||
assert_equals(getComputedStyle(f1).color, black);
|
||||
assert_equals(getComputedStyle(h1).color, black);
|
||||
}, "Preconditions.");
|
||||
|
||||
test(() => {
|
||||
a1.className = "a";
|
||||
assert_equals(getComputedStyle(b1).color, red);
|
||||
assert_equals(getComputedStyle(b3).color, red);
|
||||
assert_equals(getComputedStyle(c1).color, red);
|
||||
}, "Invalidate :not() for simple selector arguments.");
|
||||
|
||||
test(() => {
|
||||
a1.className = "a";
|
||||
assert_equals(getComputedStyle(d).color, green);
|
||||
}, "Invalidate :not() for compound selector arguments.");
|
||||
|
||||
test(() => {
|
||||
a1.className = "a";
|
||||
assert_equals(getComputedStyle(b2).color, blue);
|
||||
assert_equals(getComputedStyle(b3).color, red);
|
||||
assert_equals(getComputedStyle(f1).color, blue);
|
||||
}, "Invalidate :not() for complex selector arguments.");
|
||||
|
||||
test(() => {
|
||||
a1.className = "a";
|
||||
assert_equals(getComputedStyle(e2).color, black);
|
||||
a2.className = "a";
|
||||
assert_equals(getComputedStyle(e2).color, red);
|
||||
}, "Invalidate nested :is() inside :not().");
|
||||
|
||||
test(() => {
|
||||
a1.className = "a";
|
||||
assert_equals(getComputedStyle(b2).color, blue);
|
||||
assert_equals(getComputedStyle(h1).color, blue);
|
||||
}, "Test specificity of :not().");
|
||||
</script>
|
@ -0,0 +1,23 @@
|
||||
<!doctype html>
|
||||
<title>Default namespace gets ignored inside non-type selectors for :is() / :not() / :where().</title>
|
||||
<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
|
||||
<link rel="author" title="Mozilla" href="https://mozilla.org">
|
||||
<link rel="help" href="https://drafts.csswg.org/selectors-4/#matches">
|
||||
<!--
|
||||
Default namespace declarations do not affect the compound selector
|
||||
representing the subject of any selector within a :is() pseudo-class, unless
|
||||
that compound selector contains an explicit universal selector or type
|
||||
selector.
|
||||
-->
|
||||
<link rel="match" href="/css/reference/blank.html">
|
||||
<style>
|
||||
@namespace url("http://www.w3.org/2000/svg");
|
||||
|
||||
/* Type selector, so ns should apply and this should not match */
|
||||
*|*:is(div) {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
background: red;
|
||||
}
|
||||
</style>
|
||||
<div></div>
|
@ -0,0 +1,21 @@
|
||||
<!doctype html>
|
||||
<title>Default namespace gets ignored inside non-type selectors for :is() / :not() / :where().</title>
|
||||
<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
|
||||
<link rel="author" title="Mozilla" href="https://mozilla.org">
|
||||
<link rel="help" href="https://drafts.csswg.org/selectors-4/#matches">
|
||||
<!--
|
||||
Default namespace declarations do not affect the compound selector
|
||||
representing the subject of any selector within a :is() pseudo-class, unless
|
||||
that compound selector contains an explicit universal selector or type
|
||||
selector.
|
||||
-->
|
||||
<link rel="match" href="/css/reference/blank.html">
|
||||
<style>
|
||||
@namespace url("http://www.w3.org/2000/svg");
|
||||
|
||||
/* No type selector, so selector should match and hide the <input> */
|
||||
*|*:is(:disabled) {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
<input disabled>
|
@ -0,0 +1,27 @@
|
||||
<!doctype html>
|
||||
<title>Default namespace gets ignored inside non-type selectors for :is() / :not() / :where().</title>
|
||||
<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
|
||||
<link rel="author" title="Mozilla" href="https://mozilla.org">
|
||||
<link rel="help" href="https://drafts.csswg.org/selectors-4/#negation">
|
||||
<!--
|
||||
Default namespace declarations do not affect the compound selector
|
||||
representing the subject of any selector within a :not() pseudo-class, unless
|
||||
that compound selector contains an explicit universal selector or type
|
||||
selector.
|
||||
-->
|
||||
<link rel="match" href="/css/reference/blank.html">
|
||||
<style>
|
||||
@namespace url("http://www.w3.org/2000/svg");
|
||||
|
||||
*|div {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
background: red;
|
||||
}
|
||||
|
||||
/* Type selector, so ns should apply and this should match */
|
||||
*|*:not(div) {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
<div></div>
|
@ -0,0 +1,25 @@
|
||||
<!doctype html>
|
||||
<title>Default namespace gets ignored inside non-type selectors for :is() / :not() / :where().</title>
|
||||
<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
|
||||
<link rel="author" title="Mozilla" href="https://mozilla.org">
|
||||
<link rel="help" href="https://drafts.csswg.org/selectors-4/#negation">
|
||||
<!--
|
||||
Default namespace declarations do not affect the compound selector
|
||||
representing the subject of any selector within a :not() pseudo-class, unless
|
||||
that compound selector contains an explicit universal selector or type
|
||||
selector.
|
||||
-->
|
||||
<link rel="match" href="/css/reference/blank.html">
|
||||
<style>
|
||||
@namespace url("http://www.w3.org/2000/svg");
|
||||
|
||||
*|input {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* No type selector, so selector should _not_ match and keep the input hidden */
|
||||
*|input:not(:disabled) {
|
||||
display: initial;
|
||||
}
|
||||
</style>
|
||||
<input disabled>
|
Loading…
Reference in New Issue
Block a user