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/. */
5
6 #include "HTMLElementAccessibles.h"
7
8 #include "DocAccessible.h"
9 #include "nsAccUtils.h"
10 #include "nsTextEquivUtils.h"
11 #include "Relation.h"
12 #include "Role.h"
13 #include "States.h"
14
15 #include "mozilla/dom/HTMLLabelElement.h"
16 #include "mozilla/dom/HTMLDetailsElement.h"
17 #include "mozilla/dom/HTMLSummaryElement.h"
18
19 using namespace mozilla::a11y;
20
21 ////////////////////////////////////////////////////////////////////////////////
22 // HTMLHRAccessible
23 ////////////////////////////////////////////////////////////////////////////////
24
NativeRole() const25 role HTMLHRAccessible::NativeRole() const { return roles::SEPARATOR; }
26
27 ////////////////////////////////////////////////////////////////////////////////
28 // HTMLBRAccessible
29 ////////////////////////////////////////////////////////////////////////////////
30
NativeRole() const31 role HTMLBRAccessible::NativeRole() const { return roles::WHITESPACE; }
32
NativeState() const33 uint64_t HTMLBRAccessible::NativeState() const { return states::READONLY; }
34
NativeName(nsString & aName) const35 ENameValueFlag HTMLBRAccessible::NativeName(nsString& aName) const {
36 aName = static_cast<char16_t>('\n'); // Newline char
37 return eNameOK;
38 }
39
40 ////////////////////////////////////////////////////////////////////////////////
41 // HTMLLabelAccessible
42 ////////////////////////////////////////////////////////////////////////////////
43
NativeName(nsString & aName) const44 ENameValueFlag HTMLLabelAccessible::NativeName(nsString& aName) const {
45 nsTextEquivUtils::GetNameFromSubtree(this, aName);
46 return aName.IsEmpty() ? eNameOK : eNameFromSubtree;
47 }
48
RelationByType(RelationType aType) const49 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());
54 }
55
56 return rel;
57 }
58
ActionCount() const59 uint8_t HTMLLabelAccessible::ActionCount() const {
60 return nsCoreUtils::IsLabelWithControl(mContent) ? 1 : 0;
61 }
62
ActionNameAt(uint8_t aIndex,nsAString & aName)63 void HTMLLabelAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName) {
64 if (aIndex == 0) {
65 if (nsCoreUtils::IsLabelWithControl(mContent)) aName.AssignLiteral("click");
66 }
67 }
68
DoAction(uint8_t aIndex) const69 bool HTMLLabelAccessible::DoAction(uint8_t aIndex) const {
70 if (aIndex != 0) return false;
71
72 DoCommand();
73 return true;
74 }
75
76 ////////////////////////////////////////////////////////////////////////////////
77 // nsHTMLOuputAccessible
78 ////////////////////////////////////////////////////////////////////////////////
79
RelationByType(RelationType aType) const80 Relation HTMLOutputAccessible::RelationByType(RelationType aType) const {
81 Relation rel = AccessibleWrap::RelationByType(aType);
82 if (aType == RelationType::CONTROLLED_BY) {
83 rel.AppendIter(new IDRefsIterator(mDoc, mContent, nsGkAtoms::_for));
84 }
85
86 return rel;
87 }
88
89 ////////////////////////////////////////////////////////////////////////////////
90 // HTMLSummaryAccessible
91 ////////////////////////////////////////////////////////////////////////////////
92
HTMLSummaryAccessible(nsIContent * aContent,DocAccessible * aDoc)93 HTMLSummaryAccessible::HTMLSummaryAccessible(nsIContent* aContent,
94 DocAccessible* aDoc)
95 : HyperTextAccessibleWrap(aContent, aDoc) {
96 mGenericTypes |= eButton;
97 }
98
ActionCount() const99 uint8_t HTMLSummaryAccessible::ActionCount() const { return 1; }
100
ActionNameAt(uint8_t aIndex,nsAString & aName)101 void HTMLSummaryAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName) {
102 if (aIndex != eAction_Click) {
103 return;
104 }
105
106 dom::HTMLSummaryElement* summary =
107 dom::HTMLSummaryElement::FromNode(mContent);
108 if (!summary) {
109 return;
110 }
111
112 dom::HTMLDetailsElement* details = summary->GetDetails();
113 if (!details) {
114 return;
115 }
116
117 if (details->Open()) {
118 aName.AssignLiteral("collapse");
119 } else {
120 aName.AssignLiteral("expand");
121 }
122 }
123
DoAction(uint8_t aIndex) const124 bool HTMLSummaryAccessible::DoAction(uint8_t aIndex) const {
125 if (aIndex != eAction_Click) return false;
126
127 DoCommand();
128 return true;
129 }
130
NativeState() const131 uint64_t HTMLSummaryAccessible::NativeState() const {
132 uint64_t state = HyperTextAccessibleWrap::NativeState();
133
134 dom::HTMLSummaryElement* summary =
135 dom::HTMLSummaryElement::FromNode(mContent);
136 if (!summary) {
137 return state;
138 }
139
140 dom::HTMLDetailsElement* details = summary->GetDetails();
141 if (!details) {
142 return state;
143 }
144
145 if (details->Open()) {
146 state |= states::EXPANDED;
147 } else {
148 state |= states::COLLAPSED;
149 }
150
151 return state;
152 }
153
FromDetails(LocalAccessible * details)154 HTMLSummaryAccessible* HTMLSummaryAccessible::FromDetails(
155 LocalAccessible* details) {
156 if (!dom::HTMLDetailsElement::FromNodeOrNull(details->GetContent())) {
157 return nullptr;
158 }
159
160 HTMLSummaryAccessible* summaryAccessible = nullptr;
161 for (uint32_t i = 0; i < details->ChildCount(); i++) {
162 // Iterate through the children of our details accessible to locate main
163 // summary. This iteration includes the anonymous summary if the details
164 // element was not explicitly created with one.
165 LocalAccessible* child = details->LocalChildAt(i);
166 auto* summary =
167 mozilla::dom::HTMLSummaryElement::FromNodeOrNull(child->GetContent());
168 if (summary && summary->IsMainSummary()) {
169 summaryAccessible = static_cast<HTMLSummaryAccessible*>(child);
170 break;
171 }
172 }
173
174 return summaryAccessible;
175 }
176
177 ////////////////////////////////////////////////////////////////////////////////
178 // HTMLSummaryAccessible: Widgets
179
IsWidget() const180 bool HTMLSummaryAccessible::IsWidget() const { return true; }
181
182 ////////////////////////////////////////////////////////////////////////////////
183 // HTMLHeaderOrFooterAccessible
184 ////////////////////////////////////////////////////////////////////////////////
185
NativeRole() const186 role HTMLHeaderOrFooterAccessible::NativeRole() const {
187 // Only map header and footer if they are direct descendants of the body tag.
188 // If other sectioning or sectioning root elements, they become sections.
189 nsIContent* parent = mContent->GetParent();
190 while (parent) {
191 if (parent->IsAnyOfHTMLElements(
192 nsGkAtoms::article, nsGkAtoms::aside, nsGkAtoms::nav,
193 nsGkAtoms::section, nsGkAtoms::main, nsGkAtoms::blockquote,
194 nsGkAtoms::details, nsGkAtoms::dialog, nsGkAtoms::fieldset,
195 nsGkAtoms::figure, nsGkAtoms::td)) {
196 break;
197 }
198 parent = parent->GetParent();
199 }
200
201 // No sectioning or sectioning root elements found.
202 if (!parent) {
203 return roles::LANDMARK;
204 }
205
206 return roles::SECTION;
207 }
208
LandmarkRole() const209 nsAtom* HTMLHeaderOrFooterAccessible::LandmarkRole() const {
210 if (!HasOwnContent()) return nullptr;
211
212 a11y::role r = const_cast<HTMLHeaderOrFooterAccessible*>(this)->Role();
213 if (r == roles::LANDMARK) {
214 if (mContent->IsHTMLElement(nsGkAtoms::header)) {
215 return nsGkAtoms::banner;
216 }
217
218 if (mContent->IsHTMLElement(nsGkAtoms::footer)) {
219 return nsGkAtoms::contentinfo;
220 }
221 }
222
223 return HyperTextAccessibleWrap::LandmarkRole();
224 }
225
226 ////////////////////////////////////////////////////////////////////////////////
227 // HTMLSectionAccessible
228 ////////////////////////////////////////////////////////////////////////////////
229
NativeRole() const230 role HTMLSectionAccessible::NativeRole() const {
231 nsAutoString name;
232 const_cast<HTMLSectionAccessible*>(this)->Name(name);
233 return name.IsEmpty() ? roles::SECTION : roles::REGION;
234 }
235
LandmarkRole() const236 nsAtom* HTMLSectionAccessible::LandmarkRole() const {
237 if (!HasOwnContent()) {
238 return nullptr;
239 }
240
241 // Only return xml-roles "region" if the section has an accessible name.
242 nsAutoString name;
243 const_cast<HTMLSectionAccessible*>(this)->Name(name);
244 return name.IsEmpty() ? HyperTextAccessibleWrap::LandmarkRole()
245 : nsGkAtoms::region;
246 }
247