Bug 1954205 - Unify AT-SPI interface generation for local and remote. r=Jamie

Had to get rid of dependency on MustPrune because this happens at
instantiaton. If an accessible loses or gains a child in its lifetime
the interfaces it supports cannot change.

Differential Revision: https://phabricator.services.mozilla.com/D241690
This commit is contained in:
Eitan Isaacson
2025-03-14 22:42:22 +00:00
parent 56efb5388b
commit 0c1dd7f719
4 changed files with 133 additions and 97 deletions

View File

@@ -269,6 +269,56 @@ void AccessibleWrap::Shutdown() {
LocalAccessible::Shutdown(); LocalAccessible::Shutdown();
} }
static uint16_t CreateMaiInterfaces(Accessible* aAccessible) {
uint16_t interfaces = 1 << MAI_INTERFACE_COMPONENT;
if (aAccessible->IsHyperText() && aAccessible->IsTextRole()) {
interfaces |= (1 << MAI_INTERFACE_HYPERTEXT) | (1 << MAI_INTERFACE_TEXT) |
(1 << MAI_INTERFACE_EDITABLE_TEXT);
}
if (aAccessible->IsLink()) {
interfaces |= 1 << MAI_INTERFACE_HYPERLINK_IMPL;
}
if (aAccessible->HasNumericValue()) {
interfaces |= 1 << MAI_INTERFACE_VALUE;
}
if (aAccessible->IsTable()) {
interfaces |= 1 << MAI_INTERFACE_TABLE;
}
if (aAccessible->IsTableCell()) {
interfaces |= 1 << MAI_INTERFACE_TABLE_CELL;
}
if (aAccessible->IsImage()) {
interfaces |= 1 << MAI_INTERFACE_IMAGE;
}
if (aAccessible->IsDoc()) {
interfaces |= 1 << MAI_INTERFACE_DOCUMENT;
}
if (aAccessible->IsSelect()) {
interfaces |= 1 << MAI_INTERFACE_SELECTION;
}
if (aAccessible->IsRemote()) {
if (aAccessible->IsActionable()) {
interfaces |= 1 << MAI_INTERFACE_ACTION;
}
} else {
// XXX: Harmonize this with remote accessibles
if (aAccessible->ActionCount()) {
interfaces |= 1 << MAI_INTERFACE_ACTION;
}
}
return interfaces;
}
void AccessibleWrap::GetNativeInterface(void** aOutAccessible) { void AccessibleWrap::GetNativeInterface(void** aOutAccessible) {
*aOutAccessible = nullptr; *aOutAccessible = nullptr;
@@ -279,7 +329,7 @@ void AccessibleWrap::GetNativeInterface(void** aOutAccessible) {
return; return;
} }
GType type = GetMaiAtkType(CreateMaiInterfaces()); GType type = GetMaiAtkType(CreateMaiInterfaces(this));
if (!type) return; if (!type) return;
mAtkObject = reinterpret_cast<AtkObject*>(g_object_new(type, nullptr)); mAtkObject = reinterpret_cast<AtkObject*>(g_object_new(type, nullptr));
@@ -307,52 +357,6 @@ AtkObject* AccessibleWrap::GetAtkObject(LocalAccessible* acc) {
return atkObjPtr ? ATK_OBJECT(atkObjPtr) : nullptr; return atkObjPtr ? ATK_OBJECT(atkObjPtr) : nullptr;
} }
/* private */
uint16_t AccessibleWrap::CreateMaiInterfaces(void) {
uint16_t interfacesBits = 0;
// The Component interface is supported by all accessibles.
interfacesBits |= 1 << MAI_INTERFACE_COMPONENT;
// Add Action interface if the action count is more than zero.
if (ActionCount() > 0) interfacesBits |= 1 << MAI_INTERFACE_ACTION;
// Text, Editabletext, and Hypertext interface.
HyperTextAccessible* hyperText = AsHyperText();
if (hyperText && hyperText->IsTextRole()) {
interfacesBits |= 1 << MAI_INTERFACE_TEXT;
interfacesBits |= 1 << MAI_INTERFACE_EDITABLE_TEXT;
if (!nsAccUtils::MustPrune(this)) {
interfacesBits |= 1 << MAI_INTERFACE_HYPERTEXT;
}
}
// Value interface.
if (HasNumericValue()) interfacesBits |= 1 << MAI_INTERFACE_VALUE;
// Document interface.
if (IsDoc()) interfacesBits |= 1 << MAI_INTERFACE_DOCUMENT;
if (IsImage()) interfacesBits |= 1 << MAI_INTERFACE_IMAGE;
// HyperLink interface.
if (IsLink()) interfacesBits |= 1 << MAI_INTERFACE_HYPERLINK_IMPL;
if (!nsAccUtils::MustPrune(this)) { // These interfaces require children
// Table interface.
if (AsTable()) interfacesBits |= 1 << MAI_INTERFACE_TABLE;
if (AsTableCell()) interfacesBits |= 1 << MAI_INTERFACE_TABLE_CELL;
// Selection interface.
if (IsSelect()) {
interfacesBits |= 1 << MAI_INTERFACE_SELECTION;
}
}
return interfacesBits;
}
static GType GetMaiAtkType(uint16_t interfacesBits) { static GType GetMaiAtkType(uint16_t interfacesBits) {
GType type; GType type;
static const GTypeInfo tinfo = { static const GTypeInfo tinfo = {
@@ -886,56 +890,10 @@ AtkObject* GetWrapperFor(Accessible* aAcc) {
return AccessibleWrap::GetAtkObject(aAcc->AsLocal()); return AccessibleWrap::GetAtkObject(aAcc->AsLocal());
} }
static uint16_t GetInterfacesForProxy(RemoteAccessible* aProxy) {
uint16_t interfaces = 1 << MAI_INTERFACE_COMPONENT;
if (aProxy->IsHyperText() && aProxy->IsTextRole()) {
interfaces |= 1 << MAI_INTERFACE_TEXT;
interfaces |= 1 << MAI_INTERFACE_EDITABLE_TEXT;
if (!nsAccUtils::MustPrune(aProxy)) {
interfaces |= 1 << MAI_INTERFACE_HYPERTEXT;
}
}
if (aProxy->IsLink()) {
interfaces |= 1 << MAI_INTERFACE_HYPERLINK_IMPL;
}
if (aProxy->HasNumericValue()) {
interfaces |= 1 << MAI_INTERFACE_VALUE;
}
if (aProxy->IsTable()) {
interfaces |= 1 << MAI_INTERFACE_TABLE;
}
if (aProxy->IsTableCell()) {
interfaces |= 1 << MAI_INTERFACE_TABLE_CELL;
}
if (aProxy->IsImage()) {
interfaces |= 1 << MAI_INTERFACE_IMAGE;
}
if (aProxy->IsDoc()) {
interfaces |= 1 << MAI_INTERFACE_DOCUMENT;
}
if (aProxy->IsSelect()) {
interfaces |= 1 << MAI_INTERFACE_SELECTION;
}
if (aProxy->IsActionable()) {
interfaces |= 1 << MAI_INTERFACE_ACTION;
}
return interfaces;
}
void a11y::ProxyCreated(RemoteAccessible* aProxy) { void a11y::ProxyCreated(RemoteAccessible* aProxy) {
MOZ_ASSERT(aProxy->RemoteParent() || aProxy->IsDoc(), MOZ_ASSERT(aProxy->RemoteParent() || aProxy->IsDoc(),
"Need parent to check for HyperLink interface"); "Need parent to check for HyperLink interface");
GType type = GetMaiAtkType(GetInterfacesForProxy(aProxy)); GType type = GetMaiAtkType(CreateMaiInterfaces(aProxy));
NS_ASSERTION(type, "why don't we have a type!"); NS_ASSERTION(type, "why don't we have a type!");
AtkObject* obj = reinterpret_cast<AtkObject*>(g_object_new(type, nullptr)); AtkObject* obj = reinterpret_cast<AtkObject*>(g_object_new(type, nullptr));

View File

@@ -82,9 +82,6 @@ class AccessibleWrap : public LocalAccessible {
nsresult FireAtkTextChangedEvent(AccEvent* aEvent, AtkObject* aObject); nsresult FireAtkTextChangedEvent(AccEvent* aEvent, AtkObject* aObject);
AtkObject* mAtkObject; AtkObject* mAtkObject;
private:
uint16_t CreateMaiInterfaces();
}; };
} // namespace a11y } // namespace a11y

View File

@@ -11,6 +11,7 @@ prefs = [
"accessibility.force_disabled=-1", "accessibility.force_disabled=-1",
] ]
["browser_atspi_interfaces.js"]
["browser_groupPosition.js"] ["browser_groupPosition.js"]
["browser_prune_children.js"] ["browser_prune_children.js"]
["browser_role.js"] ["browser_role.js"]

View File

@@ -0,0 +1,80 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
addAccessibleTask(
`
<p id="p">p</p>
<a href="https://example.com" id="link">a</a>
<input id="range_input" type="range" min="0" max="10" value="8">
<input id="text_input" type="text" value="hello">
<button id="button">hello</button>`,
async function testInterfaces() {
await runPython(`
global doc
doc = getDoc()
`);
async function checkInterfaces(id, expectedInterfaces) {
let interfaces = await runPython(`
return findByDomId(doc, "${id}").get_interfaces()
`);
Assert.deepEqual(
expectedInterfaces.slice().sort(),
interfaces.sort(),
`Correct interfaces for "${id}"`
);
}
await checkInterfaces("p", [
"Accessible",
"Collection",
"Component",
"EditableText",
"Hyperlink",
"Hypertext",
"Text",
]);
await checkInterfaces("link", [
"Accessible",
"Action",
"Collection",
"Component",
"EditableText",
"Hyperlink",
"Hypertext",
"Text",
]);
await checkInterfaces("range_input", [
"Accessible",
"Collection",
"Component",
"Hyperlink",
"Value",
]);
await checkInterfaces("text_input", [
"Accessible",
"Action",
"Collection",
"Component",
"EditableText",
"Hyperlink",
"Hypertext",
"Text",
]);
await checkInterfaces("button", [
"Accessible",
"Action",
"Collection",
"Component",
"EditableText",
"Hyperlink",
"Hypertext",
"Text",
]);
},
{ chrome: true, topLevel: true }
);