no bug - Bumping Firefox l10n changesets r=release a=l10n-bump DONTBUILD CLOSED TREE
[gecko.git] / accessible / xul / XULTreeAccessible.cpp
blob818e1f8e3e9df4699128183d21134cf19db3e145
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/. */
7 #include "XULTreeAccessible.h"
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"
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"
32 using namespace mozilla::a11y;
34 ////////////////////////////////////////////////////////////////////////////////
35 // XULTreeAccessible
36 ////////////////////////////////////////////////////////////////////////////////
38 XULTreeAccessible::XULTreeAccessible(nsIContent* aContent, DocAccessible* aDoc,
39 nsTreeBodyFrame* aTreeFrame)
40 : AccessibleWrap(aContent, aDoc),
41 mAccessibleCache(kDefaultTreeCacheLength) {
42 mType = eXULTreeType;
43 mGenericTypes |= eSelect;
45 nsCOMPtr<nsITreeView> view = aTreeFrame->GetExistingView();
46 mTreeView = view;
48 mTree = nsCoreUtils::GetTree(aContent);
49 NS_ASSERTION(mTree, "Can't get mTree!\n");
51 nsIContent* parentContent = mContent->GetParent();
52 if (parentContent) {
53 nsCOMPtr<nsIAutoCompletePopup> autoCompletePopupElm =
54 do_QueryInterface(parentContent);
55 if (autoCompletePopupElm) mGenericTypes |= eAutoCompletePopup;
59 XULTreeAccessible::~XULTreeAccessible() {}
61 ////////////////////////////////////////////////////////////////////////////////
62 // XULTreeAccessible: nsISupports and cycle collection implementation
64 NS_IMPL_CYCLE_COLLECTION_INHERITED(XULTreeAccessible, LocalAccessible, mTree,
65 mAccessibleCache)
67 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(XULTreeAccessible)
68 NS_INTERFACE_MAP_END_INHERITING(LocalAccessible)
70 NS_IMPL_ADDREF_INHERITED(XULTreeAccessible, LocalAccessible)
71 NS_IMPL_RELEASE_INHERITED(XULTreeAccessible, LocalAccessible)
73 ////////////////////////////////////////////////////////////////////////////////
74 // XULTreeAccessible: LocalAccessible implementation
76 uint64_t XULTreeAccessible::NativeState() const {
77 // Get focus status from base class.
78 uint64_t state = LocalAccessible::NativeState();
80 // readonly state
81 state |= states::READONLY;
83 // multiselectable state.
84 if (!mTreeView) return state;
86 nsCOMPtr<nsITreeSelection> selection;
87 mTreeView->GetSelection(getter_AddRefs(selection));
88 NS_ENSURE_TRUE(selection, state);
90 bool isSingle = false;
91 nsresult rv = selection->GetSingle(&isSingle);
92 NS_ENSURE_SUCCESS(rv, state);
94 if (!isSingle) state |= states::MULTISELECTABLE;
96 return state;
99 void XULTreeAccessible::Value(nsString& aValue) const {
100 aValue.Truncate();
101 if (!mTreeView) return;
103 // Return the value is the first selected child.
104 nsCOMPtr<nsITreeSelection> selection;
105 mTreeView->GetSelection(getter_AddRefs(selection));
106 if (!selection) return;
108 int32_t currentIndex;
109 selection->GetCurrentIndex(&currentIndex);
110 if (currentIndex >= 0) {
111 RefPtr<nsTreeColumn> keyCol;
113 RefPtr<nsTreeColumns> cols = mTree->GetColumns();
114 if (cols) keyCol = cols->GetKeyColumn();
116 mTreeView->GetCellText(currentIndex, keyCol, aValue);
120 ////////////////////////////////////////////////////////////////////////////////
121 // XULTreeAccessible: LocalAccessible implementation
123 void XULTreeAccessible::Shutdown() {
124 if (mDoc && !mDoc->IsDefunct()) {
125 UnbindCacheEntriesFromDocument(mAccessibleCache);
128 mTree = nullptr;
129 mTreeView = nullptr;
131 AccessibleWrap::Shutdown();
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.
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;
145 RefPtr<nsTreeColumns> cols = treeFrame->Columns();
146 nsTreeColumn* primaryCol = cols->GetPrimaryColumn();
148 return primaryCol ? roles::OUTLINE : roles::LIST;
151 ////////////////////////////////////////////////////////////////////////////////
152 // XULTreeAccessible: LocalAccessible implementation (DON'T put methods here)
154 LocalAccessible* XULTreeAccessible::LocalChildAtPoint(
155 int32_t aX, int32_t aY, EWhichChildAtPoint aWhichChild) {
156 nsIFrame* frame = GetFrame();
157 if (!frame) return nullptr;
159 nsPresContext* presContext = frame->PresContext();
160 PresShell* presShell = presContext->PresShell();
162 nsIFrame* rootFrame = presShell->GetRootFrame();
163 NS_ENSURE_TRUE(rootFrame, nullptr);
165 CSSIntRect rootRect = rootFrame->GetScreenRect();
167 int32_t clientX = presContext->DevPixelsToIntCSSPixels(aX) - rootRect.X();
168 int32_t clientY = presContext->DevPixelsToIntCSSPixels(aY) - rootRect.Y();
170 ErrorResult rv;
171 dom::TreeCellInfo cellInfo;
172 mTree->GetCellAt(clientX, clientY, cellInfo, rv);
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);
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;
188 return child;
191 ////////////////////////////////////////////////////////////////////////////////
192 // XULTreeAccessible: SelectAccessible
194 LocalAccessible* XULTreeAccessible::CurrentItem() const {
195 if (!mTreeView) return nullptr;
197 nsCOMPtr<nsITreeSelection> selection;
198 mTreeView->GetSelection(getter_AddRefs(selection));
199 if (selection) {
200 int32_t currentIndex = -1;
201 selection->GetCurrentIndex(&currentIndex);
202 if (currentIndex >= 0) return GetTreeItemAccessible(currentIndex);
205 return nullptr;
208 void XULTreeAccessible::SetCurrentItem(const LocalAccessible* aItem) {
209 NS_ERROR("XULTreeAccessible::SetCurrentItem not implemented");
212 void XULTreeAccessible::SelectedItems(nsTArray<Accessible*>* aItems) {
213 if (!mTreeView) return;
215 nsCOMPtr<nsITreeSelection> selection;
216 mTreeView->GetSelection(getter_AddRefs(selection));
217 if (!selection) return;
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);
231 uint32_t XULTreeAccessible::SelectedItemCount() {
232 if (!mTreeView) return 0;
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;
242 return 0;
245 bool XULTreeAccessible::AddItemToSelection(uint32_t aIndex) {
246 if (!mTreeView) return false;
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);
255 return true;
257 return false;
260 bool XULTreeAccessible::RemoveItemFromSelection(uint32_t aIndex) {
261 if (!mTreeView) return false;
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);
270 return true;
272 return false;
275 bool XULTreeAccessible::IsItemSelected(uint32_t aIndex) {
276 if (!mTreeView) return false;
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;
285 return false;
288 bool XULTreeAccessible::UnselectAll() {
289 if (!mTreeView) return false;
291 nsCOMPtr<nsITreeSelection> selection;
292 mTreeView->GetSelection(getter_AddRefs(selection));
293 if (!selection) return false;
295 selection->ClearSelection();
296 return true;
299 Accessible* XULTreeAccessible::GetSelectedItem(uint32_t aIndex) {
300 if (!mTreeView) return nullptr;
302 nsCOMPtr<nsITreeSelection> selection;
303 mTreeView->GetSelection(getter_AddRefs(selection));
304 if (!selection) return nullptr;
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);
315 selCount++;
319 return nullptr;
322 bool XULTreeAccessible::SelectAll() {
323 // see if we are multiple select if so set ourselves as such
324 if (!mTreeView) return false;
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;
337 return false;
340 ////////////////////////////////////////////////////////////////////////////////
341 // XULTreeAccessible: LocalAccessible implementation
343 LocalAccessible* XULTreeAccessible::LocalChildAt(uint32_t aIndex) const {
344 uint32_t childCount = LocalAccessible::ChildCount();
345 if (aIndex < childCount) {
346 return LocalAccessible::LocalChildAt(aIndex);
349 return GetTreeItemAccessible(aIndex - childCount);
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;
357 int32_t rowCount = 0;
358 mTreeView->GetRowCount(&rowCount);
359 childCount += rowCount;
361 return childCount;
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));
370 return Relation();
373 return LocalAccessible::RelationByType(aType);
376 ////////////////////////////////////////////////////////////////////////////////
377 // XULTreeAccessible: Widgets
379 bool XULTreeAccessible::IsWidget() const { return true; }
381 bool XULTreeAccessible::IsActiveWidget() const {
382 if (IsAutoCompletePopup()) {
383 nsCOMPtr<nsIAutoCompletePopup> autoCompletePopupElm =
384 do_QueryInterface(mContent->GetParent());
386 if (autoCompletePopupElm) {
387 bool isOpen = false;
388 autoCompletePopupElm->GetPopupOpen(&isOpen);
389 return isOpen;
392 return FocusMgr()->HasDOMFocus(mContent);
395 bool XULTreeAccessible::AreItemsOperable() const {
396 if (IsAutoCompletePopup()) {
397 nsCOMPtr<nsIAutoCompletePopup> autoCompletePopupElm =
398 do_QueryInterface(mContent->GetParent());
400 if (autoCompletePopupElm) {
401 bool isOpen = false;
402 autoCompletePopupElm->GetPopupOpen(&isOpen);
403 return isOpen;
406 return true;
409 LocalAccessible* XULTreeAccessible::ContainerWidget() const { return nullptr; }
411 ////////////////////////////////////////////////////////////////////////////////
412 // XULTreeAccessible: public implementation
414 XULTreeItemAccessibleBase* XULTreeAccessible::GetTreeItemAccessible(
415 int32_t aRow) const {
416 if (aRow < 0 || IsDefunct() || !mTreeView) return nullptr;
418 int32_t rowCount = 0;
419 nsresult rv = mTreeView->GetRowCount(&rowCount);
420 if (NS_FAILED(rv) || aRow >= rowCount) return nullptr;
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();
429 RefPtr<XULTreeItemAccessibleBase> treeItem =
430 CreateTreeItemAccessible(aRow);
431 if (treeItem) {
432 entry.Insert(RefPtr{treeItem});
433 Document()->BindToDocument(treeItem, nullptr);
434 return treeItem.get();
437 return nullptr;
441 void XULTreeAccessible::InvalidateCache(int32_t aRow, int32_t aCount) {
442 if (IsDefunct()) return;
444 if (!mTreeView) {
445 UnbindCacheEntriesFromDocument(mAccessibleCache);
446 return;
449 // Do not invalidate the cache if rows have been inserted.
450 if (aCount > 0) return;
452 DocAccessible* document = Document();
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);
459 if (treeItem) {
460 RefPtr<AccEvent> event =
461 new AccEvent(nsIAccessibleEvent::EVENT_HIDE, treeItem);
462 nsEventShell::FireEvent(event);
464 // Unbind from document, shutdown and remove from tree cache.
465 document->UnbindFromDocument(treeItem);
466 mAccessibleCache.Remove(key);
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;
477 int32_t oldRowCount = newRowCount - aCount;
479 for (int32_t rowIdx = newRowCount; rowIdx < oldRowCount; ++rowIdx) {
480 void* key = reinterpret_cast<void*>(intptr_t(rowIdx));
481 XULTreeItemAccessibleBase* treeItem = mAccessibleCache.GetWeak(key);
483 if (treeItem) {
484 // Unbind from document, shutdown and remove from tree cache.
485 document->UnbindFromDocument(treeItem);
486 mAccessibleCache.Remove(key);
491 void XULTreeAccessible::TreeViewInvalidated(int32_t aStartRow, int32_t aEndRow,
492 int32_t aStartCol,
493 int32_t aEndCol) {
494 if (IsDefunct()) return;
496 if (!mTreeView) {
497 UnbindCacheEntriesFromDocument(mAccessibleCache);
498 return;
501 int32_t endRow = aEndRow;
503 nsresult rv;
504 if (endRow == -1) {
505 int32_t rowCount = 0;
506 rv = mTreeView->GetRowCount(&rowCount);
507 if (NS_FAILED(rv)) return;
509 endRow = rowCount - 1;
512 RefPtr<nsTreeColumns> treeColumns = mTree->GetColumns();
513 if (!treeColumns) return;
515 int32_t endCol = aEndCol;
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;
523 for (int32_t rowIdx = aStartRow; rowIdx <= endRow; ++rowIdx) {
524 void* key = reinterpret_cast<void*>(intptr_t(rowIdx));
525 XULTreeItemAccessibleBase* treeitemAcc = mAccessibleCache.GetWeak(key);
527 if (treeitemAcc) {
528 treeitemAcc->RowInvalidated(aStartCol, endCol);
533 void XULTreeAccessible::TreeViewChanged(nsITreeView* aView) {
534 if (IsDefunct()) return;
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);
542 // Clear cache.
543 UnbindCacheEntriesFromDocument(mAccessibleCache);
545 mTreeView = aView;
546 LocalAccessible* item = CurrentItem();
547 if (item) {
548 FocusMgr()->ActiveItemChanged(item, true);
552 ////////////////////////////////////////////////////////////////////////////////
553 // XULTreeAccessible: protected implementation
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);
561 return accessible.forget();
564 ////////////////////////////////////////////////////////////////////////////////
565 // XULTreeItemAccessibleBase
566 ////////////////////////////////////////////////////////////////////////////////
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;
579 XULTreeItemAccessibleBase::~XULTreeItemAccessibleBase() {}
581 ////////////////////////////////////////////////////////////////////////////////
582 // XULTreeItemAccessibleBase: nsISupports implementation
584 NS_IMPL_CYCLE_COLLECTION_INHERITED(XULTreeItemAccessibleBase, LocalAccessible,
585 mTree)
587 NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(XULTreeItemAccessibleBase,
588 LocalAccessible)
590 ////////////////////////////////////////////////////////////////////////////////
591 // XULTreeItemAccessibleBase: LocalAccessible
593 Accessible* XULTreeItemAccessibleBase::FocusedChild() {
594 return FocusMgr()->FocusedLocalAccessible() == this ? this : nullptr;
597 nsIntRect XULTreeItemAccessibleBase::BoundsInCSSPixels() const {
598 // Get x coordinate and width from treechildren element, get y coordinate and
599 // height from tree cell.
601 RefPtr<nsTreeColumn> column = nsCoreUtils::GetFirstSensibleColumn(mTree);
603 nsresult rv;
604 nsIntRect rect = mTree->GetCoordsForCellItem(mRow, column, u"cell"_ns, rv);
605 if (NS_FAILED(rv)) {
606 return nsIntRect();
609 RefPtr<dom::Element> bodyElement = mTree->GetTreeBody();
610 if (!bodyElement || !bodyElement->IsXULElement()) {
611 return nsIntRect();
614 rect.width = bodyElement->ClientWidth();
616 nsIFrame* bodyFrame = bodyElement->GetPrimaryFrame();
617 if (!bodyFrame) {
618 return nsIntRect();
621 CSSIntRect screenRect = bodyFrame->GetScreenRect();
622 rect.x = screenRect.x;
623 rect.y += screenRect.y;
624 return rect;
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()));
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);
646 void XULTreeItemAccessibleBase::TakeFocus() const {
647 nsCOMPtr<nsITreeSelection> selection;
648 mTreeView->GetSelection(getter_AddRefs(selection));
649 if (selection) selection->SetCurrentIndex(mRow);
651 // focus event will be fired here
652 LocalAccessible::TakeFocus();
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();
663 if (parentIndex == -1) return Relation(mParent);
665 XULTreeAccessible* treeAcc = mParent->AsXULTree();
666 return Relation(treeAcc->GetTreeItemAccessible(parentIndex));
669 case RelationType::NODE_PARENT_OF: {
670 bool isTrue = false;
671 if (NS_FAILED(mTreeView->IsContainerEmpty(mRow, &isTrue)) || isTrue) {
672 return Relation();
675 if (NS_FAILED(mTreeView->IsContainerOpen(mRow, &isTrue)) || !isTrue) {
676 return Relation();
679 XULTreeAccessible* tree = mParent->AsXULTree();
680 return Relation(new XULTreeItemIterator(tree, mTreeView, mRow));
683 default:
684 return Relation();
688 bool XULTreeItemAccessibleBase::HasPrimaryAction() const { return true; }
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;
696 void XULTreeItemAccessibleBase::ActionNameAt(uint8_t aIndex, nsAString& aName) {
697 if (aIndex == eAction_Click) {
698 aName.AssignLiteral("activate");
699 return;
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");
713 bool XULTreeItemAccessibleBase::DoAction(uint8_t aIndex) const {
714 if (aIndex != eAction_Click &&
715 (aIndex != eAction_Expand || !IsExpandable())) {
716 return false;
719 DoCommand(nullptr, aIndex);
720 return true;
723 ////////////////////////////////////////////////////////////////////////////////
724 // XULTreeItemAccessibleBase: LocalAccessible implementation
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
732 AccessibleWrap::Shutdown();
735 GroupPos XULTreeItemAccessibleBase::GroupPosition() {
736 GroupPos groupPos;
738 int32_t level;
739 nsresult rv = mTreeView->GetLevel(mRow, &level);
740 NS_ENSURE_SUCCESS(rv, groupPos);
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;
748 if (lvl == level) topCount++;
752 int32_t rowCount = 0;
753 rv = mTreeView->GetRowCount(&rowCount);
754 NS_ENSURE_SUCCESS(rv, groupPos);
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;
762 if (lvl == level) bottomCount++;
766 groupPos.level = level + 1;
767 groupPos.setSize = topCount + bottomCount;
768 groupPos.posInSet = topCount;
770 return groupPos;
773 uint64_t XULTreeItemAccessibleBase::NativeState() const {
774 // focusable and selectable states
775 uint64_t state = NativeInteractiveState();
777 // expanded/collapsed state
778 if (IsExpandable()) {
779 bool isContainerOpen;
780 mTreeView->IsContainerOpen(mRow, &isContainerOpen);
781 state |= isContainerOpen ? states::EXPANDED : states::COLLAPSED;
784 // selected state
785 nsCOMPtr<nsITreeSelection> selection;
786 mTreeView->GetSelection(getter_AddRefs(selection));
787 if (selection) {
788 bool isSelected;
789 selection->IsSelected(mRow, &isSelected);
790 if (isSelected) state |= states::SELECTED;
793 // focused state
794 if (FocusMgr()->IsFocused(this)) state |= states::FOCUSED;
796 // invisible state
797 int32_t firstVisibleRow = mTree->GetFirstVisibleRow();
798 int32_t lastVisibleRow = mTree->GetLastVisibleRow();
799 if (mRow < firstVisibleRow || mRow > lastVisibleRow) {
800 state |= states::INVISIBLE;
803 return state;
806 uint64_t XULTreeItemAccessibleBase::NativeInteractiveState() const {
807 return states::FOCUSABLE | states::SELECTABLE;
810 int32_t XULTreeItemAccessibleBase::IndexInParent() const {
811 return mParent ? mParent->ContentChildCount() + mRow : -1;
814 ////////////////////////////////////////////////////////////////////////////////
815 // XULTreeItemAccessibleBase: Widgets
817 LocalAccessible* XULTreeItemAccessibleBase::ContainerWidget() const {
818 return mParent;
821 ////////////////////////////////////////////////////////////////////////////////
822 // XULTreeItemAccessibleBase: LocalAccessible protected methods
824 void XULTreeItemAccessibleBase::DispatchClickEvent(
825 nsIContent* aContent, uint32_t aActionIndex) const {
826 if (IsDefunct()) return;
828 RefPtr<nsTreeColumns> columns = mTree->GetColumns();
829 if (!columns) return;
831 // Get column and pseudo element.
832 RefPtr<nsTreeColumn> column;
833 nsAutoString pseudoElm;
835 if (aActionIndex == eAction_Click) {
836 // Key column is visible and clickable.
837 column = columns->GetKeyColumn();
838 } else {
839 // Primary column contains a twisty we should click on.
840 column = columns->GetPrimaryColumn();
841 pseudoElm = u"twisty"_ns;
844 if (column) {
845 RefPtr<dom::XULTreeElement> tree = mTree;
846 nsCoreUtils::DispatchClickEvent(tree, mRow, column, pseudoElm);
850 LocalAccessible* XULTreeItemAccessibleBase::GetSiblingAtOffset(
851 int32_t aOffset, nsresult* aError) const {
852 if (aError) *aError = NS_OK; // fail peacefully
854 return mParent->LocalChildAt(IndexInParent() + aOffset);
857 ////////////////////////////////////////////////////////////////////////////////
858 // XULTreeItemAccessibleBase: protected implementation
860 bool XULTreeItemAccessibleBase::IsExpandable() const {
861 bool isContainer = false;
862 mTreeView->IsContainer(mRow, &isContainer);
863 if (isContainer) {
864 bool isEmpty = false;
865 mTreeView->IsContainerEmpty(mRow, &isEmpty);
866 if (!isEmpty) {
867 RefPtr<nsTreeColumns> columns = mTree->GetColumns();
868 if (columns) {
869 nsTreeColumn* primaryColumn = columns->GetPrimaryColumn();
870 if (primaryColumn && !nsCoreUtils::IsColumnHidden(primaryColumn)) {
871 return true;
877 return false;
880 void XULTreeItemAccessibleBase::GetCellName(nsTreeColumn* aColumn,
881 nsAString& aName) const {
882 mTreeView->GetCellText(mRow, aColumn, aName);
884 // If there is still no name try the cell value:
885 // This is for graphical cells. We need tree/table view implementors to
886 // implement FooView::GetCellValue to return a meaningful string for cases
887 // where there is something shown in the cell (non-text) such as a star icon;
888 // in which case GetCellValue for that cell would return "starred" or
889 // "flagged" for example.
890 if (aName.IsEmpty()) mTreeView->GetCellValue(mRow, aColumn, aName);
893 ////////////////////////////////////////////////////////////////////////////////
894 // XULTreeItemAccessible
895 ////////////////////////////////////////////////////////////////////////////////
897 XULTreeItemAccessible::XULTreeItemAccessible(
898 nsIContent* aContent, DocAccessible* aDoc, LocalAccessible* aParent,
899 dom::XULTreeElement* aTree, nsITreeView* aTreeView, int32_t aRow)
900 : XULTreeItemAccessibleBase(aContent, aDoc, aParent, aTree, aTreeView,
901 aRow) {
902 mStateFlags |= eNoKidsFromDOM;
903 mColumn = nsCoreUtils::GetFirstSensibleColumn(mTree, FlushType::None);
904 GetCellName(mColumn, mCachedName);
907 XULTreeItemAccessible::~XULTreeItemAccessible() {}
909 ////////////////////////////////////////////////////////////////////////////////
910 // XULTreeItemAccessible: nsISupports implementation
912 NS_IMPL_CYCLE_COLLECTION_INHERITED(XULTreeItemAccessible,
913 XULTreeItemAccessibleBase, mColumn)
915 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(XULTreeItemAccessible)
916 NS_INTERFACE_MAP_END_INHERITING(XULTreeItemAccessibleBase)
917 NS_IMPL_ADDREF_INHERITED(XULTreeItemAccessible, XULTreeItemAccessibleBase)
918 NS_IMPL_RELEASE_INHERITED(XULTreeItemAccessible, XULTreeItemAccessibleBase)
920 ////////////////////////////////////////////////////////////////////////////////
921 // XULTreeItemAccessible: nsIAccessible implementation
923 ENameValueFlag XULTreeItemAccessible::Name(nsString& aName) const {
924 aName.Truncate();
926 GetCellName(mColumn, aName);
927 return eNameOK;
930 ////////////////////////////////////////////////////////////////////////////////
931 // XULTreeItemAccessible: LocalAccessible implementation
933 void XULTreeItemAccessible::Shutdown() {
934 mColumn = nullptr;
935 XULTreeItemAccessibleBase::Shutdown();
938 role XULTreeItemAccessible::NativeRole() const {
939 RefPtr<nsTreeColumns> columns = mTree->GetColumns();
940 if (!columns) {
941 NS_ERROR("No tree columns object in the tree!");
942 return roles::NOTHING;
945 nsTreeColumn* primaryColumn = columns->GetPrimaryColumn();
947 return primaryColumn ? roles::OUTLINEITEM : roles::LISTITEM;
950 ////////////////////////////////////////////////////////////////////////////////
951 // XULTreeItemAccessible: XULTreeItemAccessibleBase implementation
953 void XULTreeItemAccessible::RowInvalidated(int32_t aStartColIdx,
954 int32_t aEndColIdx) {
955 nsAutoString name;
956 Name(name);
958 if (name != mCachedName) {
959 nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_NAME_CHANGE, this);
960 mCachedName = name;
964 ////////////////////////////////////////////////////////////////////////////////
965 // XULTreeColumAccessible
966 ////////////////////////////////////////////////////////////////////////////////
968 XULTreeColumAccessible::XULTreeColumAccessible(nsIContent* aContent,
969 DocAccessible* aDoc)
970 : XULColumAccessible(aContent, aDoc) {}
972 LocalAccessible* XULTreeColumAccessible::GetSiblingAtOffset(
973 int32_t aOffset, nsresult* aError) const {
974 if (aOffset < 0) {
975 return XULColumAccessible::GetSiblingAtOffset(aOffset, aError);
978 if (aError) *aError = NS_OK; // fail peacefully
980 RefPtr<dom::XULTreeElement> tree = nsCoreUtils::GetTree(mContent);
981 if (tree) {
982 nsCOMPtr<nsITreeView> treeView = tree->GetView(FlushType::None);
983 if (treeView) {
984 int32_t rowCount = 0;
985 treeView->GetRowCount(&rowCount);
986 if (rowCount > 0 && aOffset <= rowCount) {
987 XULTreeAccessible* treeAcc = LocalParent()->AsXULTree();
989 if (treeAcc) return treeAcc->GetTreeItemAccessible(aOffset - 1);
994 return nullptr;