diff --git a/components/selectors/parser.rs b/components/selectors/parser.rs index 7874a78e911..808e06e9c24 100644 --- a/components/selectors/parser.rs +++ b/components/selectors/parser.rs @@ -2920,6 +2920,7 @@ pub mod tests { pub enum PseudoElement { Before, After, + Highlight(String), } impl parser::PseudoElement for PseudoElement { @@ -2973,6 +2974,11 @@ pub mod tests { match *self { PseudoElement::Before => dest.write_str("::before"), PseudoElement::After => dest.write_str("::after"), + PseudoElement::Highlight(ref name) => { + dest.write_str("::highlight(")?; + serialize_identifier(&name, dest)?; + dest.write_char(')') + }, } } } @@ -3133,6 +3139,23 @@ pub mod tests { ) } + fn parse_functional_pseudo_element<'t>( + &self, + name: CowRcStr<'i>, + parser: &mut CssParser<'i, 't>, + ) -> Result> { + match_ignore_ascii_case! {&name, + "highlight" => return Ok(PseudoElement::Highlight(parser.expect_ident()?.as_ref().to_owned())), + _ => {} + } + Err( + parser.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClassOrElement( + name, + )), + ) + } + + fn default_namespace(&self) -> Option { self.default_ns.clone() } @@ -3660,6 +3683,8 @@ pub mod tests { )])) ); + assert!(parse("::highlight(foo)").is_ok()); + assert!(parse("::slotted()").is_err()); assert!(parse("::slotted(div)").is_ok()); assert!(parse("::slotted(div).foo").is_err()); diff --git a/components/style/gecko/pseudo_element.rs b/components/style/gecko/pseudo_element.rs index 855fed9a0d8..c5b2686b958 100644 --- a/components/style/gecko/pseudo_element.rs +++ b/components/style/gecko/pseudo_element.rs @@ -14,8 +14,10 @@ use crate::properties::{ComputedValues, PropertyFlags}; use crate::selector_parser::{PseudoElementCascadeType, SelectorImpl}; use crate::str::{starts_with_ignore_ascii_case, string_as_ascii_lowercase}; use crate::string_cache::Atom; +use crate::values::AtomIdent; use crate::values::serialize_atom_identifier; use cssparser::ToCss; +use static_prefs::pref; use std::fmt; include!(concat!( @@ -153,6 +155,19 @@ impl PseudoElement { !self.is_eager() && !self.is_precomputed() } + /// The identifier of the highlight this pseudo-element represents. + pub fn highlight_name(&self) -> Option<&AtomIdent> { + match &*self { + PseudoElement::Highlight(name) => Some(&name), + _ => None + } + } + + /// Whether this pseudo-element is the ::highlight pseudo. + pub fn is_highlight(&self) -> bool { + matches!(*self, PseudoElement::Highlight(_)) + } + /// 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 @@ -160,7 +175,10 @@ impl PseudoElement { /// Whether this pseudo-element is enabled for all content. pub fn enabled_in_content(&self) -> bool { - self.flags() & structs::CSS_PSEUDO_ELEMENT_ENABLED_IN_UA_SHEETS_AND_CHROME == 0 + if self.is_highlight() && !pref!("dom.customHighlightAPI.enabled") { + return false; + } + return self.flags() & structs::CSS_PSEUDO_ELEMENT_ENABLED_IN_UA_SHEETS_AND_CHROME == 0 } /// Whether this pseudo is enabled explicitly in UA sheets. diff --git a/components/style/gecko/pseudo_element_definition.mako.rs b/components/style/gecko/pseudo_element_definition.mako.rs index eab97c61bea..b7f8cca409c 100644 --- a/components/style/gecko/pseudo_element_definition.mako.rs +++ b/components/style/gecko/pseudo_element_definition.mako.rs @@ -12,6 +12,8 @@ pub enum PseudoElement { /// ${pseudo.value} % if pseudo.is_tree_pseudo_element(): ${pseudo.capitalized_pseudo()}(Box>), + % elif pseudo.pseudo_ident == "highlight": + ${pseudo.capitalized_pseudo()}(AtomIdent), % else: ${pseudo.capitalized_pseudo()}, % endif @@ -25,7 +27,7 @@ pub enum PseudoElement { /// nsCSSPseudoElements::IsEagerlyCascadedInServo. <% EAGER_PSEUDOS = ["Before", "After", "FirstLine", "FirstLetter"] %> <% TREE_PSEUDOS = [pseudo for pseudo in PSEUDOS if pseudo.is_tree_pseudo_element()] %> -<% SIMPLE_PSEUDOS = [pseudo for pseudo in PSEUDOS if not pseudo.is_tree_pseudo_element()] %> +<% SIMPLE_PSEUDOS = [pseudo for pseudo in PSEUDOS if pseudo.is_simple_pseudo_element()] %> /// The number of eager pseudo-elements. pub const EAGER_PSEUDO_COUNT: usize = ${len(EAGER_PSEUDOS)}; @@ -47,7 +49,7 @@ pub const EAGER_PSEUDOS: [PseudoElement; EAGER_PSEUDO_COUNT] = [ ]; <%def name="pseudo_element_variant(pseudo, tree_arg='..')">\ -PseudoElement::${pseudo.capitalized_pseudo()}${"({})".format(tree_arg) if pseudo.is_tree_pseudo_element() else ""}\ +PseudoElement::${pseudo.capitalized_pseudo()}${"({})".format(tree_arg) if not pseudo.is_simple_pseudo_element() else ""}\ impl PseudoElement { @@ -131,7 +133,7 @@ impl PseudoElement { pub fn from_pseudo_type(type_: PseudoStyleType) -> Option { match type_ { % for pseudo in PSEUDOS: - % if not pseudo.is_tree_pseudo_element(): + % if pseudo.is_simple_pseudo_element(): PseudoStyleType::${pseudo.pseudo_ident} => { Some(${pseudo_element_variant(pseudo)}) }, @@ -148,6 +150,8 @@ impl PseudoElement { % for pseudo in PSEUDOS: % if pseudo.is_tree_pseudo_element(): PseudoElement::${pseudo.capitalized_pseudo()}(..) => PseudoStyleType::XULTree, + % elif pseudo.pseudo_ident == "highlight": + PseudoElement::${pseudo.capitalized_pseudo()}(..) => PseudoStyleType::${pseudo.pseudo_ident}, % else: PseudoElement::${pseudo.capitalized_pseudo()} => PseudoStyleType::${pseudo.pseudo_ident}, % endif @@ -240,9 +244,14 @@ impl ToCss for PseudoElement { fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write { dest.write_char(':')?; match *self { - % for pseudo in PSEUDOS: + % for pseudo in (p for p in PSEUDOS if p.pseudo_ident != "highlight"): ${pseudo_element_variant(pseudo)} => dest.write_str("${pseudo.value}")?, % endfor + PseudoElement::Highlight(ref name) => { + dest.write_str(":highlight(")?; + serialize_atom_identifier(name, dest)?; + dest.write_char(')')?; + } PseudoElement::UnknownWebkit(ref atom) => { dest.write_str(":-webkit-")?; serialize_atom_identifier(atom, dest)?; diff --git a/components/style/gecko/regen_atoms.py b/components/style/gecko/regen_atoms.py index f05ddb8d644..61f2fc4c635 100755 --- a/components/style/gecko/regen_atoms.py +++ b/components/style/gecko/regen_atoms.py @@ -90,6 +90,9 @@ class Atom: def is_tree_pseudo_element(self): return self.value.startswith(":-moz-tree-") + def is_simple_pseudo_element(self) -> bool: + return not (self.is_tree_pseudo_element() or self.pseudo_ident == "highlight") + def collect_atoms(objdir): atoms = [] diff --git a/components/style/gecko/selector_parser.rs b/components/style/gecko/selector_parser.rs index d149cf757ac..50858549d55 100644 --- a/components/style/gecko/selector_parser.rs +++ b/components/style/gecko/selector_parser.rs @@ -443,6 +443,11 @@ 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())); + if self.is_pseudo_element_enabled(&pseudo) { + return Ok(pseudo); + } } Err( parser.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClassOrElement(