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>
|
||||
Date: Fri, 27 Feb 2026 14:49:27 +0100
|
||||
Date: Fri, 27 Feb 2026 14:54:57 +0100
|
||||
Subject: [PATCH] ns: implement VoiceOver accessibility
|
||||
|
||||
All AX getter methods now consistently dispatch_sync to the main
|
||||
thread when called from the AX server thread, eliminating the mixed
|
||||
threading strategy (some dispatch_sync, some @synchronized, some
|
||||
neither) that could cause use-after-free on cachedText.
|
||||
All AX protocol methods now dispatch_sync to the main thread,
|
||||
including accessibilityFrame, accessibilityLabel, accessibilityRole,
|
||||
accessibilityRoleDescription, accessibilityPlaceholderValue,
|
||||
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,
|
||||
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.
|
||||
* src/nsterm.m, src/nsterm.h, etc/NEWS: As described.
|
||||
---
|
||||
etc/NEWS | 11 +
|
||||
src/nsterm.h | 108 ++
|
||||
src/nsterm.m | 2798 +++++++++++++++++++++++++++++++++++++++++++++++---
|
||||
3 files changed, 2775 insertions(+), 142 deletions(-)
|
||||
src/nsterm.m | 2870 +++++++++++++++++++++++++++++++++++++++++++++++---
|
||||
3 files changed, 2847 insertions(+), 142 deletions(-)
|
||||
|
||||
diff --git a/etc/NEWS b/etc/NEWS
|
||||
index 7367e3c..0e4480a 100644
|
||||
@@ -177,7 +174,7 @@ index 7c1ee4c..6455547 100644
|
||||
|
||||
|
||||
diff --git a/src/nsterm.m b/src/nsterm.m
|
||||
index 932d209..2e47899 100644
|
||||
index 932d209..078465a 100644
|
||||
--- a/src/nsterm.m
|
||||
+++ b/src/nsterm.m
|
||||
@@ -46,6 +46,7 @@ Updated by Christian Limpach (chris@nice.ch)
|
||||
@@ -243,7 +240,7 @@ index 932d209..2e47899 100644
|
||||
ns_focus (f, NULL, 0);
|
||||
|
||||
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
|
||||
+{
|
||||
+ if (![NSThread isMainThread])
|
||||
+ {
|
||||
+ __block NSAccessibilityRole result;
|
||||
+ dispatch_sync (dispatch_get_main_queue (), ^{
|
||||
+ result = [self accessibilityRole];
|
||||
+ });
|
||||
+ return result;
|
||||
+ }
|
||||
+ struct window *w = [self validWindow];
|
||||
+ if (w && MINI_WINDOW_P (w))
|
||||
+ return NSAccessibilityTextFieldRole;
|
||||
@@ -1436,6 +1441,14 @@ index 932d209..2e47899 100644
|
||||
+
|
||||
+- (NSString *)accessibilityPlaceholderValue
|
||||
+{
|
||||
+ if (![NSThread isMainThread])
|
||||
+ {
|
||||
+ __block NSString *result;
|
||||
+ dispatch_sync (dispatch_get_main_queue (), ^{
|
||||
+ result = [self accessibilityPlaceholderValue];
|
||||
+ });
|
||||
+ return result;
|
||||
+ }
|
||||
+ struct window *w = [self validWindow];
|
||||
+ if (!w || !MINI_WINDOW_P (w))
|
||||
+ return nil;
|
||||
@@ -1447,6 +1460,14 @@ index 932d209..2e47899 100644
|
||||
+
|
||||
+- (NSString *)accessibilityRoleDescription
|
||||
+{
|
||||
+ if (![NSThread isMainThread])
|
||||
+ {
|
||||
+ __block NSString *result;
|
||||
+ dispatch_sync (dispatch_get_main_queue (), ^{
|
||||
+ result = [self accessibilityRoleDescription];
|
||||
+ });
|
||||
+ return result;
|
||||
+ }
|
||||
+ struct window *w = [self validWindow];
|
||||
+ if (w && MINI_WINDOW_P (w))
|
||||
+ return @"minibuffer";
|
||||
@@ -1455,6 +1476,14 @@ index 932d209..2e47899 100644
|
||||
+
|
||||
+- (NSString *)accessibilityLabel
|
||||
+{
|
||||
+ if (![NSThread isMainThread])
|
||||
+ {
|
||||
+ __block NSString *result;
|
||||
+ dispatch_sync (dispatch_get_main_queue (), ^{
|
||||
+ result = [self accessibilityLabel];
|
||||
+ });
|
||||
+ return result;
|
||||
+ }
|
||||
+ struct window *w = [self validWindow];
|
||||
+ if (w && WINDOW_LEAF_P (w))
|
||||
+ {
|
||||
@@ -1474,6 +1503,14 @@ index 932d209..2e47899 100644
|
||||
+
|
||||
+- (BOOL)isAccessibilityFocused
|
||||
+{
|
||||
+ if (![NSThread isMainThread])
|
||||
+ {
|
||||
+ __block BOOL result;
|
||||
+ dispatch_sync (dispatch_get_main_queue (), ^{
|
||||
+ result = [self isAccessibilityFocused];
|
||||
+ });
|
||||
+ return result;
|
||||
+ }
|
||||
+ struct window *w = [self validWindow];
|
||||
+ if (!w)
|
||||
+ return NO;
|
||||
@@ -1593,6 +1630,9 @@ index 932d209..2e47899 100644
|
||||
+
|
||||
+ [self ensureTextCache];
|
||||
+
|
||||
+ specpdl_ref count = SPECPDL_INDEX ();
|
||||
+ record_unwind_current_buffer ();
|
||||
+
|
||||
+ /* Convert accessibility index to buffer charpos via mapping. */
|
||||
+ ptrdiff_t charpos = [self charposForAccessibilityIndex:range.location];
|
||||
+
|
||||
@@ -1604,9 +1644,7 @@ index 932d209..2e47899 100644
|
||||
+
|
||||
+ block_input ();
|
||||
+
|
||||
+ /* Move point directly in the buffer. Use set_point_both which
|
||||
+ operates on the current buffer — temporarily switch if needed. */
|
||||
+ struct buffer *oldb = current_buffer;
|
||||
+ /* Move point directly in the buffer. */
|
||||
+ if (b != current_buffer)
|
||||
+ set_buffer_internal_1 (b);
|
||||
+
|
||||
@@ -1626,8 +1664,7 @@ index 932d209..2e47899 100644
|
||||
+ else
|
||||
+ bset_mark_active (b, Qnil);
|
||||
+
|
||||
+ if (b != oldb)
|
||||
+ set_buffer_internal_1 (oldb);
|
||||
+ unbind_to (count, Qnil);
|
||||
+
|
||||
+ unblock_input ();
|
||||
+
|
||||
@@ -1953,6 +1990,14 @@ index 932d209..2e47899 100644
|
||||
+
|
||||
+- (NSRect)accessibilityFrame
|
||||
+{
|
||||
+ if (![NSThread isMainThread])
|
||||
+ {
|
||||
+ __block NSRect result;
|
||||
+ dispatch_sync (dispatch_get_main_queue (), ^{
|
||||
+ result = [self accessibilityFrame];
|
||||
+ });
|
||||
+ return result;
|
||||
+ }
|
||||
+ struct window *w = [self validWindow];
|
||||
+ if (!w)
|
||||
+ return NSZeroRect;
|
||||
@@ -2452,6 +2497,14 @@ index 932d209..2e47899 100644
|
||||
+
|
||||
+- (NSString *)accessibilityLabel
|
||||
+{
|
||||
+ if (![NSThread isMainThread])
|
||||
+ {
|
||||
+ __block NSString *result;
|
||||
+ dispatch_sync (dispatch_get_main_queue (), ^{
|
||||
+ result = [self accessibilityLabel];
|
||||
+ });
|
||||
+ return result;
|
||||
+ }
|
||||
+ struct window *w = [self validWindow];
|
||||
+ if (w && WINDOW_LEAF_P (w))
|
||||
+ {
|
||||
@@ -2471,6 +2524,14 @@ index 932d209..2e47899 100644
|
||||
+
|
||||
+- (id)accessibilityValue
|
||||
+{
|
||||
+ if (![NSThread isMainThread])
|
||||
+ {
|
||||
+ __block id result;
|
||||
+ dispatch_sync (dispatch_get_main_queue (), ^{
|
||||
+ result = [self accessibilityValue];
|
||||
+ });
|
||||
+ return result;
|
||||
+ }
|
||||
+ struct window *w = [self validWindow];
|
||||
+ if (!w)
|
||||
+ return @"";
|
||||
@@ -2479,6 +2540,14 @@ index 932d209..2e47899 100644
|
||||
+
|
||||
+- (NSRect)accessibilityFrame
|
||||
+{
|
||||
+ if (![NSThread isMainThread])
|
||||
+ {
|
||||
+ __block NSRect result;
|
||||
+ dispatch_sync (dispatch_get_main_queue (), ^{
|
||||
+ result = [self accessibilityFrame];
|
||||
+ });
|
||||
+ return result;
|
||||
+ }
|
||||
+ struct window *w = [self validWindow];
|
||||
+ if (!w || !w->current_matrix)
|
||||
+ return NSZeroRect;
|
||||
@@ -2718,7 +2787,7 @@ index 932d209..2e47899 100644
|
||||
#define NS_KEYLOG 0
|
||||
|
||||
- (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);
|
||||
kbd_buffer_store_event (&event);
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -3073,7 +3142,7 @@ index 932d209..2e47899 100644
|
||||
@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_handle_drag_motion, "ns-handle-drag-motion");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user