1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/accessible/src/html/HTMLTableAccessible.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1129 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "HTMLTableAccessible.h" 1.10 + 1.11 +#include "mozilla/DebugOnly.h" 1.12 + 1.13 +#include "Accessible-inl.h" 1.14 +#include "nsAccessibilityService.h" 1.15 +#include "nsAccUtils.h" 1.16 +#include "DocAccessible.h" 1.17 +#include "nsIAccessibleRelation.h" 1.18 +#include "nsTextEquivUtils.h" 1.19 +#include "Relation.h" 1.20 +#include "Role.h" 1.21 +#include "States.h" 1.22 +#include "TreeWalker.h" 1.23 + 1.24 +#include "mozilla/dom/HTMLTableElement.h" 1.25 +#include "nsIDOMElement.h" 1.26 +#include "nsIDOMRange.h" 1.27 +#include "nsISelectionPrivate.h" 1.28 +#include "nsIDOMNodeList.h" 1.29 +#include "nsIDOMHTMLCollection.h" 1.30 +#include "nsIDocument.h" 1.31 +#include "nsIMutableArray.h" 1.32 +#include "nsIPersistentProperties2.h" 1.33 +#include "nsIPresShell.h" 1.34 +#include "nsITableCellLayout.h" 1.35 +#include "nsFrameSelection.h" 1.36 +#include "nsError.h" 1.37 +#include "nsArrayUtils.h" 1.38 +#include "nsComponentManagerUtils.h" 1.39 +#include "nsNameSpaceManager.h" 1.40 +#include "nsTableCellFrame.h" 1.41 +#include "nsTableOuterFrame.h" 1.42 + 1.43 +using namespace mozilla; 1.44 +using namespace mozilla::a11y; 1.45 + 1.46 +//////////////////////////////////////////////////////////////////////////////// 1.47 +// HTMLTableCellAccessible 1.48 +//////////////////////////////////////////////////////////////////////////////// 1.49 + 1.50 +HTMLTableCellAccessible:: 1.51 + HTMLTableCellAccessible(nsIContent* aContent, DocAccessible* aDoc) : 1.52 + HyperTextAccessibleWrap(aContent, aDoc), xpcAccessibleTableCell(this) 1.53 +{ 1.54 + mGenericTypes |= eTableCell; 1.55 +} 1.56 + 1.57 +//////////////////////////////////////////////////////////////////////////////// 1.58 +// HTMLTableCellAccessible: nsISupports implementation 1.59 + 1.60 +NS_IMPL_ISUPPORTS_INHERITED(HTMLTableCellAccessible, 1.61 + HyperTextAccessible, 1.62 + nsIAccessibleTableCell) 1.63 + 1.64 +//////////////////////////////////////////////////////////////////////////////// 1.65 +// HTMLTableCellAccessible: Accessible implementation 1.66 + 1.67 +void 1.68 +HTMLTableCellAccessible::Shutdown() 1.69 +{ 1.70 + mTableCell = nullptr; 1.71 + HyperTextAccessibleWrap::Shutdown(); 1.72 +} 1.73 + 1.74 +role 1.75 +HTMLTableCellAccessible::NativeRole() 1.76 +{ 1.77 + return roles::CELL; 1.78 +} 1.79 + 1.80 +uint64_t 1.81 +HTMLTableCellAccessible::NativeState() 1.82 +{ 1.83 + uint64_t state = HyperTextAccessibleWrap::NativeState(); 1.84 + 1.85 + nsIFrame *frame = mContent->GetPrimaryFrame(); 1.86 + NS_ASSERTION(frame, "No frame for valid cell accessible!"); 1.87 + 1.88 + if (frame && frame->IsSelected()) 1.89 + state |= states::SELECTED; 1.90 + 1.91 + return state; 1.92 +} 1.93 + 1.94 +uint64_t 1.95 +HTMLTableCellAccessible::NativeInteractiveState() const 1.96 +{ 1.97 + return HyperTextAccessibleWrap::NativeInteractiveState() | states::SELECTABLE; 1.98 +} 1.99 + 1.100 +already_AddRefed<nsIPersistentProperties> 1.101 +HTMLTableCellAccessible::NativeAttributes() 1.102 +{ 1.103 + nsCOMPtr<nsIPersistentProperties> attributes = 1.104 + HyperTextAccessibleWrap::NativeAttributes(); 1.105 + 1.106 + // table-cell-index attribute 1.107 + TableAccessible* table = Table(); 1.108 + if (!table) 1.109 + return attributes.forget(); 1.110 + 1.111 + int32_t rowIdx = -1, colIdx = -1; 1.112 + nsresult rv = GetCellIndexes(rowIdx, colIdx); 1.113 + if (NS_FAILED(rv)) 1.114 + return attributes.forget(); 1.115 + 1.116 + nsAutoString stringIdx; 1.117 + stringIdx.AppendInt(table->CellIndexAt(rowIdx, colIdx)); 1.118 + nsAccUtils::SetAccAttr(attributes, nsGkAtoms::tableCellIndex, stringIdx); 1.119 + 1.120 + // abbr attribute 1.121 + 1.122 + // Pick up object attribute from abbr DOM element (a child of the cell) or 1.123 + // from abbr DOM attribute. 1.124 + nsAutoString abbrText; 1.125 + if (ChildCount() == 1) { 1.126 + Accessible* abbr = FirstChild(); 1.127 + if (abbr->IsAbbreviation()) { 1.128 + nsIContent* firstChildNode = abbr->GetContent()->GetFirstChild(); 1.129 + if (firstChildNode) { 1.130 + nsTextEquivUtils:: 1.131 + AppendTextEquivFromTextContent(firstChildNode, &abbrText); 1.132 + } 1.133 + } 1.134 + } 1.135 + if (abbrText.IsEmpty()) 1.136 + mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::abbr, abbrText); 1.137 + 1.138 + if (!abbrText.IsEmpty()) 1.139 + nsAccUtils::SetAccAttr(attributes, nsGkAtoms::abbr, abbrText); 1.140 + 1.141 + // axis attribute 1.142 + nsAutoString axisText; 1.143 + mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::axis, axisText); 1.144 + if (!axisText.IsEmpty()) 1.145 + nsAccUtils::SetAccAttr(attributes, nsGkAtoms::axis, axisText); 1.146 + 1.147 + return attributes.forget(); 1.148 +} 1.149 + 1.150 +//////////////////////////////////////////////////////////////////////////////// 1.151 +// HTMLTableCellAccessible: nsIAccessibleTableCell implementation 1.152 + 1.153 +TableAccessible* 1.154 +HTMLTableCellAccessible::Table() const 1.155 +{ 1.156 + Accessible* parent = const_cast<HTMLTableCellAccessible*>(this); 1.157 + while ((parent = parent->Parent())) { 1.158 + roles::Role role = parent->Role(); 1.159 + if (role == roles::TABLE || role == roles::TREE_TABLE) 1.160 + return parent->AsTable(); 1.161 + } 1.162 + 1.163 + return nullptr; 1.164 +} 1.165 + 1.166 +uint32_t 1.167 +HTMLTableCellAccessible::ColIdx() const 1.168 +{ 1.169 + nsITableCellLayout* cellLayout = GetCellLayout(); 1.170 + NS_ENSURE_TRUE(cellLayout, 0); 1.171 + 1.172 + int32_t colIdx = 0; 1.173 + cellLayout->GetColIndex(colIdx); 1.174 + return colIdx > 0 ? static_cast<uint32_t>(colIdx) : 0; 1.175 +} 1.176 + 1.177 +uint32_t 1.178 +HTMLTableCellAccessible::RowIdx() const 1.179 +{ 1.180 + nsITableCellLayout* cellLayout = GetCellLayout(); 1.181 + NS_ENSURE_TRUE(cellLayout, 0); 1.182 + 1.183 + int32_t rowIdx = 0; 1.184 + cellLayout->GetRowIndex(rowIdx); 1.185 + return rowIdx > 0 ? static_cast<uint32_t>(rowIdx) : 0; 1.186 +} 1.187 + 1.188 +uint32_t 1.189 +HTMLTableCellAccessible::ColExtent() const 1.190 +{ 1.191 + int32_t rowIdx = -1, colIdx = -1; 1.192 + GetCellIndexes(rowIdx, colIdx); 1.193 + 1.194 + TableAccessible* table = Table(); 1.195 + NS_ASSERTION(table, "cell not in a table!"); 1.196 + if (!table) 1.197 + return 0; 1.198 + 1.199 + return table->ColExtentAt(rowIdx, colIdx); 1.200 +} 1.201 + 1.202 +uint32_t 1.203 +HTMLTableCellAccessible::RowExtent() const 1.204 +{ 1.205 + int32_t rowIdx = -1, colIdx = -1; 1.206 + GetCellIndexes(rowIdx, colIdx); 1.207 + 1.208 + TableAccessible* table = Table(); 1.209 + NS_ASSERTION(table, "cell not in atable!"); 1.210 + if (!table) 1.211 + return 0; 1.212 + 1.213 + return table->RowExtentAt(rowIdx, colIdx); 1.214 +} 1.215 + 1.216 +void 1.217 +HTMLTableCellAccessible::ColHeaderCells(nsTArray<Accessible*>* aCells) 1.218 +{ 1.219 + IDRefsIterator itr(mDoc, mContent, nsGkAtoms::headers); 1.220 + while (Accessible* cell = itr.Next()) { 1.221 + a11y::role cellRole = cell->Role(); 1.222 + if (cellRole == roles::COLUMNHEADER) { 1.223 + aCells->AppendElement(cell); 1.224 + } else if (cellRole != roles::ROWHEADER) { 1.225 + // If referred table cell is at the same column then treat it as a column 1.226 + // header. 1.227 + TableCellAccessible* tableCell = cell->AsTableCell(); 1.228 + if (tableCell && tableCell->ColIdx() == ColIdx()) 1.229 + aCells->AppendElement(cell); 1.230 + } 1.231 + } 1.232 + 1.233 + if (aCells->IsEmpty()) 1.234 + TableCellAccessible::ColHeaderCells(aCells); 1.235 +} 1.236 + 1.237 +void 1.238 +HTMLTableCellAccessible::RowHeaderCells(nsTArray<Accessible*>* aCells) 1.239 +{ 1.240 + IDRefsIterator itr(mDoc, mContent, nsGkAtoms::headers); 1.241 + while (Accessible* cell = itr.Next()) { 1.242 + a11y::role cellRole = cell->Role(); 1.243 + if (cellRole == roles::ROWHEADER) { 1.244 + aCells->AppendElement(cell); 1.245 + } else if (cellRole != roles::COLUMNHEADER) { 1.246 + // If referred table cell is at the same row then treat it as a column 1.247 + // header. 1.248 + TableCellAccessible* tableCell = cell->AsTableCell(); 1.249 + if (tableCell && tableCell->RowIdx() == RowIdx()) 1.250 + aCells->AppendElement(cell); 1.251 + } 1.252 + } 1.253 + 1.254 + if (aCells->IsEmpty()) 1.255 + TableCellAccessible::RowHeaderCells(aCells); 1.256 +} 1.257 + 1.258 +bool 1.259 +HTMLTableCellAccessible::Selected() 1.260 +{ 1.261 + int32_t rowIdx = -1, colIdx = -1; 1.262 + GetCellIndexes(rowIdx, colIdx); 1.263 + 1.264 + TableAccessible* table = Table(); 1.265 + NS_ENSURE_TRUE(table, false); 1.266 + 1.267 + return table->IsCellSelected(rowIdx, colIdx); 1.268 +} 1.269 + 1.270 +//////////////////////////////////////////////////////////////////////////////// 1.271 +// HTMLTableCellAccessible: protected implementation 1.272 + 1.273 +nsITableCellLayout* 1.274 +HTMLTableCellAccessible::GetCellLayout() const 1.275 +{ 1.276 + return do_QueryFrame(mContent->GetPrimaryFrame()); 1.277 +} 1.278 + 1.279 +nsresult 1.280 +HTMLTableCellAccessible::GetCellIndexes(int32_t& aRowIdx, int32_t& aColIdx) const 1.281 +{ 1.282 + nsITableCellLayout *cellLayout = GetCellLayout(); 1.283 + NS_ENSURE_STATE(cellLayout); 1.284 + 1.285 + return cellLayout->GetCellIndexes(aRowIdx, aColIdx); 1.286 +} 1.287 + 1.288 + 1.289 +//////////////////////////////////////////////////////////////////////////////// 1.290 +// HTMLTableHeaderCellAccessible 1.291 +//////////////////////////////////////////////////////////////////////////////// 1.292 + 1.293 +HTMLTableHeaderCellAccessible:: 1.294 + HTMLTableHeaderCellAccessible(nsIContent* aContent, DocAccessible* aDoc) : 1.295 + HTMLTableCellAccessible(aContent, aDoc) 1.296 +{ 1.297 +} 1.298 + 1.299 +//////////////////////////////////////////////////////////////////////////////// 1.300 +// HTMLTableHeaderCellAccessible: Accessible implementation 1.301 + 1.302 +role 1.303 +HTMLTableHeaderCellAccessible::NativeRole() 1.304 +{ 1.305 + // Check value of @scope attribute. 1.306 + static nsIContent::AttrValuesArray scopeValues[] = 1.307 + {&nsGkAtoms::col, &nsGkAtoms::row, nullptr}; 1.308 + int32_t valueIdx = 1.309 + mContent->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::scope, 1.310 + scopeValues, eCaseMatters); 1.311 + 1.312 + switch (valueIdx) { 1.313 + case 0: 1.314 + return roles::COLUMNHEADER; 1.315 + case 1: 1.316 + return roles::ROWHEADER; 1.317 + } 1.318 + 1.319 + // Assume it's columnheader if there are headers in siblings, otherwise 1.320 + // rowheader. 1.321 + // This should iterate the flattened tree 1.322 + nsIContent* parentContent = mContent->GetParent(); 1.323 + if (!parentContent) { 1.324 + NS_ERROR("Deattached content on alive accessible?"); 1.325 + return roles::NOTHING; 1.326 + } 1.327 + 1.328 + for (nsIContent* siblingContent = mContent->GetPreviousSibling(); siblingContent; 1.329 + siblingContent = siblingContent->GetPreviousSibling()) { 1.330 + if (siblingContent->IsElement()) { 1.331 + return nsCoreUtils::IsHTMLTableHeader(siblingContent) ? 1.332 + roles::COLUMNHEADER : roles::ROWHEADER; 1.333 + } 1.334 + } 1.335 + 1.336 + for (nsIContent* siblingContent = mContent->GetNextSibling(); siblingContent; 1.337 + siblingContent = siblingContent->GetNextSibling()) { 1.338 + if (siblingContent->IsElement()) { 1.339 + return nsCoreUtils::IsHTMLTableHeader(siblingContent) ? 1.340 + roles::COLUMNHEADER : roles::ROWHEADER; 1.341 + } 1.342 + } 1.343 + 1.344 + // No elements in siblings what means the table has one column only. Therefore 1.345 + // it should be column header. 1.346 + return roles::COLUMNHEADER; 1.347 +} 1.348 + 1.349 + 1.350 +//////////////////////////////////////////////////////////////////////////////// 1.351 +// HTMLTableRowAccessible 1.352 +//////////////////////////////////////////////////////////////////////////////// 1.353 + 1.354 +NS_IMPL_ISUPPORTS_INHERITED0(HTMLTableRowAccessible, Accessible) 1.355 + 1.356 +role 1.357 +HTMLTableRowAccessible::NativeRole() 1.358 +{ 1.359 + return roles::ROW; 1.360 +} 1.361 + 1.362 +//////////////////////////////////////////////////////////////////////////////// 1.363 +// HTMLTableAccessible 1.364 +//////////////////////////////////////////////////////////////////////////////// 1.365 + 1.366 +NS_IMPL_ISUPPORTS_INHERITED(HTMLTableAccessible, Accessible, 1.367 + nsIAccessibleTable) 1.368 + 1.369 +//////////////////////////////////////////////////////////////////////////////// 1.370 +// HTMLTableAccessible: Accessible 1.371 + 1.372 +void 1.373 +HTMLTableAccessible::Shutdown() 1.374 +{ 1.375 + mTable = nullptr; 1.376 + AccessibleWrap::Shutdown(); 1.377 +} 1.378 + 1.379 +void 1.380 +HTMLTableAccessible::CacheChildren() 1.381 +{ 1.382 + // Move caption accessible so that it's the first child. Check for the first 1.383 + // caption only, because nsAccessibilityService ensures we don't create 1.384 + // accessibles for the other captions, since only the first is actually 1.385 + // visible. 1.386 + TreeWalker walker(this, mContent); 1.387 + 1.388 + Accessible* child = nullptr; 1.389 + while ((child = walker.NextChild())) { 1.390 + if (child->Role() == roles::CAPTION) { 1.391 + InsertChildAt(0, child); 1.392 + while ((child = walker.NextChild()) && AppendChild(child)); 1.393 + break; 1.394 + } 1.395 + AppendChild(child); 1.396 + } 1.397 +} 1.398 + 1.399 +role 1.400 +HTMLTableAccessible::NativeRole() 1.401 +{ 1.402 + return roles::TABLE; 1.403 +} 1.404 + 1.405 +uint64_t 1.406 +HTMLTableAccessible::NativeState() 1.407 +{ 1.408 + return Accessible::NativeState() | states::READONLY; 1.409 +} 1.410 + 1.411 +ENameValueFlag 1.412 +HTMLTableAccessible::NativeName(nsString& aName) 1.413 +{ 1.414 + ENameValueFlag nameFlag = Accessible::NativeName(aName); 1.415 + if (!aName.IsEmpty()) 1.416 + return nameFlag; 1.417 + 1.418 + // Use table caption as a name. 1.419 + Accessible* caption = Caption(); 1.420 + if (caption) { 1.421 + nsIContent* captionContent = caption->GetContent(); 1.422 + if (captionContent) { 1.423 + nsTextEquivUtils::AppendTextEquivFromContent(this, captionContent, &aName); 1.424 + if (!aName.IsEmpty()) 1.425 + return eNameOK; 1.426 + } 1.427 + } 1.428 + 1.429 + // If no caption then use summary as a name. 1.430 + mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::summary, aName); 1.431 + return eNameOK; 1.432 +} 1.433 + 1.434 +already_AddRefed<nsIPersistentProperties> 1.435 +HTMLTableAccessible::NativeAttributes() 1.436 +{ 1.437 + nsCOMPtr<nsIPersistentProperties> attributes = 1.438 + AccessibleWrap::NativeAttributes(); 1.439 + if (IsProbablyLayoutTable()) { 1.440 + nsAutoString unused; 1.441 + attributes->SetStringProperty(NS_LITERAL_CSTRING("layout-guess"), 1.442 + NS_LITERAL_STRING("true"), unused); 1.443 + } 1.444 + 1.445 + return attributes.forget(); 1.446 +} 1.447 + 1.448 +//////////////////////////////////////////////////////////////////////////////// 1.449 +// HTMLTableAccessible: nsIAccessible implementation 1.450 + 1.451 +Relation 1.452 +HTMLTableAccessible::RelationByType(RelationType aType) 1.453 +{ 1.454 + Relation rel = AccessibleWrap::RelationByType(aType); 1.455 + if (aType == RelationType::LABELLED_BY) 1.456 + rel.AppendTarget(Caption()); 1.457 + 1.458 + return rel; 1.459 +} 1.460 + 1.461 +//////////////////////////////////////////////////////////////////////////////// 1.462 +// HTMLTableAccessible: nsIAccessibleTable implementation 1.463 + 1.464 +Accessible* 1.465 +HTMLTableAccessible::Caption() 1.466 +{ 1.467 + Accessible* child = mChildren.SafeElementAt(0, nullptr); 1.468 + return child && child->Role() == roles::CAPTION ? child : nullptr; 1.469 +} 1.470 + 1.471 +void 1.472 +HTMLTableAccessible::Summary(nsString& aSummary) 1.473 +{ 1.474 + dom::HTMLTableElement* table = dom::HTMLTableElement::FromContent(mContent); 1.475 + 1.476 + if (table) 1.477 + table->GetSummary(aSummary); 1.478 +} 1.479 + 1.480 +uint32_t 1.481 +HTMLTableAccessible::ColCount() 1.482 +{ 1.483 + nsTableOuterFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame()); 1.484 + return tableFrame ? tableFrame->GetColCount() : 0; 1.485 +} 1.486 + 1.487 +uint32_t 1.488 +HTMLTableAccessible::RowCount() 1.489 +{ 1.490 + nsTableOuterFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame()); 1.491 + return tableFrame ? tableFrame->GetRowCount() : 0; 1.492 +} 1.493 + 1.494 +uint32_t 1.495 +HTMLTableAccessible::SelectedCellCount() 1.496 +{ 1.497 + nsTableOuterFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame()); 1.498 + if (!tableFrame) 1.499 + return 0; 1.500 + 1.501 + uint32_t count = 0, rowCount = RowCount(), colCount = ColCount(); 1.502 + for (uint32_t rowIdx = 0; rowIdx < rowCount; rowIdx++) { 1.503 + for (uint32_t colIdx = 0; colIdx < colCount; colIdx++) { 1.504 + nsTableCellFrame* cellFrame = tableFrame->GetCellFrameAt(rowIdx, colIdx); 1.505 + if (!cellFrame || !cellFrame->IsSelected()) 1.506 + continue; 1.507 + 1.508 + int32_t startRow = -1, startCol = -1; 1.509 + cellFrame->GetRowIndex(startRow); 1.510 + cellFrame->GetColIndex(startCol); 1.511 + if (startRow >= 0 && (uint32_t)startRow == rowIdx && 1.512 + startCol >= 0 && (uint32_t)startCol == colIdx) 1.513 + count++; 1.514 + } 1.515 + } 1.516 + 1.517 + return count; 1.518 +} 1.519 + 1.520 +uint32_t 1.521 +HTMLTableAccessible::SelectedColCount() 1.522 +{ 1.523 + uint32_t count = 0, colCount = ColCount(); 1.524 + 1.525 + for (uint32_t colIdx = 0; colIdx < colCount; colIdx++) 1.526 + if (IsColSelected(colIdx)) 1.527 + count++; 1.528 + 1.529 + return count; 1.530 +} 1.531 + 1.532 +uint32_t 1.533 +HTMLTableAccessible::SelectedRowCount() 1.534 +{ 1.535 + uint32_t count = 0, rowCount = RowCount(); 1.536 + 1.537 + for (uint32_t rowIdx = 0; rowIdx < rowCount; rowIdx++) 1.538 + if (IsRowSelected(rowIdx)) 1.539 + count++; 1.540 + 1.541 + return count; 1.542 +} 1.543 + 1.544 +void 1.545 +HTMLTableAccessible::SelectedCells(nsTArray<Accessible*>* aCells) 1.546 +{ 1.547 + nsTableOuterFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame()); 1.548 + if (!tableFrame) 1.549 + return; 1.550 + 1.551 + uint32_t rowCount = RowCount(), colCount = ColCount(); 1.552 + for (uint32_t rowIdx = 0; rowIdx < rowCount; rowIdx++) { 1.553 + for (uint32_t colIdx = 0; colIdx < colCount; colIdx++) { 1.554 + nsTableCellFrame* cellFrame = tableFrame->GetCellFrameAt(rowIdx, colIdx); 1.555 + if (!cellFrame || !cellFrame->IsSelected()) 1.556 + continue; 1.557 + 1.558 + int32_t startCol = -1, startRow = -1; 1.559 + cellFrame->GetRowIndex(startRow); 1.560 + cellFrame->GetColIndex(startCol); 1.561 + if ((startRow >= 0 && (uint32_t)startRow != rowIdx) || 1.562 + (startCol >= 0 && (uint32_t)startCol != colIdx)) 1.563 + continue; 1.564 + 1.565 + Accessible* cell = mDoc->GetAccessible(cellFrame->GetContent()); 1.566 + aCells->AppendElement(cell); 1.567 + } 1.568 + } 1.569 +} 1.570 + 1.571 +void 1.572 +HTMLTableAccessible::SelectedCellIndices(nsTArray<uint32_t>* aCells) 1.573 +{ 1.574 + nsTableOuterFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame()); 1.575 + if (!tableFrame) 1.576 + return; 1.577 + 1.578 + uint32_t rowCount = RowCount(), colCount = ColCount(); 1.579 + for (uint32_t rowIdx = 0; rowIdx < rowCount; rowIdx++) { 1.580 + for (uint32_t colIdx = 0; colIdx < colCount; colIdx++) { 1.581 + nsTableCellFrame* cellFrame = tableFrame->GetCellFrameAt(rowIdx, colIdx); 1.582 + if (!cellFrame || !cellFrame->IsSelected()) 1.583 + continue; 1.584 + 1.585 + int32_t startRow = -1, startCol = -1; 1.586 + cellFrame->GetColIndex(startCol); 1.587 + cellFrame->GetRowIndex(startRow); 1.588 + if (startRow >= 0 && (uint32_t)startRow == rowIdx && 1.589 + startCol >= 0 && (uint32_t)startCol == colIdx) 1.590 + aCells->AppendElement(CellIndexAt(rowIdx, colIdx)); 1.591 + } 1.592 + } 1.593 +} 1.594 + 1.595 +void 1.596 +HTMLTableAccessible::SelectedColIndices(nsTArray<uint32_t>* aCols) 1.597 +{ 1.598 + uint32_t colCount = ColCount(); 1.599 + for (uint32_t colIdx = 0; colIdx < colCount; colIdx++) 1.600 + if (IsColSelected(colIdx)) 1.601 + aCols->AppendElement(colIdx); 1.602 +} 1.603 + 1.604 +void 1.605 +HTMLTableAccessible::SelectedRowIndices(nsTArray<uint32_t>* aRows) 1.606 +{ 1.607 + uint32_t rowCount = RowCount(); 1.608 + for (uint32_t rowIdx = 0; rowIdx < rowCount; rowIdx++) 1.609 + if (IsRowSelected(rowIdx)) 1.610 + aRows->AppendElement(rowIdx); 1.611 +} 1.612 + 1.613 +Accessible* 1.614 +HTMLTableAccessible::CellAt(uint32_t aRowIdx, uint32_t aColIdx) 1.615 +{ 1.616 + nsTableOuterFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame()); 1.617 + if (!tableFrame) 1.618 + return nullptr; 1.619 + 1.620 + nsIContent* cellContent = tableFrame->GetCellAt(aRowIdx, aColIdx); 1.621 + Accessible* cell = mDoc->GetAccessible(cellContent); 1.622 + 1.623 + // XXX bug 576838: crazy tables (like table6 in tables/test_table2.html) may 1.624 + // return itself as a cell what makes Orca hang. 1.625 + return cell == this ? nullptr : cell; 1.626 +} 1.627 + 1.628 +int32_t 1.629 +HTMLTableAccessible::CellIndexAt(uint32_t aRowIdx, uint32_t aColIdx) 1.630 +{ 1.631 + nsTableOuterFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame()); 1.632 + if (!tableFrame) 1.633 + return -1; 1.634 + 1.635 + return tableFrame->GetIndexByRowAndColumn(aRowIdx, aColIdx); 1.636 +} 1.637 + 1.638 +int32_t 1.639 +HTMLTableAccessible::ColIndexAt(uint32_t aCellIdx) 1.640 +{ 1.641 + nsTableOuterFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame()); 1.642 + if (!tableFrame) 1.643 + return -1; 1.644 + 1.645 + int32_t rowIdx = -1, colIdx = -1; 1.646 + tableFrame->GetRowAndColumnByIndex(aCellIdx, &rowIdx, &colIdx); 1.647 + return colIdx; 1.648 +} 1.649 + 1.650 +int32_t 1.651 +HTMLTableAccessible::RowIndexAt(uint32_t aCellIdx) 1.652 +{ 1.653 + nsTableOuterFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame()); 1.654 + if (!tableFrame) 1.655 + return -1; 1.656 + 1.657 + int32_t rowIdx = -1, colIdx = -1; 1.658 + tableFrame->GetRowAndColumnByIndex(aCellIdx, &rowIdx, &colIdx); 1.659 + return rowIdx; 1.660 +} 1.661 + 1.662 +void 1.663 +HTMLTableAccessible::RowAndColIndicesAt(uint32_t aCellIdx, int32_t* aRowIdx, 1.664 + int32_t* aColIdx) 1.665 +{ 1.666 + nsTableOuterFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame()); 1.667 + if (tableFrame) 1.668 + tableFrame->GetRowAndColumnByIndex(aCellIdx, aRowIdx, aColIdx); 1.669 +} 1.670 + 1.671 +uint32_t 1.672 +HTMLTableAccessible::ColExtentAt(uint32_t aRowIdx, uint32_t aColIdx) 1.673 +{ 1.674 + nsTableOuterFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame()); 1.675 + if (!tableFrame) 1.676 + return 0; 1.677 + 1.678 + return tableFrame->GetEffectiveColSpanAt(aRowIdx, aColIdx); 1.679 +} 1.680 + 1.681 +uint32_t 1.682 +HTMLTableAccessible::RowExtentAt(uint32_t aRowIdx, uint32_t aColIdx) 1.683 +{ 1.684 + nsTableOuterFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame()); 1.685 + if (!tableFrame) 1.686 + return 0; 1.687 + 1.688 + return tableFrame->GetEffectiveRowSpanAt(aRowIdx, aColIdx); 1.689 +} 1.690 + 1.691 +bool 1.692 +HTMLTableAccessible::IsColSelected(uint32_t aColIdx) 1.693 +{ 1.694 + bool isSelected = false; 1.695 + 1.696 + uint32_t rowCount = RowCount(); 1.697 + for (uint32_t rowIdx = 0; rowIdx < rowCount; rowIdx++) { 1.698 + isSelected = IsCellSelected(rowIdx, aColIdx); 1.699 + if (!isSelected) 1.700 + return false; 1.701 + } 1.702 + 1.703 + return isSelected; 1.704 +} 1.705 + 1.706 +bool 1.707 +HTMLTableAccessible::IsRowSelected(uint32_t aRowIdx) 1.708 +{ 1.709 + bool isSelected = false; 1.710 + 1.711 + uint32_t colCount = ColCount(); 1.712 + for (uint32_t colIdx = 0; colIdx < colCount; colIdx++) { 1.713 + isSelected = IsCellSelected(aRowIdx, colIdx); 1.714 + if (!isSelected) 1.715 + return false; 1.716 + } 1.717 + 1.718 + return isSelected; 1.719 +} 1.720 + 1.721 +bool 1.722 +HTMLTableAccessible::IsCellSelected(uint32_t aRowIdx, uint32_t aColIdx) 1.723 +{ 1.724 + nsTableOuterFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame()); 1.725 + if (!tableFrame) 1.726 + return false; 1.727 + 1.728 + nsTableCellFrame* cellFrame = tableFrame->GetCellFrameAt(aRowIdx, aColIdx); 1.729 + return cellFrame ? cellFrame->IsSelected() : false; 1.730 +} 1.731 + 1.732 +void 1.733 +HTMLTableAccessible::SelectRow(uint32_t aRowIdx) 1.734 +{ 1.735 + DebugOnly<nsresult> rv = 1.736 + RemoveRowsOrColumnsFromSelection(aRowIdx, 1.737 + nsISelectionPrivate::TABLESELECTION_ROW, 1.738 + true); 1.739 + NS_ASSERTION(NS_SUCCEEDED(rv), 1.740 + "RemoveRowsOrColumnsFromSelection() Shouldn't fail!"); 1.741 + 1.742 + AddRowOrColumnToSelection(aRowIdx, nsISelectionPrivate::TABLESELECTION_ROW); 1.743 +} 1.744 + 1.745 +void 1.746 +HTMLTableAccessible::SelectCol(uint32_t aColIdx) 1.747 +{ 1.748 + DebugOnly<nsresult> rv = 1.749 + RemoveRowsOrColumnsFromSelection(aColIdx, 1.750 + nsISelectionPrivate::TABLESELECTION_COLUMN, 1.751 + true); 1.752 + NS_ASSERTION(NS_SUCCEEDED(rv), 1.753 + "RemoveRowsOrColumnsFromSelection() Shouldn't fail!"); 1.754 + 1.755 + AddRowOrColumnToSelection(aColIdx, nsISelectionPrivate::TABLESELECTION_COLUMN); 1.756 +} 1.757 + 1.758 +void 1.759 +HTMLTableAccessible::UnselectRow(uint32_t aRowIdx) 1.760 +{ 1.761 + RemoveRowsOrColumnsFromSelection(aRowIdx, 1.762 + nsISelectionPrivate::TABLESELECTION_ROW, 1.763 + false); 1.764 +} 1.765 + 1.766 +void 1.767 +HTMLTableAccessible::UnselectCol(uint32_t aColIdx) 1.768 +{ 1.769 + RemoveRowsOrColumnsFromSelection(aColIdx, 1.770 + nsISelectionPrivate::TABLESELECTION_COLUMN, 1.771 + false); 1.772 +} 1.773 + 1.774 +nsresult 1.775 +HTMLTableAccessible::AddRowOrColumnToSelection(int32_t aIndex, uint32_t aTarget) 1.776 +{ 1.777 + bool doSelectRow = (aTarget == nsISelectionPrivate::TABLESELECTION_ROW); 1.778 + 1.779 + nsTableOuterFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame()); 1.780 + if (!tableFrame) 1.781 + return NS_OK; 1.782 + 1.783 + uint32_t count = 0; 1.784 + if (doSelectRow) 1.785 + count = ColCount(); 1.786 + else 1.787 + count = RowCount(); 1.788 + 1.789 + nsIPresShell* presShell(mDoc->PresShell()); 1.790 + nsRefPtr<nsFrameSelection> tableSelection = 1.791 + const_cast<nsFrameSelection*>(presShell->ConstFrameSelection()); 1.792 + 1.793 + for (uint32_t idx = 0; idx < count; idx++) { 1.794 + int32_t rowIdx = doSelectRow ? aIndex : idx; 1.795 + int32_t colIdx = doSelectRow ? idx : aIndex; 1.796 + nsTableCellFrame* cellFrame = tableFrame->GetCellFrameAt(rowIdx, colIdx); 1.797 + if (cellFrame && !cellFrame->IsSelected()) { 1.798 + nsresult rv = tableSelection->SelectCellElement(cellFrame->GetContent()); 1.799 + NS_ENSURE_SUCCESS(rv, rv); 1.800 + } 1.801 + } 1.802 + 1.803 + return NS_OK; 1.804 +} 1.805 + 1.806 +nsresult 1.807 +HTMLTableAccessible::RemoveRowsOrColumnsFromSelection(int32_t aIndex, 1.808 + uint32_t aTarget, 1.809 + bool aIsOuter) 1.810 +{ 1.811 + nsTableOuterFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame()); 1.812 + if (!tableFrame) 1.813 + return NS_OK; 1.814 + 1.815 + nsIPresShell* presShell(mDoc->PresShell()); 1.816 + nsRefPtr<nsFrameSelection> tableSelection = 1.817 + const_cast<nsFrameSelection*>(presShell->ConstFrameSelection()); 1.818 + 1.819 + bool doUnselectRow = (aTarget == nsISelectionPrivate::TABLESELECTION_ROW); 1.820 + uint32_t count = doUnselectRow ? ColCount() : RowCount(); 1.821 + 1.822 + int32_t startRowIdx = doUnselectRow ? aIndex : 0; 1.823 + int32_t endRowIdx = doUnselectRow ? aIndex : count - 1; 1.824 + int32_t startColIdx = doUnselectRow ? 0 : aIndex; 1.825 + int32_t endColIdx = doUnselectRow ? count - 1 : aIndex; 1.826 + 1.827 + if (aIsOuter) 1.828 + return tableSelection->RestrictCellsToSelection(mContent, 1.829 + startRowIdx, startColIdx, 1.830 + endRowIdx, endColIdx); 1.831 + 1.832 + return tableSelection->RemoveCellsFromSelection(mContent, 1.833 + startRowIdx, startColIdx, 1.834 + endRowIdx, endColIdx); 1.835 +} 1.836 + 1.837 +void 1.838 +HTMLTableAccessible::Description(nsString& aDescription) 1.839 +{ 1.840 + // Helpful for debugging layout vs. data tables 1.841 + aDescription.Truncate(); 1.842 + Accessible::Description(aDescription); 1.843 + if (!aDescription.IsEmpty()) 1.844 + return; 1.845 + 1.846 + // Use summary as description if it weren't used as a name. 1.847 + // XXX: get rid code duplication with NameInternal(). 1.848 + Accessible* caption = Caption(); 1.849 + if (caption) { 1.850 + nsIContent* captionContent = caption->GetContent(); 1.851 + if (captionContent) { 1.852 + nsAutoString captionText; 1.853 + nsTextEquivUtils::AppendTextEquivFromContent(this, captionContent, 1.854 + &captionText); 1.855 + 1.856 + if (!captionText.IsEmpty()) { // summary isn't used as a name. 1.857 + mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::summary, 1.858 + aDescription); 1.859 + } 1.860 + } 1.861 + } 1.862 + 1.863 +#ifdef SHOW_LAYOUT_HEURISTIC 1.864 + if (aDescription.IsEmpty()) { 1.865 + bool isProbablyForLayout = IsProbablyLayoutTable(); 1.866 + aDescription = mLayoutHeuristic; 1.867 + } 1.868 + printf("\nTABLE: %s\n", NS_ConvertUTF16toUTF8(mLayoutHeuristic).get()); 1.869 +#endif 1.870 +} 1.871 + 1.872 +bool 1.873 +HTMLTableAccessible::HasDescendant(const nsAString& aTagName, bool aAllowEmpty) 1.874 +{ 1.875 + nsCOMPtr<nsIDOMElement> tableElt(do_QueryInterface(mContent)); 1.876 + NS_ENSURE_TRUE(tableElt, false); 1.877 + 1.878 + nsCOMPtr<nsIDOMHTMLCollection> nodeList; 1.879 + tableElt->GetElementsByTagName(aTagName, getter_AddRefs(nodeList)); 1.880 + NS_ENSURE_TRUE(nodeList, false); 1.881 + 1.882 + nsCOMPtr<nsIDOMNode> foundItem; 1.883 + nodeList->Item(0, getter_AddRefs(foundItem)); 1.884 + if (!foundItem) 1.885 + return false; 1.886 + 1.887 + if (aAllowEmpty) 1.888 + return true; 1.889 + 1.890 + // Make sure that the item we found has contents and either has multiple 1.891 + // children or the found item is not a whitespace-only text node. 1.892 + nsCOMPtr<nsIContent> foundItemContent = do_QueryInterface(foundItem); 1.893 + if (foundItemContent->GetChildCount() > 1) 1.894 + return true; // Treat multiple child nodes as non-empty 1.895 + 1.896 + nsIContent *innerItemContent = foundItemContent->GetFirstChild(); 1.897 + if (innerItemContent && !innerItemContent->TextIsOnlyWhitespace()) 1.898 + return true; 1.899 + 1.900 + // If we found more than one node then return true not depending on 1.901 + // aAllowEmpty flag. 1.902 + // XXX it might be dummy but bug 501375 where we changed this addresses 1.903 + // performance problems only. Note, currently 'aAllowEmpty' flag is used for 1.904 + // caption element only. On another hand we create accessible object for 1.905 + // the first entry of caption element (see 1.906 + // HTMLTableAccessible::CacheChildren). 1.907 + nodeList->Item(1, getter_AddRefs(foundItem)); 1.908 + return !!foundItem; 1.909 +} 1.910 + 1.911 +bool 1.912 +HTMLTableAccessible::IsProbablyLayoutTable() 1.913 +{ 1.914 + // Implement a heuristic to determine if table is most likely used for layout 1.915 + // XXX do we want to look for rowspan or colspan, especialy that span all but a couple cells 1.916 + // at the beginning or end of a row/col, and especially when they occur at the edge of a table? 1.917 + // XXX expose this info via object attributes to AT-SPI 1.918 + 1.919 + // XXX For now debugging descriptions are always on via SHOW_LAYOUT_HEURISTIC 1.920 + // This will allow release trunk builds to be used by testers to refine the algorithm 1.921 + // Change to |#define SHOW_LAYOUT_HEURISTIC DEBUG| before final release 1.922 +#ifdef SHOW_LAYOUT_HEURISTIC 1.923 +#define RETURN_LAYOUT_ANSWER(isLayout, heuristic) \ 1.924 + { \ 1.925 + mLayoutHeuristic = isLayout ? \ 1.926 + NS_LITERAL_STRING("layout table: " heuristic) : \ 1.927 + NS_LITERAL_STRING("data table: " heuristic); \ 1.928 + return isLayout; \ 1.929 + } 1.930 +#else 1.931 +#define RETURN_LAYOUT_ANSWER(isLayout, heuristic) { return isLayout; } 1.932 +#endif 1.933 + 1.934 + DocAccessible* docAccessible = Document(); 1.935 + if (docAccessible) { 1.936 + uint64_t docState = docAccessible->State(); 1.937 + if (docState & states::EDITABLE) { // Need to see all elements while document is being edited 1.938 + RETURN_LAYOUT_ANSWER(false, "In editable document"); 1.939 + } 1.940 + } 1.941 + 1.942 + // Check to see if an ARIA role overrides the role from native markup, 1.943 + // but for which we still expose table semantics (treegrid, for example). 1.944 + if (Role() != roles::TABLE) 1.945 + RETURN_LAYOUT_ANSWER(false, "Has role attribute"); 1.946 + 1.947 + if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::role)) { 1.948 + // Role attribute is present, but overridden roles have already been dealt with. 1.949 + // Only landmarks and other roles that don't override the role from native 1.950 + // markup are left to deal with here. 1.951 + RETURN_LAYOUT_ANSWER(false, "Has role attribute, weak role, and role is table"); 1.952 + } 1.953 + 1.954 + if (mContent->Tag() != nsGkAtoms::table) 1.955 + RETURN_LAYOUT_ANSWER(true, "table built by CSS display:table style"); 1.956 + 1.957 + // Check if datatable attribute has "0" value. 1.958 + if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::datatable, 1.959 + NS_LITERAL_STRING("0"), eCaseMatters)) { 1.960 + RETURN_LAYOUT_ANSWER(true, "Has datatable = 0 attribute, it's for layout"); 1.961 + } 1.962 + 1.963 + // Check for legitimate data table attributes. 1.964 + nsAutoString summary; 1.965 + if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::summary, summary) && 1.966 + !summary.IsEmpty()) 1.967 + RETURN_LAYOUT_ANSWER(false, "Has summary -- legitimate table structures"); 1.968 + 1.969 + // Check for legitimate data table elements. 1.970 + Accessible* caption = FirstChild(); 1.971 + if (caption && caption->Role() == roles::CAPTION && caption->HasChildren()) 1.972 + RETURN_LAYOUT_ANSWER(false, "Not empty caption -- legitimate table structures"); 1.973 + 1.974 + for (nsIContent* childElm = mContent->GetFirstChild(); childElm; 1.975 + childElm = childElm->GetNextSibling()) { 1.976 + if (!childElm->IsHTML()) 1.977 + continue; 1.978 + 1.979 + if (childElm->Tag() == nsGkAtoms::col || 1.980 + childElm->Tag() == nsGkAtoms::colgroup || 1.981 + childElm->Tag() == nsGkAtoms::tfoot || 1.982 + childElm->Tag() == nsGkAtoms::thead) { 1.983 + RETURN_LAYOUT_ANSWER(false, 1.984 + "Has col, colgroup, tfoot or thead -- legitimate table structures"); 1.985 + } 1.986 + 1.987 + if (childElm->Tag() == nsGkAtoms::tbody) { 1.988 + for (nsIContent* rowElm = childElm->GetFirstChild(); rowElm; 1.989 + rowElm = rowElm->GetNextSibling()) { 1.990 + if (rowElm->IsHTML() && rowElm->Tag() == nsGkAtoms::tr) { 1.991 + for (nsIContent* cellElm = rowElm->GetFirstChild(); cellElm; 1.992 + cellElm = cellElm->GetNextSibling()) { 1.993 + if (cellElm->IsHTML()) { 1.994 + 1.995 + if (cellElm->NodeInfo()->Equals(nsGkAtoms::th)) { 1.996 + RETURN_LAYOUT_ANSWER(false, 1.997 + "Has th -- legitimate table structures"); 1.998 + } 1.999 + 1.1000 + if (cellElm->HasAttr(kNameSpaceID_None, nsGkAtoms::headers) || 1.1001 + cellElm->HasAttr(kNameSpaceID_None, nsGkAtoms::scope) || 1.1002 + cellElm->HasAttr(kNameSpaceID_None, nsGkAtoms::abbr)) { 1.1003 + RETURN_LAYOUT_ANSWER(false, 1.1004 + "Has headers, scope, or abbr attribute -- legitimate table structures"); 1.1005 + } 1.1006 + 1.1007 + Accessible* cell = mDoc->GetAccessible(cellElm); 1.1008 + if (cell && cell->ChildCount() == 1 && 1.1009 + cell->FirstChild()->IsAbbreviation()) { 1.1010 + RETURN_LAYOUT_ANSWER(false, 1.1011 + "has abbr -- legitimate table structures"); 1.1012 + } 1.1013 + } 1.1014 + } 1.1015 + } 1.1016 + } 1.1017 + } 1.1018 + } 1.1019 + 1.1020 + if (HasDescendant(NS_LITERAL_STRING("table"))) { 1.1021 + RETURN_LAYOUT_ANSWER(true, "Has a nested table within it"); 1.1022 + } 1.1023 + 1.1024 + // If only 1 column or only 1 row, it's for layout 1.1025 + int32_t columns, rows; 1.1026 + GetColumnCount(&columns); 1.1027 + if (columns <=1) { 1.1028 + RETURN_LAYOUT_ANSWER(true, "Has only 1 column"); 1.1029 + } 1.1030 + GetRowCount(&rows); 1.1031 + if (rows <=1) { 1.1032 + RETURN_LAYOUT_ANSWER(true, "Has only 1 row"); 1.1033 + } 1.1034 + 1.1035 + // Check for many columns 1.1036 + if (columns >= 5) { 1.1037 + RETURN_LAYOUT_ANSWER(false, ">=5 columns"); 1.1038 + } 1.1039 + 1.1040 + // Now we know there are 2-4 columns and 2 or more rows 1.1041 + // Check to see if there are visible borders on the cells 1.1042 + // XXX currently, we just check the first cell -- do we really need to do more? 1.1043 + nsTableOuterFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame()); 1.1044 + if (!tableFrame) 1.1045 + RETURN_LAYOUT_ANSWER(false, "table with no frame!"); 1.1046 + 1.1047 + nsIFrame* cellFrame = tableFrame->GetCellFrameAt(0, 0); 1.1048 + if (!cellFrame) 1.1049 + RETURN_LAYOUT_ANSWER(false, "table's first cell has no frame!"); 1.1050 + 1.1051 + nsMargin border; 1.1052 + cellFrame->GetBorder(border); 1.1053 + if (border.top && border.bottom && border.left && border.right) { 1.1054 + RETURN_LAYOUT_ANSWER(false, "Has nonzero border-width on table cell"); 1.1055 + } 1.1056 + 1.1057 + /** 1.1058 + * Rules for non-bordered tables with 2-4 columns and 2+ rows from here on forward 1.1059 + */ 1.1060 + 1.1061 + // Check for styled background color across rows (alternating background 1.1062 + // color is a common feature for data tables). 1.1063 + uint32_t childCount = ChildCount(); 1.1064 + nscolor rowColor = 0; 1.1065 + nscolor prevRowColor; 1.1066 + for (uint32_t childIdx = 0; childIdx < childCount; childIdx++) { 1.1067 + Accessible* child = GetChildAt(childIdx); 1.1068 + if (child->Role() == roles::ROW) { 1.1069 + prevRowColor = rowColor; 1.1070 + nsIFrame* rowFrame = child->GetFrame(); 1.1071 + rowColor = rowFrame->StyleBackground()->mBackgroundColor; 1.1072 + 1.1073 + if (childIdx > 0 && prevRowColor != rowColor) 1.1074 + RETURN_LAYOUT_ANSWER(false, "2 styles of row background color, non-bordered"); 1.1075 + } 1.1076 + } 1.1077 + 1.1078 + // Check for many rows 1.1079 + const int32_t kMaxLayoutRows = 20; 1.1080 + if (rows > kMaxLayoutRows) { // A ton of rows, this is probably for data 1.1081 + RETURN_LAYOUT_ANSWER(false, ">= kMaxLayoutRows (20) and non-bordered"); 1.1082 + } 1.1083 + 1.1084 + // Check for very wide table. 1.1085 + nsIFrame* documentFrame = Document()->GetFrame(); 1.1086 + nsSize documentSize = documentFrame->GetSize(); 1.1087 + if (documentSize.width > 0) { 1.1088 + nsSize tableSize = GetFrame()->GetSize(); 1.1089 + int32_t percentageOfDocWidth = (100 * tableSize.width) / documentSize.width; 1.1090 + if (percentageOfDocWidth > 95) { 1.1091 + // 3-4 columns, no borders, not a lot of rows, and 95% of the doc's width 1.1092 + // Probably for layout 1.1093 + RETURN_LAYOUT_ANSWER(true, 1.1094 + "<= 4 columns, table width is 95% of document width"); 1.1095 + } 1.1096 + } 1.1097 + 1.1098 + // Two column rules 1.1099 + if (rows * columns <= 10) { 1.1100 + RETURN_LAYOUT_ANSWER(true, "2-4 columns, 10 cells or less, non-bordered"); 1.1101 + } 1.1102 + 1.1103 + if (HasDescendant(NS_LITERAL_STRING("embed")) || 1.1104 + HasDescendant(NS_LITERAL_STRING("object")) || 1.1105 + HasDescendant(NS_LITERAL_STRING("applet")) || 1.1106 + HasDescendant(NS_LITERAL_STRING("iframe"))) { 1.1107 + RETURN_LAYOUT_ANSWER(true, "Has no borders, and has iframe, object, applet or iframe, typical of advertisements"); 1.1108 + } 1.1109 + 1.1110 + RETURN_LAYOUT_ANSWER(false, "no layout factor strong enough, so will guess data"); 1.1111 +} 1.1112 + 1.1113 + 1.1114 +//////////////////////////////////////////////////////////////////////////////// 1.1115 +// HTMLCaptionAccessible 1.1116 +//////////////////////////////////////////////////////////////////////////////// 1.1117 + 1.1118 +Relation 1.1119 +HTMLCaptionAccessible::RelationByType(RelationType aType) 1.1120 +{ 1.1121 + Relation rel = HyperTextAccessible::RelationByType(aType); 1.1122 + if (aType == RelationType::LABEL_FOR) 1.1123 + rel.AppendTarget(Parent()); 1.1124 + 1.1125 + return rel; 1.1126 +} 1.1127 + 1.1128 +role 1.1129 +HTMLCaptionAccessible::NativeRole() 1.1130 +{ 1.1131 + return roles::CAPTION; 1.1132 +}