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/. */
6 #include "XULFormControlAccessible.h"
8 #include "Accessible-inl.h"
9 #include "HTMLFormControlAccessible.h"
10 #include "nsAccUtils.h"
11 #include "DocAccessible.h"
12 #include "nsIAccessibleRelation.h"
16 #include "TreeWalker.h"
17 #include "XULMenuAccessible.h"
19 #include "nsIDOMXULButtonElement.h"
20 #include "nsIDOMXULMenuListElement.h"
21 #include "nsIDOMXULRadioGroupElement.h"
22 #include "nsIDOMXULSelectCntrlItemEl.h"
23 #include "nsIEditor.h"
25 #include "nsITextControlFrame.h"
26 #include "nsMenuPopupFrame.h"
27 #include "nsNameSpaceManager.h"
28 #include "mozilla/dom/Element.h"
30 using namespace mozilla::a11y
;
32 ////////////////////////////////////////////////////////////////////////////////
33 // XULButtonAccessible
34 ////////////////////////////////////////////////////////////////////////////////
36 XULButtonAccessible::XULButtonAccessible(nsIContent
* aContent
,
38 : AccessibleWrap(aContent
, aDoc
) {
40 mGenericTypes
|= eMenuButton
;
42 mGenericTypes
|= eButton
;
46 XULButtonAccessible::~XULButtonAccessible() {}
48 ////////////////////////////////////////////////////////////////////////////////
49 // XULButtonAccessible: nsISupports
51 ////////////////////////////////////////////////////////////////////////////////
52 // XULButtonAccessible: nsIAccessible
54 uint8_t XULButtonAccessible::ActionCount() const { return 1; }
56 void XULButtonAccessible::ActionNameAt(uint8_t aIndex
, nsAString
& aName
) {
57 if (aIndex
== eAction_Click
) aName
.AssignLiteral("press");
60 bool XULButtonAccessible::DoAction(uint8_t aIndex
) const {
61 if (aIndex
!= 0) return false;
67 ////////////////////////////////////////////////////////////////////////////////
68 // XULButtonAccessible: Accessible
70 role
XULButtonAccessible::NativeRole() const { return roles::PUSHBUTTON
; }
72 uint64_t XULButtonAccessible::NativeState() const {
73 // Possible states: focused, focusable, unavailable(disabled).
75 // get focus and disable status from base class
76 uint64_t state
= Accessible::NativeState();
78 // Buttons can be checked -- they simply appear pressed in rather than checked
79 nsCOMPtr
<nsIDOMXULButtonElement
> xulButtonElement
= Elm()->AsXULButton();
80 if (xulButtonElement
) {
82 xulButtonElement
->GetType(type
);
83 if (type
.EqualsLiteral("checkbox") || type
.EqualsLiteral("radio")) {
84 state
|= states::CHECKABLE
;
86 // Some buttons can have their checked state set without being of type
87 // checkbox or radio. Expose the pressed state unconditionally.
89 xulButtonElement
->GetChecked(&checked
);
91 state
|= states::PRESSED
;
95 if (ContainsMenu()) state
|= states::HASPOPUP
;
97 if (mContent
->AsElement()->HasAttr(kNameSpaceID_None
, nsGkAtoms::_default
))
98 state
|= states::DEFAULT
;
103 ////////////////////////////////////////////////////////////////////////////////
104 // XULButtonAccessible: Widgets
106 bool XULButtonAccessible::IsWidget() const { return true; }
108 bool XULButtonAccessible::IsActiveWidget() const {
109 return FocusMgr()->HasDOMFocus(mContent
);
112 bool XULButtonAccessible::AreItemsOperable() const {
113 if (IsMenuButton()) {
114 Accessible
* menuPopup
= mChildren
.SafeElementAt(0, nullptr);
116 nsMenuPopupFrame
* menuPopupFrame
= do_QueryFrame(menuPopup
->GetFrame());
117 return menuPopupFrame
->IsOpen();
120 return false; // no items
123 Accessible
* XULButtonAccessible::ContainerWidget() const {
124 if (IsMenuButton() && mParent
&& mParent
->IsAutoComplete()) return mParent
;
128 bool XULButtonAccessible::IsAcceptableChild(nsIContent
* aEl
) const {
129 // In general XUL button has not accessible children. Nevertheless menu
130 // buttons can have popup accessibles (@type="menu" or columnpicker).
131 return aEl
->IsXULElement(nsGkAtoms::menupopup
) ||
132 aEl
->IsXULElement(nsGkAtoms::popup
);
135 ////////////////////////////////////////////////////////////////////////////////
136 // XULButtonAccessible protected
138 bool XULButtonAccessible::ContainsMenu() const {
139 return mContent
->AsElement()->AttrValueIs(kNameSpaceID_None
, nsGkAtoms::type
,
140 nsGkAtoms::menu
, eCaseMatters
);
143 ////////////////////////////////////////////////////////////////////////////////
144 // XULDropmarkerAccessible
145 ////////////////////////////////////////////////////////////////////////////////
147 XULDropmarkerAccessible::XULDropmarkerAccessible(nsIContent
* aContent
,
149 : LeafAccessible(aContent
, aDoc
) {}
151 uint8_t XULDropmarkerAccessible::ActionCount() const { return 1; }
153 bool XULDropmarkerAccessible::DropmarkerOpen(bool aToggleOpen
) const {
156 nsIContent
* parent
= mContent
->GetFlattenedTreeParent();
159 nsCOMPtr
<nsIDOMXULButtonElement
> parentButtonElement
=
160 parent
->AsElement()->AsXULButton();
161 if (parentButtonElement
) {
162 parentButtonElement
->GetOpen(&isOpen
);
163 if (aToggleOpen
) parentButtonElement
->SetOpen(!isOpen
);
167 nsCOMPtr
<nsIDOMXULMenuListElement
> parentMenuListElement
=
168 parent
->AsElement()->AsXULMenuList();
169 if (parentMenuListElement
) {
170 parentMenuListElement
->GetOpen(&isOpen
);
171 if (aToggleOpen
) parentMenuListElement
->SetOpen(!isOpen
);
174 parent
= parent
->GetFlattenedTreeParent();
180 void XULDropmarkerAccessible::ActionNameAt(uint8_t aIndex
, nsAString
& aName
) {
182 if (aIndex
== eAction_Click
) {
183 if (DropmarkerOpen(false))
184 aName
.AssignLiteral("close");
186 aName
.AssignLiteral("open");
190 bool XULDropmarkerAccessible::DoAction(uint8_t index
) const {
191 if (index
== eAction_Click
) {
192 DropmarkerOpen(true); // Reverse the open attribute
198 role
XULDropmarkerAccessible::NativeRole() const { return roles::PUSHBUTTON
; }
200 uint64_t XULDropmarkerAccessible::NativeState() const {
201 return DropmarkerOpen(false) ? states::PRESSED
: 0;
204 ////////////////////////////////////////////////////////////////////////////////
205 // XULGroupboxAccessible
206 ////////////////////////////////////////////////////////////////////////////////
208 XULGroupboxAccessible::XULGroupboxAccessible(nsIContent
* aContent
,
210 : AccessibleWrap(aContent
, aDoc
) {}
212 role
XULGroupboxAccessible::NativeRole() const { return roles::GROUPING
; }
214 ENameValueFlag
XULGroupboxAccessible::NativeName(nsString
& aName
) const {
215 // XXX: we use the first related accessible only.
216 Accessible
* label
= RelationByType(RelationType::LABELLED_BY
).Next();
217 if (label
) return label
->Name(aName
);
222 Relation
XULGroupboxAccessible::RelationByType(RelationType aType
) const {
223 Relation rel
= AccessibleWrap::RelationByType(aType
);
225 // The label for xul:groupbox is generated from the first xul:label
226 if (aType
== RelationType::LABELLED_BY
&& ChildCount() > 0) {
227 Accessible
* childAcc
= GetChildAt(0);
228 if (childAcc
->Role() == roles::LABEL
&&
229 childAcc
->GetContent()->IsXULElement(nsGkAtoms::label
)) {
230 rel
.AppendTarget(childAcc
);
237 ////////////////////////////////////////////////////////////////////////////////
238 // XULRadioButtonAccessible
239 ////////////////////////////////////////////////////////////////////////////////
241 XULRadioButtonAccessible::XULRadioButtonAccessible(nsIContent
* aContent
,
243 : RadioButtonAccessible(aContent
, aDoc
) {}
245 uint64_t XULRadioButtonAccessible::NativeState() const {
246 uint64_t state
= LeafAccessible::NativeState();
247 state
|= states::CHECKABLE
;
249 nsCOMPtr
<nsIDOMXULSelectControlItemElement
> radioButton
=
250 Elm()->AsXULSelectControlItem();
252 bool selected
= false; // Radio buttons can be selected
253 radioButton
->GetSelected(&selected
);
255 state
|= states::CHECKED
;
262 uint64_t XULRadioButtonAccessible::NativeInteractiveState() const {
263 return NativelyUnavailable() ? states::UNAVAILABLE
: states::FOCUSABLE
;
266 ////////////////////////////////////////////////////////////////////////////////
267 // XULRadioButtonAccessible: Widgets
269 Accessible
* XULRadioButtonAccessible::ContainerWidget() const {
273 ////////////////////////////////////////////////////////////////////////////////
274 // XULRadioGroupAccessible
275 ////////////////////////////////////////////////////////////////////////////////
279 * The Radio Group proxies for the Radio Buttons themselves. The Group gets
280 * focus whereas the Buttons do not. So we only have an accessible object for
281 * this for the purpose of getting the proper RadioButton. Need this here to
282 * avoid circular reference problems when navigating the accessible tree and
283 * for getting to the radiobuttons.
286 XULRadioGroupAccessible::XULRadioGroupAccessible(nsIContent
* aContent
,
288 : XULSelectControlAccessible(aContent
, aDoc
) {}
290 role
XULRadioGroupAccessible::NativeRole() const { return roles::RADIO_GROUP
; }
292 uint64_t XULRadioGroupAccessible::NativeInteractiveState() const {
293 // The radio group is not focusable. Sometimes the focus controller will
294 // report that it is focused. That means that the actual selected radio button
295 // should be considered focused.
296 return NativelyUnavailable() ? states::UNAVAILABLE
: 0;
299 ////////////////////////////////////////////////////////////////////////////////
300 // XULRadioGroupAccessible: Widgets
302 bool XULRadioGroupAccessible::IsWidget() const { return true; }
304 bool XULRadioGroupAccessible::IsActiveWidget() const {
305 return FocusMgr()->HasDOMFocus(mContent
);
308 bool XULRadioGroupAccessible::AreItemsOperable() const { return true; }
310 Accessible
* XULRadioGroupAccessible::CurrentItem() const {
311 if (!mSelectControl
) {
315 RefPtr
<Element
> currentItemElm
;
316 nsCOMPtr
<nsIDOMXULRadioGroupElement
> group
=
317 mSelectControl
->AsXULRadioGroup();
319 group
->GetFocusedItem(getter_AddRefs(currentItemElm
));
322 if (currentItemElm
) {
323 DocAccessible
* document
= Document();
325 return document
->GetAccessible(currentItemElm
);
332 void XULRadioGroupAccessible::SetCurrentItem(const Accessible
* aItem
) {
333 if (!mSelectControl
) {
337 nsCOMPtr
<Element
> itemElm
= aItem
->Elm();
338 nsCOMPtr
<nsIDOMXULRadioGroupElement
> group
=
339 mSelectControl
->AsXULRadioGroup();
341 group
->SetFocusedItem(itemElm
);
345 ////////////////////////////////////////////////////////////////////////////////
346 // XULStatusBarAccessible
347 ////////////////////////////////////////////////////////////////////////////////
349 XULStatusBarAccessible::XULStatusBarAccessible(nsIContent
* aContent
,
351 : AccessibleWrap(aContent
, aDoc
) {}
353 role
XULStatusBarAccessible::NativeRole() const { return roles::STATUSBAR
; }
355 ////////////////////////////////////////////////////////////////////////////////
356 // XULToolbarButtonAccessible
357 ////////////////////////////////////////////////////////////////////////////////
359 XULToolbarButtonAccessible::XULToolbarButtonAccessible(nsIContent
* aContent
,
361 : XULButtonAccessible(aContent
, aDoc
) {}
363 void XULToolbarButtonAccessible::GetPositionAndSizeInternal(int32_t* aPosInSet
,
366 int32_t posInSet
= 0;
368 Accessible
* parent
= Parent();
371 uint32_t childCount
= parent
->ChildCount();
372 for (uint32_t childIdx
= 0; childIdx
< childCount
; childIdx
++) {
373 Accessible
* child
= parent
->GetChildAt(childIdx
);
374 if (IsSeparator(child
)) { // end of a group of buttons
375 if (posInSet
) break; // we've found our group, so we're done
377 setSize
= 0; // not our group, so start a new group
380 setSize
++; // another button in the group
382 if (child
== this) posInSet
= setSize
; // we've found our button
386 *aPosInSet
= posInSet
;
390 bool XULToolbarButtonAccessible::IsSeparator(Accessible
* aAccessible
) {
391 nsIContent
* content
= aAccessible
->GetContent();
392 return content
&& content
->IsAnyOfXULElements(nsGkAtoms::toolbarseparator
,
393 nsGkAtoms::toolbarspacer
,
394 nsGkAtoms::toolbarspring
);
397 ////////////////////////////////////////////////////////////////////////////////
398 // XULToolbarButtonAccessible: Widgets
400 bool XULToolbarButtonAccessible::IsAcceptableChild(nsIContent
* aEl
) const {
401 // In general XUL button has not accessible children. Nevertheless menu
402 // buttons can have popup accessibles (@type="menu" or columnpicker).
403 // Also: Toolbar buttons can have labels as children.
404 // But only if the label attribute is not present.
405 return aEl
->IsXULElement(nsGkAtoms::menupopup
) ||
406 aEl
->IsXULElement(nsGkAtoms::popup
) ||
407 (aEl
->IsXULElement(nsGkAtoms::label
) &&
408 !mContent
->AsElement()->HasAttr(kNameSpaceID_None
, nsGkAtoms::label
));
411 ////////////////////////////////////////////////////////////////////////////////
412 // XULToolbarAccessible
413 ////////////////////////////////////////////////////////////////////////////////
415 XULToolbarAccessible::XULToolbarAccessible(nsIContent
* aContent
,
417 : AccessibleWrap(aContent
, aDoc
) {}
419 role
XULToolbarAccessible::NativeRole() const { return roles::TOOLBAR
; }
421 ENameValueFlag
XULToolbarAccessible::NativeName(nsString
& aName
) const {
422 if (mContent
->AsElement()->GetAttr(kNameSpaceID_None
, nsGkAtoms::toolbarname
,
424 aName
.CompressWhitespace();
429 ////////////////////////////////////////////////////////////////////////////////
430 // XULToolbarAccessible
431 ////////////////////////////////////////////////////////////////////////////////
433 XULToolbarSeparatorAccessible::XULToolbarSeparatorAccessible(
434 nsIContent
* aContent
, DocAccessible
* aDoc
)
435 : LeafAccessible(aContent
, aDoc
) {}
437 role
XULToolbarSeparatorAccessible::NativeRole() const {
438 return roles::SEPARATOR
;
441 uint64_t XULToolbarSeparatorAccessible::NativeState() const { return 0; }