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 "ARIAGridAccessible-inl.h"
7
8 #include "Accessible-inl.h"
9 #include "AccIterator.h"
10 #include "nsAccUtils.h"
11 #include "Role.h"
12 #include "States.h"
13
14 #include "nsIMutableArray.h"
15 #include "nsIPersistentProperties2.h"
16 #include "nsComponentManagerUtils.h"
17
18 using namespace mozilla;
19 using namespace mozilla::a11y;
20
21 ////////////////////////////////////////////////////////////////////////////////
22 // ARIAGridAccessible
23 ////////////////////////////////////////////////////////////////////////////////
24
25
26 ////////////////////////////////////////////////////////////////////////////////
27 // Constructor
28
29 ARIAGridAccessible::
ARIAGridAccessible(nsIContent * aContent,DocAccessible * aDoc)30 ARIAGridAccessible(nsIContent* aContent, DocAccessible* aDoc) :
31 AccessibleWrap(aContent, aDoc)
32 {
33 }
34
NS_IMPL_ISUPPORTS_INHERITED0(ARIAGridAccessible,Accessible)35 NS_IMPL_ISUPPORTS_INHERITED0(ARIAGridAccessible, Accessible)
36
37 ////////////////////////////////////////////////////////////////////////////////
38 // Table
39
40 uint32_t
41 ARIAGridAccessible::ColCount()
42 {
43 AccIterator rowIter(this, filters::GetRow);
44 Accessible* row = rowIter.Next();
45 if (!row)
46 return 0;
47
48 AccIterator cellIter(row, filters::GetCell);
49 Accessible* cell = nullptr;
50
51 uint32_t colCount = 0;
52 while ((cell = cellIter.Next()))
53 colCount++;
54
55 return colCount;
56 }
57
58 uint32_t
RowCount()59 ARIAGridAccessible::RowCount()
60 {
61 uint32_t rowCount = 0;
62 AccIterator rowIter(this, filters::GetRow);
63 while (rowIter.Next())
64 rowCount++;
65
66 return rowCount;
67 }
68
69 Accessible*
CellAt(uint32_t aRowIndex,uint32_t aColumnIndex)70 ARIAGridAccessible::CellAt(uint32_t aRowIndex, uint32_t aColumnIndex)
71 {
72 Accessible* row = GetRowAt(aRowIndex);
73 if (!row)
74 return nullptr;
75
76 return GetCellInRowAt(row, aColumnIndex);
77 }
78
79 bool
IsColSelected(uint32_t aColIdx)80 ARIAGridAccessible::IsColSelected(uint32_t aColIdx)
81 {
82 if (IsARIARole(nsGkAtoms::table))
83 return false;
84
85 AccIterator rowIter(this, filters::GetRow);
86 Accessible* row = rowIter.Next();
87 if (!row)
88 return false;
89
90 do {
91 if (!nsAccUtils::IsARIASelected(row)) {
92 Accessible* cell = GetCellInRowAt(row, aColIdx);
93 if (!cell || !nsAccUtils::IsARIASelected(cell))
94 return false;
95 }
96 } while ((row = rowIter.Next()));
97
98 return true;
99 }
100
101 bool
IsRowSelected(uint32_t aRowIdx)102 ARIAGridAccessible::IsRowSelected(uint32_t aRowIdx)
103 {
104 if (IsARIARole(nsGkAtoms::table))
105 return false;
106
107 Accessible* row = GetRowAt(aRowIdx);
108 if(!row)
109 return false;
110
111 if (!nsAccUtils::IsARIASelected(row)) {
112 AccIterator cellIter(row, filters::GetCell);
113 Accessible* cell = nullptr;
114 while ((cell = cellIter.Next())) {
115 if (!nsAccUtils::IsARIASelected(cell))
116 return false;
117 }
118 }
119
120 return true;
121 }
122
123 bool
IsCellSelected(uint32_t aRowIdx,uint32_t aColIdx)124 ARIAGridAccessible::IsCellSelected(uint32_t aRowIdx, uint32_t aColIdx)
125 {
126 if (IsARIARole(nsGkAtoms::table))
127 return false;
128
129 Accessible* row = GetRowAt(aRowIdx);
130 if(!row)
131 return false;
132
133 if (!nsAccUtils::IsARIASelected(row)) {
134 Accessible* cell = GetCellInRowAt(row, aColIdx);
135 if (!cell || !nsAccUtils::IsARIASelected(cell))
136 return false;
137 }
138
139 return true;
140 }
141
142 uint32_t
SelectedCellCount()143 ARIAGridAccessible::SelectedCellCount()
144 {
145 if (IsARIARole(nsGkAtoms::table))
146 return 0;
147
148 uint32_t count = 0, colCount = ColCount();
149
150 AccIterator rowIter(this, filters::GetRow);
151 Accessible* row = nullptr;
152
153 while ((row = rowIter.Next())) {
154 if (nsAccUtils::IsARIASelected(row)) {
155 count += colCount;
156 continue;
157 }
158
159 AccIterator cellIter(row, filters::GetCell);
160 Accessible* cell = nullptr;
161
162 while ((cell = cellIter.Next())) {
163 if (nsAccUtils::IsARIASelected(cell))
164 count++;
165 }
166 }
167
168 return count;
169 }
170
171 uint32_t
SelectedColCount()172 ARIAGridAccessible::SelectedColCount()
173 {
174 if (IsARIARole(nsGkAtoms::table))
175 return 0;
176
177 uint32_t colCount = ColCount();
178 if (!colCount)
179 return 0;
180
181 AccIterator rowIter(this, filters::GetRow);
182 Accessible* row = rowIter.Next();
183 if (!row)
184 return 0;
185
186 nsTArray<bool> isColSelArray(colCount);
187 isColSelArray.AppendElements(colCount);
188 memset(isColSelArray.Elements(), true, colCount * sizeof(bool));
189
190 uint32_t selColCount = colCount;
191 do {
192 if (nsAccUtils::IsARIASelected(row))
193 continue;
194
195 AccIterator cellIter(row, filters::GetCell);
196 Accessible* cell = nullptr;
197 for (uint32_t colIdx = 0;
198 (cell = cellIter.Next()) && colIdx < colCount; colIdx++)
199 if (isColSelArray[colIdx] && !nsAccUtils::IsARIASelected(cell)) {
200 isColSelArray[colIdx] = false;
201 selColCount--;
202 }
203 } while ((row = rowIter.Next()));
204
205 return selColCount;
206 }
207
208 uint32_t
SelectedRowCount()209 ARIAGridAccessible::SelectedRowCount()
210 {
211 if (IsARIARole(nsGkAtoms::table))
212 return 0;
213
214 uint32_t count = 0;
215
216 AccIterator rowIter(this, filters::GetRow);
217 Accessible* row = nullptr;
218
219 while ((row = rowIter.Next())) {
220 if (nsAccUtils::IsARIASelected(row)) {
221 count++;
222 continue;
223 }
224
225 AccIterator cellIter(row, filters::GetCell);
226 Accessible* cell = cellIter.Next();
227 if (!cell)
228 continue;
229
230 bool isRowSelected = true;
231 do {
232 if (!nsAccUtils::IsARIASelected(cell)) {
233 isRowSelected = false;
234 break;
235 }
236 } while ((cell = cellIter.Next()));
237
238 if (isRowSelected)
239 count++;
240 }
241
242 return count;
243 }
244
245 void
SelectedCells(nsTArray<Accessible * > * aCells)246 ARIAGridAccessible::SelectedCells(nsTArray<Accessible*>* aCells)
247 {
248 if (IsARIARole(nsGkAtoms::table))
249 return;
250
251 AccIterator rowIter(this, filters::GetRow);
252
253 Accessible* row = nullptr;
254 while ((row = rowIter.Next())) {
255 AccIterator cellIter(row, filters::GetCell);
256 Accessible* cell = nullptr;
257
258 if (nsAccUtils::IsARIASelected(row)) {
259 while ((cell = cellIter.Next()))
260 aCells->AppendElement(cell);
261
262 continue;
263 }
264
265 while ((cell = cellIter.Next())) {
266 if (nsAccUtils::IsARIASelected(cell))
267 aCells->AppendElement(cell);
268 }
269 }
270 }
271
272 void
SelectedCellIndices(nsTArray<uint32_t> * aCells)273 ARIAGridAccessible::SelectedCellIndices(nsTArray<uint32_t>* aCells)
274 {
275 if (IsARIARole(nsGkAtoms::table))
276 return;
277
278 uint32_t colCount = ColCount();
279
280 AccIterator rowIter(this, filters::GetRow);
281 Accessible* row = nullptr;
282 for (uint32_t rowIdx = 0; (row = rowIter.Next()); rowIdx++) {
283 if (nsAccUtils::IsARIASelected(row)) {
284 for (uint32_t colIdx = 0; colIdx < colCount; colIdx++)
285 aCells->AppendElement(rowIdx * colCount + colIdx);
286
287 continue;
288 }
289
290 AccIterator cellIter(row, filters::GetCell);
291 Accessible* cell = nullptr;
292 for (uint32_t colIdx = 0; (cell = cellIter.Next()); colIdx++) {
293 if (nsAccUtils::IsARIASelected(cell))
294 aCells->AppendElement(rowIdx * colCount + colIdx);
295 }
296 }
297 }
298
299 void
SelectedColIndices(nsTArray<uint32_t> * aCols)300 ARIAGridAccessible::SelectedColIndices(nsTArray<uint32_t>* aCols)
301 {
302 if (IsARIARole(nsGkAtoms::table))
303 return;
304
305 uint32_t colCount = ColCount();
306 if (!colCount)
307 return;
308
309 AccIterator rowIter(this, filters::GetRow);
310 Accessible* row = rowIter.Next();
311 if (!row)
312 return;
313
314 nsTArray<bool> isColSelArray(colCount);
315 isColSelArray.AppendElements(colCount);
316 memset(isColSelArray.Elements(), true, colCount * sizeof(bool));
317
318 do {
319 if (nsAccUtils::IsARIASelected(row))
320 continue;
321
322 AccIterator cellIter(row, filters::GetCell);
323 Accessible* cell = nullptr;
324 for (uint32_t colIdx = 0;
325 (cell = cellIter.Next()) && colIdx < colCount; colIdx++)
326 if (isColSelArray[colIdx] && !nsAccUtils::IsARIASelected(cell)) {
327 isColSelArray[colIdx] = false;
328 }
329 } while ((row = rowIter.Next()));
330
331 for (uint32_t colIdx = 0; colIdx < colCount; colIdx++)
332 if (isColSelArray[colIdx])
333 aCols->AppendElement(colIdx);
334 }
335
336 void
SelectedRowIndices(nsTArray<uint32_t> * aRows)337 ARIAGridAccessible::SelectedRowIndices(nsTArray<uint32_t>* aRows)
338 {
339 if (IsARIARole(nsGkAtoms::table))
340 return;
341
342 AccIterator rowIter(this, filters::GetRow);
343 Accessible* row = nullptr;
344 for (uint32_t rowIdx = 0; (row = rowIter.Next()); rowIdx++) {
345 if (nsAccUtils::IsARIASelected(row)) {
346 aRows->AppendElement(rowIdx);
347 continue;
348 }
349
350 AccIterator cellIter(row, filters::GetCell);
351 Accessible* cell = cellIter.Next();
352 if (!cell)
353 continue;
354
355 bool isRowSelected = true;
356 do {
357 if (!nsAccUtils::IsARIASelected(cell)) {
358 isRowSelected = false;
359 break;
360 }
361 } while ((cell = cellIter.Next()));
362
363 if (isRowSelected)
364 aRows->AppendElement(rowIdx);
365 }
366 }
367
368 void
SelectRow(uint32_t aRowIdx)369 ARIAGridAccessible::SelectRow(uint32_t aRowIdx)
370 {
371 if (IsARIARole(nsGkAtoms::table))
372 return;
373
374 AccIterator rowIter(this, filters::GetRow);
375
376 Accessible* row = nullptr;
377 for (uint32_t rowIdx = 0; (row = rowIter.Next()); rowIdx++) {
378 DebugOnly<nsresult> rv = SetARIASelected(row, rowIdx == aRowIdx);
379 NS_ASSERTION(NS_SUCCEEDED(rv), "SetARIASelected() Shouldn't fail!");
380 }
381 }
382
383 void
SelectCol(uint32_t aColIdx)384 ARIAGridAccessible::SelectCol(uint32_t aColIdx)
385 {
386 if (IsARIARole(nsGkAtoms::table))
387 return;
388
389 AccIterator rowIter(this, filters::GetRow);
390
391 Accessible* row = nullptr;
392 while ((row = rowIter.Next())) {
393 // Unselect all cells in the row.
394 DebugOnly<nsresult> rv = SetARIASelected(row, false);
395 NS_ASSERTION(NS_SUCCEEDED(rv), "SetARIASelected() Shouldn't fail!");
396
397 // Select cell at the column index.
398 Accessible* cell = GetCellInRowAt(row, aColIdx);
399 if (cell)
400 SetARIASelected(cell, true);
401 }
402 }
403
404 void
UnselectRow(uint32_t aRowIdx)405 ARIAGridAccessible::UnselectRow(uint32_t aRowIdx)
406 {
407 if (IsARIARole(nsGkAtoms::table))
408 return;
409
410 Accessible* row = GetRowAt(aRowIdx);
411 if (row)
412 SetARIASelected(row, false);
413 }
414
415 void
UnselectCol(uint32_t aColIdx)416 ARIAGridAccessible::UnselectCol(uint32_t aColIdx)
417 {
418 if (IsARIARole(nsGkAtoms::table))
419 return;
420
421 AccIterator rowIter(this, filters::GetRow);
422
423 Accessible* row = nullptr;
424 while ((row = rowIter.Next())) {
425 Accessible* cell = GetCellInRowAt(row, aColIdx);
426 if (cell)
427 SetARIASelected(cell, false);
428 }
429 }
430
431 ////////////////////////////////////////////////////////////////////////////////
432 // Protected
433
434 Accessible*
GetRowAt(int32_t aRow)435 ARIAGridAccessible::GetRowAt(int32_t aRow)
436 {
437 int32_t rowIdx = aRow;
438
439 AccIterator rowIter(this, filters::GetRow);
440
441 Accessible* row = rowIter.Next();
442 while (rowIdx != 0 && (row = rowIter.Next()))
443 rowIdx--;
444
445 return row;
446 }
447
448 Accessible*
GetCellInRowAt(Accessible * aRow,int32_t aColumn)449 ARIAGridAccessible::GetCellInRowAt(Accessible* aRow, int32_t aColumn)
450 {
451 int32_t colIdx = aColumn;
452
453 AccIterator cellIter(aRow, filters::GetCell);
454 Accessible* cell = cellIter.Next();
455 while (colIdx != 0 && (cell = cellIter.Next()))
456 colIdx--;
457
458 return cell;
459 }
460
461 nsresult
SetARIASelected(Accessible * aAccessible,bool aIsSelected,bool aNotify)462 ARIAGridAccessible::SetARIASelected(Accessible* aAccessible,
463 bool aIsSelected, bool aNotify)
464 {
465 if (IsARIARole(nsGkAtoms::table))
466 return NS_OK;
467
468 nsIContent *content = aAccessible->GetContent();
469 NS_ENSURE_STATE(content);
470
471 nsresult rv = NS_OK;
472 if (aIsSelected)
473 rv = content->SetAttr(kNameSpaceID_None, nsGkAtoms::aria_selected,
474 NS_LITERAL_STRING("true"), aNotify);
475 else
476 rv = content->SetAttr(kNameSpaceID_None, nsGkAtoms::aria_selected,
477 NS_LITERAL_STRING("false"), aNotify);
478
479 NS_ENSURE_SUCCESS(rv, rv);
480
481 // No "smart" select/unselect for internal call.
482 if (!aNotify)
483 return NS_OK;
484
485 // If row or cell accessible was selected then we're able to not bother about
486 // selection of its cells or its row because our algorithm is row oriented,
487 // i.e. we check selection on row firstly and then on cells.
488 if (aIsSelected)
489 return NS_OK;
490
491 roles::Role role = aAccessible->Role();
492
493 // If the given accessible is row that was unselected then remove
494 // aria-selected from cell accessible.
495 if (role == roles::ROW) {
496 AccIterator cellIter(aAccessible, filters::GetCell);
497 Accessible* cell = nullptr;
498
499 while ((cell = cellIter.Next())) {
500 rv = SetARIASelected(cell, false, false);
501 NS_ENSURE_SUCCESS(rv, rv);
502 }
503 return NS_OK;
504 }
505
506 // If the given accessible is cell that was unselected and its row is selected
507 // then remove aria-selected from row and put aria-selected on
508 // siblings cells.
509 if (role == roles::GRID_CELL || role == roles::ROWHEADER ||
510 role == roles::COLUMNHEADER) {
511 Accessible* row = aAccessible->Parent();
512
513 if (row && row->Role() == roles::ROW &&
514 nsAccUtils::IsARIASelected(row)) {
515 rv = SetARIASelected(row, false, false);
516 NS_ENSURE_SUCCESS(rv, rv);
517
518 AccIterator cellIter(row, filters::GetCell);
519 Accessible* cell = nullptr;
520 while ((cell = cellIter.Next())) {
521 if (cell != aAccessible) {
522 rv = SetARIASelected(cell, true, false);
523 NS_ENSURE_SUCCESS(rv, rv);
524 }
525 }
526 }
527 }
528
529 return NS_OK;
530 }
531
532
533 ////////////////////////////////////////////////////////////////////////////////
534 // ARIARowAccessible
535 ////////////////////////////////////////////////////////////////////////////////
536
537 ARIARowAccessible::
ARIARowAccessible(nsIContent * aContent,DocAccessible * aDoc)538 ARIARowAccessible(nsIContent* aContent, DocAccessible* aDoc) :
539 AccessibleWrap(aContent, aDoc)
540 {
541 mGenericTypes |= eTableRow;
542 }
543
NS_IMPL_ISUPPORTS_INHERITED0(ARIARowAccessible,Accessible)544 NS_IMPL_ISUPPORTS_INHERITED0(ARIARowAccessible, Accessible)
545
546 GroupPos
547 ARIARowAccessible::GroupPosition()
548 {
549 int32_t count = 0, index = 0;
550 Accessible* table = nsAccUtils::TableFor(this);
551 if (table && nsCoreUtils::GetUIntAttr(table->GetContent(),
552 nsGkAtoms::aria_rowcount, &count) &&
553 nsCoreUtils::GetUIntAttr(mContent, nsGkAtoms::aria_rowindex, &index)) {
554 return GroupPos(0, index, count);
555 }
556
557 return AccessibleWrap::GroupPosition();
558 }
559
560
561 ////////////////////////////////////////////////////////////////////////////////
562 // ARIAGridCellAccessible
563 ////////////////////////////////////////////////////////////////////////////////
564
565
566 ////////////////////////////////////////////////////////////////////////////////
567 // Constructor
568
569 ARIAGridCellAccessible::
ARIAGridCellAccessible(nsIContent * aContent,DocAccessible * aDoc)570 ARIAGridCellAccessible(nsIContent* aContent, DocAccessible* aDoc) :
571 HyperTextAccessibleWrap(aContent, aDoc)
572 {
573 mGenericTypes |= eTableCell;
574 }
575
NS_IMPL_ISUPPORTS_INHERITED0(ARIAGridCellAccessible,HyperTextAccessible)576 NS_IMPL_ISUPPORTS_INHERITED0(ARIAGridCellAccessible, HyperTextAccessible)
577
578 ////////////////////////////////////////////////////////////////////////////////
579 // TableCell
580
581 TableAccessible*
582 ARIAGridCellAccessible::Table() const
583 {
584 Accessible* table = nsAccUtils::TableFor(Row());
585 return table ? table->AsTable() : nullptr;
586 }
587
588 uint32_t
ColIdx() const589 ARIAGridCellAccessible::ColIdx() const
590 {
591 Accessible* row = Row();
592 if (!row)
593 return 0;
594
595 int32_t indexInRow = IndexInParent();
596 uint32_t colIdx = 0;
597 for (int32_t idx = 0; idx < indexInRow; idx++) {
598 Accessible* cell = row->GetChildAt(idx);
599 roles::Role role = cell->Role();
600 if (role == roles::CELL || role == roles::GRID_CELL ||
601 role == roles::ROWHEADER || role == roles::COLUMNHEADER)
602 colIdx++;
603 }
604
605 return colIdx;
606 }
607
608 uint32_t
RowIdx() const609 ARIAGridCellAccessible::RowIdx() const
610 {
611 return RowIndexFor(Row());
612 }
613
614 bool
Selected()615 ARIAGridCellAccessible::Selected()
616 {
617 Accessible* row = Row();
618 if (!row)
619 return false;
620
621 return nsAccUtils::IsARIASelected(row) || nsAccUtils::IsARIASelected(this);
622 }
623
624 ////////////////////////////////////////////////////////////////////////////////
625 // Accessible
626
627 void
ApplyARIAState(uint64_t * aState) const628 ARIAGridCellAccessible::ApplyARIAState(uint64_t* aState) const
629 {
630 HyperTextAccessibleWrap::ApplyARIAState(aState);
631
632 // Return if the gridcell has aria-selected="true".
633 if (*aState & states::SELECTED)
634 return;
635
636 // Check aria-selected="true" on the row.
637 Accessible* row = Parent();
638 if (!row || row->Role() != roles::ROW)
639 return;
640
641 nsIContent *rowContent = row->GetContent();
642 if (nsAccUtils::HasDefinedARIAToken(rowContent,
643 nsGkAtoms::aria_selected) &&
644 !rowContent->AttrValueIs(kNameSpaceID_None,
645 nsGkAtoms::aria_selected,
646 nsGkAtoms::_false, eCaseMatters))
647 *aState |= states::SELECTABLE | states::SELECTED;
648 }
649
650 already_AddRefed<nsIPersistentProperties>
NativeAttributes()651 ARIAGridCellAccessible::NativeAttributes()
652 {
653 nsCOMPtr<nsIPersistentProperties> attributes =
654 HyperTextAccessibleWrap::NativeAttributes();
655
656 // Expose "table-cell-index" attribute.
657 Accessible* thisRow = Row();
658 if (!thisRow)
659 return attributes.forget();
660
661 int32_t colIdx = 0, colCount = 0;
662 uint32_t childCount = thisRow->ChildCount();
663 for (uint32_t childIdx = 0; childIdx < childCount; childIdx++) {
664 Accessible* child = thisRow->GetChildAt(childIdx);
665 if (child == this)
666 colIdx = colCount;
667
668 roles::Role role = child->Role();
669 if (role == roles::CELL || role == roles::GRID_CELL ||
670 role == roles::ROWHEADER || role == roles::COLUMNHEADER)
671 colCount++;
672 }
673
674 int32_t rowIdx = RowIndexFor(thisRow);
675
676 nsAutoString stringIdx;
677 stringIdx.AppendInt(rowIdx * colCount + colIdx);
678 nsAccUtils::SetAccAttr(attributes, nsGkAtoms::tableCellIndex, stringIdx);
679
680 #ifdef DEBUG
681 nsAutoString unused;
682 attributes->SetStringProperty(NS_LITERAL_CSTRING("cppclass"),
683 NS_LITERAL_STRING("ARIAGridCellAccessible"),
684 unused);
685 #endif
686
687 return attributes.forget();
688 }
689
690 GroupPos
GroupPosition()691 ARIAGridCellAccessible::GroupPosition()
692 {
693 int32_t count = 0, index = 0;
694 TableAccessible* table = Table();
695 if (table && nsCoreUtils::GetUIntAttr(table->AsAccessible()->GetContent(),
696 nsGkAtoms::aria_colcount, &count) &&
697 nsCoreUtils::GetUIntAttr(mContent, nsGkAtoms::aria_colindex, &index)) {
698 return GroupPos(0, index, count);
699 }
700
701 return GroupPos();
702 }
703