1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is mozilla.org code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Netscape Communications Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 2009
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Aaron Leventhal <aaronl@netscape.com> (original author)
24 : * Kyle Yuan <kyle.yuan@sun.com>
25 : * Alexander Surkov <surkov.alexander@gmail.com>
26 : *
27 : * Alternatively, the contents of this file may be used under the terms of
28 : * either of the GNU General Public License Version 2 or later (the "GPL"),
29 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 : * in which case the provisions of the GPL or the LGPL are applicable instead
31 : * of those above. If you wish to allow use of your version of this file only
32 : * under the terms of either the GPL or the LGPL, and not to allow others to
33 : * use your version of this file under the terms of the MPL, indicate your
34 : * decision by deleting the provisions above and replace them with the notice
35 : * and other provisions required by the GPL or the LGPL. If you do not delete
36 : * the provisions above, a recipient may use your version of this file under
37 : * the terms of any one of the MPL, the GPL or the LGPL.
38 : *
39 : * ***** END LICENSE BLOCK ***** */
40 :
41 : #include "nsXULComboboxAccessible.h"
42 :
43 : #include "nsAccessibilityService.h"
44 : #include "nsDocAccessible.h"
45 : #include "nsCoreUtils.h"
46 : #include "Role.h"
47 : #include "States.h"
48 :
49 : #include "nsIAutoCompleteInput.h"
50 : #include "nsIDOMXULMenuListElement.h"
51 : #include "nsIDOMXULSelectCntrlItemEl.h"
52 :
53 : using namespace mozilla::a11y;
54 :
55 : ////////////////////////////////////////////////////////////////////////////////
56 : // nsXULComboboxAccessible
57 : ////////////////////////////////////////////////////////////////////////////////
58 :
59 0 : nsXULComboboxAccessible::
60 : nsXULComboboxAccessible(nsIContent* aContent, nsDocAccessible* aDoc) :
61 0 : nsAccessibleWrap(aContent, aDoc)
62 : {
63 0 : if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
64 0 : nsGkAtoms::autocomplete, eIgnoreCase))
65 0 : mFlags |= eAutoCompleteAccessible;
66 : else
67 0 : mFlags |= eComboboxAccessible;
68 0 : }
69 :
70 : role
71 0 : nsXULComboboxAccessible::NativeRole()
72 : {
73 0 : return IsAutoComplete() ? roles::AUTOCOMPLETE : roles::COMBOBOX;
74 : }
75 :
76 : PRUint64
77 0 : nsXULComboboxAccessible::NativeState()
78 : {
79 : // As a nsComboboxAccessible we can have the following states:
80 : // STATE_FOCUSED
81 : // STATE_FOCUSABLE
82 : // STATE_HASPOPUP
83 : // STATE_EXPANDED
84 : // STATE_COLLAPSED
85 :
86 : // Get focus status from base class
87 0 : PRUint64 states = nsAccessible::NativeState();
88 :
89 0 : nsCOMPtr<nsIDOMXULMenuListElement> menuList(do_QueryInterface(mContent));
90 0 : if (menuList) {
91 : bool isOpen;
92 0 : menuList->GetOpen(&isOpen);
93 0 : if (isOpen) {
94 0 : states |= states::EXPANDED;
95 : }
96 : else {
97 0 : states |= states::COLLAPSED;
98 : }
99 : }
100 :
101 0 : states |= states::HASPOPUP | states::FOCUSABLE;
102 :
103 0 : return states;
104 : }
105 :
106 : NS_IMETHODIMP
107 0 : nsXULComboboxAccessible::GetValue(nsAString& aValue)
108 : {
109 0 : aValue.Truncate();
110 :
111 0 : if (IsDefunct())
112 0 : return NS_ERROR_FAILURE;
113 :
114 : // The value is the option or text shown entered in the combobox.
115 0 : nsCOMPtr<nsIDOMXULMenuListElement> menuList(do_QueryInterface(mContent));
116 0 : if (menuList)
117 0 : return menuList->GetLabel(aValue);
118 :
119 0 : return NS_ERROR_FAILURE;
120 : }
121 :
122 : void
123 0 : nsXULComboboxAccessible::Description(nsString& aDescription)
124 : {
125 0 : aDescription.Truncate();
126 : // Use description of currently focused option
127 0 : nsCOMPtr<nsIDOMXULMenuListElement> menuListElm(do_QueryInterface(mContent));
128 0 : if (!menuListElm)
129 : return;
130 :
131 0 : nsCOMPtr<nsIDOMXULSelectControlItemElement> focusedOptionItem;
132 0 : menuListElm->GetSelectedItem(getter_AddRefs(focusedOptionItem));
133 : nsCOMPtr<nsIContent> focusedOptionContent =
134 0 : do_QueryInterface(focusedOptionItem);
135 0 : if (focusedOptionContent && mDoc) {
136 0 : nsAccessible* focusedOptionAcc = mDoc->GetAccessible(focusedOptionContent);
137 0 : if (focusedOptionAcc)
138 0 : focusedOptionAcc->Description(aDescription);
139 : }
140 : }
141 :
142 : bool
143 0 : nsXULComboboxAccessible::CanHaveAnonChildren()
144 : {
145 0 : if (mContent->NodeInfo()->Equals(nsGkAtoms::textbox, kNameSpaceID_XUL) ||
146 0 : mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::editable,
147 0 : nsGkAtoms::_true, eIgnoreCase)) {
148 : // Both the XUL <textbox type="autocomplete"> and <menulist editable="true"> widgets
149 : // use nsXULComboboxAccessible. We need to walk the anonymous children for these
150 : // so that the entry field is a child
151 0 : return true;
152 : }
153 :
154 : // Argument of false indicates we don't walk anonymous children for
155 : // menuitems
156 0 : return false;
157 : }
158 : PRUint8
159 0 : nsXULComboboxAccessible::ActionCount()
160 : {
161 : // Just one action (click).
162 0 : return 1;
163 : }
164 :
165 : NS_IMETHODIMP
166 0 : nsXULComboboxAccessible::DoAction(PRUint8 aIndex)
167 : {
168 0 : if (aIndex != nsXULComboboxAccessible::eAction_Click) {
169 0 : return NS_ERROR_INVALID_ARG;
170 : }
171 :
172 0 : if (IsDefunct())
173 0 : return NS_ERROR_FAILURE;
174 :
175 : // Programmaticaly toggle the combo box.
176 0 : nsCOMPtr<nsIDOMXULMenuListElement> menuList(do_QueryInterface(mContent));
177 0 : if (!menuList) {
178 0 : return NS_ERROR_FAILURE;
179 : }
180 : bool isDroppedDown;
181 0 : menuList->GetOpen(&isDroppedDown);
182 0 : return menuList->SetOpen(!isDroppedDown);
183 : }
184 :
185 : NS_IMETHODIMP
186 0 : nsXULComboboxAccessible::GetActionName(PRUint8 aIndex, nsAString& aName)
187 : {
188 0 : if (aIndex != nsXULComboboxAccessible::eAction_Click) {
189 0 : return NS_ERROR_INVALID_ARG;
190 : }
191 :
192 0 : if (IsDefunct())
193 0 : return NS_ERROR_FAILURE;
194 :
195 : // Our action name is the reverse of our state:
196 : // if we are close -> open is our name.
197 : // if we are open -> close is our name.
198 : // Uses the frame to get the state, updated on every click.
199 :
200 0 : nsCOMPtr<nsIDOMXULMenuListElement> menuList(do_QueryInterface(mContent));
201 0 : if (!menuList) {
202 0 : return NS_ERROR_FAILURE;
203 : }
204 : bool isDroppedDown;
205 0 : menuList->GetOpen(&isDroppedDown);
206 0 : if (isDroppedDown)
207 0 : aName.AssignLiteral("close");
208 : else
209 0 : aName.AssignLiteral("open");
210 :
211 0 : return NS_OK;
212 : }
213 :
214 : ////////////////////////////////////////////////////////////////////////////////
215 : // Widgets
216 :
217 : bool
218 0 : nsXULComboboxAccessible::IsActiveWidget() const
219 : {
220 0 : if (IsAutoComplete() ||
221 0 : mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::editable,
222 0 : nsGkAtoms::_true, eIgnoreCase)) {
223 0 : PRInt32 childCount = mChildren.Length();
224 0 : for (PRInt32 idx = 0; idx < childCount; idx++) {
225 0 : nsAccessible* child = mChildren[idx];
226 0 : if (child->Role() == roles::ENTRY)
227 0 : return FocusMgr()->HasDOMFocus(child->GetContent());
228 : }
229 0 : return false;
230 : }
231 :
232 0 : return FocusMgr()->HasDOMFocus(mContent);
233 : }
234 :
235 : bool
236 0 : nsXULComboboxAccessible::AreItemsOperable() const
237 : {
238 0 : if (IsAutoComplete()) {
239 : nsCOMPtr<nsIAutoCompleteInput> autoCompleteInputElm =
240 0 : do_QueryInterface(mContent);
241 0 : if (autoCompleteInputElm) {
242 0 : bool isOpen = false;
243 0 : autoCompleteInputElm->GetPopupOpen(&isOpen);
244 0 : return isOpen;
245 : }
246 0 : return false;
247 : }
248 :
249 0 : nsCOMPtr<nsIDOMXULMenuListElement> menuListElm = do_QueryInterface(mContent);
250 0 : if (menuListElm) {
251 0 : bool isOpen = false;
252 0 : menuListElm->GetOpen(&isOpen);
253 0 : return isOpen;
254 : }
255 :
256 0 : return false;
257 : }
|