/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Author: Kyle Yuan (kyle.yuan@sun.com) * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "nsXULTreeAccessible.h" #include "nsDocAccessible.h" #include "nsIDOMXULElement.h" #include "nsIDOMXULMultSelectCntrlEl.h" #include "nsIDOMXULTreeElement.h" #include "nsITreeSelection.h" #include "nsIMutableArray.h" #include "nsComponentManagerUtils.h" //////////////////////////////////////////////////////////////////////////////// // Internal static functions //////////////////////////////////////////////////////////////////////////////// static PLDHashOperator ElementTraverser(const void *aKey, nsIAccessNode *aAccessNode, void *aUserArg) { nsCycleCollectionTraversalCallback *cb = static_cast(aUserArg); NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mAccessNodeCache of XUL tree entry"); cb->NoteXPCOMChild(aAccessNode); return PL_DHASH_NEXT; } //////////////////////////////////////////////////////////////////////////////// // nsXULTreeAccessible //////////////////////////////////////////////////////////////////////////////// nsXULTreeAccessible:: nsXULTreeAccessible(nsIDOMNode *aDOMNode, nsIWeakReference *aShell) : nsXULSelectableAccessible(aDOMNode, aShell) { nsCoreUtils::GetTreeBoxObject(aDOMNode, getter_AddRefs(mTree)); if (mTree) mTree->GetView(getter_AddRefs(mTreeView)); NS_ASSERTION(mTree && mTreeView, "Can't get mTree or mTreeView!\n"); mAccessNodeCache.Init(kDefaultTreeCacheSize); } //////////////////////////////////////////////////////////////////////////////// // nsXULTreeAccessible: nsISupports and cycle collection implementation NS_IMPL_CYCLE_COLLECTION_CLASS(nsXULTreeAccessible) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsXULTreeAccessible, nsAccessible) tmp->mAccessNodeCache.EnumerateRead(ElementTraverser, &cb); NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsXULTreeAccessible, nsAccessible) tmp->ClearCache(tmp->mAccessNodeCache); NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsXULTreeAccessible) NS_INTERFACE_MAP_STATIC_AMBIGUOUS(nsXULTreeAccessible) NS_INTERFACE_MAP_END_INHERITING(nsXULSelectableAccessible) NS_IMPL_ADDREF_INHERITED(nsXULTreeAccessible, nsXULSelectableAccessible) NS_IMPL_RELEASE_INHERITED(nsXULTreeAccessible, nsXULSelectableAccessible) //////////////////////////////////////////////////////////////////////////////// // nsXULTreeAccessible: nsAccessible implementation nsresult nsXULTreeAccessible::GetStateInternal(PRUint32 *aState, PRUint32 *aExtraState) { // Get focus status from base class. nsresult rv = nsAccessible::GetStateInternal(aState, aExtraState); NS_ENSURE_A11Y_SUCCESS(rv, rv); // readonly state *aState |= nsIAccessibleStates::STATE_READONLY; // remove focusable and focused states since tree items are focusable for AT *aState &= ~nsIAccessibleStates::STATE_FOCUSABLE; *aState &= ~nsIAccessibleStates::STATE_FOCUSED; // multiselectable state. nsCOMPtr selection; mTreeView->GetSelection(getter_AddRefs(selection)); NS_ENSURE_STATE(selection); PRBool isSingle = PR_FALSE; rv = selection->GetSingle(&isSingle); NS_ENSURE_SUCCESS(rv, rv); if (!isSingle) *aState |= nsIAccessibleStates::STATE_MULTISELECTABLE; return NS_OK; } NS_IMETHODIMP nsXULTreeAccessible::GetValue(nsAString& aValue) { // Return the value is the first selected child. aValue.Truncate(); if (IsDefunct()) return NS_ERROR_FAILURE; nsCOMPtr selection; mTreeView->GetSelection(getter_AddRefs(selection)); if (!selection) return NS_ERROR_FAILURE; PRInt32 currentIndex; nsCOMPtr selectItem; selection->GetCurrentIndex(¤tIndex); if (currentIndex >= 0) { nsCOMPtr keyCol; nsCOMPtr cols; mTree->GetColumns(getter_AddRefs(cols)); if (cols) cols->GetKeyColumn(getter_AddRefs(keyCol)); return mTreeView->GetCellText(currentIndex, keyCol, aValue); } return NS_OK; } //////////////////////////////////////////////////////////////////////////////// // nsXULTreeAccessible: nsAccessNode implementation PRBool nsXULTreeAccessible::IsDefunct() { return nsXULSelectableAccessible::IsDefunct() || !mTree || !mTreeView; } nsresult nsXULTreeAccessible::Shutdown() { // XXX: we don't remove accessible from document cache if shutdown wasn't // initiated by document destroying. Note, we can't remove accessible from // document cache here while document is going to be shutdown. Note, this is // not unique place where we have similar problem. ClearCache(mAccessNodeCache); mTree = nsnull; mTreeView = nsnull; nsXULSelectableAccessible::Shutdown(); return NS_OK; } //////////////////////////////////////////////////////////////////////////////// // nsXULTreeAccessible: nsAccessible implementation (put methods here) nsresult nsXULTreeAccessible::GetRoleInternal(PRUint32 *aRole) { if (IsDefunct()) return NS_ERROR_FAILURE; // 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. nsCOMPtr cols; mTree->GetColumns(getter_AddRefs(cols)); nsCOMPtr primaryCol; if (cols) cols->GetPrimaryColumn(getter_AddRefs(primaryCol)); *aRole = primaryCol ? nsIAccessibleRole::ROLE_OUTLINE : nsIAccessibleRole::ROLE_LIST; return NS_OK; } //////////////////////////////////////////////////////////////////////////////// // nsXULTreeAccessible: nsIAccessible implementation NS_IMETHODIMP nsXULTreeAccessible::GetFocusedChild(nsIAccessible **aFocusedChild) { NS_ENSURE_ARG_POINTER(aFocusedChild); *aFocusedChild = nsnull; if (IsDefunct()) return NS_ERROR_FAILURE; if (gLastFocusedNode != mDOMNode) return NS_OK; nsCOMPtr multiSelect = do_QueryInterface(mDOMNode); if (multiSelect) { PRInt32 row = -1; multiSelect->GetCurrentIndex(&row); if (row >= 0) GetTreeItemAccessible(row, aFocusedChild); } return NS_OK; } //////////////////////////////////////////////////////////////////////////////// // nsXULTreeAccessible: nsAccessible implementation (DON'T put methods here) nsresult nsXULTreeAccessible::GetChildAtPoint(PRInt32 aX, PRInt32 aY, PRBool aDeepestChild, nsIAccessible **aChild) { nsIFrame *frame = GetFrame(); if (!frame) return NS_ERROR_FAILURE; nsPresContext *presContext = frame->PresContext(); nsCOMPtr presShell = presContext->PresShell(); nsIFrame *rootFrame = presShell->GetRootFrame(); NS_ENSURE_STATE(rootFrame); nsIntRect rootRect = rootFrame->GetScreenRectExternal(); PRInt32 clientX = presContext->DevPixelsToIntCSSPixels(aX - rootRect.x); PRInt32 clientY = presContext->DevPixelsToIntCSSPixels(aY - rootRect.y); PRInt32 row = -1; nsCOMPtr column; nsCAutoString childEltUnused; mTree->GetCellAt(clientX, clientY, &row, getter_AddRefs(column), childEltUnused); // If we failed to find tree cell for the given point then it might be // tree columns. if (row == -1 || !column) return nsXULSelectableAccessible:: GetChildAtPoint(aX, aY, aDeepestChild, aChild); GetTreeItemAccessible(row, aChild); if (aDeepestChild && *aChild) { // Look for accessible cell for the found item accessible. nsRefPtr treeitemAcc = nsAccUtils::QueryObject(*aChild); nsCOMPtr cellAccessible; treeitemAcc->GetCellAccessible(column, getter_AddRefs(cellAccessible)); if (cellAccessible) cellAccessible.swap(*aChild); } return NS_OK; } //////////////////////////////////////////////////////////////////////////////// // nsXULTreeAccessible: nsAccessibleSelectable implementation NS_IMETHODIMP nsXULTreeAccessible::GetSelectedChildren(nsIArray **_retval) { // Ask tree selection to get all selected children *_retval = nsnull; if (IsDefunct()) return NS_ERROR_FAILURE; nsCOMPtr selection; mTreeView->GetSelection(getter_AddRefs(selection)); if (!selection) return NS_ERROR_FAILURE; nsCOMPtr selectedAccessibles = do_CreateInstance(NS_ARRAY_CONTRACTID); NS_ENSURE_STATE(selectedAccessibles); PRInt32 rowIndex, rowCount; PRBool isSelected; mTreeView->GetRowCount(&rowCount); for (rowIndex = 0; rowIndex < rowCount; rowIndex++) { selection->IsSelected(rowIndex, &isSelected); if (isSelected) { nsCOMPtr tempAccess; GetTreeItemAccessible(rowIndex, getter_AddRefs(tempAccess)); NS_ENSURE_STATE(tempAccess); selectedAccessibles->AppendElement(tempAccess, PR_FALSE); } } PRUint32 length; selectedAccessibles->GetLength(&length); if (length != 0) { *_retval = selectedAccessibles; NS_IF_ADDREF(*_retval); } return NS_OK; } NS_IMETHODIMP nsXULTreeAccessible::GetSelectionCount(PRInt32 *aSelectionCount) { *aSelectionCount = 0; if (IsDefunct()) return NS_ERROR_FAILURE; nsCOMPtr selection; mTreeView->GetSelection(getter_AddRefs(selection)); if (selection) selection->GetCount(aSelectionCount); return NS_OK; } NS_IMETHODIMP nsXULTreeAccessible::ChangeSelection(PRInt32 aIndex, PRUint8 aMethod, PRBool *aSelState) { if (IsDefunct()) return NS_ERROR_FAILURE; nsCOMPtr selection; mTreeView->GetSelection(getter_AddRefs(selection)); if (selection) { selection->IsSelected(aIndex, aSelState); if ((!(*aSelState) && eSelection_Add == aMethod) || ((*aSelState) && eSelection_Remove == aMethod)) return selection->ToggleSelect(aIndex); } return NS_OK; } NS_IMETHODIMP nsXULTreeAccessible::AddChildToSelection(PRInt32 aIndex) { PRBool isSelected; return ChangeSelection(aIndex, eSelection_Add, &isSelected); } NS_IMETHODIMP nsXULTreeAccessible::RemoveChildFromSelection(PRInt32 aIndex) { PRBool isSelected; return ChangeSelection(aIndex, eSelection_Remove, &isSelected); } NS_IMETHODIMP nsXULTreeAccessible::IsChildSelected(PRInt32 aIndex, PRBool *_retval) { return ChangeSelection(aIndex, eSelection_GetState, _retval); } NS_IMETHODIMP nsXULTreeAccessible::ClearSelection() { if (IsDefunct()) return NS_ERROR_FAILURE; nsCOMPtr selection; mTreeView->GetSelection(getter_AddRefs(selection)); if (selection) selection->ClearSelection(); return NS_OK; } NS_IMETHODIMP nsXULTreeAccessible::RefSelection(PRInt32 aIndex, nsIAccessible **aAccessible) { NS_ENSURE_ARG_POINTER(aAccessible); *aAccessible = nsnull; if (IsDefunct()) return NS_ERROR_FAILURE; nsCOMPtr selection; mTreeView->GetSelection(getter_AddRefs(selection)); if (!selection) return NS_ERROR_FAILURE; PRInt32 rowIndex, rowCount; PRInt32 selCount = 0; PRBool isSelected; mTreeView->GetRowCount(&rowCount); for (rowIndex = 0; rowIndex < rowCount; rowIndex++) { selection->IsSelected(rowIndex, &isSelected); if (isSelected) { if (selCount == aIndex) { GetTreeItemAccessible(rowIndex, aAccessible); return NS_OK; } selCount++; } } return NS_OK; } NS_IMETHODIMP nsXULTreeAccessible::SelectAllSelection(PRBool *aIsMultiSelectable) { NS_ENSURE_ARG_POINTER(aIsMultiSelectable); *aIsMultiSelectable = PR_FALSE; if (IsDefunct()) return NS_ERROR_FAILURE; // see if we are multiple select if so set ourselves as such nsCOMPtr element (do_QueryInterface(mDOMNode)); if (element) { nsCOMPtr selection; mTreeView->GetSelection(getter_AddRefs(selection)); if (selection) { PRBool single = PR_FALSE; selection->GetSingle(&single); if (!single) { *aIsMultiSelectable = PR_TRUE; selection->SelectAll(); } } } return NS_OK; } //////////////////////////////////////////////////////////////////////////////// // nsXULTreeAccessible: nsAccessible implementation nsAccessible* nsXULTreeAccessible::GetChildAt(PRUint32 aIndex) { PRInt32 childCount = nsAccessible::GetChildCount(); if (childCount == -1) return nsnull; if (static_cast(aIndex) < childCount) return nsAccessible::GetChildAt(aIndex); nsCOMPtr child; GetTreeItemAccessible(aIndex - childCount, getter_AddRefs(child)); nsRefPtr childAcc = nsAccUtils::QueryObject(child); return childAcc; } PRInt32 nsXULTreeAccessible::GetChildCount() { // tree's children count is row count + treecols count. PRInt32 childCount = nsAccessible::GetChildCount(); if (childCount == -1) return -1; PRInt32 rowCount = 0; mTreeView->GetRowCount(&rowCount); childCount += rowCount; return childCount; } PRInt32 nsXULTreeAccessible::GetIndexOf(nsIAccessible *aChild) { if (IsDefunct()) return -1; nsRefPtr item = nsAccUtils::QueryObject(aChild); // If the given child is not treeitem then it should be treecols accessible. if (!item) return nsAccessible::GetIndexOf(aChild); return nsAccessible::GetChildCount() + item->GetRowIndex(); } //////////////////////////////////////////////////////////////////////////////// // nsXULTreeAccessible: public implementation void nsXULTreeAccessible::GetTreeItemAccessible(PRInt32 aRow, nsIAccessible** aAccessible) { *aAccessible = nsnull; if (aRow < 0 || IsDefunct()) return; PRInt32 rowCount = 0; nsresult rv = mTreeView->GetRowCount(&rowCount); if (NS_FAILED(rv) || aRow >= rowCount) return; void *key = reinterpret_cast(aRow); nsCOMPtr accessNode; GetCacheEntry(mAccessNodeCache, key, getter_AddRefs(accessNode)); if (!accessNode) { nsRefPtr treeItemAcc; CreateTreeItemAccessible(aRow, getter_AddRefs(treeItemAcc)); if (!treeItemAcc) return; nsresult rv = treeItemAcc->Init(); if (NS_FAILED(rv)) return; accessNode = treeItemAcc; PutCacheEntry(mAccessNodeCache, key, accessNode); } CallQueryInterface(accessNode, aAccessible); } void nsXULTreeAccessible::InvalidateCache(PRInt32 aRow, PRInt32 aCount) { if (IsDefunct()) return; // Do not invalidate the cache if rows have been inserted. if (aCount > 0) return; // Fire destroy event for removed tree items and delete them from caches. for (PRInt32 rowIdx = aRow; rowIdx < aRow - aCount; rowIdx++) { void *key = reinterpret_cast(rowIdx); nsCOMPtr accessNode; GetCacheEntry(mAccessNodeCache, key, getter_AddRefs(accessNode)); if (accessNode) { nsRefPtr accessible = nsAccUtils::QueryAccessible(accessNode); nsRefPtr event = new nsAccEvent(nsIAccessibleEvent::EVENT_HIDE, accessible, PR_FALSE); nsEventShell::FireEvent(event); accessible->Shutdown(); // Remove accessible from document cache and tree cache. nsCOMPtr docAccessible = GetDocAccessible(); if (docAccessible) { nsRefPtr docAcc = nsAccUtils::QueryAccessibleDocument(docAccessible); docAcc->RemoveAccessNodeFromCache(accessible); } mAccessNodeCache.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. PRInt32 newRowCount = 0; nsresult rv = mTreeView->GetRowCount(&newRowCount); if (NS_FAILED(rv)) return; PRInt32 oldRowCount = newRowCount - aCount; for (PRInt32 rowIdx = newRowCount; rowIdx < oldRowCount; ++rowIdx) { void *key = reinterpret_cast(rowIdx); nsCOMPtr accessNode; GetCacheEntry(mAccessNodeCache, key, getter_AddRefs(accessNode)); if (accessNode) { nsRefPtr accNode = nsAccUtils::QueryAccessNode(accessNode); accNode->Shutdown(); // Remove accessible from document cache and tree cache. nsCOMPtr docAccessible = GetDocAccessible(); if (docAccessible) { nsRefPtr docAcc = nsAccUtils::QueryAccessibleDocument(docAccessible); docAcc->RemoveAccessNodeFromCache(accNode); } mAccessNodeCache.Remove(key); } } } void nsXULTreeAccessible::TreeViewInvalidated(PRInt32 aStartRow, PRInt32 aEndRow, PRInt32 aStartCol, PRInt32 aEndCol) { if (IsDefunct()) return; PRInt32 endRow = aEndRow; nsresult rv; if (endRow == -1) { PRInt32 rowCount = 0; rv = mTreeView->GetRowCount(&rowCount); if (NS_FAILED(rv)) return; endRow = rowCount - 1; } nsCOMPtr treeColumns; mTree->GetColumns(getter_AddRefs(treeColumns)); if (!treeColumns) return; PRInt32 endCol = aEndCol; if (endCol == -1) { PRInt32 colCount = 0; rv = treeColumns->GetCount(&colCount); if (NS_FAILED(rv)) return; endCol = colCount - 1; } for (PRInt32 rowIdx = aStartRow; rowIdx <= endRow; ++rowIdx) { void *key = reinterpret_cast(rowIdx); nsCOMPtr accessNode; GetCacheEntry(mAccessNodeCache, key, getter_AddRefs(accessNode)); if (accessNode) { nsRefPtr treeitemAcc = nsAccUtils::QueryObject(accessNode); NS_ASSERTION(treeitemAcc, "Wrong accessible at the given key!"); treeitemAcc->RowInvalidated(aStartCol, endCol); } } } void nsXULTreeAccessible::TreeViewChanged() { if (IsDefunct()) return; // Fire only notification destroy/create events on accessible tree to lie to // AT because it should be expensive to fire destroy events for each tree item // in cache. nsRefPtr eventDestroy = new nsAccEvent(nsIAccessibleEvent::EVENT_HIDE, this, PR_FALSE); if (!eventDestroy) return; FirePlatformEvent(eventDestroy); ClearCache(mAccessNodeCache); mTree->GetView(getter_AddRefs(mTreeView)); nsRefPtr eventCreate = new nsAccEvent(nsIAccessibleEvent::EVENT_SHOW, this, PR_FALSE); if (!eventCreate) return; FirePlatformEvent(eventCreate); } //////////////////////////////////////////////////////////////////////////////// // nsXULTreeAccessible: protected implementation void nsXULTreeAccessible::CreateTreeItemAccessible(PRInt32 aRow, nsAccessNode** aAccessNode) { *aAccessNode = new nsXULTreeItemAccessible(mDOMNode, mWeakShell, this, mTree, mTreeView, aRow); NS_IF_ADDREF(*aAccessNode); } //////////////////////////////////////////////////////////////////////////////// // nsXULTreeItemAccessibleBase //////////////////////////////////////////////////////////////////////////////// nsXULTreeItemAccessibleBase:: nsXULTreeItemAccessibleBase(nsIDOMNode *aDOMNode, nsIWeakReference *aShell, nsAccessible *aParent, nsITreeBoxObject *aTree, nsITreeView *aTreeView, PRInt32 aRow) : mTree(aTree), mTreeView(aTreeView), mRow(aRow), nsAccessibleWrap(aDOMNode, aShell) { mParent = aParent; } //////////////////////////////////////////////////////////////////////////////// // nsXULTreeItemAccessibleBase: nsISupports implementation NS_IMPL_ISUPPORTS_INHERITED1(nsXULTreeItemAccessibleBase, nsAccessible, nsXULTreeItemAccessibleBase) //////////////////////////////////////////////////////////////////////////////// // nsXULTreeItemAccessibleBase: nsIAccessNode implementation NS_IMETHODIMP nsXULTreeItemAccessibleBase::GetUniqueID(void **aUniqueID) { // Since mDOMNode is same for all tree items and tree itself, use |this| // pointer as the unique ID. *aUniqueID = static_cast(this); return NS_OK; } //////////////////////////////////////////////////////////////////////////////// // nsXULTreeItemAccessibleBase: nsIAccessible implementation NS_IMETHODIMP nsXULTreeItemAccessibleBase::GetFocusedChild(nsIAccessible **aFocusedChild) { NS_ENSURE_ARG_POINTER(aFocusedChild); *aFocusedChild = nsnull; if (IsDefunct()) return NS_ERROR_FAILURE; if (gLastFocusedNode != mDOMNode) return NS_OK; nsCOMPtr multiSelect = do_QueryInterface(mDOMNode); if (multiSelect) { PRInt32 row = -1; multiSelect->GetCurrentIndex(&row); if (row == mRow) NS_ADDREF(*aFocusedChild = this); } return NS_OK; } NS_IMETHODIMP nsXULTreeItemAccessibleBase::GetBounds(PRInt32 *aX, PRInt32 *aY, PRInt32 *aWidth, PRInt32 *aHeight) { NS_ENSURE_ARG_POINTER(aX); *aX = 0; NS_ENSURE_ARG_POINTER(aY); *aY = 0; NS_ENSURE_ARG_POINTER(aWidth); *aWidth = 0; NS_ENSURE_ARG_POINTER(aHeight); *aHeight = 0; if (IsDefunct()) return NS_ERROR_FAILURE; // Get x coordinate and width from treechildren element, get y coordinate and // height from tree cell. nsCOMPtr boxObj = nsCoreUtils::GetTreeBodyBoxObject(mTree); NS_ENSURE_STATE(boxObj); nsCOMPtr column = nsCoreUtils::GetFirstSensibleColumn(mTree); PRInt32 x = 0, y = 0, width = 0, height = 0; nsresult rv = mTree->GetCoordsForCellItem(mRow, column, EmptyCString(), &x, &y, &width, &height); NS_ENSURE_SUCCESS(rv, rv); boxObj->GetWidth(&width); PRInt32 tcX = 0, tcY = 0; boxObj->GetScreenX(&tcX); boxObj->GetScreenY(&tcY); x = tcX; y += tcY; nsPresContext *presContext = GetPresContext(); *aX = presContext->CSSPixelsToDevPixels(x); *aY = presContext->CSSPixelsToDevPixels(y); *aWidth = presContext->CSSPixelsToDevPixels(width); *aHeight = presContext->CSSPixelsToDevPixels(height); return NS_OK; } NS_IMETHODIMP nsXULTreeItemAccessibleBase::SetSelected(PRBool aSelect) { if (IsDefunct()) return NS_ERROR_FAILURE; nsCOMPtr selection; mTreeView->GetSelection(getter_AddRefs(selection)); if (selection) { PRBool isSelected; selection->IsSelected(mRow, &isSelected); if (isSelected != aSelect) selection->ToggleSelect(mRow); } return NS_OK; } NS_IMETHODIMP nsXULTreeItemAccessibleBase::TakeFocus() { if (IsDefunct()) return NS_ERROR_FAILURE; nsCOMPtr selection; mTreeView->GetSelection(getter_AddRefs(selection)); if (selection) selection->SetCurrentIndex(mRow); // focus event will be fired here return nsAccessible::TakeFocus(); } NS_IMETHODIMP nsXULTreeItemAccessibleBase::GetRelationByType(PRUint32 aRelationType, nsIAccessibleRelation **aRelation) { NS_ENSURE_ARG_POINTER(aRelation); *aRelation = nsnull; if (IsDefunct()) return NS_ERROR_FAILURE; if (aRelationType == nsIAccessibleRelation::RELATION_NODE_CHILD_OF) { PRInt32 parentIndex; if (NS_SUCCEEDED(mTreeView->GetParentIndex(mRow, &parentIndex))) { if (parentIndex == -1) return nsRelUtils::AddTarget(aRelationType, aRelation, mParent); nsRefPtr treeAcc = nsAccUtils::QueryAccessibleTree(mParent); nsCOMPtr logicalParent; treeAcc->GetTreeItemAccessible(parentIndex, getter_AddRefs(logicalParent)); return nsRelUtils::AddTarget(aRelationType, aRelation, logicalParent); } return NS_OK; } return nsAccessible::GetRelationByType(aRelationType, aRelation); } NS_IMETHODIMP nsXULTreeItemAccessibleBase::GetNumActions(PRUint8 *aActionsCount) { NS_ENSURE_ARG_POINTER(aActionsCount); *aActionsCount = 0; if (IsDefunct()) return NS_ERROR_FAILURE; // "activate" action is available for all treeitems, "expand/collapse" action // is avaible for treeitem which is container. *aActionsCount = IsExpandable() ? 2 : 1; return NS_OK; } NS_IMETHODIMP nsXULTreeItemAccessibleBase::GetActionName(PRUint8 aIndex, nsAString& aName) { if (IsDefunct()) return NS_ERROR_FAILURE; if (aIndex == eAction_Click) { aName.AssignLiteral("activate"); return NS_OK; } if (aIndex == eAction_Expand && IsExpandable()) { PRBool isContainerOpen; mTreeView->IsContainerOpen(mRow, &isContainerOpen); if (isContainerOpen) aName.AssignLiteral("collapse"); else aName.AssignLiteral("expand"); return NS_OK; } return NS_ERROR_INVALID_ARG; } NS_IMETHODIMP nsXULTreeItemAccessibleBase::DoAction(PRUint8 aIndex) { if (IsDefunct()) return NS_ERROR_FAILURE; if (aIndex != eAction_Click && (aIndex != eAction_Expand || !IsExpandable())) return NS_ERROR_INVALID_ARG; DoCommand(nsnull, aIndex); return NS_OK; } //////////////////////////////////////////////////////////////////////////////// // nsXULTreeItemAccessibleBase: nsAccessNode implementation PRBool nsXULTreeItemAccessibleBase::IsDefunct() { if (nsAccessibleWrap::IsDefunct() || !mTree || !mTreeView || mRow < 0) return PR_TRUE; PRInt32 rowCount = 0; nsresult rv = mTreeView->GetRowCount(&rowCount); return NS_FAILED(rv) || mRow >= rowCount; } nsresult nsXULTreeItemAccessibleBase::Shutdown() { mTree = nsnull; mTreeView = nsnull; mRow = -1; return nsAccessibleWrap::Shutdown(); } //////////////////////////////////////////////////////////////////////////////// // nsXULTreeItemAccessibleBase: nsAccessible public methods // nsIAccessible::groupPosition nsresult nsXULTreeItemAccessibleBase::GroupPosition(PRInt32 *aGroupLevel, PRInt32 *aSimilarItemsInGroup, PRInt32 *aPositionInGroup) { NS_ENSURE_ARG_POINTER(aGroupLevel); *aGroupLevel = 0; NS_ENSURE_ARG_POINTER(aSimilarItemsInGroup); *aSimilarItemsInGroup = 0; NS_ENSURE_ARG_POINTER(aPositionInGroup); *aPositionInGroup = 0; if (IsDefunct()) return NS_ERROR_FAILURE; PRInt32 level; nsresult rv = mTreeView->GetLevel(mRow, &level); NS_ENSURE_SUCCESS(rv, rv); PRInt32 topCount = 1; for (PRInt32 index = mRow - 1; index >= 0; index--) { PRInt32 lvl = -1; if (NS_SUCCEEDED(mTreeView->GetLevel(index, &lvl))) { if (lvl < level) break; if (lvl == level) topCount++; } } PRInt32 rowCount = 0; rv = mTreeView->GetRowCount(&rowCount); NS_ENSURE_SUCCESS(rv, rv); PRInt32 bottomCount = 0; for (PRInt32 index = mRow + 1; index < rowCount; index++) { PRInt32 lvl = -1; if (NS_SUCCEEDED(mTreeView->GetLevel(index, &lvl))) { if (lvl < level) break; if (lvl == level) bottomCount++; } } PRInt32 setSize = topCount + bottomCount; PRInt32 posInSet = topCount; *aGroupLevel = level + 1; *aSimilarItemsInGroup = setSize; *aPositionInGroup = posInSet; return NS_OK; } nsresult nsXULTreeItemAccessibleBase::GetStateInternal(PRUint32 *aState, PRUint32 *aExtraState) { NS_ENSURE_ARG_POINTER(aState); *aState = 0; if (aExtraState) *aExtraState = 0; if (IsDefunct()) { if (aExtraState) *aExtraState = nsIAccessibleStates::EXT_STATE_DEFUNCT; return NS_OK_DEFUNCT_OBJECT; } // focusable and selectable states *aState = nsIAccessibleStates::STATE_FOCUSABLE | nsIAccessibleStates::STATE_SELECTABLE; // expanded/collapsed state if (IsExpandable()) { PRBool isContainerOpen; mTreeView->IsContainerOpen(mRow, &isContainerOpen); *aState |= isContainerOpen ? nsIAccessibleStates::STATE_EXPANDED: nsIAccessibleStates::STATE_COLLAPSED; } // selected state nsCOMPtr selection; mTreeView->GetSelection(getter_AddRefs(selection)); if (selection) { PRBool isSelected; selection->IsSelected(mRow, &isSelected); if (isSelected) *aState |= nsIAccessibleStates::STATE_SELECTED; } // focused state nsCOMPtr multiSelect = do_QueryInterface(mDOMNode); if (multiSelect) { PRInt32 currentIndex; multiSelect->GetCurrentIndex(¤tIndex); if (currentIndex == mRow) { *aState |= nsIAccessibleStates::STATE_FOCUSED; } } // invisible state PRInt32 firstVisibleRow, lastVisibleRow; mTree->GetFirstVisibleRow(&firstVisibleRow); mTree->GetLastVisibleRow(&lastVisibleRow); if (mRow < firstVisibleRow || mRow > lastVisibleRow) *aState |= nsIAccessibleStates::STATE_INVISIBLE; return NS_OK; } nsAccessible* nsXULTreeItemAccessibleBase::GetParent() { return IsDefunct() ? nsnull : mParent.get(); } //////////////////////////////////////////////////////////////////////////////// // nsXULTreeItemAccessibleBase: nsAccessible protected methods void nsXULTreeItemAccessibleBase::DispatchClickEvent(nsIContent *aContent, PRUint32 aActionIndex) { if (IsDefunct()) return; nsCOMPtr columns; mTree->GetColumns(getter_AddRefs(columns)); if (!columns) return; // Get column and pseudo element. nsCOMPtr column; nsCAutoString pseudoElm; if (aActionIndex == eAction_Click) { // Key column is visible and clickable. columns->GetKeyColumn(getter_AddRefs(column)); } else { // Primary column contains a twisty we should click on. columns->GetPrimaryColumn(getter_AddRefs(column)); pseudoElm = NS_LITERAL_CSTRING("twisty"); } if (column) nsCoreUtils::DispatchClickEvent(mTree, mRow, column, pseudoElm); } nsIAccessible* nsXULTreeItemAccessibleBase::GetSiblingAtOffset(PRInt32 aOffset, nsresult* aError) { if (mRow + aOffset < 0) return nsAccessible::GetSiblingAtOffset(mRow + aOffset, aError); if (IsDefunct()) { if (aError) *aError = NS_ERROR_FAILURE; return nsnull; } if (aError) *aError = NS_OK; // fail peacefully nsRefPtr treeAcc = nsAccUtils::QueryAccessibleTree(mParent); if (!treeAcc) return nsnull; PRInt32 rowCount = 0; mTreeView->GetRowCount(&rowCount); if (mRow + aOffset >= rowCount) return nsnull; nsCOMPtr sibling; treeAcc->GetTreeItemAccessible(mRow + aOffset, getter_AddRefs(sibling)); return sibling; } //////////////////////////////////////////////////////////////////////////////// // nsXULTreeItemAccessibleBase: protected implementation PRBool nsXULTreeItemAccessibleBase::IsExpandable() { PRBool isContainer = PR_FALSE; mTreeView->IsContainer(mRow, &isContainer); if (isContainer) { PRBool isEmpty = PR_FALSE; mTreeView->IsContainerEmpty(mRow, &isEmpty); if (!isEmpty) { nsCOMPtr columns; mTree->GetColumns(getter_AddRefs(columns)); nsCOMPtr primaryColumn; if (columns) { columns->GetPrimaryColumn(getter_AddRefs(primaryColumn)); if (!nsCoreUtils::IsColumnHidden(primaryColumn)) return PR_TRUE; } } } return PR_FALSE; } //////////////////////////////////////////////////////////////////////////////// // nsXULTreeItemAccessible //////////////////////////////////////////////////////////////////////////////// nsXULTreeItemAccessible:: nsXULTreeItemAccessible(nsIDOMNode *aDOMNode, nsIWeakReference *aShell, nsAccessible *aParent, nsITreeBoxObject *aTree, nsITreeView *aTreeView, PRInt32 aRow) : nsXULTreeItemAccessibleBase(aDOMNode, aShell, aParent, aTree, aTreeView, aRow) { mColumn = nsCoreUtils::GetFirstSensibleColumn(mTree); } //////////////////////////////////////////////////////////////////////////////// // nsXULTreeItemAccessible: nsIAccessible implementation NS_IMETHODIMP nsXULTreeItemAccessible::GetName(nsAString& aName) { aName.Truncate(); if (IsDefunct()) return NS_ERROR_FAILURE; mTreeView->GetCellText(mRow, mColumn, 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, mColumn, aName); return NS_OK; } //////////////////////////////////////////////////////////////////////////////// // nsXULTreeItemAccessible: nsAccessNode implementation PRBool nsXULTreeItemAccessible::IsDefunct() { return nsXULTreeItemAccessibleBase::IsDefunct() || !mColumn; } nsresult nsXULTreeItemAccessible::Init() { nsresult rv = nsXULTreeItemAccessibleBase::Init(); NS_ENSURE_SUCCESS(rv, rv); return GetName(mCachedName); } nsresult nsXULTreeItemAccessible::Shutdown() { mColumn = nsnull; return nsXULTreeItemAccessibleBase::Shutdown(); } //////////////////////////////////////////////////////////////////////////////// // nsXULTreeItemAccessible: nsAccessible implementation nsresult nsXULTreeItemAccessible::GetRoleInternal(PRUint32 *aRole) { nsCOMPtr column = nsCoreUtils::GetFirstSensibleColumn(mTree); PRBool isPrimary = PR_FALSE; column->GetPrimary(&isPrimary); *aRole = isPrimary ? nsIAccessibleRole::ROLE_OUTLINEITEM : nsIAccessibleRole::ROLE_LISTITEM; return NS_OK; } //////////////////////////////////////////////////////////////////////////////// // nsXULTreeItemAccessible: nsXULTreeItemAccessibleBase implementation void nsXULTreeItemAccessible::RowInvalidated(PRInt32 aStartColIdx, PRInt32 aEndColIdx) { nsAutoString name; GetName(name); if (name != mCachedName) { nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_NAME_CHANGE, this); mCachedName = name; } } //////////////////////////////////////////////////////////////////////////////// // nsXULTreeItemAccessible: nsAccessible protected implementation void nsXULTreeItemAccessible::CacheChildren() { } //////////////////////////////////////////////////////////////////////////////// // nsXULTreeColumnsAccessible //////////////////////////////////////////////////////////////////////////////// nsXULTreeColumnsAccessible:: nsXULTreeColumnsAccessible(nsIDOMNode* aDOMNode, nsIWeakReference* aShell): nsXULColumnsAccessible(aDOMNode, aShell) { } nsIAccessible* nsXULTreeColumnsAccessible::GetSiblingAtOffset(PRInt32 aOffset, nsresult* aError) { if (aOffset < 0) return nsXULColumnsAccessible::GetSiblingAtOffset(aOffset, aError); if (IsDefunct()) { if (aError) *aError = NS_ERROR_FAILURE; return nsnull; } if (aError) *aError = NS_OK; // fail peacefully nsCOMPtr tree; nsCOMPtr treeView; nsCoreUtils::GetTreeBoxObject(mDOMNode, getter_AddRefs(tree)); if (tree) { tree->GetView(getter_AddRefs(treeView)); if (treeView) { PRInt32 rowCount = 0; treeView->GetRowCount(&rowCount); if (rowCount > 0 && aOffset <= rowCount) { nsRefPtr treeAcc = nsAccUtils::QueryAccessibleTree(mParent); if (treeAcc) { nsCOMPtr sibling; treeAcc->GetTreeItemAccessible(aOffset - 1, getter_AddRefs(sibling)); return sibling; } } } } return nsnull; }