/src/mozilla-central/accessible/html/HTMLTableAccessible.cpp
Line | Count | Source (jump to first uncovered line) |
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/. */ |
5 | | |
6 | | #include "HTMLTableAccessible.h" |
7 | | |
8 | | #include "mozilla/DebugOnly.h" |
9 | | |
10 | | #include "Accessible-inl.h" |
11 | | #include "nsAccessibilityService.h" |
12 | | #include "nsAccUtils.h" |
13 | | #include "DocAccessible.h" |
14 | | #include "nsTextEquivUtils.h" |
15 | | #include "Relation.h" |
16 | | #include "Role.h" |
17 | | #include "States.h" |
18 | | #include "TreeWalker.h" |
19 | | |
20 | | #include "mozilla/dom/HTMLTableElement.h" |
21 | | #include "nsIHTMLCollection.h" |
22 | | #include "nsIDocument.h" |
23 | | #include "nsIMutableArray.h" |
24 | | #include "nsIPersistentProperties2.h" |
25 | | #include "nsIPresShell.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" |
34 | | |
35 | | using namespace mozilla; |
36 | | using namespace mozilla::dom; |
37 | | using namespace mozilla::a11y; |
38 | | |
39 | | //////////////////////////////////////////////////////////////////////////////// |
40 | | // HTMLTableCellAccessible |
41 | | //////////////////////////////////////////////////////////////////////////////// |
42 | | |
43 | | HTMLTableCellAccessible:: |
44 | | HTMLTableCellAccessible(nsIContent* aContent, DocAccessible* aDoc) : |
45 | | HyperTextAccessibleWrap(aContent, aDoc) |
46 | 0 | { |
47 | 0 | mType = eHTMLTableCellType; |
48 | 0 | mGenericTypes |= eTableCell; |
49 | 0 | } |
50 | | |
51 | | //////////////////////////////////////////////////////////////////////////////// |
52 | | // HTMLTableCellAccessible: Accessible implementation |
53 | | |
54 | | role |
55 | | HTMLTableCellAccessible::NativeRole() const |
56 | 0 | { |
57 | 0 | if (mContent->IsMathMLElement(nsGkAtoms::mtd_)) { |
58 | 0 | return roles::MATHML_CELL; |
59 | 0 | } |
60 | 0 | return roles::CELL; |
61 | 0 | } |
62 | | |
63 | | uint64_t |
64 | | HTMLTableCellAccessible::NativeState() const |
65 | 0 | { |
66 | 0 | uint64_t state = HyperTextAccessibleWrap::NativeState(); |
67 | 0 |
|
68 | 0 | nsIFrame *frame = mContent->GetPrimaryFrame(); |
69 | 0 | NS_ASSERTION(frame, "No frame for valid cell accessible!"); |
70 | 0 |
|
71 | 0 | if (frame && frame->IsSelected()) |
72 | 0 | state |= states::SELECTED; |
73 | 0 |
|
74 | 0 | return state; |
75 | 0 | } |
76 | | |
77 | | uint64_t |
78 | | HTMLTableCellAccessible::NativeInteractiveState() const |
79 | 0 | { |
80 | 0 | return HyperTextAccessibleWrap::NativeInteractiveState() | states::SELECTABLE; |
81 | 0 | } |
82 | | |
83 | | already_AddRefed<nsIPersistentProperties> |
84 | | HTMLTableCellAccessible::NativeAttributes() |
85 | 0 | { |
86 | 0 | nsCOMPtr<nsIPersistentProperties> attributes = |
87 | 0 | HyperTextAccessibleWrap::NativeAttributes(); |
88 | 0 |
|
89 | 0 | // table-cell-index attribute |
90 | 0 | TableAccessible* table = Table(); |
91 | 0 | if (!table) |
92 | 0 | return attributes.forget(); |
93 | 0 | |
94 | 0 | int32_t rowIdx = -1, colIdx = -1; |
95 | 0 | nsresult rv = GetCellIndexes(rowIdx, colIdx); |
96 | 0 | if (NS_FAILED(rv)) |
97 | 0 | return attributes.forget(); |
98 | 0 | |
99 | 0 | nsAutoString stringIdx; |
100 | 0 | stringIdx.AppendInt(table->CellIndexAt(rowIdx, colIdx)); |
101 | 0 | nsAccUtils::SetAccAttr(attributes, nsGkAtoms::tableCellIndex, stringIdx); |
102 | 0 |
|
103 | 0 | // abbr attribute |
104 | 0 |
|
105 | 0 | // Pick up object attribute from abbr DOM element (a child of the cell) or |
106 | 0 | // from abbr DOM attribute. |
107 | 0 | nsAutoString abbrText; |
108 | 0 | if (ChildCount() == 1) { |
109 | 0 | Accessible* abbr = FirstChild(); |
110 | 0 | if (abbr->IsAbbreviation()) { |
111 | 0 | nsIContent* firstChildNode = abbr->GetContent()->GetFirstChild(); |
112 | 0 | if (firstChildNode) { |
113 | 0 | nsTextEquivUtils:: |
114 | 0 | AppendTextEquivFromTextContent(firstChildNode, &abbrText); |
115 | 0 | } |
116 | 0 | } |
117 | 0 | } |
118 | 0 | if (abbrText.IsEmpty()) |
119 | 0 | mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::abbr, abbrText); |
120 | 0 |
|
121 | 0 | if (!abbrText.IsEmpty()) |
122 | 0 | nsAccUtils::SetAccAttr(attributes, nsGkAtoms::abbr, abbrText); |
123 | 0 |
|
124 | 0 | // axis attribute |
125 | 0 | nsAutoString axisText; |
126 | 0 | mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::axis, axisText); |
127 | 0 | if (!axisText.IsEmpty()) |
128 | 0 | nsAccUtils::SetAccAttr(attributes, nsGkAtoms::axis, axisText); |
129 | 0 |
|
130 | | #ifdef DEBUG |
131 | | nsAutoString unused; |
132 | | attributes->SetStringProperty(NS_LITERAL_CSTRING("cppclass"), |
133 | | NS_LITERAL_STRING("HTMLTableCellAccessible"), |
134 | | unused); |
135 | | #endif |
136 | |
|
137 | 0 | return attributes.forget(); |
138 | 0 | } |
139 | | |
140 | | GroupPos |
141 | | HTMLTableCellAccessible::GroupPosition() |
142 | 0 | { |
143 | 0 | int32_t count = 0, index = 0; |
144 | 0 | TableAccessible* table = Table(); |
145 | 0 | if (table && nsCoreUtils::GetUIntAttr(table->AsAccessible()->GetContent(), |
146 | 0 | nsGkAtoms::aria_colcount, &count) && |
147 | 0 | nsCoreUtils::GetUIntAttr(mContent, nsGkAtoms::aria_colindex, &index)) { |
148 | 0 | return GroupPos(0, index, count); |
149 | 0 | } |
150 | 0 | |
151 | 0 | return HyperTextAccessibleWrap::GroupPosition(); |
152 | 0 | } |
153 | | |
154 | | //////////////////////////////////////////////////////////////////////////////// |
155 | | // HTMLTableCellAccessible: TableCellAccessible implementation |
156 | | |
157 | | TableAccessible* |
158 | | HTMLTableCellAccessible::Table() const |
159 | 0 | { |
160 | 0 | Accessible* parent = const_cast<HTMLTableCellAccessible*>(this); |
161 | 0 | while ((parent = parent->Parent())) { |
162 | 0 | if (parent->IsTable()) |
163 | 0 | return parent->AsTable(); |
164 | 0 | } |
165 | 0 |
|
166 | 0 | return nullptr; |
167 | 0 | } |
168 | | |
169 | | uint32_t |
170 | | HTMLTableCellAccessible::ColIdx() const |
171 | 0 | { |
172 | 0 | nsTableCellFrame* cellFrame = GetCellFrame(); |
173 | 0 | NS_ENSURE_TRUE(cellFrame, 0); |
174 | 0 | return cellFrame->ColIndex(); |
175 | 0 | } |
176 | | |
177 | | uint32_t |
178 | | HTMLTableCellAccessible::RowIdx() const |
179 | 0 | { |
180 | 0 | nsTableCellFrame* cellFrame = GetCellFrame(); |
181 | 0 | NS_ENSURE_TRUE(cellFrame, 0); |
182 | 0 | return cellFrame->RowIndex(); |
183 | 0 | } |
184 | | |
185 | | uint32_t |
186 | | HTMLTableCellAccessible::ColExtent() const |
187 | 0 | { |
188 | 0 | int32_t rowIdx = -1, colIdx = -1; |
189 | 0 | GetCellIndexes(rowIdx, colIdx); |
190 | 0 |
|
191 | 0 | TableAccessible* table = Table(); |
192 | 0 | NS_ASSERTION(table, "cell not in a table!"); |
193 | 0 | if (!table) |
194 | 0 | return 0; |
195 | 0 | |
196 | 0 | return table->ColExtentAt(rowIdx, colIdx); |
197 | 0 | } |
198 | | |
199 | | uint32_t |
200 | | HTMLTableCellAccessible::RowExtent() const |
201 | 0 | { |
202 | 0 | int32_t rowIdx = -1, colIdx = -1; |
203 | 0 | GetCellIndexes(rowIdx, colIdx); |
204 | 0 |
|
205 | 0 | TableAccessible* table = Table(); |
206 | 0 | NS_ASSERTION(table, "cell not in atable!"); |
207 | 0 | if (!table) |
208 | 0 | return 0; |
209 | 0 | |
210 | 0 | return table->RowExtentAt(rowIdx, colIdx); |
211 | 0 | } |
212 | | |
213 | | void |
214 | | HTMLTableCellAccessible::ColHeaderCells(nsTArray<Accessible*>* aCells) |
215 | 0 | { |
216 | 0 | IDRefsIterator itr(mDoc, mContent, nsGkAtoms::headers); |
217 | 0 | while (Accessible* cell = itr.Next()) { |
218 | 0 | a11y::role cellRole = cell->Role(); |
219 | 0 | if (cellRole == roles::COLUMNHEADER) { |
220 | 0 | aCells->AppendElement(cell); |
221 | 0 | } else if (cellRole != roles::ROWHEADER) { |
222 | 0 | // If referred table cell is at the same column then treat it as a column |
223 | 0 | // header. |
224 | 0 | TableCellAccessible* tableCell = cell->AsTableCell(); |
225 | 0 | if (tableCell && tableCell->ColIdx() == ColIdx()) |
226 | 0 | aCells->AppendElement(cell); |
227 | 0 | } |
228 | 0 | } |
229 | 0 |
|
230 | 0 | if (aCells->IsEmpty()) |
231 | 0 | TableCellAccessible::ColHeaderCells(aCells); |
232 | 0 | } |
233 | | |
234 | | void |
235 | | HTMLTableCellAccessible::RowHeaderCells(nsTArray<Accessible*>* aCells) |
236 | 0 | { |
237 | 0 | IDRefsIterator itr(mDoc, mContent, nsGkAtoms::headers); |
238 | 0 | while (Accessible* cell = itr.Next()) { |
239 | 0 | a11y::role cellRole = cell->Role(); |
240 | 0 | if (cellRole == roles::ROWHEADER) { |
241 | 0 | aCells->AppendElement(cell); |
242 | 0 | } else if (cellRole != roles::COLUMNHEADER) { |
243 | 0 | // If referred table cell is at the same row then treat it as a column |
244 | 0 | // header. |
245 | 0 | TableCellAccessible* tableCell = cell->AsTableCell(); |
246 | 0 | if (tableCell && tableCell->RowIdx() == RowIdx()) |
247 | 0 | aCells->AppendElement(cell); |
248 | 0 | } |
249 | 0 | } |
250 | 0 |
|
251 | 0 | if (aCells->IsEmpty()) |
252 | 0 | TableCellAccessible::RowHeaderCells(aCells); |
253 | 0 | } |
254 | | |
255 | | bool |
256 | | HTMLTableCellAccessible::Selected() |
257 | 0 | { |
258 | 0 | int32_t rowIdx = -1, colIdx = -1; |
259 | 0 | GetCellIndexes(rowIdx, colIdx); |
260 | 0 |
|
261 | 0 | TableAccessible* table = Table(); |
262 | 0 | NS_ENSURE_TRUE(table, false); |
263 | 0 |
|
264 | 0 | return table->IsCellSelected(rowIdx, colIdx); |
265 | 0 | } |
266 | | |
267 | | //////////////////////////////////////////////////////////////////////////////// |
268 | | // HTMLTableCellAccessible: protected implementation |
269 | | |
270 | | nsITableCellLayout* |
271 | | HTMLTableCellAccessible::GetCellLayout() const |
272 | 0 | { |
273 | 0 | return do_QueryFrame(mContent->GetPrimaryFrame()); |
274 | 0 | } |
275 | | |
276 | | nsTableCellFrame* |
277 | | HTMLTableCellAccessible::GetCellFrame() const |
278 | 0 | { |
279 | 0 | return do_QueryFrame(mContent->GetPrimaryFrame()); |
280 | 0 | } |
281 | | |
282 | | nsresult |
283 | | HTMLTableCellAccessible::GetCellIndexes(int32_t& aRowIdx, int32_t& aColIdx) const |
284 | 0 | { |
285 | 0 | nsITableCellLayout *cellLayout = GetCellLayout(); |
286 | 0 | NS_ENSURE_STATE(cellLayout); |
287 | 0 |
|
288 | 0 | return cellLayout->GetCellIndexes(aRowIdx, aColIdx); |
289 | 0 | } |
290 | | |
291 | | |
292 | | //////////////////////////////////////////////////////////////////////////////// |
293 | | // HTMLTableHeaderCellAccessible |
294 | | //////////////////////////////////////////////////////////////////////////////// |
295 | | |
296 | | HTMLTableHeaderCellAccessible:: |
297 | | HTMLTableHeaderCellAccessible(nsIContent* aContent, DocAccessible* aDoc) : |
298 | | HTMLTableCellAccessible(aContent, aDoc) |
299 | 0 | { |
300 | 0 | } |
301 | | |
302 | | //////////////////////////////////////////////////////////////////////////////// |
303 | | // HTMLTableHeaderCellAccessible: Accessible implementation |
304 | | |
305 | | role |
306 | | HTMLTableHeaderCellAccessible::NativeRole() const |
307 | 0 | { |
308 | 0 | // Check value of @scope attribute. |
309 | 0 | static Element::AttrValuesArray scopeValues[] = |
310 | 0 | { &nsGkAtoms::col, &nsGkAtoms::colgroup, |
311 | 0 | &nsGkAtoms::row, &nsGkAtoms::rowgroup, nullptr }; |
312 | 0 | int32_t valueIdx = |
313 | 0 | mContent->AsElement()->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::scope, |
314 | 0 | scopeValues, eCaseMatters); |
315 | 0 |
|
316 | 0 | switch (valueIdx) { |
317 | 0 | case 0: |
318 | 0 | case 1: |
319 | 0 | return roles::COLUMNHEADER; |
320 | 0 | case 2: |
321 | 0 | case 3: |
322 | 0 | return roles::ROWHEADER; |
323 | 0 | } |
324 | 0 | |
325 | 0 | TableAccessible* table = Table(); |
326 | 0 | if (!table) |
327 | 0 | return roles::NOTHING; |
328 | 0 | |
329 | 0 | // If the cell next to this one is not a header cell then assume this cell is |
330 | 0 | // a row header for it. |
331 | 0 | uint32_t rowIdx = RowIdx(), colIdx = ColIdx(); |
332 | 0 | Accessible* cell = table->CellAt(rowIdx, colIdx + ColExtent()); |
333 | 0 | if (cell && !nsCoreUtils::IsHTMLTableHeader(cell->GetContent())) |
334 | 0 | return roles::ROWHEADER; |
335 | 0 | |
336 | 0 | // If the cell below this one is not a header cell then assume this cell is |
337 | 0 | // a column header for it. |
338 | 0 | uint32_t rowExtent = RowExtent(); |
339 | 0 | cell = table->CellAt(rowIdx + rowExtent, colIdx); |
340 | 0 | if (cell && !nsCoreUtils::IsHTMLTableHeader(cell->GetContent())) |
341 | 0 | return roles::COLUMNHEADER; |
342 | 0 | |
343 | 0 | // Otherwise if this cell is surrounded by header cells only then make a guess |
344 | 0 | // based on its cell spanning. In other words if it is row spanned then assume |
345 | 0 | // it's a row header, otherwise it's a column header. |
346 | 0 | return rowExtent > 1 ? roles::ROWHEADER : roles::COLUMNHEADER; |
347 | 0 | } |
348 | | |
349 | | |
350 | | //////////////////////////////////////////////////////////////////////////////// |
351 | | // HTMLTableRowAccessible |
352 | | //////////////////////////////////////////////////////////////////////////////// |
353 | | |
354 | | role |
355 | | HTMLTableRowAccessible::NativeRole() const |
356 | 0 | { |
357 | 0 | if (mContent->IsMathMLElement(nsGkAtoms::mtr_)) { |
358 | 0 | return roles::MATHML_TABLE_ROW; |
359 | 0 | } else if (mContent->IsMathMLElement(nsGkAtoms::mlabeledtr_)) { |
360 | 0 | return roles::MATHML_LABELED_ROW; |
361 | 0 | } |
362 | 0 | return roles::ROW; |
363 | 0 | } |
364 | | |
365 | | GroupPos |
366 | | HTMLTableRowAccessible::GroupPosition() |
367 | 0 | { |
368 | 0 | int32_t count = 0, index = 0; |
369 | 0 | Accessible* table = nsAccUtils::TableFor(this); |
370 | 0 | if (table && nsCoreUtils::GetUIntAttr(table->GetContent(), |
371 | 0 | nsGkAtoms::aria_rowcount, &count) && |
372 | 0 | nsCoreUtils::GetUIntAttr(mContent, nsGkAtoms::aria_rowindex, &index)) { |
373 | 0 | return GroupPos(0, index, count); |
374 | 0 | } |
375 | 0 | |
376 | 0 | return AccessibleWrap::GroupPosition(); |
377 | 0 | } |
378 | | |
379 | | //////////////////////////////////////////////////////////////////////////////// |
380 | | // HTMLTableAccessible |
381 | | //////////////////////////////////////////////////////////////////////////////// |
382 | | |
383 | | //////////////////////////////////////////////////////////////////////////////// |
384 | | // HTMLTableAccessible: Accessible |
385 | | |
386 | | bool |
387 | | HTMLTableAccessible::InsertChildAt(uint32_t aIndex, Accessible* aChild) |
388 | 0 | { |
389 | 0 | // Move caption accessible so that it's the first child. Check for the first |
390 | 0 | // caption only, because nsAccessibilityService ensures we don't create |
391 | 0 | // accessibles for the other captions, since only the first is actually |
392 | 0 | // visible. |
393 | 0 | return Accessible::InsertChildAt(aChild->IsHTMLCaption() ? 0 : aIndex, aChild); |
394 | 0 | } |
395 | | |
396 | | role |
397 | | HTMLTableAccessible::NativeRole() const |
398 | 0 | { |
399 | 0 | if (mContent->IsMathMLElement(nsGkAtoms::mtable_)) { |
400 | 0 | return roles::MATHML_TABLE; |
401 | 0 | } |
402 | 0 | return roles::TABLE; |
403 | 0 | } |
404 | | |
405 | | uint64_t |
406 | | HTMLTableAccessible::NativeState() const |
407 | 0 | { |
408 | 0 | return Accessible::NativeState() | states::READONLY; |
409 | 0 | } |
410 | | |
411 | | ENameValueFlag |
412 | | HTMLTableAccessible::NativeName(nsString& aName) const |
413 | 0 | { |
414 | 0 | ENameValueFlag nameFlag = Accessible::NativeName(aName); |
415 | 0 | if (!aName.IsEmpty()) |
416 | 0 | return nameFlag; |
417 | 0 | |
418 | 0 | // Use table caption as a name. |
419 | 0 | Accessible* caption = Caption(); |
420 | 0 | if (caption) { |
421 | 0 | nsIContent* captionContent = caption->GetContent(); |
422 | 0 | if (captionContent) { |
423 | 0 | nsTextEquivUtils::AppendTextEquivFromContent(this, captionContent, &aName); |
424 | 0 | if (!aName.IsEmpty()) |
425 | 0 | return eNameOK; |
426 | 0 | } |
427 | 0 | } |
428 | 0 | |
429 | 0 | // If no caption then use summary as a name. |
430 | 0 | mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::summary, aName); |
431 | 0 | return eNameOK; |
432 | 0 | } |
433 | | |
434 | | already_AddRefed<nsIPersistentProperties> |
435 | | HTMLTableAccessible::NativeAttributes() |
436 | 0 | { |
437 | 0 | nsCOMPtr<nsIPersistentProperties> attributes = |
438 | 0 | AccessibleWrap::NativeAttributes(); |
439 | 0 |
|
440 | 0 | if (mContent->IsMathMLElement(nsGkAtoms::mtable_)) { |
441 | 0 | GetAccService()->MarkupAttributes(mContent, attributes); |
442 | 0 | } |
443 | 0 |
|
444 | 0 | if (IsProbablyLayoutTable()) { |
445 | 0 | nsAutoString unused; |
446 | 0 | attributes->SetStringProperty(NS_LITERAL_CSTRING("layout-guess"), |
447 | 0 | NS_LITERAL_STRING("true"), unused); |
448 | 0 | } |
449 | 0 |
|
450 | 0 | return attributes.forget(); |
451 | 0 | } |
452 | | |
453 | | //////////////////////////////////////////////////////////////////////////////// |
454 | | // HTMLTableAccessible: Accessible |
455 | | |
456 | | Relation |
457 | | HTMLTableAccessible::RelationByType(RelationType aType) const |
458 | 0 | { |
459 | 0 | Relation rel = AccessibleWrap::RelationByType(aType); |
460 | 0 | if (aType == RelationType::LABELLED_BY) |
461 | 0 | rel.AppendTarget(Caption()); |
462 | 0 |
|
463 | 0 | return rel; |
464 | 0 | } |
465 | | |
466 | | //////////////////////////////////////////////////////////////////////////////// |
467 | | // HTMLTableAccessible: Table |
468 | | |
469 | | Accessible* |
470 | | HTMLTableAccessible::Caption() const |
471 | 0 | { |
472 | 0 | Accessible* child = mChildren.SafeElementAt(0, nullptr); |
473 | 0 | return child && child->Role() == roles::CAPTION ? child : nullptr; |
474 | 0 | } |
475 | | |
476 | | void |
477 | | HTMLTableAccessible::Summary(nsString& aSummary) |
478 | 0 | { |
479 | 0 | dom::HTMLTableElement* table = dom::HTMLTableElement::FromNode(mContent); |
480 | 0 |
|
481 | 0 | if (table) |
482 | 0 | table->GetSummary(aSummary); |
483 | 0 | } |
484 | | |
485 | | uint32_t |
486 | | HTMLTableAccessible::ColCount() const |
487 | 0 | { |
488 | 0 | nsTableWrapperFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame()); |
489 | 0 | return tableFrame ? tableFrame->GetColCount() : 0; |
490 | 0 | } |
491 | | |
492 | | uint32_t |
493 | | HTMLTableAccessible::RowCount() |
494 | 0 | { |
495 | 0 | nsTableWrapperFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame()); |
496 | 0 | return tableFrame ? tableFrame->GetRowCount() : 0; |
497 | 0 | } |
498 | | |
499 | | uint32_t |
500 | | HTMLTableAccessible::SelectedCellCount() |
501 | 0 | { |
502 | 0 | nsTableWrapperFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame()); |
503 | 0 | if (!tableFrame) |
504 | 0 | return 0; |
505 | 0 | |
506 | 0 | uint32_t count = 0, rowCount = RowCount(), colCount = ColCount(); |
507 | 0 | for (uint32_t rowIdx = 0; rowIdx < rowCount; rowIdx++) { |
508 | 0 | for (uint32_t colIdx = 0; colIdx < colCount; colIdx++) { |
509 | 0 | nsTableCellFrame* cellFrame = tableFrame->GetCellFrameAt(rowIdx, colIdx); |
510 | 0 | if (!cellFrame || !cellFrame->IsSelected()) |
511 | 0 | continue; |
512 | 0 | |
513 | 0 | uint32_t startRow = cellFrame->RowIndex(); |
514 | 0 | uint32_t startCol = cellFrame->ColIndex(); |
515 | 0 | if (startRow == rowIdx && startCol == colIdx) |
516 | 0 | count++; |
517 | 0 | } |
518 | 0 | } |
519 | 0 |
|
520 | 0 | return count; |
521 | 0 | } |
522 | | |
523 | | uint32_t |
524 | | HTMLTableAccessible::SelectedColCount() |
525 | 0 | { |
526 | 0 | uint32_t count = 0, colCount = ColCount(); |
527 | 0 |
|
528 | 0 | for (uint32_t colIdx = 0; colIdx < colCount; colIdx++) |
529 | 0 | if (IsColSelected(colIdx)) |
530 | 0 | count++; |
531 | 0 |
|
532 | 0 | return count; |
533 | 0 | } |
534 | | |
535 | | uint32_t |
536 | | HTMLTableAccessible::SelectedRowCount() |
537 | 0 | { |
538 | 0 | uint32_t count = 0, rowCount = RowCount(); |
539 | 0 |
|
540 | 0 | for (uint32_t rowIdx = 0; rowIdx < rowCount; rowIdx++) |
541 | 0 | if (IsRowSelected(rowIdx)) |
542 | 0 | count++; |
543 | 0 |
|
544 | 0 | return count; |
545 | 0 | } |
546 | | |
547 | | void |
548 | | HTMLTableAccessible::SelectedCells(nsTArray<Accessible*>* aCells) |
549 | 0 | { |
550 | 0 | nsTableWrapperFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame()); |
551 | 0 | if (!tableFrame) |
552 | 0 | return; |
553 | 0 | |
554 | 0 | uint32_t rowCount = RowCount(), colCount = ColCount(); |
555 | 0 | for (uint32_t rowIdx = 0; rowIdx < rowCount; rowIdx++) { |
556 | 0 | for (uint32_t colIdx = 0; colIdx < colCount; colIdx++) { |
557 | 0 | nsTableCellFrame* cellFrame = tableFrame->GetCellFrameAt(rowIdx, colIdx); |
558 | 0 | if (!cellFrame || !cellFrame->IsSelected()) |
559 | 0 | continue; |
560 | 0 | |
561 | 0 | uint32_t startRow = cellFrame->RowIndex(); |
562 | 0 | uint32_t startCol = cellFrame->ColIndex(); |
563 | 0 | if (startRow != rowIdx || startCol != colIdx) |
564 | 0 | continue; |
565 | 0 | |
566 | 0 | Accessible* cell = mDoc->GetAccessible(cellFrame->GetContent()); |
567 | 0 | aCells->AppendElement(cell); |
568 | 0 | } |
569 | 0 | } |
570 | 0 | } |
571 | | |
572 | | void |
573 | | HTMLTableAccessible::SelectedCellIndices(nsTArray<uint32_t>* aCells) |
574 | 0 | { |
575 | 0 | nsTableWrapperFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame()); |
576 | 0 | if (!tableFrame) |
577 | 0 | return; |
578 | 0 | |
579 | 0 | uint32_t rowCount = RowCount(), colCount = ColCount(); |
580 | 0 | for (uint32_t rowIdx = 0; rowIdx < rowCount; rowIdx++) { |
581 | 0 | for (uint32_t colIdx = 0; colIdx < colCount; colIdx++) { |
582 | 0 | nsTableCellFrame* cellFrame = tableFrame->GetCellFrameAt(rowIdx, colIdx); |
583 | 0 | if (!cellFrame || !cellFrame->IsSelected()) |
584 | 0 | continue; |
585 | 0 | |
586 | 0 | uint32_t startCol = cellFrame->ColIndex(); |
587 | 0 | uint32_t startRow = cellFrame->RowIndex(); |
588 | 0 | if (startRow == rowIdx && startCol == colIdx) |
589 | 0 | aCells->AppendElement(CellIndexAt(rowIdx, colIdx)); |
590 | 0 | } |
591 | 0 | } |
592 | 0 | } |
593 | | |
594 | | void |
595 | | HTMLTableAccessible::SelectedColIndices(nsTArray<uint32_t>* aCols) |
596 | 0 | { |
597 | 0 | uint32_t colCount = ColCount(); |
598 | 0 | for (uint32_t colIdx = 0; colIdx < colCount; colIdx++) |
599 | 0 | if (IsColSelected(colIdx)) |
600 | 0 | aCols->AppendElement(colIdx); |
601 | 0 | } |
602 | | |
603 | | void |
604 | | HTMLTableAccessible::SelectedRowIndices(nsTArray<uint32_t>* aRows) |
605 | 0 | { |
606 | 0 | uint32_t rowCount = RowCount(); |
607 | 0 | for (uint32_t rowIdx = 0; rowIdx < rowCount; rowIdx++) |
608 | 0 | if (IsRowSelected(rowIdx)) |
609 | 0 | aRows->AppendElement(rowIdx); |
610 | 0 | } |
611 | | |
612 | | Accessible* |
613 | | HTMLTableAccessible::CellAt(uint32_t aRowIdx, uint32_t aColIdx) |
614 | 0 | { |
615 | 0 | nsTableWrapperFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame()); |
616 | 0 | if (!tableFrame) |
617 | 0 | return nullptr; |
618 | 0 | |
619 | 0 | nsIContent* cellContent = tableFrame->GetCellAt(aRowIdx, aColIdx); |
620 | 0 | Accessible* cell = mDoc->GetAccessible(cellContent); |
621 | 0 |
|
622 | 0 | // XXX bug 576838: crazy tables (like table6 in tables/test_table2.html) may |
623 | 0 | // return itself as a cell what makes Orca hang. |
624 | 0 | return cell == this ? nullptr : cell; |
625 | 0 | } |
626 | | |
627 | | int32_t |
628 | | HTMLTableAccessible::CellIndexAt(uint32_t aRowIdx, uint32_t aColIdx) |
629 | 0 | { |
630 | 0 | nsTableWrapperFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame()); |
631 | 0 | if (!tableFrame) |
632 | 0 | return -1; |
633 | 0 | |
634 | 0 | return tableFrame->GetIndexByRowAndColumn(aRowIdx, aColIdx); |
635 | 0 | } |
636 | | |
637 | | int32_t |
638 | | HTMLTableAccessible::ColIndexAt(uint32_t aCellIdx) |
639 | 0 | { |
640 | 0 | nsTableWrapperFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame()); |
641 | 0 | if (!tableFrame) |
642 | 0 | return -1; |
643 | 0 | |
644 | 0 | int32_t rowIdx = -1, colIdx = -1; |
645 | 0 | tableFrame->GetRowAndColumnByIndex(aCellIdx, &rowIdx, &colIdx); |
646 | 0 | return colIdx; |
647 | 0 | } |
648 | | |
649 | | int32_t |
650 | | HTMLTableAccessible::RowIndexAt(uint32_t aCellIdx) |
651 | 0 | { |
652 | 0 | nsTableWrapperFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame()); |
653 | 0 | if (!tableFrame) |
654 | 0 | return -1; |
655 | 0 | |
656 | 0 | int32_t rowIdx = -1, colIdx = -1; |
657 | 0 | tableFrame->GetRowAndColumnByIndex(aCellIdx, &rowIdx, &colIdx); |
658 | 0 | return rowIdx; |
659 | 0 | } |
660 | | |
661 | | void |
662 | | HTMLTableAccessible::RowAndColIndicesAt(uint32_t aCellIdx, int32_t* aRowIdx, |
663 | | int32_t* aColIdx) |
664 | 0 | { |
665 | 0 | nsTableWrapperFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame()); |
666 | 0 | if (tableFrame) |
667 | 0 | tableFrame->GetRowAndColumnByIndex(aCellIdx, aRowIdx, aColIdx); |
668 | 0 | } |
669 | | |
670 | | uint32_t |
671 | | HTMLTableAccessible::ColExtentAt(uint32_t aRowIdx, uint32_t aColIdx) |
672 | 0 | { |
673 | 0 | nsTableWrapperFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame()); |
674 | 0 | if (!tableFrame) |
675 | 0 | return 0; |
676 | 0 | |
677 | 0 | return tableFrame->GetEffectiveColSpanAt(aRowIdx, aColIdx); |
678 | 0 | } |
679 | | |
680 | | uint32_t |
681 | | HTMLTableAccessible::RowExtentAt(uint32_t aRowIdx, uint32_t aColIdx) |
682 | 0 | { |
683 | 0 | nsTableWrapperFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame()); |
684 | 0 | if (!tableFrame) |
685 | 0 | return 0; |
686 | 0 | |
687 | 0 | return tableFrame->GetEffectiveRowSpanAt(aRowIdx, aColIdx); |
688 | 0 | } |
689 | | |
690 | | bool |
691 | | HTMLTableAccessible::IsColSelected(uint32_t aColIdx) |
692 | 0 | { |
693 | 0 | bool isSelected = false; |
694 | 0 |
|
695 | 0 | uint32_t rowCount = RowCount(); |
696 | 0 | for (uint32_t rowIdx = 0; rowIdx < rowCount; rowIdx++) { |
697 | 0 | isSelected = IsCellSelected(rowIdx, aColIdx); |
698 | 0 | if (!isSelected) |
699 | 0 | return false; |
700 | 0 | } |
701 | 0 |
|
702 | 0 | return isSelected; |
703 | 0 | } |
704 | | |
705 | | bool |
706 | | HTMLTableAccessible::IsRowSelected(uint32_t aRowIdx) |
707 | 0 | { |
708 | 0 | bool isSelected = false; |
709 | 0 |
|
710 | 0 | uint32_t colCount = ColCount(); |
711 | 0 | for (uint32_t colIdx = 0; colIdx < colCount; colIdx++) { |
712 | 0 | isSelected = IsCellSelected(aRowIdx, colIdx); |
713 | 0 | if (!isSelected) |
714 | 0 | return false; |
715 | 0 | } |
716 | 0 |
|
717 | 0 | return isSelected; |
718 | 0 | } |
719 | | |
720 | | bool |
721 | | HTMLTableAccessible::IsCellSelected(uint32_t aRowIdx, uint32_t aColIdx) |
722 | 0 | { |
723 | 0 | nsTableWrapperFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame()); |
724 | 0 | if (!tableFrame) |
725 | 0 | return false; |
726 | 0 | |
727 | 0 | nsTableCellFrame* cellFrame = tableFrame->GetCellFrameAt(aRowIdx, aColIdx); |
728 | 0 | return cellFrame ? cellFrame->IsSelected() : false; |
729 | 0 | } |
730 | | |
731 | | void |
732 | | HTMLTableAccessible::SelectRow(uint32_t aRowIdx) |
733 | 0 | { |
734 | 0 | DebugOnly<nsresult> rv = |
735 | 0 | RemoveRowsOrColumnsFromSelection(aRowIdx, TableSelection::Row, true); |
736 | 0 | NS_ASSERTION(NS_SUCCEEDED(rv), |
737 | 0 | "RemoveRowsOrColumnsFromSelection() Shouldn't fail!"); |
738 | 0 |
|
739 | 0 | AddRowOrColumnToSelection(aRowIdx, TableSelection::Row); |
740 | 0 | } |
741 | | |
742 | | void |
743 | | HTMLTableAccessible::SelectCol(uint32_t aColIdx) |
744 | 0 | { |
745 | 0 | DebugOnly<nsresult> rv = |
746 | 0 | RemoveRowsOrColumnsFromSelection(aColIdx, TableSelection::Column, true); |
747 | 0 | NS_ASSERTION(NS_SUCCEEDED(rv), |
748 | 0 | "RemoveRowsOrColumnsFromSelection() Shouldn't fail!"); |
749 | 0 |
|
750 | 0 | AddRowOrColumnToSelection(aColIdx, TableSelection::Column); |
751 | 0 | } |
752 | | |
753 | | void |
754 | | HTMLTableAccessible::UnselectRow(uint32_t aRowIdx) |
755 | 0 | { |
756 | 0 | RemoveRowsOrColumnsFromSelection(aRowIdx, TableSelection::Row, false); |
757 | 0 | } |
758 | | |
759 | | void |
760 | | HTMLTableAccessible::UnselectCol(uint32_t aColIdx) |
761 | 0 | { |
762 | 0 | RemoveRowsOrColumnsFromSelection(aColIdx, TableSelection::Column, false); |
763 | 0 | } |
764 | | |
765 | | nsresult |
766 | | HTMLTableAccessible::AddRowOrColumnToSelection(int32_t aIndex, TableSelection aTarget) |
767 | 0 | { |
768 | 0 | bool doSelectRow = (aTarget == TableSelection::Row); |
769 | 0 |
|
770 | 0 | nsTableWrapperFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame()); |
771 | 0 | if (!tableFrame) |
772 | 0 | return NS_OK; |
773 | 0 | |
774 | 0 | uint32_t count = 0; |
775 | 0 | if (doSelectRow) |
776 | 0 | count = ColCount(); |
777 | 0 | else |
778 | 0 | count = RowCount(); |
779 | 0 |
|
780 | 0 | nsIPresShell* presShell(mDoc->PresShell()); |
781 | 0 | RefPtr<nsFrameSelection> tableSelection = |
782 | 0 | const_cast<nsFrameSelection*>(presShell->ConstFrameSelection()); |
783 | 0 |
|
784 | 0 | for (uint32_t idx = 0; idx < count; idx++) { |
785 | 0 | int32_t rowIdx = doSelectRow ? aIndex : idx; |
786 | 0 | int32_t colIdx = doSelectRow ? idx : aIndex; |
787 | 0 | nsTableCellFrame* cellFrame = tableFrame->GetCellFrameAt(rowIdx, colIdx); |
788 | 0 | if (cellFrame && !cellFrame->IsSelected()) { |
789 | 0 | nsresult rv = tableSelection->SelectCellElement(cellFrame->GetContent()); |
790 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
791 | 0 | } |
792 | 0 | } |
793 | 0 |
|
794 | 0 | return NS_OK; |
795 | 0 | } |
796 | | |
797 | | nsresult |
798 | | HTMLTableAccessible::RemoveRowsOrColumnsFromSelection(int32_t aIndex, |
799 | | TableSelection aTarget, |
800 | | bool aIsOuter) |
801 | 0 | { |
802 | 0 | nsTableWrapperFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame()); |
803 | 0 | if (!tableFrame) |
804 | 0 | return NS_OK; |
805 | 0 | |
806 | 0 | nsIPresShell* presShell(mDoc->PresShell()); |
807 | 0 | RefPtr<nsFrameSelection> tableSelection = |
808 | 0 | const_cast<nsFrameSelection*>(presShell->ConstFrameSelection()); |
809 | 0 |
|
810 | 0 | bool doUnselectRow = (aTarget == TableSelection::Row); |
811 | 0 | uint32_t count = doUnselectRow ? ColCount() : RowCount(); |
812 | 0 |
|
813 | 0 | int32_t startRowIdx = doUnselectRow ? aIndex : 0; |
814 | 0 | int32_t endRowIdx = doUnselectRow ? aIndex : count - 1; |
815 | 0 | int32_t startColIdx = doUnselectRow ? 0 : aIndex; |
816 | 0 | int32_t endColIdx = doUnselectRow ? count - 1 : aIndex; |
817 | 0 |
|
818 | 0 | if (aIsOuter) |
819 | 0 | return tableSelection->RestrictCellsToSelection(mContent, |
820 | 0 | startRowIdx, startColIdx, |
821 | 0 | endRowIdx, endColIdx); |
822 | 0 | |
823 | 0 | return tableSelection->RemoveCellsFromSelection(mContent, |
824 | 0 | startRowIdx, startColIdx, |
825 | 0 | endRowIdx, endColIdx); |
826 | 0 | } |
827 | | |
828 | | void |
829 | | HTMLTableAccessible::Description(nsString& aDescription) |
830 | 0 | { |
831 | 0 | // Helpful for debugging layout vs. data tables |
832 | 0 | aDescription.Truncate(); |
833 | 0 | Accessible::Description(aDescription); |
834 | 0 | if (!aDescription.IsEmpty()) |
835 | 0 | return; |
836 | 0 | |
837 | 0 | // Use summary as description if it weren't used as a name. |
838 | 0 | // XXX: get rid code duplication with NameInternal(). |
839 | 0 | Accessible* caption = Caption(); |
840 | 0 | if (caption) { |
841 | 0 | nsIContent* captionContent = caption->GetContent(); |
842 | 0 | if (captionContent) { |
843 | 0 | nsAutoString captionText; |
844 | 0 | nsTextEquivUtils::AppendTextEquivFromContent(this, captionContent, |
845 | 0 | &captionText); |
846 | 0 |
|
847 | 0 | if (!captionText.IsEmpty()) { // summary isn't used as a name. |
848 | 0 | mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::summary, |
849 | 0 | aDescription); |
850 | 0 | } |
851 | 0 | } |
852 | 0 | } |
853 | 0 |
|
854 | | #ifdef SHOW_LAYOUT_HEURISTIC |
855 | | if (aDescription.IsEmpty()) { |
856 | | bool isProbablyForLayout = IsProbablyLayoutTable(); |
857 | | aDescription = mLayoutHeuristic; |
858 | | } |
859 | | printf("\nTABLE: %s\n", NS_ConvertUTF16toUTF8(mLayoutHeuristic).get()); |
860 | | #endif |
861 | | } |
862 | | |
863 | | //////////////////////////////////////////////////////////////////////////////// |
864 | | // HTMLCaptionAccessible |
865 | | //////////////////////////////////////////////////////////////////////////////// |
866 | | |
867 | | Relation |
868 | | HTMLCaptionAccessible::RelationByType(RelationType aType) const |
869 | 0 | { |
870 | 0 | Relation rel = HyperTextAccessible::RelationByType(aType); |
871 | 0 | if (aType == RelationType::LABEL_FOR) |
872 | 0 | rel.AppendTarget(Parent()); |
873 | 0 |
|
874 | 0 | return rel; |
875 | 0 | } |
876 | | |
877 | | role |
878 | | HTMLCaptionAccessible::NativeRole() const |
879 | 0 | { |
880 | 0 | return roles::CAPTION; |
881 | 0 | } |