tor-browser

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

commit bcb5d139ef65192084cfb932688a875e47cd91e7
parent ad2c5304febf56701dca0f972797eff6dfe5f606
Author: Eitan Isaacson <eitan@monotonous.org>
Date:   Mon,  3 Nov 2025 21:04:32 +0000

Bug 1990094 - Notify a11y cache of details relation changes caused by scroll. r=Jamie,jwatt

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

Diffstat:
Maccessible/base/nsAccessibilityService.cpp | 20++++++++++++++++++++
Maccessible/base/nsAccessibilityService.h | 8++++++++
Maccessible/tests/browser/relations/browser_anchor_positioning.js | 77+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mlayout/base/PresShell.cpp | 5+++++
4 files changed, 110 insertions(+), 0 deletions(-)

diff --git a/accessible/base/nsAccessibilityService.cpp b/accessible/base/nsAccessibilityService.cpp @@ -715,6 +715,26 @@ void nsAccessibilityService::NotifyAnchorRemoved(mozilla::PresShell* aPresShell, } } +void nsAccessibilityService::NotifyAnchorPositionedScrollUpdate( + mozilla::PresShell* aPresShell, nsIFrame* aFrame) { + DocAccessible* document = aPresShell->GetDocAccessible(); + if (!document) { + return; + } + + if (LocalAccessible* positionedAcc = + document->GetAccessible(aFrame->GetContent())) { + // Refresh relations before reflow to notify current anchor. + document->RefreshAnchorRelationCacheForTarget(positionedAcc); + + // Refresh relations after next tick when reflow updated to the + // new anchor state. + document->Controller()->ScheduleNotification<DocAccessible>( + document, &DocAccessible::RefreshAnchorRelationCacheForTarget, + positionedAcc); + } +} + void nsAccessibilityService::NotifyAttrElementWillChange( mozilla::dom::Element* aElement, nsAtom* aAttr) { mozilla::dom::Document* doc = aElement->OwnerDoc(); diff --git a/accessible/base/nsAccessibilityService.h b/accessible/base/nsAccessibilityService.h @@ -284,6 +284,14 @@ class nsAccessibilityService final : public mozilla::a11y::DocManager, void NotifyAnchorRemoved(mozilla::PresShell* aPresShell, nsIFrame* aFrame); /** + * Notify accessibility that an anchor positioned frame has + * been marked for reflow because of a scroll change for one of its + * anchors. A fallback anchor may be activated or deactivated. + */ + void NotifyAnchorPositionedScrollUpdate(mozilla::PresShell* aPresShell, + nsIFrame* aFrame); + + /** * Notify accessibility that an element explicitly set for an attribute is * about to change. See dom::Element::ExplicitlySetAttrElement. */ diff --git a/accessible/tests/browser/relations/browser_anchor_positioning.js b/accessible/tests/browser/relations/browser_anchor_positioning.js @@ -663,3 +663,80 @@ addAccessibleTask( }, { chrome: true, topLevel: true } ); + +addAccessibleTask( + ` +<style> +.anchor { + width: 50px; + height: 50px; + background: pink; +} + +.positioned { + width: 50px; + height: 50px; + background: purple; + position: absolute; + position-anchor: --a; + left: anchor(right); + bottom: anchor(bottom); + position-try-fallbacks: --fb; +} + +@position-try --fb { + position-anchor: --b; +} + +.abs-cb { + width: 200px; + height: 200px; + border: 1px solid; + position: relative; +} + +.scroller { + overflow: scroll; + width: 100%; + height: 100%; + border: 1px solid; + box-sizing: border-box; +} + +.filler { + height: 125px; + width: 1px; +} + +.dn { + display: none; +} +</style> +<div class="abs-cb"> + <div class="scroller"> + <div class="filler"></div> + <div class="anchor" id="anchor1" style="anchor-name: --a;" role="group"></div> + <div class="filler"></div> + <div class="anchor" id="anchor2" style="anchor-name: --b; background: aqua;" role="group"></div> + <div class="filler"></div> + </div> + <div id="target" class="positioned" role="group"></div> +</div>`, + async function testScrollWithFallback(browser, docAcc) { + const anchor1 = findAccessibleChildByID(docAcc, "anchor1"); + const anchor2 = findAccessibleChildByID(docAcc, "anchor2"); + const target = findAccessibleChildByID(docAcc, "target"); + + info("Correct details relation before scroll"); + await testDetailsRelations(anchor1, target); + + await invokeContentTaskAndTick(browser, [], () => { + let scroller = content.document.querySelector(".scroller"); + scroller.scrollTo(0, scroller.scrollTopMax); + }); + + info("Correct details relation after scroll"); + await testDetailsRelations(anchor2, target); + }, + { chrome: true, topLevel: true } +); diff --git a/layout/base/PresShell.cpp b/layout/base/PresShell.cpp @@ -11967,6 +11967,11 @@ void PresShell::UpdateAnchorPosForScroll( positioned->GetParent()->UpdateOverflow(); referenceData.mDefaultScrollShift = offset; if (CheckOverflow(positioned, offset, referenceData)) { +#ifdef ACCESSIBILITY + if (nsAccessibilityService* accService = GetAccService()) { + accService->NotifyAnchorPositionedScrollUpdate(this, positioned); + } +#endif FrameNeedsReflow(positioned, IntrinsicDirty::None, NS_FRAME_HAS_DIRTY_CHILDREN); }