// Copyright (c) 2026 Storm Dragon // Authors: Storm Dragon, Angus Kola // SPDX-License-Identifier: GPL-3.0-or-later package atspi /* #include */ import "C" import "context" // Capture only single-character embedded children. Chromium's ordinary text // descendants share their parent's offset domain and need no extra ranges. // Require every child to occupy one placeholder, avoiding duplicate text from // containers which also expose their plain text through static children. // All handles stay on the GLib owner and are released before returning. func nativeTextChildren(ctx context.Context, source *C.AtspiAccessible, count int, children []ObjectID) []TextChild { if count <= 0 || len(children) == 0 { return nil } hypertext := C.atspi_accessible_get_hypertext_iface(source) if hypertext == nil { return nil } defer C.g_object_unref(C.gpointer(hypertext)) links := int(C.atspi_hypertext_get_n_links(hypertext, nil)) if links <= 0 || links != len(children) { return nil } text := C.atspi_accessible_get_text_iface(source) if text == nil { return nil } defer C.g_object_unref(C.gpointer(text)) known := make(map[ObjectID]bool, len(children)) for _, child := range children { known[child] = true } var result []TextChild previous := -1 for index := 0; index < links; index++ { if ctx.Err() != nil { return nil } link := C.atspi_hypertext_get_link(hypertext, C.gint(index), nil) if link == nil { return nil } start := int(C.atspi_hyperlink_get_start_index(link, nil)) end := int(C.atspi_hyperlink_get_end_index(link, nil)) child := C.atspi_hyperlink_get_object(link, 0, nil) var id ObjectID if child != nil { id = nativeObjectID(child) C.g_object_unref(C.gpointer(child)) } C.g_object_unref(C.gpointer(link)) if start <= previous || end != start+1 || start < 0 || end > count || !known[id] { return nil } value := C.atspi_text_get_text(text, C.gint(start), C.gint(end), nil) placeholder := value != nil && C.GoString(value) == "\ufffc" if value != nil { C.g_free(C.gpointer(value)) } if !placeholder { return nil } delete(known, id) result = append(result, TextChild{Offset: start, Child: id}) previous = start } return result }