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"
10 #include "nsAccessibilityService.h"
11 #include "AccAttributes.h"
13 #include "CacheConstants.h"
14 #include "LocalAccessible-inl.h"
15 #include "nsTextEquivUtils.h"
17 #include "mozilla/a11y/Role.h"
20 #include "mozilla/a11y/TableAccessible.h"
21 #include "mozilla/a11y/TableCellAccessible.h"
22 #include "mozilla/Assertions.h"
23 #include "mozilla/dom/Element.h"
24 #include "mozilla/dom/NameSpaceConstants.h"
25 #include "nsCaseTreatment.h"
28 #include "nsCoreUtils.h"
30 #include "nsIHTMLCollection.h"
32 #include "nsGkAtoms.h"
33 #include "nsLiteralString.h"
35 #include "nsQueryFrame.h"
37 #include "nsStringFwd.h"
38 #include "nsTableCellFrame.h"
39 #include "nsTableWrapperFrame.h"
41 using namespace mozilla
;
42 using namespace mozilla::dom
;
43 using namespace mozilla::a11y
;
45 ////////////////////////////////////////////////////////////////////////////////
46 // HTMLTableCellAccessible
47 ////////////////////////////////////////////////////////////////////////////////
49 HTMLTableCellAccessible::HTMLTableCellAccessible(nsIContent
* aContent
,
51 : HyperTextAccessible(aContent
, aDoc
) {
52 mType
= eHTMLTableCellType
;
53 mGenericTypes
|= eTableCell
;
56 ////////////////////////////////////////////////////////////////////////////////
57 // HTMLTableCellAccessible: LocalAccessible implementation
59 role
HTMLTableCellAccessible::NativeRole() const {
60 // We implement this rather than using the markup maps because we only want
61 // this role to be returned if this is a valid cell. An invalid cell (e.g. if
62 // the table has role="none") won't use this class, so it will get a generic
63 // role, since the markup map doesn't specify a role.
64 if (mContent
->IsMathMLElement(nsGkAtoms::mtd_
)) {
65 return roles::MATHML_CELL
;
70 uint64_t HTMLTableCellAccessible::NativeState() const {
71 uint64_t state
= HyperTextAccessible::NativeState();
73 nsIFrame
* frame
= mContent
->GetPrimaryFrame();
74 NS_ASSERTION(frame
, "No frame for valid cell accessible!");
76 if (frame
&& frame
->IsSelected()) {
77 state
|= states::SELECTED
;
83 uint64_t HTMLTableCellAccessible::NativeInteractiveState() const {
84 return HyperTextAccessible::NativeInteractiveState() | states::SELECTABLE
;
87 already_AddRefed
<AccAttributes
> HTMLTableCellAccessible::NativeAttributes() {
88 RefPtr
<AccAttributes
> attributes
= HyperTextAccessible::NativeAttributes();
90 // We only need to expose table-cell-index to clients. If we're in the content
91 // process, we don't need this, so building a CachedTableAccessible is very
92 // wasteful. This will be exposed by RemoteAccessible in the parent process
94 if (!IPCAccessibilityActive()) {
95 if (const TableCellAccessible
* cell
= AsTableCell()) {
96 TableAccessible
* table
= cell
->Table();
97 const uint32_t row
= cell
->RowIdx();
98 const uint32_t col
= cell
->ColIdx();
99 const int32_t cellIdx
= table
->CellIndexAt(row
, col
);
101 attributes
->SetAttribute(nsGkAtoms::tableCellIndex
, cellIdx
);
108 // Pick up object attribute from abbr DOM element (a child of the cell) or
109 // from abbr DOM attribute.
111 if (ChildCount() == 1) {
112 LocalAccessible
* abbr
= LocalFirstChild();
113 if (abbr
->IsAbbreviation()) {
114 nsIContent
* firstChildNode
= abbr
->GetContent()->GetFirstChild();
115 if (firstChildNode
) {
116 nsTextEquivUtils::AppendTextEquivFromTextContent(firstChildNode
,
121 if (abbrText
.IsEmpty()) {
122 mContent
->AsElement()->GetAttr(nsGkAtoms::abbr
, abbrText
);
125 if (!abbrText
.IsEmpty()) {
126 attributes
->SetAttribute(nsGkAtoms::abbr
, std::move(abbrText
));
131 mContent
->AsElement()->GetAttr(nsGkAtoms::axis
, axisText
);
132 if (!axisText
.IsEmpty()) {
133 attributes
->SetAttribute(nsGkAtoms::axis
, std::move(axisText
));
136 return attributes
.forget();
139 void HTMLTableCellAccessible::DOMAttributeChanged(int32_t aNameSpaceID
,
142 const nsAttrValue
* aOldValue
,
143 uint64_t aOldState
) {
144 HyperTextAccessible::DOMAttributeChanged(aNameSpaceID
, aAttribute
, aModType
,
145 aOldValue
, aOldState
);
147 if (aAttribute
== nsGkAtoms::headers
|| aAttribute
== nsGkAtoms::abbr
||
148 aAttribute
== nsGkAtoms::scope
) {
149 mDoc
->FireDelayedEvent(nsIAccessibleEvent::EVENT_OBJECT_ATTRIBUTE_CHANGED
,
151 if (HTMLTableAccessible
* table
= Table()) {
152 // Modifying these attributes can also modify our table's classification
153 // as either a layout or data table. Queue an update on the table itself
154 // to re-compute our "layout guess"
155 mDoc
->QueueCacheUpdate(table
, CacheDomain::Table
);
157 mDoc
->QueueCacheUpdate(this, CacheDomain::Table
);
158 } else if (aAttribute
== nsGkAtoms::rowspan
||
159 aAttribute
== nsGkAtoms::colspan
) {
160 if (HTMLTableAccessible
* table
= Table()) {
161 // Modifying these attributes can also modify our table's classification
162 // as either a layout or data table. Queue an update on the table itself
163 // to re-compute our "layout guess"
164 mDoc
->QueueCacheUpdate(table
, CacheDomain::Table
);
166 mDoc
->QueueCacheUpdate(this, CacheDomain::Table
);
170 ////////////////////////////////////////////////////////////////////////////////
171 // HTMLTableCellAccessible implementation
173 HTMLTableAccessible
* HTMLTableCellAccessible::Table() const {
174 LocalAccessible
* parent
= const_cast<HTMLTableCellAccessible
*>(this);
175 while ((parent
= parent
->LocalParent())) {
176 if (parent
->IsHTMLTable()) {
177 return HTMLTableAccessible::GetFrom(parent
);
184 uint32_t HTMLTableCellAccessible::ColExtent() const {
185 nsTableCellFrame
* cell
= do_QueryFrame(GetFrame());
187 // This probably isn't a table according to the layout engine; e.g. it has
191 nsTableFrame
* table
= cell
->GetTableFrame();
193 return table
->GetEffectiveColSpan(*cell
);
196 uint32_t HTMLTableCellAccessible::RowExtent() const {
197 nsTableCellFrame
* cell
= do_QueryFrame(GetFrame());
199 // This probably isn't a table according to the layout engine; e.g. it has
203 nsTableFrame
* table
= cell
->GetTableFrame();
205 return table
->GetEffectiveRowSpan(*cell
);
208 ////////////////////////////////////////////////////////////////////////////////
209 // HTMLTableHeaderCellAccessible
210 ////////////////////////////////////////////////////////////////////////////////
212 HTMLTableHeaderCellAccessible::HTMLTableHeaderCellAccessible(
213 nsIContent
* aContent
, DocAccessible
* aDoc
)
214 : HTMLTableCellAccessible(aContent
, aDoc
) {}
216 ////////////////////////////////////////////////////////////////////////////////
217 // HTMLTableHeaderCellAccessible: LocalAccessible implementation
219 role
HTMLTableHeaderCellAccessible::NativeRole() const {
220 dom::Element
* el
= Elm();
222 return roles::NOTHING
;
225 // Check value of @scope attribute.
226 static mozilla::dom::Element::AttrValuesArray scopeValues
[] = {
227 nsGkAtoms::col
, nsGkAtoms::colgroup
, nsGkAtoms::row
, nsGkAtoms::rowgroup
,
229 int32_t valueIdx
= el
->FindAttrValueIn(kNameSpaceID_None
, nsGkAtoms::scope
,
230 scopeValues
, eCaseMatters
);
235 return roles::COLUMNHEADER
;
238 return roles::ROWHEADER
;
241 dom::Element
* nextEl
= el
->GetNextElementSibling();
242 dom::Element
* prevEl
= el
->GetPreviousElementSibling();
243 // If this is the only cell in its row, it's a column header.
244 if (!nextEl
&& !prevEl
) {
245 return roles::COLUMNHEADER
;
247 const bool nextIsHeader
= nextEl
&& nsCoreUtils::IsHTMLTableHeader(nextEl
);
248 const bool prevIsHeader
= prevEl
&& nsCoreUtils::IsHTMLTableHeader(prevEl
);
249 // If this has a header on both sides, it is a column header.
250 if (prevIsHeader
&& nextIsHeader
) {
251 return roles::COLUMNHEADER
;
253 // If this has a header on one side and only a single normal cell on the
254 // other, it's a column header.
255 if (nextIsHeader
&& prevEl
&& !prevEl
->GetPreviousElementSibling()) {
256 return roles::COLUMNHEADER
;
258 if (prevIsHeader
&& nextEl
&& !nextEl
->GetNextElementSibling()) {
259 return roles::COLUMNHEADER
;
261 // If this has a normal cell next to it, it 's a row header.
262 if ((nextEl
&& !nextIsHeader
) || (prevEl
&& !prevIsHeader
)) {
263 return roles::ROWHEADER
;
265 // If this has a row span, it could be a row header.
266 if (RowExtent() > 1) {
267 // It isn't a row header if it has 1 or more consecutive headers next to it.
269 (!prevEl
->GetPreviousElementSibling() ||
270 nsCoreUtils::IsHTMLTableHeader(prevEl
->GetPreviousElementSibling()))) {
271 return roles::COLUMNHEADER
;
274 (!nextEl
->GetNextElementSibling() ||
275 nsCoreUtils::IsHTMLTableHeader(nextEl
->GetNextElementSibling()))) {
276 return roles::COLUMNHEADER
;
278 return roles::ROWHEADER
;
280 // Otherwise, assume it's a column header.
281 return roles::COLUMNHEADER
;
284 ////////////////////////////////////////////////////////////////////////////////
285 // HTMLTableRowAccessible
286 ////////////////////////////////////////////////////////////////////////////////
288 // LocalAccessible protected
289 ENameValueFlag
HTMLTableRowAccessible::NativeName(nsString
& aName
) const {
290 // For table row accessibles, we only want to calculate the name from the
291 // sub tree if an ARIA role is present.
292 if (HasStrongARIARole()) {
293 return AccessibleWrap::NativeName(aName
);
299 ////////////////////////////////////////////////////////////////////////////////
300 // HTMLTableAccessible
301 ////////////////////////////////////////////////////////////////////////////////
303 ////////////////////////////////////////////////////////////////////////////////
304 // HTMLTableAccessible: LocalAccessible
306 bool HTMLTableAccessible::InsertChildAt(uint32_t aIndex
,
307 LocalAccessible
* aChild
) {
308 // Move caption accessible so that it's the first child. Check for the first
309 // caption only, because nsAccessibilityService ensures we don't create
310 // accessibles for the other captions, since only the first is actually
312 return HyperTextAccessible::InsertChildAt(
313 aChild
->IsHTMLCaption() ? 0 : aIndex
, aChild
);
316 uint64_t HTMLTableAccessible::NativeState() const {
317 return LocalAccessible::NativeState() | states::READONLY
;
320 ENameValueFlag
HTMLTableAccessible::NativeName(nsString
& aName
) const {
321 ENameValueFlag nameFlag
= LocalAccessible::NativeName(aName
);
322 if (!aName
.IsEmpty()) {
326 // Use table caption as a name.
327 LocalAccessible
* caption
= Caption();
329 nsIContent
* captionContent
= caption
->GetContent();
330 if (captionContent
) {
331 nsTextEquivUtils::AppendTextEquivFromContent(this, captionContent
,
333 if (!aName
.IsEmpty()) {
339 // If no caption then use summary as a name.
340 mContent
->AsElement()->GetAttr(nsGkAtoms::summary
, aName
);
344 void HTMLTableAccessible::DOMAttributeChanged(int32_t aNameSpaceID
,
347 const nsAttrValue
* aOldValue
,
348 uint64_t aOldState
) {
349 HyperTextAccessible::DOMAttributeChanged(aNameSpaceID
, aAttribute
, aModType
,
350 aOldValue
, aOldState
);
352 if (aAttribute
== nsGkAtoms::summary
) {
355 if (name
.IsEmpty()) {
357 // XXX: Should really be checking if caption provides a name.
358 mDoc
->FireDelayedEvent(nsIAccessibleEvent::EVENT_NAME_CHANGE
, this);
362 mDoc
->FireDelayedEvent(nsIAccessibleEvent::EVENT_OBJECT_ATTRIBUTE_CHANGED
,
364 mDoc
->QueueCacheUpdate(this, CacheDomain::Table
);
368 already_AddRefed
<AccAttributes
> HTMLTableAccessible::NativeAttributes() {
369 RefPtr
<AccAttributes
> attributes
= AccessibleWrap::NativeAttributes();
371 if (mContent
->IsMathMLElement(nsGkAtoms::mtable_
)) {
372 GetAccService()->MarkupAttributes(this, attributes
);
375 if (IsProbablyLayoutTable()) {
376 attributes
->SetAttribute(nsGkAtoms::layout_guess
, true);
379 return attributes
.forget();
382 ////////////////////////////////////////////////////////////////////////////////
383 // HTMLTableAccessible: LocalAccessible
385 Relation
HTMLTableAccessible::RelationByType(RelationType aType
) const {
386 Relation rel
= AccessibleWrap::RelationByType(aType
);
387 if (aType
== RelationType::LABELLED_BY
) {
388 rel
.AppendTarget(Caption());
394 ////////////////////////////////////////////////////////////////////////////////
395 // HTMLTableAccessible: Table
397 LocalAccessible
* HTMLTableAccessible::Caption() const {
398 LocalAccessible
* child
= mChildren
.SafeElementAt(0, nullptr);
399 // Since this is an HTML table the caption needs to be a caption
400 // element with no ARIA role (except for a reduntant role='caption').
401 // If we did a full Role() calculation here we risk getting into an infinite
402 // loop where the parent role would depend on its name which would need to be
403 // calculated by retrieving the caption (bug 1420773.)
404 return child
&& child
->NativeRole() == roles::CAPTION
&&
405 (!child
->HasStrongARIARole() ||
406 child
->IsARIARole(nsGkAtoms::caption
))
411 uint32_t HTMLTableAccessible::ColCount() const {
412 nsTableWrapperFrame
* tableFrame
= GetTableWrapperFrame();
413 return tableFrame
? tableFrame
->GetColCount() : 0;
416 uint32_t HTMLTableAccessible::RowCount() {
417 nsTableWrapperFrame
* tableFrame
= GetTableWrapperFrame();
418 return tableFrame
? tableFrame
->GetRowCount() : 0;
421 bool HTMLTableAccessible::IsProbablyLayoutTable() {
422 // Implement a heuristic to determine if table is most likely used for layout.
424 // XXX do we want to look for rowspan or colspan, especialy that span all but
425 // a couple cells at the beginning or end of a row/col, and especially when
426 // they occur at the edge of a table?
428 // XXX For now debugging descriptions are always on via SHOW_LAYOUT_HEURISTIC
429 // This will allow release trunk builds to be used by testers to refine
430 // the algorithm. Integrate it into Logging.
431 // Change to |#define SHOW_LAYOUT_HEURISTIC DEBUG| before final release
432 #ifdef SHOW_LAYOUT_HEURISTIC
433 # define RETURN_LAYOUT_ANSWER(isLayout, heuristic) \
435 mLayoutHeuristic = isLayout \
436 ? nsLiteralString(u"layout table: " heuristic) \
437 : nsLiteralString(u"data table: " heuristic); \
441 # define RETURN_LAYOUT_ANSWER(isLayout, heuristic) \
445 MOZ_ASSERT(!IsDefunct(), "Table accessible should not be defunct");
447 // Need to see all elements while document is being edited.
448 if (Document()->State() & states::EDITABLE
) {
449 RETURN_LAYOUT_ANSWER(false, "In editable document");
452 // Check to see if an ARIA role overrides the role from native markup,
453 // but for which we still expose table semantics (treegrid, for example).
455 RETURN_LAYOUT_ANSWER(false, "Has role attribute");
458 dom::Element
* el
= Elm();
459 if (el
->IsMathMLElement(nsGkAtoms::mtable_
)) {
460 RETURN_LAYOUT_ANSWER(false, "MathML matrix");
463 MOZ_ASSERT(el
->IsHTMLElement(nsGkAtoms::table
),
464 "Table should not be built by CSS display:table style");
466 // Check if datatable attribute has "0" value.
467 if (el
->AttrValueIs(kNameSpaceID_None
, nsGkAtoms::datatable
, u
"0"_ns
,
469 RETURN_LAYOUT_ANSWER(true, "Has datatable = 0 attribute, it's for layout");
472 // Check for legitimate data table attributes.
473 if (el
->Element::HasNonEmptyAttr(nsGkAtoms::summary
)) {
474 RETURN_LAYOUT_ANSWER(false, "Has summary -- legitimate table structures");
477 // Check for legitimate data table elements.
478 LocalAccessible
* caption
= LocalFirstChild();
479 if (caption
&& caption
->IsHTMLCaption() && caption
->HasChildren()) {
480 RETURN_LAYOUT_ANSWER(false,
481 "Not empty caption -- legitimate table structures");
484 for (nsIContent
* childElm
= el
->GetFirstChild(); childElm
;
485 childElm
= childElm
->GetNextSibling()) {
486 if (!childElm
->IsHTMLElement()) continue;
488 if (childElm
->IsAnyOfHTMLElements(nsGkAtoms::col
, nsGkAtoms::colgroup
,
489 nsGkAtoms::tfoot
, nsGkAtoms::thead
)) {
490 RETURN_LAYOUT_ANSWER(
492 "Has col, colgroup, tfoot or thead -- legitimate table structures");
495 if (childElm
->IsHTMLElement(nsGkAtoms::tbody
)) {
496 for (nsIContent
* rowElm
= childElm
->GetFirstChild(); rowElm
;
497 rowElm
= rowElm
->GetNextSibling()) {
498 if (rowElm
->IsHTMLElement(nsGkAtoms::tr
)) {
499 if (LocalAccessible
* row
= Document()->GetAccessible(rowElm
)) {
500 if (const nsRoleMapEntry
* roleMapEntry
= row
->ARIARoleMap()) {
501 if (roleMapEntry
->role
!= roles::ROW
) {
502 RETURN_LAYOUT_ANSWER(true, "Repurposed tr with different role");
507 for (nsIContent
* cellElm
= rowElm
->GetFirstChild(); cellElm
;
508 cellElm
= cellElm
->GetNextSibling()) {
509 if (cellElm
->IsHTMLElement()) {
510 if (cellElm
->NodeInfo()->Equals(nsGkAtoms::th
)) {
511 RETURN_LAYOUT_ANSWER(false,
512 "Has th -- legitimate table structures");
515 if (cellElm
->AsElement()->HasAttr(nsGkAtoms::headers
) ||
516 cellElm
->AsElement()->HasAttr(nsGkAtoms::scope
) ||
517 cellElm
->AsElement()->HasAttr(nsGkAtoms::abbr
)) {
518 RETURN_LAYOUT_ANSWER(false,
519 "Has headers, scope, or abbr attribute -- "
520 "legitimate table structures");
523 if (LocalAccessible
* cell
= Document()->GetAccessible(cellElm
)) {
524 if (const nsRoleMapEntry
* roleMapEntry
= cell
->ARIARoleMap()) {
525 if (roleMapEntry
->role
!= roles::CELL
&&
526 roleMapEntry
->role
!= roles::COLUMNHEADER
&&
527 roleMapEntry
->role
!= roles::ROWHEADER
&&
528 roleMapEntry
->role
!= roles::GRID_CELL
) {
529 RETURN_LAYOUT_ANSWER(true,
530 "Repurposed cell with different role");
533 if (cell
->ChildCount() == 1 &&
534 cell
->LocalFirstChild()->IsAbbreviation()) {
535 RETURN_LAYOUT_ANSWER(
536 false, "has abbr -- legitimate table structures");
546 // If only 1 column or only 1 row, it's for layout.
547 auto colCount
= ColCount();
549 RETURN_LAYOUT_ANSWER(true, "Has only 1 column");
551 auto rowCount
= RowCount();
553 RETURN_LAYOUT_ANSWER(true, "Has only 1 row");
556 // Check for many columns.
558 RETURN_LAYOUT_ANSWER(false, ">=5 columns");
561 // Now we know there are 2-4 columns and 2 or more rows. Check to see if
562 // there are visible borders on the cells.
563 // XXX currently, we just check the first cell -- do we really need to do
565 nsTableWrapperFrame
* tableFrame
= do_QueryFrame(el
->GetPrimaryFrame());
567 RETURN_LAYOUT_ANSWER(false, "table with no frame!");
570 nsIFrame
* cellFrame
= tableFrame
->GetCellFrameAt(0, 0);
572 RETURN_LAYOUT_ANSWER(false, "table's first cell has no frame!");
575 nsMargin border
= cellFrame
->StyleBorder()->GetComputedBorder();
576 if (border
.top
|| border
.bottom
|| border
.left
|| border
.right
) {
577 RETURN_LAYOUT_ANSWER(false, "Has nonzero border-width on table cell");
580 // Check for nested tables.
581 nsCOMPtr
<nsIHTMLCollection
> nestedTables
=
582 el
->GetElementsByTagName(u
"table"_ns
);
583 if (nestedTables
->Length() > 0) {
584 RETURN_LAYOUT_ANSWER(true, "Has a nested table within it");
587 // Rules for non-bordered tables with 2-4 columns and 2+ rows from here on
590 // Check for styled background color across rows (alternating background
591 // color is a common feature for data tables).
592 auto childCount
= ChildCount();
593 nscolor rowColor
= 0;
594 nscolor prevRowColor
;
595 for (auto childIdx
= 0U; childIdx
< childCount
; childIdx
++) {
596 LocalAccessible
* child
= LocalChildAt(childIdx
);
597 if (child
->IsHTMLTableRow()) {
598 prevRowColor
= rowColor
;
599 nsIFrame
* rowFrame
= child
->GetFrame();
600 MOZ_ASSERT(rowFrame
, "Table hierarchy got screwed up");
602 RETURN_LAYOUT_ANSWER(false, "Unexpected table hierarchy");
605 rowColor
= rowFrame
->StyleBackground()->BackgroundColor(rowFrame
);
607 if (childIdx
> 0 && prevRowColor
!= rowColor
) {
608 RETURN_LAYOUT_ANSWER(false,
609 "2 styles of row background color, non-bordered");
614 // Check for many rows.
615 const uint32_t kMaxLayoutRows
= 20;
616 if (rowCount
> kMaxLayoutRows
) { // A ton of rows, this is probably for data
617 RETURN_LAYOUT_ANSWER(false, ">= kMaxLayoutRows (20) and non-bordered");
620 // Check for very wide table.
621 nsIFrame
* documentFrame
= Document()->GetFrame();
622 nsSize documentSize
= documentFrame
->GetSize();
623 if (documentSize
.width
> 0) {
624 nsSize tableSize
= GetFrame()->GetSize();
625 int32_t percentageOfDocWidth
= (100 * tableSize
.width
) / documentSize
.width
;
626 if (percentageOfDocWidth
> 95) {
627 // 3-4 columns, no borders, not a lot of rows, and 95% of the doc's width
628 // Probably for layout
629 RETURN_LAYOUT_ANSWER(
630 true, "<= 4 columns, table width is 95% of document width");
635 if (rowCount
* colCount
<= 10) {
636 RETURN_LAYOUT_ANSWER(true, "2-4 columns, 10 cells or less, non-bordered");
639 static const nsLiteralString tags
[] = {u
"embed"_ns
, u
"object"_ns
,
641 for (const auto& tag
: tags
) {
642 nsCOMPtr
<nsIHTMLCollection
> descendants
= el
->GetElementsByTagName(tag
);
643 if (descendants
->Length() > 0) {
644 RETURN_LAYOUT_ANSWER(true,
645 "Has no borders, and has iframe, object or embed, "
646 "typical of advertisements");
650 RETURN_LAYOUT_ANSWER(false,
651 "No layout factor strong enough, so will guess data");
654 ////////////////////////////////////////////////////////////////////////////////
655 // HTMLTableAccessible: protected implementation
657 void HTMLTableAccessible::Description(nsString
& aDescription
) const {
658 // Helpful for debugging layout vs. data tables
659 aDescription
.Truncate();
660 LocalAccessible::Description(aDescription
);
661 if (!aDescription
.IsEmpty()) {
665 // Use summary as description if it weren't used as a name.
666 // XXX: get rid code duplication with NameInternal().
667 LocalAccessible
* caption
= Caption();
669 nsIContent
* captionContent
= caption
->GetContent();
670 if (captionContent
) {
671 nsAutoString captionText
;
672 nsTextEquivUtils::AppendTextEquivFromContent(this, captionContent
,
675 if (!captionText
.IsEmpty()) { // summary isn't used as a name.
676 mContent
->AsElement()->GetAttr(nsGkAtoms::summary
, aDescription
);
681 #ifdef SHOW_LAYOUT_HEURISTIC
682 if (aDescription
.IsEmpty()) {
683 bool isProbablyForLayout
= IsProbablyLayoutTable();
684 aDescription
= mLayoutHeuristic
;
686 printf("\nTABLE: %s\n", NS_ConvertUTF16toUTF8(mLayoutHeuristic
).get());
690 nsTableWrapperFrame
* HTMLTableAccessible::GetTableWrapperFrame() const {
691 nsTableWrapperFrame
* tableFrame
= do_QueryFrame(mContent
->GetPrimaryFrame());
692 if (tableFrame
&& tableFrame
->PrincipalChildList().FirstChild()) {
699 ////////////////////////////////////////////////////////////////////////////////
700 // HTMLCaptionAccessible
701 ////////////////////////////////////////////////////////////////////////////////
703 Relation
HTMLCaptionAccessible::RelationByType(RelationType aType
) const {
704 Relation rel
= HyperTextAccessible::RelationByType(aType
);
705 if (aType
== RelationType::LABEL_FOR
) {
706 rel
.AppendTarget(LocalParent());
712 role
HTMLCaptionAccessible::NativeRole() const { return roles::CAPTION
; }