/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Author: Aaron Leventhal (aaronl@netscape.com) * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "nsHTMLTableAccessible.h" #include "nsAccessibilityAtoms.h" #include "nsAccessibleTreeWalker.h" #include "nsIDOMElement.h" #include "nsIDOMDocument.h" #include "nsIDOMDocumentRange.h" #include "nsIDOMRange.h" #include "nsISelection2.h" #include "nsISelectionPrivate.h" #include "nsINameSpaceManager.h" #include "nsIAccessibilityService.h" #include "nsIDOMCSSStyleDeclaration.h" #include "nsIDOMHTMLCollection.h" #include "nsIDOMHTMLTableCellElement.h" #include "nsIDOMHTMLTableElement.h" #include "nsIDOMHTMLTableRowElement.h" #include "nsIDOMHTMLTableSectionElem.h" #include "nsIDocument.h" #include "nsIPresShell.h" #include "nsIServiceManager.h" #include "nsITableLayout.h" #include "nsITableCellLayout.h" #include "nsFrameSelection.h" #include "nsLayoutErrors.h" //////////////////////////////////////////////////////////////////////////////// // nsHTMLTableCellAccessible implementation // nsISupports NS_IMPL_ISUPPORTS_INHERITED0(nsHTMLTableCellAccessible, nsHyperTextAccessible) nsHTMLTableCellAccessible::nsHTMLTableCellAccessible(nsIDOMNode* aDomNode, nsIWeakReference* aShell): nsHyperTextAccessibleWrap(aDomNode, aShell) { } // nsAccessible nsresult nsHTMLTableCellAccessible::GetRoleInternal(PRUint32 *aResult) { *aResult = nsIAccessibleRole::ROLE_CELL; return NS_OK; } nsresult nsHTMLTableCellAccessible::GetStateInternal(PRUint32 *aState, PRUint32 *aExtraState) { nsresult rv= nsHyperTextAccessibleWrap::GetStateInternal(aState, aExtraState); NS_ENSURE_A11Y_SUCCESS(rv, rv); nsCOMPtr content = do_QueryInterface(mDOMNode); nsCOMPtr presShell = do_QueryReferent(mWeakShell); nsIFrame *frame = presShell->GetPrimaryFrameFor(content); NS_ASSERTION(frame, "No frame for valid cell accessible!"); if (frame) { *aState |= nsIAccessibleStates::STATE_SELECTABLE; PRBool isSelected = PR_FALSE; frame->GetSelected(&isSelected); if (isSelected) *aState |= nsIAccessibleStates::STATE_SELECTED; } return NS_OK; } nsresult nsHTMLTableCellAccessible::GetAttributesInternal(nsIPersistentProperties *aAttributes) { if (IsDefunct()) return NS_ERROR_FAILURE; nsresult rv = nsHyperTextAccessibleWrap::GetAttributesInternal(aAttributes); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr 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 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 nsHTMLTableCellAccessible::GetTableAccessible() { nsCOMPtr childAcc(this); nsCOMPtr 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 content(do_QueryInterface(mDOMNode)); nsCOMPtr 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); nsITableCellLayout *cellLayout = do_QueryFrame(frame); NS_ENSURE_STATE(cellLayout); return cellLayout->GetCellIndexes(aRowIndex, aColIndex); } nsresult nsHTMLTableCellAccessible::FindCellsForRelation(PRInt32 aSearchHint, PRUint32 aRelationType, nsIAccessibleRelation **aRelation) { nsCOMPtr tableAcc(GetTableAccessible()); nsRefPtr nsTableAcc = nsAccUtils::QueryAccessibleTable(tableAcc); if (!nsTableAcc) return NS_OK; // Do not fail because of wrong markup. nsCOMPtr content(do_QueryInterface(mDOMNode)); PRInt32 rowIdx = -1, colIdx = -1; nsresult rv = GetCellIndexes(rowIdx, colIdx); NS_ENSURE_SUCCESS(rv, rv); PRBool moveToTopLeft = aSearchHint == eHeadersForCell; PRInt32 dir = (moveToTopLeft) ? -1 : 1; PRInt32 bound = 0; // left/right direction if (aSearchHint != eCellsForColumnHeader) { if (!moveToTopLeft) { tableAcc->GetColumns(&bound); bound--; } 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 cellElm; aTableAcc->GetCellAt(aRowIdx, aColIdx, *getter_AddRefs(cellElm)); if (!cellElm) return nsnull; nsCOMPtr 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 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 tableAcc(GetTableAccessible()); if (!tableAcc) return NS_OK; nsCOMPtr tableAccNode(do_QueryInterface(tableAcc)); nsCOMPtr tableNode; tableAccNode->GetDOMNode(getter_AddRefs(tableNode)); nsCOMPtr tableContent(do_QueryInterface(tableNode)); if (!tableContent) return NS_OK; nsCOMPtr 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_INHERITED2(nsHTMLTableAccessible, nsAccessible, nsHTMLTableAccessible, nsIAccessibleTable) nsHTMLTableAccessible::nsHTMLTableAccessible(nsIDOMNode* aDomNode, nsIWeakReference* aShell): nsAccessibleWrap(aDomNode, aShell) { } void nsHTMLTableAccessible::CacheChildren() { if (!mWeakShell) { // This node has been shut down mAccChildCount = eChildCountUninitialized; return; } if (mAccChildCount == eChildCountUninitialized) { nsAccessible::CacheChildren(); nsCOMPtr captionAccessible; while (NextChild(captionAccessible)) { if (nsAccUtils::Role(captionAccessible) == nsIAccessibleRole::ROLE_CAPTION) { nsCOMPtr captionParentAccessible; captionAccessible->GetParent(getter_AddRefs(captionParentAccessible)); if (captionParentAccessible != this) { NS_WARNING("Should not happen: parser ensures caption is the table's child, not the tbody's"); return; } nsCOMPtr beforeCaptionAccessible; captionAccessible->GetPreviousSibling(getter_AddRefs(beforeCaptionAccessible)); if (beforeCaptionAccessible) { // Move caption accessible so that it's the first child nsRefPtr acc = nsAccUtils::QueryAccessible(beforeCaptionAccessible); nsCOMPtr afterCaptionAccessible; captionAccessible->GetNextSibling(getter_AddRefs(afterCaptionAccessible)); acc->SetNextSibling(afterCaptionAccessible); GetFirstChild(getter_AddRefs(afterCaptionAccessible)); SetFirstChild(captionAccessible); acc = nsAccUtils::QueryAccessible(captionAccessible); acc->SetNextSibling(afterCaptionAccessible); } // Don't check for more captions, because nsAccessibilityService ensures // we don't create accessibles for the other captions, since only the // first is actually visible break; } } } } /* unsigned long getRole (); */ nsresult nsHTMLTableAccessible::GetRoleInternal(PRUint32 *aResult) { *aResult = nsIAccessibleRole::ROLE_TABLE; return NS_OK; } nsresult nsHTMLTableAccessible::GetStateInternal(PRUint32 *aState, PRUint32 *aExtraState) { nsresult rv= nsAccessible::GetStateInternal(aState, aExtraState); NS_ENSURE_A11Y_SUCCESS(rv, rv); *aState |= nsIAccessibleStates::STATE_READONLY; return NS_OK; } nsresult nsHTMLTableAccessible::GetNameInternal(nsAString& aName) { nsAccessible::GetNameInternal(aName); if (aName.IsEmpty()) { nsCOMPtr content(do_QueryInterface(mDOMNode)); if (content) { content->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::summary, aName); } } return NS_OK; } nsresult nsHTMLTableAccessible::GetAttributesInternal(nsIPersistentProperties *aAttributes) { if (!mDOMNode) { return NS_ERROR_FAILURE; // Node already shut down } nsresult rv = nsAccessibleWrap::GetAttributesInternal(aAttributes); NS_ENSURE_SUCCESS(rv, rv); PRBool isProbablyForLayout; IsProbablyForLayout(&isProbablyForLayout); if (isProbablyForLayout) { nsAutoString oldValueUnused; aAttributes->SetStringProperty(NS_LITERAL_CSTRING("layout-guess"), NS_LITERAL_STRING("true"), oldValueUnused); } return NS_OK; } NS_IMETHODIMP nsHTMLTableAccessible::GetRelationByType(PRUint32 aRelationType, nsIAccessibleRelation **aRelation) { nsresult rv = nsAccessibleWrap::GetRelationByType(aRelationType, aRelation); NS_ENSURE_SUCCESS(rv, rv); if (aRelationType == nsIAccessibleRelation::RELATION_DESCRIBED_BY) { nsCOMPtr accCaption; GetCaption(getter_AddRefs(accCaption)); return nsRelUtils::AddTarget(aRelationType, aRelation, accCaption); } return NS_OK; } NS_IMETHODIMP nsHTMLTableAccessible::GetCaption(nsIAccessible **aCaption) { nsCOMPtr firstChild; GetFirstChild(getter_AddRefs(firstChild)); if (nsAccUtils::Role(firstChild) == nsIAccessibleRole::ROLE_CAPTION) NS_ADDREF(*aCaption = firstChild); return NS_OK; } NS_IMETHODIMP nsHTMLTableAccessible::GetSummary(nsAString &aSummary) { nsCOMPtr table(do_QueryInterface(mDOMNode)); NS_ENSURE_TRUE(table, NS_ERROR_FAILURE); return table->GetSummary(aSummary); } NS_IMETHODIMP nsHTMLTableAccessible::GetColumns(PRInt32 *aColumns) { nsITableLayout *tableLayout; nsresult rv = GetTableLayout(&tableLayout); NS_ENSURE_SUCCESS(rv, rv); PRInt32 rows; return tableLayout->GetTableSize(rows, *aColumns); } NS_IMETHODIMP nsHTMLTableAccessible::GetColumnHeader(nsIAccessibleTable **aColumnHeader) { nsresult rv = NS_OK; nsCOMPtr table(do_QueryInterface(mDOMNode)); NS_ENSURE_TRUE(table, NS_ERROR_FAILURE); nsCOMPtr section; rv = table->GetTHead(getter_AddRefs(section)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr accService(do_GetService("@mozilla.org/accessibilityService;1")); NS_ENSURE_TRUE(accService, NS_ERROR_FAILURE); nsCOMPtr accHead; nsCOMPtr sectionNode(do_QueryInterface(section)); if (sectionNode) { rv = accService->GetCachedAccessible(sectionNode, mWeakShell, getter_AddRefs(accHead)); NS_ENSURE_SUCCESS(rv, rv); } if (!accHead) { accService->CreateHTMLTableHeadAccessible(section, getter_AddRefs(accHead)); NS_ENSURE_STATE(accHead); nsRefPtr accessNode = nsAccUtils::QueryAccessNode(accHead); rv = accessNode->Init(); NS_ENSURE_SUCCESS(rv, rv); } nsCOMPtr accTableHead(do_QueryInterface(accHead)); NS_ENSURE_TRUE(accTableHead, NS_ERROR_FAILURE); *aColumnHeader = accTableHead; NS_IF_ADDREF(*aColumnHeader); return rv; } NS_IMETHODIMP nsHTMLTableAccessible::GetRows(PRInt32 *aRows) { nsITableLayout *tableLayout; nsresult rv = GetTableLayout(&tableLayout); NS_ENSURE_SUCCESS(rv, rv); PRInt32 columns; return tableLayout->GetTableSize(*aRows, columns); } NS_IMETHODIMP nsHTMLTableAccessible::GetRowHeader(nsIAccessibleTable **aRowHeader) { // Can not implement because there is no row header in html table return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsHTMLTableAccessible::GetSelectedCellsCount(PRUint32* aCount) { NS_ENSURE_ARG_POINTER(aCount); *aCount = 0; PRInt32 rowsCount = 0; nsresult rv = GetRows(&rowsCount); NS_ENSURE_SUCCESS(rv, rv); PRInt32 columnsCount = 0; rv = GetColumns(&columnsCount); NS_ENSURE_SUCCESS(rv, rv); nsITableLayout *tableLayout = nsnull; rv = GetTableLayout(&tableLayout); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr domElement; PRInt32 startRowIndex = 0, startColIndex = 0, rowSpan, colSpan, actualRowSpan, actualColSpan; PRBool isSelected = PR_FALSE; PRInt32 rowIndex; for (rowIndex = 0; rowIndex < rowsCount; rowIndex++) { PRInt32 columnIndex; for (columnIndex = 0; columnIndex < columnsCount; columnIndex++) { rv = tableLayout->GetCellDataAt(rowIndex, columnIndex, *getter_AddRefs(domElement), startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan, isSelected); if (NS_SUCCEEDED(rv) && startRowIndex == rowIndex && startColIndex == columnIndex && isSelected) { (*aCount)++; } } } return NS_OK; } NS_IMETHODIMP nsHTMLTableAccessible::GetSelectedColumnsCount(PRUint32* aCount) { NS_ENSURE_ARG_POINTER(aCount); *aCount = 0; PRInt32 count = 0; nsresult rv = GetColumns(&count); NS_ENSURE_SUCCESS(rv, rv); PRInt32 index; for (index = 0; index < count; index++) { PRBool state = PR_FALSE; rv = IsColumnSelected(index, &state); NS_ENSURE_SUCCESS(rv, rv); if (state) (*aCount)++; } return NS_OK; } NS_IMETHODIMP nsHTMLTableAccessible::GetSelectedRowsCount(PRUint32* aCount) { NS_ENSURE_ARG_POINTER(aCount); *aCount = 0; PRInt32 count = 0; nsresult rv = GetRows(&count); NS_ENSURE_SUCCESS(rv, rv); PRInt32 index; for (index = 0; index < count; index++) { PRBool state = PR_FALSE; rv = IsRowSelected(index, &state); NS_ENSURE_SUCCESS(rv, rv); if (state) (*aCount)++; } return NS_OK; } NS_IMETHODIMP nsHTMLTableAccessible::GetSelectedCells(PRUint32 *aNumCells, PRInt32 **aCells) { NS_ENSURE_ARG_POINTER(aNumCells); *aNumCells = 0; NS_ENSURE_ARG_POINTER(aCells); *aCells = nsnull; PRInt32 rowsCount = 0; nsresult rv = GetRows(&rowsCount); NS_ENSURE_SUCCESS(rv, rv); PRInt32 columnsCount = 0; rv = GetColumns(&columnsCount); NS_ENSURE_SUCCESS(rv, rv); nsITableLayout *tableLayout = nsnull; rv = GetTableLayout(&tableLayout); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr domElement; PRInt32 startRowIndex = 0, startColIndex = 0, rowSpan, colSpan, actualRowSpan, actualColSpan; PRBool isSelected = PR_FALSE; PRInt32 cellsCount = columnsCount * rowsCount; nsAutoArrayPtr states(new PRBool[cellsCount]); NS_ENSURE_TRUE(states, NS_ERROR_OUT_OF_MEMORY); PRInt32 rowIndex, index; for (rowIndex = 0, index = 0; rowIndex < rowsCount; rowIndex++) { PRInt32 columnIndex; for (columnIndex = 0; columnIndex < columnsCount; columnIndex++, index++) { rv = tableLayout->GetCellDataAt(rowIndex, columnIndex, *getter_AddRefs(domElement), startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan, isSelected); if (NS_SUCCEEDED(rv) && startRowIndex == rowIndex && startColIndex == columnIndex && isSelected) { states[index] = PR_TRUE; (*aNumCells)++; } else { states[index] = PR_FALSE; } } } PRInt32 *cellsArray = (PRInt32 *)nsMemory::Alloc((*aNumCells) * sizeof(PRInt32)); NS_ENSURE_TRUE(cellsArray, NS_ERROR_OUT_OF_MEMORY); PRInt32 curr = 0; for (rowIndex = 0, index = 0; rowIndex < rowsCount; rowIndex++) { PRInt32 columnIndex; for (columnIndex = 0; columnIndex < columnsCount; columnIndex++, index++) { if (states[index]) { PRInt32 cellIndex = -1; GetIndexAt(rowIndex, columnIndex, &cellIndex); cellsArray[curr++] = cellIndex; } } } *aCells = cellsArray; return NS_OK; } NS_IMETHODIMP nsHTMLTableAccessible::GetSelectedColumns(PRUint32 *aNumColumns, PRInt32 **aColumns) { nsresult rv = NS_OK; PRInt32 columnCount; rv = GetColumns(&columnCount); NS_ENSURE_SUCCESS(rv, rv); PRBool *states = new PRBool[columnCount]; NS_ENSURE_TRUE(states, NS_ERROR_OUT_OF_MEMORY); *aNumColumns = 0; PRInt32 index; for (index = 0; index < columnCount; index++) { rv = IsColumnSelected(index, &states[index]); NS_ENSURE_SUCCESS(rv, rv); if (states[index]) { (*aNumColumns)++; } } PRInt32 *outArray = (PRInt32 *)nsMemory::Alloc((*aNumColumns) * sizeof(PRInt32)); if (!outArray) { delete []states; return NS_ERROR_OUT_OF_MEMORY; } PRInt32 curr = 0; for (index = 0; index < columnCount; index++) { if (states[index]) { outArray[curr++] = index; } } delete []states; *aColumns = outArray; return rv; } NS_IMETHODIMP nsHTMLTableAccessible::GetSelectedRows(PRUint32 *aNumRows, PRInt32 **aRows) { nsresult rv = NS_OK; PRInt32 rowCount; rv = GetRows(&rowCount); NS_ENSURE_SUCCESS(rv, rv); PRBool *states = new PRBool[rowCount]; NS_ENSURE_TRUE(states, NS_ERROR_OUT_OF_MEMORY); *aNumRows = 0; PRInt32 index; for (index = 0; index < rowCount; index++) { rv = IsRowSelected(index, &states[index]); NS_ENSURE_SUCCESS(rv, rv); if (states[index]) { (*aNumRows)++; } } PRInt32 *outArray = (PRInt32 *)nsMemory::Alloc((*aNumRows) * sizeof(PRInt32)); if (!outArray) { delete []states; return NS_ERROR_OUT_OF_MEMORY; } PRInt32 curr = 0; for (index = 0; index < rowCount; index++) { if (states[index]) { outArray[curr++] = index; } } delete []states; *aRows = outArray; return rv; } NS_IMETHODIMP nsHTMLTableAccessible::CellRefAt(PRInt32 aRow, PRInt32 aColumn, nsIAccessible **aTableCellAccessible) { NS_ENSURE_TRUE(IsValidRow(aRow) && IsValidColumn(aColumn), NS_ERROR_INVALID_ARG); nsresult rv = NS_OK; nsCOMPtr cellElement; rv = GetCellAt(aRow, aColumn, *getter_AddRefs(cellElement)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr accService(do_GetService("@mozilla.org/accessibilityService;1")); NS_ENSURE_TRUE(accService, NS_ERROR_FAILURE); return accService->GetAccessibleInWeakShell(cellElement, mWeakShell, aTableCellAccessible); } NS_IMETHODIMP nsHTMLTableAccessible::GetIndexAt(PRInt32 aRow, PRInt32 aColumn, PRInt32 *aIndex) { NS_ENSURE_ARG_POINTER(aIndex); NS_ENSURE_TRUE(IsValidRow(aRow) && IsValidColumn(aColumn), NS_ERROR_INVALID_ARG); nsITableLayout *tableLayout = nsnull; nsresult rv = GetTableLayout(&tableLayout); NS_ENSURE_SUCCESS(rv, rv); rv = tableLayout->GetIndexByRowAndColumn(aRow, aColumn, aIndex); if (rv == NS_TABLELAYOUT_CELL_NOT_FOUND) return NS_ERROR_INVALID_ARG; return NS_OK; } NS_IMETHODIMP nsHTMLTableAccessible::GetColumnAtIndex(PRInt32 aIndex, PRInt32 *aColumn) { NS_ENSURE_ARG_POINTER(aColumn); nsITableLayout *tableLayout = nsnull; nsresult rv = GetTableLayout(&tableLayout); NS_ENSURE_SUCCESS(rv, rv); PRInt32 row; return tableLayout->GetRowAndColumnByIndex(aIndex, &row, aColumn); } NS_IMETHODIMP nsHTMLTableAccessible::GetRowAtIndex(PRInt32 aIndex, PRInt32 *aRow) { NS_ENSURE_ARG_POINTER(aRow); nsITableLayout *tableLayout = nsnull; nsresult rv = GetTableLayout(&tableLayout); NS_ENSURE_SUCCESS(rv, rv); PRInt32 column; return tableLayout->GetRowAndColumnByIndex(aIndex, aRow, &column); } NS_IMETHODIMP nsHTMLTableAccessible::GetColumnExtentAt(PRInt32 aRow, PRInt32 aColumn, PRInt32 *_retval) { NS_ENSURE_TRUE(IsValidRow(aRow) && IsValidColumn(aColumn), NS_ERROR_INVALID_ARG); nsresult rv = NS_OK; nsCOMPtr domElement; rv = GetCellAt(aRow, aColumn, *getter_AddRefs(domElement)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr cell(do_QueryInterface(domElement)); NS_ENSURE_TRUE(cell, NS_ERROR_FAILURE); return cell->GetColSpan(_retval); } NS_IMETHODIMP nsHTMLTableAccessible::GetRowExtentAt(PRInt32 aRow, PRInt32 aColumn, PRInt32 *_retval) { NS_ENSURE_TRUE(IsValidRow(aRow) && IsValidColumn(aColumn), NS_ERROR_INVALID_ARG); nsresult rv = NS_OK; nsCOMPtr domElement; rv = GetCellAt(aRow, aColumn, *getter_AddRefs(domElement)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr cell(do_QueryInterface(domElement)); NS_ENSURE_TRUE(cell, NS_ERROR_FAILURE); return cell->GetRowSpan(_retval); } NS_IMETHODIMP nsHTMLTableAccessible::GetColumnDescription(PRInt32 aColumn, nsAString &_retval) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsHTMLTableAccessible::GetRowDescription(PRInt32 aRow, nsAString &_retval) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsHTMLTableAccessible::IsColumnSelected(PRInt32 aColumn, PRBool *_retval) { NS_ENSURE_ARG_POINTER(_retval); NS_ENSURE_TRUE(IsValidColumn(aColumn), NS_ERROR_INVALID_ARG); nsresult rv = NS_OK; PRInt32 rows; rv = GetRows(&rows); NS_ENSURE_SUCCESS(rv, rv); for (PRInt32 index = 0; index < rows; index++) { rv = IsCellSelected(index, aColumn, _retval); NS_ENSURE_SUCCESS(rv, rv); if (!*_retval) { break; } } return rv; } NS_IMETHODIMP nsHTMLTableAccessible::IsRowSelected(PRInt32 aRow, PRBool *_retval) { NS_ENSURE_ARG_POINTER(_retval); NS_ENSURE_TRUE(IsValidRow(aRow), NS_ERROR_INVALID_ARG); nsresult rv = NS_OK; PRInt32 columns; rv = GetColumns(&columns); NS_ENSURE_SUCCESS(rv, rv); for (PRInt32 index = 0; index < columns; index++) { rv = IsCellSelected(aRow, index, _retval); NS_ENSURE_SUCCESS(rv, rv); if (!*_retval) { break; } } return rv; } NS_IMETHODIMP nsHTMLTableAccessible::IsCellSelected(PRInt32 aRow, PRInt32 aColumn, PRBool *aIsSelected) { NS_ENSURE_ARG_POINTER(aIsSelected); *aIsSelected = PR_FALSE; NS_ENSURE_TRUE(IsValidRow(aRow) && IsValidColumn(aColumn), NS_ERROR_INVALID_ARG); nsITableLayout *tableLayout = nsnull; nsresult rv = GetTableLayout(&tableLayout); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr domElement; PRInt32 startRowIndex = 0, startColIndex = 0, rowSpan, colSpan, actualRowSpan, actualColSpan; rv = tableLayout->GetCellDataAt(aRow, aColumn, *getter_AddRefs(domElement), startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan, *aIsSelected); if (rv == NS_TABLELAYOUT_CELL_NOT_FOUND) return NS_ERROR_INVALID_ARG; return rv; } PRBool nsHTMLTableAccessible::IsValidColumn(PRInt32 aColumn) { PRInt32 colCount = 0; nsresult rv = GetColumns(&colCount); return NS_SUCCEEDED(rv) && (aColumn >= 0) && (aColumn < colCount); } PRBool nsHTMLTableAccessible::IsValidRow(PRInt32 aRow) { PRInt32 rowCount = 0; nsresult rv = GetRows(&rowCount); return NS_SUCCEEDED(rv) && (aRow >= 0) && (aRow < rowCount); } NS_IMETHODIMP nsHTMLTableAccessible::SelectRow(PRInt32 aRow) { 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) { 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) { if (IsDefunct()) return NS_ERROR_FAILURE; return RemoveRowsOrColumnsFromSelection(aRow, nsISelectionPrivate::TABLESELECTION_ROW, PR_FALSE); } NS_IMETHODIMP nsHTMLTableAccessible::UnselectColumn(PRInt32 aColumn) { if (IsDefunct()) return NS_ERROR_FAILURE; return RemoveRowsOrColumnsFromSelection(aColumn, nsISelectionPrivate::TABLESELECTION_COLUMN, PR_FALSE); } nsresult nsHTMLTableAccessible::AddRowOrColumnToSelection(PRInt32 aIndex, PRUint32 aTarget) { PRBool doSelectRow = (aTarget == nsISelectionPrivate::TABLESELECTION_ROW); nsITableLayout *tableLayout = nsnull; nsresult rv = GetTableLayout(&tableLayout); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr cellElm; PRInt32 startRowIdx, startColIdx, rowSpan, colSpan, actualRowSpan, actualColSpan; PRBool isSelected = PR_FALSE; PRInt32 count = 0; if (doSelectRow) rv = GetColumns(&count); else rv = GetRows(&count); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr presShell(GetPresShell()); nsRefPtr tableSelection = const_cast(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 cellContent(do_QueryInterface(cellElm)); rv = tableSelection->SelectCellElement(cellContent); NS_ENSURE_SUCCESS(rv, rv); } } return NS_OK; } nsresult nsHTMLTableAccessible::RemoveRowsOrColumnsFromSelection(PRInt32 aIndex, PRUint32 aTarget, PRBool aIsOuter) { nsCOMPtr content(do_QueryInterface(mDOMNode)); nsITableLayout *tableLayout = nsnull; nsresult rv = GetTableLayout(&tableLayout); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr presShell(GetPresShell()); nsRefPtr tableSelection = const_cast(presShell->ConstFrameSelection()); 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 nsHTMLTableAccessible::GetTableNode(nsIDOMNode **_retval) { nsresult rv = NS_OK; nsCOMPtr table(do_QueryInterface(mDOMNode)); if (table) { *_retval = table; NS_IF_ADDREF(*_retval); return rv; } nsCOMPtr section(do_QueryInterface(mDOMNode)); if (section) { nsCOMPtr parent; rv = section->GetParentNode(getter_AddRefs(parent)); NS_ENSURE_SUCCESS(rv, rv); *_retval = parent; NS_IF_ADDREF(*_retval); return rv; } return NS_ERROR_FAILURE; } nsresult nsHTMLTableAccessible::GetTableLayout(nsITableLayout **aTableLayout) { *aTableLayout = nsnull; nsCOMPtr tableNode; GetTableNode(getter_AddRefs(tableNode)); nsCOMPtr tableContent(do_QueryInterface(tableNode)); if (!tableContent) { return NS_ERROR_FAILURE; // Table shut down } nsCOMPtr shell = GetPresShell(); NS_ENSURE_TRUE(shell, NS_ERROR_FAILURE); nsIFrame *frame = shell->GetPrimaryFrameFor(tableContent); if (!frame) return NS_ERROR_FAILURE; *aTableLayout = do_QueryFrame(frame); return (*aTableLayout) ? NS_OK : NS_NOINTERFACE; } nsresult nsHTMLTableAccessible::GetCellAt(PRInt32 aRowIndex, PRInt32 aColIndex, nsIDOMElement* &aCell) { PRInt32 startRowIndex = 0, startColIndex = 0, rowSpan, colSpan, actualRowSpan, actualColSpan; PRBool isSelected; nsITableLayout *tableLayout = nsnull; nsresult rv = GetTableLayout(&tableLayout); NS_ENSURE_SUCCESS(rv, rv); rv = tableLayout->GetCellDataAt(aRowIndex, aColIndex, aCell, startRowIndex, startColIndex, rowSpan, colSpan, actualRowSpan, actualColSpan, isSelected); if (rv == NS_TABLELAYOUT_CELL_NOT_FOUND) return NS_ERROR_INVALID_ARG; return rv; } NS_IMETHODIMP nsHTMLTableAccessible::GetDescription(nsAString& aDescription) { // Helpful for debugging layout vs. data tables aDescription.Truncate(); nsAccessible::GetDescription(aDescription); if (!aDescription.IsEmpty()) { return NS_OK; } nsCOMPtr captionAccessible; GetCaption(getter_AddRefs(captionAccessible)); nsCOMPtr captionAccessNode = do_QueryInterface(captionAccessible); if (captionAccessNode) { nsCOMPtr captionNode; captionAccessNode->GetDOMNode(getter_AddRefs(captionNode)); nsCOMPtr captionContent = do_QueryInterface(captionNode); if (captionContent) { nsTextEquivUtils:: AppendTextEquivFromContent(this, captionContent, &aDescription); } } #ifdef SHOW_LAYOUT_HEURISTIC if (aDescription.IsEmpty()) { PRBool isProbablyForLayout; IsProbablyForLayout(&isProbablyForLayout); aDescription = mLayoutHeuristic; } #ifdef DEBUG_A11Y printf("\nTABLE: %s\n", NS_ConvertUTF16toUTF8(mLayoutHeuristic).get()); #endif #endif return NS_OK; } PRBool nsHTMLTableAccessible::HasDescendant(const nsAString& aTagName, PRBool aAllowEmpty) { nsCOMPtr tableElt(do_QueryInterface(mDOMNode)); NS_ENSURE_TRUE(tableElt, PR_FALSE); nsCOMPtr nodeList; tableElt->GetElementsByTagName(aTagName, getter_AddRefs(nodeList)); NS_ENSURE_TRUE(nodeList, PR_FALSE); nsCOMPtr foundItem; nodeList->Item(0, getter_AddRefs(foundItem)); if (!foundItem) return PR_FALSE; if (aAllowEmpty) return PR_TRUE; // Make sure that the item we found has contents and either has multiple // children or the found item is not a whitespace-only text node. nsCOMPtr foundItemContent = do_QueryInterface(foundItem); if (foundItemContent->GetChildCount() > 1) return PR_TRUE; // Treat multiple child nodes as non-empty nsIContent *innerItemContent = foundItemContent->GetChildAt(0); if (innerItemContent && !innerItemContent->TextIsOnlyWhitespace()) return PR_TRUE; // If we found more than one node then return true not depending on // aAllowEmpty flag. // XXX it might be dummy but bug 501375 where we changed this addresses // performance problems only. Note, currently 'aAllowEmpty' flag is used for // caption element only. On another hand we create accessible object for // the first entry of caption element (see // nsHTMLTableAccessible::CacheChildren). nodeList->Item(1, getter_AddRefs(foundItem)); return !!foundItem; } NS_IMETHODIMP nsHTMLTableAccessible::IsProbablyForLayout(PRBool *aIsProbablyForLayout) { // Implement a heuristic to determine if table is most likely used for layout // XXX do we want to look for rowspan or colspan, especialy that span all but a couple cells // at the beginning or end of a row/col, and especially when they occur at the edge of a table? // XXX expose this info via object attributes to AT-SPI // XXX For now debugging descriptions are always on via SHOW_LAYOUT_HEURISTIC // This will allow release trunk builds to be used by testers to refine the algorithm // Change to |#define SHOW_LAYOUT_HEURISTIC DEBUG| before final release #ifdef SHOW_LAYOUT_HEURISTIC #define RETURN_LAYOUT_ANSWER(isLayout, heuristic) \ { *aIsProbablyForLayout = isLayout; \ mLayoutHeuristic = isLayout ? NS_LITERAL_STRING("layout table: ") : NS_LITERAL_STRING("data table: "); \ mLayoutHeuristic += NS_LITERAL_STRING(heuristic); return NS_OK; } #else #define RETURN_LAYOUT_ANSWER(isLayout, heuristic) { *aIsProbablyForLayout = isLayout; return NS_OK; } #endif *aIsProbablyForLayout = PR_FALSE; nsCOMPtr content(do_QueryInterface(mDOMNode)); if (!content) { return NS_ERROR_FAILURE; // Table shut down } nsCOMPtr docAccessible = do_QueryInterface(nsCOMPtr(GetDocAccessible())); if (docAccessible) { PRUint32 state, extState; docAccessible->GetState(&state, &extState); if (extState & nsIAccessibleStates::EXT_STATE_EDITABLE) { // Need to see all elements while document is being edited RETURN_LAYOUT_ANSWER(PR_FALSE, "In editable document"); } } // Check to see if an ARIA role overrides the role from native markup, // but for which we still expose table semantics (treegrid, for example). PRBool hasNonTableRole = (nsAccUtils::Role(this) != nsIAccessibleRole::ROLE_TABLE); if (hasNonTableRole) { RETURN_LAYOUT_ANSWER(PR_FALSE, "Has role attribute"); } if (content->HasAttr(kNameSpaceID_None, nsAccessibilityAtoms::role)) { // Role attribute is present, but overridden roles have already been dealt with. // Only landmarks and other roles that don't override the role from native // markup are left to deal with here. RETURN_LAYOUT_ANSWER(PR_FALSE, "Has role attribute, weak role, and role is table"); } // Check for legitimate data table elements or attributes nsAutoString summary; if ((content->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::summary, summary) && !summary.IsEmpty()) || HasDescendant(NS_LITERAL_STRING("caption"), PR_FALSE) || HasDescendant(NS_LITERAL_STRING("th")) || HasDescendant(NS_LITERAL_STRING("thead")) || HasDescendant(NS_LITERAL_STRING("tfoot")) || HasDescendant(NS_LITERAL_STRING("colgroup"))) { RETURN_LAYOUT_ANSWER(PR_FALSE, "Has caption, summary, th, thead, tfoot or colgroup -- legitimate table structures"); } if (HasDescendant(NS_LITERAL_STRING("table"))) { RETURN_LAYOUT_ANSWER(PR_TRUE, "Has a nested table within it"); } // If only 1 column or only 1 row, it's for layout PRInt32 columns, rows; GetColumns(&columns); if (columns <=1) { RETURN_LAYOUT_ANSWER(PR_TRUE, "Has only 1 column"); } GetRows(&rows); if (rows <=1) { RETURN_LAYOUT_ANSWER(PR_TRUE, "Has only 1 row"); } // Check for many columns if (columns >= 5) { RETURN_LAYOUT_ANSWER(PR_FALSE, ">=5 columns"); } // Now we know there are 2-4 columns and 2 or more rows // Check to see if there are visible borders on the cells // XXX currently, we just check the first cell -- do we really need to do more? nsCOMPtr cellElement; nsresult rv = GetCellAt(0, 0, *getter_AddRefs(cellElement)); NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE); nsCOMPtr cellContent(do_QueryInterface(cellElement)); NS_ENSURE_TRUE(cellContent, NS_ERROR_FAILURE); nsCOMPtr shell(GetPresShell()); nsIFrame *cellFrame = shell->GetPrimaryFrameFor(cellContent); if (!cellFrame) { return NS_OK; } nsMargin border; cellFrame->GetBorder(border); if (border.top && border.bottom && border.left && border.right) { RETURN_LAYOUT_ANSWER(PR_FALSE, "Has nonzero border-width on table cell"); } /** * Rules for non-bordered tables with 2-4 columns and 2+ rows from here on forward */ // Check for styled background color across the row // Alternating background color is a common way nsCOMPtr nodeList; nsCOMPtr tableElt(do_QueryInterface(mDOMNode)); tableElt->GetElementsByTagName(NS_LITERAL_STRING("tr"), getter_AddRefs(nodeList)); NS_ENSURE_TRUE(nodeList, NS_ERROR_FAILURE); PRUint32 length; nodeList->GetLength(&length); nsAutoString color, lastRowColor; for (PRUint32 rowCount = 0; rowCount < length; rowCount ++) { nsCOMPtr rowNode; nodeList->Item(rowCount, getter_AddRefs(rowNode)); nsCOMPtr styleDecl; nsCoreUtils::GetComputedStyleDeclaration(EmptyString(), rowNode, getter_AddRefs(styleDecl)); NS_ENSURE_TRUE(styleDecl, NS_ERROR_FAILURE); lastRowColor = color; styleDecl->GetPropertyValue(NS_LITERAL_STRING("background-color"), color); if (rowCount > 0 && PR_FALSE == lastRowColor.Equals(color)) { RETURN_LAYOUT_ANSWER(PR_FALSE, "2 styles of row background color, non-bordered"); } } // Check for many rows const PRInt32 kMaxLayoutRows = 20; if (rows > kMaxLayoutRows) { // A ton of rows, this is probably for data RETURN_LAYOUT_ANSWER(PR_FALSE, ">= kMaxLayoutRows (20) and non-bordered"); } // Check for very wide table nsAutoString styledWidth; GetComputedStyleValue(EmptyString(), NS_LITERAL_STRING("width"), styledWidth); if (styledWidth.EqualsLiteral("100%")) { RETURN_LAYOUT_ANSWER(PR_TRUE, "<=4 columns and 100% width"); } if (styledWidth.Find(NS_LITERAL_STRING("px"))) { // Hardcoded in pixels nsIFrame *tableFrame = GetFrame(); NS_ENSURE_TRUE(tableFrame , NS_ERROR_FAILURE); nsSize tableSize = tableFrame->GetSize(); nsCOMPtr docAccessible = GetDocAccessible(); NS_ENSURE_TRUE(docAccessible, NS_ERROR_FAILURE); nsRefPtr docAccessNode = nsAccUtils::QueryAccessNode(docAccessible); nsIFrame *docFrame = docAccessNode->GetFrame(); NS_ENSURE_TRUE(docFrame , NS_ERROR_FAILURE); nsSize docSize = docFrame->GetSize(); if (docSize.width > 0) { PRInt32 percentageOfDocWidth = (100 * tableSize.width) / docSize.width; if (percentageOfDocWidth > 95) { // 3-4 columns, no borders, not a lot of rows, and 95% of the doc's width // Probably for layout RETURN_LAYOUT_ANSWER(PR_TRUE, "<=4 columns, width hardcoded in pixels and 95% of document width"); } } } // Two column rules if (rows * columns <= 10) { RETURN_LAYOUT_ANSWER(PR_TRUE, "2-4 columns, 10 cells or less, non-bordered"); } if (HasDescendant(NS_LITERAL_STRING("embed")) || HasDescendant(NS_LITERAL_STRING("object")) || HasDescendant(NS_LITERAL_STRING("applet")) || HasDescendant(NS_LITERAL_STRING("iframe"))) { RETURN_LAYOUT_ANSWER(PR_TRUE, "Has no borders, and has iframe, object, applet or iframe, typical of advertisements"); } RETURN_LAYOUT_ANSWER(PR_FALSE, "no layout factor strong enough, so will guess data"); } // -------------------------------------------------------- // nsHTMLTableHeadAccessible Accessible // -------------------------------------------------------- NS_IMPL_ISUPPORTS_INHERITED0(nsHTMLTableHeadAccessible, nsHTMLTableAccessible) nsHTMLTableHeadAccessible::nsHTMLTableHeadAccessible(nsIDOMNode *aDomNode, nsIWeakReference *aShell): nsHTMLTableAccessible(aDomNode, aShell) { } nsresult nsHTMLTableHeadAccessible::GetRoleInternal(PRUint32 *aRole) { *aRole = nsIAccessibleRole::ROLE_COLUMNHEADER; return NS_OK; } NS_IMETHODIMP nsHTMLTableHeadAccessible::GetCaption(nsIAccessible **aCaption) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsHTMLTableHeadAccessible::GetSummary(nsAString &aSummary) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsHTMLTableHeadAccessible::GetColumnHeader(nsIAccessibleTable **aColumnHeader) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsHTMLTableHeadAccessible::GetRows(PRInt32 *aRows) { nsresult rv = NS_OK; nsCOMPtr head(do_QueryInterface(mDOMNode)); NS_ENSURE_TRUE(head, NS_ERROR_FAILURE); nsCOMPtr rows; rv = head->GetRows(getter_AddRefs(rows)); NS_ENSURE_SUCCESS(rv, rv); return rows->GetLength((PRUint32 *)aRows); } //////////////////////////////////////////////////////////////////////////////// // nsHTMLCaptionAccessible NS_IMETHODIMP nsHTMLCaptionAccessible::GetRelationByType(PRUint32 aRelationType, nsIAccessibleRelation **aRelation) { nsresult rv = nsHyperTextAccessible::GetRelationByType(aRelationType, aRelation); NS_ENSURE_SUCCESS(rv, rv); if (aRelationType == nsIAccessibleRelation::RELATION_DESCRIPTION_FOR) { nsCOMPtr accParent; GetParent(getter_AddRefs(accParent)); return nsRelUtils::AddTarget(aRelationType, aRelation, accParent); } return NS_OK; } nsresult nsHTMLCaptionAccessible::GetRoleInternal(PRUint32 *aRole) { *aRole = nsIAccessibleRole::ROLE_CAPTION; return NS_OK; }