Skip to content

Commit 492cb6c

Browse files
committed
8176270: Fix TextInputControl String boundaries
1 parent 69e4ef3 commit 492cb6c

File tree

2 files changed

+108
-2
lines changed

2 files changed

+108
-2
lines changed

modules/javafx.controls/src/main/java/javafx/scene/control/TextInputControl.java

+4-2
Original file line numberDiff line numberDiff line change
@@ -164,8 +164,10 @@ protected TextInputControl(final Content content) {
164164
int start = sel.getStart();
165165
int end = sel.getEnd();
166166
int length = txt.length();
167-
if (end > start + length) end = length;
168-
if (start > length-1) start = end = 0;
167+
// Ensure that the last character to get is within the bounds of the txt string
168+
if (end >= start + length) end = length-1;
169+
// In case the start is after the whole txt, nothing valid is selected. Thus, return the default.
170+
if (start >= length) return "";
169171
return txt.substring(start, end);
170172
}
171173
});

modules/javafx.controls/src/test/java/test/javafx/scene/control/TextInputControlTest.java

+104
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
package test.javafx.scene.control;
2727

28+
import javafx.application.Platform;
2829
import javafx.beans.InvalidationListener;
2930
import javafx.beans.Observable;
3031
import javafx.beans.property.BooleanProperty;
@@ -35,17 +36,21 @@
3536
import javafx.beans.value.ObservableValue;
3637
import javafx.css.CssMetaData;
3738
import javafx.css.StyleableProperty;
39+
import javafx.event.Event;
3840
import javafx.event.EventHandler;
3941
import javafx.scene.Scene;
4042
import javafx.scene.input.KeyCode;
4143
import javafx.scene.input.KeyEvent;
4244
import javafx.scene.input.Clipboard;
4345
import javafx.scene.input.ClipboardContent;
46+
import javafx.scene.layout.VBox;
4447
import javafx.scene.text.Font;
4548
import javafx.scene.layout.StackPane;
4649
import javafx.stage.Stage;
4750
import java.util.Arrays;
4851
import java.util.Collection;
52+
import java.util.concurrent.Semaphore;
53+
4954
import javafx.scene.control.IndexRange;
5055
import javafx.scene.control.PasswordField;
5156
import javafx.scene.control.TextArea;
@@ -2043,6 +2048,105 @@ public void caretAndAnchorPositionAfterSettingText() {
20432048
tk.firePulse();
20442049
}
20452050

2051+
// Test for case 1 of JDK-8176270
2052+
@Test public void addingListenerWorks() {
2053+
VBox vBox = new VBox();
2054+
TextField textField = new TextField();
2055+
textField.setText("1234 5678");
2056+
vBox.getChildren().add(textField);
2057+
textField.selectedTextProperty()
2058+
.addListener((observable -> {}));
2059+
2060+
Scene scene = new Scene(vBox);
2061+
Stage stage = new Stage();
2062+
stage.setScene(scene);
2063+
stage.show();
2064+
}
2065+
2066+
// Test for case 2 of JDK-8176270
2067+
@Test public void replaceSelectionWorks() throws Exception {
2068+
VBox vBox = new VBox();
2069+
TextField textField = new TextField();
2070+
textField.setText("1234 5678");
2071+
vBox.getChildren().add(textField);
2072+
textField.selectedTextProperty()
2073+
.addListener((observable -> {}));
2074+
2075+
Scene scene = new Scene(vBox);
2076+
Stage stage = new Stage();
2077+
stage.setScene(scene);
2078+
stage.show();
2079+
2080+
textField.selectedTextProperty()
2081+
.addListener(observable -> {
2082+
// accessing the selectedTextProperty causes a
2083+
// StringOutOfBoundsException
2084+
observable.toString();
2085+
});
2086+
textField.positionCaret(5);
2087+
Semaphore semaphore = new Semaphore(0);
2088+
Platform.runLater(semaphore::release);
2089+
semaphore.acquire();
2090+
2091+
// select 2nd word
2092+
textField.selectNextWord();
2093+
semaphore = new Semaphore(0);
2094+
Platform.runLater(semaphore::release);
2095+
semaphore.acquire();
2096+
2097+
// replace selection
2098+
Platform.runLater(() -> {Event.fireEvent(scene, new KeyEvent(KeyEvent.KEY_PRESSED, "", KeyCode.DIGIT0.getName(), KeyCode.DIGIT0, false, false, false, false));});
2099+
Platform.runLater(() -> {Event.fireEvent(scene, new KeyEvent(KeyEvent.KEY_RELEASED, "", KeyCode.DIGIT0.getName(), KeyCode.DIGIT0, false, false, false, false));});
2100+
semaphore = new Semaphore(0);
2101+
Platform.runLater(semaphore::release);
2102+
semaphore.acquire();
2103+
}
2104+
2105+
// Test for workaround of JDK-8176270
2106+
@Test public void accessingTheValueInInvalidationListenerWorks() throws Exception {
2107+
VBox vBox = new VBox();
2108+
TextField textField = new TextField();
2109+
textField.setText("1234 5678");
2110+
vBox.getChildren().add(textField);
2111+
textField.selectedTextProperty()
2112+
.addListener((observable -> {}));
2113+
2114+
Scene scene = new Scene(vBox);
2115+
Stage stage = new Stage();
2116+
stage.setScene(scene);
2117+
stage.show();
2118+
2119+
textField.selectedTextProperty()
2120+
.addListener(new InvalidationListener() {
2121+
@Override
2122+
public void invalidated(Observable observable) {
2123+
Platform.runLater(() -> observable.toString());
2124+
}
2125+
});
2126+
2127+
textField.positionCaret(5);
2128+
Semaphore semaphore = new Semaphore(0);
2129+
Platform.runLater(semaphore::release);
2130+
semaphore.acquire();
2131+
2132+
// select 2nd word
2133+
textField.selectNextWord();
2134+
semaphore = new Semaphore(0);
2135+
Platform.runLater(semaphore::release);
2136+
semaphore.acquire();
2137+
2138+
// replace selection
2139+
Platform.runLater(() -> {Event.fireEvent(scene,
2140+
new KeyEvent(KeyEvent.KEY_PRESSED, "", KeyCode.DIGIT0.getName(), KeyCode.DIGIT0,
2141+
false, false, false, false));});
2142+
Platform.runLater(() -> {Event.fireEvent(scene,
2143+
new KeyEvent(KeyEvent.KEY_RELEASED, "", KeyCode.DIGIT0.getName(), KeyCode.DIGIT0,
2144+
false, false, false, false));});
2145+
semaphore = new Semaphore(0);
2146+
Platform.runLater(semaphore::release);
2147+
semaphore.acquire();
2148+
}
2149+
20462150
// TODO tests for Content firing event notification properly
20472151

20482152
// TODO tests for Content not allowing illegal characters

0 commit comments

Comments
 (0)