Bug 1735970 part 9: Support explicitly associated headers for both local and remote cells. r=morgan

Headers are associated using the headers DOM attribute, which is a list of DOM node ids.
For the cache, we send and store these as Accessible ids.

Differential Revision: https://phabricator.services.mozilla.com/D141212
This commit is contained in:
James Teh 2022-04-01 09:49:59 +00:00
Родитель ed3aeb2201
Коммит 1a56a1b78a
3 изменённых файлов: 102 добавлений и 3 удалений

Просмотреть файл

@ -6,8 +6,11 @@
#include "CachedTableAccessible.h"
#include "AccIterator.h"
#include "DocAccessibleParent.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/UniquePtr.h"
#include "nsAccUtils.h"
#include "nsIAccessiblePivot.h"
#include "Pivot.h"
@ -42,6 +45,31 @@ class TablePartRule : public PivotRule {
}
};
// Iterates through headers explicitly associated with a remote table cell via
// the headers DOM attribute. These are cached as Accessible ids.
class RemoteExplicitHeadersIterator : public AccIterable {
public:
RemoteExplicitHeadersIterator(const nsTArray<uint64_t>& aHeaders,
Accessible* aDoc)
: mHeaders(aHeaders), mDoc(aDoc), mIndex(0) {}
virtual Accessible* Next() override {
while (mIndex < mHeaders.Length()) {
uint64_t id = mHeaders[mIndex++];
Accessible* acc = nsAccUtils::GetAccessibleByID(mDoc, id);
if (acc) {
return acc;
}
}
return nullptr;
}
private:
const nsTArray<uint64_t>& mHeaders;
Accessible* mDoc;
uint32_t mIndex;
};
// The Accessible* keys should only be used for lookup. They should not be
// dereferenced.
using CachedTablesMap = nsTHashMap<Accessible*, CachedTableAccessible>;
@ -298,12 +326,47 @@ uint32_t CachedTableCellAccessible::RowExtent() const {
return 1;
}
UniquePtr<AccIterable> CachedTableCellAccessible::GetExplicitHeadersIterator() {
if (RemoteAccessible* remoteAcc = mAcc->AsRemote()) {
if (remoteAcc->mCachedFields) {
if (auto headers =
remoteAcc->mCachedFields->GetAttribute<nsTArray<uint64_t>>(
nsGkAtoms::headers)) {
return MakeUnique<RemoteExplicitHeadersIterator>(*headers,
remoteAcc->Document());
}
}
} else if (LocalAccessible* localAcc = mAcc->AsLocal()) {
return MakeUnique<IDRefsIterator>(
localAcc->Document(), localAcc->GetContent(), nsGkAtoms::headers);
}
return nullptr;
}
void CachedTableCellAccessible::ColHeaderCells(nsTArray<Accessible*>* aCells) {
// XXX Support explicitly associated headers.
auto* table = static_cast<CachedTableAccessible*>(Table());
if (!table) {
return;
}
if (auto iter = GetExplicitHeadersIterator()) {
while (Accessible* header = iter->Next()) {
role headerRole = header->Role();
if (headerRole == roles::COLUMNHEADER) {
aCells->AppendElement(header);
} else if (headerRole != roles::ROWHEADER) {
// Treat this cell as a column header only if it's in the same column.
if (auto cellIdx = table->mAccToCellIdx.Lookup(header)) {
CachedTableCellAccessible& cell = table->mCells[*cellIdx];
if (cell.ColIdx() == ColIdx()) {
aCells->AppendElement(header);
}
}
}
}
if (!aCells->IsEmpty()) {
return;
}
}
Accessible* doc = nsAccUtils::DocumentFor(table->AsAccessible());
// Each cell stores its previous implicit column header, effectively forming a
// linked list. We traverse that to get all the headers.
@ -319,11 +382,29 @@ void CachedTableCellAccessible::ColHeaderCells(nsTArray<Accessible*>* aCells) {
}
void CachedTableCellAccessible::RowHeaderCells(nsTArray<Accessible*>* aCells) {
// XXX Support explicitly associated headers.
TableAccessibleBase* table = Table();
auto* table = static_cast<CachedTableAccessible*>(Table());
if (!table) {
return;
}
if (auto iter = GetExplicitHeadersIterator()) {
while (Accessible* header = iter->Next()) {
role headerRole = header->Role();
if (headerRole == roles::ROWHEADER) {
aCells->AppendElement(header);
} else if (headerRole != roles::COLUMNHEADER) {
// Treat this cell as a row header only if it's in the same row.
if (auto cellIdx = table->mAccToCellIdx.Lookup(header)) {
CachedTableCellAccessible& cell = table->mCells[*cellIdx];
if (cell.RowIdx() == RowIdx()) {
aCells->AppendElement(header);
}
}
}
}
if (!aCells->IsEmpty()) {
return;
}
}
// We don't cache implicit row headers because there are usually not that many
// cells per row. Get all the row headers on the row before this cell.
uint32_t row = RowIdx();

Просмотреть файл

@ -9,12 +9,15 @@
#include "mozilla/a11y/TableAccessibleBase.h"
#include "mozilla/a11y/TableCellAccessibleBase.h"
#include "mozilla/UniquePtr.h"
#include "nsTHashMap.h"
namespace mozilla::a11y {
const uint32_t kNoCellIdx = UINT32_MAX;
class AccIterable;
class CachedTableAccessible;
class CachedTableCellAccessible final : public TableCellAccessibleBase {
@ -54,6 +57,8 @@ class CachedTableCellAccessible final : public TableCellAccessibleBase {
// verifying that the Accessible is valid.
Accessible* Acc(Accessible* aTableAcc) const;
UniquePtr<AccIterable> GetExplicitHeadersIterator();
uint64_t mAccID;
// CachedTableAccessible methods which fetch a cell should retrieve the
// Accessible using Acc() rather than using mAcc. We need mAcc for some

Просмотреть файл

@ -3355,6 +3355,19 @@ already_AddRefed<AccAttributes> LocalAccessible::BundleFieldsForCache(
} else if (aUpdateType == CacheUpdateType::Update) {
fields->SetAttribute(nsGkAtoms::colspan, DeleteEntry());
}
if (mContent->AsElement()->HasAttr(kNameSpaceID_None,
nsGkAtoms::headers)) {
nsTArray<uint64_t> headers;
IDRefsIterator iter(mDoc, mContent, nsGkAtoms::headers);
while (LocalAccessible* cell = iter.Next()) {
if (cell->IsTableCell()) {
headers.AppendElement(cell->ID());
}
}
fields->SetAttribute(nsGkAtoms::headers, std::move(headers));
} else {
fields->SetAttribute(nsGkAtoms::headers, DeleteEntry());
}
}
}