aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-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.
+ _ => {},
}
}
}