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 "ARIAGridAccessible-inl.h"
8 #include "LocalAccessible-inl.h"
9 #include "AccAttributes.h"
10 #include "AccIterator.h"
11 #include "nsAccUtils.h"
15 #include "mozilla/dom/Element.h"
16 #include "nsComponentManagerUtils.h"
18 using namespace mozilla
;
19 using namespace mozilla::a11y
;
21 ////////////////////////////////////////////////////////////////////////////////
23 ////////////////////////////////////////////////////////////////////////////////
25 ////////////////////////////////////////////////////////////////////////////////
28 ARIAGridAccessible::ARIAGridAccessible(nsIContent
* aContent
,
30 : HyperTextAccessibleWrap(aContent
, aDoc
) {
31 mGenericTypes
|= eTable
;
34 role
ARIAGridAccessible::NativeRole() const {
35 a11y::role r
= GetAccService()->MarkupRole(mContent
);
36 return r
!= roles::NOTHING
? r
: roles::TABLE
;
39 already_AddRefed
<AccAttributes
> ARIAGridAccessible::NativeAttributes() {
40 RefPtr
<AccAttributes
> attributes
= AccessibleWrap::NativeAttributes();
42 if (IsProbablyLayoutTable()) {
43 attributes
->SetAttribute(nsGkAtoms::layout_guess
, true);
46 return attributes
.forget();
49 ////////////////////////////////////////////////////////////////////////////////
52 uint32_t ARIAGridAccessible::ColCount() const {
53 AccIterator
rowIter(this, filters::GetRow
);
54 LocalAccessible
* row
= rowIter
.Next();
57 AccIterator
cellIter(row
, filters::GetCell
);
58 LocalAccessible
* cell
= nullptr;
60 uint32_t colCount
= 0;
61 while ((cell
= cellIter
.Next())) {
62 MOZ_ASSERT(cell
->IsTableCell(), "No table or grid cell!");
63 colCount
+= cell
->AsTableCell()->ColExtent();
69 uint32_t ARIAGridAccessible::RowCount() {
70 uint32_t rowCount
= 0;
71 AccIterator
rowIter(this, filters::GetRow
);
72 while (rowIter
.Next()) rowCount
++;
77 LocalAccessible
* ARIAGridAccessible::CellAt(uint32_t aRowIndex
,
78 uint32_t aColumnIndex
) {
79 LocalAccessible
* row
= RowAt(aRowIndex
);
80 if (!row
) return nullptr;
82 return CellInRowAt(row
, aColumnIndex
);
85 bool ARIAGridAccessible::IsColSelected(uint32_t aColIdx
) {
86 if (IsARIARole(nsGkAtoms::table
)) return false;
88 AccIterator
rowIter(this, filters::GetRow
);
89 LocalAccessible
* row
= rowIter
.Next();
90 if (!row
) return false;
93 if (!nsAccUtils::IsARIASelected(row
)) {
94 LocalAccessible
* cell
= CellInRowAt(row
, aColIdx
);
95 if (!cell
|| !nsAccUtils::IsARIASelected(cell
)) return false;
97 } while ((row
= rowIter
.Next()));
102 bool ARIAGridAccessible::IsRowSelected(uint32_t aRowIdx
) {
103 if (IsARIARole(nsGkAtoms::table
)) return false;
105 LocalAccessible
* row
= RowAt(aRowIdx
);
106 if (!row
) return false;
108 if (!nsAccUtils::IsARIASelected(row
)) {
109 AccIterator
cellIter(row
, filters::GetCell
);
110 LocalAccessible
* cell
= nullptr;
111 while ((cell
= cellIter
.Next())) {
112 if (!nsAccUtils::IsARIASelected(cell
)) return false;
119 bool ARIAGridAccessible::IsCellSelected(uint32_t aRowIdx
, uint32_t aColIdx
) {
120 if (IsARIARole(nsGkAtoms::table
)) return false;
122 LocalAccessible
* row
= RowAt(aRowIdx
);
123 if (!row
) return false;
125 if (!nsAccUtils::IsARIASelected(row
)) {
126 LocalAccessible
* cell
= CellInRowAt(row
, aColIdx
);
127 if (!cell
|| !nsAccUtils::IsARIASelected(cell
)) return false;
133 uint32_t ARIAGridAccessible::SelectedCellCount() {
134 if (IsARIARole(nsGkAtoms::table
)) return 0;
136 uint32_t count
= 0, colCount
= ColCount();
138 AccIterator
rowIter(this, filters::GetRow
);
139 LocalAccessible
* row
= nullptr;
141 while ((row
= rowIter
.Next())) {
142 if (nsAccUtils::IsARIASelected(row
)) {
147 AccIterator
cellIter(row
, filters::GetCell
);
148 LocalAccessible
* cell
= nullptr;
150 while ((cell
= cellIter
.Next())) {
151 if (nsAccUtils::IsARIASelected(cell
)) count
++;
158 uint32_t ARIAGridAccessible::SelectedColCount() {
159 if (IsARIARole(nsGkAtoms::table
)) return 0;
161 uint32_t colCount
= ColCount();
162 if (!colCount
) return 0;
164 AccIterator
rowIter(this, filters::GetRow
);
165 LocalAccessible
* row
= rowIter
.Next();
168 nsTArray
<bool> isColSelArray(colCount
);
169 isColSelArray
.AppendElements(colCount
);
170 memset(isColSelArray
.Elements(), true, colCount
* sizeof(bool));
172 uint32_t selColCount
= colCount
;
174 if (nsAccUtils::IsARIASelected(row
)) continue;
176 AccIterator
cellIter(row
, filters::GetCell
);
177 LocalAccessible
* cell
= nullptr;
178 for (uint32_t colIdx
= 0; (cell
= cellIter
.Next()) && colIdx
< colCount
;
180 if (isColSelArray
[colIdx
] && !nsAccUtils::IsARIASelected(cell
)) {
181 isColSelArray
[colIdx
] = false;
185 } while ((row
= rowIter
.Next()));
190 uint32_t ARIAGridAccessible::SelectedRowCount() {
191 if (IsARIARole(nsGkAtoms::table
)) return 0;
195 AccIterator
rowIter(this, filters::GetRow
);
196 LocalAccessible
* row
= nullptr;
198 while ((row
= rowIter
.Next())) {
199 if (nsAccUtils::IsARIASelected(row
)) {
204 AccIterator
cellIter(row
, filters::GetCell
);
205 LocalAccessible
* cell
= cellIter
.Next();
208 bool isRowSelected
= true;
210 if (!nsAccUtils::IsARIASelected(cell
)) {
211 isRowSelected
= false;
214 } while ((cell
= cellIter
.Next()));
216 if (isRowSelected
) count
++;
222 void ARIAGridAccessible::SelectedCells(nsTArray
<Accessible
*>* aCells
) {
223 if (IsARIARole(nsGkAtoms::table
)) return;
225 AccIterator
rowIter(this, filters::GetRow
);
227 LocalAccessible
* row
= nullptr;
228 while ((row
= rowIter
.Next())) {
229 AccIterator
cellIter(row
, filters::GetCell
);
230 LocalAccessible
* cell
= nullptr;
232 if (nsAccUtils::IsARIASelected(row
)) {
233 while ((cell
= cellIter
.Next())) aCells
->AppendElement(cell
);
238 while ((cell
= cellIter
.Next())) {
239 if (nsAccUtils::IsARIASelected(cell
)) aCells
->AppendElement(cell
);
244 void ARIAGridAccessible::SelectedCellIndices(nsTArray
<uint32_t>* aCells
) {
245 if (IsARIARole(nsGkAtoms::table
)) return;
247 uint32_t colCount
= ColCount();
249 AccIterator
rowIter(this, filters::GetRow
);
250 LocalAccessible
* row
= nullptr;
251 for (uint32_t rowIdx
= 0; (row
= rowIter
.Next()); rowIdx
++) {
252 if (nsAccUtils::IsARIASelected(row
)) {
253 for (uint32_t colIdx
= 0; colIdx
< colCount
; colIdx
++) {
254 aCells
->AppendElement(rowIdx
* colCount
+ colIdx
);
260 AccIterator
cellIter(row
, filters::GetCell
);
261 LocalAccessible
* cell
= nullptr;
262 for (uint32_t colIdx
= 0; (cell
= cellIter
.Next()); colIdx
++) {
263 if (nsAccUtils::IsARIASelected(cell
)) {
264 aCells
->AppendElement(rowIdx
* colCount
+ colIdx
);
270 void ARIAGridAccessible::SelectedColIndices(nsTArray
<uint32_t>* aCols
) {
271 if (IsARIARole(nsGkAtoms::table
)) return;
273 uint32_t colCount
= ColCount();
274 if (!colCount
) return;
276 AccIterator
rowIter(this, filters::GetRow
);
277 LocalAccessible
* row
= rowIter
.Next();
280 nsTArray
<bool> isColSelArray(colCount
);
281 isColSelArray
.AppendElements(colCount
);
282 memset(isColSelArray
.Elements(), true, colCount
* sizeof(bool));
285 if (nsAccUtils::IsARIASelected(row
)) continue;
287 AccIterator
cellIter(row
, filters::GetCell
);
288 LocalAccessible
* cell
= nullptr;
289 for (uint32_t colIdx
= 0; (cell
= cellIter
.Next()) && colIdx
< colCount
;
291 if (isColSelArray
[colIdx
] && !nsAccUtils::IsARIASelected(cell
)) {
292 isColSelArray
[colIdx
] = false;
295 } while ((row
= rowIter
.Next()));
297 for (uint32_t colIdx
= 0; colIdx
< colCount
; colIdx
++) {
298 if (isColSelArray
[colIdx
]) aCols
->AppendElement(colIdx
);
302 void ARIAGridAccessible::SelectedRowIndices(nsTArray
<uint32_t>* aRows
) {
303 if (IsARIARole(nsGkAtoms::table
)) return;
305 AccIterator
rowIter(this, filters::GetRow
);
306 LocalAccessible
* row
= nullptr;
307 for (uint32_t rowIdx
= 0; (row
= rowIter
.Next()); rowIdx
++) {
308 if (nsAccUtils::IsARIASelected(row
)) {
309 aRows
->AppendElement(rowIdx
);
313 AccIterator
cellIter(row
, filters::GetCell
);
314 LocalAccessible
* cell
= cellIter
.Next();
317 bool isRowSelected
= true;
319 if (!nsAccUtils::IsARIASelected(cell
)) {
320 isRowSelected
= false;
323 } while ((cell
= cellIter
.Next()));
325 if (isRowSelected
) aRows
->AppendElement(rowIdx
);
329 void ARIAGridAccessible::SelectRow(uint32_t aRowIdx
) {
330 if (IsARIARole(nsGkAtoms::table
)) return;
332 AccIterator
rowIter(this, filters::GetRow
);
334 LocalAccessible
* row
= nullptr;
335 for (uint32_t rowIdx
= 0; (row
= rowIter
.Next()); rowIdx
++) {
336 DebugOnly
<nsresult
> rv
= SetARIASelected(row
, rowIdx
== aRowIdx
);
337 NS_ASSERTION(NS_SUCCEEDED(rv
), "SetARIASelected() Shouldn't fail!");
341 void ARIAGridAccessible::SelectCol(uint32_t aColIdx
) {
342 if (IsARIARole(nsGkAtoms::table
)) return;
344 AccIterator
rowIter(this, filters::GetRow
);
346 LocalAccessible
* row
= nullptr;
347 while ((row
= rowIter
.Next())) {
348 // Unselect all cells in the row.
349 DebugOnly
<nsresult
> rv
= SetARIASelected(row
, false);
350 NS_ASSERTION(NS_SUCCEEDED(rv
), "SetARIASelected() Shouldn't fail!");
352 // Select cell at the column index.
353 LocalAccessible
* cell
= CellInRowAt(row
, aColIdx
);
354 if (cell
) SetARIASelected(cell
, true);
358 void ARIAGridAccessible::UnselectRow(uint32_t aRowIdx
) {
359 if (IsARIARole(nsGkAtoms::table
)) return;
361 LocalAccessible
* row
= RowAt(aRowIdx
);
362 if (row
) SetARIASelected(row
, false);
365 void ARIAGridAccessible::UnselectCol(uint32_t aColIdx
) {
366 if (IsARIARole(nsGkAtoms::table
)) return;
368 AccIterator
rowIter(this, filters::GetRow
);
370 LocalAccessible
* row
= nullptr;
371 while ((row
= rowIter
.Next())) {
372 LocalAccessible
* cell
= CellInRowAt(row
, aColIdx
);
373 if (cell
) SetARIASelected(cell
, false);
377 ////////////////////////////////////////////////////////////////////////////////
380 nsresult
ARIAGridAccessible::SetARIASelected(LocalAccessible
* aAccessible
,
381 bool aIsSelected
, bool aNotify
) {
382 if (IsARIARole(nsGkAtoms::table
)) return NS_OK
;
384 nsIContent
* content
= aAccessible
->GetContent();
385 NS_ENSURE_STATE(content
);
388 if (content
->IsElement()) {
390 rv
= content
->AsElement()->SetAttr(
391 kNameSpaceID_None
, nsGkAtoms::aria_selected
, u
"true"_ns
, aNotify
);
393 rv
= content
->AsElement()->SetAttr(
394 kNameSpaceID_None
, nsGkAtoms::aria_selected
, u
"false"_ns
, aNotify
);
398 NS_ENSURE_SUCCESS(rv
, rv
);
400 // No "smart" select/unselect for internal call.
401 if (!aNotify
) return NS_OK
;
403 // If row or cell accessible was selected then we're able to not bother about
404 // selection of its cells or its row because our algorithm is row oriented,
405 // i.e. we check selection on row firstly and then on cells.
406 if (aIsSelected
) return NS_OK
;
408 roles::Role role
= aAccessible
->Role();
410 // If the given accessible is row that was unselected then remove
411 // aria-selected from cell accessible.
412 if (role
== roles::ROW
) {
413 AccIterator
cellIter(aAccessible
, filters::GetCell
);
414 LocalAccessible
* cell
= nullptr;
416 while ((cell
= cellIter
.Next())) {
417 rv
= SetARIASelected(cell
, false, false);
418 NS_ENSURE_SUCCESS(rv
, rv
);
423 // If the given accessible is cell that was unselected and its row is selected
424 // then remove aria-selected from row and put aria-selected on
426 if (role
== roles::GRID_CELL
|| role
== roles::ROWHEADER
||
427 role
== roles::COLUMNHEADER
) {
428 LocalAccessible
* row
= aAccessible
->LocalParent();
430 if (row
&& row
->Role() == roles::ROW
&& nsAccUtils::IsARIASelected(row
)) {
431 rv
= SetARIASelected(row
, false, false);
432 NS_ENSURE_SUCCESS(rv
, rv
);
434 AccIterator
cellIter(row
, filters::GetCell
);
435 LocalAccessible
* cell
= nullptr;
436 while ((cell
= cellIter
.Next())) {
437 if (cell
!= aAccessible
) {
438 rv
= SetARIASelected(cell
, true, false);
439 NS_ENSURE_SUCCESS(rv
, rv
);
448 ////////////////////////////////////////////////////////////////////////////////
450 ////////////////////////////////////////////////////////////////////////////////
452 ARIARowAccessible::ARIARowAccessible(nsIContent
* aContent
, DocAccessible
* aDoc
)
453 : HyperTextAccessibleWrap(aContent
, aDoc
) {
454 mGenericTypes
|= eTableRow
;
457 role
ARIARowAccessible::NativeRole() const {
458 a11y::role r
= GetAccService()->MarkupRole(mContent
);
459 return r
!= roles::NOTHING
? r
: roles::ROW
;
462 GroupPos
ARIARowAccessible::GroupPosition() {
463 int32_t count
= 0, index
= 0;
464 LocalAccessible
* table
= nsAccUtils::TableFor(this);
466 if (nsCoreUtils::GetUIntAttr(table
->GetContent(), nsGkAtoms::aria_rowcount
,
468 nsCoreUtils::GetUIntAttr(mContent
, nsGkAtoms::aria_rowindex
, &index
)) {
469 return GroupPos(0, index
, count
);
472 // Deal with the special case here that tables and grids can have rows
473 // which are wrapped in generic text container elements. Exclude tree grids
474 // because these are dealt with elsewhere.
475 if (table
->Role() == roles::TABLE
) {
476 LocalAccessible
* row
= nullptr;
477 AccIterator
rowIter(table
, filters::GetRow
);
478 while ((row
= rowIter
.Next())) {
486 count
= table
->AsTable()->RowCount();
487 return GroupPos(0, index
, count
);
492 return AccessibleWrap::GroupPosition();
495 // LocalAccessible protected
496 ENameValueFlag
ARIARowAccessible::NativeName(nsString
& aName
) const {
497 // We want to calculate the name from content only if an ARIA role is
498 // present. ARIARowAccessible might also be used by tables with
499 // display:block; styling, in which case we do not want the name from
501 if (HasStrongARIARole()) {
502 return AccessibleWrap::NativeName(aName
);
508 ////////////////////////////////////////////////////////////////////////////////
509 // ARIAGridCellAccessible
510 ////////////////////////////////////////////////////////////////////////////////
512 ////////////////////////////////////////////////////////////////////////////////
515 ARIAGridCellAccessible::ARIAGridCellAccessible(nsIContent
* aContent
,
517 : HyperTextAccessibleWrap(aContent
, aDoc
) {
518 mGenericTypes
|= eTableCell
;
521 role
ARIAGridCellAccessible::NativeRole() const {
522 a11y::role r
= GetAccService()->MarkupRole(mContent
);
523 return r
!= roles::NOTHING
? r
: roles::CELL
;
526 ////////////////////////////////////////////////////////////////////////////////
529 TableAccessible
* ARIAGridCellAccessible::Table() const {
530 LocalAccessible
* table
= nsAccUtils::TableFor(Row());
531 return table
? table
->AsTable() : nullptr;
534 uint32_t ARIAGridCellAccessible::ColIdx() const {
535 LocalAccessible
* row
= Row();
538 int32_t indexInRow
= IndexInParent();
540 for (int32_t idx
= 0; idx
< indexInRow
; idx
++) {
541 LocalAccessible
* cell
= row
->LocalChildAt(idx
);
542 if (cell
->IsTableCell()) {
543 colIdx
+= cell
->AsTableCell()->ColExtent();
550 uint32_t ARIAGridCellAccessible::RowIdx() const { return RowIndexFor(Row()); }
552 bool ARIAGridCellAccessible::Selected() {
553 LocalAccessible
* row
= Row();
554 if (!row
) return false;
556 return nsAccUtils::IsARIASelected(row
) || nsAccUtils::IsARIASelected(this);
559 ////////////////////////////////////////////////////////////////////////////////
562 void ARIAGridCellAccessible::ApplyARIAState(uint64_t* aState
) const {
563 HyperTextAccessibleWrap::ApplyARIAState(aState
);
565 // Return if the gridcell has aria-selected="true".
566 if (*aState
& states::SELECTED
) return;
568 // Check aria-selected="true" on the row.
569 LocalAccessible
* row
= LocalParent();
570 if (!row
|| row
->Role() != roles::ROW
) return;
572 nsIContent
* rowContent
= row
->GetContent();
573 if (nsAccUtils::HasDefinedARIAToken(rowContent
, nsGkAtoms::aria_selected
) &&
574 !rowContent
->AsElement()->AttrValueIs(kNameSpaceID_None
,
575 nsGkAtoms::aria_selected
,
576 nsGkAtoms::_false
, eCaseMatters
)) {
577 *aState
|= states::SELECTABLE
| states::SELECTED
;
581 already_AddRefed
<AccAttributes
> ARIAGridCellAccessible::NativeAttributes() {
582 RefPtr
<AccAttributes
> attributes
=
583 HyperTextAccessibleWrap::NativeAttributes();
585 // Expose "table-cell-index" attribute.
586 LocalAccessible
* thisRow
= Row();
587 if (!thisRow
) return attributes
.forget();
589 int32_t rowIdx
= RowIndexFor(thisRow
);
590 if (rowIdx
== -1) { // error
591 return attributes
.forget();
594 int32_t colIdx
= 0, colCount
= 0;
595 uint32_t childCount
= thisRow
->ChildCount();
596 for (uint32_t childIdx
= 0; childIdx
< childCount
; childIdx
++) {
597 LocalAccessible
* child
= thisRow
->LocalChildAt(childIdx
);
598 if (child
== this) colIdx
= colCount
;
600 roles::Role role
= child
->Role();
601 if (role
== roles::CELL
|| role
== roles::GRID_CELL
||
602 role
== roles::ROWHEADER
|| role
== roles::COLUMNHEADER
) {
607 attributes
->SetAttribute(nsGkAtoms::tableCellIndex
,
608 rowIdx
* colCount
+ colIdx
);
611 RefPtr
<nsAtom
> cppClass
= NS_Atomize(u
"cppclass"_ns
);
612 attributes
->SetAttributeStringCopy(cppClass
, u
"ARIAGridCellAccessible"_ns
);
615 return attributes
.forget();