Skip to content

Commit 1ab653c

Browse files
Jeanette Winzenburgaghaisas
Jeanette Winzenburg
authored andcommitted
8244657: ChoiceBox/ToolBarSkin: misbehavior on switching skin
Reviewed-by: aghaisas
1 parent 804ccce commit 1ab653c

File tree

4 files changed

+155
-8
lines changed

4 files changed

+155
-8
lines changed

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

+11
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,17 @@ public ChoiceBoxSkin(ChoiceBox<T> control) {
206206

207207
/** {@inheritDoc} */
208208
@Override public void dispose() {
209+
// removing the content listener fixes NPE from listener
210+
if (choiceBoxItems != null) {
211+
choiceBoxItems.removeListener(weakChoiceBoxItemsListener);
212+
choiceBoxItems = null;
213+
}
214+
// removing the path listener fixes the memory leak on replacing skin
215+
if (selectionModel != null) {
216+
selectionModel.selectedIndexProperty().removeListener(selectionChangeListener);
217+
selectionModel = null;
218+
}
219+
209220
super.dispose();
210221

211222
if (behavior != null) {

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

+8-5
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ public class ToolBarSkin extends SkinBase<ToolBar> {
105105
private final ParentTraversalEngine engine;
106106
private final BehaviorBase<ToolBar> behavior;
107107

108-
108+
private ListChangeListener<Node> itemsListener;
109109

110110
/***************************************************************************
111111
* *
@@ -228,8 +228,8 @@ public Node selectLast(TraversalContext context) {
228228
});
229229
ParentHelper.setTraversalEngine(getSkinnable(), engine);
230230

231-
control.focusedProperty().addListener((observable, oldValue, newValue) -> {
232-
if (newValue) {
231+
registerChangeListener(control.focusedProperty(), ov -> {
232+
if (getSkinnable().isFocused()) {
233233
// TODO need to detect the focus direction
234234
// to selected the first control in the toolbar when TAB is pressed
235235
// or select the last control in the toolbar when SHIFT TAB is pressed.
@@ -241,7 +241,7 @@ public Node selectLast(TraversalContext context) {
241241
}
242242
});
243243

244-
control.getItems().addListener((ListChangeListener<Node>) c -> {
244+
itemsListener = (ListChangeListener<Node>) c -> {
245245
while (c.next()) {
246246
for (Node n: c.getRemoved()) {
247247
box.getChildren().remove(n);
@@ -250,7 +250,8 @@ public Node selectLast(TraversalContext context) {
250250
}
251251
needsUpdate = true;
252252
getSkinnable().requestLayout();
253-
});
253+
};
254+
control.getItems().addListener(itemsListener);
254255
}
255256

256257

@@ -365,6 +366,8 @@ public CssMetaData<ToolBar,Pos> getCssMetaData() {
365366

366367
/** {@inheritDoc} */
367368
@Override public void dispose() {
369+
if (getSkinnable() == null) return;
370+
getSkinnable().getItems().removeListener(itemsListener);
368371
super.dispose();
369372

370373
if (behavior != null) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
/*
2+
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
26+
package test.javafx.scene.control.skin;
27+
28+
import org.junit.After;
29+
import org.junit.Before;
30+
import org.junit.Test;
31+
32+
import static javafx.scene.control.ControlShim.*;
33+
import static org.junit.Assert.*;
34+
import static test.com.sun.javafx.scene.control.infrastructure.ControlSkinFactory.*;
35+
36+
import javafx.scene.Scene;
37+
import javafx.scene.control.Button;
38+
import javafx.scene.control.ChoiceBox;
39+
import javafx.scene.control.Control;
40+
import javafx.scene.control.ToolBar;
41+
import javafx.scene.layout.Pane;
42+
import javafx.scene.layout.VBox;
43+
import javafx.scene.shape.Rectangle;
44+
import javafx.stage.Stage;
45+
46+
/**
47+
* Tests around the cleanup task JDK-8241364.
48+
*/
49+
public class SkinCleanupTest {
50+
51+
private Scene scene;
52+
private Stage stage;
53+
private Pane root;
54+
55+
/**
56+
* NPE when adding items after skin is replaced
57+
*/
58+
@Test
59+
public void testChoiceBoxAddItems() {
60+
ChoiceBox<String> box = new ChoiceBox<>();
61+
installDefaultSkin(box);
62+
replaceSkin(box);
63+
box.getItems().add("added");
64+
}
65+
66+
@Test
67+
public void testToolBarAddItems() {
68+
ToolBar bar = new ToolBar();
69+
installDefaultSkin(bar);
70+
replaceSkin(bar);
71+
bar.getItems().add(new Rectangle());
72+
}
73+
74+
/**
75+
* Sanity test - fix changed listening to focusProperty, ensure
76+
* that it's still working as before.
77+
*/
78+
@Test
79+
public void testToolBarFocus() {
80+
ToolBar bar = new ToolBar();
81+
bar.getItems().addAll(new Button("dummy"), new Button("other"));
82+
showControl(bar, false);
83+
Button outside = new Button("outside");
84+
showControl(outside, true);
85+
bar.requestFocus();
86+
assertEquals("first item in toolbar must be focused", bar.getItems().get(0), scene.getFocusOwner());
87+
}
88+
89+
//---------------- setup and initial
90+
91+
/**
92+
* Ensures the control is shown in an active scenegraph. Requests
93+
* focus on the control if focused == true.
94+
*
95+
* @param control the control to show
96+
* @param focused if true, requests focus on the added control
97+
*/
98+
protected void showControl(Control control, boolean focused) {
99+
if (root == null) {
100+
root = new VBox();
101+
scene = new Scene(root);
102+
stage = new Stage();
103+
stage.setScene(scene);
104+
}
105+
if (!root.getChildren().contains(control)) {
106+
root.getChildren().add(control);
107+
}
108+
stage.show();
109+
if (focused) {
110+
stage.requestFocus();
111+
control.requestFocus();
112+
assertTrue(control.isFocused());
113+
assertSame(control, scene.getFocusOwner());
114+
}
115+
}
116+
117+
@After
118+
public void cleanup() {
119+
if (stage != null) stage.hide();
120+
Thread.currentThread().setUncaughtExceptionHandler(null);
121+
}
122+
123+
@Before
124+
public void setup() {
125+
Thread.currentThread().setUncaughtExceptionHandler((thread, throwable) -> {
126+
if (throwable instanceof RuntimeException) {
127+
throw (RuntimeException)throwable;
128+
} else {
129+
Thread.currentThread().getThreadGroup().uncaughtException(thread, throwable);
130+
}
131+
});
132+
}
133+
134+
}
135+
136+

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

-3
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,6 @@ public static Collection<Object[]> data() {
107107
List<Class<? extends Control>> leakingClasses = List.of(
108108
Accordion.class,
109109
ButtonBar.class,
110-
// @Ignore("8244657")
111-
ChoiceBox.class,
112110
ColorPicker.class,
113111
ComboBox.class,
114112
DatePicker.class,
@@ -132,7 +130,6 @@ public static Collection<Object[]> data() {
132130
TextArea.class,
133131
// @Ignore("8240506")
134132
TextField.class,
135-
ToolBar.class,
136133
TreeCell.class,
137134
TreeTableRow.class,
138135
TreeTableView.class,

0 commit comments

Comments
 (0)