|
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/. */ |
|
5 |
|
6 #include "DocAccessible.h" |
|
7 #include "nsObjCExceptions.h" |
|
8 |
|
9 #include "Accessible-inl.h" |
|
10 #include "nsAccUtils.h" |
|
11 #include "Role.h" |
|
12 |
|
13 #import "mozAccessible.h" |
|
14 #import "mozActionElements.h" |
|
15 #import "mozHTMLAccessible.h" |
|
16 #import "mozTextAccessible.h" |
|
17 |
|
18 using namespace mozilla::a11y; |
|
19 |
|
20 AccessibleWrap:: |
|
21 AccessibleWrap(nsIContent* aContent, DocAccessible* aDoc) : |
|
22 Accessible(aContent, aDoc), mNativeObject(nil), |
|
23 mNativeInited(false) |
|
24 { |
|
25 } |
|
26 |
|
27 AccessibleWrap::~AccessibleWrap() |
|
28 { |
|
29 } |
|
30 |
|
31 mozAccessible* |
|
32 AccessibleWrap::GetNativeObject() |
|
33 { |
|
34 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; |
|
35 |
|
36 if (!mNativeInited && !mNativeObject && !IsDefunct() && !AncestorIsFlat()) |
|
37 mNativeObject = [[GetNativeType() alloc] initWithAccessible:this]; |
|
38 |
|
39 mNativeInited = true; |
|
40 |
|
41 return mNativeObject; |
|
42 |
|
43 NS_OBJC_END_TRY_ABORT_BLOCK_NIL; |
|
44 } |
|
45 |
|
46 NS_IMETHODIMP |
|
47 AccessibleWrap::GetNativeInterface (void **aOutInterface) |
|
48 { |
|
49 NS_ENSURE_ARG_POINTER(aOutInterface); |
|
50 |
|
51 *aOutInterface = static_cast<void*>(GetNativeObject()); |
|
52 |
|
53 return *aOutInterface ? NS_OK : NS_ERROR_FAILURE; |
|
54 } |
|
55 |
|
56 // overridden in subclasses to create the right kind of object. by default we create a generic |
|
57 // 'mozAccessible' node. |
|
58 Class |
|
59 AccessibleWrap::GetNativeType () |
|
60 { |
|
61 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; |
|
62 |
|
63 if (IsXULTabpanels()) |
|
64 return [mozPaneAccessible class]; |
|
65 |
|
66 roles::Role role = Role(); |
|
67 switch (role) { |
|
68 case roles::PUSHBUTTON: |
|
69 case roles::SPLITBUTTON: |
|
70 case roles::TOGGLE_BUTTON: |
|
71 { |
|
72 // if this button may show a popup, let's make it of the popupbutton type. |
|
73 return HasPopup() ? [mozPopupButtonAccessible class] : |
|
74 [mozButtonAccessible class]; |
|
75 } |
|
76 |
|
77 case roles::PAGETAB: |
|
78 return [mozButtonAccessible class]; |
|
79 |
|
80 case roles::CHECKBUTTON: |
|
81 return [mozCheckboxAccessible class]; |
|
82 |
|
83 case roles::HEADING: |
|
84 return [mozHeadingAccessible class]; |
|
85 |
|
86 case roles::PAGETABLIST: |
|
87 return [mozTabsAccessible class]; |
|
88 |
|
89 case roles::ENTRY: |
|
90 case roles::STATICTEXT: |
|
91 case roles::CAPTION: |
|
92 case roles::ACCEL_LABEL: |
|
93 case roles::PASSWORD_TEXT: |
|
94 // normal textfield (static or editable) |
|
95 return [mozTextAccessible class]; |
|
96 |
|
97 case roles::TEXT_LEAF: |
|
98 return [mozTextLeafAccessible class]; |
|
99 |
|
100 case roles::LINK: |
|
101 return [mozLinkAccessible class]; |
|
102 |
|
103 case roles::COMBOBOX: |
|
104 return [mozPopupButtonAccessible class]; |
|
105 |
|
106 default: |
|
107 return [mozAccessible class]; |
|
108 } |
|
109 |
|
110 return nil; |
|
111 |
|
112 NS_OBJC_END_TRY_ABORT_BLOCK_NIL; |
|
113 } |
|
114 |
|
115 // this method is very important. it is fired when an accessible object "dies". after this point |
|
116 // the object might still be around (because some 3rd party still has a ref to it), but it is |
|
117 // in fact 'dead'. |
|
118 void |
|
119 AccessibleWrap::Shutdown () |
|
120 { |
|
121 // this ensure we will not try to re-create the native object. |
|
122 mNativeInited = true; |
|
123 |
|
124 // we really intend to access the member directly. |
|
125 if (mNativeObject) { |
|
126 [mNativeObject expire]; |
|
127 [mNativeObject release]; |
|
128 mNativeObject = nil; |
|
129 } |
|
130 |
|
131 Accessible::Shutdown(); |
|
132 } |
|
133 |
|
134 nsresult |
|
135 AccessibleWrap::HandleAccEvent(AccEvent* aEvent) |
|
136 { |
|
137 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; |
|
138 |
|
139 nsresult rv = Accessible::HandleAccEvent(aEvent); |
|
140 NS_ENSURE_SUCCESS(rv, rv); |
|
141 |
|
142 uint32_t eventType = aEvent->GetEventType(); |
|
143 |
|
144 // ignore everything but focus-changed, value-changed, caret and selection |
|
145 // events for now. |
|
146 if (eventType != nsIAccessibleEvent::EVENT_FOCUS && |
|
147 eventType != nsIAccessibleEvent::EVENT_VALUE_CHANGE && |
|
148 eventType != nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED && |
|
149 eventType != nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED) |
|
150 return NS_OK; |
|
151 |
|
152 Accessible* accessible = aEvent->GetAccessible(); |
|
153 NS_ENSURE_STATE(accessible); |
|
154 |
|
155 mozAccessible *nativeAcc = nil; |
|
156 accessible->GetNativeInterface((void**)&nativeAcc); |
|
157 if (!nativeAcc) |
|
158 return NS_ERROR_FAILURE; |
|
159 |
|
160 switch (eventType) { |
|
161 case nsIAccessibleEvent::EVENT_FOCUS: |
|
162 [nativeAcc didReceiveFocus]; |
|
163 break; |
|
164 case nsIAccessibleEvent::EVENT_VALUE_CHANGE: |
|
165 [nativeAcc valueDidChange]; |
|
166 break; |
|
167 case nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED: |
|
168 case nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED: |
|
169 [nativeAcc selectedTextDidChange]; |
|
170 break; |
|
171 } |
|
172 |
|
173 return NS_OK; |
|
174 |
|
175 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; |
|
176 } |
|
177 |
|
178 void |
|
179 AccessibleWrap::InvalidateChildren() |
|
180 { |
|
181 NS_OBJC_BEGIN_TRY_ABORT_BLOCK; |
|
182 |
|
183 [GetNativeObject() invalidateChildren]; |
|
184 |
|
185 Accessible::InvalidateChildren(); |
|
186 |
|
187 NS_OBJC_END_TRY_ABORT_BLOCK; |
|
188 } |
|
189 |
|
190 bool |
|
191 AccessibleWrap::InsertChildAt(uint32_t aIdx, Accessible* aAccessible) |
|
192 { |
|
193 bool inserted = Accessible::InsertChildAt(aIdx, aAccessible); |
|
194 if (inserted && mNativeObject) |
|
195 [mNativeObject appendChild:aAccessible]; |
|
196 |
|
197 return inserted; |
|
198 } |
|
199 |
|
200 bool |
|
201 AccessibleWrap::RemoveChild(Accessible* aAccessible) |
|
202 { |
|
203 bool removed = Accessible::RemoveChild(aAccessible); |
|
204 |
|
205 if (removed && mNativeObject) |
|
206 [mNativeObject invalidateChildren]; |
|
207 |
|
208 return removed; |
|
209 } |
|
210 |
|
211 // if we for some reason have no native accessible, we should be skipped over (and traversed) |
|
212 // when fetching all unignored children, etc. when counting unignored children, we will not be counted. |
|
213 bool |
|
214 AccessibleWrap::IsIgnored() |
|
215 { |
|
216 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; |
|
217 |
|
218 mozAccessible* nativeObject = GetNativeObject(); |
|
219 return (!nativeObject) || [nativeObject accessibilityIsIgnored]; |
|
220 |
|
221 NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(false); |
|
222 } |
|
223 |
|
224 void |
|
225 AccessibleWrap::GetUnignoredChildren(nsTArray<Accessible*>* aChildrenArray) |
|
226 { |
|
227 // we're flat; there are no children. |
|
228 if (nsAccUtils::MustPrune(this)) |
|
229 return; |
|
230 |
|
231 uint32_t childCount = ChildCount(); |
|
232 for (uint32_t childIdx = 0; childIdx < childCount; childIdx++) { |
|
233 AccessibleWrap* childAcc = |
|
234 static_cast<AccessibleWrap*>(GetChildAt(childIdx)); |
|
235 |
|
236 // If element is ignored, then add its children as substitutes. |
|
237 if (childAcc->IsIgnored()) { |
|
238 childAcc->GetUnignoredChildren(aChildrenArray); |
|
239 continue; |
|
240 } |
|
241 |
|
242 aChildrenArray->AppendElement(childAcc); |
|
243 } |
|
244 } |
|
245 |
|
246 Accessible* |
|
247 AccessibleWrap::GetUnignoredParent() const |
|
248 { |
|
249 // Go up the chain to find a parent that is not ignored. |
|
250 AccessibleWrap* parentWrap = static_cast<AccessibleWrap*>(Parent()); |
|
251 while (parentWrap && parentWrap->IsIgnored()) |
|
252 parentWrap = static_cast<AccessibleWrap*>(parentWrap->Parent()); |
|
253 |
|
254 return parentWrap; |
|
255 } |
|
256 |
|
257 //////////////////////////////////////////////////////////////////////////////// |
|
258 // AccessibleWrap protected |
|
259 |
|
260 bool |
|
261 AccessibleWrap::AncestorIsFlat() |
|
262 { |
|
263 // We don't create a native object if we're child of a "flat" accessible; |
|
264 // for example, on OS X buttons shouldn't have any children, because that |
|
265 // makes the OS confused. |
|
266 // |
|
267 // To maintain a scripting environment where the XPCOM accessible hierarchy |
|
268 // look the same on all platforms, we still let the C++ objects be created |
|
269 // though. |
|
270 |
|
271 Accessible* parent = Parent(); |
|
272 while (parent) { |
|
273 if (nsAccUtils::MustPrune(parent)) |
|
274 return true; |
|
275 |
|
276 parent = parent->Parent(); |
|
277 } |
|
278 // no parent was flat |
|
279 return false; |
|
280 } |