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