Bumping manifests a=b2g-bump
[gecko.git] / accessible / xul / XULTreeAccessible.cpp
blobeea941241f55e40891eca1523cef9d32584ecb39
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 "Accessible-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 "Role.h"
18 #include "States.h"
19 #include "XULTreeGridAccessible.h"
21 #include "nsComponentManagerUtils.h"
22 #include "nsIAccessibleRelation.h"
23 #include "nsIAutoCompleteInput.h"
24 #include "nsIAutoCompletePopup.h"
25 #include "nsIBoxObject.h"
26 #include "nsIDOMXULElement.h"
27 #include "nsIDOMXULMenuListElement.h"
28 #include "nsIDOMXULMultSelectCntrlEl.h"
29 #include "nsIDOMXULTreeElement.h"
30 #include "nsITreeSelection.h"
31 #include "nsIMutableArray.h"
32 #include "nsTreeBodyFrame.h"
33 #include "nsTreeColumns.h"
34 #include "nsTreeUtils.h"
36 using namespace mozilla::a11y;
38 ////////////////////////////////////////////////////////////////////////////////
39 // XULTreeAccessible
40 ////////////////////////////////////////////////////////////////////////////////
42 XULTreeAccessible::
43 XULTreeAccessible(nsIContent* aContent, DocAccessible* aDoc,
44 nsTreeBodyFrame* aTreeFrame) :
45 AccessibleWrap(aContent, aDoc),
46 mAccessibleCache(kDefaultTreeCacheLength)
48 mType = eXULTreeType;
49 mGenericTypes |= eSelect;
51 nsCOMPtr<nsITreeView> view = aTreeFrame->GetExistingView();
52 mTreeView = view;
54 mTree = nsCoreUtils::GetTreeBoxObject(aContent);
55 NS_ASSERTION(mTree, "Can't get mTree!\n");
57 nsIContent* parentContent = mContent->GetParent();
58 if (parentContent) {
59 nsCOMPtr<nsIAutoCompletePopup> autoCompletePopupElm =
60 do_QueryInterface(parentContent);
61 if (autoCompletePopupElm)
62 mGenericTypes |= eAutoCompletePopup;
66 XULTreeAccessible::~XULTreeAccessible()
70 ////////////////////////////////////////////////////////////////////////////////
71 // XULTreeAccessible: nsISupports and cycle collection implementation
73 NS_IMPL_CYCLE_COLLECTION_INHERITED(XULTreeAccessible, Accessible,
74 mTree, mAccessibleCache)
76 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(XULTreeAccessible)
77 NS_INTERFACE_MAP_END_INHERITING(Accessible)
79 NS_IMPL_ADDREF_INHERITED(XULTreeAccessible, Accessible)
80 NS_IMPL_RELEASE_INHERITED(XULTreeAccessible, Accessible)
82 ////////////////////////////////////////////////////////////////////////////////
83 // XULTreeAccessible: Accessible implementation
85 uint64_t
86 XULTreeAccessible::NativeState()
88 // Get focus status from base class.
89 uint64_t state = Accessible::NativeState();
91 // readonly state
92 state |= states::READONLY;
94 // multiselectable state.
95 if (!mTreeView)
96 return state;
98 nsCOMPtr<nsITreeSelection> selection;
99 mTreeView->GetSelection(getter_AddRefs(selection));
100 NS_ENSURE_TRUE(selection, state);
102 bool isSingle = false;
103 nsresult rv = selection->GetSingle(&isSingle);
104 NS_ENSURE_SUCCESS(rv, state);
106 if (!isSingle)
107 state |= states::MULTISELECTABLE;
109 return state;
112 void
113 XULTreeAccessible::Value(nsString& aValue)
115 aValue.Truncate();
116 if (!mTreeView)
117 return;
119 // Return the value is the first selected child.
120 nsCOMPtr<nsITreeSelection> selection;
121 mTreeView->GetSelection(getter_AddRefs(selection));
122 if (!selection)
123 return;
125 int32_t currentIndex;
126 nsCOMPtr<nsIDOMElement> selectItem;
127 selection->GetCurrentIndex(&currentIndex);
128 if (currentIndex >= 0) {
129 nsCOMPtr<nsITreeColumn> keyCol;
131 nsCOMPtr<nsITreeColumns> cols;
132 mTree->GetColumns(getter_AddRefs(cols));
133 if (cols)
134 cols->GetKeyColumn(getter_AddRefs(keyCol));
136 mTreeView->GetCellText(currentIndex, keyCol, aValue);
141 ////////////////////////////////////////////////////////////////////////////////
142 // XULTreeAccessible: Accessible implementation
144 void
145 XULTreeAccessible::Shutdown()
147 // XXX: we don't remove accessible from document cache if shutdown wasn't
148 // initiated by document destroying. Note, we can't remove accessible from
149 // document cache here while document is going to be shutdown. Note, this is
150 // not unique place where we have similar problem.
151 ClearCache(mAccessibleCache);
153 mTree = nullptr;
154 mTreeView = nullptr;
156 AccessibleWrap::Shutdown();
159 role
160 XULTreeAccessible::NativeRole()
162 // No primary column means we're in a list. In fact, history and mail turn off
163 // the primary flag when switching to a flat view.
165 nsIContent* child = nsTreeUtils::GetDescendantChild(mContent, nsGkAtoms::treechildren);
166 NS_ASSERTION(child, "tree without treechildren!");
167 nsTreeBodyFrame* treeFrame = do_QueryFrame(child->GetPrimaryFrame());
168 NS_ASSERTION(treeFrame, "xul tree accessible for tree without a frame!");
169 if (!treeFrame)
170 return roles::LIST;
172 nsRefPtr<nsTreeColumns> cols = treeFrame->Columns();
173 nsCOMPtr<nsITreeColumn> primaryCol;
174 cols->GetPrimaryColumn(getter_AddRefs(primaryCol));
176 return primaryCol ? roles::OUTLINE : roles::LIST;
179 ////////////////////////////////////////////////////////////////////////////////
180 // XULTreeAccessible: Accessible implementation (DON'T put methods here)
182 Accessible*
183 XULTreeAccessible::ChildAtPoint(int32_t aX, int32_t aY,
184 EWhichChildAtPoint aWhichChild)
186 nsIFrame *frame = GetFrame();
187 if (!frame)
188 return nullptr;
190 nsPresContext *presContext = frame->PresContext();
191 nsIPresShell* presShell = presContext->PresShell();
193 nsIFrame *rootFrame = presShell->GetRootFrame();
194 NS_ENSURE_TRUE(rootFrame, nullptr);
196 nsIntRect rootRect = rootFrame->GetScreenRect();
198 int32_t clientX = presContext->DevPixelsToIntCSSPixels(aX) - rootRect.x;
199 int32_t clientY = presContext->DevPixelsToIntCSSPixels(aY) - rootRect.y;
201 int32_t row = -1;
202 nsCOMPtr<nsITreeColumn> column;
203 nsAutoString childEltUnused;
204 mTree->GetCellAt(clientX, clientY, &row, getter_AddRefs(column),
205 childEltUnused);
207 // If we failed to find tree cell for the given point then it might be
208 // tree columns.
209 if (row == -1 || !column)
210 return AccessibleWrap::ChildAtPoint(aX, aY, aWhichChild);
212 Accessible* child = GetTreeItemAccessible(row);
213 if (aWhichChild == eDeepestChild && child) {
214 // Look for accessible cell for the found item accessible.
215 nsRefPtr<XULTreeItemAccessibleBase> treeitem = do_QueryObject(child);
217 Accessible* cell = treeitem->GetCellAccessible(column);
218 if (cell)
219 child = cell;
222 return child;
225 ////////////////////////////////////////////////////////////////////////////////
226 // XULTreeAccessible: SelectAccessible
228 Accessible*
229 XULTreeAccessible::CurrentItem()
231 if (!mTreeView)
232 return nullptr;
234 nsCOMPtr<nsITreeSelection> selection;
235 mTreeView->GetSelection(getter_AddRefs(selection));
236 if (selection) {
237 int32_t currentIndex = -1;
238 selection->GetCurrentIndex(&currentIndex);
239 if (currentIndex >= 0)
240 return GetTreeItemAccessible(currentIndex);
243 return nullptr;
246 void
247 XULTreeAccessible::SetCurrentItem(Accessible* aItem)
249 NS_ERROR("XULTreeAccessible::SetCurrentItem not implemented");
252 void
253 XULTreeAccessible::SelectedItems(nsTArray<Accessible*>* aItems)
255 if (!mTreeView)
256 return;
258 nsCOMPtr<nsITreeSelection> selection;
259 mTreeView->GetSelection(getter_AddRefs(selection));
260 if (!selection)
261 return;
263 int32_t rangeCount = 0;
264 selection->GetRangeCount(&rangeCount);
265 for (int32_t rangeIdx = 0; rangeIdx < rangeCount; rangeIdx++) {
266 int32_t firstIdx = 0, lastIdx = -1;
267 selection->GetRangeAt(rangeIdx, &firstIdx, &lastIdx);
268 for (int32_t rowIdx = firstIdx; rowIdx <= lastIdx; rowIdx++) {
269 Accessible* item = GetTreeItemAccessible(rowIdx);
270 if (item)
271 aItems->AppendElement(item);
276 uint32_t
277 XULTreeAccessible::SelectedItemCount()
279 if (!mTreeView)
280 return 0;
282 nsCOMPtr<nsITreeSelection> selection;
283 mTreeView->GetSelection(getter_AddRefs(selection));
284 if (selection) {
285 int32_t count = 0;
286 selection->GetCount(&count);
287 return count;
290 return 0;
293 bool
294 XULTreeAccessible::AddItemToSelection(uint32_t aIndex)
296 if (!mTreeView)
297 return false;
299 nsCOMPtr<nsITreeSelection> selection;
300 mTreeView->GetSelection(getter_AddRefs(selection));
301 if (selection) {
302 bool isSelected = false;
303 selection->IsSelected(aIndex, &isSelected);
304 if (!isSelected)
305 selection->ToggleSelect(aIndex);
307 return true;
309 return false;
312 bool
313 XULTreeAccessible::RemoveItemFromSelection(uint32_t aIndex)
315 if (!mTreeView)
316 return false;
318 nsCOMPtr<nsITreeSelection> selection;
319 mTreeView->GetSelection(getter_AddRefs(selection));
320 if (selection) {
321 bool isSelected = false;
322 selection->IsSelected(aIndex, &isSelected);
323 if (isSelected)
324 selection->ToggleSelect(aIndex);
326 return true;
328 return false;
331 bool
332 XULTreeAccessible::IsItemSelected(uint32_t aIndex)
334 if (!mTreeView)
335 return false;
337 nsCOMPtr<nsITreeSelection> selection;
338 mTreeView->GetSelection(getter_AddRefs(selection));
339 if (selection) {
340 bool isSelected = false;
341 selection->IsSelected(aIndex, &isSelected);
342 return isSelected;
344 return false;
347 bool
348 XULTreeAccessible::UnselectAll()
350 if (!mTreeView)
351 return false;
353 nsCOMPtr<nsITreeSelection> selection;
354 mTreeView->GetSelection(getter_AddRefs(selection));
355 if (!selection)
356 return false;
358 selection->ClearSelection();
359 return true;
362 Accessible*
363 XULTreeAccessible::GetSelectedItem(uint32_t aIndex)
365 if (!mTreeView)
366 return nullptr;
368 nsCOMPtr<nsITreeSelection> selection;
369 mTreeView->GetSelection(getter_AddRefs(selection));
370 if (!selection)
371 return nullptr;
373 uint32_t selCount = 0;
374 int32_t rangeCount = 0;
375 selection->GetRangeCount(&rangeCount);
376 for (int32_t rangeIdx = 0; rangeIdx < rangeCount; rangeIdx++) {
377 int32_t firstIdx = 0, lastIdx = -1;
378 selection->GetRangeAt(rangeIdx, &firstIdx, &lastIdx);
379 for (int32_t rowIdx = firstIdx; rowIdx <= lastIdx; rowIdx++) {
380 if (selCount == aIndex)
381 return GetTreeItemAccessible(rowIdx);
383 selCount++;
387 return nullptr;
390 bool
391 XULTreeAccessible::SelectAll()
393 // see if we are multiple select if so set ourselves as such
394 if (!mTreeView)
395 return false;
397 nsCOMPtr<nsITreeSelection> selection;
398 mTreeView->GetSelection(getter_AddRefs(selection));
399 if (selection) {
400 bool single = false;
401 selection->GetSingle(&single);
402 if (!single) {
403 selection->SelectAll();
404 return true;
408 return false;
411 ////////////////////////////////////////////////////////////////////////////////
412 // XULTreeAccessible: Accessible implementation
414 Accessible*
415 XULTreeAccessible::GetChildAt(uint32_t aIndex) const
417 uint32_t childCount = Accessible::ChildCount();
418 if (aIndex < childCount)
419 return Accessible::GetChildAt(aIndex);
421 return GetTreeItemAccessible(aIndex - childCount);
424 uint32_t
425 XULTreeAccessible::ChildCount() const
427 // Tree's children count is row count + treecols count.
428 uint32_t childCount = Accessible::ChildCount();
429 if (!mTreeView)
430 return childCount;
432 int32_t rowCount = 0;
433 mTreeView->GetRowCount(&rowCount);
434 childCount += rowCount;
436 return childCount;
439 Relation
440 XULTreeAccessible::RelationByType(RelationType aType)
442 if (aType == RelationType::NODE_PARENT_OF) {
443 if (mTreeView)
444 return Relation(new XULTreeItemIterator(this, mTreeView, -1));
446 return Relation();
449 return Accessible::RelationByType(aType);
452 ////////////////////////////////////////////////////////////////////////////////
453 // XULTreeAccessible: Widgets
455 bool
456 XULTreeAccessible::IsWidget() const
458 return true;
461 bool
462 XULTreeAccessible::IsActiveWidget() const
464 if (IsAutoCompletePopup()) {
465 nsCOMPtr<nsIAutoCompletePopup> autoCompletePopupElm =
466 do_QueryInterface(mContent->GetParent());
468 if (autoCompletePopupElm) {
469 bool isOpen = false;
470 autoCompletePopupElm->GetPopupOpen(&isOpen);
471 return isOpen;
474 return FocusMgr()->HasDOMFocus(mContent);
477 bool
478 XULTreeAccessible::AreItemsOperable() const
480 if (IsAutoCompletePopup()) {
481 nsCOMPtr<nsIAutoCompletePopup> autoCompletePopupElm =
482 do_QueryInterface(mContent->GetParent());
484 if (autoCompletePopupElm) {
485 bool isOpen = false;
486 autoCompletePopupElm->GetPopupOpen(&isOpen);
487 return isOpen;
490 return true;
493 Accessible*
494 XULTreeAccessible::ContainerWidget() const
496 if (IsAutoCompletePopup()) {
497 // This works for XUL autocompletes. It doesn't work for HTML forms
498 // autocomplete because of potential crossprocess calls (when autocomplete
499 // lives in content process while popup lives in chrome process). If that's
500 // a problem then rethink Widgets interface.
501 nsCOMPtr<nsIDOMXULMenuListElement> menuListElm =
502 do_QueryInterface(mContent->GetParent());
503 if (menuListElm) {
504 nsCOMPtr<nsIDOMNode> inputElm;
505 menuListElm->GetInputField(getter_AddRefs(inputElm));
506 if (inputElm) {
507 nsCOMPtr<nsINode> inputNode = do_QueryInterface(inputElm);
508 if (inputNode) {
509 Accessible* input =
510 mDoc->GetAccessible(inputNode);
511 return input ? input->ContainerWidget() : nullptr;
516 return nullptr;
519 ////////////////////////////////////////////////////////////////////////////////
520 // XULTreeAccessible: public implementation
522 Accessible*
523 XULTreeAccessible::GetTreeItemAccessible(int32_t aRow) const
525 if (aRow < 0 || IsDefunct() || !mTreeView)
526 return nullptr;
528 int32_t rowCount = 0;
529 nsresult rv = mTreeView->GetRowCount(&rowCount);
530 if (NS_FAILED(rv) || aRow >= rowCount)
531 return nullptr;
533 void *key = reinterpret_cast<void*>(aRow);
534 Accessible* cachedTreeItem = mAccessibleCache.GetWeak(key);
535 if (cachedTreeItem)
536 return cachedTreeItem;
538 nsRefPtr<Accessible> treeItem = CreateTreeItemAccessible(aRow);
539 if (treeItem) {
540 mAccessibleCache.Put(key, treeItem);
541 Document()->BindToDocument(treeItem, nullptr);
542 return treeItem;
545 return nullptr;
548 void
549 XULTreeAccessible::InvalidateCache(int32_t aRow, int32_t aCount)
551 if (IsDefunct())
552 return;
554 if (!mTreeView) {
555 ClearCache(mAccessibleCache);
556 return;
559 // Do not invalidate the cache if rows have been inserted.
560 if (aCount > 0)
561 return;
563 DocAccessible* document = Document();
565 // Fire destroy event for removed tree items and delete them from caches.
566 for (int32_t rowIdx = aRow; rowIdx < aRow - aCount; rowIdx++) {
568 void* key = reinterpret_cast<void*>(rowIdx);
569 Accessible* treeItem = mAccessibleCache.GetWeak(key);
571 if (treeItem) {
572 nsRefPtr<AccEvent> event =
573 new AccEvent(nsIAccessibleEvent::EVENT_HIDE, treeItem);
574 nsEventShell::FireEvent(event);
576 // Unbind from document, shutdown and remove from tree cache.
577 document->UnbindFromDocument(treeItem);
578 mAccessibleCache.Remove(key);
582 // We dealt with removed tree items already however we may keep tree items
583 // having row indexes greater than row count. We should remove these dead tree
584 // items silently from caches.
585 int32_t newRowCount = 0;
586 nsresult rv = mTreeView->GetRowCount(&newRowCount);
587 if (NS_FAILED(rv))
588 return;
590 int32_t oldRowCount = newRowCount - aCount;
592 for (int32_t rowIdx = newRowCount; rowIdx < oldRowCount; ++rowIdx) {
594 void *key = reinterpret_cast<void*>(rowIdx);
595 Accessible* treeItem = mAccessibleCache.GetWeak(key);
597 if (treeItem) {
598 // Unbind from document, shutdown and remove from tree cache.
599 document->UnbindFromDocument(treeItem);
600 mAccessibleCache.Remove(key);
605 void
606 XULTreeAccessible::TreeViewInvalidated(int32_t aStartRow, int32_t aEndRow,
607 int32_t aStartCol, int32_t aEndCol)
609 if (IsDefunct())
610 return;
612 if (!mTreeView) {
613 ClearCache(mAccessibleCache);
614 return;
617 int32_t endRow = aEndRow;
619 nsresult rv;
620 if (endRow == -1) {
621 int32_t rowCount = 0;
622 rv = mTreeView->GetRowCount(&rowCount);
623 if (NS_FAILED(rv))
624 return;
626 endRow = rowCount - 1;
629 nsCOMPtr<nsITreeColumns> treeColumns;
630 mTree->GetColumns(getter_AddRefs(treeColumns));
631 if (!treeColumns)
632 return;
634 int32_t endCol = aEndCol;
636 if (endCol == -1) {
637 int32_t colCount = 0;
638 rv = treeColumns->GetCount(&colCount);
639 if (NS_FAILED(rv))
640 return;
642 endCol = colCount - 1;
645 for (int32_t rowIdx = aStartRow; rowIdx <= endRow; ++rowIdx) {
647 void *key = reinterpret_cast<void*>(rowIdx);
648 Accessible* accessible = mAccessibleCache.GetWeak(key);
650 if (accessible) {
651 nsRefPtr<XULTreeItemAccessibleBase> treeitemAcc = do_QueryObject(accessible);
652 NS_ASSERTION(treeitemAcc, "Wrong accessible at the given key!");
654 treeitemAcc->RowInvalidated(aStartCol, endCol);
659 void
660 XULTreeAccessible::TreeViewChanged(nsITreeView* aView)
662 if (IsDefunct())
663 return;
665 // Fire reorder event on tree accessible on accessible tree (do not fire
666 // show/hide events on tree items because it can be expensive to fire them for
667 // each tree item.
668 nsRefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(this);
669 Document()->FireDelayedEvent(reorderEvent);
671 // Clear cache.
672 ClearCache(mAccessibleCache);
673 mTreeView = aView;
676 ////////////////////////////////////////////////////////////////////////////////
677 // XULTreeAccessible: protected implementation
679 already_AddRefed<Accessible>
680 XULTreeAccessible::CreateTreeItemAccessible(int32_t aRow) const
682 nsRefPtr<Accessible> accessible =
683 new XULTreeItemAccessible(mContent, mDoc, const_cast<XULTreeAccessible*>(this),
684 mTree, mTreeView, aRow);
686 return accessible.forget();
689 ////////////////////////////////////////////////////////////////////////////////
690 // XULTreeItemAccessibleBase
691 ////////////////////////////////////////////////////////////////////////////////
693 XULTreeItemAccessibleBase::
694 XULTreeItemAccessibleBase(nsIContent* aContent, DocAccessible* aDoc,
695 Accessible* aParent, nsITreeBoxObject* aTree,
696 nsITreeView* aTreeView, int32_t aRow) :
697 AccessibleWrap(aContent, aDoc),
698 mTree(aTree), mTreeView(aTreeView), mRow(aRow)
700 mParent = aParent;
701 mStateFlags |= eSharedNode;
704 XULTreeItemAccessibleBase::~XULTreeItemAccessibleBase()
708 ////////////////////////////////////////////////////////////////////////////////
709 // XULTreeItemAccessibleBase: nsISupports implementation
711 NS_IMPL_CYCLE_COLLECTION_INHERITED(XULTreeItemAccessibleBase, Accessible,
712 mTree)
714 NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(XULTreeItemAccessibleBase)
715 NS_INTERFACE_TABLE_INHERITED(XULTreeItemAccessibleBase,
716 XULTreeItemAccessibleBase)
717 NS_INTERFACE_TABLE_TAIL_INHERITING(Accessible)
718 NS_IMPL_ADDREF_INHERITED(XULTreeItemAccessibleBase, Accessible)
719 NS_IMPL_RELEASE_INHERITED(XULTreeItemAccessibleBase, Accessible)
721 ////////////////////////////////////////////////////////////////////////////////
722 // XULTreeItemAccessibleBase: Accessible
724 Accessible*
725 XULTreeItemAccessibleBase::FocusedChild()
727 return FocusMgr()->FocusedAccessible() == this ? this : nullptr;
730 nsIntRect
731 XULTreeItemAccessibleBase::Bounds() const
733 // Get x coordinate and width from treechildren element, get y coordinate and
734 // height from tree cell.
736 nsCOMPtr<nsIBoxObject> boxObj = nsCoreUtils::GetTreeBodyBoxObject(mTree);
737 if (!boxObj)
738 return nsIntRect();
740 nsCOMPtr<nsITreeColumn> column = nsCoreUtils::GetFirstSensibleColumn(mTree);
742 int32_t x = 0, y = 0, width = 0, height = 0;
743 nsresult rv = mTree->GetCoordsForCellItem(mRow, column, EmptyString(),
744 &x, &y, &width, &height);
745 if (NS_FAILED(rv))
746 return nsIntRect();
748 boxObj->GetWidth(&width);
750 int32_t tcX = 0, tcY = 0;
751 boxObj->GetScreenX(&tcX);
752 boxObj->GetScreenY(&tcY);
754 x = tcX;
755 y += tcY;
757 nsPresContext* presContext = mDoc->PresContext();
758 return nsIntRect(presContext->CSSPixelsToDevPixels(x),
759 presContext->CSSPixelsToDevPixels(y),
760 presContext->CSSPixelsToDevPixels(width),
761 presContext->CSSPixelsToDevPixels(height));
764 void
765 XULTreeItemAccessibleBase::SetSelected(bool aSelect)
767 nsCOMPtr<nsITreeSelection> selection;
768 mTreeView->GetSelection(getter_AddRefs(selection));
769 if (selection) {
770 bool isSelected = false;
771 selection->IsSelected(mRow, &isSelected);
772 if (isSelected != aSelect)
773 selection->ToggleSelect(mRow);
777 void
778 XULTreeItemAccessibleBase::TakeFocus()
780 nsCOMPtr<nsITreeSelection> selection;
781 mTreeView->GetSelection(getter_AddRefs(selection));
782 if (selection)
783 selection->SetCurrentIndex(mRow);
785 // focus event will be fired here
786 Accessible::TakeFocus();
789 Relation
790 XULTreeItemAccessibleBase::RelationByType(RelationType aType)
793 switch (aType) {
794 case RelationType::NODE_CHILD_OF: {
795 int32_t parentIndex = -1;
796 if (!NS_SUCCEEDED(mTreeView->GetParentIndex(mRow, &parentIndex)))
797 return Relation();
799 if (parentIndex == -1)
800 return Relation(mParent);
802 XULTreeAccessible* treeAcc = mParent->AsXULTree();
803 return Relation(treeAcc->GetTreeItemAccessible(parentIndex));
806 case RelationType::NODE_PARENT_OF: {
807 bool isTrue = false;
808 if (NS_FAILED(mTreeView->IsContainerEmpty(mRow, &isTrue)) || isTrue)
809 return Relation();
811 if (NS_FAILED(mTreeView->IsContainerOpen(mRow, &isTrue)) || !isTrue)
812 return Relation();
814 XULTreeAccessible* tree = mParent->AsXULTree();
815 return Relation(new XULTreeItemIterator(tree, mTreeView, mRow));
818 default:
819 return Relation();
823 uint8_t
824 XULTreeItemAccessibleBase::ActionCount()
826 // "activate" action is available for all treeitems, "expand/collapse" action
827 // is avaible for treeitem which is container.
828 return IsExpandable() ? 2 : 1;
831 void
832 XULTreeItemAccessibleBase::ActionNameAt(uint8_t aIndex, nsAString& aName)
834 if (aIndex == eAction_Click) {
835 aName.AssignLiteral("activate");
836 return;
839 if (aIndex == eAction_Expand && IsExpandable()) {
840 bool isContainerOpen = false;
841 mTreeView->IsContainerOpen(mRow, &isContainerOpen);
842 if (isContainerOpen)
843 aName.AssignLiteral("collapse");
844 else
845 aName.AssignLiteral("expand");
849 bool
850 XULTreeItemAccessibleBase::DoAction(uint8_t aIndex)
852 if (aIndex != eAction_Click &&
853 (aIndex != eAction_Expand || !IsExpandable()))
854 return false;
856 DoCommand(nullptr, aIndex);
857 return true;
860 ////////////////////////////////////////////////////////////////////////////////
861 // XULTreeItemAccessibleBase: Accessible implementation
863 void
864 XULTreeItemAccessibleBase::Shutdown()
866 mTree = nullptr;
867 mTreeView = nullptr;
868 mRow = -1;
870 AccessibleWrap::Shutdown();
873 GroupPos
874 XULTreeItemAccessibleBase::GroupPosition()
876 GroupPos groupPos;
878 int32_t level;
879 nsresult rv = mTreeView->GetLevel(mRow, &level);
880 NS_ENSURE_SUCCESS(rv, groupPos);
882 int32_t topCount = 1;
883 for (int32_t index = mRow - 1; index >= 0; index--) {
884 int32_t lvl = -1;
885 if (NS_SUCCEEDED(mTreeView->GetLevel(index, &lvl))) {
886 if (lvl < level)
887 break;
889 if (lvl == level)
890 topCount++;
894 int32_t rowCount = 0;
895 rv = mTreeView->GetRowCount(&rowCount);
896 NS_ENSURE_SUCCESS(rv, groupPos);
898 int32_t bottomCount = 0;
899 for (int32_t index = mRow + 1; index < rowCount; index++) {
900 int32_t lvl = -1;
901 if (NS_SUCCEEDED(mTreeView->GetLevel(index, &lvl))) {
902 if (lvl < level)
903 break;
905 if (lvl == level)
906 bottomCount++;
910 groupPos.level = level + 1;
911 groupPos.setSize = topCount + bottomCount;
912 groupPos.posInSet = topCount;
914 return groupPos;
917 uint64_t
918 XULTreeItemAccessibleBase::NativeState()
921 // focusable and selectable states
922 uint64_t state = NativeInteractiveState();
924 // expanded/collapsed state
925 if (IsExpandable()) {
926 bool isContainerOpen;
927 mTreeView->IsContainerOpen(mRow, &isContainerOpen);
928 state |= isContainerOpen ? states::EXPANDED : states::COLLAPSED;
931 // selected state
932 nsCOMPtr<nsITreeSelection> selection;
933 mTreeView->GetSelection(getter_AddRefs(selection));
934 if (selection) {
935 bool isSelected;
936 selection->IsSelected(mRow, &isSelected);
937 if (isSelected)
938 state |= states::SELECTED;
941 // focused state
942 if (FocusMgr()->IsFocused(this))
943 state |= states::FOCUSED;
945 // invisible state
946 int32_t firstVisibleRow, lastVisibleRow;
947 mTree->GetFirstVisibleRow(&firstVisibleRow);
948 mTree->GetLastVisibleRow(&lastVisibleRow);
949 if (mRow < firstVisibleRow || mRow > lastVisibleRow)
950 state |= states::INVISIBLE;
952 return state;
955 uint64_t
956 XULTreeItemAccessibleBase::NativeInteractiveState() const
958 return states::FOCUSABLE | states::SELECTABLE;
961 int32_t
962 XULTreeItemAccessibleBase::IndexInParent() const
964 return mParent ? mParent->ContentChildCount() + mRow : -1;
967 ////////////////////////////////////////////////////////////////////////////////
968 // XULTreeItemAccessibleBase: Widgets
970 Accessible*
971 XULTreeItemAccessibleBase::ContainerWidget() const
973 return mParent;
976 ////////////////////////////////////////////////////////////////////////////////
977 // XULTreeItemAccessibleBase: Accessible protected methods
979 void
980 XULTreeItemAccessibleBase::DispatchClickEvent(nsIContent* aContent,
981 uint32_t aActionIndex)
983 if (IsDefunct())
984 return;
986 nsCOMPtr<nsITreeColumns> columns;
987 mTree->GetColumns(getter_AddRefs(columns));
988 if (!columns)
989 return;
991 // Get column and pseudo element.
992 nsCOMPtr<nsITreeColumn> column;
993 nsAutoString pseudoElm;
995 if (aActionIndex == eAction_Click) {
996 // Key column is visible and clickable.
997 columns->GetKeyColumn(getter_AddRefs(column));
998 } else {
999 // Primary column contains a twisty we should click on.
1000 columns->GetPrimaryColumn(getter_AddRefs(column));
1001 pseudoElm = NS_LITERAL_STRING("twisty");
1004 if (column)
1005 nsCoreUtils::DispatchClickEvent(mTree, mRow, column, pseudoElm);
1008 Accessible*
1009 XULTreeItemAccessibleBase::GetSiblingAtOffset(int32_t aOffset,
1010 nsresult* aError) const
1012 if (aError)
1013 *aError = NS_OK; // fail peacefully
1015 return mParent->GetChildAt(IndexInParent() + aOffset);
1018 ////////////////////////////////////////////////////////////////////////////////
1019 // XULTreeItemAccessibleBase: protected implementation
1021 bool
1022 XULTreeItemAccessibleBase::IsExpandable()
1025 bool isContainer = false;
1026 mTreeView->IsContainer(mRow, &isContainer);
1027 if (isContainer) {
1028 bool isEmpty = false;
1029 mTreeView->IsContainerEmpty(mRow, &isEmpty);
1030 if (!isEmpty) {
1031 nsCOMPtr<nsITreeColumns> columns;
1032 mTree->GetColumns(getter_AddRefs(columns));
1033 nsCOMPtr<nsITreeColumn> primaryColumn;
1034 if (columns) {
1035 columns->GetPrimaryColumn(getter_AddRefs(primaryColumn));
1036 if (primaryColumn &&
1037 !nsCoreUtils::IsColumnHidden(primaryColumn))
1038 return true;
1043 return false;
1046 void
1047 XULTreeItemAccessibleBase::GetCellName(nsITreeColumn* aColumn, nsAString& aName)
1050 mTreeView->GetCellText(mRow, aColumn, aName);
1052 // If there is still no name try the cell value:
1053 // This is for graphical cells. We need tree/table view implementors to
1054 // implement FooView::GetCellValue to return a meaningful string for cases
1055 // where there is something shown in the cell (non-text) such as a star icon;
1056 // in which case GetCellValue for that cell would return "starred" or
1057 // "flagged" for example.
1058 if (aName.IsEmpty())
1059 mTreeView->GetCellValue(mRow, aColumn, aName);
1063 ////////////////////////////////////////////////////////////////////////////////
1064 // XULTreeItemAccessible
1065 ////////////////////////////////////////////////////////////////////////////////
1067 XULTreeItemAccessible::
1068 XULTreeItemAccessible(nsIContent* aContent, DocAccessible* aDoc,
1069 Accessible* aParent, nsITreeBoxObject* aTree,
1070 nsITreeView* aTreeView, int32_t aRow) :
1071 XULTreeItemAccessibleBase(aContent, aDoc, aParent, aTree, aTreeView, aRow)
1073 mColumn = nsCoreUtils::GetFirstSensibleColumn(mTree);
1074 GetCellName(mColumn, mCachedName);
1077 XULTreeItemAccessible::~XULTreeItemAccessible()
1081 ////////////////////////////////////////////////////////////////////////////////
1082 // XULTreeItemAccessible: nsISupports implementation
1084 NS_IMPL_CYCLE_COLLECTION_INHERITED(XULTreeItemAccessible,
1085 XULTreeItemAccessibleBase,
1086 mColumn)
1088 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(XULTreeItemAccessible)
1089 NS_INTERFACE_MAP_END_INHERITING(XULTreeItemAccessibleBase)
1090 NS_IMPL_ADDREF_INHERITED(XULTreeItemAccessible, XULTreeItemAccessibleBase)
1091 NS_IMPL_RELEASE_INHERITED(XULTreeItemAccessible, XULTreeItemAccessibleBase)
1093 ////////////////////////////////////////////////////////////////////////////////
1094 // XULTreeItemAccessible: nsIAccessible implementation
1096 ENameValueFlag
1097 XULTreeItemAccessible::Name(nsString& aName)
1099 aName.Truncate();
1101 GetCellName(mColumn, aName);
1102 return eNameOK;
1105 ////////////////////////////////////////////////////////////////////////////////
1106 // XULTreeItemAccessible: Accessible implementation
1108 void
1109 XULTreeItemAccessible::Shutdown()
1111 mColumn = nullptr;
1112 XULTreeItemAccessibleBase::Shutdown();
1115 role
1116 XULTreeItemAccessible::NativeRole()
1118 nsCOMPtr<nsITreeColumns> columns;
1119 mTree->GetColumns(getter_AddRefs(columns));
1120 if (!columns) {
1121 NS_ERROR("No tree columns object in the tree!");
1122 return roles::NOTHING;
1125 nsCOMPtr<nsITreeColumn> primaryColumn;
1126 columns->GetPrimaryColumn(getter_AddRefs(primaryColumn));
1128 return primaryColumn ? roles::OUTLINEITEM : roles::LISTITEM;
1131 ////////////////////////////////////////////////////////////////////////////////
1132 // XULTreeItemAccessible: XULTreeItemAccessibleBase implementation
1134 void
1135 XULTreeItemAccessible::RowInvalidated(int32_t aStartColIdx, int32_t aEndColIdx)
1137 nsAutoString name;
1138 Name(name);
1140 if (name != mCachedName) {
1141 nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_NAME_CHANGE, this);
1142 mCachedName = name;
1146 ////////////////////////////////////////////////////////////////////////////////
1147 // XULTreeItemAccessible: Accessible protected implementation
1149 void
1150 XULTreeItemAccessible::CacheChildren()
1155 ////////////////////////////////////////////////////////////////////////////////
1156 // XULTreeColumAccessible
1157 ////////////////////////////////////////////////////////////////////////////////
1159 XULTreeColumAccessible::
1160 XULTreeColumAccessible(nsIContent* aContent, DocAccessible* aDoc) :
1161 XULColumAccessible(aContent, aDoc)
1165 Accessible*
1166 XULTreeColumAccessible::GetSiblingAtOffset(int32_t aOffset,
1167 nsresult* aError) const
1169 if (aOffset < 0)
1170 return XULColumAccessible::GetSiblingAtOffset(aOffset, aError);
1172 if (aError)
1173 *aError = NS_OK; // fail peacefully
1175 nsCOMPtr<nsITreeBoxObject> tree = nsCoreUtils::GetTreeBoxObject(mContent);
1176 if (tree) {
1177 nsCOMPtr<nsITreeView> treeView;
1178 tree->GetView(getter_AddRefs(treeView));
1179 if (treeView) {
1180 int32_t rowCount = 0;
1181 treeView->GetRowCount(&rowCount);
1182 if (rowCount > 0 && aOffset <= rowCount) {
1183 XULTreeAccessible* treeAcc = Parent()->AsXULTree();
1185 if (treeAcc)
1186 return treeAcc->GetTreeItemAccessible(aOffset - 1);
1191 return nullptr;