diff options
Diffstat (limited to 'accessible/generic/ARIAGridAccessible.cpp')
-rw-r--r-- | accessible/generic/ARIAGridAccessible.cpp | 702 |
1 files changed, 702 insertions, 0 deletions
diff --git a/accessible/generic/ARIAGridAccessible.cpp b/accessible/generic/ARIAGridAccessible.cpp new file mode 100644 index 000000000..48de9bbf0 --- /dev/null +++ b/accessible/generic/ARIAGridAccessible.cpp @@ -0,0 +1,702 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ARIAGridAccessible-inl.h" + +#include "Accessible-inl.h" +#include "AccIterator.h" +#include "nsAccUtils.h" +#include "Role.h" +#include "States.h" + +#include "nsIMutableArray.h" +#include "nsIPersistentProperties2.h" +#include "nsComponentManagerUtils.h" + +using namespace mozilla; +using namespace mozilla::a11y; + +//////////////////////////////////////////////////////////////////////////////// +// ARIAGridAccessible +//////////////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////////////// +// Constructor + +ARIAGridAccessible:: + ARIAGridAccessible(nsIContent* aContent, DocAccessible* aDoc) : + AccessibleWrap(aContent, aDoc) +{ +} + +NS_IMPL_ISUPPORTS_INHERITED0(ARIAGridAccessible, Accessible) + +//////////////////////////////////////////////////////////////////////////////// +// Table + +uint32_t +ARIAGridAccessible::ColCount() +{ + AccIterator rowIter(this, filters::GetRow); + Accessible* row = rowIter.Next(); + if (!row) + return 0; + + AccIterator cellIter(row, filters::GetCell); + Accessible* cell = nullptr; + + uint32_t colCount = 0; + while ((cell = cellIter.Next())) + colCount++; + + return colCount; +} + +uint32_t +ARIAGridAccessible::RowCount() +{ + uint32_t rowCount = 0; + AccIterator rowIter(this, filters::GetRow); + while (rowIter.Next()) + rowCount++; + + return rowCount; +} + +Accessible* +ARIAGridAccessible::CellAt(uint32_t aRowIndex, uint32_t aColumnIndex) +{ + Accessible* row = GetRowAt(aRowIndex); + if (!row) + return nullptr; + + return GetCellInRowAt(row, aColumnIndex); +} + +bool +ARIAGridAccessible::IsColSelected(uint32_t aColIdx) +{ + if (IsARIARole(nsGkAtoms::table)) + return false; + + AccIterator rowIter(this, filters::GetRow); + Accessible* row = rowIter.Next(); + if (!row) + return false; + + do { + if (!nsAccUtils::IsARIASelected(row)) { + Accessible* cell = GetCellInRowAt(row, aColIdx); + if (!cell || !nsAccUtils::IsARIASelected(cell)) + return false; + } + } while ((row = rowIter.Next())); + + return true; +} + +bool +ARIAGridAccessible::IsRowSelected(uint32_t aRowIdx) +{ + if (IsARIARole(nsGkAtoms::table)) + return false; + + Accessible* row = GetRowAt(aRowIdx); + if(!row) + return false; + + if (!nsAccUtils::IsARIASelected(row)) { + AccIterator cellIter(row, filters::GetCell); + Accessible* cell = nullptr; + while ((cell = cellIter.Next())) { + if (!nsAccUtils::IsARIASelected(cell)) + return false; + } + } + + return true; +} + +bool +ARIAGridAccessible::IsCellSelected(uint32_t aRowIdx, uint32_t aColIdx) +{ + if (IsARIARole(nsGkAtoms::table)) + return false; + + Accessible* row = GetRowAt(aRowIdx); + if(!row) + return false; + + if (!nsAccUtils::IsARIASelected(row)) { + Accessible* cell = GetCellInRowAt(row, aColIdx); + if (!cell || !nsAccUtils::IsARIASelected(cell)) + return false; + } + + return true; +} + +uint32_t +ARIAGridAccessible::SelectedCellCount() +{ + if (IsARIARole(nsGkAtoms::table)) + return 0; + + uint32_t count = 0, colCount = ColCount(); + + AccIterator rowIter(this, filters::GetRow); + Accessible* row = nullptr; + + while ((row = rowIter.Next())) { + if (nsAccUtils::IsARIASelected(row)) { + count += colCount; + continue; + } + + AccIterator cellIter(row, filters::GetCell); + Accessible* cell = nullptr; + + while ((cell = cellIter.Next())) { + if (nsAccUtils::IsARIASelected(cell)) + count++; + } + } + + return count; +} + +uint32_t +ARIAGridAccessible::SelectedColCount() +{ + if (IsARIARole(nsGkAtoms::table)) + return 0; + + uint32_t colCount = ColCount(); + if (!colCount) + return 0; + + AccIterator rowIter(this, filters::GetRow); + Accessible* row = rowIter.Next(); + if (!row) + return 0; + + nsTArray<bool> isColSelArray(colCount); + isColSelArray.AppendElements(colCount); + memset(isColSelArray.Elements(), true, colCount * sizeof(bool)); + + uint32_t selColCount = colCount; + do { + if (nsAccUtils::IsARIASelected(row)) + continue; + + AccIterator cellIter(row, filters::GetCell); + Accessible* cell = nullptr; + for (uint32_t colIdx = 0; + (cell = cellIter.Next()) && colIdx < colCount; colIdx++) + if (isColSelArray[colIdx] && !nsAccUtils::IsARIASelected(cell)) { + isColSelArray[colIdx] = false; + selColCount--; + } + } while ((row = rowIter.Next())); + + return selColCount; +} + +uint32_t +ARIAGridAccessible::SelectedRowCount() +{ + if (IsARIARole(nsGkAtoms::table)) + return 0; + + uint32_t count = 0; + + AccIterator rowIter(this, filters::GetRow); + Accessible* row = nullptr; + + while ((row = rowIter.Next())) { + if (nsAccUtils::IsARIASelected(row)) { + count++; + continue; + } + + AccIterator cellIter(row, filters::GetCell); + Accessible* cell = cellIter.Next(); + if (!cell) + continue; + + bool isRowSelected = true; + do { + if (!nsAccUtils::IsARIASelected(cell)) { + isRowSelected = false; + break; + } + } while ((cell = cellIter.Next())); + + if (isRowSelected) + count++; + } + + return count; +} + +void +ARIAGridAccessible::SelectedCells(nsTArray<Accessible*>* aCells) +{ + if (IsARIARole(nsGkAtoms::table)) + return; + + AccIterator rowIter(this, filters::GetRow); + + Accessible* row = nullptr; + while ((row = rowIter.Next())) { + AccIterator cellIter(row, filters::GetCell); + Accessible* cell = nullptr; + + if (nsAccUtils::IsARIASelected(row)) { + while ((cell = cellIter.Next())) + aCells->AppendElement(cell); + + continue; + } + + while ((cell = cellIter.Next())) { + if (nsAccUtils::IsARIASelected(cell)) + aCells->AppendElement(cell); + } + } +} + +void +ARIAGridAccessible::SelectedCellIndices(nsTArray<uint32_t>* aCells) +{ + if (IsARIARole(nsGkAtoms::table)) + return; + + uint32_t colCount = ColCount(); + + AccIterator rowIter(this, filters::GetRow); + Accessible* row = nullptr; + for (uint32_t rowIdx = 0; (row = rowIter.Next()); rowIdx++) { + if (nsAccUtils::IsARIASelected(row)) { + for (uint32_t colIdx = 0; colIdx < colCount; colIdx++) + aCells->AppendElement(rowIdx * colCount + colIdx); + + continue; + } + + AccIterator cellIter(row, filters::GetCell); + Accessible* cell = nullptr; + for (uint32_t colIdx = 0; (cell = cellIter.Next()); colIdx++) { + if (nsAccUtils::IsARIASelected(cell)) + aCells->AppendElement(rowIdx * colCount + colIdx); + } + } +} + +void +ARIAGridAccessible::SelectedColIndices(nsTArray<uint32_t>* aCols) +{ + if (IsARIARole(nsGkAtoms::table)) + return; + + uint32_t colCount = ColCount(); + if (!colCount) + return; + + AccIterator rowIter(this, filters::GetRow); + Accessible* row = rowIter.Next(); + if (!row) + return; + + nsTArray<bool> isColSelArray(colCount); + isColSelArray.AppendElements(colCount); + memset(isColSelArray.Elements(), true, colCount * sizeof(bool)); + + do { + if (nsAccUtils::IsARIASelected(row)) + continue; + + AccIterator cellIter(row, filters::GetCell); + Accessible* cell = nullptr; + for (uint32_t colIdx = 0; + (cell = cellIter.Next()) && colIdx < colCount; colIdx++) + if (isColSelArray[colIdx] && !nsAccUtils::IsARIASelected(cell)) { + isColSelArray[colIdx] = false; + } + } while ((row = rowIter.Next())); + + for (uint32_t colIdx = 0; colIdx < colCount; colIdx++) + if (isColSelArray[colIdx]) + aCols->AppendElement(colIdx); +} + +void +ARIAGridAccessible::SelectedRowIndices(nsTArray<uint32_t>* aRows) +{ + if (IsARIARole(nsGkAtoms::table)) + return; + + AccIterator rowIter(this, filters::GetRow); + Accessible* row = nullptr; + for (uint32_t rowIdx = 0; (row = rowIter.Next()); rowIdx++) { + if (nsAccUtils::IsARIASelected(row)) { + aRows->AppendElement(rowIdx); + continue; + } + + AccIterator cellIter(row, filters::GetCell); + Accessible* cell = cellIter.Next(); + if (!cell) + continue; + + bool isRowSelected = true; + do { + if (!nsAccUtils::IsARIASelected(cell)) { + isRowSelected = false; + break; + } + } while ((cell = cellIter.Next())); + + if (isRowSelected) + aRows->AppendElement(rowIdx); + } +} + +void +ARIAGridAccessible::SelectRow(uint32_t aRowIdx) +{ + if (IsARIARole(nsGkAtoms::table)) + return; + + AccIterator rowIter(this, filters::GetRow); + + Accessible* row = nullptr; + for (uint32_t rowIdx = 0; (row = rowIter.Next()); rowIdx++) { + DebugOnly<nsresult> rv = SetARIASelected(row, rowIdx == aRowIdx); + NS_ASSERTION(NS_SUCCEEDED(rv), "SetARIASelected() Shouldn't fail!"); + } +} + +void +ARIAGridAccessible::SelectCol(uint32_t aColIdx) +{ + if (IsARIARole(nsGkAtoms::table)) + return; + + AccIterator rowIter(this, filters::GetRow); + + Accessible* row = nullptr; + while ((row = rowIter.Next())) { + // Unselect all cells in the row. + DebugOnly<nsresult> rv = SetARIASelected(row, false); + NS_ASSERTION(NS_SUCCEEDED(rv), "SetARIASelected() Shouldn't fail!"); + + // Select cell at the column index. + Accessible* cell = GetCellInRowAt(row, aColIdx); + if (cell) + SetARIASelected(cell, true); + } +} + +void +ARIAGridAccessible::UnselectRow(uint32_t aRowIdx) +{ + if (IsARIARole(nsGkAtoms::table)) + return; + + Accessible* row = GetRowAt(aRowIdx); + if (row) + SetARIASelected(row, false); +} + +void +ARIAGridAccessible::UnselectCol(uint32_t aColIdx) +{ + if (IsARIARole(nsGkAtoms::table)) + return; + + AccIterator rowIter(this, filters::GetRow); + + Accessible* row = nullptr; + while ((row = rowIter.Next())) { + Accessible* cell = GetCellInRowAt(row, aColIdx); + if (cell) + SetARIASelected(cell, false); + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Protected + +Accessible* +ARIAGridAccessible::GetRowAt(int32_t aRow) +{ + int32_t rowIdx = aRow; + + AccIterator rowIter(this, filters::GetRow); + + Accessible* row = rowIter.Next(); + while (rowIdx != 0 && (row = rowIter.Next())) + rowIdx--; + + return row; +} + +Accessible* +ARIAGridAccessible::GetCellInRowAt(Accessible* aRow, int32_t aColumn) +{ + int32_t colIdx = aColumn; + + AccIterator cellIter(aRow, filters::GetCell); + Accessible* cell = cellIter.Next(); + while (colIdx != 0 && (cell = cellIter.Next())) + colIdx--; + + return cell; +} + +nsresult +ARIAGridAccessible::SetARIASelected(Accessible* aAccessible, + bool aIsSelected, bool aNotify) +{ + if (IsARIARole(nsGkAtoms::table)) + return NS_OK; + + nsIContent *content = aAccessible->GetContent(); + NS_ENSURE_STATE(content); + + nsresult rv = NS_OK; + if (aIsSelected) + rv = content->SetAttr(kNameSpaceID_None, nsGkAtoms::aria_selected, + NS_LITERAL_STRING("true"), aNotify); + else + rv = content->SetAttr(kNameSpaceID_None, nsGkAtoms::aria_selected, + NS_LITERAL_STRING("false"), aNotify); + + NS_ENSURE_SUCCESS(rv, rv); + + // No "smart" select/unselect for internal call. + if (!aNotify) + return NS_OK; + + // If row or cell accessible was selected then we're able to not bother about + // selection of its cells or its row because our algorithm is row oriented, + // i.e. we check selection on row firstly and then on cells. + if (aIsSelected) + return NS_OK; + + roles::Role role = aAccessible->Role(); + + // If the given accessible is row that was unselected then remove + // aria-selected from cell accessible. + if (role == roles::ROW) { + AccIterator cellIter(aAccessible, filters::GetCell); + Accessible* cell = nullptr; + + while ((cell = cellIter.Next())) { + rv = SetARIASelected(cell, false, false); + NS_ENSURE_SUCCESS(rv, rv); + } + return NS_OK; + } + + // If the given accessible is cell that was unselected and its row is selected + // then remove aria-selected from row and put aria-selected on + // siblings cells. + if (role == roles::GRID_CELL || role == roles::ROWHEADER || + role == roles::COLUMNHEADER) { + Accessible* row = aAccessible->Parent(); + + if (row && row->Role() == roles::ROW && + nsAccUtils::IsARIASelected(row)) { + rv = SetARIASelected(row, false, false); + NS_ENSURE_SUCCESS(rv, rv); + + AccIterator cellIter(row, filters::GetCell); + Accessible* cell = nullptr; + while ((cell = cellIter.Next())) { + if (cell != aAccessible) { + rv = SetARIASelected(cell, true, false); + NS_ENSURE_SUCCESS(rv, rv); + } + } + } + } + + return NS_OK; +} + + +//////////////////////////////////////////////////////////////////////////////// +// ARIARowAccessible +//////////////////////////////////////////////////////////////////////////////// + +ARIARowAccessible:: + ARIARowAccessible(nsIContent* aContent, DocAccessible* aDoc) : + AccessibleWrap(aContent, aDoc) +{ + mGenericTypes |= eTableRow; +} + +NS_IMPL_ISUPPORTS_INHERITED0(ARIARowAccessible, Accessible) + +GroupPos +ARIARowAccessible::GroupPosition() +{ + int32_t count = 0, index = 0; + Accessible* table = nsAccUtils::TableFor(this); + if (table && nsCoreUtils::GetUIntAttr(table->GetContent(), + nsGkAtoms::aria_rowcount, &count) && + nsCoreUtils::GetUIntAttr(mContent, nsGkAtoms::aria_rowindex, &index)) { + return GroupPos(0, index, count); + } + + return AccessibleWrap::GroupPosition(); +} + + +//////////////////////////////////////////////////////////////////////////////// +// ARIAGridCellAccessible +//////////////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////////////// +// Constructor + +ARIAGridCellAccessible:: + ARIAGridCellAccessible(nsIContent* aContent, DocAccessible* aDoc) : + HyperTextAccessibleWrap(aContent, aDoc) +{ + mGenericTypes |= eTableCell; +} + +NS_IMPL_ISUPPORTS_INHERITED0(ARIAGridCellAccessible, HyperTextAccessible) + +//////////////////////////////////////////////////////////////////////////////// +// TableCell + +TableAccessible* +ARIAGridCellAccessible::Table() const +{ + Accessible* table = nsAccUtils::TableFor(Row()); + return table ? table->AsTable() : nullptr; +} + +uint32_t +ARIAGridCellAccessible::ColIdx() const +{ + Accessible* row = Row(); + if (!row) + return 0; + + int32_t indexInRow = IndexInParent(); + uint32_t colIdx = 0; + for (int32_t idx = 0; idx < indexInRow; idx++) { + Accessible* cell = row->GetChildAt(idx); + roles::Role role = cell->Role(); + if (role == roles::CELL || role == roles::GRID_CELL || + role == roles::ROWHEADER || role == roles::COLUMNHEADER) + colIdx++; + } + + return colIdx; +} + +uint32_t +ARIAGridCellAccessible::RowIdx() const +{ + return RowIndexFor(Row()); +} + +bool +ARIAGridCellAccessible::Selected() +{ + Accessible* row = Row(); + if (!row) + return false; + + return nsAccUtils::IsARIASelected(row) || nsAccUtils::IsARIASelected(this); +} + +//////////////////////////////////////////////////////////////////////////////// +// Accessible + +void +ARIAGridCellAccessible::ApplyARIAState(uint64_t* aState) const +{ + HyperTextAccessibleWrap::ApplyARIAState(aState); + + // Return if the gridcell has aria-selected="true". + if (*aState & states::SELECTED) + return; + + // Check aria-selected="true" on the row. + Accessible* row = Parent(); + if (!row || row->Role() != roles::ROW) + return; + + nsIContent *rowContent = row->GetContent(); + if (nsAccUtils::HasDefinedARIAToken(rowContent, + nsGkAtoms::aria_selected) && + !rowContent->AttrValueIs(kNameSpaceID_None, + nsGkAtoms::aria_selected, + nsGkAtoms::_false, eCaseMatters)) + *aState |= states::SELECTABLE | states::SELECTED; +} + +already_AddRefed<nsIPersistentProperties> +ARIAGridCellAccessible::NativeAttributes() +{ + nsCOMPtr<nsIPersistentProperties> attributes = + HyperTextAccessibleWrap::NativeAttributes(); + + // Expose "table-cell-index" attribute. + Accessible* thisRow = Row(); + if (!thisRow) + return attributes.forget(); + + int32_t colIdx = 0, colCount = 0; + uint32_t childCount = thisRow->ChildCount(); + for (uint32_t childIdx = 0; childIdx < childCount; childIdx++) { + Accessible* child = thisRow->GetChildAt(childIdx); + if (child == this) + colIdx = colCount; + + roles::Role role = child->Role(); + if (role == roles::CELL || role == roles::GRID_CELL || + role == roles::ROWHEADER || role == roles::COLUMNHEADER) + colCount++; + } + + int32_t rowIdx = RowIndexFor(thisRow); + + nsAutoString stringIdx; + stringIdx.AppendInt(rowIdx * colCount + colIdx); + nsAccUtils::SetAccAttr(attributes, nsGkAtoms::tableCellIndex, stringIdx); + +#ifdef DEBUG + nsAutoString unused; + attributes->SetStringProperty(NS_LITERAL_CSTRING("cppclass"), + NS_LITERAL_STRING("ARIAGridCellAccessible"), + unused); +#endif + + return attributes.forget(); +} + +GroupPos +ARIAGridCellAccessible::GroupPosition() +{ + int32_t count = 0, index = 0; + TableAccessible* table = Table(); + if (table && nsCoreUtils::GetUIntAttr(table->AsAccessible()->GetContent(), + nsGkAtoms::aria_colcount, &count) && + nsCoreUtils::GetUIntAttr(mContent, nsGkAtoms::aria_colindex, &index)) { + return GroupPos(0, index, count); + } + + return GroupPos(); +} |