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 "Accessible2.h"
8 #include "RemoteAccessible.h"
9 #include "ia2AccessibleRelation.h"
10 #include "ia2AccessibleValue.h"
11 #include "IGeckoCustom.h"
12 #include "mozilla/a11y/DocAccessibleParent.h"
13 #include "DocAccessible.h"
14 #include "mozilla/a11y/DocManager.h"
15 #include "mozilla/dom/Element.h"
16 #include "mozilla/dom/BrowserParent.h"
17 #include "mozilla/Unused.h"
18 #include "mozilla/a11y/Platform.h"
19 #include "RelationType.h"
20 #include "mozilla/a11y/Role.h"
21 #include "mozilla/StaticPrefs_accessibility.h"
22
23 #include <comutil.h>
24
25 static const VARIANT kChildIdSelf = {{{VT_I4}}};
26
27 namespace mozilla {
28 namespace a11y {
29
GetCOMInterface(void ** aOutAccessible) const30 bool RemoteAccessible::GetCOMInterface(void** aOutAccessible) const {
31 if (!aOutAccessible) {
32 return false;
33 }
34
35 // This should never be called if the cache is enabled. We can't get a COM
36 // proxy from the content process in that case. Instead, the code below would
37 // return an MsaaAccessible from our process which would end up calling
38 // methods here in RemoteAccessible, causing infinite recursion.
39 MOZ_ASSERT(!StaticPrefs::accessibility_cache_enabled_AtStartup());
40 if (!mCOMProxy && mSafeToRecurse) {
41 RemoteAccessible* thisPtr = const_cast<RemoteAccessible*>(this);
42 // See if we can lazily obtain a COM proxy
43 MsaaAccessible* msaa = MsaaAccessible::GetFrom(thisPtr);
44 bool isDefunct = false;
45 // NB: Don't pass CHILDID_SELF here, use the absolute MSAA ID. Otherwise
46 // GetIAccessibleFor will recurse into this function and we will just
47 // overflow the stack.
48 VARIANT realId = {{{VT_I4}}};
49 realId.ulVal = msaa->GetExistingID();
50 MOZ_DIAGNOSTIC_ASSERT(realId.ulVal != CHILDID_SELF);
51 thisPtr->mCOMProxy = msaa->GetIAccessibleFor(realId, &isDefunct);
52 }
53
54 RefPtr<IAccessible> addRefed = mCOMProxy;
55 addRefed.forget(aOutAccessible);
56 return !!mCOMProxy;
57 }
58
59 /**
60 * Specializations of this template map an IAccessible type to its IID
61 */
62 template <typename Interface>
63 struct InterfaceIID {};
64
65 template <>
66 struct InterfaceIID<IAccessibleValue> {
Valuemozilla::a11y::InterfaceIID67 static REFIID Value() { return IID_IAccessibleValue; }
68 };
69
70 template <>
71 struct InterfaceIID<IAccessibleText> {
Valuemozilla::a11y::InterfaceIID72 static REFIID Value() { return IID_IAccessibleText; }
73 };
74
75 template <>
76 struct InterfaceIID<IAccessibleHyperlink> {
Valuemozilla::a11y::InterfaceIID77 static REFIID Value() { return IID_IAccessibleHyperlink; }
78 };
79
80 template <>
81 struct InterfaceIID<IGeckoCustom> {
Valuemozilla::a11y::InterfaceIID82 static REFIID Value() { return IID_IGeckoCustom; }
83 };
84
85 template <>
86 struct InterfaceIID<IAccessible2_2> {
Valuemozilla::a11y::InterfaceIID87 static REFIID Value() { return IID_IAccessible2_2; }
88 };
89
90 /**
91 * Get the COM proxy for this proxy accessible and QueryInterface it with the
92 * correct IID
93 */
94 template <typename Interface>
QueryInterface(const RemoteAccessible * aProxy)95 static already_AddRefed<Interface> QueryInterface(
96 const RemoteAccessible* aProxy) {
97 RefPtr<IAccessible> acc;
98 if (!aProxy->GetCOMInterface((void**)getter_AddRefs(acc))) {
99 return nullptr;
100 }
101
102 RefPtr<Interface> acc2;
103 if (FAILED(acc->QueryInterface(InterfaceIID<Interface>::Value(),
104 (void**)getter_AddRefs(acc2)))) {
105 return nullptr;
106 }
107
108 return acc2.forget();
109 }
110
GetProxyFor(DocAccessibleParent * aDoc,IUnknown * aCOMProxy)111 static RemoteAccessible* GetProxyFor(DocAccessibleParent* aDoc,
112 IUnknown* aCOMProxy) {
113 RefPtr<IGeckoCustom> custom;
114 if (FAILED(aCOMProxy->QueryInterface(IID_IGeckoCustom,
115 (void**)getter_AddRefs(custom)))) {
116 return nullptr;
117 }
118
119 uint64_t id;
120 if (FAILED(custom->get_ID(&id))) {
121 return nullptr;
122 }
123
124 return aDoc->GetAccessible(id);
125 }
126
Name(nsString & aName) const127 ENameValueFlag RemoteAccessible::Name(nsString& aName) const {
128 if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
129 return RemoteAccessibleBase<RemoteAccessible>::Name(aName);
130 }
131
132 /* The return values here exist only to match behvaiour required
133 * by the header declaration of this function. On Mac, we'd like
134 * to return the associated ENameValueFlag, but we don't have
135 * access to that here, so we return a dummy eNameOK value instead.
136 */
137 aName.Truncate();
138 RefPtr<IAccessible> acc;
139 if (!GetCOMInterface((void**)getter_AddRefs(acc))) {
140 return eNameOK;
141 }
142
143 BSTR result;
144 HRESULT hr = acc->get_accName(kChildIdSelf, &result);
145 _bstr_t resultWrap(result, false);
146 if (FAILED(hr)) {
147 return eNameOK;
148 }
149 aName = (wchar_t*)resultWrap;
150 return eNameOK;
151 }
152
Value(nsString & aValue) const153 void RemoteAccessible::Value(nsString& aValue) const {
154 if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
155 RemoteAccessibleBase<RemoteAccessible>::Value(aValue);
156 return;
157 }
158
159 aValue.Truncate();
160 RefPtr<IAccessible> acc;
161 if (!GetCOMInterface((void**)getter_AddRefs(acc))) {
162 return;
163 }
164
165 BSTR result;
166 HRESULT hr = acc->get_accValue(kChildIdSelf, &result);
167 _bstr_t resultWrap(result, false);
168 if (FAILED(hr)) {
169 return;
170 }
171 aValue = (wchar_t*)resultWrap;
172 }
173
Step() const174 double RemoteAccessible::Step() const {
175 if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
176 return RemoteAccessibleBase<RemoteAccessible>::Step();
177 }
178
179 RefPtr<IGeckoCustom> custom = QueryInterface<IGeckoCustom>(this);
180 if (!custom) {
181 return 0;
182 }
183
184 double increment;
185 HRESULT hr = custom->get_minimumIncrement(&increment);
186 if (FAILED(hr)) {
187 return 0;
188 }
189
190 return increment;
191 }
192
Description(nsString & aDesc) const193 void RemoteAccessible::Description(nsString& aDesc) const {
194 if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
195 return RemoteAccessibleBase<RemoteAccessible>::Description(aDesc);
196 }
197
198 aDesc.Truncate();
199 RefPtr<IAccessible> acc;
200 if (!GetCOMInterface((void**)getter_AddRefs(acc))) {
201 return;
202 }
203
204 BSTR result;
205 HRESULT hr = acc->get_accDescription(kChildIdSelf, &result);
206 _bstr_t resultWrap(result, false);
207 if (FAILED(hr)) {
208 return;
209 }
210 aDesc = (wchar_t*)resultWrap;
211 }
212
State()213 uint64_t RemoteAccessible::State() {
214 if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
215 return RemoteAccessibleBase<RemoteAccessible>::State();
216 }
217
218 RefPtr<IGeckoCustom> custom = QueryInterface<IGeckoCustom>(this);
219 if (!custom) {
220 return 0;
221 }
222
223 uint64_t state;
224 HRESULT hr = custom->get_mozState(&state);
225 if (FAILED(hr)) {
226 return 0;
227 }
228 return state;
229 }
230
Bounds() const231 LayoutDeviceIntRect RemoteAccessible::Bounds() const {
232 if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
233 return RemoteAccessibleBase<RemoteAccessible>::Bounds();
234 }
235
236 LayoutDeviceIntRect rect;
237
238 RefPtr<IAccessible> acc;
239 if (!GetCOMInterface((void**)getter_AddRefs(acc))) {
240 return rect;
241 }
242
243 long left;
244 long top;
245 long width;
246 long height;
247 HRESULT hr = acc->accLocation(&left, &top, &width, &height, kChildIdSelf);
248 if (FAILED(hr)) {
249 return rect;
250 }
251 rect.SetRect(left, top, width, height);
252 return rect;
253 }
254
BoundsInCSSPixels()255 nsIntRect RemoteAccessible::BoundsInCSSPixels() {
256 RefPtr<IGeckoCustom> custom = QueryInterface<IGeckoCustom>(this);
257 if (!custom) {
258 return nsIntRect();
259 }
260
261 nsIntRect rect;
262 Unused << custom->get_boundsInCSSPixels(&rect.x, &rect.y, &rect.width,
263 &rect.height);
264 return rect;
265 }
266
Language(nsString & aLocale)267 void RemoteAccessible::Language(nsString& aLocale) {
268 aLocale.Truncate();
269
270 RefPtr<IAccessible> acc;
271 if (!GetCOMInterface((void**)getter_AddRefs(acc))) {
272 return;
273 }
274
275 RefPtr<IAccessible2> acc2;
276 if (FAILED(acc->QueryInterface(IID_IAccessible2,
277 (void**)getter_AddRefs(acc2)))) {
278 return;
279 }
280
281 IA2Locale locale;
282 HRESULT hr = acc2->get_locale(&locale);
283
284 _bstr_t langWrap(locale.language, false);
285 _bstr_t countryWrap(locale.country, false);
286 _bstr_t variantWrap(locale.variant, false);
287
288 if (FAILED(hr)) {
289 return;
290 }
291
292 // The remaining code should essentially be the inverse of the
293 // ia2Accessible::get_locale conversion to IA2Locale.
294
295 if (!!variantWrap) {
296 aLocale = (wchar_t*)variantWrap;
297 return;
298 }
299
300 if (!!langWrap) {
301 aLocale = (wchar_t*)langWrap;
302 if (!!countryWrap) {
303 aLocale += L"-";
304 aLocale += (wchar_t*)countryWrap;
305 }
306 }
307 }
308
IsEscapedChar(const wchar_t c)309 static bool IsEscapedChar(const wchar_t c) {
310 return c == L'\\' || c == L':' || c == ',' || c == '=' || c == ';';
311 }
312
313 // XXX: This creates an all-strings AccAttributes, this isn't ideal
314 // but an OK temporary stop-gap before IPC goes full IPDL.
ConvertBSTRAttributesToAccAttributes(const nsAString & aStr,RefPtr<AccAttributes> & aAttrs)315 static bool ConvertBSTRAttributesToAccAttributes(
316 const nsAString& aStr, RefPtr<AccAttributes>& aAttrs) {
317 enum { eName = 0, eValue = 1, eNumStates } state;
318 nsString tokens[eNumStates];
319 auto itr = aStr.BeginReading(), end = aStr.EndReading();
320
321 state = eName;
322 while (itr != end) {
323 switch (*itr) {
324 case L'\\':
325 // Skip the backslash so that we're looking at the escaped char
326 ++itr;
327 if (itr == end || !IsEscapedChar(*itr)) {
328 // Invalid state
329 return false;
330 }
331 break;
332 case L':':
333 if (state != eName) {
334 // Bad, should be looking at name
335 return false;
336 }
337 state = eValue;
338 ++itr;
339 continue;
340 case L';': {
341 if (state != eValue) {
342 // Bad, should be looking at value
343 return false;
344 }
345 state = eName;
346 RefPtr<nsAtom> nameAtom = NS_Atomize(tokens[eName]);
347 aAttrs->SetAttribute(nameAtom, std::move(tokens[eValue]));
348 tokens[eName].Truncate();
349 ++itr;
350 continue;
351 }
352 default:
353 break;
354 }
355 tokens[state] += *itr;
356 ++itr;
357 }
358 return true;
359 }
360
Attributes()361 already_AddRefed<AccAttributes> RemoteAccessible::Attributes() {
362 if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
363 return RemoteAccessibleBase<RemoteAccessible>::Attributes();
364 }
365 RefPtr<AccAttributes> attrsObj = new AccAttributes();
366 RefPtr<IAccessible> acc;
367 if (!GetCOMInterface((void**)getter_AddRefs(acc))) {
368 return attrsObj.forget();
369 }
370
371 RefPtr<IAccessible2> acc2;
372 if (FAILED(acc->QueryInterface(IID_IAccessible2,
373 (void**)getter_AddRefs(acc2)))) {
374 return attrsObj.forget();
375 }
376
377 BSTR attrs;
378 HRESULT hr = acc2->get_attributes(&attrs);
379 _bstr_t attrsWrap(attrs, false);
380 if (FAILED(hr)) {
381 return attrsObj.forget();
382 }
383
384 ConvertBSTRAttributesToAccAttributes(
385 nsDependentString((wchar_t*)attrs, attrsWrap.length()), attrsObj);
386 return attrsObj.forget();
387 }
388
RelationByType(RelationType aType) const389 nsTArray<RemoteAccessible*> RemoteAccessible::RelationByType(
390 RelationType aType) const {
391 RefPtr<IAccessible2_2> acc = QueryInterface<IAccessible2_2>(this);
392 if (!acc) {
393 return nsTArray<RemoteAccessible*>();
394 }
395
396 _bstr_t relationType;
397 for (uint32_t idx = 0; idx < ArrayLength(sRelationTypePairs); idx++) {
398 if (aType == sRelationTypePairs[idx].first) {
399 relationType = sRelationTypePairs[idx].second;
400 break;
401 }
402 }
403
404 if (!relationType) {
405 return nsTArray<RemoteAccessible*>();
406 }
407
408 IUnknown** targets;
409 long nTargets = 0;
410 HRESULT hr =
411 acc->get_relationTargetsOfType(relationType, 0, &targets, &nTargets);
412 if (FAILED(hr)) {
413 return nsTArray<RemoteAccessible*>();
414 }
415
416 nsTArray<RemoteAccessible*> proxies;
417 for (long idx = 0; idx < nTargets; idx++) {
418 IUnknown* target = targets[idx];
419 proxies.AppendElement(GetProxyFor(Document(), target));
420 target->Release();
421 }
422 CoTaskMemFree(targets);
423
424 return proxies;
425 }
426
CurValue() const427 double RemoteAccessible::CurValue() const {
428 if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
429 return RemoteAccessibleBase<RemoteAccessible>::CurValue();
430 }
431
432 RefPtr<IAccessibleValue> acc = QueryInterface<IAccessibleValue>(this);
433 if (!acc) {
434 return UnspecifiedNaN<double>();
435 }
436
437 VARIANT currentValue;
438 HRESULT hr = acc->get_currentValue(¤tValue);
439 if (FAILED(hr) || currentValue.vt != VT_R8) {
440 return UnspecifiedNaN<double>();
441 }
442
443 return currentValue.dblVal;
444 }
445
SetCurValue(double aValue)446 bool RemoteAccessible::SetCurValue(double aValue) {
447 RefPtr<IAccessibleValue> acc = QueryInterface<IAccessibleValue>(this);
448 if (!acc) {
449 return false;
450 }
451
452 VARIANT currentValue;
453 VariantInit(¤tValue);
454 currentValue.vt = VT_R8;
455 currentValue.dblVal = aValue;
456 HRESULT hr = acc->setCurrentValue(currentValue);
457 return SUCCEEDED(hr);
458 }
459
MinValue() const460 double RemoteAccessible::MinValue() const {
461 if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
462 return RemoteAccessibleBase<RemoteAccessible>::MinValue();
463 }
464
465 RefPtr<IAccessibleValue> acc = QueryInterface<IAccessibleValue>(this);
466 if (!acc) {
467 return UnspecifiedNaN<double>();
468 }
469
470 VARIANT minimumValue;
471 HRESULT hr = acc->get_minimumValue(&minimumValue);
472 if (FAILED(hr) || minimumValue.vt != VT_R8) {
473 return UnspecifiedNaN<double>();
474 }
475
476 return minimumValue.dblVal;
477 }
478
MaxValue() const479 double RemoteAccessible::MaxValue() const {
480 if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
481 return RemoteAccessibleBase<RemoteAccessible>::MaxValue();
482 }
483
484 RefPtr<IAccessibleValue> acc = QueryInterface<IAccessibleValue>(this);
485 if (!acc) {
486 return UnspecifiedNaN<double>();
487 }
488
489 VARIANT maximumValue;
490 HRESULT hr = acc->get_maximumValue(&maximumValue);
491 if (FAILED(hr) || maximumValue.vt != VT_R8) {
492 return UnspecifiedNaN<double>();
493 }
494
495 return maximumValue.dblVal;
496 }
497
GetIA2TextBoundary(AccessibleTextBoundary aGeckoBoundaryType)498 static IA2TextBoundaryType GetIA2TextBoundary(
499 AccessibleTextBoundary aGeckoBoundaryType) {
500 switch (aGeckoBoundaryType) {
501 case nsIAccessibleText::BOUNDARY_CHAR:
502 return IA2_TEXT_BOUNDARY_CHAR;
503 case nsIAccessibleText::BOUNDARY_WORD_START:
504 return IA2_TEXT_BOUNDARY_WORD;
505 case nsIAccessibleText::BOUNDARY_LINE_START:
506 return IA2_TEXT_BOUNDARY_LINE;
507 case nsIAccessibleText::BOUNDARY_PARAGRAPH:
508 return IA2_TEXT_BOUNDARY_PARAGRAPH;
509 default:
510 MOZ_CRASH();
511 }
512 }
513
OffsetAtPoint(int32_t aX,int32_t aY,uint32_t aCoordinateType)514 int32_t RemoteAccessible::OffsetAtPoint(int32_t aX, int32_t aY,
515 uint32_t aCoordinateType) {
516 RefPtr<IAccessibleText> acc = QueryInterface<IAccessibleText>(this);
517 if (!acc) {
518 return -1;
519 }
520
521 IA2CoordinateType coordType;
522 if (aCoordinateType ==
523 nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE) {
524 coordType = IA2_COORDTYPE_SCREEN_RELATIVE;
525 } else if (aCoordinateType ==
526 nsIAccessibleCoordinateType::COORDTYPE_PARENT_RELATIVE) {
527 coordType = IA2_COORDTYPE_PARENT_RELATIVE;
528 } else {
529 MOZ_CRASH("unsupported coord type");
530 }
531
532 long offset;
533 HRESULT hr = acc->get_offsetAtPoint(
534 static_cast<long>(aX), static_cast<long>(aY), coordType, &offset);
535 if (FAILED(hr)) {
536 return -1;
537 }
538
539 return static_cast<int32_t>(offset);
540 }
541
TextSubstring(int32_t aStartOffset,int32_t aEndOffset,nsAString & aText) const542 void RemoteAccessible::TextSubstring(int32_t aStartOffset, int32_t aEndOffset,
543 nsAString& aText) const {
544 if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
545 return RemoteAccessibleBase<RemoteAccessible>::TextSubstring(
546 aStartOffset, aEndOffset, aText);
547 }
548
549 RefPtr<IAccessibleText> acc = QueryInterface<IAccessibleText>(this);
550 if (!acc) {
551 return;
552 }
553
554 BSTR result;
555 HRESULT hr = acc->get_text(static_cast<long>(aStartOffset),
556 static_cast<long>(aEndOffset), &result);
557 if (FAILED(hr)) {
558 return;
559 }
560
561 _bstr_t resultWrap(result, false);
562 aText = (wchar_t*)result;
563 }
564
TextBeforeOffset(int32_t aOffset,AccessibleTextBoundary aBoundaryType,int32_t * aStartOffset,int32_t * aEndOffset,nsAString & aText)565 void RemoteAccessible::TextBeforeOffset(int32_t aOffset,
566 AccessibleTextBoundary aBoundaryType,
567 int32_t* aStartOffset,
568 int32_t* aEndOffset, nsAString& aText) {
569 if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
570 return RemoteAccessibleBase<RemoteAccessible>::TextBeforeOffset(
571 aOffset, aBoundaryType, aStartOffset, aEndOffset, aText);
572 }
573 RefPtr<IAccessibleText> acc = QueryInterface<IAccessibleText>(this);
574 if (!acc) {
575 return;
576 }
577
578 BSTR result;
579 long start, end;
580 HRESULT hr = acc->get_textBeforeOffset(
581 aOffset, GetIA2TextBoundary(aBoundaryType), &start, &end, &result);
582 if (FAILED(hr)) {
583 return;
584 }
585
586 _bstr_t resultWrap(result, false);
587 *aStartOffset = start;
588 *aEndOffset = end;
589 aText = (wchar_t*)result;
590 }
591
TextAfterOffset(int32_t aOffset,AccessibleTextBoundary aBoundaryType,int32_t * aStartOffset,int32_t * aEndOffset,nsAString & aText)592 void RemoteAccessible::TextAfterOffset(int32_t aOffset,
593 AccessibleTextBoundary aBoundaryType,
594 int32_t* aStartOffset,
595 int32_t* aEndOffset, nsAString& aText) {
596 if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
597 return RemoteAccessibleBase<RemoteAccessible>::TextAfterOffset(
598 aOffset, aBoundaryType, aStartOffset, aEndOffset, aText);
599 }
600 RefPtr<IAccessibleText> acc = QueryInterface<IAccessibleText>(this);
601 if (!acc) {
602 return;
603 }
604
605 BSTR result;
606 long start, end;
607 HRESULT hr = acc->get_textAfterOffset(
608 aOffset, GetIA2TextBoundary(aBoundaryType), &start, &end, &result);
609 if (FAILED(hr)) {
610 return;
611 }
612
613 _bstr_t resultWrap(result, false);
614 aText = (wchar_t*)result;
615 *aStartOffset = start;
616 *aEndOffset = end;
617 }
618
TextAtOffset(int32_t aOffset,AccessibleTextBoundary aBoundaryType,int32_t * aStartOffset,int32_t * aEndOffset,nsAString & aText)619 void RemoteAccessible::TextAtOffset(int32_t aOffset,
620 AccessibleTextBoundary aBoundaryType,
621 int32_t* aStartOffset, int32_t* aEndOffset,
622 nsAString& aText) {
623 if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
624 return RemoteAccessibleBase<RemoteAccessible>::TextAtOffset(
625 aOffset, aBoundaryType, aStartOffset, aEndOffset, aText);
626 }
627 RefPtr<IAccessibleText> acc = QueryInterface<IAccessibleText>(this);
628 if (!acc) {
629 return;
630 }
631
632 BSTR result;
633 long start, end;
634 HRESULT hr = acc->get_textAtOffset(aOffset, GetIA2TextBoundary(aBoundaryType),
635 &start, &end, &result);
636 if (FAILED(hr)) {
637 return;
638 }
639
640 _bstr_t resultWrap(result, false);
641 aText = (wchar_t*)result;
642 *aStartOffset = start;
643 *aEndOffset = end;
644 }
645
AddToSelection(int32_t aStartOffset,int32_t aEndOffset)646 bool RemoteAccessible::AddToSelection(int32_t aStartOffset,
647 int32_t aEndOffset) {
648 RefPtr<IAccessibleText> acc = QueryInterface<IAccessibleText>(this);
649 if (!acc) {
650 return false;
651 }
652
653 return SUCCEEDED(acc->addSelection(static_cast<long>(aStartOffset),
654 static_cast<long>(aEndOffset)));
655 }
656
RemoveFromSelection(int32_t aSelectionNum)657 bool RemoteAccessible::RemoveFromSelection(int32_t aSelectionNum) {
658 RefPtr<IAccessibleText> acc = QueryInterface<IAccessibleText>(this);
659 if (!acc) {
660 return false;
661 }
662
663 return SUCCEEDED(acc->removeSelection(static_cast<long>(aSelectionNum)));
664 }
665
CaretOffset() const666 int32_t RemoteAccessible::CaretOffset() const {
667 if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
668 return RemoteAccessibleBase<RemoteAccessible>::CaretOffset();
669 }
670
671 RefPtr<IAccessibleText> acc = QueryInterface<IAccessibleText>(this);
672 if (!acc) {
673 return -1;
674 }
675
676 long offset;
677 HRESULT hr = acc->get_caretOffset(&offset);
678 if (FAILED(hr)) {
679 return -1;
680 }
681
682 return static_cast<int32_t>(offset);
683 }
684
SetCaretOffset(int32_t aOffset)685 void RemoteAccessible::SetCaretOffset(int32_t aOffset) {
686 RefPtr<IAccessibleText> acc = QueryInterface<IAccessibleText>(this);
687 if (!acc) {
688 return;
689 }
690
691 acc->setCaretOffset(static_cast<long>(aOffset));
692 }
693
694 /**
695 * aScrollType should be one of the nsIAccessiblescrollType constants.
696 */
ScrollSubstringTo(int32_t aStartOffset,int32_t aEndOffset,uint32_t aScrollType)697 void RemoteAccessible::ScrollSubstringTo(int32_t aStartOffset,
698 int32_t aEndOffset,
699 uint32_t aScrollType) {
700 RefPtr<IAccessibleText> acc = QueryInterface<IAccessibleText>(this);
701 if (!acc) {
702 return;
703 }
704
705 acc->scrollSubstringTo(static_cast<long>(aStartOffset),
706 static_cast<long>(aEndOffset),
707 static_cast<IA2ScrollType>(aScrollType));
708 }
709
710 /**
711 * aCoordinateType is one of the nsIAccessibleCoordinateType constants.
712 */
ScrollSubstringToPoint(int32_t aStartOffset,int32_t aEndOffset,uint32_t aCoordinateType,int32_t aX,int32_t aY)713 void RemoteAccessible::ScrollSubstringToPoint(int32_t aStartOffset,
714 int32_t aEndOffset,
715 uint32_t aCoordinateType,
716 int32_t aX, int32_t aY) {
717 RefPtr<IAccessibleText> acc = QueryInterface<IAccessibleText>(this);
718 if (!acc) {
719 return;
720 }
721
722 IA2CoordinateType coordType;
723 if (aCoordinateType ==
724 nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE) {
725 coordType = IA2_COORDTYPE_SCREEN_RELATIVE;
726 } else if (aCoordinateType ==
727 nsIAccessibleCoordinateType::COORDTYPE_PARENT_RELATIVE) {
728 coordType = IA2_COORDTYPE_PARENT_RELATIVE;
729 } else {
730 MOZ_CRASH("unsupported coord type");
731 }
732
733 acc->scrollSubstringToPoint(static_cast<long>(aStartOffset),
734 static_cast<long>(aEndOffset), coordType,
735 static_cast<long>(aX), static_cast<long>(aY));
736 }
737
IsLinkValid()738 bool RemoteAccessible::IsLinkValid() {
739 RefPtr<IAccessibleHyperlink> acc = QueryInterface<IAccessibleHyperlink>(this);
740 if (!acc) {
741 return false;
742 }
743
744 boolean valid;
745 if (FAILED(acc->get_valid(&valid))) {
746 return false;
747 }
748
749 return valid;
750 }
751
AnchorCount(bool * aOk)752 uint32_t RemoteAccessible::AnchorCount(bool* aOk) {
753 *aOk = false;
754 RefPtr<IGeckoCustom> custom = QueryInterface<IGeckoCustom>(this);
755 if (!custom) {
756 return 0;
757 }
758
759 long count;
760 if (FAILED(custom->get_anchorCount(&count))) {
761 return 0;
762 }
763
764 *aOk = true;
765 return count;
766 }
767
AnchorAt(uint32_t aIdx)768 RemoteAccessible* RemoteAccessible::AnchorAt(uint32_t aIdx) {
769 RefPtr<IAccessibleHyperlink> link =
770 QueryInterface<IAccessibleHyperlink>(this);
771 if (!link) {
772 return nullptr;
773 }
774
775 VARIANT anchor;
776 if (FAILED(link->get_anchor(aIdx, &anchor))) {
777 return nullptr;
778 }
779
780 MOZ_ASSERT(anchor.vt == VT_UNKNOWN);
781 RemoteAccessible* proxyAnchor = GetProxyFor(Document(), anchor.punkVal);
782 anchor.punkVal->Release();
783 return proxyAnchor;
784 }
785
DOMNodeID(nsString & aID) const786 void RemoteAccessible::DOMNodeID(nsString& aID) const {
787 if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
788 return RemoteAccessibleBase<RemoteAccessible>::DOMNodeID(aID);
789 }
790
791 aID.Truncate();
792 RefPtr<IGeckoCustom> custom = QueryInterface<IGeckoCustom>(this);
793 if (!custom) {
794 return;
795 }
796
797 BSTR result;
798 HRESULT hr = custom->get_DOMNodeID(&result);
799 _bstr_t resultWrap(result, false);
800 if (FAILED(hr)) {
801 return;
802 }
803 aID = (wchar_t*)resultWrap;
804 }
805
TakeFocus() const806 void RemoteAccessible::TakeFocus() const {
807 if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
808 return RemoteAccessibleBase<RemoteAccessible>::TakeFocus();
809 }
810 RefPtr<IAccessible> acc;
811 if (!GetCOMInterface((void**)getter_AddRefs(acc))) {
812 return;
813 }
814 acc->accSelect(SELFLAG_TAKEFOCUS, kChildIdSelf);
815 }
816
ChildAtPoint(int32_t aX,int32_t aY,Accessible::EWhichChildAtPoint aWhichChild)817 Accessible* RemoteAccessible::ChildAtPoint(
818 int32_t aX, int32_t aY, Accessible::EWhichChildAtPoint aWhichChild) {
819 RefPtr<IAccessible2_2> target = QueryInterface<IAccessible2_2>(this);
820 if (!target) {
821 return nullptr;
822 }
823 DocAccessibleParent* doc = Document();
824 RemoteAccessible* proxy = this;
825 // accHitTest only does direct children, but we might want the deepest child.
826 for (;;) {
827 VARIANT childVar;
828 if (FAILED(target->accHitTest(aX, aY, &childVar)) ||
829 childVar.vt == VT_EMPTY) {
830 return nullptr;
831 }
832 if (childVar.vt == VT_I4 && childVar.lVal == CHILDID_SELF) {
833 break;
834 }
835 MOZ_ASSERT(childVar.vt == VT_DISPATCH && childVar.pdispVal);
836 target = nullptr;
837 childVar.pdispVal->QueryInterface(IID_IAccessible2_2,
838 getter_AddRefs(target));
839 childVar.pdispVal->Release();
840 if (!target) {
841 return nullptr;
842 }
843 // We can't always use GetProxyFor because it can't cross document
844 // boundaries.
845 if (proxy->ChildCount() == 1) {
846 proxy = proxy->RemoteChildAt(0);
847 if (proxy->IsDoc()) {
848 // We're crossing into a child document.
849 doc = proxy->AsDoc();
850 }
851 } else {
852 proxy = GetProxyFor(doc, target);
853 }
854 if (aWhichChild == Accessible::EWhichChildAtPoint::DirectChild) {
855 break;
856 }
857 }
858 return proxy;
859 }
860
GroupPosition()861 GroupPos RemoteAccessible::GroupPosition() {
862 if (StaticPrefs::accessibility_cache_enabled_AtStartup()) {
863 return RemoteAccessibleBase<RemoteAccessible>::GroupPosition();
864 }
865
866 // This is only supported when cache is enabled.
867 return GroupPos();
868 }
869
870 } // namespace a11y
871 } // namespace mozilla
872