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
996 lines
30 KiB
C++
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(¤tIndex);
|
|
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(¤tIndex);
|
|
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;
|
|
}
|