1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5  * You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "TableCellAccessible.h"
8 
9 #include "LocalAccessible-inl.h"
10 #include "TableAccessible.h"
11 
12 using namespace mozilla;
13 using namespace mozilla::a11y;
14 
RowHeaderCells(nsTArray<LocalAccessible * > * aCells)15 void TableCellAccessible::RowHeaderCells(nsTArray<LocalAccessible*>* aCells) {
16   uint32_t rowIdx = RowIdx(), colIdx = ColIdx();
17   TableAccessible* table = Table();
18   if (!table) return;
19 
20   // Move to the left to find row header cells
21   for (uint32_t curColIdx = colIdx - 1; curColIdx < colIdx; curColIdx--) {
22     LocalAccessible* cell = table->CellAt(rowIdx, curColIdx);
23     if (!cell) continue;
24 
25     // CellAt should always return a TableCellAccessible (XXX Bug 587529)
26     TableCellAccessible* tableCell = cell->AsTableCell();
27     NS_ASSERTION(tableCell, "cell should be a table cell!");
28     if (!tableCell) continue;
29 
30     // Avoid addding cells multiple times, if this cell spans more columns
31     // we'll get it later.
32     if (tableCell->ColIdx() == curColIdx && cell->Role() == roles::ROWHEADER) {
33       aCells->AppendElement(cell);
34     }
35   }
36 }
37 
PrevColHeader()38 LocalAccessible* TableCellAccessible::PrevColHeader() {
39   TableAccessible* table = Table();
40   if (!table) {
41     return nullptr;
42   }
43 
44   TableAccessible::HeaderCache& cache = table->GetHeaderCache();
45   bool inCache = false;
46   LocalAccessible* cachedHeader = cache.GetWeak(this, &inCache);
47   if (inCache) {
48     // Cached but null means we know there is no previous column header.
49     // if defunct, the cell was removed, so behave as if there is no cached
50     // value.
51     if (!cachedHeader || !cachedHeader->IsDefunct()) {
52       return cachedHeader;
53     }
54   }
55 
56   uint32_t rowIdx = RowIdx(), colIdx = ColIdx();
57   for (uint32_t curRowIdx = rowIdx - 1; curRowIdx < rowIdx; curRowIdx--) {
58     LocalAccessible* cell = table->CellAt(curRowIdx, colIdx);
59     if (!cell) {
60       continue;
61     }
62     // CellAt should always return a TableCellAccessible (XXX Bug 587529)
63     TableCellAccessible* tableCell = cell->AsTableCell();
64     MOZ_ASSERT(tableCell, "cell should be a table cell!");
65     if (!tableCell) {
66       continue;
67     }
68 
69     // Check whether the previous table cell has a cached value.
70     cachedHeader = cache.GetWeak(tableCell, &inCache);
71     if (
72         // We check the cache first because even though we might not use it,
73         // it's faster than the other conditions.
74         inCache &&
75         // Only use the cached value if:
76         // 1. cell is a table cell which is not a column header. In that case,
77         // cell is the previous header and cachedHeader is the one before that.
78         // We will return cell later.
79         cell->Role() != roles::COLUMNHEADER &&
80         // 2. cell starts in this column. If it starts in a previous column and
81         // extends into this one, its header will be for the starting column,
82         // which is wrong for this cell.
83         // ColExtent is faster than ColIdx, so check that first.
84         (tableCell->ColExtent() == 1 || tableCell->ColIdx() == colIdx)) {
85       if (!cachedHeader || !cachedHeader->IsDefunct()) {
86         // Cache it for this cell.
87         cache.InsertOrUpdate(this, RefPtr<LocalAccessible>(cachedHeader));
88         return cachedHeader;
89       }
90     }
91 
92     // Avoid addding cells multiple times, if this cell spans more rows
93     // we'll get it later.
94     if (cell->Role() != roles::COLUMNHEADER ||
95         tableCell->RowIdx() != curRowIdx) {
96       continue;
97     }
98 
99     // Cache the header we found.
100     cache.InsertOrUpdate(this, RefPtr<LocalAccessible>(cell));
101     return cell;
102   }
103 
104   // There's no header, so cache that fact.
105   cache.InsertOrUpdate(this, RefPtr<LocalAccessible>(nullptr));
106   return nullptr;
107 }
108 
ColHeaderCells(nsTArray<LocalAccessible * > * aCells)109 void TableCellAccessible::ColHeaderCells(nsTArray<LocalAccessible*>* aCells) {
110   for (LocalAccessible* cell = PrevColHeader(); cell;
111        cell = cell->AsTableCell()->PrevColHeader()) {
112     aCells->AppendElement(cell);
113   }
114 }
115