accessible/src/xul/XULFormControlAccessible.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:58435d23c9b3
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 "XULFormControlAccessible.h"
7
8 #include "Accessible-inl.h"
9 #include "HTMLFormControlAccessible.h"
10 #include "nsAccUtils.h"
11 #include "nsCoreUtils.h"
12 #include "DocAccessible.h"
13 #include "nsIAccessibleRelation.h"
14 #include "Relation.h"
15 #include "Role.h"
16 #include "States.h"
17 #include "TreeWalker.h"
18 #include "XULMenuAccessible.h"
19
20 #include "nsIDOMNSEditableElement.h"
21 #include "nsIDOMXULButtonElement.h"
22 #include "nsIDOMXULCheckboxElement.h"
23 #include "nsIDOMXULMenuListElement.h"
24 #include "nsIDOMXULSelectCntrlItemEl.h"
25 #include "nsIDOMXULTextboxElement.h"
26 #include "nsIEditor.h"
27 #include "nsIFrame.h"
28 #include "nsITextControlFrame.h"
29 #include "nsMenuPopupFrame.h"
30 #include "nsNameSpaceManager.h"
31 #include "mozilla/dom/Element.h"
32
33 using namespace mozilla::a11y;
34
35 ////////////////////////////////////////////////////////////////////////////////
36 // XULButtonAccessible
37 ////////////////////////////////////////////////////////////////////////////////
38
39 XULButtonAccessible::
40 XULButtonAccessible(nsIContent* aContent, DocAccessible* aDoc) :
41 AccessibleWrap(aContent, aDoc)
42 {
43 if (ContainsMenu()) {
44 mGenericTypes |= eMenuButton;
45 } else {
46 mGenericTypes |= eButton;
47 }
48 }
49
50 ////////////////////////////////////////////////////////////////////////////////
51 // XULButtonAccessible: nsISupports
52
53 NS_IMPL_ISUPPORTS_INHERITED0(XULButtonAccessible, Accessible)
54
55 ////////////////////////////////////////////////////////////////////////////////
56 // XULButtonAccessible: nsIAccessible
57
58 uint8_t
59 XULButtonAccessible::ActionCount()
60 {
61 return 1;
62 }
63
64 NS_IMETHODIMP
65 XULButtonAccessible::GetActionName(uint8_t aIndex, nsAString& aName)
66 {
67 if (aIndex == eAction_Click) {
68 aName.AssignLiteral("press");
69 return NS_OK;
70 }
71 return NS_ERROR_INVALID_ARG;
72 }
73
74 NS_IMETHODIMP
75 XULButtonAccessible::DoAction(uint8_t aIndex)
76 {
77 if (aIndex != 0)
78 return NS_ERROR_INVALID_ARG;
79
80 DoCommand();
81 return NS_OK;
82 }
83
84 ////////////////////////////////////////////////////////////////////////////////
85 // XULButtonAccessible: Accessible
86
87 role
88 XULButtonAccessible::NativeRole()
89 {
90 return roles::PUSHBUTTON;
91 }
92
93 uint64_t
94 XULButtonAccessible::NativeState()
95 {
96 // Possible states: focused, focusable, unavailable(disabled).
97
98 // get focus and disable status from base class
99 uint64_t state = Accessible::NativeState();
100
101 // Buttons can be checked -- they simply appear pressed in rather than checked
102 nsCOMPtr<nsIDOMXULButtonElement> xulButtonElement(do_QueryInterface(mContent));
103 if (xulButtonElement) {
104 nsAutoString type;
105 xulButtonElement->GetType(type);
106 if (type.EqualsLiteral("checkbox") || type.EqualsLiteral("radio")) {
107 state |= states::CHECKABLE;
108 bool checked = false;
109 int32_t checkState = 0;
110 xulButtonElement->GetChecked(&checked);
111 if (checked) {
112 state |= states::PRESSED;
113 xulButtonElement->GetCheckState(&checkState);
114 if (checkState == nsIDOMXULButtonElement::CHECKSTATE_MIXED) {
115 state |= states::MIXED;
116 }
117 }
118 }
119 }
120
121 if (ContainsMenu())
122 state |= states::HASPOPUP;
123
124 if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::_default))
125 state |= states::DEFAULT;
126
127 return state;
128 }
129
130 ////////////////////////////////////////////////////////////////////////////////
131 // XULButtonAccessible: Widgets
132
133 bool
134 XULButtonAccessible::IsWidget() const
135 {
136 return true;
137 }
138
139 bool
140 XULButtonAccessible::IsActiveWidget() const
141 {
142 return FocusMgr()->HasDOMFocus(mContent);
143 }
144
145 bool
146 XULButtonAccessible::AreItemsOperable() const
147 {
148 if (IsMenuButton()) {
149 Accessible* menuPopup = mChildren.SafeElementAt(0, nullptr);
150 if (menuPopup) {
151 nsMenuPopupFrame* menuPopupFrame = do_QueryFrame(menuPopup->GetFrame());
152 return menuPopupFrame->IsOpen();
153 }
154 }
155 return false; // no items
156 }
157
158 Accessible*
159 XULButtonAccessible::ContainerWidget() const
160 {
161 if (IsMenuButton() && mParent && mParent->IsAutoComplete())
162 return mParent;
163 return nullptr;
164 }
165
166 bool
167 XULButtonAccessible::IsAcceptableChild(Accessible* aPossibleChild) const
168 {
169 // In general XUL button has not accessible children. Nevertheless menu
170 // buttons can have button (@type="menu-button") and popup accessibles
171 // (@type="menu-button", @type="menu" or columnpicker.
172
173 // XXX: no children until the button is menu button. Probably it's not
174 // totally correct but in general AT wants to have leaf buttons.
175 roles::Role role = aPossibleChild->Role();
176
177 // Get an accessible for menupopup or panel elements.
178 if (role == roles::MENUPOPUP)
179 return true;
180
181 // Button type="menu-button" contains a real button. Get an accessible
182 // for it. Ignore dropmarker button which is placed as a last child.
183 if (role != roles::PUSHBUTTON ||
184 aPossibleChild->GetContent()->Tag() == nsGkAtoms::dropMarker)
185 return false;
186
187 return mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
188 nsGkAtoms::menuButton, eCaseMatters);
189 }
190
191 ////////////////////////////////////////////////////////////////////////////////
192 // XULButtonAccessible protected
193
194 bool
195 XULButtonAccessible::ContainsMenu()
196 {
197 static nsIContent::AttrValuesArray strings[] =
198 {&nsGkAtoms::menu, &nsGkAtoms::menuButton, nullptr};
199
200 return mContent->FindAttrValueIn(kNameSpaceID_None,
201 nsGkAtoms::type,
202 strings, eCaseMatters) >= 0;
203 }
204
205 ////////////////////////////////////////////////////////////////////////////////
206 // XULDropmarkerAccessible
207 ////////////////////////////////////////////////////////////////////////////////
208
209 XULDropmarkerAccessible::
210 XULDropmarkerAccessible(nsIContent* aContent, DocAccessible* aDoc) :
211 LeafAccessible(aContent, aDoc)
212 {
213 }
214
215 uint8_t
216 XULDropmarkerAccessible::ActionCount()
217 {
218 return 1;
219 }
220
221 bool
222 XULDropmarkerAccessible::DropmarkerOpen(bool aToggleOpen)
223 {
224 bool isOpen = false;
225
226 nsCOMPtr<nsIDOMXULButtonElement> parentButtonElement =
227 do_QueryInterface(mContent->GetFlattenedTreeParent());
228
229 if (parentButtonElement) {
230 parentButtonElement->GetOpen(&isOpen);
231 if (aToggleOpen)
232 parentButtonElement->SetOpen(!isOpen);
233 }
234 else {
235 nsCOMPtr<nsIDOMXULMenuListElement> parentMenuListElement =
236 do_QueryInterface(parentButtonElement);
237 if (parentMenuListElement) {
238 parentMenuListElement->GetOpen(&isOpen);
239 if (aToggleOpen)
240 parentMenuListElement->SetOpen(!isOpen);
241 }
242 }
243
244 return isOpen;
245 }
246
247 /**
248 * Return the name of our only action
249 */
250 NS_IMETHODIMP
251 XULDropmarkerAccessible::GetActionName(uint8_t aIndex, nsAString& aName)
252 {
253 if (aIndex == eAction_Click) {
254 if (DropmarkerOpen(false))
255 aName.AssignLiteral("close");
256 else
257 aName.AssignLiteral("open");
258 return NS_OK;
259 }
260
261 return NS_ERROR_INVALID_ARG;
262 }
263
264 /**
265 * Tell the Dropmarker to do its action
266 */
267 NS_IMETHODIMP
268 XULDropmarkerAccessible::DoAction(uint8_t index)
269 {
270 if (index == eAction_Click) {
271 DropmarkerOpen(true); // Reverse the open attribute
272 return NS_OK;
273 }
274 return NS_ERROR_INVALID_ARG;
275 }
276
277 role
278 XULDropmarkerAccessible::NativeRole()
279 {
280 return roles::PUSHBUTTON;
281 }
282
283 uint64_t
284 XULDropmarkerAccessible::NativeState()
285 {
286 return DropmarkerOpen(false) ? states::PRESSED : 0;
287 }
288
289 ////////////////////////////////////////////////////////////////////////////////
290 // XULCheckboxAccessible
291 ////////////////////////////////////////////////////////////////////////////////
292
293 XULCheckboxAccessible::
294 XULCheckboxAccessible(nsIContent* aContent, DocAccessible* aDoc) :
295 LeafAccessible(aContent, aDoc)
296 {
297 }
298
299 role
300 XULCheckboxAccessible::NativeRole()
301 {
302 return roles::CHECKBUTTON;
303 }
304
305 uint8_t
306 XULCheckboxAccessible::ActionCount()
307 {
308 return 1;
309 }
310
311 /**
312 * Return the name of our only action
313 */
314 NS_IMETHODIMP
315 XULCheckboxAccessible::GetActionName(uint8_t aIndex, nsAString& aName)
316 {
317 if (aIndex == eAction_Click) {
318 // check or uncheck
319
320 if (NativeState() & states::CHECKED)
321 aName.AssignLiteral("uncheck");
322 else
323 aName.AssignLiteral("check");
324
325 return NS_OK;
326 }
327 return NS_ERROR_INVALID_ARG;
328 }
329
330 /**
331 * Tell the checkbox to do its only action -- check( or uncheck) itself
332 */
333 NS_IMETHODIMP
334 XULCheckboxAccessible::DoAction(uint8_t aIndex)
335 {
336 if (aIndex != eAction_Click)
337 return NS_ERROR_INVALID_ARG;
338
339 DoCommand();
340 return NS_OK;
341 }
342
343 uint64_t
344 XULCheckboxAccessible::NativeState()
345 {
346 // Possible states: focused, focusable, unavailable(disabled), checked
347 // Get focus and disable status from base class
348 uint64_t state = LeafAccessible::NativeState();
349
350 state |= states::CHECKABLE;
351
352 // Determine Checked state
353 nsCOMPtr<nsIDOMXULCheckboxElement> xulCheckboxElement =
354 do_QueryInterface(mContent);
355 if (xulCheckboxElement) {
356 bool checked = false;
357 xulCheckboxElement->GetChecked(&checked);
358 if (checked) {
359 state |= states::CHECKED;
360 int32_t checkState = 0;
361 xulCheckboxElement->GetCheckState(&checkState);
362 if (checkState == nsIDOMXULCheckboxElement::CHECKSTATE_MIXED)
363 state |= states::MIXED;
364 }
365 }
366
367 return state;
368 }
369
370 ////////////////////////////////////////////////////////////////////////////////
371 // XULGroupboxAccessible
372 ////////////////////////////////////////////////////////////////////////////////
373
374 XULGroupboxAccessible::
375 XULGroupboxAccessible(nsIContent* aContent, DocAccessible* aDoc) :
376 AccessibleWrap(aContent, aDoc)
377 {
378 }
379
380 role
381 XULGroupboxAccessible::NativeRole()
382 {
383 return roles::GROUPING;
384 }
385
386 ENameValueFlag
387 XULGroupboxAccessible::NativeName(nsString& aName)
388 {
389 // XXX: we use the first related accessible only.
390 Accessible* label =
391 RelationByType(RelationType::LABELLED_BY).Next();
392 if (label)
393 return label->Name(aName);
394
395 return eNameOK;
396 }
397
398 Relation
399 XULGroupboxAccessible::RelationByType(RelationType aType)
400 {
401 Relation rel = AccessibleWrap::RelationByType(aType);
402 if (aType != RelationType::LABELLED_BY)
403 return rel;
404
405 // The label for xul:groupbox is generated from xul:label that is
406 // inside the anonymous content of the xul:caption.
407 // The xul:label has an accessible object but the xul:caption does not
408 uint32_t childCount = ChildCount();
409 for (uint32_t childIdx = 0; childIdx < childCount; childIdx++) {
410 Accessible* childAcc = GetChildAt(childIdx);
411 if (childAcc->Role() == roles::LABEL) {
412 // Ensure that it's our label
413 Relation reverseRel = childAcc->RelationByType(RelationType::LABEL_FOR);
414 Accessible* testGroupbox = nullptr;
415 while ((testGroupbox = reverseRel.Next()))
416 if (testGroupbox == this) {
417 // The <label> points back to this groupbox
418 rel.AppendTarget(childAcc);
419 }
420 }
421 }
422
423 return rel;
424 }
425
426 ////////////////////////////////////////////////////////////////////////////////
427 // XULRadioButtonAccessible
428 ////////////////////////////////////////////////////////////////////////////////
429
430 XULRadioButtonAccessible::
431 XULRadioButtonAccessible(nsIContent* aContent, DocAccessible* aDoc) :
432 RadioButtonAccessible(aContent, aDoc)
433 {
434 }
435
436 uint64_t
437 XULRadioButtonAccessible::NativeState()
438 {
439 uint64_t state = LeafAccessible::NativeState();
440 state |= states::CHECKABLE;
441
442 nsCOMPtr<nsIDOMXULSelectControlItemElement> radioButton =
443 do_QueryInterface(mContent);
444 if (radioButton) {
445 bool selected = false; // Radio buttons can be selected
446 radioButton->GetSelected(&selected);
447 if (selected) {
448 state |= states::CHECKED;
449 }
450 }
451
452 return state;
453 }
454
455 uint64_t
456 XULRadioButtonAccessible::NativeInteractiveState() const
457 {
458 return NativelyUnavailable() ? states::UNAVAILABLE : states::FOCUSABLE;
459 }
460
461 ////////////////////////////////////////////////////////////////////////////////
462 // XULRadioButtonAccessible: Widgets
463
464 Accessible*
465 XULRadioButtonAccessible::ContainerWidget() const
466 {
467 return mParent;
468 }
469
470
471 ////////////////////////////////////////////////////////////////////////////////
472 // XULRadioGroupAccessible
473 ////////////////////////////////////////////////////////////////////////////////
474
475 /**
476 * XUL Radio Group
477 * The Radio Group proxies for the Radio Buttons themselves. The Group gets
478 * focus whereas the Buttons do not. So we only have an accessible object for
479 * this for the purpose of getting the proper RadioButton. Need this here to
480 * avoid circular reference problems when navigating the accessible tree and
481 * for getting to the radiobuttons.
482 */
483
484 XULRadioGroupAccessible::
485 XULRadioGroupAccessible(nsIContent* aContent, DocAccessible* aDoc) :
486 XULSelectControlAccessible(aContent, aDoc)
487 {
488 }
489
490 role
491 XULRadioGroupAccessible::NativeRole()
492 {
493 return roles::GROUPING;
494 }
495
496 uint64_t
497 XULRadioGroupAccessible::NativeInteractiveState() const
498 {
499 // The radio group is not focusable. Sometimes the focus controller will
500 // report that it is focused. That means that the actual selected radio button
501 // should be considered focused.
502 return NativelyUnavailable() ? states::UNAVAILABLE : 0;
503 }
504
505 ////////////////////////////////////////////////////////////////////////////////
506 // XULRadioGroupAccessible: Widgets
507
508 bool
509 XULRadioGroupAccessible::IsWidget() const
510 {
511 return true;
512 }
513
514 bool
515 XULRadioGroupAccessible::IsActiveWidget() const
516 {
517 return FocusMgr()->HasDOMFocus(mContent);
518 }
519
520 bool
521 XULRadioGroupAccessible::AreItemsOperable() const
522 {
523 return true;
524 }
525
526
527 ////////////////////////////////////////////////////////////////////////////////
528 // XULStatusBarAccessible
529 ////////////////////////////////////////////////////////////////////////////////
530
531 XULStatusBarAccessible::
532 XULStatusBarAccessible(nsIContent* aContent, DocAccessible* aDoc) :
533 AccessibleWrap(aContent, aDoc)
534 {
535 }
536
537 role
538 XULStatusBarAccessible::NativeRole()
539 {
540 return roles::STATUSBAR;
541 }
542
543
544 ////////////////////////////////////////////////////////////////////////////////
545 // XULToolbarButtonAccessible
546 ////////////////////////////////////////////////////////////////////////////////
547
548 XULToolbarButtonAccessible::
549 XULToolbarButtonAccessible(nsIContent* aContent, DocAccessible* aDoc) :
550 XULButtonAccessible(aContent, aDoc)
551 {
552 }
553
554 void
555 XULToolbarButtonAccessible::GetPositionAndSizeInternal(int32_t* aPosInSet,
556 int32_t* aSetSize)
557 {
558 int32_t setSize = 0;
559 int32_t posInSet = 0;
560
561 Accessible* parent = Parent();
562 if (!parent)
563 return;
564
565 uint32_t childCount = parent->ChildCount();
566 for (uint32_t childIdx = 0; childIdx < childCount; childIdx++) {
567 Accessible* child = parent->GetChildAt(childIdx);
568 if (IsSeparator(child)) { // end of a group of buttons
569 if (posInSet)
570 break; // we've found our group, so we're done
571
572 setSize = 0; // not our group, so start a new group
573
574 } else {
575 setSize++; // another button in the group
576
577 if (child == this)
578 posInSet = setSize; // we've found our button
579 }
580 }
581
582 *aPosInSet = posInSet;
583 *aSetSize = setSize;
584 }
585
586 bool
587 XULToolbarButtonAccessible::IsSeparator(Accessible* aAccessible)
588 {
589 nsIContent* content = aAccessible->GetContent();
590 return content && ((content->Tag() == nsGkAtoms::toolbarseparator) ||
591 (content->Tag() == nsGkAtoms::toolbarspacer) ||
592 (content->Tag() == nsGkAtoms::toolbarspring)); }
593
594
595 ////////////////////////////////////////////////////////////////////////////////
596 // XULToolbarAccessible
597 ////////////////////////////////////////////////////////////////////////////////
598
599 XULToolbarAccessible::
600 XULToolbarAccessible(nsIContent* aContent, DocAccessible* aDoc) :
601 AccessibleWrap(aContent, aDoc)
602 {
603 }
604
605 role
606 XULToolbarAccessible::NativeRole()
607 {
608 return roles::TOOLBAR;
609 }
610
611 ENameValueFlag
612 XULToolbarAccessible::NativeName(nsString& aName)
613 {
614 if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::toolbarname, aName))
615 aName.CompressWhitespace();
616
617 return eNameOK;
618 }
619
620
621 ////////////////////////////////////////////////////////////////////////////////
622 // XULToolbarAccessible
623 ////////////////////////////////////////////////////////////////////////////////
624
625 XULToolbarSeparatorAccessible::
626 XULToolbarSeparatorAccessible(nsIContent* aContent, DocAccessible* aDoc) :
627 LeafAccessible(aContent, aDoc)
628 {
629 }
630
631 role
632 XULToolbarSeparatorAccessible::NativeRole()
633 {
634 return roles::SEPARATOR;
635 }
636
637 uint64_t
638 XULToolbarSeparatorAccessible::NativeState()
639 {
640 return 0;
641 }

mercurial