Backed out changeset 62d0239c5141 (bug 1936381) for causing build bustages @updater...
[gecko.git] / accessible / html / HTMLElementAccessibles.cpp
blobf1adbde5f1a375834ee75b4f46bee0e2b2fac78e
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "HTMLElementAccessibles.h"
8 #include "CacheConstants.h"
9 #include "nsCoreUtils.h"
10 #include "nsTextEquivUtils.h"
11 #include "Relation.h"
12 #include "mozilla/a11y/Role.h"
13 #include "States.h"
15 #include "mozilla/dom/HTMLLabelElement.h"
16 #include "mozilla/dom/HTMLDetailsElement.h"
17 #include "mozilla/dom/HTMLSummaryElement.h"
19 using namespace mozilla::a11y;
21 ////////////////////////////////////////////////////////////////////////////////
22 // HTMLHRAccessible
23 ////////////////////////////////////////////////////////////////////////////////
25 role HTMLHRAccessible::NativeRole() const { return roles::SEPARATOR; }
27 ////////////////////////////////////////////////////////////////////////////////
28 // HTMLBRAccessible
29 ////////////////////////////////////////////////////////////////////////////////
31 role HTMLBRAccessible::NativeRole() const { return roles::WHITESPACE; }
33 uint64_t HTMLBRAccessible::NativeState() const { return states::READONLY; }
35 ENameValueFlag HTMLBRAccessible::NativeName(nsString& aName) const {
36 aName = static_cast<char16_t>('\n'); // Newline char
37 return eNameOK;
40 ////////////////////////////////////////////////////////////////////////////////
41 // HTMLLabelAccessible
42 ////////////////////////////////////////////////////////////////////////////////
44 ENameValueFlag HTMLLabelAccessible::NativeName(nsString& aName) const {
45 nsTextEquivUtils::GetNameFromSubtree(this, aName);
46 return aName.IsEmpty() ? eNameOK : eNameFromSubtree;
49 Relation HTMLLabelAccessible::RelationByType(RelationType aType) const {
50 Relation rel = AccessibleWrap::RelationByType(aType);
51 if (aType == RelationType::LABEL_FOR) {
52 dom::HTMLLabelElement* label = dom::HTMLLabelElement::FromNode(mContent);
53 rel.AppendTarget(mDoc, label->GetControl());
56 return rel;
59 void HTMLLabelAccessible::DOMAttributeChanged(int32_t aNameSpaceID,
60 nsAtom* aAttribute,
61 int32_t aModType,
62 const nsAttrValue* aOldValue,
63 uint64_t aOldState) {
64 HyperTextAccessible::DOMAttributeChanged(aNameSpaceID, aAttribute, aModType,
65 aOldValue, aOldState);
67 if (aAttribute == nsGkAtoms::_for) {
68 mDoc->QueueCacheUpdate(this, CacheDomain::Relations | CacheDomain::Actions);
72 bool HTMLLabelAccessible::HasPrimaryAction() const {
73 return nsCoreUtils::IsLabelWithControl(mContent);
76 void HTMLLabelAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName) {
77 if (aIndex == 0) {
78 if (HasPrimaryAction()) {
79 aName.AssignLiteral("click");
84 ////////////////////////////////////////////////////////////////////////////////
85 // nsHTMLOuputAccessible
86 ////////////////////////////////////////////////////////////////////////////////
88 Relation HTMLOutputAccessible::RelationByType(RelationType aType) const {
89 Relation rel = AccessibleWrap::RelationByType(aType);
90 if (aType == RelationType::CONTROLLED_BY) {
91 rel.AppendIter(
92 new AssociatedElementsIterator(mDoc, mContent, nsGkAtoms::_for));
95 return rel;
98 void HTMLOutputAccessible::DOMAttributeChanged(int32_t aNameSpaceID,
99 nsAtom* aAttribute,
100 int32_t aModType,
101 const nsAttrValue* aOldValue,
102 uint64_t aOldState) {
103 HyperTextAccessible::DOMAttributeChanged(aNameSpaceID, aAttribute, aModType,
104 aOldValue, aOldState);
106 if (aAttribute == nsGkAtoms::_for) {
107 mDoc->QueueCacheUpdate(this, CacheDomain::Relations);
111 ////////////////////////////////////////////////////////////////////////////////
112 // HTMLSummaryAccessible
113 ////////////////////////////////////////////////////////////////////////////////
115 HTMLSummaryAccessible::HTMLSummaryAccessible(nsIContent* aContent,
116 DocAccessible* aDoc)
117 : HyperTextAccessible(aContent, aDoc) {
118 mGenericTypes |= eButton;
121 bool HTMLSummaryAccessible::HasPrimaryAction() const { return true; }
123 void HTMLSummaryAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName) {
124 if (aIndex != eAction_Click) {
125 return;
128 dom::HTMLSummaryElement* summary =
129 dom::HTMLSummaryElement::FromNode(mContent);
130 if (!summary) {
131 return;
134 dom::HTMLDetailsElement* details = summary->GetDetails();
135 if (!details) {
136 return;
139 if (details->Open()) {
140 aName.AssignLiteral("collapse");
141 } else {
142 aName.AssignLiteral("expand");
146 uint64_t HTMLSummaryAccessible::NativeState() const {
147 uint64_t state = HyperTextAccessible::NativeState();
149 dom::HTMLSummaryElement* summary =
150 dom::HTMLSummaryElement::FromNode(mContent);
151 if (!summary) {
152 return state;
155 dom::HTMLDetailsElement* details = summary->GetDetails();
156 if (!details) {
157 return state;
160 if (details->Open()) {
161 state |= states::EXPANDED;
162 } else {
163 state |= states::COLLAPSED;
166 return state;
169 HTMLSummaryAccessible* HTMLSummaryAccessible::FromDetails(
170 LocalAccessible* details) {
171 if (!dom::HTMLDetailsElement::FromNodeOrNull(details->GetContent())) {
172 return nullptr;
175 HTMLSummaryAccessible* summaryAccessible = nullptr;
176 for (uint32_t i = 0; i < details->ChildCount(); i++) {
177 // Iterate through the children of our details accessible to locate main
178 // summary. This iteration includes the anonymous summary if the details
179 // element was not explicitly created with one.
180 LocalAccessible* child = details->LocalChildAt(i);
181 auto* summary =
182 mozilla::dom::HTMLSummaryElement::FromNodeOrNull(child->GetContent());
183 if (summary && summary->IsMainSummary()) {
184 summaryAccessible = static_cast<HTMLSummaryAccessible*>(child);
185 break;
189 return summaryAccessible;
192 ////////////////////////////////////////////////////////////////////////////////
193 // HTMLSummaryAccessible: Widgets
195 bool HTMLSummaryAccessible::IsWidget() const { return true; }
197 ////////////////////////////////////////////////////////////////////////////////
198 // HTMLHeaderOrFooterAccessible
199 ////////////////////////////////////////////////////////////////////////////////
201 role HTMLHeaderOrFooterAccessible::NativeRole() const {
202 // Only map header and footer if they are direct descendants of the body tag.
203 // If other sectioning or sectioning root elements, they become sections.
204 nsIContent* parent = mContent->GetParent();
205 while (parent) {
206 if (parent->IsAnyOfHTMLElements(
207 nsGkAtoms::article, nsGkAtoms::aside, nsGkAtoms::nav,
208 nsGkAtoms::section, nsGkAtoms::main, nsGkAtoms::blockquote,
209 nsGkAtoms::details, nsGkAtoms::dialog, nsGkAtoms::fieldset,
210 nsGkAtoms::figure, nsGkAtoms::td)) {
211 break;
213 parent = parent->GetParent();
216 // No sectioning or sectioning root elements found.
217 if (!parent) {
218 return roles::LANDMARK;
221 return roles::SECTION;
224 ////////////////////////////////////////////////////////////////////////////////
225 // HTMLAsideAccessible
226 ////////////////////////////////////////////////////////////////////////////////
228 role HTMLAsideAccessible::NativeRole() const {
229 // Per the HTML-AAM spec, there are two cases for aside elements:
230 // 1. scoped to body or main elements -> 'complementary' role
231 // 2. scoped to sectioning content elements
232 // -> if the element has an accessible name, 'complementary' role
233 // -> otherwise, 'generic' role
234 // To implement this, walk ancestors until we find a sectioning content
235 // element, or a body/main element, then take actions based on the rules
236 // above.
237 nsIContent* parent = mContent->GetParent();
238 while (parent) {
239 if (parent->IsAnyOfHTMLElements(nsGkAtoms::article, nsGkAtoms::aside,
240 nsGkAtoms::nav, nsGkAtoms::section)) {
241 return !NameIsEmpty() ? roles::LANDMARK : roles::SECTION;
243 if (parent->IsAnyOfHTMLElements(nsGkAtoms::main, nsGkAtoms::body)) {
244 return roles::LANDMARK;
246 parent = parent->GetParent();
249 // Fall back to landmark, though we always expect to find a body element.
250 return roles::LANDMARK;
253 ////////////////////////////////////////////////////////////////////////////////
254 // HTMLSectionAccessible
255 ////////////////////////////////////////////////////////////////////////////////
257 role HTMLSectionAccessible::NativeRole() const {
258 return NameIsEmpty() ? roles::SECTION : roles::REGION;