зеркало из https://github.com/mozilla/pjs.git
Bug 491851 - implement relations inside HTML table, r=marcoz, davidb, sr=neil
This commit is contained in:
Родитель
9d965cf68e
Коммит
1d48177650
|
@ -45,6 +45,7 @@
|
|||
|
||||
#include "nsAccessibleEventData.h"
|
||||
#include "nsHyperTextAccessible.h"
|
||||
#include "nsHTMLTableAccessible.h"
|
||||
#include "nsAccessibilityAtoms.h"
|
||||
#include "nsAccessibleTreeWalker.h"
|
||||
#include "nsAccessible.h"
|
||||
|
@ -725,6 +726,16 @@ nsAccUtils::QueryAccessible(nsIAccessible *aAccessible)
|
|||
return accessible;
|
||||
}
|
||||
|
||||
already_AddRefed<nsHTMLTableAccessible>
|
||||
nsAccUtils::QueryAccessibleTable(nsIAccessibleTable *aAccessibleTable)
|
||||
{
|
||||
nsHTMLTableAccessible* accessible = nsnull;
|
||||
if (aAccessibleTable)
|
||||
CallQueryInterface(aAccessibleTable, &accessible);
|
||||
|
||||
return accessible;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_A11Y
|
||||
|
||||
PRBool
|
||||
|
|
|
@ -44,6 +44,7 @@
|
|||
#include "nsIAccessibleDocument.h"
|
||||
#include "nsIAccessibleRole.h"
|
||||
#include "nsIAccessibleText.h"
|
||||
#include "nsIAccessibleTable.h"
|
||||
#include "nsARIAMap.h"
|
||||
|
||||
#include "nsIDOMNode.h"
|
||||
|
@ -53,6 +54,7 @@
|
|||
|
||||
class nsAccessNode;
|
||||
class nsAccessible;
|
||||
class nsHTMLTableAccessible;
|
||||
|
||||
class nsAccUtils
|
||||
{
|
||||
|
@ -327,6 +329,12 @@ public:
|
|||
static already_AddRefed<nsAccessible>
|
||||
QueryAccessible(nsIAccessible *aAccessible);
|
||||
|
||||
/**
|
||||
* Query nsHTMLTableAccessible from the given nsIAccessibleTable.
|
||||
*/
|
||||
static already_AddRefed<nsHTMLTableAccessible>
|
||||
QueryAccessibleTable(nsIAccessibleTable *aAccessibleTable);
|
||||
|
||||
#ifdef DEBUG_A11Y
|
||||
/**
|
||||
* Detect whether the given accessible object implements nsIAccessibleText,
|
||||
|
|
|
@ -56,12 +56,14 @@
|
|||
|
||||
|
||||
// Alphabetical list of generic atoms
|
||||
ACCESSIBILITY_ATOM(_empty, "")
|
||||
ACCESSIBILITY_ATOM(button, "button")
|
||||
ACCESSIBILITY_ATOM(col, "col")
|
||||
ACCESSIBILITY_ATOM(_empty, "")
|
||||
ACCESSIBILITY_ATOM(_false, "false")
|
||||
ACCESSIBILITY_ATOM(image, "image")
|
||||
ACCESSIBILITY_ATOM(password, "password")
|
||||
ACCESSIBILITY_ATOM(reset, "reset")
|
||||
ACCESSIBILITY_ATOM(row, "row")
|
||||
ACCESSIBILITY_ATOM(submit, "submit")
|
||||
ACCESSIBILITY_ATOM(_true, "true")
|
||||
ACCESSIBILITY_ATOM(_undefined, "undefined")
|
||||
|
@ -165,6 +167,7 @@ ACCESSIBILITY_ATOM(data, "data")
|
|||
ACCESSIBILITY_ATOM(droppable, "droppable") // XUL combo box
|
||||
ACCESSIBILITY_ATOM(editable, "editable")
|
||||
ACCESSIBILITY_ATOM(_for, "for")
|
||||
ACCESSIBILITY_ATOM(headers, "headers") // HTML table
|
||||
ACCESSIBILITY_ATOM(hidden, "hidden") // XUL tree columns
|
||||
ACCESSIBILITY_ATOM(href, "href") // XUL, XLink
|
||||
ACCESSIBILITY_ATOM(increment, "increment") // XUL
|
||||
|
@ -178,6 +181,7 @@ ACCESSIBILITY_ATOM(multiline, "multiline") // XUL
|
|||
ACCESSIBILITY_ATOM(name, "name")
|
||||
ACCESSIBILITY_ATOM(onclick, "onclick")
|
||||
ACCESSIBILITY_ATOM(readonly, "readonly")
|
||||
ACCESSIBILITY_ATOM(scope, "scope") // HTML table
|
||||
ACCESSIBILITY_ATOM(simple, "simple") // XLink
|
||||
ACCESSIBILITY_ATOM(src, "src")
|
||||
ACCESSIBILITY_ATOM(selected, "selected")
|
||||
|
|
|
@ -557,6 +557,10 @@ nsAccessibilityService::CreateHTMLAccessibleByMarkup(nsIFrame *aFrame,
|
|||
tag == nsAccessibilityAtoms::q) {
|
||||
return CreateHyperTextAccessible(aFrame, aAccessible);
|
||||
}
|
||||
else if (nsCoreUtils::IsHTMLTableHeader(content)) {
|
||||
*aAccessible = new nsHTMLTableHeaderAccessible(aNode, aWeakShell);
|
||||
}
|
||||
|
||||
NS_IF_ADDREF(*aAccessible);
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
@ -71,7 +71,6 @@
|
|||
#include "nsContentCID.h"
|
||||
#include "nsComponentManagerUtils.h"
|
||||
#include "nsIInterfaceRequestorUtils.h"
|
||||
#include "nsIMutableArray.h"
|
||||
|
||||
static NS_DEFINE_IID(kRangeCID, NS_RANGE_CID);
|
||||
|
||||
|
@ -805,6 +804,56 @@ nsCoreUtils::GetElementsByIDRefsAttr(nsIContent *aContent, nsIAtom *aAttr,
|
|||
return;
|
||||
}
|
||||
|
||||
void
|
||||
nsCoreUtils::GetElementsHavingIDRefsAttr(nsIContent *aRootContent,
|
||||
nsIContent *aContent,
|
||||
nsIAtom *aIDRefsAttr,
|
||||
nsIArray **aElements)
|
||||
{
|
||||
*aElements = nsnull;
|
||||
|
||||
nsAutoString id;
|
||||
if (!GetID(aContent, id))
|
||||
return;
|
||||
|
||||
nsCAutoString idWithSpaces(' ');
|
||||
LossyAppendUTF16toASCII(id, idWithSpaces);
|
||||
idWithSpaces += ' ';
|
||||
|
||||
nsCOMPtr<nsIMutableArray> elms = do_CreateInstance(NS_ARRAY_CONTRACTID);
|
||||
if (!elms)
|
||||
return;
|
||||
|
||||
GetElementsHavingIDRefsAttrImpl(aRootContent, idWithSpaces, aIDRefsAttr,
|
||||
elms);
|
||||
NS_ADDREF(*aElements = elms);
|
||||
}
|
||||
|
||||
void
|
||||
nsCoreUtils::GetElementsHavingIDRefsAttrImpl(nsIContent *aRootContent,
|
||||
nsCString& aIdWithSpaces,
|
||||
nsIAtom *aIDRefsAttr,
|
||||
nsIMutableArray *aElements)
|
||||
{
|
||||
PRUint32 childCount = aRootContent->GetChildCount();
|
||||
for (PRUint32 index = 0; index < childCount; index++) {
|
||||
nsIContent* child = aRootContent->GetChildAt(index);
|
||||
nsAutoString idList;
|
||||
if (child->GetAttr(kNameSpaceID_None, aIDRefsAttr, idList)) {
|
||||
idList.Insert(' ', 0); // Surround idlist with spaces for search
|
||||
idList.Append(' ');
|
||||
// idList is now a set of id's with spaces around each, and id also has
|
||||
// spaces around it. If id is a substring of idList then we have a match.
|
||||
if (idList.Find(aIdWithSpaces) != -1) {
|
||||
aElements->AppendElement(child, PR_FALSE);
|
||||
continue; // Do not search inside children.
|
||||
}
|
||||
}
|
||||
GetElementsHavingIDRefsAttrImpl(child, aIdWithSpaces,
|
||||
aIDRefsAttr, aElements);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsCoreUtils::GetComputedStyleDeclaration(const nsAString& aPseudoElt,
|
||||
nsIDOMNode *aNode,
|
||||
|
|
|
@ -49,6 +49,7 @@
|
|||
#include "nsIFrame.h"
|
||||
#include "nsIDocShellTreeItem.h"
|
||||
#include "nsIArray.h"
|
||||
#include "nsIMutableArray.h"
|
||||
#include "nsPoint.h"
|
||||
|
||||
class nsCoreUtils
|
||||
|
@ -248,6 +249,27 @@ public:
|
|||
static void GetElementsByIDRefsAttr(nsIContent *aContent, nsIAtom *aAttr,
|
||||
nsIArray **aRefElements);
|
||||
|
||||
/**
|
||||
* Return the array of elements having IDRefs that points to the given node.
|
||||
*
|
||||
* @param aRootContent [in] root element to search inside
|
||||
* @param aContent [in] an element having ID attribute
|
||||
* @param aIDRefsAttr [in] IDRefs attribute
|
||||
* @param aElements [out] result array of elements
|
||||
*/
|
||||
static void GetElementsHavingIDRefsAttr(nsIContent *aRootContent,
|
||||
nsIContent *aContent,
|
||||
nsIAtom *aIDRefsAttr,
|
||||
nsIArray **aElements);
|
||||
|
||||
/**
|
||||
* Helper method for GetElementsHavingIDRefsAttr.
|
||||
*/
|
||||
static void GetElementsHavingIDRefsAttrImpl(nsIContent *aRootContent,
|
||||
nsCString& aIdWithSpaces,
|
||||
nsIAtom *aIDRefsAttr,
|
||||
nsIMutableArray *aElements);
|
||||
|
||||
/**
|
||||
* Return computed styles declaration for the given node.
|
||||
*/
|
||||
|
@ -339,6 +361,15 @@ public:
|
|||
*/
|
||||
static already_AddRefed<nsIBoxObject>
|
||||
GetTreeBodyBoxObject(nsITreeBoxObject *aTreeBoxObj);
|
||||
|
||||
/**
|
||||
* Return true if the given node is table header element.
|
||||
*/
|
||||
static PRBool IsHTMLTableHeader(nsIContent *aContent)
|
||||
{
|
||||
return aContent->NodeInfo()->Equals(nsAccessibilityAtoms::th) ||
|
||||
aContent->HasAttr(kNameSpaceID_None, nsAccessibilityAtoms::scope);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -158,3 +158,33 @@ nsRelUtils::AddTargetFromNeighbour(PRUint32 aRelationType,
|
|||
nsCoreUtils::FindNeighbourPointingToNode(aContent, aNeighboutAttr,
|
||||
aNeighboutTagName));
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsRelUtils::AddTargetFromChildrenHavingIDRefsAttr(PRUint32 aRelationType,
|
||||
nsIAccessibleRelation **aRelation,
|
||||
nsIContent *aRootContent,
|
||||
nsIContent *aContent,
|
||||
nsIAtom *aIDRefsAttr)
|
||||
{
|
||||
nsCOMPtr<nsIArray> elms;
|
||||
nsCoreUtils::GetElementsHavingIDRefsAttr(aRootContent, aContent, aIDRefsAttr,
|
||||
getter_AddRefs(elms));
|
||||
if (!elms)
|
||||
return NS_OK_NO_RELATION_TARGET;
|
||||
|
||||
PRUint32 count = 0;
|
||||
nsresult rv = elms->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(elms, idx, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = AddTargetFromContent(aRelationType, aRelation, content);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
@ -134,6 +134,23 @@ public:
|
|||
nsIAtom *aNeighboutAttr,
|
||||
nsIAtom *aNeighboutTagName = nsnull);
|
||||
|
||||
/**
|
||||
* Create the relation if the given relation is null and add the targets to it
|
||||
* that have IDRefs attribute pointing to the given node.
|
||||
*
|
||||
* @param aRelationType [in] relation type
|
||||
* @param aRelation [in, out] relation object
|
||||
* @param aRootContent [in] root node we search inside of
|
||||
* @param aContent [in] node having ID
|
||||
* @param aIDRefsAttr [in] IDRefs attribute
|
||||
*/
|
||||
static nsresult
|
||||
AddTargetFromChildrenHavingIDRefsAttr(PRUint32 aRelationType,
|
||||
nsIAccessibleRelation **aRelation,
|
||||
nsIContent *aRootContent,
|
||||
nsIContent *aContent,
|
||||
nsIAtom *aIDRefsAttr);
|
||||
|
||||
/**
|
||||
* Query nsAccessibleRelation from the given nsIAccessibleRelation.
|
||||
*/
|
||||
|
|
|
@ -61,7 +61,9 @@
|
|||
#include "nsLayoutErrors.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsHTMLTableCellAccessible
|
||||
// nsHTMLTableCellAccessible implementation
|
||||
|
||||
// nsISupports
|
||||
|
||||
NS_IMPL_ISUPPORTS_INHERITED0(nsHTMLTableCellAccessible, nsHyperTextAccessible)
|
||||
|
||||
|
@ -70,6 +72,8 @@ nsHyperTextAccessibleWrap(aDomNode, aShell)
|
|||
{
|
||||
}
|
||||
|
||||
// nsAccessible
|
||||
|
||||
/* unsigned long getRole (); */
|
||||
nsresult
|
||||
nsHTMLTableCellAccessible::GetRoleInternal(PRUint32 *aResult)
|
||||
|
@ -87,11 +91,94 @@ nsHTMLTableCellAccessible::GetAttributesInternal(nsIPersistentProperties *aAttri
|
|||
nsresult rv = nsHyperTextAccessibleWrap::GetAttributesInternal(aAttributes);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIAccessibleTable> tableAcc(GetTableAccessible());
|
||||
if (!tableAcc)
|
||||
return NS_OK;
|
||||
|
||||
PRInt32 rowIdx = -1, colIdx = -1;
|
||||
rv = GetCellIndexes(rowIdx, colIdx);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
PRInt32 idx = -1;
|
||||
rv = tableAcc->GetIndexAt(rowIdx, colIdx, &idx);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsAutoString stringIdx;
|
||||
stringIdx.AppendInt(idx);
|
||||
nsAccUtils::SetAccAttr(aAttributes, nsAccessibilityAtoms::tableCellIndex,
|
||||
stringIdx);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// nsIAccessible
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsHTMLTableCellAccessible::GetRelationByType(PRUint32 aRelationType,
|
||||
nsIAccessibleRelation **aRelation)
|
||||
{
|
||||
nsresult rv = nsHyperTextAccessibleWrap::GetRelationByType(aRelationType,
|
||||
aRelation);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (aRelationType != nsIAccessibleRelation::RELATION_DESCRIBED_BY)
|
||||
return NS_OK;
|
||||
|
||||
// 'described_by' relation from @headers attribute.
|
||||
nsCOMPtr<nsIContent> content(do_QueryInterface(mDOMNode));
|
||||
rv = nsRelUtils::AddTargetFromIDRefsAttr(aRelationType, aRelation,
|
||||
content, nsAccessibilityAtoms::headers);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (rv != NS_OK_NO_RELATION_TARGET)
|
||||
return rv; // Do not calculate more relations.
|
||||
|
||||
// 'described_by' relation from hierarchy (see 11.4.3 "Algorithm to find
|
||||
// heading information" of w3c HTML 4.01)
|
||||
return FindCellsForRelation(eHeadersForCell, aRelationType, aRelation);
|
||||
}
|
||||
|
||||
// nsHTMLTableCellAccessible
|
||||
|
||||
already_AddRefed<nsIAccessibleTable>
|
||||
nsHTMLTableCellAccessible::GetTableAccessible()
|
||||
{
|
||||
nsCOMPtr<nsIAccessible> childAcc(this);
|
||||
|
||||
nsCOMPtr<nsIAccessible> parentAcc;
|
||||
nsresult rv = childAcc->GetParent(getter_AddRefs(parentAcc));
|
||||
if (NS_FAILED(rv))
|
||||
return nsnull;
|
||||
|
||||
while (parentAcc) {
|
||||
if (nsAccUtils::Role(parentAcc) == nsIAccessibleRole::ROLE_TABLE) {
|
||||
// Table accessible must implement nsIAccessibleTable interface but if
|
||||
// it isn't happen (for example because of ARIA usage).
|
||||
if (!parentAcc)
|
||||
return nsnull;
|
||||
|
||||
nsIAccessibleTable* tableAcc = nsnull;
|
||||
CallQueryInterface(parentAcc, &tableAcc);
|
||||
return tableAcc;
|
||||
}
|
||||
|
||||
parentAcc.swap(childAcc);
|
||||
rv = childAcc->GetParent(getter_AddRefs(parentAcc));
|
||||
if (NS_FAILED(rv))
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHTMLTableCellAccessible::GetCellIndexes(PRInt32& aRowIndex,
|
||||
PRInt32& aColIndex)
|
||||
{
|
||||
nsCOMPtr<nsIContent> content(do_QueryInterface(mDOMNode));
|
||||
|
||||
nsCOMPtr<nsIPresShell> shell = GetPresShell();
|
||||
NS_ENSURE_STATE(shell);
|
||||
|
||||
|
||||
nsIFrame *frame = shell->GetPrimaryFrameFor(content);
|
||||
NS_ASSERTION(frame, "The frame cannot be obtaied for HTML table cell.");
|
||||
NS_ENSURE_STATE(frame);
|
||||
|
@ -99,48 +186,211 @@ nsHTMLTableCellAccessible::GetAttributesInternal(nsIPersistentProperties *aAttri
|
|||
nsITableCellLayout *cellLayout = do_QueryFrame(frame);
|
||||
NS_ENSURE_STATE(cellLayout);
|
||||
|
||||
PRInt32 rowIdx = -1, cellIdx = -1;
|
||||
rv = cellLayout->GetCellIndexes(rowIdx, cellIdx);
|
||||
return cellLayout->GetCellIndexes(aRowIndex, aColIndex);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHTMLTableCellAccessible::FindCellsForRelation(PRInt32 aSearchHint,
|
||||
PRUint32 aRelationType,
|
||||
nsIAccessibleRelation **aRelation)
|
||||
{
|
||||
nsCOMPtr<nsIAccessibleTable> tableAcc(GetTableAccessible());
|
||||
nsRefPtr<nsHTMLTableAccessible> nsTableAcc =
|
||||
nsAccUtils::QueryAccessibleTable(tableAcc);
|
||||
if (!nsTableAcc)
|
||||
return NS_OK; // Do not fail because of wrong markup.
|
||||
|
||||
nsCOMPtr<nsIContent> content(do_QueryInterface(mDOMNode));
|
||||
|
||||
PRInt32 rowIdx = -1, colIdx = -1;
|
||||
nsresult rv = GetCellIndexes(rowIdx, colIdx);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIAccessible> childAcc(this);
|
||||
PRBool moveToTopLeft = aSearchHint == eHeadersForCell;
|
||||
PRInt32 dir = (moveToTopLeft) ? -1 : 1;
|
||||
PRInt32 bound = 0;
|
||||
|
||||
nsCOMPtr<nsIAccessible> parentAcc;
|
||||
rv = childAcc->GetParent(getter_AddRefs(parentAcc));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
while (parentAcc) {
|
||||
if (nsAccUtils::Role(parentAcc) == nsIAccessibleRole::ROLE_TABLE) {
|
||||
// Table accessible must implement nsIAccessibleTable interface but if
|
||||
// it isn't happen (for example because of ARIA usage) we shouldn't fail
|
||||
// on getting other attributes.
|
||||
nsCOMPtr<nsIAccessibleTable> tableAcc(do_QueryInterface(parentAcc));
|
||||
if (!tableAcc)
|
||||
return NS_OK;
|
||||
|
||||
PRInt32 idx = -1;
|
||||
rv = tableAcc->GetIndexAt(rowIdx, cellIdx, &idx);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsAutoString stringIdx;
|
||||
stringIdx.AppendInt(idx);
|
||||
nsAccUtils::SetAccAttr(aAttributes, nsAccessibilityAtoms::tableCellIndex,
|
||||
stringIdx);
|
||||
return NS_OK;
|
||||
// left/right direction
|
||||
if (aSearchHint != eCellsForColumnHeader) {
|
||||
if (!moveToTopLeft) {
|
||||
tableAcc->GetColumns(&bound);
|
||||
bound--;
|
||||
}
|
||||
|
||||
parentAcc.swap(childAcc);
|
||||
rv = childAcc->GetParent(getter_AddRefs(parentAcc));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
for (PRInt32 index = colIdx + dir; dir * index <= bound; index += dir) {
|
||||
// Left direction means we look for the first columnheader. Right direction
|
||||
// means we look for all cells underneath of columnheader.
|
||||
nsIContent *cellContent = FindCell(nsTableAcc, content, rowIdx, index,
|
||||
moveToTopLeft);
|
||||
|
||||
if (cellContent) {
|
||||
nsRelUtils::AddTargetFromContent(aRelationType, aRelation, cellContent);
|
||||
if (moveToTopLeft)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// up/down direction
|
||||
if (aSearchHint != eCellsForRowHeader) {
|
||||
if (!moveToTopLeft) {
|
||||
tableAcc->GetRows(&bound);
|
||||
bound--;
|
||||
}
|
||||
|
||||
for (PRInt32 index = rowIdx + dir; dir * index <= bound; index += dir) {
|
||||
// Left direction means we look for the first rowheader. Right direction
|
||||
// means we look for all cells underneath of rowheader.
|
||||
nsIContent *cellContent = FindCell(nsTableAcc, content, index, colIdx,
|
||||
moveToTopLeft);
|
||||
|
||||
if (cellContent) {
|
||||
nsRelUtils::AddTargetFromContent(aRelationType, aRelation, cellContent);
|
||||
if (moveToTopLeft)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsIContent*
|
||||
nsHTMLTableCellAccessible::FindCell(nsHTMLTableAccessible *aTableAcc,
|
||||
nsIContent *aAnchorCell,
|
||||
PRInt32 aRowIdx, PRInt32 aColIdx,
|
||||
PRInt32 aLookForHeader)
|
||||
{
|
||||
nsCOMPtr<nsIDOMElement> cellElm;
|
||||
aTableAcc->GetCellAt(aRowIdx, aColIdx, *getter_AddRefs(cellElm));
|
||||
if (!cellElm)
|
||||
return nsnull;
|
||||
|
||||
nsCOMPtr<nsIContent> cellContent(do_QueryInterface(cellElm));
|
||||
if (aAnchorCell == cellContent) // colspan or rowspan case
|
||||
return nsnull;
|
||||
|
||||
if (aLookForHeader) {
|
||||
if (nsCoreUtils::IsHTMLTableHeader(cellContent))
|
||||
return cellContent;
|
||||
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
return cellContent;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsHTMLTableHeaderAccessible
|
||||
|
||||
nsHTMLTableHeaderAccessible::
|
||||
nsHTMLTableHeaderAccessible(nsIDOMNode* aDomNode, nsIWeakReference* aShell):
|
||||
nsHTMLTableCellAccessible(aDomNode, aShell)
|
||||
{
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHTMLTableHeaderAccessible::GetRoleInternal(PRUint32 *aRole)
|
||||
{
|
||||
nsCOMPtr<nsIContent> content(do_QueryInterface(mDOMNode));
|
||||
|
||||
// Check value of @scope attribute.
|
||||
static nsIContent::AttrValuesArray scopeValues[] =
|
||||
{&nsAccessibilityAtoms::col, &nsAccessibilityAtoms::row, nsnull};
|
||||
PRInt32 valueIdx =
|
||||
content->FindAttrValueIn(kNameSpaceID_None, nsAccessibilityAtoms::scope,
|
||||
scopeValues, eCaseMatters);
|
||||
|
||||
switch (valueIdx) {
|
||||
case 0:
|
||||
*aRole = nsIAccessibleRole::ROLE_COLUMNHEADER;
|
||||
return NS_OK;
|
||||
case 1:
|
||||
*aRole = nsIAccessibleRole::ROLE_ROWHEADER;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Assume it's columnheader if there are headers in siblings, oterwise
|
||||
// rowheader.
|
||||
nsIContent* parent = content->GetParent();
|
||||
PRInt32 indexInParent = parent->IndexOf(content);
|
||||
|
||||
for (PRInt32 idx = indexInParent - 1; idx >= 0; idx--) {
|
||||
nsIContent* sibling = parent->GetChildAt(idx);
|
||||
if (sibling && sibling->IsNodeOfType(nsINode::eELEMENT)) {
|
||||
if (nsCoreUtils::IsHTMLTableHeader(sibling))
|
||||
*aRole = nsIAccessibleRole::ROLE_COLUMNHEADER;
|
||||
else
|
||||
*aRole = nsIAccessibleRole::ROLE_ROWHEADER;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
PRInt32 childCount = parent->GetChildCount();
|
||||
for (PRInt32 idx = indexInParent + 1; idx < childCount; idx++) {
|
||||
nsIContent* sibling = parent->GetChildAt(idx);
|
||||
if (sibling && sibling->IsNodeOfType(nsINode::eELEMENT)) {
|
||||
if (nsCoreUtils::IsHTMLTableHeader(sibling))
|
||||
*aRole = nsIAccessibleRole::ROLE_COLUMNHEADER;
|
||||
else
|
||||
*aRole = nsIAccessibleRole::ROLE_ROWHEADER;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsHTMLTableHeaderAccessible::GetRelationByType(PRUint32 aRelationType,
|
||||
nsIAccessibleRelation **aRelation)
|
||||
{
|
||||
nsresult rv = nsHyperTextAccessibleWrap::
|
||||
GetRelationByType(aRelationType, aRelation);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (aRelationType != nsIAccessibleRelation::RELATION_DESCRIPTION_FOR)
|
||||
return rv;
|
||||
|
||||
// 'description_for' relation from @headers attribute placed on table cells.
|
||||
nsCOMPtr<nsIAccessibleTable> tableAcc(GetTableAccessible());
|
||||
if (!tableAcc)
|
||||
return NS_OK;
|
||||
|
||||
nsCOMPtr<nsIAccessNode> tableAccNode(do_QueryInterface(tableAcc));
|
||||
nsCOMPtr<nsIDOMNode> tableNode;
|
||||
tableAccNode->GetDOMNode(getter_AddRefs(tableNode));
|
||||
nsCOMPtr<nsIContent> tableContent(do_QueryInterface(tableNode));
|
||||
if (!tableContent)
|
||||
return NS_OK;
|
||||
|
||||
nsCOMPtr<nsIContent> content(do_QueryInterface(mDOMNode));
|
||||
rv = nsRelUtils::
|
||||
AddTargetFromChildrenHavingIDRefsAttr(aRelationType, aRelation,
|
||||
tableContent, content,
|
||||
nsAccessibilityAtoms::headers);
|
||||
|
||||
if (rv != NS_OK_NO_RELATION_TARGET)
|
||||
return rv; // Do not calculate more relations.
|
||||
|
||||
// 'description_for' relation from hierarchy.
|
||||
PRUint32 role;
|
||||
rv = GetRoleInternal(&role);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (role == nsIAccessibleRole::ROLE_COLUMNHEADER)
|
||||
return FindCellsForRelation(eCellsForColumnHeader, aRelationType, aRelation);
|
||||
|
||||
return FindCellsForRelation(eCellsForRowHeader, aRelationType, aRelation);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsHTMLTableAccessible
|
||||
|
||||
NS_IMPL_ISUPPORTS_INHERITED1(nsHTMLTableAccessible, nsAccessible, nsIAccessibleTable)
|
||||
NS_IMPL_ISUPPORTS_INHERITED2(nsHTMLTableAccessible, nsAccessible,
|
||||
nsHTMLTableAccessible, nsIAccessibleTable)
|
||||
|
||||
nsHTMLTableAccessible::nsHTMLTableAccessible(nsIDOMNode* aDomNode, nsIWeakReference* aShell):
|
||||
nsAccessibleWrap(aDomNode, aShell)
|
||||
|
|
|
@ -42,25 +42,105 @@
|
|||
#include "nsBaseWidgetAccessible.h"
|
||||
#include "nsIAccessibleTable.h"
|
||||
|
||||
class nsITableLayout;
|
||||
|
||||
/**
|
||||
* HTML table cell accessible (html:td).
|
||||
*/
|
||||
class nsHTMLTableCellAccessible : public nsHyperTextAccessibleWrap
|
||||
{
|
||||
public:
|
||||
nsHTMLTableCellAccessible(nsIDOMNode* aDomNode, nsIWeakReference* aShell);
|
||||
|
||||
// nsISupports
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
// nsIAccessible
|
||||
NS_IMETHOD GetRelationByType(PRUint32 aRelationType,
|
||||
nsIAccessibleRelation **aRelation);
|
||||
|
||||
// nsAccessible
|
||||
virtual nsresult GetRoleInternal(PRUint32 *aRole);
|
||||
virtual nsresult GetAttributesInternal(nsIPersistentProperties *aAttributes);
|
||||
|
||||
protected:
|
||||
already_AddRefed<nsIAccessibleTable> GetTableAccessible();
|
||||
nsresult GetCellIndexes(PRInt32& aRowIdx, PRInt32& aColIdx);
|
||||
|
||||
/**
|
||||
* Search hint enum constants. Used by FindCellsForRelation method.
|
||||
*/
|
||||
enum {
|
||||
// search for header cells, up-left direction search
|
||||
eHeadersForCell,
|
||||
// search for row header cell, right direction search
|
||||
eCellsForRowHeader,
|
||||
// search for column header cell, down direction search
|
||||
eCellsForColumnHeader
|
||||
};
|
||||
|
||||
/**
|
||||
* Add found cells as relation targets.
|
||||
*
|
||||
* @param aSearchHint [in] enum constan defined above, defines an
|
||||
* algorithm to search cells
|
||||
* @param aRelationType [in] relation type
|
||||
* @param aRelation [in, out] relation object
|
||||
*/
|
||||
nsresult FindCellsForRelation(PRInt32 aSearchHint, PRUint32 aRelationType,
|
||||
nsIAccessibleRelation **aRelation);
|
||||
|
||||
/**
|
||||
* Return the cell or header cell at the given row and column.
|
||||
*
|
||||
* @param aTableAcc [in] table accessible the search is prepared in
|
||||
* @param aAnchorCell [in] anchor cell, found cell should be different
|
||||
* from it
|
||||
* @param aRowIdx [in] row index
|
||||
* @param aColIdx [in] column index
|
||||
* @param aLookForHeader [in] flag specifies if found cell must be a header
|
||||
* @return found cell content
|
||||
*/
|
||||
nsIContent* FindCell(nsHTMLTableAccessible *aTableAcc, nsIContent *aAnchorCell,
|
||||
PRInt32 aRowIdx, PRInt32 aColIdx,
|
||||
PRInt32 aLookForHeader);
|
||||
};
|
||||
|
||||
class nsITableLayout;
|
||||
|
||||
/**
|
||||
* HTML table row/column header accessible (html:th or html:td@scope).
|
||||
*/
|
||||
class nsHTMLTableHeaderAccessible : public nsHTMLTableCellAccessible
|
||||
{
|
||||
public:
|
||||
nsHTMLTableHeaderAccessible(nsIDOMNode* aDomNode, nsIWeakReference* aShell);
|
||||
|
||||
// nsAccessible
|
||||
virtual nsresult GetRoleInternal(PRUint32 *aRole);
|
||||
|
||||
// nsIAccessible
|
||||
NS_IMETHOD GetRelationByType(PRUint32 aRelationType,
|
||||
nsIAccessibleRelation **aRelation);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* HTML table accessible.
|
||||
*/
|
||||
|
||||
// To turn on table debugging descriptions define SHOW_LAYOUT_HEURISTIC
|
||||
// This allow release trunk builds to be used by testers to refine the
|
||||
// data vs. layout heuristic
|
||||
// #define SHOW_LAYOUT_HEURISTIC
|
||||
|
||||
#define NS_TABLEACCESSIBLE_IMPL_CID \
|
||||
{ /* 8d6d9c40-74bd-47ac-88dc-4a23516aa23d */ \
|
||||
0x8d6d9c40, \
|
||||
0x74bd, \
|
||||
0x47ac, \
|
||||
{ 0x88, 0xdc, 0x4a, 0x23, 0x51, 0x6a, 0xa2, 0x3d } \
|
||||
}
|
||||
|
||||
class nsHTMLTableAccessible : public nsAccessibleWrap,
|
||||
public nsIAccessibleTable
|
||||
{
|
||||
|
@ -69,6 +149,7 @@ public:
|
|||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_NSIACCESSIBLETABLE
|
||||
NS_DECLARE_STATIC_IID_ACCESSOR(NS_TABLEACCESSIBLE_IMPL_CID)
|
||||
|
||||
// nsIAccessible
|
||||
NS_IMETHOD GetDescription(nsAString& aDescription);
|
||||
|
@ -97,6 +178,11 @@ public:
|
|||
*/
|
||||
PRBool IsValidRow(PRInt32 aRow);
|
||||
|
||||
/**
|
||||
* Retun cell element at the given row and column index.
|
||||
*/
|
||||
nsresult GetCellAt(PRInt32 aRowIndex, PRInt32 aColIndex,
|
||||
nsIDOMElement* &aCell);
|
||||
protected:
|
||||
|
||||
/**
|
||||
|
@ -124,15 +210,16 @@ protected:
|
|||
virtual void CacheChildren();
|
||||
nsresult GetTableNode(nsIDOMNode **_retval);
|
||||
nsresult GetTableLayout(nsITableLayout **aLayoutObject);
|
||||
nsresult GetCellAt(PRInt32 aRowIndex,
|
||||
PRInt32 aColIndex,
|
||||
nsIDOMElement* &aCell);
|
||||
PRBool HasDescendant(char *aTagName, PRBool aAllowEmpty = PR_TRUE);
|
||||
#ifdef SHOW_LAYOUT_HEURISTIC
|
||||
nsAutoString mLayoutHeuristic;
|
||||
#endif
|
||||
};
|
||||
|
||||
NS_DEFINE_STATIC_IID_ACCESSOR(nsHTMLTableAccessible,
|
||||
NS_TABLEACCESSIBLE_IMPL_CID)
|
||||
|
||||
|
||||
class nsHTMLTableHeadAccessible : public nsHTMLTableAccessible
|
||||
{
|
||||
public:
|
||||
|
|
|
@ -110,6 +110,7 @@ _TEST_FILES =\
|
|||
test_objectattrs.html \
|
||||
test_relations.html \
|
||||
test_relations.xul \
|
||||
test_relations_table.html \
|
||||
test_role_nsHyperTextAcc.html \
|
||||
test_role_table_cells.html \
|
||||
test_states.html \
|
||||
|
|
|
@ -0,0 +1,112 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//w3c//dtd html 4.0 transitional//en">
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/MochiKit/packed.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/a11y/accessible/common.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/a11y/accessible/relations.js"></script>
|
||||
|
||||
<script type="application/javascript">
|
||||
|
||||
function doTest()
|
||||
{
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// relations from markup
|
||||
|
||||
// 'description_for' relations for column headers
|
||||
testRelation("table1_0", RELATION_DESCRIPTION_FOR,
|
||||
["table1_3", "table1_6"]);
|
||||
testRelation("table1_1", RELATION_DESCRIPTION_FOR,
|
||||
["table1_4", "table1_7"]);
|
||||
testRelation("table1_2", RELATION_DESCRIPTION_FOR,
|
||||
["table1_5", "table1_8"]);
|
||||
|
||||
// 'description_for' relations for row headers
|
||||
testRelation("table1_3", RELATION_DESCRIPTION_FOR,
|
||||
["table1_4", "table1_5"]);
|
||||
testRelation("table1_6", RELATION_DESCRIPTION_FOR,
|
||||
["table1_7", "table1_8"]);
|
||||
|
||||
// 'described_by' relations for cells
|
||||
testRelation("table1_4", RELATION_DESCRIBED_BY,
|
||||
["table1_1", "table1_3"]);
|
||||
testRelation("table1_5", RELATION_DESCRIBED_BY,
|
||||
["table1_2", "table1_3"]);
|
||||
testRelation("table1_7", RELATION_DESCRIBED_BY,
|
||||
["table1_1", "table1_6"]);
|
||||
testRelation("table1_8", RELATION_DESCRIBED_BY,
|
||||
["table1_2", "table1_6"]);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// relations from @headers
|
||||
|
||||
// 'description_for' relations for column headers
|
||||
testRelation("table2_3", RELATION_DESCRIPTION_FOR,
|
||||
["table2_0"]);
|
||||
testRelation("table2_4", RELATION_DESCRIPTION_FOR,
|
||||
["table2_1"]);
|
||||
|
||||
// 'description_by' relations for cells
|
||||
testRelation("table2_0", RELATION_DESCRIBED_BY,
|
||||
["table2_3"]);
|
||||
testRelation("table2_1", RELATION_DESCRIBED_BY,
|
||||
["table2_4"]);
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addLoadEvent(doTest);
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id="
|
||||
title="">Mozilla Bug </a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
|
||||
<table id="table1">
|
||||
<thead>
|
||||
<tr>
|
||||
<th id="table1_0">col1</th>
|
||||
<th id="table1_1">col2</th>
|
||||
<td id="table1_2" scope="col">col3</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th id="table1_3">row1</th>
|
||||
<td id="table1_4">cell1</td>
|
||||
<td id="table1_5">cell2</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td id="table1_6" scope="row">row2</td>
|
||||
<td id="table1_7">cell3</td>
|
||||
<td id="table1_8">cell4</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<table id="table2">
|
||||
<tr>
|
||||
<td id="table2_0" headers="table2_3">cell1</td>
|
||||
<td id="table2_1" headers="table2_4">cell2</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td id="table2_3" scope="col">col1</td>
|
||||
<td id="table2_4" scope="col">col2</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
Загрузка…
Ссылка в новой задаче