зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1911216 - Part 2: Support :only-child for view transition selectors. r=layout-reviewers,firefox-style-system-reviewers,emilio
Introduce the concept of pseudo element tree and make sure we achieve the following behaviors: 1. `::view-transition` doesn't accept any non-functional pseudo class after it. 2. `::view-transition-*(name)` accepts only `only-child` pseudo class after it. So for named view transition pseudo elements, we make them accept non-functional pseudo classes and tree structural pseudo classes after them, for consistency. However, only `:only-child` is allowed. Differential Revision: https://phabricator.services.mozilla.com/D219714
This commit is contained in:
Родитель
7c3a0ab05d
Коммит
d524f0f5e5
|
@ -45,6 +45,13 @@ pub trait PseudoElement: Sized + ToCss {
|
|||
fn specificity_count(&self) -> u32 {
|
||||
1
|
||||
}
|
||||
|
||||
/// Whether this pseudo-element is in a pseudo-element tree (excluding the pseudo-element
|
||||
/// root).
|
||||
/// https://drafts.csswg.org/css-view-transitions-1/#pseudo-root
|
||||
fn is_in_pseudo_element_tree(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// A trait that represents a pseudo-class.
|
||||
|
@ -83,7 +90,7 @@ fn to_ascii_lowercase(s: &str) -> Cow<str> {
|
|||
bitflags! {
|
||||
/// Flags that indicate at which point of parsing a selector are we.
|
||||
#[derive(Copy, Clone)]
|
||||
struct SelectorParsingState: u8 {
|
||||
struct SelectorParsingState: u16 {
|
||||
/// Whether we should avoid adding default namespaces to selectors that
|
||||
/// aren't type or universal selectors.
|
||||
const SKIP_DEFAULT_NAMESPACE = 1 << 0;
|
||||
|
@ -122,6 +129,10 @@ bitflags! {
|
|||
|
||||
/// Whether we explicitly disallow relative selectors (i.e. `:has()`).
|
||||
const DISALLOW_RELATIVE_SELECTOR = 1 << 7;
|
||||
|
||||
/// Whether we've parsed a pseudo-element which is in a pseudo-element tree (i.e. it is a
|
||||
/// descendant pseudo of a pseudo-element root).
|
||||
const IN_PSEUDO_ELEMENT_TREE = 1 << 8;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -149,13 +160,18 @@ impl SelectorParsingState {
|
|||
|
||||
#[inline]
|
||||
fn allows_tree_structural_pseudo_classes(self) -> bool {
|
||||
!self.intersects(Self::AFTER_PSEUDO)
|
||||
!self.intersects(Self::AFTER_PSEUDO) || self.intersects(Self::IN_PSEUDO_ELEMENT_TREE)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn allows_combinators(self) -> bool {
|
||||
!self.intersects(Self::DISALLOW_COMBINATORS)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn allows_only_child_pseudo_class_only(self) -> bool {
|
||||
self.intersects(Self::IN_PSEUDO_ELEMENT_TREE)
|
||||
}
|
||||
}
|
||||
|
||||
pub type SelectorParseError<'i> = ParseError<'i, SelectorParseErrorKind<'i>>;
|
||||
|
@ -3261,6 +3277,9 @@ where
|
|||
if !p.accepts_state_pseudo_classes() {
|
||||
state.insert(SelectorParsingState::AFTER_NON_STATEFUL_PSEUDO_ELEMENT);
|
||||
}
|
||||
if p.is_in_pseudo_element_tree() {
|
||||
state.insert(SelectorParsingState::IN_PSEUDO_ELEMENT_TREE);
|
||||
}
|
||||
builder.push_combinator(Combinator::PseudoElement);
|
||||
builder.push_simple_selector(Component::PseudoElement(p));
|
||||
},
|
||||
|
@ -3574,6 +3593,20 @@ where
|
|||
}
|
||||
|
||||
if state.allows_tree_structural_pseudo_classes() {
|
||||
// If a descendant pseudo of a pseudo-element root has no other siblings, then :only-child
|
||||
// matches that pseudo. Note that we don't accept other tree structural pseudo classes in
|
||||
// this case (to match other browsers). And the spec mentions only `:only-child` as well.
|
||||
// https://drafts.csswg.org/css-view-transitions-1/#pseudo-root
|
||||
if state.allows_only_child_pseudo_class_only() {
|
||||
if name.eq_ignore_ascii_case("only-child") {
|
||||
return Ok(Component::Nth(NthSelectorData::only(/* of_type = */ false)));
|
||||
}
|
||||
// Other non-functional pseudo classes are not allowed.
|
||||
// FIXME: Perhaps we can refactor this, e.g. distinguish tree-structural pseudo classes
|
||||
// from other non-ts pseudo classes. Otherwise, this special case looks weird.
|
||||
return Err(location.new_custom_error(SelectorParseErrorKind::InvalidState));
|
||||
}
|
||||
|
||||
match_ignore_ascii_case! { &name,
|
||||
"first-child" => return Ok(Component::Nth(NthSelectorData::first(/* of_type = */ false))),
|
||||
"last-child" => return Ok(Component::Nth(NthSelectorData::last(/* of_type = */ false))),
|
||||
|
|
|
@ -45,13 +45,22 @@ impl ::selectors::parser::PseudoElement for PseudoElement {
|
|||
|
||||
#[inline]
|
||||
fn accepts_state_pseudo_classes(&self) -> bool {
|
||||
self.supports_user_action_state()
|
||||
// Note: if the pseudo element is a descendants of a pseudo element, `only-child` should be
|
||||
// allowed after it.
|
||||
self.supports_user_action_state() || self.is_in_pseudo_element_tree()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn specificity_count(&self) -> u32 {
|
||||
self.specificity_count()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_in_pseudo_element_tree(&self) -> bool {
|
||||
// All the named view transition pseudo-elements are the descendants of a pseudo-element
|
||||
// root.
|
||||
self.is_named_view_transition()
|
||||
}
|
||||
}
|
||||
|
||||
impl PseudoElement {
|
||||
|
@ -170,6 +179,17 @@ impl PseudoElement {
|
|||
*self == PseudoElement::TargetText
|
||||
}
|
||||
|
||||
/// Whether this pseudo-element is a named view transition pseudo-element.
|
||||
pub fn is_named_view_transition(&self) -> bool {
|
||||
matches!(
|
||||
*self,
|
||||
Self::ViewTransitionGroup(..) |
|
||||
Self::ViewTransitionImagePair(..) |
|
||||
Self::ViewTransitionOld(..) |
|
||||
Self::ViewTransitionNew(..)
|
||||
)
|
||||
}
|
||||
|
||||
/// The count we contribute to the specificity from this pseudo-element.
|
||||
pub fn specificity_count(&self) -> u32 {
|
||||
match *self {
|
||||
|
|
|
@ -1,144 +0,0 @@
|
|||
[pseudo-elements-valid.html]
|
||||
["::view-transition-group(*):only-child" should be a valid selector]
|
||||
expected: FAIL
|
||||
|
||||
[":root::view-transition-group(*):only-child" should be a valid selector]
|
||||
expected: FAIL
|
||||
|
||||
[".a::view-transition-group(*):only-child" should be a valid selector]
|
||||
expected: FAIL
|
||||
|
||||
["div ::view-transition-group(*):only-child" should be a valid selector]
|
||||
expected: FAIL
|
||||
|
||||
["::view-transition-group(root):only-child" should be a valid selector]
|
||||
expected: FAIL
|
||||
|
||||
[":root::view-transition-group(root):only-child" should be a valid selector]
|
||||
expected: FAIL
|
||||
|
||||
[".a::view-transition-group(root):only-child" should be a valid selector]
|
||||
expected: FAIL
|
||||
|
||||
["div ::view-transition-group(root):only-child" should be a valid selector]
|
||||
expected: FAIL
|
||||
|
||||
["::view-transition-group(dashed-ident):only-child" should be a valid selector]
|
||||
expected: FAIL
|
||||
|
||||
[":root::view-transition-group(dashed-ident):only-child" should be a valid selector]
|
||||
expected: FAIL
|
||||
|
||||
[".a::view-transition-group(dashed-ident):only-child" should be a valid selector]
|
||||
expected: FAIL
|
||||
|
||||
["div ::view-transition-group(dashed-ident):only-child" should be a valid selector]
|
||||
expected: FAIL
|
||||
|
||||
["::view-transition-image-pair(*):only-child" should be a valid selector]
|
||||
expected: FAIL
|
||||
|
||||
[":root::view-transition-image-pair(*):only-child" should be a valid selector]
|
||||
expected: FAIL
|
||||
|
||||
[".a::view-transition-image-pair(*):only-child" should be a valid selector]
|
||||
expected: FAIL
|
||||
|
||||
["div ::view-transition-image-pair(*):only-child" should be a valid selector]
|
||||
expected: FAIL
|
||||
|
||||
["::view-transition-image-pair(root):only-child" should be a valid selector]
|
||||
expected: FAIL
|
||||
|
||||
[":root::view-transition-image-pair(root):only-child" should be a valid selector]
|
||||
expected: FAIL
|
||||
|
||||
[".a::view-transition-image-pair(root):only-child" should be a valid selector]
|
||||
expected: FAIL
|
||||
|
||||
["div ::view-transition-image-pair(root):only-child" should be a valid selector]
|
||||
expected: FAIL
|
||||
|
||||
["::view-transition-image-pair(dashed-ident):only-child" should be a valid selector]
|
||||
expected: FAIL
|
||||
|
||||
[":root::view-transition-image-pair(dashed-ident):only-child" should be a valid selector]
|
||||
expected: FAIL
|
||||
|
||||
[".a::view-transition-image-pair(dashed-ident):only-child" should be a valid selector]
|
||||
expected: FAIL
|
||||
|
||||
["div ::view-transition-image-pair(dashed-ident):only-child" should be a valid selector]
|
||||
expected: FAIL
|
||||
|
||||
["::view-transition-old(*):only-child" should be a valid selector]
|
||||
expected: FAIL
|
||||
|
||||
[":root::view-transition-old(*):only-child" should be a valid selector]
|
||||
expected: FAIL
|
||||
|
||||
[".a::view-transition-old(*):only-child" should be a valid selector]
|
||||
expected: FAIL
|
||||
|
||||
["div ::view-transition-old(*):only-child" should be a valid selector]
|
||||
expected: FAIL
|
||||
|
||||
["::view-transition-old(root):only-child" should be a valid selector]
|
||||
expected: FAIL
|
||||
|
||||
[":root::view-transition-old(root):only-child" should be a valid selector]
|
||||
expected: FAIL
|
||||
|
||||
[".a::view-transition-old(root):only-child" should be a valid selector]
|
||||
expected: FAIL
|
||||
|
||||
["div ::view-transition-old(root):only-child" should be a valid selector]
|
||||
expected: FAIL
|
||||
|
||||
["::view-transition-old(dashed-ident):only-child" should be a valid selector]
|
||||
expected: FAIL
|
||||
|
||||
[":root::view-transition-old(dashed-ident):only-child" should be a valid selector]
|
||||
expected: FAIL
|
||||
|
||||
[".a::view-transition-old(dashed-ident):only-child" should be a valid selector]
|
||||
expected: FAIL
|
||||
|
||||
["div ::view-transition-old(dashed-ident):only-child" should be a valid selector]
|
||||
expected: FAIL
|
||||
|
||||
["::view-transition-new(*):only-child" should be a valid selector]
|
||||
expected: FAIL
|
||||
|
||||
[":root::view-transition-new(*):only-child" should be a valid selector]
|
||||
expected: FAIL
|
||||
|
||||
[".a::view-transition-new(*):only-child" should be a valid selector]
|
||||
expected: FAIL
|
||||
|
||||
["div ::view-transition-new(*):only-child" should be a valid selector]
|
||||
expected: FAIL
|
||||
|
||||
["::view-transition-new(root):only-child" should be a valid selector]
|
||||
expected: FAIL
|
||||
|
||||
[":root::view-transition-new(root):only-child" should be a valid selector]
|
||||
expected: FAIL
|
||||
|
||||
[".a::view-transition-new(root):only-child" should be a valid selector]
|
||||
expected: FAIL
|
||||
|
||||
["div ::view-transition-new(root):only-child" should be a valid selector]
|
||||
expected: FAIL
|
||||
|
||||
["::view-transition-new(dashed-ident):only-child" should be a valid selector]
|
||||
expected: FAIL
|
||||
|
||||
[":root::view-transition-new(dashed-ident):only-child" should be a valid selector]
|
||||
expected: FAIL
|
||||
|
||||
[".a::view-transition-new(dashed-ident):only-child" should be a valid selector]
|
||||
expected: FAIL
|
||||
|
||||
["div ::view-transition-new(dashed-ident):only-child" should be a valid selector]
|
||||
expected: FAIL
|
|
@ -18,11 +18,18 @@ function test_invalid_selector_combinations(pseudo) {
|
|||
test_invalid_selector(`${pseudo}.a`);
|
||||
test_invalid_selector(`${pseudo} div`);
|
||||
test_invalid_selector(`${pseudo}:hover`);
|
||||
test_invalid_selector(`${pseudo}:active`);
|
||||
test_invalid_selector(`${pseudo}:first-child`);
|
||||
test_invalid_selector(`${pseudo}:last-child`);
|
||||
test_invalid_selector(`${pseudo}:empty`);
|
||||
test_invalid_selector(`${pseudo}:visited`);
|
||||
test_invalid_selector(`${pseudo}:enabled`);
|
||||
test_invalid_selector(`:not(${pseudo})`);
|
||||
test_invalid_selector(`:has(${pseudo})`);
|
||||
}
|
||||
|
||||
test_invalid_selector_combinations("::view-transition");
|
||||
test_invalid_selector("::view-transition:only-child");
|
||||
test_invalid_selector("::view-transition(*)");
|
||||
test_invalid_selector("::view-transition(valid)");
|
||||
test_invalid_selector("::view-transition(root)");
|
||||
|
|
Загрузка…
Ссылка в новой задаче