Web fixes.
This commit is contained in:
1 parent
914d174925
commit
453206b85b
11 files changed
+643
-25
No files matched your search
@@ -8,6 +8,56 @@ static gchar *cthulhu_capture_text(AtspiAccessible *source, gchar **beforeCaret,
|
||||
static void cthulhu_capture_document_metrics(AtspiAccessible *source, cthulhu_snapshot *snapshot);
|
||||
static gchar *cthulhu_table_cell_label(AtspiAccessible *source, guint depth, guint *remaining);
|
||||
|
||||
// Chromium exposes doDefault even on inert text. Only an anchor with a label
|
||||
// explicitly offering ancestor activation supplies evidence of a custom action.
|
||||
// Never turn every descendant of a delegated click handler into a clickable.
|
||||
static gboolean cthulhu_anchor_has_click_label(AtspiAccessible *source) {
|
||||
gint count = atspi_accessible_get_child_count(source, NULL);
|
||||
for (gint index = 0; index < MIN(count, 16); index++) {
|
||||
AtspiAccessible *child = atspi_accessible_get_child_at_index(source, index, NULL);
|
||||
if (child == NULL) continue;
|
||||
AtspiRole role = atspi_accessible_get_role(child, NULL);
|
||||
gboolean label = role == ATSPI_ROLE_STATIC || role == ATSPI_ROLE_TEXT;
|
||||
gboolean found = FALSE;
|
||||
AtspiStateSet *states = atspi_accessible_get_state_set(child);
|
||||
gboolean passive = states != NULL &&
|
||||
!atspi_state_set_contains(states, ATSPI_STATE_FOCUSABLE) &&
|
||||
!atspi_state_set_contains(states, ATSPI_STATE_EDITABLE);
|
||||
if (states != NULL) g_object_unref(states);
|
||||
if (passive && label) {
|
||||
AtspiAction *action = atspi_accessible_get_action_iface(child);
|
||||
if (action != NULL) {
|
||||
gint actions = MIN(atspi_action_get_n_actions(action, NULL), 16);
|
||||
for (gint i = 0; i < actions && !found; i++) {
|
||||
gchar *name = atspi_action_get_name(action, i, NULL);
|
||||
found = name != NULL && g_ascii_strcasecmp(name, "clickAncestor") == 0;
|
||||
g_free(name);
|
||||
}
|
||||
g_object_unref(action);
|
||||
}
|
||||
}
|
||||
g_object_unref(child);
|
||||
if (found) return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Shared by capture and activation so a discovered target can actually be
|
||||
// invoked. Lower numbers retain the existing preference for direct actions.
|
||||
gint cthulhu_document_action_priority(AtspiAccessible *source, const gchar *name, gboolean anchor) {
|
||||
if (name == NULL) return 0;
|
||||
const gchar *direct[] = {"click", "click-ancestor", "press", "jump", "open", "activate"};
|
||||
for (guint index = 0; index < G_N_ELEMENTS(direct); index++) {
|
||||
if (g_ascii_strcasecmp(name, direct[index]) == 0) return index + 1;
|
||||
}
|
||||
if (!anchor) return 0;
|
||||
if (g_ascii_strcasecmp(name, "clickAncestor") == 0) return 7;
|
||||
if (g_ascii_strcasecmp(name, "doDefault") == 0) {
|
||||
if (cthulhu_anchor_has_click_label(source)) return 8;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Some toolkits expose an edit field's label only as a relation, leaving its
|
||||
// accessible name empty. Follow direct targets only, with a shared query and
|
||||
// output budget; never infer a label from neighbouring controls or field text.
|
||||
@@ -590,22 +640,14 @@ cthulhu_snapshot cthulhu_capture_snapshot(AtspiAccessible *source, gboolean capt
|
||||
if (error != NULL) {
|
||||
g_clear_error(&error);
|
||||
}
|
||||
for (gint index = 0; index < actionCount; index++) {
|
||||
for (gint index = 0; index < MIN(actionCount, 64); index++) {
|
||||
error = NULL;
|
||||
gchar *actionName = atspi_action_get_name(action, index, &error);
|
||||
if (error != NULL) {
|
||||
g_clear_error(&error);
|
||||
}
|
||||
// Web providers use different action names for activation. Keep
|
||||
// this list aligned with activateNativeDocumentObject so browse
|
||||
// Enter can reach objects exposing click-ancestor or press.
|
||||
if (actionName != NULL &&
|
||||
(g_ascii_strcasecmp(actionName, "click") == 0 ||
|
||||
g_ascii_strcasecmp(actionName, "click-ancestor") == 0 ||
|
||||
g_ascii_strcasecmp(actionName, "press") == 0 ||
|
||||
g_ascii_strcasecmp(actionName, "jump") == 0 ||
|
||||
g_ascii_strcasecmp(actionName, "open") == 0 ||
|
||||
g_ascii_strcasecmp(actionName, "activate") == 0)) {
|
||||
if (cthulhu_document_action_priority(source, actionName,
|
||||
g_strcmp0(snapshot.htmlTag, "a") == 0) != 0) {
|
||||
snapshot.supportsClick = TRUE;
|
||||
g_free(actionName);
|
||||
break;
|
||||
|
||||
@@ -13,7 +13,6 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
"unsafe"
|
||||
@@ -109,7 +108,7 @@ func (adapter *Adapter) SetDocumentPosition(ctx context.Context, position Object
|
||||
}
|
||||
|
||||
// CaptureDocumentSoundGeometry refreshes only the presented object and its
|
||||
// optional linked heading target, after navigation has scrolled them into view.
|
||||
// optional semantic link target, after navigation has scrolled them into view.
|
||||
// It never changes the cached document semantics or retains new native handles.
|
||||
func (adapter *Adapter) CaptureDocumentSoundGeometry(ctx context.Context, object, link ObjectReference) ([2]SoundGeometry, error) {
|
||||
if !object.Valid() {
|
||||
@@ -231,7 +230,6 @@ func (adapter *Adapter) activateNativeDocumentObject(ctx context.Context, positi
|
||||
return errors.New("current document object has no accessible action")
|
||||
}
|
||||
defer C.g_object_unref(C.gpointer(action))
|
||||
names := []string{"click", "click-ancestor", "press", "jump", "open", "activate"}
|
||||
var nativeErr *C.GError
|
||||
count := int(C.atspi_action_get_n_actions(action, &nativeErr))
|
||||
if nativeErr != nil {
|
||||
@@ -241,18 +239,22 @@ func (adapter *Adapter) activateNativeDocumentObject(ctx context.Context, positi
|
||||
}
|
||||
// Read each action name once. Previously each preferred spelling caused
|
||||
// another full round of synchronous accessibility calls.
|
||||
actionNames := make([]string, min(max(count, 0), 64))
|
||||
for index := range actionNames {
|
||||
priorities := make([]int, min(max(count, 0), 64))
|
||||
anchor := C.gboolean(0)
|
||||
if snapshot.HTMLTag == "a" {
|
||||
anchor = 1
|
||||
}
|
||||
for index := range priorities {
|
||||
if err := ctx.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
name := C.atspi_action_get_name(action, C.gint(index), nil)
|
||||
actionNames[index] = C.GoString(name)
|
||||
priorities[index] = int(C.cthulhu_document_action_priority(source, name, anchor))
|
||||
C.g_free(C.gpointer(name))
|
||||
}
|
||||
for _, wanted := range names {
|
||||
for index, name := range actionNames {
|
||||
if !strings.EqualFold(name, wanted) {
|
||||
for wanted := 1; wanted <= 8; wanted++ {
|
||||
for index, priority := range priorities {
|
||||
if priority != wanted {
|
||||
continue
|
||||
}
|
||||
if err := ctx.Err(); err != nil {
|
||||
|
||||
@@ -93,6 +93,7 @@ typedef struct {
|
||||
gchar *cthulhu_table_context(AtspiAccessible *source, gint *rows, gint *columns,
|
||||
gchar **tableName, gchar **tablePath,
|
||||
gint *rowIndex, gint *columnIndex, gboolean *guiTable);
|
||||
gint cthulhu_document_action_priority(AtspiAccessible *source, const gchar *name, gboolean anchor);
|
||||
cthulhu_snapshot cthulhu_capture_snapshot(AtspiAccessible *source, gboolean captureText,
|
||||
gboolean captureCaretCharacter,
|
||||
gboolean captureDocumentMetadata,
|
||||
|
||||
@@ -250,6 +250,14 @@ func captureNativeTree(ctx context.Context, root *C.AtspiAccessible, reviewBusNa
|
||||
// A visibility change can reveal children omitted by an earlier review
|
||||
// capture, even when the provider emits no children-changed event.
|
||||
cachedChildren := known && !cached.childrenInvalid && !cached.childrenUnavailable && (!reviewOnly || !cached.geometryInvalid)
|
||||
if document && snapshot.Role == "internal frame" {
|
||||
// Chromium can replace a sibling iframe document without an
|
||||
// invalidation for its retained frame object. Refresh this boundary
|
||||
// on recapture so a successful header action cannot leave the old
|
||||
// content document pinned in either our cache or libatspi's cache.
|
||||
cachedChildren = false
|
||||
C.atspi_accessible_clear_cache_single(source)
|
||||
}
|
||||
childrenUnavailable := false
|
||||
for _, id := range cached.children {
|
||||
if cache.sources[id] == nil {
|
||||
|
||||
Reference in new issue
Block a user