|
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 "nsIAccessibleRelation.h" |
|
15 #include "nsTextEquivUtils.h" |
|
16 #include "Relation.h" |
|
17 #include "Role.h" |
|
18 #include "States.h" |
|
19 #include "TreeWalker.h" |
|
20 |
|
21 #include "mozilla/dom/HTMLTableElement.h" |
|
22 #include "nsIDOMElement.h" |
|
23 #include "nsIDOMRange.h" |
|
24 #include "nsISelectionPrivate.h" |
|
25 #include "nsIDOMNodeList.h" |
|
26 #include "nsIDOMHTMLCollection.h" |
|
27 #include "nsIDocument.h" |
|
28 #include "nsIMutableArray.h" |
|
29 #include "nsIPersistentProperties2.h" |
|
30 #include "nsIPresShell.h" |
|
31 #include "nsITableCellLayout.h" |
|
32 #include "nsFrameSelection.h" |
|
33 #include "nsError.h" |
|
34 #include "nsArrayUtils.h" |
|
35 #include "nsComponentManagerUtils.h" |
|
36 #include "nsNameSpaceManager.h" |
|
37 #include "nsTableCellFrame.h" |
|
38 #include "nsTableOuterFrame.h" |
|
39 |
|
40 using namespace mozilla; |
|
41 using namespace mozilla::a11y; |
|
42 |
|
43 //////////////////////////////////////////////////////////////////////////////// |
|
44 // HTMLTableCellAccessible |
|
45 //////////////////////////////////////////////////////////////////////////////// |
|
46 |
|
47 HTMLTableCellAccessible:: |
|
48 HTMLTableCellAccessible(nsIContent* aContent, DocAccessible* aDoc) : |
|
49 HyperTextAccessibleWrap(aContent, aDoc), xpcAccessibleTableCell(this) |
|
50 { |
|
51 mGenericTypes |= eTableCell; |
|
52 } |
|
53 |
|
54 //////////////////////////////////////////////////////////////////////////////// |
|
55 // HTMLTableCellAccessible: nsISupports implementation |
|
56 |
|
57 NS_IMPL_ISUPPORTS_INHERITED(HTMLTableCellAccessible, |
|
58 HyperTextAccessible, |
|
59 nsIAccessibleTableCell) |
|
60 |
|
61 //////////////////////////////////////////////////////////////////////////////// |
|
62 // HTMLTableCellAccessible: Accessible implementation |
|
63 |
|
64 void |
|
65 HTMLTableCellAccessible::Shutdown() |
|
66 { |
|
67 mTableCell = nullptr; |
|
68 HyperTextAccessibleWrap::Shutdown(); |
|
69 } |
|
70 |
|
71 role |
|
72 HTMLTableCellAccessible::NativeRole() |
|
73 { |
|
74 return roles::CELL; |
|
75 } |
|
76 |
|
77 uint64_t |
|
78 HTMLTableCellAccessible::NativeState() |
|
79 { |
|
80 uint64_t state = HyperTextAccessibleWrap::NativeState(); |
|
81 |
|
82 nsIFrame *frame = mContent->GetPrimaryFrame(); |
|
83 NS_ASSERTION(frame, "No frame for valid cell accessible!"); |
|
84 |
|
85 if (frame && frame->IsSelected()) |
|
86 state |= states::SELECTED; |
|
87 |
|
88 return state; |
|
89 } |
|
90 |
|
91 uint64_t |
|
92 HTMLTableCellAccessible::NativeInteractiveState() const |
|
93 { |
|
94 return HyperTextAccessibleWrap::NativeInteractiveState() | states::SELECTABLE; |
|
95 } |
|
96 |
|
97 already_AddRefed<nsIPersistentProperties> |
|
98 HTMLTableCellAccessible::NativeAttributes() |
|
99 { |
|
100 nsCOMPtr<nsIPersistentProperties> attributes = |
|
101 HyperTextAccessibleWrap::NativeAttributes(); |
|
102 |
|
103 // table-cell-index attribute |
|
104 TableAccessible* table = Table(); |
|
105 if (!table) |
|
106 return attributes.forget(); |
|
107 |
|
108 int32_t rowIdx = -1, colIdx = -1; |
|
109 nsresult rv = GetCellIndexes(rowIdx, colIdx); |
|
110 if (NS_FAILED(rv)) |
|
111 return attributes.forget(); |
|
112 |
|
113 nsAutoString stringIdx; |
|
114 stringIdx.AppendInt(table->CellIndexAt(rowIdx, colIdx)); |
|
115 nsAccUtils::SetAccAttr(attributes, nsGkAtoms::tableCellIndex, stringIdx); |
|
116 |
|
117 // abbr attribute |
|
118 |
|
119 // Pick up object attribute from abbr DOM element (a child of the cell) or |
|
120 // from abbr DOM attribute. |
|
121 nsAutoString abbrText; |
|
122 if (ChildCount() == 1) { |
|
123 Accessible* abbr = FirstChild(); |
|
124 if (abbr->IsAbbreviation()) { |
|
125 nsIContent* firstChildNode = abbr->GetContent()->GetFirstChild(); |
|
126 if (firstChildNode) { |
|
127 nsTextEquivUtils:: |
|
128 AppendTextEquivFromTextContent(firstChildNode, &abbrText); |
|
129 } |
|
130 } |
|
131 } |
|
132 if (abbrText.IsEmpty()) |
|
133 mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::abbr, abbrText); |
|
134 |
|
135 if (!abbrText.IsEmpty()) |
|
136 nsAccUtils::SetAccAttr(attributes, nsGkAtoms::abbr, abbrText); |
|
137 |
|
138 // axis attribute |
|
139 nsAutoString axisText; |
|
140 mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::axis, axisText); |
|
141 if (!axisText.IsEmpty()) |
|
142 nsAccUtils::SetAccAttr(attributes, nsGkAtoms::axis, axisText); |
|
143 |
|
144 return attributes.forget(); |
|
145 } |
|
146 |
|
147 //////////////////////////////////////////////////////////////////////////////// |
|
148 // HTMLTableCellAccessible: nsIAccessibleTableCell implementation |
|
149 |
|
150 TableAccessible* |
|
151 HTMLTableCellAccessible::Table() const |
|
152 { |
|
153 Accessible* parent = const_cast<HTMLTableCellAccessible*>(this); |
|
154 while ((parent = parent->Parent())) { |
|
155 roles::Role role = parent->Role(); |
|
156 if (role == roles::TABLE || role == roles::TREE_TABLE) |
|
157 return parent->AsTable(); |
|
158 } |
|
159 |
|
160 return nullptr; |
|
161 } |
|
162 |
|
163 uint32_t |
|
164 HTMLTableCellAccessible::ColIdx() const |
|
165 { |
|
166 nsITableCellLayout* cellLayout = GetCellLayout(); |
|
167 NS_ENSURE_TRUE(cellLayout, 0); |
|
168 |
|
169 int32_t colIdx = 0; |
|
170 cellLayout->GetColIndex(colIdx); |
|
171 return colIdx > 0 ? static_cast<uint32_t>(colIdx) : 0; |
|
172 } |
|
173 |
|
174 uint32_t |
|
175 HTMLTableCellAccessible::RowIdx() const |
|
176 { |
|
177 nsITableCellLayout* cellLayout = GetCellLayout(); |
|
178 NS_ENSURE_TRUE(cellLayout, 0); |
|
179 |
|
180 int32_t rowIdx = 0; |
|
181 cellLayout->GetRowIndex(rowIdx); |
|
182 return rowIdx > 0 ? static_cast<uint32_t>(rowIdx) : 0; |
|
183 } |
|
184 |
|
185 uint32_t |
|
186 HTMLTableCellAccessible::ColExtent() const |
|
187 { |
|
188 int32_t rowIdx = -1, colIdx = -1; |
|
189 GetCellIndexes(rowIdx, colIdx); |
|
190 |
|
191 TableAccessible* table = Table(); |
|
192 NS_ASSERTION(table, "cell not in a table!"); |
|
193 if (!table) |
|
194 return 0; |
|
195 |
|
196 return table->ColExtentAt(rowIdx, colIdx); |
|
197 } |
|
198 |
|
199 uint32_t |
|
200 HTMLTableCellAccessible::RowExtent() const |
|
201 { |
|
202 int32_t rowIdx = -1, colIdx = -1; |
|
203 GetCellIndexes(rowIdx, colIdx); |
|
204 |
|
205 TableAccessible* table = Table(); |
|
206 NS_ASSERTION(table, "cell not in atable!"); |
|
207 if (!table) |
|
208 return 0; |
|
209 |
|
210 return table->RowExtentAt(rowIdx, colIdx); |
|
211 } |
|
212 |
|
213 void |
|
214 HTMLTableCellAccessible::ColHeaderCells(nsTArray<Accessible*>* aCells) |
|
215 { |
|
216 IDRefsIterator itr(mDoc, mContent, nsGkAtoms::headers); |
|
217 while (Accessible* cell = itr.Next()) { |
|
218 a11y::role cellRole = cell->Role(); |
|
219 if (cellRole == roles::COLUMNHEADER) { |
|
220 aCells->AppendElement(cell); |
|
221 } else if (cellRole != roles::ROWHEADER) { |
|
222 // If referred table cell is at the same column then treat it as a column |
|
223 // header. |
|
224 TableCellAccessible* tableCell = cell->AsTableCell(); |
|
225 if (tableCell && tableCell->ColIdx() == ColIdx()) |
|
226 aCells->AppendElement(cell); |
|
227 } |
|
228 } |
|
229 |
|
230 if (aCells->IsEmpty()) |
|
231 TableCellAccessible::ColHeaderCells(aCells); |
|
232 } |
|
233 |
|
234 void |
|
235 HTMLTableCellAccessible::RowHeaderCells(nsTArray<Accessible*>* aCells) |
|
236 { |
|
237 IDRefsIterator itr(mDoc, mContent, nsGkAtoms::headers); |
|
238 while (Accessible* cell = itr.Next()) { |
|
239 a11y::role cellRole = cell->Role(); |
|
240 if (cellRole == roles::ROWHEADER) { |
|
241 aCells->AppendElement(cell); |
|
242 } else if (cellRole != roles::COLUMNHEADER) { |
|
243 // If referred table cell is at the same row then treat it as a column |
|
244 // header. |
|
245 TableCellAccessible* tableCell = cell->AsTableCell(); |
|
246 if (tableCell && tableCell->RowIdx() == RowIdx()) |
|
247 aCells->AppendElement(cell); |
|
248 } |
|
249 } |
|
250 |
|
251 if (aCells->IsEmpty()) |
|
252 TableCellAccessible::RowHeaderCells(aCells); |
|
253 } |
|
254 |
|
255 bool |
|
256 HTMLTableCellAccessible::Selected() |
|
257 { |
|
258 int32_t rowIdx = -1, colIdx = -1; |
|
259 GetCellIndexes(rowIdx, colIdx); |
|
260 |
|
261 TableAccessible* table = Table(); |
|
262 NS_ENSURE_TRUE(table, false); |
|
263 |
|
264 return table->IsCellSelected(rowIdx, colIdx); |
|
265 } |
|
266 |
|
267 //////////////////////////////////////////////////////////////////////////////// |
|
268 // HTMLTableCellAccessible: protected implementation |
|
269 |
|
270 nsITableCellLayout* |
|
271 HTMLTableCellAccessible::GetCellLayout() const |
|
272 { |
|
273 return do_QueryFrame(mContent->GetPrimaryFrame()); |
|
274 } |
|
275 |
|
276 nsresult |
|
277 HTMLTableCellAccessible::GetCellIndexes(int32_t& aRowIdx, int32_t& aColIdx) const |
|
278 { |
|
279 nsITableCellLayout *cellLayout = GetCellLayout(); |
|
280 NS_ENSURE_STATE(cellLayout); |
|
281 |
|
282 return cellLayout->GetCellIndexes(aRowIdx, aColIdx); |
|
283 } |
|
284 |
|
285 |
|
286 //////////////////////////////////////////////////////////////////////////////// |
|
287 // HTMLTableHeaderCellAccessible |
|
288 //////////////////////////////////////////////////////////////////////////////// |
|
289 |
|
290 HTMLTableHeaderCellAccessible:: |
|
291 HTMLTableHeaderCellAccessible(nsIContent* aContent, DocAccessible* aDoc) : |
|
292 HTMLTableCellAccessible(aContent, aDoc) |
|
293 { |
|
294 } |
|
295 |
|
296 //////////////////////////////////////////////////////////////////////////////// |
|
297 // HTMLTableHeaderCellAccessible: Accessible implementation |
|
298 |
|
299 role |
|
300 HTMLTableHeaderCellAccessible::NativeRole() |
|
301 { |
|
302 // Check value of @scope attribute. |
|
303 static nsIContent::AttrValuesArray scopeValues[] = |
|
304 {&nsGkAtoms::col, &nsGkAtoms::row, nullptr}; |
|
305 int32_t valueIdx = |
|
306 mContent->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::scope, |
|
307 scopeValues, eCaseMatters); |
|
308 |
|
309 switch (valueIdx) { |
|
310 case 0: |
|
311 return roles::COLUMNHEADER; |
|
312 case 1: |
|
313 return roles::ROWHEADER; |
|
314 } |
|
315 |
|
316 // Assume it's columnheader if there are headers in siblings, otherwise |
|
317 // rowheader. |
|
318 // This should iterate the flattened tree |
|
319 nsIContent* parentContent = mContent->GetParent(); |
|
320 if (!parentContent) { |
|
321 NS_ERROR("Deattached content on alive accessible?"); |
|
322 return roles::NOTHING; |
|
323 } |
|
324 |
|
325 for (nsIContent* siblingContent = mContent->GetPreviousSibling(); siblingContent; |
|
326 siblingContent = siblingContent->GetPreviousSibling()) { |
|
327 if (siblingContent->IsElement()) { |
|
328 return nsCoreUtils::IsHTMLTableHeader(siblingContent) ? |
|
329 roles::COLUMNHEADER : roles::ROWHEADER; |
|
330 } |
|
331 } |
|
332 |
|
333 for (nsIContent* siblingContent = mContent->GetNextSibling(); siblingContent; |
|
334 siblingContent = siblingContent->GetNextSibling()) { |
|
335 if (siblingContent->IsElement()) { |
|
336 return nsCoreUtils::IsHTMLTableHeader(siblingContent) ? |
|
337 roles::COLUMNHEADER : roles::ROWHEADER; |
|
338 } |
|
339 } |
|
340 |
|
341 // No elements in siblings what means the table has one column only. Therefore |
|
342 // it should be column header. |
|
343 return roles::COLUMNHEADER; |
|
344 } |
|
345 |
|
346 |
|
347 //////////////////////////////////////////////////////////////////////////////// |
|
348 // HTMLTableRowAccessible |
|
349 //////////////////////////////////////////////////////////////////////////////// |
|
350 |
|
351 NS_IMPL_ISUPPORTS_INHERITED0(HTMLTableRowAccessible, Accessible) |
|
352 |
|
353 role |
|
354 HTMLTableRowAccessible::NativeRole() |
|
355 { |
|
356 return roles::ROW; |
|
357 } |
|
358 |
|
359 //////////////////////////////////////////////////////////////////////////////// |
|
360 // HTMLTableAccessible |
|
361 //////////////////////////////////////////////////////////////////////////////// |
|
362 |
|
363 NS_IMPL_ISUPPORTS_INHERITED(HTMLTableAccessible, Accessible, |
|
364 nsIAccessibleTable) |
|
365 |
|
366 //////////////////////////////////////////////////////////////////////////////// |
|
367 // HTMLTableAccessible: Accessible |
|
368 |
|
369 void |
|
370 HTMLTableAccessible::Shutdown() |
|
371 { |
|
372 mTable = nullptr; |
|
373 AccessibleWrap::Shutdown(); |
|
374 } |
|
375 |
|
376 void |
|
377 HTMLTableAccessible::CacheChildren() |
|
378 { |
|
379 // Move caption accessible so that it's the first child. Check for the first |
|
380 // caption only, because nsAccessibilityService ensures we don't create |
|
381 // accessibles for the other captions, since only the first is actually |
|
382 // visible. |
|
383 TreeWalker walker(this, mContent); |
|
384 |
|
385 Accessible* child = nullptr; |
|
386 while ((child = walker.NextChild())) { |
|
387 if (child->Role() == roles::CAPTION) { |
|
388 InsertChildAt(0, child); |
|
389 while ((child = walker.NextChild()) && AppendChild(child)); |
|
390 break; |
|
391 } |
|
392 AppendChild(child); |
|
393 } |
|
394 } |
|
395 |
|
396 role |
|
397 HTMLTableAccessible::NativeRole() |
|
398 { |
|
399 return roles::TABLE; |
|
400 } |
|
401 |
|
402 uint64_t |
|
403 HTMLTableAccessible::NativeState() |
|
404 { |
|
405 return Accessible::NativeState() | states::READONLY; |
|
406 } |
|
407 |
|
408 ENameValueFlag |
|
409 HTMLTableAccessible::NativeName(nsString& aName) |
|
410 { |
|
411 ENameValueFlag nameFlag = Accessible::NativeName(aName); |
|
412 if (!aName.IsEmpty()) |
|
413 return nameFlag; |
|
414 |
|
415 // Use table caption as a name. |
|
416 Accessible* caption = Caption(); |
|
417 if (caption) { |
|
418 nsIContent* captionContent = caption->GetContent(); |
|
419 if (captionContent) { |
|
420 nsTextEquivUtils::AppendTextEquivFromContent(this, captionContent, &aName); |
|
421 if (!aName.IsEmpty()) |
|
422 return eNameOK; |
|
423 } |
|
424 } |
|
425 |
|
426 // If no caption then use summary as a name. |
|
427 mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::summary, aName); |
|
428 return eNameOK; |
|
429 } |
|
430 |
|
431 already_AddRefed<nsIPersistentProperties> |
|
432 HTMLTableAccessible::NativeAttributes() |
|
433 { |
|
434 nsCOMPtr<nsIPersistentProperties> attributes = |
|
435 AccessibleWrap::NativeAttributes(); |
|
436 if (IsProbablyLayoutTable()) { |
|
437 nsAutoString unused; |
|
438 attributes->SetStringProperty(NS_LITERAL_CSTRING("layout-guess"), |
|
439 NS_LITERAL_STRING("true"), unused); |
|
440 } |
|
441 |
|
442 return attributes.forget(); |
|
443 } |
|
444 |
|
445 //////////////////////////////////////////////////////////////////////////////// |
|
446 // HTMLTableAccessible: nsIAccessible implementation |
|
447 |
|
448 Relation |
|
449 HTMLTableAccessible::RelationByType(RelationType aType) |
|
450 { |
|
451 Relation rel = AccessibleWrap::RelationByType(aType); |
|
452 if (aType == RelationType::LABELLED_BY) |
|
453 rel.AppendTarget(Caption()); |
|
454 |
|
455 return rel; |
|
456 } |
|
457 |
|
458 //////////////////////////////////////////////////////////////////////////////// |
|
459 // HTMLTableAccessible: nsIAccessibleTable implementation |
|
460 |
|
461 Accessible* |
|
462 HTMLTableAccessible::Caption() |
|
463 { |
|
464 Accessible* child = mChildren.SafeElementAt(0, nullptr); |
|
465 return child && child->Role() == roles::CAPTION ? child : nullptr; |
|
466 } |
|
467 |
|
468 void |
|
469 HTMLTableAccessible::Summary(nsString& aSummary) |
|
470 { |
|
471 dom::HTMLTableElement* table = dom::HTMLTableElement::FromContent(mContent); |
|
472 |
|
473 if (table) |
|
474 table->GetSummary(aSummary); |
|
475 } |
|
476 |
|
477 uint32_t |
|
478 HTMLTableAccessible::ColCount() |
|
479 { |
|
480 nsTableOuterFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame()); |
|
481 return tableFrame ? tableFrame->GetColCount() : 0; |
|
482 } |
|
483 |
|
484 uint32_t |
|
485 HTMLTableAccessible::RowCount() |
|
486 { |
|
487 nsTableOuterFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame()); |
|
488 return tableFrame ? tableFrame->GetRowCount() : 0; |
|
489 } |
|
490 |
|
491 uint32_t |
|
492 HTMLTableAccessible::SelectedCellCount() |
|
493 { |
|
494 nsTableOuterFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame()); |
|
495 if (!tableFrame) |
|
496 return 0; |
|
497 |
|
498 uint32_t count = 0, rowCount = RowCount(), colCount = ColCount(); |
|
499 for (uint32_t rowIdx = 0; rowIdx < rowCount; rowIdx++) { |
|
500 for (uint32_t colIdx = 0; colIdx < colCount; colIdx++) { |
|
501 nsTableCellFrame* cellFrame = tableFrame->GetCellFrameAt(rowIdx, colIdx); |
|
502 if (!cellFrame || !cellFrame->IsSelected()) |
|
503 continue; |
|
504 |
|
505 int32_t startRow = -1, startCol = -1; |
|
506 cellFrame->GetRowIndex(startRow); |
|
507 cellFrame->GetColIndex(startCol); |
|
508 if (startRow >= 0 && (uint32_t)startRow == rowIdx && |
|
509 startCol >= 0 && (uint32_t)startCol == colIdx) |
|
510 count++; |
|
511 } |
|
512 } |
|
513 |
|
514 return count; |
|
515 } |
|
516 |
|
517 uint32_t |
|
518 HTMLTableAccessible::SelectedColCount() |
|
519 { |
|
520 uint32_t count = 0, colCount = ColCount(); |
|
521 |
|
522 for (uint32_t colIdx = 0; colIdx < colCount; colIdx++) |
|
523 if (IsColSelected(colIdx)) |
|
524 count++; |
|
525 |
|
526 return count; |
|
527 } |
|
528 |
|
529 uint32_t |
|
530 HTMLTableAccessible::SelectedRowCount() |
|
531 { |
|
532 uint32_t count = 0, rowCount = RowCount(); |
|
533 |
|
534 for (uint32_t rowIdx = 0; rowIdx < rowCount; rowIdx++) |
|
535 if (IsRowSelected(rowIdx)) |
|
536 count++; |
|
537 |
|
538 return count; |
|
539 } |
|
540 |
|
541 void |
|
542 HTMLTableAccessible::SelectedCells(nsTArray<Accessible*>* aCells) |
|
543 { |
|
544 nsTableOuterFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame()); |
|
545 if (!tableFrame) |
|
546 return; |
|
547 |
|
548 uint32_t rowCount = RowCount(), colCount = ColCount(); |
|
549 for (uint32_t rowIdx = 0; rowIdx < rowCount; rowIdx++) { |
|
550 for (uint32_t colIdx = 0; colIdx < colCount; colIdx++) { |
|
551 nsTableCellFrame* cellFrame = tableFrame->GetCellFrameAt(rowIdx, colIdx); |
|
552 if (!cellFrame || !cellFrame->IsSelected()) |
|
553 continue; |
|
554 |
|
555 int32_t startCol = -1, startRow = -1; |
|
556 cellFrame->GetRowIndex(startRow); |
|
557 cellFrame->GetColIndex(startCol); |
|
558 if ((startRow >= 0 && (uint32_t)startRow != rowIdx) || |
|
559 (startCol >= 0 && (uint32_t)startCol != colIdx)) |
|
560 continue; |
|
561 |
|
562 Accessible* cell = mDoc->GetAccessible(cellFrame->GetContent()); |
|
563 aCells->AppendElement(cell); |
|
564 } |
|
565 } |
|
566 } |
|
567 |
|
568 void |
|
569 HTMLTableAccessible::SelectedCellIndices(nsTArray<uint32_t>* aCells) |
|
570 { |
|
571 nsTableOuterFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame()); |
|
572 if (!tableFrame) |
|
573 return; |
|
574 |
|
575 uint32_t rowCount = RowCount(), colCount = ColCount(); |
|
576 for (uint32_t rowIdx = 0; rowIdx < rowCount; rowIdx++) { |
|
577 for (uint32_t colIdx = 0; colIdx < colCount; colIdx++) { |
|
578 nsTableCellFrame* cellFrame = tableFrame->GetCellFrameAt(rowIdx, colIdx); |
|
579 if (!cellFrame || !cellFrame->IsSelected()) |
|
580 continue; |
|
581 |
|
582 int32_t startRow = -1, startCol = -1; |
|
583 cellFrame->GetColIndex(startCol); |
|
584 cellFrame->GetRowIndex(startRow); |
|
585 if (startRow >= 0 && (uint32_t)startRow == rowIdx && |
|
586 startCol >= 0 && (uint32_t)startCol == colIdx) |
|
587 aCells->AppendElement(CellIndexAt(rowIdx, colIdx)); |
|
588 } |
|
589 } |
|
590 } |
|
591 |
|
592 void |
|
593 HTMLTableAccessible::SelectedColIndices(nsTArray<uint32_t>* aCols) |
|
594 { |
|
595 uint32_t colCount = ColCount(); |
|
596 for (uint32_t colIdx = 0; colIdx < colCount; colIdx++) |
|
597 if (IsColSelected(colIdx)) |
|
598 aCols->AppendElement(colIdx); |
|
599 } |
|
600 |
|
601 void |
|
602 HTMLTableAccessible::SelectedRowIndices(nsTArray<uint32_t>* aRows) |
|
603 { |
|
604 uint32_t rowCount = RowCount(); |
|
605 for (uint32_t rowIdx = 0; rowIdx < rowCount; rowIdx++) |
|
606 if (IsRowSelected(rowIdx)) |
|
607 aRows->AppendElement(rowIdx); |
|
608 } |
|
609 |
|
610 Accessible* |
|
611 HTMLTableAccessible::CellAt(uint32_t aRowIdx, uint32_t aColIdx) |
|
612 { |
|
613 nsTableOuterFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame()); |
|
614 if (!tableFrame) |
|
615 return nullptr; |
|
616 |
|
617 nsIContent* cellContent = tableFrame->GetCellAt(aRowIdx, aColIdx); |
|
618 Accessible* cell = mDoc->GetAccessible(cellContent); |
|
619 |
|
620 // XXX bug 576838: crazy tables (like table6 in tables/test_table2.html) may |
|
621 // return itself as a cell what makes Orca hang. |
|
622 return cell == this ? nullptr : cell; |
|
623 } |
|
624 |
|
625 int32_t |
|
626 HTMLTableAccessible::CellIndexAt(uint32_t aRowIdx, uint32_t aColIdx) |
|
627 { |
|
628 nsTableOuterFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame()); |
|
629 if (!tableFrame) |
|
630 return -1; |
|
631 |
|
632 return tableFrame->GetIndexByRowAndColumn(aRowIdx, aColIdx); |
|
633 } |
|
634 |
|
635 int32_t |
|
636 HTMLTableAccessible::ColIndexAt(uint32_t aCellIdx) |
|
637 { |
|
638 nsTableOuterFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame()); |
|
639 if (!tableFrame) |
|
640 return -1; |
|
641 |
|
642 int32_t rowIdx = -1, colIdx = -1; |
|
643 tableFrame->GetRowAndColumnByIndex(aCellIdx, &rowIdx, &colIdx); |
|
644 return colIdx; |
|
645 } |
|
646 |
|
647 int32_t |
|
648 HTMLTableAccessible::RowIndexAt(uint32_t aCellIdx) |
|
649 { |
|
650 nsTableOuterFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame()); |
|
651 if (!tableFrame) |
|
652 return -1; |
|
653 |
|
654 int32_t rowIdx = -1, colIdx = -1; |
|
655 tableFrame->GetRowAndColumnByIndex(aCellIdx, &rowIdx, &colIdx); |
|
656 return rowIdx; |
|
657 } |
|
658 |
|
659 void |
|
660 HTMLTableAccessible::RowAndColIndicesAt(uint32_t aCellIdx, int32_t* aRowIdx, |
|
661 int32_t* aColIdx) |
|
662 { |
|
663 nsTableOuterFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame()); |
|
664 if (tableFrame) |
|
665 tableFrame->GetRowAndColumnByIndex(aCellIdx, aRowIdx, aColIdx); |
|
666 } |
|
667 |
|
668 uint32_t |
|
669 HTMLTableAccessible::ColExtentAt(uint32_t aRowIdx, uint32_t aColIdx) |
|
670 { |
|
671 nsTableOuterFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame()); |
|
672 if (!tableFrame) |
|
673 return 0; |
|
674 |
|
675 return tableFrame->GetEffectiveColSpanAt(aRowIdx, aColIdx); |
|
676 } |
|
677 |
|
678 uint32_t |
|
679 HTMLTableAccessible::RowExtentAt(uint32_t aRowIdx, uint32_t aColIdx) |
|
680 { |
|
681 nsTableOuterFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame()); |
|
682 if (!tableFrame) |
|
683 return 0; |
|
684 |
|
685 return tableFrame->GetEffectiveRowSpanAt(aRowIdx, aColIdx); |
|
686 } |
|
687 |
|
688 bool |
|
689 HTMLTableAccessible::IsColSelected(uint32_t aColIdx) |
|
690 { |
|
691 bool isSelected = false; |
|
692 |
|
693 uint32_t rowCount = RowCount(); |
|
694 for (uint32_t rowIdx = 0; rowIdx < rowCount; rowIdx++) { |
|
695 isSelected = IsCellSelected(rowIdx, aColIdx); |
|
696 if (!isSelected) |
|
697 return false; |
|
698 } |
|
699 |
|
700 return isSelected; |
|
701 } |
|
702 |
|
703 bool |
|
704 HTMLTableAccessible::IsRowSelected(uint32_t aRowIdx) |
|
705 { |
|
706 bool isSelected = false; |
|
707 |
|
708 uint32_t colCount = ColCount(); |
|
709 for (uint32_t colIdx = 0; colIdx < colCount; colIdx++) { |
|
710 isSelected = IsCellSelected(aRowIdx, colIdx); |
|
711 if (!isSelected) |
|
712 return false; |
|
713 } |
|
714 |
|
715 return isSelected; |
|
716 } |
|
717 |
|
718 bool |
|
719 HTMLTableAccessible::IsCellSelected(uint32_t aRowIdx, uint32_t aColIdx) |
|
720 { |
|
721 nsTableOuterFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame()); |
|
722 if (!tableFrame) |
|
723 return false; |
|
724 |
|
725 nsTableCellFrame* cellFrame = tableFrame->GetCellFrameAt(aRowIdx, aColIdx); |
|
726 return cellFrame ? cellFrame->IsSelected() : false; |
|
727 } |
|
728 |
|
729 void |
|
730 HTMLTableAccessible::SelectRow(uint32_t aRowIdx) |
|
731 { |
|
732 DebugOnly<nsresult> rv = |
|
733 RemoveRowsOrColumnsFromSelection(aRowIdx, |
|
734 nsISelectionPrivate::TABLESELECTION_ROW, |
|
735 true); |
|
736 NS_ASSERTION(NS_SUCCEEDED(rv), |
|
737 "RemoveRowsOrColumnsFromSelection() Shouldn't fail!"); |
|
738 |
|
739 AddRowOrColumnToSelection(aRowIdx, nsISelectionPrivate::TABLESELECTION_ROW); |
|
740 } |
|
741 |
|
742 void |
|
743 HTMLTableAccessible::SelectCol(uint32_t aColIdx) |
|
744 { |
|
745 DebugOnly<nsresult> rv = |
|
746 RemoveRowsOrColumnsFromSelection(aColIdx, |
|
747 nsISelectionPrivate::TABLESELECTION_COLUMN, |
|
748 true); |
|
749 NS_ASSERTION(NS_SUCCEEDED(rv), |
|
750 "RemoveRowsOrColumnsFromSelection() Shouldn't fail!"); |
|
751 |
|
752 AddRowOrColumnToSelection(aColIdx, nsISelectionPrivate::TABLESELECTION_COLUMN); |
|
753 } |
|
754 |
|
755 void |
|
756 HTMLTableAccessible::UnselectRow(uint32_t aRowIdx) |
|
757 { |
|
758 RemoveRowsOrColumnsFromSelection(aRowIdx, |
|
759 nsISelectionPrivate::TABLESELECTION_ROW, |
|
760 false); |
|
761 } |
|
762 |
|
763 void |
|
764 HTMLTableAccessible::UnselectCol(uint32_t aColIdx) |
|
765 { |
|
766 RemoveRowsOrColumnsFromSelection(aColIdx, |
|
767 nsISelectionPrivate::TABLESELECTION_COLUMN, |
|
768 false); |
|
769 } |
|
770 |
|
771 nsresult |
|
772 HTMLTableAccessible::AddRowOrColumnToSelection(int32_t aIndex, uint32_t aTarget) |
|
773 { |
|
774 bool doSelectRow = (aTarget == nsISelectionPrivate::TABLESELECTION_ROW); |
|
775 |
|
776 nsTableOuterFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame()); |
|
777 if (!tableFrame) |
|
778 return NS_OK; |
|
779 |
|
780 uint32_t count = 0; |
|
781 if (doSelectRow) |
|
782 count = ColCount(); |
|
783 else |
|
784 count = RowCount(); |
|
785 |
|
786 nsIPresShell* presShell(mDoc->PresShell()); |
|
787 nsRefPtr<nsFrameSelection> tableSelection = |
|
788 const_cast<nsFrameSelection*>(presShell->ConstFrameSelection()); |
|
789 |
|
790 for (uint32_t idx = 0; idx < count; idx++) { |
|
791 int32_t rowIdx = doSelectRow ? aIndex : idx; |
|
792 int32_t colIdx = doSelectRow ? idx : aIndex; |
|
793 nsTableCellFrame* cellFrame = tableFrame->GetCellFrameAt(rowIdx, colIdx); |
|
794 if (cellFrame && !cellFrame->IsSelected()) { |
|
795 nsresult rv = tableSelection->SelectCellElement(cellFrame->GetContent()); |
|
796 NS_ENSURE_SUCCESS(rv, rv); |
|
797 } |
|
798 } |
|
799 |
|
800 return NS_OK; |
|
801 } |
|
802 |
|
803 nsresult |
|
804 HTMLTableAccessible::RemoveRowsOrColumnsFromSelection(int32_t aIndex, |
|
805 uint32_t aTarget, |
|
806 bool aIsOuter) |
|
807 { |
|
808 nsTableOuterFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame()); |
|
809 if (!tableFrame) |
|
810 return NS_OK; |
|
811 |
|
812 nsIPresShell* presShell(mDoc->PresShell()); |
|
813 nsRefPtr<nsFrameSelection> tableSelection = |
|
814 const_cast<nsFrameSelection*>(presShell->ConstFrameSelection()); |
|
815 |
|
816 bool doUnselectRow = (aTarget == nsISelectionPrivate::TABLESELECTION_ROW); |
|
817 uint32_t count = doUnselectRow ? ColCount() : RowCount(); |
|
818 |
|
819 int32_t startRowIdx = doUnselectRow ? aIndex : 0; |
|
820 int32_t endRowIdx = doUnselectRow ? aIndex : count - 1; |
|
821 int32_t startColIdx = doUnselectRow ? 0 : aIndex; |
|
822 int32_t endColIdx = doUnselectRow ? count - 1 : aIndex; |
|
823 |
|
824 if (aIsOuter) |
|
825 return tableSelection->RestrictCellsToSelection(mContent, |
|
826 startRowIdx, startColIdx, |
|
827 endRowIdx, endColIdx); |
|
828 |
|
829 return tableSelection->RemoveCellsFromSelection(mContent, |
|
830 startRowIdx, startColIdx, |
|
831 endRowIdx, endColIdx); |
|
832 } |
|
833 |
|
834 void |
|
835 HTMLTableAccessible::Description(nsString& aDescription) |
|
836 { |
|
837 // Helpful for debugging layout vs. data tables |
|
838 aDescription.Truncate(); |
|
839 Accessible::Description(aDescription); |
|
840 if (!aDescription.IsEmpty()) |
|
841 return; |
|
842 |
|
843 // Use summary as description if it weren't used as a name. |
|
844 // XXX: get rid code duplication with NameInternal(). |
|
845 Accessible* caption = Caption(); |
|
846 if (caption) { |
|
847 nsIContent* captionContent = caption->GetContent(); |
|
848 if (captionContent) { |
|
849 nsAutoString captionText; |
|
850 nsTextEquivUtils::AppendTextEquivFromContent(this, captionContent, |
|
851 &captionText); |
|
852 |
|
853 if (!captionText.IsEmpty()) { // summary isn't used as a name. |
|
854 mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::summary, |
|
855 aDescription); |
|
856 } |
|
857 } |
|
858 } |
|
859 |
|
860 #ifdef SHOW_LAYOUT_HEURISTIC |
|
861 if (aDescription.IsEmpty()) { |
|
862 bool isProbablyForLayout = IsProbablyLayoutTable(); |
|
863 aDescription = mLayoutHeuristic; |
|
864 } |
|
865 printf("\nTABLE: %s\n", NS_ConvertUTF16toUTF8(mLayoutHeuristic).get()); |
|
866 #endif |
|
867 } |
|
868 |
|
869 bool |
|
870 HTMLTableAccessible::HasDescendant(const nsAString& aTagName, bool aAllowEmpty) |
|
871 { |
|
872 nsCOMPtr<nsIDOMElement> tableElt(do_QueryInterface(mContent)); |
|
873 NS_ENSURE_TRUE(tableElt, false); |
|
874 |
|
875 nsCOMPtr<nsIDOMHTMLCollection> nodeList; |
|
876 tableElt->GetElementsByTagName(aTagName, getter_AddRefs(nodeList)); |
|
877 NS_ENSURE_TRUE(nodeList, false); |
|
878 |
|
879 nsCOMPtr<nsIDOMNode> foundItem; |
|
880 nodeList->Item(0, getter_AddRefs(foundItem)); |
|
881 if (!foundItem) |
|
882 return false; |
|
883 |
|
884 if (aAllowEmpty) |
|
885 return true; |
|
886 |
|
887 // Make sure that the item we found has contents and either has multiple |
|
888 // children or the found item is not a whitespace-only text node. |
|
889 nsCOMPtr<nsIContent> foundItemContent = do_QueryInterface(foundItem); |
|
890 if (foundItemContent->GetChildCount() > 1) |
|
891 return true; // Treat multiple child nodes as non-empty |
|
892 |
|
893 nsIContent *innerItemContent = foundItemContent->GetFirstChild(); |
|
894 if (innerItemContent && !innerItemContent->TextIsOnlyWhitespace()) |
|
895 return true; |
|
896 |
|
897 // If we found more than one node then return true not depending on |
|
898 // aAllowEmpty flag. |
|
899 // XXX it might be dummy but bug 501375 where we changed this addresses |
|
900 // performance problems only. Note, currently 'aAllowEmpty' flag is used for |
|
901 // caption element only. On another hand we create accessible object for |
|
902 // the first entry of caption element (see |
|
903 // HTMLTableAccessible::CacheChildren). |
|
904 nodeList->Item(1, getter_AddRefs(foundItem)); |
|
905 return !!foundItem; |
|
906 } |
|
907 |
|
908 bool |
|
909 HTMLTableAccessible::IsProbablyLayoutTable() |
|
910 { |
|
911 // Implement a heuristic to determine if table is most likely used for layout |
|
912 // XXX do we want to look for rowspan or colspan, especialy that span all but a couple cells |
|
913 // at the beginning or end of a row/col, and especially when they occur at the edge of a table? |
|
914 // XXX expose this info via object attributes to AT-SPI |
|
915 |
|
916 // XXX For now debugging descriptions are always on via SHOW_LAYOUT_HEURISTIC |
|
917 // This will allow release trunk builds to be used by testers to refine the algorithm |
|
918 // Change to |#define SHOW_LAYOUT_HEURISTIC DEBUG| before final release |
|
919 #ifdef SHOW_LAYOUT_HEURISTIC |
|
920 #define RETURN_LAYOUT_ANSWER(isLayout, heuristic) \ |
|
921 { \ |
|
922 mLayoutHeuristic = isLayout ? \ |
|
923 NS_LITERAL_STRING("layout table: " heuristic) : \ |
|
924 NS_LITERAL_STRING("data table: " heuristic); \ |
|
925 return isLayout; \ |
|
926 } |
|
927 #else |
|
928 #define RETURN_LAYOUT_ANSWER(isLayout, heuristic) { return isLayout; } |
|
929 #endif |
|
930 |
|
931 DocAccessible* docAccessible = Document(); |
|
932 if (docAccessible) { |
|
933 uint64_t docState = docAccessible->State(); |
|
934 if (docState & states::EDITABLE) { // Need to see all elements while document is being edited |
|
935 RETURN_LAYOUT_ANSWER(false, "In editable document"); |
|
936 } |
|
937 } |
|
938 |
|
939 // Check to see if an ARIA role overrides the role from native markup, |
|
940 // but for which we still expose table semantics (treegrid, for example). |
|
941 if (Role() != roles::TABLE) |
|
942 RETURN_LAYOUT_ANSWER(false, "Has role attribute"); |
|
943 |
|
944 if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::role)) { |
|
945 // Role attribute is present, but overridden roles have already been dealt with. |
|
946 // Only landmarks and other roles that don't override the role from native |
|
947 // markup are left to deal with here. |
|
948 RETURN_LAYOUT_ANSWER(false, "Has role attribute, weak role, and role is table"); |
|
949 } |
|
950 |
|
951 if (mContent->Tag() != nsGkAtoms::table) |
|
952 RETURN_LAYOUT_ANSWER(true, "table built by CSS display:table style"); |
|
953 |
|
954 // Check if datatable attribute has "0" value. |
|
955 if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::datatable, |
|
956 NS_LITERAL_STRING("0"), eCaseMatters)) { |
|
957 RETURN_LAYOUT_ANSWER(true, "Has datatable = 0 attribute, it's for layout"); |
|
958 } |
|
959 |
|
960 // Check for legitimate data table attributes. |
|
961 nsAutoString summary; |
|
962 if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::summary, summary) && |
|
963 !summary.IsEmpty()) |
|
964 RETURN_LAYOUT_ANSWER(false, "Has summary -- legitimate table structures"); |
|
965 |
|
966 // Check for legitimate data table elements. |
|
967 Accessible* caption = FirstChild(); |
|
968 if (caption && caption->Role() == roles::CAPTION && caption->HasChildren()) |
|
969 RETURN_LAYOUT_ANSWER(false, "Not empty caption -- legitimate table structures"); |
|
970 |
|
971 for (nsIContent* childElm = mContent->GetFirstChild(); childElm; |
|
972 childElm = childElm->GetNextSibling()) { |
|
973 if (!childElm->IsHTML()) |
|
974 continue; |
|
975 |
|
976 if (childElm->Tag() == nsGkAtoms::col || |
|
977 childElm->Tag() == nsGkAtoms::colgroup || |
|
978 childElm->Tag() == nsGkAtoms::tfoot || |
|
979 childElm->Tag() == nsGkAtoms::thead) { |
|
980 RETURN_LAYOUT_ANSWER(false, |
|
981 "Has col, colgroup, tfoot or thead -- legitimate table structures"); |
|
982 } |
|
983 |
|
984 if (childElm->Tag() == nsGkAtoms::tbody) { |
|
985 for (nsIContent* rowElm = childElm->GetFirstChild(); rowElm; |
|
986 rowElm = rowElm->GetNextSibling()) { |
|
987 if (rowElm->IsHTML() && rowElm->Tag() == nsGkAtoms::tr) { |
|
988 for (nsIContent* cellElm = rowElm->GetFirstChild(); cellElm; |
|
989 cellElm = cellElm->GetNextSibling()) { |
|
990 if (cellElm->IsHTML()) { |
|
991 |
|
992 if (cellElm->NodeInfo()->Equals(nsGkAtoms::th)) { |
|
993 RETURN_LAYOUT_ANSWER(false, |
|
994 "Has th -- legitimate table structures"); |
|
995 } |
|
996 |
|
997 if (cellElm->HasAttr(kNameSpaceID_None, nsGkAtoms::headers) || |
|
998 cellElm->HasAttr(kNameSpaceID_None, nsGkAtoms::scope) || |
|
999 cellElm->HasAttr(kNameSpaceID_None, nsGkAtoms::abbr)) { |
|
1000 RETURN_LAYOUT_ANSWER(false, |
|
1001 "Has headers, scope, or abbr attribute -- legitimate table structures"); |
|
1002 } |
|
1003 |
|
1004 Accessible* cell = mDoc->GetAccessible(cellElm); |
|
1005 if (cell && cell->ChildCount() == 1 && |
|
1006 cell->FirstChild()->IsAbbreviation()) { |
|
1007 RETURN_LAYOUT_ANSWER(false, |
|
1008 "has abbr -- legitimate table structures"); |
|
1009 } |
|
1010 } |
|
1011 } |
|
1012 } |
|
1013 } |
|
1014 } |
|
1015 } |
|
1016 |
|
1017 if (HasDescendant(NS_LITERAL_STRING("table"))) { |
|
1018 RETURN_LAYOUT_ANSWER(true, "Has a nested table within it"); |
|
1019 } |
|
1020 |
|
1021 // If only 1 column or only 1 row, it's for layout |
|
1022 int32_t columns, rows; |
|
1023 GetColumnCount(&columns); |
|
1024 if (columns <=1) { |
|
1025 RETURN_LAYOUT_ANSWER(true, "Has only 1 column"); |
|
1026 } |
|
1027 GetRowCount(&rows); |
|
1028 if (rows <=1) { |
|
1029 RETURN_LAYOUT_ANSWER(true, "Has only 1 row"); |
|
1030 } |
|
1031 |
|
1032 // Check for many columns |
|
1033 if (columns >= 5) { |
|
1034 RETURN_LAYOUT_ANSWER(false, ">=5 columns"); |
|
1035 } |
|
1036 |
|
1037 // Now we know there are 2-4 columns and 2 or more rows |
|
1038 // Check to see if there are visible borders on the cells |
|
1039 // XXX currently, we just check the first cell -- do we really need to do more? |
|
1040 nsTableOuterFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame()); |
|
1041 if (!tableFrame) |
|
1042 RETURN_LAYOUT_ANSWER(false, "table with no frame!"); |
|
1043 |
|
1044 nsIFrame* cellFrame = tableFrame->GetCellFrameAt(0, 0); |
|
1045 if (!cellFrame) |
|
1046 RETURN_LAYOUT_ANSWER(false, "table's first cell has no frame!"); |
|
1047 |
|
1048 nsMargin border; |
|
1049 cellFrame->GetBorder(border); |
|
1050 if (border.top && border.bottom && border.left && border.right) { |
|
1051 RETURN_LAYOUT_ANSWER(false, "Has nonzero border-width on table cell"); |
|
1052 } |
|
1053 |
|
1054 /** |
|
1055 * Rules for non-bordered tables with 2-4 columns and 2+ rows from here on forward |
|
1056 */ |
|
1057 |
|
1058 // Check for styled background color across rows (alternating background |
|
1059 // color is a common feature for data tables). |
|
1060 uint32_t childCount = ChildCount(); |
|
1061 nscolor rowColor = 0; |
|
1062 nscolor prevRowColor; |
|
1063 for (uint32_t childIdx = 0; childIdx < childCount; childIdx++) { |
|
1064 Accessible* child = GetChildAt(childIdx); |
|
1065 if (child->Role() == roles::ROW) { |
|
1066 prevRowColor = rowColor; |
|
1067 nsIFrame* rowFrame = child->GetFrame(); |
|
1068 rowColor = rowFrame->StyleBackground()->mBackgroundColor; |
|
1069 |
|
1070 if (childIdx > 0 && prevRowColor != rowColor) |
|
1071 RETURN_LAYOUT_ANSWER(false, "2 styles of row background color, non-bordered"); |
|
1072 } |
|
1073 } |
|
1074 |
|
1075 // Check for many rows |
|
1076 const int32_t kMaxLayoutRows = 20; |
|
1077 if (rows > kMaxLayoutRows) { // A ton of rows, this is probably for data |
|
1078 RETURN_LAYOUT_ANSWER(false, ">= kMaxLayoutRows (20) and non-bordered"); |
|
1079 } |
|
1080 |
|
1081 // Check for very wide table. |
|
1082 nsIFrame* documentFrame = Document()->GetFrame(); |
|
1083 nsSize documentSize = documentFrame->GetSize(); |
|
1084 if (documentSize.width > 0) { |
|
1085 nsSize tableSize = GetFrame()->GetSize(); |
|
1086 int32_t percentageOfDocWidth = (100 * tableSize.width) / documentSize.width; |
|
1087 if (percentageOfDocWidth > 95) { |
|
1088 // 3-4 columns, no borders, not a lot of rows, and 95% of the doc's width |
|
1089 // Probably for layout |
|
1090 RETURN_LAYOUT_ANSWER(true, |
|
1091 "<= 4 columns, table width is 95% of document width"); |
|
1092 } |
|
1093 } |
|
1094 |
|
1095 // Two column rules |
|
1096 if (rows * columns <= 10) { |
|
1097 RETURN_LAYOUT_ANSWER(true, "2-4 columns, 10 cells or less, non-bordered"); |
|
1098 } |
|
1099 |
|
1100 if (HasDescendant(NS_LITERAL_STRING("embed")) || |
|
1101 HasDescendant(NS_LITERAL_STRING("object")) || |
|
1102 HasDescendant(NS_LITERAL_STRING("applet")) || |
|
1103 HasDescendant(NS_LITERAL_STRING("iframe"))) { |
|
1104 RETURN_LAYOUT_ANSWER(true, "Has no borders, and has iframe, object, applet or iframe, typical of advertisements"); |
|
1105 } |
|
1106 |
|
1107 RETURN_LAYOUT_ANSWER(false, "no layout factor strong enough, so will guess data"); |
|
1108 } |
|
1109 |
|
1110 |
|
1111 //////////////////////////////////////////////////////////////////////////////// |
|
1112 // HTMLCaptionAccessible |
|
1113 //////////////////////////////////////////////////////////////////////////////// |
|
1114 |
|
1115 Relation |
|
1116 HTMLCaptionAccessible::RelationByType(RelationType aType) |
|
1117 { |
|
1118 Relation rel = HyperTextAccessible::RelationByType(aType); |
|
1119 if (aType == RelationType::LABEL_FOR) |
|
1120 rel.AppendTarget(Parent()); |
|
1121 |
|
1122 return rel; |
|
1123 } |
|
1124 |
|
1125 role |
|
1126 HTMLCaptionAccessible::NativeRole() |
|
1127 { |
|
1128 return roles::CAPTION; |
|
1129 } |