diff --git a/src/components/style/selector_matching.rs b/src/components/style/selector_matching.rs index db6afabccb24..feed3494098e 100644 --- a/src/components/style/selector_matching.rs +++ b/src/components/style/selector_matching.rs @@ -489,14 +489,24 @@ fn matches_simple_selector, T: TreeNodeRefAsElement, E: Ele } } } - FirstChild => matches_first_child(element), + FirstChild => matches_first_child(element), LastChild => matches_last_child(element), - - OnlyChild => matches_first_child(element) && matches_last_child(element), + OnlyChild => matches_first_child(element) && + matches_last_child(element), Root => matches_root(element), + NthChild(a, b) => matches_generic_nth_child(element, a, b, false, false), + NthLastChild(a, b) => matches_generic_nth_child(element, a, b, false, true), + NthOfType(a, b) => matches_generic_nth_child(element, a, b, true, false), + NthLastOfType(a, b) => matches_generic_nth_child(element, a, b, true, true), + + FirstOfType => matches_generic_nth_child(element, 0, 1, true, false), + LastOfType => matches_generic_nth_child(element, 0, 1, true, true), + OnlyOfType => matches_generic_nth_child(element, 0, 1, true, false) && + matches_generic_nth_child(element, 0, 1, true, true), + Negation(ref negated) => { !negated.iter().all(|s| matches_simple_selector(s, element)) }, @@ -510,6 +520,66 @@ fn url_is_visited(_url: &str) -> bool { false } +#[inline] +fn matches_generic_nth_child, T: TreeNodeRefAsElement, E: ElementLike>( + element: &T, a: i32, b: i32, isOfType: bool, isFromEnd: bool) -> bool { + let mut node = element.clone(); + // fail if we can't find a parent or if the node is the root element + // of the document (Cf. Selectors Level 3) + match node.node().parent_node() { + Some(parent) => if parent.is_document() { + return false; + }, + None => return false + }; + + let mut local_name = ""; + if isOfType { + // FIXME this is wrong + // TODO when the DOM supports namespaces on elements + do element.with_imm_element_like |element: &E| { + local_name = element.get_local_name(); + } + } + + let mut index = 1; + loop { + if isFromEnd { + match node.node().next_sibling() { + None => break, + Some(next_sibling) => node = next_sibling + } + } else { + match node.node().prev_sibling() { + None => break, + Some(prev_sibling) => node = prev_sibling + } + } + + if node.is_element() { + if isOfType { + // FIXME this is wrong + // TODO when the DOM supports namespaces on elements + do node.with_imm_element_like |node: &E| { + if local_name == node.get_local_name() { + index += 1; + } + } + } else { + index += 1; + } + } + + } + + if a == 0 { + return b == index; + } + + let n: i32 = (((index as f32) - (b as f32)) / (a as f32)) as i32; + n >= 0 && (a * n == index - b) +} + #[inline] fn matches_root, T: TreeNodeRefAsElement, E: ElementLike>( element: &T) -> bool { diff --git a/src/components/style/selectors.rs b/src/components/style/selectors.rs index d668b8085a4a..c6042ee655dc 100644 --- a/src/components/style/selectors.rs +++ b/src/components/style/selectors.rs @@ -5,6 +5,7 @@ use std::{vec, iter}; use std::ascii::StrAsciiExt; use cssparser::ast::*; +use cssparser::parse_nth; use namespaces::NamespaceMap; @@ -66,7 +67,13 @@ pub enum SimpleSelector { // Empty, Root, // Lang(~str), -// NthChild(i32, i32), + NthChild(i32, i32), + NthLastChild(i32, i32), + NthOfType(i32, i32), + NthLastOfType(i32, i32), + FirstOfType, + LastOfType, + OnlyOfType // ... } @@ -192,7 +199,10 @@ fn compute_specificity(mut selector: &CompoundSelector, | &AttrPrefixMatch(*) | &AttrSubstringMatch(*) | &AttrSuffixMatch(*) | &AnyLink | &Link | &Visited | &FirstChild | &LastChild | &OnlyChild | &Root -// | &Empty | &Lang(*) | &NthChild(*) +// | &Empty | &Lang(*) + | &NthChild(*) | &NthLastChild(*) + | &NthOfType(*) | &NthLastOfType(*) + | &FirstOfType | &LastOfType | &OnlyOfType => specificity.class_like_selectors += 1, &NamespaceSelector(*) => (), &Negation(ref negated) @@ -441,6 +451,9 @@ fn parse_simple_pseudo_class(name: &str) -> Option { "last-child" => Some(LastChild), "only-child" => Some(OnlyChild), "root" => Some(Root), + "first-of-type" => Some(FirstOfType), + "last-of-type" => Some(LastOfType), + "only-of-type" => Some(OnlyOfType), // "empty" => Some(Empty), _ => None } @@ -452,7 +465,10 @@ fn parse_functional_pseudo_class(name: ~str, arguments: ~[ComponentValue], -> Option { match name.to_ascii_lower().as_slice() { // "lang" => parse_lang(arguments), -// "nth-child" => parse_nth(arguments).map(|&(a, b)| NthChild(a, b)), + "nth-child" => parse_nth(arguments).map(|(a, b)| NthChild(a, b)), + "nth-last-child" => parse_nth(arguments).map(|(a, b)| NthLastChild(a, b)), + "nth-of-type" => parse_nth(arguments).map(|(a, b)| NthOfType(a, b)), + "nth-last-of-type" => parse_nth(arguments).map(|(a, b)| NthLastOfType(a, b)), "not" => if inside_negation { None } else { parse_negation(arguments, namespaces) }, _ => None } diff --git a/src/test/ref/basic.list b/src/test/ref/basic.list index 649d35a28ca4..a18747e8ab91 100644 --- a/src/test/ref/basic.list +++ b/src/test/ref/basic.list @@ -5,3 +5,10 @@ == first_child_pseudo_a.html first_child_pseudo_b.html == last_child_pseudo_a.html last_child_pseudo_b.html == only_child_pseudo_a.html only_child_pseudo_b.html +== nth_child_pseudo_a.html nth_child_pseudo_b.html +== nth_last_child_pseudo_a.html nth_last_child_pseudo_b.html +== nth_of_type_pseudo_a.html nth_of_type_pseudo_b.html +== nth_last_of_type_pseudo_a.html nth_last_of_type_pseudo_b.html +== first_of_type_pseudo_a.html first_of_type_pseudo_b.html +== last_of_type_pseudo_a.html last_of_type_pseudo_b.html +== only_of_type_pseudo_a.html only_of_type_pseudo_b.html diff --git a/src/test/ref/first_of_type_pseudo_a.html b/src/test/ref/first_of_type_pseudo_a.html new file mode 100644 index 000000000000..1edc27fa3252 --- /dev/null +++ b/src/test/ref/first_of_type_pseudo_a.html @@ -0,0 +1,52 @@ + + + + :first-of-type test + + + +
+

+
+
+

+
+

+
+

+

+
+
+

+
+

+

+
+ + diff --git a/src/test/ref/first_of_type_pseudo_b.html b/src/test/ref/first_of_type_pseudo_b.html new file mode 100644 index 000000000000..46e9528f8efd --- /dev/null +++ b/src/test/ref/first_of_type_pseudo_b.html @@ -0,0 +1,48 @@ + + + + :first-of-type test + + + +
+

+
+
+

+
+

+
+

+

+
+
+

+
+

+

+
+ + diff --git a/src/test/ref/last_of_type_pseudo_a.html b/src/test/ref/last_of_type_pseudo_a.html new file mode 100644 index 000000000000..244355ff7656 --- /dev/null +++ b/src/test/ref/last_of_type_pseudo_a.html @@ -0,0 +1,52 @@ + + + + :last-of-type test + + + +
+

+
+
+

+
+

+
+

+

+
+
+

+
+

+

+
+ + diff --git a/src/test/ref/last_of_type_pseudo_b.html b/src/test/ref/last_of_type_pseudo_b.html new file mode 100644 index 000000000000..18ebcdfa078b --- /dev/null +++ b/src/test/ref/last_of_type_pseudo_b.html @@ -0,0 +1,48 @@ + + + + :last-of-type test + + + +
+

+
+
+

+
+

+
+

+

+
+
+

+
+

+

+
+ + diff --git a/src/test/ref/nth_child_pseudo_a.html b/src/test/ref/nth_child_pseudo_a.html new file mode 100644 index 000000000000..9603887b6c11 --- /dev/null +++ b/src/test/ref/nth_child_pseudo_a.html @@ -0,0 +1,89 @@ + + + + :nth-child test + + + +
+

+

+

+

+

+

+

+

+
+
+

+

+

+

+

+

+

+

+
+
+

+

+

+

+

+

+

+

+

+

+

+
+
+

+

+

+

+

+

+

+

+

+

+

+
+
+

+

+

+

+

+

+

+

+

+

+

+
+ + diff --git a/src/test/ref/nth_child_pseudo_b.html b/src/test/ref/nth_child_pseudo_b.html new file mode 100644 index 000000000000..b0112cb0777f --- /dev/null +++ b/src/test/ref/nth_child_pseudo_b.html @@ -0,0 +1,74 @@ + + + + :nth-child test + + + +
+

+

+

+

+

+

+

+

+
+
+

+

+

+

+

+

+

+

+
+
+

+

+

+

+

+

+

+

+

+

+

+
+
+

+

+

+

+

+

+

+

+

+

+

+
+
+

+

+

+

+

+

+

+

+

+

+

+
+ + diff --git a/src/test/ref/nth_last_child_pseudo_a.html b/src/test/ref/nth_last_child_pseudo_a.html new file mode 100644 index 000000000000..3afd8e37344f --- /dev/null +++ b/src/test/ref/nth_last_child_pseudo_a.html @@ -0,0 +1,89 @@ + + + + :nth-last-child test + + + +
+

+

+

+

+

+

+

+

+
+
+

+

+

+

+

+

+

+

+
+
+

+

+

+

+

+

+

+

+

+

+

+
+
+

+

+

+

+

+

+

+

+

+

+

+
+
+

+

+

+

+

+

+

+

+

+

+

+
+ + diff --git a/src/test/ref/nth_last_child_pseudo_b.html b/src/test/ref/nth_last_child_pseudo_b.html new file mode 100644 index 000000000000..5be3dbb34161 --- /dev/null +++ b/src/test/ref/nth_last_child_pseudo_b.html @@ -0,0 +1,74 @@ + + + + :nth-last-child test + + + +
+

+

+

+

+

+

+

+

+
+
+

+

+

+

+

+

+

+

+
+
+

+

+

+

+

+

+

+

+

+

+

+
+
+

+

+

+

+

+

+

+

+

+

+

+
+
+

+

+

+

+

+

+

+

+

+

+

+
+ + diff --git a/src/test/ref/nth_last_of_type_pseudo_a.html b/src/test/ref/nth_last_of_type_pseudo_a.html new file mode 100644 index 000000000000..5ad59e770c51 --- /dev/null +++ b/src/test/ref/nth_last_of_type_pseudo_a.html @@ -0,0 +1,131 @@ + + + + :nth-last-of-type test + + + +
+

+
+
+
+

+

+
+

+

+
+
+

+
+

+

+
+
+

+
+
+
+

+

+
+

+

+
+
+

+
+

+

+
+
+

+
+
+
+

+

+
+

+

+
+
+

+
+

+

+
+
+

+
+
+
+

+

+
+

+

+
+
+

+
+

+

+
+
+

+
+
+
+

+

+
+

+

+
+
+

+
+

+

+
+ + diff --git a/src/test/ref/nth_last_of_type_pseudo_b.html b/src/test/ref/nth_last_of_type_pseudo_b.html new file mode 100644 index 000000000000..fe04106efc68 --- /dev/null +++ b/src/test/ref/nth_last_of_type_pseudo_b.html @@ -0,0 +1,116 @@ + + + + :nth-last-of-type test + + + +
+

+
+
+
+

+

+
+

+

+
+
+

+
+

+

+
+
+

+
+
+
+

+

+
+

+

+
+
+

+
+

+

+
+
+

+
+
+
+

+

+
+

+

+
+
+

+
+

+

+
+
+

+
+
+
+

+

+
+

+

+
+
+

+
+

+

+
+
+

+
+
+
+

+

+
+

+

+
+
+

+
+

+

+
+ + diff --git a/src/test/ref/nth_of_type_pseudo_a.html b/src/test/ref/nth_of_type_pseudo_a.html new file mode 100644 index 000000000000..1f4416d7bbc9 --- /dev/null +++ b/src/test/ref/nth_of_type_pseudo_a.html @@ -0,0 +1,131 @@ + + + + :nth-of-type test + + + +
+

+
+
+
+

+

+
+

+

+
+
+

+
+

+

+
+
+

+
+
+
+

+

+
+

+

+
+
+

+
+

+

+
+
+

+
+
+
+

+

+
+

+

+
+
+

+
+

+

+
+
+

+
+
+
+

+

+
+

+

+
+
+

+
+

+

+
+
+

+
+
+
+

+

+
+

+

+
+
+

+
+

+

+
+ + diff --git a/src/test/ref/nth_of_type_pseudo_b.html b/src/test/ref/nth_of_type_pseudo_b.html new file mode 100644 index 000000000000..8b76202920a7 --- /dev/null +++ b/src/test/ref/nth_of_type_pseudo_b.html @@ -0,0 +1,115 @@ + + + + :nth-of-type test + + + +
+

+
+
+
+

+

+
+

+

+
+
+

+
+

+

+
+
+

+
+
+
+

+

+
+

+

+
+
+

+
+

+

+
+
+

+
+
+
+

+

+
+

+

+
+
+

+
+

+

+
+
+

+
+
+
+

+

+
+

+

+
+
+

+
+

+

+
+
+

+
+
+
+

+

+
+

+

+
+
+

+
+

+

+
+ + diff --git a/src/test/ref/only_of_type_pseudo_a.html b/src/test/ref/only_of_type_pseudo_a.html new file mode 100644 index 000000000000..18b3d4f21dd6 --- /dev/null +++ b/src/test/ref/only_of_type_pseudo_a.html @@ -0,0 +1,47 @@ + + + + :only-of-type test + + + +
+

+
+
+

+
+

+
+

+

+

+
+

+

+
+ + diff --git a/src/test/ref/only_of_type_pseudo_b.html b/src/test/ref/only_of_type_pseudo_b.html new file mode 100644 index 000000000000..c66c0faadda9 --- /dev/null +++ b/src/test/ref/only_of_type_pseudo_b.html @@ -0,0 +1,42 @@ + + + + :only-of-type test + + + +
+

+
+
+

+
+

+
+

+

+

+
+

+

+
+ +