Bug 527461 - Implement RELATION_NODE_PARENT_OF, r=tbsaunde

This commit is contained in:
Alexander Surkov 2013-02-26 16:17:10 +09:00
parent f79e9a13d1
commit caaa87b509
18 changed files with 459 additions and 141 deletions

View File

@ -10,37 +10,62 @@ interface nsIAccessible;
/**
* This interface gives access to an accessible's set of relations.
* Be carefull, do not change constants until ATK has a structure to map gecko
* constants into ATK constants.
*/
[scriptable, uuid(f42a1589-70ab-4704-877f-4a9162bbe188)]
[scriptable, uuid(9f85fc0d-2969-48e6-b822-68140f7e5770)]
interface nsIAccessibleRelation : nsISupports
{
const unsigned long RELATION_NUL = 0x00;
/**
* Some attribute of this object is affected by a target object.
* This object is labelled by a target object.
*/
const unsigned long RELATION_CONTROLLED_BY = 0x01;
// First relation
const unsigned long RELATION_FIRST = RELATION_CONTROLLED_BY;
/**
* This object is interactive and controls some attribute of a target object.
*/
const unsigned long RELATION_CONTROLLER_FOR = 0x02;
const unsigned long RELATION_LABELLED_BY = 0x00;
/**
* This object is label for a target object.
*/
const unsigned long RELATION_LABEL_FOR = 0x03;
const unsigned long RELATION_LABEL_FOR = 0x01;
/**
* This object is labelled by a target object.
* This object is described by the target object.
*/
const unsigned long RELATION_LABELLED_BY = 0x04;
const unsigned long RELATION_DESCRIBED_BY = 0x02;
/**
* This object is describes the target object.
*/
const unsigned long RELATION_DESCRIPTION_FOR = 0x3;
/**
* This object is a child of a target object.
*/
const unsigned long RELATION_NODE_CHILD_OF = 0x4;
/**
* This object is a parent of a target object. A dual relation to
* RELATION_NODE_CHILD_OF
*/
const unsigned long RELATION_NODE_PARENT_OF = 0x5;
/**
* Some attribute of this object is affected by a target object.
*/
const unsigned long RELATION_CONTROLLED_BY = 0x06;
/**
* This object is interactive and controls some attribute of a target object.
*/
const unsigned long RELATION_CONTROLLER_FOR = 0x07;
/**
* Content flows from this object to a target object, i.e. has content that
* flows logically to another object in a sequential way, e.g. text flow.
*/
const unsigned long RELATION_FLOWS_TO = 0x08;
/**
* Content flows to this object from a target object, i.e. has content that
* flows logically from another object in a sequential way, e.g. text flow.
*/
const unsigned long RELATION_FLOWS_FROM = 0x09;
/**
* This object is a member of a group of one or more objects. When there is
@ -48,71 +73,41 @@ interface nsIAccessibleRelation : nsISupports
* target, e.g. a grouping object. It is also possible that each member has
* multiple additional targets, e.g. one for every other member in the group.
*/
const unsigned long RELATION_MEMBER_OF = 0x05;
/**
* This object is a child of a target object.
*/
const unsigned long RELATION_NODE_CHILD_OF = 0x06;
/**
* Content flows from this object to a target object, i.e. has content that
* flows logically to another object in a sequential way, e.g. text flow.
*/
const unsigned long RELATION_FLOWS_TO = 0x07;
/**
* Content flows to this object from a target object, i.e. has content that
* flows logically from another object in a sequential way, e.g. text flow.
*/
const unsigned long RELATION_FLOWS_FROM = 0x08;
const unsigned long RELATION_MEMBER_OF = 0x0a;
/**
* This object is a sub window of a target object.
*/
const unsigned long RELATION_SUBWINDOW_OF = 0x09;
const unsigned long RELATION_SUBWINDOW_OF = 0x0b;
/**
* This object embeds a target object. This relation can be used on the
* OBJID_CLIENT accessible for a top level window to show where the content
* areas are.
*/
const unsigned long RELATION_EMBEDS = 0x0a;
const unsigned long RELATION_EMBEDS = 0x0c;
/**
* This object is embedded by a target object.
*/
const unsigned long RELATION_EMBEDDED_BY = 0x0b;
const unsigned long RELATION_EMBEDDED_BY = 0x0d;
/**
* This object is a transient component related to the target object. When
* this object is activated the target object doesn't lose focus.
*/
const unsigned long RELATION_POPUP_FOR = 0x0c;
const unsigned long RELATION_POPUP_FOR = 0x0e;
/**
* This object is a parent window of the target object.
*/
const unsigned long RELATION_PARENT_WINDOW_OF = 0x0d;
/**
* This object is described by the target object.
*/
const unsigned long RELATION_DESCRIBED_BY = 0x0e;
/**
* This object is describes the target object.
*/
const unsigned long RELATION_DESCRIPTION_FOR = 0x0f;
// Last relation that is standard to desktop accessibility APIs
const unsigned long RELATION_LAST = RELATION_DESCRIPTION_FOR;
const unsigned long RELATION_PARENT_WINDOW_OF = 0x0f;
/**
* Part of a form/dialog with a related default button. It is used for
* MSAA only, no for IA2 nor ATK.
* MSAA/XPCOM, it isn't for IA2 or ATK.
*/
const unsigned long RELATION_DEFAULT_BUTTON = 0x4000;
const unsigned long RELATION_DEFAULT_BUTTON = 0x10;
/**
* Returns the type of the relation.

View File

@ -853,21 +853,29 @@ refRelationSetCB(AtkObject *aAtkObj)
if (!accWrap)
return relation_set;
uint32_t relationTypes[] = {
nsIAccessibleRelation::RELATION_LABELLED_BY,
nsIAccessibleRelation::RELATION_LABEL_FOR,
nsIAccessibleRelation::RELATION_NODE_CHILD_OF,
// Keep in sync with AtkRelationType enum.
static const uint32_t relationTypes[] = {
nsIAccessibleRelation::RELATION_CONTROLLED_BY,
nsIAccessibleRelation::RELATION_CONTROLLER_FOR,
nsIAccessibleRelation::RELATION_EMBEDS,
nsIAccessibleRelation::RELATION_LABEL_FOR,
nsIAccessibleRelation::RELATION_LABELLED_BY,
nsIAccessibleRelation::RELATION_MEMBER_OF,
nsIAccessibleRelation::RELATION_NODE_CHILD_OF,
nsIAccessibleRelation::RELATION_FLOWS_TO,
nsIAccessibleRelation::RELATION_FLOWS_FROM,
nsIAccessibleRelation::RELATION_SUBWINDOW_OF,
nsIAccessibleRelation::RELATION_EMBEDS,
nsIAccessibleRelation::RELATION_EMBEDDED_BY,
nsIAccessibleRelation::RELATION_POPUP_FOR,
nsIAccessibleRelation::RELATION_PARENT_WINDOW_OF,
nsIAccessibleRelation::RELATION_DESCRIBED_BY,
nsIAccessibleRelation::RELATION_DESCRIPTION_FOR,
nsIAccessibleRelation::RELATION_NODE_PARENT_OF
};
for (uint32_t i = 0; i < ArrayLength(relationTypes); i++) {
AtkRelationType atkType = static_cast<AtkRelationType>(relationTypes[i]);
// Shift to 1 to skip ATK_RELATION_NULL.
AtkRelationType atkType = static_cast<AtkRelationType>(i + 1);
AtkRelation* atkRelation =
atk_relation_set_get_relation_by_type(relation_set, atkType);
if (atkRelation)

View File

@ -138,6 +138,55 @@ AccGroupInfo::AccGroupInfo(Accessible* aItem, role aRole) :
mParent = parentPrevSibling;
}
Accessible*
AccGroupInfo::FirstItemOf(Accessible* aContainer)
{
// ARIA trees can be arranged by ARIA groups, otherwise aria-level works.
a11y::role containerRole = aContainer->Role();
Accessible* item = aContainer->NextSibling();
if (item) {
if (containerRole == roles::OUTLINEITEM && item->Role() == roles::GROUPING)
item = item->FirstChild();
AccGroupInfo* itemGroupInfo = item->GetGroupInfo();
if (itemGroupInfo && itemGroupInfo->ConceptualParent() == aContainer)
return item;
}
// Otherwise it can be a direct child.
item = aContainer->FirstChild();
if (item && IsConceptualParent(BaseRole(item->Role()), containerRole))
return item;
return nullptr;
}
Accessible*
AccGroupInfo::NextItemTo(Accessible* aItem)
{
AccGroupInfo* groupInfo = aItem->GetGroupInfo();
if (!groupInfo)
return nullptr;
// If the item in middle of the group then search next item in siblings.
if (groupInfo->PosInSet() >= groupInfo->SetSize())
return nullptr;
Accessible* parent = aItem->Parent();
uint32_t childCount = parent->ChildCount();
for (int32_t idx = aItem->IndexInParent() + 1; idx < childCount; idx++) {
Accessible* nextItem = parent->GetChildAt(idx);
AccGroupInfo* nextGroupInfo = nextItem->GetGroupInfo();
if (nextGroupInfo &&
nextGroupInfo->ConceptualParent() == groupInfo->ConceptualParent()) {
return nextItem;
}
}
NS_NOTREACHED("Item in the midle of the group but there's no next item!");
return nullptr;
}
bool
AccGroupInfo::IsConceptualParent(role aRole, role aParentRole)
{

View File

@ -17,11 +17,22 @@ namespace a11y {
class AccGroupInfo
{
public:
AccGroupInfo(Accessible* aItem, mozilla::a11y::role aRole);
~AccGroupInfo() { MOZ_COUNT_DTOR(AccGroupInfo); }
int32_t PosInSet() const { return mPosInSet; }
/**
* Return 1-based position in the group.
*/
uint32_t PosInSet() const { return mPosInSet; }
/**
* Return a number of items in the group.
*/
uint32_t SetSize() const { return mSetSize; }
/**
* Return a direct or logical parent of the accessible that this group info is
* created for.
*/
Accessible* ConceptualParent() const { return mParent; }
/**
@ -50,9 +61,23 @@ public:
return info;
}
/**
* Return a first item for the given container.
*/
static Accessible* FirstItemOf(Accessible* aContainer);
/**
* Return next item of the same group to the given item.
*/
static Accessible* NextItemTo(Accessible* aItem);
protected:
AccGroupInfo(Accessible* aItem, a11y::role aRole);
private:
AccGroupInfo(const AccGroupInfo&);
AccGroupInfo& operator =(const AccGroupInfo&);
AccGroupInfo() MOZ_DELETE;
AccGroupInfo(const AccGroupInfo&) MOZ_DELETE;
AccGroupInfo& operator =(const AccGroupInfo&) MOZ_DELETE;
static mozilla::a11y::role BaseRole(mozilla::a11y::role aRole)
{
@ -71,8 +96,7 @@ private:
* Return true if the given parent role is conceptual parent of the given
* role.
*/
static bool IsConceptualParent(mozilla::a11y::role aRole,
mozilla::a11y::role aParentRole);
static bool IsConceptualParent(a11y::role aRole, a11y::role aParentRole);
uint32_t mPosInSet;
uint32_t mSetSize;

View File

@ -5,7 +5,11 @@
#include "AccIterator.h"
#include "nsAccessibilityService.h"
#include "AccGroupInfo.h"
#include "Accessible-inl.h"
#ifdef MOZ_XUL
#include "XULTreeAccessible.h"
#endif
#include "mozilla/dom/Element.h"
#include "nsBindingManager.h"
@ -329,6 +333,11 @@ IDRefsIterator::Next()
return nextElm ? mDoc->GetAccessible(nextElm) : nullptr;
}
////////////////////////////////////////////////////////////////////////////////
// SingleAccIterator
////////////////////////////////////////////////////////////////////////////////
Accessible*
SingleAccIterator::Next()
{
@ -337,3 +346,56 @@ SingleAccIterator::Next()
return (nextAcc && !nextAcc->IsDefunct()) ? nextAcc : nullptr;
}
////////////////////////////////////////////////////////////////////////////////
// ItemIterator
////////////////////////////////////////////////////////////////////////////////
Accessible*
ItemIterator::Next()
{
if (mContainer) {
mAnchor = AccGroupInfo::FirstItemOf(mContainer);
mContainer = nullptr;
return mAnchor;
}
return mAnchor ? (mAnchor = AccGroupInfo::NextItemTo(mAnchor)) : nullptr;
}
////////////////////////////////////////////////////////////////////////////////
// XULTreeItemIterator
////////////////////////////////////////////////////////////////////////////////
XULTreeItemIterator::XULTreeItemIterator(XULTreeAccessible* aXULTree,
nsITreeView* aTreeView,
int32_t aRowIdx) :
mXULTree(aXULTree), mTreeView(aTreeView), mRowCount(-1),
mContainerLevel(-1), mCurrRowIdx(aRowIdx + 1)
{
mTreeView->GetRowCount(&mRowCount);
if (aRowIdx != -1)
mTreeView->GetLevel(aRowIdx, &mContainerLevel);
}
Accessible*
XULTreeItemIterator::Next()
{
while (mCurrRowIdx < mRowCount) {
int32_t level = 0;
mTreeView->GetLevel(mCurrRowIdx, &level);
if (level == mContainerLevel + 1)
return mXULTree->GetTreeItemAccessible(mCurrRowIdx++);
if (level <= mContainerLevel) { // got level up
mCurrRowIdx = mRowCount;
break;
}
mCurrRowIdx++;
}
return nullptr;
}

View File

@ -266,6 +266,53 @@ private:
nsRefPtr<Accessible> mAcc;
};
/**
* Used to iterate items of the given item container.
*/
class ItemIterator : public AccIterable
{
public:
ItemIterator(Accessible* aItemContainer) :
mContainer(aItemContainer), mAnchor(nullptr) { }
virtual ~ItemIterator() { }
virtual Accessible* Next();
private:
ItemIterator() MOZ_DELETE;
ItemIterator(const ItemIterator&) MOZ_DELETE;
ItemIterator& operator = (const ItemIterator&) MOZ_DELETE;
Accessible* mContainer;
Accessible* mAnchor;
};
/**
* Used to iterate through XUL tree items of the same level.
*/
class XULTreeItemIterator : public AccIterable
{
public:
XULTreeItemIterator(XULTreeAccessible* aXULTree, nsITreeView* aTreeView,
int32_t aRowIdx);
virtual ~XULTreeItemIterator() { }
virtual Accessible* Next();
private:
XULTreeItemIterator() MOZ_DELETE;
XULTreeItemIterator(const XULTreeItemIterator&) MOZ_DELETE;
XULTreeItemIterator& operator = (const XULTreeItemIterator&) MOZ_DELETE;
XULTreeAccessible* mXULTree;
nsITreeView* mTreeView;
int32_t mRowCount;
int32_t mContainerLevel;
int32_t mCurrRowIdx;
};
} // namespace a11y
} // namespace mozilla

View File

@ -19,12 +19,11 @@ namespace a11y {
*/
struct RelationCopyHelper
{
RelationCopyHelper(mozilla::a11y::AccIterable* aFirstIter,
mozilla::a11y::AccIterable* aLastIter) :
RelationCopyHelper(AccIterable* aFirstIter, AccIterable* aLastIter) :
mFirstIter(aFirstIter), mLastIter(aLastIter) { }
mozilla::a11y::AccIterable* mFirstIter;
mozilla::a11y::AccIterable* mLastIter;
AccIterable* mFirstIter;
AccIterable* mLastIter;
};
/**
@ -39,7 +38,7 @@ public:
Relation(const RelationCopyHelper aRelation) :
mFirstIter(aRelation.mFirstIter), mLastIter(aRelation.mLastIter) { }
Relation(mozilla::a11y::AccIterable* aIter) :
Relation(AccIterable* aIter) :
mFirstIter(aIter), mLastIter(aIter) { }
Relation(Accessible* aAcc) :
@ -69,7 +68,7 @@ public:
return RelationCopyHelper(mFirstIter.forget(), mLastIter);
}
inline void AppendIter(mozilla::a11y::AccIterable* aIter)
inline void AppendIter(AccIterable* aIter)
{
if (mLastIter)
mLastIter->mNextIter = aIter;
@ -85,7 +84,7 @@ public:
inline void AppendTarget(Accessible* aAcc)
{
if (aAcc)
AppendIter(new mozilla::a11y::SingleAccIterator(aAcc));
AppendIter(new SingleAccIterator(aAcc));
}
/**
@ -118,8 +117,8 @@ public:
private:
Relation& operator = (const Relation&);
nsAutoPtr<mozilla::a11y::AccIterable> mFirstIter;
mozilla::a11y::AccIterable* mLastIter;
nsAutoPtr<AccIterable> mFirstIter;
AccIterable* mLastIter;
};
} // namespace a11y

View File

@ -587,9 +587,8 @@ enum Role {
TOGGLE_BUTTON = 93,
/**
* Representas a control that is capable of expanding and collapsing rows as
* Represent a control that is capable of expanding and collapsing rows as
* well as showing multiple columns of data.
* XXX: it looks like this role is dupe of OUTLINE.
*/
TREE_TABLE = 94,

View File

@ -318,22 +318,22 @@ static const char kEventTypeNames[][40] = {
* nsIAccessibleRetrieval::getStringRelationType() method.
*/
static const char kRelationTypeNames[][20] = {
"unknown", // RELATION_NUL
"labelled by", // RELATION_LABELLED_BY
"label for", // RELATION_LABEL_FOR
"described by", // RELATION_DESCRIBED_BY
"description for", // RELATION_DESCRIPTION_FOR
"node child of", // RELATION_NODE_CHILD_OF
"node parent of", // RELATION_NODE_PARENT_OF
"controlled by", // RELATION_CONTROLLED_BY
"controller for", // RELATION_CONTROLLER_FOR
"label for", // RELATION_LABEL_FOR
"labelled by", // RELATION_LABELLED_BY
"member of", // RELATION_MEMBER_OF
"node child of", // RELATION_NODE_CHILD_OF
"flows to", // RELATION_FLOWS_TO
"flows from", // RELATION_FLOWS_FROM
"member of", // RELATION_MEMBER_OF
"subwindow of", // RELATION_SUBWINDOW_OF
"embeds", // RELATION_EMBEDS
"embedded by", // RELATION_EMBEDDED_BY
"popup for", // RELATION_POPUP_FOR
"parent window of", // RELATION_PARENT_WINDOW_OF
"described by", // RELATION_DESCRIBED_BY
"description for", // RELATION_DESCRIPTION_FOR
"default button" // RELATION_DEFAULT_BUTTON
};

View File

@ -1967,16 +1967,6 @@ Accessible::RelationByType(uint32_t aType)
// Relationships are defined on the same content node that the role would be
// defined on.
switch (aType) {
case nsIAccessibleRelation::RELATION_LABEL_FOR: {
Relation rel(new RelatedAccIterator(Document(), mContent,
nsGkAtoms::aria_labelledby));
if (mContent->Tag() == nsGkAtoms::label)
rel.AppendIter(new IDRefsIterator(mDoc, mContent, mContent->IsHTML() ?
nsGkAtoms::_for :
nsGkAtoms::control));
return rel;
}
case nsIAccessibleRelation::RELATION_LABELLED_BY: {
Relation rel(new IDRefsIterator(mDoc, mContent,
nsGkAtoms::aria_labelledby));
@ -1988,6 +1978,18 @@ Accessible::RelationByType(uint32_t aType)
return rel;
}
case nsIAccessibleRelation::RELATION_LABEL_FOR: {
Relation rel(new RelatedAccIterator(Document(), mContent,
nsGkAtoms::aria_labelledby));
if (mContent->Tag() == nsGkAtoms::label)
rel.AppendIter(new IDRefsIterator(mDoc, mContent, mContent->IsHTML() ?
nsGkAtoms::_for :
nsGkAtoms::control));
return rel;
}
case nsIAccessibleRelation::RELATION_DESCRIBED_BY: {
Relation rel(new IDRefsIterator(mDoc, mContent,
nsGkAtoms::aria_describedby));
@ -1996,6 +1998,7 @@ Accessible::RelationByType(uint32_t aType)
return rel;
}
case nsIAccessibleRelation::RELATION_DESCRIPTION_FOR: {
Relation rel(new RelatedAccIterator(Document(), mContent,
nsGkAtoms::aria_describedby));
@ -2010,19 +2013,16 @@ Accessible::RelationByType(uint32_t aType)
return rel;
}
case nsIAccessibleRelation::RELATION_NODE_CHILD_OF: {
Relation rel(new RelatedAccIterator(Document(), mContent,
nsGkAtoms::aria_owns));
// This is an ARIA tree or treegrid that doesn't use owns, so we need to
// get the parent the hard way.
if (mRoleMapEntry && (mRoleMapEntry->role == roles::OUTLINEITEM ||
mRoleMapEntry->role == roles::ROW)) {
AccGroupInfo* groupInfo = GetGroupInfo();
if (!groupInfo)
return rel;
rel.AppendTarget(groupInfo->ConceptualParent());
rel.AppendTarget(GetGroupInfo()->ConceptualParent());
}
// If accessible is in its own Window, or is the root of a document,
@ -2043,21 +2043,52 @@ Accessible::RelationByType(uint32_t aType)
return rel;
}
case nsIAccessibleRelation::RELATION_NODE_PARENT_OF: {
Relation rel(new IDRefsIterator(mDoc, mContent, nsGkAtoms::aria_owns));
// ARIA tree or treegrid can do the hierarchy by @aria-level, ARIA trees
// also can be organized by groups.
if (mRoleMapEntry &&
(mRoleMapEntry->role == roles::OUTLINEITEM ||
mRoleMapEntry->role == roles::ROW ||
mRoleMapEntry->role == roles::OUTLINE ||
mRoleMapEntry->role == roles::TREE_TABLE)) {
rel.AppendIter(new ItemIterator(this));
}
return rel;
}
case nsIAccessibleRelation::RELATION_CONTROLLED_BY:
return Relation(new RelatedAccIterator(Document(), mContent,
nsGkAtoms::aria_controls));
case nsIAccessibleRelation::RELATION_CONTROLLER_FOR: {
Relation rel(new IDRefsIterator(mDoc, mContent,
nsGkAtoms::aria_controls));
rel.AppendIter(new HTMLOutputIterator(Document(), mContent));
return rel;
}
case nsIAccessibleRelation::RELATION_FLOWS_TO:
return Relation(new IDRefsIterator(mDoc, mContent,
nsGkAtoms::aria_flowto));
case nsIAccessibleRelation::RELATION_FLOWS_FROM:
return Relation(new RelatedAccIterator(Document(), mContent,
nsGkAtoms::aria_flowto));
case nsIAccessibleRelation::RELATION_MEMBER_OF:
return Relation(mDoc, GetAtomicRegion());
case nsIAccessibleRelation::RELATION_SUBWINDOW_OF:
case nsIAccessibleRelation::RELATION_EMBEDS:
case nsIAccessibleRelation::RELATION_EMBEDDED_BY:
case nsIAccessibleRelation::RELATION_POPUP_FOR:
case nsIAccessibleRelation::RELATION_PARENT_WINDOW_OF:
return Relation();
case nsIAccessibleRelation::RELATION_DEFAULT_BUTTON: {
if (mContent->IsHTML()) {
// HTML form controls implements nsIFormControl interface.
@ -2105,15 +2136,9 @@ Accessible::RelationByType(uint32_t aType)
}
return Relation();
}
case nsIAccessibleRelation::RELATION_MEMBER_OF:
return Relation(mDoc, GetAtomicRegion());
case nsIAccessibleRelation::RELATION_SUBWINDOW_OF:
case nsIAccessibleRelation::RELATION_EMBEDS:
case nsIAccessibleRelation::RELATION_EMBEDDED_BY:
case nsIAccessibleRelation::RELATION_POPUP_FOR:
case nsIAccessibleRelation::RELATION_PARENT_WINDOW_OF:
default:
return Relation();
return Relation();
}
}
@ -2129,12 +2154,29 @@ Accessible::GetRelations(nsIArray **aRelations)
nsCOMPtr<nsIMutableArray> relations = do_CreateInstance(NS_ARRAY_CONTRACTID);
NS_ENSURE_TRUE(relations, NS_ERROR_OUT_OF_MEMORY);
for (uint32_t relType = nsIAccessibleRelation::RELATION_FIRST;
relType < nsIAccessibleRelation::RELATION_LAST;
++relType) {
static const uint32_t relationTypes[] = {
nsIAccessibleRelation::RELATION_LABELLED_BY,
nsIAccessibleRelation::RELATION_LABEL_FOR,
nsIAccessibleRelation::RELATION_DESCRIBED_BY,
nsIAccessibleRelation::RELATION_DESCRIPTION_FOR,
nsIAccessibleRelation::RELATION_NODE_CHILD_OF,
nsIAccessibleRelation::RELATION_NODE_PARENT_OF,
nsIAccessibleRelation::RELATION_CONTROLLED_BY,
nsIAccessibleRelation::RELATION_CONTROLLER_FOR,
nsIAccessibleRelation::RELATION_FLOWS_TO,
nsIAccessibleRelation::RELATION_FLOWS_FROM,
nsIAccessibleRelation::RELATION_MEMBER_OF,
nsIAccessibleRelation::RELATION_SUBWINDOW_OF,
nsIAccessibleRelation::RELATION_EMBEDS,
nsIAccessibleRelation::RELATION_EMBEDDED_BY,
nsIAccessibleRelation::RELATION_POPUP_FOR,
nsIAccessibleRelation::RELATION_PARENT_WINDOW_OF,
nsIAccessibleRelation::RELATION_DEFAULT_BUTTON
};
for (uint32_t idx = 0; idx < ArrayLength(relationTypes); idx++) {
nsCOMPtr<nsIAccessibleRelation> relation;
nsresult rv = GetRelationByType(relType, getter_AddRefs(relation));
nsresult rv = GetRelationByType(relationTypes[idx], getter_AddRefs(relation));
if (NS_SUCCEEDED(rv) && relation) {
uint32_t targets = 0;

View File

@ -13,7 +13,6 @@
#include "nsAccUtils.h"
#include "nsCoreUtils.h"
#include "nsIAccessibleEvent.h"
#include "nsIAccessibleRelation.h"
#include "nsWinUtils.h"
#include "ServiceProvider.h"
#include "Relation.h"
@ -1048,9 +1047,8 @@ AccessibleWrap::get_nRelations(long *aNRelations)
if (IsDefunct())
return CO_E_OBJNOTCONNECTED;
for (uint32_t relType = nsIAccessibleRelation::RELATION_FIRST;
relType <= nsIAccessibleRelation::RELATION_LAST; relType++) {
Relation rel = RelationByType(relType);
for (unsigned int idx = 0; idx < ArrayLength(sRelationTypesForIA2); idx++) {
Relation rel = RelationByType(sRelationTypesForIA2[idx]);
if (rel.Next())
(*aNRelations)++;
}
@ -1074,8 +1072,8 @@ AccessibleWrap::get_relation(long aRelationIndex,
return CO_E_OBJNOTCONNECTED;
long relIdx = 0;
for (uint32_t relType = nsIAccessibleRelation::RELATION_FIRST;
relType <= nsIAccessibleRelation::RELATION_LAST; relType++) {
for (unsigned int idx = 0; idx < ArrayLength(sRelationTypesForIA2); idx++) {
uint32_t relType = sRelationTypesForIA2[idx];
Relation rel = RelationByType(relType);
nsRefPtr<ia2AccessibleRelation> ia2Relation =
new ia2AccessibleRelation(relType, &rel);
@ -1109,9 +1107,9 @@ AccessibleWrap::get_relations(long aMaxRelations,
if (IsDefunct())
return CO_E_OBJNOTCONNECTED;
for (uint32_t relType = nsIAccessibleRelation::RELATION_FIRST;
relType <= nsIAccessibleRelation::RELATION_LAST &&
*aNRelations < aMaxRelations; relType++) {
for (unsigned int idx = 0; idx < ArrayLength(sRelationTypesForIA2) &&
*aNRelations < aMaxRelations; idx++) {
uint32_t relType = sRelationTypesForIA2[idx];
Relation rel = RelationByType(relType);
nsRefPtr<ia2AccessibleRelation> ia2Rel =
new ia2AccessibleRelation(relType, &rel);

View File

@ -9,6 +9,7 @@
#define _NS_ACCESSIBLE_RELATION_WRAP_H
#include "Accessible.h"
#include "nsIAccessibleRelation.h"
#include "nsTArray.h"
@ -60,6 +61,28 @@ private:
ULONG mReferences;
};
/**
* Relations exposed to IAccessible2.
*/
static const uint32_t sRelationTypesForIA2[] = {
nsIAccessibleRelation::RELATION_LABELLED_BY,
nsIAccessibleRelation::RELATION_LABEL_FOR,
nsIAccessibleRelation::RELATION_DESCRIBED_BY,
nsIAccessibleRelation::RELATION_DESCRIPTION_FOR,
nsIAccessibleRelation::RELATION_NODE_CHILD_OF,
nsIAccessibleRelation::RELATION_CONTROLLED_BY,
nsIAccessibleRelation::RELATION_CONTROLLER_FOR,
nsIAccessibleRelation::RELATION_FLOWS_TO,
nsIAccessibleRelation::RELATION_FLOWS_FROM,
nsIAccessibleRelation::RELATION_MEMBER_OF,
nsIAccessibleRelation::RELATION_SUBWINDOW_OF,
nsIAccessibleRelation::RELATION_EMBEDS,
nsIAccessibleRelation::RELATION_EMBEDDED_BY,
nsIAccessibleRelation::RELATION_POPUP_FOR,
nsIAccessibleRelation::RELATION_PARENT_WINDOW_OF
};
} // namespace a11y
} // namespace mozilla

View File

@ -437,6 +437,19 @@ XULTreeAccessible::ChildCount() const
return childCount;
}
Relation
XULTreeAccessible::RelationByType(uint32_t aType)
{
if (aType == nsIAccessibleRelation::RELATION_NODE_PARENT_OF) {
if (mTreeView)
return Relation(new XULTreeItemIterator(this, mTreeView, -1));
return Relation();
}
return Accessible::RelationByType(aType);
}
////////////////////////////////////////////////////////////////////////////////
// XULTreeAccessible: Widgets
@ -798,18 +811,34 @@ XULTreeItemAccessibleBase::RelationByType(uint32_t aType)
if (!mTreeView)
return Relation();
if (aType != nsIAccessibleRelation::RELATION_NODE_CHILD_OF)
return Relation();
switch (aType) {
case nsIAccessibleRelation::RELATION_NODE_CHILD_OF: {
int32_t parentIndex = -1;
if (!NS_SUCCEEDED(mTreeView->GetParentIndex(mRow, &parentIndex)))
return Relation();
int32_t parentIndex = -1;
if (!NS_SUCCEEDED(mTreeView->GetParentIndex(mRow, &parentIndex)))
return Relation();
if (parentIndex == -1)
return Relation(mParent);
if (parentIndex == -1)
return Relation(mParent);
XULTreeAccessible* treeAcc = mParent->AsXULTree();
return Relation(treeAcc->GetTreeItemAccessible(parentIndex));
}
XULTreeAccessible* treeAcc = mParent->AsXULTree();
return Relation(treeAcc->GetTreeItemAccessible(parentIndex));
case nsIAccessibleRelation::RELATION_NODE_PARENT_OF: {
bool isTrue = false;
if (NS_FAILED(mTreeView->IsContainerEmpty(mRow, &isTrue)) || isTrue)
return Relation();
if (NS_FAILED(mTreeView->IsContainerOpen(mRow, &isTrue)) || !isTrue)
return Relation();
XULTreeAccessible* tree = mParent->AsXULTree();
return Relation(new XULTreeItemIterator(tree, mTreeView, mRow));
}
default:
return Relation();
}
}
uint8_t

View File

@ -47,6 +47,7 @@ public:
virtual Accessible* GetChildAt(uint32_t aIndex);
virtual uint32_t ChildCount() const;
virtual Relation RelationByType(uint32_t aType);
// SelectAccessible
virtual already_AddRefed<nsIArray> SelectedItems();

View File

@ -610,6 +610,17 @@ function getTextFromClipboard()
*/
function prettyName(aIdentifier)
{
if (aIdentifier instanceof Array) {
var msg = "";
for (var idx = 0; idx < aIdentifier.length; idx++) {
if (msg != "")
msg += ", ";
msg += prettyName(aIdentifier[idx]);
}
return msg;
}
if (aIdentifier instanceof nsIAccessible) {
var acc = getAccessible(aIdentifier);
var msg = "[" + getNodePrettyName(acc.DOMNode);

View File

@ -14,6 +14,7 @@ const RELATION_LABEL_FOR = nsIAccessibleRelation.RELATION_LABEL_FOR;
const RELATION_LABELLED_BY = nsIAccessibleRelation.RELATION_LABELLED_BY;
const RELATION_MEMBER_OF = nsIAccessibleRelation.RELATION_MEMBER_OF;
const RELATION_NODE_CHILD_OF = nsIAccessibleRelation.RELATION_NODE_CHILD_OF;
const RELATION_NODE_PARENT_OF = nsIAccessibleRelation.RELATION_NODE_PARENT_OF;
const RELATION_PARENT_WINDOW_OF = nsIAccessibleRelation.RELATION_PARENT_WINDOW_OF;
const RELATION_POPUP_FOR = nsIAccessibleRelation.RELATION_POPUP_FOR;
const RELATION_SUBWINDOW_OF = nsIAccessibleRelation.RELATION_SUBWINDOW_OF;

View File

@ -71,7 +71,8 @@
testRelation("treeitem3", RELATION_NODE_CHILD_OF, "tree");
testRelation("treeitem4", RELATION_NODE_CHILD_OF, "tree");
testRelation("treeitem5", RELATION_NODE_CHILD_OF, "treeitem4");
testRelation("treeitem6", RELATION_NODE_CHILD_OF, "treeitem5");
testRelation("treeitem6", RELATION_NODE_CHILD_OF, "tree");
testRelation("treeitem7", RELATION_NODE_CHILD_OF, "treeitem6");
// 'node child of' relation for row role of treegrid
testRelation("treegridrow1", RELATION_NODE_CHILD_OF, "treegrid");
@ -86,6 +87,19 @@
var iframeDocAcc = getAccessible(iframeDoc);
testRelation(iframeDocAcc, RELATION_NODE_CHILD_OF, iframeAcc);
// 'node parent of' relation on ARIA tree and treegrid.
testRelation("tree", RELATION_NODE_PARENT_OF,
["treeitem1", "treeitem2", // aria-owns
"treeitem3", "treeitem4", "treeitem6"]); // children
testRelation("treeitem4", RELATION_NODE_PARENT_OF,
"treeitem5"); // aria-level
testRelation("treeitem6", RELATION_NODE_PARENT_OF,
"treeitem7"); // // group role
testRelation("treegridrow2", RELATION_NODE_PARENT_OF, "treegridrow3");
testRelation("treegrid", RELATION_NODE_PARENT_OF,
["treegridrow1", "treegridrow2"]);
// aria-controls
getAccessible("tab");
todo(false,
@ -141,22 +155,27 @@
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=475298"
title="mochitests for accessible relations">
Mozilla Bug 475298
Bug 475298
</a>
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=527461"
title="Implement RELATION_NODE_PARENT_OF">
Bug 527461
</a>
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=558036"
title="make HTML <output> accessible">
Mozilla Bug 558036
Bug 558036
</a>
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=682790"
title="Ignore implicit label association when it's associated explicitly">
Mozilla Bug 682790
Bug 682790
</a>
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=687393"
title="HTML select options gets relation from containing label">
Mozilla Bug 687393
Bug 687393
</a>
<p id="display"></p>
<div id="content" style="display: none"></div>
@ -227,8 +246,9 @@
<div role="treeitem" id="treeitem3">Blue</div>
<div role="treeitem" id="treeitem4" aria-level="1">Green</div>
<div role="treeitem" id="treeitem5" aria-level="2">Light green</div>
<div role="treeitem" id="treeitem6" aria-level="1">Green2</div>
<div role="group">
<div role="treeitem" id="treeitem6">Super light green</div>
<div role="treeitem" id="treeitem7">Super light green</div>
</div>
</div>

View File

@ -47,6 +47,11 @@
var treeitem6 = treeitem5.nextSibling;
testRelation(treeitem6, RELATION_NODE_CHILD_OF, [tree]);
testRelation(tree, RELATION_NODE_PARENT_OF,
[treeitem1, treeitem2, treeitem5, treeitem6]);
testRelation(treeitem2, RELATION_NODE_PARENT_OF,
[treeitem3, treeitem4]);
// treeitems and treecells shouldn't pick up relations from tree
testRelation(treeitem1, RELATION_LABELLED_BY, null);
testRelation(treeitem1.firstChild, RELATION_LABELLED_BY, null);
@ -64,12 +69,17 @@
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=503727"
title="Reorganize implementation of XUL tree accessibility">
Mozilla Bug 503727
Bug 503727
</a>
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=527461"
title="Implement RELATION_NODE_PARENT_OF">
Bug 527461
</a>
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=691248"
title="XUL tree items shouldn't pick up relations from XUL tree">
Mozilla Bug 691248
Bug 691248
</a>
<p id="display"></p>
<div id="content" style="display: none">