зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
f84015c1ae
Коммит
2cd46a7d14
|
@ -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();
|
||||
|
|
Загрузка…
Ссылка в новой задаче