Skip to content

Commit e931501

Browse files
author
Jeanette Winzenburg
committed
8269081: Tree/ListViewSkin: must remove flow on dispose
Reviewed-by: arapte
1 parent 91f0170 commit e931501

File tree

3 files changed

+72
-22
lines changed

3 files changed

+72
-22
lines changed

modules/javafx.controls/src/main/java/javafx/scene/control/skin/ListViewSkin.java

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -175,8 +175,6 @@ public class ListViewSkin<T> extends VirtualContainerBase<ListView<T>, ListCell<
175175
private WeakInvalidationListener
176176
weakItemsChangeListener = new WeakInvalidationListener(itemsChangeListener);
177177

178-
private EventHandler<MouseEvent> ml;
179-
180178
/* *************************************************************************
181179
* *
182180
* Constructors *
@@ -218,7 +216,7 @@ public ListViewSkin(final ListView<T> control) {
218216
flow.setFixedCellSize(control.getFixedCellSize());
219217
getChildren().add(flow);
220218

221-
ml = event -> {
219+
EventHandler<MouseEvent> ml = event -> {
222220
// RT-15127: cancel editing on scroll. This is a bit extreme
223221
// (we are cancelling editing on touching the scrollbars).
224222
// This can be improved at a later date.
@@ -281,12 +279,7 @@ public ListViewSkin(final ListView<T> control) {
281279
listViewItems.removeListener(weakListViewItemsListener);
282280
listViewItems = null;
283281
}
284-
// flow related cleanup
285-
// leaking without nulling factory
286-
flow.setCellFactory(null);
287-
// for completeness - but no effect with/out?
288-
flow.getVbar().removeEventFilter(MouseEvent.MOUSE_PRESSED, ml);
289-
flow.getHbar().removeEventFilter(MouseEvent.MOUSE_PRESSED, ml);
282+
getChildren().remove(flow);
290283
super.dispose();
291284

292285
if (behavior != null) {

modules/javafx.controls/src/main/java/javafx/scene/control/skin/TreeViewSkin.java

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -136,11 +136,6 @@ public class TreeViewSkin<T> extends VirtualContainerBase<TreeView<T>, TreeCell<
136136
private WeakEventHandler<TreeModificationEvent<T>> weakRootListener;
137137

138138

139-
140-
private EventHandler<MouseEvent> ml;
141-
142-
143-
144139
/* *************************************************************************
145140
* *
146141
* Constructors *
@@ -170,7 +165,7 @@ public TreeViewSkin(final TreeView control) {
170165

171166
setRoot(getSkinnable().getRoot());
172167

173-
ml = event -> {
168+
EventHandler<MouseEvent> ml = event -> {
174169
// RT-15127: cancel editing on scroll. This is a bit extreme
175170
// (we are cancelling editing on touching the scrollbars).
176171
// This can be improved at a later date.
@@ -235,13 +230,7 @@ public TreeViewSkin(final TreeView control) {
235230

236231
getSkinnable().getProperties().removeListener(propertiesMapListener);
237232
setRoot(null);
238-
// leaking without nulling factory
239-
flow.setCellFactory(null);
240-
// for completeness - but no effect with/out? Same as in ListViewSkin
241-
// don't without seeing any effect - it's not on the skinnable, but on a child, so shouldn't
242-
flow.getVbar().removeEventFilter(MouseEvent.MOUSE_PRESSED, ml);
243-
flow.getHbar().removeEventFilter(MouseEvent.MOUSE_PRESSED, ml);
244-
233+
getChildren().remove(flow);
245234
super.dispose();
246235

247236
if (behavior != null) {

modules/javafx.controls/src/test/java/test/javafx/scene/control/skin/SkinMemoryLeakTest.java

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,13 @@
3535
import org.junit.runner.RunWith;
3636
import org.junit.runners.Parameterized;
3737

38+
import com.sun.javafx.tk.Toolkit;
39+
3840
import static javafx.scene.control.ControlShim.*;
3941
import static org.junit.Assert.*;
4042
import static test.com.sun.javafx.scene.control.infrastructure.ControlSkinFactory.*;
4143

44+
import javafx.scene.Scene;
4245
import javafx.scene.control.Accordion;
4346
import javafx.scene.control.ButtonBar;
4447
import javafx.scene.control.ColorPicker;
@@ -51,13 +54,18 @@
5154
import javafx.scene.control.PasswordField;
5255
import javafx.scene.control.ScrollBar;
5356
import javafx.scene.control.ScrollPane;
57+
import javafx.scene.control.Separator;
58+
import javafx.scene.control.Skin;
5459
import javafx.scene.control.Spinner;
5560
import javafx.scene.control.SplitMenuButton;
5661
import javafx.scene.control.SplitPane;
5762
import javafx.scene.control.TableRow;
5863
import javafx.scene.control.TableView;
5964
import javafx.scene.control.TreeTableRow;
6065
import javafx.scene.control.TreeTableView;
66+
import javafx.scene.layout.Pane;
67+
import javafx.scene.layout.VBox;
68+
import javafx.stage.Stage;
6169

6270
/**
6371
* Test memory leaks in Skin implementations.
@@ -78,12 +86,39 @@ public class SkinMemoryLeakTest {
7886
@Test
7987
public void testMemoryLeakAlternativeSkin() {
8088
installDefaultSkin(control);
89+
// FIXME: JDK-8265406 - fragile test pattern
8190
WeakReference<?> weakRef = new WeakReference<>(replaceSkin(control));
8291
assertNotNull(weakRef.get());
8392
attemptGC(weakRef);
8493
assertEquals("Skin must be gc'ed", null, weakRef.get());
8594
}
8695

96+
/**
97+
* default skin -> set alternative while showing
98+
*/
99+
@Test
100+
public void testMemoryLeakAlternativeSkinShowing() {
101+
showControl(control, true);
102+
Skin<?> replacedSkin = replaceSkin(control);
103+
WeakReference<?> weakRef = new WeakReference<>(replacedSkin);
104+
assertNotNull(weakRef.get());
105+
// beware: this is important - we might get false reds without!
106+
Toolkit.getToolkit().firePulse();
107+
replacedSkin = null;
108+
attemptGC(weakRef);
109+
assertEquals("Skin must be gc'ed", null, weakRef.get());
110+
}
111+
112+
@Test
113+
public void testControlChildren() {
114+
installDefaultSkin(control);
115+
int childCount = control.getChildrenUnmodifiable().size();
116+
String skinClass = control.getSkin().getClass().getSimpleName();
117+
replaceSkin(control);
118+
assertEquals(skinClass + " must remove direct children that it has added",
119+
childCount, control.getChildrenUnmodifiable().size());
120+
}
121+
87122
//------------ parameters
88123

89124
// Note: name property not supported before junit 4.11
@@ -106,6 +141,8 @@ public static Collection<Object[]> data() {
106141
PasswordField.class,
107142
ScrollBar.class,
108143
ScrollPane.class,
144+
// @Ignore("8273071")
145+
Separator.class,
109146
// @Ignore("8245145")
110147
Spinner.class,
111148
SplitMenuButton.class,
@@ -126,6 +163,36 @@ public SkinMemoryLeakTest(Class<Control> controlClass) {
126163

127164
//------------ setup
128165

166+
private Scene scene;
167+
private Stage stage;
168+
private Pane root;
169+
170+
/**
171+
* Ensures the control is shown in an active scenegraph. Requests
172+
* focus on the control if focused == true.
173+
*
174+
* @param control the control to show
175+
* @param focused if true, requests focus on the added control
176+
*/
177+
protected void showControl(Control control, boolean focused) {
178+
if (root == null) {
179+
root = new VBox();
180+
scene = new Scene(root);
181+
stage = new Stage();
182+
stage.setScene(scene);
183+
}
184+
if (!root.getChildren().contains(control)) {
185+
root.getChildren().add(control);
186+
}
187+
stage.show();
188+
if (focused) {
189+
stage.requestFocus();
190+
control.requestFocus();
191+
assertTrue(control.isFocused());
192+
assertSame(control, scene.getFocusOwner());
193+
}
194+
}
195+
129196
@Before
130197
public void setup() {
131198
Thread.currentThread().setUncaughtExceptionHandler((thread, throwable) -> {
@@ -141,6 +208,7 @@ public void setup() {
141208

142209
@After
143210
public void cleanup() {
211+
if (stage != null) stage.hide();
144212
Thread.currentThread().setUncaughtExceptionHandler(null);
145213
}
146214

0 commit comments

Comments
 (0)