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:
Emilio Cobos Álvarez 2020-10-29 18:03:54 +00:00
parent e685a3df8c
commit 82e837eb83
18 changed files with 378 additions and 180 deletions

1
Cargo.lock generated
View File

@ -4429,7 +4429,6 @@ dependencies = [
"precomputed-hash",
"servo_arc",
"smallvec",
"thin-slice",
"to_shmem",
"to_shmem_derive",
]

View File

@ -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; }

View File

@ -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.

View File

@ -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.",

View File

@ -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" }

View File

@ -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);
}
},
}
}

View File

@ -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
}

View File

@ -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;

View File

@ -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
}),
}
}

View File

@ -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(),

View File

@ -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,
}
},

View File

@ -1,2 +0,0 @@
[css3-modsel-83.xml]
expected: FAIL

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>