// Copyright (c) 2026 Storm Dragon // Authors: Storm Dragon, Angus Kola // SPDX-License-Identifier: GPL-3.0-or-later #include "adapter_native.h" #include AtspiTextRange *cthulhu_text_get_string_at_offset(AtspiText *text, gint offset, AtspiTextGranularity unit, GError **error) { if (unit != ATSPI_TEXT_GRANULARITY_LINE || offset < 0) { return atspi_text_get_string_at_offset(text, offset, unit, error); } // Chromium can retain layout boundaries past truncated hypertext. In a // captured crash, LINE at the final U+2026 (140 of 141 characters) tried // to take a substring starting at 146 and aborted the entire browser. // Checking returned boundaries is too late: use bounded GetText for an // ellipsis before invoking the provider's boundary calculation. GError *probeError = NULL; guint character = atspi_text_get_character_at_offset(text, offset, &probeError); if (probeError != NULL) { g_propagate_error(error, probeError); return NULL; } if (character != 0x2026) { return atspi_text_get_string_at_offset(text, offset, unit, error); } gint count = atspi_text_get_character_count(text, &probeError); if (probeError != NULL) { g_propagate_error(error, probeError); return NULL; } if (count <= 0 || offset != count - 1) { return atspi_text_get_string_at_offset(text, offset, unit, error); } gint start = MAX(0, offset - 4095); gchar *content = atspi_text_get_text(text, start, offset + 1, error); if (content == NULL) return NULL; if (!g_utf8_validate(content, -1, NULL) || g_utf8_strlen(content, -1) != offset + 1 - start || !g_str_has_suffix(content, "\342\200\246")) { g_free(content); return NULL; } // Preserve explicit line breaks. A provider's visual wrapping is unsafe // at this character; retain the bounded logical line and native offsets. gchar *line = strrchr(content, '\n'); line = line != NULL ? line + 1 : content; AtspiTextRange *range = g_new0(AtspiTextRange, 1); range->start_offset = start + g_utf8_pointer_to_offset(content, line); range->end_offset = offset + 1; range->content = g_strdup(line); g_free(content); return range; }