зеркало из https://github.com/mozilla/pjs.git
Bug 417929 - nsIAccessiblTable selectRows does not unselect previously selected rows, r=marcoz, davidb, ginn, smaug, sr=roc
This commit is contained in:
Родитель
5ef33c9cfd
Коммит
e5ea189576
|
@ -58,6 +58,7 @@
|
|||
#include "nsIServiceManager.h"
|
||||
#include "nsITableLayout.h"
|
||||
#include "nsITableCellLayout.h"
|
||||
#include "nsFrameSelection.h"
|
||||
#include "nsLayoutErrors.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -1079,117 +1080,142 @@ nsHTMLTableAccessible::IsValidRow(PRInt32 aRow)
|
|||
NS_IMETHODIMP
|
||||
nsHTMLTableAccessible::SelectRow(PRInt32 aRow)
|
||||
{
|
||||
return SelectRowOrColumn(aRow, nsISelectionPrivate::TABLESELECTION_ROW,
|
||||
PR_TRUE);
|
||||
if (IsDefunct())
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
nsresult rv =
|
||||
RemoveRowsOrColumnsFromSelection(aRow,
|
||||
nsISelectionPrivate::TABLESELECTION_ROW,
|
||||
PR_TRUE);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return AddRowOrColumnToSelection(aRow,
|
||||
nsISelectionPrivate::TABLESELECTION_ROW);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsHTMLTableAccessible::SelectColumn(PRInt32 aColumn)
|
||||
{
|
||||
return SelectRowOrColumn(aColumn, nsISelectionPrivate::TABLESELECTION_COLUMN,
|
||||
PR_TRUE);
|
||||
if (IsDefunct())
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
nsresult rv =
|
||||
RemoveRowsOrColumnsFromSelection(aColumn,
|
||||
nsISelectionPrivate::TABLESELECTION_COLUMN,
|
||||
PR_TRUE);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return AddRowOrColumnToSelection(aColumn,
|
||||
nsISelectionPrivate::TABLESELECTION_COLUMN);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsHTMLTableAccessible::UnselectRow(PRInt32 aRow)
|
||||
{
|
||||
return SelectRowOrColumn(aRow, nsISelectionPrivate::TABLESELECTION_ROW,
|
||||
PR_FALSE);
|
||||
if (IsDefunct())
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
return
|
||||
RemoveRowsOrColumnsFromSelection(aRow,
|
||||
nsISelectionPrivate::TABLESELECTION_ROW,
|
||||
PR_FALSE);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsHTMLTableAccessible::UnselectColumn(PRInt32 aColumn)
|
||||
{
|
||||
return SelectRowOrColumn(aColumn, nsISelectionPrivate::TABLESELECTION_COLUMN,
|
||||
PR_FALSE);
|
||||
if (IsDefunct())
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
return
|
||||
RemoveRowsOrColumnsFromSelection(aColumn,
|
||||
nsISelectionPrivate::TABLESELECTION_COLUMN,
|
||||
PR_FALSE);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHTMLTableAccessible::SelectRowOrColumn(PRInt32 aIndex, PRUint32 aTarget,
|
||||
PRBool aDoSelect)
|
||||
nsHTMLTableAccessible::AddRowOrColumnToSelection(PRInt32 aIndex,
|
||||
PRUint32 aTarget)
|
||||
{
|
||||
PRBool doSelectRow = (aTarget == nsISelectionPrivate::TABLESELECTION_ROW);
|
||||
|
||||
nsCOMPtr<nsIContent> content(do_QueryInterface(mDOMNode));
|
||||
if (!content)
|
||||
return NS_OK;
|
||||
|
||||
nsCOMPtr<nsIDocument> document = content->GetCurrentDoc();
|
||||
NS_ENSURE_STATE(document);
|
||||
|
||||
nsCOMPtr<nsISelectionController> selController(
|
||||
do_QueryInterface(document->GetPrimaryShell()));
|
||||
NS_ENSURE_STATE(selController);
|
||||
|
||||
nsCOMPtr<nsISelection> selection;
|
||||
selController->GetSelection(nsISelectionController::SELECTION_NORMAL,
|
||||
getter_AddRefs(selection));
|
||||
NS_ENSURE_STATE(selection);
|
||||
|
||||
PRInt32 count = 0;
|
||||
nsresult rv = doSelectRow ? GetColumns(&count) : GetRows(&count);
|
||||
nsITableLayout *tableLayout = nsnull;
|
||||
nsresult rv = GetTableLayout(&tableLayout);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
for (PRInt32 index = 0; index < count; index++) {
|
||||
nsCOMPtr<nsIDOMElement> cellElm;
|
||||
PRInt32 column = doSelectRow ? index : aIndex;
|
||||
PRInt32 row = doSelectRow ? aIndex : index;
|
||||
nsCOMPtr<nsIDOMElement> cellElm;
|
||||
PRInt32 startRowIdx, startColIdx, rowSpan, colSpan,
|
||||
actualRowSpan, actualColSpan;
|
||||
PRBool isSelected = PR_FALSE;
|
||||
|
||||
rv = GetCellAt(row, column, *getter_AddRefs(cellElm));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
PRInt32 count = 0;
|
||||
if (doSelectRow)
|
||||
rv = GetColumns(&count);
|
||||
else
|
||||
rv = GetRows(&count);
|
||||
|
||||
rv = SelectCell(selection, document, cellElm, aDoSelect);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIPresShell> presShell(GetPresShell());
|
||||
nsRefPtr<nsFrameSelection> tableSelection =
|
||||
const_cast<nsFrameSelection*>(presShell->ConstFrameSelection());
|
||||
|
||||
for (PRInt32 idx = 0; idx < count; idx++) {
|
||||
PRInt32 rowIdx = doSelectRow ? aIndex : idx;
|
||||
PRInt32 colIdx = doSelectRow ? idx : aIndex;
|
||||
rv = tableLayout->GetCellDataAt(rowIdx, colIdx,
|
||||
*getter_AddRefs(cellElm),
|
||||
startRowIdx, startColIdx,
|
||||
rowSpan, colSpan,
|
||||
actualRowSpan, actualColSpan,
|
||||
isSelected);
|
||||
|
||||
if (NS_SUCCEEDED(rv) && !isSelected) {
|
||||
nsCOMPtr<nsIContent> cellContent(do_QueryInterface(cellElm));
|
||||
rv = tableSelection->SelectCellElement(cellContent);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsHTMLTableAccessible::SelectCell(nsISelection *aSelection,
|
||||
nsIDocument *aDocument,
|
||||
nsIDOMElement *aCellElement,
|
||||
PRBool aDoSelect)
|
||||
nsHTMLTableAccessible::RemoveRowsOrColumnsFromSelection(PRInt32 aIndex,
|
||||
PRUint32 aTarget,
|
||||
PRBool aIsOuter)
|
||||
{
|
||||
if (aDoSelect) {
|
||||
nsCOMPtr<nsIDOMDocumentRange> documentRange(do_QueryInterface(aDocument));
|
||||
NS_ENSURE_STATE(documentRange);
|
||||
nsCOMPtr<nsIContent> content(do_QueryInterface(mDOMNode));
|
||||
|
||||
nsCOMPtr<nsIDOMRange> range;
|
||||
documentRange->CreateRange(getter_AddRefs(range));
|
||||
|
||||
nsCOMPtr<nsIDOMNode> cellNode(do_QueryInterface(aCellElement));
|
||||
NS_ENSURE_STATE(cellNode);
|
||||
|
||||
range->SelectNode(cellNode);
|
||||
return aSelection->AddRange(range);
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIContent> cell(do_QueryInterface(aCellElement));
|
||||
NS_ENSURE_STATE(cell);
|
||||
|
||||
nsCOMPtr<nsIContent> cellParent = cell->GetParent();
|
||||
NS_ENSURE_STATE(cellParent);
|
||||
|
||||
PRInt32 offset = cellParent->IndexOf(cell);
|
||||
NS_ENSURE_STATE(offset != -1);
|
||||
|
||||
nsCOMPtr<nsIDOMNode> parent(do_QueryInterface(cellParent));
|
||||
NS_ENSURE_STATE(parent);
|
||||
|
||||
nsCOMPtr<nsISelection2> selection2(do_QueryInterface(aSelection));
|
||||
NS_ENSURE_STATE(selection2);
|
||||
|
||||
nsCOMArray<nsIDOMRange> ranges;
|
||||
nsresult rv = selection2->GetRangesForIntervalCOMArray(parent, offset,
|
||||
parent, offset,
|
||||
PR_TRUE, &ranges);
|
||||
nsITableLayout *tableLayout = nsnull;
|
||||
nsresult rv = GetTableLayout(&tableLayout);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
for (PRInt32 i = 0; i < ranges.Count(); i ++)
|
||||
aSelection->RemoveRange(ranges[i]);
|
||||
nsCOMPtr<nsIPresShell> presShell(GetPresShell());
|
||||
nsRefPtr<nsFrameSelection> tableSelection =
|
||||
const_cast<nsFrameSelection*>(presShell->ConstFrameSelection());
|
||||
|
||||
return NS_OK;
|
||||
PRBool doUnselectRow = (aTarget == nsISelectionPrivate::TABLESELECTION_ROW);
|
||||
|
||||
PRInt32 count = 0;
|
||||
if (doUnselectRow)
|
||||
rv = GetColumns(&count);
|
||||
else
|
||||
rv = GetRows(&count);
|
||||
|
||||
PRInt32 startRowIdx = doUnselectRow ? aIndex : 0;
|
||||
PRInt32 endRowIdx = doUnselectRow ? aIndex : count - 1;
|
||||
PRInt32 startColIdx = doUnselectRow ? 0 : aIndex;
|
||||
PRInt32 endColIdx = doUnselectRow ? count - 1 : aIndex;
|
||||
|
||||
if (aIsOuter)
|
||||
return tableSelection->RestrictCellsToSelection(content,
|
||||
startRowIdx, startColIdx,
|
||||
endRowIdx, endColIdx);
|
||||
|
||||
return tableSelection->RemoveCellsFromSelection(content,
|
||||
startRowIdx, startColIdx,
|
||||
endRowIdx, endColIdx);
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
|
|
@ -187,26 +187,26 @@ public:
|
|||
protected:
|
||||
|
||||
/**
|
||||
* Selects or unselects row or column.
|
||||
* Add row or column to selection.
|
||||
*
|
||||
* @param aIndex - index of row or column to be selected
|
||||
* @param aTarget - indicates what should be selected, either row or column
|
||||
* @param aIndex [in] index of row or column to be selected
|
||||
* @param aTarget [in] indicates what should be selected, either row or column
|
||||
* (see nsISelectionPrivate)
|
||||
* @param aDoSelect - indicates whether row or column should selected or
|
||||
* unselected
|
||||
*/
|
||||
nsresult SelectRowOrColumn(PRInt32 aIndex, PRUint32 aTarget, PRBool aDoSelect);
|
||||
nsresult AddRowOrColumnToSelection(PRInt32 aIndex, PRUint32 aTarget);
|
||||
|
||||
/**
|
||||
* Selects or unselects the cell.
|
||||
* Removes rows or columns at the given index or outside it from selection.
|
||||
*
|
||||
* @param aSelection - the selection of document
|
||||
* @param aDocument - the document that contains the cell
|
||||
* @param aCellElement - the cell of table
|
||||
* @param aDoSelect - indicates whether cell should be selected or unselected
|
||||
* @param aIndex [in] row or column index
|
||||
* @param aTarget [in] indicates whether row or column should unselected
|
||||
* @param aIsOuter [in] indicates whether all rows or column excepting
|
||||
* the given one should be unselected or the given one
|
||||
* should be unselected only
|
||||
*/
|
||||
nsresult SelectCell(nsISelection *aSelection, nsIDocument *aDocument,
|
||||
nsIDOMElement *aCellElement, PRBool aDoSelect);
|
||||
nsresult RemoveRowsOrColumnsFromSelection(PRInt32 aIndex,
|
||||
PRUint32 aTarget,
|
||||
PRBool aIsOuter);
|
||||
|
||||
virtual void CacheChildren();
|
||||
nsresult GetTableNode(nsIDOMNode **_retval);
|
||||
|
|
|
@ -101,12 +101,22 @@ function testTableIndexes(aIdentifier, aIdxes)
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constants used to describe cells states array. Values 0 and 1 are reserved
|
||||
* for boolean flag indicating the cell is origin and it is whether unselected
|
||||
* or selected.
|
||||
*/
|
||||
const kRowSpanned = 2; // Indicates the cell is not origin and row spanned
|
||||
const kColSpanned = 4; // Indicates the cell is not origin and column spanned
|
||||
const kSpanned = kRowSpanned | kColSpanned;
|
||||
|
||||
/**
|
||||
* Test table getters selection methods.
|
||||
*
|
||||
* @param aIdentifier [in] table accessible identifier
|
||||
* @param aCellsArray [in] two dimensional array (row X columns) of selected
|
||||
* cells states.
|
||||
* @param aCellsArray [in] two dimensional array (row X columns) of cells
|
||||
* states (either boolean (selected/unselected) if cell is
|
||||
* origin, or one of constants defined above).
|
||||
* @param aMsg [in] text appended before every message
|
||||
*/
|
||||
function testTableSelection(aIdentifier, aCellsArray, aMsg)
|
||||
|
@ -204,9 +214,9 @@ function testTableSelection(aIdentifier, aCellsArray, aMsg)
|
|||
// isCellSelected test
|
||||
for (var rowIdx = 0; rowIdx < rowsCount; rowIdx++) {
|
||||
for (var colIdx = 0; colIdx < colsCount; colIdx++) {
|
||||
if (aCellsArray[rowIdx][colIdx] == undefined)
|
||||
if (!isOrigCell(aCellsArray[rowIdx][colIdx]))
|
||||
continue;
|
||||
|
||||
|
||||
is(acc.isCellSelected(rowIdx, colIdx), aCellsArray[rowIdx][colIdx],
|
||||
msg + "Wrong selection state of cell at " + rowIdx + " row and " +
|
||||
colIdx + " column for " + prettyName(aIdentifier));
|
||||
|
@ -237,7 +247,7 @@ function testTableSelection(aIdentifier, aCellsArray, aMsg)
|
|||
// selected states tests
|
||||
for (var rowIdx = 0; rowIdx < rowsCount; rowIdx++) {
|
||||
for (var colIdx = 0; colIdx < colsCount; colIdx++) {
|
||||
if (aCellsArray[rowIdx][colIdx] == undefined)
|
||||
if (!isOrigCell(aCellsArray[rowIdx][colIdx]))
|
||||
continue;
|
||||
|
||||
var cell = acc.cellRefAt(rowIdx, colIdx);
|
||||
|
@ -261,8 +271,11 @@ function testUnselectTableColumn(aIdentifier, aColIdx, aCellsArray)
|
|||
|
||||
var rowsCount = aCellsArray.length;
|
||||
for (var rowIdx = 0; rowIdx < rowsCount; rowIdx++) {
|
||||
if (aCellsArray[rowIdx][aColIdx] != undefined)
|
||||
aCellsArray[rowIdx][aColIdx] = false;
|
||||
var cellState = aCellsArray[rowIdx][aColIdx];
|
||||
// Unselect origin cell.
|
||||
var [origRowIdx, origColIdx] =
|
||||
getOrigRowAndColumn(aCellsArray, rowIdx, aColIdx);
|
||||
aCellsArray[origRowIdx][origColIdx] = false;
|
||||
}
|
||||
|
||||
acc.unselectColumn(aColIdx);
|
||||
|
@ -284,8 +297,42 @@ function testSelectTableColumn(aIdentifier, aColIdx, aCellsArray)
|
|||
|
||||
for (var rowIdx = 0; rowIdx < rowsCount; rowIdx++) {
|
||||
for (var colIdx = 0; colIdx < colsCount; colIdx++) {
|
||||
if (aCellsArray[rowIdx][colIdx] != undefined)
|
||||
aCellsArray[rowIdx][colIdx] = (colIdx == aColIdx);
|
||||
var cellState = aCellsArray[rowIdx][colIdx];
|
||||
|
||||
if (colIdx == aColIdx) { // select target column
|
||||
if (isOrigCell(cellState)) {
|
||||
// Select the cell if it is origin.
|
||||
aCellsArray[rowIdx][colIdx] = true;
|
||||
|
||||
} else {
|
||||
// If the cell is spanned then search origin cell and select it.
|
||||
var [origRowIdx, origColIdx] = getOrigRowAndColumn(aCellsArray,
|
||||
rowIdx, colIdx);
|
||||
aCellsArray[origRowIdx][origColIdx] = true;
|
||||
}
|
||||
|
||||
} else if (isOrigCell(cellState)) { // unselect other columns
|
||||
if (colIdx > aColIdx) {
|
||||
// Unselect the cell if traversed column index is greater than column
|
||||
// index of target cell.
|
||||
aCellsArray[rowIdx][colIdx] = false;
|
||||
|
||||
} else if (!isColSpannedCell(aCellsArray[rowIdx][aColIdx])) {
|
||||
// Unselect the cell if the target cell is not row spanned.
|
||||
aCellsArray[rowIdx][colIdx] = false;
|
||||
|
||||
} else {
|
||||
// Unselect the cell if it is not spanned to the target cell.
|
||||
for (var spannedColIdx = colIdx + 1; spannedColIdx < aColIdx;
|
||||
spannedColIdx++) {
|
||||
var spannedCellState = aCellsArray[rowIdx][spannedColIdx];
|
||||
if (!isRowSpannedCell(spannedCellState)) {
|
||||
aCellsArray[rowIdx][colIdx] = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -305,8 +352,10 @@ function testUnselectTableRow(aIdentifier, aRowIdx, aCellsArray)
|
|||
|
||||
var colsCount = aCellsArray[0].length;
|
||||
for (var colIdx = 0; colIdx < colsCount; colIdx++) {
|
||||
if (aCellsArray[aRowIdx][colIdx] != undefined)
|
||||
aCellsArray[aRowIdx][colIdx] = false;
|
||||
// Unselect origin cell.
|
||||
var [origRowIdx, origColIdx] = getOrigRowAndColumn(aCellsArray,
|
||||
aRowIdx, colIdx);
|
||||
aCellsArray[origRowIdx][origColIdx] = false;
|
||||
}
|
||||
|
||||
acc.unselectRow(aRowIdx);
|
||||
|
@ -328,8 +377,43 @@ function testSelectTableRow(aIdentifier, aRowIdx, aCellsArray)
|
|||
|
||||
for (var rowIdx = 0; rowIdx < rowsCount; rowIdx++) {
|
||||
for (var colIdx = 0; colIdx < colsCount; colIdx++) {
|
||||
if (aCellsArray[rowIdx][colIdx] != undefined)
|
||||
aCellsArray[rowIdx][colIdx] = (rowIdx == aRowIdx);
|
||||
var cellState = aCellsArray[rowIdx][colIdx];
|
||||
|
||||
if (rowIdx == aRowIdx) { // select the given row
|
||||
if (isOrigCell(cellState)) {
|
||||
// Select the cell if it is origin.
|
||||
aCellsArray[rowIdx][colIdx] = true;
|
||||
|
||||
} else {
|
||||
// If the cell is spanned then search origin cell and select it.
|
||||
var [origRowIdx, origColIdx] = getOrigRowAndColumn(aCellsArray,
|
||||
rowIdx, colIdx);
|
||||
|
||||
aCellsArray[origRowIdx][origColIdx] = true;
|
||||
}
|
||||
|
||||
} else if (isOrigCell(cellState)) { // unselect other rows
|
||||
if (rowIdx > aRowIdx) {
|
||||
// Unselect the cell if traversed row index is greater than row
|
||||
// index of target cell.
|
||||
aCellsArray[rowIdx][colIdx] = false;
|
||||
|
||||
} else if (!isRowSpannedCell(aCellsArray[aRowIdx][colIdx])) {
|
||||
// Unselect the cell if the target cell is not row spanned.
|
||||
aCellsArray[rowIdx][colIdx] = false;
|
||||
|
||||
} else {
|
||||
// Unselect the cell if it is not spanned to the target cell.
|
||||
for (var spannedRowIdx = rowIdx + 1; spannedRowIdx < aRowIdx;
|
||||
spannedRowIdx++) {
|
||||
var spannedCellState = aCellsArray[spannedRowIdx][colIdx];
|
||||
if (!isRowSpannedCell(spannedCellState)) {
|
||||
aCellsArray[rowIdx][colIdx] = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -337,3 +421,52 @@ function testSelectTableRow(aIdentifier, aRowIdx, aCellsArray)
|
|||
testTableSelection(aIdentifier, aCellsArray,
|
||||
"Select " + aRowIdx + " row: ");
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// private implementation
|
||||
|
||||
/**
|
||||
* Return row and column of orig cell for the given spanned cell.
|
||||
*/
|
||||
function getOrigRowAndColumn(aCellsArray, aRowIdx, aColIdx)
|
||||
{
|
||||
var cellState = aCellsArray[aRowIdx][aColIdx];
|
||||
|
||||
var origRowIdx = aRowIdx, origColIdx = aColIdx;
|
||||
if (isRowSpannedCell(cellState)) {
|
||||
for (var prevRowIdx = aRowIdx - 1; prevRowIdx >= 0; prevRowIdx--) {
|
||||
var prevCellState = aCellsArray[prevRowIdx][aColIdx];
|
||||
if (!isRowSpannedCell(prevCellState)) {
|
||||
origRowIdx = prevRowIdx;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isColSpannedCell(cellState)) {
|
||||
for (var prevColIdx = aColIdx - 1; prevColIdx >= 0; prevColIdx--) {
|
||||
var prevCellState = aCellsArray[aRowIdx][prevColIdx];
|
||||
if (!isColSpannedCell(prevCellState)) {
|
||||
origColIdx = prevColIdx;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [origRowIdx, origColIdx];
|
||||
}
|
||||
|
||||
function isOrigCell(aCellState)
|
||||
{
|
||||
return !(aCellState & (kRowSpanned | kColSpanned));
|
||||
}
|
||||
|
||||
function isRowSpannedCell(aCellState)
|
||||
{
|
||||
return aCellState & kRowSpanned;
|
||||
}
|
||||
|
||||
function isColSpannedCell(aCellState)
|
||||
{
|
||||
return aCellState & kColSpanned;
|
||||
}
|
||||
|
|
|
@ -24,59 +24,35 @@
|
|||
{
|
||||
var cellsArray =
|
||||
[
|
||||
[false, false, false, undefined, false, false, false, false],
|
||||
[false, false, false, false, false, false, false, undefined],
|
||||
[false, false, undefined, false, false, false, false, undefined],
|
||||
[false, false, undefined, false, false, undefined, false, undefined]
|
||||
[false, false, false, kColSpanned, false, false, false, false],
|
||||
[false, false, false, false, false, false, false, kRowSpanned],
|
||||
[false, false, kColSpanned, false, false, false, false, kRowSpanned],
|
||||
[false, kRowSpanned, kSpanned, false, false, kRowSpanned, false, kRowSpanned]
|
||||
];
|
||||
|
||||
testTableSelection("table", cellsArray);
|
||||
|
||||
testSelectTableRow("table", 0, cellsArray);
|
||||
var rowsCount = 4;
|
||||
for (var rowIdx = 0; rowIdx < rowsCount; rowIdx++)
|
||||
testSelectTableRow("table", rowIdx, cellsArray);
|
||||
|
||||
for (var rowIdx = 0; rowIdx < rowsCount; rowIdx++) {
|
||||
testSelectTableRow("table", rowIdx, cellsArray);
|
||||
testUnselectTableRow("table", rowIdx, cellsArray);
|
||||
}
|
||||
|
||||
var columsCount = 8;
|
||||
for (var colIdx = 0; colIdx < columsCount; colIdx++)
|
||||
testSelectTableColumn("table", colIdx, cellsArray);
|
||||
|
||||
for (var colIdx = 0; colIdx < columsCount; colIdx++) {
|
||||
testSelectTableColumn("table", colIdx, cellsArray);
|
||||
testUnselectTableColumn("table", colIdx, cellsArray);
|
||||
}
|
||||
|
||||
var accTable = getAccessible("table", [nsIAccessibleTable]);
|
||||
for (var i = 0; i < 4; i++) {
|
||||
accTable.selectRow(i);
|
||||
for (var j = 0; j < 4; j++)
|
||||
if (i == j)
|
||||
ok(accTable.isRowSelected(i),"row not selected");
|
||||
else
|
||||
todo(!accTable.isRowSelected(i),"row selected");
|
||||
}
|
||||
todo_is(accTable.selectedRowsCount, 1, "only one row should be selected");
|
||||
todo_is(accTable.getSelectedRows({}).length, 1,
|
||||
"only one row should be selected");
|
||||
|
||||
for (var i = 0; i < 4; i++) {
|
||||
accTable.unselectRow(i);
|
||||
ok(!accTable.isRowSelected(i), "row still selected");
|
||||
}
|
||||
todo_is(accTable.getSelectedCells({}).length, 0, "no cell selected");
|
||||
todo_is(accTable.selectedCellsCount, 0, "no cell selected");
|
||||
var s = window.getSelection();
|
||||
if (s.rangeCount > 0)
|
||||
s.removeAllRanges();
|
||||
|
||||
is(accTable.getSelectedCells({}).length, 0, "no cell selected");
|
||||
is(accTable.selectedCellsCount, 0, "no cell selected");
|
||||
for (var i = 0; i < 8; i++) {
|
||||
accTable.selectColumn(i);
|
||||
for (var j = 0; j < 8; j++)
|
||||
if (i ==j)
|
||||
ok(accTable.isColumnSelected(i),"column not selected");
|
||||
else
|
||||
todo(!accTable.isColumnSelected(i),"column is selected");
|
||||
}
|
||||
todo_is(accTable.selectedColumnsCount, 1,
|
||||
"only one column should be selected");
|
||||
todo_is(accTable.getSelectedColumns({}).length, 1,
|
||||
"only one column should be selected");
|
||||
for (var i = 0; i < 8; i++) {
|
||||
accTable.unselectColumn(i);
|
||||
ok(!accTable.isColumnSelected(i),"column still selected");
|
||||
}
|
||||
is(accTable.selectedColumnsCount, 0, "no column should be selected");
|
||||
ok(!accTable.isProbablyForLayout(), "table is not for layout");
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
|
@ -96,6 +72,11 @@
|
|||
title="nsHTMLTableAccessible::GetSelectedCells contains index duplicates for spanned rows or columns">
|
||||
Mozilla Bug 501635
|
||||
</a>
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=417929"
|
||||
title="nsIAccessiblTable selectRows does not unselect previously selected rows">
|
||||
Mozilla Bug 417929
|
||||
</a>
|
||||
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
|
|
|
@ -49,10 +49,10 @@
|
|||
#include "nsIRange.h"
|
||||
|
||||
// IID for the nsFrameSelection interface
|
||||
// 0ea74459-e3f9-48b0-8aa4-5dfef53bf1f7
|
||||
// 3c6ae2d0-4cf1-44a1-9e9d-2411867f19c6
|
||||
#define NS_FRAME_SELECTION_IID \
|
||||
{ 0xea74459, 0xe3f9, 0x48b0, \
|
||||
{ 0x8a, 0xa4, 0x5d, 0xfe, 0xf5, 0x3b, 0xf1, 0xf7 } }
|
||||
{ 0x3c6ae2d0, 0x4cf1, 0x44a1, \
|
||||
{ 0x9e, 0x9d, 0x24, 0x11, 0x86, 0x7f, 0x19, 0xc6 } }
|
||||
|
||||
#ifdef IBMBIDI // Constant for Set/Get CaretBidiLevel
|
||||
#define BIDI_LEVEL_UNDEFINED 0x80
|
||||
|
@ -292,6 +292,58 @@ public:
|
|||
PRInt32 aTarget,
|
||||
nsMouseEvent *aMouseEvent);
|
||||
|
||||
/**
|
||||
* Add cell to the selection.
|
||||
*
|
||||
* @param aCell [in] HTML td element.
|
||||
*/
|
||||
virtual nsresult SelectCellElement(nsIContent *aCell);
|
||||
|
||||
/**
|
||||
* Add cells to the selection inside of the given cells range.
|
||||
*
|
||||
* @param aTable [in] HTML table element
|
||||
* @param aStartRowIndex [in] row index where the cells range starts
|
||||
* @param aStartColumnIndex [in] column index where the cells range starts
|
||||
* @param aEndRowIndex [in] row index where the cells range ends
|
||||
* @param aEndColumnIndex [in] column index where the cells range ends
|
||||
*/
|
||||
virtual nsresult AddCellsToSelection(nsIContent *aTable,
|
||||
PRInt32 aStartRowIndex,
|
||||
PRInt32 aStartColumnIndex,
|
||||
PRInt32 aEndRowIndex,
|
||||
PRInt32 aEndColumnIndex);
|
||||
|
||||
/**
|
||||
* Remove cells from selection inside of the given cell range.
|
||||
*
|
||||
* @param aTable [in] HTML table element
|
||||
* @param aStartRowIndex [in] row index where the cells range starts
|
||||
* @param aStartColumnIndex [in] column index where the cells range starts
|
||||
* @param aEndRowIndex [in] row index where the cells range ends
|
||||
* @param aEndColumnIndex [in] column index where the cells range ends
|
||||
*/
|
||||
virtual nsresult RemoveCellsFromSelection(nsIContent *aTable,
|
||||
PRInt32 aStartRowIndex,
|
||||
PRInt32 aStartColumnIndex,
|
||||
PRInt32 aEndRowIndex,
|
||||
PRInt32 aEndColumnIndex);
|
||||
|
||||
/**
|
||||
* Remove cells from selection outside of the given cell range.
|
||||
*
|
||||
* @param aTable [in] HTML table element
|
||||
* @param aStartRowIndex [in] row index where the cells range starts
|
||||
* @param aStartColumnIndex [in] column index where the cells range starts
|
||||
* @param aEndRowIndex [in] row index where the cells range ends
|
||||
* @param aEndColumnIndex [in] column index where the cells range ends
|
||||
*/
|
||||
virtual nsresult RestrictCellsToSelection(nsIContent *aTable,
|
||||
PRInt32 aStartRowIndex,
|
||||
PRInt32 aStartColumnIndex,
|
||||
PRInt32 aEndRowIndex,
|
||||
PRInt32 aEndColumnIndex);
|
||||
|
||||
/** StartAutoScrollTimer is responsible for scrolling views so that aPoint is always
|
||||
* visible, and for selecting any frame that contains aPoint. The timer will also reset
|
||||
* itself to fire again if we have not scrolled to the end of the document.
|
||||
|
@ -626,6 +678,11 @@ private:
|
|||
|
||||
nsresult SelectBlockOfCells(nsIContent *aStartNode, nsIContent *aEndNode);
|
||||
nsresult SelectRowOrColumn(nsIContent *aCellContent, PRUint32 aTarget);
|
||||
nsresult UnselectCells(nsIContent *aTable,
|
||||
PRInt32 aStartRowIndex, PRInt32 aStartColumnIndex,
|
||||
PRInt32 aEndRowIndex, PRInt32 aEndColumnIndex,
|
||||
PRBool aRemoveOutsideOfCellRange);
|
||||
|
||||
nsresult GetCellIndexes(nsIContent *aCell, PRInt32 &aRowIndex, PRInt32 &aColIndex);
|
||||
|
||||
// Get our first range, if its first selected node is a cell. If this does
|
||||
|
@ -641,7 +698,6 @@ private:
|
|||
nsIContent* IsInSameTable(nsIContent *aContent1, nsIContent *aContent2) const;
|
||||
// Might return null
|
||||
nsIContent* GetParentTable(nsIContent *aCellNode) const;
|
||||
nsresult SelectCellElement(nsIContent* aCellElement);
|
||||
nsresult CreateAndAddRange(nsINode *aParentNode, PRInt32 aOffset);
|
||||
nsresult ClearNormalSelection();
|
||||
|
||||
|
|
|
@ -2766,69 +2766,134 @@ nsFrameSelection::SelectBlockOfCells(nsIContent *aStartCell, nsIContent *aEndCel
|
|||
result = GetCellIndexes(aEndCell, endRowIndex, endColIndex);
|
||||
if(NS_FAILED(result)) return result;
|
||||
|
||||
// Check that |table| is a table.
|
||||
if (!GetTableLayout(table)) return NS_ERROR_FAILURE;
|
||||
|
||||
PRInt32 curRowIndex, curColIndex;
|
||||
|
||||
if (mDragSelectingCells)
|
||||
{
|
||||
// Drag selecting: remove selected cells outside of new block limits
|
||||
|
||||
PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
|
||||
if (!mDomSelections[index])
|
||||
return NS_ERROR_NULL_POINTER;
|
||||
|
||||
// Strong reference because we sometimes remove the range
|
||||
nsCOMPtr<nsIRange> range = GetFirstCellRange();
|
||||
nsIContent* cellNode = GetFirstSelectedContent(range);
|
||||
NS_PRECONDITION(!range || cellNode, "Must have cellNode if had a range");
|
||||
|
||||
PRInt32 minRowIndex = PR_MIN(startRowIndex, endRowIndex);
|
||||
PRInt32 maxRowIndex = PR_MAX(startRowIndex, endRowIndex);
|
||||
PRInt32 minColIndex = PR_MIN(startColIndex, endColIndex);
|
||||
PRInt32 maxColIndex = PR_MAX(startColIndex, endColIndex);
|
||||
|
||||
while (cellNode)
|
||||
{
|
||||
result = GetCellIndexes(cellNode, curRowIndex, curColIndex);
|
||||
if (NS_FAILED(result)) return result;
|
||||
|
||||
#ifdef DEBUG_TABLE_SELECTION
|
||||
if (!range)
|
||||
printf("SelectBlockOfCells -- range is null\n");
|
||||
#endif
|
||||
if (range &&
|
||||
(curRowIndex < minRowIndex || curRowIndex > maxRowIndex ||
|
||||
curColIndex < minColIndex || curColIndex > maxColIndex))
|
||||
{
|
||||
mDomSelections[index]->RemoveRange(range);
|
||||
// Since we've removed the range, decrement pointer to next range
|
||||
mSelectedCellIndex--;
|
||||
}
|
||||
range = GetNextCellRange();
|
||||
cellNode = GetFirstSelectedContent(range);
|
||||
NS_PRECONDITION(!range || cellNode, "Must have cellNode if had a range");
|
||||
}
|
||||
UnselectCells(table, startRowIndex, startColIndex, endRowIndex, endColIndex,
|
||||
PR_TRUE);
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDOMElement> cellElement;
|
||||
PRInt32 rowSpan, colSpan, actualRowSpan, actualColSpan;
|
||||
PRBool isSelected;
|
||||
|
||||
// Note that we select block in the direction of user's mouse dragging,
|
||||
// which means start cell may be after the end cell in either row or column
|
||||
PRInt32 row = startRowIndex;
|
||||
return AddCellsToSelection(table, startRowIndex, startColIndex,
|
||||
endRowIndex, endColIndex);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsFrameSelection::UnselectCells(nsIContent *aTableContent,
|
||||
PRInt32 aStartRowIndex,
|
||||
PRInt32 aStartColumnIndex,
|
||||
PRInt32 aEndRowIndex,
|
||||
PRInt32 aEndColumnIndex,
|
||||
PRBool aRemoveOutsideOfCellRange)
|
||||
{
|
||||
PRInt8 index =
|
||||
GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
|
||||
if (!mDomSelections[index])
|
||||
return NS_ERROR_NULL_POINTER;
|
||||
|
||||
nsITableLayout *tableLayout = GetTableLayout(aTableContent);
|
||||
if (!tableLayout)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
PRInt32 minRowIndex = PR_MIN(aStartRowIndex, aEndRowIndex);
|
||||
PRInt32 maxRowIndex = PR_MAX(aStartRowIndex, aEndRowIndex);
|
||||
PRInt32 minColIndex = PR_MIN(aStartColumnIndex, aEndColumnIndex);
|
||||
PRInt32 maxColIndex = PR_MAX(aStartColumnIndex, aEndColumnIndex);
|
||||
|
||||
// Strong reference because we sometimes remove the range
|
||||
nsCOMPtr<nsIRange> range = GetFirstCellRange();
|
||||
nsIContent* cellNode = GetFirstSelectedContent(range);
|
||||
NS_PRECONDITION(!range || cellNode, "Must have cellNode if had a range");
|
||||
|
||||
PRInt32 curRowIndex, curColIndex;
|
||||
while (cellNode)
|
||||
{
|
||||
nsresult result = GetCellIndexes(cellNode, curRowIndex, curColIndex);
|
||||
if (NS_FAILED(result))
|
||||
return result;
|
||||
|
||||
#ifdef DEBUG_TABLE_SELECTION
|
||||
if (!range)
|
||||
printf("RemoveCellsToSelection -- range is null\n");
|
||||
#endif
|
||||
|
||||
if (range) {
|
||||
if (aRemoveOutsideOfCellRange) {
|
||||
if (curRowIndex < minRowIndex || curRowIndex > maxRowIndex ||
|
||||
curColIndex < minColIndex || curColIndex > maxColIndex) {
|
||||
|
||||
mDomSelections[index]->RemoveRange(range);
|
||||
// Since we've removed the range, decrement pointer to next range
|
||||
mSelectedCellIndex--;
|
||||
}
|
||||
|
||||
} else {
|
||||
// Remove cell from selection if it belongs to the given cells range or
|
||||
// it is spanned onto the cells range.
|
||||
nsCOMPtr<nsIDOMElement> cellElement;
|
||||
PRInt32 origRowIndex, origColIndex, rowSpan, colSpan,
|
||||
actualRowSpan, actualColSpan;
|
||||
PRBool isSelected;
|
||||
|
||||
result = tableLayout->GetCellDataAt(curRowIndex, curColIndex,
|
||||
*getter_AddRefs(cellElement),
|
||||
origRowIndex, origColIndex,
|
||||
rowSpan, colSpan,
|
||||
actualRowSpan, actualColSpan,
|
||||
isSelected);
|
||||
if (NS_FAILED(result))
|
||||
return result;
|
||||
|
||||
if (origRowIndex <= maxRowIndex &&
|
||||
origRowIndex + actualRowSpan - 1 >= minRowIndex &&
|
||||
origColIndex <= maxColIndex &&
|
||||
origColIndex + actualColSpan - 1 >= minColIndex) {
|
||||
|
||||
mDomSelections[index]->RemoveRange(range);
|
||||
// Since we've removed the range, decrement pointer to next range
|
||||
mSelectedCellIndex--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
range = GetNextCellRange();
|
||||
cellNode = GetFirstSelectedContent(range);
|
||||
NS_PRECONDITION(!range || cellNode, "Must have cellNode if had a range");
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsFrameSelection::AddCellsToSelection(nsIContent *aTableContent,
|
||||
PRInt32 aStartRowIndex,
|
||||
PRInt32 aStartColumnIndex,
|
||||
PRInt32 aEndRowIndex,
|
||||
PRInt32 aEndColumnIndex)
|
||||
{
|
||||
PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
|
||||
if (!mDomSelections[index])
|
||||
return NS_ERROR_NULL_POINTER;
|
||||
|
||||
// Get TableLayout interface to access cell data based on cellmap location
|
||||
// frames are not ref counted, so don't use an nsCOMPtr
|
||||
nsITableLayout *tableLayoutObject = GetTableLayout(aTableContent);
|
||||
if (!tableLayoutObject) // Check that |table| is a table.
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
nsCOMPtr<nsIDOMElement> cellElement;
|
||||
PRInt32 rowSpan, colSpan, actualRowSpan, actualColSpan,
|
||||
curRowIndex, curColIndex;
|
||||
PRBool isSelected;
|
||||
nsresult result = NS_OK;
|
||||
|
||||
PRInt32 row = aStartRowIndex;
|
||||
while(PR_TRUE)
|
||||
{
|
||||
PRInt32 col = startColIndex;
|
||||
PRInt32 col = aStartColumnIndex;
|
||||
while(PR_TRUE)
|
||||
{
|
||||
// Get TableLayout interface to access cell data based on cellmap location
|
||||
// frames are not ref counted, so don't use an nsCOMPtr
|
||||
nsITableLayout *tableLayoutObject = GetTableLayout(table);
|
||||
if (!tableLayoutObject) return NS_ERROR_FAILURE;
|
||||
|
||||
result = tableLayoutObject->GetCellDataAt(row, col, *getter_AddRefs(cellElement),
|
||||
curRowIndex, curColIndex, rowSpan, colSpan,
|
||||
actualRowSpan, actualColSpan, isSelected);
|
||||
|
@ -2844,16 +2909,16 @@ printf("SelectBlockOfCells -- range is null\n");
|
|||
if (NS_FAILED(result)) return result;
|
||||
}
|
||||
// Done when we reach end column
|
||||
if (col == endColIndex) break;
|
||||
if (col == aEndColumnIndex) break;
|
||||
|
||||
if (startColIndex < endColIndex)
|
||||
if (aStartColumnIndex < aEndColumnIndex)
|
||||
col ++;
|
||||
else
|
||||
col--;
|
||||
};
|
||||
if (row == endRowIndex) break;
|
||||
if (row == aEndRowIndex) break;
|
||||
|
||||
if (startRowIndex < endRowIndex)
|
||||
if (aStartRowIndex < aEndRowIndex)
|
||||
row++;
|
||||
else
|
||||
row--;
|
||||
|
@ -2861,6 +2926,28 @@ printf("SelectBlockOfCells -- range is null\n");
|
|||
return result;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsFrameSelection::RemoveCellsFromSelection(nsIContent *aTable,
|
||||
PRInt32 aStartRowIndex,
|
||||
PRInt32 aStartColumnIndex,
|
||||
PRInt32 aEndRowIndex,
|
||||
PRInt32 aEndColumnIndex)
|
||||
{
|
||||
return UnselectCells(aTable, aStartRowIndex, aStartColumnIndex,
|
||||
aEndRowIndex, aEndColumnIndex, PR_FALSE);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsFrameSelection::RestrictCellsToSelection(nsIContent *aTable,
|
||||
PRInt32 aStartRowIndex,
|
||||
PRInt32 aStartColumnIndex,
|
||||
PRInt32 aEndRowIndex,
|
||||
PRInt32 aEndColumnIndex)
|
||||
{
|
||||
return UnselectCells(aTable, aStartRowIndex, aStartColumnIndex,
|
||||
aEndRowIndex, aEndColumnIndex, PR_TRUE);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsFrameSelection::SelectRowOrColumn(nsIContent *aCellContent, PRUint32 aTarget)
|
||||
{
|
||||
|
|
Загрузка…
Ссылка в новой задаче