Files
hjozwiak 7eef6e8be6 Make web document navigation survive live-updating pages
Recreated-node caret recovery, tolerant caret validation, per-root retained document trees and cursors across tab switches, document warm-up, coalesced structural moves, and hung-provider fast-fail with an 'Application is not responding' announcement.
2026-11-03 10:34:55 -05:00

56 lines
2.0 KiB
Go

// Copyright (c) 2026 Storm Dragon
// Authors: Storm Dragon, Angus Kola
// SPDX-License-Identifier: GPL-3.0-or-later
package atspi
import (
"fmt"
"strings"
)
// A removed Chromium generated-text object can return zero characters before
// its removal event reaches us. Refresh its metadata and parent's child order
// rather than silently skipping text from an obsolete snapshot. Empty text
// alone does not prove removal, so keep identities and live siblings intact.
func (adapter *Adapter) document_text_became_empty(object ObjectID) error {
adapter.treeMutex.Lock()
tree := adapter.documentTree
adapter.treeMutex.Unlock()
if tree != nil {
tree.mu.RLock()
entry := tree.nodes[object]
tree.mu.RUnlock()
adapter.treeInvalidations.Add(uint64(tree.InvalidateSnapshot(object)))
if entry.hasParent {
adapter.treeInvalidations.Add(uint64(tree.MarkChildrenChanged(entry.parent)))
}
}
delete(adapter.documentLineCache, object)
return ErrStaleText
}
// libatspi forwards Chromium's missing-object response as an untyped GError.
// Match the queried path exactly; a timeout or unsupported caret is not proof
// that the virtual reading position has disappeared.
func (adapter *Adapter) document_object_error(object ObjectID, err error) error {
if err == nil || !object.Valid() || !strings.HasSuffix(strings.TrimSpace(err.Error()),
"could not be processed as object "+object.Path+" does not exist") {
return err
}
for _, tree := range adapter.ownedTrees() {
tree.mu.RLock()
entry := tree.nodes[object]
tree.mu.RUnlock()
adapter.treeInvalidations.Add(uint64(tree.Invalidate(object)))
if entry.hasParent {
// Virtualized pages can replace the enclosing post or feed, not
// just this target. Refresh every ancestor's child list so capture
// reaches the live replacement rather than reusing stale branches.
adapter.treeInvalidations.Add(uint64(tree.markAncestorChildrenChanged(entry.parent)))
}
}
clear(adapter.documentLineCache)
return fmt.Errorf("%w: %w", ErrDeadObject, err)
}