Skip to content
Permalink
Browse files
AX ITM: Fix for accessibility/Mac/aria-errormessage.html in isolated …
…tree mode.

https://bugs.webkit.org/show_bug.cgi?id=241398

Test: accessibility/Mac/aria-errormessage.html

Reviewed by Chris Fleizach and Tyler Wilcock.

This test was failing because the elements that are referenced as error messages are <span> elements, which are ignored on the mac platform. Therefore there was no isolated object created for those elements and AXIsolatedTree::objectsForIDs will not return them. This patch solves this problem by creating orphan isolated objects in AXIsolatedTree:objectsForIDs if there is no isolated object for the requested AXID, and the corresponding live objects exists.

Canonical link: https://commits.webkit.org/251496@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@295491 268f45cc-cd09-0410-ab3c-d52691b4dbfc
  • Loading branch information
AndresGonzalezApple committed Jun 13, 2022
1 parent 2fab778 commit 5f855f274696039ec3c3d8b78d6d006066ea90d4
Show file tree
Hide file tree
Showing 6 changed files with 47 additions and 13 deletions.
@@ -958,7 +958,7 @@ Vector<RefPtr<AXCoreObject>> AXObjectCache::objectsForIDs(const Vector<AXID>& ax

return axIDs.map([this] (const auto& axID) -> RefPtr<AXCoreObject> {
ASSERT(axID.isValid());
return objectFromAXID(axID);
return objectForID(axID);
});
}

@@ -3843,6 +3843,7 @@ void AXObjectCache::addRelation(AccessibilityObject* origin, AccessibilityObject
}
targetsIterator->value.append(target->objectID());
}
m_relationTargets.add(target->objectID());

if (addingSymmetricRelation == AddingSymmetricRelation::No) {
if (auto symmetric = symmetricRelation(relationType); symmetric != AXRelationType::None)
@@ -3859,6 +3860,7 @@ void AXObjectCache::updateRelationsIfNeeded()
relationsNeedUpdate(false);
AXLOG("Updating relations.");
m_relations.clear();
m_relationTargets.clear();

struct RelationOrigin {
Element* originElement { nullptr };
@@ -3919,6 +3921,12 @@ HashMap<AXID, AXRelations> AXObjectCache::relations()
return m_relations;
}

const HashSet<AXID>& AXObjectCache::relationTargetIDs()
{
updateRelationsIfNeeded();
return m_relationTargets;
}

std::optional<Vector<AXID>> AXObjectCache::relatedObjectIDsFor(const AXCoreObject& object, AXRelationType relationType)
{
updateRelationsIfNeeded();
@@ -223,7 +223,7 @@ class AXObjectCache {
bool nodeIsTextControl(const Node*);

AXID platformGenerateAXID() const;
AccessibilityObject* objectFromAXID(AXID id) const { return m_objects.get(id); }
AccessibilityObject* objectForID(const AXID& id) const { return m_objects.get(id); }
Vector<RefPtr<AXCoreObject>> objectsForIDs(const Vector<AXID>&) const;

// Text marker utilities.
@@ -510,6 +510,7 @@ class AXObjectCache {
void updateRelationsIfNeeded();
void relationsNeedUpdate(bool);
HashMap<AXID, AXRelations> relations();
const HashSet<AXID>& relationTargetIDs();

Document& m_document;
const std::optional<PageIdentifier> m_pageID; // constant for object's lifetime.
@@ -571,6 +572,7 @@ class AXObjectCache {
// Relationships between objects.
HashMap<AXID, AXRelations> m_relations;
bool m_relationsNeedUpdate { true };
HashSet<AXID> m_relationTargets;

#if USE(ATSPI)
ListHashSet<RefPtr<AXCoreObject>> m_deferredParentChangedList;
@@ -76,8 +76,6 @@ void AXIsolatedObject::initializeProperties(AXCoreObject& coreObject, IsRoot isR
{
ASSERT(is<AccessibilityObject>(coreObject));
auto& object = downcast<AccessibilityObject>(coreObject);
// We should never create an isolated object from an ignored object.
ASSERT(!object.accessibilityIsIgnored());

setProperty(AXPropertyName::ARIALandmarkRoleDescription, object.ariaLandmarkRoleDescription().isolatedCopy());
setProperty(AXPropertyName::AccessibilityDescription, object.accessibilityDescription().isolatedCopy());
@@ -413,7 +411,7 @@ AXCoreObject* AXIsolatedObject::associatedAXObject() const
return nullptr;

auto* axObjectCache = this->axObjectCache();
return axObjectCache ? axObjectCache->objectFromAXID(m_id) : nullptr;
return axObjectCache ? axObjectCache->objectForID(m_id) : nullptr;
}

void AXIsolatedObject::setMathscripts(AXPropertyName propertyName, AXCoreObject& object)
@@ -145,7 +145,7 @@ RefPtr<AXIsolatedTree> AXIsolatedTree::treeForPageID(PageIdentifier pageID)
return nullptr;
}

RefPtr<AXIsolatedObject> AXIsolatedTree::nodeForID(AXID axID) const
RefPtr<AXIsolatedObject> AXIsolatedTree::nodeForID(const AXID& axID) const
{
// In isolated tree mode 2, only access m_readerThreadNodeMap on the AX thread.
ASSERT(m_usedOnAXThread ? !isMainThread() : isMainThread());
@@ -155,17 +155,42 @@ RefPtr<AXIsolatedObject> AXIsolatedTree::nodeForID(AXID axID) const
return axID.isValid() ? m_readerThreadNodeMap.get(axID) : nullptr;
}

Vector<RefPtr<AXCoreObject>> AXIsolatedTree::objectsForIDs(const Vector<AXID>& axIDs) const
Vector<RefPtr<AXCoreObject>> AXIsolatedTree::objectsForIDs(const Vector<AXID>& axIDs)
{
AXTRACE("AXIsolatedTree::objectsForIDs"_s);
ASSERT(!isMainThread());

Vector<RefPtr<AXCoreObject>> result;
result.reserveInitialCapacity(axIDs.size());
for (auto& axID : axIDs) {
if (auto object = nodeForID(axID))
auto object = nodeForID(axID);
if (object) {
result.uncheckedAppend(object);
continue;
}

// There is no isolated object for this AXID. This can happen if the corresponding live object is ignored.
// If there is a live object for this ID and it is an ignored target of a relationship, create an isolated object for it.
object = Accessibility::retrieveValueFromMainThread<RefPtr<AXIsolatedObject>>([&axID, this] () -> RefPtr<AXIsolatedObject> {
auto* cache = axObjectCache();
if (!cache || !cache->relationTargetIDs().contains(axID))
return nullptr;

auto* axObject = cache->objectForID(axID);
if (!axObject || !axObject->accessibilityIsIgnored())
return nullptr;

auto object = AXIsolatedObject::create(*axObject, const_cast<AXIsolatedTree*>(this));
ASSERT(axObject->wrapper());
object->attachPlatformWrapper(axObject->wrapper());
return object;
});
if (object) {
m_readerThreadNodeMap.add(axID, *object);
result.uncheckedAppend(object);
}
}
result.shrinkToFit();

return result;
}

@@ -263,9 +288,10 @@ void AXIsolatedTree::queueRemovalsAndUnresolvedChanges(const Vector<AXID>& subtr
if (auto* cache = axObjectCache()) {
resolvedAppends.reserveInitialCapacity(m_unresolvedPendingAppends.size());
for (const auto& unresolvedAppend : m_unresolvedPendingAppends) {
if (auto* axObject = cache->objectFromAXID(unresolvedAppend.key))
if (auto* axObject = cache->objectForID(unresolvedAppend.key)) {
if (auto nodeChange = nodeChangeForObject(*axObject, unresolvedAppend.value))
resolvedAppends.uncheckedAppend(WTFMove(*nodeChange));
}
}
m_unresolvedPendingAppends.clear();
}
@@ -332,8 +332,8 @@ class AXIsolatedTree : public ThreadSafeRefCounted<AXIsolatedTree> {

RefPtr<AXIsolatedObject> rootNode();
RefPtr<AXIsolatedObject> focusedNode();
RefPtr<AXIsolatedObject> nodeForID(AXID) const;
Vector<RefPtr<AXCoreObject>> objectsForIDs(const Vector<AXID>&) const;
RefPtr<AXIsolatedObject> nodeForID(const AXID&) const;
Vector<RefPtr<AXCoreObject>> objectsForIDs(const Vector<AXID>&);

struct NodeChange {
Ref<AXIsolatedObject> isolatedObject;
@@ -1039,7 +1039,7 @@ HRESULT AccessibleBase::getAccessibilityObjectForChild(VARIANT vChild, Accessibi
if (!document)
return E_FAIL;

childObj = document->axObjectCache()->objectFromAXID(makeObjectIdentifier<AXIDType>(-vChild.lVal));
childObj = document->axObjectCache()->objectForID(makeObjectIdentifier<AXIDType>(-vChild.lVal));
} else {
size_t childIndex = static_cast<size_t>(vChild.lVal - 1);

0 comments on commit 5f855f2

Please sign in to comment.