Revert "patches: add 0009 resource safety hardening + update 0007/0008"

This reverts commit acc2a2985e.
This commit is contained in:
Martin Sukany
2026-02-28 19:05:02 +01:00
parent acc2a2985e
commit 98b3d04597
3 changed files with 26 additions and 179 deletions

View File

@@ -1,7 +1,7 @@
From 8712cf8f567f3b0c02cc70a93aff931faa3a2df3 Mon Sep 17 00:00:00 2001 From 6e907a1000a8b138976d6a906e40449fdf1a61c5 Mon Sep 17 00:00:00 2001
From: Martin Sukany <martin@sukany.cz> From: Martin Sukany <martin@sukany.cz>
Date: Sat, 28 Feb 2026 14:46:25 +0100 Date: Sat, 28 Feb 2026 14:46:25 +0100
Subject: [PATCH 1/3] ns: announce overlay completion candidates for VoiceOver Subject: [PATCH 1/2] ns: announce overlay completion candidates for VoiceOver
Completion frameworks such as Vertico, Ivy, and Icomplete render Completion frameworks such as Vertico, Ivy, and Icomplete render
candidates via overlay before-string/after-string properties rather candidates via overlay before-string/after-string properties rather
@@ -52,8 +52,8 @@ Independent overlay branch, BUF_CHARS_MODIFF gating, candidate
announcement with overlay Zoom rect storage. announcement with overlay Zoom rect storage.
--- ---
src/nsterm.h | 3 + src/nsterm.h | 3 +
src/nsterm.m | 333 +++++++++++++++++++++++++++++++++++++++++++++------ src/nsterm.m | 331 +++++++++++++++++++++++++++++++++++++++++++++------
2 files changed, 300 insertions(+), 36 deletions(-) 2 files changed, 298 insertions(+), 36 deletions(-)
diff --git a/src/nsterm.h b/src/nsterm.h diff --git a/src/nsterm.h b/src/nsterm.h
index 51c30ca..5c15639 100644 index 51c30ca..5c15639 100644
@@ -77,7 +77,7 @@ index 51c30ca..5c15639 100644
BOOL font_panel_active; BOOL font_panel_active;
NSFont *font_panel_result; NSFont *font_panel_result;
diff --git a/src/nsterm.m b/src/nsterm.m diff --git a/src/nsterm.m b/src/nsterm.m
index 1780194..c1fc3cb 100644 index 1780194..143e784 100644
--- a/src/nsterm.m --- a/src/nsterm.m
+++ b/src/nsterm.m +++ b/src/nsterm.m
@@ -3258,7 +3258,12 @@ ns_draw_window_cursor (struct window *w, struct glyph_row *glyph_row, @@ -3258,7 +3258,12 @@ ns_draw_window_cursor (struct window *w, struct glyph_row *glyph_row,
@@ -403,7 +403,7 @@ index 1780194..c1fc3cb 100644
self.cachedPoint = point; self.cachedPoint = point;
NSDictionary *change = @{ NSDictionary *change = @{
@@ -8789,16 +8938,128 @@ ns_ax_completion_text_for_span (EmacsAccessibilityBuffer *elem, @@ -8789,16 +8938,126 @@ ns_ax_completion_text_for_span (EmacsAccessibilityBuffer *elem,
BOOL markActive = !NILP (BVAR (b, mark_active)); BOOL markActive = !NILP (BVAR (b, mark_active));
/* --- Text changed (edit) --- */ /* --- Text changed (edit) --- */
@@ -428,7 +428,6 @@ index 1780194..c1fc3cb 100644
+ if (chars_modiff != self.cachedCharsModiff) + if (chars_modiff != self.cachedCharsModiff)
+ { + {
+ self.cachedCharsModiff = chars_modiff; + self.cachedCharsModiff = chars_modiff;
+ if (self.emacsView)
+ self.emacsView->overlayZoomActive = NO; + self.emacsView->overlayZoomActive = NO;
+ [self postTextChangedNotification:point]; + [self postTextChangedNotification:point];
+ textDidChange = YES; + textDidChange = YES;
@@ -517,7 +516,6 @@ index 1780194..c1fc3cb 100644
+ (minibuffer exit, C-g, etc.) or overlay has no + (minibuffer exit, C-g, etc.) or overlay has no
+ recognizable selection face. Return Zoom to the + recognizable selection face. Return Zoom to the
+ text cursor. */ + text cursor. */
+ if (self.emacsView)
+ self.emacsView->overlayZoomActive = NO; + self.emacsView->overlayZoomActive = NO;
+ } + }
} }
@@ -536,7 +534,7 @@ index 1780194..c1fc3cb 100644
{ {
ptrdiff_t oldPoint = self.cachedPoint; ptrdiff_t oldPoint = self.cachedPoint;
BOOL oldMarkActive = self.cachedMarkActive; BOOL oldMarkActive = self.cachedMarkActive;
@@ -8966,7 +9227,7 @@ ns_ax_completion_text_for_span (EmacsAccessibilityBuffer *elem, @@ -8966,7 +9225,7 @@ ns_ax_completion_text_for_span (EmacsAccessibilityBuffer *elem,
/* =================================================================== /* ===================================================================
@@ -545,7 +543,7 @@ index 1780194..c1fc3cb 100644
=================================================================== */ =================================================================== */
/* Scan visible range of window W for interactive spans. /* Scan visible range of window W for interactive spans.
@@ -9157,7 +9418,7 @@ ns_ax_scan_interactive_spans (struct window *w, @@ -9157,7 +9416,7 @@ ns_ax_scan_interactive_spans (struct window *w,
- (BOOL) isAccessibilityFocused - (BOOL) isAccessibilityFocused
{ {
/* Read the cached point stored by EmacsAccessibilityBuffer on the main /* Read the cached point stored by EmacsAccessibilityBuffer on the main
@@ -554,7 +552,7 @@ index 1780194..c1fc3cb 100644
EmacsAccessibilityBuffer *pb = self.parentBuffer; EmacsAccessibilityBuffer *pb = self.parentBuffer;
if (!pb) if (!pb)
return NO; return NO;
@@ -9174,7 +9435,7 @@ ns_ax_scan_interactive_spans (struct window *w, @@ -9174,7 +9433,7 @@ ns_ax_scan_interactive_spans (struct window *w,
dispatch_async (dispatch_get_main_queue (), ^{ dispatch_async (dispatch_get_main_queue (), ^{
/* lwin is a Lisp_Object captured by value. This is GC-safe /* lwin is a Lisp_Object captured by value. This is GC-safe
because Lisp_Objects are tagged integers/pointers that because Lisp_Objects are tagged integers/pointers that
@@ -563,7 +561,7 @@ index 1780194..c1fc3cb 100644
Emacs. The WINDOW_LIVE_P check below guards against the Emacs. The WINDOW_LIVE_P check below guards against the
window being deleted between capture and execution. */ window being deleted between capture and execution. */
if (!WINDOWP (lwin) || NILP (Fwindow_live_p (lwin))) if (!WINDOWP (lwin) || NILP (Fwindow_live_p (lwin)))
@@ -9200,7 +9461,7 @@ ns_ax_scan_interactive_spans (struct window *w, @@ -9200,7 +9459,7 @@ ns_ax_scan_interactive_spans (struct window *w,
@end @end
@@ -572,7 +570,7 @@ index 1780194..c1fc3cb 100644
Methods are kept here (same .m file) so they access the ivars Methods are kept here (same .m file) so they access the ivars
declared in the @interface ivar block. */ declared in the @interface ivar block. */
@implementation EmacsAccessibilityBuffer (InteractiveSpans) @implementation EmacsAccessibilityBuffer (InteractiveSpans)
@@ -11922,7 +12183,7 @@ ns_ax_collect_windows (Lisp_Object window, EmacsView *view, @@ -11922,7 +12181,7 @@ ns_ax_collect_windows (Lisp_Object window, EmacsView *view,
if (WINDOW_LEAF_P (w)) if (WINDOW_LEAF_P (w))
{ {
@@ -581,7 +579,7 @@ index 1780194..c1fc3cb 100644
EmacsAccessibilityBuffer *elem EmacsAccessibilityBuffer *elem
= [existing objectForKey:[NSValue valueWithPointer:w]]; = [existing objectForKey:[NSValue valueWithPointer:w]];
if (!elem) if (!elem)
@@ -11956,7 +12217,7 @@ ns_ax_collect_windows (Lisp_Object window, EmacsView *view, @@ -11956,7 +12215,7 @@ ns_ax_collect_windows (Lisp_Object window, EmacsView *view,
} }
else else
{ {
@@ -590,7 +588,7 @@ index 1780194..c1fc3cb 100644
Lisp_Object child = w->contents; Lisp_Object child = w->contents;
while (!NILP (child)) while (!NILP (child))
{ {
@@ -12068,7 +12329,7 @@ ns_ax_collect_windows (Lisp_Object window, EmacsView *view, @@ -12068,7 +12327,7 @@ ns_ax_collect_windows (Lisp_Object window, EmacsView *view,
accessibilityUpdating = YES; accessibilityUpdating = YES;
/* Detect window tree change (split, delete, new buffer). Compare /* Detect window tree change (split, delete, new buffer). Compare
@@ -599,7 +597,7 @@ index 1780194..c1fc3cb 100644
Lisp_Object curRoot = FRAME_ROOT_WINDOW (emacsframe); Lisp_Object curRoot = FRAME_ROOT_WINDOW (emacsframe);
if (!EQ (curRoot, lastRootWindow)) if (!EQ (curRoot, lastRootWindow))
{ {
@@ -12077,12 +12338,12 @@ ns_ax_collect_windows (Lisp_Object window, EmacsView *view, @@ -12077,12 +12336,12 @@ ns_ax_collect_windows (Lisp_Object window, EmacsView *view,
} }
/* If tree is stale, rebuild FIRST so we don't iterate freed /* If tree is stale, rebuild FIRST so we don't iterate freed

View File

@@ -1,7 +1,7 @@
From 7d20ec80aa0d4ca97fa789f9b85389e25d2ff719 Mon Sep 17 00:00:00 2001 From 8564e4989f5f358092bd1494c3894a42974ee6e1 Mon Sep 17 00:00:00 2001
From: Martin Sukany <martin@sukany.cz> From: Martin Sukany <martin@sukany.cz>
Date: Sat, 28 Feb 2026 16:01:29 +0100 Date: Sat, 28 Feb 2026 16:01:29 +0100
Subject: [PATCH 2/3] ns: announce child frame completion candidates for Subject: [PATCH 2/2] ns: announce child frame completion candidates for
VoiceOver VoiceOver
Completion frameworks such as Corfu, Company-box, and similar Completion frameworks such as Corfu, Company-box, and similar
@@ -43,8 +43,8 @@ childFrameCompletionActive flag.
refocus parent buffer element when child frame closes. refocus parent buffer element when child frame closes.
--- ---
src/nsterm.h | 2 + src/nsterm.h | 2 +
src/nsterm.m | 306 ++++++++++++++++++++++++++++++++++++++++++++++++++- src/nsterm.m | 279 ++++++++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 305 insertions(+), 3 deletions(-) 2 files changed, 278 insertions(+), 3 deletions(-)
diff --git a/src/nsterm.h b/src/nsterm.h diff --git a/src/nsterm.h b/src/nsterm.h
index 5c15639..8b34300 100644 index 5c15639..8b34300 100644
@@ -67,7 +67,7 @@ index 5c15639..8b34300 100644
@end @end
diff --git a/src/nsterm.m b/src/nsterm.m diff --git a/src/nsterm.m b/src/nsterm.m
index c1fc3cb..abecb4c 100644 index 143e784..da1a319 100644
--- a/src/nsterm.m --- a/src/nsterm.m
+++ b/src/nsterm.m +++ b/src/nsterm.m
@@ -7066,6 +7066,110 @@ ns_ax_selected_overlay_text (struct buffer *b, @@ -7066,6 +7066,110 @@ ns_ax_selected_overlay_text (struct buffer *b,
@@ -181,7 +181,7 @@ index c1fc3cb..abecb4c 100644
/* Build accessibility text for window W, skipping invisible text. /* Build accessibility text for window W, skipping invisible text.
Populates *OUT_START with the buffer start charpos. Populates *OUT_START with the buffer start charpos.
Populates *OUT_RUNS with an array of visible runs and *OUT_NRUNS Populates *OUT_RUNS with an array of visible runs and *OUT_NRUNS
@@ -12313,6 +12417,146 @@ ns_ax_collect_windows (Lisp_Object window, EmacsView *view, @@ -12311,6 +12415,122 @@ ns_ax_collect_windows (Lisp_Object window, EmacsView *view,
The existing elements carry cached state (modiff, point) from the The existing elements carry cached state (modiff, point) from the
previous redisplay cycle. Rebuilding first would create fresh previous redisplay cycle. Rebuilding first would create fresh
elements with current values, making change detection impossible. */ elements with current values, making change detection impossible. */
@@ -289,30 +289,6 @@ index c1fc3cb..abecb4c 100644
+static EMACS_INT lastChildFrameModiff; +static EMACS_INT lastChildFrameModiff;
+static char *lastChildFrameCandidate; +static char *lastChildFrameCandidate;
+ +
+/* Child frame completion dedup state. File-scope so that
+ lastChildFrameBuffer can be staticpro'd against GC. */
+static Lisp_Object lastChildFrameBuffer;
+static EMACS_INT lastChildFrameModiff;
+static char *lastChildFrameCandidate;
+
+/* Reset the re-entrance guard when unwinding past
+ postAccessibilityUpdates due to a Lisp signal (longjmp).
+ Without this, a signal during Lisp calls (e.g. Fget_char_property
+ in overlay or child frame scanning) would leave
+ accessibilityUpdating = YES permanently, suppressing all future
+ accessibility notifications. */
+static void
+ns_ax_reset_accessibility_updating (void *view)
+{
+ ((EmacsView *)view)->accessibilityUpdating = NO;
+}
+
+/* Child frame completion dedup state. File-scope so that
+ lastChildFrameBuffer can be staticpro'd against GC. */
+static Lisp_Object lastChildFrameBuffer;
+static EMACS_INT lastChildFrameModiff;
+static char *lastChildFrameCandidate;
+
+/* Reset the re-entrance guard when unwinding past +/* Reset the re-entrance guard when unwinding past
+ postAccessibilityUpdates due to a Lisp signal (longjmp). + postAccessibilityUpdates due to a Lisp signal (longjmp).
+ Without this, a signal during Lisp calls (e.g. Fget_char_property + Without this, a signal during Lisp calls (e.g. Fget_char_property
@@ -328,7 +304,7 @@ index c1fc3cb..abecb4c 100644
- (void)postAccessibilityUpdates - (void)postAccessibilityUpdates
{ {
NSTRACE ("[EmacsView postAccessibilityUpdates]"); NSTRACE ("[EmacsView postAccessibilityUpdates]");
@@ -12323,10 +12567,60 @@ ns_ax_collect_windows (Lisp_Object window, EmacsView *view, @@ -12321,10 +12541,60 @@ ns_ax_collect_windows (Lisp_Object window, EmacsView *view,
/* Re-entrance guard: VoiceOver callbacks during notification posting /* Re-entrance guard: VoiceOver callbacks during notification posting
can trigger redisplay, which calls ns_update_end, which calls us can trigger redisplay, which calls ns_update_end, which calls us
@@ -390,7 +366,7 @@ index c1fc3cb..abecb4c 100644
/* Detect window tree change (split, delete, new buffer). Compare /* Detect window tree change (split, delete, new buffer). Compare
FRAME_ROOT_WINDOW --- if it changed, the tree structure changed. */ FRAME_ROOT_WINDOW --- if it changed, the tree structure changed. */
@@ -12357,7 +12651,7 @@ ns_ax_collect_windows (Lisp_Object window, EmacsView *view, @@ -12355,7 +12625,7 @@ ns_ax_collect_windows (Lisp_Object window, EmacsView *view,
NSAccessibilityFocusedUIElementChangedNotification); NSAccessibilityFocusedUIElementChangedNotification);
lastSelectedWindow = emacsframe->selected_window; lastSelectedWindow = emacsframe->selected_window;
@@ -399,7 +375,7 @@ index c1fc3cb..abecb4c 100644
return; return;
} }
@@ -12401,7 +12695,7 @@ ns_ax_collect_windows (Lisp_Object window, EmacsView *view, @@ -12399,7 +12669,7 @@ ns_ax_collect_windows (Lisp_Object window, EmacsView *view,
NSAccessibilityFocusedUIElementChangedNotification); NSAccessibilityFocusedUIElementChangedNotification);
} }
@@ -408,15 +384,12 @@ index c1fc3cb..abecb4c 100644
} }
/* ---- Cursor position for Zoom (via accessibilityBoundsForRange:) ---- /* ---- Cursor position for Zoom (via accessibilityBoundsForRange:) ----
@@ -14343,6 +14637,12 @@ syms_of_nsterm (void) @@ -14341,6 +14611,9 @@ syms_of_nsterm (void)
DEFSYM (Qns_ax_completion, "completion"); DEFSYM (Qns_ax_completion, "completion");
DEFSYM (Qns_ax_completions_highlight, "completions-highlight"); DEFSYM (Qns_ax_completions_highlight, "completions-highlight");
DEFSYM (Qns_ax_backtab, "backtab"); DEFSYM (Qns_ax_backtab, "backtab");
+ +
+ lastChildFrameBuffer = Qnil; + lastChildFrameBuffer = Qnil;
+ staticpro (&lastChildFrameBuffer);
+
+ lastChildFrameBuffer = Qnil;
+ staticpro (&lastChildFrameBuffer); + staticpro (&lastChildFrameBuffer);
/* Qmouse_face and Qkeymap are defined in textprop.c / keymap.c. */ /* Qmouse_face and Qkeymap are defined in textprop.c / keymap.c. */
Fput (Qalt, Qmodifier_value, make_fixnum (alt_modifier)); Fput (Qalt, Qmodifier_value, make_fixnum (alt_modifier));

View File

@@ -1,124 +0,0 @@
From 0812e650c24f90bda79368078fa0ad45c18f39d2 Mon Sep 17 00:00:00 2001
From: T <t@t>
Date: Sat, 28 Feb 2026 18:45:14 +0100
Subject: [PATCH 3/3] ns: harden VoiceOver accessibility resource safety
Fix several resource safety issues found during maintainer review:
* Announcement coalescing: add 50ms minimum interval between
AnnouncementRequested notifications to prevent VoiceOver speech
synthesizer stalls from rapid-fire high-priority interruptions
(e.g. holding C-n in a completion list).
* cachedText thread safety: return [[cachedText retain] autorelease]
from accessibilityValue to prevent use-after-free when the main
thread replaces cachedText while the AX server thread is still
using the previous value.
* EmacsView dealloc safety: nil out emacsView back-references on
all accessibility elements before releasing them. Queued
dispatch_async blocks that hold a retained element reference would
otherwise access a dangling emacsView pointer.
* Nil guards: add emacsView nil checks in accessibilityParent,
accessibilityWindow, accessibilityTopLevelUIElement, and
overlayZoomActive access sites.
* src/nsterm.m (ns_ax_post_notification_with_info): Add timestamp
coalescing for AnnouncementRequested.
(accessibilityValue): Return retained+autoreleased cachedText.
(dealloc): Nil out emacsView on all accessibility elements.
(accessibilityParent, accessibilityWindow)
(accessibilityTopLevelUIElement): Add nil guards.
---
src/nsterm.m | 38 +++++++++++++++++++++++++++++++++++++-
1 file changed, 37 insertions(+), 1 deletion(-)
diff --git a/src/nsterm.m b/src/nsterm.m
index abecb4c..3724b05 100644
--- a/src/nsterm.m
+++ b/src/nsterm.m
@@ -7521,11 +7521,32 @@ ns_ax_post_notification (id element,
});
}
+/* Minimum interval between AnnouncementRequested notifications
+ (in seconds). VoiceOver can stall if overwhelmed with rapid-fire
+ high-priority announcements that each interrupt the previous
+ utterance. 50ms lets the speech synthesizer start before the
+ next interruption. */
+#define NS_AX_ANNOUNCE_MIN_INTERVAL 0.05
+
static inline void
ns_ax_post_notification_with_info (id element,
NSAccessibilityNotificationName name,
NSDictionary *info)
{
+ /* Coalesce AnnouncementRequested: skip if the previous one was
+ less than NS_AX_ANNOUNCE_MIN_INTERVAL seconds ago. Prevents
+ speech synthesizer stalls from rapid-fire high-priority
+ interruptions (e.g. holding C-n in a completion list). */
+ if ([name isEqualToString:
+ NSAccessibilityAnnouncementRequestedNotification])
+ {
+ static CFAbsoluteTime lastAnnouncementTime;
+ CFAbsoluteTime now = CFAbsoluteTimeGetCurrent ();
+ if (now - lastAnnouncementTime < NS_AX_ANNOUNCE_MIN_INTERVAL)
+ return;
+ lastAnnouncementTime = now;
+ }
+
dispatch_async (dispatch_get_main_queue (), ^{
NSAccessibilityPostNotificationWithUserInfo (element, name, info);
});
@@ -7571,16 +7592,22 @@ ns_ax_post_notification_with_info (id element,
- (id)accessibilityParent
{
+ if (!self.emacsView)
+ return nil;
return NSAccessibilityUnignoredAncestor (self.emacsView);
}
- (id)accessibilityWindow
{
+ if (!self.emacsView)
+ return nil;
return [self.emacsView window];
}
- (id)accessibilityTopLevelUIElement
{
+ if (!self.emacsView)
+ return nil;
return [self.emacsView window];
}
@@ -8143,7 +8170,7 @@ ns_ax_completion_text_for_span (EmacsAccessibilityBuffer *elem,
return result;
}
[self ensureTextCache];
- return cachedText ? cachedText : @"";
+ return cachedText ? [[cachedText retain] autorelease] : @"";
}
- (NSInteger)accessibilityNumberOfCharacters
@@ -9659,6 +9686,15 @@ ns_ax_scan_interactive_spans (struct window *w,
[layer release];
#endif
+ /* Nil out back-references before releasing elements. Queued
+ dispatch_async blocks may still hold a retained reference to
+ an element; without this they would access a dangling
+ emacsView pointer after EmacsView is freed. */
+ for (id elem in accessibilityElements)
+ {
+ if ([elem respondsToSelector:@selector (setEmacsView:)])
+ [elem setEmacsView:nil];
+ }
[accessibilityElements release];
[[self menu] release];
[super dealloc];
--
2.43.0