Wed, 31 Dec 2014 07:16:47 +0100
Revert simplistic fix pending revisit of Mozilla integration attempt.
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 "Accessible-inl.h"
9 #include "AccIterator.h"
10 #include "nsAccUtils.h"
11 #include "Role.h"
12 #include "States.h"
14 #include "nsIMutableArray.h"
15 #include "nsIPersistentProperties2.h"
16 #include "nsComponentManagerUtils.h"
18 using namespace mozilla;
19 using namespace mozilla::a11y;
21 ////////////////////////////////////////////////////////////////////////////////
22 // ARIAGridAccessible
23 ////////////////////////////////////////////////////////////////////////////////
26 ////////////////////////////////////////////////////////////////////////////////
27 // Constructor
29 ARIAGridAccessible::
30 ARIAGridAccessible(nsIContent* aContent, DocAccessible* aDoc) :
31 AccessibleWrap(aContent, aDoc), xpcAccessibleTable(this)
32 {
33 }
35 ////////////////////////////////////////////////////////////////////////////////
36 // nsISupports
38 NS_IMPL_ISUPPORTS_INHERITED(ARIAGridAccessible,
39 Accessible,
40 nsIAccessibleTable)
42 ////////////////////////////////////////////////////////////////////////////////
43 // Accessible
45 void
46 ARIAGridAccessible::Shutdown()
47 {
48 mTable = nullptr;
49 AccessibleWrap::Shutdown();
50 }
52 ////////////////////////////////////////////////////////////////////////////////
53 // nsIAccessibleTable
55 uint32_t
56 ARIAGridAccessible::ColCount()
57 {
58 AccIterator rowIter(this, filters::GetRow);
59 Accessible* row = rowIter.Next();
60 if (!row)
61 return 0;
63 AccIterator cellIter(row, filters::GetCell);
64 Accessible* cell = nullptr;
66 uint32_t colCount = 0;
67 while ((cell = cellIter.Next()))
68 colCount++;
70 return colCount;
71 }
73 uint32_t
74 ARIAGridAccessible::RowCount()
75 {
76 uint32_t rowCount = 0;
77 AccIterator rowIter(this, filters::GetRow);
78 while (rowIter.Next())
79 rowCount++;
81 return rowCount;
82 }
84 Accessible*
85 ARIAGridAccessible::CellAt(uint32_t aRowIndex, uint32_t aColumnIndex)
86 {
87 Accessible* row = GetRowAt(aRowIndex);
88 if (!row)
89 return nullptr;
91 return GetCellInRowAt(row, aColumnIndex);
92 }
94 bool
95 ARIAGridAccessible::IsColSelected(uint32_t aColIdx)
96 {
97 AccIterator rowIter(this, filters::GetRow);
98 Accessible* row = rowIter.Next();
99 if (!row)
100 return false;
102 do {
103 if (!nsAccUtils::IsARIASelected(row)) {
104 Accessible* cell = GetCellInRowAt(row, aColIdx);
105 if (!cell || !nsAccUtils::IsARIASelected(cell))
106 return false;
107 }
108 } while ((row = rowIter.Next()));
110 return true;
111 }
113 bool
114 ARIAGridAccessible::IsRowSelected(uint32_t aRowIdx)
115 {
116 Accessible* row = GetRowAt(aRowIdx);
117 if(!row)
118 return false;
120 if (!nsAccUtils::IsARIASelected(row)) {
121 AccIterator cellIter(row, filters::GetCell);
122 Accessible* cell = nullptr;
123 while ((cell = cellIter.Next())) {
124 if (!nsAccUtils::IsARIASelected(cell))
125 return false;
126 }
127 }
129 return true;
130 }
132 bool
133 ARIAGridAccessible::IsCellSelected(uint32_t aRowIdx, uint32_t aColIdx)
134 {
135 Accessible* row = GetRowAt(aRowIdx);
136 if(!row)
137 return false;
139 if (!nsAccUtils::IsARIASelected(row)) {
140 Accessible* cell = GetCellInRowAt(row, aColIdx);
141 if (!cell || !nsAccUtils::IsARIASelected(cell))
142 return false;
143 }
145 return true;
146 }
148 uint32_t
149 ARIAGridAccessible::SelectedCellCount()
150 {
151 uint32_t count = 0, colCount = ColCount();
153 AccIterator rowIter(this, filters::GetRow);
154 Accessible* row = nullptr;
156 while ((row = rowIter.Next())) {
157 if (nsAccUtils::IsARIASelected(row)) {
158 count += colCount;
159 continue;
160 }
162 AccIterator cellIter(row, filters::GetCell);
163 Accessible* cell = nullptr;
165 while ((cell = cellIter.Next())) {
166 if (nsAccUtils::IsARIASelected(cell))
167 count++;
168 }
169 }
171 return count;
172 }
174 uint32_t
175 ARIAGridAccessible::SelectedColCount()
176 {
177 uint32_t colCount = ColCount();
178 if (!colCount)
179 return 0;
181 AccIterator rowIter(this, filters::GetRow);
182 Accessible* row = rowIter.Next();
183 if (!row)
184 return 0;
186 nsTArray<bool> isColSelArray(colCount);
187 isColSelArray.AppendElements(colCount);
188 memset(isColSelArray.Elements(), true, colCount * sizeof(bool));
190 uint32_t selColCount = colCount;
191 do {
192 if (nsAccUtils::IsARIASelected(row))
193 continue;
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()));
205 return selColCount;
206 }
208 uint32_t
209 ARIAGridAccessible::SelectedRowCount()
210 {
211 uint32_t count = 0;
213 AccIterator rowIter(this, filters::GetRow);
214 Accessible* row = nullptr;
216 while ((row = rowIter.Next())) {
217 if (nsAccUtils::IsARIASelected(row)) {
218 count++;
219 continue;
220 }
222 AccIterator cellIter(row, filters::GetCell);
223 Accessible* cell = cellIter.Next();
224 if (!cell)
225 continue;
227 bool isRowSelected = true;
228 do {
229 if (!nsAccUtils::IsARIASelected(cell)) {
230 isRowSelected = false;
231 break;
232 }
233 } while ((cell = cellIter.Next()));
235 if (isRowSelected)
236 count++;
237 }
239 return count;
240 }
242 void
243 ARIAGridAccessible::SelectedCells(nsTArray<Accessible*>* aCells)
244 {
245 AccIterator rowIter(this, filters::GetRow);
247 Accessible* row = nullptr;
248 while ((row = rowIter.Next())) {
249 AccIterator cellIter(row, filters::GetCell);
250 Accessible* cell = nullptr;
252 if (nsAccUtils::IsARIASelected(row)) {
253 while ((cell = cellIter.Next()))
254 aCells->AppendElement(cell);
256 continue;
257 }
259 while ((cell = cellIter.Next())) {
260 if (nsAccUtils::IsARIASelected(cell))
261 aCells->AppendElement(cell);
262 }
263 }
264 }
266 void
267 ARIAGridAccessible::SelectedCellIndices(nsTArray<uint32_t>* aCells)
268 {
269 uint32_t colCount = ColCount();
271 AccIterator rowIter(this, filters::GetRow);
272 Accessible* row = nullptr;
273 for (uint32_t rowIdx = 0; (row = rowIter.Next()); rowIdx++) {
274 if (nsAccUtils::IsARIASelected(row)) {
275 for (uint32_t colIdx = 0; colIdx < colCount; colIdx++)
276 aCells->AppendElement(rowIdx * colCount + colIdx);
278 continue;
279 }
281 AccIterator cellIter(row, filters::GetCell);
282 Accessible* cell = nullptr;
283 for (uint32_t colIdx = 0; (cell = cellIter.Next()); colIdx++) {
284 if (nsAccUtils::IsARIASelected(cell))
285 aCells->AppendElement(rowIdx * colCount + colIdx);
286 }
287 }
288 }
290 void
291 ARIAGridAccessible::SelectedColIndices(nsTArray<uint32_t>* aCols)
292 {
293 uint32_t colCount = ColCount();
294 if (!colCount)
295 return;
297 AccIterator rowIter(this, filters::GetRow);
298 Accessible* row = rowIter.Next();
299 if (!row)
300 return;
302 nsTArray<bool> isColSelArray(colCount);
303 isColSelArray.AppendElements(colCount);
304 memset(isColSelArray.Elements(), true, colCount * sizeof(bool));
306 do {
307 if (nsAccUtils::IsARIASelected(row))
308 continue;
310 AccIterator cellIter(row, filters::GetCell);
311 Accessible* cell = nullptr;
312 for (uint32_t colIdx = 0;
313 (cell = cellIter.Next()) && colIdx < colCount; colIdx++)
314 if (isColSelArray[colIdx] && !nsAccUtils::IsARIASelected(cell)) {
315 isColSelArray[colIdx] = false;
316 }
317 } while ((row = rowIter.Next()));
319 for (uint32_t colIdx = 0; colIdx < colCount; colIdx++)
320 if (isColSelArray[colIdx])
321 aCols->AppendElement(colIdx);
322 }
324 void
325 ARIAGridAccessible::SelectedRowIndices(nsTArray<uint32_t>* aRows)
326 {
327 AccIterator rowIter(this, filters::GetRow);
328 Accessible* row = nullptr;
329 for (uint32_t rowIdx = 0; (row = rowIter.Next()); rowIdx++) {
330 if (nsAccUtils::IsARIASelected(row)) {
331 aRows->AppendElement(rowIdx);
332 continue;
333 }
335 AccIterator cellIter(row, filters::GetCell);
336 Accessible* cell = cellIter.Next();
337 if (!cell)
338 continue;
340 bool isRowSelected = true;
341 do {
342 if (!nsAccUtils::IsARIASelected(cell)) {
343 isRowSelected = false;
344 break;
345 }
346 } while ((cell = cellIter.Next()));
348 if (isRowSelected)
349 aRows->AppendElement(rowIdx);
350 }
351 }
353 void
354 ARIAGridAccessible::SelectRow(uint32_t aRowIdx)
355 {
356 AccIterator rowIter(this, filters::GetRow);
358 Accessible* row = nullptr;
359 for (uint32_t rowIdx = 0; (row = rowIter.Next()); rowIdx++) {
360 DebugOnly<nsresult> rv = SetARIASelected(row, rowIdx == aRowIdx);
361 NS_ASSERTION(NS_SUCCEEDED(rv), "SetARIASelected() Shouldn't fail!");
362 }
363 }
365 void
366 ARIAGridAccessible::SelectCol(uint32_t aColIdx)
367 {
368 AccIterator rowIter(this, filters::GetRow);
370 Accessible* row = nullptr;
371 while ((row = rowIter.Next())) {
372 // Unselect all cells in the row.
373 DebugOnly<nsresult> rv = SetARIASelected(row, false);
374 NS_ASSERTION(NS_SUCCEEDED(rv), "SetARIASelected() Shouldn't fail!");
376 // Select cell at the column index.
377 Accessible* cell = GetCellInRowAt(row, aColIdx);
378 if (cell)
379 SetARIASelected(cell, true);
380 }
381 }
383 void
384 ARIAGridAccessible::UnselectRow(uint32_t aRowIdx)
385 {
386 Accessible* row = GetRowAt(aRowIdx);
388 if (row)
389 SetARIASelected(row, false);
390 }
392 void
393 ARIAGridAccessible::UnselectCol(uint32_t aColIdx)
394 {
395 AccIterator rowIter(this, filters::GetRow);
397 Accessible* row = nullptr;
398 while ((row = rowIter.Next())) {
399 Accessible* cell = GetCellInRowAt(row, aColIdx);
400 if (cell)
401 SetARIASelected(cell, false);
402 }
403 }
405 ////////////////////////////////////////////////////////////////////////////////
406 // Protected
408 bool
409 ARIAGridAccessible::IsValidRow(int32_t aRow)
410 {
411 if (aRow < 0)
412 return false;
414 int32_t rowCount = 0;
415 GetRowCount(&rowCount);
416 return aRow < rowCount;
417 }
419 bool
420 ARIAGridAccessible::IsValidColumn(int32_t aColumn)
421 {
422 if (aColumn < 0)
423 return false;
425 int32_t colCount = 0;
426 GetColumnCount(&colCount);
427 return aColumn < colCount;
428 }
430 Accessible*
431 ARIAGridAccessible::GetRowAt(int32_t aRow)
432 {
433 int32_t rowIdx = aRow;
435 AccIterator rowIter(this, filters::GetRow);
437 Accessible* row = rowIter.Next();
438 while (rowIdx != 0 && (row = rowIter.Next()))
439 rowIdx--;
441 return row;
442 }
444 Accessible*
445 ARIAGridAccessible::GetCellInRowAt(Accessible* aRow, int32_t aColumn)
446 {
447 int32_t colIdx = aColumn;
449 AccIterator cellIter(aRow, filters::GetCell);
450 Accessible* cell = cellIter.Next();
451 while (colIdx != 0 && (cell = cellIter.Next()))
452 colIdx--;
454 return cell;
455 }
457 nsresult
458 ARIAGridAccessible::SetARIASelected(Accessible* aAccessible,
459 bool aIsSelected, bool aNotify)
460 {
461 nsIContent *content = aAccessible->GetContent();
462 NS_ENSURE_STATE(content);
464 nsresult rv = NS_OK;
465 if (aIsSelected)
466 rv = content->SetAttr(kNameSpaceID_None, nsGkAtoms::aria_selected,
467 NS_LITERAL_STRING("true"), aNotify);
468 else
469 rv = content->SetAttr(kNameSpaceID_None, nsGkAtoms::aria_selected,
470 NS_LITERAL_STRING("false"), aNotify);
472 NS_ENSURE_SUCCESS(rv, rv);
474 // No "smart" select/unselect for internal call.
475 if (!aNotify)
476 return NS_OK;
478 // If row or cell accessible was selected then we're able to not bother about
479 // selection of its cells or its row because our algorithm is row oriented,
480 // i.e. we check selection on row firstly and then on cells.
481 if (aIsSelected)
482 return NS_OK;
484 roles::Role role = aAccessible->Role();
486 // If the given accessible is row that was unselected then remove
487 // aria-selected from cell accessible.
488 if (role == roles::ROW) {
489 AccIterator cellIter(aAccessible, filters::GetCell);
490 Accessible* cell = nullptr;
492 while ((cell = cellIter.Next())) {
493 rv = SetARIASelected(cell, false, false);
494 NS_ENSURE_SUCCESS(rv, rv);
495 }
496 return NS_OK;
497 }
499 // If the given accessible is cell that was unselected and its row is selected
500 // then remove aria-selected from row and put aria-selected on
501 // siblings cells.
502 if (role == roles::GRID_CELL || role == roles::ROWHEADER ||
503 role == roles::COLUMNHEADER) {
504 Accessible* row = aAccessible->Parent();
506 if (row && row->Role() == roles::ROW &&
507 nsAccUtils::IsARIASelected(row)) {
508 rv = SetARIASelected(row, false, false);
509 NS_ENSURE_SUCCESS(rv, rv);
511 AccIterator cellIter(row, filters::GetCell);
512 Accessible* cell = nullptr;
513 while ((cell = cellIter.Next())) {
514 if (cell != aAccessible) {
515 rv = SetARIASelected(cell, true, false);
516 NS_ENSURE_SUCCESS(rv, rv);
517 }
518 }
519 }
520 }
522 return NS_OK;
523 }
525 ////////////////////////////////////////////////////////////////////////////////
526 // ARIAGridCellAccessible
527 ////////////////////////////////////////////////////////////////////////////////
530 ////////////////////////////////////////////////////////////////////////////////
531 // Constructor
533 ARIAGridCellAccessible::
534 ARIAGridCellAccessible(nsIContent* aContent, DocAccessible* aDoc) :
535 HyperTextAccessibleWrap(aContent, aDoc), xpcAccessibleTableCell(this)
536 {
537 mGenericTypes |= eTableCell;
538 }
540 ////////////////////////////////////////////////////////////////////////////////
541 // nsISupports
543 NS_IMPL_ISUPPORTS_INHERITED(ARIAGridCellAccessible,
544 HyperTextAccessible,
545 nsIAccessibleTableCell)
547 ////////////////////////////////////////////////////////////////////////////////
548 // nsIAccessibleTableCell
550 TableAccessible*
551 ARIAGridCellAccessible::Table() const
552 {
553 Accessible* table = TableFor(Row());
554 return table ? table->AsTable() : nullptr;
555 }
557 uint32_t
558 ARIAGridCellAccessible::ColIdx() const
559 {
560 Accessible* row = Row();
561 if (!row)
562 return 0;
564 int32_t indexInRow = IndexInParent();
565 uint32_t colIdx = 0;
566 for (int32_t idx = 0; idx < indexInRow; idx++) {
567 Accessible* cell = row->GetChildAt(idx);
568 roles::Role role = cell->Role();
569 if (role == roles::GRID_CELL || role == roles::ROWHEADER ||
570 role == roles::COLUMNHEADER)
571 colIdx++;
572 }
574 return colIdx;
575 }
577 uint32_t
578 ARIAGridCellAccessible::RowIdx() const
579 {
580 return RowIndexFor(Row());
581 }
583 bool
584 ARIAGridCellAccessible::Selected()
585 {
586 Accessible* row = Row();
587 if (!row)
588 return false;
590 return nsAccUtils::IsARIASelected(row) || nsAccUtils::IsARIASelected(this);
591 }
593 ////////////////////////////////////////////////////////////////////////////////
594 // Accessible
596 void
597 ARIAGridCellAccessible::ApplyARIAState(uint64_t* aState) const
598 {
599 HyperTextAccessibleWrap::ApplyARIAState(aState);
601 // Return if the gridcell has aria-selected="true".
602 if (*aState & states::SELECTED)
603 return;
605 // Check aria-selected="true" on the row.
606 Accessible* row = Parent();
607 if (!row || row->Role() != roles::ROW)
608 return;
610 nsIContent *rowContent = row->GetContent();
611 if (nsAccUtils::HasDefinedARIAToken(rowContent,
612 nsGkAtoms::aria_selected) &&
613 !rowContent->AttrValueIs(kNameSpaceID_None,
614 nsGkAtoms::aria_selected,
615 nsGkAtoms::_false, eCaseMatters))
616 *aState |= states::SELECTABLE | states::SELECTED;
617 }
619 already_AddRefed<nsIPersistentProperties>
620 ARIAGridCellAccessible::NativeAttributes()
621 {
622 nsCOMPtr<nsIPersistentProperties> attributes =
623 HyperTextAccessibleWrap::NativeAttributes();
625 // Expose "table-cell-index" attribute.
626 Accessible* thisRow = Row();
627 if (!thisRow)
628 return attributes.forget();
630 int32_t colIdx = 0, colCount = 0;
631 uint32_t childCount = thisRow->ChildCount();
632 for (uint32_t childIdx = 0; childIdx < childCount; childIdx++) {
633 Accessible* child = thisRow->GetChildAt(childIdx);
634 if (child == this)
635 colIdx = colCount;
637 roles::Role role = child->Role();
638 if (role == roles::GRID_CELL || role == roles::ROWHEADER ||
639 role == roles::COLUMNHEADER)
640 colCount++;
641 }
643 int32_t rowIdx = RowIndexFor(thisRow);
645 nsAutoString stringIdx;
646 stringIdx.AppendInt(rowIdx * colCount + colIdx);
647 nsAccUtils::SetAccAttr(attributes, nsGkAtoms::tableCellIndex, stringIdx);
649 return attributes.forget();
650 }
652 void
653 ARIAGridCellAccessible::Shutdown()
654 {
655 mTableCell = nullptr;
656 HyperTextAccessibleWrap::Shutdown();
657 }