# HG changeset patch # User ant # Date 1213695448 -14400 # Node ID a2782dd9f312f52721877df3d7e6b34f0a1e6fc9 # Parent 4e03057627b7d429b75d3d90b20319c354eb670a 4685768: A11y issue - Focus set to disabled component, can't Tab/Shift-Tab Summary: The restore-focus procedure should skip disabled components. Reviewed-by: art, dcherepanov diff -r 4e03057627b7 -r a2782dd9f312 jdk/src/share/classes/java/awt/Component.java --- a/jdk/src/share/classes/java/awt/Component.java Wed Jun 11 01:31:42 2008 -0700 +++ b/jdk/src/share/classes/java/awt/Component.java Tue Jun 17 13:37:28 2008 +0400 @@ -7488,9 +7488,7 @@ Container rootAncestor = getTraversalRoot(); Component comp = this; while (rootAncestor != null && - !(rootAncestor.isShowing() && - rootAncestor.isFocusable() && - rootAncestor.isEnabled())) + !(rootAncestor.isShowing() && rootAncestor.canBeFocusOwner())) { comp = rootAncestor; rootAncestor = comp.getFocusCycleRootAncestor(); @@ -7539,9 +7537,7 @@ Container rootAncestor = getTraversalRoot(); Component comp = this; while (rootAncestor != null && - !(rootAncestor.isShowing() && - rootAncestor.isFocusable() && - rootAncestor.isEnabled())) + !(rootAncestor.isShowing() && rootAncestor.canBeFocusOwner())) { comp = rootAncestor; rootAncestor = comp.getFocusCycleRootAncestor(); @@ -8518,6 +8514,14 @@ setComponentOrientation(orientation); } + final boolean canBeFocusOwner() { + // It is enabled, visible, focusable. + if (isEnabled() && isDisplayable() && isVisible() && isFocusable()) { + return true; + } + return false; + } + /** * Checks that this component meets the prerequesites to be focus owner: * - it is enabled, visible, focusable @@ -8527,9 +8531,9 @@ * this component as focus owner * @since 1.5 */ - final boolean canBeFocusOwner() { + final boolean canBeFocusOwnerRecursively() { // - it is enabled, visible, focusable - if (!(isEnabled() && isDisplayable() && isVisible() && isFocusable())) { + if (!canBeFocusOwner()) { return false; } diff -r 4e03057627b7 -r a2782dd9f312 jdk/src/share/classes/java/awt/Container.java --- a/jdk/src/share/classes/java/awt/Container.java Wed Jun 11 01:31:42 2008 -0700 +++ b/jdk/src/share/classes/java/awt/Container.java Tue Jun 17 13:37:28 2008 +0400 @@ -860,11 +860,11 @@ // If component is focus owner or parent container of focus owner check that after reparenting // focus owner moved out if new container prohibit this kind of focus owner. - if (comp.isFocusOwner() && !comp.canBeFocusOwner()) { + if (comp.isFocusOwner() && !comp.canBeFocusOwnerRecursively()) { comp.transferFocus(); } else if (comp instanceof Container) { Component focusOwner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner(); - if (focusOwner != null && isParentOf(focusOwner) && !focusOwner.canBeFocusOwner()) { + if (focusOwner != null && isParentOf(focusOwner) && !focusOwner.canBeFocusOwnerRecursively()) { focusOwner.transferFocus(); } } diff -r 4e03057627b7 -r a2782dd9f312 jdk/src/share/classes/java/awt/ContainerOrderFocusTraversalPolicy.java --- a/jdk/src/share/classes/java/awt/ContainerOrderFocusTraversalPolicy.java Wed Jun 11 01:31:42 2008 -0700 +++ b/jdk/src/share/classes/java/awt/ContainerOrderFocusTraversalPolicy.java Tue Jun 17 13:37:28 2008 +0400 @@ -556,8 +556,7 @@ * enabled, and focusable; false otherwise */ protected boolean accept(Component aComponent) { - if (!(aComponent.isVisible() && aComponent.isDisplayable() && - aComponent.isFocusable() && aComponent.isEnabled())) { + if (!aComponent.canBeFocusOwner()) { return false; } diff -r 4e03057627b7 -r a2782dd9f312 jdk/src/share/classes/java/awt/DefaultKeyboardFocusManager.java --- a/jdk/src/share/classes/java/awt/DefaultKeyboardFocusManager.java Wed Jun 11 01:31:42 2008 -0700 +++ b/jdk/src/share/classes/java/awt/DefaultKeyboardFocusManager.java Tue Jun 17 13:37:28 2008 +0400 @@ -154,7 +154,7 @@ private boolean doRestoreFocus(Component toFocus, Component vetoedComponent, boolean clearOnFailure) { - if (toFocus != vetoedComponent && toFocus.isShowing() && toFocus.isFocusable() && + if (toFocus != vetoedComponent && toFocus.isShowing() && toFocus.canBeFocusOwner() && toFocus.requestFocus(false, CausedFocusEvent.Cause.ROLLBACK)) { return true; @@ -500,8 +500,11 @@ } } - if (!(newFocusOwner.isFocusable() && newFocusOwner.isEnabled() - && newFocusOwner.isShowing())) + if (!(newFocusOwner.isFocusable() && newFocusOwner.isShowing() && + // Refuse focus on a disabled component if the focus event + // isn't of UNKNOWN reason (i.e. not a result of a direct request + // but traversal, activation or system generated). + (newFocusOwner.isEnabled() || cause.equals(CausedFocusEvent.Cause.UNKNOWN)))) { // we should not accept focus on such component, so reject it. dequeueKeyEvents(-1, newFocusOwner); @@ -742,8 +745,7 @@ public boolean dispatchKeyEvent(KeyEvent e) { Component focusOwner = (((AWTEvent)e).isPosted) ? getFocusOwner() : e.getComponent(); - if (focusOwner != null && focusOwner.isShowing() && - focusOwner.isFocusable() && focusOwner.isEnabled()) { + if (focusOwner != null && focusOwner.isShowing() && focusOwner.canBeFocusOwner()) { if (!e.isConsumed()) { Component comp = e.getComponent(); if (comp != null && comp.isEnabled()) { diff -r 4e03057627b7 -r a2782dd9f312 jdk/src/share/classes/java/awt/Window.java --- a/jdk/src/share/classes/java/awt/Window.java Wed Jun 11 01:31:42 2008 -0700 +++ b/jdk/src/share/classes/java/awt/Window.java Tue Jun 17 13:37:28 2008 +0400 @@ -3145,9 +3145,7 @@ Component previousComp = temporaryLostComponent; // Check that "component" is an acceptable focus owner and don't store it otherwise // - or later we will have problems with opposite while handling WINDOW_GAINED_FOCUS - if (component == null - || (component.isDisplayable() && component.isVisible() && component.isEnabled() && component.isFocusable())) - { + if (component == null || component.canBeFocusOwner()) { temporaryLostComponent = component; } else { temporaryLostComponent = null; diff -r 4e03057627b7 -r a2782dd9f312 jdk/test/java/awt/Focus/NoAutotransferToDisabledCompTest/NoAutotransferToDisabledCompTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/awt/Focus/NoAutotransferToDisabledCompTest/NoAutotransferToDisabledCompTest.java Tue Jun 17 13:37:28 2008 +0400 @@ -0,0 +1,109 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + @test + @bug 4685768 + @summary Tests that auto-transfering focus doesn't stuck on a disabled component. + @author Anton Tarasov: area=awt.focus + @library ../../regtesthelpers + @build Util + @run main NoAutotransferToDisabledCompTest +*/ + +import java.awt.Robot; +import javax.swing.*; +import java.awt.*; +import java.awt.event.*; +import java.applet.Applet; +import test.java.awt.regtesthelpers.Util; + +public class NoAutotransferToDisabledCompTest extends Applet { + Robot robot; + JFrame frame = new JFrame("Frame"); + JButton b0 = new JButton("b0"); + JButton b1 = new JButton("b1"); + JButton b2 = new JButton("b2"); + + public static void main(String[] args) { + NoAutotransferToDisabledCompTest app = new NoAutotransferToDisabledCompTest(); + app.init(); + app.start(); + } + + public void init() { + robot = Util.createRobot(); + frame.add(b0); + frame.add(b1); + frame.add(b2); + frame.setLayout(new FlowLayout()); + frame.pack(); + + b1.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + b1.setEnabled(false); + b2.setEnabled(false); + } + }); + } + + public void start() { + Util.showWindowWait(frame); + + // Request focus on b1. + if (!Util.focusComponent(b1, 2000)) { + throw new TestErrorException("couldn't focus " + b1); + } + + // Activate b1. + robot.keyPress(KeyEvent.VK_SPACE); + robot.delay(50); + robot.keyRelease(KeyEvent.VK_SPACE); + Util.waitForIdle(robot); + + // Check that focus has been transfered to b0. + if (!b0.hasFocus()) { + throw new TestFailedException("focus wasn't auto-transfered properly!"); + } + System.out.println("Test passed."); + } +} + +/** + * Thrown when the behavior being verified is found wrong. + */ +class TestFailedException extends RuntimeException { + TestFailedException(String msg) { + super("Test failed: " + msg); + } +} + +/** + * Thrown when an error not related to the behavior being verified is encountered. + */ +class TestErrorException extends RuntimeException { + TestErrorException(String msg) { + super("Unexpected error: " + msg); + } +} + diff -r 4e03057627b7 -r a2782dd9f312 jdk/test/java/awt/Focus/RequestFocusToDisabledCompTest/RequestFocusToDisabledCompTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/awt/Focus/RequestFocusToDisabledCompTest/RequestFocusToDisabledCompTest.java Tue Jun 17 13:37:28 2008 +0400 @@ -0,0 +1,97 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + @test + @bug 4685768 + @summary Tests that it's possible to manually request focus on a disabled component. + @author Anton Tarasov: area=awt.focus + @library ../../regtesthelpers + @build Util + @run main RequestFocusToDisabledCompTest +*/ + +import java.awt.Robot; +import javax.swing.*; +import java.awt.*; +import java.awt.event.*; +import java.applet.Applet; +import test.java.awt.regtesthelpers.Util; + +public class RequestFocusToDisabledCompTest extends Applet { + Robot robot; + JFrame frame = new JFrame("Frame"); + JButton b0 = new JButton("b0"); + JButton b1 = new JButton("b1"); + + public static void main(String[] args) { + RequestFocusToDisabledCompTest app = new RequestFocusToDisabledCompTest(); + app.init(); + app.start(); + } + + public void init() { + robot = Util.createRobot(); + frame.add(b0); + frame.add(b1); + frame.setLayout(new FlowLayout()); + frame.pack(); + + b1.setEnabled(false); + } + + public void start() { + Util.showWindowWait(frame); + + if (!b0.hasFocus()) { + // Request focus on b0. + if (!Util.focusComponent(b0, 2000)) { + throw new TestErrorException("couldn't focus " + b0); + } + } + + // Try to request focus on b1. + if (!Util.focusComponent(b1, 2000)) { + throw new TestFailedException("focus wasn't requested on disabled " + b1); + } + System.out.println("Test passed."); + } +} + +/** + * Thrown when the behavior being verified is found wrong. + */ +class TestFailedException extends RuntimeException { + TestFailedException(String msg) { + super("Test failed: " + msg); + } +} + +/** + * Thrown when an error not related to the behavior being verified is encountered. + */ +class TestErrorException extends RuntimeException { + TestErrorException(String msg) { + super("Unexpected error: " + msg); + } +} diff -r 4e03057627b7 -r a2782dd9f312 jdk/test/java/awt/regtesthelpers/Util.java --- a/jdk/test/java/awt/regtesthelpers/Util.java Wed Jun 11 01:31:42 2008 -0700 +++ b/jdk/test/java/awt/regtesthelpers/Util.java Tue Jun 17 13:37:28 2008 +0400 @@ -124,6 +124,14 @@ } /** + * Makes the window visible and waits until it's shown. + */ + public static void showWindowWait(Window win) { + win.setVisible(true); + waitTillShown(win); + } + + /** * Moves mouse pointer in the center of given {@code comp} component * using {@code robot} parameter. */ @@ -574,4 +582,22 @@ public static boolean trackActionPerformed(Button button, Runnable action, int time, boolean printEvent) { return trackEvent(ActionEvent.ACTION_PERFORMED, button, action, time, printEvent); } + + /* + * Requests focus on the component provided and waits for the result. + * @return true if the component has been focused, false otherwise. + */ + public static boolean focusComponent(Component comp, int time) { + return focusComponent(comp, time, false); + } + public static boolean focusComponent(final Component comp, int time, boolean printEvent) { + return trackFocusGained(comp, + new Runnable() { + public void run() { + comp.requestFocus(); + } + }, + time, printEvent); + + } }