Bug 1774043: [Part 2] Add implicit relations map, pre- and post-update map processing in ApplyCache r=Jamie

Differential Revision: https://phabricator.services.mozilla.com/D152101
This commit is contained in:
Morgan Rae Reschenberg 2022-08-03 05:09:53 +00:00
Родитель f84015c1ae
Коммит 2cd46a7d14
4 изменённых файлов: 135 добавлений и 13 удалений

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

@ -50,22 +50,25 @@ struct RelationData {
nsStaticAtom* const mAtom;
nsStaticAtom* const mValidTag;
RelationType mType;
RelationType mReverseType;
};
/**
* This array of RelationData lists our relation types and the cache
* attribute atoms that store their targets. Attributes may describe
* different kinds of relations, depending on the element they originate on.
* For example, an <output> element's `for` attribute describes a CONTROLLER_FOR
* relation, while the `for` attribute of a <label> describes a LABEL_FOR
* relation.
* To ensure we process these attributes appropriately, RelationData.mValidTag
* contains the atom for the tag this attribute/relation type paring is valid
* on. If the pairing is valid for all tag types, this field is null.
* This array of RelationData lists our relation types (explicit and reverse)
* and the cache attribute atoms that store their targets. Attributes may
* describe different kinds of relations, depending on the element they
* originate on. For example, an <output> element's `for` attribute describes a
* CONTROLLER_FOR relation, while the `for` attribute of a <label> describes a
* LABEL_FOR relation. To ensure we process these attributes appropriately,
* RelationData.mValidTag contains the atom for the tag this attribute/relation
* type paring is valid on. If the pairing is valid for all tag types, this
* field is null.
*/
static constexpr RelationData kRelationTypeAtoms[] = {
{nsGkAtoms::aria_labelledby, nullptr, RelationType::LABELLED_BY},
{nsGkAtoms::_for, nsGkAtoms::label, RelationType::LABEL_FOR},
{nsGkAtoms::aria_labelledby, nullptr, RelationType::LABELLED_BY,
RelationType::LABEL_FOR},
{nsGkAtoms::_for, nsGkAtoms::label, RelationType::LABEL_FOR,
RelationType::LABELLED_BY},
};
} // namespace a11y

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

@ -319,6 +319,12 @@ class DocAccessibleParent : public RemoteAccessible,
void URL(nsAString& aURL) const;
// Tracks cached reverse relations (ie. those not set explicitly by an
// attribute like aria-labelledby) for accessibles in this doc. This map is of
// the form: {accID, {relationType, [targetAccID, targetAccID, ...]}}
nsTHashMap<uint64_t, nsTHashMap<uint64_t, nsTArray<uint64_t>>>
mReverseRelations;
private:
~DocAccessibleParent();

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

@ -56,8 +56,22 @@ void RemoteAccessibleBase<Derived>::Shutdown() {
CachedTableAccessible::Invalidate(this);
}
// XXX Ideally this wouldn't be necessary, but it seems OuterDoc accessibles
// can be destroyed before the doc they own.
if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
// Remove this acc's relation map from the doc's map of
// reverse relations. We don't need to do additional processing
// of the corresponding forward relations, because this shutdown
// should trigger a cache update from the content process.
// Similarly, we don't need to remove the reverse rels created
// by this acc's forward rels because they'll be cleared during
// the next update's call to PreProcessRelations().
// In short, accs are responsible for managing their own
// reverse relation map, both in PreProcessRelations() and in
// Shutdown().
Unused << mDoc->mReverseRelations.Remove(ID());
}
// XXX Ideally this wouldn't be necessary, but it seems OuterDoc
// accessibles can be destroyed before the doc they own.
uint32_t childCount = mChildren.Length();
if (!IsOuterDoc()) {
for (uint32_t idx = 0; idx < childCount; idx++) mChildren[idx]->Shutdown();
@ -642,6 +656,82 @@ void RemoteAccessibleBase<Derived>::AppendTextTo(nsAString& aText,
}
}
template <class Derived>
nsTArray<bool> RemoteAccessibleBase<Derived>::PreProcessRelations(
AccAttributes* aFields) {
nsTArray<bool> updateTracker(ArrayLength(kRelationTypeAtoms));
for (auto const& data : kRelationTypeAtoms) {
if (data.mValidTag && TagName() != data.mValidTag) {
// If the relation we're currently processing only applies to specific
// elements, and we are not one of them, do no pre-processing. Also,
// note in our updateTracker that we should do no post-processing.
updateTracker.AppendElement(false);
continue;
}
nsStaticAtom* const relAtom = data.mAtom;
auto newRelationTargets =
aFields->GetAttribute<nsTArray<uint64_t>>(relAtom);
bool shouldAddNewImplicitRels =
newRelationTargets && newRelationTargets->Length();
// Remove existing implicit relations if we need to perform an update, or
// if we've recieved a DeleteEntry(). Only do this if mCachedFields is
// initialized. If mCachedFields is not initialized, we still need to
// construct the update array so we correctly handle reverse rels in
// PostProcessRelations
if ((shouldAddNewImplicitRels ||
aFields->GetAttribute<DeleteEntry>(relAtom)) &&
mCachedFields) {
if (auto maybeOldIDs =
mCachedFields->GetAttribute<nsTArray<uint64_t>>(relAtom)) {
for (uint64_t id : *maybeOldIDs) {
// For each target, fetch its reverse relation map
nsTHashMap<nsUint64HashKey, nsTArray<uint64_t>>& reverseRels =
Document()->mReverseRelations.LookupOrInsert(id);
// Then fetch its reverse relation's ID list
nsTArray<uint64_t>& reverseRelIDs = reverseRels.LookupOrInsert(
static_cast<uint64_t>(data.mReverseType));
// There might be other reverse relations stored for this acc, so
// remove our ID instead of deleting the array entirely.
DebugOnly<bool> removed = reverseRelIDs.RemoveElement(ID());
MOZ_ASSERT(removed, "Can't find old reverse relation");
}
}
}
updateTracker.AppendElement(shouldAddNewImplicitRels);
}
return updateTracker;
}
template <class Derived>
void RemoteAccessibleBase<Derived>::PostProcessRelations(
const nsTArray<bool>& aToUpdate) {
size_t updateCount = aToUpdate.Length();
MOZ_ASSERT(updateCount == ArrayLength(kRelationTypeAtoms),
"Did not note update status for every relation type!");
for (size_t i = 0; i < updateCount; i++) {
if (aToUpdate.ElementAt(i)) {
// Since kRelationTypeAtoms was used to generate aToUpdate, we
// know the ith entry of aToUpdate corresponds to the relation type in
// the ith entry of kRelationTypeAtoms. Fetch the related data here.
auto const& data = kRelationTypeAtoms[i];
const nsTArray<uint64_t>& newIDs =
*mCachedFields->GetAttribute<nsTArray<uint64_t>>(data.mAtom);
for (uint64_t id : newIDs) {
nsTHashMap<nsUint64HashKey, nsTArray<uint64_t>>& relations =
Document()->mReverseRelations.LookupOrInsert(id);
nsTArray<uint64_t>& ids =
relations.LookupOrInsert(static_cast<uint64_t>(data.mReverseType));
ids.AppendElement(ID());
}
}
}
}
template <class Derived>
uint32_t RemoteAccessibleBase<Derived>::GetCachedTextLength() {
MOZ_ASSERT(!HasChildren());

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

@ -255,6 +255,7 @@ class RemoteAccessibleBase : public Accessible, public HyperTextAccessibleBase {
DocAccessibleParent* AsDoc() const { return IsDoc() ? mDoc : nullptr; }
void ApplyCache(CacheUpdateType aUpdateType, AccAttributes* aFields) {
const nsTArray<bool> relUpdatesNeeded = PreProcessRelations(aFields);
if (aUpdateType == CacheUpdateType::Initial) {
mCachedFields = aFields;
} else {
@ -274,6 +275,8 @@ class RemoteAccessibleBase : public Accessible, public HyperTextAccessibleBase {
parent->InvalidateCachedHyperTextOffsets();
}
}
PostProcessRelations(relUpdatesNeeded);
}
void UpdateStateCache(uint64_t aState, bool aEnabled) {
@ -304,6 +307,26 @@ class RemoteAccessibleBase : public Accessible, public HyperTextAccessibleBase {
virtual bool TableIsProbablyForLayout();
/**
* Iterates through each atom in kRelationTypeAtoms, checking to see
* if it is present in aFields. If it is present (or if aFields contains
* a DeleteEntry() for this atom) and mCachedFields is initialized,
* fetches the old rel targets and removes their existing reverse relations
* stored in mReverseRelations.
* Returns an array of bools where the ith array entry corresponds
* to whether or not the rel at the ith entry of kRelationTypeAtoms
* requires a post-processing update.
*/
nsTArray<bool> PreProcessRelations(AccAttributes* aFields);
/**
* Takes in the array returned from PreProcessRelations.
* For each entry requiring an update, fetches the new relation
* targets stored in mCachedFields and appropriately
* updates their reverse relations in mReverseRelations.
*/
void PostProcessRelations(const nsTArray<bool>& aToUpdate);
uint32_t GetCachedTextLength();
Maybe<const nsTArray<int32_t>&> GetCachedTextLines();
Maybe<nsTArray<nsRect>> GetCachedCharData();