Files
cthulhu/internal/atspi/document_hypertext_native.go

77 lines
2.2 KiB
Go

// Copyright (c) 2026 Storm Dragon
// Authors: Storm Dragon, Angus Kola
// SPDX-License-Identifier: GPL-3.0-or-later
package atspi
/*
#include <atspi/atspi.h>
*/
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
}