summaryrefslogtreecommitdiffstats
path: root/accessible/generic/ARIAGridAccessible.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
commit43a97878ce14b72f0981164f87f2e35e14151312 (patch)
tree620249daf56c0258faa40cbdcf9cfba06de2a846 /accessible/generic/ARIAGridAccessible.cpp
parentInitial commit. (diff)
downloadfirefox-43a97878ce14b72f0981164f87f2e35e14151312.tar.xz
firefox-43a97878ce14b72f0981164f87f2e35e14151312.zip
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'accessible/generic/ARIAGridAccessible.cpp')
-rw-r--r--accessible/generic/ARIAGridAccessible.cpp625
1 files changed, 625 insertions, 0 deletions
diff --git a/accessible/generic/ARIAGridAccessible.cpp b/accessible/generic/ARIAGridAccessible.cpp
new file mode 100644
index 0000000000..399370835a
--- /dev/null
+++ b/accessible/generic/ARIAGridAccessible.cpp
@@ -0,0 +1,625 @@
+/* -*- 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 "LocalAccessible-inl.h"
+#include "AccAttributes.h"
+#include "AccIterator.h"
+#include "nsAccUtils.h"
+#include "Role.h"
+#include "States.h"
+
+#include "mozilla/dom/Element.h"
+#include "nsComponentManagerUtils.h"
+
+using namespace mozilla;
+using namespace mozilla::a11y;
+
+////////////////////////////////////////////////////////////////////////////////
+// ARIAGridAccessible
+////////////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////////////
+// Constructor
+
+ARIAGridAccessible::ARIAGridAccessible(nsIContent* aContent,
+ DocAccessible* aDoc)
+ : HyperTextAccessibleWrap(aContent, aDoc) {
+ mGenericTypes |= eTable;
+}
+
+role ARIAGridAccessible::NativeRole() const {
+ a11y::role r = GetAccService()->MarkupRole(mContent);
+ return r != roles::NOTHING ? r : roles::TABLE;
+}
+
+already_AddRefed<AccAttributes> ARIAGridAccessible::NativeAttributes() {
+ RefPtr<AccAttributes> attributes = AccessibleWrap::NativeAttributes();
+
+ if (IsProbablyLayoutTable()) {
+ attributes->SetAttribute(nsGkAtoms::layout_guess, true);
+ }
+
+ return attributes.forget();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Table
+
+uint32_t ARIAGridAccessible::ColCount() const {
+ AccIterator rowIter(this, filters::GetRow);
+ LocalAccessible* row = rowIter.Next();
+ if (!row) return 0;
+
+ AccIterator cellIter(row, filters::GetCell);
+ LocalAccessible* cell = nullptr;
+
+ uint32_t colCount = 0;
+ while ((cell = cellIter.Next())) {
+ MOZ_ASSERT(cell->IsTableCell(), "No table or grid cell!");
+ colCount += cell->AsTableCell()->ColExtent();
+ }
+
+ return colCount;
+}
+
+uint32_t ARIAGridAccessible::RowCount() {
+ uint32_t rowCount = 0;
+ AccIterator rowIter(this, filters::GetRow);
+ while (rowIter.Next()) rowCount++;
+
+ return rowCount;
+}
+
+LocalAccessible* ARIAGridAccessible::CellAt(uint32_t aRowIndex,
+ uint32_t aColumnIndex) {
+ LocalAccessible* row = RowAt(aRowIndex);
+ if (!row) return nullptr;
+
+ return CellInRowAt(row, aColumnIndex);
+}
+
+bool ARIAGridAccessible::IsColSelected(uint32_t aColIdx) {
+ if (IsARIARole(nsGkAtoms::table)) return false;
+
+ AccIterator rowIter(this, filters::GetRow);
+ LocalAccessible* row = rowIter.Next();
+ if (!row) return false;
+
+ do {
+ if (!nsAccUtils::IsARIASelected(row)) {
+ LocalAccessible* cell = CellInRowAt(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;
+
+ LocalAccessible* row = RowAt(aRowIdx);
+ if (!row) return false;
+
+ if (!nsAccUtils::IsARIASelected(row)) {
+ AccIterator cellIter(row, filters::GetCell);
+ LocalAccessible* 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;
+
+ LocalAccessible* row = RowAt(aRowIdx);
+ if (!row) return false;
+
+ if (!nsAccUtils::IsARIASelected(row)) {
+ LocalAccessible* cell = CellInRowAt(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);
+ LocalAccessible* row = nullptr;
+
+ while ((row = rowIter.Next())) {
+ if (nsAccUtils::IsARIASelected(row)) {
+ count += colCount;
+ continue;
+ }
+
+ AccIterator cellIter(row, filters::GetCell);
+ LocalAccessible* 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);
+ LocalAccessible* 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);
+ LocalAccessible* 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);
+ LocalAccessible* row = nullptr;
+
+ while ((row = rowIter.Next())) {
+ if (nsAccUtils::IsARIASelected(row)) {
+ count++;
+ continue;
+ }
+
+ AccIterator cellIter(row, filters::GetCell);
+ LocalAccessible* 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);
+
+ LocalAccessible* row = nullptr;
+ while ((row = rowIter.Next())) {
+ AccIterator cellIter(row, filters::GetCell);
+ LocalAccessible* 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);
+ LocalAccessible* 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);
+ LocalAccessible* 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);
+ LocalAccessible* 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);
+ LocalAccessible* 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);
+ LocalAccessible* row = nullptr;
+ for (uint32_t rowIdx = 0; (row = rowIter.Next()); rowIdx++) {
+ if (nsAccUtils::IsARIASelected(row)) {
+ aRows->AppendElement(rowIdx);
+ continue;
+ }
+
+ AccIterator cellIter(row, filters::GetCell);
+ LocalAccessible* 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);
+
+ LocalAccessible* 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);
+
+ LocalAccessible* 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.
+ LocalAccessible* cell = CellInRowAt(row, aColIdx);
+ if (cell) SetARIASelected(cell, true);
+ }
+}
+
+void ARIAGridAccessible::UnselectRow(uint32_t aRowIdx) {
+ if (IsARIARole(nsGkAtoms::table)) return;
+
+ LocalAccessible* row = RowAt(aRowIdx);
+ if (row) SetARIASelected(row, false);
+}
+
+void ARIAGridAccessible::UnselectCol(uint32_t aColIdx) {
+ if (IsARIARole(nsGkAtoms::table)) return;
+
+ AccIterator rowIter(this, filters::GetRow);
+
+ LocalAccessible* row = nullptr;
+ while ((row = rowIter.Next())) {
+ LocalAccessible* cell = CellInRowAt(row, aColIdx);
+ if (cell) SetARIASelected(cell, false);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Protected
+
+nsresult ARIAGridAccessible::SetARIASelected(LocalAccessible* 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 (content->IsElement()) {
+ if (aIsSelected) {
+ rv = content->AsElement()->SetAttr(
+ kNameSpaceID_None, nsGkAtoms::aria_selected, u"true"_ns, aNotify);
+ } else {
+ rv = content->AsElement()->SetAttr(
+ kNameSpaceID_None, nsGkAtoms::aria_selected, u"false"_ns, 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);
+ LocalAccessible* 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) {
+ LocalAccessible* row = aAccessible->LocalParent();
+
+ if (row && row->Role() == roles::ROW && nsAccUtils::IsARIASelected(row)) {
+ rv = SetARIASelected(row, false, false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ AccIterator cellIter(row, filters::GetCell);
+ LocalAccessible* 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)
+ : HyperTextAccessibleWrap(aContent, aDoc) {
+ mGenericTypes |= eTableRow;
+}
+
+role ARIARowAccessible::NativeRole() const {
+ a11y::role r = GetAccService()->MarkupRole(mContent);
+ return r != roles::NOTHING ? r : roles::ROW;
+}
+
+GroupPos ARIARowAccessible::GroupPosition() {
+ int32_t count = 0, index = 0;
+ LocalAccessible* table = nsAccUtils::TableFor(this);
+ if (table) {
+ if (nsCoreUtils::GetUIntAttr(table->GetContent(), nsGkAtoms::aria_rowcount,
+ &count) &&
+ nsCoreUtils::GetUIntAttr(mContent, nsGkAtoms::aria_rowindex, &index)) {
+ return GroupPos(0, index, count);
+ }
+
+ // Deal with the special case here that tables and grids can have rows
+ // which are wrapped in generic text container elements. Exclude tree grids
+ // because these are dealt with elsewhere.
+ if (table->Role() == roles::TABLE) {
+ LocalAccessible* row = nullptr;
+ AccIterator rowIter(table, filters::GetRow);
+ while ((row = rowIter.Next())) {
+ index++;
+ if (row == this) {
+ break;
+ }
+ }
+
+ if (row) {
+ count = table->AsTable()->RowCount();
+ return GroupPos(0, index, count);
+ }
+ }
+ }
+
+ return AccessibleWrap::GroupPosition();
+}
+
+// LocalAccessible protected
+ENameValueFlag ARIARowAccessible::NativeName(nsString& aName) const {
+ // We want to calculate the name from content only if an ARIA role is
+ // present. ARIARowAccessible might also be used by tables with
+ // display:block; styling, in which case we do not want the name from
+ // content.
+ if (HasStrongARIARole()) {
+ return AccessibleWrap::NativeName(aName);
+ }
+
+ return eNameOK;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// ARIAGridCellAccessible
+////////////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////////////
+// Constructor
+
+ARIAGridCellAccessible::ARIAGridCellAccessible(nsIContent* aContent,
+ DocAccessible* aDoc)
+ : HyperTextAccessibleWrap(aContent, aDoc) {
+ mGenericTypes |= eTableCell;
+}
+
+role ARIAGridCellAccessible::NativeRole() const {
+ const a11y::role r = GetAccService()->MarkupRole(mContent);
+ if (r != role::NOTHING) {
+ return r;
+ }
+
+ // Special case to handle th elements mapped to ARIA grid cells.
+ if (GetContent() && GetContent()->IsHTMLElement(nsGkAtoms::th)) {
+ return GetHeaderCellRole(this);
+ }
+
+ return role::CELL;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// TableCell
+
+TableAccessible* ARIAGridCellAccessible::Table() const {
+ LocalAccessible* table = nsAccUtils::TableFor(Row());
+ return table ? table->AsTable() : nullptr;
+}
+
+uint32_t ARIAGridCellAccessible::ColIdx() const {
+ LocalAccessible* row = Row();
+ if (!row) return 0;
+
+ int32_t indexInRow = IndexInParent();
+ uint32_t colIdx = 0;
+ for (int32_t idx = 0; idx < indexInRow; idx++) {
+ LocalAccessible* cell = row->LocalChildAt(idx);
+ if (cell->IsTableCell()) {
+ colIdx += cell->AsTableCell()->ColExtent();
+ }
+ }
+
+ return colIdx;
+}
+
+uint32_t ARIAGridCellAccessible::RowIdx() const { return RowIndexFor(Row()); }
+
+bool ARIAGridCellAccessible::Selected() {
+ LocalAccessible* row = Row();
+ if (!row) return false;
+
+ return nsAccUtils::IsARIASelected(row) || nsAccUtils::IsARIASelected(this);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// LocalAccessible
+
+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.
+ LocalAccessible* row = LocalParent();
+ if (!row || row->Role() != roles::ROW) return;
+
+ nsIContent* rowContent = row->GetContent();
+ if (nsAccUtils::HasDefinedARIAToken(rowContent, nsGkAtoms::aria_selected) &&
+ !nsAccUtils::ARIAAttrValueIs(rowContent->AsElement(),
+ nsGkAtoms::aria_selected, nsGkAtoms::_false,
+ eCaseMatters)) {
+ *aState |= states::SELECTABLE | states::SELECTED;
+ }
+}
+
+already_AddRefed<AccAttributes> ARIAGridCellAccessible::NativeAttributes() {
+ RefPtr<AccAttributes> attributes =
+ HyperTextAccessibleWrap::NativeAttributes();
+
+ // Expose "table-cell-index" attribute.
+ LocalAccessible* thisRow = Row();
+ if (!thisRow) return attributes.forget();
+
+ int32_t rowIdx = RowIndexFor(thisRow);
+ if (rowIdx == -1) { // error
+ return attributes.forget();
+ }
+
+ int32_t colIdx = 0, colCount = 0;
+ uint32_t childCount = thisRow->ChildCount();
+ for (uint32_t childIdx = 0; childIdx < childCount; childIdx++) {
+ LocalAccessible* child = thisRow->LocalChildAt(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++;
+ }
+ }
+
+ attributes->SetAttribute(nsGkAtoms::tableCellIndex,
+ rowIdx * colCount + colIdx);
+
+#ifdef DEBUG
+ RefPtr<nsAtom> cppClass = NS_Atomize(u"cppclass"_ns);
+ attributes->SetAttributeStringCopy(cppClass, u"ARIAGridCellAccessible"_ns);
+#endif
+
+ return attributes.forget();
+}