aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorbors-servo <release+servo@mozilla.com>2014-04-04 11:19:36 -0400
committerbors-servo <release+servo@mozilla.com>2014-04-04 11:19:36 -0400
commit7ece5f92dbd82bae1ef4862497c8f1bc810c6c55 (patch)
tree29561ded4dd4410576b750a0a9fe31d40a305eda
parentfe1615bc3dc3bd2f8d3fbb6f776e701b4c6dda76 (diff)
parent55cc9ccf227378a8d4235708eeca1b1bf176cf20 (diff)
downloadservo-7ece5f92dbd82bae1ef4862497c8f1bc810c6c55.tar.gz
servo-7ece5f92dbd82bae1ef4862497c8f1bc810c6c55.zip
auto merge of #1971 : Constellation/servo/quick-fail-selector-matching, r=SimonSapin
Restart selector matching from an appropriate selector Introducing 3 matching failure statuses, + NotMatchedGlobally + NotMatchedAndRestartFromClosestDescendant + NotMatchedAndRestartFromClosestLaterSibling When NotMatchedGlobally appears, stop selector matching completely since the succeeding selector never matches. It is raised when + Child selector cannot find the candidate element + Descendant selector cannot find the candidate element When NotMatchedAndRestartFromClosestDescendant appears, the selector matching does backtracking and restarts from the closest Descendant selector. It is raised when + NextSibling selector cannot find the candidate element. + LaterSibling selector cannot find the candidate element. + Child selector doesn't match on the found element. When NotMatchedAndRestartFromClosestLaterSibling appears, the selector matching does backtracking and restarts from the closest LaterSibling selector. It is raised when + NextSibling selector doesn't match on the found element. For example, when the selector "d1 d2 a" is provided and we cannot *find* an appropriate ancestor node for "d1", this selector matching raises NotMatchedGlobally since even if "d2" is moved to more upper node, the candidates for "d1" becomes less than before and d1 . The next example is siblings. When the selector "b1 + b2 ~ d1 a" is providied and we cannot *find* an appropriate brother node for b1, the selector matching raises NotMatchedAndRestartFromClosestDescendant. The selectors ("b1 + b2 ~") doesn't match and matching restart from "d1". The additional example is child and sibling. When the selector "b1 + c1 > b2 ~ d1 a" is provided and the selector "b1" doesn't match on the element, this "b1" raises NotMatchedAndRestartFromClosestLaterSibling. However since the selector "c1" raises NotMatchedAndRestartFromClosestDescendant. So the selector "b1 + c1 > b2 ~ " doesn't match and restart matching from "d1".
-rw-r--r--src/components/style/selector_matching.rs108
1 files changed, 96 insertions, 12 deletions
diff --git a/src/components/style/selector_matching.rs b/src/components/style/selector_matching.rs
index 926bcc1ea58..fffa2a4b85e 100644
--- a/src/components/style/selector_matching.rs
+++ b/src/components/style/selector_matching.rs
@@ -527,19 +527,80 @@ fn matches_compound_selector<E:TElement,
element: &N,
shareable: &mut bool)
-> bool {
+ match matches_compound_selector_internal(selector, element, shareable) {
+ Matched => true,
+ _ => false
+ }
+}
+
+/// A result of selector matching, includes 3 failure types,
+///
+/// NotMatchedAndRestartFromClosestLaterSibling
+/// NotMatchedAndRestartFromClosestDescendant
+/// NotMatchedGlobally
+///
+/// When NotMatchedGlobally appears, stop selector matching completely since
+/// the succeeding selectors never matches.
+/// It is raised when
+/// Child combinator cannot find the candidate element.
+/// Descendant combinator cannot find the candidate element.
+///
+/// When NotMatchedAndRestartFromClosestDescendant appears, the selector
+/// matching does backtracking and restarts from the closest Descendant
+/// combinator.
+/// It is raised when
+/// NextSibling combinator cannot find the candidate element.
+/// LaterSibling combinator cannot find the candidate element.
+/// Child combinator doesn't match on the found element.
+///
+/// When NotMatchedAndRestartFromClosestLaterSibling appears, the selector
+/// matching does backtracking and restarts from the closest LaterSibling
+/// combinator.
+/// It is raised when
+/// NextSibling combinator doesn't match on the found element.
+///
+/// For example, when the selector "d1 d2 a" is provided and we cannot *find*
+/// an appropriate ancestor node for "d1", this selector matching raises
+/// NotMatchedGlobally since even if "d2" is moved to more upper node, the
+/// candidates for "d1" becomes less than before and d1 .
+///
+/// The next example is siblings. When the selector "b1 + b2 ~ d1 a" is
+/// providied and we cannot *find* an appropriate brother node for b1,
+/// the selector matching raises NotMatchedAndRestartFromClosestDescendant.
+/// The selectors ("b1 + b2 ~") doesn't match and matching restart from "d1".
+///
+/// The additional example is child and sibling. When the selector
+/// "b1 + c1 > b2 ~ d1 a" is provided and the selector "b1" doesn't match on
+/// the element, this "b1" raises NotMatchedAndRestartFromClosestLaterSibling.
+/// However since the selector "c1" raises
+/// NotMatchedAndRestartFromClosestDescendant. So the selector
+/// "b1 + c1 > b2 ~ " doesn't match and restart matching from "d1".
+enum SelectorMatchingResult {
+ Matched,
+ NotMatchedAndRestartFromClosestLaterSibling,
+ NotMatchedAndRestartFromClosestDescendant,
+ NotMatchedGlobally,
+}
+
+fn matches_compound_selector_internal<E:TElement,
+ N:TNode<E>>(
+ selector: &CompoundSelector,
+ element: &N,
+ shareable: &mut bool)
+ -> SelectorMatchingResult {
if !selector.simple_selectors.iter().all(|simple_selector| {
matches_simple_selector(simple_selector, element, shareable)
}) {
- return false
+ return NotMatchedAndRestartFromClosestLaterSibling
}
match selector.next {
- None => true,
+ None => Matched,
Some((ref next_selector, combinator)) => {
- let (siblings, just_one) = match combinator {
- Child => (false, true),
- Descendant => (false, false),
- NextSibling => (true, true),
- LaterSibling => (true, false),
+ let (siblings, candidate_not_found) = match combinator {
+ Child => (false, NotMatchedGlobally),
+ Descendant => (false, NotMatchedGlobally),
+ NextSibling => (true, NotMatchedAndRestartFromClosestDescendant),
+ LaterSibling => (true, NotMatchedAndRestartFromClosestDescendant),
};
let mut node = (*element).clone();
loop {
@@ -549,14 +610,37 @@ fn matches_compound_selector<E:TElement,
node.parent_node()
};
match next_node {
- None => return false,
+ None => return candidate_not_found,
Some(next_node) => node = next_node,
}
if node.is_element() {
- if matches_compound_selector(&**next_selector, &node, shareable) {
- return true
- } else if just_one {
- return false
+ let result = matches_compound_selector_internal(&**next_selector,
+ &node,
+ shareable);
+ match (result, combinator) {
+ // Return the status immediately.
+ (Matched, _) => return result,
+ (NotMatchedGlobally, _) => return result,
+
+ // Upgrade the failure status to
+ // NotMatchedAndRestartFromClosestDescendant.
+ (_, Child) => return NotMatchedAndRestartFromClosestDescendant,
+
+ // Return the status directly.
+ (_, NextSibling) => return result,
+
+ // If the failure status is NotMatchedAndRestartFromClosestDescendant
+ // and combinator is LaterSibling, give up this LaterSibling matching
+ // and restart from the closest descendant combinator.
+ (NotMatchedAndRestartFromClosestDescendant, LaterSibling) => return result,
+
+ // The Descendant combinator and the status is
+ // NotMatchedAndRestartFromClosestLaterSibling or
+ // NotMatchedAndRestartFromClosestDescendant,
+ // or the LaterSibling combinator and the status is
+ // NotMatchedAndRestartFromClosestDescendant
+ // can continue to matching on the next candidate element.
+ _ => {},
}
}
}