mirror of
https://github.com/servo/servo.git
synced 2025-08-06 14:10:11 +01:00
style: Accept a comma-separated list of language codes in the :lang() pseudo
This is the other extension to the :lang() pseudo-class in Selectors-4. (Also supported in Safari.) Depends on D174999 Differential Revision: https://phabricator.services.mozilla.com/D175000
This commit is contained in:
parent
13e2d10474
commit
a2df8f7ea5
2 changed files with 48 additions and 15 deletions
|
@ -19,7 +19,7 @@ use dom::{DocumentState, ElementState};
|
||||||
use selectors::parser::SelectorParseErrorKind;
|
use selectors::parser::SelectorParseErrorKind;
|
||||||
use selectors::SelectorList;
|
use selectors::SelectorList;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss as ToCss_};
|
use style_traits::{Comma, CssWriter, OneOrMoreSeparated, ParseError, StyleParseErrorKind, ToCss as ToCss_};
|
||||||
|
|
||||||
pub use crate::gecko::pseudo_element::{
|
pub use crate::gecko::pseudo_element::{
|
||||||
PseudoElement, EAGER_PSEUDOS, EAGER_PSEUDO_COUNT, PSEUDO_COUNT,
|
PseudoElement, EAGER_PSEUDOS, EAGER_PSEUDO_COUNT, PSEUDO_COUNT,
|
||||||
|
@ -38,7 +38,13 @@ bitflags! {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The type used to store the language argument to the `:lang` pseudo-class.
|
/// The type used to store the language argument to the `:lang` pseudo-class.
|
||||||
pub type Lang = AtomIdent;
|
#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq, ToShmem)]
|
||||||
|
pub enum Lang {
|
||||||
|
/// A single language code.
|
||||||
|
Single(AtomIdent),
|
||||||
|
/// A list of language codes.
|
||||||
|
List(Box<Vec<AtomIdent>>),
|
||||||
|
}
|
||||||
|
|
||||||
macro_rules! pseudo_class_name {
|
macro_rules! pseudo_class_name {
|
||||||
([$(($css:expr, $name:ident, $state:tt, $flags:tt),)*]) => {
|
([$(($css:expr, $name:ident, $state:tt, $flags:tt),)*]) => {
|
||||||
|
@ -60,6 +66,10 @@ macro_rules! pseudo_class_name {
|
||||||
}
|
}
|
||||||
apply_non_ts_list!(pseudo_class_name);
|
apply_non_ts_list!(pseudo_class_name);
|
||||||
|
|
||||||
|
impl OneOrMoreSeparated for AtomIdent {
|
||||||
|
type S = Comma;
|
||||||
|
}
|
||||||
|
|
||||||
impl ToCss for NonTSPseudoClass {
|
impl ToCss for NonTSPseudoClass {
|
||||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result
|
fn to_css<W>(&self, dest: &mut W) -> fmt::Result
|
||||||
where
|
where
|
||||||
|
@ -71,7 +81,10 @@ impl ToCss for NonTSPseudoClass {
|
||||||
$(NonTSPseudoClass::$name => concat!(":", $css),)*
|
$(NonTSPseudoClass::$name => concat!(":", $css),)*
|
||||||
NonTSPseudoClass::Lang(ref s) => {
|
NonTSPseudoClass::Lang(ref s) => {
|
||||||
dest.write_str(":lang(")?;
|
dest.write_str(":lang(")?;
|
||||||
cssparser::ToCss::to_css(s, dest)?;
|
match &s {
|
||||||
|
Lang::Single(lang) => cssparser::ToCss::to_css(lang, dest)?,
|
||||||
|
Lang::List(list) => list.to_css(&mut CssWriter::new(dest))?,
|
||||||
|
}
|
||||||
return dest.write_char(')');
|
return dest.write_char(')');
|
||||||
},
|
},
|
||||||
NonTSPseudoClass::MozLocaleDir(ref dir) => {
|
NonTSPseudoClass::MozLocaleDir(ref dir) => {
|
||||||
|
@ -375,8 +388,17 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> {
|
||||||
) -> Result<NonTSPseudoClass, ParseError<'i>> {
|
) -> Result<NonTSPseudoClass, ParseError<'i>> {
|
||||||
let pseudo_class = match_ignore_ascii_case! { &name,
|
let pseudo_class = match_ignore_ascii_case! { &name,
|
||||||
"lang" => {
|
"lang" => {
|
||||||
let name = parser.expect_ident_or_string()?;
|
let result = parser.parse_comma_separated(|input| {
|
||||||
NonTSPseudoClass::Lang(Lang::from(name.as_ref()))
|
Ok(AtomIdent::from(input.expect_ident_or_string()?.as_ref()))
|
||||||
|
})?;
|
||||||
|
if result.is_empty() {
|
||||||
|
return Err(parser.new_custom_error(StyleParseErrorKind::UnspecifiedError));
|
||||||
|
}
|
||||||
|
if result.len() == 1 {
|
||||||
|
NonTSPseudoClass::Lang(Lang::Single(result[0].clone()))
|
||||||
|
} else {
|
||||||
|
NonTSPseudoClass::Lang(Lang::List(Box::new(result)))
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"-moz-locale-dir" => {
|
"-moz-locale-dir" => {
|
||||||
NonTSPseudoClass::MozLocaleDir(Direction::parse(parser)?)
|
NonTSPseudoClass::MozLocaleDir(Direction::parse(parser)?)
|
||||||
|
|
|
@ -1560,20 +1560,31 @@ impl<'le> TElement for GeckoElement<'le> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn match_element_lang(&self, override_lang: Option<Option<AttrValue>>, value: &Lang) -> bool {
|
fn match_element_lang(&self, override_lang: Option<Option<AttrValue>>, value: &Lang) -> bool {
|
||||||
// Gecko supports :lang() from CSS Selectors 3, which only accepts a
|
// Gecko supports :lang() from CSS Selectors 4, which accepts a list
|
||||||
// single language tag, and which performs simple dash-prefix matching
|
// of language tags, and does BCP47-style range matching.
|
||||||
// on it.
|
|
||||||
let override_lang_ptr = match override_lang {
|
let override_lang_ptr = match override_lang {
|
||||||
Some(Some(ref atom)) => atom.as_ptr(),
|
Some(Some(ref atom)) => atom.as_ptr(),
|
||||||
_ => ptr::null_mut(),
|
_ => ptr::null_mut(),
|
||||||
};
|
};
|
||||||
unsafe {
|
match value {
|
||||||
Gecko_MatchLang(
|
Lang::Single(lang) => unsafe {
|
||||||
self.0,
|
Gecko_MatchLang(
|
||||||
override_lang_ptr,
|
self.0,
|
||||||
override_lang.is_some(),
|
override_lang_ptr,
|
||||||
value.as_slice().as_ptr(),
|
override_lang.is_some(),
|
||||||
)
|
lang.as_slice().as_ptr(),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
Lang::List(list) => {
|
||||||
|
list.iter().any(|lang| unsafe {
|
||||||
|
Gecko_MatchLang(
|
||||||
|
self.0,
|
||||||
|
override_lang_ptr,
|
||||||
|
override_lang.is_some(),
|
||||||
|
lang.as_slice().as_ptr(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue