XULTreeAccessible.cpp (30450B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "XULTreeAccessible.h" 8 9 #include "LocalAccessible-inl.h" 10 #include "DocAccessible-inl.h" 11 #include "nsAccCache.h" 12 #include "nsAccUtils.h" 13 #include "nsCoreUtils.h" 14 #include "nsEventShell.h" 15 #include "DocAccessible.h" 16 #include "Relation.h" 17 #include "mozilla/a11y/Role.h" 18 #include "States.h" 19 #include "XULTreeGridAccessible.h" 20 #include "nsQueryObject.h" 21 22 #include "nsComponentManagerUtils.h" 23 #include "nsIAutoCompletePopup.h" 24 #include "nsIDOMXULMenuListElement.h" 25 #include "nsITreeSelection.h" 26 #include "nsTreeBodyFrame.h" 27 #include "nsTreeColumns.h" 28 #include "nsTreeUtils.h" 29 #include "mozilla/PresShell.h" 30 #include "mozilla/dom/XULTreeElementBinding.h" 31 32 using namespace mozilla::a11y; 33 34 //////////////////////////////////////////////////////////////////////////////// 35 // XULTreeAccessible 36 //////////////////////////////////////////////////////////////////////////////// 37 38 XULTreeAccessible::XULTreeAccessible(nsIContent* aContent, DocAccessible* aDoc, 39 nsTreeBodyFrame* aTreeFrame) 40 : AccessibleWrap(aContent, aDoc), 41 mAccessibleCache(kDefaultTreeCacheLength) { 42 mType = eXULTreeType; 43 mGenericTypes |= eSelect; 44 45 nsCOMPtr<nsITreeView> view = aTreeFrame->GetExistingView(); 46 mTreeView = view; 47 48 mTree = nsCoreUtils::GetTree(aContent); 49 NS_ASSERTION(mTree, "Can't get mTree!\n"); 50 51 nsIContent* parentContent = mContent->GetParent(); 52 if (parentContent) { 53 nsCOMPtr<nsIAutoCompletePopup> autoCompletePopupElm = 54 do_QueryInterface(parentContent); 55 if (autoCompletePopupElm) mGenericTypes |= eAutoCompletePopup; 56 } 57 } 58 59 XULTreeAccessible::~XULTreeAccessible() {} 60 61 //////////////////////////////////////////////////////////////////////////////// 62 // XULTreeAccessible: nsISupports and cycle collection implementation 63 64 NS_IMPL_CYCLE_COLLECTION_INHERITED(XULTreeAccessible, LocalAccessible, mTree, 65 mAccessibleCache) 66 67 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(XULTreeAccessible) 68 NS_INTERFACE_MAP_END_INHERITING(LocalAccessible) 69 70 NS_IMPL_ADDREF_INHERITED(XULTreeAccessible, LocalAccessible) 71 NS_IMPL_RELEASE_INHERITED(XULTreeAccessible, LocalAccessible) 72 73 //////////////////////////////////////////////////////////////////////////////// 74 // XULTreeAccessible: LocalAccessible implementation 75 76 uint64_t XULTreeAccessible::NativeState() const { 77 // Get focus status from base class. 78 uint64_t state = LocalAccessible::NativeState(); 79 80 // readonly state 81 state |= states::READONLY; 82 83 // multiselectable state. 84 if (!mTreeView) return state; 85 86 nsCOMPtr<nsITreeSelection> selection; 87 mTreeView->GetSelection(getter_AddRefs(selection)); 88 NS_ENSURE_TRUE(selection, state); 89 90 bool isSingle = false; 91 nsresult rv = selection->GetSingle(&isSingle); 92 NS_ENSURE_SUCCESS(rv, state); 93 94 if (!isSingle) state |= states::MULTISELECTABLE; 95 96 return state; 97 } 98 99 void XULTreeAccessible::Value(nsString& aValue) const { 100 aValue.Truncate(); 101 if (!mTreeView) return; 102 103 // Return the value is the first selected child. 104 nsCOMPtr<nsITreeSelection> selection; 105 mTreeView->GetSelection(getter_AddRefs(selection)); 106 if (!selection) return; 107 108 int32_t currentIndex; 109 selection->GetCurrentIndex(¤tIndex); 110 if (currentIndex >= 0) { 111 RefPtr<nsTreeColumn> keyCol; 112 113 RefPtr<nsTreeColumns> cols = mTree->GetColumns(); 114 if (cols) keyCol = cols->GetKeyColumn(); 115 116 mTreeView->GetCellText(currentIndex, keyCol, aValue); 117 } 118 } 119 120 //////////////////////////////////////////////////////////////////////////////// 121 // XULTreeAccessible: LocalAccessible implementation 122 123 void XULTreeAccessible::Shutdown() { 124 if (mDoc && !mDoc->IsDefunct()) { 125 UnbindCacheEntriesFromDocument(mAccessibleCache); 126 } 127 128 mTree = nullptr; 129 mTreeView = nullptr; 130 131 AccessibleWrap::Shutdown(); 132 } 133 134 role XULTreeAccessible::NativeRole() const { 135 // No primary column means we're in a list. In fact, history and mail turn off 136 // the primary flag when switching to a flat view. 137 138 nsIContent* child = 139 nsTreeUtils::GetDescendantChild(mContent, nsGkAtoms::treechildren); 140 NS_ASSERTION(child, "tree without treechildren!"); 141 nsTreeBodyFrame* treeFrame = do_QueryFrame(child->GetPrimaryFrame()); 142 NS_ASSERTION(treeFrame, "xul tree accessible for tree without a frame!"); 143 if (!treeFrame) return roles::LIST; 144 145 RefPtr<nsTreeColumns> cols = treeFrame->Columns(); 146 nsTreeColumn* primaryCol = cols->GetPrimaryColumn(); 147 148 return primaryCol ? roles::OUTLINE : roles::LIST; 149 } 150 151 //////////////////////////////////////////////////////////////////////////////// 152 // XULTreeAccessible: LocalAccessible implementation (DON'T put methods here) 153 154 LocalAccessible* XULTreeAccessible::LocalChildAtPoint( 155 int32_t aX, int32_t aY, EWhichChildAtPoint aWhichChild) { 156 nsIFrame* frame = GetFrame(); 157 if (!frame) return nullptr; 158 159 nsPresContext* presContext = frame->PresContext(); 160 PresShell* presShell = presContext->PresShell(); 161 162 nsIFrame* rootFrame = presShell->GetRootFrame(); 163 NS_ENSURE_TRUE(rootFrame, nullptr); 164 165 CSSIntRect rootRect = rootFrame->GetScreenRect(); 166 167 int32_t clientX = presContext->DevPixelsToIntCSSPixels(aX) - rootRect.X(); 168 int32_t clientY = presContext->DevPixelsToIntCSSPixels(aY) - rootRect.Y(); 169 170 ErrorResult rv; 171 dom::TreeCellInfo cellInfo; 172 mTree->GetCellAt(clientX, clientY, cellInfo, rv); 173 174 // If we failed to find tree cell for the given point then it might be 175 // tree columns. 176 if (cellInfo.mRow == -1 || !cellInfo.mCol) { 177 return AccessibleWrap::LocalChildAtPoint(aX, aY, aWhichChild); 178 } 179 180 XULTreeItemAccessibleBase* child = GetTreeItemAccessible(cellInfo.mRow); 181 if (aWhichChild == EWhichChildAtPoint::DeepestChild && child) { 182 LocalAccessible* cell = child->GetCellAccessible(cellInfo.mCol); 183 if (cell) { 184 return cell; 185 } 186 } 187 188 return child; 189 } 190 191 //////////////////////////////////////////////////////////////////////////////// 192 // XULTreeAccessible: SelectAccessible 193 194 LocalAccessible* XULTreeAccessible::CurrentItem() const { 195 if (!mTreeView) return nullptr; 196 197 nsCOMPtr<nsITreeSelection> selection; 198 mTreeView->GetSelection(getter_AddRefs(selection)); 199 if (selection) { 200 int32_t currentIndex = -1; 201 selection->GetCurrentIndex(¤tIndex); 202 if (currentIndex >= 0) return GetTreeItemAccessible(currentIndex); 203 } 204 205 return nullptr; 206 } 207 208 void XULTreeAccessible::SetCurrentItem(const LocalAccessible* aItem) { 209 NS_ERROR("XULTreeAccessible::SetCurrentItem not implemented"); 210 } 211 212 void XULTreeAccessible::SelectedItems(nsTArray<Accessible*>* aItems) { 213 if (!mTreeView) return; 214 215 nsCOMPtr<nsITreeSelection> selection; 216 mTreeView->GetSelection(getter_AddRefs(selection)); 217 if (!selection) return; 218 219 int32_t rangeCount = 0; 220 selection->GetRangeCount(&rangeCount); 221 for (int32_t rangeIdx = 0; rangeIdx < rangeCount; rangeIdx++) { 222 int32_t firstIdx = 0, lastIdx = -1; 223 selection->GetRangeAt(rangeIdx, &firstIdx, &lastIdx); 224 for (int32_t rowIdx = firstIdx; rowIdx <= lastIdx; rowIdx++) { 225 LocalAccessible* item = GetTreeItemAccessible(rowIdx); 226 if (item) aItems->AppendElement(item); 227 } 228 } 229 } 230 231 uint32_t XULTreeAccessible::SelectedItemCount() { 232 if (!mTreeView) return 0; 233 234 nsCOMPtr<nsITreeSelection> selection; 235 mTreeView->GetSelection(getter_AddRefs(selection)); 236 if (selection) { 237 int32_t count = 0; 238 selection->GetCount(&count); 239 return count; 240 } 241 242 return 0; 243 } 244 245 bool XULTreeAccessible::AddItemToSelection(uint32_t aIndex) { 246 if (!mTreeView) return false; 247 248 nsCOMPtr<nsITreeSelection> selection; 249 mTreeView->GetSelection(getter_AddRefs(selection)); 250 if (selection) { 251 bool isSelected = false; 252 selection->IsSelected(aIndex, &isSelected); 253 if (!isSelected) selection->ToggleSelect(aIndex); 254 255 return true; 256 } 257 return false; 258 } 259 260 bool XULTreeAccessible::RemoveItemFromSelection(uint32_t aIndex) { 261 if (!mTreeView) return false; 262 263 nsCOMPtr<nsITreeSelection> selection; 264 mTreeView->GetSelection(getter_AddRefs(selection)); 265 if (selection) { 266 bool isSelected = false; 267 selection->IsSelected(aIndex, &isSelected); 268 if (isSelected) selection->ToggleSelect(aIndex); 269 270 return true; 271 } 272 return false; 273 } 274 275 bool XULTreeAccessible::IsItemSelected(uint32_t aIndex) { 276 if (!mTreeView) return false; 277 278 nsCOMPtr<nsITreeSelection> selection; 279 mTreeView->GetSelection(getter_AddRefs(selection)); 280 if (selection) { 281 bool isSelected = false; 282 selection->IsSelected(aIndex, &isSelected); 283 return isSelected; 284 } 285 return false; 286 } 287 288 bool XULTreeAccessible::UnselectAll() { 289 if (!mTreeView) return false; 290 291 nsCOMPtr<nsITreeSelection> selection; 292 mTreeView->GetSelection(getter_AddRefs(selection)); 293 if (!selection) return false; 294 295 selection->ClearSelection(); 296 return true; 297 } 298 299 Accessible* XULTreeAccessible::GetSelectedItem(uint32_t aIndex) { 300 if (!mTreeView) return nullptr; 301 302 nsCOMPtr<nsITreeSelection> selection; 303 mTreeView->GetSelection(getter_AddRefs(selection)); 304 if (!selection) return nullptr; 305 306 uint32_t selCount = 0; 307 int32_t rangeCount = 0; 308 selection->GetRangeCount(&rangeCount); 309 for (int32_t rangeIdx = 0; rangeIdx < rangeCount; rangeIdx++) { 310 int32_t firstIdx = 0, lastIdx = -1; 311 selection->GetRangeAt(rangeIdx, &firstIdx, &lastIdx); 312 for (int32_t rowIdx = firstIdx; rowIdx <= lastIdx; rowIdx++) { 313 if (selCount == aIndex) return GetTreeItemAccessible(rowIdx); 314 315 selCount++; 316 } 317 } 318 319 return nullptr; 320 } 321 322 bool XULTreeAccessible::SelectAll() { 323 // see if we are multiple select if so set ourselves as such 324 if (!mTreeView) return false; 325 326 nsCOMPtr<nsITreeSelection> selection; 327 mTreeView->GetSelection(getter_AddRefs(selection)); 328 if (selection) { 329 bool single = false; 330 selection->GetSingle(&single); 331 if (!single) { 332 selection->SelectAll(); 333 return true; 334 } 335 } 336 337 return false; 338 } 339 340 //////////////////////////////////////////////////////////////////////////////// 341 // XULTreeAccessible: LocalAccessible implementation 342 343 LocalAccessible* XULTreeAccessible::LocalChildAt(uint32_t aIndex) const { 344 uint32_t childCount = LocalAccessible::ChildCount(); 345 if (aIndex < childCount) { 346 return LocalAccessible::LocalChildAt(aIndex); 347 } 348 349 return GetTreeItemAccessible(aIndex - childCount); 350 } 351 352 uint32_t XULTreeAccessible::ChildCount() const { 353 // Tree's children count is row count + treecols count. 354 uint32_t childCount = LocalAccessible::ChildCount(); 355 if (!mTreeView) return childCount; 356 357 int32_t rowCount = 0; 358 mTreeView->GetRowCount(&rowCount); 359 childCount += rowCount; 360 361 return childCount; 362 } 363 364 Relation XULTreeAccessible::RelationByType(RelationType aType) const { 365 if (aType == RelationType::NODE_PARENT_OF) { 366 if (mTreeView) { 367 return Relation(new XULTreeItemIterator(this, mTreeView, -1)); 368 } 369 370 return Relation(); 371 } 372 373 return LocalAccessible::RelationByType(aType); 374 } 375 376 //////////////////////////////////////////////////////////////////////////////// 377 // XULTreeAccessible: Widgets 378 379 bool XULTreeAccessible::IsWidget() const { return true; } 380 381 bool XULTreeAccessible::IsActiveWidget() const { 382 if (IsAutoCompletePopup()) { 383 nsCOMPtr<nsIAutoCompletePopup> autoCompletePopupElm = 384 do_QueryInterface(mContent->GetParent()); 385 386 if (autoCompletePopupElm) { 387 bool isOpen = false; 388 autoCompletePopupElm->GetPopupOpen(&isOpen); 389 return isOpen; 390 } 391 } 392 return FocusMgr()->HasDOMFocus(mContent); 393 } 394 395 bool XULTreeAccessible::AreItemsOperable() const { 396 if (IsAutoCompletePopup()) { 397 nsCOMPtr<nsIAutoCompletePopup> autoCompletePopupElm = 398 do_QueryInterface(mContent->GetParent()); 399 400 if (autoCompletePopupElm) { 401 bool isOpen = false; 402 autoCompletePopupElm->GetPopupOpen(&isOpen); 403 return isOpen; 404 } 405 } 406 return true; 407 } 408 409 LocalAccessible* XULTreeAccessible::ContainerWidget() const { return nullptr; } 410 411 //////////////////////////////////////////////////////////////////////////////// 412 // XULTreeAccessible: public implementation 413 414 XULTreeItemAccessibleBase* XULTreeAccessible::GetTreeItemAccessible( 415 int32_t aRow) const { 416 if (aRow < 0 || IsDefunct() || !mTreeView) return nullptr; 417 418 int32_t rowCount = 0; 419 nsresult rv = mTreeView->GetRowCount(&rowCount); 420 if (NS_FAILED(rv) || aRow >= rowCount) return nullptr; 421 422 void* key = reinterpret_cast<void*>(intptr_t(aRow)); 423 return mAccessibleCache.WithEntryHandle( 424 key, [&](auto&& entry) -> XULTreeItemAccessibleBase* { 425 if (entry) { 426 return entry->get(); 427 } 428 429 RefPtr<XULTreeItemAccessibleBase> treeItem = 430 CreateTreeItemAccessible(aRow); 431 if (treeItem) { 432 entry.Insert(RefPtr{treeItem}); 433 Document()->BindToDocument(treeItem, nullptr); 434 return treeItem.get(); 435 } 436 437 return nullptr; 438 }); 439 } 440 441 void XULTreeAccessible::InvalidateCache(int32_t aRow, int32_t aCount) { 442 if (IsDefunct()) return; 443 444 if (!mTreeView) { 445 UnbindCacheEntriesFromDocument(mAccessibleCache); 446 return; 447 } 448 449 // Do not invalidate the cache if rows have been inserted. 450 if (aCount > 0) return; 451 452 DocAccessible* document = Document(); 453 454 // Fire destroy event for removed tree items and delete them from caches. 455 for (int32_t rowIdx = aRow; rowIdx < aRow - aCount; rowIdx++) { 456 void* key = reinterpret_cast<void*>(intptr_t(rowIdx)); 457 XULTreeItemAccessibleBase* treeItem = mAccessibleCache.GetWeak(key); 458 459 if (treeItem) { 460 RefPtr<AccEvent> event = 461 new AccEvent(nsIAccessibleEvent::EVENT_HIDE, treeItem); 462 nsEventShell::FireEvent(event); 463 464 // Unbind from document, shutdown and remove from tree cache. 465 document->UnbindFromDocument(treeItem); 466 mAccessibleCache.Remove(key); 467 } 468 } 469 470 // We dealt with removed tree items already however we may keep tree items 471 // having row indexes greater than row count. We should remove these dead tree 472 // items silently from caches. 473 int32_t newRowCount = 0; 474 nsresult rv = mTreeView->GetRowCount(&newRowCount); 475 if (NS_FAILED(rv)) return; 476 477 int32_t oldRowCount = newRowCount - aCount; 478 479 for (int32_t rowIdx = newRowCount; rowIdx < oldRowCount; ++rowIdx) { 480 void* key = reinterpret_cast<void*>(intptr_t(rowIdx)); 481 XULTreeItemAccessibleBase* treeItem = mAccessibleCache.GetWeak(key); 482 483 if (treeItem) { 484 // Unbind from document, shutdown and remove from tree cache. 485 document->UnbindFromDocument(treeItem); 486 mAccessibleCache.Remove(key); 487 } 488 } 489 } 490 491 void XULTreeAccessible::TreeViewInvalidated(int32_t aStartRow, int32_t aEndRow, 492 int32_t aStartCol, 493 int32_t aEndCol) { 494 if (IsDefunct()) return; 495 496 if (!mTreeView) { 497 UnbindCacheEntriesFromDocument(mAccessibleCache); 498 return; 499 } 500 501 int32_t endRow = aEndRow; 502 503 nsresult rv; 504 if (endRow == -1) { 505 int32_t rowCount = 0; 506 rv = mTreeView->GetRowCount(&rowCount); 507 if (NS_FAILED(rv)) return; 508 509 endRow = rowCount - 1; 510 } 511 512 RefPtr<nsTreeColumns> treeColumns = mTree->GetColumns(); 513 if (!treeColumns) return; 514 515 int32_t endCol = aEndCol; 516 517 if (endCol == -1) { 518 // We need to make sure to cast to int32_t before we do the subtraction, in 519 // case the column count is 0. 520 endCol = static_cast<int32_t>(treeColumns->Count()) - 1; 521 } 522 523 for (int32_t rowIdx = aStartRow; rowIdx <= endRow; ++rowIdx) { 524 void* key = reinterpret_cast<void*>(intptr_t(rowIdx)); 525 XULTreeItemAccessibleBase* treeitemAcc = mAccessibleCache.GetWeak(key); 526 527 if (treeitemAcc) { 528 treeitemAcc->RowInvalidated(aStartCol, endCol); 529 } 530 } 531 } 532 533 void XULTreeAccessible::TreeViewChanged(nsITreeView* aView) { 534 if (IsDefunct()) return; 535 536 // Fire reorder event on tree accessible on accessible tree (do not fire 537 // show/hide events on tree items because it can be expensive to fire them for 538 // each tree item. 539 RefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(this); 540 Document()->FireDelayedEvent(reorderEvent); 541 542 // Clear cache. 543 UnbindCacheEntriesFromDocument(mAccessibleCache); 544 545 mTreeView = aView; 546 LocalAccessible* item = CurrentItem(); 547 if (item) { 548 FocusMgr()->ActiveItemChanged(item, true); 549 } 550 } 551 552 //////////////////////////////////////////////////////////////////////////////// 553 // XULTreeAccessible: protected implementation 554 555 already_AddRefed<XULTreeItemAccessibleBase> 556 XULTreeAccessible::CreateTreeItemAccessible(int32_t aRow) const { 557 RefPtr<XULTreeItemAccessibleBase> accessible = new XULTreeItemAccessible( 558 mContent, mDoc, const_cast<XULTreeAccessible*>(this), mTree, mTreeView, 559 aRow); 560 561 return accessible.forget(); 562 } 563 564 //////////////////////////////////////////////////////////////////////////////// 565 // XULTreeItemAccessibleBase 566 //////////////////////////////////////////////////////////////////////////////// 567 568 XULTreeItemAccessibleBase::XULTreeItemAccessibleBase( 569 nsIContent* aContent, DocAccessible* aDoc, LocalAccessible* aParent, 570 dom::XULTreeElement* aTree, nsITreeView* aTreeView, int32_t aRow) 571 : AccessibleWrap(aContent, aDoc), 572 mTree(aTree), 573 mTreeView(aTreeView), 574 mRow(aRow) { 575 mParent = aParent; 576 mStateFlags |= eSharedNode; 577 } 578 579 XULTreeItemAccessibleBase::~XULTreeItemAccessibleBase() {} 580 581 //////////////////////////////////////////////////////////////////////////////// 582 // XULTreeItemAccessibleBase: nsISupports implementation 583 584 NS_IMPL_CYCLE_COLLECTION_INHERITED(XULTreeItemAccessibleBase, LocalAccessible, 585 mTree) 586 587 NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(XULTreeItemAccessibleBase, 588 LocalAccessible) 589 590 //////////////////////////////////////////////////////////////////////////////// 591 // XULTreeItemAccessibleBase: LocalAccessible 592 593 Accessible* XULTreeItemAccessibleBase::FocusedChild() { 594 return FocusMgr()->FocusedLocalAccessible() == this ? this : nullptr; 595 } 596 597 nsIntRect XULTreeItemAccessibleBase::BoundsInCSSPixels() const { 598 // Get x coordinate and width from treechildren element, get y coordinate and 599 // height from tree cell. 600 601 RefPtr<nsTreeColumn> column = nsCoreUtils::GetFirstSensibleColumn(mTree); 602 603 nsresult rv; 604 nsIntRect rect = mTree->GetCoordsForCellItem(mRow, column, u"cell"_ns, rv); 605 if (NS_FAILED(rv)) { 606 return nsIntRect(); 607 } 608 609 RefPtr<dom::Element> bodyElement = mTree->GetTreeBody(); 610 if (!bodyElement || !bodyElement->IsXULElement()) { 611 return nsIntRect(); 612 } 613 614 rect.width = bodyElement->ClientWidth(); 615 616 nsIFrame* bodyFrame = bodyElement->GetPrimaryFrame(); 617 if (!bodyFrame) { 618 return nsIntRect(); 619 } 620 621 CSSIntRect screenRect = bodyFrame->GetScreenRect(); 622 rect.x = screenRect.x; 623 rect.y += screenRect.y; 624 return rect; 625 } 626 627 nsRect XULTreeItemAccessibleBase::BoundsInAppUnits() const { 628 nsIntRect bounds = BoundsInCSSPixels(); 629 nsPresContext* presContext = mDoc->PresContext(); 630 return nsRect(presContext->CSSPixelsToAppUnits(bounds.X()), 631 presContext->CSSPixelsToAppUnits(bounds.Y()), 632 presContext->CSSPixelsToAppUnits(bounds.Width()), 633 presContext->CSSPixelsToAppUnits(bounds.Height())); 634 } 635 636 void XULTreeItemAccessibleBase::SetSelected(bool aSelect) { 637 nsCOMPtr<nsITreeSelection> selection; 638 mTreeView->GetSelection(getter_AddRefs(selection)); 639 if (selection) { 640 bool isSelected = false; 641 selection->IsSelected(mRow, &isSelected); 642 if (isSelected != aSelect) selection->ToggleSelect(mRow); 643 } 644 } 645 646 void XULTreeItemAccessibleBase::TakeFocus() const { 647 nsCOMPtr<nsITreeSelection> selection; 648 mTreeView->GetSelection(getter_AddRefs(selection)); 649 if (selection) selection->SetCurrentIndex(mRow); 650 651 // focus event will be fired here 652 LocalAccessible::TakeFocus(); 653 } 654 655 Relation XULTreeItemAccessibleBase::RelationByType(RelationType aType) const { 656 switch (aType) { 657 case RelationType::NODE_CHILD_OF: { 658 int32_t parentIndex = -1; 659 if (!NS_SUCCEEDED(mTreeView->GetParentIndex(mRow, &parentIndex))) { 660 return Relation(); 661 } 662 663 if (parentIndex == -1) return Relation(mParent); 664 665 XULTreeAccessible* treeAcc = mParent->AsXULTree(); 666 return Relation(treeAcc->GetTreeItemAccessible(parentIndex)); 667 } 668 669 case RelationType::NODE_PARENT_OF: { 670 bool isTrue = false; 671 if (NS_FAILED(mTreeView->IsContainerEmpty(mRow, &isTrue)) || isTrue) { 672 return Relation(); 673 } 674 675 if (NS_FAILED(mTreeView->IsContainerOpen(mRow, &isTrue)) || !isTrue) { 676 return Relation(); 677 } 678 679 XULTreeAccessible* tree = mParent->AsXULTree(); 680 return Relation(new XULTreeItemIterator(tree, mTreeView, mRow)); 681 } 682 683 default: 684 return Relation(); 685 } 686 } 687 688 bool XULTreeItemAccessibleBase::HasPrimaryAction() const { return true; } 689 690 uint8_t XULTreeItemAccessibleBase::ActionCount() const { 691 // "activate" action is available for all treeitems, "expand/collapse" action 692 // is avaible for treeitem which is container. 693 return IsExpandable() ? 2 : 1; 694 } 695 696 void XULTreeItemAccessibleBase::ActionNameAt(uint8_t aIndex, nsAString& aName) { 697 if (aIndex == eAction_Click) { 698 aName.AssignLiteral("activate"); 699 return; 700 } 701 702 if (aIndex == eAction_Expand && IsExpandable()) { 703 bool isContainerOpen = false; 704 mTreeView->IsContainerOpen(mRow, &isContainerOpen); 705 if (isContainerOpen) { 706 aName.AssignLiteral("collapse"); 707 } else { 708 aName.AssignLiteral("expand"); 709 } 710 } 711 } 712 713 bool XULTreeItemAccessibleBase::DoAction(uint8_t aIndex) const { 714 if (aIndex != eAction_Click && 715 (aIndex != eAction_Expand || !IsExpandable())) { 716 return false; 717 } 718 719 DoCommand(aIndex); 720 return true; 721 } 722 723 //////////////////////////////////////////////////////////////////////////////// 724 // XULTreeItemAccessibleBase: LocalAccessible implementation 725 726 void XULTreeItemAccessibleBase::Shutdown() { 727 mTree = nullptr; 728 mTreeView = nullptr; 729 mRow = -1; 730 mParent = nullptr; // null-out to prevent base class's shutdown ops 731 732 AccessibleWrap::Shutdown(); 733 } 734 735 GroupPos XULTreeItemAccessibleBase::GroupPosition() { 736 GroupPos groupPos; 737 738 int32_t level; 739 nsresult rv = mTreeView->GetLevel(mRow, &level); 740 NS_ENSURE_SUCCESS(rv, groupPos); 741 742 int32_t topCount = 1; 743 for (int32_t index = mRow - 1; index >= 0; index--) { 744 int32_t lvl = -1; 745 if (NS_SUCCEEDED(mTreeView->GetLevel(index, &lvl))) { 746 if (lvl < level) break; 747 748 if (lvl == level) topCount++; 749 } 750 } 751 752 int32_t rowCount = 0; 753 rv = mTreeView->GetRowCount(&rowCount); 754 NS_ENSURE_SUCCESS(rv, groupPos); 755 756 int32_t bottomCount = 0; 757 for (int32_t index = mRow + 1; index < rowCount; index++) { 758 int32_t lvl = -1; 759 if (NS_SUCCEEDED(mTreeView->GetLevel(index, &lvl))) { 760 if (lvl < level) break; 761 762 if (lvl == level) bottomCount++; 763 } 764 } 765 766 groupPos.level = level + 1; 767 groupPos.setSize = topCount + bottomCount; 768 groupPos.posInSet = topCount; 769 770 return groupPos; 771 } 772 773 uint64_t XULTreeItemAccessibleBase::NativeState() const { 774 // focusable and selectable states 775 uint64_t state = NativeInteractiveState(); 776 777 // expanded/collapsed state 778 if (IsExpandable()) { 779 bool isContainerOpen; 780 mTreeView->IsContainerOpen(mRow, &isContainerOpen); 781 if (isContainerOpen) { 782 state |= states::EXPANDED; 783 } 784 785 state |= states::EXPANDABLE; 786 } 787 788 // selected state 789 nsCOMPtr<nsITreeSelection> selection; 790 mTreeView->GetSelection(getter_AddRefs(selection)); 791 if (selection) { 792 bool isSelected; 793 selection->IsSelected(mRow, &isSelected); 794 if (isSelected) state |= states::SELECTED; 795 } 796 797 // focused state 798 if (FocusMgr()->IsFocused(this)) state |= states::FOCUSED; 799 800 // invisible state 801 int32_t firstVisibleRow = mTree->GetFirstVisibleRow(); 802 int32_t lastVisibleRow = mTree->GetLastVisibleRow(); 803 if (mRow < firstVisibleRow || mRow > lastVisibleRow) { 804 state |= states::INVISIBLE; 805 } 806 807 return state; 808 } 809 810 uint64_t XULTreeItemAccessibleBase::NativeInteractiveState() const { 811 return states::FOCUSABLE | states::SELECTABLE; 812 } 813 814 int32_t XULTreeItemAccessibleBase::IndexInParent() const { 815 return mParent ? mParent->ContentChildCount() + mRow : -1; 816 } 817 818 //////////////////////////////////////////////////////////////////////////////// 819 // XULTreeItemAccessibleBase: Widgets 820 821 LocalAccessible* XULTreeItemAccessibleBase::ContainerWidget() const { 822 return mParent; 823 } 824 825 //////////////////////////////////////////////////////////////////////////////// 826 // XULTreeItemAccessibleBase: LocalAccessible protected methods 827 828 void XULTreeItemAccessibleBase::DispatchClickEvent( 829 uint32_t aActionIndex) const { 830 if (IsDefunct()) return; 831 832 RefPtr<nsTreeColumns> columns = mTree->GetColumns(); 833 if (!columns) return; 834 835 // Get column and pseudo element. 836 RefPtr<nsTreeColumn> column; 837 nsAutoString pseudoElm; 838 839 if (aActionIndex == eAction_Click) { 840 // Key column is visible and clickable. 841 column = columns->GetKeyColumn(); 842 } else { 843 // Primary column contains a twisty we should click on. 844 column = columns->GetPrimaryColumn(); 845 pseudoElm = u"twisty"_ns; 846 } 847 848 if (column) { 849 RefPtr<dom::XULTreeElement> tree = mTree; 850 nsCoreUtils::DispatchClickEvent(tree, mRow, column, pseudoElm); 851 } 852 } 853 854 LocalAccessible* XULTreeItemAccessibleBase::GetSiblingAtOffset( 855 int32_t aOffset, nsresult* aError) const { 856 if (aError) *aError = NS_OK; // fail peacefully 857 858 return mParent->LocalChildAt(IndexInParent() + aOffset); 859 } 860 861 //////////////////////////////////////////////////////////////////////////////// 862 // XULTreeItemAccessibleBase: protected implementation 863 864 bool XULTreeItemAccessibleBase::IsExpandable() const { 865 bool isContainer = false; 866 mTreeView->IsContainer(mRow, &isContainer); 867 if (isContainer) { 868 bool isEmpty = false; 869 mTreeView->IsContainerEmpty(mRow, &isEmpty); 870 if (!isEmpty) { 871 RefPtr<nsTreeColumns> columns = mTree->GetColumns(); 872 if (columns) { 873 nsTreeColumn* primaryColumn = columns->GetPrimaryColumn(); 874 if (primaryColumn && !nsCoreUtils::IsColumnHidden(primaryColumn)) { 875 return true; 876 } 877 } 878 } 879 } 880 881 return false; 882 } 883 884 void XULTreeItemAccessibleBase::GetCellName(nsTreeColumn* aColumn, 885 nsAString& aName) const { 886 mTreeView->GetCellText(mRow, aColumn, aName); 887 888 // If there is still no name try the cell value: 889 // This is for graphical cells. We need tree/table view implementors to 890 // implement FooView::GetCellValue to return a meaningful string for cases 891 // where there is something shown in the cell (non-text) such as a star icon; 892 // in which case GetCellValue for that cell would return "starred" or 893 // "flagged" for example. 894 if (aName.IsEmpty()) mTreeView->GetCellValue(mRow, aColumn, aName); 895 } 896 897 //////////////////////////////////////////////////////////////////////////////// 898 // XULTreeItemAccessible 899 //////////////////////////////////////////////////////////////////////////////// 900 901 XULTreeItemAccessible::XULTreeItemAccessible( 902 nsIContent* aContent, DocAccessible* aDoc, LocalAccessible* aParent, 903 dom::XULTreeElement* aTree, nsITreeView* aTreeView, int32_t aRow) 904 : XULTreeItemAccessibleBase(aContent, aDoc, aParent, aTree, aTreeView, 905 aRow) { 906 mStateFlags |= eNoKidsFromDOM; 907 mColumn = nsCoreUtils::GetFirstSensibleColumn(mTree, FlushType::None); 908 GetCellName(mColumn, mCachedName); 909 } 910 911 XULTreeItemAccessible::~XULTreeItemAccessible() {} 912 913 //////////////////////////////////////////////////////////////////////////////// 914 // XULTreeItemAccessible: nsISupports implementation 915 916 NS_IMPL_CYCLE_COLLECTION_INHERITED(XULTreeItemAccessible, 917 XULTreeItemAccessibleBase, mColumn) 918 919 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(XULTreeItemAccessible) 920 NS_INTERFACE_MAP_END_INHERITING(XULTreeItemAccessibleBase) 921 NS_IMPL_ADDREF_INHERITED(XULTreeItemAccessible, XULTreeItemAccessibleBase) 922 NS_IMPL_RELEASE_INHERITED(XULTreeItemAccessible, XULTreeItemAccessibleBase) 923 924 //////////////////////////////////////////////////////////////////////////////// 925 // XULTreeItemAccessible: nsIAccessible implementation 926 927 ENameValueFlag XULTreeItemAccessible::DirectName(nsString& aName) const { 928 aName.Truncate(); 929 930 GetCellName(mColumn, aName); 931 return eNameOK; 932 } 933 934 //////////////////////////////////////////////////////////////////////////////// 935 // XULTreeItemAccessible: LocalAccessible implementation 936 937 void XULTreeItemAccessible::Shutdown() { 938 mColumn = nullptr; 939 XULTreeItemAccessibleBase::Shutdown(); 940 } 941 942 role XULTreeItemAccessible::NativeRole() const { 943 RefPtr<nsTreeColumns> columns = mTree->GetColumns(); 944 if (!columns) { 945 NS_ERROR("No tree columns object in the tree!"); 946 return roles::NOTHING; 947 } 948 949 nsTreeColumn* primaryColumn = columns->GetPrimaryColumn(); 950 951 return primaryColumn ? roles::OUTLINEITEM : roles::LISTITEM; 952 } 953 954 //////////////////////////////////////////////////////////////////////////////// 955 // XULTreeItemAccessible: XULTreeItemAccessibleBase implementation 956 957 void XULTreeItemAccessible::RowInvalidated(int32_t aStartColIdx, 958 int32_t aEndColIdx) { 959 nsAutoString name; 960 Name(name); 961 962 if (name != mCachedName) { 963 nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_NAME_CHANGE, this); 964 mCachedName = name; 965 } 966 } 967 968 //////////////////////////////////////////////////////////////////////////////// 969 // XULTreeColumAccessible 970 //////////////////////////////////////////////////////////////////////////////// 971 972 XULTreeColumAccessible::XULTreeColumAccessible(nsIContent* aContent, 973 DocAccessible* aDoc) 974 : XULColumAccessible(aContent, aDoc) {} 975 976 LocalAccessible* XULTreeColumAccessible::GetSiblingAtOffset( 977 int32_t aOffset, nsresult* aError) const { 978 if (aOffset < 0) { 979 return XULColumAccessible::GetSiblingAtOffset(aOffset, aError); 980 } 981 982 if (aError) *aError = NS_OK; // fail peacefully 983 984 RefPtr<dom::XULTreeElement> tree = nsCoreUtils::GetTree(mContent); 985 if (tree) { 986 nsCOMPtr<nsITreeView> treeView = tree->GetView(FlushType::None); 987 if (treeView) { 988 int32_t rowCount = 0; 989 treeView->GetRowCount(&rowCount); 990 if (rowCount > 0 && aOffset <= rowCount) { 991 XULTreeAccessible* treeAcc = LocalParent()->AsXULTree(); 992 993 if (treeAcc) return treeAcc->GetTreeItemAccessible(aOffset - 1); 994 } 995 } 996 } 997 998 return nullptr; 999 }