style: Basic implementation of Custom Highlight API

Added WebIDL interfaces as per spec, added some necessary changes to support maplike and setlike structures to be accessed from C++.

Added `::highlight(foo)` pseudo element to CSS engine.

Implemented Highlight as new kind of `Selection` using `HighlightType::eHighlight`. This implies Selections being added/removed during runtime (one `Selection` object per highlight identifier), therefore a dynamic container for highlight `Selection` objects was added to `nsFrameSelection`. Also, the painting code queries the highlight style for highlight Selections.

Implementation is currently hidden behind a pref `dom.customHighlightAPI.enabled`.

Differential Revision: https://phabricator.services.mozilla.com/D164203
This commit is contained in:
Jan-Niklas Jaeschke 2023-01-27 11:42:18 +00:00 committed by Martin Robinson
parent 29c6094c80
commit 896aac5e4a
5 changed files with 65 additions and 5 deletions

View file

@ -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<PseudoElement, SelectorParseError<'i>> {
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<DummyAtom> {
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());

View file

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

View file

@ -12,6 +12,8 @@ pub enum PseudoElement {
/// ${pseudo.value}
% if pseudo.is_tree_pseudo_element():
${pseudo.capitalized_pseudo()}(Box<Box<[Atom]>>),
% 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 ""}\
</%def>
impl PseudoElement {
@ -131,7 +133,7 @@ impl PseudoElement {
pub fn from_pseudo_type(type_: PseudoStyleType) -> Option<Self> {
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<W>(&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)?;

View file

@ -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 = []

View file

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