tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 5ee6854ffbe6ed627a3684fbe8d3481bd6081b4b
parent 175e4ab73d6ac0fd05030107546e969af208071d
Author: James Teh <jteh@mozilla.com>
Date:   Tue, 16 Dec 2025 22:44:23 +0000

Bug 1994032 part 1: Implement UIA AccessibleActions property. r=eeejay

This is a custom property that isn't defined by Windows, so this patch also adds infrastructure to allow for registration, exposure and testing of custom properties.

Differential Revision: https://phabricator.services.mozilla.com/D276599

Diffstat:
Maccessible/tests/browser/windows/a11y_setup.py | 20+++++++++++++++++++-
Maccessible/tests/browser/windows/uia/browser_relationProps.js | 33+++++++++++++++++++++++++++++++++
Maccessible/windows/uia/uiaRawElmProvider.cpp | 37+++++++++++++++++++++++++++++++++++++
Maccessible/windows/uia/uiaRawElmProvider.h | 13+++++++++++++
4 files changed, 102 insertions(+), 1 deletion(-)

diff --git a/accessible/tests/browser/windows/a11y_setup.py b/accessible/tests/browser/windows/a11y_setup.py @@ -15,7 +15,7 @@ from dataclasses import dataclass import comtypes.automation import comtypes.client import psutil -from comtypes import COMError, IServiceProvider +from comtypes import GUID, COMError, IServiceProvider CHILDID_SELF = 0 COWAIT_DEFAULT = 0 @@ -100,6 +100,24 @@ uiaClient = comtypes.CoCreateInstance( clsctx=comtypes.CLSCTX_INPROC_SERVER, ) +# Register UIA custom properties. +# IUIAutomationRegistrar is in a different type library. +uiaCoreMod = comtypes.client.GetModule(("{930299ce-9965-4dec-b0f4-a54848d4b667}",)) +uiaReg = comtypes.CoCreateInstance( + uiaCoreMod.CUIAutomationRegistrar._reg_clsid_, + interface=uiaCoreMod.IUIAutomationRegistrar, +) +uiaAccessibleActionsPropertyId = uiaReg.RegisterProperty( + byref( + uiaCoreMod.UIAutomationPropertyInfo( + GUID("{8C787AC3-0405-4C94-AC09-7A56A173F7EF}"), + "AccessibleActions", + uiaCoreMod.UIAutomationType_ElementArray, + ) + ) +) +del uiaReg, uiaCoreMod + _threadLocal = threading.local() diff --git a/accessible/tests/browser/windows/uia/browser_relationProps.js b/accessible/tests/browser/windows/uia/browser_relationProps.js @@ -12,6 +12,18 @@ function testUiaRelationArray(id, prop, targets) { ); } +function testCustomUiaRelationArray(id, prop, targets) { + return isUiaElementArray( + ` + findUiaByDomId(doc, "${id}") + .GetCurrentPropertyValue(uia${prop}PropertyId) + .QueryInterface(IUIAutomationElementArray) + `, + targets, + `${id} has correct ${prop} targets` + ); +} + /** * Test the ControllerFor property. */ @@ -141,3 +153,24 @@ addUiaTask( // The IA2 -> UIA proxy doesn't expose LabeledBy properly. { uiaEnabled: true, uiaDisabled: false } ); + +/** + * Test the AccessibleActions property. + */ +addUiaTask( + ` +<dialog aria-actions="btn" id="dlg" onclick="" open> + Dialog with its own click listener + <form method="dialog"> + <button id="btn">Close</button> + </form> +</dialog> + `, + async function testActions() { + await definePyVar("doc", `getDocUia()`); + await testCustomUiaRelationArray("dlg", "AccessibleActions", ["btn"]); + await testCustomUiaRelationArray("btn", "AccessibleActions", []); + }, + // The IA2 -> UIA proxy doesn't support AccessibleActions. + { uiaEnabled: true, uiaDisabled: false } +); diff --git a/accessible/windows/uia/uiaRawElmProvider.cpp b/accessible/windows/uia/uiaRawElmProvider.cpp @@ -775,6 +775,17 @@ uiaRawElmProvider::GetPropertyValue(PROPERTYID aPropertyId, aPropertyValue->vt = VT_I4; aPropertyValue->lVal = acc->GroupPosition().setSize; return S_OK; + + default: { + // These can't be included as case statements because they are not + // constant expressions. + const UiaRegistrations& registrations = GetUiaRegistrations(); + if (aPropertyId == registrations.mAccessibleActions) { + aPropertyValue->vt = VT_UNKNOWN | VT_ARRAY; + aPropertyValue->parray = AccRelationsToUiaArray({RelationType::ACTION}); + return S_OK; + } + } } return S_OK; @@ -1570,3 +1581,29 @@ SAFEARRAY* a11y::AccessibleArrayToUiaArray(const nsTArray<Accessible*>& aAccs) { } return uias; } + +const UiaRegistrations& a11y::GetUiaRegistrations() { + static UiaRegistrations sRegistrations = {}; + static bool sRegistered = false; + if (sRegistered) { + return sRegistrations; + } + RefPtr<IUIAutomationRegistrar> registrar; + if (FAILED(CoCreateInstance(CLSID_CUIAutomationRegistrar, nullptr, + CLSCTX_INPROC_SERVER, IID_IUIAutomationRegistrar, + getter_AddRefs(registrar)))) { + return sRegistrations; + } + UIAutomationPropertyInfo actionsInfo = { + // https://w3c.github.io/core-aam/#ariaActions + // {8C787AC3-0405-4C94-AC09-7A56A173F7EF} + {0x8C787AC3, + 0x0405, + 0x4C94, + {0xAC, 0x09, 0x7A, 0x56, 0xA1, 0x73, 0xF7, 0xEF}}, + L"AccessibleActions", + UIAutomationType_ElementArray}; + registrar->RegisterProperty(&actionsInfo, &sRegistrations.mAccessibleActions); + sRegistered = true; + return sRegistrations; +} diff --git a/accessible/windows/uia/uiaRawElmProvider.h b/accessible/windows/uia/uiaRawElmProvider.h @@ -26,6 +26,10 @@ namespace a11y { class Accessible; enum class RelationType; +struct UiaRegistrations { + PROPERTYID mAccessibleActions = 0; +}; + /** * IRawElementProviderSimple implementation (maintains IAccessibleEx approach). */ @@ -210,6 +214,15 @@ class uiaRawElmProvider : public IAccessibleEx, SAFEARRAY* AccessibleArrayToUiaArray(const nsTArray<Accessible*>& aAccs); +/** + * Get ids for custom UI Automation properties and events which are not defined + * by Windows and therefore need to be registered at runtime. This function will + * register the properties and events the first time it is called. Thereafter, + * cached ids will be returned, since they are valid for the lifetime of the + * process. + */ +const UiaRegistrations& GetUiaRegistrations(); + } // namespace a11y } // namespace mozilla