1 /* -*- Mode: C++; tab-width: 4; 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 "XULListboxAccessible.h"
7 
8 #include "Accessible-inl.h"
9 #include "nsAccessibilityService.h"
10 #include "nsAccUtils.h"
11 #include "DocAccessible.h"
12 #include "Role.h"
13 #include "States.h"
14 
15 #include "nsComponentManagerUtils.h"
16 #include "nsIAutoCompleteInput.h"
17 #include "nsIAutoCompletePopup.h"
18 #include "nsIDOMXULMenuListElement.h"
19 #include "nsIDOMXULMultSelectCntrlEl.h"
20 #include "nsIDOMNodeList.h"
21 #include "nsIDOMXULSelectCntrlItemEl.h"
22 #include "nsIMutableArray.h"
23 #include "nsIPersistentProperties2.h"
24 
25 using namespace mozilla::a11y;
26 
27 ////////////////////////////////////////////////////////////////////////////////
28 // XULColumAccessible
29 ////////////////////////////////////////////////////////////////////////////////
30 
XULColumAccessible(nsIContent * aContent,DocAccessible * aDoc)31 XULColumAccessible::XULColumAccessible(nsIContent* aContent,
32                                        DocAccessible* aDoc)
33     : AccessibleWrap(aContent, aDoc) {}
34 
NativeRole()35 role XULColumAccessible::NativeRole() { return roles::LIST; }
36 
NativeState()37 uint64_t XULColumAccessible::NativeState() { return states::READONLY; }
38 
39 ////////////////////////////////////////////////////////////////////////////////
40 // XULColumnItemAccessible
41 ////////////////////////////////////////////////////////////////////////////////
42 
XULColumnItemAccessible(nsIContent * aContent,DocAccessible * aDoc)43 XULColumnItemAccessible::XULColumnItemAccessible(nsIContent* aContent,
44                                                  DocAccessible* aDoc)
45     : LeafAccessible(aContent, aDoc) {}
46 
NativeRole()47 role XULColumnItemAccessible::NativeRole() { return roles::COLUMNHEADER; }
48 
NativeState()49 uint64_t XULColumnItemAccessible::NativeState() { return states::READONLY; }
50 
ActionCount()51 uint8_t XULColumnItemAccessible::ActionCount() { return 1; }
52 
ActionNameAt(uint8_t aIndex,nsAString & aName)53 void XULColumnItemAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName) {
54   if (aIndex == eAction_Click) aName.AssignLiteral("click");
55 }
56 
DoAction(uint8_t aIndex)57 bool XULColumnItemAccessible::DoAction(uint8_t aIndex) {
58   if (aIndex != eAction_Click) return false;
59 
60   DoCommand();
61   return true;
62 }
63 
64 ////////////////////////////////////////////////////////////////////////////////
65 // XULListboxAccessible
66 ////////////////////////////////////////////////////////////////////////////////
67 
XULListboxAccessible(nsIContent * aContent,DocAccessible * aDoc)68 XULListboxAccessible::XULListboxAccessible(nsIContent* aContent,
69                                            DocAccessible* aDoc)
70     : XULSelectControlAccessible(aContent, aDoc) {
71   nsIContent* parentContent = mContent->GetFlattenedTreeParent();
72   if (parentContent) {
73     nsCOMPtr<nsIAutoCompletePopup> autoCompletePopupElm =
74         do_QueryInterface(parentContent);
75     if (autoCompletePopupElm) mGenericTypes |= eAutoCompletePopup;
76   }
77 
78   if (IsMulticolumn()) mGenericTypes |= eTable;
79 }
80 
81 ////////////////////////////////////////////////////////////////////////////////
82 // XULListboxAccessible: Accessible
83 
NativeState()84 uint64_t XULListboxAccessible::NativeState() {
85   // As a XULListboxAccessible we can have the following states:
86   //   FOCUSED, READONLY, FOCUSABLE
87 
88   // Get focus status from base class
89   uint64_t states = Accessible::NativeState();
90 
91   // see if we are multiple select if so set ourselves as such
92 
93   if (mContent->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::seltype,
94                                          nsGkAtoms::multiple, eCaseMatters)) {
95     states |= states::MULTISELECTABLE | states::EXTSELECTABLE;
96   }
97 
98   return states;
99 }
100 
101 /**
102  * Our value is the label of our ( first ) selected child.
103  */
Value(nsString & aValue)104 void XULListboxAccessible::Value(nsString& aValue) {
105   aValue.Truncate();
106 
107   nsCOMPtr<nsIDOMXULSelectControlElement> select(do_QueryInterface(mContent));
108   if (select) {
109     nsCOMPtr<nsIDOMXULSelectControlItemElement> selectedItem;
110     select->GetSelectedItem(getter_AddRefs(selectedItem));
111     if (selectedItem) selectedItem->GetLabel(aValue);
112   }
113 }
114 
NativeRole()115 role XULListboxAccessible::NativeRole() {
116   // A richlistbox is used with the new autocomplete URL bar, and has a parent
117   // popup <panel>.
118   if (mContent->GetParent() &&
119       mContent->GetParent()->IsXULElement(nsGkAtoms::panel))
120     return roles::COMBOBOX_LIST;
121 
122   return IsMulticolumn() ? roles::TABLE : roles::LISTBOX;
123 }
124 
125 ////////////////////////////////////////////////////////////////////////////////
126 // XULListboxAccessible: Table
127 
ColCount()128 uint32_t XULListboxAccessible::ColCount() {
129   nsIContent* headContent = nullptr;
130   for (nsIContent* childContent = mContent->GetFirstChild(); childContent;
131        childContent = childContent->GetNextSibling()) {
132     if (childContent->NodeInfo()->Equals(nsGkAtoms::listcols,
133                                          kNameSpaceID_XUL)) {
134       headContent = childContent;
135     }
136   }
137   if (!headContent) return 0;
138 
139   uint32_t columnCount = 0;
140   for (nsIContent* childContent = headContent->GetFirstChild(); childContent;
141        childContent = childContent->GetNextSibling()) {
142     if (childContent->NodeInfo()->Equals(nsGkAtoms::listcol,
143                                          kNameSpaceID_XUL)) {
144       columnCount++;
145     }
146   }
147 
148   return columnCount;
149 }
150 
RowCount()151 uint32_t XULListboxAccessible::RowCount() {
152   nsCOMPtr<nsIDOMXULSelectControlElement> element(do_QueryInterface(mContent));
153 
154   uint32_t itemCount = 0;
155   if (element) element->GetItemCount(&itemCount);
156 
157   return itemCount;
158 }
159 
CellAt(uint32_t aRowIndex,uint32_t aColumnIndex)160 Accessible* XULListboxAccessible::CellAt(uint32_t aRowIndex,
161                                          uint32_t aColumnIndex) {
162   nsCOMPtr<nsIDOMXULSelectControlElement> control = do_QueryInterface(mContent);
163   NS_ENSURE_TRUE(control, nullptr);
164 
165   nsCOMPtr<nsIDOMXULSelectControlItemElement> item;
166   control->GetItemAtIndex(aRowIndex, getter_AddRefs(item));
167   if (!item) return nullptr;
168 
169   nsCOMPtr<nsIContent> itemContent(do_QueryInterface(item));
170   if (!itemContent) return nullptr;
171 
172   Accessible* row = mDoc->GetAccessible(itemContent);
173   NS_ENSURE_TRUE(row, nullptr);
174 
175   return row->GetChildAt(aColumnIndex);
176 }
177 
IsColSelected(uint32_t aColIdx)178 bool XULListboxAccessible::IsColSelected(uint32_t aColIdx) {
179   nsCOMPtr<nsIDOMXULMultiSelectControlElement> control =
180       do_QueryInterface(mContent);
181   NS_ASSERTION(control,
182                "Doesn't implement nsIDOMXULMultiSelectControlElement.");
183 
184   int32_t selectedrowCount = 0;
185   nsresult rv = control->GetSelectedCount(&selectedrowCount);
186   NS_ENSURE_SUCCESS(rv, false);
187 
188   return selectedrowCount == static_cast<int32_t>(RowCount());
189 }
190 
IsRowSelected(uint32_t aRowIdx)191 bool XULListboxAccessible::IsRowSelected(uint32_t aRowIdx) {
192   nsCOMPtr<nsIDOMXULSelectControlElement> control = do_QueryInterface(mContent);
193   NS_ASSERTION(control, "Doesn't implement nsIDOMXULSelectControlElement.");
194 
195   nsCOMPtr<nsIDOMXULSelectControlItemElement> item;
196   nsresult rv = control->GetItemAtIndex(aRowIdx, getter_AddRefs(item));
197   NS_ENSURE_SUCCESS(rv, false);
198 
199   bool isSelected = false;
200   item->GetSelected(&isSelected);
201   return isSelected;
202 }
203 
IsCellSelected(uint32_t aRowIdx,uint32_t aColIdx)204 bool XULListboxAccessible::IsCellSelected(uint32_t aRowIdx, uint32_t aColIdx) {
205   return IsRowSelected(aRowIdx);
206 }
207 
SelectedCellCount()208 uint32_t XULListboxAccessible::SelectedCellCount() {
209   nsCOMPtr<nsIDOMXULMultiSelectControlElement> control =
210       do_QueryInterface(mContent);
211   NS_ASSERTION(control,
212                "Doesn't implement nsIDOMXULMultiSelectControlElement.");
213 
214   nsCOMPtr<nsIDOMNodeList> selectedItems;
215   control->GetSelectedItems(getter_AddRefs(selectedItems));
216   if (!selectedItems) return 0;
217 
218   uint32_t selectedItemsCount = 0;
219   nsresult rv = selectedItems->GetLength(&selectedItemsCount);
220   NS_ENSURE_SUCCESS(rv, 0);
221 
222   return selectedItemsCount * ColCount();
223 }
224 
SelectedColCount()225 uint32_t XULListboxAccessible::SelectedColCount() {
226   nsCOMPtr<nsIDOMXULMultiSelectControlElement> control =
227       do_QueryInterface(mContent);
228   NS_ASSERTION(control,
229                "Doesn't implement nsIDOMXULMultiSelectControlElement.");
230 
231   int32_t selectedRowCount = 0;
232   nsresult rv = control->GetSelectedCount(&selectedRowCount);
233   NS_ENSURE_SUCCESS(rv, 0);
234 
235   return selectedRowCount > 0 &&
236                  selectedRowCount == static_cast<int32_t>(RowCount())
237              ? ColCount()
238              : 0;
239 }
240 
SelectedRowCount()241 uint32_t XULListboxAccessible::SelectedRowCount() {
242   nsCOMPtr<nsIDOMXULMultiSelectControlElement> control =
243       do_QueryInterface(mContent);
244   NS_ASSERTION(control,
245                "Doesn't implement nsIDOMXULMultiSelectControlElement.");
246 
247   int32_t selectedRowCount = 0;
248   nsresult rv = control->GetSelectedCount(&selectedRowCount);
249   NS_ENSURE_SUCCESS(rv, 0);
250 
251   return selectedRowCount >= 0 ? selectedRowCount : 0;
252 }
253 
SelectedCells(nsTArray<Accessible * > * aCells)254 void XULListboxAccessible::SelectedCells(nsTArray<Accessible*>* aCells) {
255   nsCOMPtr<nsIDOMXULMultiSelectControlElement> control =
256       do_QueryInterface(mContent);
257   NS_ASSERTION(control,
258                "Doesn't implement nsIDOMXULMultiSelectControlElement.");
259 
260   nsCOMPtr<nsIDOMNodeList> selectedItems;
261   control->GetSelectedItems(getter_AddRefs(selectedItems));
262   if (!selectedItems) return;
263 
264   uint32_t selectedItemsCount = 0;
265   DebugOnly<nsresult> rv = selectedItems->GetLength(&selectedItemsCount);
266   NS_ASSERTION(NS_SUCCEEDED(rv), "GetLength() Shouldn't fail!");
267 
268   for (uint32_t index = 0; index < selectedItemsCount; index++) {
269     nsCOMPtr<nsIDOMNode> itemNode;
270     selectedItems->Item(index, getter_AddRefs(itemNode));
271     nsCOMPtr<nsIContent> itemContent(do_QueryInterface(itemNode));
272     Accessible* item = mDoc->GetAccessible(itemContent);
273 
274     if (item) {
275       uint32_t cellCount = item->ChildCount();
276       for (uint32_t cellIdx = 0; cellIdx < cellCount; cellIdx++) {
277         Accessible* cell = mChildren[cellIdx];
278         if (cell->Role() == roles::CELL) aCells->AppendElement(cell);
279       }
280     }
281   }
282 }
283 
SelectedCellIndices(nsTArray<uint32_t> * aCells)284 void XULListboxAccessible::SelectedCellIndices(nsTArray<uint32_t>* aCells) {
285   nsCOMPtr<nsIDOMXULMultiSelectControlElement> control =
286       do_QueryInterface(mContent);
287   NS_ASSERTION(control,
288                "Doesn't implement nsIDOMXULMultiSelectControlElement.");
289 
290   nsCOMPtr<nsIDOMNodeList> selectedItems;
291   control->GetSelectedItems(getter_AddRefs(selectedItems));
292   if (!selectedItems) return;
293 
294   uint32_t selectedItemsCount = 0;
295   DebugOnly<nsresult> rv = selectedItems->GetLength(&selectedItemsCount);
296   NS_ASSERTION(NS_SUCCEEDED(rv), "GetLength() Shouldn't fail!");
297 
298   uint32_t colCount = ColCount();
299   aCells->SetCapacity(selectedItemsCount * colCount);
300   aCells->AppendElements(selectedItemsCount * colCount);
301 
302   for (uint32_t selItemsIdx = 0, cellsIdx = 0; selItemsIdx < selectedItemsCount;
303        selItemsIdx++) {
304     nsCOMPtr<nsIDOMNode> itemNode;
305     selectedItems->Item(selItemsIdx, getter_AddRefs(itemNode));
306     nsCOMPtr<nsIDOMXULSelectControlItemElement> item =
307         do_QueryInterface(itemNode);
308 
309     if (item) {
310       int32_t itemIdx = -1;
311       control->GetIndexOfItem(item, &itemIdx);
312       if (itemIdx >= 0)
313         for (uint32_t colIdx = 0; colIdx < colCount; colIdx++, cellsIdx++)
314           aCells->ElementAt(cellsIdx) = itemIdx * colCount + colIdx;
315     }
316   }
317 }
318 
SelectedColIndices(nsTArray<uint32_t> * aCols)319 void XULListboxAccessible::SelectedColIndices(nsTArray<uint32_t>* aCols) {
320   uint32_t selColCount = SelectedColCount();
321   aCols->SetCapacity(selColCount);
322 
323   for (uint32_t colIdx = 0; colIdx < selColCount; colIdx++)
324     aCols->AppendElement(colIdx);
325 }
326 
SelectedRowIndices(nsTArray<uint32_t> * aRows)327 void XULListboxAccessible::SelectedRowIndices(nsTArray<uint32_t>* aRows) {
328   nsCOMPtr<nsIDOMXULMultiSelectControlElement> control =
329       do_QueryInterface(mContent);
330   NS_ASSERTION(control,
331                "Doesn't implement nsIDOMXULMultiSelectControlElement.");
332 
333   nsCOMPtr<nsIDOMNodeList> selectedItems;
334   control->GetSelectedItems(getter_AddRefs(selectedItems));
335   if (!selectedItems) return;
336 
337   uint32_t rowCount = 0;
338   DebugOnly<nsresult> rv = selectedItems->GetLength(&rowCount);
339   NS_ASSERTION(NS_SUCCEEDED(rv), "GetLength() Shouldn't fail!");
340 
341   if (!rowCount) return;
342 
343   aRows->SetCapacity(rowCount);
344   aRows->AppendElements(rowCount);
345 
346   for (uint32_t rowIdx = 0; rowIdx < rowCount; rowIdx++) {
347     nsCOMPtr<nsIDOMNode> itemNode;
348     selectedItems->Item(rowIdx, getter_AddRefs(itemNode));
349     nsCOMPtr<nsIDOMXULSelectControlItemElement> item =
350         do_QueryInterface(itemNode);
351 
352     if (item) {
353       int32_t itemIdx = -1;
354       control->GetIndexOfItem(item, &itemIdx);
355       if (itemIdx >= 0) aRows->ElementAt(rowIdx) = itemIdx;
356     }
357   }
358 }
359 
SelectRow(uint32_t aRowIdx)360 void XULListboxAccessible::SelectRow(uint32_t aRowIdx) {
361   nsCOMPtr<nsIDOMXULMultiSelectControlElement> control =
362       do_QueryInterface(mContent);
363   NS_ASSERTION(control,
364                "Doesn't implement nsIDOMXULMultiSelectControlElement.");
365 
366   nsCOMPtr<nsIDOMXULSelectControlItemElement> item;
367   control->GetItemAtIndex(aRowIdx, getter_AddRefs(item));
368   control->SelectItem(item);
369 }
370 
UnselectRow(uint32_t aRowIdx)371 void XULListboxAccessible::UnselectRow(uint32_t aRowIdx) {
372   nsCOMPtr<nsIDOMXULMultiSelectControlElement> control =
373       do_QueryInterface(mContent);
374   NS_ASSERTION(control,
375                "Doesn't implement nsIDOMXULMultiSelectControlElement.");
376 
377   nsCOMPtr<nsIDOMXULSelectControlItemElement> item;
378   control->GetItemAtIndex(aRowIdx, getter_AddRefs(item));
379   control->RemoveItemFromSelection(item);
380 }
381 
382 ////////////////////////////////////////////////////////////////////////////////
383 // XULListboxAccessible: Widgets
384 
IsWidget() const385 bool XULListboxAccessible::IsWidget() const { return true; }
386 
IsActiveWidget() const387 bool XULListboxAccessible::IsActiveWidget() const {
388   if (IsAutoCompletePopup()) {
389     nsCOMPtr<nsIAutoCompletePopup> autoCompletePopupElm =
390         do_QueryInterface(mContent->GetParent());
391 
392     if (autoCompletePopupElm) {
393       bool isOpen = false;
394       autoCompletePopupElm->GetPopupOpen(&isOpen);
395       return isOpen;
396     }
397   }
398   return FocusMgr()->HasDOMFocus(mContent);
399 }
400 
AreItemsOperable() const401 bool XULListboxAccessible::AreItemsOperable() const {
402   if (IsAutoCompletePopup()) {
403     nsCOMPtr<nsIAutoCompletePopup> autoCompletePopupElm =
404         do_QueryInterface(mContent->GetParent());
405 
406     if (autoCompletePopupElm) {
407       bool isOpen = false;
408       autoCompletePopupElm->GetPopupOpen(&isOpen);
409       return isOpen;
410     }
411   }
412   return true;
413 }
414 
ContainerWidget() const415 Accessible* XULListboxAccessible::ContainerWidget() const {
416   if (IsAutoCompletePopup()) {
417     // This works for XUL autocompletes. It doesn't work for HTML forms
418     // autocomplete because of potential crossprocess calls (when autocomplete
419     // lives in content process while popup lives in chrome process). If that's
420     // a problem then rethink Widgets interface.
421     nsCOMPtr<nsIDOMXULMenuListElement> menuListElm =
422         do_QueryInterface(mContent->GetParent());
423     if (menuListElm) {
424       nsCOMPtr<nsIDOMNode> inputElm;
425       menuListElm->GetInputField(getter_AddRefs(inputElm));
426       if (inputElm) {
427         nsCOMPtr<nsINode> inputNode = do_QueryInterface(inputElm);
428         if (inputNode) {
429           Accessible* input = mDoc->GetAccessible(inputNode);
430           return input ? input->ContainerWidget() : nullptr;
431         }
432       }
433     }
434   }
435   return nullptr;
436 }
437 
438 ////////////////////////////////////////////////////////////////////////////////
439 // XULListitemAccessible
440 ////////////////////////////////////////////////////////////////////////////////
441 
XULListitemAccessible(nsIContent * aContent,DocAccessible * aDoc)442 XULListitemAccessible::XULListitemAccessible(nsIContent* aContent,
443                                              DocAccessible* aDoc)
444     : XULMenuitemAccessible(aContent, aDoc) {
445   mIsCheckbox = mContent->AsElement()->AttrValueIs(
446       kNameSpaceID_None, nsGkAtoms::type, nsGkAtoms::checkbox, eCaseMatters);
447   mType = eXULListItemType;
448 
449   // Walk XBL anonymous children for list items. Overrides the flag value from
450   // base XULMenuitemAccessible class.
451   mStateFlags &= ~eNoXBLKids;
452 }
453 
~XULListitemAccessible()454 XULListitemAccessible::~XULListitemAccessible() {}
455 
GetListAccessible() const456 Accessible* XULListitemAccessible::GetListAccessible() const {
457   if (IsDefunct()) return nullptr;
458 
459   nsCOMPtr<nsIDOMXULSelectControlItemElement> listItem =
460       do_QueryInterface(mContent);
461   if (!listItem) return nullptr;
462 
463   nsCOMPtr<nsIDOMXULSelectControlElement> list;
464   listItem->GetControl(getter_AddRefs(list));
465 
466   nsCOMPtr<nsIContent> listContent(do_QueryInterface(list));
467   if (!listContent) return nullptr;
468 
469   return mDoc->GetAccessible(listContent);
470 }
471 
472 ////////////////////////////////////////////////////////////////////////////////
473 // XULListitemAccessible Accessible
474 
Description(nsString & aDesc)475 void XULListitemAccessible::Description(nsString& aDesc) {
476   AccessibleWrap::Description(aDesc);
477 }
478 
479 ////////////////////////////////////////////////////////////////////////////////
480 // XULListitemAccessible: Accessible
481 
482 /**
483  * If there is a Listcell as a child ( not anonymous ) use it, otherwise
484  *   default to getting the name from GetXULName
485  */
NativeName(nsString & aName)486 ENameValueFlag XULListitemAccessible::NativeName(nsString& aName) {
487   nsIContent* childContent = mContent->GetFirstChild();
488   if (childContent) {
489     if (childContent->NodeInfo()->Equals(nsGkAtoms::listcell,
490                                          kNameSpaceID_XUL)) {
491       childContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::label,
492                                          aName);
493       return eNameOK;
494     }
495   }
496 
497   return Accessible::NativeName(aName);
498 }
499 
NativeRole()500 role XULListitemAccessible::NativeRole() {
501   Accessible* list = GetListAccessible();
502   if (!list) {
503     NS_ERROR("No list accessible for listitem accessible!");
504     return roles::NOTHING;
505   }
506 
507   if (list->Role() == roles::TABLE) return roles::ROW;
508 
509   if (mIsCheckbox) return roles::CHECK_RICH_OPTION;
510 
511   if (mParent && mParent->Role() == roles::COMBOBOX_LIST)
512     return roles::COMBOBOX_OPTION;
513 
514   return roles::RICH_OPTION;
515 }
516 
NativeState()517 uint64_t XULListitemAccessible::NativeState() {
518   if (mIsCheckbox) return XULMenuitemAccessible::NativeState();
519 
520   uint64_t states = NativeInteractiveState();
521 
522   nsCOMPtr<nsIDOMXULSelectControlItemElement> listItem =
523       do_QueryInterface(mContent);
524 
525   if (listItem) {
526     bool isSelected;
527     listItem->GetSelected(&isSelected);
528     if (isSelected) states |= states::SELECTED;
529 
530     if (FocusMgr()->IsFocused(this)) states |= states::FOCUSED;
531   }
532 
533   return states;
534 }
535 
NativeInteractiveState() const536 uint64_t XULListitemAccessible::NativeInteractiveState() const {
537   return NativelyUnavailable() || (mParent && mParent->NativelyUnavailable())
538              ? states::UNAVAILABLE
539              : states::FOCUSABLE | states::SELECTABLE;
540 }
541 
ActionNameAt(uint8_t aIndex,nsAString & aName)542 void XULListitemAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName) {
543   if (aIndex == eAction_Click && mIsCheckbox) {
544     uint64_t states = NativeState();
545     if (states & states::CHECKED)
546       aName.AssignLiteral("uncheck");
547     else
548       aName.AssignLiteral("check");
549   }
550 }
551 
552 ////////////////////////////////////////////////////////////////////////////////
553 // XULListitemAccessible: Widgets
554 
ContainerWidget() const555 Accessible* XULListitemAccessible::ContainerWidget() const { return Parent(); }
556 
557 ////////////////////////////////////////////////////////////////////////////////
558 // XULListCellAccessible
559 ////////////////////////////////////////////////////////////////////////////////
560 
XULListCellAccessible(nsIContent * aContent,DocAccessible * aDoc)561 XULListCellAccessible::XULListCellAccessible(nsIContent* aContent,
562                                              DocAccessible* aDoc)
563     : HyperTextAccessibleWrap(aContent, aDoc) {
564   mGenericTypes |= eTableCell;
565 }
566 
567 ////////////////////////////////////////////////////////////////////////////////
568 // nsISupports
569 
570 ////////////////////////////////////////////////////////////////////////////////
571 // XULListCellAccessible: TableCell
572 
Table() const573 TableAccessible* XULListCellAccessible::Table() const {
574   Accessible* thisRow = Parent();
575   if (!thisRow || thisRow->Role() != roles::ROW) return nullptr;
576 
577   Accessible* table = thisRow->Parent();
578   if (!table || table->Role() != roles::TABLE) return nullptr;
579 
580   return table->AsTable();
581 }
582 
ColIdx() const583 uint32_t XULListCellAccessible::ColIdx() const {
584   Accessible* row = Parent();
585   if (!row) return 0;
586 
587   int32_t indexInRow = IndexInParent();
588   uint32_t colIdx = 0;
589   for (int32_t idx = 0; idx < indexInRow; idx++) {
590     Accessible* cell = row->GetChildAt(idx);
591     roles::Role role = cell->Role();
592     if (role == roles::CELL || role == roles::GRID_CELL ||
593         role == roles::ROWHEADER || role == roles::COLUMNHEADER)
594       colIdx++;
595   }
596 
597   return colIdx;
598 }
599 
RowIdx() const600 uint32_t XULListCellAccessible::RowIdx() const {
601   Accessible* row = Parent();
602   if (!row) return 0;
603 
604   Accessible* table = row->Parent();
605   if (!table) return 0;
606 
607   int32_t indexInTable = row->IndexInParent();
608   uint32_t rowIdx = 0;
609   for (int32_t idx = 0; idx < indexInTable; idx++) {
610     row = table->GetChildAt(idx);
611     if (row->Role() == roles::ROW) rowIdx++;
612   }
613 
614   return rowIdx;
615 }
616 
ColHeaderCells(nsTArray<Accessible * > * aCells)617 void XULListCellAccessible::ColHeaderCells(nsTArray<Accessible*>* aCells) {
618   TableAccessible* table = Table();
619   NS_ASSERTION(table, "cell not in a table!");
620   if (!table) return;
621 
622   // Get column header cell from XUL listhead.
623   Accessible* list = nullptr;
624 
625   Accessible* tableAcc = table->AsAccessible();
626   uint32_t tableChildCount = tableAcc->ChildCount();
627   for (uint32_t childIdx = 0; childIdx < tableChildCount; childIdx++) {
628     Accessible* child = tableAcc->GetChildAt(childIdx);
629     if (child->Role() == roles::LIST) {
630       list = child;
631       break;
632     }
633   }
634 
635   if (list) {
636     Accessible* headerCell = list->GetChildAt(ColIdx());
637     if (headerCell) {
638       aCells->AppendElement(headerCell);
639       return;
640     }
641   }
642 
643   // No column header cell from XUL markup, try to get it from ARIA markup.
644   TableCellAccessible::ColHeaderCells(aCells);
645 }
646 
Selected()647 bool XULListCellAccessible::Selected() {
648   TableAccessible* table = Table();
649   NS_ENSURE_TRUE(table, false);  // we expect to be in a listbox (table)
650 
651   return table->IsRowSelected(RowIdx());
652 }
653 
654 ////////////////////////////////////////////////////////////////////////////////
655 // XULListCellAccessible. Accessible implementation
656 
NativeRole()657 role XULListCellAccessible::NativeRole() { return roles::CELL; }
658 
659 already_AddRefed<nsIPersistentProperties>
NativeAttributes()660 XULListCellAccessible::NativeAttributes() {
661   nsCOMPtr<nsIPersistentProperties> attributes =
662       HyperTextAccessibleWrap::NativeAttributes();
663 
664   // "table-cell-index" attribute
665   TableAccessible* table = Table();
666   if (!table)  // we expect to be in a listbox (table)
667     return attributes.forget();
668 
669   nsAutoString stringIdx;
670   stringIdx.AppendInt(table->CellIndexAt(RowIdx(), ColIdx()));
671   nsAccUtils::SetAccAttr(attributes, nsGkAtoms::tableCellIndex, stringIdx);
672 
673   return attributes.forget();
674 }
675