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"
17 #include "mozilla/a11y/Role.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 ////////////////////////////////////////////////////////////////////////////////
36 ////////////////////////////////////////////////////////////////////////////////
38 XULTreeAccessible::XULTreeAccessible(nsIContent
* aContent
, DocAccessible
* aDoc
,
39 nsTreeBodyFrame
* aTreeFrame
)
40 : AccessibleWrap(aContent
, aDoc
),
41 mAccessibleCache(kDefaultTreeCacheLength
) {
43 mGenericTypes
|= eSelect
;
45 nsCOMPtr
<nsITreeView
> view
= aTreeFrame
->GetExistingView();
48 mTree
= nsCoreUtils::GetTree(aContent
);
49 NS_ASSERTION(mTree
, "Can't get mTree!\n");
51 nsIContent
* parentContent
= mContent
->GetParent();
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
,
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();
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
;
99 void XULTreeAccessible::Value(nsString
& aValue
) const {
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(¤tIndex
);
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
);
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.
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();
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
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
);
191 ////////////////////////////////////////////////////////////////////////////////
192 // XULTreeAccessible: SelectAccessible
194 LocalAccessible
* XULTreeAccessible::CurrentItem() const {
195 if (!mTreeView
) return nullptr;
197 nsCOMPtr
<nsITreeSelection
> selection
;
198 mTreeView
->GetSelection(getter_AddRefs(selection
));
200 int32_t currentIndex
= -1;
201 selection
->GetCurrentIndex(¤tIndex
);
202 if (currentIndex
>= 0) return GetTreeItemAccessible(currentIndex
);
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
));
238 selection
->GetCount(&count
);
245 bool XULTreeAccessible::AddItemToSelection(uint32_t aIndex
) {
246 if (!mTreeView
) return false;
248 nsCOMPtr
<nsITreeSelection
> selection
;
249 mTreeView
->GetSelection(getter_AddRefs(selection
));
251 bool isSelected
= false;
252 selection
->IsSelected(aIndex
, &isSelected
);
253 if (!isSelected
) selection
->ToggleSelect(aIndex
);
260 bool XULTreeAccessible::RemoveItemFromSelection(uint32_t aIndex
) {
261 if (!mTreeView
) return false;
263 nsCOMPtr
<nsITreeSelection
> selection
;
264 mTreeView
->GetSelection(getter_AddRefs(selection
));
266 bool isSelected
= false;
267 selection
->IsSelected(aIndex
, &isSelected
);
268 if (isSelected
) selection
->ToggleSelect(aIndex
);
275 bool XULTreeAccessible::IsItemSelected(uint32_t aIndex
) {
276 if (!mTreeView
) return false;
278 nsCOMPtr
<nsITreeSelection
> selection
;
279 mTreeView
->GetSelection(getter_AddRefs(selection
));
281 bool isSelected
= false;
282 selection
->IsSelected(aIndex
, &isSelected
);
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();
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
);
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
));
330 selection
->GetSingle(&single
);
332 selection
->SelectAll();
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
;
364 Relation
XULTreeAccessible::RelationByType(RelationType aType
) const {
365 if (aType
== RelationType::NODE_PARENT_OF
) {
367 return Relation(new XULTreeItemIterator(this, mTreeView
, -1));
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
) {
388 autoCompletePopupElm
->GetPopupOpen(&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
) {
402 autoCompletePopupElm
->GetPopupOpen(&isOpen
);
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
* {
429 RefPtr
<XULTreeItemAccessibleBase
> treeItem
=
430 CreateTreeItemAccessible(aRow
);
432 entry
.Insert(RefPtr
{treeItem
});
433 Document()->BindToDocument(treeItem
, nullptr);
434 return treeItem
.get();
441 void XULTreeAccessible::InvalidateCache(int32_t aRow
, int32_t aCount
) {
442 if (IsDefunct()) return;
445 UnbindCacheEntriesFromDocument(mAccessibleCache
);
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
);
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
);
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
,
494 if (IsDefunct()) return;
497 UnbindCacheEntriesFromDocument(mAccessibleCache
);
501 int32_t endRow
= aEndRow
;
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
;
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
);
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
539 RefPtr
<AccReorderEvent
> reorderEvent
= new AccReorderEvent(this);
540 Document()->FireDelayedEvent(reorderEvent
);
543 UnbindCacheEntriesFromDocument(mAccessibleCache
);
546 LocalAccessible
* item
= CurrentItem();
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
,
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
),
573 mTreeView(aTreeView
),
576 mStateFlags
|= eSharedNode
;
579 XULTreeItemAccessibleBase::~XULTreeItemAccessibleBase() {}
581 ////////////////////////////////////////////////////////////////////////////////
582 // XULTreeItemAccessibleBase: nsISupports implementation
584 NS_IMPL_CYCLE_COLLECTION_INHERITED(XULTreeItemAccessibleBase
, LocalAccessible
,
587 NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(XULTreeItemAccessibleBase
,
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
);
604 nsIntRect rect
= mTree
->GetCoordsForCellItem(mRow
, column
, u
"cell"_ns
, rv
);
609 RefPtr
<dom::Element
> bodyElement
= mTree
->GetTreeBody();
610 if (!bodyElement
|| !bodyElement
->IsXULElement()) {
614 rect
.width
= bodyElement
->ClientWidth();
616 nsIFrame
* bodyFrame
= bodyElement
->GetPrimaryFrame();
621 CSSIntRect screenRect
= bodyFrame
->GetScreenRect();
622 rect
.x
= screenRect
.x
;
623 rect
.y
+= screenRect
.y
;
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
));
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 {
657 case RelationType::NODE_CHILD_OF
: {
658 int32_t parentIndex
= -1;
659 if (!NS_SUCCEEDED(mTreeView
->GetParentIndex(mRow
, &parentIndex
))) {
663 if (parentIndex
== -1) return Relation(mParent
);
665 XULTreeAccessible
* treeAcc
= mParent
->AsXULTree();
666 return Relation(treeAcc
->GetTreeItemAccessible(parentIndex
));
669 case RelationType::NODE_PARENT_OF
: {
671 if (NS_FAILED(mTreeView
->IsContainerEmpty(mRow
, &isTrue
)) || isTrue
) {
675 if (NS_FAILED(mTreeView
->IsContainerOpen(mRow
, &isTrue
)) || !isTrue
) {
679 XULTreeAccessible
* tree
= mParent
->AsXULTree();
680 return Relation(new XULTreeItemIterator(tree
, mTreeView
, mRow
));
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");
702 if (aIndex
== eAction_Expand
&& IsExpandable()) {
703 bool isContainerOpen
= false;
704 mTreeView
->IsContainerOpen(mRow
, &isContainerOpen
);
705 if (isContainerOpen
) {
706 aName
.AssignLiteral("collapse");
708 aName
.AssignLiteral("expand");
713 bool XULTreeItemAccessibleBase::DoAction(uint8_t aIndex
) const {
714 if (aIndex
!= eAction_Click
&&
715 (aIndex
!= eAction_Expand
|| !IsExpandable())) {
719 DoCommand(nullptr, aIndex
);
723 ////////////////////////////////////////////////////////////////////////////////
724 // XULTreeItemAccessibleBase: LocalAccessible implementation
726 void XULTreeItemAccessibleBase::Shutdown() {
730 mParent
= nullptr; // null-out to prevent base class's shutdown ops
732 AccessibleWrap::Shutdown();
735 GroupPos
XULTreeItemAccessibleBase::GroupPosition() {
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
--) {
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
++) {
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
;
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
;
785 nsCOMPtr
<nsITreeSelection
> selection
;
786 mTreeView
->GetSelection(getter_AddRefs(selection
));
789 selection
->IsSelected(mRow
, &isSelected
);
790 if (isSelected
) state
|= states::SELECTED
;
794 if (FocusMgr()->IsFocused(this)) state
|= states::FOCUSED
;
797 int32_t firstVisibleRow
= mTree
->GetFirstVisibleRow();
798 int32_t lastVisibleRow
= mTree
->GetLastVisibleRow();
799 if (mRow
< firstVisibleRow
|| mRow
> lastVisibleRow
) {
800 state
|= states::INVISIBLE
;
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 {
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();
839 // Primary column contains a twisty we should click on.
840 column
= columns
->GetPrimaryColumn();
841 pseudoElm
= u
"twisty"_ns
;
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
);
864 bool isEmpty
= false;
865 mTreeView
->IsContainerEmpty(mRow
, &isEmpty
);
867 RefPtr
<nsTreeColumns
> columns
= mTree
->GetColumns();
869 nsTreeColumn
* primaryColumn
= columns
->GetPrimaryColumn();
870 if (primaryColumn
&& !nsCoreUtils::IsColumnHidden(primaryColumn
)) {
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
,
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 {
926 GetCellName(mColumn
, aName
);
930 ////////////////////////////////////////////////////////////////////////////////
931 // XULTreeItemAccessible: LocalAccessible implementation
933 void XULTreeItemAccessible::Shutdown() {
935 XULTreeItemAccessibleBase::Shutdown();
938 role
XULTreeItemAccessible::NativeRole() const {
939 RefPtr
<nsTreeColumns
> columns
= mTree
->GetColumns();
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
) {
958 if (name
!= mCachedName
) {
959 nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_NAME_CHANGE
, this);
964 ////////////////////////////////////////////////////////////////////////////////
965 // XULTreeColumAccessible
966 ////////////////////////////////////////////////////////////////////////////////
968 XULTreeColumAccessible::XULTreeColumAccessible(nsIContent
* aContent
,
970 : XULColumAccessible(aContent
, aDoc
) {}
972 LocalAccessible
* XULTreeColumAccessible::GetSiblingAtOffset(
973 int32_t aOffset
, nsresult
* aError
) const {
975 return XULColumAccessible::GetSiblingAtOffset(aOffset
, aError
);
978 if (aError
) *aError
= NS_OK
; // fail peacefully
980 RefPtr
<dom::XULTreeElement
> tree
= nsCoreUtils::GetTree(mContent
);
982 nsCOMPtr
<nsITreeView
> treeView
= tree
->GetView(FlushType::None
);
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);