Bug 1911216 - Part 1: Add CSS selector parsing support for view transition pseudo-elements. r=layout-reviewers,firefox-style-system-reviewers,emilio,devtools-reviewers,nchevobbe

This adds the pasring support for:
1. `::view-transition`
2. `::view-transition-group(name)`
3. `::view-transition-image-pair(name)`
4. `::view-transition-old(name)`
5. `::view-transition-new(name)`

The `name` here is a `<custom-ident>` or `*`.

Differential Revision: https://phabricator.services.mozilla.com/D219713
This commit is contained in:
Boris Chiou 2024-09-03 19:29:32 +00:00
parent 80efd83dc1
commit 7c3a0ab05d
15 changed files with 140 additions and 176 deletions

View File

@ -690,6 +690,7 @@ class PageStyleActor extends Actor {
continue;
}
// FIXME: Bug 1909173. Need to handle view transitions peudo-elements.
if (readPseudo === "::highlight") {
InspectorUtils.getRegisteredCssHighlights(
this.inspector.targetActor.window.document,
@ -801,6 +802,14 @@ class PageStyleActor extends Actor {
case "::slider-thumb":
case "::slider-track":
return node.nodeName == "INPUT" && node.type == "range";
case "::view-transition":
case "::view-transition-group":
case "::view-transition-image-pair":
case "::view-transition-old":
case "::view-transition-new":
// FIXME: Bug 1909173. Need to handle view transitions peudo-elements
// for DevTools. For now we skip them.
return false;
default:
console.error("Unhandled pseudo-element " + pseudo);
return false;

View File

@ -7,6 +7,7 @@ prefs = [
"layout.css.starting-style-at-rules.enabled=true",
"layout.css.transition-behavior.enabled=true",
"dom.customHighlightAPI.enabled=true",
"dom.viewTransitions.enabled=true",
]
support-files = [
"bug1202095.css",

View File

@ -9,7 +9,7 @@
<script type="application/javascript">
const InspectorUtils = SpecialPowers.InspectorUtils;
// NOTE(emilio): When this changes, make sure to update _pseudoIsRelevant in devtools/server/actors/styles.js
// NOTE(emilio): When this changes, make sure to update _pseudoIsRelevant in devtools/server/actors/page-style.js
let expected = new Set([
"::after",
"::before",
@ -23,6 +23,11 @@
"::placeholder",
"::selection",
"::target-text",
"::view-transition",
"::view-transition-group",
"::view-transition-image-pair",
"::view-transition-old",
"::view-transition-new",
"::-moz-color-swatch",
"::-moz-focus-inner",
"::-moz-meter-bar",
@ -60,4 +65,4 @@
</pre>
</body>
</html>
</html>

View File

@ -52,6 +52,13 @@ CSS_PSEUDO_ELEMENT(selection, ":selection",
CSS_PSEUDO_ELEMENT_CONTAINS_ELEMENTS)
CSS_PSEUDO_ELEMENT(targetText, ":target-text", 0)
CSS_PSEUDO_ELEMENT(viewTransition, ":view-transition", 0)
CSS_PSEUDO_ELEMENT(viewTransitionGroup, ":view-transition-group", 0)
CSS_PSEUDO_ELEMENT(viewTransitionImagePair, ":view-transition-image-pair", 0)
CSS_PSEUDO_ELEMENT(viewTransitionOld, ":view-transition-old", 0)
CSS_PSEUDO_ELEMENT(viewTransitionNew, ":view-transition-new", 0)
// XXXbz should we really allow random content to style these? Maybe
// use our flags to prevent that?
CSS_PSEUDO_ELEMENT(mozFocusInner, ":-moz-focus-inner", 0)

View File

@ -136,6 +136,12 @@ class nsCSSPseudoElements {
case Type::sliderThumb:
case Type::sliderFill:
return mozilla::StaticPrefs::layout_css_modern_range_pseudos_enabled();
case Type::viewTransition:
case Type::viewTransitionGroup:
case Type::viewTransitionImagePair:
case Type::viewTransitionOld:
case Type::viewTransitionNew:
return mozilla::StaticPrefs::dom_viewTransitions_enabled();
default:
return !PseudoElementHasAnyFlag(
aType, CSS_PSEUDO_ELEMENT_ENABLED_IN_UA_SHEETS_AND_CHROME);

View File

@ -4458,6 +4458,7 @@
type: RelaxedAtomicBool
value: false
mirror: always
rust: true
# Is support for WebVR APIs enabled?
# Disabled everywhere, but not removed.

View File

@ -286,9 +286,10 @@ where
flags.insert(SelectorFlags::HAS_PART);
specificity.element_selectors += 1
},
Component::PseudoElement(..) => {
Component::PseudoElement(ref pseudo) => {
use crate::parser::PseudoElement;
flags.insert(SelectorFlags::HAS_PSEUDO);
specificity.element_selectors += 1
specificity.element_selectors += pseudo.specificity_count();
},
Component::LocalName(..) => {
flags.insert(SelectorFlags::HAS_NON_FEATURELESS_COMPONENT);

View File

@ -40,6 +40,11 @@ pub trait PseudoElement: Sized + ToCss {
fn valid_after_slotted(&self) -> bool {
false
}
/// The count we contribute to the specificity from this pseudo-element.
fn specificity_count(&self) -> u32 {
1
}
}
/// A trait that represents a pseudo-class.

View File

@ -47,6 +47,11 @@ impl ::selectors::parser::PseudoElement for PseudoElement {
fn accepts_state_pseudo_classes(&self) -> bool {
self.supports_user_action_state()
}
#[inline]
fn specificity_count(&self) -> u32 {
self.specificity_count()
}
}
impl PseudoElement {
@ -164,6 +169,24 @@ impl PseudoElement {
pub fn is_target_text(&self) -> bool {
*self == PseudoElement::TargetText
}
/// The count we contribute to the specificity from this pseudo-element.
pub fn specificity_count(&self) -> u32 {
match *self {
Self::ViewTransitionGroup(ref name) |
Self::ViewTransitionImagePair(ref name) |
Self::ViewTransitionOld(ref name) |
Self::ViewTransitionNew(ref name) => {
// The specificity of a named view transition pseudo-element selector with a
// `<custom-ident>` argument is equivalent to a type selector.
// The specificity of a named view transition pseudo-element selector with a `*`
// argument is zero.
(name.0 != atom!("*")) as u32
},
_ => 1,
}
}
/// Whether this pseudo-element supports user action selectors.
pub fn supports_user_action_state(&self) -> bool {
(self.flags() & structs::CSS_PSEUDO_ELEMENT_SUPPORTS_USER_ACTION_STATE) != 0
@ -177,6 +200,11 @@ impl PseudoElement {
Self::SliderFill | Self::SliderTrack | Self::SliderThumb => {
pref!("layout.css.modern-range-pseudos.enabled")
},
Self::ViewTransition |
Self::ViewTransitionGroup(..) |
Self::ViewTransitionImagePair(..) |
Self::ViewTransitionOld(..) |
Self::ViewTransitionNew(..) => pref!("dom.viewTransitions.enabled"),
// If it's not explicitly enabled in UA sheets or chrome, then we're enabled for
// content.
_ => (self.flags() & structs::CSS_PSEUDO_ELEMENT_ENABLED_IN_UA_SHEETS_AND_CHROME) == 0,

View File

@ -14,6 +14,9 @@ pub enum PseudoElement {
${pseudo.capitalized_pseudo()}(thin_vec::ThinVec<Atom>),
% elif pseudo.pseudo_ident == "highlight":
${pseudo.capitalized_pseudo()}(AtomIdent),
% elif pseudo.is_named_view_transition_pseudo():
/// <pt-name-selector> = '*' | <custom-ident>
${pseudo.capitalized_pseudo()}(AtomIdent),
% else:
${pseudo.capitalized_pseudo()},
% endif
@ -138,6 +141,10 @@ impl PseudoElement {
debug_assert!(functional_pseudo_parameter.is_none());
Some(${pseudo_element_variant(pseudo)})
},
% elif pseudo.is_named_view_transition_pseudo():
PseudoStyleType::${pseudo.pseudo_ident} => {
functional_pseudo_parameter.map(PseudoElement::${pseudo.capitalized_pseudo()})
},
% endif
% endfor
PseudoStyleType::highlight => {
@ -157,7 +164,7 @@ impl PseudoElement {
% for pseudo in PSEUDOS:
% if pseudo.is_tree_pseudo_element():
PseudoElement::${pseudo.capitalized_pseudo()}(..) => PseudoStyleType::XULTree,
% elif pseudo.pseudo_ident == "highlight":
% elif pseudo.pseudo_ident == "highlight" or pseudo.is_named_view_transition_pseudo():
PseudoElement::${pseudo.capitalized_pseudo()}(..) => PseudoStyleType::${pseudo.pseudo_ident},
% else:
PseudoElement::${pseudo.capitalized_pseudo()} => PseudoStyleType::${pseudo.pseudo_ident},
@ -247,7 +254,21 @@ impl ToCss for PseudoElement {
dest.write_char(':')?;
match *self {
% for pseudo in (p for p in PSEUDOS if p.pseudo_ident != "highlight"):
%if pseudo.is_named_view_transition_pseudo():
PseudoElement::${pseudo.capitalized_pseudo()}(ref name) => {
dest.write_str("${pseudo.value}(")?;
if name.0 == atom!("*") {
// serialize_atom_identifier() may serialize "*" as "\*", so we handle it
// separately.
dest.write_char('*')?;
} else {
serialize_atom_identifier(name, dest)?;
}
dest.write_char(')')?;
}
%else:
${pseudo_element_variant(pseudo)} => dest.write_str("${pseudo.value}")?,
%endif
% endfor
PseudoElement::Highlight(ref name) => {
dest.write_str(":highlight(")?;

View File

@ -90,8 +90,20 @@ class Atom:
def is_tree_pseudo_element(self):
return self.value.startswith(":-moz-tree-")
def is_named_view_transition_pseudo(self) -> bool:
return (
self.pseudo_ident == "viewTransitionGroup"
or self.pseudo_ident == "viewTransitionImagePair"
or self.pseudo_ident == "viewTransitionOld"
or self.pseudo_ident == "viewTransitionNew"
)
def is_simple_pseudo_element(self) -> bool:
return not (self.is_tree_pseudo_element() or self.pseudo_ident == "highlight")
return not (
self.is_tree_pseudo_element()
or self.pseudo_ident == "highlight"
or self.is_named_view_transition_pseudo()
)
def collect_atoms(objdir):

View File

@ -474,12 +474,43 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> {
return Ok(pseudo);
}
}
} else if name.eq_ignore_ascii_case("highlight") {
let pseudo = PseudoElement::Highlight(AtomIdent::from(parser.expect_ident()?.as_ref()));
} else {
// <pt-name-selector> = '*' | <custom-ident>
// https://drafts.csswg.org/css-view-transitions-1/#named-view-transition-pseudo
let parse_pt_name = |input: &mut Parser<'i, '_>| {
use crate::values::CustomIdent;
if input.try_parse(|i| i.expect_delim('*')).is_ok() {
Ok(AtomIdent::new(atom!("*")))
} else {
CustomIdent::parse(input, &[]).map(|c| AtomIdent::new(c.0))
}
};
let pseudo = match_ignore_ascii_case! { &name,
"highlight" => {
PseudoElement::Highlight(AtomIdent::from(parser.expect_ident()?.as_ref()))
},
"view-transition-group" => {
PseudoElement::ViewTransitionGroup(parse_pt_name(parser)?)
},
"view-transition-image-pair" => {
PseudoElement::ViewTransitionImagePair(parse_pt_name(parser)?)
},
"view-transition-old" => {
PseudoElement::ViewTransitionOld(parse_pt_name(parser)?)
},
"view-transition-new" => {
PseudoElement::ViewTransitionNew(parse_pt_name(parser)?)
},
_ => return Err(parser.new_custom_error(
SelectorParseErrorKind::UnsupportedPseudoClassOrElement(name)
))
};
if self.is_pseudo_element_enabled(&pseudo) {
return Ok(pseudo);
}
}
Err(
parser.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClassOrElement(
name,

View File

@ -1,16 +1,4 @@
[pseudo-elements-valid-with-classes.html]
["::view-transition" should be a valid selector]
expected: FAIL
[":root::view-transition" should be a valid selector]
expected: FAIL
[".a::view-transition" should be a valid selector]
expected: FAIL
["div ::view-transition" should be a valid selector]
expected: FAIL
["::view-transition-group(*.class)" should be a valid selector]
expected: FAIL

View File

@ -1,28 +1,4 @@
[pseudo-elements-valid.html]
["::view-transition" should be a valid selector]
expected: FAIL
[":root::view-transition" should be a valid selector]
expected: FAIL
[".a::view-transition" should be a valid selector]
expected: FAIL
["div ::view-transition" should be a valid selector]
expected: FAIL
["::view-transition-group(*)" should be a valid selector]
expected: FAIL
[":root::view-transition-group(*)" should be a valid selector]
expected: FAIL
[".a::view-transition-group(*)" should be a valid selector]
expected: FAIL
["div ::view-transition-group(*)" should be a valid selector]
expected: FAIL
["::view-transition-group(*):only-child" should be a valid selector]
expected: FAIL
@ -35,18 +11,6 @@
["div ::view-transition-group(*):only-child" should be a valid selector]
expected: FAIL
["::view-transition-group(root)" should be a valid selector]
expected: FAIL
[":root::view-transition-group(root)" should be a valid selector]
expected: FAIL
[".a::view-transition-group(root)" should be a valid selector]
expected: FAIL
["div ::view-transition-group(root)" should be a valid selector]
expected: FAIL
["::view-transition-group(root):only-child" should be a valid selector]
expected: FAIL
@ -59,18 +23,6 @@
["div ::view-transition-group(root):only-child" should be a valid selector]
expected: FAIL
["::view-transition-group(dashed-ident)" should be a valid selector]
expected: FAIL
[":root::view-transition-group(dashed-ident)" should be a valid selector]
expected: FAIL
[".a::view-transition-group(dashed-ident)" should be a valid selector]
expected: FAIL
["div ::view-transition-group(dashed-ident)" should be a valid selector]
expected: FAIL
["::view-transition-group(dashed-ident):only-child" should be a valid selector]
expected: FAIL
@ -83,18 +35,6 @@
["div ::view-transition-group(dashed-ident):only-child" should be a valid selector]
expected: FAIL
["::view-transition-image-pair(*)" should be a valid selector]
expected: FAIL
[":root::view-transition-image-pair(*)" should be a valid selector]
expected: FAIL
[".a::view-transition-image-pair(*)" should be a valid selector]
expected: FAIL
["div ::view-transition-image-pair(*)" should be a valid selector]
expected: FAIL
["::view-transition-image-pair(*):only-child" should be a valid selector]
expected: FAIL
@ -107,18 +47,6 @@
["div ::view-transition-image-pair(*):only-child" should be a valid selector]
expected: FAIL
["::view-transition-image-pair(root)" should be a valid selector]
expected: FAIL
[":root::view-transition-image-pair(root)" should be a valid selector]
expected: FAIL
[".a::view-transition-image-pair(root)" should be a valid selector]
expected: FAIL
["div ::view-transition-image-pair(root)" should be a valid selector]
expected: FAIL
["::view-transition-image-pair(root):only-child" should be a valid selector]
expected: FAIL
@ -131,18 +59,6 @@
["div ::view-transition-image-pair(root):only-child" should be a valid selector]
expected: FAIL
["::view-transition-image-pair(dashed-ident)" should be a valid selector]
expected: FAIL
[":root::view-transition-image-pair(dashed-ident)" should be a valid selector]
expected: FAIL
[".a::view-transition-image-pair(dashed-ident)" should be a valid selector]
expected: FAIL
["div ::view-transition-image-pair(dashed-ident)" should be a valid selector]
expected: FAIL
["::view-transition-image-pair(dashed-ident):only-child" should be a valid selector]
expected: FAIL
@ -155,18 +71,6 @@
["div ::view-transition-image-pair(dashed-ident):only-child" should be a valid selector]
expected: FAIL
["::view-transition-old(*)" should be a valid selector]
expected: FAIL
[":root::view-transition-old(*)" should be a valid selector]
expected: FAIL
[".a::view-transition-old(*)" should be a valid selector]
expected: FAIL
["div ::view-transition-old(*)" should be a valid selector]
expected: FAIL
["::view-transition-old(*):only-child" should be a valid selector]
expected: FAIL
@ -179,18 +83,6 @@
["div ::view-transition-old(*):only-child" should be a valid selector]
expected: FAIL
["::view-transition-old(root)" should be a valid selector]
expected: FAIL
[":root::view-transition-old(root)" should be a valid selector]
expected: FAIL
[".a::view-transition-old(root)" should be a valid selector]
expected: FAIL
["div ::view-transition-old(root)" should be a valid selector]
expected: FAIL
["::view-transition-old(root):only-child" should be a valid selector]
expected: FAIL
@ -203,18 +95,6 @@
["div ::view-transition-old(root):only-child" should be a valid selector]
expected: FAIL
["::view-transition-old(dashed-ident)" should be a valid selector]
expected: FAIL
[":root::view-transition-old(dashed-ident)" should be a valid selector]
expected: FAIL
[".a::view-transition-old(dashed-ident)" should be a valid selector]
expected: FAIL
["div ::view-transition-old(dashed-ident)" should be a valid selector]
expected: FAIL
["::view-transition-old(dashed-ident):only-child" should be a valid selector]
expected: FAIL
@ -227,18 +107,6 @@
["div ::view-transition-old(dashed-ident):only-child" should be a valid selector]
expected: FAIL
["::view-transition-new(*)" should be a valid selector]
expected: FAIL
[":root::view-transition-new(*)" should be a valid selector]
expected: FAIL
[".a::view-transition-new(*)" should be a valid selector]
expected: FAIL
["div ::view-transition-new(*)" should be a valid selector]
expected: FAIL
["::view-transition-new(*):only-child" should be a valid selector]
expected: FAIL
@ -251,18 +119,6 @@
["div ::view-transition-new(*):only-child" should be a valid selector]
expected: FAIL
["::view-transition-new(root)" should be a valid selector]
expected: FAIL
[":root::view-transition-new(root)" should be a valid selector]
expected: FAIL
[".a::view-transition-new(root)" should be a valid selector]
expected: FAIL
["div ::view-transition-new(root)" should be a valid selector]
expected: FAIL
["::view-transition-new(root):only-child" should be a valid selector]
expected: FAIL
@ -275,18 +131,6 @@
["div ::view-transition-new(root):only-child" should be a valid selector]
expected: FAIL
["::view-transition-new(dashed-ident)" should be a valid selector]
expected: FAIL
[":root::view-transition-new(dashed-ident)" should be a valid selector]
expected: FAIL
[".a::view-transition-new(dashed-ident)" should be a valid selector]
expected: FAIL
["div ::view-transition-new(dashed-ident)" should be a valid selector]
expected: FAIL
["::view-transition-new(dashed-ident):only-child" should be a valid selector]
expected: FAIL

View File

@ -2504,6 +2504,11 @@ STATIC_ATOMS = [
PseudoElementAtom("PseudoElement_highlight", ":highlight"),
PseudoElementAtom("PseudoElement_selection", ":selection"),
PseudoElementAtom("PseudoElement_targetText", ":target-text"),
PseudoElementAtom("PseudoElement_viewTransition", ":view-transition"),
PseudoElementAtom("PseudoElement_viewTransitionGroup", ":view-transition-group"),
PseudoElementAtom("PseudoElement_viewTransitionImagePair", ":view-transition-image-pair"),
PseudoElementAtom("PseudoElement_viewTransitionOld", ":view-transition-old"),
PseudoElementAtom("PseudoElement_viewTransitionNew", ":view-transition-new"),
PseudoElementAtom("PseudoElement_mozFocusInner", ":-moz-focus-inner"),
PseudoElementAtom("PseudoElement_mozNumberSpinBox", ":-moz-number-spin-box"),
PseudoElementAtom("PseudoElement_mozNumberSpinUp", ":-moz-number-spin-up"),