Bug 491851 - implement relations inside HTML table, r=marcoz, davidb, sr=neil

This commit is contained in:
Alexander Surkov 2009-05-14 13:31:09 +08:00
Родитель 9d965cf68e
Коммит 1d48177650
12 изменённых файлов: 641 добавлений и 37 удалений

Просмотреть файл

@ -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>