|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* vim: set ts=2 et sw=2 tw=80: */ |
|
3 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 #include "AccessibleWrap.h" |
|
8 #include "Accessible-inl.h" |
|
9 |
|
10 #include "Compatibility.h" |
|
11 #include "DocAccessible-inl.h" |
|
12 #include "EnumVariant.h" |
|
13 #include "nsAccUtils.h" |
|
14 #include "nsCoreUtils.h" |
|
15 #include "nsIAccessibleEvent.h" |
|
16 #include "nsIAccessibleRelation.h" |
|
17 #include "nsWinUtils.h" |
|
18 #include "ServiceProvider.h" |
|
19 #include "Relation.h" |
|
20 #include "Role.h" |
|
21 #include "RootAccessible.h" |
|
22 #include "sdnAccessible.h" |
|
23 #include "States.h" |
|
24 |
|
25 #ifdef A11Y_LOG |
|
26 #include "Logging.h" |
|
27 #endif |
|
28 |
|
29 #include "nsIMutableArray.h" |
|
30 #include "nsIFrame.h" |
|
31 #include "nsIScrollableFrame.h" |
|
32 #include "nsINodeInfo.h" |
|
33 #include "nsIServiceManager.h" |
|
34 #include "nsNameSpaceManager.h" |
|
35 #include "nsTextFormatter.h" |
|
36 #include "nsView.h" |
|
37 #include "nsViewManager.h" |
|
38 #include "nsEventMap.h" |
|
39 #include "nsArrayUtils.h" |
|
40 #include "mozilla/Preferences.h" |
|
41 |
|
42 #include "oleacc.h" |
|
43 |
|
44 using namespace mozilla; |
|
45 using namespace mozilla::a11y; |
|
46 |
|
47 const uint32_t USE_ROLE_STRING = 0; |
|
48 |
|
49 /* For documentation of the accessibility architecture, |
|
50 * see http://lxr.mozilla.org/seamonkey/source/accessible/accessible-docs.html |
|
51 */ |
|
52 |
|
53 //#define DEBUG_LEAKS |
|
54 |
|
55 #ifdef DEBUG_LEAKS |
|
56 static gAccessibles = 0; |
|
57 #endif |
|
58 |
|
59 static const int32_t kIEnumVariantDisconnected = -1; |
|
60 |
|
61 //////////////////////////////////////////////////////////////////////////////// |
|
62 // AccessibleWrap |
|
63 //////////////////////////////////////////////////////////////////////////////// |
|
64 |
|
65 ITypeInfo* AccessibleWrap::gTypeInfo = nullptr; |
|
66 |
|
67 NS_IMPL_ISUPPORTS_INHERITED0(AccessibleWrap, Accessible) |
|
68 |
|
69 //----------------------------------------------------- |
|
70 // IUnknown interface methods - see iunknown.h for documentation |
|
71 //----------------------------------------------------- |
|
72 |
|
73 // Microsoft COM QueryInterface |
|
74 STDMETHODIMP |
|
75 AccessibleWrap::QueryInterface(REFIID iid, void** ppv) |
|
76 { |
|
77 A11Y_TRYBLOCK_BEGIN |
|
78 |
|
79 if (!ppv) |
|
80 return E_INVALIDARG; |
|
81 |
|
82 *ppv = nullptr; |
|
83 |
|
84 if (IID_IUnknown == iid || IID_IDispatch == iid || IID_IAccessible == iid) |
|
85 *ppv = static_cast<IAccessible*>(this); |
|
86 else if (IID_IEnumVARIANT == iid) { |
|
87 // Don't support this interface for leaf elements. |
|
88 if (!HasChildren() || nsAccUtils::MustPrune(this)) |
|
89 return E_NOINTERFACE; |
|
90 |
|
91 *ppv = static_cast<IEnumVARIANT*>(new ChildrenEnumVariant(this)); |
|
92 } else if (IID_IServiceProvider == iid) |
|
93 *ppv = new ServiceProvider(this); |
|
94 else if (IID_ISimpleDOMNode == iid) { |
|
95 if (IsDefunct() || (!HasOwnContent() && !IsDoc())) |
|
96 return E_NOINTERFACE; |
|
97 |
|
98 *ppv = static_cast<ISimpleDOMNode*>(new sdnAccessible(GetNode())); |
|
99 } |
|
100 |
|
101 if (nullptr == *ppv) { |
|
102 HRESULT hr = ia2Accessible::QueryInterface(iid, ppv); |
|
103 if (SUCCEEDED(hr)) |
|
104 return hr; |
|
105 } |
|
106 |
|
107 if (nullptr == *ppv) { |
|
108 HRESULT hr = ia2AccessibleComponent::QueryInterface(iid, ppv); |
|
109 if (SUCCEEDED(hr)) |
|
110 return hr; |
|
111 } |
|
112 |
|
113 if (nullptr == *ppv) { |
|
114 HRESULT hr = ia2AccessibleHyperlink::QueryInterface(iid, ppv); |
|
115 if (SUCCEEDED(hr)) |
|
116 return hr; |
|
117 } |
|
118 |
|
119 if (nullptr == *ppv) { |
|
120 HRESULT hr = ia2AccessibleValue::QueryInterface(iid, ppv); |
|
121 if (SUCCEEDED(hr)) |
|
122 return hr; |
|
123 } |
|
124 |
|
125 if (nullptr == *ppv) |
|
126 return E_NOINTERFACE; |
|
127 |
|
128 (reinterpret_cast<IUnknown*>(*ppv))->AddRef(); |
|
129 return S_OK; |
|
130 |
|
131 A11Y_TRYBLOCK_END |
|
132 } |
|
133 |
|
134 //----------------------------------------------------- |
|
135 // IAccessible methods |
|
136 //----------------------------------------------------- |
|
137 |
|
138 STDMETHODIMP |
|
139 AccessibleWrap::get_accParent( IDispatch __RPC_FAR *__RPC_FAR *ppdispParent) |
|
140 { |
|
141 A11Y_TRYBLOCK_BEGIN |
|
142 |
|
143 if (!ppdispParent) |
|
144 return E_INVALIDARG; |
|
145 |
|
146 *ppdispParent = nullptr; |
|
147 |
|
148 if (IsDefunct()) |
|
149 return CO_E_OBJNOTCONNECTED; |
|
150 |
|
151 DocAccessible* doc = AsDoc(); |
|
152 if (doc) { |
|
153 // Return window system accessible object for root document and tab document |
|
154 // accessibles. |
|
155 if (!doc->ParentDocument() || |
|
156 (nsWinUtils::IsWindowEmulationStarted() && |
|
157 nsCoreUtils::IsTabDocument(doc->DocumentNode()))) { |
|
158 HWND hwnd = static_cast<HWND>(doc->GetNativeWindow()); |
|
159 if (hwnd && SUCCEEDED(::AccessibleObjectFromWindow(hwnd, OBJID_WINDOW, |
|
160 IID_IAccessible, |
|
161 (void**)ppdispParent))) { |
|
162 return S_OK; |
|
163 } |
|
164 } |
|
165 } |
|
166 |
|
167 Accessible* xpParentAcc = Parent(); |
|
168 if (!xpParentAcc) |
|
169 return S_FALSE; |
|
170 |
|
171 *ppdispParent = NativeAccessible(xpParentAcc); |
|
172 return S_OK; |
|
173 |
|
174 A11Y_TRYBLOCK_END |
|
175 } |
|
176 |
|
177 STDMETHODIMP |
|
178 AccessibleWrap::get_accChildCount( long __RPC_FAR *pcountChildren) |
|
179 { |
|
180 A11Y_TRYBLOCK_BEGIN |
|
181 |
|
182 if (!pcountChildren) |
|
183 return E_INVALIDARG; |
|
184 |
|
185 *pcountChildren = 0; |
|
186 |
|
187 if (IsDefunct()) |
|
188 return CO_E_OBJNOTCONNECTED; |
|
189 |
|
190 if (nsAccUtils::MustPrune(this)) |
|
191 return S_OK; |
|
192 |
|
193 *pcountChildren = ChildCount(); |
|
194 return S_OK; |
|
195 |
|
196 A11Y_TRYBLOCK_END |
|
197 } |
|
198 |
|
199 STDMETHODIMP |
|
200 AccessibleWrap::get_accChild( |
|
201 /* [in] */ VARIANT varChild, |
|
202 /* [retval][out] */ IDispatch __RPC_FAR *__RPC_FAR *ppdispChild) |
|
203 { |
|
204 A11Y_TRYBLOCK_BEGIN |
|
205 |
|
206 if (!ppdispChild) |
|
207 return E_INVALIDARG; |
|
208 |
|
209 *ppdispChild = nullptr; |
|
210 if (IsDefunct()) |
|
211 return CO_E_OBJNOTCONNECTED; |
|
212 |
|
213 // IAccessible::accChild is used to return this accessible or child accessible |
|
214 // at the given index or to get an accessible by child ID in the case of |
|
215 // document accessible (it's handled by overriden GetXPAccessibleFor method |
|
216 // on the document accessible). The getting an accessible by child ID is used |
|
217 // by AccessibleObjectFromEvent() called by AT when AT handles our MSAA event. |
|
218 Accessible* child = GetXPAccessibleFor(varChild); |
|
219 if (!child) |
|
220 return E_INVALIDARG; |
|
221 |
|
222 if (child->IsDefunct()) |
|
223 return CO_E_OBJNOTCONNECTED; |
|
224 |
|
225 *ppdispChild = NativeAccessible(child); |
|
226 return S_OK; |
|
227 |
|
228 A11Y_TRYBLOCK_END |
|
229 } |
|
230 |
|
231 STDMETHODIMP |
|
232 AccessibleWrap::get_accName( |
|
233 /* [optional][in] */ VARIANT varChild, |
|
234 /* [retval][out] */ BSTR __RPC_FAR *pszName) |
|
235 { |
|
236 A11Y_TRYBLOCK_BEGIN |
|
237 |
|
238 if (!pszName) |
|
239 return E_INVALIDARG; |
|
240 |
|
241 *pszName = nullptr; |
|
242 |
|
243 if (IsDefunct()) |
|
244 return CO_E_OBJNOTCONNECTED; |
|
245 |
|
246 Accessible* xpAccessible = GetXPAccessibleFor(varChild); |
|
247 if (!xpAccessible) |
|
248 return E_INVALIDARG; |
|
249 |
|
250 if (xpAccessible->IsDefunct()) |
|
251 return CO_E_OBJNOTCONNECTED; |
|
252 |
|
253 nsAutoString name; |
|
254 xpAccessible->Name(name); |
|
255 |
|
256 // The name was not provided, e.g. no alt attribute for an image. A screen |
|
257 // reader may choose to invent its own accessible name, e.g. from an image src |
|
258 // attribute. Refer to eNoNameOnPurpose return value. |
|
259 if (name.IsVoid()) |
|
260 return S_FALSE; |
|
261 |
|
262 *pszName = ::SysAllocStringLen(name.get(), name.Length()); |
|
263 if (!*pszName) |
|
264 return E_OUTOFMEMORY; |
|
265 return S_OK; |
|
266 |
|
267 A11Y_TRYBLOCK_END |
|
268 } |
|
269 |
|
270 |
|
271 STDMETHODIMP |
|
272 AccessibleWrap::get_accValue( |
|
273 /* [optional][in] */ VARIANT varChild, |
|
274 /* [retval][out] */ BSTR __RPC_FAR *pszValue) |
|
275 { |
|
276 A11Y_TRYBLOCK_BEGIN |
|
277 |
|
278 if (!pszValue) |
|
279 return E_INVALIDARG; |
|
280 |
|
281 *pszValue = nullptr; |
|
282 |
|
283 if (IsDefunct()) |
|
284 return CO_E_OBJNOTCONNECTED; |
|
285 |
|
286 Accessible* xpAccessible = GetXPAccessibleFor(varChild); |
|
287 if (!xpAccessible) |
|
288 return E_INVALIDARG; |
|
289 |
|
290 if (xpAccessible->IsDefunct()) |
|
291 return CO_E_OBJNOTCONNECTED; |
|
292 |
|
293 if (xpAccessible->NativeRole() == roles::PASSWORD_TEXT) |
|
294 return E_ACCESSDENIED; |
|
295 |
|
296 nsAutoString value; |
|
297 xpAccessible->Value(value); |
|
298 |
|
299 // See bug 438784: need to expose URL on doc's value attribute. For this, |
|
300 // reverting part of fix for bug 425693 to make this MSAA method behave |
|
301 // IAccessible2-style. |
|
302 if (value.IsEmpty()) |
|
303 return S_FALSE; |
|
304 |
|
305 *pszValue = ::SysAllocStringLen(value.get(), value.Length()); |
|
306 if (!*pszValue) |
|
307 return E_OUTOFMEMORY; |
|
308 return S_OK; |
|
309 |
|
310 A11Y_TRYBLOCK_END |
|
311 } |
|
312 |
|
313 STDMETHODIMP |
|
314 AccessibleWrap::get_accDescription(VARIANT varChild, |
|
315 BSTR __RPC_FAR *pszDescription) |
|
316 { |
|
317 A11Y_TRYBLOCK_BEGIN |
|
318 |
|
319 if (!pszDescription) |
|
320 return E_INVALIDARG; |
|
321 |
|
322 *pszDescription = nullptr; |
|
323 |
|
324 if (IsDefunct()) |
|
325 return CO_E_OBJNOTCONNECTED; |
|
326 |
|
327 Accessible* xpAccessible = GetXPAccessibleFor(varChild); |
|
328 if (!xpAccessible) |
|
329 return E_INVALIDARG; |
|
330 |
|
331 if (xpAccessible->IsDefunct()) |
|
332 return CO_E_OBJNOTCONNECTED; |
|
333 |
|
334 nsAutoString description; |
|
335 xpAccessible->Description(description); |
|
336 |
|
337 *pszDescription = ::SysAllocStringLen(description.get(), |
|
338 description.Length()); |
|
339 return *pszDescription ? S_OK : E_OUTOFMEMORY; |
|
340 |
|
341 A11Y_TRYBLOCK_END |
|
342 } |
|
343 |
|
344 STDMETHODIMP |
|
345 AccessibleWrap::get_accRole( |
|
346 /* [optional][in] */ VARIANT varChild, |
|
347 /* [retval][out] */ VARIANT __RPC_FAR *pvarRole) |
|
348 { |
|
349 A11Y_TRYBLOCK_BEGIN |
|
350 |
|
351 if (!pvarRole) |
|
352 return E_INVALIDARG; |
|
353 |
|
354 VariantInit(pvarRole); |
|
355 |
|
356 if (IsDefunct()) |
|
357 return CO_E_OBJNOTCONNECTED; |
|
358 |
|
359 Accessible* xpAccessible = GetXPAccessibleFor(varChild); |
|
360 if (!xpAccessible) |
|
361 return E_INVALIDARG; |
|
362 |
|
363 if (xpAccessible->IsDefunct()) |
|
364 return CO_E_OBJNOTCONNECTED; |
|
365 |
|
366 #ifdef DEBUG |
|
367 NS_ASSERTION(nsAccUtils::IsTextInterfaceSupportCorrect(xpAccessible), |
|
368 "Does not support nsIAccessibleText when it should"); |
|
369 #endif |
|
370 |
|
371 a11y::role geckoRole = xpAccessible->Role(); |
|
372 uint32_t msaaRole = 0; |
|
373 |
|
374 #define ROLE(_geckoRole, stringRole, atkRole, macRole, \ |
|
375 _msaaRole, ia2Role, nameRule) \ |
|
376 case roles::_geckoRole: \ |
|
377 msaaRole = _msaaRole; \ |
|
378 break; |
|
379 |
|
380 switch (geckoRole) { |
|
381 #include "RoleMap.h" |
|
382 default: |
|
383 MOZ_CRASH("Unknown role."); |
|
384 }; |
|
385 |
|
386 #undef ROLE |
|
387 |
|
388 // Special case, if there is a ROLE_ROW inside of a ROLE_TREE_TABLE, then call the MSAA role |
|
389 // a ROLE_OUTLINEITEM for consistency and compatibility. |
|
390 // We need this because ARIA has a role of "row" for both grid and treegrid |
|
391 if (geckoRole == roles::ROW) { |
|
392 Accessible* xpParent = Parent(); |
|
393 if (xpParent && xpParent->Role() == roles::TREE_TABLE) |
|
394 msaaRole = ROLE_SYSTEM_OUTLINEITEM; |
|
395 } |
|
396 |
|
397 // -- Try enumerated role |
|
398 if (msaaRole != USE_ROLE_STRING) { |
|
399 pvarRole->vt = VT_I4; |
|
400 pvarRole->lVal = msaaRole; // Normal enumerated role |
|
401 return S_OK; |
|
402 } |
|
403 |
|
404 // -- Try BSTR role |
|
405 // Could not map to known enumerated MSAA role like ROLE_BUTTON |
|
406 // Use BSTR role to expose role attribute or tag name + namespace |
|
407 nsIContent *content = xpAccessible->GetContent(); |
|
408 if (!content) |
|
409 return E_FAIL; |
|
410 |
|
411 if (content->IsElement()) { |
|
412 nsAutoString roleString; |
|
413 if (msaaRole != ROLE_SYSTEM_CLIENT && |
|
414 !content->GetAttr(kNameSpaceID_None, nsGkAtoms::role, roleString)) { |
|
415 nsIDocument * document = content->GetCurrentDoc(); |
|
416 if (!document) |
|
417 return E_FAIL; |
|
418 |
|
419 nsINodeInfo *nodeInfo = content->NodeInfo(); |
|
420 nodeInfo->GetName(roleString); |
|
421 |
|
422 // Only append name space if different from that of current document. |
|
423 if (!nodeInfo->NamespaceEquals(document->GetDefaultNamespaceID())) { |
|
424 nsAutoString nameSpaceURI; |
|
425 nodeInfo->GetNamespaceURI(nameSpaceURI); |
|
426 roleString += NS_LITERAL_STRING(", ") + nameSpaceURI; |
|
427 } |
|
428 } |
|
429 |
|
430 if (!roleString.IsEmpty()) { |
|
431 pvarRole->vt = VT_BSTR; |
|
432 pvarRole->bstrVal = ::SysAllocString(roleString.get()); |
|
433 return S_OK; |
|
434 } |
|
435 } |
|
436 |
|
437 return E_FAIL; |
|
438 |
|
439 A11Y_TRYBLOCK_END |
|
440 } |
|
441 |
|
442 STDMETHODIMP |
|
443 AccessibleWrap::get_accState( |
|
444 /* [optional][in] */ VARIANT varChild, |
|
445 /* [retval][out] */ VARIANT __RPC_FAR *pvarState) |
|
446 { |
|
447 A11Y_TRYBLOCK_BEGIN |
|
448 |
|
449 if (!pvarState) |
|
450 return E_INVALIDARG; |
|
451 |
|
452 VariantInit(pvarState); |
|
453 pvarState->vt = VT_I4; |
|
454 pvarState->lVal = 0; |
|
455 |
|
456 if (IsDefunct()) |
|
457 return CO_E_OBJNOTCONNECTED; |
|
458 |
|
459 Accessible* xpAccessible = GetXPAccessibleFor(varChild); |
|
460 if (!xpAccessible) |
|
461 return E_INVALIDARG; |
|
462 |
|
463 if (xpAccessible->IsDefunct()) |
|
464 return CO_E_OBJNOTCONNECTED; |
|
465 |
|
466 // MSAA only has 31 states and the lowest 31 bits of our state bit mask |
|
467 // are the same states as MSAA. |
|
468 // Note: we map the following Gecko states to different MSAA states: |
|
469 // REQUIRED -> ALERT_LOW |
|
470 // ALERT -> ALERT_MEDIUM |
|
471 // INVALID -> ALERT_HIGH |
|
472 // CHECKABLE -> MARQUEED |
|
473 |
|
474 uint32_t msaaState = 0; |
|
475 nsAccUtils::To32States(xpAccessible->State(), &msaaState, nullptr); |
|
476 pvarState->lVal = msaaState; |
|
477 return S_OK; |
|
478 |
|
479 A11Y_TRYBLOCK_END |
|
480 } |
|
481 |
|
482 |
|
483 STDMETHODIMP |
|
484 AccessibleWrap::get_accHelp( |
|
485 /* [optional][in] */ VARIANT varChild, |
|
486 /* [retval][out] */ BSTR __RPC_FAR *pszHelp) |
|
487 { |
|
488 A11Y_TRYBLOCK_BEGIN |
|
489 |
|
490 if (!pszHelp) |
|
491 return E_INVALIDARG; |
|
492 |
|
493 *pszHelp = nullptr; |
|
494 return S_FALSE; |
|
495 |
|
496 A11Y_TRYBLOCK_END |
|
497 } |
|
498 |
|
499 STDMETHODIMP |
|
500 AccessibleWrap::get_accHelpTopic( |
|
501 /* [out] */ BSTR __RPC_FAR *pszHelpFile, |
|
502 /* [optional][in] */ VARIANT varChild, |
|
503 /* [retval][out] */ long __RPC_FAR *pidTopic) |
|
504 { |
|
505 A11Y_TRYBLOCK_BEGIN |
|
506 |
|
507 if (!pszHelpFile || !pidTopic) |
|
508 return E_INVALIDARG; |
|
509 |
|
510 *pszHelpFile = nullptr; |
|
511 *pidTopic = 0; |
|
512 return S_FALSE; |
|
513 |
|
514 A11Y_TRYBLOCK_END |
|
515 } |
|
516 |
|
517 STDMETHODIMP |
|
518 AccessibleWrap::get_accKeyboardShortcut( |
|
519 /* [optional][in] */ VARIANT varChild, |
|
520 /* [retval][out] */ BSTR __RPC_FAR *pszKeyboardShortcut) |
|
521 { |
|
522 A11Y_TRYBLOCK_BEGIN |
|
523 |
|
524 if (!pszKeyboardShortcut) |
|
525 return E_INVALIDARG; |
|
526 *pszKeyboardShortcut = nullptr; |
|
527 |
|
528 if (IsDefunct()) |
|
529 return CO_E_OBJNOTCONNECTED; |
|
530 |
|
531 Accessible* acc = GetXPAccessibleFor(varChild); |
|
532 if (!acc) |
|
533 return E_INVALIDARG; |
|
534 |
|
535 if (acc->IsDefunct()) |
|
536 return CO_E_OBJNOTCONNECTED; |
|
537 |
|
538 KeyBinding keyBinding = acc->AccessKey(); |
|
539 if (keyBinding.IsEmpty()) |
|
540 keyBinding = acc->KeyboardShortcut(); |
|
541 |
|
542 nsAutoString shortcut; |
|
543 keyBinding.ToString(shortcut); |
|
544 |
|
545 *pszKeyboardShortcut = ::SysAllocStringLen(shortcut.get(), |
|
546 shortcut.Length()); |
|
547 return *pszKeyboardShortcut ? S_OK : E_OUTOFMEMORY; |
|
548 |
|
549 A11Y_TRYBLOCK_END |
|
550 } |
|
551 |
|
552 STDMETHODIMP |
|
553 AccessibleWrap::get_accFocus( |
|
554 /* [retval][out] */ VARIANT __RPC_FAR *pvarChild) |
|
555 { |
|
556 A11Y_TRYBLOCK_BEGIN |
|
557 |
|
558 if (!pvarChild) |
|
559 return E_INVALIDARG; |
|
560 |
|
561 VariantInit(pvarChild); |
|
562 |
|
563 // VT_EMPTY: None. This object does not have the keyboard focus itself |
|
564 // and does not contain a child that has the keyboard focus. |
|
565 // VT_I4: lVal is CHILDID_SELF. The object itself has the keyboard focus. |
|
566 // VT_I4: lVal contains the child ID of the child element with the keyboard focus. |
|
567 // VT_DISPATCH: pdispVal member is the address of the IDispatch interface |
|
568 // for the child object with the keyboard focus. |
|
569 if (IsDefunct()) |
|
570 return CO_E_OBJNOTCONNECTED; |
|
571 |
|
572 // Return the current IAccessible child that has focus |
|
573 Accessible* focusedAccessible = FocusedChild(); |
|
574 if (focusedAccessible == this) { |
|
575 pvarChild->vt = VT_I4; |
|
576 pvarChild->lVal = CHILDID_SELF; |
|
577 } |
|
578 else if (focusedAccessible) { |
|
579 pvarChild->vt = VT_DISPATCH; |
|
580 pvarChild->pdispVal = NativeAccessible(focusedAccessible); |
|
581 } |
|
582 else { |
|
583 pvarChild->vt = VT_EMPTY; // No focus or focus is not a child |
|
584 } |
|
585 |
|
586 return S_OK; |
|
587 |
|
588 A11Y_TRYBLOCK_END |
|
589 } |
|
590 |
|
591 // This helper class implements IEnumVARIANT for a nsIArray containing nsIAccessible objects. |
|
592 |
|
593 class AccessibleEnumerator MOZ_FINAL : public IEnumVARIANT |
|
594 { |
|
595 public: |
|
596 AccessibleEnumerator(nsIArray* aArray) : mArray(aArray), mCurIndex(0) { } |
|
597 AccessibleEnumerator(const AccessibleEnumerator& toCopy) : |
|
598 mArray(toCopy.mArray), mCurIndex(toCopy.mCurIndex) { } |
|
599 ~AccessibleEnumerator() { } |
|
600 |
|
601 // IUnknown |
|
602 DECL_IUNKNOWN |
|
603 |
|
604 // IEnumVARIANT |
|
605 STDMETHODIMP Next(unsigned long celt, VARIANT FAR* rgvar, unsigned long FAR* pceltFetched); |
|
606 STDMETHODIMP Skip(unsigned long celt); |
|
607 STDMETHODIMP Reset() |
|
608 { |
|
609 mCurIndex = 0; |
|
610 return S_OK; |
|
611 } |
|
612 STDMETHODIMP Clone(IEnumVARIANT FAR* FAR* ppenum); |
|
613 |
|
614 private: |
|
615 nsCOMPtr<nsIArray> mArray; |
|
616 uint32_t mCurIndex; |
|
617 }; |
|
618 |
|
619 STDMETHODIMP |
|
620 AccessibleEnumerator::QueryInterface(REFIID iid, void ** ppvObject) |
|
621 { |
|
622 A11Y_TRYBLOCK_BEGIN |
|
623 |
|
624 if (iid == IID_IEnumVARIANT) { |
|
625 *ppvObject = static_cast<IEnumVARIANT*>(this); |
|
626 AddRef(); |
|
627 return S_OK; |
|
628 } |
|
629 if (iid == IID_IUnknown) { |
|
630 *ppvObject = static_cast<IUnknown*>(this); |
|
631 AddRef(); |
|
632 return S_OK; |
|
633 } |
|
634 |
|
635 *ppvObject = nullptr; |
|
636 return E_NOINTERFACE; |
|
637 |
|
638 A11Y_TRYBLOCK_END |
|
639 } |
|
640 |
|
641 STDMETHODIMP |
|
642 AccessibleEnumerator::Next(unsigned long celt, VARIANT FAR* rgvar, unsigned long FAR* pceltFetched) |
|
643 { |
|
644 A11Y_TRYBLOCK_BEGIN |
|
645 |
|
646 uint32_t length = 0; |
|
647 mArray->GetLength(&length); |
|
648 |
|
649 HRESULT hr = S_OK; |
|
650 |
|
651 // Can't get more elements than there are... |
|
652 if (celt > length - mCurIndex) { |
|
653 hr = S_FALSE; |
|
654 celt = length - mCurIndex; |
|
655 } |
|
656 |
|
657 for (uint32_t i = 0; i < celt; ++i, ++mCurIndex) { |
|
658 // Copy the elements of the array into rgvar |
|
659 nsCOMPtr<nsIAccessible> accel(do_QueryElementAt(mArray, mCurIndex)); |
|
660 NS_ASSERTION(accel, "Invalid pointer in mArray"); |
|
661 |
|
662 if (accel) { |
|
663 rgvar[i].vt = VT_DISPATCH; |
|
664 rgvar[i].pdispVal = AccessibleWrap::NativeAccessible(accel); |
|
665 } |
|
666 } |
|
667 |
|
668 if (pceltFetched) |
|
669 *pceltFetched = celt; |
|
670 |
|
671 return hr; |
|
672 |
|
673 A11Y_TRYBLOCK_END |
|
674 } |
|
675 |
|
676 STDMETHODIMP |
|
677 AccessibleEnumerator::Clone(IEnumVARIANT FAR* FAR* ppenum) |
|
678 { |
|
679 A11Y_TRYBLOCK_BEGIN |
|
680 |
|
681 *ppenum = new AccessibleEnumerator(*this); |
|
682 if (!*ppenum) |
|
683 return E_OUTOFMEMORY; |
|
684 NS_ADDREF(*ppenum); |
|
685 return S_OK; |
|
686 |
|
687 A11Y_TRYBLOCK_END |
|
688 } |
|
689 |
|
690 STDMETHODIMP |
|
691 AccessibleEnumerator::Skip(unsigned long celt) |
|
692 { |
|
693 A11Y_TRYBLOCK_BEGIN |
|
694 |
|
695 uint32_t length = 0; |
|
696 mArray->GetLength(&length); |
|
697 // Check if we can skip the requested number of elements |
|
698 if (celt > length - mCurIndex) { |
|
699 mCurIndex = length; |
|
700 return S_FALSE; |
|
701 } |
|
702 mCurIndex += celt; |
|
703 return S_OK; |
|
704 |
|
705 A11Y_TRYBLOCK_END |
|
706 } |
|
707 |
|
708 /** |
|
709 * This method is called when a client wants to know which children of a node |
|
710 * are selected. Note that this method can only find selected children for |
|
711 * nsIAccessible object which implement SelectAccessible. |
|
712 * |
|
713 * The VARIANT return value arguement is expected to either contain a single IAccessible |
|
714 * or an IEnumVARIANT of IAccessibles. We return the IEnumVARIANT regardless of the number |
|
715 * of children selected, unless there are none selected in which case we return an empty |
|
716 * VARIANT. |
|
717 * |
|
718 * We get the selected options from the select's accessible object and wrap |
|
719 * those in an AccessibleEnumerator which we then put in the return VARIANT. |
|
720 * |
|
721 * returns a VT_EMPTY VARIANT if: |
|
722 * - there are no selected children for this object |
|
723 * - the object is not the type that can have children selected |
|
724 */ |
|
725 STDMETHODIMP |
|
726 AccessibleWrap::get_accSelection(VARIANT __RPC_FAR *pvarChildren) |
|
727 { |
|
728 A11Y_TRYBLOCK_BEGIN |
|
729 |
|
730 if (!pvarChildren) |
|
731 return E_INVALIDARG; |
|
732 |
|
733 VariantInit(pvarChildren); |
|
734 pvarChildren->vt = VT_EMPTY; |
|
735 |
|
736 if (IsDefunct()) |
|
737 return CO_E_OBJNOTCONNECTED; |
|
738 |
|
739 if (IsSelect()) { |
|
740 nsCOMPtr<nsIArray> selectedItems = SelectedItems(); |
|
741 if (selectedItems) { |
|
742 // 1) Create and initialize the enumeration |
|
743 nsRefPtr<AccessibleEnumerator> pEnum = |
|
744 new AccessibleEnumerator(selectedItems); |
|
745 |
|
746 // 2) Put the enumerator in the VARIANT |
|
747 if (!pEnum) |
|
748 return E_OUTOFMEMORY; |
|
749 pvarChildren->vt = VT_UNKNOWN; // this must be VT_UNKNOWN for an IEnumVARIANT |
|
750 NS_ADDREF(pvarChildren->punkVal = pEnum); |
|
751 } |
|
752 } |
|
753 return S_OK; |
|
754 |
|
755 A11Y_TRYBLOCK_END |
|
756 } |
|
757 |
|
758 STDMETHODIMP |
|
759 AccessibleWrap::get_accDefaultAction( |
|
760 /* [optional][in] */ VARIANT varChild, |
|
761 /* [retval][out] */ BSTR __RPC_FAR *pszDefaultAction) |
|
762 { |
|
763 A11Y_TRYBLOCK_BEGIN |
|
764 |
|
765 if (!pszDefaultAction) |
|
766 return E_INVALIDARG; |
|
767 |
|
768 *pszDefaultAction = nullptr; |
|
769 |
|
770 if (IsDefunct()) |
|
771 return CO_E_OBJNOTCONNECTED; |
|
772 |
|
773 Accessible* xpAccessible = GetXPAccessibleFor(varChild); |
|
774 if (!xpAccessible) |
|
775 return E_INVALIDARG; |
|
776 |
|
777 if (xpAccessible->IsDefunct()) |
|
778 return CO_E_OBJNOTCONNECTED; |
|
779 |
|
780 nsAutoString defaultAction; |
|
781 if (NS_FAILED(xpAccessible->GetActionName(0, defaultAction))) |
|
782 return E_FAIL; |
|
783 |
|
784 *pszDefaultAction = ::SysAllocStringLen(defaultAction.get(), |
|
785 defaultAction.Length()); |
|
786 return *pszDefaultAction ? S_OK : E_OUTOFMEMORY; |
|
787 |
|
788 A11Y_TRYBLOCK_END |
|
789 } |
|
790 |
|
791 STDMETHODIMP |
|
792 AccessibleWrap::accSelect( |
|
793 /* [in] */ long flagsSelect, |
|
794 /* [optional][in] */ VARIANT varChild) |
|
795 { |
|
796 A11Y_TRYBLOCK_BEGIN |
|
797 |
|
798 if (IsDefunct()) |
|
799 return CO_E_OBJNOTCONNECTED; |
|
800 |
|
801 // currently only handle focus and selection |
|
802 Accessible* xpAccessible = GetXPAccessibleFor(varChild); |
|
803 if (!xpAccessible) |
|
804 return E_INVALIDARG; |
|
805 |
|
806 if (xpAccessible->IsDefunct()) |
|
807 return CO_E_OBJNOTCONNECTED; |
|
808 |
|
809 if (flagsSelect & (SELFLAG_TAKEFOCUS|SELFLAG_TAKESELECTION|SELFLAG_REMOVESELECTION)) |
|
810 { |
|
811 if (flagsSelect & SELFLAG_TAKEFOCUS) |
|
812 xpAccessible->TakeFocus(); |
|
813 |
|
814 if (flagsSelect & SELFLAG_TAKESELECTION) |
|
815 xpAccessible->TakeSelection(); |
|
816 |
|
817 if (flagsSelect & SELFLAG_ADDSELECTION) |
|
818 xpAccessible->SetSelected(true); |
|
819 |
|
820 if (flagsSelect & SELFLAG_REMOVESELECTION) |
|
821 xpAccessible->SetSelected(false); |
|
822 |
|
823 if (flagsSelect & SELFLAG_EXTENDSELECTION) |
|
824 xpAccessible->ExtendSelection(); |
|
825 |
|
826 return S_OK; |
|
827 } |
|
828 |
|
829 return E_FAIL; |
|
830 |
|
831 A11Y_TRYBLOCK_END |
|
832 } |
|
833 |
|
834 STDMETHODIMP |
|
835 AccessibleWrap::accLocation( |
|
836 /* [out] */ long __RPC_FAR *pxLeft, |
|
837 /* [out] */ long __RPC_FAR *pyTop, |
|
838 /* [out] */ long __RPC_FAR *pcxWidth, |
|
839 /* [out] */ long __RPC_FAR *pcyHeight, |
|
840 /* [optional][in] */ VARIANT varChild) |
|
841 { |
|
842 A11Y_TRYBLOCK_BEGIN |
|
843 |
|
844 if (!pxLeft || !pyTop || !pcxWidth || !pcyHeight) |
|
845 return E_INVALIDARG; |
|
846 |
|
847 *pxLeft = 0; |
|
848 *pyTop = 0; |
|
849 *pcxWidth = 0; |
|
850 *pcyHeight = 0; |
|
851 |
|
852 if (IsDefunct()) |
|
853 return CO_E_OBJNOTCONNECTED; |
|
854 |
|
855 Accessible* xpAccessible = GetXPAccessibleFor(varChild); |
|
856 if (!xpAccessible) |
|
857 return E_INVALIDARG; |
|
858 |
|
859 if (xpAccessible->IsDefunct()) |
|
860 return CO_E_OBJNOTCONNECTED; |
|
861 |
|
862 int32_t x, y, width, height; |
|
863 if (NS_FAILED(xpAccessible->GetBounds(&x, &y, &width, &height))) |
|
864 return E_FAIL; |
|
865 |
|
866 *pxLeft = x; |
|
867 *pyTop = y; |
|
868 *pcxWidth = width; |
|
869 *pcyHeight = height; |
|
870 return S_OK; |
|
871 |
|
872 A11Y_TRYBLOCK_END |
|
873 } |
|
874 |
|
875 STDMETHODIMP |
|
876 AccessibleWrap::accNavigate( |
|
877 /* [in] */ long navDir, |
|
878 /* [optional][in] */ VARIANT varStart, |
|
879 /* [retval][out] */ VARIANT __RPC_FAR *pvarEndUpAt) |
|
880 { |
|
881 A11Y_TRYBLOCK_BEGIN |
|
882 |
|
883 if (!pvarEndUpAt) |
|
884 return E_INVALIDARG; |
|
885 |
|
886 VariantInit(pvarEndUpAt); |
|
887 |
|
888 if (IsDefunct()) |
|
889 return CO_E_OBJNOTCONNECTED; |
|
890 |
|
891 Accessible* accessible = GetXPAccessibleFor(varStart); |
|
892 if (!accessible) |
|
893 return E_INVALIDARG; |
|
894 |
|
895 if (accessible->IsDefunct()) |
|
896 return CO_E_OBJNOTCONNECTED; |
|
897 |
|
898 Accessible* navAccessible = nullptr; |
|
899 Maybe<RelationType> xpRelation; |
|
900 |
|
901 #define RELATIONTYPE(geckoType, stringType, atkType, msaaType, ia2Type) \ |
|
902 case msaaType: \ |
|
903 xpRelation.construct(RelationType::geckoType); \ |
|
904 break; |
|
905 |
|
906 switch(navDir) { |
|
907 case NAVDIR_FIRSTCHILD: |
|
908 if (!nsAccUtils::MustPrune(accessible)) |
|
909 navAccessible = accessible->FirstChild(); |
|
910 break; |
|
911 case NAVDIR_LASTCHILD: |
|
912 if (!nsAccUtils::MustPrune(accessible)) |
|
913 navAccessible = accessible->LastChild(); |
|
914 break; |
|
915 case NAVDIR_NEXT: |
|
916 navAccessible = accessible->NextSibling(); |
|
917 break; |
|
918 case NAVDIR_PREVIOUS: |
|
919 navAccessible = accessible->PrevSibling(); |
|
920 break; |
|
921 case NAVDIR_DOWN: |
|
922 case NAVDIR_LEFT: |
|
923 case NAVDIR_RIGHT: |
|
924 case NAVDIR_UP: |
|
925 return E_NOTIMPL; |
|
926 |
|
927 // MSAA relationship extensions to accNavigate |
|
928 #include "RelationTypeMap.h" |
|
929 |
|
930 default: |
|
931 return E_INVALIDARG; |
|
932 } |
|
933 |
|
934 #undef RELATIONTYPE |
|
935 |
|
936 pvarEndUpAt->vt = VT_EMPTY; |
|
937 |
|
938 if (!xpRelation.empty()) { |
|
939 Relation rel = RelationByType(xpRelation.ref()); |
|
940 navAccessible = rel.Next(); |
|
941 } |
|
942 |
|
943 if (!navAccessible) |
|
944 return E_FAIL; |
|
945 |
|
946 pvarEndUpAt->pdispVal = NativeAccessible(navAccessible); |
|
947 pvarEndUpAt->vt = VT_DISPATCH; |
|
948 return S_OK; |
|
949 |
|
950 A11Y_TRYBLOCK_END |
|
951 } |
|
952 |
|
953 STDMETHODIMP |
|
954 AccessibleWrap::accHitTest( |
|
955 /* [in] */ long xLeft, |
|
956 /* [in] */ long yTop, |
|
957 /* [retval][out] */ VARIANT __RPC_FAR *pvarChild) |
|
958 { |
|
959 A11Y_TRYBLOCK_BEGIN |
|
960 |
|
961 if (!pvarChild) |
|
962 return E_INVALIDARG; |
|
963 |
|
964 VariantInit(pvarChild); |
|
965 |
|
966 if (IsDefunct()) |
|
967 return CO_E_OBJNOTCONNECTED; |
|
968 |
|
969 Accessible* accessible = ChildAtPoint(xLeft, yTop, eDirectChild); |
|
970 |
|
971 // if we got a child |
|
972 if (accessible) { |
|
973 // if the child is us |
|
974 if (accessible == this) { |
|
975 pvarChild->vt = VT_I4; |
|
976 pvarChild->lVal = CHILDID_SELF; |
|
977 } else { // its not create an Accessible for it. |
|
978 pvarChild->vt = VT_DISPATCH; |
|
979 pvarChild->pdispVal = NativeAccessible(accessible); |
|
980 } |
|
981 } else { |
|
982 // no child at that point |
|
983 pvarChild->vt = VT_EMPTY; |
|
984 return S_FALSE; |
|
985 } |
|
986 return S_OK; |
|
987 |
|
988 A11Y_TRYBLOCK_END |
|
989 } |
|
990 |
|
991 STDMETHODIMP |
|
992 AccessibleWrap::accDoDefaultAction( |
|
993 /* [optional][in] */ VARIANT varChild) |
|
994 { |
|
995 A11Y_TRYBLOCK_BEGIN |
|
996 |
|
997 if (IsDefunct()) |
|
998 return CO_E_OBJNOTCONNECTED; |
|
999 |
|
1000 Accessible* xpAccessible = GetXPAccessibleFor(varChild); |
|
1001 if (!xpAccessible) |
|
1002 return E_INVALIDARG; |
|
1003 |
|
1004 if (xpAccessible->IsDefunct()) |
|
1005 return CO_E_OBJNOTCONNECTED; |
|
1006 |
|
1007 return GetHRESULT(xpAccessible->DoAction(0)); |
|
1008 |
|
1009 A11Y_TRYBLOCK_END |
|
1010 } |
|
1011 |
|
1012 STDMETHODIMP |
|
1013 AccessibleWrap::put_accName( |
|
1014 /* [optional][in] */ VARIANT varChild, |
|
1015 /* [in] */ BSTR szName) |
|
1016 { |
|
1017 return E_NOTIMPL; |
|
1018 } |
|
1019 |
|
1020 STDMETHODIMP |
|
1021 AccessibleWrap::put_accValue( |
|
1022 /* [optional][in] */ VARIANT varChild, |
|
1023 /* [in] */ BSTR szValue) |
|
1024 { |
|
1025 return E_NOTIMPL; |
|
1026 } |
|
1027 |
|
1028 //////////////////////////////////////////////////////////////////////////////// |
|
1029 // IDispatch |
|
1030 |
|
1031 STDMETHODIMP |
|
1032 AccessibleWrap::GetTypeInfoCount(UINT *pctinfo) |
|
1033 { |
|
1034 if (!pctinfo) |
|
1035 return E_INVALIDARG; |
|
1036 |
|
1037 *pctinfo = 1; |
|
1038 return S_OK; |
|
1039 } |
|
1040 |
|
1041 STDMETHODIMP |
|
1042 AccessibleWrap::GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo) |
|
1043 { |
|
1044 if (!ppTInfo) |
|
1045 return E_INVALIDARG; |
|
1046 |
|
1047 *ppTInfo = nullptr; |
|
1048 |
|
1049 if (iTInfo != 0) |
|
1050 return DISP_E_BADINDEX; |
|
1051 |
|
1052 ITypeInfo * typeInfo = GetTI(lcid); |
|
1053 if (!typeInfo) |
|
1054 return E_FAIL; |
|
1055 |
|
1056 typeInfo->AddRef(); |
|
1057 *ppTInfo = typeInfo; |
|
1058 |
|
1059 return S_OK; |
|
1060 } |
|
1061 |
|
1062 STDMETHODIMP |
|
1063 AccessibleWrap::GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, |
|
1064 UINT cNames, LCID lcid, DISPID *rgDispId) |
|
1065 { |
|
1066 ITypeInfo *typeInfo = GetTI(lcid); |
|
1067 if (!typeInfo) |
|
1068 return E_FAIL; |
|
1069 |
|
1070 HRESULT hr = DispGetIDsOfNames(typeInfo, rgszNames, cNames, rgDispId); |
|
1071 return hr; |
|
1072 } |
|
1073 |
|
1074 STDMETHODIMP |
|
1075 AccessibleWrap::Invoke(DISPID dispIdMember, REFIID riid, |
|
1076 LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, |
|
1077 VARIANT *pVarResult, EXCEPINFO *pExcepInfo, |
|
1078 UINT *puArgErr) |
|
1079 { |
|
1080 ITypeInfo *typeInfo = GetTI(lcid); |
|
1081 if (!typeInfo) |
|
1082 return E_FAIL; |
|
1083 |
|
1084 return typeInfo->Invoke(static_cast<IAccessible*>(this), dispIdMember, |
|
1085 wFlags, pDispParams, pVarResult, pExcepInfo, |
|
1086 puArgErr); |
|
1087 } |
|
1088 |
|
1089 |
|
1090 // nsIAccessible method |
|
1091 NS_IMETHODIMP |
|
1092 AccessibleWrap::GetNativeInterface(void **aOutAccessible) |
|
1093 { |
|
1094 *aOutAccessible = static_cast<IAccessible*>(this); |
|
1095 NS_ADDREF_THIS(); |
|
1096 return NS_OK; |
|
1097 } |
|
1098 |
|
1099 //////////////////////////////////////////////////////////////////////////////// |
|
1100 // Accessible |
|
1101 |
|
1102 nsresult |
|
1103 AccessibleWrap::HandleAccEvent(AccEvent* aEvent) |
|
1104 { |
|
1105 nsresult rv = Accessible::HandleAccEvent(aEvent); |
|
1106 NS_ENSURE_SUCCESS(rv, rv); |
|
1107 |
|
1108 // Don't fire native MSAA events or mess with the system caret |
|
1109 // when running in metro mode. This confuses input focus tracking |
|
1110 // in metro's UIA implementation. |
|
1111 if (XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Metro) { |
|
1112 return NS_OK; |
|
1113 } |
|
1114 |
|
1115 uint32_t eventType = aEvent->GetEventType(); |
|
1116 |
|
1117 static_assert(sizeof(gWinEventMap)/sizeof(gWinEventMap[0]) == nsIAccessibleEvent::EVENT_LAST_ENTRY, |
|
1118 "MSAA event map skewed"); |
|
1119 |
|
1120 NS_ENSURE_TRUE(eventType > 0 && eventType < ArrayLength(gWinEventMap), NS_ERROR_FAILURE); |
|
1121 |
|
1122 uint32_t winEvent = gWinEventMap[eventType]; |
|
1123 if (!winEvent) |
|
1124 return NS_OK; |
|
1125 |
|
1126 // Means we're not active. |
|
1127 NS_ENSURE_TRUE(!IsDefunct(), NS_ERROR_FAILURE); |
|
1128 |
|
1129 Accessible* accessible = aEvent->GetAccessible(); |
|
1130 if (!accessible) |
|
1131 return NS_OK; |
|
1132 |
|
1133 if (eventType == nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED || |
|
1134 eventType == nsIAccessibleEvent::EVENT_FOCUS) { |
|
1135 UpdateSystemCaretFor(accessible); |
|
1136 } |
|
1137 |
|
1138 int32_t childID = GetChildIDFor(accessible); // get the id for the accessible |
|
1139 if (!childID) |
|
1140 return NS_OK; // Can't fire an event without a child ID |
|
1141 |
|
1142 HWND hWnd = GetHWNDFor(accessible); |
|
1143 NS_ENSURE_TRUE(hWnd, NS_ERROR_FAILURE); |
|
1144 |
|
1145 nsAutoString tag; |
|
1146 nsAutoCString id; |
|
1147 nsIContent* cnt = accessible->GetContent(); |
|
1148 if (cnt) { |
|
1149 cnt->Tag()->ToString(tag); |
|
1150 nsIAtom* aid = cnt->GetID(); |
|
1151 if (aid) |
|
1152 aid->ToUTF8String(id); |
|
1153 } |
|
1154 |
|
1155 #ifdef A11Y_LOG |
|
1156 if (logging::IsEnabled(logging::ePlatforms)) { |
|
1157 printf("\n\nMSAA event: event: %d, target: %s@id='%s', childid: %d, hwnd: %d\n\n", |
|
1158 eventType, NS_ConvertUTF16toUTF8(tag).get(), id.get(), |
|
1159 childID, hWnd); |
|
1160 } |
|
1161 #endif |
|
1162 |
|
1163 // Fire MSAA event for client area window. |
|
1164 ::NotifyWinEvent(winEvent, hWnd, OBJID_CLIENT, childID); |
|
1165 |
|
1166 // JAWS announces collapsed combobox navigation based on focus events. |
|
1167 if (Compatibility::IsJAWS()) { |
|
1168 if (eventType == nsIAccessibleEvent::EVENT_SELECTION && |
|
1169 accessible->Role() == roles::COMBOBOX_OPTION) { |
|
1170 ::NotifyWinEvent(EVENT_OBJECT_FOCUS, hWnd, OBJID_CLIENT, childID); |
|
1171 } |
|
1172 } |
|
1173 |
|
1174 return NS_OK; |
|
1175 } |
|
1176 |
|
1177 //////////////////////////////////////////////////////////////////////////////// |
|
1178 // AccessibleWrap |
|
1179 |
|
1180 //------- Helper methods --------- |
|
1181 |
|
1182 int32_t |
|
1183 AccessibleWrap::GetChildIDFor(Accessible* aAccessible) |
|
1184 { |
|
1185 // A child ID of the window is required, when we use NotifyWinEvent, |
|
1186 // so that the 3rd party application can call back and get the IAccessible |
|
1187 // the event occurred on. |
|
1188 |
|
1189 // Yes, this means we're only compatibible with 32 bit |
|
1190 // MSAA is only available for 32 bit windows, so it's okay |
|
1191 // XXX: bug 606080 |
|
1192 return aAccessible ? - NS_PTR_TO_INT32(aAccessible->UniqueID()) : 0; |
|
1193 } |
|
1194 |
|
1195 HWND |
|
1196 AccessibleWrap::GetHWNDFor(Accessible* aAccessible) |
|
1197 { |
|
1198 if (aAccessible) { |
|
1199 DocAccessible* document = aAccessible->Document(); |
|
1200 if(!document) |
|
1201 return nullptr; |
|
1202 |
|
1203 // Popup lives in own windows, use its HWND until the popup window is |
|
1204 // hidden to make old JAWS versions work with collapsed comboboxes (see |
|
1205 // discussion in bug 379678). |
|
1206 nsIFrame* frame = aAccessible->GetFrame(); |
|
1207 if (frame) { |
|
1208 nsIWidget* widget = frame->GetNearestWidget(); |
|
1209 if (widget && widget->IsVisible()) { |
|
1210 nsIPresShell* shell = document->PresShell(); |
|
1211 nsViewManager* vm = shell->GetViewManager(); |
|
1212 if (vm) { |
|
1213 nsCOMPtr<nsIWidget> rootWidget; |
|
1214 vm->GetRootWidget(getter_AddRefs(rootWidget)); |
|
1215 // Make sure the accessible belongs to popup. If not then use |
|
1216 // document HWND (which might be different from root widget in the |
|
1217 // case of window emulation). |
|
1218 if (rootWidget != widget) |
|
1219 return static_cast<HWND>(widget->GetNativeData(NS_NATIVE_WINDOW)); |
|
1220 } |
|
1221 } |
|
1222 } |
|
1223 |
|
1224 return static_cast<HWND>(document->GetNativeWindow()); |
|
1225 } |
|
1226 return nullptr; |
|
1227 } |
|
1228 |
|
1229 IDispatch* |
|
1230 AccessibleWrap::NativeAccessible(nsIAccessible* aAccessible) |
|
1231 { |
|
1232 if (!aAccessible) { |
|
1233 NS_WARNING("Not passing in an aAccessible"); |
|
1234 return nullptr; |
|
1235 } |
|
1236 |
|
1237 IAccessible* msaaAccessible = nullptr; |
|
1238 aAccessible->GetNativeInterface(reinterpret_cast<void**>(&msaaAccessible)); |
|
1239 return static_cast<IDispatch*>(msaaAccessible); |
|
1240 } |
|
1241 |
|
1242 Accessible* |
|
1243 AccessibleWrap::GetXPAccessibleFor(const VARIANT& aVarChild) |
|
1244 { |
|
1245 if (aVarChild.vt != VT_I4) |
|
1246 return nullptr; |
|
1247 |
|
1248 // if its us real easy - this seems to always be the case |
|
1249 if (aVarChild.lVal == CHILDID_SELF) |
|
1250 return this; |
|
1251 |
|
1252 if (nsAccUtils::MustPrune(this)) |
|
1253 return nullptr; |
|
1254 |
|
1255 // If lVal negative then it is treated as child ID and we should look for |
|
1256 // accessible through whole accessible subtree including subdocuments. |
|
1257 // Otherwise we treat lVal as index in parent. |
|
1258 |
|
1259 if (aVarChild.lVal < 0) { |
|
1260 // Convert child ID to unique ID. |
|
1261 void* uniqueID = reinterpret_cast<void*>(-aVarChild.lVal); |
|
1262 |
|
1263 DocAccessible* document = Document(); |
|
1264 Accessible* child = |
|
1265 document->GetAccessibleByUniqueIDInSubtree(uniqueID); |
|
1266 |
|
1267 // If it is a document then just return an accessible. |
|
1268 if (IsDoc()) |
|
1269 return child; |
|
1270 |
|
1271 // Otherwise check whether the accessible is a child (this path works for |
|
1272 // ARIA documents and popups). |
|
1273 Accessible* parent = child; |
|
1274 while (parent && parent != document) { |
|
1275 if (parent == this) |
|
1276 return child; |
|
1277 |
|
1278 parent = parent->Parent(); |
|
1279 } |
|
1280 |
|
1281 return nullptr; |
|
1282 } |
|
1283 |
|
1284 // Gecko child indices are 0-based in contrast to indices used in MSAA. |
|
1285 return GetChildAt(aVarChild.lVal - 1); |
|
1286 } |
|
1287 |
|
1288 void |
|
1289 AccessibleWrap::UpdateSystemCaretFor(Accessible* aAccessible) |
|
1290 { |
|
1291 // Move the system caret so that Windows Tablet Edition and tradional ATs with |
|
1292 // off-screen model can follow the caret |
|
1293 ::DestroyCaret(); |
|
1294 |
|
1295 HyperTextAccessible* text = aAccessible->AsHyperText(); |
|
1296 if (!text) |
|
1297 return; |
|
1298 |
|
1299 nsIWidget* widget = nullptr; |
|
1300 nsIntRect caretRect = text->GetCaretRect(&widget); |
|
1301 HWND caretWnd; |
|
1302 if (caretRect.IsEmpty() || !(caretWnd = (HWND)widget->GetNativeData(NS_NATIVE_WINDOW))) { |
|
1303 return; |
|
1304 } |
|
1305 |
|
1306 // Create invisible bitmap for caret, otherwise its appearance interferes |
|
1307 // with Gecko caret |
|
1308 HBITMAP caretBitMap = CreateBitmap(1, caretRect.height, 1, 1, nullptr); |
|
1309 if (::CreateCaret(caretWnd, caretBitMap, 1, caretRect.height)) { // Also destroys the last caret |
|
1310 ::ShowCaret(caretWnd); |
|
1311 RECT windowRect; |
|
1312 ::GetWindowRect(caretWnd, &windowRect); |
|
1313 ::SetCaretPos(caretRect.x - windowRect.left, caretRect.y - windowRect.top); |
|
1314 ::DeleteObject(caretBitMap); |
|
1315 } |
|
1316 } |
|
1317 |
|
1318 ITypeInfo* |
|
1319 AccessibleWrap::GetTI(LCID lcid) |
|
1320 { |
|
1321 if (gTypeInfo) |
|
1322 return gTypeInfo; |
|
1323 |
|
1324 ITypeLib *typeLib = nullptr; |
|
1325 HRESULT hr = LoadRegTypeLib(LIBID_Accessibility, 1, 0, lcid, &typeLib); |
|
1326 if (FAILED(hr)) |
|
1327 return nullptr; |
|
1328 |
|
1329 hr = typeLib->GetTypeInfoOfGuid(IID_IAccessible, &gTypeInfo); |
|
1330 typeLib->Release(); |
|
1331 |
|
1332 if (FAILED(hr)) |
|
1333 return nullptr; |
|
1334 |
|
1335 return gTypeInfo; |
|
1336 } |