Files
tubestation/accessible/xul/XULTreeAccessible.cpp
James Teh 8f0cff7edd Bug 1930322 part 1: cleanup: Remove aContent argument to LocalAccessible::DoCommand/DispatchClickEvent. r=morgan
We never set this argument to anything other than the default, which meant it always used the LocalAccessible's mContent.
This makes the argument unnecessary and potentially confusing in an area which is already a bit difficult to follow, so just remove it.
There should be no functional change.

Differential Revision: https://phabricator.services.mozilla.com/D229297
2024-11-20 04:13:48 +00:00

996 lines
30 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "XULTreeAccessible.h"
#include "LocalAccessible-inl.h"
#include "DocAccessible-inl.h"
#include "nsAccCache.h"
#include "nsAccUtils.h"
#include "nsCoreUtils.h"
#include "nsEventShell.h"
#include "DocAccessible.h"
#include "Relation.h"
#include "mozilla/a11y/Role.h"
#include "States.h"
#include "XULTreeGridAccessible.h"
#include "nsQueryObject.h"
#include "nsComponentManagerUtils.h"
#include "nsIAutoCompletePopup.h"
#include "nsIDOMXULMenuListElement.h"
#include "nsITreeSelection.h"
#include "nsTreeBodyFrame.h"
#include "nsTreeColumns.h"
#include "nsTreeUtils.h"
#include "mozilla/PresShell.h"
#include "mozilla/dom/XULTreeElementBinding.h"
using namespace mozilla::a11y;
////////////////////////////////////////////////////////////////////////////////
// XULTreeAccessible
////////////////////////////////////////////////////////////////////////////////
XULTreeAccessible::XULTreeAccessible(nsIContent* aContent, DocAccessible* aDoc,
nsTreeBodyFrame* aTreeFrame)
: AccessibleWrap(aContent, aDoc),
mAccessibleCache(kDefaultTreeCacheLength) {
mType = eXULTreeType;
mGenericTypes |= eSelect;
nsCOMPtr<nsITreeView> view = aTreeFrame->GetExistingView();
mTreeView = view;
mTree = nsCoreUtils::GetTree(aContent);
NS_ASSERTION(mTree, "Can't get mTree!\n");
nsIContent* parentContent = mContent->GetParent();
if (parentContent) {
nsCOMPtr<nsIAutoCompletePopup> autoCompletePopupElm =
do_QueryInterface(parentContent);
if (autoCompletePopupElm) mGenericTypes |= eAutoCompletePopup;
}
}
XULTreeAccessible::~XULTreeAccessible() {}
////////////////////////////////////////////////////////////////////////////////
// XULTreeAccessible: nsISupports and cycle collection implementation
NS_IMPL_CYCLE_COLLECTION_INHERITED(XULTreeAccessible, LocalAccessible, mTree,
mAccessibleCache)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(XULTreeAccessible)
NS_INTERFACE_MAP_END_INHERITING(LocalAccessible)
NS_IMPL_ADDREF_INHERITED(XULTreeAccessible, LocalAccessible)
NS_IMPL_RELEASE_INHERITED(XULTreeAccessible, LocalAccessible)
////////////////////////////////////////////////////////////////////////////////
// XULTreeAccessible: LocalAccessible implementation
uint64_t XULTreeAccessible::NativeState() const {
// Get focus status from base class.
uint64_t state = LocalAccessible::NativeState();
// readonly state
state |= states::READONLY;
// multiselectable state.
if (!mTreeView) return state;
nsCOMPtr<nsITreeSelection> selection;
mTreeView->GetSelection(getter_AddRefs(selection));
NS_ENSURE_TRUE(selection, state);
bool isSingle = false;
nsresult rv = selection->GetSingle(&isSingle);
NS_ENSURE_SUCCESS(rv, state);
if (!isSingle) state |= states::MULTISELECTABLE;
return state;
}
void XULTreeAccessible::Value(nsString& aValue) const {
aValue.Truncate();
if (!mTreeView) return;
// Return the value is the first selected child.
nsCOMPtr<nsITreeSelection> selection;
mTreeView->GetSelection(getter_AddRefs(selection));
if (!selection) return;
int32_t currentIndex;
selection->GetCurrentIndex(&currentIndex);
if (currentIndex >= 0) {
RefPtr<nsTreeColumn> keyCol;
RefPtr<nsTreeColumns> cols = mTree->GetColumns();
if (cols) keyCol = cols->GetKeyColumn();
mTreeView->GetCellText(currentIndex, keyCol, aValue);
}
}
////////////////////////////////////////////////////////////////////////////////
// XULTreeAccessible: LocalAccessible implementation
void XULTreeAccessible::Shutdown() {
if (mDoc && !mDoc->IsDefunct()) {
UnbindCacheEntriesFromDocument(mAccessibleCache);
}
mTree = nullptr;
mTreeView = nullptr;
AccessibleWrap::Shutdown();
}
role XULTreeAccessible::NativeRole() const {
// No primary column means we're in a list. In fact, history and mail turn off
// the primary flag when switching to a flat view.
nsIContent* child =
nsTreeUtils::GetDescendantChild(mContent, nsGkAtoms::treechildren);
NS_ASSERTION(child, "tree without treechildren!");
nsTreeBodyFrame* treeFrame = do_QueryFrame(child->GetPrimaryFrame());
NS_ASSERTION(treeFrame, "xul tree accessible for tree without a frame!");
if (!treeFrame) return roles::LIST;
RefPtr<nsTreeColumns> cols = treeFrame->Columns();
nsTreeColumn* primaryCol = cols->GetPrimaryColumn();
return primaryCol ? roles::OUTLINE : roles::LIST;
}
////////////////////////////////////////////////////////////////////////////////
// XULTreeAccessible: LocalAccessible implementation (DON'T put methods here)
LocalAccessible* XULTreeAccessible::LocalChildAtPoint(
int32_t aX, int32_t aY, EWhichChildAtPoint aWhichChild) {
nsIFrame* frame = GetFrame();
if (!frame) return nullptr;
nsPresContext* presContext = frame->PresContext();
PresShell* presShell = presContext->PresShell();
nsIFrame* rootFrame = presShell->GetRootFrame();
NS_ENSURE_TRUE(rootFrame, nullptr);
CSSIntRect rootRect = rootFrame->GetScreenRect();
int32_t clientX = presContext->DevPixelsToIntCSSPixels(aX) - rootRect.X();
int32_t clientY = presContext->DevPixelsToIntCSSPixels(aY) - rootRect.Y();
ErrorResult rv;
dom::TreeCellInfo cellInfo;
mTree->GetCellAt(clientX, clientY, cellInfo, rv);
// If we failed to find tree cell for the given point then it might be
// tree columns.
if (cellInfo.mRow == -1 || !cellInfo.mCol) {
return AccessibleWrap::LocalChildAtPoint(aX, aY, aWhichChild);
}
XULTreeItemAccessibleBase* child = GetTreeItemAccessible(cellInfo.mRow);
if (aWhichChild == EWhichChildAtPoint::DeepestChild && child) {
LocalAccessible* cell = child->GetCellAccessible(cellInfo.mCol);
if (cell) {
return cell;
}
}
return child;
}
////////////////////////////////////////////////////////////////////////////////
// XULTreeAccessible: SelectAccessible
LocalAccessible* XULTreeAccessible::CurrentItem() const {
if (!mTreeView) return nullptr;
nsCOMPtr<nsITreeSelection> selection;
mTreeView->GetSelection(getter_AddRefs(selection));
if (selection) {
int32_t currentIndex = -1;
selection->GetCurrentIndex(&currentIndex);
if (currentIndex >= 0) return GetTreeItemAccessible(currentIndex);
}
return nullptr;
}
void XULTreeAccessible::SetCurrentItem(const LocalAccessible* aItem) {
NS_ERROR("XULTreeAccessible::SetCurrentItem not implemented");
}
void XULTreeAccessible::SelectedItems(nsTArray<Accessible*>* aItems) {
if (!mTreeView) return;
nsCOMPtr<nsITreeSelection> selection;
mTreeView->GetSelection(getter_AddRefs(selection));
if (!selection) return;
int32_t rangeCount = 0;
selection->GetRangeCount(&rangeCount);
for (int32_t rangeIdx = 0; rangeIdx < rangeCount; rangeIdx++) {
int32_t firstIdx = 0, lastIdx = -1;
selection->GetRangeAt(rangeIdx, &firstIdx, &lastIdx);
for (int32_t rowIdx = firstIdx; rowIdx <= lastIdx; rowIdx++) {
LocalAccessible* item = GetTreeItemAccessible(rowIdx);
if (item) aItems->AppendElement(item);
}
}
}
uint32_t XULTreeAccessible::SelectedItemCount() {
if (!mTreeView) return 0;
nsCOMPtr<nsITreeSelection> selection;
mTreeView->GetSelection(getter_AddRefs(selection));
if (selection) {
int32_t count = 0;
selection->GetCount(&count);
return count;
}
return 0;
}
bool XULTreeAccessible::AddItemToSelection(uint32_t aIndex) {
if (!mTreeView) return false;
nsCOMPtr<nsITreeSelection> selection;
mTreeView->GetSelection(getter_AddRefs(selection));
if (selection) {
bool isSelected = false;
selection->IsSelected(aIndex, &isSelected);
if (!isSelected) selection->ToggleSelect(aIndex);
return true;
}
return false;
}
bool XULTreeAccessible::RemoveItemFromSelection(uint32_t aIndex) {
if (!mTreeView) return false;
nsCOMPtr<nsITreeSelection> selection;
mTreeView->GetSelection(getter_AddRefs(selection));
if (selection) {
bool isSelected = false;
selection->IsSelected(aIndex, &isSelected);
if (isSelected) selection->ToggleSelect(aIndex);
return true;
}
return false;
}
bool XULTreeAccessible::IsItemSelected(uint32_t aIndex) {
if (!mTreeView) return false;
nsCOMPtr<nsITreeSelection> selection;
mTreeView->GetSelection(getter_AddRefs(selection));
if (selection) {
bool isSelected = false;
selection->IsSelected(aIndex, &isSelected);
return isSelected;
}
return false;
}
bool XULTreeAccessible::UnselectAll() {
if (!mTreeView) return false;
nsCOMPtr<nsITreeSelection> selection;
mTreeView->GetSelection(getter_AddRefs(selection));
if (!selection) return false;
selection->ClearSelection();
return true;
}
Accessible* XULTreeAccessible::GetSelectedItem(uint32_t aIndex) {
if (!mTreeView) return nullptr;
nsCOMPtr<nsITreeSelection> selection;
mTreeView->GetSelection(getter_AddRefs(selection));
if (!selection) return nullptr;
uint32_t selCount = 0;
int32_t rangeCount = 0;
selection->GetRangeCount(&rangeCount);
for (int32_t rangeIdx = 0; rangeIdx < rangeCount; rangeIdx++) {
int32_t firstIdx = 0, lastIdx = -1;
selection->GetRangeAt(rangeIdx, &firstIdx, &lastIdx);
for (int32_t rowIdx = firstIdx; rowIdx <= lastIdx; rowIdx++) {
if (selCount == aIndex) return GetTreeItemAccessible(rowIdx);
selCount++;
}
}
return nullptr;
}
bool XULTreeAccessible::SelectAll() {
// see if we are multiple select if so set ourselves as such
if (!mTreeView) return false;
nsCOMPtr<nsITreeSelection> selection;
mTreeView->GetSelection(getter_AddRefs(selection));
if (selection) {
bool single = false;
selection->GetSingle(&single);
if (!single) {
selection->SelectAll();
return true;
}
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
// XULTreeAccessible: LocalAccessible implementation
LocalAccessible* XULTreeAccessible::LocalChildAt(uint32_t aIndex) const {
uint32_t childCount = LocalAccessible::ChildCount();
if (aIndex < childCount) {
return LocalAccessible::LocalChildAt(aIndex);
}
return GetTreeItemAccessible(aIndex - childCount);
}
uint32_t XULTreeAccessible::ChildCount() const {
// Tree's children count is row count + treecols count.
uint32_t childCount = LocalAccessible::ChildCount();
if (!mTreeView) return childCount;
int32_t rowCount = 0;
mTreeView->GetRowCount(&rowCount);
childCount += rowCount;
return childCount;
}
Relation XULTreeAccessible::RelationByType(RelationType aType) const {
if (aType == RelationType::NODE_PARENT_OF) {
if (mTreeView) {
return Relation(new XULTreeItemIterator(this, mTreeView, -1));
}
return Relation();
}
return LocalAccessible::RelationByType(aType);
}
////////////////////////////////////////////////////////////////////////////////
// XULTreeAccessible: Widgets
bool XULTreeAccessible::IsWidget() const { return true; }
bool XULTreeAccessible::IsActiveWidget() const {
if (IsAutoCompletePopup()) {
nsCOMPtr<nsIAutoCompletePopup> autoCompletePopupElm =
do_QueryInterface(mContent->GetParent());
if (autoCompletePopupElm) {
bool isOpen = false;
autoCompletePopupElm->GetPopupOpen(&isOpen);
return isOpen;
}
}
return FocusMgr()->HasDOMFocus(mContent);
}
bool XULTreeAccessible::AreItemsOperable() const {
if (IsAutoCompletePopup()) {
nsCOMPtr<nsIAutoCompletePopup> autoCompletePopupElm =
do_QueryInterface(mContent->GetParent());
if (autoCompletePopupElm) {
bool isOpen = false;
autoCompletePopupElm->GetPopupOpen(&isOpen);
return isOpen;
}
}
return true;
}
LocalAccessible* XULTreeAccessible::ContainerWidget() const { return nullptr; }
////////////////////////////////////////////////////////////////////////////////
// XULTreeAccessible: public implementation
XULTreeItemAccessibleBase* XULTreeAccessible::GetTreeItemAccessible(
int32_t aRow) const {
if (aRow < 0 || IsDefunct() || !mTreeView) return nullptr;
int32_t rowCount = 0;
nsresult rv = mTreeView->GetRowCount(&rowCount);
if (NS_FAILED(rv) || aRow >= rowCount) return nullptr;
void* key = reinterpret_cast<void*>(intptr_t(aRow));
return mAccessibleCache.WithEntryHandle(
key, [&](auto&& entry) -> XULTreeItemAccessibleBase* {
if (entry) {
return entry->get();
}
RefPtr<XULTreeItemAccessibleBase> treeItem =
CreateTreeItemAccessible(aRow);
if (treeItem) {
entry.Insert(RefPtr{treeItem});
Document()->BindToDocument(treeItem, nullptr);
return treeItem.get();
}
return nullptr;
});
}
void XULTreeAccessible::InvalidateCache(int32_t aRow, int32_t aCount) {
if (IsDefunct()) return;
if (!mTreeView) {
UnbindCacheEntriesFromDocument(mAccessibleCache);
return;
}
// Do not invalidate the cache if rows have been inserted.
if (aCount > 0) return;
DocAccessible* document = Document();
// Fire destroy event for removed tree items and delete them from caches.
for (int32_t rowIdx = aRow; rowIdx < aRow - aCount; rowIdx++) {
void* key = reinterpret_cast<void*>(intptr_t(rowIdx));
XULTreeItemAccessibleBase* treeItem = mAccessibleCache.GetWeak(key);
if (treeItem) {
RefPtr<AccEvent> event =
new AccEvent(nsIAccessibleEvent::EVENT_HIDE, treeItem);
nsEventShell::FireEvent(event);
// Unbind from document, shutdown and remove from tree cache.
document->UnbindFromDocument(treeItem);
mAccessibleCache.Remove(key);
}
}
// We dealt with removed tree items already however we may keep tree items
// having row indexes greater than row count. We should remove these dead tree
// items silently from caches.
int32_t newRowCount = 0;
nsresult rv = mTreeView->GetRowCount(&newRowCount);
if (NS_FAILED(rv)) return;
int32_t oldRowCount = newRowCount - aCount;
for (int32_t rowIdx = newRowCount; rowIdx < oldRowCount; ++rowIdx) {
void* key = reinterpret_cast<void*>(intptr_t(rowIdx));
XULTreeItemAccessibleBase* treeItem = mAccessibleCache.GetWeak(key);
if (treeItem) {
// Unbind from document, shutdown and remove from tree cache.
document->UnbindFromDocument(treeItem);
mAccessibleCache.Remove(key);
}
}
}
void XULTreeAccessible::TreeViewInvalidated(int32_t aStartRow, int32_t aEndRow,
int32_t aStartCol,
int32_t aEndCol) {
if (IsDefunct()) return;
if (!mTreeView) {
UnbindCacheEntriesFromDocument(mAccessibleCache);
return;
}
int32_t endRow = aEndRow;
nsresult rv;
if (endRow == -1) {
int32_t rowCount = 0;
rv = mTreeView->GetRowCount(&rowCount);
if (NS_FAILED(rv)) return;
endRow = rowCount - 1;
}
RefPtr<nsTreeColumns> treeColumns = mTree->GetColumns();
if (!treeColumns) return;
int32_t endCol = aEndCol;
if (endCol == -1) {
// We need to make sure to cast to int32_t before we do the subtraction, in
// case the column count is 0.
endCol = static_cast<int32_t>(treeColumns->Count()) - 1;
}
for (int32_t rowIdx = aStartRow; rowIdx <= endRow; ++rowIdx) {
void* key = reinterpret_cast<void*>(intptr_t(rowIdx));
XULTreeItemAccessibleBase* treeitemAcc = mAccessibleCache.GetWeak(key);
if (treeitemAcc) {
treeitemAcc->RowInvalidated(aStartCol, endCol);
}
}
}
void XULTreeAccessible::TreeViewChanged(nsITreeView* aView) {
if (IsDefunct()) return;
// Fire reorder event on tree accessible on accessible tree (do not fire
// show/hide events on tree items because it can be expensive to fire them for
// each tree item.
RefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(this);
Document()->FireDelayedEvent(reorderEvent);
// Clear cache.
UnbindCacheEntriesFromDocument(mAccessibleCache);
mTreeView = aView;
LocalAccessible* item = CurrentItem();
if (item) {
FocusMgr()->ActiveItemChanged(item, true);
}
}
////////////////////////////////////////////////////////////////////////////////
// XULTreeAccessible: protected implementation
already_AddRefed<XULTreeItemAccessibleBase>
XULTreeAccessible::CreateTreeItemAccessible(int32_t aRow) const {
RefPtr<XULTreeItemAccessibleBase> accessible = new XULTreeItemAccessible(
mContent, mDoc, const_cast<XULTreeAccessible*>(this), mTree, mTreeView,
aRow);
return accessible.forget();
}
////////////////////////////////////////////////////////////////////////////////
// XULTreeItemAccessibleBase
////////////////////////////////////////////////////////////////////////////////
XULTreeItemAccessibleBase::XULTreeItemAccessibleBase(
nsIContent* aContent, DocAccessible* aDoc, LocalAccessible* aParent,
dom::XULTreeElement* aTree, nsITreeView* aTreeView, int32_t aRow)
: AccessibleWrap(aContent, aDoc),
mTree(aTree),
mTreeView(aTreeView),
mRow(aRow) {
mParent = aParent;
mStateFlags |= eSharedNode;
}
XULTreeItemAccessibleBase::~XULTreeItemAccessibleBase() {}
////////////////////////////////////////////////////////////////////////////////
// XULTreeItemAccessibleBase: nsISupports implementation
NS_IMPL_CYCLE_COLLECTION_INHERITED(XULTreeItemAccessibleBase, LocalAccessible,
mTree)
NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(XULTreeItemAccessibleBase,
LocalAccessible)
////////////////////////////////////////////////////////////////////////////////
// XULTreeItemAccessibleBase: LocalAccessible
Accessible* XULTreeItemAccessibleBase::FocusedChild() {
return FocusMgr()->FocusedLocalAccessible() == this ? this : nullptr;
}
nsIntRect XULTreeItemAccessibleBase::BoundsInCSSPixels() const {
// Get x coordinate and width from treechildren element, get y coordinate and
// height from tree cell.
RefPtr<nsTreeColumn> column = nsCoreUtils::GetFirstSensibleColumn(mTree);
nsresult rv;
nsIntRect rect = mTree->GetCoordsForCellItem(mRow, column, u"cell"_ns, rv);
if (NS_FAILED(rv)) {
return nsIntRect();
}
RefPtr<dom::Element> bodyElement = mTree->GetTreeBody();
if (!bodyElement || !bodyElement->IsXULElement()) {
return nsIntRect();
}
rect.width = bodyElement->ClientWidth();
nsIFrame* bodyFrame = bodyElement->GetPrimaryFrame();
if (!bodyFrame) {
return nsIntRect();
}
CSSIntRect screenRect = bodyFrame->GetScreenRect();
rect.x = screenRect.x;
rect.y += screenRect.y;
return rect;
}
nsRect XULTreeItemAccessibleBase::BoundsInAppUnits() const {
nsIntRect bounds = BoundsInCSSPixels();
nsPresContext* presContext = mDoc->PresContext();
return nsRect(presContext->CSSPixelsToAppUnits(bounds.X()),
presContext->CSSPixelsToAppUnits(bounds.Y()),
presContext->CSSPixelsToAppUnits(bounds.Width()),
presContext->CSSPixelsToAppUnits(bounds.Height()));
}
void XULTreeItemAccessibleBase::SetSelected(bool aSelect) {
nsCOMPtr<nsITreeSelection> selection;
mTreeView->GetSelection(getter_AddRefs(selection));
if (selection) {
bool isSelected = false;
selection->IsSelected(mRow, &isSelected);
if (isSelected != aSelect) selection->ToggleSelect(mRow);
}
}
void XULTreeItemAccessibleBase::TakeFocus() const {
nsCOMPtr<nsITreeSelection> selection;
mTreeView->GetSelection(getter_AddRefs(selection));
if (selection) selection->SetCurrentIndex(mRow);
// focus event will be fired here
LocalAccessible::TakeFocus();
}
Relation XULTreeItemAccessibleBase::RelationByType(RelationType aType) const {
switch (aType) {
case RelationType::NODE_CHILD_OF: {
int32_t parentIndex = -1;
if (!NS_SUCCEEDED(mTreeView->GetParentIndex(mRow, &parentIndex))) {
return Relation();
}
if (parentIndex == -1) return Relation(mParent);
XULTreeAccessible* treeAcc = mParent->AsXULTree();
return Relation(treeAcc->GetTreeItemAccessible(parentIndex));
}
case RelationType::NODE_PARENT_OF: {
bool isTrue = false;
if (NS_FAILED(mTreeView->IsContainerEmpty(mRow, &isTrue)) || isTrue) {
return Relation();
}
if (NS_FAILED(mTreeView->IsContainerOpen(mRow, &isTrue)) || !isTrue) {
return Relation();
}
XULTreeAccessible* tree = mParent->AsXULTree();
return Relation(new XULTreeItemIterator(tree, mTreeView, mRow));
}
default:
return Relation();
}
}
bool XULTreeItemAccessibleBase::HasPrimaryAction() const { return true; }
uint8_t XULTreeItemAccessibleBase::ActionCount() const {
// "activate" action is available for all treeitems, "expand/collapse" action
// is avaible for treeitem which is container.
return IsExpandable() ? 2 : 1;
}
void XULTreeItemAccessibleBase::ActionNameAt(uint8_t aIndex, nsAString& aName) {
if (aIndex == eAction_Click) {
aName.AssignLiteral("activate");
return;
}
if (aIndex == eAction_Expand && IsExpandable()) {
bool isContainerOpen = false;
mTreeView->IsContainerOpen(mRow, &isContainerOpen);
if (isContainerOpen) {
aName.AssignLiteral("collapse");
} else {
aName.AssignLiteral("expand");
}
}
}
bool XULTreeItemAccessibleBase::DoAction(uint8_t aIndex) const {
if (aIndex != eAction_Click &&
(aIndex != eAction_Expand || !IsExpandable())) {
return false;
}
DoCommand(aIndex);
return true;
}
////////////////////////////////////////////////////////////////////////////////
// XULTreeItemAccessibleBase: LocalAccessible implementation
void XULTreeItemAccessibleBase::Shutdown() {
mTree = nullptr;
mTreeView = nullptr;
mRow = -1;
mParent = nullptr; // null-out to prevent base class's shutdown ops
AccessibleWrap::Shutdown();
}
GroupPos XULTreeItemAccessibleBase::GroupPosition() {
GroupPos groupPos;
int32_t level;
nsresult rv = mTreeView->GetLevel(mRow, &level);
NS_ENSURE_SUCCESS(rv, groupPos);
int32_t topCount = 1;
for (int32_t index = mRow - 1; index >= 0; index--) {
int32_t lvl = -1;
if (NS_SUCCEEDED(mTreeView->GetLevel(index, &lvl))) {
if (lvl < level) break;
if (lvl == level) topCount++;
}
}
int32_t rowCount = 0;
rv = mTreeView->GetRowCount(&rowCount);
NS_ENSURE_SUCCESS(rv, groupPos);
int32_t bottomCount = 0;
for (int32_t index = mRow + 1; index < rowCount; index++) {
int32_t lvl = -1;
if (NS_SUCCEEDED(mTreeView->GetLevel(index, &lvl))) {
if (lvl < level) break;
if (lvl == level) bottomCount++;
}
}
groupPos.level = level + 1;
groupPos.setSize = topCount + bottomCount;
groupPos.posInSet = topCount;
return groupPos;
}
uint64_t XULTreeItemAccessibleBase::NativeState() const {
// focusable and selectable states
uint64_t state = NativeInteractiveState();
// expanded/collapsed state
if (IsExpandable()) {
bool isContainerOpen;
mTreeView->IsContainerOpen(mRow, &isContainerOpen);
state |= isContainerOpen ? states::EXPANDED : states::COLLAPSED;
}
// selected state
nsCOMPtr<nsITreeSelection> selection;
mTreeView->GetSelection(getter_AddRefs(selection));
if (selection) {
bool isSelected;
selection->IsSelected(mRow, &isSelected);
if (isSelected) state |= states::SELECTED;
}
// focused state
if (FocusMgr()->IsFocused(this)) state |= states::FOCUSED;
// invisible state
int32_t firstVisibleRow = mTree->GetFirstVisibleRow();
int32_t lastVisibleRow = mTree->GetLastVisibleRow();
if (mRow < firstVisibleRow || mRow > lastVisibleRow) {
state |= states::INVISIBLE;
}
return state;
}
uint64_t XULTreeItemAccessibleBase::NativeInteractiveState() const {
return states::FOCUSABLE | states::SELECTABLE;
}
int32_t XULTreeItemAccessibleBase::IndexInParent() const {
return mParent ? mParent->ContentChildCount() + mRow : -1;
}
////////////////////////////////////////////////////////////////////////////////
// XULTreeItemAccessibleBase: Widgets
LocalAccessible* XULTreeItemAccessibleBase::ContainerWidget() const {
return mParent;
}
////////////////////////////////////////////////////////////////////////////////
// XULTreeItemAccessibleBase: LocalAccessible protected methods
void XULTreeItemAccessibleBase::DispatchClickEvent(
uint32_t aActionIndex) const {
if (IsDefunct()) return;
RefPtr<nsTreeColumns> columns = mTree->GetColumns();
if (!columns) return;
// Get column and pseudo element.
RefPtr<nsTreeColumn> column;
nsAutoString pseudoElm;
if (aActionIndex == eAction_Click) {
// Key column is visible and clickable.
column = columns->GetKeyColumn();
} else {
// Primary column contains a twisty we should click on.
column = columns->GetPrimaryColumn();
pseudoElm = u"twisty"_ns;
}
if (column) {
RefPtr<dom::XULTreeElement> tree = mTree;
nsCoreUtils::DispatchClickEvent(tree, mRow, column, pseudoElm);
}
}
LocalAccessible* XULTreeItemAccessibleBase::GetSiblingAtOffset(
int32_t aOffset, nsresult* aError) const {
if (aError) *aError = NS_OK; // fail peacefully
return mParent->LocalChildAt(IndexInParent() + aOffset);
}
////////////////////////////////////////////////////////////////////////////////
// XULTreeItemAccessibleBase: protected implementation
bool XULTreeItemAccessibleBase::IsExpandable() const {
bool isContainer = false;
mTreeView->IsContainer(mRow, &isContainer);
if (isContainer) {
bool isEmpty = false;
mTreeView->IsContainerEmpty(mRow, &isEmpty);
if (!isEmpty) {
RefPtr<nsTreeColumns> columns = mTree->GetColumns();
if (columns) {
nsTreeColumn* primaryColumn = columns->GetPrimaryColumn();
if (primaryColumn && !nsCoreUtils::IsColumnHidden(primaryColumn)) {
return true;
}
}
}
}
return false;
}
void XULTreeItemAccessibleBase::GetCellName(nsTreeColumn* aColumn,
nsAString& aName) const {
mTreeView->GetCellText(mRow, aColumn, aName);
// If there is still no name try the cell value:
// This is for graphical cells. We need tree/table view implementors to
// implement FooView::GetCellValue to return a meaningful string for cases
// where there is something shown in the cell (non-text) such as a star icon;
// in which case GetCellValue for that cell would return "starred" or
// "flagged" for example.
if (aName.IsEmpty()) mTreeView->GetCellValue(mRow, aColumn, aName);
}
////////////////////////////////////////////////////////////////////////////////
// XULTreeItemAccessible
////////////////////////////////////////////////////////////////////////////////
XULTreeItemAccessible::XULTreeItemAccessible(
nsIContent* aContent, DocAccessible* aDoc, LocalAccessible* aParent,
dom::XULTreeElement* aTree, nsITreeView* aTreeView, int32_t aRow)
: XULTreeItemAccessibleBase(aContent, aDoc, aParent, aTree, aTreeView,
aRow) {
mStateFlags |= eNoKidsFromDOM;
mColumn = nsCoreUtils::GetFirstSensibleColumn(mTree, FlushType::None);
GetCellName(mColumn, mCachedName);
}
XULTreeItemAccessible::~XULTreeItemAccessible() {}
////////////////////////////////////////////////////////////////////////////////
// XULTreeItemAccessible: nsISupports implementation
NS_IMPL_CYCLE_COLLECTION_INHERITED(XULTreeItemAccessible,
XULTreeItemAccessibleBase, mColumn)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(XULTreeItemAccessible)
NS_INTERFACE_MAP_END_INHERITING(XULTreeItemAccessibleBase)
NS_IMPL_ADDREF_INHERITED(XULTreeItemAccessible, XULTreeItemAccessibleBase)
NS_IMPL_RELEASE_INHERITED(XULTreeItemAccessible, XULTreeItemAccessibleBase)
////////////////////////////////////////////////////////////////////////////////
// XULTreeItemAccessible: nsIAccessible implementation
ENameValueFlag XULTreeItemAccessible::Name(nsString& aName) const {
aName.Truncate();
GetCellName(mColumn, aName);
return eNameOK;
}
////////////////////////////////////////////////////////////////////////////////
// XULTreeItemAccessible: LocalAccessible implementation
void XULTreeItemAccessible::Shutdown() {
mColumn = nullptr;
XULTreeItemAccessibleBase::Shutdown();
}
role XULTreeItemAccessible::NativeRole() const {
RefPtr<nsTreeColumns> columns = mTree->GetColumns();
if (!columns) {
NS_ERROR("No tree columns object in the tree!");
return roles::NOTHING;
}
nsTreeColumn* primaryColumn = columns->GetPrimaryColumn();
return primaryColumn ? roles::OUTLINEITEM : roles::LISTITEM;
}
////////////////////////////////////////////////////////////////////////////////
// XULTreeItemAccessible: XULTreeItemAccessibleBase implementation
void XULTreeItemAccessible::RowInvalidated(int32_t aStartColIdx,
int32_t aEndColIdx) {
nsAutoString name;
Name(name);
if (name != mCachedName) {
nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_NAME_CHANGE, this);
mCachedName = name;
}
}
////////////////////////////////////////////////////////////////////////////////
// XULTreeColumAccessible
////////////////////////////////////////////////////////////////////////////////
XULTreeColumAccessible::XULTreeColumAccessible(nsIContent* aContent,
DocAccessible* aDoc)
: XULColumAccessible(aContent, aDoc) {}
LocalAccessible* XULTreeColumAccessible::GetSiblingAtOffset(
int32_t aOffset, nsresult* aError) const {
if (aOffset < 0) {
return XULColumAccessible::GetSiblingAtOffset(aOffset, aError);
}
if (aError) *aError = NS_OK; // fail peacefully
RefPtr<dom::XULTreeElement> tree = nsCoreUtils::GetTree(mContent);
if (tree) {
nsCOMPtr<nsITreeView> treeView = tree->GetView(FlushType::None);
if (treeView) {
int32_t rowCount = 0;
treeView->GetRowCount(&rowCount);
if (rowCount > 0 && aOffset <= rowCount) {
XULTreeAccessible* treeAcc = LocalParent()->AsXULTree();
if (treeAcc) return treeAcc->GetTreeItemAccessible(aOffset - 1);
}
}
}
return nullptr;
}