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:
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);
}