JBR-3017 Focus issue in presence of third-party accessibility tool

use the new invocation approach for 'makeKeyAndOrderFront' as well, as it can also cause synchronous back-calls to accessibility subsystem, and change the global call order unexpectedly

this commit fixes TypeaheadSetVisibleTest and TypeaheadToFrontTest, when they are run with AltTab active

Guard against possible deadlocks, if UI-related methods are invoked not on EDT.
Sample deadlock scenario:
* Application thread attempts to show the window, this involves calling CWrapper.NSWindow.makeKeyAndOrderFront under AWT tree lock, which blocks till 'makeKeyAndOrderFront' completes on AppKit thread
* AppKit thread, while executing 'makeKeyAndOrderFront' performs 'back-call' to CAccessibility.getFocusOwner, which waits for execution on EDT
* EDT performs some activity requiring AWT tree lock (e.g. processing of PaintEvent)

(cherry picked from commits e3aaff5db4, 09941119e1)
This commit is contained in:
Dmitry Batrak
2021-02-02 11:15:09 +03:00
committed by jbrbot
parent e3e1919fc3
commit d85bb2aef5
5 changed files with 41 additions and 20 deletions

View File

@@ -87,7 +87,7 @@ public class CPlatformWindow extends CFRetainedResource implements PlatformWindo
double x, double y, double w, double h);
private static native void nativeSetNSWindowMinMax(long nsWindowPtr, double minW, double minH, double maxW, double maxH);
private static native void nativePushNSWindowToBack(long nsWindowPtr);
private static native void nativePushNSWindowToFront(long nsWindowPtr);
private static native void nativePushNSWindowToFront(long nsWindowPtr, boolean wait);
private static native void nativeSetNSWindowTitle(long nsWindowPtr, String title);
private static native void nativeRevalidateNSWindowShadow(long nsWindowPtr);
private static native void nativeSetNSWindowMinimizedIcon(long nsWindowPtr, long nsImage);
@@ -872,7 +872,7 @@ public class CPlatformWindow extends CFRetainedResource implements PlatformWindo
lwcToolkit.activateApplicationIgnoringOtherApps();
}
updateFocusabilityForAutoRequestFocus(false);
execute(CPlatformWindow::nativePushNSWindowToFront);
execute(ptr -> AWTThreading.executeWaitToolkit(wait -> nativePushNSWindowToFront(ptr, wait)));
updateFocusabilityForAutoRequestFocus(true);
}

View File

@@ -47,7 +47,9 @@ final class CWrapper {
// 'level' is one of the keys defined above
static native void setLevel(long window, int level);
static native void makeKeyAndOrderFront(long window);
static void makeKeyAndOrderFront(long window) {
AWTThreading.executeWaitToolkit(wait -> nativeMakeKeyAndOrderFront(window, wait));
}
static native void makeKeyWindow(long window);
static native void makeMainWindow(long window);
static native boolean canBecomeMainWindow(long window);
@@ -63,10 +65,11 @@ final class CWrapper {
* @param window the pointer of the NSWindow
*/
static void orderOut(long window) {
AWTThreading.executeWaitToolkit(() -> nativeOrderOut(window));
AWTThreading.executeWaitToolkit(wait -> nativeOrderOut(window, wait));
}
private static native void nativeOrderOut(long window);
private static native void nativeOrderOut(long window, boolean wait);
private static native void nativeMakeKeyAndOrderFront(long window, boolean wait);
/**
* Removes the window from the screen and releases it. According to

View File

@@ -1445,15 +1445,15 @@ JNI_COCOA_EXIT(env);
/*
* Class: sun_lwawt_macosx_CPlatformWindow
* Method: nativePushNSWindowToFront
* Signature: (J)V
* Signature: (JZ)V
*/
JNIEXPORT void JNICALL Java_sun_lwawt_macosx_CPlatformWindow_nativePushNSWindowToFront
(JNIEnv *env, jclass clazz, jlong windowPtr)
(JNIEnv *env, jclass clazz, jlong windowPtr, jboolean wait)
{
JNI_COCOA_ENTER(env);
NSWindow *nsWindow = OBJC(windowPtr);
[ThreadUtilities performOnMainThreadWaiting:NO block:^(){
[ThreadUtilities performOnMainThreadWaiting:(BOOL)wait block:^(){
if (![nsWindow isKeyWindow]) {
[nsWindow makeKeyAndOrderFront:nsWindow];

View File

@@ -30,12 +30,12 @@
/*
* Class: sun_lwawt_macosx_CWrapper$NSWindow
* Method: makeKeyAndOrderFront
* Signature: (J)V
* Method: nativeMakeKeyAndOrderFront
* Signature: (JZ)V
*/
JNIEXPORT void JNICALL
Java_sun_lwawt_macosx_CWrapper_00024NSWindow_makeKeyAndOrderFront
(JNIEnv *env, jclass cls, jlong windowPtr)
Java_sun_lwawt_macosx_CWrapper_00024NSWindow_nativeMakeKeyAndOrderFront
(JNIEnv *env, jclass cls, jlong windowPtr, jboolean wait)
{
JNI_COCOA_ENTER(env);
@@ -43,7 +43,7 @@ JNI_COCOA_ENTER(env);
[ThreadUtilities performOnMainThread:@selector(makeKeyAndOrderFront:)
on:window
withObject:nil
waitUntilDone:NO];
waitUntilDone:(BOOL)wait];
JNI_COCOA_EXIT(env);
}
@@ -157,11 +157,11 @@ JNI_COCOA_EXIT(env);
/*
* Class: sun_lwawt_macosx_CWrapper$NSWindow
* Method: nativeOrderOut
* Signature: (J)V
* Signature: (JZ)V
*/
JNIEXPORT void JNICALL
Java_sun_lwawt_macosx_CWrapper_00024NSWindow_nativeOrderOut
(JNIEnv *env, jclass cls, jlong windowPtr)
(JNIEnv *env, jclass cls, jlong windowPtr, jboolean wait)
{
JNI_COCOA_ENTER(env);
@@ -169,7 +169,7 @@ JNI_COCOA_ENTER(env);
[ThreadUtilities performOnMainThread:@selector(orderOut:)
on:window
withObject:window
waitUntilDone:YES];
waitUntilDone:(BOOL)wait];
JNI_COCOA_EXIT(env);
}

View File

@@ -1,6 +1,7 @@
package sun.awt;
import sun.font.FontUtilities;
import sun.util.logging.PlatformLogger;
import java.awt.*;
import java.awt.event.InvocationEvent;
@@ -15,6 +16,8 @@ import java.util.concurrent.*;
* Used to perform a cross threads (EventDispatch, Toolkit) execution so that the execution does not cause a deadlock.
*/
public class AWTThreading {
private static final PlatformLogger logger = PlatformLogger.getLogger("sun.awt.AWTThreading");
private ExecutorService executor;
// every invokeAndWait() pushes a queue of invocations
private final Stack<TrackingQueue> invocations = new Stack<>();
@@ -52,11 +55,14 @@ public class AWTThreading {
}
/**
* Same as {@link #executeWaitToolkit(Callable)}, but without returning a value.
* Same as {@link #executeWaitToolkit(Callable)}, but without returning a value. If requested (as indicated by
* the passed parameter), the invoked native method is supposed to wait for the result of invocation on AppKit
* thread, and vice versa.
*/
public static void executeWaitToolkit(Runnable runnable) {
public static void executeWaitToolkit(Task runnable) {
boolean wait = EventQueue.isDispatchThread();
executeWaitToolkit(() -> {
runnable.run();
runnable.run(wait);
return null;
});
}
@@ -67,12 +73,20 @@ public class AWTThreading {
public static <T> T executeWaitToolkit(Callable<T> callable, long timeout, TimeUnit unit) {
if (callable == null) return null;
if (FontUtilities.isMacOSX && EventQueue.isDispatchThread()) {
boolean isEDT = EventQueue.isDispatchThread();
if (FontUtilities.isMacOSX && isEDT) {
AWTThreading instance = getInstance(Thread.currentThread());
if (instance != null) {
return instance.execute(callable, timeout, unit);
}
}
if (!isEDT && logger.isLoggable(PlatformLogger.Level.FINE)) {
// this can cause deadlock if calling thread is holding a lock which EDT might require (e.g. AWT tree lock)
logger.fine("AWTThreading.executeWaitToolkit invoked from non-EDT thread", new Throwable());
}
// fallback to default
try {
return callable.call();
@@ -233,4 +247,8 @@ public class AWTThreading {
return EDT_TO_INSTANCE_MAP.computeIfAbsent(edt, key -> new AWTThreading());
}
public interface Task {
void run(boolean wait);
}
}