566551 - ARIA grid and accessible selectable methods shouldn't use GetNextSibling, r=marcoz, sr=neil

This commit is contained in:
Alexander Surkov 2010-05-20 16:40:25 +09:00
parent 4770823335
commit d2b83f1189
10 changed files with 446 additions and 195 deletions

View File

@ -50,6 +50,7 @@ LIBXUL_LIBRARY = 1
CPPSRCS = \
nsAccessNode.cpp \
nsAccEvent.cpp \
nsAccIterator.cpp \
nsARIAGridAccessible.cpp \
nsARIAMap.cpp \
nsDocAccessible.cpp \

View File

@ -38,7 +38,7 @@
#include "nsARIAGridAccessible.h"
#include "nsAccUtils.h"
#include "nsAccIterator.h"
#include "nsIMutableArray.h"
#include "nsComponentManagerUtils.h"
@ -101,9 +101,13 @@ nsARIAGridAccessible::GetColumnCount(PRInt32 *acolumnCount)
if (IsDefunct())
return NS_ERROR_FAILURE;
nsCOMPtr<nsIAccessible> row = GetNextRow();
nsCOMPtr<nsIAccessible> cell;
while ((cell = GetNextCellInRow(row, cell)))
nsAccIterator rowIter(this, nsAccIterator::GetRow);
nsAccessible *row = rowIter.GetNext();
nsAccIterator cellIter(row, nsAccIterator::GetCell);
nsAccessible *cell = nsnull;
while ((cell = cellIter.GetNext()))
(*acolumnCount)++;
return NS_OK;
@ -118,8 +122,8 @@ nsARIAGridAccessible::GetRowCount(PRInt32 *arowCount)
if (IsDefunct())
return NS_ERROR_FAILURE;
nsCOMPtr<nsIAccessible> row;
while ((row = GetNextRow(row)))
nsAccIterator rowIter(this, nsAccIterator::GetRow);
while (rowIter.GetNext())
(*arowCount)++;
return NS_OK;
@ -135,10 +139,10 @@ nsARIAGridAccessible::GetCellAt(PRInt32 aRowIndex, PRInt32 aColumnIndex,
if (IsDefunct())
return NS_ERROR_FAILURE;
nsCOMPtr<nsIAccessible> row = GetRowAt(aRowIndex);
nsAccessible *row = GetRowAt(aRowIndex);
NS_ENSURE_ARG(row);
nsCOMPtr<nsIAccessible> cell = GetCellInRowAt(row, aColumnIndex);
nsAccessible *cell = GetCellInRowAt(row, aColumnIndex);
NS_ENSURE_ARG(cell);
NS_ADDREF(*aAccessible = cell);
@ -288,20 +292,21 @@ nsARIAGridAccessible::IsColumnSelected(PRInt32 aColumn, PRBool *aIsSelected)
NS_ENSURE_ARG(IsValidColumn(aColumn));
nsCOMPtr<nsIAccessible> row = GetNextRow();
nsAccIterator rowIter(this, nsAccIterator::GetRow);
nsAccessible *row = rowIter.GetNext();
if (!row)
return NS_OK;
do {
if (!nsAccUtils::IsARIASelected(row)) {
nsCOMPtr<nsIAccessible> cell = GetCellInRowAt(row, aColumn);
nsAccessible *cell = GetCellInRowAt(row, aColumn);
if (!cell) // Do not fail due to wrong markup
return NS_OK;
if (!nsAccUtils::IsARIASelected(cell))
return NS_OK;
}
} while ((row = GetNextRow(row)));
} while ((row = rowIter.GetNext()));
*aIsSelected = PR_TRUE;
return NS_OK;
@ -316,12 +321,13 @@ nsARIAGridAccessible::IsRowSelected(PRInt32 aRow, PRBool *aIsSelected)
if (IsDefunct())
return NS_ERROR_FAILURE;
nsCOMPtr<nsIAccessible> row = GetRowAt(aRow);
nsAccessible *row = GetRowAt(aRow);
NS_ENSURE_ARG(row);
if (!nsAccUtils::IsARIASelected(row)) {
nsCOMPtr<nsIAccessible> cell;
while ((cell = GetNextCellInRow(row, cell))) {
nsAccIterator cellIter(row, nsAccIterator::GetCell);
nsAccessible *cell = nsnull;
while ((cell = cellIter.GetNext())) {
if (!nsAccUtils::IsARIASelected(cell))
return NS_OK;
}
@ -341,11 +347,11 @@ nsARIAGridAccessible::IsCellSelected(PRInt32 aRow, PRInt32 aColumn,
if (IsDefunct())
return NS_ERROR_FAILURE;
nsCOMPtr<nsIAccessible> row(GetRowAt(aRow));
nsAccessible *row = GetRowAt(aRow);
NS_ENSURE_ARG(row);
if (!nsAccUtils::IsARIASelected(row)) {
nsCOMPtr<nsIAccessible> cell(GetCellInRowAt(row, aColumn));
nsAccessible *cell = GetCellInRowAt(row, aColumn);
NS_ENSURE_ARG(cell);
if (!nsAccUtils::IsARIASelected(cell))
@ -367,16 +373,20 @@ nsARIAGridAccessible::GetSelectedCellCount(PRUint32* aCount)
PRInt32 colCount = 0;
GetColumnCount(&colCount);
nsCOMPtr<nsIAccessible> row;
while ((row = GetNextRow(row))) {
nsAccIterator rowIter(this, nsAccIterator::GetRow);
nsAccessible *row = nsnull;
while ((row = rowIter.GetNext())) {
if (nsAccUtils::IsARIASelected(row)) {
(*aCount) += colCount;
continue;
}
nsCOMPtr<nsIAccessible> cell;
while ((cell = GetNextCellInRow(row, cell))) {
nsAccIterator cellIter(row, nsAccIterator::GetCell);
nsAccessible *cell = nsnull;
while ((cell = cellIter.GetNext())) {
if (nsAccUtils::IsARIASelected(cell))
(*aCount)++;
}
@ -400,14 +410,17 @@ nsARIAGridAccessible::GetSelectedRowCount(PRUint32* aCount)
if (IsDefunct())
return NS_ERROR_FAILURE;
nsCOMPtr<nsIAccessible> row;
while ((row = GetNextRow(row))) {
nsAccIterator rowIter(this, nsAccIterator::GetRow);
nsAccessible *row = nsnull;
while ((row = rowIter.GetNext())) {
if (nsAccUtils::IsARIASelected(row)) {
(*aCount)++;
continue;
}
nsCOMPtr<nsIAccessible> cell = GetNextCellInRow(row);
nsAccIterator cellIter(row, nsAccIterator::GetCell);
nsAccessible *cell = cellIter.GetNext();
if (!cell)
continue;
@ -417,7 +430,7 @@ nsARIAGridAccessible::GetSelectedRowCount(PRUint32* aCount)
isRowSelected = PR_FALSE;
break;
}
} while ((cell = GetNextCellInRow(row, cell)));
} while ((cell = cellIter.GetNext()));
if (isRowSelected)
(*aCount)++;
@ -440,18 +453,21 @@ nsARIAGridAccessible::GetSelectedCells(nsIArray **aCells)
do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIAccessible> row;
while (row = GetNextRow(row)) {
nsAccIterator rowIter(this, nsAccIterator::GetRow);
nsAccessible *row = nsnull;
while (row = rowIter.GetNext()) {
nsAccIterator cellIter(row, nsAccIterator::GetCell);
nsIAccessible *cell = nsnull;
if (nsAccUtils::IsARIASelected(row)) {
nsCOMPtr<nsIAccessible> cell;
while (cell = GetNextCellInRow(row, cell))
while (cell = cellIter.GetNext())
selCells->AppendElement(cell, PR_FALSE);
continue;
}
nsCOMPtr<nsIAccessible> cell;
while (cell = GetNextCellInRow(row, cell)) {
while (cell = cellIter.GetNext()) {
if (nsAccUtils::IsARIASelected(cell))
selCells->AppendElement(cell, PR_FALSE);
}
@ -481,8 +497,10 @@ nsARIAGridAccessible::GetSelectedCellIndices(PRUint32 *aCellsCount,
nsTArray<PRInt32> selCells(rowCount * colCount);
nsCOMPtr<nsIAccessible> row;
for (PRInt32 rowIdx = 0; row = GetNextRow(row); rowIdx++) {
nsAccIterator rowIter(this, nsAccIterator::GetRow);
nsAccessible *row = nsnull;
for (PRInt32 rowIdx = 0; row = rowIter.GetNext(); rowIdx++) {
if (nsAccUtils::IsARIASelected(row)) {
for (PRInt32 colIdx = 0; colIdx < colCount; colIdx++)
selCells.AppendElement(rowIdx * colCount + colIdx);
@ -490,8 +508,10 @@ nsARIAGridAccessible::GetSelectedCellIndices(PRUint32 *aCellsCount,
continue;
}
nsCOMPtr<nsIAccessible> cell;
for (PRInt32 colIdx = 0; cell = GetNextCellInRow(row, cell); colIdx++) {
nsAccIterator cellIter(row, nsAccIterator::GetCell);
nsAccessible *cell = nsnull;
for (PRInt32 colIdx = 0; cell = cellIter.GetNext(); colIdx++) {
if (nsAccUtils::IsARIASelected(cell))
selCells.AppendElement(rowIdx * colCount + colIdx);
}
@ -537,14 +557,17 @@ nsARIAGridAccessible::GetSelectedRowIndices(PRUint32 *arowCount,
nsTArray<PRInt32> selRows(rowCount);
nsCOMPtr<nsIAccessible> row;
for (PRInt32 rowIdx = 0; row = GetNextRow(row); rowIdx++) {
nsAccIterator rowIter(this, nsAccIterator::GetRow);
nsAccessible *row = nsnull;
for (PRInt32 rowIdx = 0; row = rowIter.GetNext(); rowIdx++) {
if (nsAccUtils::IsARIASelected(row)) {
selRows.AppendElement(rowIdx);
continue;
}
nsCOMPtr<nsIAccessible> cell = GetNextCellInRow(row);
nsAccIterator cellIter(row, nsAccIterator::GetCell);
nsAccessible *cell = cellIter.GetNext();
if (!cell)
continue;
@ -554,7 +577,7 @@ nsARIAGridAccessible::GetSelectedRowIndices(PRUint32 *arowCount,
isRowSelected = PR_FALSE;
break;
}
} while ((cell = GetNextCellInRow(row, cell)));
} while ((cell = cellIter.GetNext()));
if (isRowSelected)
selRows.AppendElement(rowIdx);
@ -580,8 +603,10 @@ nsARIAGridAccessible::SelectRow(PRInt32 aRow)
if (IsDefunct())
return NS_ERROR_FAILURE;
nsCOMPtr<nsIAccessible> row;
for (PRInt32 rowIdx = 0; row = GetNextRow(row); rowIdx++) {
nsAccIterator rowIter(this, nsAccIterator::GetRow);
nsAccessible *row = nsnull;
for (PRInt32 rowIdx = 0; row = rowIter.GetNext(); rowIdx++) {
nsresult rv = SetARIASelected(row, rowIdx == aRow);
NS_ENSURE_SUCCESS(rv, rv);
}
@ -597,14 +622,16 @@ nsARIAGridAccessible::SelectColumn(PRInt32 aColumn)
if (IsDefunct())
return NS_ERROR_FAILURE;
nsCOMPtr<nsIAccessible> row;
while ((row = GetNextRow(row))) {
nsAccIterator rowIter(this, nsAccIterator::GetRow);
nsAccessible *row = nsnull;
while ((row = rowIter.GetNext())) {
// Unselect all cells in the row.
nsresult rv = SetARIASelected(row, PR_FALSE);
NS_ENSURE_SUCCESS(rv, rv);
// Select cell at the column index.
nsCOMPtr<nsIAccessible> cell = GetCellInRowAt(row, aColumn);
nsAccessible *cell = GetCellInRowAt(row, aColumn);
if (cell) {
rv = SetARIASelected(cell, PR_TRUE);
NS_ENSURE_SUCCESS(rv, rv);
@ -620,7 +647,7 @@ nsARIAGridAccessible::UnselectRow(PRInt32 aRow)
if (IsDefunct())
return NS_ERROR_FAILURE;
nsCOMPtr<nsIAccessible> row = GetRowAt(aRow);
nsAccessible *row = GetRowAt(aRow);
NS_ENSURE_ARG(row);
return SetARIASelected(row, PR_FALSE);
@ -634,9 +661,11 @@ nsARIAGridAccessible::UnselectColumn(PRInt32 aColumn)
if (IsDefunct())
return NS_ERROR_FAILURE;
nsCOMPtr<nsIAccessible> row;
while ((row = GetNextRow(row))) {
nsCOMPtr<nsIAccessible> cell = GetCellInRowAt(row, aColumn);
nsAccIterator rowIter(this, nsAccIterator::GetRow);
nsAccessible *row = nsnull;
while ((row = rowIter.GetNext())) {
nsAccessible *cell = GetCellInRowAt(row, aColumn);
if (cell) {
nsresult rv = SetARIASelected(cell, PR_FALSE);
NS_ENSURE_SUCCESS(rv, rv);
@ -696,84 +725,39 @@ nsARIAGridAccessible::IsValidRowNColumn(PRInt32 aRow, PRInt32 aColumn)
return aColumn < colCount;
}
already_AddRefed<nsIAccessible>
nsAccessible*
nsARIAGridAccessible::GetRowAt(PRInt32 aRow)
{
PRInt32 rowIdx = aRow;
nsCOMPtr<nsIAccessible> row(GetNextRow());
while (rowIdx != 0 && (row = GetNextRow(row)))
nsAccIterator rowIter(this, nsAccIterator::GetRow);
nsAccessible *row = rowIter.GetNext();
while (rowIdx != 0 && (row = rowIter.GetNext()))
rowIdx--;
return row.forget();
return row;
}
already_AddRefed<nsIAccessible>
nsARIAGridAccessible::GetCellInRowAt(nsIAccessible *aRow, PRInt32 aColumn)
nsAccessible*
nsARIAGridAccessible::GetCellInRowAt(nsAccessible *aRow, PRInt32 aColumn)
{
PRInt32 colIdx = aColumn;
nsCOMPtr<nsIAccessible> cell(GetNextCellInRow(aRow));
while (colIdx != 0 && (cell = GetNextCellInRow(aRow, cell)))
nsAccIterator cellIter(aRow, nsAccIterator::GetCell);
nsAccessible *cell = cellIter.GetNext();
while (colIdx != 0 && (cell = cellIter.GetNext()))
colIdx--;
return cell.forget();
}
already_AddRefed<nsIAccessible>
nsARIAGridAccessible::GetNextRow(nsIAccessible *aRow)
{
nsCOMPtr<nsIAccessible> nextRow, tmpAcc;
if (!aRow)
GetFirstChild(getter_AddRefs(nextRow));
else
aRow->GetNextSibling(getter_AddRefs(nextRow));
while (nextRow) {
if (nsAccUtils::Role(nextRow) == nsIAccessibleRole::ROLE_ROW)
return nextRow.forget();
nextRow->GetNextSibling(getter_AddRefs(tmpAcc));
tmpAcc.swap(nextRow);
}
return nsnull;
}
already_AddRefed<nsIAccessible>
nsARIAGridAccessible::GetNextCellInRow(nsIAccessible *aRow, nsIAccessible *aCell)
{
if (!aRow)
return nsnull;
nsCOMPtr<nsIAccessible> nextCell, tmpAcc;
if (!aCell)
aRow->GetFirstChild(getter_AddRefs(nextCell));
else
aCell->GetNextSibling(getter_AddRefs(nextCell));
while (nextCell) {
PRUint32 role = nsAccUtils::Role(nextCell);
if (role == nsIAccessibleRole::ROLE_GRID_CELL ||
role == nsIAccessibleRole::ROLE_ROWHEADER ||
role == nsIAccessibleRole::ROLE_COLUMNHEADER)
return nextCell.forget();
nextCell->GetNextSibling(getter_AddRefs(tmpAcc));
tmpAcc.swap(nextCell);
}
return nsnull;
return cell;
}
nsresult
nsARIAGridAccessible::SetARIASelected(nsIAccessible *aAccessible,
nsARIAGridAccessible::SetARIASelected(nsAccessible *aAccessible,
PRBool aIsSelected, PRBool aNotify)
{
nsRefPtr<nsAccessible> acc = do_QueryObject(aAccessible);
nsCOMPtr<nsIDOMNode> node;
acc->GetDOMNode(getter_AddRefs(node));
NS_ENSURE_STATE(node);
nsCOMPtr<nsIContent> content(do_QueryInterface(node));
nsCOMPtr<nsIContent> content(do_QueryInterface(aAccessible->GetDOMNode()));
NS_ENSURE_STATE(content);
nsresult rv = NS_OK;
if (aIsSelected)
@ -800,8 +784,10 @@ nsARIAGridAccessible::SetARIASelected(nsIAccessible *aAccessible,
// If the given accessible is row that was unselected then remove
// aria-selected from cell accessible.
if (role == nsIAccessibleRole::ROLE_ROW) {
nsCOMPtr<nsIAccessible> cell;
while ((cell = GetNextCellInRow(aAccessible, cell))) {
nsAccIterator cellIter(aAccessible, nsAccIterator::GetCell);
nsAccessible *cell = nsnull;
while ((cell = cellIter.GetNext())) {
rv = SetARIASelected(cell, PR_FALSE, PR_FALSE);
NS_ENSURE_SUCCESS(rv, rv);
}
@ -814,16 +800,16 @@ nsARIAGridAccessible::SetARIASelected(nsIAccessible *aAccessible,
if (role == nsIAccessibleRole::ROLE_GRID_CELL ||
role == nsIAccessibleRole::ROLE_ROWHEADER ||
role == nsIAccessibleRole::ROLE_COLUMNHEADER) {
nsCOMPtr<nsIAccessible> row;
aAccessible->GetParent(getter_AddRefs(row));
nsAccessible *row = aAccessible->GetParent();
if (nsAccUtils::Role(row) == nsIAccessibleRole::ROLE_ROW &&
nsAccUtils::IsARIASelected(row)) {
rv = SetARIASelected(row, PR_FALSE, PR_FALSE);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIAccessible> cell;
while ((cell = GetNextCellInRow(row, cell))) {
nsAccIterator cellIter(row, nsAccIterator::GetCell);
nsAccessible *cell = nsnull;
while ((cell = cellIter.GetNext())) {
if (cell != aAccessible) {
rv = SetARIASelected(cell, PR_TRUE, PR_FALSE);
NS_ENSURE_SUCCESS(rv, rv);
@ -847,7 +833,8 @@ nsARIAGridAccessible::GetSelectedColumnsArray(PRUint32 *acolumnCount,
if (IsDefunct())
return NS_ERROR_FAILURE;
nsCOMPtr<nsIAccessible> row = GetNextRow();
nsAccIterator rowIter(this, nsAccIterator::GetRow);
nsAccessible *row = rowIter.GetNext();
if (!row)
return NS_OK;
@ -868,15 +855,17 @@ nsARIAGridAccessible::GetSelectedColumnsArray(PRUint32 *acolumnCount,
continue;
PRInt32 colIdx = 0;
nsCOMPtr<nsIAccessible> cell;
for (colIdx = 0; cell = GetNextCellInRow(row, cell); colIdx++) {
nsAccIterator cellIter(row, nsAccIterator::GetCell);
nsAccessible *cell = nsnull;
for (colIdx = 0; cell = cellIter.GetNext(); colIdx++) {
if (isColSelArray.SafeElementAt(colIdx, PR_FALSE) &&
!nsAccUtils::IsARIASelected(cell)) {
isColSelArray[colIdx] = PR_FALSE;
selColCount--;
}
}
} while ((row = GetNextRow(row)));
} while ((row = rowIter.GetNext()));
if (!selColCount)
return NS_OK;

View File

@ -77,31 +77,12 @@ protected:
/**
* Return row accessible at the given row index.
*/
already_AddRefed<nsIAccessible> GetRowAt(PRInt32 aRow);
nsAccessible *GetRowAt(PRInt32 aRow);
/**
* Return cell accessible at the given column index in the row.
*/
already_AddRefed<nsIAccessible> GetCellInRowAt(nsIAccessible *aRow,
PRInt32 aColumn);
/**
* Return next row accessible relative given row accessible or first row
* accessible if it is null.
*
* @param aRow [in, optional] row accessible
*/
already_AddRefed<nsIAccessible> GetNextRow(nsIAccessible *aRow = nsnull);
/**
* Return next cell accessible relative given cell accessible or first cell
* in the given row accessible if given cell accessible is null.
*
* @param aRow [in] row accessible
* @param aCell [in, optional] cell accessible
*/
already_AddRefed<nsIAccessible> GetNextCellInRow(nsIAccessible *aRow,
nsIAccessible *aCell = nsnull);
nsAccessible *GetCellInRowAt(nsAccessible *aRow, PRInt32 aColumn);
/**
* Set aria-selected attribute value on DOM node of the given accessible.
@ -111,7 +92,7 @@ protected:
* @param aNotify [in, optional] specifies if DOM should be notified
* about attribute change (used internally).
*/
nsresult SetARIASelected(nsIAccessible *aAccessible, PRBool aIsSelected,
nsresult SetARIASelected(nsAccessible *aAccessible, PRBool aIsSelected,
PRBool aNotify = PR_TRUE);
/**

View File

@ -0,0 +1,93 @@
/* ***** 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) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Alexander Surkov <surkov.alexander@gmail.com> (original author)
*
* Alternatively, the contents of this file may be used under the terms of
* either 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 "nsAccIterator.h"
////////////////////////////////////////////////////////////////////////////////
// nsAccIterator
nsAccIterator::nsAccIterator(nsAccessible *aAccessible,
AccIteratorFilterFuncPtr aFilterFunc,
IterationType aIterationType) :
mFilterFunc(aFilterFunc), mIsDeep(aIterationType != eFlatNav)
{
mState = new IteratorState(aAccessible);
}
nsAccIterator::~nsAccIterator()
{
while (mState) {
IteratorState *tmp = mState;
mState = tmp->mParentState;
delete tmp;
}
}
nsAccessible*
nsAccIterator::GetNext()
{
while (mState) {
nsAccessible *child = mState->mParent->GetChildAt(mState->mIndex++);
if (!child) {
IteratorState *tmp = mState;
mState = mState->mParentState;
delete tmp;
continue;
}
PRBool isComplying = mFilterFunc(child);
if (isComplying)
return child;
if (mIsDeep) {
IteratorState *childState = new IteratorState(child, mState);
mState = childState;
}
}
return nsnull;
}
////////////////////////////////////////////////////////////////////////////////
// nsAccIterator::IteratorState
nsAccIterator::IteratorState::IteratorState(nsAccessible *aParent,
IteratorState *mParentState) :
mParent(aParent), mIndex(0), mParentState(mParentState)
{
}

View File

@ -0,0 +1,124 @@
/* ***** 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) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Alexander Surkov <surkov.alexander@gmail.com> (original author)
*
* Alternatively, the contents of this file may be used under the terms of
* either 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 nsAccIterator_h_
#define nsAccIterator_h_
#include "nsAccessible.h"
#include "nsAccUtils.h"
/**
* Return true if the traversed accessible complies with filter.
*/
typedef PRBool (*AccIteratorFilterFuncPtr) (nsAccessible *);
/**
* Allows to iterate through accessible children or subtree complying with
* filter function.
*/
class nsAccIterator
{
public:
/**
* Used to define iteration type.
*/
enum IterationType {
/**
* Navigation happens through direct children.
*/
eFlatNav,
/**
* Navigation through subtree excluding iterator root; if the accessible
* complies with filter, iterator ignores its children.
*/
eTreeNav
};
nsAccIterator(nsAccessible *aRoot, AccIteratorFilterFuncPtr aFilterFunc,
IterationType aIterationType = eFlatNav);
~nsAccIterator();
/**
* Return next accessible complying with filter function. Return the first
* accessible for the first time.
*/
nsAccessible *GetNext();
/**
* Predefined filters.
*/
static PRBool GetSelected(nsAccessible *aAccessible)
{
return nsAccUtils::State(aAccessible) & nsIAccessibleStates::STATE_SELECTED;
}
static PRBool GetSelectable(nsAccessible *aAccessible)
{
return nsAccUtils::State(aAccessible) & nsIAccessibleStates::STATE_SELECTABLE;
}
static PRBool GetRow(nsAccessible *aAccessible)
{
return nsAccUtils::Role(aAccessible) == nsIAccessibleRole::ROLE_ROW;
}
static PRBool GetCell(nsAccessible *aAccessible)
{
PRUint32 role = nsAccUtils::Role(aAccessible);
return role == nsIAccessibleRole::ROLE_GRID_CELL ||
role == nsIAccessibleRole::ROLE_ROWHEADER ||
role == nsIAccessibleRole::ROLE_COLUMNHEADER;
}
private:
nsAccIterator();
nsAccIterator(const nsAccIterator&);
nsAccIterator& operator =(const nsAccIterator&);
struct IteratorState
{
IteratorState(nsAccessible *aParent, IteratorState *mParentState = nsnull);
nsAccessible *mParent;
PRInt32 mIndex;
IteratorState *mParentState;
};
AccIteratorFilterFuncPtr mFilterFunc;
PRBool mIsDeep;
IteratorState *mState;
};
#endif

View File

@ -41,6 +41,7 @@
#include "nsIXBLAccessible.h"
#include "nsAccIterator.h"
#include "nsAccUtils.h"
#include "nsARIAMap.h"
#include "nsDocAccessible.h"
@ -2466,39 +2467,6 @@ nsAccessible::DispatchClickEvent(nsIContent *aContent, PRUint32 aActionIndex)
nsCoreUtils::DispatchMouseEvent(NS_MOUSE_BUTTON_UP, presShell, aContent);
}
already_AddRefed<nsIAccessible>
nsAccessible::GetNextWithState(nsIAccessible *aStart, PRUint32 matchState)
{
// Return the next descendant that matches one of the states in matchState
// Uses depth first search
NS_ASSERTION(matchState, "GetNextWithState() not called with a state to match");
NS_ASSERTION(aStart, "GetNextWithState() not called with an accessible to start with");
nsCOMPtr<nsIAccessible> look, current = aStart;
PRUint32 state = 0;
while (0 == (state & matchState)) {
current->GetFirstChild(getter_AddRefs(look));
while (!look) {
if (current == this) {
return nsnull; // At top of subtree
}
current->GetNextSibling(getter_AddRefs(look));
if (!look) {
current->GetParent(getter_AddRefs(look));
current = look;
look = nsnull;
continue;
}
}
current.swap(look);
state = nsAccUtils::State(current);
}
nsIAccessible *returnAccessible = nsnull;
current.swap(returnAccessible);
return returnAccessible;
}
// nsIAccessibleSelectable
NS_IMETHODIMP nsAccessible::GetSelectedChildren(nsIArray **aSelectedAccessibles)
{
@ -2508,10 +2476,10 @@ NS_IMETHODIMP nsAccessible::GetSelectedChildren(nsIArray **aSelectedAccessibles)
do_CreateInstance(NS_ARRAY_CONTRACTID);
NS_ENSURE_STATE(selectedAccessibles);
nsCOMPtr<nsIAccessible> selected = this;
while ((selected = GetNextWithState(selected, nsIAccessibleStates::STATE_SELECTED)) != nsnull) {
nsAccIterator iter(this, nsAccIterator::GetSelected, nsAccIterator::eTreeNav);
nsIAccessible *selected = nsnull;
while ((selected = iter.GetNext()))
selectedAccessibles->AppendElement(selected, PR_FALSE);
}
PRUint32 length = 0;
selectedAccessibles->GetLength(&length);
@ -2526,16 +2494,22 @@ NS_IMETHODIMP nsAccessible::GetSelectedChildren(nsIArray **aSelectedAccessibles)
// return the nth selected descendant nsIAccessible object
NS_IMETHODIMP nsAccessible::RefSelection(PRInt32 aIndex, nsIAccessible **aSelected)
{
NS_ENSURE_ARG_POINTER(aSelected);
*aSelected = nsnull;
if (aIndex < 0) {
return NS_ERROR_FAILURE;
return NS_ERROR_INVALID_ARG;
}
nsCOMPtr<nsIAccessible> selected = this;
nsAccIterator iter(this, nsAccIterator::GetSelected, nsAccIterator::eTreeNav);
nsAccessible *selected = nsnull;
PRInt32 count = 0;
while (count ++ <= aIndex) {
selected = GetNextWithState(selected, nsIAccessibleStates::STATE_SELECTED);
selected = iter.GetNext();
if (!selected) {
return NS_ERROR_FAILURE; // aIndex out of range
// The index is out of range.
return NS_ERROR_INVALID_ARG;
}
}
NS_IF_ADDREF(*aSelected = selected);
@ -2544,11 +2518,13 @@ NS_IMETHODIMP nsAccessible::RefSelection(PRInt32 aIndex, nsIAccessible **aSelect
NS_IMETHODIMP nsAccessible::GetSelectionCount(PRInt32 *aSelectionCount)
{
NS_ENSURE_ARG_POINTER(aSelectionCount);
*aSelectionCount = 0;
nsCOMPtr<nsIAccessible> selected = this;
while ((selected = GetNextWithState(selected, nsIAccessibleStates::STATE_SELECTED)) != nsnull) {
++ *aSelectionCount;
}
nsAccIterator iter(this, nsAccIterator::GetSelected, nsAccIterator::eTreeNav);
nsAccessible *selected = nsnull;
while ((selected = iter.GetNext()))
++(*aSelectionCount);
return NS_OK;
}
@ -2604,21 +2580,24 @@ NS_IMETHODIMP nsAccessible::IsChildSelected(PRInt32 aIndex, PRBool *aIsSelected)
return NS_OK;
}
NS_IMETHODIMP nsAccessible::ClearSelection()
NS_IMETHODIMP
nsAccessible::ClearSelection()
{
nsCOMPtr<nsIAccessible> selected = this;
while ((selected = GetNextWithState(selected, nsIAccessibleStates::STATE_SELECTED)) != nsnull) {
nsAccIterator iter(this, nsAccIterator::GetSelected, nsAccIterator::eTreeNav);
nsAccessible *selected = nsnull;
while ((selected = iter.GetNext()))
selected->SetSelected(PR_FALSE);
}
return NS_OK;
}
NS_IMETHODIMP nsAccessible::SelectAllSelection(PRBool *_retval)
{
nsCOMPtr<nsIAccessible> selectable = this;
while ((selectable = GetNextWithState(selectable, nsIAccessibleStates::STATE_SELECTED)) != nsnull) {
nsAccIterator iter(this, nsAccIterator::GetSelectable, nsAccIterator::eTreeNav);
nsAccessible *selectable = nsnull;
while((selectable = iter.GetNext()))
selectable->SetSelected(PR_TRUE);
}
return NS_OK;
}

View File

@ -325,8 +325,6 @@ protected:
// helper method to verify frames
static nsresult GetFullKeyName(const nsAString& aModifierName, const nsAString& aKeyName, nsAString& aStringOut);
static nsresult GetTranslatedString(const nsAString& aKey, nsAString& aStringOut);
already_AddRefed<nsIAccessible> GetNextWithState(nsIAccessible *aStart, PRUint32 matchState);
/**
* Return an accessible for the given DOM node, or if that node isn't

View File

@ -68,6 +68,7 @@ _TEST_FILES =\
nsIAccessible_selects.js \
relations.js \
role.js \
selectable.js \
states.js \
table.js \
value.js \

View File

@ -0,0 +1,36 @@
/**
* Test selection getter methods of nsIAccessibleSelectable.
*
* @param aIdentifier [in] selectable container accessible
* @param aSelectedChildren [in] array of selected children
*/
function testSelectableSelection(aIdentifier, aSelectedChildren)
{
var acc = getAccessible(aIdentifier, [nsIAccessibleSelectable]);
if (!acc)
return;
var len = aSelectedChildren.length;
// getSelectedChildren
var selectedChildren = acc.GetSelectedChildren();
is(selectedChildren ? selectedChildren.length : 0, aSelectedChildren.length,
"getSelectedChildren: wrong selected children count for " + prettyName(aIdentifier));
for (var idx = 0; idx < len; idx++) {
var expectedAcc = getAccessible(aSelectedChildren[idx]);
is(selectedChildren.queryElementAt(idx, nsIAccessible), expectedAcc,
"getSelectedChildren: wrong selected child at index " + idx + " for " + prettyName(aIdentifier));
}
// selectionCount
is(acc.selectionCount, aSelectedChildren.length,
"selectionCount: wrong selected children count for " + prettyName(aIdentifier));
// refSelection
for (var idx = 0; idx < len; idx++) {
var expectedAcc = getAccessible(aSelectedChildren[idx]);
is(acc.refSelection(idx), expectedAcc,
"refSelection: wrong selected child at index " + idx + " for " + prettyName(aIdentifier));
}
}

View File

@ -17,8 +17,23 @@
src="chrome://mochikit/content/a11y/accessible/common.js"></script>
<script type="application/javascript"
src="chrome://mochikit/content/a11y/accessible/role.js"></script>
<script type="application/javascript"
src="chrome://mochikit/content/a11y/accessible/selectable.js"></script>
<script type="application/javascript">
function testSelectable(aID, aSelectableChildren)
{
var acc = getAccessible(aID, [nsIAccessibleSelectable]);
testSelectableSelection(acc, []);
acc.selectAllSelection();
testSelectableSelection(acc, aSelectableChildren);
acc.clearSelection();
testSelectableSelection(acc, []);
}
function doTest()
{
var id = "list1";
@ -45,6 +60,11 @@
ok(isAccessible(id, [nsIAccessibleSelectable]),
"No selectable accessible for " + id);
// Test selection methods for selectable children in subtree.
testSelectable("grid2",
["grid2_colhead1", "grid2_colhead2", "grid2_colhead3",
"grid2_rowhead", "grid2_cell1", "grid2_cell2"]);
SimpleTest.finish();
}
@ -61,6 +81,11 @@
title="ARIA single selectable widget should implement nsIAccessibleSelectable">
Mozilla Bug 530014
</a><br>
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=566551"
title="ARIA grid and accessible selectable methods shouldn't use GetNextSibling">
Mozilla Bug 566551
</a><br>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test">
@ -116,5 +141,29 @@
<span role="gridcell">cell</span>
</div>
</div>
<table tabindex="0" border="2" cellspacing="0" id="grid2" role="grid"
aria-multiselectable="true">
<thead>
<tr>
<th tabindex="-1" role="columnheader" id="grid2_colhead1"
style="width:6em">Entry #</th>
<th tabindex="-1" role="columnheader" id="grid2_colhead2"
style="width:10em">Date</th>
<th tabindex="-1" role="columnheader" id="grid2_colhead3"
style="width:20em">Expense</th>
</tr>
</thead>
<tbody>
<tr>
<td tabindex="-1" role="rowheader" id="grid2_rowhead"
aria-readonly="true">1</td>
<td tabindex="-1" role="gridcell" id="grid2_cell1"
aria-selected="false">03/14/05</td>
<td tabindex="-1" role="gridcell" id="grid2_cell2"
aria-selected="false">Conference Fee</td>
</tr>
</tobdy>
</table>
</body>
</html>