michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set ts=2 et sw=2 tw=80: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "XULTreeAccessible.h" michael@0: michael@0: #include "Accessible-inl.h" michael@0: #include "DocAccessible-inl.h" michael@0: #include "nsAccCache.h" michael@0: #include "nsAccUtils.h" michael@0: #include "nsCoreUtils.h" michael@0: #include "nsEventShell.h" michael@0: #include "DocAccessible.h" michael@0: #include "Relation.h" michael@0: #include "Role.h" michael@0: #include "States.h" michael@0: michael@0: #include "nsComponentManagerUtils.h" michael@0: #include "nsIAccessibleRelation.h" michael@0: #include "nsIAutoCompleteInput.h" michael@0: #include "nsIAutoCompletePopup.h" michael@0: #include "nsIBoxObject.h" michael@0: #include "nsIDOMXULElement.h" michael@0: #include "nsIDOMXULMenuListElement.h" michael@0: #include "nsIDOMXULMultSelectCntrlEl.h" michael@0: #include "nsIDOMXULTreeElement.h" michael@0: #include "nsITreeSelection.h" michael@0: #include "nsIMutableArray.h" michael@0: #include "nsTreeBodyFrame.h" michael@0: #include "nsTreeColumns.h" michael@0: #include "nsTreeUtils.h" michael@0: michael@0: using namespace mozilla::a11y; michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // XULTreeAccessible michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: XULTreeAccessible:: michael@0: XULTreeAccessible(nsIContent* aContent, DocAccessible* aDoc, michael@0: nsTreeBodyFrame* aTreeFrame) : michael@0: AccessibleWrap(aContent, aDoc), michael@0: mAccessibleCache(kDefaultTreeCacheSize) michael@0: { michael@0: mType = eXULTreeType; michael@0: mGenericTypes |= eSelect; michael@0: michael@0: nsCOMPtr view = aTreeFrame->GetExistingView(); michael@0: mTreeView = view; michael@0: michael@0: mTree = nsCoreUtils::GetTreeBoxObject(aContent); michael@0: NS_ASSERTION(mTree, "Can't get mTree!\n"); michael@0: michael@0: nsIContent* parentContent = mContent->GetParent(); michael@0: if (parentContent) { michael@0: nsCOMPtr autoCompletePopupElm = michael@0: do_QueryInterface(parentContent); michael@0: if (autoCompletePopupElm) michael@0: mGenericTypes |= eAutoCompletePopup; michael@0: } michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // XULTreeAccessible: nsISupports and cycle collection implementation michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_INHERITED(XULTreeAccessible, Accessible, michael@0: mTree, mAccessibleCache) michael@0: michael@0: NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(XULTreeAccessible) michael@0: NS_INTERFACE_MAP_END_INHERITING(Accessible) michael@0: michael@0: NS_IMPL_ADDREF_INHERITED(XULTreeAccessible, Accessible) michael@0: NS_IMPL_RELEASE_INHERITED(XULTreeAccessible, Accessible) michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // XULTreeAccessible: Accessible implementation michael@0: michael@0: uint64_t michael@0: XULTreeAccessible::NativeState() michael@0: { michael@0: // Get focus status from base class. michael@0: uint64_t state = Accessible::NativeState(); michael@0: michael@0: // readonly state michael@0: state |= states::READONLY; michael@0: michael@0: // multiselectable state. michael@0: if (!mTreeView) michael@0: return state; michael@0: michael@0: nsCOMPtr selection; michael@0: mTreeView->GetSelection(getter_AddRefs(selection)); michael@0: NS_ENSURE_TRUE(selection, state); michael@0: michael@0: bool isSingle = false; michael@0: nsresult rv = selection->GetSingle(&isSingle); michael@0: NS_ENSURE_SUCCESS(rv, state); michael@0: michael@0: if (!isSingle) michael@0: state |= states::MULTISELECTABLE; michael@0: michael@0: return state; michael@0: } michael@0: michael@0: void michael@0: XULTreeAccessible::Value(nsString& aValue) michael@0: { michael@0: aValue.Truncate(); michael@0: if (!mTreeView) michael@0: return; michael@0: michael@0: // Return the value is the first selected child. michael@0: nsCOMPtr selection; michael@0: mTreeView->GetSelection(getter_AddRefs(selection)); michael@0: if (!selection) michael@0: return; michael@0: michael@0: int32_t currentIndex; michael@0: nsCOMPtr selectItem; michael@0: selection->GetCurrentIndex(¤tIndex); michael@0: if (currentIndex >= 0) { michael@0: nsCOMPtr keyCol; michael@0: michael@0: nsCOMPtr cols; michael@0: mTree->GetColumns(getter_AddRefs(cols)); michael@0: if (cols) michael@0: cols->GetKeyColumn(getter_AddRefs(keyCol)); michael@0: michael@0: mTreeView->GetCellText(currentIndex, keyCol, aValue); michael@0: } michael@0: michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // XULTreeAccessible: Accessible implementation michael@0: michael@0: void michael@0: XULTreeAccessible::Shutdown() michael@0: { michael@0: // XXX: we don't remove accessible from document cache if shutdown wasn't michael@0: // initiated by document destroying. Note, we can't remove accessible from michael@0: // document cache here while document is going to be shutdown. Note, this is michael@0: // not unique place where we have similar problem. michael@0: ClearCache(mAccessibleCache); michael@0: michael@0: mTree = nullptr; michael@0: mTreeView = nullptr; michael@0: michael@0: AccessibleWrap::Shutdown(); michael@0: } michael@0: michael@0: role michael@0: XULTreeAccessible::NativeRole() michael@0: { michael@0: // No primary column means we're in a list. In fact, history and mail turn off michael@0: // the primary flag when switching to a flat view. michael@0: michael@0: nsIContent* child = nsTreeUtils::GetDescendantChild(mContent, nsGkAtoms::treechildren); michael@0: NS_ASSERTION(child, "tree without treechildren!"); michael@0: nsTreeBodyFrame* treeFrame = do_QueryFrame(child->GetPrimaryFrame()); michael@0: NS_ASSERTION(treeFrame, "xul tree accessible for tree without a frame!"); michael@0: if (!treeFrame) michael@0: return roles::LIST; michael@0: michael@0: nsRefPtr cols = treeFrame->Columns(); michael@0: nsCOMPtr primaryCol; michael@0: cols->GetPrimaryColumn(getter_AddRefs(primaryCol)); michael@0: michael@0: return primaryCol ? roles::OUTLINE : roles::LIST; michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // XULTreeAccessible: Accessible implementation (DON'T put methods here) michael@0: michael@0: Accessible* michael@0: XULTreeAccessible::ChildAtPoint(int32_t aX, int32_t aY, michael@0: EWhichChildAtPoint aWhichChild) michael@0: { michael@0: nsIFrame *frame = GetFrame(); michael@0: if (!frame) michael@0: return nullptr; michael@0: michael@0: nsPresContext *presContext = frame->PresContext(); michael@0: nsIPresShell* presShell = presContext->PresShell(); michael@0: michael@0: nsIFrame *rootFrame = presShell->GetRootFrame(); michael@0: NS_ENSURE_TRUE(rootFrame, nullptr); michael@0: michael@0: nsIntRect rootRect = rootFrame->GetScreenRect(); michael@0: michael@0: int32_t clientX = presContext->DevPixelsToIntCSSPixels(aX) - rootRect.x; michael@0: int32_t clientY = presContext->DevPixelsToIntCSSPixels(aY) - rootRect.y; michael@0: michael@0: int32_t row = -1; michael@0: nsCOMPtr column; michael@0: nsAutoCString childEltUnused; michael@0: mTree->GetCellAt(clientX, clientY, &row, getter_AddRefs(column), michael@0: childEltUnused); michael@0: michael@0: // If we failed to find tree cell for the given point then it might be michael@0: // tree columns. michael@0: if (row == -1 || !column) michael@0: return AccessibleWrap::ChildAtPoint(aX, aY, aWhichChild); michael@0: michael@0: Accessible* child = GetTreeItemAccessible(row); michael@0: if (aWhichChild == eDeepestChild && child) { michael@0: // Look for accessible cell for the found item accessible. michael@0: nsRefPtr treeitem = do_QueryObject(child); michael@0: michael@0: Accessible* cell = treeitem->GetCellAccessible(column); michael@0: if (cell) michael@0: child = cell; michael@0: } michael@0: michael@0: return child; michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // XULTreeAccessible: SelectAccessible michael@0: michael@0: Accessible* michael@0: XULTreeAccessible::CurrentItem() michael@0: { michael@0: if (!mTreeView) michael@0: return nullptr; michael@0: michael@0: nsCOMPtr selection; michael@0: mTreeView->GetSelection(getter_AddRefs(selection)); michael@0: if (selection) { michael@0: int32_t currentIndex = -1; michael@0: selection->GetCurrentIndex(¤tIndex); michael@0: if (currentIndex >= 0) michael@0: return GetTreeItemAccessible(currentIndex); michael@0: } michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: void michael@0: XULTreeAccessible::SetCurrentItem(Accessible* aItem) michael@0: { michael@0: NS_ERROR("XULTreeAccessible::SetCurrentItem not implemented"); michael@0: } michael@0: michael@0: already_AddRefed michael@0: XULTreeAccessible::SelectedItems() michael@0: { michael@0: if (!mTreeView) michael@0: return nullptr; michael@0: michael@0: nsCOMPtr selection; michael@0: mTreeView->GetSelection(getter_AddRefs(selection)); michael@0: if (!selection) michael@0: return nullptr; michael@0: michael@0: nsCOMPtr selectedItems = michael@0: do_CreateInstance(NS_ARRAY_CONTRACTID); michael@0: if (!selectedItems) michael@0: return nullptr; michael@0: michael@0: int32_t rangeCount = 0; michael@0: selection->GetRangeCount(&rangeCount); michael@0: for (int32_t rangeIdx = 0; rangeIdx < rangeCount; rangeIdx++) { michael@0: int32_t firstIdx = 0, lastIdx = -1; michael@0: selection->GetRangeAt(rangeIdx, &firstIdx, &lastIdx); michael@0: for (int32_t rowIdx = firstIdx; rowIdx <= lastIdx; rowIdx++) { michael@0: nsIAccessible* item = GetTreeItemAccessible(rowIdx); michael@0: if (item) michael@0: selectedItems->AppendElement(item, false); michael@0: } michael@0: } michael@0: michael@0: return selectedItems.forget(); michael@0: } michael@0: michael@0: uint32_t michael@0: XULTreeAccessible::SelectedItemCount() michael@0: { michael@0: if (!mTreeView) michael@0: return 0; michael@0: michael@0: nsCOMPtr selection; michael@0: mTreeView->GetSelection(getter_AddRefs(selection)); michael@0: if (selection) { michael@0: int32_t count = 0; michael@0: selection->GetCount(&count); michael@0: return count; michael@0: } michael@0: michael@0: return 0; michael@0: } michael@0: michael@0: bool michael@0: XULTreeAccessible::AddItemToSelection(uint32_t aIndex) michael@0: { michael@0: if (!mTreeView) michael@0: return false; michael@0: michael@0: nsCOMPtr selection; michael@0: mTreeView->GetSelection(getter_AddRefs(selection)); michael@0: if (selection) { michael@0: bool isSelected = false; michael@0: selection->IsSelected(aIndex, &isSelected); michael@0: if (!isSelected) michael@0: selection->ToggleSelect(aIndex); michael@0: michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: bool michael@0: XULTreeAccessible::RemoveItemFromSelection(uint32_t aIndex) michael@0: { michael@0: if (!mTreeView) michael@0: return false; michael@0: michael@0: nsCOMPtr selection; michael@0: mTreeView->GetSelection(getter_AddRefs(selection)); michael@0: if (selection) { michael@0: bool isSelected = false; michael@0: selection->IsSelected(aIndex, &isSelected); michael@0: if (isSelected) michael@0: selection->ToggleSelect(aIndex); michael@0: michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: bool michael@0: XULTreeAccessible::IsItemSelected(uint32_t aIndex) michael@0: { michael@0: if (!mTreeView) michael@0: return false; michael@0: michael@0: nsCOMPtr selection; michael@0: mTreeView->GetSelection(getter_AddRefs(selection)); michael@0: if (selection) { michael@0: bool isSelected = false; michael@0: selection->IsSelected(aIndex, &isSelected); michael@0: return isSelected; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: bool michael@0: XULTreeAccessible::UnselectAll() michael@0: { michael@0: if (!mTreeView) michael@0: return false; michael@0: michael@0: nsCOMPtr selection; michael@0: mTreeView->GetSelection(getter_AddRefs(selection)); michael@0: if (!selection) michael@0: return false; michael@0: michael@0: selection->ClearSelection(); michael@0: return true; michael@0: } michael@0: michael@0: Accessible* michael@0: XULTreeAccessible::GetSelectedItem(uint32_t aIndex) michael@0: { michael@0: if (!mTreeView) michael@0: return nullptr; michael@0: michael@0: nsCOMPtr selection; michael@0: mTreeView->GetSelection(getter_AddRefs(selection)); michael@0: if (!selection) michael@0: return nullptr; michael@0: michael@0: uint32_t selCount = 0; michael@0: int32_t rangeCount = 0; michael@0: selection->GetRangeCount(&rangeCount); michael@0: for (int32_t rangeIdx = 0; rangeIdx < rangeCount; rangeIdx++) { michael@0: int32_t firstIdx = 0, lastIdx = -1; michael@0: selection->GetRangeAt(rangeIdx, &firstIdx, &lastIdx); michael@0: for (int32_t rowIdx = firstIdx; rowIdx <= lastIdx; rowIdx++) { michael@0: if (selCount == aIndex) michael@0: return GetTreeItemAccessible(rowIdx); michael@0: michael@0: selCount++; michael@0: } michael@0: } michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: bool michael@0: XULTreeAccessible::SelectAll() michael@0: { michael@0: // see if we are multiple select if so set ourselves as such michael@0: if (!mTreeView) michael@0: return false; michael@0: michael@0: nsCOMPtr selection; michael@0: mTreeView->GetSelection(getter_AddRefs(selection)); michael@0: if (selection) { michael@0: bool single = false; michael@0: selection->GetSingle(&single); michael@0: if (!single) { michael@0: selection->SelectAll(); michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // XULTreeAccessible: Accessible implementation michael@0: michael@0: Accessible* michael@0: XULTreeAccessible::GetChildAt(uint32_t aIndex) const michael@0: { michael@0: uint32_t childCount = Accessible::ChildCount(); michael@0: if (aIndex < childCount) michael@0: return Accessible::GetChildAt(aIndex); michael@0: michael@0: return GetTreeItemAccessible(aIndex - childCount); michael@0: } michael@0: michael@0: uint32_t michael@0: XULTreeAccessible::ChildCount() const michael@0: { michael@0: // Tree's children count is row count + treecols count. michael@0: uint32_t childCount = Accessible::ChildCount(); michael@0: if (!mTreeView) michael@0: return childCount; michael@0: michael@0: int32_t rowCount = 0; michael@0: mTreeView->GetRowCount(&rowCount); michael@0: childCount += rowCount; michael@0: michael@0: return childCount; michael@0: } michael@0: michael@0: Relation michael@0: XULTreeAccessible::RelationByType(RelationType aType) michael@0: { michael@0: if (aType == RelationType::NODE_PARENT_OF) { michael@0: if (mTreeView) michael@0: return Relation(new XULTreeItemIterator(this, mTreeView, -1)); michael@0: michael@0: return Relation(); michael@0: } michael@0: michael@0: return Accessible::RelationByType(aType); michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // XULTreeAccessible: Widgets michael@0: michael@0: bool michael@0: XULTreeAccessible::IsWidget() const michael@0: { michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: XULTreeAccessible::IsActiveWidget() const michael@0: { michael@0: if (IsAutoCompletePopup()) { michael@0: nsCOMPtr autoCompletePopupElm = michael@0: do_QueryInterface(mContent->GetParent()); michael@0: michael@0: if (autoCompletePopupElm) { michael@0: bool isOpen = false; michael@0: autoCompletePopupElm->GetPopupOpen(&isOpen); michael@0: return isOpen; michael@0: } michael@0: } michael@0: return FocusMgr()->HasDOMFocus(mContent); michael@0: } michael@0: michael@0: bool michael@0: XULTreeAccessible::AreItemsOperable() const michael@0: { michael@0: if (IsAutoCompletePopup()) { michael@0: nsCOMPtr autoCompletePopupElm = michael@0: do_QueryInterface(mContent->GetParent()); michael@0: michael@0: if (autoCompletePopupElm) { michael@0: bool isOpen = false; michael@0: autoCompletePopupElm->GetPopupOpen(&isOpen); michael@0: return isOpen; michael@0: } michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: Accessible* michael@0: XULTreeAccessible::ContainerWidget() const michael@0: { michael@0: if (IsAutoCompletePopup()) { michael@0: // This works for XUL autocompletes. It doesn't work for HTML forms michael@0: // autocomplete because of potential crossprocess calls (when autocomplete michael@0: // lives in content process while popup lives in chrome process). If that's michael@0: // a problem then rethink Widgets interface. michael@0: nsCOMPtr menuListElm = michael@0: do_QueryInterface(mContent->GetParent()); michael@0: if (menuListElm) { michael@0: nsCOMPtr inputElm; michael@0: menuListElm->GetInputField(getter_AddRefs(inputElm)); michael@0: if (inputElm) { michael@0: nsCOMPtr inputNode = do_QueryInterface(inputElm); michael@0: if (inputNode) { michael@0: Accessible* input = michael@0: mDoc->GetAccessible(inputNode); michael@0: return input ? input->ContainerWidget() : nullptr; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: return nullptr; michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // XULTreeAccessible: public implementation michael@0: michael@0: Accessible* michael@0: XULTreeAccessible::GetTreeItemAccessible(int32_t aRow) const michael@0: { michael@0: if (aRow < 0 || IsDefunct() || !mTreeView) michael@0: return nullptr; michael@0: michael@0: int32_t rowCount = 0; michael@0: nsresult rv = mTreeView->GetRowCount(&rowCount); michael@0: if (NS_FAILED(rv) || aRow >= rowCount) michael@0: return nullptr; michael@0: michael@0: void *key = reinterpret_cast(aRow); michael@0: Accessible* cachedTreeItem = mAccessibleCache.GetWeak(key); michael@0: if (cachedTreeItem) michael@0: return cachedTreeItem; michael@0: michael@0: nsRefPtr treeItem = CreateTreeItemAccessible(aRow); michael@0: if (treeItem) { michael@0: mAccessibleCache.Put(key, treeItem); michael@0: Document()->BindToDocument(treeItem, nullptr); michael@0: return treeItem; michael@0: } michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: void michael@0: XULTreeAccessible::InvalidateCache(int32_t aRow, int32_t aCount) michael@0: { michael@0: if (IsDefunct()) michael@0: return; michael@0: michael@0: if (!mTreeView) { michael@0: ClearCache(mAccessibleCache); michael@0: return; michael@0: } michael@0: michael@0: // Do not invalidate the cache if rows have been inserted. michael@0: if (aCount > 0) michael@0: return; michael@0: michael@0: DocAccessible* document = Document(); michael@0: michael@0: // Fire destroy event for removed tree items and delete them from caches. michael@0: for (int32_t rowIdx = aRow; rowIdx < aRow - aCount; rowIdx++) { michael@0: michael@0: void* key = reinterpret_cast(rowIdx); michael@0: Accessible* treeItem = mAccessibleCache.GetWeak(key); michael@0: michael@0: if (treeItem) { michael@0: nsRefPtr event = michael@0: new AccEvent(nsIAccessibleEvent::EVENT_HIDE, treeItem); michael@0: nsEventShell::FireEvent(event); michael@0: michael@0: // Unbind from document, shutdown and remove from tree cache. michael@0: document->UnbindFromDocument(treeItem); michael@0: mAccessibleCache.Remove(key); michael@0: } michael@0: } michael@0: michael@0: // We dealt with removed tree items already however we may keep tree items michael@0: // having row indexes greater than row count. We should remove these dead tree michael@0: // items silently from caches. michael@0: int32_t newRowCount = 0; michael@0: nsresult rv = mTreeView->GetRowCount(&newRowCount); michael@0: if (NS_FAILED(rv)) michael@0: return; michael@0: michael@0: int32_t oldRowCount = newRowCount - aCount; michael@0: michael@0: for (int32_t rowIdx = newRowCount; rowIdx < oldRowCount; ++rowIdx) { michael@0: michael@0: void *key = reinterpret_cast(rowIdx); michael@0: Accessible* treeItem = mAccessibleCache.GetWeak(key); michael@0: michael@0: if (treeItem) { michael@0: // Unbind from document, shutdown and remove from tree cache. michael@0: document->UnbindFromDocument(treeItem); michael@0: mAccessibleCache.Remove(key); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: XULTreeAccessible::TreeViewInvalidated(int32_t aStartRow, int32_t aEndRow, michael@0: int32_t aStartCol, int32_t aEndCol) michael@0: { michael@0: if (IsDefunct()) michael@0: return; michael@0: michael@0: if (!mTreeView) { michael@0: ClearCache(mAccessibleCache); michael@0: return; michael@0: } michael@0: michael@0: int32_t endRow = aEndRow; michael@0: michael@0: nsresult rv; michael@0: if (endRow == -1) { michael@0: int32_t rowCount = 0; michael@0: rv = mTreeView->GetRowCount(&rowCount); michael@0: if (NS_FAILED(rv)) michael@0: return; michael@0: michael@0: endRow = rowCount - 1; michael@0: } michael@0: michael@0: nsCOMPtr treeColumns; michael@0: mTree->GetColumns(getter_AddRefs(treeColumns)); michael@0: if (!treeColumns) michael@0: return; michael@0: michael@0: int32_t endCol = aEndCol; michael@0: michael@0: if (endCol == -1) { michael@0: int32_t colCount = 0; michael@0: rv = treeColumns->GetCount(&colCount); michael@0: if (NS_FAILED(rv)) michael@0: return; michael@0: michael@0: endCol = colCount - 1; michael@0: } michael@0: michael@0: for (int32_t rowIdx = aStartRow; rowIdx <= endRow; ++rowIdx) { michael@0: michael@0: void *key = reinterpret_cast(rowIdx); michael@0: Accessible* accessible = mAccessibleCache.GetWeak(key); michael@0: michael@0: if (accessible) { michael@0: nsRefPtr treeitemAcc = do_QueryObject(accessible); michael@0: NS_ASSERTION(treeitemAcc, "Wrong accessible at the given key!"); michael@0: michael@0: treeitemAcc->RowInvalidated(aStartCol, endCol); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: XULTreeAccessible::TreeViewChanged(nsITreeView* aView) michael@0: { michael@0: if (IsDefunct()) michael@0: return; michael@0: michael@0: // Fire reorder event on tree accessible on accessible tree (do not fire michael@0: // show/hide events on tree items because it can be expensive to fire them for michael@0: // each tree item. michael@0: nsRefPtr reorderEvent = new AccReorderEvent(this); michael@0: Document()->FireDelayedEvent(reorderEvent); michael@0: michael@0: // Clear cache. michael@0: ClearCache(mAccessibleCache); michael@0: mTreeView = aView; michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // XULTreeAccessible: protected implementation michael@0: michael@0: already_AddRefed michael@0: XULTreeAccessible::CreateTreeItemAccessible(int32_t aRow) const michael@0: { michael@0: nsRefPtr accessible = michael@0: new XULTreeItemAccessible(mContent, mDoc, const_cast(this), michael@0: mTree, mTreeView, aRow); michael@0: michael@0: return accessible.forget(); michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // XULTreeItemAccessibleBase michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: XULTreeItemAccessibleBase:: michael@0: XULTreeItemAccessibleBase(nsIContent* aContent, DocAccessible* aDoc, michael@0: Accessible* aParent, nsITreeBoxObject* aTree, michael@0: nsITreeView* aTreeView, int32_t aRow) : michael@0: AccessibleWrap(aContent, aDoc), michael@0: mTree(aTree), mTreeView(aTreeView), mRow(aRow) michael@0: { michael@0: mParent = aParent; michael@0: mStateFlags |= eSharedNode; michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // XULTreeItemAccessibleBase: nsISupports implementation michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_INHERITED(XULTreeItemAccessibleBase, Accessible, michael@0: mTree) michael@0: michael@0: NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(XULTreeItemAccessibleBase) michael@0: NS_INTERFACE_TABLE_INHERITED(XULTreeItemAccessibleBase, michael@0: XULTreeItemAccessibleBase) michael@0: NS_INTERFACE_TABLE_TAIL_INHERITING(Accessible) michael@0: NS_IMPL_ADDREF_INHERITED(XULTreeItemAccessibleBase, Accessible) michael@0: NS_IMPL_RELEASE_INHERITED(XULTreeItemAccessibleBase, Accessible) michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // XULTreeItemAccessibleBase: nsIAccessible implementation michael@0: michael@0: Accessible* michael@0: XULTreeItemAccessibleBase::FocusedChild() michael@0: { michael@0: return FocusMgr()->FocusedAccessible() == this ? this : nullptr; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: XULTreeItemAccessibleBase::GetBounds(int32_t* aX, int32_t* aY, michael@0: int32_t* aWidth, int32_t* aHeight) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aX); michael@0: *aX = 0; michael@0: NS_ENSURE_ARG_POINTER(aY); michael@0: *aY = 0; michael@0: NS_ENSURE_ARG_POINTER(aWidth); michael@0: *aWidth = 0; michael@0: NS_ENSURE_ARG_POINTER(aHeight); michael@0: *aHeight = 0; michael@0: michael@0: if (IsDefunct()) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: // Get x coordinate and width from treechildren element, get y coordinate and michael@0: // height from tree cell. michael@0: michael@0: nsCOMPtr boxObj = nsCoreUtils::GetTreeBodyBoxObject(mTree); michael@0: NS_ENSURE_STATE(boxObj); michael@0: michael@0: nsCOMPtr column = nsCoreUtils::GetFirstSensibleColumn(mTree); michael@0: michael@0: int32_t x = 0, y = 0, width = 0, height = 0; michael@0: nsresult rv = mTree->GetCoordsForCellItem(mRow, column, EmptyCString(), michael@0: &x, &y, &width, &height); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: boxObj->GetWidth(&width); michael@0: michael@0: int32_t tcX = 0, tcY = 0; michael@0: boxObj->GetScreenX(&tcX); michael@0: boxObj->GetScreenY(&tcY); michael@0: michael@0: x = tcX; michael@0: y += tcY; michael@0: michael@0: nsPresContext* presContext = mDoc->PresContext(); michael@0: *aX = presContext->CSSPixelsToDevPixels(x); michael@0: *aY = presContext->CSSPixelsToDevPixels(y); michael@0: *aWidth = presContext->CSSPixelsToDevPixels(width); michael@0: *aHeight = presContext->CSSPixelsToDevPixels(height); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: XULTreeItemAccessibleBase::SetSelected(bool aSelect) michael@0: { michael@0: if (IsDefunct()) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: nsCOMPtr selection; michael@0: mTreeView->GetSelection(getter_AddRefs(selection)); michael@0: if (selection) { michael@0: bool isSelected; michael@0: selection->IsSelected(mRow, &isSelected); michael@0: if (isSelected != aSelect) michael@0: selection->ToggleSelect(mRow); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: XULTreeItemAccessibleBase::TakeFocus() michael@0: { michael@0: if (IsDefunct()) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: nsCOMPtr selection; michael@0: mTreeView->GetSelection(getter_AddRefs(selection)); michael@0: if (selection) michael@0: selection->SetCurrentIndex(mRow); michael@0: michael@0: // focus event will be fired here michael@0: return Accessible::TakeFocus(); michael@0: } michael@0: michael@0: Relation michael@0: XULTreeItemAccessibleBase::RelationByType(RelationType aType) michael@0: { michael@0: michael@0: switch (aType) { michael@0: case RelationType::NODE_CHILD_OF: { michael@0: int32_t parentIndex = -1; michael@0: if (!NS_SUCCEEDED(mTreeView->GetParentIndex(mRow, &parentIndex))) michael@0: return Relation(); michael@0: michael@0: if (parentIndex == -1) michael@0: return Relation(mParent); michael@0: michael@0: XULTreeAccessible* treeAcc = mParent->AsXULTree(); michael@0: return Relation(treeAcc->GetTreeItemAccessible(parentIndex)); michael@0: } michael@0: michael@0: case RelationType::NODE_PARENT_OF: { michael@0: bool isTrue = false; michael@0: if (NS_FAILED(mTreeView->IsContainerEmpty(mRow, &isTrue)) || isTrue) michael@0: return Relation(); michael@0: michael@0: if (NS_FAILED(mTreeView->IsContainerOpen(mRow, &isTrue)) || !isTrue) michael@0: return Relation(); michael@0: michael@0: XULTreeAccessible* tree = mParent->AsXULTree(); michael@0: return Relation(new XULTreeItemIterator(tree, mTreeView, mRow)); michael@0: } michael@0: michael@0: default: michael@0: return Relation(); michael@0: } michael@0: } michael@0: michael@0: uint8_t michael@0: XULTreeItemAccessibleBase::ActionCount() michael@0: { michael@0: // "activate" action is available for all treeitems, "expand/collapse" action michael@0: // is avaible for treeitem which is container. michael@0: return IsExpandable() ? 2 : 1; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: XULTreeItemAccessibleBase::GetActionName(uint8_t aIndex, nsAString& aName) michael@0: { michael@0: if (IsDefunct()) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: if (aIndex == eAction_Click) { michael@0: aName.AssignLiteral("activate"); michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (aIndex == eAction_Expand && IsExpandable()) { michael@0: bool isContainerOpen; michael@0: mTreeView->IsContainerOpen(mRow, &isContainerOpen); michael@0: if (isContainerOpen) michael@0: aName.AssignLiteral("collapse"); michael@0: else michael@0: aName.AssignLiteral("expand"); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: XULTreeItemAccessibleBase::DoAction(uint8_t aIndex) michael@0: { michael@0: if (IsDefunct()) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: if (aIndex != eAction_Click && michael@0: (aIndex != eAction_Expand || !IsExpandable())) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: DoCommand(nullptr, aIndex); michael@0: return NS_OK; michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // XULTreeItemAccessibleBase: Accessible implementation michael@0: michael@0: void michael@0: XULTreeItemAccessibleBase::Shutdown() michael@0: { michael@0: mTree = nullptr; michael@0: mTreeView = nullptr; michael@0: mRow = -1; michael@0: michael@0: AccessibleWrap::Shutdown(); michael@0: } michael@0: michael@0: GroupPos michael@0: XULTreeItemAccessibleBase::GroupPosition() michael@0: { michael@0: GroupPos groupPos; michael@0: michael@0: int32_t level; michael@0: nsresult rv = mTreeView->GetLevel(mRow, &level); michael@0: NS_ENSURE_SUCCESS(rv, groupPos); michael@0: michael@0: int32_t topCount = 1; michael@0: for (int32_t index = mRow - 1; index >= 0; index--) { michael@0: int32_t lvl = -1; michael@0: if (NS_SUCCEEDED(mTreeView->GetLevel(index, &lvl))) { michael@0: if (lvl < level) michael@0: break; michael@0: michael@0: if (lvl == level) michael@0: topCount++; michael@0: } michael@0: } michael@0: michael@0: int32_t rowCount = 0; michael@0: rv = mTreeView->GetRowCount(&rowCount); michael@0: NS_ENSURE_SUCCESS(rv, groupPos); michael@0: michael@0: int32_t bottomCount = 0; michael@0: for (int32_t index = mRow + 1; index < rowCount; index++) { michael@0: int32_t lvl = -1; michael@0: if (NS_SUCCEEDED(mTreeView->GetLevel(index, &lvl))) { michael@0: if (lvl < level) michael@0: break; michael@0: michael@0: if (lvl == level) michael@0: bottomCount++; michael@0: } michael@0: } michael@0: michael@0: groupPos.level = level + 1; michael@0: groupPos.setSize = topCount + bottomCount; michael@0: groupPos.posInSet = topCount; michael@0: michael@0: return groupPos; michael@0: } michael@0: michael@0: uint64_t michael@0: XULTreeItemAccessibleBase::NativeState() michael@0: { michael@0: michael@0: // focusable and selectable states michael@0: uint64_t state = NativeInteractiveState(); michael@0: michael@0: // expanded/collapsed state michael@0: if (IsExpandable()) { michael@0: bool isContainerOpen; michael@0: mTreeView->IsContainerOpen(mRow, &isContainerOpen); michael@0: state |= isContainerOpen ? states::EXPANDED : states::COLLAPSED; michael@0: } michael@0: michael@0: // selected state michael@0: nsCOMPtr selection; michael@0: mTreeView->GetSelection(getter_AddRefs(selection)); michael@0: if (selection) { michael@0: bool isSelected; michael@0: selection->IsSelected(mRow, &isSelected); michael@0: if (isSelected) michael@0: state |= states::SELECTED; michael@0: } michael@0: michael@0: // focused state michael@0: if (FocusMgr()->IsFocused(this)) michael@0: state |= states::FOCUSED; michael@0: michael@0: // invisible state michael@0: int32_t firstVisibleRow, lastVisibleRow; michael@0: mTree->GetFirstVisibleRow(&firstVisibleRow); michael@0: mTree->GetLastVisibleRow(&lastVisibleRow); michael@0: if (mRow < firstVisibleRow || mRow > lastVisibleRow) michael@0: state |= states::INVISIBLE; michael@0: michael@0: return state; michael@0: } michael@0: michael@0: uint64_t michael@0: XULTreeItemAccessibleBase::NativeInteractiveState() const michael@0: { michael@0: return states::FOCUSABLE | states::SELECTABLE; michael@0: } michael@0: michael@0: int32_t michael@0: XULTreeItemAccessibleBase::IndexInParent() const michael@0: { michael@0: return mParent ? mParent->ContentChildCount() + mRow : -1; michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // XULTreeItemAccessibleBase: Widgets michael@0: michael@0: Accessible* michael@0: XULTreeItemAccessibleBase::ContainerWidget() const michael@0: { michael@0: return mParent; michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // XULTreeItemAccessibleBase: Accessible protected methods michael@0: michael@0: void michael@0: XULTreeItemAccessibleBase::DispatchClickEvent(nsIContent* aContent, michael@0: uint32_t aActionIndex) michael@0: { michael@0: if (IsDefunct()) michael@0: return; michael@0: michael@0: nsCOMPtr columns; michael@0: mTree->GetColumns(getter_AddRefs(columns)); michael@0: if (!columns) michael@0: return; michael@0: michael@0: // Get column and pseudo element. michael@0: nsCOMPtr column; michael@0: nsAutoCString pseudoElm; michael@0: michael@0: if (aActionIndex == eAction_Click) { michael@0: // Key column is visible and clickable. michael@0: columns->GetKeyColumn(getter_AddRefs(column)); michael@0: } else { michael@0: // Primary column contains a twisty we should click on. michael@0: columns->GetPrimaryColumn(getter_AddRefs(column)); michael@0: pseudoElm = NS_LITERAL_CSTRING("twisty"); michael@0: } michael@0: michael@0: if (column) michael@0: nsCoreUtils::DispatchClickEvent(mTree, mRow, column, pseudoElm); michael@0: } michael@0: michael@0: Accessible* michael@0: XULTreeItemAccessibleBase::GetSiblingAtOffset(int32_t aOffset, michael@0: nsresult* aError) const michael@0: { michael@0: if (aError) michael@0: *aError = NS_OK; // fail peacefully michael@0: michael@0: return mParent->GetChildAt(IndexInParent() + aOffset); michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // XULTreeItemAccessibleBase: protected implementation michael@0: michael@0: bool michael@0: XULTreeItemAccessibleBase::IsExpandable() michael@0: { michael@0: michael@0: bool isContainer = false; michael@0: mTreeView->IsContainer(mRow, &isContainer); michael@0: if (isContainer) { michael@0: bool isEmpty = false; michael@0: mTreeView->IsContainerEmpty(mRow, &isEmpty); michael@0: if (!isEmpty) { michael@0: nsCOMPtr columns; michael@0: mTree->GetColumns(getter_AddRefs(columns)); michael@0: nsCOMPtr primaryColumn; michael@0: if (columns) { michael@0: columns->GetPrimaryColumn(getter_AddRefs(primaryColumn)); michael@0: if (primaryColumn && michael@0: !nsCoreUtils::IsColumnHidden(primaryColumn)) michael@0: return true; michael@0: } michael@0: } michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: void michael@0: XULTreeItemAccessibleBase::GetCellName(nsITreeColumn* aColumn, nsAString& aName) michael@0: { michael@0: michael@0: mTreeView->GetCellText(mRow, aColumn, aName); michael@0: michael@0: // If there is still no name try the cell value: michael@0: // This is for graphical cells. We need tree/table view implementors to michael@0: // implement FooView::GetCellValue to return a meaningful string for cases michael@0: // where there is something shown in the cell (non-text) such as a star icon; michael@0: // in which case GetCellValue for that cell would return "starred" or michael@0: // "flagged" for example. michael@0: if (aName.IsEmpty()) michael@0: mTreeView->GetCellValue(mRow, aColumn, aName); michael@0: } michael@0: michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // XULTreeItemAccessible michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: XULTreeItemAccessible:: michael@0: XULTreeItemAccessible(nsIContent* aContent, DocAccessible* aDoc, michael@0: Accessible* aParent, nsITreeBoxObject* aTree, michael@0: nsITreeView* aTreeView, int32_t aRow) : michael@0: XULTreeItemAccessibleBase(aContent, aDoc, aParent, aTree, aTreeView, aRow) michael@0: { michael@0: mColumn = nsCoreUtils::GetFirstSensibleColumn(mTree); michael@0: GetCellName(mColumn, mCachedName); michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // XULTreeItemAccessible: nsISupports implementation michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_INHERITED(XULTreeItemAccessible, michael@0: XULTreeItemAccessibleBase, michael@0: mColumn) michael@0: michael@0: NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(XULTreeItemAccessible) michael@0: NS_INTERFACE_MAP_END_INHERITING(XULTreeItemAccessibleBase) michael@0: NS_IMPL_ADDREF_INHERITED(XULTreeItemAccessible, XULTreeItemAccessibleBase) michael@0: NS_IMPL_RELEASE_INHERITED(XULTreeItemAccessible, XULTreeItemAccessibleBase) michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // XULTreeItemAccessible: nsIAccessible implementation michael@0: michael@0: ENameValueFlag michael@0: XULTreeItemAccessible::Name(nsString& aName) michael@0: { michael@0: aName.Truncate(); michael@0: michael@0: GetCellName(mColumn, aName); michael@0: return eNameOK; michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // XULTreeItemAccessible: Accessible implementation michael@0: michael@0: void michael@0: XULTreeItemAccessible::Shutdown() michael@0: { michael@0: mColumn = nullptr; michael@0: XULTreeItemAccessibleBase::Shutdown(); michael@0: } michael@0: michael@0: role michael@0: XULTreeItemAccessible::NativeRole() michael@0: { michael@0: nsCOMPtr columns; michael@0: mTree->GetColumns(getter_AddRefs(columns)); michael@0: if (!columns) { michael@0: NS_ERROR("No tree columns object in the tree!"); michael@0: return roles::NOTHING; michael@0: } michael@0: michael@0: nsCOMPtr primaryColumn; michael@0: columns->GetPrimaryColumn(getter_AddRefs(primaryColumn)); michael@0: michael@0: return primaryColumn ? roles::OUTLINEITEM : roles::LISTITEM; michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // XULTreeItemAccessible: XULTreeItemAccessibleBase implementation michael@0: michael@0: void michael@0: XULTreeItemAccessible::RowInvalidated(int32_t aStartColIdx, int32_t aEndColIdx) michael@0: { michael@0: nsAutoString name; michael@0: Name(name); michael@0: michael@0: if (name != mCachedName) { michael@0: nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_NAME_CHANGE, this); michael@0: mCachedName = name; michael@0: } michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // XULTreeItemAccessible: Accessible protected implementation michael@0: michael@0: void michael@0: XULTreeItemAccessible::CacheChildren() michael@0: { michael@0: } michael@0: michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // XULTreeColumAccessible michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: XULTreeColumAccessible:: michael@0: XULTreeColumAccessible(nsIContent* aContent, DocAccessible* aDoc) : michael@0: XULColumAccessible(aContent, aDoc) michael@0: { michael@0: } michael@0: michael@0: Accessible* michael@0: XULTreeColumAccessible::GetSiblingAtOffset(int32_t aOffset, michael@0: nsresult* aError) const michael@0: { michael@0: if (aOffset < 0) michael@0: return XULColumAccessible::GetSiblingAtOffset(aOffset, aError); michael@0: michael@0: if (aError) michael@0: *aError = NS_OK; // fail peacefully michael@0: michael@0: nsCOMPtr tree = nsCoreUtils::GetTreeBoxObject(mContent); michael@0: if (tree) { michael@0: nsCOMPtr treeView; michael@0: tree->GetView(getter_AddRefs(treeView)); michael@0: if (treeView) { michael@0: int32_t rowCount = 0; michael@0: treeView->GetRowCount(&rowCount); michael@0: if (rowCount > 0 && aOffset <= rowCount) { michael@0: XULTreeAccessible* treeAcc = Parent()->AsXULTree(); michael@0: michael@0: if (treeAcc) michael@0: return treeAcc->GetTreeItemAccessible(aOffset - 1); michael@0: } michael@0: } michael@0: } michael@0: michael@0: return nullptr; michael@0: } michael@0: