mirror of
https://github.com/servo/servo.git
synced 2025-08-05 13:40:08 +01:00
Add [foo=bar i] case-insensitive attribute selectors.
This commit is contained in:
parent
2f9808e130
commit
1117d86b63
5 changed files with 81 additions and 17 deletions
|
@ -2,6 +2,7 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
use std::ascii::AsciiExt;
|
||||||
use std::collections::hashmap::HashMap;
|
use std::collections::hashmap::HashMap;
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
use std::num::div_rem;
|
use std::num::div_rem;
|
||||||
|
@ -723,14 +724,17 @@ pub fn matches_simple_selector<'a, E:TElement<'a>,
|
||||||
*shareable = false;
|
*shareable = false;
|
||||||
element.match_attr(attr, |_| true)
|
element.match_attr(attr, |_| true)
|
||||||
}
|
}
|
||||||
AttrEqual(ref attr, ref value) => {
|
AttrEqual(ref attr, ref value, case_sensitivity) => {
|
||||||
if value.as_slice() != "DIR" {
|
if value.as_slice() != "DIR" {
|
||||||
// FIXME(pcwalton): Remove once we start actually supporting RTL text. This is in
|
// FIXME(pcwalton): Remove once we start actually supporting RTL text. This is in
|
||||||
// here because the UA style otherwise disables all style sharing completely.
|
// here because the UA style otherwise disables all style sharing completely.
|
||||||
*shareable = false
|
*shareable = false
|
||||||
}
|
}
|
||||||
element.match_attr(attr, |attr_value| {
|
element.match_attr(attr, |attr_value| {
|
||||||
attr_value == value.as_slice()
|
match case_sensitivity {
|
||||||
|
CaseSensitive => attr_value == value.as_slice(),
|
||||||
|
CaseInsensitive => attr_value.eq_ignore_ascii_case(value.as_slice()),
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
AttrIncludes(ref attr, ref value) => {
|
AttrIncludes(ref attr, ref value) => {
|
||||||
|
|
|
@ -61,7 +61,7 @@ pub enum SimpleSelector {
|
||||||
|
|
||||||
// Attribute selectors
|
// Attribute selectors
|
||||||
AttrExists(AttrSelector), // [foo]
|
AttrExists(AttrSelector), // [foo]
|
||||||
AttrEqual(AttrSelector, String), // [foo=bar]
|
AttrEqual(AttrSelector, String, CaseSensitivity), // [foo=bar]
|
||||||
AttrIncludes(AttrSelector, String), // [foo~=bar]
|
AttrIncludes(AttrSelector, String), // [foo~=bar]
|
||||||
AttrDashMatch(AttrSelector, String, String), // [foo|=bar] Second string is the first + "-"
|
AttrDashMatch(AttrSelector, String, String), // [foo|=bar] Second string is the first + "-"
|
||||||
AttrPrefixMatch(AttrSelector, String), // [foo^=bar]
|
AttrPrefixMatch(AttrSelector, String), // [foo^=bar]
|
||||||
|
@ -90,6 +90,14 @@ pub enum SimpleSelector {
|
||||||
// ...
|
// ...
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[deriving(Eq, PartialEq, Clone, Hash)]
|
||||||
|
pub enum CaseSensitivity {
|
||||||
|
CaseSensitive, // Selectors spec says language-defined, but HTML says sensitive.
|
||||||
|
CaseInsensitive,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#[deriving(Eq, PartialEq, Clone, Hash)]
|
#[deriving(Eq, PartialEq, Clone, Hash)]
|
||||||
pub struct LocalName {
|
pub struct LocalName {
|
||||||
pub name: Atom,
|
pub name: Atom,
|
||||||
|
@ -446,25 +454,21 @@ fn parse_attribute_selector(content: Vec<ComponentValue>, namespaces: &Namespace
|
||||||
};
|
};
|
||||||
skip_whitespace(iter);
|
skip_whitespace(iter);
|
||||||
// TODO: deal with empty value or value containing whitespace (see spec)
|
// TODO: deal with empty value or value containing whitespace (see spec)
|
||||||
macro_rules! get_value( () => {{
|
|
||||||
skip_whitespace(iter);
|
|
||||||
match iter.next() {
|
|
||||||
Some(Ident(value)) | Some(QuotedString(value)) => value,
|
|
||||||
_ => return Err(())
|
|
||||||
}
|
|
||||||
}};)
|
|
||||||
let result = match iter.next() {
|
let result = match iter.next() {
|
||||||
None => AttrExists(attr), // [foo]
|
None => AttrExists(attr), // [foo]
|
||||||
Some(Delim('=')) => AttrEqual(attr, (get_value!())), // [foo=bar]
|
Some(Delim('=')) => AttrEqual(
|
||||||
Some(IncludeMatch) => AttrIncludes(attr, (get_value!())), // [foo~=bar]
|
attr, try!(parse_attribute_value(iter)),
|
||||||
|
try!(parse_attribute_flags(iter))), // [foo=bar]
|
||||||
|
Some(IncludeMatch) => AttrIncludes(attr, try!(parse_attribute_value(iter))), // [foo~=bar]
|
||||||
Some(DashMatch) => {
|
Some(DashMatch) => {
|
||||||
let value = get_value!();
|
let value = try!(parse_attribute_value(iter));
|
||||||
let dashing_value = format!("{}-", value);
|
let dashing_value = format!("{}-", value);
|
||||||
AttrDashMatch(attr, value, dashing_value) // [foo|=bar]
|
AttrDashMatch(attr, value, dashing_value) // [foo|=bar]
|
||||||
},
|
},
|
||||||
Some(PrefixMatch) => AttrPrefixMatch(attr, (get_value!())), // [foo^=bar]
|
Some(PrefixMatch) => AttrPrefixMatch(attr, try!(parse_attribute_value(iter))), // [foo^=bar]
|
||||||
Some(SubstringMatch) => AttrSubstringMatch(attr, (get_value!())), // [foo*=bar]
|
// [foo*=bar]
|
||||||
Some(SuffixMatch) => AttrSuffixMatch(attr, (get_value!())), // [foo$=bar]
|
Some(SubstringMatch) => AttrSubstringMatch(attr, try!(parse_attribute_value(iter))),
|
||||||
|
Some(SuffixMatch) => AttrSuffixMatch(attr, try!(parse_attribute_value(iter))), // [foo$=bar]
|
||||||
_ => return Err(())
|
_ => return Err(())
|
||||||
};
|
};
|
||||||
skip_whitespace(iter);
|
skip_whitespace(iter);
|
||||||
|
@ -472,6 +476,27 @@ fn parse_attribute_selector(content: Vec<ComponentValue>, namespaces: &Namespace
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn parse_attribute_value<I: Iterator<ComponentValue>>(iter: &mut Iter<I>) -> Result<String, ()> {
|
||||||
|
skip_whitespace(iter);
|
||||||
|
match iter.next() {
|
||||||
|
Some(Ident(value)) | Some(QuotedString(value)) => Ok(value),
|
||||||
|
_ => Err(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn parse_attribute_flags<I: Iterator<ComponentValue>>(iter: &mut Iter<I>)
|
||||||
|
-> Result<CaseSensitivity, ()> {
|
||||||
|
skip_whitespace(iter);
|
||||||
|
match iter.next() {
|
||||||
|
None => Ok(CaseSensitive),
|
||||||
|
Some(Ident(ref value)) if value.as_slice().eq_ignore_ascii_case("i")
|
||||||
|
=> Ok(CaseInsensitive),
|
||||||
|
_ => Err(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
fn parse_simple_pseudo_class(name: &str) -> Result<SimpleSelector, ()> {
|
fn parse_simple_pseudo_class(name: &str) -> Result<SimpleSelector, ()> {
|
||||||
match name.to_ascii_lower().as_slice() {
|
match name.to_ascii_lower().as_slice() {
|
||||||
"any-link" => Ok(AnyLink),
|
"any-link" => Ok(AnyLink),
|
||||||
|
|
17
tests/ref/attr_selector_case_sensitivity.html
Normal file
17
tests/ref/attr_selector_case_sensitivity.html
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Attribute selector case-sensitivity: [foo=bar] and [foo=bar i]</title>
|
||||||
|
<style>
|
||||||
|
p[data-foo=Bar] { color: green }
|
||||||
|
p[data-foo=bar] { color: red }
|
||||||
|
p[data-foo=baz i] { color: green }
|
||||||
|
p[data-foo=baz] { color: red }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p data-foo="Bar">This text should be green.</p>
|
||||||
|
<p data-foo="Baz">This text should be green.</p>
|
||||||
|
<p>This text should be black.</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
17
tests/ref/attr_selector_case_sensitivity_ref.html
Normal file
17
tests/ref/attr_selector_case_sensitivity_ref.html
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Attribute selector case-sensitivity: [foo=bar] and [foo=bar i]</title>
|
||||||
|
<style>
|
||||||
|
p[data-foo=Bar] { color: green }
|
||||||
|
p[data-foo=bar] { color: red }
|
||||||
|
p[data-foo=baz i] { color: green }
|
||||||
|
p[data-foo=baz] { color: red }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p style="color: green">This text should be green.</p>
|
||||||
|
<p style="color: green">This text should be green.</p>
|
||||||
|
<p>This text should be black.</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -30,6 +30,7 @@
|
||||||
# inline_border_a.html inline_border_b.html
|
# inline_border_a.html inline_border_b.html
|
||||||
== anon_block_inherit_a.html anon_block_inherit_b.html
|
== anon_block_inherit_a.html anon_block_inherit_b.html
|
||||||
== attr_exists_selector.html attr_exists_selector_ref.html
|
== attr_exists_selector.html attr_exists_selector_ref.html
|
||||||
|
== attr_selector_case_sensitivity.html attr_selector_case_sensitivity_ref.html
|
||||||
!= noteq_attr_exists_selector.html attr_exists_selector_ref.html
|
!= noteq_attr_exists_selector.html attr_exists_selector_ref.html
|
||||||
== data_img_a.html data_img_b.html
|
== data_img_a.html data_img_b.html
|
||||||
== background_style_attr.html background_ref.html
|
== background_style_attr.html background_ref.html
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue