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.

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

mercurial