Auto merge of #19027 - emilio:svg-use-bug, r=upsuper

selectors: Be consistent about how we get a parent element for selector matching

This fixes bug 1412011.

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/19027)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2017-10-26 19:54:09 -05:00 committed by GitHub
commit de7595f16f

View file

@ -486,6 +486,32 @@ enum Rightmost {
No, No,
} }
#[inline(always)]
fn next_element_for_combinator<E>(
element: &E,
combinator: Combinator,
) -> Option<E>
where
E: Element,
{
match combinator {
Combinator::NextSibling |
Combinator::LaterSibling => {
element.prev_sibling_element()
}
Combinator::Child |
Combinator::Descendant => {
if element.blocks_ancestor_combinators() {
return None;
}
element.parent_element()
}
Combinator::PseudoElement => {
element.pseudo_element_originating_element()
}
}
}
fn matches_complex_selector_internal<E, F>( fn matches_complex_selector_internal<E, F>(
mut selector_iter: SelectorIter<E::Impl>, mut selector_iter: SelectorIter<E::Impl>,
element: &E, element: &E,
@ -524,8 +550,7 @@ where
}; };
let combinator = selector_iter.next_sequence(); let combinator = selector_iter.next_sequence();
let siblings = combinator.map_or(false, |c| c.is_sibling()); if combinator.map_or(false, |c| c.is_sibling()) {
if siblings {
flags_setter(element, HAS_SLOW_SELECTOR_LATER_SIBLINGS); flags_setter(element, HAS_SLOW_SELECTOR_LATER_SIBLINGS);
} }
@ -533,31 +558,28 @@ where
return SelectorMatchingResult::NotMatchedAndRestartFromClosestLaterSibling; return SelectorMatchingResult::NotMatchedAndRestartFromClosestLaterSibling;
} }
match combinator { let combinator = match combinator {
None => SelectorMatchingResult::Matched, None => return SelectorMatchingResult::Matched,
Some(c) => { Some(c) => c,
let (mut next_element, candidate_not_found) = match c { };
Combinator::NextSibling | Combinator::LaterSibling => {
let candidate_not_found = match combinator {
Combinator::NextSibling |
Combinator::LaterSibling => {
// Only ancestor combinators are allowed while looking for // Only ancestor combinators are allowed while looking for
// relevant links, so switch to not looking. // relevant links, so switch to not looking.
*relevant_link = RelevantLinkStatus::NotLooking; *relevant_link = RelevantLinkStatus::NotLooking;
(element.prev_sibling_element(), SelectorMatchingResult::NotMatchedAndRestartFromClosestDescendant
SelectorMatchingResult::NotMatchedAndRestartFromClosestDescendant)
}
Combinator::Child | Combinator::Descendant => {
if element.blocks_ancestor_combinators() {
(None, SelectorMatchingResult::NotMatchedGlobally)
} else {
(element.parent_element(),
SelectorMatchingResult::NotMatchedGlobally)
}
} }
Combinator::Child |
Combinator::Descendant |
Combinator::PseudoElement => { Combinator::PseudoElement => {
(element.pseudo_element_originating_element(), SelectorMatchingResult::NotMatchedGlobally
SelectorMatchingResult::NotMatchedGlobally)
} }
}; };
let mut next_element = next_element_for_combinator(element, combinator);
loop { loop {
let element = match next_element { let element = match next_element {
None => return candidate_not_found, None => return candidate_not_found,
@ -571,40 +593,40 @@ where
flags_setter, flags_setter,
Rightmost::No, Rightmost::No,
); );
match (result, c) {
match (result, combinator) {
// Return the status immediately. // Return the status immediately.
(SelectorMatchingResult::Matched, _) => return result, (SelectorMatchingResult::Matched, _) |
(SelectorMatchingResult::NotMatchedGlobally, _) => return result, (SelectorMatchingResult::NotMatchedGlobally, _) |
(_, Combinator::NextSibling) => {
return result;
}
// Upgrade the failure status to // Upgrade the failure status to
// NotMatchedAndRestartFromClosestDescendant. // NotMatchedAndRestartFromClosestDescendant.
(_, Combinator::PseudoElement) | (_, Combinator::PseudoElement) |
(_, Combinator::Child) => return SelectorMatchingResult::NotMatchedAndRestartFromClosestDescendant, (_, Combinator::Child) => {
return SelectorMatchingResult::NotMatchedAndRestartFromClosestDescendant;
}
// Return the status directly. // If the failure status is
(_, Combinator::NextSibling) => return result, // NotMatchedAndRestartFromClosestDescendant and combinator is
// Combinator::LaterSibling, give up this Combinator::LaterSibling
// If the failure status is NotMatchedAndRestartFromClosestDescendant // matching and restart from the closest descendant combinator.
// and combinator is Combinator::LaterSibling, give up this Combinator::LaterSibling matching (SelectorMatchingResult::NotMatchedAndRestartFromClosestDescendant, Combinator::LaterSibling) => {
// and restart from the closest descendant combinator. return result;
(SelectorMatchingResult::NotMatchedAndRestartFromClosestDescendant, Combinator::LaterSibling) }
=> return result,
// The Combinator::Descendant combinator and the status is // The Combinator::Descendant combinator and the status is
// NotMatchedAndRestartFromClosestLaterSibling or // NotMatchedAndRestartFromClosestLaterSibling or
// NotMatchedAndRestartFromClosestDescendant, // NotMatchedAndRestartFromClosestDescendant, or the
// or the Combinator::LaterSibling combinator and the status is // Combinator::LaterSibling combinator and the status is
// NotMatchedAndRestartFromClosestDescendant // NotMatchedAndRestartFromClosestDescendant, we can continue to
// can continue to matching on the next candidate element. // matching on the next candidate element.
_ => {}, _ => {},
} }
next_element = if siblings {
element.prev_sibling_element() next_element = next_element_for_combinator(&element, combinator);
} else {
element.parent_element()
};
}
}
} }
} }