|
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 |
|
9 #include "Accessible2_i.c" |
|
10 #include "Accessible2_2_i.c" |
|
11 #include "AccessibleRole.h" |
|
12 #include "AccessibleStates.h" |
|
13 |
|
14 #include "Compatibility.h" |
|
15 #include "ia2AccessibleRelation.h" |
|
16 #include "IUnknownImpl.h" |
|
17 #include "nsCoreUtils.h" |
|
18 #include "nsIAccessibleTypes.h" |
|
19 #include "Relation.h" |
|
20 |
|
21 #include "nsIPersistentProperties2.h" |
|
22 #include "nsISimpleEnumerator.h" |
|
23 |
|
24 using namespace mozilla; |
|
25 using namespace mozilla::a11y; |
|
26 |
|
27 //////////////////////////////////////////////////////////////////////////////// |
|
28 // ia2Accessible |
|
29 //////////////////////////////////////////////////////////////////////////////// |
|
30 |
|
31 STDMETHODIMP |
|
32 ia2Accessible::QueryInterface(REFIID iid, void** ppv) |
|
33 { |
|
34 if (!ppv) |
|
35 return E_INVALIDARG; |
|
36 |
|
37 *ppv = nullptr; |
|
38 |
|
39 if (IID_IAccessible2_2 == iid) |
|
40 *ppv = static_cast<IAccessible2_2*>(this); |
|
41 else if (IID_IAccessible2 == iid && !Compatibility::IsIA2Off()) |
|
42 *ppv = static_cast<IAccessible2*>(this); |
|
43 |
|
44 if (*ppv) { |
|
45 (reinterpret_cast<IUnknown*>(*ppv))->AddRef(); |
|
46 return S_OK; |
|
47 } |
|
48 |
|
49 return E_NOINTERFACE; |
|
50 } |
|
51 |
|
52 //////////////////////////////////////////////////////////////////////////////// |
|
53 // IAccessible2 |
|
54 |
|
55 STDMETHODIMP |
|
56 ia2Accessible::get_nRelations(long* aNRelations) |
|
57 { |
|
58 A11Y_TRYBLOCK_BEGIN |
|
59 |
|
60 if (!aNRelations) |
|
61 return E_INVALIDARG; |
|
62 *aNRelations = 0; |
|
63 |
|
64 AccessibleWrap* acc = static_cast<AccessibleWrap*>(this); |
|
65 if (acc->IsDefunct()) |
|
66 return CO_E_OBJNOTCONNECTED; |
|
67 |
|
68 for (uint32_t idx = 0; idx < ArrayLength(sRelationTypePairs); idx++) { |
|
69 if (sRelationTypePairs[idx].second == IA2_RELATION_NULL) |
|
70 continue; |
|
71 |
|
72 Relation rel = acc->RelationByType(sRelationTypePairs[idx].first); |
|
73 if (rel.Next()) |
|
74 (*aNRelations)++; |
|
75 } |
|
76 return S_OK; |
|
77 |
|
78 A11Y_TRYBLOCK_END |
|
79 } |
|
80 |
|
81 STDMETHODIMP |
|
82 ia2Accessible::get_relation(long aRelationIndex, |
|
83 IAccessibleRelation** aRelation) |
|
84 { |
|
85 A11Y_TRYBLOCK_BEGIN |
|
86 |
|
87 if (!aRelation) |
|
88 return E_INVALIDARG; |
|
89 *aRelation = nullptr; |
|
90 |
|
91 AccessibleWrap* acc = static_cast<AccessibleWrap*>(this); |
|
92 if (acc->IsDefunct()) |
|
93 return CO_E_OBJNOTCONNECTED; |
|
94 |
|
95 long relIdx = 0; |
|
96 for (uint32_t idx = 0; idx < ArrayLength(sRelationTypePairs); idx++) { |
|
97 if (sRelationTypePairs[idx].second == IA2_RELATION_NULL) |
|
98 continue; |
|
99 |
|
100 RelationType relationType = sRelationTypePairs[idx].first; |
|
101 Relation rel = acc->RelationByType(relationType); |
|
102 nsRefPtr<ia2AccessibleRelation> ia2Relation = |
|
103 new ia2AccessibleRelation(relationType, &rel); |
|
104 if (ia2Relation->HasTargets()) { |
|
105 if (relIdx == aRelationIndex) { |
|
106 ia2Relation.forget(aRelation); |
|
107 return S_OK; |
|
108 } |
|
109 |
|
110 relIdx++; |
|
111 } |
|
112 } |
|
113 |
|
114 return E_INVALIDARG; |
|
115 |
|
116 A11Y_TRYBLOCK_END |
|
117 } |
|
118 |
|
119 STDMETHODIMP |
|
120 ia2Accessible::get_relations(long aMaxRelations, |
|
121 IAccessibleRelation** aRelation, |
|
122 long *aNRelations) |
|
123 { |
|
124 A11Y_TRYBLOCK_BEGIN |
|
125 |
|
126 if (!aRelation || !aNRelations) |
|
127 return E_INVALIDARG; |
|
128 *aNRelations = 0; |
|
129 |
|
130 AccessibleWrap* acc = static_cast<AccessibleWrap*>(this); |
|
131 if (acc->IsDefunct()) |
|
132 return CO_E_OBJNOTCONNECTED; |
|
133 |
|
134 for (uint32_t idx = 0; idx < ArrayLength(sRelationTypePairs) && |
|
135 *aNRelations < aMaxRelations; idx++) { |
|
136 if (sRelationTypePairs[idx].second == IA2_RELATION_NULL) |
|
137 continue; |
|
138 |
|
139 RelationType relationType = sRelationTypePairs[idx].first; |
|
140 Relation rel = acc->RelationByType(relationType); |
|
141 nsRefPtr<ia2AccessibleRelation> ia2Rel = |
|
142 new ia2AccessibleRelation(relationType, &rel); |
|
143 if (ia2Rel->HasTargets()) { |
|
144 ia2Rel.forget(aRelation + (*aNRelations)); |
|
145 (*aNRelations)++; |
|
146 } |
|
147 } |
|
148 return S_OK; |
|
149 |
|
150 A11Y_TRYBLOCK_END |
|
151 } |
|
152 |
|
153 STDMETHODIMP |
|
154 ia2Accessible::role(long* aRole) |
|
155 { |
|
156 A11Y_TRYBLOCK_BEGIN |
|
157 |
|
158 if (!aRole) |
|
159 return E_INVALIDARG; |
|
160 *aRole = 0; |
|
161 |
|
162 AccessibleWrap* acc = static_cast<AccessibleWrap*>(this); |
|
163 if (acc->IsDefunct()) |
|
164 return CO_E_OBJNOTCONNECTED; |
|
165 |
|
166 #define ROLE(_geckoRole, stringRole, atkRole, macRole, \ |
|
167 msaaRole, ia2Role, nameRule) \ |
|
168 case roles::_geckoRole: \ |
|
169 *aRole = ia2Role; \ |
|
170 break; |
|
171 |
|
172 a11y::role geckoRole = acc->Role(); |
|
173 switch (geckoRole) { |
|
174 #include "RoleMap.h" |
|
175 default: |
|
176 MOZ_CRASH("Unknown role."); |
|
177 }; |
|
178 |
|
179 #undef ROLE |
|
180 |
|
181 // Special case, if there is a ROLE_ROW inside of a ROLE_TREE_TABLE, then call |
|
182 // the IA2 role a ROLE_OUTLINEITEM. |
|
183 if (geckoRole == roles::ROW) { |
|
184 Accessible* xpParent = acc->Parent(); |
|
185 if (xpParent && xpParent->Role() == roles::TREE_TABLE) |
|
186 *aRole = ROLE_SYSTEM_OUTLINEITEM; |
|
187 } |
|
188 |
|
189 return S_OK; |
|
190 |
|
191 A11Y_TRYBLOCK_END |
|
192 } |
|
193 |
|
194 STDMETHODIMP |
|
195 ia2Accessible::scrollTo(enum IA2ScrollType aScrollType) |
|
196 { |
|
197 A11Y_TRYBLOCK_BEGIN |
|
198 |
|
199 AccessibleWrap* acc = static_cast<AccessibleWrap*>(this); |
|
200 if (acc->IsDefunct()) |
|
201 return CO_E_OBJNOTCONNECTED; |
|
202 |
|
203 nsCoreUtils::ScrollTo(acc->Document()->PresShell(), |
|
204 acc->GetContent(), aScrollType); |
|
205 return S_OK; |
|
206 |
|
207 A11Y_TRYBLOCK_END |
|
208 } |
|
209 |
|
210 STDMETHODIMP |
|
211 ia2Accessible::scrollToPoint(enum IA2CoordinateType aCoordType, |
|
212 long aX, long aY) |
|
213 { |
|
214 A11Y_TRYBLOCK_BEGIN |
|
215 |
|
216 AccessibleWrap* acc = static_cast<AccessibleWrap*>(this); |
|
217 if (acc->IsDefunct()) |
|
218 return CO_E_OBJNOTCONNECTED; |
|
219 |
|
220 uint32_t geckoCoordType = (aCoordType == IA2_COORDTYPE_SCREEN_RELATIVE) ? |
|
221 nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE : |
|
222 nsIAccessibleCoordinateType::COORDTYPE_PARENT_RELATIVE; |
|
223 |
|
224 nsresult rv = acc->ScrollToPoint(geckoCoordType, aX, aY); |
|
225 return GetHRESULT(rv); |
|
226 |
|
227 A11Y_TRYBLOCK_END |
|
228 } |
|
229 |
|
230 STDMETHODIMP |
|
231 ia2Accessible::get_groupPosition(long* aGroupLevel, |
|
232 long* aSimilarItemsInGroup, |
|
233 long* aPositionInGroup) |
|
234 { |
|
235 A11Y_TRYBLOCK_BEGIN |
|
236 |
|
237 if (!aGroupLevel || !aSimilarItemsInGroup || !aPositionInGroup) |
|
238 return E_INVALIDARG; |
|
239 |
|
240 *aGroupLevel = 0; |
|
241 *aSimilarItemsInGroup = 0; |
|
242 *aPositionInGroup = 0; |
|
243 |
|
244 AccessibleWrap* acc = static_cast<AccessibleWrap*>(this); |
|
245 if (acc->IsDefunct()) |
|
246 return CO_E_OBJNOTCONNECTED; |
|
247 |
|
248 GroupPos groupPos = acc->GroupPosition(); |
|
249 |
|
250 // Group information for accessibles having level only (like html headings |
|
251 // elements) isn't exposed by this method. AT should look for 'level' object |
|
252 // attribute. |
|
253 if (!groupPos.setSize && !groupPos.posInSet) |
|
254 return S_FALSE; |
|
255 |
|
256 *aGroupLevel = groupPos.level; |
|
257 *aSimilarItemsInGroup = groupPos.setSize; |
|
258 *aPositionInGroup = groupPos.posInSet; |
|
259 |
|
260 return S_OK; |
|
261 |
|
262 A11Y_TRYBLOCK_END |
|
263 } |
|
264 |
|
265 STDMETHODIMP |
|
266 ia2Accessible::get_states(AccessibleStates* aStates) |
|
267 { |
|
268 A11Y_TRYBLOCK_BEGIN |
|
269 |
|
270 if (!aStates) |
|
271 return E_INVALIDARG; |
|
272 *aStates = 0; |
|
273 |
|
274 // XXX: bug 344674 should come with better approach that we have here. |
|
275 |
|
276 AccessibleWrap* acc = static_cast<AccessibleWrap*>(this); |
|
277 uint64_t state = acc->State(); |
|
278 |
|
279 if (state & states::INVALID) |
|
280 *aStates |= IA2_STATE_INVALID_ENTRY; |
|
281 if (state & states::REQUIRED) |
|
282 *aStates |= IA2_STATE_REQUIRED; |
|
283 |
|
284 // The following IA2 states are not supported by Gecko |
|
285 // IA2_STATE_ARMED |
|
286 // IA2_STATE_MANAGES_DESCENDANTS |
|
287 // IA2_STATE_ICONIFIED |
|
288 // IA2_STATE_INVALID // This is not a state, it is the absence of a state |
|
289 |
|
290 if (state & states::ACTIVE) |
|
291 *aStates |= IA2_STATE_ACTIVE; |
|
292 if (state & states::DEFUNCT) |
|
293 *aStates |= IA2_STATE_DEFUNCT; |
|
294 if (state & states::EDITABLE) |
|
295 *aStates |= IA2_STATE_EDITABLE; |
|
296 if (state & states::HORIZONTAL) |
|
297 *aStates |= IA2_STATE_HORIZONTAL; |
|
298 if (state & states::MODAL) |
|
299 *aStates |= IA2_STATE_MODAL; |
|
300 if (state & states::MULTI_LINE) |
|
301 *aStates |= IA2_STATE_MULTI_LINE; |
|
302 if (state & states::OPAQUE1) |
|
303 *aStates |= IA2_STATE_OPAQUE; |
|
304 if (state & states::SELECTABLE_TEXT) |
|
305 *aStates |= IA2_STATE_SELECTABLE_TEXT; |
|
306 if (state & states::SINGLE_LINE) |
|
307 *aStates |= IA2_STATE_SINGLE_LINE; |
|
308 if (state & states::STALE) |
|
309 *aStates |= IA2_STATE_STALE; |
|
310 if (state & states::SUPPORTS_AUTOCOMPLETION) |
|
311 *aStates |= IA2_STATE_SUPPORTS_AUTOCOMPLETION; |
|
312 if (state & states::TRANSIENT) |
|
313 *aStates |= IA2_STATE_TRANSIENT; |
|
314 if (state & states::VERTICAL) |
|
315 *aStates |= IA2_STATE_VERTICAL; |
|
316 if (state & states::CHECKED) |
|
317 *aStates |= IA2_STATE_CHECKABLE; |
|
318 if (state & states::PINNED) |
|
319 *aStates |= IA2_STATE_PINNED; |
|
320 |
|
321 return S_OK; |
|
322 |
|
323 A11Y_TRYBLOCK_END |
|
324 } |
|
325 |
|
326 STDMETHODIMP |
|
327 ia2Accessible::get_extendedRole(BSTR* aExtendedRole) |
|
328 { |
|
329 A11Y_TRYBLOCK_BEGIN |
|
330 |
|
331 if (!aExtendedRole) |
|
332 return E_INVALIDARG; |
|
333 |
|
334 *aExtendedRole = nullptr; |
|
335 return E_NOTIMPL; |
|
336 |
|
337 A11Y_TRYBLOCK_END |
|
338 } |
|
339 |
|
340 STDMETHODIMP |
|
341 ia2Accessible::get_localizedExtendedRole(BSTR* aLocalizedExtendedRole) |
|
342 { |
|
343 A11Y_TRYBLOCK_BEGIN |
|
344 |
|
345 if (!aLocalizedExtendedRole) |
|
346 return E_INVALIDARG; |
|
347 |
|
348 *aLocalizedExtendedRole = nullptr; |
|
349 return E_NOTIMPL; |
|
350 |
|
351 A11Y_TRYBLOCK_END |
|
352 } |
|
353 |
|
354 STDMETHODIMP |
|
355 ia2Accessible::get_nExtendedStates(long* aNExtendedStates) |
|
356 { |
|
357 A11Y_TRYBLOCK_BEGIN |
|
358 |
|
359 if (!aNExtendedStates) |
|
360 return E_INVALIDARG; |
|
361 |
|
362 *aNExtendedStates = 0; |
|
363 return E_NOTIMPL; |
|
364 |
|
365 A11Y_TRYBLOCK_END |
|
366 } |
|
367 |
|
368 STDMETHODIMP |
|
369 ia2Accessible::get_extendedStates(long aMaxExtendedStates, |
|
370 BSTR** aExtendedStates, |
|
371 long* aNExtendedStates) |
|
372 { |
|
373 A11Y_TRYBLOCK_BEGIN |
|
374 |
|
375 if (!aExtendedStates || !aNExtendedStates) |
|
376 return E_INVALIDARG; |
|
377 |
|
378 *aExtendedStates = nullptr; |
|
379 *aNExtendedStates = 0; |
|
380 return E_NOTIMPL; |
|
381 |
|
382 A11Y_TRYBLOCK_END |
|
383 } |
|
384 |
|
385 STDMETHODIMP |
|
386 ia2Accessible::get_localizedExtendedStates(long aMaxLocalizedExtendedStates, |
|
387 BSTR** aLocalizedExtendedStates, |
|
388 long* aNLocalizedExtendedStates) |
|
389 { |
|
390 A11Y_TRYBLOCK_BEGIN |
|
391 |
|
392 if (!aLocalizedExtendedStates || !aNLocalizedExtendedStates) |
|
393 return E_INVALIDARG; |
|
394 |
|
395 *aLocalizedExtendedStates = nullptr; |
|
396 *aNLocalizedExtendedStates = 0; |
|
397 return E_NOTIMPL; |
|
398 |
|
399 A11Y_TRYBLOCK_END |
|
400 } |
|
401 |
|
402 STDMETHODIMP |
|
403 ia2Accessible::get_uniqueID(long* aUniqueID) |
|
404 { |
|
405 A11Y_TRYBLOCK_BEGIN |
|
406 |
|
407 if (!aUniqueID) |
|
408 return E_INVALIDARG; |
|
409 |
|
410 AccessibleWrap* acc = static_cast<AccessibleWrap*>(this); |
|
411 *aUniqueID = - reinterpret_cast<intptr_t>(acc->UniqueID()); |
|
412 return S_OK; |
|
413 |
|
414 A11Y_TRYBLOCK_END |
|
415 } |
|
416 |
|
417 STDMETHODIMP |
|
418 ia2Accessible::get_windowHandle(HWND* aWindowHandle) |
|
419 { |
|
420 A11Y_TRYBLOCK_BEGIN |
|
421 |
|
422 if (!aWindowHandle) |
|
423 return E_INVALIDARG; |
|
424 *aWindowHandle = 0; |
|
425 |
|
426 AccessibleWrap* acc = static_cast<AccessibleWrap*>(this); |
|
427 if (acc->IsDefunct()) |
|
428 return CO_E_OBJNOTCONNECTED; |
|
429 |
|
430 *aWindowHandle = AccessibleWrap::GetHWNDFor(acc); |
|
431 return S_OK; |
|
432 |
|
433 A11Y_TRYBLOCK_END |
|
434 } |
|
435 |
|
436 STDMETHODIMP |
|
437 ia2Accessible::get_indexInParent(long* aIndexInParent) |
|
438 { |
|
439 A11Y_TRYBLOCK_BEGIN |
|
440 |
|
441 if (!aIndexInParent) |
|
442 return E_INVALIDARG; |
|
443 *aIndexInParent = -1; |
|
444 |
|
445 AccessibleWrap* acc = static_cast<AccessibleWrap*>(this); |
|
446 if (acc->IsDefunct()) |
|
447 return CO_E_OBJNOTCONNECTED; |
|
448 |
|
449 *aIndexInParent = acc->IndexInParent(); |
|
450 if (*aIndexInParent == -1) |
|
451 return S_FALSE; |
|
452 |
|
453 return S_OK; |
|
454 |
|
455 A11Y_TRYBLOCK_END |
|
456 } |
|
457 |
|
458 STDMETHODIMP |
|
459 ia2Accessible::get_locale(IA2Locale* aLocale) |
|
460 { |
|
461 A11Y_TRYBLOCK_BEGIN |
|
462 |
|
463 if (!aLocale) |
|
464 return E_INVALIDARG; |
|
465 |
|
466 // Language codes consist of a primary code and a possibly empty series of |
|
467 // subcodes: language-code = primary-code ( "-" subcode )* |
|
468 // Two-letter primary codes are reserved for [ISO639] language abbreviations. |
|
469 // Any two-letter subcode is understood to be a [ISO3166] country code. |
|
470 |
|
471 AccessibleWrap* acc = static_cast<AccessibleWrap*>(this); |
|
472 if (acc->IsDefunct()) |
|
473 return CO_E_OBJNOTCONNECTED; |
|
474 |
|
475 nsAutoString lang; |
|
476 acc->Language(lang); |
|
477 |
|
478 // If primary code consists from two letters then expose it as language. |
|
479 int32_t offset = lang.FindChar('-', 0); |
|
480 if (offset == -1) { |
|
481 if (lang.Length() == 2) { |
|
482 aLocale->language = ::SysAllocString(lang.get()); |
|
483 return S_OK; |
|
484 } |
|
485 } else if (offset == 2) { |
|
486 aLocale->language = ::SysAllocStringLen(lang.get(), 2); |
|
487 |
|
488 // If the first subcode consists from two letters then expose it as |
|
489 // country. |
|
490 offset = lang.FindChar('-', 3); |
|
491 if (offset == -1) { |
|
492 if (lang.Length() == 5) { |
|
493 aLocale->country = ::SysAllocString(lang.get() + 3); |
|
494 return S_OK; |
|
495 } |
|
496 } else if (offset == 5) { |
|
497 aLocale->country = ::SysAllocStringLen(lang.get() + 3, 2); |
|
498 } |
|
499 } |
|
500 |
|
501 // Expose as a string if primary code or subcode cannot point to language or |
|
502 // country abbreviations or if there are more than one subcode. |
|
503 aLocale->variant = ::SysAllocString(lang.get()); |
|
504 return S_OK; |
|
505 |
|
506 A11Y_TRYBLOCK_END |
|
507 } |
|
508 |
|
509 STDMETHODIMP |
|
510 ia2Accessible::get_attributes(BSTR* aAttributes) |
|
511 { |
|
512 A11Y_TRYBLOCK_BEGIN |
|
513 |
|
514 if (!aAttributes) |
|
515 return E_INVALIDARG; |
|
516 *aAttributes = nullptr; |
|
517 |
|
518 AccessibleWrap* acc = static_cast<AccessibleWrap*>(this); |
|
519 if (acc->IsDefunct()) |
|
520 return CO_E_OBJNOTCONNECTED; |
|
521 |
|
522 // The format is name:value;name:value; with \ for escaping these |
|
523 // characters ":;=,\". |
|
524 nsCOMPtr<nsIPersistentProperties> attributes = acc->Attributes(); |
|
525 return ConvertToIA2Attributes(attributes, aAttributes); |
|
526 |
|
527 A11Y_TRYBLOCK_END |
|
528 } |
|
529 |
|
530 //////////////////////////////////////////////////////////////////////////////// |
|
531 // IAccessible2_2 |
|
532 |
|
533 STDMETHODIMP |
|
534 ia2Accessible::get_attribute(BSTR name, VARIANT* aAttribute) |
|
535 { |
|
536 A11Y_TRYBLOCK_BEGIN |
|
537 |
|
538 if (!aAttribute) |
|
539 return E_INVALIDARG; |
|
540 |
|
541 return E_NOTIMPL; |
|
542 |
|
543 A11Y_TRYBLOCK_END |
|
544 } |
|
545 |
|
546 STDMETHODIMP |
|
547 ia2Accessible::get_accessibleWithCaret(IUnknown** aAccessible, |
|
548 long* aCaretOffset) |
|
549 { |
|
550 A11Y_TRYBLOCK_BEGIN |
|
551 |
|
552 if (!aAccessible || !aCaretOffset) |
|
553 return E_INVALIDARG; |
|
554 |
|
555 *aAccessible = nullptr; |
|
556 *aCaretOffset = -1; |
|
557 return E_NOTIMPL; |
|
558 |
|
559 A11Y_TRYBLOCK_END |
|
560 } |
|
561 |
|
562 STDMETHODIMP |
|
563 ia2Accessible::get_relationTargetsOfType(BSTR aType, |
|
564 long aMaxTargets, |
|
565 IUnknown*** aTargets, |
|
566 long* aNTargets) |
|
567 { |
|
568 A11Y_TRYBLOCK_BEGIN |
|
569 |
|
570 if (!aTargets || !aNTargets) |
|
571 return E_INVALIDARG; |
|
572 *aNTargets = 0; |
|
573 |
|
574 Maybe<RelationType> relationType; |
|
575 for (uint32_t idx = 0; idx < ArrayLength(sRelationTypePairs); idx++) { |
|
576 if (wcscmp(aType, sRelationTypePairs[idx].second) == 0) { |
|
577 relationType.construct(sRelationTypePairs[idx].first); |
|
578 break; |
|
579 } |
|
580 } |
|
581 if (relationType.empty()) |
|
582 return E_INVALIDARG; |
|
583 |
|
584 AccessibleWrap* acc = static_cast<AccessibleWrap*>(this); |
|
585 if (acc->IsDefunct()) |
|
586 return CO_E_OBJNOTCONNECTED; |
|
587 |
|
588 Relation rel = acc->RelationByType(relationType.ref()); |
|
589 |
|
590 nsTArray<Accessible*> targets; |
|
591 Accessible* target = nullptr; |
|
592 while ((target = rel.Next()) && |
|
593 static_cast<long>(targets.Length()) <= aMaxTargets) |
|
594 targets.AppendElement(target); |
|
595 |
|
596 *aNTargets = targets.Length(); |
|
597 *aTargets = static_cast<IUnknown**>( |
|
598 ::CoTaskMemAlloc(sizeof(IUnknown*) * *aNTargets)); |
|
599 if (!*aTargets) |
|
600 return E_OUTOFMEMORY; |
|
601 |
|
602 for (int32_t i = 0; i < *aNTargets; i++) { |
|
603 AccessibleWrap* target= static_cast<AccessibleWrap*>(targets[i]); |
|
604 (*aTargets)[i] = static_cast<IAccessible2*>(target); |
|
605 (*aTargets)[i]->AddRef(); |
|
606 } |
|
607 |
|
608 return S_OK; |
|
609 |
|
610 A11Y_TRYBLOCK_END |
|
611 } |
|
612 |
|
613 //////////////////////////////////////////////////////////////////////////////// |
|
614 // Helpers |
|
615 |
|
616 HRESULT |
|
617 ia2Accessible::ConvertToIA2Attributes(nsIPersistentProperties* aAttributes, |
|
618 BSTR* aIA2Attributes) |
|
619 { |
|
620 *aIA2Attributes = nullptr; |
|
621 |
|
622 // The format is name:value;name:value; with \ for escaping these |
|
623 // characters ":;=,\". |
|
624 |
|
625 if (!aAttributes) |
|
626 return S_FALSE; |
|
627 |
|
628 nsCOMPtr<nsISimpleEnumerator> propEnum; |
|
629 aAttributes->Enumerate(getter_AddRefs(propEnum)); |
|
630 if (!propEnum) |
|
631 return E_FAIL; |
|
632 |
|
633 nsAutoString strAttrs; |
|
634 |
|
635 const char kCharsToEscape[] = ":;=,\\"; |
|
636 |
|
637 bool hasMore = false; |
|
638 while (NS_SUCCEEDED(propEnum->HasMoreElements(&hasMore)) && hasMore) { |
|
639 nsCOMPtr<nsISupports> propSupports; |
|
640 propEnum->GetNext(getter_AddRefs(propSupports)); |
|
641 |
|
642 nsCOMPtr<nsIPropertyElement> propElem(do_QueryInterface(propSupports)); |
|
643 if (!propElem) |
|
644 return E_FAIL; |
|
645 |
|
646 nsAutoCString name; |
|
647 if (NS_FAILED(propElem->GetKey(name))) |
|
648 return E_FAIL; |
|
649 |
|
650 int32_t offset = 0; |
|
651 while ((offset = name.FindCharInSet(kCharsToEscape, offset)) != kNotFound) { |
|
652 name.Insert('\\', offset); |
|
653 offset += 2; |
|
654 } |
|
655 |
|
656 nsAutoString value; |
|
657 if (NS_FAILED(propElem->GetValue(value))) |
|
658 return E_FAIL; |
|
659 |
|
660 offset = 0; |
|
661 while ((offset = value.FindCharInSet(kCharsToEscape, offset)) != kNotFound) { |
|
662 value.Insert('\\', offset); |
|
663 offset += 2; |
|
664 } |
|
665 |
|
666 AppendUTF8toUTF16(name, strAttrs); |
|
667 strAttrs.Append(':'); |
|
668 strAttrs.Append(value); |
|
669 strAttrs.Append(';'); |
|
670 } |
|
671 |
|
672 if (strAttrs.IsEmpty()) |
|
673 return S_FALSE; |
|
674 |
|
675 *aIA2Attributes = ::SysAllocStringLen(strAttrs.get(), strAttrs.Length()); |
|
676 return *aIA2Attributes ? S_OK : E_OUTOFMEMORY; |
|
677 } |