diff options
Diffstat (limited to 'components/style/selectors.rs')
-rw-r--r-- | components/style/selectors.rs | 788 |
1 files changed, 0 insertions, 788 deletions
diff --git a/components/style/selectors.rs b/components/style/selectors.rs deleted file mode 100644 index 93428c120de..00000000000 --- a/components/style/selectors.rs +++ /dev/null @@ -1,788 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * 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::cmp; -use std::ascii::{AsciiExt, OwnedAsciiExt}; -use std::sync::Arc; -use std::string::CowString; - -use cssparser::{Token, Parser, parse_nth}; -use string_cache::{Atom, Namespace}; -use url::Url; - -use parser::ParserContext; -use stylesheets::Origin; - - -#[derive(PartialEq, Clone, Debug)] -pub struct Selector { - pub compound_selectors: Arc<CompoundSelector>, - pub pseudo_element: Option<PseudoElement>, - pub specificity: u32, -} - -#[derive(Eq, PartialEq, Clone, Hash, Copy, Debug)] -pub enum PseudoElement { - Before, - After, - // ... -} - - -#[derive(PartialEq, Clone, Debug)] -pub struct CompoundSelector { - pub simple_selectors: Vec<SimpleSelector>, - pub next: Option<(Box<CompoundSelector>, Combinator)>, // c.next is left of c -} - -#[derive(PartialEq, Clone, Copy, Debug)] -pub enum Combinator { - Child, // > - Descendant, // space - NextSibling, // + - LaterSibling, // ~ -} - -#[derive(Eq, PartialEq, Clone, Hash, Debug)] -pub enum SimpleSelector { - ID(Atom), - Class(Atom), - LocalName(LocalName), - Namespace(Namespace), - - // Attribute selectors - AttrExists(AttrSelector), // [foo] - AttrEqual(AttrSelector, String, CaseSensitivity), // [foo=bar] - AttrIncludes(AttrSelector, String), // [foo~=bar] - 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] - - // Pseudo-classes - Negation(Vec<SimpleSelector>), - AnyLink, - Link, - Visited, - Hover, - Disabled, - Enabled, - Checked, - Indeterminate, - FirstChild, LastChild, OnlyChild, - Root, - NthChild(i32, i32), - NthLastChild(i32, i32), - NthOfType(i32, i32), - NthLastOfType(i32, i32), - FirstOfType, - LastOfType, - OnlyOfType, - ServoNonzeroBorder, - // ... -} - - -#[derive(Eq, PartialEq, Clone, Hash, Copy, Debug)] -pub enum CaseSensitivity { - CaseSensitive, // Selectors spec says language-defined, but HTML says sensitive. - CaseInsensitive, -} - - -#[derive(Eq, PartialEq, Clone, Hash, Debug)] -pub struct LocalName { - pub name: Atom, - pub lower_name: Atom, -} - -#[derive(Eq, PartialEq, Clone, Hash, Debug)] -pub struct AttrSelector { - pub name: Atom, - pub lower_name: Atom, - pub namespace: NamespaceConstraint, -} - -#[derive(Eq, PartialEq, Clone, Hash, Debug)] -pub enum NamespaceConstraint { - Any, - Specific(Namespace), -} - - -fn compute_specificity(mut selector: &CompoundSelector, - pseudo_element: &Option<PseudoElement>) -> u32 { - struct Specificity { - id_selectors: u32, - class_like_selectors: u32, - element_selectors: u32, - } - let mut specificity = Specificity { - id_selectors: 0, - class_like_selectors: 0, - element_selectors: 0, - }; - if pseudo_element.is_some() { specificity.element_selectors += 1 } - - simple_selectors_specificity(&selector.simple_selectors, &mut specificity); - loop { - match selector.next { - None => break, - Some((ref next_selector, _)) => { - selector = &**next_selector; - simple_selectors_specificity(&selector.simple_selectors, &mut specificity) - } - } - } - - fn simple_selectors_specificity(simple_selectors: &[SimpleSelector], - specificity: &mut Specificity) { - for simple_selector in simple_selectors.iter() { - match simple_selector { - &SimpleSelector::LocalName(..) => - specificity.element_selectors += 1, - &SimpleSelector::ID(..) => - specificity.id_selectors += 1, - &SimpleSelector::Class(..) | - &SimpleSelector::AttrExists(..) | - &SimpleSelector::AttrEqual(..) | - &SimpleSelector::AttrIncludes(..) | - &SimpleSelector::AttrDashMatch(..) | - &SimpleSelector::AttrPrefixMatch(..) | - &SimpleSelector::AttrSubstringMatch(..) | - &SimpleSelector::AttrSuffixMatch(..) | - &SimpleSelector::AnyLink | &SimpleSelector::Link | - &SimpleSelector::Visited | &SimpleSelector::Hover | - &SimpleSelector::Disabled | &SimpleSelector::Enabled | - &SimpleSelector::FirstChild | &SimpleSelector::LastChild | - &SimpleSelector::OnlyChild | &SimpleSelector::Root | - &SimpleSelector::Checked | - &SimpleSelector::Indeterminate | - &SimpleSelector::NthChild(..) | - &SimpleSelector::NthLastChild(..) | - &SimpleSelector::NthOfType(..) | - &SimpleSelector::NthLastOfType(..) | - &SimpleSelector::FirstOfType | &SimpleSelector::LastOfType | - &SimpleSelector::OnlyOfType | - &SimpleSelector::ServoNonzeroBorder => - specificity.class_like_selectors += 1, - &SimpleSelector::Namespace(..) => (), - &SimpleSelector::Negation(ref negated) => - simple_selectors_specificity(negated, specificity), - } - } - } - - static MAX_10BIT: u32 = (1u32 << 10) - 1; - cmp::min(specificity.id_selectors, MAX_10BIT) << 20 - | cmp::min(specificity.class_like_selectors, MAX_10BIT) << 10 - | cmp::min(specificity.element_selectors, MAX_10BIT) -} - - - -pub fn parse_author_origin_selector_list_from_str(input: &str) -> Result<Vec<Selector>, ()> { - let url = Url::parse("about:blank").unwrap(); - let context = ParserContext::new(Origin::Author, &url); - parse_selector_list(&context, &mut Parser::new(input)) -} - -/// Parse a comma-separated list of Selectors. -/// aka Selector Group in http://www.w3.org/TR/css3-selectors/#grouping -/// -/// Return the Selectors or None if there is an invalid selector. -pub fn parse_selector_list(context: &ParserContext, input: &mut Parser) - -> Result<Vec<Selector>,()> { - input.parse_comma_separated(|input| parse_selector(context, input)) -} - - -/// Build up a Selector. -/// selector : simple_selector_sequence [ combinator simple_selector_sequence ]* ; -/// -/// `Err` means invalid selector. -fn parse_selector(context: &ParserContext, input: &mut Parser) -> Result<Selector,()> { - let (first, mut pseudo_element) = try!(parse_simple_selectors(context, input)); - let mut compound = CompoundSelector{ simple_selectors: first, next: None }; - - 'outer_loop: while pseudo_element.is_none() { - let combinator; - let mut any_whitespace = false; - loop { - let position = input.position(); - match input.next_including_whitespace() { - Err(()) => break 'outer_loop, - Ok(Token::WhiteSpace(_)) => any_whitespace = true, - Ok(Token::Delim('>')) => { - combinator = Combinator::Child; - break - } - Ok(Token::Delim('+')) => { - combinator = Combinator::NextSibling; - break - } - Ok(Token::Delim('~')) => { - combinator = Combinator::LaterSibling; - break - } - Ok(_) => { - input.reset(position); - if any_whitespace { - combinator = Combinator::Descendant; - break - } else { - break 'outer_loop - } - } - } - } - let (simple_selectors, pseudo) = try!(parse_simple_selectors(context, input)); - compound = CompoundSelector { - simple_selectors: simple_selectors, - next: Some((box compound, combinator)) - }; - pseudo_element = pseudo; - } - Ok(Selector { - specificity: compute_specificity(&compound, &pseudo_element), - compound_selectors: Arc::new(compound), - pseudo_element: pseudo_element, - }) -} - - -/// * `Err(())`: Invalid selector, abort -/// * `Ok(None)`: Not a type selector, could be something else. `input` was not consumed. -/// * `Ok(Some(vec))`: Length 0 (`*|*`), 1 (`*|E` or `ns|*`) or 2 (`|E` or `ns|E`) -fn parse_type_selector(context: &ParserContext, input: &mut Parser) - -> Result<Option<Vec<SimpleSelector>>, ()> { - match try!(parse_qualified_name(context, input, /* in_attr_selector = */ false)) { - None => Ok(None), - Some((namespace, local_name)) => { - let mut simple_selectors = vec!(); - match namespace { - NamespaceConstraint::Specific(ns) => { - simple_selectors.push(SimpleSelector::Namespace(ns)) - }, - NamespaceConstraint::Any => (), - } - match local_name { - Some(name) => { - simple_selectors.push(SimpleSelector::LocalName(LocalName { - name: Atom::from_slice(&name), - lower_name: Atom::from_slice(&name.into_owned().into_ascii_lowercase()) - })) - } - None => (), - } - Ok(Some(simple_selectors)) - } - } -} - - -#[derive(Debug)] -enum SimpleSelectorParseResult { - SimpleSelector(SimpleSelector), - PseudoElement(PseudoElement), -} - - -/// * `Err(())`: Invalid selector, abort -/// * `Ok(None)`: Not a simple selector, could be something else. `input` was not consumed. -/// * `Ok(Some((namespace, local_name)))`: `None` for the local name means a `*` universal selector -fn parse_qualified_name<'i, 't> - (context: &ParserContext, input: &mut Parser<'i, 't>, - in_attr_selector: bool) - -> Result<Option<(NamespaceConstraint, Option<CowString<'i>>)>, ()> { - let default_namespace = |local_name| { - let namespace = match context.namespaces.default { - Some(ref ns) => NamespaceConstraint::Specific(ns.clone()), - None => NamespaceConstraint::Any, - }; - Ok(Some((namespace, local_name))) - }; - - let explicit_namespace = |input: &mut Parser<'i, 't>, namespace| { - match input.next_including_whitespace() { - Ok(Token::Delim('*')) if !in_attr_selector => { - Ok(Some((namespace, None))) - }, - Ok(Token::Ident(local_name)) => { - Ok(Some((namespace, Some(local_name)))) - }, - _ => Err(()), - } - }; - - let position = input.position(); - match input.next_including_whitespace() { - Ok(Token::Ident(value)) => { - let position = input.position(); - match input.next_including_whitespace() { - Ok(Token::Delim('|')) => { - let result = context.namespaces.prefix_map.get(&*value); - let namespace = try!(result.ok_or(())); - explicit_namespace(input, NamespaceConstraint::Specific(namespace.clone())) - }, - _ => { - input.reset(position); - if in_attr_selector { - Ok(Some((NamespaceConstraint::Specific(ns!("")), Some(value)))) - } else { - default_namespace(Some(value)) - } - } - } - }, - Ok(Token::Delim('*')) => { - let position = input.position(); - match input.next_including_whitespace() { - Ok(Token::Delim('|')) => explicit_namespace(input, NamespaceConstraint::Any), - _ => { - input.reset(position); - if in_attr_selector { - Err(()) - } else { - default_namespace(None) - } - }, - } - }, - Ok(Token::Delim('|')) => explicit_namespace(input, NamespaceConstraint::Specific(ns!(""))), - _ => { - input.reset(position); - Ok(None) - } - } -} - - -fn parse_attribute_selector(context: &ParserContext, input: &mut Parser) - -> Result<SimpleSelector, ()> { - let attr = match try!(parse_qualified_name(context, input, /* in_attr_selector = */ true)) { - None => return Err(()), - Some((_, None)) => unreachable!(), - Some((namespace, Some(local_name))) => AttrSelector { - namespace: namespace, - lower_name: Atom::from_slice(&local_name.to_ascii_lowercase()), - name: Atom::from_slice(&local_name), - }, - }; - - fn parse_value(input: &mut Parser) -> Result<String, ()> { - Ok((try!(input.expect_ident_or_string())).into_owned()) - } - // TODO: deal with empty value or value containing whitespace (see spec) - match input.next() { - // [foo] - Err(()) => Ok(SimpleSelector::AttrExists(attr)), - - // [foo=bar] - Ok(Token::Delim('=')) => { - Ok(SimpleSelector::AttrEqual(attr, try!(parse_value(input)), - try!(parse_attribute_flags(input)))) - } - // [foo~=bar] - Ok(Token::IncludeMatch) => { - Ok(SimpleSelector::AttrIncludes(attr, try!(parse_value(input)))) - } - // [foo|=bar] - Ok(Token::DashMatch) => { - let value = try!(parse_value(input)); - let dashing_value = format!("{:?}-", value); - Ok(SimpleSelector::AttrDashMatch(attr, value, dashing_value)) - } - // [foo^=bar] - Ok(Token::PrefixMatch) => { - Ok(SimpleSelector::AttrPrefixMatch(attr, try!(parse_value(input)))) - } - // [foo*=bar] - Ok(Token::SubstringMatch) => { - Ok(SimpleSelector::AttrSubstringMatch(attr, try!(parse_value(input)))) - } - // [foo$=bar] - Ok(Token::SuffixMatch) => { - Ok(SimpleSelector::AttrSuffixMatch(attr, try!(parse_value(input)))) - } - _ => Err(()) - } -} - - -fn parse_attribute_flags(input: &mut Parser) -> Result<CaseSensitivity, ()> { - match input.next() { - Err(()) => Ok(CaseSensitivity::CaseSensitive), - Ok(Token::Ident(ref value)) if value.eq_ignore_ascii_case("i") => { - Ok(CaseSensitivity::CaseInsensitive) - } - _ => Err(()) - } -} - - -/// Level 3: Parse **one** simple_selector -fn parse_negation(context: &ParserContext, input: &mut Parser) -> Result<SimpleSelector,()> { - match try!(parse_type_selector(context, input)) { - Some(type_selector) => Ok(SimpleSelector::Negation(type_selector)), - None => { - match try!(parse_one_simple_selector(context, - input, - /* inside_negation = */ true)) { - Some(SimpleSelectorParseResult::SimpleSelector(simple_selector)) => { - Ok(SimpleSelector::Negation(vec![simple_selector])) - } - _ => Err(()) - } - }, - } -} - -/// simple_selector_sequence -/// : [ type_selector | universal ] [ HASH | class | attrib | pseudo | negation ]* -/// | [ HASH | class | attrib | pseudo | negation ]+ -/// -/// `Err(())` means invalid selector -fn parse_simple_selectors(context: &ParserContext, input: &mut Parser) - -> Result<(Vec<SimpleSelector>, Option<PseudoElement>),()> { - // Consume any leading whitespace. - loop { - let position = input.position(); - if !matches!(input.next_including_whitespace(), Ok(Token::WhiteSpace(_))) { - input.reset(position); - break - } - } - let mut empty = true; - let mut simple_selectors = match try!(parse_type_selector(context, input)) { - None => vec![], - Some(s) => { empty = false; s } - }; - - let mut pseudo_element = None; - loop { - match try!(parse_one_simple_selector(context, - input, - /* inside_negation = */ false)) { - None => break, - Some(SimpleSelectorParseResult::SimpleSelector(s)) => { - simple_selectors.push(s); - empty = false - } - Some(SimpleSelectorParseResult::PseudoElement(p)) => { - pseudo_element = Some(p); - empty = false; - break - } - } - } - if empty { - // An empty selector is invalid. - Err(()) - } else { - Ok((simple_selectors, pseudo_element)) - } -} - -fn parse_functional_pseudo_class(context: &ParserContext, - input: &mut Parser, - name: &str, - inside_negation: bool) - -> Result<SimpleSelector,()> { - match_ignore_ascii_case! { name, - "nth-child" => parse_nth_pseudo_class(input, SimpleSelector::NthChild), - "nth-of-type" => parse_nth_pseudo_class(input, SimpleSelector::NthOfType), - "nth-last-child" => parse_nth_pseudo_class(input, SimpleSelector::NthLastChild), - "nth-last-of-type" => parse_nth_pseudo_class(input, SimpleSelector::NthLastOfType), - "not" => { - if inside_negation { - Err(()) - } else { - parse_negation(context, input) - } - } - _ => Err(()) - } -} - - -fn parse_nth_pseudo_class<F>(input: &mut Parser, selector: F) -> Result<SimpleSelector, ()> -where F: FnOnce(i32, i32) -> SimpleSelector { - let (a, b) = try!(parse_nth(input)); - Ok(selector(a, b)) -} - - -/// Parse a simple selector other than a type selector. -/// -/// * `Err(())`: Invalid selector, abort -/// * `Ok(None)`: Not a simple selector, could be something else. `input` was not consumed. -/// * `Ok(Some(_))`: Parsed a simple selector or pseudo-element -fn parse_one_simple_selector(context: &ParserContext, - input: &mut Parser, - inside_negation: bool) - -> Result<Option<SimpleSelectorParseResult>,()> { - let start_position = input.position(); - match input.next_including_whitespace() { - Ok(Token::IDHash(id)) => { - let id = SimpleSelector::ID(Atom::from_slice(&id)); - Ok(Some(SimpleSelectorParseResult::SimpleSelector(id))) - } - Ok(Token::Delim('.')) => { - match input.next_including_whitespace() { - Ok(Token::Ident(class)) => { - let class = SimpleSelector::Class(Atom::from_slice(&class)); - Ok(Some(SimpleSelectorParseResult::SimpleSelector(class))) - } - _ => Err(()), - } - } - Ok(Token::SquareBracketBlock) => { - let attr = try!(input.parse_nested_block(|input| { - parse_attribute_selector(context, input) - })); - Ok(Some(SimpleSelectorParseResult::SimpleSelector(attr))) - } - Ok(Token::Colon) => { - match input.next_including_whitespace() { - Ok(Token::Ident(name)) => { - match parse_simple_pseudo_class(context, &name) { - Err(()) => { - let pseudo_element = match_ignore_ascii_case! { name, - // Supported CSS 2.1 pseudo-elements only. - // ** Do not add to this list! ** - "before" => PseudoElement::Before, - "after" => PseudoElement::After, - "first-line" => return Err(()), - "first-letter" => return Err(()) - _ => return Err(()) - }; - Ok(Some(SimpleSelectorParseResult::PseudoElement(pseudo_element))) - }, - Ok(result) => Ok(Some(SimpleSelectorParseResult::SimpleSelector(result))), - } - } - Ok(Token::Function(name)) => { - let pseudo = try!(input.parse_nested_block(|input| { - parse_functional_pseudo_class(context, input, &name, inside_negation) - })); - Ok(Some(SimpleSelectorParseResult::SimpleSelector(pseudo))) - } - Ok(Token::Colon) => { - match input.next() { - Ok(Token::Ident(name)) => { - let pseudo = try!(parse_pseudo_element(&name)); - Ok(Some(SimpleSelectorParseResult::PseudoElement(pseudo))) - } - _ => Err(()) - } - } - _ => Err(()) - } - } - _ => { - input.reset(start_position); - Ok(None) - } - } -} - -fn parse_simple_pseudo_class(context: &ParserContext, name: &str) -> Result<SimpleSelector,()> { - match_ignore_ascii_case! { name, - "any-link" => Ok(SimpleSelector::AnyLink), - "link" => Ok(SimpleSelector::Link), - "visited" => Ok(SimpleSelector::Visited), - "hover" => Ok(SimpleSelector::Hover), - "disabled" => Ok(SimpleSelector::Disabled), - "enabled" => Ok(SimpleSelector::Enabled), - "checked" => Ok(SimpleSelector::Checked), - "indeterminate" => Ok(SimpleSelector::Indeterminate), - "first-child" => Ok(SimpleSelector::FirstChild), - "last-child" => Ok(SimpleSelector::LastChild), - "only-child" => Ok(SimpleSelector::OnlyChild), - "root" => Ok(SimpleSelector::Root), - "first-of-type" => Ok(SimpleSelector::FirstOfType), - "last-of-type" => Ok(SimpleSelector::LastOfType), - "only-of-type" => Ok(SimpleSelector::OnlyOfType), - "-servo-nonzero-border" => { - if context.in_user_agent_stylesheet() { - Ok(SimpleSelector::ServoNonzeroBorder) - } else { - Err(()) - } - } - _ => Err(()) - } -} - -fn parse_pseudo_element(name: &str) -> Result<PseudoElement, ()> { - match_ignore_ascii_case! { name, - "before" => Ok(PseudoElement::Before), - "after" => Ok(PseudoElement::After) - _ => Err(()) - } -} - - -#[cfg(test)] -mod tests { - use std::sync::Arc; - use cssparser::Parser; - use stylesheets::Origin; - use string_cache::Atom; - use parser::ParserContext; - use url::Url; - use super::*; - - fn parse(input: &str) -> Result<Vec<Selector>, ()> { - parse_ns(input, &ParserContext::new(Origin::Author, &Url::parse("about:blank").unwrap())) - } - - fn parse_ns(input: &str, context: &ParserContext) -> Result<Vec<Selector>, ()> { - parse_selector_list(context, &mut Parser::new(input)) - } - - fn specificity(a: u32, b: u32, c: u32) -> u32 { - a << 20 | b << 10 | c - } - - #[test] - fn test_parsing() { - assert_eq!(parse(""), Err(())) ; - assert_eq!(parse("EeÉ"), Ok(vec!(Selector { - compound_selectors: Arc::new(CompoundSelector { - simple_selectors: vec!(SimpleSelector::LocalName(LocalName { - name: Atom::from_slice("EeÉ"), - lower_name: Atom::from_slice("eeÉ") })), - next: None, - }), - pseudo_element: None, - specificity: specificity(0, 0, 1), - }))); - assert_eq!(parse(".foo"), Ok(vec!(Selector { - compound_selectors: Arc::new(CompoundSelector { - simple_selectors: vec!(SimpleSelector::Class(Atom::from_slice("foo"))), - next: None, - }), - pseudo_element: None, - specificity: specificity(0, 1, 0), - }))); - assert_eq!(parse("#bar"), Ok(vec!(Selector { - compound_selectors: Arc::new(CompoundSelector { - simple_selectors: vec!(SimpleSelector::ID(Atom::from_slice("bar"))), - next: None, - }), - pseudo_element: None, - specificity: specificity(1, 0, 0), - }))); - assert_eq!(parse("e.foo#bar"), Ok(vec!(Selector { - compound_selectors: Arc::new(CompoundSelector { - simple_selectors: vec!(SimpleSelector::LocalName(LocalName { - name: Atom::from_slice("e"), - lower_name: Atom::from_slice("e") }), - SimpleSelector::Class(Atom::from_slice("foo")), - SimpleSelector::ID(Atom::from_slice("bar"))), - next: None, - }), - pseudo_element: None, - specificity: specificity(1, 1, 1), - }))); - assert_eq!(parse("e.foo #bar"), Ok(vec!(Selector { - compound_selectors: Arc::new(CompoundSelector { - simple_selectors: vec!(SimpleSelector::ID(Atom::from_slice("bar"))), - next: Some((box CompoundSelector { - simple_selectors: vec!(SimpleSelector::LocalName(LocalName { - name: Atom::from_slice("e"), - lower_name: Atom::from_slice("e") }), - SimpleSelector::Class(Atom::from_slice("foo"))), - next: None, - }, Combinator::Descendant)), - }), - pseudo_element: None, - specificity: specificity(1, 1, 1), - }))); - // Default namespace does not apply to attribute selectors - // https://github.com/mozilla/servo/pull/1652 - let url = Url::parse("about:blank").unwrap(); - let mut context = ParserContext::new(Origin::Author, &url); - assert_eq!(parse_ns("[Foo]", &context), Ok(vec!(Selector { - compound_selectors: Arc::new(CompoundSelector { - simple_selectors: vec!(SimpleSelector::AttrExists(AttrSelector { - name: Atom::from_slice("Foo"), - lower_name: Atom::from_slice("foo"), - namespace: NamespaceConstraint::Specific(ns!("")), - })), - next: None, - }), - pseudo_element: None, - specificity: specificity(0, 1, 0), - }))); - // Default namespace does not apply to attribute selectors - // https://github.com/mozilla/servo/pull/1652 - context.namespaces.default = Some(ns!(MathML)); - assert_eq!(parse_ns("[Foo]", &context), Ok(vec!(Selector { - compound_selectors: Arc::new(CompoundSelector { - simple_selectors: vec!(SimpleSelector::AttrExists(AttrSelector { - name: Atom::from_slice("Foo"), - lower_name: Atom::from_slice("foo"), - namespace: NamespaceConstraint::Specific(ns!("")), - })), - next: None, - }), - pseudo_element: None, - specificity: specificity(0, 1, 0), - }))); - // Default namespace does apply to type selectors - assert_eq!(parse_ns("e", &context), Ok(vec!(Selector { - compound_selectors: Arc::new(CompoundSelector { - simple_selectors: vec!( - SimpleSelector::Namespace(ns!(MathML)), - SimpleSelector::LocalName(LocalName { - name: Atom::from_slice("e"), - lower_name: Atom::from_slice("e") }), - ), - next: None, - }), - pseudo_element: None, - specificity: specificity(0, 0, 1), - }))); - // https://github.com/mozilla/servo/issues/1723 - assert_eq!(parse("::before"), Ok(vec!(Selector { - compound_selectors: Arc::new(CompoundSelector { - simple_selectors: vec!(), - next: None, - }), - pseudo_element: Some(PseudoElement::Before), - specificity: specificity(0, 0, 1), - }))); - assert_eq!(parse("div :after"), Ok(vec!(Selector { - compound_selectors: Arc::new(CompoundSelector { - simple_selectors: vec!(), - next: Some((box CompoundSelector { - simple_selectors: vec!(SimpleSelector::LocalName(LocalName { - name: atom!("div"), - lower_name: atom!("div") })), - next: None, - }, Combinator::Descendant)), - }), - pseudo_element: Some(PseudoElement::After), - specificity: specificity(0, 0, 2), - }))); - assert_eq!(parse("#d1 > .ok"), Ok(vec![Selector { - compound_selectors: Arc::new(CompoundSelector { - simple_selectors: vec![ - SimpleSelector::Class(Atom::from_slice("ok")), - ], - next: Some((box CompoundSelector { - simple_selectors: vec![ - SimpleSelector::ID(Atom::from_slice("d1")), - ], - next: None, - }, Combinator::Child)), - }), - pseudo_element: None, - specificity: (1 << 20) + (1 << 10) + (0 << 0), - }])) - } -} |