Skip to content

Commit

Permalink
Implement :list-child and :only-child pseudo-classes. Fix #1295, fix #…
Browse files Browse the repository at this point in the history
  • Loading branch information
therealglazou authored and SimonSapin committed Nov 21, 2013
1 parent 97fd66a commit 3f44193
Show file tree
Hide file tree
Showing 7 changed files with 132 additions and 2 deletions.
27 changes: 27 additions & 0 deletions src/components/style/selector_matching.rs
Expand Up @@ -478,6 +478,10 @@ fn matches_simple_selector<N: TreeNode<T>, T: TreeNodeRefAsElement<N, E>, E: Ele
}
FirstChild => matches_first_child(element),

LastChild => matches_last_child(element),

OnlyChild => matches_first_child(element) && matches_last_child(element),

Root => matches_root(element),

Negation(ref negated) => {
Expand Down Expand Up @@ -525,6 +529,29 @@ fn matches_first_child<N: TreeNode<T>, T: TreeNodeRefAsElement<N, E>, E: Element
}
}

#[inline]
fn matches_last_child<N: TreeNode<T>, T: TreeNodeRefAsElement<N, E>, E: ElementLike>(
element: &T) -> bool {
let mut node = element.clone();
loop {
match node.node().next_sibling() {
Some(next_sibling) => {
node = next_sibling;
if node.is_element() {
return false
}
},
None => match node.node().parent_node() {
// Selectors level 3 says :last-child does not match the
// root of the document; Warning, level 4 says, for the time
// being, the contrary...
Some(parent) => return !parent.is_document(),
None => return false
}
}
}
}

#[inline]
fn match_attribute<N: TreeNode<T>, T: TreeNodeRefAsElement<N, E>, E: ElementLike>(
attr: &AttrSelector, element: &T, f: &fn(&str)-> bool) -> bool {
Expand Down
6 changes: 4 additions & 2 deletions src/components/style/selectors.rs
Expand Up @@ -62,7 +62,7 @@ pub enum SimpleSelector {
AnyLink,
Link,
Visited,
FirstChild,
FirstChild, LastChild, OnlyChild,
// Empty,
Root,
// Lang(~str),
Expand Down Expand Up @@ -191,7 +191,7 @@ fn compute_specificity(mut selector: &CompoundSelector,
| &AttrExists(*) | &AttrEqual(*) | &AttrIncludes(*) | &AttrDashMatch(*)
| &AttrPrefixMatch(*) | &AttrSubstringMatch(*) | &AttrSuffixMatch(*)
| &AnyLink | &Link | &Visited
| &FirstChild | &Root
| &FirstChild | &LastChild | &OnlyChild | &Root
// | &Empty | &Lang(*) | &NthChild(*)
=> specificity.class_like_selectors += 1,
&NamespaceSelector(*) => (),
Expand Down Expand Up @@ -438,6 +438,8 @@ fn parse_simple_pseudo_class(name: &str) -> Option<SimpleSelector> {
"link" => Some(Link),
"visited" => Some(Visited),
"first-child" => Some(FirstChild),
"last-child" => Some(LastChild),
"only-child" => Some(OnlyChild),
"root" => Some(Root),
// "empty" => Some(Empty),
_ => None
Expand Down
2 changes: 2 additions & 0 deletions src/test/ref/basic.list
Expand Up @@ -3,3 +3,5 @@
== margin_a.html margin_b.html
== root_pseudo_a.html root_pseudo_b.html
== 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
25 changes: 25 additions & 0 deletions src/test/ref/last_child_pseudo_a.html
@@ -0,0 +1,25 @@
<!DOCTYPE html>
<html>
<head>
<title>:last-child test</title>
<style type="text/css">
html:last-child { background: red; }
html { background: yellow;}

p { width: 20px; height: 20px; background: orange; float: left; margin-left: 10px; }
div { clear: both; }
#p1, #p2, #p3 { background: red; }
#d1 > *:last-child { background: green }
#d2 > *:last-child { background: green }
#d3 > *:last-child { background: green }
#p4 { background: green; }
#d4 > *:last-child { background: red }
</style>
</head>
<body>
<div id="d1"><p> </p><p id="p1"> </p></div>
<div id="d2"><p> </p><p id="p2"> </p> </div>
<div id="d3"><p> </p><p id="p3"> </p><!-- comment --></div>
<div id="d4"><p> </p><p id="p4"> </p><span> </span></div>
</body>
</html>
22 changes: 22 additions & 0 deletions src/test/ref/last_child_pseudo_b.html
@@ -0,0 +1,22 @@
<!DOCTYPE html>
<html>
<head>
<title>:last-child test</title>
<style type="text/css">
html { background: yellow;}

p { width: 20px; height: 20px; background: orange; float: left; margin-left: 10px; }
div { clear: both; }
#p1 { background: green; }
#p2 { background: green; }
#p3 { background: green; }
#p4 { background: green; }
</style>
</head>
<body>
<div id="d1"><p> </p><p id="p1"> </p></div>
<div id="d2"><p> </p><p id="p2"> </p> </div>
<div id="d3"><p> </p><p id="p3"> </p><!-- comment --></div>
<div id="d4"><p> </p><p id="p4"> </p><span> </span></div>
</body>
</html>
29 changes: 29 additions & 0 deletions src/test/ref/only_child_pseudo_a.html
@@ -0,0 +1,29 @@
<!DOCTYPE html>
<html>
<head>
<title>:only-child test</title>
<style type="text/css">
html:only-child { background: red; }
html { background: yellow;}

p { width: 20px; height: 20px; background: orange; float: left; margin-left: 10px; }
div { clear: both; }
#d1 > p, #d2 > p, #d3 > p, #d4 > p { background: red; }
#d1 > *:only-child { background: green }
#d2 > *:only-child { background: green }
#d3 > *:only-child { background: green }
#d4 > *:only-child { background: green }
#p5, #p6 { background: green; }
#d5 > *:only-child { background: red }
#d6 > *:only-child { background: red }
</style>
</head>
<body>
<div id="d1"><p> </p></div>
<div id="d2"> <p> </p></div>
<div id="d3"><p> </p><!-- comment --></div>
<div id="d4"><!-- comment --><p> </p></div>
<div id="d5"><p id="p5"> </p><span> </span></div>
<div id="d6"><span> </span><p id="p6"> </p></div>
</body>
</html>
23 changes: 23 additions & 0 deletions src/test/ref/only_child_pseudo_b.html
@@ -0,0 +1,23 @@
<!DOCTYPE html>
<html>
<head>
<title>:only-child test</title>
<style type="text/css">
html:only-child { background: red; }
html { background: yellow;}

p { width: 20px; height: 20px; background: orange; float: left; margin-left: 10px; }
div { clear: both; }
#d1 > p, #d2 > p, #d3 > p, #d4 > p { background: green; }
#p5, #p6 { background: green; }
</style>
</head>
<body>
<div id="d1"><p> </p></div>
<div id="d2"> <p> </p></div>
<div id="d3"><p> </p><!-- comment --></div>
<div id="d4"><!-- comment --><p> </p></div>
<div id="d5"><p id="p5"> </p><span> </span></div>
<div id="d6"><span> </span><p id="p6"> </p></div>
</body>
</html>

5 comments on commit 3f44193

@bors-servo
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

saw approval from SimonSapin
at SimonSapin@3f44193

@bors-servo
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

merging SimonSapin/servo/last-child = 3f44193 into auto

@bors-servo
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SimonSapin/servo/last-child = 3f44193 merged ok, testing candidate = df7ec26

@bors-servo
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bors-servo
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fast-forwarding master to auto = df7ec26

Please sign in to comment.