tor-browser

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

commit 809deced26bdf7864314601b2cf37d7362d3238b
parent f1caff7d2fc2d79be3e9c63f2be061297ea65fdf
Author: Eitan Isaacson <eitan@monotonous.org>
Date:   Tue,  4 Nov 2025 19:55:02 +0000

Bug 1992028 - Implement details-from attribute. r=Jamie

This allows ATs to distinguish the source of details relations.

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

Diffstat:
Maccessible/generic/LocalAccessible.cpp | 16++++++++++++++++
Maccessible/ipc/RemoteAccessible.cpp | 19+++++++++++++++++--
Maccessible/tests/browser/relations/browser_anchor_positioning.js | 7++++++-
Maccessible/tests/browser/relations/browser_popover_and_command.js | 24++++++++++++++++++++++++
Mdevtools/server/tests/browser/browser_accessibility_node.js | 1+
Mxpcom/ds/StaticAtoms.py | 1+
6 files changed, 65 insertions(+), 3 deletions(-)

diff --git a/accessible/generic/LocalAccessible.cpp b/accessible/generic/LocalAccessible.cpp @@ -1169,6 +1169,22 @@ already_AddRefed<AccAttributes> LocalAccessible::Attributes() { } } + nsString detailsFrom; + AssociatedElementsIterator iter(mDoc, Elm(), nsGkAtoms::aria_details); + if (iter.Next()) { + detailsFrom.AssignLiteral("aria-details"); + } else if (GetCommandForDetailsRelation()) { + detailsFrom.AssignLiteral("command-for"); + } else if (GetPopoverTargetDetailsRelation()) { + detailsFrom.AssignLiteral("popover-target"); + } else if (GetAnchorPositionTargetDetailsRelation()) { + detailsFrom.AssignLiteral("css-anchor"); + } + + if (!detailsFrom.IsEmpty()) { + attributes->SetAttribute(nsGkAtoms::details_from, std::move(detailsFrom)); + } + return attributes.forget(); } diff --git a/accessible/ipc/RemoteAccessible.cpp b/accessible/ipc/RemoteAccessible.cpp @@ -1752,8 +1752,8 @@ already_AddRefed<AccAttributes> RemoteAccessible::Attributes() { CacheDomain::State | // State CacheDomain::Viewport | // State CacheDomain::Table | // TableIsProbablyForLayout - CacheDomain::DOMNodeIDAndClass // DOMNodeID - )) { + CacheDomain::DOMNodeIDAndClass | // DOMNodeID + CacheDomain::Relations)) { return attributes.forget(); } @@ -1877,6 +1877,21 @@ already_AddRefed<AccAttributes> RemoteAccessible::Attributes() { mCachedFields->GetAttribute<bool>(CacheKey::HasActions)) { attributes->SetAttribute(nsGkAtoms::hasActions, *hasActions); } + + nsString detailsFrom; + if (mCachedFields->HasAttribute(nsGkAtoms::aria_details)) { + detailsFrom.AssignLiteral("aria-details"); + } else if (mCachedFields->HasAttribute(nsGkAtoms::commandfor)) { + detailsFrom.AssignLiteral("command-for"); + } else if (mCachedFields->HasAttribute(nsGkAtoms::popovertarget)) { + detailsFrom.AssignLiteral("popover-target"); + } else if (mCachedFields->HasAttribute(nsGkAtoms::target)) { + detailsFrom.AssignLiteral("css-anchor"); + } + + if (!detailsFrom.IsEmpty()) { + attributes->SetAttribute(nsGkAtoms::details_from, std::move(detailsFrom)); + } } nsAutoString name; diff --git a/accessible/tests/browser/relations/browser_anchor_positioning.js b/accessible/tests/browser/relations/browser_anchor_positioning.js @@ -76,6 +76,11 @@ addAccessibleTask( const target1 = findAccessibleChildByID(docAcc, "target1"); await testDetailsRelations(btn1, target1); + is( + btn1.attributes.getStringProperty("details-from"), + "css-anchor", + "Correct details-from attribute" + ); info("Make anchor invalid"); await invokeContentTaskAndTick(browser, [], () => { Object.assign(content.document.getElementById("btn1").style, { @@ -448,7 +453,7 @@ addAccessibleTask( <div id="target-targetsetdetails" aria-details="" class="target">World</div> <button id="btn-targetsetdetails">Hello</button> `, - async function testTooltipPositionAnchor(browser, docAcc) { + async function testOtherRelationsWithAnchor(browser, docAcc) { info("Test no details relations when explicit relations are set"); const btnDescribedby = findAccessibleChildByID(docAcc, "btn-describedby"); const targetDescribedby = findAccessibleChildByID( diff --git a/accessible/tests/browser/relations/browser_popover_and_command.js b/accessible/tests/browser/relations/browser_popover_and_command.js @@ -42,6 +42,12 @@ addAccessibleTask( await testCachedRelation(toggleSibling, RELATION_DETAILS, []); await testCachedRelation(popover, RELATION_DETAILS_FOR, toggle1); + is( + toggle1.attributes.getStringProperty("details-from"), + "popover-target", + "Correct details-from attribute" + ); + info("Setting toggle2 popovertarget"); await invokeSetAttribute(browser, "toggle2", "popovertarget", "popover"); await testCachedRelation(toggle2, RELATION_DETAILS, popover); @@ -134,10 +140,16 @@ addAccessibleTask( // of the popover. await testCachedRelation(toggleSibling, RELATION_DETAILS, []); await testCachedRelation(popover, RELATION_DETAILS_FOR, [toggle1, show]); + is( + toggle1.attributes.getStringProperty("details-from"), + "command-for", + "Correct details-from attribute" + ); info("Setting toggle2 commandfor"); await invokeSetAttribute(browser, "toggle2", "commandfor", "popover"); await testCachedRelation(toggle2, RELATION_DETAILS, popover); + await testCachedRelation(popover, RELATION_DETAILS_FOR, [ toggle1, show, @@ -154,6 +166,12 @@ addAccessibleTask( const details = findAccessibleChildByID(docAcc, "details"); // aria-details overrides popover. await testCachedRelation(toggle1, RELATION_DETAILS, details); + is( + toggle1.attributes.getStringProperty("details-from"), + "aria-details", + "Correct details-from attribute" + ); + await testCachedRelation(popover, RELATION_DETAILS_FOR, [show]); info("Removing aria-details from toggle1"); @@ -161,6 +179,12 @@ addAccessibleTask( await testCachedRelation(toggle1, RELATION_DETAILS, popover); await testCachedRelation(popover, RELATION_DETAILS_FOR, [toggle1, show]); + is( + toggle1.attributes.getStringProperty("details-from"), + "command-for", + "Correct details-from attribute" + ); + info("Hiding popover"); let hidden = waitForEvent(EVENT_HIDE, popover); toggle1.doAction(0); diff --git a/devtools/server/tests/browser/browser_accessibility_node.js b/devtools/server/tests/browser/browser_accessibility_node.js @@ -134,6 +134,7 @@ add_task(async function () { "margin-right": "0px", display: "inline-block", "explicit-name": "true", + "details-from": "aria-details", }, }); diff --git a/xpcom/ds/StaticAtoms.py b/xpcom/ds/StaticAtoms.py @@ -340,6 +340,7 @@ STATIC_ATOMS = [ Atom("description", "description"), Atom("destructor", "destructor"), Atom("details", "details"), + Atom("details_from", "details-from"), Atom("deviceAspectRatio", "device-aspect-ratio"), Atom("deviceHeight", "device-height"), Atom("devicePixelRatio", "device-pixel-ratio"),