Bug 1785189 [wpt PR 34715] - Remove origin policy testing infrastructure, a=testonly
[gecko.git] / accessible / html / HTMLTableAccessible.cpp
blob73bb10d89d20621a5d2c4e00441f3c9d0159fd2f
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"
17 #include "Relation.h"
18 #include "Role.h"
19 #include "States.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"
28 #include "nsError.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,
44 DocAccessible* aDoc)
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;
57 return roles::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;
70 return state;
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();
83 if (!table) {
84 return attributes.forget();
87 int32_t rowIdx = -1, colIdx = -1;
88 nsresult rv = GetCellIndexes(rowIdx, colIdx);
89 if (NS_FAILED(rv)) {
90 return attributes.forget();
93 attributes->SetAttribute(nsGkAtoms::tableCellIndex,
94 table->CellIndexAt(rowIdx, colIdx));
96 // abbr attribute
98 // Pick up object attribute from abbr DOM element (a child of the cell) or
99 // from abbr DOM attribute.
100 nsString abbrText;
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,
107 &abbrText);
111 if (abbrText.IsEmpty()) {
112 mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::abbr,
113 abbrText);
116 if (!abbrText.IsEmpty()) {
117 attributes->SetAttribute(nsGkAtoms::abbr, std::move(abbrText));
120 // axis attribute
121 nsString axisText;
122 mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::axis, axisText);
123 if (!axisText.IsEmpty()) {
124 attributes->SetAttribute(nsGkAtoms::axis, std::move(axisText));
127 #ifdef DEBUG
128 RefPtr<nsAtom> cppClass = NS_Atomize(u"cppclass"_ns);
129 attributes->SetAttributeStringCopy(cppClass, u"HTMLTableCellAccessible"_ns);
130 #endif
132 return attributes.forget();
135 void HTMLTableCellAccessible::DOMAttributeChanged(int32_t aNameSpaceID,
136 nsAtom* aAttribute,
137 int32_t aModType,
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,
146 this);
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();
165 return nullptr;
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))) {
183 return 0;
186 TableAccessible* table = Table();
187 NS_ASSERTION(table, "cell not in a table!");
188 if (!table) {
189 return 0;
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))) {
198 return 0;
201 TableAccessible* table = Table();
202 NS_ASSERTION(table, "cell not in atable!");
203 if (!table) {
204 return 0;
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
218 // header.
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
239 // header.
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))) {
255 return false;
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,
298 nullptr};
299 int32_t valueIdx = mContent->AsElement()->FindAttrValueIn(
300 kNameSpaceID_None, nsGkAtoms::scope, scopeValues, eCaseMatters);
302 switch (valueIdx) {
303 case 0:
304 case 1:
305 return roles::COLUMNHEADER;
306 case 2:
307 case 3:
308 return roles::ROWHEADER;
311 TableAccessible* table = Table();
312 if (!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;
348 return roles::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);
359 return eNameOK;
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
374 // visible.
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;
383 return roles::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()) {
393 return nameFlag;
396 // Use table caption as a name.
397 LocalAccessible* caption = Caption();
398 if (caption) {
399 nsIContent* captionContent = caption->GetContent();
400 if (captionContent) {
401 nsTextEquivUtils::AppendTextEquivFromContent(this, captionContent,
402 &aName);
403 if (!aName.IsEmpty()) {
404 return eNameOK;
409 // If no caption then use summary as a name.
410 mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::summary, aName);
411 return eNameOK;
414 void HTMLTableAccessible::DOMAttributeChanged(int32_t aNameSpaceID,
415 nsAtom* aAttribute,
416 int32_t aModType,
417 const nsAttrValue* aOldValue,
418 uint64_t aOldState) {
419 HyperTextAccessibleWrap::DOMAttributeChanged(aNameSpaceID, aAttribute,
420 aModType, aOldValue, aOldState);
422 if (aAttribute == nsGkAtoms::summary) {
423 nsAutoString name;
424 ARIAName(name);
425 if (name.IsEmpty()) {
426 if (!Caption()) {
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,
433 this);
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());
460 return rel;
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))
476 ? child
477 : nullptr;
480 void HTMLTableAccessible::Summary(nsString& aSummary) {
481 dom::HTMLTableElement* table = dom::HTMLTableElement::FromNode(mContent);
483 if (table) {
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();
500 if (!tableFrame) {
501 return 0;
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()) {
509 continue;
512 uint32_t startRow = cellFrame->RowIndex();
513 uint32_t startCol = cellFrame->ColIndex();
514 if (startRow == rowIdx && startCol == colIdx) {
515 count++;
520 return count;
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)) {
528 count++;
532 return count;
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)) {
540 count++;
544 return count;
547 void HTMLTableAccessible::SelectedCells(nsTArray<Accessible*>* aCells) {
548 nsTableWrapperFrame* tableFrame = GetTableWrapperFrame();
549 if (!tableFrame) {
550 return;
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()) {
558 continue;
561 uint32_t startRow = cellFrame->RowIndex();
562 uint32_t startCol = cellFrame->ColIndex();
563 if (startRow != rowIdx || startCol != colIdx) {
564 continue;
567 LocalAccessible* cell = mDoc->GetAccessible(cellFrame->GetContent());
568 aCells->AppendElement(cell);
573 void HTMLTableAccessible::SelectedCellIndices(nsTArray<uint32_t>* aCells) {
574 nsTableWrapperFrame* tableFrame = GetTableWrapperFrame();
575 if (!tableFrame) {
576 return;
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()) {
584 continue;
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,
615 uint32_t aColIdx) {
616 nsTableWrapperFrame* tableFrame = GetTableWrapperFrame();
617 if (!tableFrame) {
618 return nullptr;
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
627 // it.
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();
639 if (!tableFrame) {
640 return -1;
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
648 // it.
649 nsIContent* cellContent = tableFrame->GetCellAt(aRowIdx, aColIdx);
650 LocalAccessible* cell = mDoc->GetAccessible(cellContent);
651 if (cell && cell->IsTableRow()) {
652 return TableAccessible::CellIndexAt(aRowIdx, aColIdx);
656 return cellIndex;
659 int32_t HTMLTableAccessible::ColIndexAt(uint32_t aCellIdx) {
660 nsTableWrapperFrame* tableFrame = GetTableWrapperFrame();
661 if (!tableFrame) {
662 return -1;
665 int32_t rowIdx = -1, colIdx = -1;
666 tableFrame->GetRowAndColumnByIndex(aCellIdx, &rowIdx, &colIdx);
668 if (colIdx == -1) {
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);
675 return colIdx;
678 int32_t HTMLTableAccessible::RowIndexAt(uint32_t aCellIdx) {
679 nsTableWrapperFrame* tableFrame = GetTableWrapperFrame();
680 if (!tableFrame) {
681 return -1;
684 int32_t rowIdx = -1, colIdx = -1;
685 tableFrame->GetRowAndColumnByIndex(aCellIdx, &rowIdx, &colIdx);
687 if (rowIdx == -1) {
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);
694 return rowIdx;
697 void HTMLTableAccessible::RowAndColIndicesAt(uint32_t aCellIdx,
698 int32_t* aRowIdx,
699 int32_t* aColIdx) {
700 nsTableWrapperFrame* tableFrame = GetTableWrapperFrame();
701 if (tableFrame) {
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();
714 if (!tableFrame) {
715 return 0;
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);
727 return colExtent;
730 uint32_t HTMLTableAccessible::RowExtentAt(uint32_t aRowIdx, uint32_t aColIdx) {
731 nsTableWrapperFrame* tableFrame = GetTableWrapperFrame();
732 if (!tableFrame) {
733 return 0;
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);
745 if (!isSelected) {
746 return false;
750 return isSelected;
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);
759 if (!isSelected) {
760 return false;
764 return isSelected;
767 bool HTMLTableAccessible::IsCellSelected(uint32_t aRowIdx, uint32_t aColIdx) {
768 nsTableWrapperFrame* tableFrame = GetTableWrapperFrame();
769 if (!tableFrame) {
770 return false;
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();
811 if (!tableFrame) {
812 return NS_OK;
815 uint32_t count = 0;
816 if (doSelectRow) {
817 count = ColCount();
818 } else {
819 count = RowCount();
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);
836 return NS_OK;
839 nsresult HTMLTableAccessible::RemoveRowsOrColumnsFromSelection(
840 int32_t aIndex, TableSelectionMode aTarget, bool aIsOuter) {
841 nsTableWrapperFrame* tableFrame = GetTableWrapperFrame();
842 if (!tableFrame) {
843 return NS_OK;
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;
858 if (aIsOuter) {
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()) {
872 return;
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();
878 if (caption) {
879 nsIContent* captionContent = caption->GetContent();
880 if (captionContent) {
881 nsAutoString captionText;
882 nsTextEquivUtils::AppendTextEquivFromContent(this, captionContent,
883 &captionText);
885 if (!captionText.IsEmpty()) { // summary isn't used as a name.
886 mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::summary,
887 aDescription);
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());
898 #endif
901 nsTableWrapperFrame* HTMLTableAccessible::GetTableWrapperFrame() const {
902 nsTableWrapperFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame());
903 if (tableFrame &&
904 tableFrame->GetChildList(nsIFrame::kPrincipalList).FirstChild()) {
905 return tableFrame;
908 return nullptr;
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());
921 return rel;
924 role HTMLCaptionAccessible::NativeRole() const { return roles::CAPTION; }