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 "DocAccessible.h"
14 #include "LocalAccessible-inl.h"
15 #include "nsTextEquivUtils.h"
19 #include "TreeWalker.h"
21 #include "mozilla/PresShell.h"
22 #include "mozilla/dom/HTMLTableElement.h"
23 #include "nsIHTMLCollection.h"
24 #include "mozilla/dom/Document.h"
25 #include "nsITableCellLayout.h"
26 #include "nsFrameSelection.h"
28 #include "nsArrayUtils.h"
29 #include "nsComponentManagerUtils.h"
30 #include "nsNameSpaceManager.h"
31 #include "nsTableCellFrame.h"
32 #include "nsTableWrapperFrame.h"
34 using namespace mozilla
;
35 using namespace mozilla::dom
;
36 using namespace mozilla::a11y
;
38 ////////////////////////////////////////////////////////////////////////////////
39 // HTMLTableCellAccessible
40 ////////////////////////////////////////////////////////////////////////////////
42 HTMLTableCellAccessible::HTMLTableCellAccessible(nsIContent
* aContent
,
44 : HyperTextAccessibleWrap(aContent
, aDoc
) {
45 mType
= eHTMLTableCellType
;
46 mGenericTypes
|= eTableCell
;
49 ////////////////////////////////////////////////////////////////////////////////
50 // HTMLTableCellAccessible: LocalAccessible implementation
52 role
HTMLTableCellAccessible::NativeRole() const {
53 if (mContent
->IsMathMLElement(nsGkAtoms::mtd_
)) {
54 return roles::MATHML_CELL
;
59 uint64_t HTMLTableCellAccessible::NativeState() const {
60 uint64_t state
= HyperTextAccessibleWrap::NativeState();
62 nsIFrame
* frame
= mContent
->GetPrimaryFrame();
63 NS_ASSERTION(frame
, "No frame for valid cell accessible!");
65 if (frame
&& frame
->IsSelected()) state
|= states::SELECTED
;
70 uint64_t HTMLTableCellAccessible::NativeInteractiveState() const {
71 return HyperTextAccessibleWrap::NativeInteractiveState() | states::SELECTABLE
;
74 already_AddRefed
<AccAttributes
> HTMLTableCellAccessible::NativeAttributes() {
75 RefPtr
<AccAttributes
> attributes
=
76 HyperTextAccessibleWrap::NativeAttributes();
78 // table-cell-index attribute
79 TableAccessible
* table
= Table();
80 if (!table
) return attributes
.forget();
82 int32_t rowIdx
= -1, colIdx
= -1;
83 nsresult rv
= GetCellIndexes(rowIdx
, colIdx
);
84 if (NS_FAILED(rv
)) return attributes
.forget();
86 attributes
->SetAttribute(nsGkAtoms::tableCellIndex
,
87 table
->CellIndexAt(rowIdx
, colIdx
));
91 // Pick up object attribute from abbr DOM element (a child of the cell) or
92 // from abbr DOM attribute.
94 if (ChildCount() == 1) {
95 LocalAccessible
* abbr
= LocalFirstChild();
96 if (abbr
->IsAbbreviation()) {
97 nsIContent
* firstChildNode
= abbr
->GetContent()->GetFirstChild();
99 nsTextEquivUtils::AppendTextEquivFromTextContent(firstChildNode
,
104 if (abbrText
.IsEmpty()) {
105 mContent
->AsElement()->GetAttr(kNameSpaceID_None
, nsGkAtoms::abbr
,
109 if (!abbrText
.IsEmpty()) {
110 attributes
->SetAttribute(nsGkAtoms::abbr
, std::move(abbrText
));
115 mContent
->AsElement()->GetAttr(kNameSpaceID_None
, nsGkAtoms::axis
, axisText
);
116 if (!axisText
.IsEmpty()) {
117 attributes
->SetAttribute(nsGkAtoms::axis
, std::move(axisText
));
121 RefPtr
<nsAtom
> cppClass
= NS_Atomize(u
"cppclass"_ns
);
122 attributes
->SetAttributeStringCopy(cppClass
, u
"HTMLTableCellAccessible"_ns
);
125 return attributes
.forget();
128 GroupPos
HTMLTableCellAccessible::GroupPosition() {
129 int32_t count
= 0, index
= 0;
130 TableAccessible
* table
= Table();
132 nsCoreUtils::GetUIntAttr(table
->AsAccessible()->GetContent(),
133 nsGkAtoms::aria_colcount
, &count
) &&
134 nsCoreUtils::GetUIntAttr(mContent
, nsGkAtoms::aria_colindex
, &index
)) {
135 return GroupPos(0, index
, count
);
138 return HyperTextAccessibleWrap::GroupPosition();
141 void HTMLTableCellAccessible::DOMAttributeChanged(int32_t aNameSpaceID
,
144 const nsAttrValue
* aOldValue
,
145 uint64_t aOldState
) {
146 HyperTextAccessibleWrap::DOMAttributeChanged(aNameSpaceID
, aAttribute
,
147 aModType
, aOldValue
, aOldState
);
149 if (aAttribute
== nsGkAtoms::headers
|| aAttribute
== nsGkAtoms::abbr
||
150 aAttribute
== nsGkAtoms::scope
) {
151 mDoc
->FireDelayedEvent(nsIAccessibleEvent::EVENT_OBJECT_ATTRIBUTE_CHANGED
,
156 ////////////////////////////////////////////////////////////////////////////////
157 // HTMLTableCellAccessible: TableCellAccessible implementation
159 TableAccessible
* HTMLTableCellAccessible::Table() const {
160 LocalAccessible
* parent
= const_cast<HTMLTableCellAccessible
*>(this);
161 while ((parent
= parent
->LocalParent())) {
162 if (parent
->IsTable()) 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 GetCellIndexes(rowIdx
, colIdx
);
184 TableAccessible
* table
= Table();
185 NS_ASSERTION(table
, "cell not in a table!");
186 if (!table
) return 0;
188 return table
->ColExtentAt(rowIdx
, colIdx
);
191 uint32_t HTMLTableCellAccessible::RowExtent() const {
192 int32_t rowIdx
= -1, colIdx
= -1;
193 GetCellIndexes(rowIdx
, colIdx
);
195 TableAccessible
* table
= Table();
196 NS_ASSERTION(table
, "cell not in atable!");
197 if (!table
) return 0;
199 return table
->RowExtentAt(rowIdx
, colIdx
);
202 void HTMLTableCellAccessible::ColHeaderCells(
203 nsTArray
<LocalAccessible
*>* aCells
) {
204 IDRefsIterator
itr(mDoc
, mContent
, nsGkAtoms::headers
);
205 while (LocalAccessible
* cell
= itr
.Next()) {
206 a11y::role cellRole
= cell
->Role();
207 if (cellRole
== roles::COLUMNHEADER
) {
208 aCells
->AppendElement(cell
);
209 } else if (cellRole
!= roles::ROWHEADER
) {
210 // If referred table cell is at the same column then treat it as a column
212 TableCellAccessible
* tableCell
= cell
->AsTableCell();
213 if (tableCell
&& tableCell
->ColIdx() == ColIdx()) {
214 aCells
->AppendElement(cell
);
219 if (aCells
->IsEmpty()) TableCellAccessible::ColHeaderCells(aCells
);
222 void HTMLTableCellAccessible::RowHeaderCells(
223 nsTArray
<LocalAccessible
*>* aCells
) {
224 IDRefsIterator
itr(mDoc
, mContent
, nsGkAtoms::headers
);
225 while (LocalAccessible
* cell
= itr
.Next()) {
226 a11y::role cellRole
= cell
->Role();
227 if (cellRole
== roles::ROWHEADER
) {
228 aCells
->AppendElement(cell
);
229 } else if (cellRole
!= roles::COLUMNHEADER
) {
230 // If referred table cell is at the same row then treat it as a column
232 TableCellAccessible
* tableCell
= cell
->AsTableCell();
233 if (tableCell
&& tableCell
->RowIdx() == RowIdx()) {
234 aCells
->AppendElement(cell
);
239 if (aCells
->IsEmpty()) TableCellAccessible::RowHeaderCells(aCells
);
242 bool HTMLTableCellAccessible::Selected() {
243 int32_t rowIdx
= -1, colIdx
= -1;
244 GetCellIndexes(rowIdx
, colIdx
);
246 TableAccessible
* table
= Table();
247 NS_ENSURE_TRUE(table
, false);
249 return table
->IsCellSelected(rowIdx
, colIdx
);
252 ////////////////////////////////////////////////////////////////////////////////
253 // HTMLTableCellAccessible: protected implementation
255 nsITableCellLayout
* HTMLTableCellAccessible::GetCellLayout() const {
256 return do_QueryFrame(mContent
->GetPrimaryFrame());
259 nsTableCellFrame
* HTMLTableCellAccessible::GetCellFrame() const {
260 return do_QueryFrame(mContent
->GetPrimaryFrame());
263 nsresult
HTMLTableCellAccessible::GetCellIndexes(int32_t& aRowIdx
,
264 int32_t& aColIdx
) const {
265 nsITableCellLayout
* cellLayout
= GetCellLayout();
266 NS_ENSURE_STATE(cellLayout
);
268 return cellLayout
->GetCellIndexes(aRowIdx
, aColIdx
);
271 ////////////////////////////////////////////////////////////////////////////////
272 // HTMLTableHeaderCellAccessible
273 ////////////////////////////////////////////////////////////////////////////////
275 HTMLTableHeaderCellAccessible::HTMLTableHeaderCellAccessible(
276 nsIContent
* aContent
, DocAccessible
* aDoc
)
277 : HTMLTableCellAccessible(aContent
, aDoc
) {}
279 ////////////////////////////////////////////////////////////////////////////////
280 // HTMLTableHeaderCellAccessible: LocalAccessible implementation
282 role
HTMLTableHeaderCellAccessible::NativeRole() const {
283 // Check value of @scope attribute.
284 static Element::AttrValuesArray scopeValues
[] = {
285 nsGkAtoms::col
, nsGkAtoms::colgroup
, nsGkAtoms::row
, nsGkAtoms::rowgroup
,
287 int32_t valueIdx
= mContent
->AsElement()->FindAttrValueIn(
288 kNameSpaceID_None
, nsGkAtoms::scope
, scopeValues
, eCaseMatters
);
293 return roles::COLUMNHEADER
;
296 return roles::ROWHEADER
;
299 TableAccessible
* table
= Table();
300 if (!table
) return roles::NOTHING
;
302 // If the cell next to this one is not a header cell then assume this cell is
303 // a row header for it.
304 uint32_t rowIdx
= RowIdx(), colIdx
= ColIdx();
305 LocalAccessible
* cell
= table
->CellAt(rowIdx
, colIdx
+ ColExtent());
306 if (cell
&& !nsCoreUtils::IsHTMLTableHeader(cell
->GetContent())) {
307 return roles::ROWHEADER
;
310 // If the cell below this one is not a header cell then assume this cell is
311 // a column header for it.
312 uint32_t rowExtent
= RowExtent();
313 cell
= table
->CellAt(rowIdx
+ rowExtent
, colIdx
);
314 if (cell
&& !nsCoreUtils::IsHTMLTableHeader(cell
->GetContent())) {
315 return roles::COLUMNHEADER
;
318 // Otherwise if this cell is surrounded by header cells only then make a guess
319 // based on its cell spanning. In other words if it is row spanned then assume
320 // it's a row header, otherwise it's a column header.
321 return rowExtent
> 1 ? roles::ROWHEADER
: roles::COLUMNHEADER
;
324 ////////////////////////////////////////////////////////////////////////////////
325 // HTMLTableRowAccessible
326 ////////////////////////////////////////////////////////////////////////////////
328 role
HTMLTableRowAccessible::NativeRole() const {
329 if (mContent
->IsMathMLElement(nsGkAtoms::mtr_
)) {
330 return roles::MATHML_TABLE_ROW
;
331 } else if (mContent
->IsMathMLElement(nsGkAtoms::mlabeledtr_
)) {
332 return roles::MATHML_LABELED_ROW
;
337 GroupPos
HTMLTableRowAccessible::GroupPosition() {
338 int32_t count
= 0, index
= 0;
339 LocalAccessible
* table
= nsAccUtils::TableFor(this);
341 nsCoreUtils::GetUIntAttr(table
->GetContent(), nsGkAtoms::aria_rowcount
,
343 nsCoreUtils::GetUIntAttr(mContent
, nsGkAtoms::aria_rowindex
, &index
)) {
344 return GroupPos(0, index
, count
);
347 return AccessibleWrap::GroupPosition();
350 // LocalAccessible protected
351 ENameValueFlag
HTMLTableRowAccessible::NativeName(nsString
& aName
) const {
352 // For table row accessibles, we only want to calculate the name from the
353 // sub tree if an ARIA role is present.
354 if (HasStrongARIARole()) {
355 return AccessibleWrap::NativeName(aName
);
361 ////////////////////////////////////////////////////////////////////////////////
362 // HTMLTableAccessible
363 ////////////////////////////////////////////////////////////////////////////////
365 ////////////////////////////////////////////////////////////////////////////////
366 // HTMLTableAccessible: LocalAccessible
368 bool HTMLTableAccessible::InsertChildAt(uint32_t aIndex
,
369 LocalAccessible
* aChild
) {
370 // Move caption accessible so that it's the first child. Check for the first
371 // caption only, because nsAccessibilityService ensures we don't create
372 // accessibles for the other captions, since only the first is actually
374 return LocalAccessible::InsertChildAt(aChild
->IsHTMLCaption() ? 0 : aIndex
,
378 role
HTMLTableAccessible::NativeRole() const {
379 if (mContent
->IsMathMLElement(nsGkAtoms::mtable_
)) {
380 return roles::MATHML_TABLE
;
385 uint64_t HTMLTableAccessible::NativeState() const {
386 return LocalAccessible::NativeState() | states::READONLY
;
389 ENameValueFlag
HTMLTableAccessible::NativeName(nsString
& aName
) const {
390 ENameValueFlag nameFlag
= LocalAccessible::NativeName(aName
);
391 if (!aName
.IsEmpty()) return nameFlag
;
393 // Use table caption as a name.
394 LocalAccessible
* caption
= Caption();
396 nsIContent
* captionContent
= caption
->GetContent();
397 if (captionContent
) {
398 nsTextEquivUtils::AppendTextEquivFromContent(this, captionContent
,
400 if (!aName
.IsEmpty()) return eNameOK
;
404 // If no caption then use summary as a name.
405 mContent
->AsElement()->GetAttr(kNameSpaceID_None
, nsGkAtoms::summary
, aName
);
409 void HTMLTableAccessible::DOMAttributeChanged(int32_t aNameSpaceID
,
412 const nsAttrValue
* aOldValue
,
413 uint64_t aOldState
) {
414 HyperTextAccessibleWrap::DOMAttributeChanged(aNameSpaceID
, aAttribute
,
415 aModType
, aOldValue
, aOldState
);
417 if (aAttribute
== nsGkAtoms::summary
) {
420 if (name
.IsEmpty()) {
422 // XXX: Should really be checking if caption provides a name.
423 mDoc
->FireDelayedEvent(nsIAccessibleEvent::EVENT_NAME_CHANGE
, this);
427 mDoc
->FireDelayedEvent(nsIAccessibleEvent::EVENT_OBJECT_ATTRIBUTE_CHANGED
,
432 already_AddRefed
<AccAttributes
> HTMLTableAccessible::NativeAttributes() {
433 RefPtr
<AccAttributes
> attributes
= AccessibleWrap::NativeAttributes();
435 if (mContent
->IsMathMLElement(nsGkAtoms::mtable_
)) {
436 GetAccService()->MarkupAttributes(mContent
, attributes
);
439 if (IsProbablyLayoutTable()) {
440 attributes
->SetAttribute(nsGkAtoms::layout_guess
, true);
443 return attributes
.forget();
446 ////////////////////////////////////////////////////////////////////////////////
447 // HTMLTableAccessible: LocalAccessible
449 Relation
HTMLTableAccessible::RelationByType(RelationType aType
) const {
450 Relation rel
= AccessibleWrap::RelationByType(aType
);
451 if (aType
== RelationType::LABELLED_BY
) rel
.AppendTarget(Caption());
456 ////////////////////////////////////////////////////////////////////////////////
457 // HTMLTableAccessible: Table
459 LocalAccessible
* HTMLTableAccessible::Caption() const {
460 LocalAccessible
* child
= mChildren
.SafeElementAt(0, nullptr);
461 // Since this is an HTML table the caption needs to be a caption
462 // element with no ARIA role (except for a reduntant role='caption').
463 // If we did a full Role() calculation here we risk getting into an infinite
464 // loop where the parent role would depend on its name which would need to be
465 // calculated by retrieving the caption (bug 1420773.)
466 return child
&& child
->NativeRole() == roles::CAPTION
&&
467 (!child
->HasStrongARIARole() ||
468 child
->IsARIARole(nsGkAtoms::caption
))
473 void HTMLTableAccessible::Summary(nsString
& aSummary
) {
474 dom::HTMLTableElement
* table
= dom::HTMLTableElement::FromNode(mContent
);
476 if (table
) table
->GetSummary(aSummary
);
479 uint32_t HTMLTableAccessible::ColCount() const {
480 nsTableWrapperFrame
* tableFrame
= GetTableWrapperFrame();
481 return tableFrame
? tableFrame
->GetColCount() : 0;
484 uint32_t HTMLTableAccessible::RowCount() {
485 nsTableWrapperFrame
* tableFrame
= GetTableWrapperFrame();
486 return tableFrame
? tableFrame
->GetRowCount() : 0;
489 uint32_t HTMLTableAccessible::SelectedCellCount() {
490 nsTableWrapperFrame
* tableFrame
= GetTableWrapperFrame();
491 if (!tableFrame
) return 0;
493 uint32_t count
= 0, rowCount
= RowCount(), colCount
= ColCount();
494 for (uint32_t rowIdx
= 0; rowIdx
< rowCount
; rowIdx
++) {
495 for (uint32_t colIdx
= 0; colIdx
< colCount
; colIdx
++) {
496 nsTableCellFrame
* cellFrame
= tableFrame
->GetCellFrameAt(rowIdx
, colIdx
);
497 if (!cellFrame
|| !cellFrame
->IsSelected()) continue;
499 uint32_t startRow
= cellFrame
->RowIndex();
500 uint32_t startCol
= cellFrame
->ColIndex();
501 if (startRow
== rowIdx
&& startCol
== colIdx
) count
++;
508 uint32_t HTMLTableAccessible::SelectedColCount() {
509 uint32_t count
= 0, colCount
= ColCount();
511 for (uint32_t colIdx
= 0; colIdx
< colCount
; colIdx
++) {
512 if (IsColSelected(colIdx
)) count
++;
518 uint32_t HTMLTableAccessible::SelectedRowCount() {
519 uint32_t count
= 0, rowCount
= RowCount();
521 for (uint32_t rowIdx
= 0; rowIdx
< rowCount
; rowIdx
++) {
522 if (IsRowSelected(rowIdx
)) count
++;
528 void HTMLTableAccessible::SelectedCells(nsTArray
<LocalAccessible
*>* aCells
) {
529 nsTableWrapperFrame
* tableFrame
= GetTableWrapperFrame();
530 if (!tableFrame
) return;
532 uint32_t rowCount
= RowCount(), colCount
= ColCount();
533 for (uint32_t rowIdx
= 0; rowIdx
< rowCount
; rowIdx
++) {
534 for (uint32_t colIdx
= 0; colIdx
< colCount
; colIdx
++) {
535 nsTableCellFrame
* cellFrame
= tableFrame
->GetCellFrameAt(rowIdx
, colIdx
);
536 if (!cellFrame
|| !cellFrame
->IsSelected()) continue;
538 uint32_t startRow
= cellFrame
->RowIndex();
539 uint32_t startCol
= cellFrame
->ColIndex();
540 if (startRow
!= rowIdx
|| startCol
!= colIdx
) continue;
542 LocalAccessible
* cell
= mDoc
->GetAccessible(cellFrame
->GetContent());
543 aCells
->AppendElement(cell
);
548 void HTMLTableAccessible::SelectedCellIndices(nsTArray
<uint32_t>* aCells
) {
549 nsTableWrapperFrame
* tableFrame
= GetTableWrapperFrame();
550 if (!tableFrame
) return;
552 uint32_t rowCount
= RowCount(), colCount
= ColCount();
553 for (uint32_t rowIdx
= 0; rowIdx
< rowCount
; rowIdx
++) {
554 for (uint32_t colIdx
= 0; colIdx
< colCount
; colIdx
++) {
555 nsTableCellFrame
* cellFrame
= tableFrame
->GetCellFrameAt(rowIdx
, colIdx
);
556 if (!cellFrame
|| !cellFrame
->IsSelected()) continue;
558 uint32_t startCol
= cellFrame
->ColIndex();
559 uint32_t startRow
= cellFrame
->RowIndex();
560 if (startRow
== rowIdx
&& startCol
== colIdx
) {
561 aCells
->AppendElement(CellIndexAt(rowIdx
, colIdx
));
567 void HTMLTableAccessible::SelectedColIndices(nsTArray
<uint32_t>* aCols
) {
568 uint32_t colCount
= ColCount();
569 for (uint32_t colIdx
= 0; colIdx
< colCount
; colIdx
++) {
570 if (IsColSelected(colIdx
)) aCols
->AppendElement(colIdx
);
574 void HTMLTableAccessible::SelectedRowIndices(nsTArray
<uint32_t>* aRows
) {
575 uint32_t rowCount
= RowCount();
576 for (uint32_t rowIdx
= 0; rowIdx
< rowCount
; rowIdx
++) {
577 if (IsRowSelected(rowIdx
)) aRows
->AppendElement(rowIdx
);
581 LocalAccessible
* HTMLTableAccessible::CellAt(uint32_t aRowIdx
,
583 nsTableWrapperFrame
* tableFrame
= GetTableWrapperFrame();
584 if (!tableFrame
) return nullptr;
586 nsIContent
* cellContent
= tableFrame
->GetCellAt(aRowIdx
, aColIdx
);
587 LocalAccessible
* cell
= mDoc
->GetAccessible(cellContent
);
589 // Sometimes, the accessible returned here is a row accessible instead of
590 // a cell accessible, for example when a cell has CSS display:block; set.
591 // In such cases, iterate through the cells in this row differently to find
593 if (cell
&& cell
->IsTableRow()) {
594 return CellInRowAt(cell
, aColIdx
);
597 // XXX bug 576838: bizarre tables (like table6 in tables/test_table2.html) may
598 // return itself as a cell what makes Orca hang.
599 return cell
== this ? nullptr : cell
;
602 int32_t HTMLTableAccessible::CellIndexAt(uint32_t aRowIdx
, uint32_t aColIdx
) {
603 nsTableWrapperFrame
* tableFrame
= GetTableWrapperFrame();
604 if (!tableFrame
) return -1;
606 int32_t cellIndex
= tableFrame
->GetIndexByRowAndColumn(aRowIdx
, aColIdx
);
607 if (cellIndex
== -1) {
608 // Sometimes, the accessible returned here is a row accessible instead of
609 // a cell accessible, for example when a cell has CSS display:block; set.
610 // In such cases, iterate through the cells in this row differently to find
612 nsIContent
* cellContent
= tableFrame
->GetCellAt(aRowIdx
, aColIdx
);
613 LocalAccessible
* cell
= mDoc
->GetAccessible(cellContent
);
614 if (cell
&& cell
->IsTableRow()) {
615 return TableAccessible::CellIndexAt(aRowIdx
, aColIdx
);
622 int32_t HTMLTableAccessible::ColIndexAt(uint32_t aCellIdx
) {
623 nsTableWrapperFrame
* tableFrame
= GetTableWrapperFrame();
624 if (!tableFrame
) return -1;
626 int32_t rowIdx
= -1, colIdx
= -1;
627 tableFrame
->GetRowAndColumnByIndex(aCellIdx
, &rowIdx
, &colIdx
);
630 // Sometimes, the index returned indicates that this is not a regular
631 // cell, for example when a cell has CSS display:block; set.
632 // In such cases, try the super class method to find it.
633 return TableAccessible::ColIndexAt(aCellIdx
);
639 int32_t HTMLTableAccessible::RowIndexAt(uint32_t aCellIdx
) {
640 nsTableWrapperFrame
* tableFrame
= GetTableWrapperFrame();
641 if (!tableFrame
) return -1;
643 int32_t rowIdx
= -1, colIdx
= -1;
644 tableFrame
->GetRowAndColumnByIndex(aCellIdx
, &rowIdx
, &colIdx
);
647 // Sometimes, the index returned indicates that this is not a regular
648 // cell, for example when a cell has CSS display:block; set.
649 // In such cases, try the super class method to find it.
650 return TableAccessible::RowIndexAt(aCellIdx
);
656 void HTMLTableAccessible::RowAndColIndicesAt(uint32_t aCellIdx
,
659 nsTableWrapperFrame
* tableFrame
= GetTableWrapperFrame();
661 tableFrame
->GetRowAndColumnByIndex(aCellIdx
, aRowIdx
, aColIdx
);
662 if (*aRowIdx
== -1 || *aColIdx
== -1) {
663 // Sometimes, the index returned indicates that this is not a regular
664 // cell, for example when a cell has CSS display:block; set.
665 // In such cases, try the super class method to find it.
666 TableAccessible::RowAndColIndicesAt(aCellIdx
, aRowIdx
, aColIdx
);
671 uint32_t HTMLTableAccessible::ColExtentAt(uint32_t aRowIdx
, uint32_t aColIdx
) {
672 nsTableWrapperFrame
* tableFrame
= GetTableWrapperFrame();
673 if (!tableFrame
) return 0;
675 uint32_t colExtent
= tableFrame
->GetEffectiveColSpanAt(aRowIdx
, aColIdx
);
676 if (colExtent
== 0) {
677 nsIContent
* cellContent
= tableFrame
->GetCellAt(aRowIdx
, aColIdx
);
678 LocalAccessible
* cell
= mDoc
->GetAccessible(cellContent
);
679 if (cell
&& cell
->IsTableRow()) {
680 return TableAccessible::ColExtentAt(aRowIdx
, aColIdx
);
687 uint32_t HTMLTableAccessible::RowExtentAt(uint32_t aRowIdx
, uint32_t aColIdx
) {
688 nsTableWrapperFrame
* tableFrame
= GetTableWrapperFrame();
689 if (!tableFrame
) return 0;
691 return tableFrame
->GetEffectiveRowSpanAt(aRowIdx
, aColIdx
);
694 bool HTMLTableAccessible::IsColSelected(uint32_t aColIdx
) {
695 bool isSelected
= false;
697 uint32_t rowCount
= RowCount();
698 for (uint32_t rowIdx
= 0; rowIdx
< rowCount
; rowIdx
++) {
699 isSelected
= IsCellSelected(rowIdx
, aColIdx
);
700 if (!isSelected
) return false;
706 bool HTMLTableAccessible::IsRowSelected(uint32_t aRowIdx
) {
707 bool isSelected
= false;
709 uint32_t colCount
= ColCount();
710 for (uint32_t colIdx
= 0; colIdx
< colCount
; colIdx
++) {
711 isSelected
= IsCellSelected(aRowIdx
, colIdx
);
712 if (!isSelected
) return false;
718 bool HTMLTableAccessible::IsCellSelected(uint32_t aRowIdx
, uint32_t aColIdx
) {
719 nsTableWrapperFrame
* tableFrame
= GetTableWrapperFrame();
720 if (!tableFrame
) return false;
722 nsTableCellFrame
* cellFrame
= tableFrame
->GetCellFrameAt(aRowIdx
, aColIdx
);
723 return cellFrame
? cellFrame
->IsSelected() : false;
726 void HTMLTableAccessible::SelectRow(uint32_t aRowIdx
) {
727 DebugOnly
<nsresult
> rv
=
728 RemoveRowsOrColumnsFromSelection(aRowIdx
, TableSelectionMode::Row
, true);
729 NS_ASSERTION(NS_SUCCEEDED(rv
),
730 "RemoveRowsOrColumnsFromSelection() Shouldn't fail!");
732 AddRowOrColumnToSelection(aRowIdx
, TableSelectionMode::Row
);
735 void HTMLTableAccessible::SelectCol(uint32_t aColIdx
) {
736 DebugOnly
<nsresult
> rv
= RemoveRowsOrColumnsFromSelection(
737 aColIdx
, TableSelectionMode::Column
, true);
738 NS_ASSERTION(NS_SUCCEEDED(rv
),
739 "RemoveRowsOrColumnsFromSelection() Shouldn't fail!");
741 AddRowOrColumnToSelection(aColIdx
, TableSelectionMode::Column
);
744 void HTMLTableAccessible::UnselectRow(uint32_t aRowIdx
) {
745 RemoveRowsOrColumnsFromSelection(aRowIdx
, TableSelectionMode::Row
, false);
748 void HTMLTableAccessible::UnselectCol(uint32_t aColIdx
) {
749 RemoveRowsOrColumnsFromSelection(aColIdx
, TableSelectionMode::Column
, false);
752 ////////////////////////////////////////////////////////////////////////////////
753 // HTMLTableAccessible: protected implementation
755 nsresult
HTMLTableAccessible::AddRowOrColumnToSelection(
756 int32_t aIndex
, TableSelectionMode aTarget
) {
757 bool doSelectRow
= (aTarget
== TableSelectionMode::Row
);
759 nsTableWrapperFrame
* tableFrame
= GetTableWrapperFrame();
760 if (!tableFrame
) return NS_OK
;
769 PresShell
* presShell
= mDoc
->PresShellPtr();
770 RefPtr
<nsFrameSelection
> tableSelection
=
771 const_cast<nsFrameSelection
*>(presShell
->ConstFrameSelection());
773 for (uint32_t idx
= 0; idx
< count
; idx
++) {
774 int32_t rowIdx
= doSelectRow
? aIndex
: idx
;
775 int32_t colIdx
= doSelectRow
? idx
: aIndex
;
776 nsTableCellFrame
* cellFrame
= tableFrame
->GetCellFrameAt(rowIdx
, colIdx
);
777 if (cellFrame
&& !cellFrame
->IsSelected()) {
778 nsresult rv
= tableSelection
->SelectCellElement(cellFrame
->GetContent());
779 NS_ENSURE_SUCCESS(rv
, rv
);
786 nsresult
HTMLTableAccessible::RemoveRowsOrColumnsFromSelection(
787 int32_t aIndex
, TableSelectionMode aTarget
, bool aIsOuter
) {
788 nsTableWrapperFrame
* tableFrame
= GetTableWrapperFrame();
789 if (!tableFrame
) return NS_OK
;
791 PresShell
* presShell
= mDoc
->PresShellPtr();
792 RefPtr
<nsFrameSelection
> tableSelection
=
793 const_cast<nsFrameSelection
*>(presShell
->ConstFrameSelection());
795 bool doUnselectRow
= (aTarget
== TableSelectionMode::Row
);
796 uint32_t count
= doUnselectRow
? ColCount() : RowCount();
798 int32_t startRowIdx
= doUnselectRow
? aIndex
: 0;
799 int32_t endRowIdx
= doUnselectRow
? aIndex
: count
- 1;
800 int32_t startColIdx
= doUnselectRow
? 0 : aIndex
;
801 int32_t endColIdx
= doUnselectRow
? count
- 1 : aIndex
;
804 return tableSelection
->RestrictCellsToSelection(
805 mContent
, startRowIdx
, startColIdx
, endRowIdx
, endColIdx
);
808 return tableSelection
->RemoveCellsFromSelection(
809 mContent
, startRowIdx
, startColIdx
, endRowIdx
, endColIdx
);
812 void HTMLTableAccessible::Description(nsString
& aDescription
) const {
813 // Helpful for debugging layout vs. data tables
814 aDescription
.Truncate();
815 LocalAccessible::Description(aDescription
);
816 if (!aDescription
.IsEmpty()) return;
818 // Use summary as description if it weren't used as a name.
819 // XXX: get rid code duplication with NameInternal().
820 LocalAccessible
* caption
= Caption();
822 nsIContent
* captionContent
= caption
->GetContent();
823 if (captionContent
) {
824 nsAutoString captionText
;
825 nsTextEquivUtils::AppendTextEquivFromContent(this, captionContent
,
828 if (!captionText
.IsEmpty()) { // summary isn't used as a name.
829 mContent
->AsElement()->GetAttr(kNameSpaceID_None
, nsGkAtoms::summary
,
835 #ifdef SHOW_LAYOUT_HEURISTIC
836 if (aDescription
.IsEmpty()) {
837 bool isProbablyForLayout
= IsProbablyLayoutTable();
838 aDescription
= mLayoutHeuristic
;
840 printf("\nTABLE: %s\n", NS_ConvertUTF16toUTF8(mLayoutHeuristic
).get());
844 nsTableWrapperFrame
* HTMLTableAccessible::GetTableWrapperFrame() const {
845 nsTableWrapperFrame
* tableFrame
= do_QueryFrame(mContent
->GetPrimaryFrame());
847 tableFrame
->GetChildList(nsIFrame::kPrincipalList
).FirstChild()) {
854 ////////////////////////////////////////////////////////////////////////////////
855 // HTMLCaptionAccessible
856 ////////////////////////////////////////////////////////////////////////////////
858 Relation
HTMLCaptionAccessible::RelationByType(RelationType aType
) const {
859 Relation rel
= HyperTextAccessible::RelationByType(aType
);
860 if (aType
== RelationType::LABEL_FOR
) {
861 rel
.AppendTarget(LocalParent());
867 role
HTMLCaptionAccessible::NativeRole() const { return roles::CAPTION
; }