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 "LocalAccessible-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 "nsIAutoCompletePopup.h"
17 #include "nsIDOMXULMenuListElement.h"
18 #include "nsIDOMXULMultSelectCntrlEl.h"
19 #include "nsIDOMXULSelectCntrlItemEl.h"
20 #include "nsINodeList.h"
21 
22 using namespace mozilla::a11y;
23 
24 ////////////////////////////////////////////////////////////////////////////////
25 // XULColumAccessible
26 ////////////////////////////////////////////////////////////////////////////////
27 
XULColumAccessible(nsIContent * aContent,DocAccessible * aDoc)28 XULColumAccessible::XULColumAccessible(nsIContent* aContent,
29                                        DocAccessible* aDoc)
30     : AccessibleWrap(aContent, aDoc) {}
31 
NativeRole() const32 role XULColumAccessible::NativeRole() const { return roles::LIST; }
33 
NativeState() const34 uint64_t XULColumAccessible::NativeState() const { return states::READONLY; }
35 
36 ////////////////////////////////////////////////////////////////////////////////
37 // XULColumnItemAccessible
38 ////////////////////////////////////////////////////////////////////////////////
39 
XULColumnItemAccessible(nsIContent * aContent,DocAccessible * aDoc)40 XULColumnItemAccessible::XULColumnItemAccessible(nsIContent* aContent,
41                                                  DocAccessible* aDoc)
42     : LeafAccessible(aContent, aDoc) {}
43 
NativeRole() const44 role XULColumnItemAccessible::NativeRole() const { return roles::COLUMNHEADER; }
45 
NativeState() const46 uint64_t XULColumnItemAccessible::NativeState() const {
47   return states::READONLY;
48 }
49 
ActionCount() const50 uint8_t XULColumnItemAccessible::ActionCount() const { return 1; }
51 
ActionNameAt(uint8_t aIndex,nsAString & aName)52 void XULColumnItemAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName) {
53   if (aIndex == eAction_Click) aName.AssignLiteral("click");
54 }
55 
DoAction(uint8_t aIndex) const56 bool XULColumnItemAccessible::DoAction(uint8_t aIndex) const {
57   if (aIndex != eAction_Click) return false;
58 
59   DoCommand();
60   return true;
61 }
62 
63 ////////////////////////////////////////////////////////////////////////////////
64 // XULListboxAccessible
65 ////////////////////////////////////////////////////////////////////////////////
66 
XULListboxAccessible(nsIContent * aContent,DocAccessible * aDoc)67 XULListboxAccessible::XULListboxAccessible(nsIContent* aContent,
68                                            DocAccessible* aDoc)
69     : XULSelectControlAccessible(aContent, aDoc) {
70   dom::Element* parentEl = mContent->GetParentElement();
71   if (parentEl) {
72     nsCOMPtr<nsIAutoCompletePopup> autoCompletePopupElm =
73         parentEl->AsAutoCompletePopup();
74     if (autoCompletePopupElm) mGenericTypes |= eAutoCompletePopup;
75   }
76 
77   if (IsMulticolumn()) mGenericTypes |= eTable;
78 }
79 
80 ////////////////////////////////////////////////////////////////////////////////
81 // XULListboxAccessible: LocalAccessible
82 
NativeState() const83 uint64_t XULListboxAccessible::NativeState() const {
84   // As a XULListboxAccessible we can have the following states:
85   //   FOCUSED, READONLY, FOCUSABLE
86 
87   // Get focus status from base class
88   uint64_t states = LocalAccessible::NativeState();
89 
90   // see if we are multiple select if so set ourselves as such
91 
92   if (mContent->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::seltype,
93                                          nsGkAtoms::multiple, eCaseMatters)) {
94     states |= states::MULTISELECTABLE | states::EXTSELECTABLE;
95   }
96 
97   return states;
98 }
99 
NativeRole() const100 role XULListboxAccessible::NativeRole() const {
101   // A richlistbox is used with the new autocomplete URL bar, and has a parent
102   // popup <panel>.
103   if (mContent->GetParent() &&
104       mContent->GetParent()->IsXULElement(nsGkAtoms::panel)) {
105     return roles::COMBOBOX_LIST;
106   }
107 
108   return IsMulticolumn() ? roles::TABLE : roles::LISTBOX;
109 }
110 
111 ////////////////////////////////////////////////////////////////////////////////
112 // XULListboxAccessible: Table
113 
ColCount() const114 uint32_t XULListboxAccessible::ColCount() const { return 0; }
115 
RowCount()116 uint32_t XULListboxAccessible::RowCount() {
117   nsCOMPtr<nsIDOMXULSelectControlElement> element = Elm()->AsXULSelectControl();
118 
119   uint32_t itemCount = 0;
120   if (element) element->GetItemCount(&itemCount);
121 
122   return itemCount;
123 }
124 
CellAt(uint32_t aRowIndex,uint32_t aColumnIndex)125 LocalAccessible* XULListboxAccessible::CellAt(uint32_t aRowIndex,
126                                               uint32_t aColumnIndex) {
127   nsCOMPtr<nsIDOMXULSelectControlElement> control = Elm()->AsXULSelectControl();
128   NS_ENSURE_TRUE(control, nullptr);
129 
130   RefPtr<dom::Element> element;
131   control->GetItemAtIndex(aRowIndex, getter_AddRefs(element));
132   if (!element) return nullptr;
133 
134   LocalAccessible* row = mDoc->GetAccessible(element);
135   NS_ENSURE_TRUE(row, nullptr);
136 
137   return row->LocalChildAt(aColumnIndex);
138 }
139 
IsColSelected(uint32_t aColIdx)140 bool XULListboxAccessible::IsColSelected(uint32_t aColIdx) {
141   nsCOMPtr<nsIDOMXULMultiSelectControlElement> control =
142       Elm()->AsXULMultiSelectControl();
143   NS_ASSERTION(control,
144                "Doesn't implement nsIDOMXULMultiSelectControlElement.");
145 
146   int32_t selectedrowCount = 0;
147   nsresult rv = control->GetSelectedCount(&selectedrowCount);
148   NS_ENSURE_SUCCESS(rv, false);
149 
150   return selectedrowCount == static_cast<int32_t>(RowCount());
151 }
152 
IsRowSelected(uint32_t aRowIdx)153 bool XULListboxAccessible::IsRowSelected(uint32_t aRowIdx) {
154   nsCOMPtr<nsIDOMXULSelectControlElement> control = Elm()->AsXULSelectControl();
155   NS_ASSERTION(control, "Doesn't implement nsIDOMXULSelectControlElement.");
156 
157   RefPtr<dom::Element> element;
158   nsresult rv = control->GetItemAtIndex(aRowIdx, getter_AddRefs(element));
159   NS_ENSURE_SUCCESS(rv, false);
160   if (!element) {
161     return false;
162   }
163 
164   nsCOMPtr<nsIDOMXULSelectControlItemElement> item =
165       element->AsXULSelectControlItem();
166 
167   bool isSelected = false;
168   item->GetSelected(&isSelected);
169   return isSelected;
170 }
171 
IsCellSelected(uint32_t aRowIdx,uint32_t aColIdx)172 bool XULListboxAccessible::IsCellSelected(uint32_t aRowIdx, uint32_t aColIdx) {
173   return IsRowSelected(aRowIdx);
174 }
175 
SelectedCellCount()176 uint32_t XULListboxAccessible::SelectedCellCount() {
177   nsCOMPtr<nsIDOMXULMultiSelectControlElement> control =
178       Elm()->AsXULMultiSelectControl();
179   NS_ASSERTION(control,
180                "Doesn't implement nsIDOMXULMultiSelectControlElement.");
181 
182   nsCOMPtr<nsINodeList> selectedItems;
183   control->GetSelectedItems(getter_AddRefs(selectedItems));
184   if (!selectedItems) return 0;
185 
186   uint32_t selectedItemsCount = selectedItems->Length();
187 
188   return selectedItemsCount * ColCount();
189 }
190 
SelectedColCount()191 uint32_t XULListboxAccessible::SelectedColCount() {
192   nsCOMPtr<nsIDOMXULMultiSelectControlElement> control =
193       Elm()->AsXULMultiSelectControl();
194   NS_ASSERTION(control,
195                "Doesn't implement nsIDOMXULMultiSelectControlElement.");
196 
197   int32_t selectedRowCount = 0;
198   nsresult rv = control->GetSelectedCount(&selectedRowCount);
199   NS_ENSURE_SUCCESS(rv, 0);
200 
201   return selectedRowCount > 0 &&
202                  selectedRowCount == static_cast<int32_t>(RowCount())
203              ? ColCount()
204              : 0;
205 }
206 
SelectedRowCount()207 uint32_t XULListboxAccessible::SelectedRowCount() {
208   nsCOMPtr<nsIDOMXULMultiSelectControlElement> control =
209       Elm()->AsXULMultiSelectControl();
210   NS_ASSERTION(control,
211                "Doesn't implement nsIDOMXULMultiSelectControlElement.");
212 
213   int32_t selectedRowCount = 0;
214   nsresult rv = control->GetSelectedCount(&selectedRowCount);
215   NS_ENSURE_SUCCESS(rv, 0);
216 
217   return selectedRowCount >= 0 ? selectedRowCount : 0;
218 }
219 
SelectedCells(nsTArray<LocalAccessible * > * aCells)220 void XULListboxAccessible::SelectedCells(nsTArray<LocalAccessible*>* aCells) {
221   nsCOMPtr<nsIDOMXULMultiSelectControlElement> control =
222       Elm()->AsXULMultiSelectControl();
223   NS_ASSERTION(control,
224                "Doesn't implement nsIDOMXULMultiSelectControlElement.");
225 
226   nsCOMPtr<nsINodeList> selectedItems;
227   control->GetSelectedItems(getter_AddRefs(selectedItems));
228   if (!selectedItems) return;
229 
230   uint32_t selectedItemsCount = selectedItems->Length();
231 
232   for (uint32_t index = 0; index < selectedItemsCount; index++) {
233     nsIContent* itemContent = selectedItems->Item(index);
234     LocalAccessible* item = mDoc->GetAccessible(itemContent);
235 
236     if (item) {
237       uint32_t cellCount = item->ChildCount();
238       for (uint32_t cellIdx = 0; cellIdx < cellCount; cellIdx++) {
239         LocalAccessible* cell = mChildren[cellIdx];
240         if (cell->Role() == roles::CELL) aCells->AppendElement(cell);
241       }
242     }
243   }
244 }
245 
SelectedCellIndices(nsTArray<uint32_t> * aCells)246 void XULListboxAccessible::SelectedCellIndices(nsTArray<uint32_t>* aCells) {
247   nsCOMPtr<nsIDOMXULMultiSelectControlElement> control =
248       Elm()->AsXULMultiSelectControl();
249   NS_ASSERTION(control,
250                "Doesn't implement nsIDOMXULMultiSelectControlElement.");
251 
252   nsCOMPtr<nsINodeList> selectedItems;
253   control->GetSelectedItems(getter_AddRefs(selectedItems));
254   if (!selectedItems) return;
255 
256   uint32_t selectedItemsCount = selectedItems->Length();
257 
258   uint32_t colCount = ColCount();
259   aCells->SetCapacity(selectedItemsCount * colCount);
260   aCells->AppendElements(selectedItemsCount * colCount);
261 
262   for (uint32_t selItemsIdx = 0, cellsIdx = 0; selItemsIdx < selectedItemsCount;
263        selItemsIdx++) {
264     nsIContent* itemContent = selectedItems->Item(selItemsIdx);
265 
266     nsCOMPtr<nsIDOMXULSelectControlItemElement> item =
267         itemContent->AsElement()->AsXULSelectControlItem();
268     if (item) {
269       int32_t itemIdx = -1;
270       control->GetIndexOfItem(item, &itemIdx);
271       if (itemIdx >= 0) {
272         for (uint32_t colIdx = 0; colIdx < colCount; colIdx++, cellsIdx++) {
273           aCells->ElementAt(cellsIdx) = itemIdx * colCount + colIdx;
274         }
275       }
276     }
277   }
278 }
279 
SelectedColIndices(nsTArray<uint32_t> * aCols)280 void XULListboxAccessible::SelectedColIndices(nsTArray<uint32_t>* aCols) {
281   uint32_t selColCount = SelectedColCount();
282   aCols->SetCapacity(selColCount);
283 
284   for (uint32_t colIdx = 0; colIdx < selColCount; colIdx++) {
285     aCols->AppendElement(colIdx);
286   }
287 }
288 
SelectedRowIndices(nsTArray<uint32_t> * aRows)289 void XULListboxAccessible::SelectedRowIndices(nsTArray<uint32_t>* aRows) {
290   nsCOMPtr<nsIDOMXULMultiSelectControlElement> control =
291       Elm()->AsXULMultiSelectControl();
292   NS_ASSERTION(control,
293                "Doesn't implement nsIDOMXULMultiSelectControlElement.");
294 
295   nsCOMPtr<nsINodeList> selectedItems;
296   control->GetSelectedItems(getter_AddRefs(selectedItems));
297   if (!selectedItems) return;
298 
299   uint32_t rowCount = selectedItems->Length();
300 
301   if (!rowCount) return;
302 
303   aRows->SetCapacity(rowCount);
304   aRows->AppendElements(rowCount);
305 
306   for (uint32_t rowIdx = 0; rowIdx < rowCount; rowIdx++) {
307     nsIContent* itemContent = selectedItems->Item(rowIdx);
308     nsCOMPtr<nsIDOMXULSelectControlItemElement> item =
309         itemContent->AsElement()->AsXULSelectControlItem();
310 
311     if (item) {
312       int32_t itemIdx = -1;
313       control->GetIndexOfItem(item, &itemIdx);
314       if (itemIdx >= 0) aRows->ElementAt(rowIdx) = itemIdx;
315     }
316   }
317 }
318 
SelectRow(uint32_t aRowIdx)319 void XULListboxAccessible::SelectRow(uint32_t aRowIdx) {
320   nsCOMPtr<nsIDOMXULMultiSelectControlElement> control =
321       Elm()->AsXULMultiSelectControl();
322   NS_ASSERTION(control,
323                "Doesn't implement nsIDOMXULMultiSelectControlElement.");
324 
325   RefPtr<dom::Element> item;
326   control->GetItemAtIndex(aRowIdx, getter_AddRefs(item));
327   if (!item) {
328     return;
329   }
330 
331   nsCOMPtr<nsIDOMXULSelectControlItemElement> itemElm =
332       item->AsXULSelectControlItem();
333   control->SelectItem(itemElm);
334 }
335 
UnselectRow(uint32_t aRowIdx)336 void XULListboxAccessible::UnselectRow(uint32_t aRowIdx) {
337   nsCOMPtr<nsIDOMXULMultiSelectControlElement> control =
338       Elm()->AsXULMultiSelectControl();
339   NS_ASSERTION(control,
340                "Doesn't implement nsIDOMXULMultiSelectControlElement.");
341 
342   RefPtr<dom::Element> item;
343   control->GetItemAtIndex(aRowIdx, getter_AddRefs(item));
344   if (!item) {
345     return;
346   }
347 
348   nsCOMPtr<nsIDOMXULSelectControlItemElement> itemElm =
349       item->AsXULSelectControlItem();
350   control->RemoveItemFromSelection(itemElm);
351 }
352 
353 ////////////////////////////////////////////////////////////////////////////////
354 // XULListboxAccessible: Widgets
355 
IsWidget() const356 bool XULListboxAccessible::IsWidget() const { return true; }
357 
IsActiveWidget() const358 bool XULListboxAccessible::IsActiveWidget() const {
359   if (IsAutoCompletePopup()) {
360     nsIContent* parentContent = mContent->GetParent();
361     if (parentContent) {
362       nsCOMPtr<nsIAutoCompletePopup> autoCompletePopupElm =
363           parentContent->AsElement()->AsAutoCompletePopup();
364       if (autoCompletePopupElm) {
365         bool isOpen = false;
366         autoCompletePopupElm->GetPopupOpen(&isOpen);
367         return isOpen;
368       }
369     }
370   }
371   return FocusMgr()->HasDOMFocus(mContent);
372 }
373 
AreItemsOperable() const374 bool XULListboxAccessible::AreItemsOperable() const {
375   if (IsAutoCompletePopup()) {
376     nsIContent* parentContent = mContent->GetParent();
377     if (parentContent) {
378       nsCOMPtr<nsIAutoCompletePopup> autoCompletePopupElm =
379           parentContent->AsElement()->AsAutoCompletePopup();
380       if (autoCompletePopupElm) {
381         bool isOpen = false;
382         autoCompletePopupElm->GetPopupOpen(&isOpen);
383         return isOpen;
384       }
385     }
386   }
387   return true;
388 }
389 
ContainerWidget() const390 LocalAccessible* XULListboxAccessible::ContainerWidget() const {
391   if (IsAutoCompletePopup() && mContent->GetParent()) {
392     // This works for XUL autocompletes. It doesn't work for HTML forms
393     // autocomplete because of potential crossprocess calls (when autocomplete
394     // lives in content process while popup lives in chrome process). If that's
395     // a problem then rethink Widgets interface.
396     nsCOMPtr<nsIDOMXULMenuListElement> menuListElm =
397         mContent->GetParent()->AsElement()->AsXULMenuList();
398     if (menuListElm) {
399       RefPtr<mozilla::dom::Element> inputElm;
400       menuListElm->GetInputField(getter_AddRefs(inputElm));
401       if (inputElm) {
402         LocalAccessible* input = mDoc->GetAccessible(inputElm);
403         return input ? input->ContainerWidget() : nullptr;
404       }
405     }
406   }
407   return nullptr;
408 }
409 
410 ////////////////////////////////////////////////////////////////////////////////
411 // XULListitemAccessible
412 ////////////////////////////////////////////////////////////////////////////////
413 
XULListitemAccessible(nsIContent * aContent,DocAccessible * aDoc)414 XULListitemAccessible::XULListitemAccessible(nsIContent* aContent,
415                                              DocAccessible* aDoc)
416     : XULMenuitemAccessible(aContent, aDoc) {
417   mIsCheckbox = mContent->AsElement()->AttrValueIs(
418       kNameSpaceID_None, nsGkAtoms::type, nsGkAtoms::checkbox, eCaseMatters);
419   mType = eXULListItemType;
420 }
421 
~XULListitemAccessible()422 XULListitemAccessible::~XULListitemAccessible() {}
423 
GetListAccessible() const424 LocalAccessible* XULListitemAccessible::GetListAccessible() const {
425   if (IsDefunct()) return nullptr;
426 
427   nsCOMPtr<nsIDOMXULSelectControlItemElement> listItem =
428       Elm()->AsXULSelectControlItem();
429   if (!listItem) return nullptr;
430 
431   RefPtr<dom::Element> listElement;
432   listItem->GetControl(getter_AddRefs(listElement));
433   if (!listElement) return nullptr;
434 
435   return mDoc->GetAccessible(listElement);
436 }
437 
438 ////////////////////////////////////////////////////////////////////////////////
439 // XULListitemAccessible LocalAccessible
440 
Description(nsString & aDesc)441 void XULListitemAccessible::Description(nsString& aDesc) {
442   AccessibleWrap::Description(aDesc);
443 }
444 
445 ////////////////////////////////////////////////////////////////////////////////
446 // XULListitemAccessible: LocalAccessible
447 
448 /**
449  * Get the name from GetXULName.
450  */
NativeName(nsString & aName) const451 ENameValueFlag XULListitemAccessible::NativeName(nsString& aName) const {
452   return LocalAccessible::NativeName(aName);
453 }
454 
NativeRole() const455 role XULListitemAccessible::NativeRole() const {
456   LocalAccessible* list = GetListAccessible();
457   if (!list) {
458     NS_ERROR("No list accessible for listitem accessible!");
459     return roles::NOTHING;
460   }
461 
462   if (list->Role() == roles::TABLE) return roles::ROW;
463 
464   if (mIsCheckbox) return roles::CHECK_RICH_OPTION;
465 
466   if (mParent && mParent->Role() == roles::COMBOBOX_LIST) {
467     return roles::COMBOBOX_OPTION;
468   }
469 
470   return roles::RICH_OPTION;
471 }
472 
NativeState() const473 uint64_t XULListitemAccessible::NativeState() const {
474   if (mIsCheckbox) return XULMenuitemAccessible::NativeState();
475 
476   uint64_t states = NativeInteractiveState();
477 
478   nsCOMPtr<nsIDOMXULSelectControlItemElement> listItem =
479       Elm()->AsXULSelectControlItem();
480   if (listItem) {
481     bool isSelected;
482     listItem->GetSelected(&isSelected);
483     if (isSelected) states |= states::SELECTED;
484 
485     if (FocusMgr()->IsFocused(this)) states |= states::FOCUSED;
486   }
487 
488   return states;
489 }
490 
NativeInteractiveState() const491 uint64_t XULListitemAccessible::NativeInteractiveState() const {
492   return NativelyUnavailable() || (mParent && mParent->NativelyUnavailable())
493              ? states::UNAVAILABLE
494              : states::FOCUSABLE | states::SELECTABLE;
495 }
496 
ActionNameAt(uint8_t aIndex,nsAString & aName)497 void XULListitemAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName) {
498   if (aIndex == eAction_Click && mIsCheckbox) {
499     uint64_t states = NativeState();
500     if (states & states::CHECKED) {
501       aName.AssignLiteral("uncheck");
502     } else {
503       aName.AssignLiteral("check");
504     }
505   }
506 }
507 
508 ////////////////////////////////////////////////////////////////////////////////
509 // XULListitemAccessible: Widgets
510 
ContainerWidget() const511 LocalAccessible* XULListitemAccessible::ContainerWidget() const {
512   return LocalParent();
513 }
514