tor-browser

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

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:
Maccessible/base/AccTypes.h | 1+
Maccessible/base/HTMLMarkupMap.h | 14++++++++++++--
Maccessible/basetypes/Accessible.h | 2++
Maccessible/generic/HyperTextAccessible.cpp | 13+------------
Maccessible/generic/LocalAccessible.h | 5-----
Maccessible/html/HTMLElementAccessibles.cpp | 36++++++++++++++++++++++++++++++++++++
Maccessible/html/HTMLElementAccessibles.h | 25+++++++++++++++++++++++++
Maccessible/tests/browser/e10s/browser_caching_name.js | 49+++++++++++++++++++++++++++++++++++++++++++++++++
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 } +);