From e4129581b7fc08c24c7b85112677b647663f9551 Mon Sep 17 00:00:00 2001 From: Daneel Date: Fri, 27 Feb 2026 14:49:33 +0100 Subject: [PATCH] =?UTF-8?q?patches:=20unify=20AX=20threading=20=E2=80=94?= =?UTF-8?q?=20dispatch=5Fsync=20on=20all=20getter=20methods?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 8 methods were missing thread guards: accessibilityNumberOfCharacters, accessibilitySelectedText, accessibilityStringForRange:, accessibilityLineForIndex:, accessibilityRangeForLine:, accessibilityRangeForIndex:, accessibilityVisibleCharacterRange, setAccessibilitySelectedTextRange:. All AX getters now consistently dispatch_sync to main thread. GC safety comment added to InteractiveSpan setAccessibilityFocused:. --- ...oundsForRange-for-macOS-Zoom-cursor-.patch | 107 ++++++++++++++---- 1 file changed, 88 insertions(+), 19 deletions(-) diff --git a/patches/0001-ns-implement-AXBoundsForRange-for-macOS-Zoom-cursor-.patch b/patches/0001-ns-implement-AXBoundsForRange-for-macOS-Zoom-cursor-.patch index ff6be8e..d023ee9 100644 --- a/patches/0001-ns-implement-AXBoundsForRange-for-macOS-Zoom-cursor-.patch +++ b/patches/0001-ns-implement-AXBoundsForRange-for-macOS-Zoom-cursor-.patch @@ -1,24 +1,25 @@ -From 6aca76b67027293e2656279c7622519618425bd4 Mon Sep 17 00:00:00 2001 +From 4871f48958e63cbf1d0086ba1cc0c15fabb6f7df Mon Sep 17 00:00:00 2001 From: Martin Sukany -Date: Fri, 27 Feb 2026 14:37:02 +0100 -Subject: [PATCH] ns: implement VoiceOver accessibility (AXBoundsForRange, line - nav, completions, interactive spans) +Date: Fri, 27 Feb 2026 14:49:27 +0100 +Subject: [PATCH] ns: implement VoiceOver accessibility -* src/nsterm.h (EmacsAccessibilityElement, EmacsAccessibilityBuffer, -EmacsAccessibilityModeLine, EmacsAccessibilityInteractiveSpan, -ns_ax_visible_run): New types for virtual accessibility tree. - -* src/nsterm.m: Full VoiceOver/Zoom support with thread-safe caching, -record_unwind_current_buffer for all buffer switches, @synchronized -for cross-thread data access, NSTRACE debugging, Foverlays_in bulk -queries, and MAC_OS_X_VERSION guard for UAZoom APIs. +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. +* 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. --- etc/NEWS | 11 + src/nsterm.h | 108 ++ - src/nsterm.m | 2730 +++++++++++++++++++++++++++++++++++++++++++++++--- - 3 files changed, 2707 insertions(+), 142 deletions(-) + src/nsterm.m | 2798 +++++++++++++++++++++++++++++++++++++++++++++++--- + 3 files changed, 2775 insertions(+), 142 deletions(-) diff --git a/etc/NEWS b/etc/NEWS index 7367e3c..0e4480a 100644 @@ -176,7 +177,7 @@ index 7c1ee4c..6455547 100644 diff --git a/src/nsterm.m b/src/nsterm.m -index 932d209..c9d058b 100644 +index 932d209..2e47899 100644 --- a/src/nsterm.m +++ b/src/nsterm.m @@ -46,6 +46,7 @@ Updated by Christian Limpach (chris@nice.ch) @@ -242,7 +243,7 @@ index 932d209..c9d058b 100644 ns_focus (f, NULL, 0); NSGraphicsContext *ctx = [NSGraphicsContext currentContext]; -@@ -6849,213 +6891,2264 @@ - (BOOL)fulfillService: (NSString *)name withArg: (NSString *)arg +@@ -6849,213 +6891,2332 @@ - (BOOL)fulfillService: (NSString *)name withArg: (NSString *)arg /* ========================================================================== @@ -1061,6 +1062,11 @@ index 932d209..c9d058b 100644 + ptrdiff_t target = self.charposStart; + Lisp_Object lwin = self.lispWindow; + dispatch_async (dispatch_get_main_queue (), ^{ ++ /* lwin is a Lisp_Object captured by value. This is GC-safe ++ because Lisp_Objects are tagged integers/pointers that ++ remain valid across GC — GC does not relocate objects in ++ Emacs. The WINDOW_LIVE_P check below guards against the ++ window being deleted between capture and execution. */ + if (!WINDOWP (lwin) || NILP (Fwindow_live_p (lwin))) + return; + /* block_input prevents SIGIO delivery while we modify point/buffer. @@ -1496,12 +1502,28 @@ index 932d209..c9d058b 100644 + +- (NSInteger)accessibilityNumberOfCharacters +{ ++ if (![NSThread isMainThread]) ++ { ++ __block NSInteger result; ++ dispatch_sync (dispatch_get_main_queue (), ^{ ++ result = [self accessibilityNumberOfCharacters]; ++ }); ++ return result; ++ } + [self ensureTextCache]; + return cachedText ? [cachedText length] : 0; +} + +- (NSString *)accessibilitySelectedText +{ ++ if (![NSThread isMainThread]) ++ { ++ __block NSString *result; ++ dispatch_sync (dispatch_get_main_queue (), ^{ ++ result = [self accessibilitySelectedText]; ++ }); ++ return result; ++ } + struct window *w = [self validWindow]; + if (!w || !WINDOW_LEAF_P (w)) + return @""; @@ -1554,6 +1576,13 @@ index 932d209..c9d058b 100644 + +- (void)setAccessibilitySelectedTextRange:(NSRange)range +{ ++ if (![NSThread isMainThread]) ++ { ++ dispatch_async (dispatch_get_main_queue (), ^{ ++ [self setAccessibilitySelectedTextRange:range]; ++ }); ++ return; ++ } + struct window *w = [self validWindow]; + if (!w || !WINDOW_LEAF_P (w)) + return; @@ -1681,6 +1710,14 @@ index 932d209..c9d058b 100644 + +- (NSString *)accessibilityStringForRange:(NSRange)range +{ ++ if (![NSThread isMainThread]) ++ { ++ __block NSString *result; ++ dispatch_sync (dispatch_get_main_queue (), ^{ ++ result = [self accessibilityStringForRange:range]; ++ }); ++ return result; ++ } + [self ensureTextCache]; + if (!cachedText || range.location + range.length > [cachedText length]) + return @""; @@ -1695,6 +1732,14 @@ index 932d209..c9d058b 100644 + +- (NSInteger)accessibilityLineForIndex:(NSInteger)index +{ ++ if (![NSThread isMainThread]) ++ { ++ __block NSInteger result; ++ dispatch_sync (dispatch_get_main_queue (), ^{ ++ result = [self accessibilityLineForIndex:index]; ++ }); ++ return result; ++ } + [self ensureTextCache]; + if (!cachedText || index < 0) + return 0; @@ -1715,6 +1760,14 @@ index 932d209..c9d058b 100644 + +- (NSRange)accessibilityRangeForLine:(NSInteger)line +{ ++ if (![NSThread isMainThread]) ++ { ++ __block NSRange result; ++ dispatch_sync (dispatch_get_main_queue (), ^{ ++ result = [self accessibilityRangeForLine:line]; ++ }); ++ return result; ++ } + [self ensureTextCache]; + if (!cachedText || line < 0) + return NSMakeRange (NSNotFound, 0); @@ -1750,6 +1803,14 @@ index 932d209..c9d058b 100644 + +- (NSRange)accessibilityRangeForIndex:(NSInteger)index +{ ++ if (![NSThread isMainThread]) ++ { ++ __block NSRange result; ++ dispatch_sync (dispatch_get_main_queue (), ^{ ++ result = [self accessibilityRangeForIndex:index]; ++ }); ++ return result; ++ } + [self ensureTextCache]; + if (!cachedText || index < 0 + || (NSUInteger) index >= [cachedText length]) @@ -1875,6 +1936,14 @@ index 932d209..c9d058b 100644 + +- (NSRange)accessibilityVisibleCharacterRange +{ ++ if (![NSThread isMainThread]) ++ { ++ __block NSRange result; ++ dispatch_sync (dispatch_get_main_queue (), ^{ ++ result = [self accessibilityVisibleCharacterRange]; ++ }); ++ return result; ++ } + /* Return the full cached text range. VoiceOver interprets the + visible range boundary as end-of-text, so we must expose the + entire buffer to avoid premature "end of text" announcements. */ @@ -2649,7 +2718,7 @@ index 932d209..c9d058b 100644 #define NS_KEYLOG 0 - (void)keyDown: (NSEvent *)theEvent -@@ -8237,6 +10330,31 @@ - (void)windowDidBecomeKey /* for direct calls */ +@@ -8237,6 +10398,31 @@ - (void)windowDidBecomeKey /* for direct calls */ XSETFRAME (event.frame_or_window, emacsframe); kbd_buffer_store_event (&event); ns_send_appdefined (-1); // Kick main loop @@ -2681,7 +2750,7 @@ index 932d209..c9d058b 100644 } -@@ -9474,6 +11592,322 @@ - (int) fullscreenState +@@ -9474,6 +11660,322 @@ - (int) fullscreenState return fs_state; } @@ -3004,7 +3073,7 @@ index 932d209..c9d058b 100644 @end /* EmacsView */ -@@ -11303,6 +13737,18 @@ Convert an X font name (XLFD) to an NS font name. +@@ -11303,6 +13805,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");