Skip to content

Commit a854fe9

Browse files
committed
Range setEndBefore() now really updates the end
1 parent fce591c commit a854fe9

4 files changed

Lines changed: 96 additions & 7 deletions

File tree

src/changes/changes.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88

99
<body>
1010
<release version="5.3.0" date="July xx, 2026" description="Bugfixes">
11+
<action type="fix" dev="rbri">
12+
Range setEndBefore() now really updates the end.
13+
</action>
1114
<action type="update" dev="rbri">
1215
SimpleRange containedNodes() now works also if startNode and endNode are the same.
1316
</action>

src/main/java/org/htmlunit/html/impl/SimpleRange.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,6 @@ public void deleteContents() {
224224

225225
private void deleteContents(final DomNode ancestor) {
226226
final DomNode start;
227-
final DomNode end;
228227
if (isOffsetChars(startContainer_)) {
229228
start = startContainer_;
230229
String text = getText(start);
@@ -239,6 +238,8 @@ else if (startContainer_.getChildNodes().getLength() > startOffset_) {
239238
else {
240239
start = startContainer_.getNextSibling();
241240
}
241+
242+
final DomNode end;
242243
if (isOffsetChars(endContainer_)) {
243244
end = endContainer_;
244245
String text = getText(end);
@@ -410,10 +411,10 @@ public void selectNode(final DomNode node) {
410411
* @param node Node to select from
411412
*/
412413
public void selectNodeContents(final DomNode node) {
413-
startContainer_ = node.getFirstChild();
414+
startContainer_ = node;
414415
startOffset_ = 0;
415-
endContainer_ = node.getLastChild();
416-
endOffset_ = getMaxOffset(node.getLastChild());
416+
endContainer_ = node;
417+
endOffset_ = getMaxOffset(node);
417418
}
418419

419420
/**

src/main/java/org/htmlunit/javascript/host/dom/Range.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -181,8 +181,8 @@ public void setEndBefore(final Node refNode) {
181181
if (refNode == null) {
182182
throw JavaScriptEngine.reportRuntimeError("It is illegal to call Range.setEndBefore() with a null node.");
183183
}
184-
internSetStartContainer(refNode.getParent());
185-
internSetStartOffset(getPositionInContainer(refNode));
184+
internSetEndContainer(refNode.getParent());
185+
internSetEndOffset(getPositionInContainer(refNode));
186186
}
187187

188188
/**

src/test/java/org/htmlunit/javascript/host/dom/RangeTest.java

Lines changed: 86 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -504,8 +504,8 @@ public void testToString() throws Exception {
504504
+ " log(r.toString());\n"
505505
+ "</script></body></html>";
506506
loadPageVerifyTitle2(html);
507-
508507
}
508+
509509
/**
510510
* Test getClientRects for a range over a text node (setStart/setEnd on a text node with char offsets).
511511
*
@@ -620,4 +620,89 @@ public void getClientRectsOnTextNodeMidString() throws Exception {
620620
+ "</script></body></html>";
621621
loadPageVerifyTitle2(html);
622622
}
623+
624+
/**
625+
* Tests all six boundary-point setters: setStart, setStartBefore, setStartAfter,
626+
* setEnd, setEndBefore, setEndAfter.
627+
* Verifies that each method sets the correct boundary (start vs end) and the
628+
* correct container/offset. In particular, setEndBefore was bugged: it called
629+
* internSetStartContainer/internSetStartOffset instead of the End variants.
630+
* @throws Exception if an error occurs
631+
*/
632+
@Test
633+
@Alerts({
634+
// setStart(b, 1) -> startContainer=DIV, startOffset=1
635+
"setStart: DIV 1 DIV 4",
636+
// setStartBefore(b) -> startContainer=DIV, startOffset=1 (b is at index 1)
637+
"setStartBefore: DIV 1 DIV 4",
638+
// setStartAfter(b) -> startContainer=DIV, startOffset=2 (after b = index 2)
639+
"setStartAfter: DIV 2 DIV 4",
640+
// setEnd(c, 2) -> endContainer=DIV, endOffset=2
641+
"setEnd: DIV 0 DIV 2",
642+
// setEndBefore(c) -> endContainer=DIV, endOffset=2 (c is at index 2)
643+
"setEndBefore: DIV 0 DIV 2",
644+
// setEndAfter(c) -> endContainer=DIV, endOffset=3 (after c = index 3)
645+
"setEndAfter: DIV 0 DIV 3"
646+
})
647+
public void setBoundaryPoints() throws Exception {
648+
final String html = DOCTYPE_HTML
649+
+ "<html><body>\n"
650+
+ " <div id='d'>"
651+
+ "<span id='a'>a</span>"
652+
+ "<span id='b'>b</span>"
653+
+ "<span id='c'>c</span>"
654+
+ "<span id='e'>e</span>"
655+
+ "</div>\n"
656+
+ "<script>\n"
657+
+ LOG_TITLE_FUNCTION
658+
+ "function rangeInfo(label, r) {\n"
659+
+ " var sc = r.startContainer.nodeName;\n"
660+
+ " var ec = r.endContainer.nodeName;\n"
661+
+ " log(label + ': ' + sc + ' ' + r.startOffset + ' ' + ec + ' ' + r.endOffset);\n"
662+
+ "}\n"
663+
+ "var d = document.getElementById('d');\n"
664+
+ "var a = document.getElementById('a');\n"
665+
+ "var b = document.getElementById('b');\n"
666+
+ "var c = document.getElementById('c');\n"
667+
668+
// setStart: sets startContainer=d, startOffset=1; end untouched (stays at d,3 after selectNodeContents)
669+
+ "var r = document.createRange();\n"
670+
+ "r.selectNodeContents(d);\n" // start=d/0, end=d/4
671+
+ "r.setStart(d, 1);\n"
672+
+ "rangeInfo('setStart', r);\n"
673+
674+
// setStartBefore(b): b is child index 1 of d -> startContainer=d, startOffset=1
675+
+ "r = document.createRange();\n"
676+
+ "r.selectNodeContents(d);\n"
677+
+ "r.setStartBefore(b);\n"
678+
+ "rangeInfo('setStartBefore', r);\n"
679+
680+
// setStartAfter(b): b is child index 1, so after = offset 2 -> startContainer=d, startOffset=2
681+
+ "r = document.createRange();\n"
682+
+ "r.selectNodeContents(d);\n"
683+
+ "r.setStartAfter(b);\n"
684+
+ "rangeInfo('setStartAfter', r);\n"
685+
686+
// setEnd: sets endContainer=d, endOffset=2; start untouched (stays at d,0)
687+
+ "r = document.createRange();\n"
688+
+ "r.selectNodeContents(d);\n"
689+
+ "r.setEnd(d, 2);\n"
690+
+ "rangeInfo('setEnd', r);\n"
691+
692+
// setEndBefore(c): c is child index 2 -> endContainer=d, endOffset=2; start must stay at d,0
693+
// BUG: setEndBefore was calling internSetStartContainer instead of internSetEndContainer
694+
+ "r = document.createRange();\n"
695+
+ "r.selectNodeContents(d);\n"
696+
+ "r.setEndBefore(c);\n"
697+
+ "rangeInfo('setEndBefore', r);\n"
698+
699+
// setEndAfter(c): c is child index 2, so after = offset 3 -> endContainer=d, endOffset=3; start stays at d,0
700+
+ "r = document.createRange();\n"
701+
+ "r.selectNodeContents(d);\n"
702+
+ "r.setEndAfter(c);\n"
703+
+ "rangeInfo('setEndAfter', r);\n"
704+
705+
+ "</script></body></html>";
706+
loadPageVerifyTitle2(html);
707+
}
623708
}

0 commit comments

Comments
 (0)