accessible/src/html/HTMLTableAccessible.cpp

changeset 0
6474c204b198
     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 +}

mercurial