Skip to content
This repository has been archived by the owner on Jul 17, 2024. It is now read-only.
/ jfx22u Public archive

Commit

Permalink
8329705: Add missing Application thread checks to platform specific a…
Browse files Browse the repository at this point in the history
…11y methods

Backport-of: 0eb4d7196099d817cc6467985b882242845bdd2e
  • Loading branch information
Alexander Zuev authored and kevinrushforth committed Apr 5, 2024
1 parent e657264 commit 1e21a6f
Show file tree
Hide file tree
Showing 4 changed files with 299 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
import javafx.scene.input.KeyCombination;
import javafx.scene.text.Font;
import com.sun.glass.ui.Accessible;
import com.sun.glass.ui.Application;
import com.sun.glass.ui.Screen;
import com.sun.glass.ui.View;
import static javafx.scene.AccessibleAttribute.*;
Expand Down Expand Up @@ -672,6 +673,7 @@ private static enum MacText {

@Override
public void dispose() {
Application.checkEventThread();
if (peer != 0L) {
if (getView() == null) {
NSAccessibilityPostNotification(peer, MacNotification.NSAccessibilityUIElementDestroyedNotification.ptr);
Expand All @@ -684,6 +686,7 @@ public void dispose() {

@Override
public void sendNotification(AccessibleAttribute notification) {
Application.checkEventThread();
if (isDisposed()) return;

MacNotification macNotification = null;
Expand Down Expand Up @@ -824,6 +827,7 @@ public void sendNotification(AccessibleAttribute notification) {

@Override
protected long getNativeAccessible() {
Application.checkEventThread();
if (this.peer == 0L) {
AccessibleRole role = (AccessibleRole) getAttribute(ROLE);
if (role == null) role = AccessibleRole.NODE;
Expand All @@ -837,6 +841,7 @@ protected long getNativeAccessible() {
}

private View getRootView(Scene scene) {
Application.checkEventThread();
if (scene == null) return null;
Accessible acc = getAccessible(scene);
if (acc == null || acc.isDisposed()) return null;
Expand All @@ -846,6 +851,7 @@ private View getRootView(Scene scene) {
}

private long[] getUnignoredChildren(ObservableList<Node> children) {
Application.checkEventThread();
if (children == null) return new long[0];
long[] ids = children.stream()
.filter(Node::isVisible)
Expand Down Expand Up @@ -990,6 +996,7 @@ private Bounds flipBounds(Bounds bounds) {

/* NSAccessibility Protocol - JNI entry points */
private long[] accessibilityAttributeNames() {
Application.checkEventThread();
if (getView() != null) return null; /* Let NSView answer for the Scene */
AccessibleRole role = (AccessibleRole)getAttribute(ROLE);
if (role != null) {
Expand Down Expand Up @@ -1036,6 +1043,7 @@ private long[] accessibilityAttributeNames() {
}

private int accessibilityArrayAttributeCount(long attribute) {
Application.checkEventThread();
MacAttribute attr = MacAttribute.getAttribute(attribute);
if (attr == null) {
return -1;
Expand Down Expand Up @@ -1091,6 +1099,7 @@ private int accessibilityArrayAttributeCount(long attribute) {
}

private long[] accessibilityArrayAttributeValues(long attribute, int index, int maxCount) {
Application.checkEventThread();
MacAttribute attr = MacAttribute.getAttribute(attribute);
if (attr == null) {
return null;
Expand Down Expand Up @@ -1150,6 +1159,7 @@ private long[] accessibilityArrayAttributeValues(long attribute, int index, int
}

private boolean accessibilityIsAttributeSettable(long attribute) {
Application.checkEventThread();
MacAttribute attr = MacAttribute.getAttribute(attribute);
if (attr == null) return false;
switch (attr) {
Expand Down Expand Up @@ -1177,6 +1187,7 @@ private boolean accessibilityIsAttributeSettable(long attribute) {
}

private MacVariant accessibilityAttributeValue(long attribute) {
Application.checkEventThread();
MacAttribute attr = MacAttribute.getAttribute(attribute);
if (attr == null) {
return null;
Expand Down Expand Up @@ -1623,6 +1634,7 @@ private MacVariant accessibilityAttributeValue(long attribute) {
}

private void accessibilitySetValue(long value, long attribute) {
Application.checkEventThread();
MacAttribute attr = MacAttribute.getAttribute(attribute);
if (attr != null) {
switch (attr) {
Expand Down Expand Up @@ -1714,6 +1726,7 @@ private long accessibilityIndexOfChild(long child) {
}

private long[] accessibilityParameterizedAttributeNames() {
Application.checkEventThread();
if (getView() != null) return null; /* Let NSView answer for the Scene */
AccessibleRole role = (AccessibleRole)getAttribute(ROLE);
if (role != null) {
Expand Down Expand Up @@ -1743,6 +1756,7 @@ private long[] accessibilityParameterizedAttributeNames() {
}

private MacVariant accessibilityAttributeValueForParameter(long attribute, long parameter) {
Application.checkEventThread();
MacAttribute attr = MacAttribute.getAttribute(attribute);
if (attr == null || attr.inputType == 0 || attr.jfxAttr == null) {
return null;
Expand Down Expand Up @@ -1863,6 +1877,7 @@ private MacVariant accessibilityAttributeValueForParameter(long attribute, long
}

private long[] accessibilityActionNames() {
Application.checkEventThread();
if (getView() != null) return null; /* Let NSView answer for the Scene */
AccessibleRole role = (AccessibleRole)getAttribute(ROLE);
List<MacAction> actions = new ArrayList<>();
Expand Down Expand Up @@ -1915,6 +1930,7 @@ private void accessibilityPerformAction(long action) {
}

private long accessibilityFocusedUIElement() {
Application.checkEventThread();
Node node = (Node)getAttribute(FOCUS_NODE);
if (node == null) return 0L;

Expand All @@ -1924,6 +1940,7 @@ private long accessibilityFocusedUIElement() {
}

private boolean accessibilityIsIgnored() {
Application.checkEventThread();
if (isIgnored()) return true;
if (isInSlider()) {
/*
Expand All @@ -1944,6 +1961,7 @@ private boolean accessibilityIsIgnored() {
}

private long accessibilityHitTest(float x, float y) {
Application.checkEventThread();
View view = getView();
if (view == null || view.getWindow() == null) {
return 0L;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import javafx.scene.Scene;
import javafx.scene.input.KeyCombination;
import com.sun.glass.ui.Accessible;
import com.sun.glass.ui.Application;
import com.sun.glass.ui.Screen;
import com.sun.glass.ui.View;
import com.sun.javafx.stage.WindowHelper;
Expand Down Expand Up @@ -238,6 +239,7 @@ final class WinAccessible extends Accessible {
private native static boolean UiaClientsAreListening();

WinAccessible() {
Application.checkEventThread();
this.peer = _createGlassAccessible();
if (this.peer == 0L) {
throw new RuntimeException("could not create platform accessible");
Expand All @@ -247,6 +249,7 @@ final class WinAccessible extends Accessible {

@Override
public void dispose() {
Application.checkEventThread();
super.dispose();
if (selectionRange != null) {
selectionRange.dispose();
Expand All @@ -265,6 +268,7 @@ public void dispose() {

@Override
public void sendNotification(AccessibleAttribute notification) {
Application.checkEventThread();
if (isDisposed()) return;

switch (notification) {
Expand Down Expand Up @@ -416,6 +420,7 @@ public void sendNotification(AccessibleAttribute notification) {
}

private void notifyToggleState() {
Application.checkEventThread();
int state = get_ToggleState();
WinVariant vo = new WinVariant();
vo.vt = WinVariant.VT_I4;
Expand All @@ -428,6 +433,7 @@ private void notifyToggleState() {

@Override
protected long getNativeAccessible() {
Application.checkEventThread();
return peer;
}

Expand Down
160 changes: 160 additions & 0 deletions tests/manual/a11y/AccessibilityNotificationTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
/*
* Copyright (c) 2024, Oracle and/or its affiliates. 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/

import java.io.File;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;

public class AccessibilityNotificationTest extends Application implements ChangeListener<Boolean> {
volatile static int exitCode;
static final String INSTRUCTIONS =
"This test is suitable for macOS and Windows only.\n" +
"Please carefully read instructions before start testing!\n" +
"1) Enable accessibility subsystem (Narrator on Windows, VoiceOver on macOS);\n" +
"2) Click \"Start test\" button. The 5 seconds countdown will start;\n" +
"3) Wait for the test to complete;\n" +
"4) The test passes if it doesn't crash; " +
"exceptions logged to the file \"error.log\" are expected.\n";
static CountDownLatch latch = new CountDownLatch(1);
static Button button = new Button("Start test");
static TextField notificationArea = new TextField();

public static void main(String[] args) throws InterruptedException {
Application.launch(args);
}

@Override
public void start(Stage stage) throws InterruptedException {
Platform.accessibilityActiveProperty().addListener(this);
stage.setTitle("Accessibility Notification Test");
GridPane root = new GridPane();
TextArea instructions = new TextArea();
instructions.setText(INSTRUCTIONS);
root.add(instructions, 0, 0);
button = new Button("Start test");
button.setOnAction(e -> {
button.setDisable(true);
new Thread(this::performTest).start();
});
root.add(button, 0, 1);
root.add(notificationArea, 0, 2);
if (Platform.accessibilityActiveProperty().get()) {
setNotificationArea("Ready", "palegreen");
button.setDisable(false);
} else {
setNotificationArea("Please enable accessibility", "red");
button.setDisable(true);
}
Scene scene = new Scene(root);
stage.setScene(scene);
stage.sizeToScene();
stage.show();
}

public void yieldFor(long ms) {
long completiontime = System.currentTimeMillis() + ms;
do {
try {
Thread.sleep(1);
} catch (InterruptedException ignore) {}
} while (System.currentTimeMillis() < completiontime);
}

public void setNotificationArea(String notification, String color) {
Platform.runLater(() -> {
notificationArea.setText(notification);
notificationArea.setStyle("-fx-background-color: " + color);
});
}

public void performTest() {
final String logFileName = "error.log";
final String appName = "AccessibilityNotificationTestApp";

new Thread(() -> {
try {
latch.await();
} catch (InterruptedException ignore) {
} finally {
if (exitCode == 0) {
setNotificationArea("Complete", "green");
}
}
}).start();
for (int c = 5; c > 0 ; c--) {
setNotificationArea("Start in " + c, "yellow");
yieldFor(1000);
}
setNotificationArea("Running...", "lightyellow");
new Thread(() -> {
try {
File logFile = new File(logFileName);
String runArgs = "@../../../build/run.args";
ProcessBuilder pb = new ProcessBuilder("java", runArgs, appName);
pb.redirectErrorStream(true);
pb.redirectOutput(logFile);
Process process = pb.start();
if (!process.waitFor(30, TimeUnit.SECONDS)) {
process.destroyForcibly();
System.err.println("FAIL: Timeout waiting for test to complete");
System.err.println("See " + logFileName + " for more information");
throw new AssertionError("Error");
}
exitCode = process.exitValue();
if (exitCode != 0) {
System.out.println("FAIL: Test exited abnormally; exitCode = " + exitCode);
System.out.println("See " + logFileName + " for more information");
setNotificationArea("TEST FAILED", "red");
throw new AssertionError("Error");
}
} catch (Throwable ignore) {
} finally {
latch.countDown();
}
}).start();
}

@Override
public void changed(ObservableValue<? extends Boolean> observableValue,
Boolean oldValue, Boolean newValue) {
if (newValue) {
setNotificationArea("Ready", "palegreen");
button.setDisable(false);
} else {
setNotificationArea("Please enable accessibility", "red");
button.setDisable(true);
}}
}
Loading

1 comment on commit 1e21a6f

@openjdk-notifier
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.