patches: complete thread safety — dispatch_sync on ALL AX methods
Add dispatch_sync guard to: Buffer accessibilityFrame, accessibilityLabel, accessibilityRole, accessibilityRoleDescription, accessibilityPlaceholderValue, isAccessibilityFocused. ModeLine accessibilityValue, accessibilityFrame, accessibilityLabel. setAccessibilitySelectedTextRange now uses record_unwind_current_buffer + unbind_to.
This commit is contained in:
@@ -1,25 +1,22 @@
|
|||||||
From 4871f48958e63cbf1d0086ba1cc0c15fabb6f7df Mon Sep 17 00:00:00 2001
|
From 6878620f894a738eb11f9742a3920434cfb00e1e Mon Sep 17 00:00:00 2001
|
||||||
From: Martin Sukany <martin@sukany.cz>
|
From: Martin Sukany <martin@sukany.cz>
|
||||||
Date: Fri, 27 Feb 2026 14:49:27 +0100
|
Date: Fri, 27 Feb 2026 14:54:57 +0100
|
||||||
Subject: [PATCH] ns: implement VoiceOver accessibility
|
Subject: [PATCH] ns: implement VoiceOver accessibility
|
||||||
|
|
||||||
All AX getter methods now consistently dispatch_sync to the main
|
All AX protocol methods now dispatch_sync to the main thread,
|
||||||
thread when called from the AX server thread, eliminating the mixed
|
including accessibilityFrame, accessibilityLabel, accessibilityRole,
|
||||||
threading strategy (some dispatch_sync, some @synchronized, some
|
accessibilityRoleDescription, accessibilityPlaceholderValue,
|
||||||
neither) that could cause use-after-free on cachedText.
|
isAccessibilityFocused on EmacsAccessibilityBuffer, and
|
||||||
|
accessibilityValue/accessibilityFrame/accessibilityLabel on
|
||||||
|
EmacsAccessibilityModeLine. setAccessibilitySelectedTextRange
|
||||||
|
uses record_unwind_current_buffer for exception safety.
|
||||||
|
|
||||||
* src/nsterm.m: Add dispatch_sync guards to accessibilityNumberOfCharacters,
|
* src/nsterm.m, src/nsterm.h, etc/NEWS: As described.
|
||||||
accessibilitySelectedText, accessibilityStringForRange:,
|
|
||||||
accessibilityLineForIndex:, accessibilityRangeForLine:,
|
|
||||||
accessibilityRangeForIndex:, accessibilityVisibleCharacterRange,
|
|
||||||
setAccessibilitySelectedTextRange:. Document GC safety of
|
|
||||||
Lisp_Object capture in setAccessibilityFocused: GCD block.
|
|
||||||
* etc/NEWS: Announce VoiceOver accessibility on macOS.
|
|
||||||
---
|
---
|
||||||
etc/NEWS | 11 +
|
etc/NEWS | 11 +
|
||||||
src/nsterm.h | 108 ++
|
src/nsterm.h | 108 ++
|
||||||
src/nsterm.m | 2798 +++++++++++++++++++++++++++++++++++++++++++++++---
|
src/nsterm.m | 2870 +++++++++++++++++++++++++++++++++++++++++++++++---
|
||||||
3 files changed, 2775 insertions(+), 142 deletions(-)
|
3 files changed, 2847 insertions(+), 142 deletions(-)
|
||||||
|
|
||||||
diff --git a/etc/NEWS b/etc/NEWS
|
diff --git a/etc/NEWS b/etc/NEWS
|
||||||
index 7367e3c..0e4480a 100644
|
index 7367e3c..0e4480a 100644
|
||||||
@@ -177,7 +174,7 @@ index 7c1ee4c..6455547 100644
|
|||||||
|
|
||||||
|
|
||||||
diff --git a/src/nsterm.m b/src/nsterm.m
|
diff --git a/src/nsterm.m b/src/nsterm.m
|
||||||
index 932d209..2e47899 100644
|
index 932d209..078465a 100644
|
||||||
--- a/src/nsterm.m
|
--- a/src/nsterm.m
|
||||||
+++ b/src/nsterm.m
|
+++ b/src/nsterm.m
|
||||||
@@ -46,6 +46,7 @@ Updated by Christian Limpach (chris@nice.ch)
|
@@ -46,6 +46,7 @@ Updated by Christian Limpach (chris@nice.ch)
|
||||||
@@ -243,7 +240,7 @@ index 932d209..2e47899 100644
|
|||||||
ns_focus (f, NULL, 0);
|
ns_focus (f, NULL, 0);
|
||||||
|
|
||||||
NSGraphicsContext *ctx = [NSGraphicsContext currentContext];
|
NSGraphicsContext *ctx = [NSGraphicsContext currentContext];
|
||||||
@@ -6849,213 +6891,2332 @@ - (BOOL)fulfillService: (NSString *)name withArg: (NSString *)arg
|
@@ -6849,213 +6891,2404 @@ - (BOOL)fulfillService: (NSString *)name withArg: (NSString *)arg
|
||||||
|
|
||||||
/* ==========================================================================
|
/* ==========================================================================
|
||||||
|
|
||||||
@@ -1428,6 +1425,14 @@ index 932d209..2e47899 100644
|
|||||||
+
|
+
|
||||||
+- (NSAccessibilityRole)accessibilityRole
|
+- (NSAccessibilityRole)accessibilityRole
|
||||||
+{
|
+{
|
||||||
|
+ if (![NSThread isMainThread])
|
||||||
|
+ {
|
||||||
|
+ __block NSAccessibilityRole result;
|
||||||
|
+ dispatch_sync (dispatch_get_main_queue (), ^{
|
||||||
|
+ result = [self accessibilityRole];
|
||||||
|
+ });
|
||||||
|
+ return result;
|
||||||
|
+ }
|
||||||
+ struct window *w = [self validWindow];
|
+ struct window *w = [self validWindow];
|
||||||
+ if (w && MINI_WINDOW_P (w))
|
+ if (w && MINI_WINDOW_P (w))
|
||||||
+ return NSAccessibilityTextFieldRole;
|
+ return NSAccessibilityTextFieldRole;
|
||||||
@@ -1436,6 +1441,14 @@ index 932d209..2e47899 100644
|
|||||||
+
|
+
|
||||||
+- (NSString *)accessibilityPlaceholderValue
|
+- (NSString *)accessibilityPlaceholderValue
|
||||||
+{
|
+{
|
||||||
|
+ if (![NSThread isMainThread])
|
||||||
|
+ {
|
||||||
|
+ __block NSString *result;
|
||||||
|
+ dispatch_sync (dispatch_get_main_queue (), ^{
|
||||||
|
+ result = [self accessibilityPlaceholderValue];
|
||||||
|
+ });
|
||||||
|
+ return result;
|
||||||
|
+ }
|
||||||
+ struct window *w = [self validWindow];
|
+ struct window *w = [self validWindow];
|
||||||
+ if (!w || !MINI_WINDOW_P (w))
|
+ if (!w || !MINI_WINDOW_P (w))
|
||||||
+ return nil;
|
+ return nil;
|
||||||
@@ -1447,6 +1460,14 @@ index 932d209..2e47899 100644
|
|||||||
+
|
+
|
||||||
+- (NSString *)accessibilityRoleDescription
|
+- (NSString *)accessibilityRoleDescription
|
||||||
+{
|
+{
|
||||||
|
+ if (![NSThread isMainThread])
|
||||||
|
+ {
|
||||||
|
+ __block NSString *result;
|
||||||
|
+ dispatch_sync (dispatch_get_main_queue (), ^{
|
||||||
|
+ result = [self accessibilityRoleDescription];
|
||||||
|
+ });
|
||||||
|
+ return result;
|
||||||
|
+ }
|
||||||
+ struct window *w = [self validWindow];
|
+ struct window *w = [self validWindow];
|
||||||
+ if (w && MINI_WINDOW_P (w))
|
+ if (w && MINI_WINDOW_P (w))
|
||||||
+ return @"minibuffer";
|
+ return @"minibuffer";
|
||||||
@@ -1455,6 +1476,14 @@ index 932d209..2e47899 100644
|
|||||||
+
|
+
|
||||||
+- (NSString *)accessibilityLabel
|
+- (NSString *)accessibilityLabel
|
||||||
+{
|
+{
|
||||||
|
+ if (![NSThread isMainThread])
|
||||||
|
+ {
|
||||||
|
+ __block NSString *result;
|
||||||
|
+ dispatch_sync (dispatch_get_main_queue (), ^{
|
||||||
|
+ result = [self accessibilityLabel];
|
||||||
|
+ });
|
||||||
|
+ return result;
|
||||||
|
+ }
|
||||||
+ struct window *w = [self validWindow];
|
+ struct window *w = [self validWindow];
|
||||||
+ if (w && WINDOW_LEAF_P (w))
|
+ if (w && WINDOW_LEAF_P (w))
|
||||||
+ {
|
+ {
|
||||||
@@ -1474,6 +1503,14 @@ index 932d209..2e47899 100644
|
|||||||
+
|
+
|
||||||
+- (BOOL)isAccessibilityFocused
|
+- (BOOL)isAccessibilityFocused
|
||||||
+{
|
+{
|
||||||
|
+ if (![NSThread isMainThread])
|
||||||
|
+ {
|
||||||
|
+ __block BOOL result;
|
||||||
|
+ dispatch_sync (dispatch_get_main_queue (), ^{
|
||||||
|
+ result = [self isAccessibilityFocused];
|
||||||
|
+ });
|
||||||
|
+ return result;
|
||||||
|
+ }
|
||||||
+ struct window *w = [self validWindow];
|
+ struct window *w = [self validWindow];
|
||||||
+ if (!w)
|
+ if (!w)
|
||||||
+ return NO;
|
+ return NO;
|
||||||
@@ -1593,6 +1630,9 @@ index 932d209..2e47899 100644
|
|||||||
+
|
+
|
||||||
+ [self ensureTextCache];
|
+ [self ensureTextCache];
|
||||||
+
|
+
|
||||||
|
+ specpdl_ref count = SPECPDL_INDEX ();
|
||||||
|
+ record_unwind_current_buffer ();
|
||||||
|
+
|
||||||
+ /* Convert accessibility index to buffer charpos via mapping. */
|
+ /* Convert accessibility index to buffer charpos via mapping. */
|
||||||
+ ptrdiff_t charpos = [self charposForAccessibilityIndex:range.location];
|
+ ptrdiff_t charpos = [self charposForAccessibilityIndex:range.location];
|
||||||
+
|
+
|
||||||
@@ -1604,9 +1644,7 @@ index 932d209..2e47899 100644
|
|||||||
+
|
+
|
||||||
+ block_input ();
|
+ block_input ();
|
||||||
+
|
+
|
||||||
+ /* Move point directly in the buffer. Use set_point_both which
|
+ /* Move point directly in the buffer. */
|
||||||
+ operates on the current buffer — temporarily switch if needed. */
|
|
||||||
+ struct buffer *oldb = current_buffer;
|
|
||||||
+ if (b != current_buffer)
|
+ if (b != current_buffer)
|
||||||
+ set_buffer_internal_1 (b);
|
+ set_buffer_internal_1 (b);
|
||||||
+
|
+
|
||||||
@@ -1626,8 +1664,7 @@ index 932d209..2e47899 100644
|
|||||||
+ else
|
+ else
|
||||||
+ bset_mark_active (b, Qnil);
|
+ bset_mark_active (b, Qnil);
|
||||||
+
|
+
|
||||||
+ if (b != oldb)
|
+ unbind_to (count, Qnil);
|
||||||
+ set_buffer_internal_1 (oldb);
|
|
||||||
+
|
+
|
||||||
+ unblock_input ();
|
+ unblock_input ();
|
||||||
+
|
+
|
||||||
@@ -1953,6 +1990,14 @@ index 932d209..2e47899 100644
|
|||||||
+
|
+
|
||||||
+- (NSRect)accessibilityFrame
|
+- (NSRect)accessibilityFrame
|
||||||
+{
|
+{
|
||||||
|
+ if (![NSThread isMainThread])
|
||||||
|
+ {
|
||||||
|
+ __block NSRect result;
|
||||||
|
+ dispatch_sync (dispatch_get_main_queue (), ^{
|
||||||
|
+ result = [self accessibilityFrame];
|
||||||
|
+ });
|
||||||
|
+ return result;
|
||||||
|
+ }
|
||||||
+ struct window *w = [self validWindow];
|
+ struct window *w = [self validWindow];
|
||||||
+ if (!w)
|
+ if (!w)
|
||||||
+ return NSZeroRect;
|
+ return NSZeroRect;
|
||||||
@@ -2452,6 +2497,14 @@ index 932d209..2e47899 100644
|
|||||||
+
|
+
|
||||||
+- (NSString *)accessibilityLabel
|
+- (NSString *)accessibilityLabel
|
||||||
+{
|
+{
|
||||||
|
+ if (![NSThread isMainThread])
|
||||||
|
+ {
|
||||||
|
+ __block NSString *result;
|
||||||
|
+ dispatch_sync (dispatch_get_main_queue (), ^{
|
||||||
|
+ result = [self accessibilityLabel];
|
||||||
|
+ });
|
||||||
|
+ return result;
|
||||||
|
+ }
|
||||||
+ struct window *w = [self validWindow];
|
+ struct window *w = [self validWindow];
|
||||||
+ if (w && WINDOW_LEAF_P (w))
|
+ if (w && WINDOW_LEAF_P (w))
|
||||||
+ {
|
+ {
|
||||||
@@ -2471,6 +2524,14 @@ index 932d209..2e47899 100644
|
|||||||
+
|
+
|
||||||
+- (id)accessibilityValue
|
+- (id)accessibilityValue
|
||||||
+{
|
+{
|
||||||
|
+ if (![NSThread isMainThread])
|
||||||
|
+ {
|
||||||
|
+ __block id result;
|
||||||
|
+ dispatch_sync (dispatch_get_main_queue (), ^{
|
||||||
|
+ result = [self accessibilityValue];
|
||||||
|
+ });
|
||||||
|
+ return result;
|
||||||
|
+ }
|
||||||
+ struct window *w = [self validWindow];
|
+ struct window *w = [self validWindow];
|
||||||
+ if (!w)
|
+ if (!w)
|
||||||
+ return @"";
|
+ return @"";
|
||||||
@@ -2479,6 +2540,14 @@ index 932d209..2e47899 100644
|
|||||||
+
|
+
|
||||||
+- (NSRect)accessibilityFrame
|
+- (NSRect)accessibilityFrame
|
||||||
+{
|
+{
|
||||||
|
+ if (![NSThread isMainThread])
|
||||||
|
+ {
|
||||||
|
+ __block NSRect result;
|
||||||
|
+ dispatch_sync (dispatch_get_main_queue (), ^{
|
||||||
|
+ result = [self accessibilityFrame];
|
||||||
|
+ });
|
||||||
|
+ return result;
|
||||||
|
+ }
|
||||||
+ struct window *w = [self validWindow];
|
+ struct window *w = [self validWindow];
|
||||||
+ if (!w || !w->current_matrix)
|
+ if (!w || !w->current_matrix)
|
||||||
+ return NSZeroRect;
|
+ return NSZeroRect;
|
||||||
@@ -2718,7 +2787,7 @@ index 932d209..2e47899 100644
|
|||||||
#define NS_KEYLOG 0
|
#define NS_KEYLOG 0
|
||||||
|
|
||||||
- (void)keyDown: (NSEvent *)theEvent
|
- (void)keyDown: (NSEvent *)theEvent
|
||||||
@@ -8237,6 +10398,31 @@ - (void)windowDidBecomeKey /* for direct calls */
|
@@ -8237,6 +10470,31 @@ - (void)windowDidBecomeKey /* for direct calls */
|
||||||
XSETFRAME (event.frame_or_window, emacsframe);
|
XSETFRAME (event.frame_or_window, emacsframe);
|
||||||
kbd_buffer_store_event (&event);
|
kbd_buffer_store_event (&event);
|
||||||
ns_send_appdefined (-1); // Kick main loop
|
ns_send_appdefined (-1); // Kick main loop
|
||||||
@@ -2750,7 +2819,7 @@ index 932d209..2e47899 100644
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -9474,6 +11660,322 @@ - (int) fullscreenState
|
@@ -9474,6 +11732,322 @@ - (int) fullscreenState
|
||||||
return fs_state;
|
return fs_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3073,7 +3142,7 @@ index 932d209..2e47899 100644
|
|||||||
@end /* EmacsView */
|
@end /* EmacsView */
|
||||||
|
|
||||||
|
|
||||||
@@ -11303,6 +13805,18 @@ Convert an X font name (XLFD) to an NS font name.
|
@@ -11303,6 +13877,18 @@ Convert an X font name (XLFD) to an NS font name.
|
||||||
DEFSYM (Qns_drag_operation_generic, "ns-drag-operation-generic");
|
DEFSYM (Qns_drag_operation_generic, "ns-drag-operation-generic");
|
||||||
DEFSYM (Qns_handle_drag_motion, "ns-handle-drag-motion");
|
DEFSYM (Qns_handle_drag_motion, "ns-handle-drag-motion");
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user