1 /* -*- Mode: C++; tab-width: 4; 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 "XULComboboxAccessible.h"
7 
8 #include "LocalAccessible-inl.h"
9 #include "nsAccessibilityService.h"
10 #include "DocAccessible.h"
11 #include "nsCoreUtils.h"
12 #include "Role.h"
13 #include "States.h"
14 
15 #include "mozilla/dom/Element.h"
16 #include "nsIDOMXULMenuListElement.h"
17 
18 using namespace mozilla::a11y;
19 
20 ////////////////////////////////////////////////////////////////////////////////
21 // XULComboboxAccessible
22 ////////////////////////////////////////////////////////////////////////////////
23 
XULComboboxAccessible(nsIContent * aContent,DocAccessible * aDoc)24 XULComboboxAccessible::XULComboboxAccessible(nsIContent* aContent,
25                                              DocAccessible* aDoc)
26     : AccessibleWrap(aContent, aDoc) {
27   mGenericTypes |= eCombobox;
28 }
29 
NativeRole() const30 role XULComboboxAccessible::NativeRole() const { return roles::COMBOBOX; }
31 
NativeState() const32 uint64_t XULComboboxAccessible::NativeState() const {
33   // As a nsComboboxAccessible we can have the following states:
34   //     STATE_FOCUSED
35   //     STATE_FOCUSABLE
36   //     STATE_HASPOPUP
37   //     STATE_EXPANDED
38   //     STATE_COLLAPSED
39 
40   // Get focus status from base class
41   uint64_t state = LocalAccessible::NativeState();
42 
43   nsCOMPtr<nsIDOMXULMenuListElement> menuList = Elm()->AsXULMenuList();
44   if (menuList) {
45     bool isOpen = false;
46     menuList->GetOpen(&isOpen);
47     if (isOpen) {
48       state |= states::EXPANDED;
49     } else {
50       state |= states::COLLAPSED;
51     }
52   }
53 
54   return state | states::HASPOPUP;
55 }
56 
Description(nsString & aDescription) const57 void XULComboboxAccessible::Description(nsString& aDescription) const {
58   aDescription.Truncate();
59   // Use description of currently focused option
60   nsCOMPtr<nsIDOMXULMenuListElement> menuListElm = Elm()->AsXULMenuList();
61   if (!menuListElm) return;
62 
63   nsCOMPtr<dom::Element> focusedOptionItem;
64   menuListElm->GetSelectedItem(getter_AddRefs(focusedOptionItem));
65   if (focusedOptionItem && mDoc) {
66     LocalAccessible* focusedOptionAcc = mDoc->GetAccessible(focusedOptionItem);
67     if (focusedOptionAcc) focusedOptionAcc->Description(aDescription);
68   }
69 }
70 
Value(nsString & aValue) const71 void XULComboboxAccessible::Value(nsString& aValue) const {
72   aValue.Truncate();
73 
74   // The value is the option or text shown entered in the combobox.
75   nsCOMPtr<nsIDOMXULMenuListElement> menuList = Elm()->AsXULMenuList();
76   if (menuList) menuList->GetLabel(aValue);
77 }
78 
HasPrimaryAction() const79 bool XULComboboxAccessible::HasPrimaryAction() const { return true; }
80 
DoAction(uint8_t aIndex) const81 bool XULComboboxAccessible::DoAction(uint8_t aIndex) const {
82   if (aIndex != XULComboboxAccessible::eAction_Click) return false;
83 
84   // Programmaticaly toggle the combo box.
85   nsCOMPtr<nsIDOMXULMenuListElement> menuList = Elm()->AsXULMenuList();
86   if (!menuList) return false;
87 
88   bool isDroppedDown = false;
89   menuList->GetOpen(&isDroppedDown);
90   menuList->SetOpen(!isDroppedDown);
91   return true;
92 }
93 
ActionNameAt(uint8_t aIndex,nsAString & aName)94 void XULComboboxAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName) {
95   aName.Truncate();
96   if (aIndex != XULComboboxAccessible::eAction_Click) return;
97 
98   nsCOMPtr<nsIDOMXULMenuListElement> menuList = Elm()->AsXULMenuList();
99   if (!menuList) return;
100 
101   bool isDroppedDown = false;
102   menuList->GetOpen(&isDroppedDown);
103   if (isDroppedDown) {
104     aName.AssignLiteral("close");
105   } else {
106     aName.AssignLiteral("open");
107   }
108 }
109 
110 ////////////////////////////////////////////////////////////////////////////////
111 // Widgets
112 
IsActiveWidget() const113 bool XULComboboxAccessible::IsActiveWidget() const {
114   if (mContent->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::editable,
115                                          nsGkAtoms::_true, eIgnoreCase)) {
116     int32_t childCount = mChildren.Length();
117     for (int32_t idx = 0; idx < childCount; idx++) {
118       LocalAccessible* child = mChildren[idx];
119       if (child->Role() == roles::ENTRY) {
120         return FocusMgr()->HasDOMFocus(child->GetContent());
121       }
122     }
123     return false;
124   }
125 
126   return FocusMgr()->HasDOMFocus(mContent);
127 }
128 
AreItemsOperable() const129 bool XULComboboxAccessible::AreItemsOperable() const {
130   nsCOMPtr<nsIDOMXULMenuListElement> menuListElm = Elm()->AsXULMenuList();
131   if (menuListElm) {
132     bool isOpen = false;
133     menuListElm->GetOpen(&isOpen);
134     return isOpen;
135   }
136 
137   return false;
138 }
139