Suppress hypertext object markers in text-change speech
Thunderbird's message composer emits U+FFFC object replacement characters when creating or removing paragraph objects. Treating these structural updates as inserted or deleted text sent raw placeholders to speech, including multi-character updates that bypass ordinary key-echo settings. Remove object markers from insertion and deletion presentation while preserving surrounding text. Normalize typing-echo payloads before deciding the echo kind or applying character, word, and sentence echo policies. Return no text or echo kind for updates containing only object markers. Add regression cases for single and repeated markers, mixed marker/text payloads, and ordinary text through both event presentation and typing echo. Validation: - go test ./... -count=1, with the local cache and dependency network disabled. - Built the reader and the BrlAPI-tagged Braille helper. - A disposable Thunderbird profile on a private X11 display and session bus produced one marker-containing presentation before the fix and none after. - Private Thunderbird checks preserved character, word, and line navigation after typing several paragraphs. - The user confirmed main-arrow reading worked with the restarted build. The original silent-arrow failure was not reproduced in isolation. Restarting restored reading in the user's session; this commit does not claim to fix its underlying cause or change arrow-key handling. Physical Braille output was not tested. Preserve the original and fresh diagnostic logs locally.
This commit is contained in:
1 parent
9462ebfc82
commit
7aa4617d04
3 files changed
+33
No files matched your search
@@ -256,6 +256,9 @@ func EventSpeechWithOptions(event atspi.Event, options Options) Speech {
|
||||
return Speech{Text: event.Source.Text}
|
||||
}
|
||||
case atspi.TextInsertedEvent:
|
||||
// Hypertext editors report paragraph creation as inserted object
|
||||
// placeholders. These describe structure rather than typed text.
|
||||
event.Payload = strings.ReplaceAll(event.Payload, "\ufffc", "")
|
||||
if !shouldEchoInsertion(event.Payload, insertionEchoOptions(event, options)) {
|
||||
return Speech{}
|
||||
}
|
||||
@@ -264,6 +267,7 @@ func EventSpeechWithOptions(event atspi.Event, options Options) Speech {
|
||||
}
|
||||
return Speech{Text: event.Payload}
|
||||
case atspi.TextDeletedEvent:
|
||||
event.Payload = strings.ReplaceAll(event.Payload, "\ufffc", "")
|
||||
if utf8.RuneCountInString(event.Payload) == 1 && unicode.IsSpace(firstRune(event.Payload)) {
|
||||
return Speech{Text: caretCharacterText(event.Payload)}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,12 @@ func TypingEchoText(event atspi.Event, previousText string, options Options) str
|
||||
}
|
||||
|
||||
func TypingEcho(event atspi.Event, previousText string, options Options) (string, string) {
|
||||
if event.Type == atspi.TextInsertedEvent || event.Type == atspi.TextDeletedEvent {
|
||||
event.Payload = strings.ReplaceAll(event.Payload, "\ufffc", "")
|
||||
if event.Payload == "" {
|
||||
return "", ""
|
||||
}
|
||||
}
|
||||
options = insertionEchoOptions(event, options)
|
||||
kind := ""
|
||||
if event.Type == atspi.TextInsertedEvent && utf8.RuneCountInString(event.Payload) == 1 {
|
||||
|
||||
@@ -164,3 +164,26 @@ func TestTypingEchoInEditFieldsOnly(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestEditorStructuralChangesDoNotSpeakObjectMarkers(t *testing.T) {
|
||||
for _, eventType := range []string{atspi.TextInsertedEvent, atspi.TextDeletedEvent} {
|
||||
for _, tc := range []struct{ payload, want string }{
|
||||
{"\ufffc", ""},
|
||||
{"\ufffc\ufffc", ""},
|
||||
{"\ufffcDraft\ufffc", "Draft"},
|
||||
{"ordinary text", "ordinary text"},
|
||||
} {
|
||||
t.Run(eventType+tc.payload, func(t *testing.T) {
|
||||
event := atspi.NewEventWithPayload(eventType, atspi.Snapshot{
|
||||
Role: "document web", Editable: true,
|
||||
}, tc.payload)
|
||||
if got := EventTextWithOptions(event, DefaultOptions()); got != tc.want {
|
||||
t.Fatalf("structural change speech = %q, want %q", got, tc.want)
|
||||
}
|
||||
if got := TypingEchoText(event, "", DefaultOptions()); got != tc.want {
|
||||
t.Fatalf("structural change echo = %q, want %q", got, tc.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user