auto merge of #3635 : SimonSapin/servo/ua-stylesheet, r=pcwalton

Based on https://html.spec.whatwg.org/multipage/rendering.html rather than http://dev.w3.org/csswg/css2/grammar.html

Fixes #3629.

r? @pcwalton
This commit is contained in:
bors-servo 2014-10-13 12:21:44 -06:00
commit 6e3c776387
18 changed files with 891 additions and 152 deletions

View file

@ -2,6 +2,7 @@
* 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/. */
use std::ascii::AsciiExt;
use std::collections::hashmap::HashMap;
use std::hash::Hash;
use std::num::div_rem;
@ -279,12 +280,18 @@ impl Stylist {
after_map: PerPseudoElementSelectorMap::new(),
rules_source_order: 0u,
};
let ua_stylesheet = Stylesheet::from_bytes(
read_resource_file(["user-agent.css"]).unwrap().as_slice(),
Url::parse("chrome:///user-agent.css").unwrap(),
None,
None);
stylist.add_stylesheet(ua_stylesheet, UserAgentOrigin);
// FIXME: Add quirks-mode.css in quirks mode.
// FIXME: Add iso-8859-9.css when the documents encoding is ISO-8859-8.
// FIXME: presentational-hints.css should be at author origin with zero specificity.
// (Does it make a difference?)
for &filename in ["user-agent.css", "servo.css", "presentational-hints.css"].iter() {
let ua_stylesheet = Stylesheet::from_bytes(
read_resource_file([filename]).unwrap().as_slice(),
Url::parse(format!("chrome:///{}", filename).as_slice()).unwrap(),
None,
None);
stylist.add_stylesheet(ua_stylesheet, UserAgentOrigin);
}
stylist
}
@ -729,14 +736,17 @@ pub fn matches_simple_selector<'a, E:TElement<'a>,
*shareable = false;
element.match_attr(attr, |_| true)
}
AttrEqual(ref attr, ref value) => {
AttrEqual(ref attr, ref value, case_sensitivity) => {
if value.as_slice() != "DIR" {
// FIXME(pcwalton): Remove once we start actually supporting RTL text. This is in
// here because the UA style otherwise disables all style sharing completely.
*shareable = false
}
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) => {

View file

@ -61,9 +61,9 @@ pub enum SimpleSelector {
// Attribute selectors
AttrExists(AttrSelector), // [foo]
AttrEqual(AttrSelector, String), // [foo=bar]
AttrEqual(AttrSelector, String, CaseSensitivity), // [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]
AttrSubstringMatch(AttrSelector, String), // [foo*=bar]
AttrSuffixMatch(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)]
pub struct LocalName {
pub name: Atom,
@ -446,25 +454,21 @@ fn parse_attribute_selector(content: Vec<ComponentValue>, namespaces: &Namespace
};
skip_whitespace(iter);
// 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() {
None => AttrExists(attr), // [foo]
Some(Delim('=')) => AttrEqual(attr, (get_value!())), // [foo=bar]
Some(IncludeMatch) => AttrIncludes(attr, (get_value!())), // [foo~=bar]
Some(Delim('=')) => AttrEqual(
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) => {
let value = get_value!();
let value = try!(parse_attribute_value(iter));
let dashing_value = format!("{}-", value);
AttrDashMatch(attr, value, dashing_value) // [foo|=bar]
},
Some(PrefixMatch) => AttrPrefixMatch(attr, (get_value!())), // [foo^=bar]
Some(SubstringMatch) => AttrSubstringMatch(attr, (get_value!())), // [foo*=bar]
Some(SuffixMatch) => AttrSuffixMatch(attr, (get_value!())), // [foo$=bar]
Some(PrefixMatch) => AttrPrefixMatch(attr, try!(parse_attribute_value(iter))), // [foo^=bar]
// [foo*=bar]
Some(SubstringMatch) => AttrSubstringMatch(attr, try!(parse_attribute_value(iter))),
Some(SuffixMatch) => AttrSuffixMatch(attr, try!(parse_attribute_value(iter))), // [foo$=bar]
_ => return Err(())
};
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, ()> {
match name.to_ascii_lower().as_slice() {
"any-link" => Ok(AnyLink),