mirror of
https://github.com/servo/servo.git
synced 2025-08-04 13:10:20 +01:00
Implement parsing of tree pseudo-elements.
This commit is contained in:
parent
832c4c4079
commit
a307653581
4 changed files with 94 additions and 21 deletions
|
@ -8,7 +8,7 @@
|
||||||
//! `pseudo_element_definition.mako.rs`. If you touch that file, you probably
|
//! `pseudo_element_definition.mako.rs`. If you touch that file, you probably
|
||||||
//! need to update the checked-in files for Servo.
|
//! need to update the checked-in files for Servo.
|
||||||
|
|
||||||
use cssparser::ToCss;
|
use cssparser::{ToCss, serialize_identifier};
|
||||||
use gecko_bindings::structs::{self, CSSPseudoElementType};
|
use gecko_bindings::structs::{self, CSSPseudoElementType};
|
||||||
use selector_parser::{NonTSPseudoClass, PseudoElementCascadeType, SelectorImpl};
|
use selector_parser::{NonTSPseudoClass, PseudoElementCascadeType, SelectorImpl};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
@ -114,10 +114,3 @@ impl PseudoElement {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToCss for PseudoElement {
|
|
||||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
|
||||||
dest.write_char(':')?;
|
|
||||||
dest.write_str(self.as_str())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -7,7 +7,11 @@
|
||||||
pub enum PseudoElement {
|
pub enum PseudoElement {
|
||||||
% for pseudo in PSEUDOS:
|
% for pseudo in PSEUDOS:
|
||||||
/// ${pseudo.value}
|
/// ${pseudo.value}
|
||||||
|
% if pseudo.is_tree_pseudo_element():
|
||||||
|
${pseudo.capitalized()}(Box<[String]>),
|
||||||
|
% else:
|
||||||
${pseudo.capitalized()},
|
${pseudo.capitalized()},
|
||||||
|
% endif
|
||||||
% endfor
|
% endfor
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,8 +27,11 @@ pub const EAGER_PSEUDOS: [PseudoElement; EAGER_PSEUDO_COUNT] = [
|
||||||
% endfor
|
% endfor
|
||||||
];
|
];
|
||||||
|
|
||||||
<%def name="pseudo_element_variant(pseudo)">
|
<% TREE_PSEUDOS = [pseudo for pseudo in PSEUDOS if pseudo.is_tree_pseudo_element()] %>
|
||||||
PseudoElement::${pseudo.capitalized()}
|
<% SIMPLE_PSEUDOS = [pseudo for pseudo in PSEUDOS if not pseudo.is_tree_pseudo_element()] %>
|
||||||
|
|
||||||
|
<%def name="pseudo_element_variant(pseudo, tree_arg='..')">\
|
||||||
|
PseudoElement::${pseudo.capitalized()}${"({})".format(tree_arg) if pseudo.is_tree_pseudo_element() else ""}\
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
impl PseudoElement {
|
impl PseudoElement {
|
||||||
|
@ -33,7 +40,7 @@ impl PseudoElement {
|
||||||
pub fn each_simple<F>(mut fun: F)
|
pub fn each_simple<F>(mut fun: F)
|
||||||
where F: FnMut(Self),
|
where F: FnMut(Self),
|
||||||
{
|
{
|
||||||
% for pseudo in PSEUDOS:
|
% for pseudo in SIMPLE_PSEUDOS:
|
||||||
fun(${pseudo_element_variant(pseudo)});
|
fun(${pseudo_element_variant(pseudo)});
|
||||||
% endfor
|
% endfor
|
||||||
}
|
}
|
||||||
|
@ -74,7 +81,9 @@ impl PseudoElement {
|
||||||
match *self {
|
match *self {
|
||||||
% for pseudo in PSEUDOS:
|
% for pseudo in PSEUDOS:
|
||||||
${pseudo_element_variant(pseudo)} =>
|
${pseudo_element_variant(pseudo)} =>
|
||||||
% if pseudo.is_anon_box():
|
% if pseudo.is_tree_pseudo_element():
|
||||||
|
0,
|
||||||
|
% elif pseudo.is_anon_box():
|
||||||
structs::CSS_PSEUDO_ELEMENT_UA_SHEET_ONLY,
|
structs::CSS_PSEUDO_ELEMENT_UA_SHEET_ONLY,
|
||||||
% else:
|
% else:
|
||||||
structs::SERVO_CSS_PSEUDO_ELEMENT_FLAGS_${pseudo.original_ident},
|
structs::SERVO_CSS_PSEUDO_ELEMENT_FLAGS_${pseudo.original_ident},
|
||||||
|
@ -102,7 +111,9 @@ impl PseudoElement {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn from_anon_box_atom(atom: &Atom) -> Option<Self> {
|
pub fn from_anon_box_atom(atom: &Atom) -> Option<Self> {
|
||||||
% for pseudo in PSEUDOS:
|
% for pseudo in PSEUDOS:
|
||||||
% if pseudo.is_anon_box():
|
% if pseudo.is_tree_pseudo_element():
|
||||||
|
// We cannot generate ${pseudo_element_variant(pseudo)} from just an atom.
|
||||||
|
% elif pseudo.is_anon_box():
|
||||||
if atom == &atom!("${pseudo.value}") {
|
if atom == &atom!("${pseudo.value}") {
|
||||||
return Some(${pseudo_element_variant(pseudo)});
|
return Some(${pseudo_element_variant(pseudo)});
|
||||||
}
|
}
|
||||||
|
@ -122,7 +133,10 @@ impl PseudoElement {
|
||||||
pub fn from_slice(s: &str, in_ua_stylesheet: bool) -> Option<Self> {
|
pub fn from_slice(s: &str, in_ua_stylesheet: bool) -> Option<Self> {
|
||||||
use std::ascii::AsciiExt;
|
use std::ascii::AsciiExt;
|
||||||
|
|
||||||
% for pseudo in PSEUDOS:
|
// We don't need to support tree pseudos because functional
|
||||||
|
// pseudo-elements needs arguments, and thus should be created
|
||||||
|
// via other methods.
|
||||||
|
% for pseudo in SIMPLE_PSEUDOS:
|
||||||
if in_ua_stylesheet || ${pseudo_element_variant(pseudo)}.exposed_in_non_ua_sheets() {
|
if in_ua_stylesheet || ${pseudo_element_variant(pseudo)}.exposed_in_non_ua_sheets() {
|
||||||
if s.eq_ignore_ascii_case("${pseudo.value[1:]}") {
|
if s.eq_ignore_ascii_case("${pseudo.value[1:]}") {
|
||||||
return Some(${pseudo_element_variant(pseudo)});
|
return Some(${pseudo_element_variant(pseudo)});
|
||||||
|
@ -133,13 +147,47 @@ impl PseudoElement {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the pseudo-element's definition as a string, with only one colon
|
/// Constructs a tree pseudo-element from the given name and arguments.
|
||||||
/// before it.
|
/// "name" must start with "-moz-tree-".
|
||||||
pub fn as_str(&self) -> &'static str {
|
///
|
||||||
match *self {
|
/// Returns `None` if the pseudo-element is not recognized.
|
||||||
% for pseudo in PSEUDOS:
|
#[inline]
|
||||||
PseudoElement::${pseudo.capitalized()} => "${pseudo.value}",
|
pub fn tree_pseudo_element(name: &str, args: Box<[String]>) -> Option<Self> {
|
||||||
|
use std::ascii::AsciiExt;
|
||||||
|
debug_assert!(name.starts_with("-moz-tree-"));
|
||||||
|
let tree_part = &name[10..];
|
||||||
|
% for pseudo in TREE_PSEUDOS:
|
||||||
|
if tree_part.eq_ignore_ascii_case("${pseudo.value[11:]}") {
|
||||||
|
return Some(${pseudo_element_variant(pseudo, "args")});
|
||||||
|
}
|
||||||
% endfor
|
% endfor
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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:
|
||||||
|
${pseudo_element_variant(pseudo)} => dest.write_str("${pseudo.value}")?,
|
||||||
|
% endfor
|
||||||
|
}
|
||||||
|
match *self {
|
||||||
|
${" |\n ".join("PseudoElement::{}(ref args)".format(pseudo.capitalized())
|
||||||
|
for pseudo in TREE_PSEUDOS)} => {
|
||||||
|
dest.write_char('(')?;
|
||||||
|
let mut iter = args.iter();
|
||||||
|
if let Some(first) = iter.next() {
|
||||||
|
serialize_identifier(first, dest)?;
|
||||||
|
for item in iter {
|
||||||
|
dest.write_str(", ")?;
|
||||||
|
serialize_identifier(item, dest)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dest.write_char(')')
|
||||||
|
}
|
||||||
|
_ => Ok(()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -103,6 +103,9 @@ class Atom:
|
||||||
def is_anon_box(self):
|
def is_anon_box(self):
|
||||||
return self.type() == "nsICSSAnonBoxPseudo"
|
return self.type() == "nsICSSAnonBoxPseudo"
|
||||||
|
|
||||||
|
def is_tree_pseudo_element(self):
|
||||||
|
return self.value.startswith(":-moz-tree-")
|
||||||
|
|
||||||
|
|
||||||
def collect_atoms(objdir):
|
def collect_atoms(objdir):
|
||||||
atoms = []
|
atoms = []
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
//! Gecko-specific bits for selector-parsing.
|
//! Gecko-specific bits for selector-parsing.
|
||||||
|
|
||||||
use cssparser::{Parser, ToCss, CompactCowStr};
|
use cssparser::{BasicParseError, Parser, ToCss, Token, CompactCowStr};
|
||||||
use element_state::ElementState;
|
use element_state::ElementState;
|
||||||
use gecko_bindings::structs::CSSPseudoClassType;
|
use gecko_bindings::structs::CSSPseudoClassType;
|
||||||
use selector_parser::{SelectorParser, PseudoElementCascadeType};
|
use selector_parser::{SelectorParser, PseudoElementCascadeType};
|
||||||
|
@ -267,6 +267,11 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> {
|
||||||
type Impl = SelectorImpl;
|
type Impl = SelectorImpl;
|
||||||
type Error = StyleParseError<'i>;
|
type Error = StyleParseError<'i>;
|
||||||
|
|
||||||
|
fn is_pseudo_element_allows_single_colon(name: &CompactCowStr<'i>) -> bool {
|
||||||
|
::selectors::parser::is_css2_pseudo_element(name) ||
|
||||||
|
name.starts_with("-moz-tree-") // tree pseudo-elements
|
||||||
|
}
|
||||||
|
|
||||||
fn parse_non_ts_pseudo_class(&self, name: CompactCowStr<'i>)
|
fn parse_non_ts_pseudo_class(&self, name: CompactCowStr<'i>)
|
||||||
-> Result<NonTSPseudoClass, ParseError<'i>> {
|
-> Result<NonTSPseudoClass, ParseError<'i>> {
|
||||||
macro_rules! pseudo_class_parse {
|
macro_rules! pseudo_class_parse {
|
||||||
|
@ -338,6 +343,30 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> {
|
||||||
.ok_or(SelectorParseError::UnexpectedIdent(name.clone()).into())
|
.ok_or(SelectorParseError::UnexpectedIdent(name.clone()).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_functional_pseudo_element<'t>(&self, name: CompactCowStr<'i>,
|
||||||
|
parser: &mut Parser<'i, 't>)
|
||||||
|
-> Result<PseudoElement, ParseError<'i>> {
|
||||||
|
if name.starts_with("-moz-tree-") {
|
||||||
|
// Tree pseudo-elements can have zero or more arguments,
|
||||||
|
// separated by either comma or space.
|
||||||
|
let mut args = Vec::new();
|
||||||
|
loop {
|
||||||
|
match parser.next() {
|
||||||
|
Ok(Token::Ident(ident)) => args.push(ident.into_owned()),
|
||||||
|
Ok(Token::Comma) => {},
|
||||||
|
Ok(t) => return Err(BasicParseError::UnexpectedToken(t).into()),
|
||||||
|
Err(BasicParseError::EndOfInput) => break,
|
||||||
|
_ => unreachable!("Parser::next() shouldn't return any other error"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let args = args.into_boxed_slice();
|
||||||
|
if let Some(pseudo) = PseudoElement::tree_pseudo_element(&name, args) {
|
||||||
|
return Ok(pseudo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(SelectorParseError::UnexpectedIdent(name.clone()).into())
|
||||||
|
}
|
||||||
|
|
||||||
fn default_namespace(&self) -> Option<Namespace> {
|
fn default_namespace(&self) -> Option<Namespace> {
|
||||||
self.namespaces.default.clone().as_ref().map(|&(ref ns, _)| ns.clone())
|
self.namespaces.default.clone().as_ref().map(|&(ref ns, _)| ns.clone())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue