Skip to content

Commit

Permalink
For has() queries, always test children
Browse files Browse the repository at this point in the history
Even if we checked siblings.

Fixes #2187
  • Loading branch information
jhy committed Sep 10, 2024
1 parent 7def06d commit 7e2d503
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 10 deletions.
3 changes: 3 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@
untracked.[2175](https://github.com/jhy/jsoup/issues/2175)
* When a document has no doctype, or a doctype not named `html`, it should be parsed in Quirks
Mode. [2197](https://github.com/jhy/jsoup/issues/2197)
* With a selector like `div:has(span + a)`, the `has()` component was not working correctly, as the inner combining
query caused the evaluator to match those against the outer's siblings, not
children. [2187](https://github.com/jhy/jsoup/issues/2187)

## 1.18.1 (2024-Jul-10)

Expand Down
19 changes: 9 additions & 10 deletions src/main/java/org/jsoup/select/StructuralEvaluator.java
Original file line number Diff line number Diff line change
Expand Up @@ -69,16 +69,15 @@ public Has(Evaluator evaluator) {
return true;
}
}
} else {
// otherwise we only want to match children (or below), and not the input element. And we want to minimize GCs so reusing the Iterator obj
NodeIterator<Element> it = ThreadElementIter.get();
it.restart(element);
while (it.hasNext()) {
Element el = it.next();
if (el == element) continue; // don't match self, only descendants
if (evaluator.matches(element, el))
return true;
}
}
// otherwise we only want to match children (or below), and not the input element. And we want to minimize GCs so reusing the Iterator obj
NodeIterator<Element> it = ThreadElementIter.get();
it.restart(element);
while (it.hasNext()) {
Element el = it.next();
if (el == element) continue; // don't match self, only descendants
if (evaluator.matches(element, el))
return true;
}
return false;
}
Expand Down
28 changes: 28 additions & 0 deletions src/test/java/org/jsoup/select/SelectorTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -1296,4 +1296,32 @@ public void emptyPseudo() {
Elements emptyAttr = doc.select("p:not([*])");
assertSelectedOwnText(emptyAttr, "Three");
}

@Test void divHasSpanPreceding() {
// https://github.com/jhy/jsoup/issues/2187
String html = "<div><span>abc</span><a>def</a></div>";
String q = "div:has(span + a)";

Document doc = Jsoup.parse(html);
Elements els = doc.select(q);
assertEquals(1, els.size());
assertEquals("div", els.first().normalName());
}

@Test void divHasDivPreceding() {
// https://github.com/jhy/jsoup/issues/2131 , dupe of https://github.com/jhy/jsoup/issues/2187
String html = "<div id=1>\n" +
"<div 1><span>hello</span></div>\n" +
"<div 2><span>there</span></div>\n" +
"\n" +
"</div>";

String q = "div:has(>div + div)";

Document doc = Jsoup.parse(html);
Elements els = doc.select(q);
assertEquals(1, els.size());
assertEquals("div", els.first().normalName());
assertEquals("1", els.first().id());
}
}

0 comments on commit 7e2d503

Please sign in to comment.