tor-browser

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

commit e212f6811e0be1068bbc4de89de6bdba1ef8d75e
parent a02a3ce634015d230f8c167fdbd350ea849e04a6
Author: Eitan Isaacson <eitan@monotonous.org>
Date:   Fri, 24 Oct 2025 14:31:34 +0000

Bug 1994455 - P2: Expose has-actions attribute when aria-actions is present. r=Jamie

We don't care about the value of the attribute or if there are any valid
targets, as long as it is present has-actions should be true.

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

Diffstat:
Maccessible/base/CacheConstants.h | 3+++
Maccessible/generic/LocalAccessible.cpp | 18++++++++++++++++++
Maccessible/ipc/RemoteAccessible.cpp | 5+++++
Maccessible/tests/browser/e10s/browser_caching_attributes.js | 65+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mxpcom/ds/StaticAtoms.py | 1+
5 files changed, 92 insertions(+), 0 deletions(-)

diff --git a/accessible/base/CacheConstants.h b/accessible/base/CacheConstants.h @@ -178,6 +178,9 @@ class CacheKey { // nsTArray<int32_t>, no domain // As returned by HyperTextAccessibleBase::CachedHyperTextOffsets. static constexpr nsStaticAtom* HyperTextOffsets = nsGkAtoms::offset; + // bool, CacheDomain::ARIA + // Accessible has aria-actions + static constexpr nsStaticAtom* HasActions = nsGkAtoms::hasActions; // bool, CacheDomain::Actions // Whether this image has a longdesc. static constexpr nsStaticAtom* HasLongdesc = nsGkAtoms::longdesc; diff --git a/accessible/generic/LocalAccessible.cpp b/accessible/generic/LocalAccessible.cpp @@ -1149,6 +1149,10 @@ already_AddRefed<AccAttributes> LocalAccessible::Attributes() { attribIter.ExposeAttr(attributes); } + if (nsAccUtils::HasARIAAttr(Elm(), nsGkAtoms::aria_actions)) { + attributes->SetAttribute(nsGkAtoms::hasActions, true); + } + // If there is no aria-live attribute then expose default value of 'live' // object attribute used for ARIA role of this accessible. const nsRoleMapEntry* roleMapEntry = ARIARoleMap(); @@ -1406,6 +1410,14 @@ void LocalAccessible::DOMAttributeChanged(int32_t aNameSpaceID, } } + if (aAttribute == nsGkAtoms::aria_actions && IsAdditionOrRemoval(aModType)) { + // We only care about the presence of aria-actions, not its value. + mDoc->QueueCacheUpdate(this, CacheDomain::ARIA); + RefPtr<AccEvent> event = + new AccObjectAttrChangedEvent(this, nsGkAtoms::hasActions); + mDoc->FireDelayedEvent(event); + } + dom::Element* elm = Elm(); if (HasNumericValue() && @@ -4036,6 +4048,12 @@ already_AddRefed<AccAttributes> LocalAccessible::BundleFieldsForCache( } else if (IsUpdatePush(CacheDomain::ARIA)) { fields->SetAttribute(CacheKey::ARIAAttributes, DeleteEntry()); } + + if (nsAccUtils::HasARIAAttr(Elm(), nsGkAtoms::aria_actions)) { + fields->SetAttribute(CacheKey::HasActions, true); + } else if (IsUpdatePush(CacheDomain::ARIA)) { + fields->SetAttribute(CacheKey::HasActions, DeleteEntry()); + } } if (aCacheDomain & CacheDomain::Relations && mContent) { diff --git a/accessible/ipc/RemoteAccessible.cpp b/accessible/ipc/RemoteAccessible.cpp @@ -1872,6 +1872,11 @@ already_AddRefed<AccAttributes> RemoteAccessible::Attributes() { if (!popupType.IsEmpty()) { attributes->SetAttribute(nsGkAtoms::ispopup, std::move(popupType)); } + + if (auto hasActions = + mCachedFields->GetAttribute<bool>(CacheKey::HasActions)) { + attributes->SetAttribute(nsGkAtoms::hasActions, *hasActions); + } } nsAutoString name; diff --git a/accessible/tests/browser/e10s/browser_caching_attributes.js b/accessible/tests/browser/e10s/browser_caching_attributes.js @@ -761,3 +761,68 @@ addAccessibleTask( }, { chrome: true, topLevel: true } ); + +/** + * Test has-actions attribute. + */ +addAccessibleTask( + `<dialog aria-actions="btn" id="dlg" open> + Hello + <button id="btn">Close</button> + <button id="btn-hidden" hidden>Pin</button> + </dialog>`, + async function testHasActionsAttribute(browser, docAcc) { + function getDlgHasActions() { + try { + return dlg.attributes.getStringProperty("has-actions"); + } catch (e) { + return null; + } + } + + const dlg = findAccessibleChildByID(docAcc, "dlg"); + is(getDlgHasActions(), "true", "dlg has-actions attribute is true"); + + // Removing the 'aria-actions' attribute from the element + // should remove the 'has-actions' attribute from the accessible. + let changed = waitForEvent(EVENT_OBJECT_ATTRIBUTE_CHANGED, "dlg"); + await invokeSetAttribute(browser, "dlg", "aria-actions"); + await changed; + await untilCacheIs( + getDlgHasActions, + null, + "dlg has-actions attribute removed" + ); + + // Setting the 'aria-actions' attribute to an empty string + // should make the 'has-actions' accessible attribute true. + changed = waitForEvent(EVENT_OBJECT_ATTRIBUTE_CHANGED, "dlg"); + await invokeSetAttribute(browser, "dlg", "aria-actions", ""); + await changed; + await untilCacheIs( + getDlgHasActions, + "true", + "dlg has-actions attribute re-added" + ); + + // Remove again to set up for next test + await invokeSetAttribute(browser, "dlg", "aria-actions"); + await untilCacheIs( + getDlgHasActions, + null, + "dlg has-actions attribute removed again" + ); + + // Setting the 'aria-actions' attribute to a hidden target + // should still make 'has-actions' true + changed = waitForEvent(EVENT_OBJECT_ATTRIBUTE_CHANGED, "dlg"); + await invokeSetAttribute(browser, "dlg", "aria-actions", "btn-hidden"); + await changed; + await untilCacheIs( + getDlgHasActions, + "true", + "dlg has-actions attribute re-added with hidden target" + ); + }, + { chrome: true, topLevel: true } +); diff --git a/xpcom/ds/StaticAtoms.py b/xpcom/ds/StaticAtoms.py @@ -516,6 +516,7 @@ STATIC_ATOMS = [ Atom("handler", "handler"), Atom("handlers", "handlers"), Atom("HARD", "HARD"), + Atom("hasActions", "has-actions"), Atom("hasSameNode", "has-same-node"), Atom("hbox", "hbox"), Atom("head", "head"),