mirror of
https://github.com/JetBrains/JetBrainsRuntime.git
synced 2025-12-06 09:29:38 +01:00
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 commitse3aaff5db4,09941119e1)
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user