// Copyright (c) 2026 Storm Dragon // Authors: Storm Dragon, Angus Kola // SPDX-License-Identifier: GPL-3.0-or-later package atspi func documentNavigationEchoBit(event Event, offset int, selectionOffsets map[int]bool) uint8 { switch event.Type { case FocusEvent: return 1 case FocusedStateEvent: return 2 case CaretEvent: // New native selection is user input, even if its endpoint happens // to match a previously requested caret or collapsed selection. if event.Source.TextCaretKnown && event.Source.SelectionEnd > event.Source.SelectionStart { return 0 } if event.Detail1 == offset { return 4 } if event.Source.TextCaretKnown && selectionOffsets[event.Detail1] { return 8 } } return 0 } // Chromium can echo a static text caret on its enclosing label/paragraph. // Include only a single-child chain sharing the complete text offset domain. // Composite parents have their own hypertext offsets and cannot be matched. func documentCaretEchoAncestors(tree *AccessibleTree, reference ObjectReference) []ObjectID { var ids []ObjectID child, err := tree.Resolve(reference) if err != nil || child.CharacterCount <= 0 { return nil } for attempts := 0; attempts < maxTreeNodes; attempts++ { parent, ok, err := tree.Parent(reference) if err != nil || !ok { break } snapshot, err := tree.Resolve(parent) if err != nil || IsWebDocumentRole(snapshot.Role) || snapshot.Editable || snapshot.CharacterCount != child.CharacterCount { break } first, ok, err := tree.FirstChild(parent) if err != nil || !ok || first != reference { break } last, ok, err := tree.LastChild(parent) if err != nil || !ok || last != reference { break } ids = append(ids, parent.ID()) reference, child = parent, snapshot } return ids }