tor-browser

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

commit e5e837d5fccfdf3d203fbe785b89556dbaab2236
parent 6cbbb9bc81e4e55a4a00101cc25cd44686f3255b
Author: Eitan Isaacson <eitan@monotonous.org>
Date:   Fri, 14 Nov 2025 04:53:26 +0000

Bug 1999436 - Don't cache relation atom if its not associated with a valid tag. r=morgan

This brings us back in to compliance with `label[for]` tags. Before this
patch we would erroneously be caching `for` attributes even though we
check the DOM for it being an HTML label with a control. This was
because we ended up caching the for attribute as-is via the `output[for]`
relation even though the element is a label and not an output.

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

Diffstat:
Maccessible/generic/LocalAccessible.cpp | 3+++
Maccessible/tests/browser/relations/browser_relations_general.js | 32++++++++++++++++++++++----------
Maccessible/tests/browser/relations/browser_relations_general_002.js | 82++++++++++++++++++++++++++++++++-----------------------------------------------
3 files changed, 58 insertions(+), 59 deletions(-)

diff --git a/accessible/generic/LocalAccessible.cpp b/accessible/generic/LocalAccessible.cpp @@ -4089,6 +4089,9 @@ already_AddRefed<AccAttributes> LocalAccessible::BundleFieldsForCache( for (auto const& data : kRelationTypeAtoms) { nsTArray<uint64_t> ids; nsStaticAtom* const relAtom = data.mAtom; + if (data.mValidTag && !mContent->IsHTMLElement(data.mValidTag)) { + continue; + } Relation rel; if (data.mType == RelationType::LABEL_FOR) { diff --git a/accessible/tests/browser/relations/browser_relations_general.js b/accessible/tests/browser/relations/browser_relations_general.js @@ -44,19 +44,31 @@ addAccessibleTask( */ addAccessibleTask( ` - <input type="checkbox" id="dependant1"> - <input type="checkbox" id="dependant2"> + <input type="checkbox" id="dependant"> <label id="host">label</label>`, async function (browser, accDoc) { - await testRelated( - browser, - accDoc, - "for", - RELATION_LABEL_FOR, - RELATION_LABELLED_BY - ); + const host = findAccessibleChildByID(accDoc, "host"); + const dependant = findAccessibleChildByID(accDoc, "dependant"); + async function testLabel(hasLabel) { + await testCachedRelation( + host, + RELATION_LABEL_FOR, + hasLabel ? dependant : [] + ); + await testCachedRelation( + dependant, + RELATION_LABELLED_BY, + hasLabel ? host : [] + ); + } + + await testLabel(false); + await invokeSetAttribute(browser, "host", "for", "dependant"); + await testLabel(true); + await invokeSetAttribute(browser, "dependant", "id", "invalid"); + await testLabel(false); }, - { iframe: true, remoteIframe: true } + { chrome: true, iframe: true, remoteIframe: true } ); /** diff --git a/accessible/tests/browser/relations/browser_relations_general_002.js b/accessible/tests/browser/relations/browser_relations_general_002.js @@ -89,44 +89,6 @@ addAccessibleTask( ); /* - * Test mutation of LABEL relations via accessible shutdown. - */ -addAccessibleTask( - ` - <div id="d"></div> - <label id="l"> - <select id="s"> - `, - async function (browser, accDoc) { - const label = findAccessibleChildByID(accDoc, "l"); - const select = findAccessibleChildByID(accDoc, "s"); - const div = findAccessibleChildByID(accDoc, "d"); - - await testCachedRelation(label, RELATION_LABEL_FOR, select); - await testCachedRelation(select, RELATION_LABELLED_BY, label); - await testCachedRelation(div, RELATION_LABELLED_BY, []); - - const r = waitForEvent(EVENT_REORDER, "l"); - await invokeContentTask(browser, [], () => { - content.document.getElementById("s").remove(); - }); - await r; - await invokeContentTask(browser, [], () => { - const l = content.document.getElementById("l"); - l.htmlFor = "d"; - }); - await testCachedRelation(label, RELATION_LABEL_FOR, div); - await testCachedRelation(div, RELATION_LABELLED_BY, label); - }, - { - chrome: false, - iframe: true, - remoteIframe: true, - topLevel: true, - } -); - -/* * Test mutation of LABEL relations via DOM ID reuse. */ addAccessibleTask( @@ -191,19 +153,23 @@ addAccessibleTask( */ addAccessibleTask( ` - <div id="d"></div> + <input id="d"></input> <label id="l"> <select id="s"> `, async function (browser, accDoc) { const label = findAccessibleChildByID(accDoc, "l"); const select = findAccessibleChildByID(accDoc, "s"); - const div = findAccessibleChildByID(accDoc, "d"); + const input = findAccessibleChildByID(accDoc, "d"); await testCachedRelation(label, RELATION_LABEL_FOR, select); await testCachedRelation(select, RELATION_LABELLED_BY, label); - await testCachedRelation(div, RELATION_LABELLED_BY, []); + await testCachedRelation(input, RELATION_LABELLED_BY, []); await untilCacheOk(() => { + if (!browser.isRemoteBrowser) { + return true; + } + try { // We should get an acc ID back from this, but we don't have a way of // verifying its correctness -- it should be the ID of the select. @@ -214,12 +180,16 @@ addAccessibleTask( } }, "Label for relation exists"); - const r = waitForEvent(EVENT_REORDER, "l"); + const r = waitForEvent(EVENT_INNER_REORDER, "l"); await invokeContentTask(browser, [], () => { content.document.getElementById("s").remove(); }); await r; await untilCacheOk(() => { + if (!browser.isRemoteBrowser) { + return true; + } + try { label.cache.getStringProperty("for"); } catch (e) { @@ -234,15 +204,11 @@ addAccessibleTask( const l = content.document.getElementById("l"); l.htmlFor = "d"; }); - await testCachedRelation(label, RELATION_LABEL_FOR, div); - await testCachedRelation(div, RELATION_LABELLED_BY, label); + await testCachedRelation(label, RELATION_LABEL_FOR, input); + await testCachedRelation(input, RELATION_LABELLED_BY, label); }, { - /** - * This functionality is broken in our LocalAcccessible implementation, - * so we avoid running this test in chrome or when the cache is off. - */ - chrome: false, + chrome: true, iframe: true, remoteIframe: true, topLevel: true, @@ -374,3 +340,21 @@ addAccessibleTask( }, { chrome: true, topLevel: true } ); + +/** + * Test elements that are not label by spec do not get LABEL relations + */ +addAccessibleTask( + ` + <label id="label" for="btn">label</label> + <div role="button" id="btn"></div> + `, + async function testLabelOnDiv(browser, docAcc) { + const btn = findAccessibleChildByID(docAcc, "btn"); + + const label = findAccessibleChildByID(docAcc, "label"); + await testCachedRelation(btn, RELATION_LABELLED_BY, []); + await testCachedRelation(label, RELATION_LABEL_FOR, []); + }, + { chrome: true, topLevel: true } +);