bug 1138436 - start on proxying IAccessible2 r=surkov, r=davidb

This commit is contained in:
Trevor Saunders 2015-02-09 11:57:23 -05:00
parent 1109c5aa2c
commit 878d2906bc
4 changed files with 162 additions and 29 deletions

View File

@ -42,6 +42,9 @@ public:
uint32_t ChildrenCount() const { return mChildren.Length(); }
ProxyAccessible* ChildAt(uint32_t aIdx) const { return mChildren[aIdx]; }
// XXX evaluate if this is fast enough.
size_t IndexInParent() const { return mParent->mChildren.IndexOf(this); }
bool MustPruneChildren() const;
void Shutdown();

View File

@ -16,6 +16,7 @@
#include "IUnknownImpl.h"
#include "nsCoreUtils.h"
#include "nsIAccessibleTypes.h"
#include "mozilla/a11y/PDocAccessible.h"
#include "Relation.h"
#include "nsIPersistentProperties2.h"
@ -24,6 +25,8 @@
using namespace mozilla;
using namespace mozilla::a11y;
template<typename String> static void EscapeAttributeChars(String& aStr);
////////////////////////////////////////////////////////////////////////////////
// ia2Accessible
////////////////////////////////////////////////////////////////////////////////
@ -65,6 +68,15 @@ ia2Accessible::get_nRelations(long* aNRelations)
if (acc->IsDefunct())
return CO_E_OBJNOTCONNECTED;
if (acc->IsProxy()) {
// XXX evaluate performance of collecting all relation targets.
nsTArray<RelationType> types;
nsTArray<nsTArray<ProxyAccessible*>> targetSets;
acc->Proxy()->Relations(&types, &targetSets);
*aNRelations = types.Length();
return S_OK;
}
for (uint32_t idx = 0; idx < ArrayLength(sRelationTypePairs); idx++) {
if (sRelationTypePairs[idx].second == IA2_RELATION_NULL)
continue;
@ -84,7 +96,7 @@ ia2Accessible::get_relation(long aRelationIndex,
{
A11Y_TRYBLOCK_BEGIN
if (!aRelation)
if (!aRelation || aRelationIndex < 0)
return E_INVALIDARG;
*aRelation = nullptr;
@ -92,6 +104,34 @@ ia2Accessible::get_relation(long aRelationIndex,
if (acc->IsDefunct())
return CO_E_OBJNOTCONNECTED;
if (acc->IsProxy()) {
nsTArray<RelationType> types;
nsTArray<nsTArray<ProxyAccessible*>> targetSets;
acc->Proxy()->Relations(&types, &targetSets);
size_t targetSetCount = targetSets.Length();
for (size_t i = 0; i < targetSetCount; i++) {
uint32_t relTypeIdx = static_cast<uint32_t>(types[i]);
MOZ_ASSERT(sRelationTypePairs[relTypeIdx].first == types[i]);
if (sRelationTypePairs[relTypeIdx].second == IA2_RELATION_NULL)
continue;
if (static_cast<size_t>(aRelationIndex) == i) {
nsTArray<nsRefPtr<Accessible>> targets;
size_t targetCount = targetSets[i].Length();
for (size_t j = 0; j < targetCount; j++)
targets.AppendElement(WrapperFor(targetSets[i][j]));
nsRefPtr<ia2AccessibleRelation> rel =
new ia2AccessibleRelation(types[i], Move(targets));
rel.forget(aRelation);
return S_OK;
}
}
return E_INVALIDARG;
}
long relIdx = 0;
for (uint32_t idx = 0; idx < ArrayLength(sRelationTypePairs); idx++) {
if (sRelationTypePairs[idx].second == IA2_RELATION_NULL)
@ -123,7 +163,7 @@ ia2Accessible::get_relations(long aMaxRelations,
{
A11Y_TRYBLOCK_BEGIN
if (!aRelation || !aNRelations)
if (!aRelation || !aNRelations || aMaxRelations <= 0)
return E_INVALIDARG;
*aNRelations = 0;
@ -131,6 +171,34 @@ ia2Accessible::get_relations(long aMaxRelations,
if (acc->IsDefunct())
return CO_E_OBJNOTCONNECTED;
if (acc->IsProxy()) {
nsTArray<RelationType> types;
nsTArray<nsTArray<ProxyAccessible*>> targetSets;
acc->Proxy()->Relations(&types, &targetSets);
size_t count = std::min(targetSets.Length(),
static_cast<size_t>(aMaxRelations));
size_t i = 0;
while (i < count) {
uint32_t relTypeIdx = static_cast<uint32_t>(types[i]);
if (sRelationTypePairs[relTypeIdx].second == IA2_RELATION_NULL)
continue;
size_t targetCount = targetSets[i].Length();
nsTArray<nsRefPtr<Accessible>> targets(targetCount);
for (size_t j = 0; j < targetCount; j++)
targets.AppendElement(WrapperFor(targetSets[i][j]));
nsRefPtr<ia2AccessibleRelation> rel =
new ia2AccessibleRelation(types[i], Move(targets));
rel.forget(aRelation + i);
i++;
}
*aNRelations = i;
return S_OK;
}
for (uint32_t idx = 0; idx < ArrayLength(sRelationTypePairs) &&
*aNRelations < aMaxRelations; idx++) {
if (sRelationTypePairs[idx].second == IA2_RELATION_NULL)
@ -169,7 +237,11 @@ ia2Accessible::role(long* aRole)
*aRole = ia2Role; \
break;
a11y::role geckoRole = acc->Role();
a11y::role geckoRole;
if (acc->IsProxy())
geckoRole = acc->Proxy()->Role();
else
geckoRole = acc->Role();
switch (geckoRole) {
#include "RoleMap.h"
default:
@ -180,10 +252,16 @@ ia2Accessible::role(long* aRole)
// Special case, if there is a ROLE_ROW inside of a ROLE_TREE_TABLE, then call
// the IA2 role a ROLE_OUTLINEITEM.
if (geckoRole == roles::ROW) {
Accessible* xpParent = acc->Parent();
if (xpParent && xpParent->Role() == roles::TREE_TABLE)
if (acc->IsProxy()) {
if (geckoRole == roles::ROW && acc->Proxy()->Parent() &&
acc->Proxy()->Parent()->Role() == roles::TREE_TABLE)
*aRole = ROLE_SYSTEM_OUTLINEITEM;
} else {
if (geckoRole == roles::ROW) {
Accessible* xpParent = acc->Parent();
if (xpParent && xpParent->Role() == roles::TREE_TABLE)
*aRole = ROLE_SYSTEM_OUTLINEITEM;
}
}
return S_OK;
@ -274,7 +352,16 @@ ia2Accessible::get_states(AccessibleStates* aStates)
// XXX: bug 344674 should come with better approach that we have here.
AccessibleWrap* acc = static_cast<AccessibleWrap*>(this);
uint64_t state = acc->State();
if (acc->IsDefunct()) {
*aStates = IA2_STATE_DEFUNCT;
return CO_E_OBJNOTCONNECTED;
}
uint64_t state;
if (acc->IsProxy())
state = acc->Proxy()->State();
else
state = acc->State();
if (state & states::INVALID)
*aStates |= IA2_STATE_INVALID_ENTRY;
@ -446,7 +533,11 @@ ia2Accessible::get_indexInParent(long* aIndexInParent)
if (acc->IsDefunct())
return CO_E_OBJNOTCONNECTED;
*aIndexInParent = acc->IndexInParent();
if (acc->IsProxy())
*aIndexInParent = acc->Proxy()->IndexInParent();
else
*aIndexInParent = acc->IndexInParent();
if (*aIndexInParent == -1)
return S_FALSE;
@ -521,8 +612,29 @@ ia2Accessible::get_attributes(BSTR* aAttributes)
// The format is name:value;name:value; with \ for escaping these
// characters ":;=,\".
nsCOMPtr<nsIPersistentProperties> attributes = acc->Attributes();
return ConvertToIA2Attributes(attributes, aAttributes);
if (!acc->IsProxy()) {
nsCOMPtr<nsIPersistentProperties> attributes = acc->Attributes();
return ConvertToIA2Attributes(attributes, aAttributes);
}
nsTArray<Attribute> attrs;
acc->Proxy()->Attributes(&attrs);
nsString attrStr;
size_t attrCount = attrs.Length();
for (size_t i = 0; i < attrCount; i++) {
EscapeAttributeChars(attrs[i].Name());
EscapeAttributeChars(attrs[i].Value());
AppendUTF8toUTF16(attrs[i].Name(), attrStr);
attrStr.Append(':');
attrStr.Append(attrs[i].Value());
attrStr.Append(';');
}
if (attrStr.IsEmpty())
return S_FALSE;
*aAttributes = ::SysAllocStringLen(attrStr.get(), attrStr.Length());
return *aAttributes ? S_OK : E_OUTOFMEMORY;
A11Y_TRYBLOCK_END
}
@ -567,7 +679,7 @@ ia2Accessible::get_relationTargetsOfType(BSTR aType,
{
A11Y_TRYBLOCK_BEGIN
if (!aTargets || !aNTargets)
if (!aTargets || !aNTargets || aMaxTargets < 0)
return E_INVALIDARG;
*aNTargets = 0;
@ -585,13 +697,23 @@ ia2Accessible::get_relationTargetsOfType(BSTR aType,
if (acc->IsDefunct())
return CO_E_OBJNOTCONNECTED;
Relation rel = acc->RelationByType(*relationType);
nsTArray<Accessible*> targets;
Accessible* target = nullptr;
while ((target = rel.Next()) &&
static_cast<long>(targets.Length()) <= aMaxTargets)
targets.AppendElement(target);
if (acc->IsProxy()) {
nsTArray<ProxyAccessible*> targetProxies =
acc->Proxy()->RelationByType(*relationType);
size_t targetCount = aMaxTargets;
if (targetProxies.Length() < targetCount)
targetCount = targetProxies.Length();
for (size_t i = 0; i < targetCount; i++)
targets.AppendElement(WrapperFor(targetProxies[i]));
} else {
Relation rel = acc->RelationByType(*relationType);
Accessible* target = nullptr;
while ((target = rel.Next()) &&
static_cast<long>(targets.Length()) <= aMaxTargets)
targets.AppendElement(target);
}
*aNTargets = targets.Length();
*aTargets = static_cast<IUnknown**>(
@ -613,6 +735,18 @@ ia2Accessible::get_relationTargetsOfType(BSTR aType,
////////////////////////////////////////////////////////////////////////////////
// Helpers
template<typename String>
static inline void
EscapeAttributeChars(String& aStr)
{
int32_t offset = 0;
static const char kCharsToEscape[] = ":;=,\\";
while ((offset = aStr.FindCharInSet(kCharsToEscape, offset)) != kNotFound) {
aStr.Insert('\\', offset);
offset += 2;
}
}
HRESULT
ia2Accessible::ConvertToIA2Attributes(nsIPersistentProperties* aAttributes,
BSTR* aIA2Attributes)
@ -632,8 +766,6 @@ ia2Accessible::ConvertToIA2Attributes(nsIPersistentProperties* aAttributes,
nsAutoString strAttrs;
const char kCharsToEscape[] = ":;=,\\";
bool hasMore = false;
while (NS_SUCCEEDED(propEnum->HasMoreElements(&hasMore)) && hasMore) {
nsCOMPtr<nsISupports> propSupports;
@ -647,21 +779,13 @@ ia2Accessible::ConvertToIA2Attributes(nsIPersistentProperties* aAttributes,
if (NS_FAILED(propElem->GetKey(name)))
return E_FAIL;
int32_t offset = 0;
while ((offset = name.FindCharInSet(kCharsToEscape, offset)) != kNotFound) {
name.Insert('\\', offset);
offset += 2;
}
EscapeAttributeChars(name);
nsAutoString value;
if (NS_FAILED(propElem->GetValue(value)))
return E_FAIL;
offset = 0;
while ((offset = value.FindCharInSet(kCharsToEscape, offset)) != kNotFound) {
value.Insert('\\', offset);
offset += 2;
}
EscapeAttributeChars(value);
AppendUTF8toUTF16(name, strAttrs);
strAttrs.Append(':');

View File

@ -24,6 +24,10 @@ class ia2AccessibleRelation MOZ_FINAL : public IAccessibleRelation
public:
ia2AccessibleRelation(RelationType aType, Relation* aRel);
ia2AccessibleRelation(RelationType aType,
nsTArray<nsRefPtr<Accessible>>&& aTargets) :
mType(aType), mTargets(Move(aTargets)) {}
// IUnknown
DECL_IUNKNOWN

View File

@ -52,3 +52,5 @@ if CONFIG['OS_ARCH'] == 'WINNT':
DEFINES['NOMINMAX'] = True
FAIL_ON_WARNINGS = True
include('/ipc/chromium/chromium-config.mozbuild')