Bug 345780 - Support multiple targets for same relation, r=davidb, r=MarcoZ, sr=neil

This commit is contained in:
Alexander Surkov 2009-02-10 11:03:30 +01:00
parent ce48d2a2db
commit 30549e95c1
30 changed files with 934 additions and 377 deletions

View File

@ -58,7 +58,7 @@ interface nsIAccessibleRelation;
*
* @status UNDER_REVIEW
*/
[scriptable, uuid(670fc322-14ec-4f3b-8279-9d62ab8895c0)]
[scriptable, uuid(76c72c70-2c4b-4fce-8e75-b121db024333)]
interface nsIAccessible : nsISupports
{
/**
@ -251,10 +251,10 @@ interface nsIAccessible : nsISupports
nsIAccessible getAccessibleBelow();
/**
* Return accessible related to this one by the given relation type (see.
* Return accessible relation by the given relation type (see.
* constants defined in nsIAccessibleRelation).
*/
nsIAccessible getAccessibleRelated(in unsigned long aRelationType);
nsIAccessibleRelation getRelationByType(in unsigned long aRelationType);
/**
* Returns the number of accessible relations for this object.

View File

@ -1053,7 +1053,6 @@ refRelationSetCB(AtkObject *aAtkObj)
return relation_set;
}
AtkObject *accessible_array[1];
AtkRelation* relation;
PRUint32 relationType[] = {nsIAccessibleRelation::RELATION_LABELLED_BY,
@ -1074,14 +1073,28 @@ refRelationSetCB(AtkObject *aAtkObj)
atk_relation_set_remove(relation_set, relation);
}
nsIAccessible* accRelated;
nsresult rv = accWrap->GetAccessibleRelated(relationType[i], &accRelated);
if (NS_SUCCEEDED(rv) && accRelated) {
accessible_array[0] = nsAccessibleWrap::GetAtkObject(accRelated);
relation = atk_relation_new(accessible_array, 1,
static_cast<AtkRelationType>(relationType[i]));
atk_relation_set_add(relation_set, relation);
g_object_unref(relation);
nsCOMPtr<nsIAccessibleRelation> geckoRelation;
nsresult rv = accWrap->GetRelationByType(relationType[i],
getter_AddRefs(geckoRelation));
if (NS_SUCCEEDED(rv) && geckoRelation) {
PRUint32 targetsCount = 0;
geckoRelation->GetTargetsCount(&targetsCount);
if (targetsCount) {
AtkObject** accessible_array = new AtkObject*[targetsCount];
for (PRUint32 index = 0; index < targetsCount; index++) {
nsCOMPtr<nsIAccessible> geckoTarget;
geckoRelation->GetTarget(index, getter_AddRefs(geckoTarget));
accessible_array[index] =
nsAccessibleWrap::GetAtkObject(geckoTarget);
}
relation = atk_relation_new(accessible_array, targetsCount,
static_cast<AtkRelationType>(relationType[i]));
atk_relation_set_add(relation_set, relation);
g_object_unref(relation);
delete [] accessible_array;
}
}
}

View File

@ -82,6 +82,7 @@ CPPSRCS = \
nsCoreUtils.cpp \
nsAccUtils.cpp \
nsNameUtils.cpp \
nsRelUtils.cpp \
nsAccessibilityService.cpp \
nsAccessible.cpp \
nsAccessibleRelation.cpp \

View File

@ -84,6 +84,7 @@
#include "nsIPrefBranch.h"
#include "nsIURI.h"
#include "nsITimer.h"
#include "nsArrayUtils.h"
#include "nsIMutableArray.h"
#include "nsIObserverService.h"
#include "nsIServiceManager.h"
@ -1701,48 +1702,35 @@ nsresult nsAccessible::GetTextFromRelationID(nsIAtom *aIDProperty, nsString &aNa
// Get DHTML name from content subtree pointed to by ID attribute
aName.Truncate();
NS_ASSERTION(mDOMNode, "Called from shutdown accessible");
nsCOMPtr<nsIContent> content = nsCoreUtils::GetRoleContent(mDOMNode);
if (!content)
return NS_OK;
nsAutoString ids;
if (!content->GetAttr(kNameSpaceID_None, aIDProperty, ids))
nsCOMPtr<nsIArray> refElms;
nsCoreUtils::GetElementsByIDRefsAttr(content, aIDProperty,
getter_AddRefs(refElms));
if (!refElms)
return NS_OK;
ids.CompressWhitespace(PR_TRUE, PR_TRUE);
PRUint32 count = 0;
nsresult rv = refElms->GetLength(&count);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(content->GetOwnerDoc());
NS_ENSURE_TRUE(domDoc, NS_ERROR_FAILURE);
nsCOMPtr<nsIContent> refContent;
for (PRUint32 idx = 0; idx < count; idx++) {
refContent = do_QueryElementAt(refElms, idx, &rv);
NS_ENSURE_SUCCESS(rv, rv);
// Support idlist as in aria-labelledby="id1 id2 id3"
while (!ids.IsEmpty()) {
nsAutoString id;
PRInt32 idLength = ids.FindChar(' ');
NS_ASSERTION(idLength != 0, "Should not be 0 because of CompressWhitespace() call above");
if (idLength == kNotFound) {
id = ids;
ids.Truncate();
} else {
id = Substring(ids, 0, idLength);
ids.Cut(0, idLength + 1);
}
if (!aName.IsEmpty()) {
if (!aName.IsEmpty())
aName += ' '; // Need whitespace between multiple labels or descriptions
}
nsCOMPtr<nsIDOMElement> labelElement;
domDoc->GetElementById(id, getter_AddRefs(labelElement));
content = do_QueryInterface(labelElement);
if (!content) {
return NS_OK;
}
// We have a label content
nsresult rv = AppendFlatStringFromSubtree(content, &aName);
if (NS_SUCCEEDED(rv)) {
aName.CompressWhitespace();
}
rv = AppendFlatStringFromSubtree(refContent, &aName);
NS_ENSURE_SUCCESS(rv, rv);
}
aName.CompressWhitespace();
return NS_OK;
}
@ -1922,7 +1910,8 @@ NS_IMETHODIMP nsAccessible::GetFinalRole(PRUint32 *aRole)
*aRole = nsIAccessibleRole::ROLE_COMBOBOX_LIST;
}
else { // Check to see if combo owns the listbox instead
GetAccessibleRelated(nsIAccessibleRelation::RELATION_NODE_CHILD_OF, getter_AddRefs(possibleCombo));
possibleCombo = nsRelUtils::
GetRelatedAccessible(this, nsIAccessibleRelation::RELATION_NODE_CHILD_OF);
if (nsAccUtils::Role(possibleCombo) == nsIAccessibleRole::ROLE_COMBOBOX)
*aRole = nsIAccessibleRole::ROLE_COMBOBOX_LIST;
}
@ -2228,10 +2217,8 @@ nsAccessible::GetState(PRUint32 *aState, PRUint32 *aExtraState)
} else {
// Expose 'selected' state on ARIA tab if the focus is on internal element
// of related tabpanel.
nsCOMPtr<nsIAccessible> tabPanel;
rv = GetAccessibleRelated(nsIAccessibleRelation::RELATION_LABEL_FOR,
getter_AddRefs(tabPanel));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIAccessible> tabPanel = nsRelUtils::
GetRelatedAccessible(this, nsIAccessibleRelation::RELATION_LABEL_FOR);
if (nsAccUtils::Role(tabPanel) == nsIAccessibleRole::ROLE_PROPERTYPAGE) {
nsCOMPtr<nsIAccessNode> tabPanelAccessNode(do_QueryInterface(tabPanel));
@ -2705,78 +2692,119 @@ nsIDOMNode* nsAccessible::GetAtomicRegion()
return atomicRegion;
}
/* nsIAccessible getAccessibleRelated(); */
NS_IMETHODIMP nsAccessible::GetAccessibleRelated(PRUint32 aRelationType, nsIAccessible **aRelated)
// nsIAccessible getRelationByType()
NS_IMETHODIMP
nsAccessible::GetRelationByType(PRUint32 aRelationType,
nsIAccessibleRelation **aRelation)
{
// When adding support for relations, make sure to add them to
// appropriate places in nsAccessibleWrap implementations
*aRelated = nsnull;
NS_ENSURE_ARG_POINTER(aRelation);
*aRelation = nsnull;
// Relationships are defined on the same content node
// that the role would be defined on
if (IsDefunct())
return NS_ERROR_FAILURE;
// Relationships are defined on the same content node that the role would be
// defined on.
nsIContent *content = nsCoreUtils::GetRoleContent(mDOMNode);
if (!content) {
return NS_ERROR_FAILURE; // Node already shut down
}
if (!content)
return NS_OK;
nsCOMPtr<nsIDOMNode> relatedNode;
nsAutoString relatedID;
nsresult rv;
// Search for the related DOM node according to the specified "relation type"
switch (aRelationType)
{
case nsIAccessibleRelation::RELATION_LABEL_FOR:
{
if (content->Tag() == nsAccessibilityAtoms::label) {
nsIAtom *relatedIDAttr = content->IsNodeOfType(nsINode::eHTML) ?
nsIAtom *IDAttr = content->IsNodeOfType(nsINode::eHTML) ?
nsAccessibilityAtoms::_for : nsAccessibilityAtoms::control;
content->GetAttr(kNameSpaceID_None, relatedIDAttr, relatedID);
rv = nsRelUtils::
AddTargetFromIDRefAttr(aRelationType, aRelation, content, IDAttr);
NS_ENSURE_SUCCESS(rv, rv);
if (rv != NS_OK_NO_RELATION_TARGET)
return NS_OK; // XXX bug 381599, avoid performance problems
}
if (relatedID.IsEmpty()) {
relatedNode =
do_QueryInterface(nsCoreUtils::FindNeighbourPointingToNode(content, nsAccessibilityAtoms::aria_labelledby));
}
break;
return nsRelUtils::
AddTargetFromNeighbour(aRelationType, aRelation, content,
nsAccessibilityAtoms::aria_labelledby);
}
case nsIAccessibleRelation::RELATION_LABELLED_BY:
{
if (!content->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_labelledby, relatedID)) {
relatedNode = do_QueryInterface(nsCoreUtils::GetLabelContent(content));
}
break;
rv = nsRelUtils::
AddTargetFromIDRefsAttr(aRelationType, aRelation, content,
nsAccessibilityAtoms::aria_labelledby);
NS_ENSURE_SUCCESS(rv, rv);
if (rv != NS_OK_NO_RELATION_TARGET)
return NS_OK; // XXX bug 381599, avoid performance problems
return nsRelUtils::
AddTargetFromContent(aRelationType, aRelation,
nsCoreUtils::GetLabelContent(content));
}
case nsIAccessibleRelation::RELATION_DESCRIBED_BY:
{
if (!content->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_describedby, relatedID)) {
relatedNode = do_QueryInterface(
nsCoreUtils::FindNeighbourPointingToNode(content, nsAccessibilityAtoms::control, nsAccessibilityAtoms::description));
}
break;
rv = nsRelUtils::
AddTargetFromIDRefsAttr(aRelationType, aRelation, content,
nsAccessibilityAtoms::aria_describedby);
NS_ENSURE_SUCCESS(rv, rv);
if (rv != NS_OK_NO_RELATION_TARGET)
return NS_OK; // XXX bug 381599, avoid performance problems
return nsRelUtils::
AddTargetFromNeighbour(aRelationType, aRelation, content,
nsAccessibilityAtoms::control,
nsAccessibilityAtoms::description);
}
case nsIAccessibleRelation::RELATION_DESCRIPTION_FOR:
{
relatedNode =
do_QueryInterface(nsCoreUtils::FindNeighbourPointingToNode(content, nsAccessibilityAtoms::aria_describedby));
rv = nsRelUtils::
AddTargetFromNeighbour(aRelationType, aRelation, content,
nsAccessibilityAtoms::aria_describedby);
NS_ENSURE_SUCCESS(rv, rv);
if (!relatedNode && content->Tag() == nsAccessibilityAtoms::description &&
if (rv != NS_OK_NO_RELATION_TARGET)
return NS_OK; // XXX bug 381599, avoid performance problems
if (content->Tag() == nsAccessibilityAtoms::description &&
content->IsNodeOfType(nsINode::eXUL)) {
// This affectively adds an optional control attribute to xul:description,
// which only affects accessibility, by allowing the description to be
// tied to a control.
content->GetAttr(kNameSpaceID_None,
nsAccessibilityAtoms::control, relatedID);
return nsRelUtils::
AddTargetFromIDRefAttr(aRelationType, aRelation, content,
nsAccessibilityAtoms::control);
}
break;
return NS_OK;
}
case nsIAccessibleRelation::RELATION_NODE_CHILD_OF:
{
relatedNode =
do_QueryInterface(nsCoreUtils::FindNeighbourPointingToNode(content, nsAccessibilityAtoms::aria_owns));
if (!relatedNode && mRoleMapEntry && mRoleMapEntry->role == nsIAccessibleRole::ROLE_OUTLINEITEM) {
// This is an ARIA tree that doesn't use owns, so we need to get the parent the hard way
nsAccUtils::GetARIATreeItemParent(this, content, aRelated);
return NS_OK;
rv = nsRelUtils::
AddTargetFromNeighbour(aRelationType, aRelation, content,
nsAccessibilityAtoms::aria_owns);
NS_ENSURE_SUCCESS(rv, rv);
if (rv != NS_OK_NO_RELATION_TARGET)
return NS_OK; // XXX bug 381599, avoid performance problems
if (mRoleMapEntry &&
mRoleMapEntry->role == nsIAccessibleRole::ROLE_OUTLINEITEM) {
// This is an ARIA tree that doesn't use owns, so we need to get
// the parent the hard way.
nsCOMPtr<nsIAccessible> accTarget;
nsAccUtils::GetARIATreeItemParent(this, content,
getter_AddRefs(accTarget));
return nsRelUtils::AddTarget(aRelationType, aRelation, accTarget);
}
// If accessible is in its own Window then we should provide NODE_CHILD_OF relation
// so that MSAA clients can easily get to true parent instead of getting to oleacc's
// ROLE_WINDOW accessible which will prevent us from going up further (because it is
@ -2787,34 +2815,44 @@ NS_IMETHODIMP nsAccessible::GetAccessibleRelated(PRUint32 aRelationType, nsIAcce
if (view) {
nsIScrollableFrame *scrollFrame = do_QueryFrame(frame);
if (scrollFrame || view->GetWidget()) {
return GetParent(aRelated);
nsCOMPtr<nsIAccessible> accTarget;
GetParent(getter_AddRefs(accTarget));
return nsRelUtils::AddTarget(aRelationType, aRelation, accTarget);
}
}
}
break;
return NS_OK;
}
case nsIAccessibleRelation::RELATION_CONTROLLED_BY:
{
relatedNode =
do_QueryInterface(nsCoreUtils::FindNeighbourPointingToNode(content, nsAccessibilityAtoms::aria_controls));
break;
return nsRelUtils::
AddTargetFromNeighbour(aRelationType, aRelation, content,
nsAccessibilityAtoms::aria_controls);
}
case nsIAccessibleRelation::RELATION_CONTROLLER_FOR:
{
content->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_controls, relatedID);
break;
return nsRelUtils::
AddTargetFromIDRefsAttr(aRelationType, aRelation, content,
nsAccessibilityAtoms::aria_controls);
}
case nsIAccessibleRelation::RELATION_FLOWS_TO:
{
content->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::aria_flowto, relatedID);
break;
return nsRelUtils::
AddTargetFromIDRefsAttr(aRelationType, aRelation, content,
nsAccessibilityAtoms::aria_flowto);
}
case nsIAccessibleRelation::RELATION_FLOWS_FROM:
{
relatedNode =
do_QueryInterface(nsCoreUtils::FindNeighbourPointingToNode(content, nsAccessibilityAtoms::aria_flowto));
break;
return nsRelUtils::
AddTargetFromNeighbour(aRelationType, aRelation, content,
nsAccessibilityAtoms::aria_flowto);
}
case nsIAccessibleRelation::RELATION_DEFAULT_BUTTON:
{
if (content->IsNodeOfType(nsINode::eHTML)) {
@ -2824,8 +2862,12 @@ NS_IMETHODIMP nsAccessible::GetAccessibleRelated(PRUint32 aRelationType, nsIAcce
nsCOMPtr<nsIDOMHTMLFormElement> htmlform;
control->GetForm(getter_AddRefs(htmlform));
nsCOMPtr<nsIForm> form(do_QueryInterface(htmlform));
if (form)
relatedNode = do_QueryInterface(form->GetDefaultSubmitElement());
if (form) {
nsCOMPtr<nsIContent> formContent =
do_QueryInterface(form->GetDefaultSubmitElement());
return nsRelUtils::AddTargetFromContent(aRelationType, aRelation,
formContent);
}
}
}
else {
@ -2864,37 +2906,24 @@ NS_IMETHODIMP nsAccessible::GetAccessibleRelated(PRUint32 aRelationType, nsIAcce
}
}
}
relatedNode = do_QueryInterface(buttonEl);
nsCOMPtr<nsIContent> relatedContent(do_QueryInterface(buttonEl));
return nsRelUtils::AddTargetFromContent(aRelationType, aRelation,
relatedContent);
}
}
break;
return NS_OK;
}
case nsIAccessibleRelation::RELATION_MEMBER_OF:
{
relatedNode = GetAtomicRegion();
break;
nsCOMPtr<nsIContent> regionContent = do_QueryInterface(GetAtomicRegion());
return nsRelUtils::
AddTargetFromContent(aRelationType, aRelation, regionContent);
}
default:
return NS_ERROR_NOT_IMPLEMENTED;
return NS_ERROR_INVALID_ARG;
}
if (!relatedID.IsEmpty()) {
// In some cases we need to get the relatedNode from an ID-style attribute
nsCOMPtr<nsIDOMDocument> domDoc;
mDOMNode->GetOwnerDocument(getter_AddRefs(domDoc));
NS_ENSURE_TRUE(domDoc, NS_ERROR_FAILURE);
nsCOMPtr<nsIDOMElement> relatedEl;
domDoc->GetElementById(relatedID, getter_AddRefs(relatedEl));
relatedNode = do_QueryInterface(relatedEl);
}
// Return the corresponding accessible if the related DOM node is found
if (relatedNode) {
nsCOMPtr<nsIAccessibilityService> accService = GetAccService();
NS_ENSURE_TRUE(accService, NS_ERROR_FAILURE);
accService->GetAccessibleInWeakShell(relatedNode, mWeakShell, aRelated);
}
return NS_OK;
}
NS_IMETHODIMP
@ -2945,16 +2974,12 @@ nsAccessible::GetRelations(nsIArray **aRelations)
for (PRUint32 relType = nsIAccessibleRelation::RELATION_FIRST;
relType < nsIAccessibleRelation::RELATION_LAST;
++relType) {
nsCOMPtr<nsIAccessible> accessible;
GetAccessibleRelated(relType, getter_AddRefs(accessible));
if (accessible) {
nsCOMPtr<nsIAccessibleRelation> relation =
new nsAccessibleRelationWrap(relType, accessible);
NS_ENSURE_TRUE(relation, NS_ERROR_OUT_OF_MEMORY);
nsCOMPtr<nsIAccessibleRelation> relation;
nsresult rv = GetRelationByType(relType, getter_AddRefs(relation));
if (NS_SUCCEEDED(rv) && relation)
relations->AppendElement(relation, PR_FALSE);
}
}
NS_ADDREF(*aRelations = relations);

View File

@ -48,9 +48,10 @@
#include "nsIAccessibleValue.h"
#include "nsIAccessibleRole.h"
#include "nsIAccessibleStates.h"
#include "nsAccessibleRelationWrap.h"
#include "nsIAccessibleEvent.h"
#include "nsRelUtils.h"
#include "nsIDOMNodeList.h"
#include "nsINameSpaceManager.h"
#include "nsWeakReference.h"

View File

@ -38,17 +38,21 @@
#include "nsAccessibleRelation.h"
#include "nsIMutableArray.h"
#include "nsArrayUtils.h"
#include "nsComponentManagerUtils.h"
nsAccessibleRelation::
nsAccessibleRelation(PRUint32 aType, nsIAccessible *aTarget) :
mType(aType), mTarget(aTarget)
mType(aType)
{
mTargets = do_CreateInstance(NS_ARRAY_CONTRACTID);
if (aTarget)
mTargets->AppendElement(aTarget, PR_FALSE);
}
// nsISupports
NS_IMPL_ISUPPORTS1(nsAccessibleRelation, nsIAccessibleRelation)
NS_IMPL_ISUPPORTS2(nsAccessibleRelation, nsAccessibleRelation,
nsIAccessibleRelation)
// nsIAccessibleRelation
NS_IMETHODIMP
@ -64,33 +68,48 @@ NS_IMETHODIMP
nsAccessibleRelation::GetTargetsCount(PRUint32 *aCount)
{
NS_ENSURE_ARG_POINTER(aCount);
*aCount = 0;
*aCount = 1;
return NS_OK;
NS_ENSURE_TRUE(mTargets, NS_ERROR_NOT_INITIALIZED);
return mTargets->GetLength(aCount);
}
NS_IMETHODIMP
nsAccessibleRelation::GetTarget(PRUint32 aIndex, nsIAccessible **aTarget)
{
NS_ENSURE_ARG_POINTER(aTarget);
*aTarget = nsnull;
if (aIndex != 0)
return NS_ERROR_INVALID_ARG;
NS_ENSURE_TRUE(mTargets, NS_ERROR_NOT_INITIALIZED);
NS_IF_ADDREF(*aTarget = mTarget);
nsresult rv = NS_OK;
nsCOMPtr<nsIAccessible> target = do_QueryElementAt(mTargets, aIndex, &rv);
NS_ENSURE_SUCCESS(rv, rv);
target.swap(*aTarget);
return NS_OK;
}
NS_IMETHODIMP
nsAccessibleRelation::GetTargets(nsIArray **aRelations)
nsAccessibleRelation::GetTargets(nsIArray **aTargets)
{
NS_ENSURE_ARG_POINTER(aRelations);
NS_ENSURE_ARG_POINTER(aTargets);
*aTargets = nsnull;
nsCOMPtr<nsIMutableArray> relations = do_CreateInstance(NS_ARRAY_CONTRACTID);
NS_ENSURE_TRUE(relations, NS_ERROR_OUT_OF_MEMORY);
NS_ENSURE_TRUE(mTargets, NS_ERROR_NOT_INITIALIZED);
relations->AppendElement(mTarget, PR_FALSE);
NS_ADDREF(*aRelations = relations);
NS_ADDREF(*aTargets = mTargets);
return NS_OK;
}
// nsAccessibleRelation
nsresult
nsAccessibleRelation::AddTarget(nsIAccessible *aTarget)
{
NS_ENSURE_ARG(aTarget);
NS_ENSURE_TRUE(mTargets, NS_ERROR_NOT_INITIALIZED);
return mTargets->AppendElement(aTarget, PR_FALSE);
}

View File

@ -43,7 +43,19 @@
#include "nsIAccessibleRelation.h"
#include "nsCOMPtr.h"
#include "nsIMutableArray.h"
#define NS_ACCRELATION_IMPL_CID \
{ \
0xb20390d0, \
0x40d3, \
0x4c76, \
{ 0xb6, 0x2e, 0xc2, 0x30, 0xc8, 0xea, 0x0c, 0x1e } \
}
/**
* Class represents an accessible relation.
*/
class nsAccessibleRelation: public nsIAccessibleRelation
{
public:
@ -52,9 +64,20 @@ public:
NS_DECL_ISUPPORTS
NS_DECL_NSIACCESSIBLERELATION
NS_DECLARE_STATIC_IID_ACCESSOR(NS_ACCRELATION_IMPL_CID)
/**
* Add target for the given key.
*
* @param aTarget - accessible target for the given relation.
*/
nsresult AddTarget(nsIAccessible *aTarget);
private:
PRUint32 mType;
nsCOMPtr<nsIAccessible> mTarget;
nsCOMPtr<nsIMutableArray> mTargets;
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsAccessibleRelation, NS_ACCRELATION_IMPL_CID)
#endif

View File

@ -70,6 +70,7 @@
#include "nsContentCID.h"
#include "nsComponentManagerUtils.h"
#include "nsIInterfaceRequestorUtils.h"
#include "nsIMutableArray.h"
static NS_DEFINE_IID(kRangeCID, NS_RANGE_CID);
@ -742,6 +743,51 @@ nsCoreUtils::GetLanguageFor(nsIContent *aContent, nsIContent *aRootContent,
walkUp = walkUp->GetParent();
}
void
nsCoreUtils::GetElementsByIDRefsAttr(nsIContent *aContent, nsIAtom *aAttr,
nsIArray **aRefElements)
{
*aRefElements = nsnull;
nsAutoString ids;
if (!aContent->GetAttr(kNameSpaceID_None, aAttr, ids))
return;
ids.CompressWhitespace(PR_TRUE, PR_TRUE);
nsCOMPtr<nsIDOMDocument> document = do_QueryInterface(aContent->GetOwnerDoc());
NS_ASSERTION(document, "The given node is not in document!");
if (!document)
return;
nsCOMPtr<nsIMutableArray> refElms = do_CreateInstance(NS_ARRAY_CONTRACTID);
while (!ids.IsEmpty()) {
nsAutoString id;
PRInt32 idLength = ids.FindChar(' ');
NS_ASSERTION(idLength != 0,
"Should not be 0 because of CompressWhitespace() call above");
if (idLength == kNotFound) {
id = ids;
ids.Truncate();
} else {
id = Substring(ids, 0, idLength);
ids.Cut(0, idLength + 1);
}
nsCOMPtr<nsIDOMElement> refElement;
document->GetElementById(id, getter_AddRefs(refElement));
if (!refElement)
continue;
refElms->AppendElement(refElement, PR_FALSE);
}
NS_ADDREF(*aRefElements = refElms);
return;
}
void
nsCoreUtils::GetComputedStyleDeclaration(const nsAString& aPseudoElt,
nsIDOMNode *aNode,

View File

@ -45,6 +45,7 @@
#include "nsIContent.h"
#include "nsIFrame.h"
#include "nsIDocShellTreeItem.h"
#include "nsIArray.h"
#include "nsPoint.h"
class nsCoreUtils
@ -233,6 +234,17 @@ public:
static void GetLanguageFor(nsIContent *aContent, nsIContent *aRootContent,
nsAString& aLanguage);
/**
* Return the array of elements the given node is referred to by its
* IDRefs attribute.
*
* @param aContent [in] the given node
* @param aAttr [in] IDRefs attribute on the given node
* @param aRefElements [out] result array of elements
*/
static void GetElementsByIDRefsAttr(nsIContent *aContent, nsIAtom *aAttr,
nsIArray **aRefElements);
/**
* Return computed styles declaration for the given node.
*/

View File

@ -0,0 +1,160 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2009
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Alexander Surkov <surkov.alexander@gmail.com> (original author)
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsRelUtils.h"
#include "nsAccessNode.h"
#include "nsIDOMDocument.h"
#include "nsIDOMElement.h"
#include "nsAutoPtr.h"
#include "nsArrayUtils.h"
already_AddRefed<nsIAccessible>
nsRelUtils::GetRelatedAccessible(nsIAccessible *aAccessible,
PRUint32 aRelationType)
{
nsCOMPtr<nsIAccessibleRelation> relation;
nsresult rv = aAccessible->GetRelationByType(aRelationType,
getter_AddRefs(relation));
if (NS_FAILED(rv) || !relation)
return nsnull;
nsIAccessible *targetAccessible = nsnull;
rv = relation->GetTarget(0, &targetAccessible);
return targetAccessible;
}
nsresult
nsRelUtils::AddTarget(PRUint32 aRelationType, nsIAccessibleRelation **aRelation,
nsIAccessible *aTarget)
{
if (!aTarget)
return NS_OK_NO_RELATION_TARGET;
if (*aRelation) {
nsRefPtr<nsAccessibleRelation> relation = QueryAccRelation(*aRelation);
return relation->AddTarget(aTarget);
}
*aRelation = new nsAccessibleRelationWrap(aRelationType, aTarget);
NS_ENSURE_TRUE(*aRelation, NS_ERROR_OUT_OF_MEMORY);
NS_ADDREF(*aRelation);
return NS_OK;
}
nsresult
nsRelUtils::AddTargetFromContent(PRUint32 aRelationType,
nsIAccessibleRelation **aRelation,
nsIContent *aContent)
{
if (!aContent)
return NS_OK_NO_RELATION_TARGET;
nsCOMPtr<nsIAccessibilityService> accService = nsAccessNode::GetAccService();
nsCOMPtr<nsIDOMNode> node(do_QueryInterface(aContent));
nsCOMPtr<nsIAccessible> accessible;
accService->GetAccessibleFor(node, getter_AddRefs(accessible));
return AddTarget(aRelationType, aRelation, accessible);
}
nsresult
nsRelUtils::AddTargetFromIDRefAttr(PRUint32 aRelationType,
nsIAccessibleRelation **aRelation,
nsIContent *aContent, nsIAtom *aAttr)
{
nsAutoString id;
if (!aContent->GetAttr(kNameSpaceID_None, aAttr, id))
return NS_OK_NO_RELATION_TARGET;
nsCOMPtr<nsIDOMDocument> document =
do_QueryInterface(aContent->GetOwnerDoc());
NS_ASSERTION(document, "The given node is not in document!");
if (!document)
return NS_OK_NO_RELATION_TARGET;
nsCOMPtr<nsIDOMElement> refElm;
document->GetElementById(id, getter_AddRefs(refElm));
nsCOMPtr<nsIContent> refContent(do_QueryInterface(refElm));
return AddTargetFromContent(aRelationType, aRelation, refContent);
}
nsresult
nsRelUtils::AddTargetFromIDRefsAttr(PRUint32 aRelationType,
nsIAccessibleRelation **aRelation,
nsIContent *aContent, nsIAtom *aAttr)
{
nsCOMPtr<nsIArray> refElms;
nsCoreUtils::GetElementsByIDRefsAttr(aContent, aAttr, getter_AddRefs(refElms));
if (!refElms)
return NS_OK_NO_RELATION_TARGET;
PRUint32 count = 0;
nsresult rv = refElms->GetLength(&count);
if (NS_FAILED(rv) || count == 0)
return NS_OK_NO_RELATION_TARGET;
nsCOMPtr<nsIContent> content;
for (PRUint32 idx = 0; idx < count; idx++) {
content = do_QueryElementAt(refElms, idx, &rv);
NS_ENSURE_SUCCESS(rv, rv);
rv = AddTargetFromContent(aRelationType, aRelation, content);
NS_ENSURE_SUCCESS(rv, rv);
}
return NS_OK;
}
nsresult
nsRelUtils::AddTargetFromNeighbour(PRUint32 aRelationType,
nsIAccessibleRelation **aRelation,
nsIContent *aContent,
nsIAtom *aNeighboutAttr,
nsIAtom *aNeighboutTagName)
{
return AddTargetFromContent(
aRelationType, aRelation,
nsCoreUtils::FindNeighbourPointingToNode(aContent, aNeighboutAttr,
aNeighboutTagName));
}

View File

@ -0,0 +1,151 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2009
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Alexander Surkov <surkov.alexander@gmail.com> (original author)
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef _nsRelUtils_H_
#define _nsRelUtils_H_
#include "nsAccessibleRelationWrap.h"
#include "nsIAtom.h"
#include "nsIContent.h"
// Used by AddTarget...() methods. Returned when can't get target accessible.
#define NS_OK_NO_RELATION_TARGET \
NS_ERROR_GENERATE_SUCCESS(NS_ERROR_MODULE_GENERAL, 0x24)
/**
* Utils class designed to work with accessible relations.
*/
class nsRelUtils
{
public:
/**
* Return first target of the relation of the given relation type for
* the given accessible.
*
* @param aAccessible [in] the accessible to get an relation
* @param aRelationType [in] relation type
* @return an accessible
*/
static already_AddRefed<nsIAccessible>
GetRelatedAccessible(nsIAccessible *aAccessible, PRUint32 aRelationType);
/**
* Create the relation if the given relation is null. Add target to it
* which is the given accessible.
*
* @param aRelationType [in] relation type
* @param aRelation [in, out] relation object
* @param aTarget [in] accessible object
*/
static nsresult AddTarget(PRUint32 aRelationType,
nsIAccessibleRelation **aRelation,
nsIAccessible *aTarget);
/**
* Create the relation if the given relation is null and add the target to it
* which is the accessible for the given node.
*
* @param aRelationType [in] relation type
* @param aRelation [in, out] relation object
* @param aContent [in] accessible node
*/
static nsresult AddTargetFromContent(PRUint32 aRelationType,
nsIAccessibleRelation **aRelation,
nsIContent *aContent);
/**
* Create the relation if the given relation is null and add the target to it
* pointed by IDRef attribute on the given node.
*
* @param aRelationType [in] relation type
* @param aRelation [in, out] relation object
* @param aContent [in] node having the given IDRef attribute
* @param aAttr [in] IDRef attribute
*/
static nsresult AddTargetFromIDRefAttr(PRUint32 aRelationType,
nsIAccessibleRelation **aRelation,
nsIContent *aContent, nsIAtom *aAttr);
/**
* Create the relation if the given relation is null and add the targets to it
* that are pointed by IDRefs attribute on the given node.
*
* @param aRelationType [in] relation type
* @param aRelation [in, out] relation object
* @param aContent [in] node having the given IDRefs attribute
* @param aAttr [in] IDRefs attribute
*/
static nsresult AddTargetFromIDRefsAttr(PRUint32 aRelationType,
nsIAccessibleRelation **aRelation,
nsIContent *aContent, nsIAtom *aAttr);
/**
* Create the relation if the given relation is null and add the target to it
* found in neighbour tree.
*
* @param aRelationType [in] relation type
* @param aRelation [in, out] relation object
* @param aContent [in] node defining neighbour tree
* @param aNeighboutAttr [in] IDRef attribute of the node in neighbour
* tree pointing to node defining neighbour tree
* @param aNeighboutTagName [in, optional] tag name of the node in neighbour
* tree having IDRef attribute pointed by previous
* argument
*/
static nsresult AddTargetFromNeighbour(PRUint32 aRelationType,
nsIAccessibleRelation **aRelation,
nsIContent *aContent,
nsIAtom *aNeighboutAttr,
nsIAtom *aNeighboutTagName = nsnull);
/**
* Query nsAccessibleRelation from the given nsIAccessibleRelation.
*/
static already_AddRefed<nsAccessibleRelation>
QueryAccRelation(nsIAccessibleRelation *aRelation)
{
nsAccessibleRelation* relation = nsnull;
if (aRelation)
CallQueryInterface(aRelation, &relation);
return relation;
}
};
#endif

View File

@ -1029,13 +1029,15 @@ nsRootAccessible::GetContentDocShell(nsIDocShellTreeItem *aStart)
return nsnull;
}
NS_IMETHODIMP nsRootAccessible::GetAccessibleRelated(PRUint32 aRelationType,
nsIAccessible **aRelated)
NS_IMETHODIMP
nsRootAccessible::GetRelationByType(PRUint32 aRelationType,
nsIAccessibleRelation **aRelation)
{
*aRelated = nsnull;
NS_ENSURE_ARG_POINTER(aRelation);
*aRelation = nsnull;
if (!mDOMNode || aRelationType != nsIAccessibleRelation::RELATION_EMBEDS) {
return nsDocAccessibleWrap::GetAccessibleRelated(aRelationType, aRelated);
return nsDocAccessibleWrap::GetRelationByType(aRelationType, aRelation);
}
nsCOMPtr<nsIDocShellTreeItem> treeItem =
@ -1046,9 +1048,10 @@ NS_IMETHODIMP nsRootAccessible::GetAccessibleRelated(PRUint32 aRelationType,
nsCOMPtr<nsIAccessibleDocument> accDoc =
GetDocAccessibleFor(contentTreeItem, PR_TRUE);
if (accDoc)
CallQueryInterface(accDoc, aRelated);
nsCOMPtr<nsIAccessible> acc(do_QueryInterface(accDoc));
return nsRelUtils::AddTarget(aRelationType, aRelation, acc);
}
return NS_OK;
}

View File

@ -77,8 +77,8 @@ class nsRootAccessible : public nsDocAccessibleWrap,
NS_IMETHOD GetName(nsAString& aName);
NS_IMETHOD GetParent(nsIAccessible * *aParent);
NS_IMETHOD GetRole(PRUint32 *aRole);
NS_IMETHOD GetAccessibleRelated(PRUint32 aRelationType,
nsIAccessible **aRelated);
NS_IMETHOD GetRelationByType(PRUint32 aRelationType,
nsIAccessibleRelation **aRelation);
// ----- nsPIAccessibleDocument -----------------------
NS_IMETHOD FireDocLoadEvents(PRUint32 aEventType);

View File

@ -619,28 +619,17 @@ nsHTMLGroupboxAccessible::GetNameInternal(nsAString& aName)
}
NS_IMETHODIMP
nsHTMLGroupboxAccessible::GetAccessibleRelated(PRUint32 aRelationType,
nsIAccessible **aRelated)
nsHTMLGroupboxAccessible::GetRelationByType(PRUint32 aRelationType,
nsIAccessibleRelation **aRelation)
{
if (!mDOMNode) {
return NS_ERROR_FAILURE;
}
NS_ENSURE_ARG_POINTER(aRelated);
*aRelated = nsnull;
nsresult rv = nsHyperTextAccessibleWrap::GetAccessibleRelated(aRelationType, aRelated);
if (NS_FAILED(rv) || *aRelated) {
// Either the node is shut down, or another relation mechanism has been used
return rv;
}
nsresult rv = nsHyperTextAccessibleWrap::GetRelationByType(aRelationType,
aRelation);
NS_ENSURE_SUCCESS(rv, rv);
if (aRelationType == nsIAccessibleRelation::RELATION_LABELLED_BY) {
// No override for label, so use <legend> for this <fieldset>
nsCOMPtr<nsIDOMNode> legendNode = do_QueryInterface(GetLegend());
if (legendNode) {
GetAccService()->GetAccessibleInWeakShell(legendNode, mWeakShell, aRelated);
}
return nsRelUtils::
AddTargetFromContent(aRelationType, aRelation, GetLegend());
}
return NS_OK;
@ -652,31 +641,28 @@ nsHyperTextAccessibleWrap(aNode, aShell)
}
NS_IMETHODIMP
nsHTMLLegendAccessible::GetAccessibleRelated(PRUint32 aRelationType,
nsIAccessible **aRelated)
nsHTMLLegendAccessible::GetRelationByType(PRUint32 aRelationType,
nsIAccessibleRelation **aRelation)
{
*aRelated = nsnull;
nsresult rv = nsHyperTextAccessibleWrap::GetAccessibleRelated(aRelationType, aRelated);
if (NS_FAILED(rv) || *aRelated) {
// Either the node is shut down, or another relation mechanism has been used
return rv;
}
nsresult rv = nsHyperTextAccessibleWrap::
GetRelationByType(aRelationType, aRelation);
NS_ENSURE_SUCCESS(rv, rv);
if (aRelationType == nsIAccessibleRelation::RELATION_LABEL_FOR) {
// Look for groupbox parent
nsCOMPtr<nsIContent> content = do_QueryInterface(mDOMNode);
if (!content) {
return NS_ERROR_FAILURE; // Node already shut down
}
nsCOMPtr<nsIAccessible> groupboxAccessible = GetParent();
if (nsAccUtils::Role(groupboxAccessible) == nsIAccessibleRole::ROLE_GROUPING) {
nsCOMPtr<nsIAccessible> testLabelAccessible;
groupboxAccessible->GetAccessibleRelated(nsIAccessibleRelation::RELATION_LABELLED_BY,
getter_AddRefs(testLabelAccessible));
// XXX: if group box exposes more than one relation of the given type
// then we fail.
nsCOMPtr<nsIAccessible> testLabelAccessible =
nsRelUtils::GetRelatedAccessible(groupboxAccessible,
nsIAccessibleRelation::RELATION_LABELLED_BY);
if (testLabelAccessible == this) {
// We're the first child of the parent groupbox
NS_ADDREF(*aRelated = groupboxAccessible);
// We're the first child of the parent groupbox, see
// nsHTMLGroupboxAccessible::GetRelationByType().
return nsRelUtils::
AddTarget(aRelationType, aRelation, groupboxAccessible);
}
}
}

View File

@ -140,7 +140,8 @@ public:
// nsIAccessible
NS_IMETHOD GetRole(PRUint32 *aRole);
NS_IMETHOD GetAccessibleRelated(PRUint32 aRelationType, nsIAccessible **aRelated);
NS_IMETHOD GetRelationByType(PRUint32 aRelationType,
nsIAccessibleRelation **aRelation);
// nsAccessible
virtual nsresult GetNameInternal(nsAString& aName);
@ -153,8 +154,12 @@ class nsHTMLLegendAccessible : public nsHyperTextAccessibleWrap
{
public:
nsHTMLLegendAccessible(nsIDOMNode* aNode, nsIWeakReference* aShell);
NS_IMETHOD GetAccessibleRelated(PRUint32 aRelationType, nsIAccessible **aRelated);
// nsIAccessible
NS_IMETHOD GetRole(PRUint32 *aRole) { *aRole = nsIAccessibleRole::ROLE_LABEL; return NS_OK; }
NS_IMETHOD GetRelationByType(PRUint32 aRelationType,
nsIAccessibleRelation **aRelation);
};
#endif

View File

@ -241,24 +241,17 @@ nsHTMLTableAccessible::GetAttributesInternal(nsIPersistentProperties *aAttribute
}
NS_IMETHODIMP
nsHTMLTableAccessible::GetAccessibleRelated(PRUint32 aRelationType,
nsIAccessible **aRelated)
nsHTMLTableAccessible::GetRelationByType(PRUint32 aRelationType,
nsIAccessibleRelation **aRelation)
{
NS_ENSURE_ARG_POINTER(aRelated);
*aRelated = nsnull;
if (!mDOMNode) {
return NS_ERROR_FAILURE;
}
nsresult rv = nsAccessibleWrap::GetAccessibleRelated(aRelationType, aRelated);
if (NS_FAILED(rv) || *aRelated) {
// Either the node is shut down, or another relation mechanism has been used
return rv;
}
nsresult rv = nsAccessibleWrap::GetRelationByType(aRelationType,
aRelation);
NS_ENSURE_SUCCESS(rv, rv);
if (aRelationType == nsIAccessibleRelation::RELATION_DESCRIBED_BY) {
return GetCaption(aRelated);
nsCOMPtr<nsIAccessible> accCaption;
GetCaption(getter_AddRefs(accCaption));
return nsRelUtils::AddTarget(aRelationType, aRelation, accCaption);
}
return NS_OK;
@ -1254,24 +1247,17 @@ nsHTMLTableHeadAccessible::GetRows(PRInt32 *aRows)
}
NS_IMETHODIMP
nsHTMLCaptionAccessible::GetAccessibleRelated(PRUint32 aRelationType,
nsIAccessible **aRelated)
nsHTMLCaptionAccessible::GetRelationByType(PRUint32 aRelationType,
nsIAccessibleRelation **aRelation)
{
NS_ENSURE_ARG_POINTER(aRelated);
*aRelated = nsnull;
if (!mDOMNode) {
return NS_ERROR_FAILURE;
}
nsresult rv = nsHyperTextAccessible::GetAccessibleRelated(aRelationType, aRelated);
if (NS_FAILED(rv) || *aRelated) {
// Either the node is shut down, or another relation mechanism has been used
return rv;
}
nsresult rv = nsHyperTextAccessible::GetRelationByType(aRelationType,
aRelation);
NS_ENSURE_SUCCESS(rv, rv);
if (aRelationType == nsIAccessibleRelation::RELATION_DESCRIPTION_FOR) {
return GetParent(aRelated);
nsCOMPtr<nsIAccessible> accParent;
GetParent(getter_AddRefs(accParent));
return nsRelUtils::AddTarget(aRelationType, aRelation, accParent);
}
return NS_OK;

View File

@ -75,7 +75,8 @@ public:
// nsIAccessible
NS_IMETHOD GetRole(PRUint32 *aResult);
NS_IMETHOD GetDescription(nsAString& aDescription);
NS_IMETHOD GetAccessibleRelated(PRUint32 aRelationType, nsIAccessible **aRelated);
NS_IMETHOD GetRelationByType(PRUint32 aRelationType,
nsIAccessibleRelation **aRelation);
// nsAccessible
virtual nsresult GetNameInternal(nsAString& aName);
@ -160,7 +161,9 @@ public:
// nsIAccessible
NS_IMETHOD GetRole(PRUint32 *aRole)
{ *aRole = nsIAccessibleRole::ROLE_CAPTION; return NS_OK; }
NS_IMETHOD GetAccessibleRelated(PRUint32 aRelationType, nsIAccessible **aRelated);
NS_IMETHOD GetRelationByType(PRUint32 aRelationType,
nsIAccessibleRelation **aRelation);
};
#endif

View File

@ -987,13 +987,8 @@ __try {
pvarEndUpAt->vt = VT_EMPTY;
if (xpRelation) {
nsresult rv = GetAccessibleRelated(xpRelation,
getter_AddRefs(xpAccessibleResult));
if (rv == NS_ERROR_NOT_IMPLEMENTED) {
return E_NOTIMPL;
}
}
if (xpRelation)
xpAccessibleResult = nsRelUtils::GetRelatedAccessible(this, xpRelation);
if (xpAccessibleResult) {
pvarEndUpAt->pdispVal = NativeAccessible(xpAccessibleResult);

View File

@ -421,9 +421,10 @@ NS_IMETHODIMP nsXULGroupboxAccessible::GetRole(PRUint32 *aRole)
nsresult
nsXULGroupboxAccessible::GetNameInternal(nsAString& aName)
{
nsCOMPtr<nsIAccessible> label;
GetAccessibleRelated(nsIAccessibleRelation::RELATION_LABELLED_BY,
getter_AddRefs(label));
// XXX: we use the first related accessible only.
nsCOMPtr<nsIAccessible> label =
nsRelUtils::GetRelatedAccessible(this, nsIAccessibleRelation::RELATION_LABELLED_BY);
if (label) {
return label->GetName(aName);
}
@ -432,16 +433,11 @@ nsXULGroupboxAccessible::GetNameInternal(nsAString& aName)
}
NS_IMETHODIMP
nsXULGroupboxAccessible::GetAccessibleRelated(PRUint32 aRelationType,
nsIAccessible **aRelated)
nsXULGroupboxAccessible::GetRelationByType(PRUint32 aRelationType,
nsIAccessibleRelation **aRelation)
{
*aRelated = nsnull;
nsresult rv = nsAccessibleWrap::GetAccessibleRelated(aRelationType, aRelated);
if (NS_FAILED(rv) || *aRelated) {
// Either the node is shut down, or another relation mechanism has been used
return rv;
}
nsresult rv = nsAccessibleWrap::GetRelationByType(aRelationType, aRelation);
NS_ENSURE_SUCCESS(rv, rv);
if (aRelationType == nsIAccessibleRelation::RELATION_LABELLED_BY) {
// The label for xul:groupbox is generated from xul:label that is
@ -451,13 +447,16 @@ nsXULGroupboxAccessible::GetAccessibleRelated(PRUint32 aRelationType,
while (NextChild(testLabelAccessible)) {
if (nsAccUtils::Role(testLabelAccessible) == nsIAccessibleRole::ROLE_LABEL) {
// Ensure that it's our label
nsCOMPtr<nsIAccessible> testGroupboxAccessible;
testLabelAccessible->GetAccessibleRelated(nsIAccessibleRelation::RELATION_LABEL_FOR,
getter_AddRefs(testGroupboxAccessible));
// XXX: we'll fail if group accessible expose more than one relation
// targets.
nsCOMPtr<nsIAccessible> testGroupboxAccessible =
nsRelUtils::GetRelatedAccessible(testLabelAccessible,
nsIAccessibleRelation::RELATION_LABEL_FOR);
if (testGroupboxAccessible == this) {
// The <label> points back to this groupbox
NS_ADDREF(*aRelated = testLabelAccessible);
return NS_OK;
return nsRelUtils::
AddTarget(aRelationType, aRelation, testLabelAccessible);
}
}
}

View File

@ -108,7 +108,8 @@ public:
// nsIAccessible
NS_IMETHOD GetRole(PRUint32 *_retval);
NS_IMETHOD GetAccessibleRelated(PRUint32 aRelationType, nsIAccessible **aRelated);
NS_IMETHOD GetRelationByType(PRUint32 aRelationType,
nsIAccessibleRelation **aRelation);
// nsAccessible
virtual nsresult GetNameInternal(nsAString& aName);

View File

@ -130,21 +130,14 @@ nsXULTabAccessible::GetStateInternal(PRUint32 *aState, PRUint32 *aExtraState)
}
NS_IMETHODIMP
nsXULTabAccessible::GetAccessibleRelated(PRUint32 aRelationType,
nsIAccessible **aRelatedAccessible)
nsXULTabAccessible::GetRelationByType(PRUint32 aRelationType,
nsIAccessibleRelation **aRelation)
{
NS_ENSURE_ARG_POINTER(aRelatedAccessible);
*aRelatedAccessible = nsnull;
if (!mDOMNode)
return NS_ERROR_FAILURE;
nsresult rv = nsLeafAccessible::GetAccessibleRelated(aRelationType,
aRelatedAccessible);
nsresult rv = nsLeafAccessible::GetRelationByType(aRelationType,
aRelation);
NS_ENSURE_SUCCESS(rv, rv);
if (*aRelatedAccessible ||
aRelationType != nsIAccessibleRelation::RELATION_LABEL_FOR)
if (aRelationType != nsIAccessibleRelation::RELATION_LABEL_FOR)
return NS_OK;
// Expose 'LABEL_FOR' relation on tab accessible for tabpanel accessible.
@ -154,24 +147,12 @@ nsXULTabAccessible::GetAccessibleRelated(PRUint32 aRelationType,
// Check whether tab and tabpanel are related by 'linkedPanel' attribute on
// xul:tab element.
nsAutoString linkedPanelID;
content->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::linkedPanel,
linkedPanelID);
rv = nsRelUtils::AddTargetFromIDRefAttr(aRelationType, aRelation, content,
nsAccessibilityAtoms::linkedPanel);
NS_ENSURE_SUCCESS(rv, rv);
if (!linkedPanelID.IsEmpty()) {
nsCOMPtr<nsIDOMDocument> document;
mDOMNode->GetOwnerDocument(getter_AddRefs(document));
NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
nsCOMPtr<nsIDOMElement> linkedPanel;
document->GetElementById(linkedPanelID, getter_AddRefs(linkedPanel));
if (linkedPanel) {
nsCOMPtr<nsIDOMNode> linkedPanelNode(do_QueryInterface(linkedPanel));
GetAccService()->GetAccessibleInWeakShell(linkedPanelNode, mWeakShell,
aRelatedAccessible);
return NS_OK;
}
}
if (rv != NS_OK_NO_RELATION_TARGET)
return NS_OK;
// If there is no 'linkedPanel' attribute on xul:tab element then we
// assume tab and tabpanels are related 1 to 1. We follow algorithm from
@ -205,10 +186,8 @@ nsXULTabAccessible::GetAccessibleRelated(PRUint32 aRelationType,
tabBoxAcc->GetFirstChild(getter_AddRefs(childAcc));
while (childAcc) {
if (nsAccUtils::Role(childAcc) == nsIAccessibleRole::ROLE_PROPERTYPAGE) {
if (tabIndex == 0) {
NS_ADDREF(*aRelatedAccessible = childAcc);
return NS_OK;
}
if (tabIndex == 0)
return nsRelUtils::AddTarget(aRelationType, aRelation, childAcc);
tabIndex--;
}
@ -319,21 +298,13 @@ nsXULTabpanelAccessible::GetRole(PRUint32 *aRole)
}
NS_IMETHODIMP
nsXULTabpanelAccessible::GetAccessibleRelated(PRUint32 aRelationType,
nsIAccessible **aRelatedAccessible)
nsXULTabpanelAccessible::GetRelationByType(PRUint32 aRelationType,
nsIAccessibleRelation **aRelation)
{
NS_ENSURE_ARG_POINTER(aRelatedAccessible);
*aRelatedAccessible = nsnull;
if (!mDOMNode)
return NS_ERROR_FAILURE;
nsresult rv = nsAccessibleWrap::GetAccessibleRelated(aRelationType,
aRelatedAccessible);
nsresult rv = nsAccessibleWrap::GetRelationByType(aRelationType, aRelation);
NS_ENSURE_SUCCESS(rv, rv);
if (*aRelatedAccessible ||
aRelationType != nsIAccessibleRelation::RELATION_LABELLED_BY)
if (aRelationType != nsIAccessibleRelation::RELATION_LABELLED_BY)
return NS_OK;
// Expose 'LABELLED_BY' relation on tabpanel accessible for tab accessible.
@ -384,8 +355,7 @@ nsXULTabpanelAccessible::GetAccessibleRelated(PRUint32 aRelationType,
if (tabContent->AttrValueIs(kNameSpaceID_None,
nsAccessibilityAtoms::linkedPanel, atomID,
eCaseMatters)) {
NS_ADDREF(*aRelatedAccessible = childAcc);
return NS_OK;
return nsRelUtils::AddTarget(aRelationType, aRelation, childAcc);
}
}
@ -403,7 +373,6 @@ nsXULTabpanelAccessible::GetAccessibleRelated(PRUint32 aRelationType,
childAcc.swap(acc);
}
NS_IF_ADDREF(*aRelatedAccessible = foundTabAcc);
return NS_OK;
}

View File

@ -58,8 +58,8 @@ public:
NS_IMETHOD GetNumActions(PRUint8 *_retval);
NS_IMETHOD GetActionName(PRUint8 aIndex, nsAString& aName);
NS_IMETHOD DoAction(PRUint8 index);
NS_IMETHOD GetAccessibleRelated(PRUint32 aRelationType,
nsIAccessible **aRelatedAccessible);
NS_IMETHOD GetRelationByType(PRUint32 aRelationType,
nsIAccessibleRelation **aRelation);
// nsAccessible
virtual nsresult GetAttributesInternal(nsIPersistentProperties *aAttributes);
@ -108,8 +108,8 @@ public:
// nsIAccessible
NS_IMETHOD GetRole(PRUint32 *aRole);
NS_IMETHOD GetAccessibleRelated(PRUint32 aRelationType,
nsIAccessible **aRelatedAccessible);
NS_IMETHOD GetRelationByType(PRUint32 aRelationType,
nsIAccessibleRelation **aRelation);
};
#endif

View File

@ -81,19 +81,16 @@ nsXULTextAccessible::GetStateInternal(PRUint32 *aState, PRUint32 *aExtraState)
}
NS_IMETHODIMP
nsXULTextAccessible::GetAccessibleRelated(PRUint32 aRelationType,
nsIAccessible **aRelated)
nsXULTextAccessible::GetRelationByType(PRUint32 aRelationType,
nsIAccessibleRelation **aRelation)
{
nsresult rv =
nsHyperTextAccessibleWrap::GetAccessibleRelated(aRelationType, aRelated);
nsHyperTextAccessibleWrap::GetRelationByType(aRelationType, aRelation);
NS_ENSURE_SUCCESS(rv, rv);
if (*aRelated) {
return NS_OK;
}
nsIContent *content = nsCoreUtils::GetRoleContent(mDOMNode);
if (!content)
return NS_ERROR_FAILURE;
return NS_OK;
if (aRelationType == nsIAccessibleRelation::RELATION_LABEL_FOR) {
// Caption is the label for groupbox
@ -102,7 +99,8 @@ nsXULTextAccessible::GetAccessibleRelated(PRUint32 aRelationType,
nsCOMPtr<nsIAccessible> parentAccessible;
GetParent(getter_AddRefs(parentAccessible));
if (nsAccUtils::Role(parentAccessible) == nsIAccessibleRole::ROLE_GROUPING)
parentAccessible.swap(*aRelated);
return nsRelUtils::
AddTarget(aRelationType, aRelation, parentAccessible);
}
}

View File

@ -54,8 +54,8 @@ public:
// nsIAccessible
NS_IMETHOD GetRole(PRUint32 *aRole) { *aRole = nsIAccessibleRole::ROLE_LABEL; return NS_OK; }
NS_IMETHOD GetAccessibleRelated(PRUint32 aRelationType,
nsIAccessible **aRelated);
NS_IMETHOD GetRelationByType(PRUint32 aRelationType,
nsIAccessibleRelation **aRelation);
// nsAccessible
virtual nsresult GetNameInternal(nsAString& aName);

View File

@ -1281,31 +1281,40 @@ NS_IMETHODIMP nsXULTreeitemAccessible::TakeFocus()
return nsAccessible::TakeFocus();
}
NS_IMETHODIMP nsXULTreeitemAccessible::GetAccessibleRelated(PRUint32 aRelationType, nsIAccessible **aRelated)
NS_IMETHODIMP
nsXULTreeitemAccessible::GetRelationByType(PRUint32 aRelationType,
nsIAccessibleRelation **aRelation)
{
NS_ENSURE_ARG_POINTER(aRelation);
*aRelation = nsnull;
if (IsDefunct())
return NS_ERROR_FAILURE;
*aRelated = nsnull;
if (aRelationType == nsIAccessibleRelation::RELATION_NODE_CHILD_OF) {
PRInt32 columnIndex;
if (NS_SUCCEEDED(mColumn->GetIndex(&columnIndex)) && columnIndex == 0) {
PRInt32 parentIndex;
if (NS_SUCCEEDED(mTreeView->GetParentIndex(mRow, &parentIndex))) {
if (parentIndex == -1) {
NS_IF_ADDREF(*aRelated = mParent);
return NS_OK;
} else {
nsCOMPtr<nsIAccessibleTreeCache> cache =
do_QueryInterface(mParent);
return cache->GetCachedTreeitemAccessible(parentIndex, mColumn, aRelated);
}
if (parentIndex == -1)
return nsRelUtils::AddTarget(aRelationType, aRelation, mParent);
nsCOMPtr<nsIAccessibleTreeCache> cache =
do_QueryInterface(mParent);
nsCOMPtr<nsIAccessible> accParent;
nsresult rv = cache->
GetCachedTreeitemAccessible(parentIndex, mColumn,
getter_AddRefs(accParent));
NS_ENSURE_SUCCESS(rv, rv);
return nsRelUtils::AddTarget(aRelationType, aRelation, accParent);
}
}
return NS_OK;
}
return nsAccessible::GetAccessibleRelated(aRelationType, aRelated);
return nsAccessible::GetRelationByType(aRelationType, aRelation);
}
// attribute AString nsIAccessibleTreeItem::cachedName

View File

@ -129,7 +129,8 @@ public:
NS_IMETHOD SetSelected(PRBool aSelect);
NS_IMETHOD TakeFocus(void);
NS_IMETHOD GetAccessibleRelated(PRUint32 aRelationType, nsIAccessible **aRelated);
NS_IMETHOD GetRelationByType(PRUint32 aRelationType,
nsIAccessibleRelation **aRelation);
// nsIAccessNode
NS_IMETHOD GetUniqueID(void **aUniqueID);

View File

@ -283,6 +283,22 @@ function relationTypeToString(aRelationType)
return gAccRetrieval.getStringRelationType(aRelationType);
}
/**
* Return pretty name for identifier, it may be ID, DOM node or accessible.
*/
function prettyName(aIdentifier)
{
if (aIdentifier instanceof nsIAccessible) {
var acc = getAccessible(aIdentifier, [nsIAccessNode]);
return getNodePrettyName(acc.DOMNode);
}
if (aIdentifier instanceof nsIDOMNode)
return getNodePrettyName(aIdentifier);
return " '" + aIdentifier + "' ";
}
////////////////////////////////////////////////////////////////////////////////
// Private
////////////////////////////////////////////////////////////////////////////////
@ -297,3 +313,11 @@ function initialize()
}
addLoadEvent(initialize);
function getNodePrettyName(aNode)
{
if (aNode.nodeType == nsIDOMNode.ELEMENT_NODE && aNode.hasAttribute("id"))
return " '" + aNode.getAttribute("id") + "' ";
return " '" + aNode.localName + " node' ";
}

View File

@ -24,59 +24,106 @@ const RELATION_SUBWINDOW_OF = nsIAccessibleRelation.RELATION_SUBWINDOW_OF;
/**
* Test the accessible relation.
*
* @param aIdentifier [in] identifier to get an accessible implementing
* the given interfaces may be ID attribute or DOM
* element or accessible object
* @param aRelType [in] relation type (see constants above)
* @param aRelatedIdentifier [in] identifier of expected related accessible
* @param aIdentifier [in] identifier to get an accessible, may be ID
* attribute or DOM element or accessible object
* @param aRelType [in] relation type (see constants above)
* @param aRelatedIdentifiers [in] identifier or array of identifiers of
* expected related accessibles
*/
function testRelation(aIdentifier, aRelType, aRelatedIdentifier)
function testRelation(aIdentifier, aRelType, aRelatedIdentifiers)
{
var actualRelatedAcc = getRelatedAccessible(aIdentifier, aRelType);
var relation = getRelationByType(aIdentifier, aRelType);
var relDescr = getRelationErrorMsg(aIdentifier, aRelType);
if (!actualRelatedAcc && !aRelatedIdentifier) {
ok(true, "No" + relDescr);
var relDescrStart = getRelationErrorMsg(aIdentifier, aRelType, true);
if (!relation || !relation.targetsCount) {
if (!aRelatedIdentifiers) {
ok(true, "No" + relDescr);
return;
}
var msg = relDescrStart + "has no expected targets: '" +
prettyName(aRelatedIdentifiers) + "'";
ok(false, msg);
return;
} else if (!aRelatedIdentifiers) {
ok(false, "There are unexpected targets of " + relDescr);
return;
}
var relatedAcc = getAccessible(aRelatedIdentifier);
if (!relatedAcc)
var relatedIds = (aRelatedIdentifiers instanceof Array) ?
aRelatedIdentifiers : [aRelatedIdentifiers];
var targets = [];
for (var idx = 0; idx < relatedIds.length; idx++)
targets.push(getAccessible(relatedIds[idx]));
if (targets.length != relatedIds.length)
return;
is(actualRelatedAcc, relatedAcc,
aRelatedIdentifier + " is not a target of" + relDescr);
var actualTargets = relation.getTargets();
// Check if all given related accessibles are targets of obtained relation.
for (var idx = 0; idx < targets.length; idx++) {
var isFound = false;
var enumerate = actualTargets.enumerate();
while (enumerate.hasMoreElements()) {
var relatedAcc = enumerate.getNext().QueryInterface(nsIAccessible);
if (targets[idx] == relatedAcc) {
isFound = true;
break;
}
}
ok(isFound, relatedIds[idx] + " is not a target of" + relDescr);
}
// Check if all obtained targets are given related accessibles.
var enumerate = actualTargets.enumerate();
while (enumerate.hasMoreElements()) {
var relatedAcc = enumerate.getNext().QueryInterface(nsIAccessible);
for (var idx = 0; idx < targets.length && relatedAcc != targets[idx]; idx++);
if (idx == targets.length)
ok(false, "There is unexpected target" + prettyName(relatedAcc) + "of" + relDescr);
}
}
/**
* Return related accessible for the given relation type.
*
* @param aIdentifier [in] identifier to get an accessible implementing
* the given interfaces may be ID attribute or DOM
* element or accessible object
* @param aIdentifier [in] identifier to get an accessible, may be ID attribute
* or DOM element or accessible object
* @param aRelType [in] relation type (see constants above)
*/
function getRelatedAccessible(aIdentifier, aRelType)
function getRelationByType(aIdentifier, aRelType)
{
var acc = getAccessible(aIdentifier);
if (!acc)
return;
var relatedAcc = null;
var relation = null;
try {
relatedAcc = acc.getAccessibleRelated(aRelType);
relation = acc.getRelationByType(aRelType);
} catch (e) {
ok(false, "Can't get" + getRelationErrorMsg(aIdentifier, aRelType));
}
return relatedAcc;
return relation;
}
////////////////////////////////////////////////////////////////////////////////
// Private implementation details
function getRelationErrorMsg(aIdentifier, aRelType)
function getRelationErrorMsg(aIdentifier, aRelType, aIsStartSentence)
{
var relStr = relationTypeToString(aRelType);
return " relation of '" + relStr + "' type for " + aIdentifier + ".";
var msg = aIsStartSentence ? "Relation of '" : " relation of '";
msg += relStr + "' type for '" + prettyName(aIdentifier) + "'";
msg += aIsStartSentence ? " " : ".";
return msg;
}

View File

@ -24,17 +24,28 @@
testRelation("label2", RELATION_LABEL_FOR, "checkbox2");
testRelation("checkbox2", RELATION_LABELLED_BY, "label2");
// aria-describedby
testRelation("descr1", RELATION_DESCRIPTION_FOR, "checkbox3");
testRelation("checkbox3", RELATION_DESCRIBED_BY, "descr1");
// aria-labelledby, multiple relations
testRelation("label3", RELATION_LABEL_FOR, "checkbox3");
testRelation("label4", RELATION_LABEL_FOR, "checkbox3");
testRelation("checkbox3", RELATION_LABELLED_BY, ["label3", "label4"]);
// aria_owns
// aria-describedby
testRelation("descr1", RELATION_DESCRIPTION_FOR, "checkbox4");
testRelation("checkbox4", RELATION_DESCRIBED_BY, "descr1");
// aria-describedby, multiple relations
testRelation("descr2", RELATION_DESCRIPTION_FOR, "checkbox5");
testRelation("descr3", RELATION_DESCRIPTION_FOR, "checkbox5");
testRelation("checkbox5", RELATION_DESCRIBED_BY, ["descr2", "descr3"]);
// aria_owns, multiple relations
testRelation("treeitem1", RELATION_NODE_CHILD_OF, "tree");
testRelation("treeitem2", RELATION_NODE_CHILD_OF, "tree");
// 'node child of' relation for outlineitem role
testRelation("treeitem2", RELATION_NODE_CHILD_OF, "tree");
testRelation("treeitem3", RELATION_NODE_CHILD_OF, "tree");
testRelation("treeitem4", RELATION_NODE_CHILD_OF, "treeitem3");
testRelation("treeitem4", RELATION_NODE_CHILD_OF, "tree");
testRelation("treeitem5", RELATION_NODE_CHILD_OF, "treeitem4");
// 'node child of' relation for the document having window, returns
// direct accessible parent (fixed in bug 419770).
@ -48,10 +59,20 @@
testRelation("tabpanel", RELATION_CONTROLLED_BY, "tab");
testRelation("tab", RELATION_CONTROLLER_FOR, "tabpanel");
// aria-controls, multiple relations
testRelation("lr1", RELATION_CONTROLLED_BY, "button");
testRelation("lr2", RELATION_CONTROLLED_BY, "button");
testRelation("button", RELATION_CONTROLLER_FOR, ["lr1", "lr2"]);
// aria-flowto
testRelation("flowto", RELATION_FLOWS_TO, "flowfrom");
testRelation("flowfrom", RELATION_FLOWS_FROM, "flowto");
// aria-flowto, multiple relations
testRelation("flowto1", RELATION_FLOWS_TO, ["flowfrom1", "flowfrom2"]);
testRelation("flowfrom1", RELATION_FLOWS_FROM, "flowto1");
testRelation("flowfrom2", RELATION_FLOWS_FROM, "flowto1");
// 'default button' relation
testRelation("input", RELATION_DEFAULT_BUTTON, "submit");
@ -104,16 +125,25 @@
<input id="checkbox1" />
<span id="label2">label</span>
<span role="checkbox" id="checkbox2" aria-labelledby="label2" />
<span role="checkbox" id="checkbox2" aria-labelledby="label2"></span>
<span id="label3">label1</span>
<span id="label4">label2</span>
<span role="checkbox" id="checkbox3" aria-labelledby="label3 label4"></span>
<span id="descr1">description</span>
<span role="checkbox" id="checkbox3" aria-describedby="descr1" />
<span role="checkbox" id="checkbox4" aria-describedby="descr1"></span>
<span id="descr2">description1</span>
<span id="descr3">description2</span>
<span role="checkbox" id="checkbox5" aria-describedby="descr2 descr3"></span>
<div role="treeitem" id="treeitem1">Yellow</div>
<div id="tree" role="tree" aria-owns="treeitem1">
<div role="treeitem" id="treeitem2">Blue</div>
<div role="treeitem" id="treeitem3" aria-level="1">Green</div>
<div role="treeitem" id="treeitem4" aria-level="2">Light green</div>
<div role="treeitem" id="treeitem2">Orange</div>
<div id="tree" role="tree" aria-owns="treeitem1 treeitem2">
<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>
<iframe id="iframe"></iframe>
@ -123,9 +153,18 @@
</div>
<div id="tabpanel" role="tabpanel">tabpanel</div>
<div id="lr1" aria-live="assertive">1</div>
<div id="lr2" aria-live="assertive">a</div>
<input type="button" id="button" aria-controls="lr1 lr2"
onclick="getNode('lr1').textContent += '1'; getNode('lr2').textContent += 'a';"/>
<span id="flowto" aria-flowto="flowfrom">flow to</span>
<span id="flowfrom">flow from</span>
<span id="flowto1" aria-flowto="flowfrom1 flowfrom2">flow to</span>
<span id="flowfrom1">flow from</span>
<span id="flowfrom2">flow from</span>
<form>
<input id="input" />
<input type="submit" id="submit" />

View File

@ -28,21 +28,32 @@
testRelation("label2", RELATION_LABEL_FOR, "checkbox2");
testRelation("checkbox2", RELATION_LABELLED_BY, "label2");
// aria-labelledby, multiple relations
testRelation("label3", RELATION_LABEL_FOR, "checkbox3");
testRelation("label4", RELATION_LABEL_FOR, "checkbox3");
testRelation("checkbox3", RELATION_LABELLED_BY, ["label3", "label4"]);
// aria-describedby
testRelation("descr1", RELATION_DESCRIPTION_FOR, "checkbox3");
testRelation("checkbox3", RELATION_DESCRIBED_BY, "descr1");
testRelation("descr1", RELATION_DESCRIPTION_FOR, "checkbox4");
testRelation("checkbox4", RELATION_DESCRIBED_BY, "descr1");
// aria-describedby, multiple relations
testRelation("descr2", RELATION_DESCRIPTION_FOR, "checkbox5");
testRelation("descr3", RELATION_DESCRIPTION_FOR, "checkbox5");
testRelation("checkbox5", RELATION_DESCRIBED_BY, ["descr2", "descr3"]);
// xul:description@control
testRelation("descr2", RELATION_DESCRIPTION_FOR, "checkbox4");
testRelation("checkbox4", RELATION_DESCRIBED_BY, "descr2");
testRelation("descr4", RELATION_DESCRIPTION_FOR, "checkbox6");
testRelation("checkbox6", RELATION_DESCRIBED_BY, "descr4");
// aria_owns
// aria_owns, multiple relations
testRelation("treeitem1", RELATION_NODE_CHILD_OF, "tree");
testRelation("treeitem2", RELATION_NODE_CHILD_OF, "tree");
// 'node child of' relation for outlineitem role
testRelation("treeitem2", RELATION_NODE_CHILD_OF, "tree");
testRelation("treeitem3", RELATION_NODE_CHILD_OF, "tree");
testRelation("treeitem4", RELATION_NODE_CHILD_OF, "treeitem3");
testRelation("treeitem4", RELATION_NODE_CHILD_OF, "tree");
testRelation("treeitem5", RELATION_NODE_CHILD_OF, "treeitem4");
// 'node child of' relation for the document having window, returns
// direct accessible parent (fixed in bug 419770).
@ -56,10 +67,20 @@
testRelation("tabpanel", RELATION_CONTROLLED_BY, "tab");
testRelation("tab", RELATION_CONTROLLER_FOR, "tabpanel");
// aria-controls, multiple relations
testRelation("lr1", RELATION_CONTROLLED_BY, "button");
testRelation("lr2", RELATION_CONTROLLED_BY, "button");
testRelation("button", RELATION_CONTROLLER_FOR, ["lr1", "lr2"]);
// aria-flowto
testRelation("flowto", RELATION_FLOWS_TO, "flowfrom");
testRelation("flowfrom", RELATION_FLOWS_FROM, "flowto");
// aria-flowto, multiple relations
testRelation("flowto1", RELATION_FLOWS_TO, ["flowfrom1", "flowfrom2"]);
testRelation("flowfrom1", RELATION_FLOWS_FROM, "flowto1");
testRelation("flowfrom2", RELATION_FLOWS_FROM, "flowto1");
// 'default button' relation
testRelation("textbox", RELATION_DEFAULT_BUTTON, "submit");
@ -113,17 +134,28 @@
<description id="label2">label</description>
<description role="checkbox" id="checkbox2" aria-labelledby="label2"/>
<description id="descr1">description</description>
<description role="checkbox" id="checkbox3" aria-describedby="descr1"/>
<description id="label3">label</description>
<description id="label4">label</description>
<description role="checkbox" id="checkbox3"
aria-labelledby="label3 label4"/>
<description id="descr2" control="checkbox4">description</description>
<checkbox id="checkbox4"/>
<description id="descr1">description</description>
<description role="checkbox" id="checkbox4" aria-describedby="descr1"/>
<description id="descr2">label</description>
<description id="descr3">label</description>
<description role="checkbox" id="checkbox5"
aria-describedby="descr2 descr3"/>
<description id="descr4" control="checkbox6">description</description>
<checkbox id="checkbox6"/>
<description role="treeitem" id="treeitem1">Yellow</description>
<vbox id="tree" role="tree" aria-owns="treeitem1">
<description role="treeitem" id="treeitem2">Blue</description>
<description role="treeitem" id="treeitem3" aria-level="1">Green</description>
<description role="treeitem" id="treeitem4" aria-level="2">Light green</description>
<description role="treeitem" id="treeitem2">Orange</description>
<vbox id="tree" role="tree" aria-owns="treeitem1 treeitem2">
<description role="treeitem" id="treeitem3">Blue</description>
<description role="treeitem" id="treeitem4" aria-level="1">Green</description>
<description role="treeitem" id="treeitem5" aria-level="2">Light green</description>
</vbox>
<iframe id="iframe"/>
@ -133,6 +165,15 @@
</hbox>
<description id="tabpanel" role="tabpanel">tabpanel</description>
<description id="lr1" aria-live="assertive">1</description>
<description id="lr2" aria-live="assertive">a</description>
<button id="button" aria-controls="lr1 lr2" label="button"
oncommand="getNode('lr1').textContent += '1'; getNode('lr2').textContent += 'a';"/>
<description id="flowto1" aria-flowto="flowfrom1 flowfrom2">flow to</description>
<description id="flowfrom1">flow from</description>
<description id="flowfrom2">flow from</description>
<description id="flowto" aria-flowto="flowfrom">flow to</description>
<description id="flowfrom">flow from</description>