1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #include "XULTreeGridAccessibleWrap.h"
7 
8 #include "AccAttributes.h"
9 #include "LocalAccessible-inl.h"
10 #include "nsAccCache.h"
11 #include "nsAccessibilityService.h"
12 #include "nsAccUtils.h"
13 #include "DocAccessible.h"
14 #include "nsEventShell.h"
15 #include "Relation.h"
16 #include "Role.h"
17 #include "States.h"
18 #include "nsQueryObject.h"
19 #include "nsTreeColumns.h"
20 
21 #include "nsITreeSelection.h"
22 #include "nsComponentManagerUtils.h"
23 #include "mozilla/PresShell.h"
24 #include "mozilla/dom/Element.h"
25 #include "mozilla/dom/TreeColumnBinding.h"
26 #include "mozilla/dom/XULTreeElementBinding.h"
27 
28 using namespace mozilla::a11y;
29 using namespace mozilla;
30 
~XULTreeGridAccessible()31 XULTreeGridAccessible::~XULTreeGridAccessible() {}
32 
33 ////////////////////////////////////////////////////////////////////////////////
34 // XULTreeGridAccessible: Table
35 
ColCount() const36 uint32_t XULTreeGridAccessible::ColCount() const {
37   return nsCoreUtils::GetSensibleColumnCount(mTree);
38 }
39 
RowCount()40 uint32_t XULTreeGridAccessible::RowCount() {
41   if (!mTreeView) return 0;
42 
43   int32_t rowCount = 0;
44   mTreeView->GetRowCount(&rowCount);
45   return rowCount >= 0 ? rowCount : 0;
46 }
47 
SelectedCellCount()48 uint32_t XULTreeGridAccessible::SelectedCellCount() {
49   return SelectedRowCount() * ColCount();
50 }
51 
SelectedColCount()52 uint32_t XULTreeGridAccessible::SelectedColCount() {
53   // If all the row has been selected, then all the columns are selected,
54   // because we can't select a column alone.
55 
56   uint32_t selectedRowCount = SelectedItemCount();
57   return selectedRowCount > 0 && selectedRowCount == RowCount() ? ColCount()
58                                                                 : 0;
59 }
60 
SelectedRowCount()61 uint32_t XULTreeGridAccessible::SelectedRowCount() {
62   return SelectedItemCount();
63 }
64 
SelectedCells(nsTArray<LocalAccessible * > * aCells)65 void XULTreeGridAccessible::SelectedCells(nsTArray<LocalAccessible*>* aCells) {
66   uint32_t colCount = ColCount(), rowCount = RowCount();
67 
68   for (uint32_t rowIdx = 0; rowIdx < rowCount; rowIdx++) {
69     if (IsRowSelected(rowIdx)) {
70       for (uint32_t colIdx = 0; colIdx < colCount; colIdx++) {
71         LocalAccessible* cell = CellAt(rowIdx, colIdx);
72         aCells->AppendElement(cell);
73       }
74     }
75   }
76 }
77 
SelectedCellIndices(nsTArray<uint32_t> * aCells)78 void XULTreeGridAccessible::SelectedCellIndices(nsTArray<uint32_t>* aCells) {
79   uint32_t colCount = ColCount(), rowCount = RowCount();
80 
81   for (uint32_t rowIdx = 0; rowIdx < rowCount; rowIdx++) {
82     if (IsRowSelected(rowIdx)) {
83       for (uint32_t colIdx = 0; colIdx < colCount; colIdx++) {
84         aCells->AppendElement(rowIdx * colCount + colIdx);
85       }
86     }
87   }
88 }
89 
SelectedColIndices(nsTArray<uint32_t> * aCols)90 void XULTreeGridAccessible::SelectedColIndices(nsTArray<uint32_t>* aCols) {
91   if (RowCount() != SelectedRowCount()) return;
92 
93   uint32_t colCount = ColCount();
94   aCols->SetCapacity(colCount);
95   for (uint32_t colIdx = 0; colIdx < colCount; colIdx++) {
96     aCols->AppendElement(colIdx);
97   }
98 }
99 
SelectedRowIndices(nsTArray<uint32_t> * aRows)100 void XULTreeGridAccessible::SelectedRowIndices(nsTArray<uint32_t>* aRows) {
101   uint32_t rowCount = RowCount();
102   for (uint32_t rowIdx = 0; rowIdx < rowCount; rowIdx++) {
103     if (IsRowSelected(rowIdx)) aRows->AppendElement(rowIdx);
104   }
105 }
106 
CellAt(uint32_t aRowIndex,uint32_t aColumnIndex)107 LocalAccessible* XULTreeGridAccessible::CellAt(uint32_t aRowIndex,
108                                                uint32_t aColumnIndex) {
109   LocalAccessible* row = GetTreeItemAccessible(aRowIndex);
110   if (!row) return nullptr;
111 
112   RefPtr<nsTreeColumn> column =
113       nsCoreUtils::GetSensibleColumnAt(mTree, aColumnIndex);
114   if (!column) return nullptr;
115 
116   RefPtr<XULTreeItemAccessibleBase> rowAcc = do_QueryObject(row);
117   if (!rowAcc) return nullptr;
118 
119   return rowAcc->GetCellAccessible(column);
120 }
121 
ColDescription(uint32_t aColIdx,nsString & aDescription)122 void XULTreeGridAccessible::ColDescription(uint32_t aColIdx,
123                                            nsString& aDescription) {
124   aDescription.Truncate();
125 
126   LocalAccessible* treeColumns = LocalAccessible::LocalChildAt(0);
127   if (treeColumns) {
128     LocalAccessible* treeColumnItem = treeColumns->LocalChildAt(aColIdx);
129     if (treeColumnItem) treeColumnItem->Name(aDescription);
130   }
131 }
132 
IsColSelected(uint32_t aColIdx)133 bool XULTreeGridAccessible::IsColSelected(uint32_t aColIdx) {
134   // If all the row has been selected, then all the columns are selected.
135   // Because we can't select a column alone.
136   return SelectedItemCount() == RowCount();
137 }
138 
IsRowSelected(uint32_t aRowIdx)139 bool XULTreeGridAccessible::IsRowSelected(uint32_t aRowIdx) {
140   if (!mTreeView) return false;
141 
142   nsCOMPtr<nsITreeSelection> selection;
143   nsresult rv = mTreeView->GetSelection(getter_AddRefs(selection));
144   NS_ENSURE_SUCCESS(rv, false);
145 
146   bool isSelected = false;
147   selection->IsSelected(aRowIdx, &isSelected);
148   return isSelected;
149 }
150 
IsCellSelected(uint32_t aRowIdx,uint32_t aColIdx)151 bool XULTreeGridAccessible::IsCellSelected(uint32_t aRowIdx, uint32_t aColIdx) {
152   return IsRowSelected(aRowIdx);
153 }
154 
SelectRow(uint32_t aRowIdx)155 void XULTreeGridAccessible::SelectRow(uint32_t aRowIdx) {
156   if (!mTreeView) return;
157 
158   nsCOMPtr<nsITreeSelection> selection;
159   mTreeView->GetSelection(getter_AddRefs(selection));
160   NS_ASSERTION(selection, "GetSelection() Shouldn't fail!");
161 
162   selection->Select(aRowIdx);
163 }
164 
UnselectRow(uint32_t aRowIdx)165 void XULTreeGridAccessible::UnselectRow(uint32_t aRowIdx) {
166   if (!mTreeView) return;
167 
168   nsCOMPtr<nsITreeSelection> selection;
169   mTreeView->GetSelection(getter_AddRefs(selection));
170 
171   if (selection) selection->ClearRange(aRowIdx, aRowIdx);
172 }
173 
174 ////////////////////////////////////////////////////////////////////////////////
175 // XULTreeGridAccessible: LocalAccessible implementation
176 
NativeRole() const177 role XULTreeGridAccessible::NativeRole() const {
178   RefPtr<nsTreeColumns> treeColumns = mTree->GetColumns(FlushType::None);
179   if (!treeColumns) {
180     NS_ERROR("No treecolumns object for tree!");
181     return roles::NOTHING;
182   }
183 
184   nsTreeColumn* primaryColumn = treeColumns->GetPrimaryColumn();
185 
186   return primaryColumn ? roles::TREE_TABLE : roles::TABLE;
187 }
188 
189 ////////////////////////////////////////////////////////////////////////////////
190 // XULTreeGridAccessible: XULTreeAccessible implementation
191 
192 already_AddRefed<LocalAccessible>
CreateTreeItemAccessible(int32_t aRow) const193 XULTreeGridAccessible::CreateTreeItemAccessible(int32_t aRow) const {
194   RefPtr<LocalAccessible> accessible = new XULTreeGridRowAccessible(
195       mContent, mDoc, const_cast<XULTreeGridAccessible*>(this), mTree,
196       mTreeView, aRow);
197 
198   return accessible.forget();
199 }
200 
201 ////////////////////////////////////////////////////////////////////////////////
202 // XULTreeGridRowAccessible
203 ////////////////////////////////////////////////////////////////////////////////
204 
XULTreeGridRowAccessible(nsIContent * aContent,DocAccessible * aDoc,LocalAccessible * aTreeAcc,dom::XULTreeElement * aTree,nsITreeView * aTreeView,int32_t aRow)205 XULTreeGridRowAccessible::XULTreeGridRowAccessible(
206     nsIContent* aContent, DocAccessible* aDoc, LocalAccessible* aTreeAcc,
207     dom::XULTreeElement* aTree, nsITreeView* aTreeView, int32_t aRow)
208     : XULTreeItemAccessibleBase(aContent, aDoc, aTreeAcc, aTree, aTreeView,
209                                 aRow),
210       mAccessibleCache(kDefaultTreeCacheLength) {
211   mGenericTypes |= eTableRow;
212   mStateFlags |= eNoKidsFromDOM;
213 }
214 
~XULTreeGridRowAccessible()215 XULTreeGridRowAccessible::~XULTreeGridRowAccessible() {}
216 
217 ////////////////////////////////////////////////////////////////////////////////
218 // XULTreeGridRowAccessible: nsISupports and cycle collection implementation
219 
NS_IMPL_CYCLE_COLLECTION_INHERITED(XULTreeGridRowAccessible,XULTreeItemAccessibleBase,mAccessibleCache)220 NS_IMPL_CYCLE_COLLECTION_INHERITED(XULTreeGridRowAccessible,
221                                    XULTreeItemAccessibleBase, mAccessibleCache)
222 
223 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(XULTreeGridRowAccessible)
224 NS_INTERFACE_MAP_END_INHERITING(XULTreeItemAccessibleBase)
225 
226 NS_IMPL_ADDREF_INHERITED(XULTreeGridRowAccessible, XULTreeItemAccessibleBase)
227 NS_IMPL_RELEASE_INHERITED(XULTreeGridRowAccessible, XULTreeItemAccessibleBase)
228 
229 ////////////////////////////////////////////////////////////////////////////////
230 // XULTreeGridRowAccessible: LocalAccessible implementation
231 
232 void XULTreeGridRowAccessible::Shutdown() {
233   if (mDoc && !mDoc->IsDefunct()) {
234     UnbindCacheEntriesFromDocument(mAccessibleCache);
235   }
236 
237   XULTreeItemAccessibleBase::Shutdown();
238 }
239 
NativeRole() const240 role XULTreeGridRowAccessible::NativeRole() const { return roles::ROW; }
241 
Name(nsString & aName) const242 ENameValueFlag XULTreeGridRowAccessible::Name(nsString& aName) const {
243   aName.Truncate();
244 
245   // XXX: the row name sholdn't be a concatenation of cell names (bug 664384).
246   RefPtr<nsTreeColumn> column = nsCoreUtils::GetFirstSensibleColumn(mTree);
247   while (column) {
248     if (!aName.IsEmpty()) aName.Append(' ');
249 
250     nsAutoString cellName;
251     GetCellName(column, cellName);
252     aName.Append(cellName);
253 
254     column = nsCoreUtils::GetNextSensibleColumn(column);
255   }
256 
257   return eNameOK;
258 }
259 
LocalChildAtPoint(int32_t aX,int32_t aY,EWhichChildAtPoint aWhichChild)260 LocalAccessible* XULTreeGridRowAccessible::LocalChildAtPoint(
261     int32_t aX, int32_t aY, EWhichChildAtPoint aWhichChild) {
262   nsIFrame* frame = GetFrame();
263   if (!frame) return nullptr;
264 
265   nsPresContext* presContext = frame->PresContext();
266   PresShell* presShell = presContext->PresShell();
267 
268   nsIFrame* rootFrame = presShell->GetRootFrame();
269   NS_ENSURE_TRUE(rootFrame, nullptr);
270 
271   CSSIntRect rootRect = rootFrame->GetScreenRect();
272 
273   int32_t clientX = presContext->DevPixelsToIntCSSPixels(aX) - rootRect.X();
274   int32_t clientY = presContext->DevPixelsToIntCSSPixels(aY) - rootRect.Y();
275 
276   ErrorResult rv;
277   dom::TreeCellInfo cellInfo;
278   mTree->GetCellAt(clientX, clientY, cellInfo, rv);
279 
280   // Return if we failed to find tree cell in the row for the given point.
281   if (cellInfo.mRow != mRow || !cellInfo.mCol) return nullptr;
282 
283   return GetCellAccessible(cellInfo.mCol);
284 }
285 
LocalChildAt(uint32_t aIndex) const286 LocalAccessible* XULTreeGridRowAccessible::LocalChildAt(uint32_t aIndex) const {
287   if (IsDefunct()) return nullptr;
288 
289   RefPtr<nsTreeColumn> column = nsCoreUtils::GetSensibleColumnAt(mTree, aIndex);
290   if (!column) return nullptr;
291 
292   return GetCellAccessible(column);
293 }
294 
ChildCount() const295 uint32_t XULTreeGridRowAccessible::ChildCount() const {
296   return nsCoreUtils::GetSensibleColumnCount(mTree);
297 }
298 
299 ////////////////////////////////////////////////////////////////////////////////
300 // XULTreeGridRowAccessible: XULTreeItemAccessibleBase implementation
301 
GetCellAccessible(nsTreeColumn * aColumn) const302 XULTreeGridCellAccessible* XULTreeGridRowAccessible::GetCellAccessible(
303     nsTreeColumn* aColumn) const {
304   MOZ_ASSERT(aColumn, "No tree column!");
305 
306   void* key = static_cast<void*>(aColumn);
307   XULTreeGridCellAccessible* cachedCell = mAccessibleCache.GetWeak(key);
308   if (cachedCell) return cachedCell;
309 
310   RefPtr<XULTreeGridCellAccessible> cell = new XULTreeGridCellAccessibleWrap(
311       mContent, mDoc, const_cast<XULTreeGridRowAccessible*>(this), mTree,
312       mTreeView, mRow, aColumn);
313   mAccessibleCache.InsertOrUpdate(key, RefPtr{cell});
314   Document()->BindToDocument(cell, nullptr);
315   return cell;
316 }
317 
RowInvalidated(int32_t aStartColIdx,int32_t aEndColIdx)318 void XULTreeGridRowAccessible::RowInvalidated(int32_t aStartColIdx,
319                                               int32_t aEndColIdx) {
320   RefPtr<nsTreeColumns> treeColumns = mTree->GetColumns(FlushType::None);
321   if (!treeColumns) return;
322 
323   bool nameChanged = false;
324   for (int32_t colIdx = aStartColIdx; colIdx <= aEndColIdx; ++colIdx) {
325     nsTreeColumn* column = treeColumns->GetColumnAt(colIdx);
326     if (column && !nsCoreUtils::IsColumnHidden(column)) {
327       XULTreeGridCellAccessible* cell = GetCellAccessible(column);
328       if (cell) nameChanged |= cell->CellInvalidated();
329     }
330   }
331 
332   if (nameChanged) {
333     nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_NAME_CHANGE, this);
334   }
335 }
336 
337 ////////////////////////////////////////////////////////////////////////////////
338 // XULTreeGridCellAccessible
339 ////////////////////////////////////////////////////////////////////////////////
340 
XULTreeGridCellAccessible(nsIContent * aContent,DocAccessible * aDoc,XULTreeGridRowAccessible * aRowAcc,dom::XULTreeElement * aTree,nsITreeView * aTreeView,int32_t aRow,nsTreeColumn * aColumn)341 XULTreeGridCellAccessible::XULTreeGridCellAccessible(
342     nsIContent* aContent, DocAccessible* aDoc,
343     XULTreeGridRowAccessible* aRowAcc, dom::XULTreeElement* aTree,
344     nsITreeView* aTreeView, int32_t aRow, nsTreeColumn* aColumn)
345     : LeafAccessible(aContent, aDoc),
346       mTree(aTree),
347       mTreeView(aTreeView),
348       mRow(aRow),
349       mColumn(aColumn) {
350   mParent = aRowAcc;
351   mStateFlags |= eSharedNode;
352   mGenericTypes |= eTableCell;
353 
354   NS_ASSERTION(mTreeView, "mTreeView is null");
355 
356   if (mColumn->Type() == dom::TreeColumn_Binding::TYPE_CHECKBOX) {
357     mTreeView->GetCellValue(mRow, mColumn, mCachedTextEquiv);
358   } else {
359     mTreeView->GetCellText(mRow, mColumn, mCachedTextEquiv);
360   }
361 }
362 
~XULTreeGridCellAccessible()363 XULTreeGridCellAccessible::~XULTreeGridCellAccessible() {}
364 
365 ////////////////////////////////////////////////////////////////////////////////
366 // XULTreeGridCellAccessible: nsISupports implementation
367 
NS_IMPL_CYCLE_COLLECTION_INHERITED(XULTreeGridCellAccessible,LeafAccessible,mTree,mColumn)368 NS_IMPL_CYCLE_COLLECTION_INHERITED(XULTreeGridCellAccessible, LeafAccessible,
369                                    mTree, mColumn)
370 
371 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(XULTreeGridCellAccessible)
372 NS_INTERFACE_MAP_END_INHERITING(LeafAccessible)
373 NS_IMPL_ADDREF_INHERITED(XULTreeGridCellAccessible, LeafAccessible)
374 NS_IMPL_RELEASE_INHERITED(XULTreeGridCellAccessible, LeafAccessible)
375 
376 ////////////////////////////////////////////////////////////////////////////////
377 // XULTreeGridCellAccessible: LocalAccessible
378 
379 void XULTreeGridCellAccessible::Shutdown() {
380   mTree = nullptr;
381   mTreeView = nullptr;
382   mRow = -1;
383   mColumn = nullptr;
384   mParent = nullptr;  // null-out to prevent base class's shutdown ops
385 
386   LeafAccessible::Shutdown();
387 }
388 
FocusedChild()389 LocalAccessible* XULTreeGridCellAccessible::FocusedChild() { return nullptr; }
390 
Name(nsString & aName) const391 ENameValueFlag XULTreeGridCellAccessible::Name(nsString& aName) const {
392   aName.Truncate();
393 
394   if (!mTreeView) return eNameOK;
395 
396   mTreeView->GetCellText(mRow, mColumn, aName);
397 
398   // If there is still no name try the cell value:
399   // This is for graphical cells. We need tree/table view implementors to
400   // implement FooView::GetCellValue to return a meaningful string for cases
401   // where there is something shown in the cell (non-text) such as a star icon;
402   // in which case GetCellValue for that cell would return "starred" or
403   // "flagged" for example.
404   if (aName.IsEmpty()) mTreeView->GetCellValue(mRow, mColumn, aName);
405 
406   return eNameOK;
407 }
408 
BoundsInCSSPixels() const409 nsIntRect XULTreeGridCellAccessible::BoundsInCSSPixels() const {
410   // Get bounds for tree cell and add x and y of treechildren element to
411   // x and y of the cell.
412   nsresult rv;
413   nsIntRect rect = mTree->GetCoordsForCellItem(mRow, mColumn, u"cell"_ns, rv);
414   if (NS_FAILED(rv)) {
415     return nsIntRect();
416   }
417 
418   RefPtr<dom::Element> bodyElement = mTree->GetTreeBody();
419   if (!bodyElement || !bodyElement->IsXULElement()) {
420     return nsIntRect();
421   }
422 
423   nsIFrame* bodyFrame = bodyElement->GetPrimaryFrame();
424   if (!bodyFrame) {
425     return nsIntRect();
426   }
427 
428   CSSIntRect screenRect = bodyFrame->GetScreenRect();
429   rect.x += screenRect.x;
430   rect.y += screenRect.y;
431   return rect;
432 }
433 
BoundsInAppUnits() const434 nsRect XULTreeGridCellAccessible::BoundsInAppUnits() const {
435   nsIntRect bounds = BoundsInCSSPixels();
436   nsPresContext* presContext = mDoc->PresContext();
437   return nsRect(presContext->CSSPixelsToAppUnits(bounds.X()),
438                 presContext->CSSPixelsToAppUnits(bounds.Y()),
439                 presContext->CSSPixelsToAppUnits(bounds.Width()),
440                 presContext->CSSPixelsToAppUnits(bounds.Height()));
441 }
442 
HasPrimaryAction() const443 bool XULTreeGridCellAccessible::HasPrimaryAction() const {
444   return mColumn->Cycler() ||
445          (mColumn->Type() == dom::TreeColumn_Binding::TYPE_CHECKBOX &&
446           IsEditable());
447 }
448 
ActionNameAt(uint8_t aIndex,nsAString & aName)449 void XULTreeGridCellAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName) {
450   aName.Truncate();
451 
452   if (aIndex != eAction_Click || !mTreeView) return;
453 
454   if (mColumn->Cycler()) {
455     aName.AssignLiteral("cycle");
456     return;
457   }
458 
459   if (mColumn->Type() == dom::TreeColumn_Binding::TYPE_CHECKBOX &&
460       IsEditable()) {
461     nsAutoString value;
462     mTreeView->GetCellValue(mRow, mColumn, value);
463     if (value.EqualsLiteral("true")) {
464       aName.AssignLiteral("uncheck");
465     } else {
466       aName.AssignLiteral("check");
467     }
468   }
469 }
470 
471 ////////////////////////////////////////////////////////////////////////////////
472 // XULTreeGridCellAccessible: TableCell
473 
Table() const474 TableAccessible* XULTreeGridCellAccessible::Table() const {
475   LocalAccessible* grandParent = mParent->LocalParent();
476   if (grandParent) return grandParent->AsTable();
477 
478   return nullptr;
479 }
480 
ColIdx() const481 uint32_t XULTreeGridCellAccessible::ColIdx() const {
482   uint32_t colIdx = 0;
483   RefPtr<nsTreeColumn> column = mColumn;
484   while ((column = nsCoreUtils::GetPreviousSensibleColumn(column))) colIdx++;
485 
486   return colIdx;
487 }
488 
RowIdx() const489 uint32_t XULTreeGridCellAccessible::RowIdx() const { return mRow; }
490 
ColHeaderCells(nsTArray<LocalAccessible * > * aHeaderCells)491 void XULTreeGridCellAccessible::ColHeaderCells(
492     nsTArray<LocalAccessible*>* aHeaderCells) {
493   dom::Element* columnElm = mColumn->Element();
494 
495   LocalAccessible* headerCell = mDoc->GetAccessible(columnElm);
496   if (headerCell) aHeaderCells->AppendElement(headerCell);
497 }
498 
Selected()499 bool XULTreeGridCellAccessible::Selected() {
500   nsCOMPtr<nsITreeSelection> selection;
501   nsresult rv = mTreeView->GetSelection(getter_AddRefs(selection));
502   NS_ENSURE_SUCCESS(rv, false);
503 
504   bool selected = false;
505   selection->IsSelected(mRow, &selected);
506   return selected;
507 }
508 
509 ////////////////////////////////////////////////////////////////////////////////
510 // XULTreeGridCellAccessible: LocalAccessible public implementation
511 
NativeAttributes()512 already_AddRefed<AccAttributes> XULTreeGridCellAccessible::NativeAttributes() {
513   RefPtr<AccAttributes> attributes = new AccAttributes();
514 
515   // "table-cell-index" attribute
516   TableAccessible* table = Table();
517   if (!table) return attributes.forget();
518 
519   attributes->SetAttribute(nsGkAtoms::tableCellIndex,
520                            table->CellIndexAt(mRow, ColIdx()));
521 
522   // "cycles" attribute
523   if (mColumn->Cycler()) {
524     attributes->SetAttribute(nsGkAtoms::cycles, true);
525   }
526 
527   return attributes.forget();
528 }
529 
NativeRole() const530 role XULTreeGridCellAccessible::NativeRole() const { return roles::GRID_CELL; }
531 
NativeState() const532 uint64_t XULTreeGridCellAccessible::NativeState() const {
533   if (!mTreeView) return states::DEFUNCT;
534 
535   // selectable/selected state
536   uint64_t states =
537       states::SELECTABLE;  // keep in sync with NativeInteractiveState
538 
539   nsCOMPtr<nsITreeSelection> selection;
540   mTreeView->GetSelection(getter_AddRefs(selection));
541   if (selection) {
542     bool isSelected = false;
543     selection->IsSelected(mRow, &isSelected);
544     if (isSelected) states |= states::SELECTED;
545   }
546 
547   // checked state
548   if (mColumn->Type() == dom::TreeColumn_Binding::TYPE_CHECKBOX) {
549     states |= states::CHECKABLE;
550     nsAutoString checked;
551     mTreeView->GetCellValue(mRow, mColumn, checked);
552     if (checked.EqualsIgnoreCase("true")) states |= states::CHECKED;
553   }
554 
555   return states;
556 }
557 
NativeInteractiveState() const558 uint64_t XULTreeGridCellAccessible::NativeInteractiveState() const {
559   return states::SELECTABLE;
560 }
561 
IndexInParent() const562 int32_t XULTreeGridCellAccessible::IndexInParent() const { return ColIdx(); }
563 
RelationByType(RelationType aType) const564 Relation XULTreeGridCellAccessible::RelationByType(RelationType aType) const {
565   return Relation();
566 }
567 
568 ////////////////////////////////////////////////////////////////////////////////
569 // XULTreeGridCellAccessible: public implementation
570 
CellInvalidated()571 bool XULTreeGridCellAccessible::CellInvalidated() {
572   nsAutoString textEquiv;
573 
574   if (mColumn->Type() == dom::TreeColumn_Binding::TYPE_CHECKBOX) {
575     mTreeView->GetCellValue(mRow, mColumn, textEquiv);
576     if (mCachedTextEquiv != textEquiv) {
577       bool isEnabled = textEquiv.EqualsLiteral("true");
578       RefPtr<AccEvent> accEvent =
579           new AccStateChangeEvent(this, states::CHECKED, isEnabled);
580       nsEventShell::FireEvent(accEvent);
581 
582       mCachedTextEquiv = textEquiv;
583       return true;
584     }
585 
586     return false;
587   }
588 
589   mTreeView->GetCellText(mRow, mColumn, textEquiv);
590   if (mCachedTextEquiv != textEquiv) {
591     nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_NAME_CHANGE, this);
592     mCachedTextEquiv = textEquiv;
593     return true;
594   }
595 
596   return false;
597 }
598 
599 ////////////////////////////////////////////////////////////////////////////////
600 // XULTreeGridCellAccessible: LocalAccessible protected implementation
601 
GetSiblingAtOffset(int32_t aOffset,nsresult * aError) const602 LocalAccessible* XULTreeGridCellAccessible::GetSiblingAtOffset(
603     int32_t aOffset, nsresult* aError) const {
604   if (aError) *aError = NS_OK;  // fail peacefully
605 
606   RefPtr<nsTreeColumn> columnAtOffset(mColumn), column;
607   if (aOffset < 0) {
608     for (int32_t index = aOffset; index < 0 && columnAtOffset; index++) {
609       column = nsCoreUtils::GetPreviousSensibleColumn(columnAtOffset);
610       column.swap(columnAtOffset);
611     }
612   } else {
613     for (int32_t index = aOffset; index > 0 && columnAtOffset; index--) {
614       column = nsCoreUtils::GetNextSensibleColumn(columnAtOffset);
615       column.swap(columnAtOffset);
616     }
617   }
618 
619   if (!columnAtOffset) return nullptr;
620 
621   RefPtr<XULTreeItemAccessibleBase> rowAcc = do_QueryObject(LocalParent());
622   return rowAcc->GetCellAccessible(columnAtOffset);
623 }
624 
DispatchClickEvent(nsIContent * aContent,uint32_t aActionIndex) const625 void XULTreeGridCellAccessible::DispatchClickEvent(
626     nsIContent* aContent, uint32_t aActionIndex) const {
627   if (IsDefunct()) return;
628 
629   RefPtr<dom::XULTreeElement> tree = mTree;
630   RefPtr<nsTreeColumn> column = mColumn;
631   nsCoreUtils::DispatchClickEvent(tree, mRow, column);
632 }
633 
634 ////////////////////////////////////////////////////////////////////////////////
635 // XULTreeGridCellAccessible: protected implementation
636 
IsEditable() const637 bool XULTreeGridCellAccessible::IsEditable() const {
638   // XXX: logic corresponds to tree.xml, it's preferable to have interface
639   // method to check it.
640   bool isEditable = false;
641   nsresult rv = mTreeView->IsEditable(mRow, mColumn, &isEditable);
642   if (NS_FAILED(rv) || !isEditable) return false;
643 
644   dom::Element* columnElm = mColumn->Element();
645 
646   if (!columnElm->AttrValueIs(kNameSpaceID_None, nsGkAtoms::editable,
647                               nsGkAtoms::_true, eCaseMatters)) {
648     return false;
649   }
650 
651   return mContent->AsElement()->AttrValueIs(
652       kNameSpaceID_None, nsGkAtoms::editable, nsGkAtoms::_true, eCaseMatters);
653 }
654