Backed out changeset 3152110c63b5 (bug 1868374) as requested because it is not yet...
[gecko.git] / accessible / xul / XULFormControlAccessible.cpp
blob4336a80234024577992b87d23fc7689c803b5717
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 "LocalAccessible-inl.h"
9 #include "HTMLFormControlAccessible.h"
10 #include "nsAccUtils.h"
11 #include "DocAccessible.h"
12 #include "Relation.h"
13 #include "mozilla/a11y/Role.h"
14 #include "States.h"
15 #include "TreeWalker.h"
16 #include "XULMenuAccessible.h"
18 #include "nsIDOMXULButtonElement.h"
19 #include "nsIDOMXULMenuListElement.h"
20 #include "nsIDOMXULRadioGroupElement.h"
21 #include "nsIDOMXULSelectCntrlItemEl.h"
22 #include "nsIFrame.h"
23 #include "nsITextControlFrame.h"
24 #include "nsMenuPopupFrame.h"
25 #include "nsNameSpaceManager.h"
26 #include "mozilla/dom/Element.h"
28 using namespace mozilla::a11y;
30 ////////////////////////////////////////////////////////////////////////////////
31 // XULButtonAccessible
32 ////////////////////////////////////////////////////////////////////////////////
34 XULButtonAccessible::XULButtonAccessible(nsIContent* aContent,
35 DocAccessible* aDoc)
36 : AccessibleWrap(aContent, aDoc) {
37 if (ContainsMenu()) {
38 mGenericTypes |= eMenuButton;
39 } else {
40 mGenericTypes |= eButton;
44 XULButtonAccessible::~XULButtonAccessible() {}
46 ////////////////////////////////////////////////////////////////////////////////
47 // XULButtonAccessible: nsISupports
49 ////////////////////////////////////////////////////////////////////////////////
50 // XULButtonAccessible: nsIAccessible
52 bool XULButtonAccessible::HasPrimaryAction() const { return true; }
54 void XULButtonAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName) {
55 if (aIndex == eAction_Click) aName.AssignLiteral("press");
58 ////////////////////////////////////////////////////////////////////////////////
59 // XULButtonAccessible: LocalAccessible
61 role XULButtonAccessible::NativeRole() const {
62 // Buttons can be checked; they simply appear pressed in rather than checked.
63 // In this case, we must expose them as toggle buttons.
64 nsCOMPtr<nsIDOMXULButtonElement> xulButtonElement = Elm()->AsXULButton();
65 if (xulButtonElement) {
66 nsAutoString type;
67 xulButtonElement->GetType(type);
68 if (type.EqualsLiteral("checkbox") || type.EqualsLiteral("radio")) {
69 return roles::TOGGLE_BUTTON;
72 return roles::PUSHBUTTON;
75 uint64_t XULButtonAccessible::NativeState() const {
76 // Possible states: focused, focusable, unavailable(disabled).
78 // get focus and disable status from base class
79 uint64_t state = LocalAccessible::NativeState();
81 nsCOMPtr<nsIDOMXULButtonElement> xulButtonElement = Elm()->AsXULButton();
82 if (xulButtonElement) {
83 // Some buttons can have their checked state set without being of type
84 // checkbox or radio. Expose the pressed state unconditionally.
85 bool checked = false;
86 xulButtonElement->GetChecked(&checked);
87 if (checked) {
88 state |= states::PRESSED;
92 if (ContainsMenu()) state |= states::HASPOPUP;
94 if (mContent->AsElement()->HasAttr(nsGkAtoms::_default)) {
95 state |= states::DEFAULT;
98 return state;
101 bool XULButtonAccessible::AttributeChangesState(nsAtom* aAttribute) {
102 if (aAttribute == nsGkAtoms::checked) {
103 return true;
105 return AccessibleWrap::AttributeChangesState(aAttribute);
108 ////////////////////////////////////////////////////////////////////////////////
109 // XULButtonAccessible: Widgets
111 bool XULButtonAccessible::IsWidget() const { return true; }
113 bool XULButtonAccessible::IsActiveWidget() const {
114 return FocusMgr()->HasDOMFocus(mContent);
117 bool XULButtonAccessible::AreItemsOperable() const {
118 if (IsMenuButton()) {
119 LocalAccessible* menuPopup = mChildren.SafeElementAt(0, nullptr);
120 if (menuPopup) {
121 nsMenuPopupFrame* menuPopupFrame = do_QueryFrame(menuPopup->GetFrame());
122 return menuPopupFrame->IsOpen();
125 return false; // no items
128 bool XULButtonAccessible::IsAcceptableChild(nsIContent* aEl) const {
129 // In general XUL buttons should not have accessible children. However:
130 return
131 // menu buttons can have popup accessibles (@type="menu" or
132 // columnpicker).
133 aEl->IsXULElement(nsGkAtoms::menupopup) ||
134 aEl->IsXULElement(nsGkAtoms::popup) ||
135 // A XUL button can be labelled by a direct child text node, so we need to
136 // allow that as a child so it will be picked up when computing name from
137 // subtree.
138 (aEl->IsText() && aEl->GetParent() == mContent);
141 ////////////////////////////////////////////////////////////////////////////////
142 // XULButtonAccessible protected
144 bool XULButtonAccessible::ContainsMenu() const {
145 return mContent->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
146 nsGkAtoms::menu, eCaseMatters);
149 ////////////////////////////////////////////////////////////////////////////////
150 // XULDropmarkerAccessible
151 ////////////////////////////////////////////////////////////////////////////////
153 XULDropmarkerAccessible::XULDropmarkerAccessible(nsIContent* aContent,
154 DocAccessible* aDoc)
155 : LeafAccessible(aContent, aDoc) {}
157 bool XULDropmarkerAccessible::HasPrimaryAction() const { return true; }
159 bool XULDropmarkerAccessible::DropmarkerOpen(bool aToggleOpen) const {
160 bool isOpen = false;
162 nsIContent* parent = mContent->GetFlattenedTreeParent();
164 while (parent) {
165 nsCOMPtr<nsIDOMXULButtonElement> parentButtonElement =
166 parent->AsElement()->AsXULButton();
167 if (parentButtonElement) {
168 parentButtonElement->GetOpen(&isOpen);
169 if (aToggleOpen) parentButtonElement->SetOpen(!isOpen);
170 return isOpen;
173 nsCOMPtr<nsIDOMXULMenuListElement> parentMenuListElement =
174 parent->AsElement()->AsXULMenuList();
175 if (parentMenuListElement) {
176 parentMenuListElement->GetOpen(&isOpen);
177 if (aToggleOpen) parentMenuListElement->SetOpen(!isOpen);
178 return isOpen;
180 parent = parent->GetFlattenedTreeParent();
183 return isOpen;
186 void XULDropmarkerAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName) {
187 aName.Truncate();
188 if (aIndex == eAction_Click) {
189 if (DropmarkerOpen(false)) {
190 aName.AssignLiteral("close");
191 } else {
192 aName.AssignLiteral("open");
197 bool XULDropmarkerAccessible::DoAction(uint8_t index) const {
198 if (index == eAction_Click) {
199 DropmarkerOpen(true); // Reverse the open attribute
200 return true;
202 return false;
205 role XULDropmarkerAccessible::NativeRole() const { return roles::PUSHBUTTON; }
207 uint64_t XULDropmarkerAccessible::NativeState() const {
208 return DropmarkerOpen(false) ? states::PRESSED : 0;
211 ////////////////////////////////////////////////////////////////////////////////
212 // XULGroupboxAccessible
213 ////////////////////////////////////////////////////////////////////////////////
215 XULGroupboxAccessible::XULGroupboxAccessible(nsIContent* aContent,
216 DocAccessible* aDoc)
217 : AccessibleWrap(aContent, aDoc) {}
219 role XULGroupboxAccessible::NativeRole() const { return roles::GROUPING; }
221 ENameValueFlag XULGroupboxAccessible::NativeName(nsString& aName) const {
222 // XXX: we use the first related accessible only.
223 LocalAccessible* label =
224 RelationByType(RelationType::LABELLED_BY).LocalNext();
225 if (label) return label->Name(aName);
227 return eNameOK;
230 Relation XULGroupboxAccessible::RelationByType(RelationType aType) const {
231 Relation rel = AccessibleWrap::RelationByType(aType);
233 // The label for xul:groupbox is generated from the first xul:label
234 if (aType == RelationType::LABELLED_BY && ChildCount() > 0) {
235 LocalAccessible* childAcc = LocalChildAt(0);
236 if (childAcc->Role() == roles::LABEL &&
237 childAcc->GetContent()->IsXULElement(nsGkAtoms::label)) {
238 rel.AppendTarget(childAcc);
242 return rel;
245 ////////////////////////////////////////////////////////////////////////////////
246 // XULRadioButtonAccessible
247 ////////////////////////////////////////////////////////////////////////////////
249 XULRadioButtonAccessible::XULRadioButtonAccessible(nsIContent* aContent,
250 DocAccessible* aDoc)
251 : RadioButtonAccessible(aContent, aDoc) {}
253 uint64_t XULRadioButtonAccessible::NativeState() const {
254 uint64_t state = LeafAccessible::NativeState();
255 state |= states::CHECKABLE;
257 nsCOMPtr<nsIDOMXULSelectControlItemElement> radioButton =
258 Elm()->AsXULSelectControlItem();
259 if (radioButton) {
260 bool selected = false; // Radio buttons can be selected
261 radioButton->GetSelected(&selected);
262 if (selected) {
263 state |= states::CHECKED;
267 return state;
270 uint64_t XULRadioButtonAccessible::NativeInteractiveState() const {
271 return NativelyUnavailable() ? states::UNAVAILABLE : states::FOCUSABLE;
274 ////////////////////////////////////////////////////////////////////////////////
275 // XULRadioButtonAccessible: Widgets
277 LocalAccessible* XULRadioButtonAccessible::ContainerWidget() const {
278 return mParent;
281 ////////////////////////////////////////////////////////////////////////////////
282 // XULRadioGroupAccessible
283 ////////////////////////////////////////////////////////////////////////////////
286 * XUL Radio Group
287 * The Radio Group proxies for the Radio Buttons themselves. The Group gets
288 * focus whereas the Buttons do not. So we only have an accessible object for
289 * this for the purpose of getting the proper RadioButton. Need this here to
290 * avoid circular reference problems when navigating the accessible tree and
291 * for getting to the radiobuttons.
294 XULRadioGroupAccessible::XULRadioGroupAccessible(nsIContent* aContent,
295 DocAccessible* aDoc)
296 : XULSelectControlAccessible(aContent, aDoc) {}
298 role XULRadioGroupAccessible::NativeRole() const { return roles::RADIO_GROUP; }
300 uint64_t XULRadioGroupAccessible::NativeInteractiveState() const {
301 // The radio group is not focusable. Sometimes the focus controller will
302 // report that it is focused. That means that the actual selected radio button
303 // should be considered focused.
304 return NativelyUnavailable() ? states::UNAVAILABLE : 0;
307 ////////////////////////////////////////////////////////////////////////////////
308 // XULRadioGroupAccessible: Widgets
310 bool XULRadioGroupAccessible::IsWidget() const { return true; }
312 bool XULRadioGroupAccessible::IsActiveWidget() const {
313 return FocusMgr()->HasDOMFocus(mContent);
316 bool XULRadioGroupAccessible::AreItemsOperable() const { return true; }
318 LocalAccessible* XULRadioGroupAccessible::CurrentItem() const {
319 if (!mSelectControl) {
320 return nullptr;
323 RefPtr<dom::Element> currentItemElm;
324 nsCOMPtr<nsIDOMXULRadioGroupElement> group =
325 mSelectControl->AsXULRadioGroup();
326 if (group) {
327 group->GetFocusedItem(getter_AddRefs(currentItemElm));
330 if (currentItemElm) {
331 DocAccessible* document = Document();
332 if (document) {
333 return document->GetAccessible(currentItemElm);
337 return nullptr;
340 void XULRadioGroupAccessible::SetCurrentItem(const LocalAccessible* aItem) {
341 if (!mSelectControl) {
342 return;
345 nsCOMPtr<dom::Element> itemElm = aItem->Elm();
346 nsCOMPtr<nsIDOMXULRadioGroupElement> group =
347 mSelectControl->AsXULRadioGroup();
348 if (group) {
349 group->SetFocusedItem(itemElm);
353 ////////////////////////////////////////////////////////////////////////////////
354 // XULStatusBarAccessible
355 ////////////////////////////////////////////////////////////////////////////////
357 XULStatusBarAccessible::XULStatusBarAccessible(nsIContent* aContent,
358 DocAccessible* aDoc)
359 : AccessibleWrap(aContent, aDoc) {}
361 role XULStatusBarAccessible::NativeRole() const { return roles::STATUSBAR; }
363 ////////////////////////////////////////////////////////////////////////////////
364 // XULToolbarButtonAccessible
365 ////////////////////////////////////////////////////////////////////////////////
367 XULToolbarButtonAccessible::XULToolbarButtonAccessible(nsIContent* aContent,
368 DocAccessible* aDoc)
369 : XULButtonAccessible(aContent, aDoc) {}
371 void XULToolbarButtonAccessible::GetPositionAndSetSize(int32_t* aPosInSet,
372 int32_t* aSetSize) {
373 int32_t setSize = 0;
374 int32_t posInSet = 0;
376 LocalAccessible* parent = LocalParent();
377 if (!parent) return;
379 uint32_t childCount = parent->ChildCount();
380 for (uint32_t childIdx = 0; childIdx < childCount; childIdx++) {
381 LocalAccessible* child = parent->LocalChildAt(childIdx);
382 if (IsSeparator(child)) { // end of a group of buttons
383 if (posInSet) break; // we've found our group, so we're done
385 setSize = 0; // not our group, so start a new group
387 } else {
388 setSize++; // another button in the group
390 if (child == this) posInSet = setSize; // we've found our button
394 *aPosInSet = posInSet;
395 *aSetSize = setSize;
398 bool XULToolbarButtonAccessible::IsSeparator(LocalAccessible* aAccessible) {
399 nsIContent* content = aAccessible->GetContent();
400 return content && content->IsAnyOfXULElements(nsGkAtoms::toolbarseparator,
401 nsGkAtoms::toolbarspacer,
402 nsGkAtoms::toolbarspring);
405 ////////////////////////////////////////////////////////////////////////////////
406 // XULToolbarButtonAccessible: Widgets
408 bool XULToolbarButtonAccessible::IsAcceptableChild(nsIContent* aEl) const {
409 return XULButtonAccessible::IsAcceptableChild(aEl) ||
410 // In addition to the children allowed by buttons, toolbarbuttons can
411 // have labels as children, but only if the label attribute is not
412 // present.
413 (aEl->IsXULElement(nsGkAtoms::label) &&
414 !mContent->AsElement()->HasAttr(nsGkAtoms::label));
417 ////////////////////////////////////////////////////////////////////////////////
418 // XULToolbarAccessible
419 ////////////////////////////////////////////////////////////////////////////////
421 XULToolbarAccessible::XULToolbarAccessible(nsIContent* aContent,
422 DocAccessible* aDoc)
423 : AccessibleWrap(aContent, aDoc) {}
425 role XULToolbarAccessible::NativeRole() const { return roles::TOOLBAR; }
427 ENameValueFlag XULToolbarAccessible::NativeName(nsString& aName) const {
428 if (mContent->AsElement()->GetAttr(nsGkAtoms::toolbarname, aName)) {
429 aName.CompressWhitespace();
432 return eNameOK;
435 ////////////////////////////////////////////////////////////////////////////////
436 // XULToolbarAccessible
437 ////////////////////////////////////////////////////////////////////////////////
439 XULToolbarSeparatorAccessible::XULToolbarSeparatorAccessible(
440 nsIContent* aContent, DocAccessible* aDoc)
441 : LeafAccessible(aContent, aDoc) {}
443 role XULToolbarSeparatorAccessible::NativeRole() const {
444 return roles::SEPARATOR;
447 uint64_t XULToolbarSeparatorAccessible::NativeState() const { return 0; }