accessible/src/windows/ia2/ia2Accessible.cpp

branch
TOR_BUG_9701
changeset 8
97036ab72558
equal deleted inserted replaced
-1:000000000000 0:175e70b41de8
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 }

mercurial