From 8f0b3e7aff6fcdc57fe8139d357f44ec0fdfbc58 Mon Sep 17 00:00:00 2001 From: Alexander Surkov Date: Tue, 10 Feb 2009 11:03:30 +0100 Subject: [PATCH] Bug 345780 - Support multiple targets for same relation, r=davidb, r=MarcoZ, sr=neil --- accessible/public/nsIAccessible.idl | 6 +- accessible/src/atk/nsAccessibleWrap.cpp | 31 +- accessible/src/base/Makefile.in | 1 + accessible/src/base/nsAccessible.cpp | 269 ++++++++++-------- accessible/src/base/nsAccessible.h | 3 +- accessible/src/base/nsAccessibleRelation.cpp | 49 +++- accessible/src/base/nsAccessibleRelation.h | 25 +- accessible/src/base/nsCoreUtils.cpp | 46 +++ accessible/src/base/nsCoreUtils.h | 12 + accessible/src/base/nsRelUtils.cpp | 160 +++++++++++ accessible/src/base/nsRelUtils.h | 151 ++++++++++ accessible/src/base/nsRootAccessible.cpp | 15 +- accessible/src/base/nsRootAccessible.h | 4 +- .../src/html/nsHTMLFormControlAccessible.cpp | 58 ++-- .../src/html/nsHTMLFormControlAccessible.h | 9 +- accessible/src/html/nsHTMLTableAccessible.cpp | 46 ++- accessible/src/html/nsHTMLTableAccessible.h | 7 +- accessible/src/msaa/nsAccessibleWrap.cpp | 9 +- .../src/xul/nsXULFormControlAccessible.cpp | 33 ++- .../src/xul/nsXULFormControlAccessible.h | 3 +- accessible/src/xul/nsXULTabAccessible.cpp | 65 ++--- accessible/src/xul/nsXULTabAccessible.h | 8 +- accessible/src/xul/nsXULTextAccessible.cpp | 14 +- accessible/src/xul/nsXULTextAccessible.h | 4 +- accessible/src/xul/nsXULTreeAccessible.cpp | 31 +- accessible/src/xul/nsXULTreeAccessible.h | 3 +- accessible/tests/mochitest/common.js | 24 ++ accessible/tests/mochitest/relations.js | 91 ++++-- .../tests/mochitest/test_relations.html | 63 +++- accessible/tests/mochitest/test_relations.xul | 71 ++++- 30 files changed, 934 insertions(+), 377 deletions(-) create mode 100644 accessible/src/base/nsRelUtils.cpp create mode 100644 accessible/src/base/nsRelUtils.h diff --git a/accessible/public/nsIAccessible.idl b/accessible/public/nsIAccessible.idl index 757abf21fb1..b2d77f793d9 100644 --- a/accessible/public/nsIAccessible.idl +++ b/accessible/public/nsIAccessible.idl @@ -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. diff --git a/accessible/src/atk/nsAccessibleWrap.cpp b/accessible/src/atk/nsAccessibleWrap.cpp index f647e5a7d61..c4a6b83dad2 100644 --- a/accessible/src/atk/nsAccessibleWrap.cpp +++ b/accessible/src/atk/nsAccessibleWrap.cpp @@ -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(relationType[i])); - atk_relation_set_add(relation_set, relation); - g_object_unref(relation); + nsCOMPtr 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 geckoTarget; + geckoRelation->GetTarget(index, getter_AddRefs(geckoTarget)); + accessible_array[index] = + nsAccessibleWrap::GetAtkObject(geckoTarget); + } + + relation = atk_relation_new(accessible_array, targetsCount, + static_cast(relationType[i])); + atk_relation_set_add(relation_set, relation); + g_object_unref(relation); + + delete [] accessible_array; + } } } diff --git a/accessible/src/base/Makefile.in b/accessible/src/base/Makefile.in index a2f8e66ab4c..ef5f8e7703b 100644 --- a/accessible/src/base/Makefile.in +++ b/accessible/src/base/Makefile.in @@ -82,6 +82,7 @@ CPPSRCS = \ nsCoreUtils.cpp \ nsAccUtils.cpp \ nsNameUtils.cpp \ + nsRelUtils.cpp \ nsAccessibilityService.cpp \ nsAccessible.cpp \ nsAccessibleRelation.cpp \ diff --git a/accessible/src/base/nsAccessible.cpp b/accessible/src/base/nsAccessible.cpp index 7a1dd6f606e..293ec8e10d6 100644 --- a/accessible/src/base/nsAccessible.cpp +++ b/accessible/src/base/nsAccessible.cpp @@ -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 content = nsCoreUtils::GetRoleContent(mDOMNode); if (!content) return NS_OK; - nsAutoString ids; - if (!content->GetAttr(kNameSpaceID_None, aIDProperty, ids)) + nsCOMPtr 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 domDoc = do_QueryInterface(content->GetOwnerDoc()); - NS_ENSURE_TRUE(domDoc, NS_ERROR_FAILURE); + nsCOMPtr 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 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 tabPanel; - rv = GetAccessibleRelated(nsIAccessibleRelation::RELATION_LABEL_FOR, - getter_AddRefs(tabPanel)); - NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr tabPanel = nsRelUtils:: + GetRelatedAccessible(this, nsIAccessibleRelation::RELATION_LABEL_FOR); if (nsAccUtils::Role(tabPanel) == nsIAccessibleRole::ROLE_PROPERTYPAGE) { nsCOMPtr 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 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 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 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 htmlform; control->GetForm(getter_AddRefs(htmlform)); nsCOMPtr form(do_QueryInterface(htmlform)); - if (form) - relatedNode = do_QueryInterface(form->GetDefaultSubmitElement()); + if (form) { + nsCOMPtr 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 relatedContent(do_QueryInterface(buttonEl)); + return nsRelUtils::AddTargetFromContent(aRelationType, aRelation, + relatedContent); } } - break; + return NS_OK; } + case nsIAccessibleRelation::RELATION_MEMBER_OF: { - relatedNode = GetAtomicRegion(); - break; + nsCOMPtr 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 domDoc; - mDOMNode->GetOwnerDocument(getter_AddRefs(domDoc)); - NS_ENSURE_TRUE(domDoc, NS_ERROR_FAILURE); - nsCOMPtr 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 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 accessible; - GetAccessibleRelated(relType, getter_AddRefs(accessible)); - if (accessible) { - nsCOMPtr relation = - new nsAccessibleRelationWrap(relType, accessible); - NS_ENSURE_TRUE(relation, NS_ERROR_OUT_OF_MEMORY); + nsCOMPtr relation; + nsresult rv = GetRelationByType(relType, getter_AddRefs(relation)); + if (NS_SUCCEEDED(rv) && relation) relations->AppendElement(relation, PR_FALSE); - } } NS_ADDREF(*aRelations = relations); diff --git a/accessible/src/base/nsAccessible.h b/accessible/src/base/nsAccessible.h index 88f3d070f22..31bedfc3d43 100644 --- a/accessible/src/base/nsAccessible.h +++ b/accessible/src/base/nsAccessible.h @@ -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" diff --git a/accessible/src/base/nsAccessibleRelation.cpp b/accessible/src/base/nsAccessibleRelation.cpp index 0b01bbb1acf..1e5cb2f254a 100644 --- a/accessible/src/base/nsAccessibleRelation.cpp +++ b/accessible/src/base/nsAccessibleRelation.cpp @@ -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 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 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); +} diff --git a/accessible/src/base/nsAccessibleRelation.h b/accessible/src/base/nsAccessibleRelation.h index f55f995d933..d460788326a 100644 --- a/accessible/src/base/nsAccessibleRelation.h +++ b/accessible/src/base/nsAccessibleRelation.h @@ -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 mTarget; + nsCOMPtr mTargets; }; +NS_DEFINE_STATIC_IID_ACCESSOR(nsAccessibleRelation, NS_ACCRELATION_IMPL_CID) + #endif diff --git a/accessible/src/base/nsCoreUtils.cpp b/accessible/src/base/nsCoreUtils.cpp index 285a7cdf1c2..4cc8c15b2f5 100644 --- a/accessible/src/base/nsCoreUtils.cpp +++ b/accessible/src/base/nsCoreUtils.cpp @@ -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 document = do_QueryInterface(aContent->GetOwnerDoc()); + NS_ASSERTION(document, "The given node is not in document!"); + if (!document) + return; + + nsCOMPtr 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 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, diff --git a/accessible/src/base/nsCoreUtils.h b/accessible/src/base/nsCoreUtils.h index 48675eb2c89..e4700326690 100644 --- a/accessible/src/base/nsCoreUtils.h +++ b/accessible/src/base/nsCoreUtils.h @@ -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. */ diff --git a/accessible/src/base/nsRelUtils.cpp b/accessible/src/base/nsRelUtils.cpp new file mode 100644 index 00000000000..35a673c5860 --- /dev/null +++ b/accessible/src/base/nsRelUtils.cpp @@ -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 (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 +nsRelUtils::GetRelatedAccessible(nsIAccessible *aAccessible, + PRUint32 aRelationType) +{ + nsCOMPtr 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 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 accService = nsAccessNode::GetAccService(); + nsCOMPtr node(do_QueryInterface(aContent)); + + nsCOMPtr 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 document = + do_QueryInterface(aContent->GetOwnerDoc()); + NS_ASSERTION(document, "The given node is not in document!"); + if (!document) + return NS_OK_NO_RELATION_TARGET; + + nsCOMPtr refElm; + document->GetElementById(id, getter_AddRefs(refElm)); + + nsCOMPtr refContent(do_QueryInterface(refElm)); + return AddTargetFromContent(aRelationType, aRelation, refContent); +} + +nsresult +nsRelUtils::AddTargetFromIDRefsAttr(PRUint32 aRelationType, + nsIAccessibleRelation **aRelation, + nsIContent *aContent, nsIAtom *aAttr) +{ + nsCOMPtr 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 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)); +} diff --git a/accessible/src/base/nsRelUtils.h b/accessible/src/base/nsRelUtils.h new file mode 100644 index 00000000000..ed613358474 --- /dev/null +++ b/accessible/src/base/nsRelUtils.h @@ -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 (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 + 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 + QueryAccRelation(nsIAccessibleRelation *aRelation) + { + nsAccessibleRelation* relation = nsnull; + if (aRelation) + CallQueryInterface(aRelation, &relation); + + return relation; + } +}; + +#endif diff --git a/accessible/src/base/nsRootAccessible.cpp b/accessible/src/base/nsRootAccessible.cpp index 96888116987..3cd1d0c40f8 100644 --- a/accessible/src/base/nsRootAccessible.cpp +++ b/accessible/src/base/nsRootAccessible.cpp @@ -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 treeItem = @@ -1046,9 +1048,10 @@ NS_IMETHODIMP nsRootAccessible::GetAccessibleRelated(PRUint32 aRelationType, nsCOMPtr accDoc = GetDocAccessibleFor(contentTreeItem, PR_TRUE); - if (accDoc) - CallQueryInterface(accDoc, aRelated); + nsCOMPtr acc(do_QueryInterface(accDoc)); + return nsRelUtils::AddTarget(aRelationType, aRelation, acc); } + return NS_OK; } diff --git a/accessible/src/base/nsRootAccessible.h b/accessible/src/base/nsRootAccessible.h index 7fc4419ccc0..acba0e25b49 100644 --- a/accessible/src/base/nsRootAccessible.h +++ b/accessible/src/base/nsRootAccessible.h @@ -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); diff --git a/accessible/src/html/nsHTMLFormControlAccessible.cpp b/accessible/src/html/nsHTMLFormControlAccessible.cpp index e1133b0decc..32a9a1300de 100644 --- a/accessible/src/html/nsHTMLFormControlAccessible.cpp +++ b/accessible/src/html/nsHTMLFormControlAccessible.cpp @@ -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 for this
- nsCOMPtr 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 content = do_QueryInterface(mDOMNode); - if (!content) { - return NS_ERROR_FAILURE; // Node already shut down - } nsCOMPtr groupboxAccessible = GetParent(); if (nsAccUtils::Role(groupboxAccessible) == nsIAccessibleRole::ROLE_GROUPING) { - nsCOMPtr 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 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); } } } diff --git a/accessible/src/html/nsHTMLFormControlAccessible.h b/accessible/src/html/nsHTMLFormControlAccessible.h index 34902e5a91e..3ad1b8950a8 100644 --- a/accessible/src/html/nsHTMLFormControlAccessible.h +++ b/accessible/src/html/nsHTMLFormControlAccessible.h @@ -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 diff --git a/accessible/src/html/nsHTMLTableAccessible.cpp b/accessible/src/html/nsHTMLTableAccessible.cpp index 30b33d1903f..c3bddde9f9a 100644 --- a/accessible/src/html/nsHTMLTableAccessible.cpp +++ b/accessible/src/html/nsHTMLTableAccessible.cpp @@ -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 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 accParent; + GetParent(getter_AddRefs(accParent)); + return nsRelUtils::AddTarget(aRelationType, aRelation, accParent); } return NS_OK; diff --git a/accessible/src/html/nsHTMLTableAccessible.h b/accessible/src/html/nsHTMLTableAccessible.h index f5810413b69..3b912fc6d00 100644 --- a/accessible/src/html/nsHTMLTableAccessible.h +++ b/accessible/src/html/nsHTMLTableAccessible.h @@ -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 diff --git a/accessible/src/msaa/nsAccessibleWrap.cpp b/accessible/src/msaa/nsAccessibleWrap.cpp index 3f2267ec2e6..a0ff6cd7d21 100644 --- a/accessible/src/msaa/nsAccessibleWrap.cpp +++ b/accessible/src/msaa/nsAccessibleWrap.cpp @@ -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); diff --git a/accessible/src/xul/nsXULFormControlAccessible.cpp b/accessible/src/xul/nsXULFormControlAccessible.cpp index f73e937b155..65c890b89da 100644 --- a/accessible/src/xul/nsXULFormControlAccessible.cpp +++ b/accessible/src/xul/nsXULFormControlAccessible.cpp @@ -421,9 +421,10 @@ NS_IMETHODIMP nsXULGroupboxAccessible::GetRole(PRUint32 *aRole) nsresult nsXULGroupboxAccessible::GetNameInternal(nsAString& aName) { - nsCOMPtr label; - GetAccessibleRelated(nsIAccessibleRelation::RELATION_LABELLED_BY, - getter_AddRefs(label)); + // XXX: we use the first related accessible only. + nsCOMPtr 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 testGroupboxAccessible; - testLabelAccessible->GetAccessibleRelated(nsIAccessibleRelation::RELATION_LABEL_FOR, - getter_AddRefs(testGroupboxAccessible)); + // XXX: we'll fail if group accessible expose more than one relation + // targets. + nsCOMPtr testGroupboxAccessible = + nsRelUtils::GetRelatedAccessible(testLabelAccessible, + nsIAccessibleRelation::RELATION_LABEL_FOR); + if (testGroupboxAccessible == this) { // The