accessible/src/html/HTMLTableAccessible.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include "HTMLTableAccessible.h"
     8 #include "mozilla/DebugOnly.h"
    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"
    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"
    40 using namespace mozilla;
    41 using namespace mozilla::a11y;
    43 ////////////////////////////////////////////////////////////////////////////////
    44 // HTMLTableCellAccessible
    45 ////////////////////////////////////////////////////////////////////////////////
    47 HTMLTableCellAccessible::
    48   HTMLTableCellAccessible(nsIContent* aContent, DocAccessible* aDoc) :
    49   HyperTextAccessibleWrap(aContent, aDoc), xpcAccessibleTableCell(this)
    50 {
    51   mGenericTypes |= eTableCell;
    52 }
    54 ////////////////////////////////////////////////////////////////////////////////
    55 // HTMLTableCellAccessible: nsISupports implementation
    57 NS_IMPL_ISUPPORTS_INHERITED(HTMLTableCellAccessible,
    58                             HyperTextAccessible,
    59                             nsIAccessibleTableCell)
    61 ////////////////////////////////////////////////////////////////////////////////
    62 // HTMLTableCellAccessible: Accessible implementation
    64 void
    65 HTMLTableCellAccessible::Shutdown()
    66 {
    67   mTableCell = nullptr;
    68   HyperTextAccessibleWrap::Shutdown();
    69 }
    71 role
    72 HTMLTableCellAccessible::NativeRole()
    73 {
    74   return roles::CELL;
    75 }
    77 uint64_t
    78 HTMLTableCellAccessible::NativeState()
    79 {
    80   uint64_t state = HyperTextAccessibleWrap::NativeState();
    82   nsIFrame *frame = mContent->GetPrimaryFrame();
    83   NS_ASSERTION(frame, "No frame for valid cell accessible!");
    85   if (frame && frame->IsSelected())
    86     state |= states::SELECTED;
    88   return state;
    89 }
    91 uint64_t
    92 HTMLTableCellAccessible::NativeInteractiveState() const
    93 {
    94   return HyperTextAccessibleWrap::NativeInteractiveState() | states::SELECTABLE;
    95 }
    97 already_AddRefed<nsIPersistentProperties>
    98 HTMLTableCellAccessible::NativeAttributes()
    99 {
   100   nsCOMPtr<nsIPersistentProperties> attributes =
   101     HyperTextAccessibleWrap::NativeAttributes();
   103   // table-cell-index attribute
   104   TableAccessible* table = Table();
   105   if (!table)
   106     return attributes.forget();
   108   int32_t rowIdx = -1, colIdx = -1;
   109   nsresult rv = GetCellIndexes(rowIdx, colIdx);
   110   if (NS_FAILED(rv))
   111     return attributes.forget();
   113   nsAutoString stringIdx;
   114   stringIdx.AppendInt(table->CellIndexAt(rowIdx, colIdx));
   115   nsAccUtils::SetAccAttr(attributes, nsGkAtoms::tableCellIndex, stringIdx);
   117   // abbr attribute
   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);
   135   if (!abbrText.IsEmpty())
   136     nsAccUtils::SetAccAttr(attributes, nsGkAtoms::abbr, abbrText);
   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);
   144   return attributes.forget();
   145 }
   147 ////////////////////////////////////////////////////////////////////////////////
   148 // HTMLTableCellAccessible: nsIAccessibleTableCell implementation
   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   }
   160   return nullptr;
   161 }
   163 uint32_t
   164 HTMLTableCellAccessible::ColIdx() const
   165 {
   166   nsITableCellLayout* cellLayout = GetCellLayout();
   167   NS_ENSURE_TRUE(cellLayout, 0);
   169   int32_t colIdx = 0;
   170   cellLayout->GetColIndex(colIdx);
   171   return colIdx > 0 ? static_cast<uint32_t>(colIdx) : 0;
   172 }
   174 uint32_t
   175 HTMLTableCellAccessible::RowIdx() const
   176 {
   177   nsITableCellLayout* cellLayout = GetCellLayout();
   178   NS_ENSURE_TRUE(cellLayout, 0);
   180   int32_t rowIdx = 0;
   181   cellLayout->GetRowIndex(rowIdx);
   182   return rowIdx > 0 ? static_cast<uint32_t>(rowIdx) : 0;
   183 }
   185 uint32_t
   186 HTMLTableCellAccessible::ColExtent() const
   187 {
   188   int32_t rowIdx = -1, colIdx = -1;
   189   GetCellIndexes(rowIdx, colIdx);
   191   TableAccessible* table = Table();
   192   NS_ASSERTION(table, "cell not in a table!");
   193   if (!table)
   194     return 0;
   196   return table->ColExtentAt(rowIdx, colIdx);
   197 }
   199 uint32_t
   200 HTMLTableCellAccessible::RowExtent() const
   201 {
   202   int32_t rowIdx = -1, colIdx = -1;
   203   GetCellIndexes(rowIdx, colIdx);
   205   TableAccessible* table = Table();
   206   NS_ASSERTION(table, "cell not in atable!");
   207   if (!table)
   208     return 0;
   210   return table->RowExtentAt(rowIdx, colIdx);
   211 }
   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   }
   230   if (aCells->IsEmpty())
   231     TableCellAccessible::ColHeaderCells(aCells);
   232 }
   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   }
   251   if (aCells->IsEmpty())
   252     TableCellAccessible::RowHeaderCells(aCells);
   253 }
   255 bool
   256 HTMLTableCellAccessible::Selected()
   257 {
   258   int32_t rowIdx = -1, colIdx = -1;
   259   GetCellIndexes(rowIdx, colIdx);
   261   TableAccessible* table = Table();
   262   NS_ENSURE_TRUE(table, false);
   264   return table->IsCellSelected(rowIdx, colIdx);
   265 }
   267 ////////////////////////////////////////////////////////////////////////////////
   268 // HTMLTableCellAccessible: protected implementation
   270 nsITableCellLayout*
   271 HTMLTableCellAccessible::GetCellLayout() const
   272 {
   273   return do_QueryFrame(mContent->GetPrimaryFrame());
   274 }
   276 nsresult
   277 HTMLTableCellAccessible::GetCellIndexes(int32_t& aRowIdx, int32_t& aColIdx) const
   278 {
   279   nsITableCellLayout *cellLayout = GetCellLayout();
   280   NS_ENSURE_STATE(cellLayout);
   282   return cellLayout->GetCellIndexes(aRowIdx, aColIdx);
   283 }
   286 ////////////////////////////////////////////////////////////////////////////////
   287 // HTMLTableHeaderCellAccessible
   288 ////////////////////////////////////////////////////////////////////////////////
   290 HTMLTableHeaderCellAccessible::
   291   HTMLTableHeaderCellAccessible(nsIContent* aContent, DocAccessible* aDoc) :
   292   HTMLTableCellAccessible(aContent, aDoc)
   293 {
   294 }
   296 ////////////////////////////////////////////////////////////////////////////////
   297 // HTMLTableHeaderCellAccessible: Accessible implementation
   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);
   309   switch (valueIdx) {
   310     case 0:
   311       return roles::COLUMNHEADER;
   312     case 1:
   313       return roles::ROWHEADER;
   314   }
   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   }
   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   }
   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   }
   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 }
   347 ////////////////////////////////////////////////////////////////////////////////
   348 // HTMLTableRowAccessible
   349 ////////////////////////////////////////////////////////////////////////////////
   351 NS_IMPL_ISUPPORTS_INHERITED0(HTMLTableRowAccessible, Accessible)
   353 role
   354 HTMLTableRowAccessible::NativeRole()
   355 {
   356   return roles::ROW;
   357 }
   359 ////////////////////////////////////////////////////////////////////////////////
   360 // HTMLTableAccessible
   361 ////////////////////////////////////////////////////////////////////////////////
   363 NS_IMPL_ISUPPORTS_INHERITED(HTMLTableAccessible, Accessible,
   364                             nsIAccessibleTable)
   366 ////////////////////////////////////////////////////////////////////////////////
   367 // HTMLTableAccessible: Accessible
   369 void
   370 HTMLTableAccessible::Shutdown()
   371 {
   372   mTable = nullptr;
   373   AccessibleWrap::Shutdown();
   374 }
   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);
   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 }
   396 role
   397 HTMLTableAccessible::NativeRole()
   398 {
   399   return roles::TABLE;
   400 }
   402 uint64_t
   403 HTMLTableAccessible::NativeState()
   404 {
   405   return Accessible::NativeState() | states::READONLY;
   406 }
   408 ENameValueFlag
   409 HTMLTableAccessible::NativeName(nsString& aName)
   410 {
   411   ENameValueFlag nameFlag = Accessible::NativeName(aName);
   412   if (!aName.IsEmpty())
   413     return nameFlag;
   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   }
   426   // If no caption then use summary as a name.
   427   mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::summary, aName);
   428   return eNameOK;
   429 }
   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   }
   442   return attributes.forget();
   443 }
   445 ////////////////////////////////////////////////////////////////////////////////
   446 // HTMLTableAccessible: nsIAccessible implementation
   448 Relation
   449 HTMLTableAccessible::RelationByType(RelationType aType)
   450 {
   451   Relation rel = AccessibleWrap::RelationByType(aType);
   452   if (aType == RelationType::LABELLED_BY)
   453     rel.AppendTarget(Caption());
   455   return rel;
   456 }
   458 ////////////////////////////////////////////////////////////////////////////////
   459 // HTMLTableAccessible: nsIAccessibleTable implementation
   461 Accessible*
   462 HTMLTableAccessible::Caption()
   463 {
   464   Accessible* child = mChildren.SafeElementAt(0, nullptr);
   465   return child && child->Role() == roles::CAPTION ? child : nullptr;
   466 }
   468 void
   469 HTMLTableAccessible::Summary(nsString& aSummary)
   470 {
   471   dom::HTMLTableElement* table = dom::HTMLTableElement::FromContent(mContent);
   473   if (table)
   474     table->GetSummary(aSummary);
   475 }
   477 uint32_t
   478 HTMLTableAccessible::ColCount()
   479 {
   480   nsTableOuterFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame());
   481   return tableFrame ? tableFrame->GetColCount() : 0;
   482 }
   484 uint32_t
   485 HTMLTableAccessible::RowCount()
   486 {
   487   nsTableOuterFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame());
   488   return tableFrame ? tableFrame->GetRowCount() : 0;
   489 }
   491 uint32_t
   492 HTMLTableAccessible::SelectedCellCount()
   493 {
   494   nsTableOuterFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame());
   495   if (!tableFrame)
   496     return 0;
   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;
   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   }
   514   return count;
   515 }
   517 uint32_t
   518 HTMLTableAccessible::SelectedColCount()
   519 {
   520   uint32_t count = 0, colCount = ColCount();
   522   for (uint32_t colIdx = 0; colIdx < colCount; colIdx++)
   523     if (IsColSelected(colIdx))
   524       count++;
   526   return count;
   527 }
   529 uint32_t
   530 HTMLTableAccessible::SelectedRowCount()
   531 {
   532   uint32_t count = 0, rowCount = RowCount();
   534   for (uint32_t rowIdx = 0; rowIdx < rowCount; rowIdx++)
   535     if (IsRowSelected(rowIdx))
   536       count++;
   538   return count;
   539 }
   541 void
   542 HTMLTableAccessible::SelectedCells(nsTArray<Accessible*>* aCells)
   543 {
   544   nsTableOuterFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame());
   545   if (!tableFrame)
   546     return;
   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;
   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;
   562       Accessible* cell = mDoc->GetAccessible(cellFrame->GetContent());
   563         aCells->AppendElement(cell);
   564     }
   565   }
   566 }
   568 void
   569 HTMLTableAccessible::SelectedCellIndices(nsTArray<uint32_t>* aCells)
   570 {
   571   nsTableOuterFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame());
   572   if (!tableFrame)
   573     return;
   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;
   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 }
   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 }
   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 }
   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;
   617   nsIContent* cellContent = tableFrame->GetCellAt(aRowIdx, aColIdx);
   618   Accessible* cell = mDoc->GetAccessible(cellContent);
   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 }
   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;
   632   return tableFrame->GetIndexByRowAndColumn(aRowIdx, aColIdx);
   633 }
   635 int32_t
   636 HTMLTableAccessible::ColIndexAt(uint32_t aCellIdx)
   637 {
   638   nsTableOuterFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame());
   639   if (!tableFrame)
   640     return -1;
   642   int32_t rowIdx = -1, colIdx = -1;
   643   tableFrame->GetRowAndColumnByIndex(aCellIdx, &rowIdx, &colIdx);
   644   return colIdx;
   645 }
   647 int32_t
   648 HTMLTableAccessible::RowIndexAt(uint32_t aCellIdx)
   649 {
   650   nsTableOuterFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame());
   651   if (!tableFrame)
   652     return -1;
   654   int32_t rowIdx = -1, colIdx = -1;
   655   tableFrame->GetRowAndColumnByIndex(aCellIdx, &rowIdx, &colIdx);
   656   return rowIdx;
   657 }
   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 }
   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;
   675   return tableFrame->GetEffectiveColSpanAt(aRowIdx, aColIdx);
   676 }
   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;
   685   return tableFrame->GetEffectiveRowSpanAt(aRowIdx, aColIdx);
   686 }
   688 bool
   689 HTMLTableAccessible::IsColSelected(uint32_t aColIdx)
   690 {
   691   bool isSelected = false;
   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   }
   700   return isSelected;
   701 }
   703 bool
   704 HTMLTableAccessible::IsRowSelected(uint32_t aRowIdx)
   705 {
   706   bool isSelected = false;
   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   }
   715   return isSelected;
   716 }
   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;
   725   nsTableCellFrame* cellFrame = tableFrame->GetCellFrameAt(aRowIdx, aColIdx);
   726   return cellFrame ? cellFrame->IsSelected() : false;
   727 }
   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!");
   739   AddRowOrColumnToSelection(aRowIdx, nsISelectionPrivate::TABLESELECTION_ROW);
   740 }
   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!");
   752   AddRowOrColumnToSelection(aColIdx, nsISelectionPrivate::TABLESELECTION_COLUMN);
   753 }
   755 void
   756 HTMLTableAccessible::UnselectRow(uint32_t aRowIdx)
   757 {
   758   RemoveRowsOrColumnsFromSelection(aRowIdx,
   759                                    nsISelectionPrivate::TABLESELECTION_ROW,
   760                                    false);
   761 }
   763 void
   764 HTMLTableAccessible::UnselectCol(uint32_t aColIdx)
   765 {
   766   RemoveRowsOrColumnsFromSelection(aColIdx,
   767                                    nsISelectionPrivate::TABLESELECTION_COLUMN,
   768                                    false);
   769 }
   771 nsresult
   772 HTMLTableAccessible::AddRowOrColumnToSelection(int32_t aIndex, uint32_t aTarget)
   773 {
   774   bool doSelectRow = (aTarget == nsISelectionPrivate::TABLESELECTION_ROW);
   776   nsTableOuterFrame* tableFrame = do_QueryFrame(mContent->GetPrimaryFrame());
   777   if (!tableFrame)
   778     return NS_OK;
   780   uint32_t count = 0;
   781   if (doSelectRow)
   782     count = ColCount();
   783   else
   784     count = RowCount();
   786   nsIPresShell* presShell(mDoc->PresShell());
   787   nsRefPtr<nsFrameSelection> tableSelection =
   788     const_cast<nsFrameSelection*>(presShell->ConstFrameSelection());
   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   }
   800   return NS_OK;
   801 }
   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;
   812   nsIPresShell* presShell(mDoc->PresShell());
   813   nsRefPtr<nsFrameSelection> tableSelection =
   814     const_cast<nsFrameSelection*>(presShell->ConstFrameSelection());
   816   bool doUnselectRow = (aTarget == nsISelectionPrivate::TABLESELECTION_ROW);
   817   uint32_t count = doUnselectRow ? ColCount() : RowCount();
   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;
   824   if (aIsOuter)
   825     return tableSelection->RestrictCellsToSelection(mContent,
   826                                                     startRowIdx, startColIdx,
   827                                                     endRowIdx, endColIdx);
   829   return tableSelection->RemoveCellsFromSelection(mContent,
   830                                                   startRowIdx, startColIdx,
   831                                                   endRowIdx, endColIdx);
   832 }
   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;
   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);
   853       if (!captionText.IsEmpty()) { // summary isn't used as a name.
   854         mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::summary,
   855                           aDescription);
   856       }
   857     }
   858   }
   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 }
   869 bool
   870 HTMLTableAccessible::HasDescendant(const nsAString& aTagName, bool aAllowEmpty)
   871 {
   872   nsCOMPtr<nsIDOMElement> tableElt(do_QueryInterface(mContent));
   873   NS_ENSURE_TRUE(tableElt, false);
   875   nsCOMPtr<nsIDOMHTMLCollection> nodeList;
   876   tableElt->GetElementsByTagName(aTagName, getter_AddRefs(nodeList));
   877   NS_ENSURE_TRUE(nodeList, false);
   879   nsCOMPtr<nsIDOMNode> foundItem;
   880   nodeList->Item(0, getter_AddRefs(foundItem));
   881   if (!foundItem)
   882     return false;
   884   if (aAllowEmpty)
   885     return true;
   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
   893   nsIContent *innerItemContent = foundItemContent->GetFirstChild();
   894   if (innerItemContent && !innerItemContent->TextIsOnlyWhitespace())
   895     return true;
   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 }
   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
   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
   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   }
   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");
   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   }
   951   if (mContent->Tag() != nsGkAtoms::table)
   952     RETURN_LAYOUT_ANSWER(true, "table built by CSS display:table style");
   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   }
   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");
   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");
   971   for (nsIContent* childElm = mContent->GetFirstChild(); childElm;
   972        childElm = childElm->GetNextSibling()) {
   973     if (!childElm->IsHTML())
   974       continue;
   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     }
   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()) {
   992               if (cellElm->NodeInfo()->Equals(nsGkAtoms::th)) {
   993                 RETURN_LAYOUT_ANSWER(false,
   994                                      "Has th -- legitimate table structures");
   995               }
   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");
  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");
  1017   if (HasDescendant(NS_LITERAL_STRING("table"))) {
  1018     RETURN_LAYOUT_ANSWER(true, "Has a nested table within it");
  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");
  1027   GetRowCount(&rows);
  1028   if (rows <=1) {
  1029     RETURN_LAYOUT_ANSWER(true, "Has only 1 row");
  1032   // Check for many columns
  1033   if (columns >= 5) {
  1034     RETURN_LAYOUT_ANSWER(false, ">=5 columns");
  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!");
  1044   nsIFrame* cellFrame = tableFrame->GetCellFrameAt(0, 0);
  1045   if (!cellFrame)
  1046     RETURN_LAYOUT_ANSWER(false, "table's first cell has no frame!");
  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");
  1054   /**
  1055    * Rules for non-bordered tables with 2-4 columns and 2+ rows from here on forward
  1056    */
  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;
  1070       if (childIdx > 0 && prevRowColor != rowColor)
  1071         RETURN_LAYOUT_ANSWER(false, "2 styles of row background color, non-bordered");
  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");
  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");
  1095   // Two column rules
  1096   if (rows * columns <= 10) {
  1097     RETURN_LAYOUT_ANSWER(true, "2-4 columns, 10 cells or less, non-bordered");
  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");
  1107   RETURN_LAYOUT_ANSWER(false, "no layout factor strong enough, so will guess data");
  1111 ////////////////////////////////////////////////////////////////////////////////
  1112 // HTMLCaptionAccessible
  1113 ////////////////////////////////////////////////////////////////////////////////
  1115 Relation
  1116 HTMLCaptionAccessible::RelationByType(RelationType aType)
  1118   Relation rel = HyperTextAccessible::RelationByType(aType);
  1119   if (aType == RelationType::LABEL_FOR)
  1120     rel.AppendTarget(Parent());
  1122   return rel;
  1125 role
  1126 HTMLCaptionAccessible::NativeRole()
  1128   return roles::CAPTION;

mercurial