commit 2aa75940f5f8e8b2e2e3c90168abbe08f187ad1d
parent e1a20b25e5e4ff99a3c2d4e4dc8e6644ce481b67
Author: Eitan Isaacson <eitan@monotonous.org>
Date: Mon, 10 Nov 2025 22:23:36 +0000
Bug 1997461 - Introduce HTMLAbbreviationAccessible. r=morgan
Made a new subclass for abbr, this also removes a special case in
HyperTextAccessible, which is good.
Differential Revision: https://phabricator.services.mozilla.com/D270762
Diffstat:
8 files changed, 126 insertions(+), 19 deletions(-)
diff --git a/accessible/base/AccTypes.h b/accessible/base/AccTypes.h
@@ -19,6 +19,7 @@ enum AccType {
* alphabetical order since they are used in switch statement.
*/
eNoType,
+ eHTMLAbbrevType,
eHTMLBRType,
eHTMLButtonType,
eHTMLCanvasType,
diff --git a/accessible/base/HTMLMarkupMap.h b/accessible/base/HTMLMarkupMap.h
@@ -26,9 +26,19 @@ MARKUPMAP(
},
0)
-MARKUPMAP(abbr, New_HyperText, 0)
+MARKUPMAP(
+ abbr,
+ [](Element* aElement, LocalAccessible* aContext) -> LocalAccessible* {
+ return new HTMLAbbreviationAccessible(aElement, aContext->Document());
+ },
+ 0)
-MARKUPMAP(acronym, New_HyperText, 0)
+MARKUPMAP(
+ acronym,
+ [](Element* aElement, LocalAccessible* aContext) -> LocalAccessible* {
+ return new HTMLAbbreviationAccessible(aElement, aContext->Document());
+ },
+ 0)
MARKUPMAP(address, New_HyperText, roles::GROUPING)
diff --git a/accessible/basetypes/Accessible.h b/accessible/basetypes/Accessible.h
@@ -583,6 +583,8 @@ class Accessible {
// Type "is" methods
+ bool IsAbbreviation() const { return mType == eHTMLAbbrevType; }
+
bool IsDoc() const { return HasGenericType(eDocument); }
bool IsTableRow() const { return HasGenericType(eTableRow); }
diff --git a/accessible/generic/HyperTextAccessible.cpp b/accessible/generic/HyperTextAccessible.cpp
@@ -853,18 +853,7 @@ ENameValueFlag HyperTextAccessible::NativeName(nsString& aName) const {
if (!aName.IsEmpty()) return eNameOK;
}
- ENameValueFlag nameFlag = AccessibleWrap::NativeName(aName);
- if (!aName.IsEmpty()) return nameFlag;
-
- // Get name from title attribute for HTML abbr and acronym elements making it
- // a valid name from markup. Otherwise their name isn't picked up by recursive
- // name computation algorithm. See NS_OK_NAME_FROM_TOOLTIP.
- if (IsAbbreviation() && mContent->AsElement()->GetAttr(
- kNameSpaceID_None, nsGkAtoms::title, aName)) {
- aName.CompressWhitespace();
- }
-
- return eNameOK;
+ return AccessibleWrap::NativeName(aName);
}
void HyperTextAccessible::Shutdown() {
diff --git a/accessible/generic/LocalAccessible.h b/accessible/generic/LocalAccessible.h
@@ -450,11 +450,6 @@ class LocalAccessible : public nsISupports, public Accessible {
//////////////////////////////////////////////////////////////////////////////
// Downcasting and types
- inline bool IsAbbreviation() const {
- return mContent &&
- mContent->IsAnyOfHTMLElements(nsGkAtoms::abbr, nsGkAtoms::acronym);
- }
-
ApplicationAccessible* AsApplication();
DocAccessible* AsDoc();
diff --git a/accessible/html/HTMLElementAccessibles.cpp b/accessible/html/HTMLElementAccessibles.cpp
@@ -257,3 +257,39 @@ role HTMLAsideAccessible::NativeRole() const {
role HTMLSectionAccessible::NativeRole() const {
return NameIsEmpty() ? roles::SECTION : roles::REGION;
}
+
+////////////////////////////////////////////////////////////////////////////////
+// HTMLAbbreviationAccessible
+////////////////////////////////////////////////////////////////////////////////
+
+ENameValueFlag HTMLAbbreviationAccessible::NativeName(nsString& aName) const {
+ if (mContent->AsElement()->GetAttr(nsGkAtoms::title, aName)) {
+ // "title" tag takes priority
+ return eNameOK;
+ }
+
+ return HyperTextAccessible::NativeName(aName);
+}
+
+void HTMLAbbreviationAccessible::DOMAttributeChanged(
+ int32_t aNameSpaceID, nsAtom* aAttribute, AttrModType aModType,
+ const nsAttrValue* aOldValue, uint64_t aOldState) {
+ if (aAttribute == nsGkAtoms::title) {
+ nsAutoString name;
+ ARIAName(name);
+ if (name.IsEmpty()) {
+ mDoc->FireDelayedEvent(nsIAccessibleEvent::EVENT_NAME_CHANGE, this);
+ return;
+ }
+
+ if (!mContent->AsElement()->HasAttr(nsGkAtoms::aria_describedby)) {
+ mDoc->FireDelayedEvent(nsIAccessibleEvent::EVENT_DESCRIPTION_CHANGE,
+ this);
+ }
+
+ return;
+ }
+
+ HyperTextAccessible::DOMAttributeChanged(aNameSpaceID, aAttribute, aModType,
+ aOldValue, aOldState);
+}
diff --git a/accessible/html/HTMLElementAccessibles.h b/accessible/html/HTMLElementAccessibles.h
@@ -170,6 +170,31 @@ class HTMLSectionAccessible : public HyperTextAccessible {
virtual ~HTMLSectionAccessible() = default;
};
+/**
+ * Used for abbr and the deprecated acronym elements.
+ */
+class HTMLAbbreviationAccessible : public HyperTextAccessible {
+ public:
+ HTMLAbbreviationAccessible(nsIContent* aContent, DocAccessible* aDoc)
+ : HyperTextAccessible(aContent, aDoc) {
+ mType = eHTMLAbbrevType;
+ }
+
+ NS_INLINE_DECL_REFCOUNTING_INHERITED(HTMLAbbreviationAccessible,
+ HyperTextAccessible)
+
+ protected:
+ // LocalAccessible
+ virtual ENameValueFlag NativeName(nsString& aName) const override;
+
+ virtual void DOMAttributeChanged(int32_t aNameSpaceID, nsAtom* aAttribute,
+ AttrModType aModType,
+ const nsAttrValue* aOldValue,
+ uint64_t aOldState) override;
+
+ virtual ~HTMLAbbreviationAccessible() = default;
+};
+
} // namespace a11y
} // namespace mozilla
diff --git a/accessible/tests/browser/e10s/browser_caching_name.js b/accessible/tests/browser/e10s/browser_caching_name.js
@@ -593,3 +593,52 @@ addAccessibleTask(
},
{ chrome: true, topLevel: true }
);
+
+/**
+ * Test abbr name change notification
+ */
+addAccessibleTask(
+ `<abbr id="abbr" title="JavaScript Object Notation">JSON</abbr>
+ <abbr id="labelled-abbr" aria-label="YML"
+ title="Yet Another Markup Language">YAML</abbr>`,
+ async function testAbbrName(browser, docAcc) {
+ const abbr = findAccessibleChildByID(docAcc, "abbr");
+ testName(abbr, "JavaScript Object Notation");
+
+ let nameChanged = waitForEvent(EVENT_NAME_CHANGE, abbr);
+ invokeSetAttribute(browser, "abbr", "title", "Jason's Other Nettle");
+ await nameChanged;
+ testName(abbr, "Jason's Other Nettle");
+
+ nameChanged = waitForEvent(EVENT_NAME_CHANGE, abbr);
+ invokeSetAttribute(browser, "abbr", "title");
+ await nameChanged;
+ testName(abbr, null);
+
+ const labelledAbbr = findAccessibleChildByID(docAcc, "labelled-abbr");
+ testName(labelledAbbr, "YML");
+
+ let events = waitForEvents({
+ expected: [[EVENT_DESCRIPTION_CHANGE, labelledAbbr]],
+ unexpected: [[EVENT_NAME_CHANGE, labelledAbbr]],
+ });
+ invokeSetAttribute(
+ browser,
+ "labelled-abbr",
+ "title",
+ "Your Another Marker Lye"
+ );
+ await events;
+ is(labelledAbbr.description, "Your Another Marker Lye");
+
+ events = waitForEvents([
+ [EVENT_NAME_CHANGE, labelledAbbr],
+ // Bug 1997464 - When losing ARIA name, we lose the description that is used
+ // now for the name.
+ // [EVENT_DESCRIPTION_CHANGE, labelledAbbr],
+ ]);
+ invokeSetAttribute(browser, "labelled-abbr", "aria-label");
+ await events;
+ },
+ { chrome: true, topLevel: true }
+);