Skip to content

Commit ca0d3d0

Browse files
author
Jeanette Winzenburg
committed
8256821: TreeViewSkin/Behavior: misbehavior on switching skin
Reviewed-by: arapte
1 parent 00a8646 commit ca0d3d0

File tree

6 files changed

+145
-16
lines changed

6 files changed

+145
-16
lines changed

modules/javafx.controls/src/main/java/com/sun/javafx/scene/control/behavior/TreeViewBehavior.java

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -154,14 +154,6 @@ public class TreeViewBehavior<T> extends BehaviorBase<TreeView<T>> {
154154
public TreeViewBehavior(TreeView<T> control) {
155155
super(control);
156156

157-
// // Fix for RT-16565
158-
// getNode().selectionModelProperty().addListener(weakSelectionModelListener);
159-
// if (control.getSelectionModel() != null) {
160-
// control.getSelectionModel().getSelectedIndices().addListener(weakSelectedIndicesListener);
161-
// }
162-
163-
164-
165157
// create a map for treeView-specific mappings
166158
treeViewInputMap = createInputMap();
167159

@@ -255,6 +247,12 @@ public TreeViewBehavior(TreeView<T> control) {
255247
}
256248

257249
@Override public void dispose() {
250+
getNode().selectionModelProperty().removeListener(weakSelectionModelListener);
251+
MultipleSelectionModel<TreeItem<T>> sm = getNode().getSelectionModel();
252+
if (sm != null) {
253+
sm.getSelectedIndices().removeListener(weakSelectedIndicesListener);
254+
}
255+
getNode().removeEventFilter(KeyEvent.ANY, keyEventListener);
258256
TreeCellBehavior.removeAnchor(getNode());
259257
super.dispose();
260258
}

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

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,10 @@ public class TreeViewSkin<T> extends VirtualContainerBase<TreeView<T>, TreeCell<
136136

137137

138138

139+
private EventHandler<MouseEvent> ml;
140+
141+
142+
139143
/***************************************************************************
140144
* *
141145
* Constructors *
@@ -165,7 +169,7 @@ public TreeViewSkin(final TreeView control) {
165169

166170
setRoot(getSkinnable().getRoot());
167171

168-
EventHandler<MouseEvent> ml = event -> {
172+
ml = event -> {
169173
// RT-15127: cancel editing on scroll. This is a bit extreme
170174
// (we are cancelling editing on touching the scrollbars).
171175
// This can be improved at a later date.
@@ -226,6 +230,17 @@ public TreeViewSkin(final TreeView control) {
226230

227231
/** {@inheritDoc} */
228232
@Override public void dispose() {
233+
if (getSkinnable() == null) return;
234+
235+
getSkinnable().getProperties().removeListener(propertiesMapListener);
236+
setRoot(null);
237+
// leaking without nulling factory
238+
flow.setCellFactory(null);
239+
// for completeness - but no effect with/out? Same as in ListViewSkin
240+
// don't without seeing any effect - it's not on the skinnable, but on a child, so shouldn't
241+
flow.getVbar().removeEventFilter(MouseEvent.MOUSE_PRESSED, ml);
242+
flow.getHbar().removeEventFilter(MouseEvent.MOUSE_PRESSED, ml);
243+
229244
super.dispose();
230245

231246
if (behavior != null) {

modules/javafx.controls/src/test/java/test/com/sun/javafx/scene/control/behavior/BehaviorCleanupTest.java

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@
3939
import static test.com.sun.javafx.scene.control.infrastructure.ControlSkinFactory.*;
4040

4141
import javafx.scene.control.ListView;
42+
import javafx.scene.control.TreeItem;
43+
import javafx.scene.control.TreeView;
4244

4345
/**
4446
* Test for misbehavior of individual implementations that turned
@@ -47,6 +49,51 @@
4749
*/
4850
public class BehaviorCleanupTest {
4951

52+
//----------- TreeView
53+
54+
/**
55+
* Test cleanup of selection listeners in TreeViewBehavior.
56+
*/
57+
@Test
58+
public void testTreeViewBehaviorDisposeSelect() {
59+
TreeView<String> treeView = new TreeView<>(createRoot());
60+
WeakReference<BehaviorBase<?>> weakRef = new WeakReference<>(createBehavior(treeView));
61+
treeView.getSelectionModel().select(1);
62+
weakRef.get().dispose();
63+
treeView.getSelectionModel().select(0);
64+
assertNull("anchor must remain cleared on selecting when disposed",
65+
treeView.getProperties().get("anchor"));
66+
}
67+
68+
@Test
69+
public void testTreeViewBehaviorSelect() {
70+
TreeView<String> treeView = new TreeView<>(createRoot());
71+
createBehavior(treeView);
72+
int last = 1;
73+
treeView.getSelectionModel().select(last);
74+
assertEquals("anchor must be set", last, treeView.getProperties().get("anchor"));
75+
}
76+
77+
@Test
78+
public void testTreeViewBehaviorDispose() {
79+
TreeView<String> treeView = new TreeView<>(createRoot());
80+
WeakReference<BehaviorBase<?>> weakRef = new WeakReference<>(createBehavior(treeView));
81+
treeView.getSelectionModel().select(1);
82+
weakRef.get().dispose();
83+
assertNull("anchor must be cleared after dispose", treeView.getProperties().get("anchor"));
84+
}
85+
86+
/**
87+
* Creates and returns an expanded treeItem with two children.
88+
*/
89+
private TreeItem<String> createRoot() {
90+
TreeItem<String> root = new TreeItem<>("root");
91+
root.setExpanded(true);
92+
root.getChildren().addAll(new TreeItem<>("child one"), new TreeItem<>("child two"));
93+
return root;
94+
}
95+
96+
5097
// ---------- ListView
5198

5299
/**

modules/javafx.controls/src/test/java/test/com/sun/javafx/scene/control/behavior/BehaviorMemoryLeakTest.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@
4646
import javafx.scene.control.TextArea;
4747
import javafx.scene.control.TextField;
4848
import javafx.scene.control.TreeTableView;
49-
import javafx.scene.control.TreeView;
5049

5150
/**
5251
* Test for memory leaks in Behavior implementations.
@@ -86,8 +85,7 @@ public static Collection<Object[]> data() {
8685
TableView.class,
8786
TextArea.class,
8887
TextField.class,
89-
TreeTableView.class,
90-
TreeView.class
88+
TreeTableView.class
9189
);
9290
// remove the known issues to make the test pass
9391
controlClasses.removeAll(leakingClasses);

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

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,10 @@
2525

2626
package test.javafx.scene.control.skin;
2727

28+
import java.lang.ref.WeakReference;
29+
2830
import org.junit.After;
2931
import org.junit.Before;
30-
import org.junit.Ignore;
3132
import org.junit.Test;
3233

3334
import static javafx.collections.FXCollections.*;
@@ -43,6 +44,7 @@
4344
import javafx.scene.control.ListView;
4445
import javafx.scene.control.ToolBar;
4546
import javafx.scene.control.TreeCell;
47+
import javafx.scene.control.TreeItem;
4648
import javafx.scene.control.TreeView;
4749
import javafx.scene.layout.Pane;
4850
import javafx.scene.layout.VBox;
@@ -58,6 +60,77 @@ public class SkinCleanupTest {
5860
private Stage stage;
5961
private Pane root;
6062

63+
//---------------- TreeView
64+
65+
/**
66+
* Sanity: replacing the root has no side-effect, listener to rootProperty
67+
* is registered with skin api
68+
*/
69+
@Test
70+
public void testTreeViewSetRoot() {
71+
TreeView<String> treeView = new TreeView<>(createRoot());
72+
installDefaultSkin(treeView);
73+
replaceSkin(treeView);
74+
treeView.setRoot(createRoot());
75+
}
76+
77+
/**
78+
* NPE from event handler to treeModification of root.
79+
*/
80+
@Test
81+
public void testTreeViewAddRootChild() {
82+
TreeView<String> treeView = new TreeView<>(createRoot());
83+
installDefaultSkin(treeView);
84+
replaceSkin(treeView);
85+
treeView.getRoot().getChildren().add(createRoot());
86+
}
87+
88+
/**
89+
* NPE from event handler to treeModification of root.
90+
*/
91+
@Test
92+
public void testTreeViewReplaceRootChildren() {
93+
TreeView<String> treeView = new TreeView<>(createRoot());
94+
installDefaultSkin(treeView);
95+
replaceSkin(treeView);
96+
treeView.getRoot().getChildren().setAll(createRoot().getChildren());
97+
}
98+
99+
/**
100+
* NPE due to properties listener not removed
101+
*/
102+
@Test
103+
public void testTreeViewRefresh() {
104+
TreeView<String> treeView = new TreeView<>();
105+
installDefaultSkin(treeView);
106+
replaceSkin(treeView);
107+
treeView.refresh();
108+
}
109+
110+
/**
111+
* Sanity: guard against potential memory leak from root property listener.
112+
*/
113+
@Test
114+
public void testMemoryLeakAlternativeSkinWithRoot() {
115+
TreeView<String> treeView = new TreeView<>(createRoot());
116+
installDefaultSkin(treeView);
117+
WeakReference<?> weakRef = new WeakReference<>(replaceSkin(treeView));
118+
assertNotNull(weakRef.get());
119+
attemptGC(weakRef);
120+
assertEquals("Skin must be gc'ed", null, weakRef.get());
121+
}
122+
123+
/**
124+
* Creates and returns an expanded treeItem with two children
125+
*/
126+
private TreeItem<String> createRoot() {
127+
TreeItem<String> root = new TreeItem<>("root");
128+
root.setExpanded(true);
129+
root.getChildren().addAll(new TreeItem<>("child one"), new TreeItem<>("child two"));
130+
return root;
131+
}
132+
133+
61134
// ------------------ TreeCell
62135

63136
@Test

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

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,6 @@
6161
import javafx.scene.control.TextField;
6262
import javafx.scene.control.TreeTableRow;
6363
import javafx.scene.control.TreeTableView;
64-
import javafx.scene.control.TreeView;
6564

6665
/**
6766
* Test memory leaks in Skin implementations.
@@ -123,8 +122,7 @@ public static Collection<Object[]> data() {
123122
// @Ignore("8240506")
124123
TextField.class,
125124
TreeTableRow.class,
126-
TreeTableView.class,
127-
TreeView.class
125+
TreeTableView.class
128126
);
129127
// remove the known issues to make the test pass
130128
controlClasses.removeAll(leakingClasses);

0 commit comments

Comments
 (0)