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