Bug 1554951 [wpt PR 17043] - wake-lock: Fix invalid types test in wakelock-type.https...
[gecko.git] / accessible / xul / XULFormControlAccessible.cpp
blob1180aca53cc6eaadf43b177910acdfd8566e5fd4
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"
13 #include "Relation.h"
14 #include "Role.h"
15 #include "States.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"
24 #include "nsIFrame.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,
37 DocAccessible* aDoc)
38 : AccessibleWrap(aContent, aDoc) {
39 if (ContainsMenu()) {
40 mGenericTypes |= eMenuButton;
41 } else {
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;
63 DoCommand();
64 return true;
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) {
81 nsAutoString type;
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.
88 bool checked = false;
89 xulButtonElement->GetChecked(&checked);
90 if (checked) {
91 state |= states::PRESSED;
95 if (ContainsMenu()) state |= states::HASPOPUP;
97 if (mContent->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::_default))
98 state |= states::DEFAULT;
100 return state;
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);
115 if (menuPopup) {
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;
125 return nullptr;
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,
148 DocAccessible* aDoc)
149 : LeafAccessible(aContent, aDoc) {}
151 uint8_t XULDropmarkerAccessible::ActionCount() const { return 1; }
153 bool XULDropmarkerAccessible::DropmarkerOpen(bool aToggleOpen) const {
154 bool isOpen = false;
156 nsIContent* parent = mContent->GetFlattenedTreeParent();
158 while (parent) {
159 nsCOMPtr<nsIDOMXULButtonElement> parentButtonElement =
160 parent->AsElement()->AsXULButton();
161 if (parentButtonElement) {
162 parentButtonElement->GetOpen(&isOpen);
163 if (aToggleOpen) parentButtonElement->SetOpen(!isOpen);
164 return isOpen;
167 nsCOMPtr<nsIDOMXULMenuListElement> parentMenuListElement =
168 parent->AsElement()->AsXULMenuList();
169 if (parentMenuListElement) {
170 parentMenuListElement->GetOpen(&isOpen);
171 if (aToggleOpen) parentMenuListElement->SetOpen(!isOpen);
172 return isOpen;
174 parent = parent->GetFlattenedTreeParent();
177 return isOpen;
180 void XULDropmarkerAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName) {
181 aName.Truncate();
182 if (aIndex == eAction_Click) {
183 if (DropmarkerOpen(false))
184 aName.AssignLiteral("close");
185 else
186 aName.AssignLiteral("open");
190 bool XULDropmarkerAccessible::DoAction(uint8_t index) const {
191 if (index == eAction_Click) {
192 DropmarkerOpen(true); // Reverse the open attribute
193 return true;
195 return false;
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,
209 DocAccessible* aDoc)
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);
219 return eNameOK;
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);
234 return rel;
237 ////////////////////////////////////////////////////////////////////////////////
238 // XULRadioButtonAccessible
239 ////////////////////////////////////////////////////////////////////////////////
241 XULRadioButtonAccessible::XULRadioButtonAccessible(nsIContent* aContent,
242 DocAccessible* aDoc)
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();
251 if (radioButton) {
252 bool selected = false; // Radio buttons can be selected
253 radioButton->GetSelected(&selected);
254 if (selected) {
255 state |= states::CHECKED;
259 return state;
262 uint64_t XULRadioButtonAccessible::NativeInteractiveState() const {
263 return NativelyUnavailable() ? states::UNAVAILABLE : states::FOCUSABLE;
266 ////////////////////////////////////////////////////////////////////////////////
267 // XULRadioButtonAccessible: Widgets
269 Accessible* XULRadioButtonAccessible::ContainerWidget() const {
270 return mParent;
273 ////////////////////////////////////////////////////////////////////////////////
274 // XULRadioGroupAccessible
275 ////////////////////////////////////////////////////////////////////////////////
278 * XUL Radio Group
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,
287 DocAccessible* aDoc)
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) {
312 return nullptr;
315 RefPtr<Element> currentItemElm;
316 nsCOMPtr<nsIDOMXULRadioGroupElement> group =
317 mSelectControl->AsXULRadioGroup();
318 if (group) {
319 group->GetFocusedItem(getter_AddRefs(currentItemElm));
322 if (currentItemElm) {
323 DocAccessible* document = Document();
324 if (document) {
325 return document->GetAccessible(currentItemElm);
329 return nullptr;
332 void XULRadioGroupAccessible::SetCurrentItem(const Accessible* aItem) {
333 if (!mSelectControl) {
334 return;
337 nsCOMPtr<Element> itemElm = aItem->Elm();
338 nsCOMPtr<nsIDOMXULRadioGroupElement> group =
339 mSelectControl->AsXULRadioGroup();
340 if (group) {
341 group->SetFocusedItem(itemElm);
345 ////////////////////////////////////////////////////////////////////////////////
346 // XULStatusBarAccessible
347 ////////////////////////////////////////////////////////////////////////////////
349 XULStatusBarAccessible::XULStatusBarAccessible(nsIContent* aContent,
350 DocAccessible* aDoc)
351 : AccessibleWrap(aContent, aDoc) {}
353 role XULStatusBarAccessible::NativeRole() const { return roles::STATUSBAR; }
355 ////////////////////////////////////////////////////////////////////////////////
356 // XULToolbarButtonAccessible
357 ////////////////////////////////////////////////////////////////////////////////
359 XULToolbarButtonAccessible::XULToolbarButtonAccessible(nsIContent* aContent,
360 DocAccessible* aDoc)
361 : XULButtonAccessible(aContent, aDoc) {}
363 void XULToolbarButtonAccessible::GetPositionAndSizeInternal(int32_t* aPosInSet,
364 int32_t* aSetSize) {
365 int32_t setSize = 0;
366 int32_t posInSet = 0;
368 Accessible* parent = Parent();
369 if (!parent) return;
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
379 } else {
380 setSize++; // another button in the group
382 if (child == this) posInSet = setSize; // we've found our button
386 *aPosInSet = posInSet;
387 *aSetSize = setSize;
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,
416 DocAccessible* aDoc)
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,
423 aName))
424 aName.CompressWhitespace();
426 return eNameOK;
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; }