Skip to content

Commit

Permalink
style: Add support for 's' flag on attribute selectors.
Browse files Browse the repository at this point in the history
We could keep using ParsedCaseSensitivity::CaseSensitive as a temporary stand-in
for "case-sensitive or maybe not depending on what HTML says" until we check the
attribute list, but it seems better to make that explicit.

Differential Revision: https://phabricator.services.mozilla.com/D14093
  • Loading branch information
bzbarsky authored and emilio committed Dec 16, 2018
1 parent 901c055 commit a519d9e
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 25 deletions.
12 changes: 10 additions & 2 deletions components/selectors/attr.rs
Expand Up @@ -134,8 +134,13 @@ pub static SELECTOR_WHITESPACE: &'static [char] = &[' ', '\t', '\n', '\r', '\x0C

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ParsedCaseSensitivity {
CaseSensitive,
// 's' was specified.
ExplicitCaseSensitive,
// 'i' was specified.
AsciiCaseInsensitive,
// No flags were specified and HTML says this is a case-sensitive attribute.
CaseSensitive,
// No flags were specified and HTML says this is a case-insensitive attribute.
AsciiCaseInsensitiveIfInHtmlElementInHtmlDocument,
}

Expand All @@ -150,7 +155,10 @@ impl ParsedCaseSensitivity {
ParsedCaseSensitivity::AsciiCaseInsensitiveIfInHtmlElementInHtmlDocument => {
CaseSensitivity::CaseSensitive
},
ParsedCaseSensitivity::CaseSensitive => CaseSensitivity::CaseSensitive,
ParsedCaseSensitivity::CaseSensitive |
ParsedCaseSensitivity::ExplicitCaseSensitive => {
CaseSensitivity::CaseSensitive
},
ParsedCaseSensitivity::AsciiCaseInsensitive => CaseSensitivity::AsciiCaseInsensitive,
}
}
Expand Down
86 changes: 63 additions & 23 deletions components/selectors/parser.rs
Expand Up @@ -1223,6 +1223,7 @@ impl<Impl: SelectorImpl> ToCss for Component<Impl> {
ParsedCaseSensitivity::CaseSensitive |
ParsedCaseSensitivity::AsciiCaseInsensitiveIfInHtmlElementInHtmlDocument => {},
ParsedCaseSensitivity::AsciiCaseInsensitive => dest.write_str(" i")?,
ParsedCaseSensitivity::ExplicitCaseSensitive => dest.write_str(" s")?,
}
dest.write_char(']')
},
Expand Down Expand Up @@ -1301,6 +1302,7 @@ impl<Impl: SelectorImpl> ToCss for AttrSelectorWithOptionalNamespace<Impl> {
ParsedCaseSensitivity::CaseSensitive |
ParsedCaseSensitivity::AsciiCaseInsensitiveIfInHtmlElementInHtmlDocument => {},
ParsedCaseSensitivity::AsciiCaseInsensitive => dest.write_str(" i")?,
ParsedCaseSensitivity::ExplicitCaseSensitive => dest.write_str(" i")?,
}
},
}
Expand Down Expand Up @@ -1711,24 +1713,15 @@ where
AttrSelectorOperator::Suffix => value.is_empty(),
};

let mut case_sensitivity = parse_attribute_flags(input)?;
let attribute_flags = parse_attribute_flags(input)?;

let value = value.as_ref().into();
let local_name_lower;
let local_name_is_ascii_lowercase;
let case_sensitivity;
{
let local_name_lower_cow = to_ascii_lowercase(&local_name);
if let ParsedCaseSensitivity::CaseSensitive = case_sensitivity {
if namespace.is_none() && include!(concat!(
env!("OUT_DIR"),
"/ascii_case_insensitive_html_attributes.rs"
))
.contains(&*local_name_lower_cow)
{
case_sensitivity =
ParsedCaseSensitivity::AsciiCaseInsensitiveIfInHtmlElementInHtmlDocument
}
}
case_sensitivity = attribute_flags.to_case_sensitivity(local_name_lower_cow.as_ref(), namespace.is_some());
local_name_lower = local_name_lower_cow.as_ref().into();
local_name_is_ascii_lowercase = matches!(local_name_lower_cow, Cow::Borrowed(..));
}
Expand Down Expand Up @@ -1758,20 +1751,67 @@ where
}
}

/// An attribute selector can have 's' or 'i' as flags, or no flags at all.
enum AttributeFlags {
// Matching should be case-sensitive ('s' flag).
CaseSensitive,
// Matching should be case-insensitive ('i' flag).
AsciiCaseInsensitive,
// No flags. Matching behavior depends on the name of the attribute.
CaseSensitivityDependsOnName
}

impl AttributeFlags {
fn to_case_sensitivity(
self,
local_name: &str,
have_namespace: bool,
) -> ParsedCaseSensitivity {
match self {
AttributeFlags::CaseSensitive =>
ParsedCaseSensitivity::ExplicitCaseSensitive,
AttributeFlags::AsciiCaseInsensitive =>
ParsedCaseSensitivity::AsciiCaseInsensitive,
AttributeFlags::CaseSensitivityDependsOnName => {
if !have_namespace && include!(concat!(
env!("OUT_DIR"),
"/ascii_case_insensitive_html_attributes.rs"
))
.contains(local_name)
{
ParsedCaseSensitivity::AsciiCaseInsensitiveIfInHtmlElementInHtmlDocument
} else {
ParsedCaseSensitivity::CaseSensitive
}
}
}
}
}

fn parse_attribute_flags<'i, 't>(
input: &mut CssParser<'i, 't>,
) -> Result<ParsedCaseSensitivity, BasicParseError<'i>> {
) -> Result<AttributeFlags, BasicParseError<'i>> {
let location = input.current_source_location();
match input.next() {
Err(_) => {
// Selectors spec says language-defined, but HTML says sensitive.
Ok(ParsedCaseSensitivity::CaseSensitive)
},
Ok(&Token::Ident(ref value)) if value.eq_ignore_ascii_case("i") => {
Ok(ParsedCaseSensitivity::AsciiCaseInsensitive)
},
Ok(t) => Err(location.new_basic_unexpected_token_error(t.clone())),
}
let token = match input.next() {
Ok(t) => t,
Err(..) => {
// Selectors spec says language-defined; HTML says it depends on the
// exact attribute name.
return Ok(AttributeFlags::CaseSensitivityDependsOnName);
}
};

let ident = match *token {
Token::Ident(ref i) => i,
ref other => return Err(location.new_basic_unexpected_token_error(other.clone())),
};

Ok(match_ignore_ascii_case! {
ident,
"i" => AttributeFlags::AsciiCaseInsensitive,
"s" => AttributeFlags::CaseSensitive,
_ => return Err(location.new_basic_unexpected_token_error(token.clone())),
})
}

/// Level 3: Parse **one** simple_selector. (Though we might insert a second
Expand Down

0 comments on commit a519d9e

Please sign in to comment.