1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "HTMLTableAccessible.h"
8 #include "mozilla/DebugOnly.h"
10 #include "nsAccessibilityService.h"
11 #include "nsAccUtils.h"
12 #include "AccAttributes.h"
13 #include "CacheConstants.h"
14 #include "DocAccessible.h"
15 #include "LocalAccessible-inl.h"
16 #include "nsTextEquivUtils.h"
20 #include "TreeWalker.h"
22 #include "mozilla/PresShell.h"
23 #include "mozilla/dom/HTMLTableElement.h"
24 #include "nsIHTMLCollection.h"
25 #include "mozilla/dom/Document.h"
26 #include "nsITableCellLayout.h"
27 #include "nsFrameSelection.h"
29 #include "nsArrayUtils.h"
30 #include "nsComponentManagerUtils.h"
31 #include "nsNameSpaceManager.h"
32 #include "nsTableCellFrame.h"
33 #include "nsTableWrapperFrame.h"
35 using namespace mozilla
;
36 using namespace mozilla::dom
;
37 using namespace mozilla::a11y
;
39 ////////////////////////////////////////////////////////////////////////////////
40 // HTMLTableCellAccessible
41 ////////////////////////////////////////////////////////////////////////////////
43 HTMLTableCellAccessible::HTMLTableCellAccessible(nsIContent
* aContent
,
45 : HyperTextAccessibleWrap(aContent
, aDoc
) {
46 mType
= eHTMLTableCellType
;
47 mGenericTypes
|= eTableCell
;
50 ////////////////////////////////////////////////////////////////////////////////
51 // HTMLTableCellAccessible: LocalAccessible implementation
53 role
HTMLTableCellAccessible::NativeRole() const {
54 if (mContent
->IsMathMLElement(nsGkAtoms::mtd_
)) {
55 return roles::MATHML_CELL
;
60 uint64_t HTMLTableCellAccessible::NativeState() const {
61 uint64_t state
= HyperTextAccessibleWrap::NativeState();
63 nsIFrame
* frame
= mContent
->GetPrimaryFrame();
64 NS_ASSERTION(frame
, "No frame for valid cell accessible!");
66 if (frame
&& frame
->IsSelected()) {
67 state
|= states::SELECTED
;
73 uint64_t HTMLTableCellAccessible::NativeInteractiveState() const {
74 return HyperTextAccessibleWrap::NativeInteractiveState() | states::SELECTABLE
;
77 already_AddRefed
<AccAttributes
> HTMLTableCellAccessible::NativeAttributes() {
78 RefPtr
<AccAttributes
> attributes
=
79 HyperTextAccessibleWrap::NativeAttributes();
81 // table-cell-index attribute
82 TableAccessible
* table
= Table();
84 return attributes
.forget();
87 int32_t rowIdx
= -1, colIdx
= -1;
88 nsresult rv
= GetCellIndexes(rowIdx
, colIdx
);
90 return attributes
.forget();
93 attributes
->SetAttribute(nsGkAtoms::tableCellIndex
,
94 table
->CellIndexAt(rowIdx
, colIdx
));
98 // Pick up object attribute from abbr DOM element (a child of the cell) or
99 // from abbr DOM attribute.
101 if (ChildCount() == 1) {
102 LocalAccessible
* abbr
= LocalFirstChild();
103 if (abbr
->IsAbbreviation()) {
104 nsIContent
* firstChildNode
= abbr
->GetContent()->GetFirstChild();
105 if (firstChildNode
) {
106 nsTextEquivUtils::AppendTextEquivFromTextContent(firstChildNode
,
111 if (abbrText
.IsEmpty()) {
112 mContent
->AsElement()->GetAttr(kNameSpaceID_None
, nsGkAtoms::abbr
,
116 if (!abbrText
.IsEmpty()) {
117 attributes
->SetAttribute(nsGkAtoms::abbr
, std::move(abbrText
));
122 mContent
->AsElement()->GetAttr(kNameSpaceID_None
, nsGkAtoms::axis
, axisText
);
123 if (!axisText
.IsEmpty()) {
124 attributes
->SetAttribute(nsGkAtoms::axis
, std::move(axisText
));
128 RefPtr
<nsAtom
> cppClass
= NS_Atomize(u
"cppclass"_ns
);
129 attributes
->SetAttributeStringCopy(cppClass
, u
"HTMLTableCellAccessible"_ns
);
132 return attributes
.forget();
135 void HTMLTableCellAccessible::DOMAttributeChanged(int32_t aNameSpaceID
,
138 const nsAttrValue
* aOldValue
,
139 uint64_t aOldState
) {
140 HyperTextAccessibleWrap::DOMAttributeChanged(aNameSpaceID
, aAttribute
,
141 aModType
, aOldValue
, aOldState
);
143 if (aAttribute
== nsGkAtoms::headers
|| aAttribute
== nsGkAtoms::abbr
||
144 aAttribute
== nsGkAtoms::scope
) {
145 mDoc
->FireDelayedEvent(nsIAccessibleEvent::EVENT_OBJECT_ATTRIBUTE_CHANGED
,
147 mDoc
->QueueCacheUpdate(this, CacheDomain::Table
);
148 } else if (aAttribute
== nsGkAtoms::rowspan
||
149 aAttribute
== nsGkAtoms::colspan
) {
150 mDoc
->QueueCacheUpdate(this, CacheDomain::Table
);
154 ////////////////////////////////////////////////////////////////////////////////
155 // HTMLTableCellAccessible: TableCellAccessible implementation
157 TableAccessible
* HTMLTableCellAccessible::Table() const {
158 LocalAccessible
* parent
= const_cast<HTMLTableCellAccessible
*>(this);
159 while ((parent
= parent
->LocalParent())) {
160 if (parent
->IsTable()) {
161 return parent
->AsTable();
168 uint32_t HTMLTableCellAccessible::ColIdx() const {
169 nsTableCellFrame
* cellFrame
= GetCellFrame();
170 NS_ENSURE_TRUE(cellFrame
, 0);
171 return cellFrame
->ColIndex();
174 uint32_t HTMLTableCellAccessible::RowIdx() const {
175 nsTableCellFrame
* cellFrame
= GetCellFrame();
176 NS_ENSURE_TRUE(cellFrame
, 0);
177 return cellFrame
->RowIndex();
180 uint32_t HTMLTableCellAccessible::ColExtent() const {
181 int32_t rowIdx
= -1, colIdx
= -1;
182 if (NS_FAILED(GetCellIndexes(rowIdx
, colIdx
))) {
186 TableAccessible
* table
= Table();
187 NS_ASSERTION(table
, "cell not in a table!");
192 return table
->ColExtentAt(rowIdx
, colIdx
);
195 uint32_t HTMLTableCellAccessible::RowExtent() const {
196 int32_t rowIdx
= -1, colIdx
= -1;
197 if (NS_FAILED(GetCellIndexes(rowIdx
, colIdx
))) {
201 TableAccessible
* table
= Table();
202 NS_ASSERTION(table
, "cell not in atable!");
207 return table
->RowExtentAt(rowIdx
, colIdx
);
210 void HTMLTableCellAccessible::ColHeaderCells(nsTArray
<Accessible
*>* aCells
) {
211 IDRefsIterator
itr(mDoc
, mContent
, nsGkAtoms::headers
);
212 while (LocalAccessible
* cell
= itr
.Next()) {
213 a11y::role cellRole
= cell
->Role();
214 if (cellRole
== roles::COLUMNHEADER
) {
215 aCells
->AppendElement(cell
);
216 } else if (cellRole
!= roles::ROWHEADER
) {
217 // If referred table cell is at the same column then treat it as a column
219 TableCellAccessible
* tableCell
= cell
->AsTableCell();
220 if (tableCell
&& tableCell
->ColIdx() == ColIdx()) {
221 aCells
->AppendElement(cell
);
226 if (aCells
->IsEmpty()) {
227 TableCellAccessible::ColHeaderCells(aCells
);
231 void HTMLTableCellAccessible::RowHeaderCells(nsTArray
<Accessible
*>* aCells
) {
232 IDRefsIterator
itr(mDoc
, mContent
, nsGkAtoms::headers
);
233 while (LocalAccessible
* cell
= itr
.Next()) {
234 a11y::role cellRole
= cell
->Role();
235 if (cellRole
== roles::ROWHEADER
) {
236 aCells
->AppendElement(cell
);
237 } else if (cellRole
!= roles::COLUMNHEADER
) {
238 // If referred table cell is at the same row then treat it as a column
240 TableCellAccessible
* tableCell
= cell
->AsTableCell();
241 if (tableCell
&& tableCell
->RowIdx() == RowIdx()) {
242 aCells
->AppendElement(cell
);
247 if (aCells
->IsEmpty()) {
248 TableCellAccessible::RowHeaderCells(aCells
);
252 bool HTMLTableCellAccessible::Selected() {
253 int32_t rowIdx
= -1, colIdx
= -1;
254 if (NS_FAILED(GetCellIndexes(rowIdx
, colIdx
))) {
258 TableAccessible
* table
= Table();
259 NS_ENSURE_TRUE(table
, false);
261 return table
->IsCellSelected(rowIdx
, colIdx
);
264 ////////////////////////////////////////////////////////////////////////////////
265 // HTMLTableCellAccessible: protected implementation
267 nsITableCellLayout
* HTMLTableCellAccessible::GetCellLayout() const {
268 return do_QueryFrame(mContent
->GetPrimaryFrame());
271 nsTableCellFrame
* HTMLTableCellAccessible::GetCellFrame() const {
272 return do_QueryFrame(mContent
->GetPrimaryFrame());
275 nsresult
HTMLTableCellAccessible::GetCellIndexes(int32_t& aRowIdx
,
276 int32_t& aColIdx
) const {
277 nsITableCellLayout
* cellLayout
= GetCellLayout();
278 NS_ENSURE_STATE(cellLayout
);
280 return cellLayout
->GetCellIndexes(aRowIdx
, aColIdx
);
283 ////////////////////////////////////////////////////////////////////////////////
284 // HTMLTableHeaderCellAccessible
285 ////////////////////////////////////////////////////////////////////////////////
287 HTMLTableHeaderCellAccessible::HTMLTableHeaderCellAccessible(
288 nsIContent
* aContent
, DocAccessible
* aDoc
)
289 : HTMLTableCellAccessible(aContent
, aDoc
) {}
291 ////////////////////////////////////////////////////////////////////////////////
292 // HTMLTableHeaderCellAccessible: LocalAccessible implementation
294 role
HTMLTableHeaderCellAccessible::NativeRole() const {
295 // Check value of @scope attribute.
296 static Element::AttrValuesArray scopeValues
[] = {
297 nsGkAtoms::col
, nsGkAtoms::colgroup
, nsGkAtoms::row
, nsGkAtoms::rowgroup
,
299 int32_t valueIdx
= mContent
->AsElement()->FindAttrValueIn(
300 kNameSpaceID_None
, nsGkAtoms::scope
, scopeValues
, eCaseMatters
);
305 return roles::COLUMNHEADER
;
308 return roles::ROWHEADER
;
311 TableAccessible
* table
= Table();
313 return roles::NOTHING
;
316 // If the cell next to this one is not a header cell then assume this cell is
317 // a row header for it.
318 uint32_t rowIdx
= RowIdx(), colIdx
= ColIdx();
319 LocalAccessible
* cell
= table
->CellAt(rowIdx
, colIdx
+ ColExtent());
320 if (cell
&& !nsCoreUtils::IsHTMLTableHeader(cell
->GetContent())) {
321 return roles::ROWHEADER
;
324 // If the cell below this one is not a header cell then assume this cell is
325 // a column header for it.
326 uint32_t rowExtent
= RowExtent();
327 cell
= table
->CellAt(rowIdx
+ rowExtent
, colIdx
);
328 if (cell
&& !nsCoreUtils::IsHTMLTableHeader(cell
->GetContent())) {
329 return roles::COLUMNHEADER
;
332 // Otherwise if this cell is surrounded by header cells only then make a guess
333 // based on its cell spanning. In other words if it is row spanned then assume
334 // it's a row header, otherwise it's a column header.
335 return rowExtent
> 1 ? roles::ROWHEADER
: roles::COLUMNHEADER
;
338 ////////////////////////////////////////////////////////////////////////////////
339 // HTMLTableRowAccessible
340 ////////////////////////////////////////////////////////////////////////////////
342 role
HTMLTableRowAccessible::NativeRole() const {
343 if (mContent
->IsMathMLElement(nsGkAtoms::mtr_
)) {
344 return roles::MATHML_TABLE_ROW
;
345 } else if (mContent
->IsMathMLElement(nsGkAtoms::mlabeledtr_
)) {
346 return roles::MATHML_LABELED_ROW
;
351 // LocalAccessible protected
352 ENameValueFlag
HTMLTableRowAccessible::NativeName(nsString
& aName
) const {
353 // For table row accessibles, we only want to calculate the name from the
354 // sub tree if an ARIA role is present.
355 if (HasStrongARIARole()) {
356 return AccessibleWrap::NativeName(aName
);
362 ////////////////////////////////////////////////////////////////////////////////
363 // HTMLTableAccessible
364 ////////////////////////////////////////////////////////////////////////////////
366 ////////////////////////////////////////////////////////////////////////////////
367 // HTMLTableAccessible: LocalAccessible
369 bool HTMLTableAccessible::InsertChildAt(uint32_t aIndex
,
370 LocalAccessible
* aChild
) {
371 // Move caption accessible so that it's the first child. Check for the first
372 // caption only, because nsAccessibilityService ensures we don't create
373 // accessibles for the other captions, since only the first is actually
375 return HyperTextAccessible::InsertChildAt(
376 aChild
->IsHTMLCaption() ? 0 : aIndex
, aChild
);
379 role
HTMLTableAccessible::NativeRole() const {
380 if (mContent
->IsMathMLElement(nsGkAtoms::mtable_
)) {
381 return roles::MATHML_TABLE
;
386 uint64_t HTMLTableAccessible::NativeState() const {
387 return LocalAccessible::NativeState() | states::READONLY
;
390 ENameValueFlag
HTMLTableAccessible::NativeName(nsString
& aName
) const {
391 ENameValueFlag nameFlag
= LocalAccessible::NativeName(aName
);
392 if (!aName
.IsEmpty()) {
396 // Use table caption as a name.
397 LocalAccessible
* caption
= Caption();
399 nsIContent
* captionContent
= caption
->GetContent();
400 if (captionContent
) {
401 nsTextEquivUtils::AppendTextEquivFromContent(this, captionContent
,
403 if (!aName
.IsEmpty()) {
409 // If no caption then use summary as a name.
410 mContent
->AsElement()->GetAttr(kNameSpaceID_None
, nsGkAtoms::summary
, aName
);
414 void HTMLTableAccessible::DOMAttributeChanged(int32_t aNameSpaceID
,
417 const nsAttrValue
* aOldValue
,
418 uint64_t aOldState
) {
419 HyperTextAccessibleWrap::DOMAttributeChanged(aNameSpaceID
, aAttribute
,
420 aModType
, aOldValue
, aOldState
);
422 if (aAttribute
== nsGkAtoms::summary
) {
425 if (name
.IsEmpty()) {
427 // XXX: Should really be checking if caption provides a name.
428 mDoc
->FireDelayedEvent(nsIAccessibleEvent::EVENT_NAME_CHANGE
, this);
432 mDoc
->FireDelayedEvent(nsIAccessibleEvent::EVENT_OBJECT_ATTRIBUTE_CHANGED
,
437 already_AddRefed
<AccAttributes
> HTMLTableAccessible::NativeAttributes() {
438 RefPtr
<AccAttributes
> attributes
= AccessibleWrap::NativeAttributes();
440 if (mContent
->IsMathMLElement(nsGkAtoms::mtable_
)) {
441 GetAccService()->MarkupAttributes(this, attributes
);
444 if (IsProbablyLayoutTable()) {
445 attributes
->SetAttribute(nsGkAtoms::layout_guess
, true);
448 return attributes
.forget();
451 ////////////////////////////////////////////////////////////////////////////////
452 // HTMLTableAccessible: LocalAccessible
454 Relation
HTMLTableAccessible::RelationByType(RelationType aType
) const {
455 Relation rel
= AccessibleWrap::RelationByType(aType
);
456 if (aType
== RelationType::LABELLED_BY
) {
457 rel
.AppendTarget(Caption());
463 ////////////////////////////////////////////////////////////////////////////////
464 // HTMLTableAccessible: Table
466 LocalAccessible
* HTMLTableAccessible::Caption() const {
467 LocalAccessible
* child
= mChildren
.SafeElementAt(0, nullptr);
468 // Since this is an HTML table the caption needs to be a caption
469 // element with no ARIA role (except for a reduntant role='caption').
470 // If we did a full Role() calculation here we risk getting into an infinite
471 // loop where the parent role would depend on its name which would need to be
472 // calculated by retrieving the caption (bug 1420773.)
473 return child
&& child
->NativeRole() == roles::CAPTION
&&
474 (!child
->HasStrongARIARole() ||
475 child
->IsARIARole(nsGkAtoms::caption
))
480 void HTMLTableAccessible::Summary(nsString
& aSummary
) {
481 dom::HTMLTableElement
* table
= dom::HTMLTableElement::FromNode(mContent
);
484 table
->GetSummary(aSummary
);
488 uint32_t HTMLTableAccessible::ColCount() const {
489 nsTableWrapperFrame
* tableFrame
= GetTableWrapperFrame();
490 return tableFrame
? tableFrame
->GetColCount() : 0;
493 uint32_t HTMLTableAccessible::RowCount() {
494 nsTableWrapperFrame
* tableFrame
= GetTableWrapperFrame();
495 return tableFrame
? tableFrame
->GetRowCount() : 0;
498 uint32_t HTMLTableAccessible::SelectedCellCount() {
499 nsTableWrapperFrame
* tableFrame
= GetTableWrapperFrame();
504 uint32_t count
= 0, rowCount
= RowCount(), colCount
= ColCount();
505 for (uint32_t rowIdx
= 0; rowIdx
< rowCount
; rowIdx
++) {
506 for (uint32_t colIdx
= 0; colIdx
< colCount
; colIdx
++) {
507 nsTableCellFrame
* cellFrame
= tableFrame
->GetCellFrameAt(rowIdx
, colIdx
);
508 if (!cellFrame
|| !cellFrame
->IsSelected()) {
512 uint32_t startRow
= cellFrame
->RowIndex();
513 uint32_t startCol
= cellFrame
->ColIndex();
514 if (startRow
== rowIdx
&& startCol
== colIdx
) {
523 uint32_t HTMLTableAccessible::SelectedColCount() {
524 uint32_t count
= 0, colCount
= ColCount();
526 for (uint32_t colIdx
= 0; colIdx
< colCount
; colIdx
++) {
527 if (IsColSelected(colIdx
)) {
535 uint32_t HTMLTableAccessible::SelectedRowCount() {
536 uint32_t count
= 0, rowCount
= RowCount();
538 for (uint32_t rowIdx
= 0; rowIdx
< rowCount
; rowIdx
++) {
539 if (IsRowSelected(rowIdx
)) {
547 void HTMLTableAccessible::SelectedCells(nsTArray
<Accessible
*>* aCells
) {
548 nsTableWrapperFrame
* tableFrame
= GetTableWrapperFrame();
553 uint32_t rowCount
= RowCount(), colCount
= ColCount();
554 for (uint32_t rowIdx
= 0; rowIdx
< rowCount
; rowIdx
++) {
555 for (uint32_t colIdx
= 0; colIdx
< colCount
; colIdx
++) {
556 nsTableCellFrame
* cellFrame
= tableFrame
->GetCellFrameAt(rowIdx
, colIdx
);
557 if (!cellFrame
|| !cellFrame
->IsSelected()) {
561 uint32_t startRow
= cellFrame
->RowIndex();
562 uint32_t startCol
= cellFrame
->ColIndex();
563 if (startRow
!= rowIdx
|| startCol
!= colIdx
) {
567 LocalAccessible
* cell
= mDoc
->GetAccessible(cellFrame
->GetContent());
568 aCells
->AppendElement(cell
);
573 void HTMLTableAccessible::SelectedCellIndices(nsTArray
<uint32_t>* aCells
) {
574 nsTableWrapperFrame
* tableFrame
= GetTableWrapperFrame();
579 uint32_t rowCount
= RowCount(), colCount
= ColCount();
580 for (uint32_t rowIdx
= 0; rowIdx
< rowCount
; rowIdx
++) {
581 for (uint32_t colIdx
= 0; colIdx
< colCount
; colIdx
++) {
582 nsTableCellFrame
* cellFrame
= tableFrame
->GetCellFrameAt(rowIdx
, colIdx
);
583 if (!cellFrame
|| !cellFrame
->IsSelected()) {
587 uint32_t startCol
= cellFrame
->ColIndex();
588 uint32_t startRow
= cellFrame
->RowIndex();
589 if (startRow
== rowIdx
&& startCol
== colIdx
) {
590 aCells
->AppendElement(CellIndexAt(rowIdx
, colIdx
));
596 void HTMLTableAccessible::SelectedColIndices(nsTArray
<uint32_t>* aCols
) {
597 uint32_t colCount
= ColCount();
598 for (uint32_t colIdx
= 0; colIdx
< colCount
; colIdx
++) {
599 if (IsColSelected(colIdx
)) {
600 aCols
->AppendElement(colIdx
);
605 void HTMLTableAccessible::SelectedRowIndices(nsTArray
<uint32_t>* aRows
) {
606 uint32_t rowCount
= RowCount();
607 for (uint32_t rowIdx
= 0; rowIdx
< rowCount
; rowIdx
++) {
608 if (IsRowSelected(rowIdx
)) {
609 aRows
->AppendElement(rowIdx
);
614 LocalAccessible
* HTMLTableAccessible::CellAt(uint32_t aRowIdx
,
616 nsTableWrapperFrame
* tableFrame
= GetTableWrapperFrame();
621 nsIContent
* cellContent
= tableFrame
->GetCellAt(aRowIdx
, aColIdx
);
622 LocalAccessible
* cell
= mDoc
->GetAccessible(cellContent
);
624 // Sometimes, the accessible returned here is a row accessible instead of
625 // a cell accessible, for example when a cell has CSS display:block; set.
626 // In such cases, iterate through the cells in this row differently to find
628 if (cell
&& cell
->IsTableRow()) {
629 return CellInRowAt(cell
, aColIdx
);
632 // XXX bug 576838: bizarre tables (like table6 in tables/test_table2.html) may
633 // return itself as a cell what makes Orca hang.
634 return cell
== this ? nullptr : cell
;
637 int32_t HTMLTableAccessible::CellIndexAt(uint32_t aRowIdx
, uint32_t aColIdx
) {
638 nsTableWrapperFrame
* tableFrame
= GetTableWrapperFrame();
643 int32_t cellIndex
= tableFrame
->GetIndexByRowAndColumn(aRowIdx
, aColIdx
);
644 if (cellIndex
== -1) {
645 // Sometimes, the accessible returned here is a row accessible instead of
646 // a cell accessible, for example when a cell has CSS display:block; set.
647 // In such cases, iterate through the cells in this row differently to find
649 nsIContent
* cellContent
= tableFrame
->GetCellAt(aRowIdx
, aColIdx
);
650 LocalAccessible
* cell
= mDoc
->GetAccessible(cellContent
);
651 if (cell
&& cell
->IsTableRow()) {
652 return TableAccessible::CellIndexAt(aRowIdx
, aColIdx
);
659 int32_t HTMLTableAccessible::ColIndexAt(uint32_t aCellIdx
) {
660 nsTableWrapperFrame
* tableFrame
= GetTableWrapperFrame();
665 int32_t rowIdx
= -1, colIdx
= -1;
666 tableFrame
->GetRowAndColumnByIndex(aCellIdx
, &rowIdx
, &colIdx
);
669 // Sometimes, the index returned indicates that this is not a regular
670 // cell, for example when a cell has CSS display:block; set.
671 // In such cases, try the super class method to find it.
672 return TableAccessible::ColIndexAt(aCellIdx
);
678 int32_t HTMLTableAccessible::RowIndexAt(uint32_t aCellIdx
) {
679 nsTableWrapperFrame
* tableFrame
= GetTableWrapperFrame();
684 int32_t rowIdx
= -1, colIdx
= -1;
685 tableFrame
->GetRowAndColumnByIndex(aCellIdx
, &rowIdx
, &colIdx
);
688 // Sometimes, the index returned indicates that this is not a regular
689 // cell, for example when a cell has CSS display:block; set.
690 // In such cases, try the super class method to find it.
691 return TableAccessible::RowIndexAt(aCellIdx
);
697 void HTMLTableAccessible::RowAndColIndicesAt(uint32_t aCellIdx
,
700 nsTableWrapperFrame
* tableFrame
= GetTableWrapperFrame();
702 tableFrame
->GetRowAndColumnByIndex(aCellIdx
, aRowIdx
, aColIdx
);
703 if (*aRowIdx
== -1 || *aColIdx
== -1) {
704 // Sometimes, the index returned indicates that this is not a regular
705 // cell, for example when a cell has CSS display:block; set.
706 // In such cases, try the super class method to find it.
707 TableAccessible::RowAndColIndicesAt(aCellIdx
, aRowIdx
, aColIdx
);
712 uint32_t HTMLTableAccessible::ColExtentAt(uint32_t aRowIdx
, uint32_t aColIdx
) {
713 nsTableWrapperFrame
* tableFrame
= GetTableWrapperFrame();
718 uint32_t colExtent
= tableFrame
->GetEffectiveColSpanAt(aRowIdx
, aColIdx
);
719 if (colExtent
== 0) {
720 nsIContent
* cellContent
= tableFrame
->GetCellAt(aRowIdx
, aColIdx
);
721 LocalAccessible
* cell
= mDoc
->GetAccessible(cellContent
);
722 if (cell
&& cell
->IsTableRow()) {
723 return TableAccessible::ColExtentAt(aRowIdx
, aColIdx
);
730 uint32_t HTMLTableAccessible::RowExtentAt(uint32_t aRowIdx
, uint32_t aColIdx
) {
731 nsTableWrapperFrame
* tableFrame
= GetTableWrapperFrame();
736 return tableFrame
->GetEffectiveRowSpanAt(aRowIdx
, aColIdx
);
739 bool HTMLTableAccessible::IsColSelected(uint32_t aColIdx
) {
740 bool isSelected
= false;
742 uint32_t rowCount
= RowCount();
743 for (uint32_t rowIdx
= 0; rowIdx
< rowCount
; rowIdx
++) {
744 isSelected
= IsCellSelected(rowIdx
, aColIdx
);
753 bool HTMLTableAccessible::IsRowSelected(uint32_t aRowIdx
) {
754 bool isSelected
= false;
756 uint32_t colCount
= ColCount();
757 for (uint32_t colIdx
= 0; colIdx
< colCount
; colIdx
++) {
758 isSelected
= IsCellSelected(aRowIdx
, colIdx
);
767 bool HTMLTableAccessible::IsCellSelected(uint32_t aRowIdx
, uint32_t aColIdx
) {
768 nsTableWrapperFrame
* tableFrame
= GetTableWrapperFrame();
773 nsTableCellFrame
* cellFrame
= tableFrame
->GetCellFrameAt(aRowIdx
, aColIdx
);
774 return cellFrame
? cellFrame
->IsSelected() : false;
777 void HTMLTableAccessible::SelectRow(uint32_t aRowIdx
) {
778 DebugOnly
<nsresult
> rv
=
779 RemoveRowsOrColumnsFromSelection(aRowIdx
, TableSelectionMode::Row
, true);
780 NS_ASSERTION(NS_SUCCEEDED(rv
),
781 "RemoveRowsOrColumnsFromSelection() Shouldn't fail!");
783 AddRowOrColumnToSelection(aRowIdx
, TableSelectionMode::Row
);
786 void HTMLTableAccessible::SelectCol(uint32_t aColIdx
) {
787 DebugOnly
<nsresult
> rv
= RemoveRowsOrColumnsFromSelection(
788 aColIdx
, TableSelectionMode::Column
, true);
789 NS_ASSERTION(NS_SUCCEEDED(rv
),
790 "RemoveRowsOrColumnsFromSelection() Shouldn't fail!");
792 AddRowOrColumnToSelection(aColIdx
, TableSelectionMode::Column
);
795 void HTMLTableAccessible::UnselectRow(uint32_t aRowIdx
) {
796 RemoveRowsOrColumnsFromSelection(aRowIdx
, TableSelectionMode::Row
, false);
799 void HTMLTableAccessible::UnselectCol(uint32_t aColIdx
) {
800 RemoveRowsOrColumnsFromSelection(aColIdx
, TableSelectionMode::Column
, false);
803 ////////////////////////////////////////////////////////////////////////////////
804 // HTMLTableAccessible: protected implementation
806 nsresult
HTMLTableAccessible::AddRowOrColumnToSelection(
807 int32_t aIndex
, TableSelectionMode aTarget
) {
808 bool doSelectRow
= (aTarget
== TableSelectionMode::Row
);
810 nsTableWrapperFrame
* tableFrame
= GetTableWrapperFrame();
822 PresShell
* presShell
= mDoc
->PresShellPtr();
823 RefPtr
<nsFrameSelection
> tableSelection
=
824 const_cast<nsFrameSelection
*>(presShell
->ConstFrameSelection());
826 for (uint32_t idx
= 0; idx
< count
; idx
++) {
827 int32_t rowIdx
= doSelectRow
? aIndex
: idx
;
828 int32_t colIdx
= doSelectRow
? idx
: aIndex
;
829 nsTableCellFrame
* cellFrame
= tableFrame
->GetCellFrameAt(rowIdx
, colIdx
);
830 if (cellFrame
&& !cellFrame
->IsSelected()) {
831 nsresult rv
= tableSelection
->SelectCellElement(cellFrame
->GetContent());
832 NS_ENSURE_SUCCESS(rv
, rv
);
839 nsresult
HTMLTableAccessible::RemoveRowsOrColumnsFromSelection(
840 int32_t aIndex
, TableSelectionMode aTarget
, bool aIsOuter
) {
841 nsTableWrapperFrame
* tableFrame
= GetTableWrapperFrame();
846 PresShell
* presShell
= mDoc
->PresShellPtr();
847 RefPtr
<nsFrameSelection
> tableSelection
=
848 const_cast<nsFrameSelection
*>(presShell
->ConstFrameSelection());
850 bool doUnselectRow
= (aTarget
== TableSelectionMode::Row
);
851 uint32_t count
= doUnselectRow
? ColCount() : RowCount();
853 int32_t startRowIdx
= doUnselectRow
? aIndex
: 0;
854 int32_t endRowIdx
= doUnselectRow
? aIndex
: count
- 1;
855 int32_t startColIdx
= doUnselectRow
? 0 : aIndex
;
856 int32_t endColIdx
= doUnselectRow
? count
- 1 : aIndex
;
859 return tableSelection
->RestrictCellsToSelection(
860 mContent
, startRowIdx
, startColIdx
, endRowIdx
, endColIdx
);
863 return tableSelection
->RemoveCellsFromSelection(
864 mContent
, startRowIdx
, startColIdx
, endRowIdx
, endColIdx
);
867 void HTMLTableAccessible::Description(nsString
& aDescription
) const {
868 // Helpful for debugging layout vs. data tables
869 aDescription
.Truncate();
870 LocalAccessible::Description(aDescription
);
871 if (!aDescription
.IsEmpty()) {
875 // Use summary as description if it weren't used as a name.
876 // XXX: get rid code duplication with NameInternal().
877 LocalAccessible
* caption
= Caption();
879 nsIContent
* captionContent
= caption
->GetContent();
880 if (captionContent
) {
881 nsAutoString captionText
;
882 nsTextEquivUtils::AppendTextEquivFromContent(this, captionContent
,
885 if (!captionText
.IsEmpty()) { // summary isn't used as a name.
886 mContent
->AsElement()->GetAttr(kNameSpaceID_None
, nsGkAtoms::summary
,
892 #ifdef SHOW_LAYOUT_HEURISTIC
893 if (aDescription
.IsEmpty()) {
894 bool isProbablyForLayout
= IsProbablyLayoutTable();
895 aDescription
= mLayoutHeuristic
;
897 printf("\nTABLE: %s\n", NS_ConvertUTF16toUTF8(mLayoutHeuristic
).get());
901 nsTableWrapperFrame
* HTMLTableAccessible::GetTableWrapperFrame() const {
902 nsTableWrapperFrame
* tableFrame
= do_QueryFrame(mContent
->GetPrimaryFrame());
904 tableFrame
->GetChildList(nsIFrame::kPrincipalList
).FirstChild()) {
911 ////////////////////////////////////////////////////////////////////////////////
912 // HTMLCaptionAccessible
913 ////////////////////////////////////////////////////////////////////////////////
915 Relation
HTMLCaptionAccessible::RelationByType(RelationType aType
) const {
916 Relation rel
= HyperTextAccessible::RelationByType(aType
);
917 if (aType
== RelationType::LABEL_FOR
) {
918 rel
.AppendTarget(LocalParent());
924 role
HTMLCaptionAccessible::NativeRole() const { return roles::CAPTION
; }