1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=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 #if defined(MOZILLA_INTERNAL_API)
8 #  error This code is NOT for internal Gecko use!
9 #endif  // defined(MOZILLA_INTERNAL_API)
10 
11 #include "AccessibleHandler.h"
12 #include "AccessibleHandlerControl.h"
13 #include "HandlerChildEnumerator.h"
14 #include "HandlerRelation.h"
15 
16 #include "Factory.h"
17 #include "mozilla/ArrayUtils.h"
18 #include "mozilla/a11y/HandlerDataCleanup.h"
19 #include "mozilla/mscom/Registration.h"
20 #include "mozilla/UniquePtr.h"
21 
22 #include <objbase.h>
23 #include <uiautomation.h>
24 #include <winreg.h>
25 
26 #include "AccessibleHypertext.h"
27 #include "AccessibleHypertext2.h"
28 #include "AccessibleRole.h"
29 #include "Accessible2_i.c"
30 #include "Accessible2_2_i.c"
31 #include "Accessible2_3_i.c"
32 #include "AccessibleAction_i.c"
33 #include "AccessibleHyperlink_i.c"
34 #include "AccessibleHypertext_i.c"
35 #include "AccessibleHypertext2_i.c"
36 #include "AccessibleTable_i.c"
37 #include "AccessibleTable2_i.c"
38 #include "AccessibleTableCell_i.c"
39 #include "AccessibleText_i.c"
40 
41 namespace mozilla {
42 namespace a11y {
43 
44 // Must be kept in sync with kClassNameTabContent in
45 // accessible/windows/msaa/nsWinUtils.h.
46 const WCHAR kEmulatedWindowClassName[] = L"MozillaContentWindowClass";
47 const uint32_t kEmulatedWindowClassNameNChars =
48     sizeof(kEmulatedWindowClassName) / sizeof(WCHAR);
49 // Mask to get the content process portion of a Windows accessible unique id.
50 // This is bits 24 through 30 (LSB 0) of the id. This must be kept in sync
51 // with kNumContentProcessIDBits in accessible/windows/msaa/MsaaIdGenerator.cpp.
52 const uint32_t kIdContentProcessMask = 0x7F000000;
53 
54 static mscom::Factory<AccessibleHandler> sHandlerFactory;
55 
56 HRESULT
Create(IUnknown * aOuter,REFIID aIid,void ** aOutInterface)57 AccessibleHandler::Create(IUnknown* aOuter, REFIID aIid, void** aOutInterface) {
58   if (!aOutInterface || !aOuter || aIid != IID_IUnknown) {
59     return E_INVALIDARG;
60   }
61 
62   *aOutInterface = nullptr;
63 
64   HRESULT hr;
65   RefPtr<AccessibleHandler> handler(new AccessibleHandler(aOuter, &hr));
66   if (!handler) {
67     return E_OUTOFMEMORY;
68   }
69   if (FAILED(hr)) {
70     return hr;
71   }
72 
73   return handler->InternalQueryInterface(aIid, aOutInterface);
74 }
75 
AccessibleHandler(IUnknown * aOuter,HRESULT * aResult)76 AccessibleHandler::AccessibleHandler(IUnknown* aOuter, HRESULT* aResult)
77     : mscom::Handler(aOuter, aResult),
78       mDispatch(nullptr),
79       mIA2PassThru(nullptr),
80       mServProvPassThru(nullptr),
81       mIAHyperlinkPassThru(nullptr),
82       mIATableCellPassThru(nullptr),
83       mIAHypertextPassThru(nullptr),
84       mCachedData(),
85       mCachedDynamicDataMarshaledByCom(false),
86       mCacheGen(0),
87       mCachedHyperlinks(nullptr),
88       mCachedNHyperlinks(-1),
89       mCachedTextAttribRuns(nullptr),
90       mCachedNTextAttribRuns(-1),
91       mCachedRelations(nullptr),
92       mCachedNRelations(-1),
93       mIsEmulatedWindow(false) {
94   RefPtr<AccessibleHandlerControl> ctl(gControlFactory.GetOrCreateSingleton());
95   MOZ_ASSERT(ctl);
96   if (!ctl) {
97     if (aResult) {
98       *aResult = E_UNEXPECTED;
99     }
100     return;
101   }
102 
103   mCacheGen = ctl->GetCacheGen();
104 }
105 
~AccessibleHandler()106 AccessibleHandler::~AccessibleHandler() {
107   CleanupDynamicIA2Data(mCachedData.mDynamicData,
108                         mCachedDynamicDataMarshaledByCom);
109   if (mCachedData.mGeckoBackChannel) {
110     mCachedData.mGeckoBackChannel->Release();
111   }
112   ClearTextCache();
113   ClearRelationCache();
114 }
115 
116 HRESULT
ResolveIA2()117 AccessibleHandler::ResolveIA2() {
118   if (mIA2PassThru) {
119     return S_OK;
120   }
121 
122   RefPtr<IUnknown> proxy(GetProxy());
123   if (!proxy) {
124     return E_UNEXPECTED;
125   }
126 
127   HRESULT hr = proxy->QueryInterface(NEWEST_IA2_IID,
128                                      reinterpret_cast<void**>(&mIA2PassThru));
129   if (SUCCEEDED(hr)) {
130     // mIA2PassThru is a weak reference (see comments in AccesssibleHandler.h)
131     mIA2PassThru->Release();
132   }
133 
134   return hr;
135 }
136 
137 HRESULT
ResolveIAHyperlink()138 AccessibleHandler::ResolveIAHyperlink() {
139   if (mIAHyperlinkPassThru) {
140     return S_OK;
141   }
142 
143   RefPtr<IUnknown> proxy(GetProxy());
144   if (!proxy) {
145     return E_UNEXPECTED;
146   }
147 
148   HRESULT hr =
149       proxy->QueryInterface(IID_IAccessibleHyperlink,
150                             reinterpret_cast<void**>(&mIAHyperlinkPassThru));
151   if (SUCCEEDED(hr)) {
152     // mIAHyperlinkPassThru is a weak reference
153     // (see comments in AccesssibleHandler.h)
154     mIAHyperlinkPassThru->Release();
155   }
156 
157   return hr;
158 }
159 
160 HRESULT
ResolveIATableCell()161 AccessibleHandler::ResolveIATableCell() {
162   if (mIATableCellPassThru) {
163     return S_OK;
164   }
165 
166   RefPtr<IUnknown> proxy(GetProxy());
167   if (!proxy) {
168     return E_UNEXPECTED;
169   }
170 
171   HRESULT hr =
172       proxy->QueryInterface(IID_IAccessibleTableCell,
173                             reinterpret_cast<void**>(&mIATableCellPassThru));
174   if (SUCCEEDED(hr)) {
175     // mIATableCellPassThru is a weak reference
176     // (see comments in AccesssibleHandler.h)
177     mIATableCellPassThru->Release();
178   }
179 
180   return hr;
181 }
182 
183 HRESULT
ResolveIAHypertext()184 AccessibleHandler::ResolveIAHypertext() {
185   if (mIAHypertextPassThru) {
186     return S_OK;
187   }
188 
189   RefPtr<IUnknown> proxy(GetProxy());
190   if (!proxy) {
191     return E_UNEXPECTED;
192   }
193 
194   HRESULT hr =
195       proxy->QueryInterface(IID_IAccessibleHypertext2,
196                             reinterpret_cast<void**>(&mIAHypertextPassThru));
197   if (SUCCEEDED(hr)) {
198     // mIAHypertextPassThru is a weak reference
199     // (see comments in AccessibleHandler.h)
200     mIAHypertextPassThru->Release();
201   }
202 
203   return hr;
204 }
205 
206 HRESULT
MaybeUpdateCachedData()207 AccessibleHandler::MaybeUpdateCachedData() {
208   RefPtr<AccessibleHandlerControl> ctl(gControlFactory.GetOrCreateSingleton());
209   if (!ctl) {
210     return E_OUTOFMEMORY;
211   }
212 
213   uint32_t gen = ctl->GetCacheGen();
214   if (gen == mCacheGen) {
215     // Cache is already up to date.
216     return S_OK;
217   }
218 
219   if (!mCachedData.mGeckoBackChannel) {
220     return E_POINTER;
221   }
222 
223   // While we're making the outgoing COM call below, an incoming COM call can
224   // be handled which calls ReadHandlerPayload or re-enters this function.
225   // Therefore, we mustn't update the cached data directly lest it be mutated
226   // elsewhere before the outgoing COM call returns and cause corruption or
227   // memory leaks. Instead, pass a temporary struct and update the cached data
228   // only after this call completes.
229   DynamicIA2Data newData;
230   HRESULT hr = mCachedData.mGeckoBackChannel->Refresh(&newData);
231   if (SUCCEEDED(hr)) {
232     // Clean up the old data.
233     CleanupDynamicIA2Data(mCachedData.mDynamicData,
234                           mCachedDynamicDataMarshaledByCom);
235     mCachedData.mDynamicData = newData;
236     mCachedDynamicDataMarshaledByCom = true;
237     // We just updated the cache, so update this object's cache generation
238     // so we only update the cache again after the next change.
239     mCacheGen = gen;
240   }
241   return hr;
242 }
243 
244 HRESULT
GetAllTextInfo(BSTR * aText)245 AccessibleHandler::GetAllTextInfo(BSTR* aText) {
246   MOZ_ASSERT(mCachedData.mGeckoBackChannel);
247 
248   ClearTextCache();
249 
250   return mCachedData.mGeckoBackChannel->get_AllTextInfo(
251       aText, &mCachedHyperlinks, &mCachedNHyperlinks, &mCachedTextAttribRuns,
252       &mCachedNTextAttribRuns);
253 }
254 
ClearTextCache()255 void AccessibleHandler::ClearTextCache() {
256   if (mCachedNHyperlinks >= 0) {
257     // We cached hyperlinks, but the caller never retrieved them.
258     for (long index = 0; index < mCachedNHyperlinks; ++index) {
259       mCachedHyperlinks[index]->Release();
260     }
261     // mCachedHyperlinks might already be null if there are no hyperlinks.
262     if (mCachedHyperlinks) {
263       ::CoTaskMemFree(mCachedHyperlinks);
264       mCachedHyperlinks = nullptr;
265     }
266     mCachedNHyperlinks = -1;
267   }
268 
269   if (mCachedTextAttribRuns) {
270     for (long index = 0; index < mCachedNTextAttribRuns; ++index) {
271       if (mCachedTextAttribRuns[index].text) {
272         // The caller never requested this attribute run.
273         ::SysFreeString(mCachedTextAttribRuns[index].text);
274       }
275     }
276     // This array is internal to us, so we must always free it.
277     ::CoTaskMemFree(mCachedTextAttribRuns);
278     mCachedTextAttribRuns = nullptr;
279     mCachedNTextAttribRuns = -1;
280   }
281 }
282 
283 HRESULT
GetRelationsInfo()284 AccessibleHandler::GetRelationsInfo() {
285   MOZ_ASSERT(mCachedData.mGeckoBackChannel);
286 
287   ClearRelationCache();
288 
289   return mCachedData.mGeckoBackChannel->get_RelationsInfo(&mCachedRelations,
290                                                           &mCachedNRelations);
291 }
292 
ClearRelationCache()293 void AccessibleHandler::ClearRelationCache() {
294   if (mCachedNRelations == -1) {
295     // No cache; nothing to do.
296     return;
297   }
298 
299   // We cached relations, but the client never retrieved them.
300   if (mCachedRelations) {
301     for (long index = 0; index < mCachedNRelations; ++index) {
302       IARelationData& relData = mCachedRelations[index];
303       if (relData.mType) {
304         ::SysFreeString(relData.mType);
305       }
306     }
307     // This array is internal to us, so we must always free it.
308     ::CoTaskMemFree(mCachedRelations);
309     mCachedRelations = nullptr;
310   }
311   mCachedNRelations = -1;
312 }
313 
314 HRESULT
ResolveIDispatch()315 AccessibleHandler::ResolveIDispatch() {
316   if (mDispatch) {
317     return S_OK;
318   }
319 
320   HRESULT hr;
321 
322   if (!mDispatchUnk) {
323     RefPtr<AccessibleHandlerControl> ctl(
324         gControlFactory.GetOrCreateSingleton());
325     if (!ctl) {
326       return E_OUTOFMEMORY;
327     }
328 
329     RefPtr<ITypeInfo> typeinfo;
330     hr = ctl->GetHandlerTypeInfo(getter_AddRefs(typeinfo));
331     if (FAILED(hr)) {
332       return hr;
333     }
334 
335     hr = ::CreateStdDispatch(GetOuter(),
336                              static_cast<NEWEST_IA2_INTERFACE*>(this), typeinfo,
337                              getter_AddRefs(mDispatchUnk));
338     if (FAILED(hr)) {
339       return hr;
340     }
341   }
342 
343   hr = mDispatchUnk->QueryInterface(IID_IDispatch,
344                                     reinterpret_cast<void**>(&mDispatch));
345   if (SUCCEEDED(hr)) {
346     // mDispatch is weak (see comments in AccessibleHandler.h)
347     mDispatch->Release();
348   }
349 
350   return hr;
351 }
352 
353 HRESULT
QueryHandlerInterface(IUnknown * aProxyUnknown,REFIID aIid,void ** aOutInterface)354 AccessibleHandler::QueryHandlerInterface(IUnknown* aProxyUnknown, REFIID aIid,
355                                          void** aOutInterface) {
356   MOZ_ASSERT(aProxyUnknown);
357 
358   static_assert(&NEWEST_IA2_IID == &IID_IAccessible2_3,
359                 "You have modified NEWEST_IA2_IID. This code needs updating.");
360   if (aIid == IID_IDispatch || aIid == IID_IAccessible2_3 ||
361       aIid == IID_IAccessible2_2 || aIid == IID_IAccessible2 ||
362       aIid == IID_IAccessible) {
363     RefPtr<NEWEST_IA2_INTERFACE> ia2(static_cast<NEWEST_IA2_INTERFACE*>(this));
364     ia2.forget(aOutInterface);
365     return S_OK;
366   }
367 
368   if (aIid == IID_IServiceProvider) {
369     RefPtr<IServiceProvider> svcProv(static_cast<IServiceProvider*>(this));
370     svcProv.forget(aOutInterface);
371     return S_OK;
372   }
373 
374   if (HasPayload()) {
375     // The proxy manager caches interfaces marshaled in the payload
376     // and returns them on QI without a cross-process call.
377     // However, it doesn't know about interfaces which don't exist.
378     // We can determine this from the payload.
379     if (((aIid == IID_IAccessibleText || aIid == IID_IAccessibleHypertext ||
380           aIid == IID_IAccessibleHypertext2) &&
381          !mCachedData.mStaticData.mIAHypertext) ||
382         ((aIid == IID_IAccessibleAction || aIid == IID_IAccessibleHyperlink) &&
383          !mCachedData.mStaticData.mIAHyperlink) ||
384         (aIid == IID_IAccessibleTable && !mCachedData.mStaticData.mIATable) ||
385         (aIid == IID_IAccessibleTable2 && !mCachedData.mStaticData.mIATable2) ||
386         (aIid == IID_IAccessibleTableCell &&
387          !mCachedData.mStaticData.mIATableCell)) {
388       // We already know this interface is not available, so don't query
389       // the proxy, thus avoiding a pointless cross-process call.
390       // If we return E_NOINTERFACE here, mscom::Handler will try the COM
391       // proxy. S_FALSE signals that the proxy should not be tried.
392       return S_FALSE;
393     }
394   }
395 
396   if (aIid == IID_IAccessibleAction || aIid == IID_IAccessibleHyperlink) {
397     RefPtr<IAccessibleHyperlink> iaLink(
398         static_cast<IAccessibleHyperlink*>(this));
399     iaLink.forget(aOutInterface);
400     return S_OK;
401   }
402 
403   if (aIid == IID_IAccessibleTableCell) {
404     RefPtr<IAccessibleTableCell> iaCell(
405         static_cast<IAccessibleTableCell*>(this));
406     iaCell.forget(aOutInterface);
407     return S_OK;
408   }
409 
410   if (aIid == IID_IAccessibleText || aIid == IID_IAccessibleHypertext ||
411       aIid == IID_IAccessibleHypertext2) {
412     RefPtr<IAccessibleHypertext2> iaHt(
413         static_cast<IAccessibleHypertext2*>(this));
414     iaHt.forget(aOutInterface);
415     return S_OK;
416   }
417 
418   if (aIid == IID_IProvideClassInfo) {
419     RefPtr<IProvideClassInfo> clsInfo(this);
420     clsInfo.forget(aOutInterface);
421     return S_OK;
422   }
423 
424   if (aIid == IID_IEnumVARIANT && mCachedData.mGeckoBackChannel) {
425     if (mCachedData.mDynamicData.mChildCount == 0) {
426       return E_NOINTERFACE;
427     }
428     if (mCachedData.mDynamicData.mIA2Role == IA2_ROLE_INTERNAL_FRAME &&
429         mCachedData.mDynamicData.mChildCount == 1) {
430       // This is for an iframe. HandlerChildEnumerator works fine for iframes
431       // rendered in the same content process. However, for out-of-process
432       // iframes, HandlerProvider::get_AllChildren (called by
433       // HandlerChildEnumerator) will fail. This is because we only send down
434       // an IDispatch COM proxy for the embedded document, but get_AllChildren
435       // will try to QueryInterface this to IAccessible2 to reduce QI calls
436       // from the parent process. Because the content process is sandboxed,
437       // it can't make the outgoing COM call to QI the proxy from IDispatch to
438       // IAccessible2 and so it fails.
439       // Since an iframe only has one child anyway, we don't need the bulk fetch
440       // optimization offered by HandlerChildEnumerator or even IEnumVARIANT.
441       // Therefore, we explicitly tell the client this interface is not
442       // supported, which will cause the oleacc AccessibleChildren function
443       // to fall back to accChild.
444       // If we return E_NOINTERFACE here, mscom::Handler will try the COM
445       // proxy. S_FALSE signals that the proxy should not be tried.
446       return S_FALSE;
447     }
448     RefPtr<IEnumVARIANT> childEnum(
449         new HandlerChildEnumerator(this, mCachedData.mGeckoBackChannel));
450     childEnum.forget(aOutInterface);
451     return S_OK;
452   }
453 
454   return E_NOINTERFACE;
455 }
456 
457 HRESULT
ReadHandlerPayload(IStream * aStream,REFIID aIid)458 AccessibleHandler::ReadHandlerPayload(IStream* aStream, REFIID aIid) {
459   if (!aStream) {
460     return E_INVALIDARG;
461   }
462 
463   mscom::StructFromStream deserializer(aStream);
464   if (!deserializer) {
465     return E_FAIL;
466   }
467   if (deserializer.IsEmpty()) {
468     return S_FALSE;
469   }
470 
471   // QueryHandlerInterface might get called while we deserialize the payload,
472   // but that checks the interface pointers in the payload to determine what
473   // interfaces are available. Therefore, deserialize into a temporary struct
474   // and update mCachedData only after deserialization completes.
475   // The decoding functions can misbehave if their target memory is not zeroed
476   // beforehand, so ensure we do that.
477   IA2Payload newData{};
478   if (!deserializer.Read(&newData, &IA2Payload_Decode)) {
479     return E_FAIL;
480   }
481   // Clean up the old data.
482   CleanupDynamicIA2Data(mCachedData.mDynamicData,
483                         mCachedDynamicDataMarshaledByCom);
484   mCachedData = newData;
485   mCachedDynamicDataMarshaledByCom = false;
486 
487   // These interfaces have been aggregated into the proxy manager.
488   // The proxy manager will resolve these interfaces now on QI,
489   // so we can release these pointers.
490   // However, we don't null them out because we use their presence
491   // to determine whether the interface is available
492   // so as to avoid pointless cross-proc QI calls returning E_NOINTERFACE.
493   // Note that if pointers to other objects (in contrast to
494   // interfaces of *this* object) are added in future, we should not release
495   // those pointers.
496   ReleaseStaticIA2DataInterfaces(mCachedData.mStaticData);
497 
498   WCHAR className[kEmulatedWindowClassNameNChars];
499   if (mCachedData.mDynamicData.mHwnd &&
500       ::GetClassName(
501           reinterpret_cast<HWND>(uintptr_t(mCachedData.mDynamicData.mHwnd)),
502           className, kEmulatedWindowClassNameNChars) > 0 &&
503       wcscmp(className, kEmulatedWindowClassName) == 0) {
504     mIsEmulatedWindow = true;
505   }
506 
507   if (!mCachedData.mGeckoBackChannel) {
508     return S_OK;
509   }
510 
511   RefPtr<AccessibleHandlerControl> ctl(gControlFactory.GetOrCreateSingleton());
512   if (!ctl) {
513     return E_OUTOFMEMORY;
514   }
515 
516   if (mCachedData.mDynamicData.mIA2Role == ROLE_SYSTEM_COLUMNHEADER ||
517       mCachedData.mDynamicData.mIA2Role == ROLE_SYSTEM_ROWHEADER) {
518     // Because the same headers can apply to many cells, handler payloads
519     // include the ids of header cells, rather than potentially marshaling the
520     // same objects many times. We need to cache header cells here so we can
521     // get them by id later.
522     ctl->CacheAccessible(mCachedData.mDynamicData.mUniqueId, this);
523   }
524 
525   return ctl->Register(WrapNotNull(mCachedData.mGeckoBackChannel));
526 }
527 
528 REFIID
MarshalAs(REFIID aIid)529 AccessibleHandler::MarshalAs(REFIID aIid) {
530   static_assert(&NEWEST_IA2_IID == &IID_IAccessible2_3,
531                 "You have modified NEWEST_IA2_IID. This code needs updating.");
532   if (aIid == IID_IAccessible2_3 || aIid == IID_IAccessible2_2 ||
533       aIid == IID_IAccessible2 || aIid == IID_IAccessible ||
534       aIid == IID_IDispatch) {
535     return NEWEST_IA2_IID;
536   }
537 
538   return aIid;
539 }
540 
541 HRESULT
GetMarshalInterface(REFIID aMarshalAsIid,NotNull<IUnknown * > aProxy,NotNull<IID * > aOutIid,NotNull<IUnknown ** > aOutUnk)542 AccessibleHandler::GetMarshalInterface(REFIID aMarshalAsIid,
543                                        NotNull<IUnknown*> aProxy,
544                                        NotNull<IID*> aOutIid,
545                                        NotNull<IUnknown**> aOutUnk) {
546   if (aMarshalAsIid == NEWEST_IA2_IID) {
547     *aOutIid = IID_IAccessible;
548   } else {
549     *aOutIid = aMarshalAsIid;
550   }
551 
552   return aProxy->QueryInterface(
553       aMarshalAsIid,
554       reinterpret_cast<void**>(static_cast<IUnknown**>(aOutUnk)));
555 }
556 
557 HRESULT
GetHandlerPayloadSize(REFIID aIid,DWORD * aOutPayloadSize)558 AccessibleHandler::GetHandlerPayloadSize(REFIID aIid, DWORD* aOutPayloadSize) {
559   if (!aOutPayloadSize) {
560     return E_INVALIDARG;
561   }
562 
563   // If we're sending the payload to somebody else, we'd better make sure that
564   // it is up to date. If the cache update fails then we'll return a 0 payload
565   // size so that we don't transfer obsolete data.
566   if (FAILED(MaybeUpdateCachedData())) {
567     *aOutPayloadSize = mscom::StructToStream::GetEmptySize();
568     return S_OK;
569   }
570 
571   mSerializer =
572       MakeUnique<mscom::StructToStream>(mCachedData, &IA2Payload_Encode);
573   if (!mSerializer) {
574     return E_FAIL;
575   }
576 
577   *aOutPayloadSize = mSerializer->GetSize();
578   return S_OK;
579 }
580 
581 HRESULT
WriteHandlerPayload(IStream * aStream,REFIID aIid)582 AccessibleHandler::WriteHandlerPayload(IStream* aStream, REFIID aIid) {
583   if (!aStream) {
584     return E_INVALIDARG;
585   }
586 
587   if (!mSerializer) {
588     return E_UNEXPECTED;
589   }
590 
591   HRESULT hr = mSerializer->Write(aStream);
592   mSerializer.reset();
593   return hr;
594 }
595 
596 HRESULT
QueryInterface(REFIID riid,void ** ppv)597 AccessibleHandler::QueryInterface(REFIID riid, void** ppv) {
598   return Handler::QueryInterface(riid, ppv);
599 }
600 
601 ULONG
AddRef()602 AccessibleHandler::AddRef() { return Handler::AddRef(); }
603 
604 ULONG
Release()605 AccessibleHandler::Release() { return Handler::Release(); }
606 
607 HRESULT
GetTypeInfoCount(UINT * pctinfo)608 AccessibleHandler::GetTypeInfoCount(UINT* pctinfo) {
609   HRESULT hr = ResolveIDispatch();
610   if (FAILED(hr)) {
611     return hr;
612   }
613 
614   return mDispatch->GetTypeInfoCount(pctinfo);
615 }
616 
617 HRESULT
GetTypeInfo(UINT iTInfo,LCID lcid,ITypeInfo ** ppTInfo)618 AccessibleHandler::GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo) {
619   HRESULT hr = ResolveIDispatch();
620   if (FAILED(hr)) {
621     return hr;
622   }
623 
624   return mDispatch->GetTypeInfo(iTInfo, lcid, ppTInfo);
625 }
626 
627 HRESULT
GetIDsOfNames(REFIID riid,LPOLESTR * rgszNames,UINT cNames,LCID lcid,DISPID * rgDispId)628 AccessibleHandler::GetIDsOfNames(REFIID riid, LPOLESTR* rgszNames, UINT cNames,
629                                  LCID lcid, DISPID* rgDispId) {
630   HRESULT hr = ResolveIDispatch();
631   if (FAILED(hr)) {
632     return hr;
633   }
634 
635   return mDispatch->GetIDsOfNames(riid, rgszNames, cNames, lcid, rgDispId);
636 }
637 
638 HRESULT
Invoke(DISPID dispIdMember,REFIID riid,LCID lcid,WORD wFlags,DISPPARAMS * pDispParams,VARIANT * pVarResult,EXCEPINFO * pExcepInfo,UINT * puArgErr)639 AccessibleHandler::Invoke(DISPID dispIdMember, REFIID riid, LCID lcid,
640                           WORD wFlags, DISPPARAMS* pDispParams,
641                           VARIANT* pVarResult, EXCEPINFO* pExcepInfo,
642                           UINT* puArgErr) {
643   HRESULT hr = ResolveIDispatch();
644   if (FAILED(hr)) {
645     return hr;
646   }
647 
648   return mDispatch->Invoke(dispIdMember, riid, lcid, wFlags, pDispParams,
649                            pVarResult, pExcepInfo, puArgErr);
650 }
651 
652 #define BEGIN_CACHE_ACCESS                      \
653   {                                             \
654     HRESULT hr;                                 \
655     if (FAILED(hr = MaybeUpdateCachedData())) { \
656       return hr;                                \
657     }                                           \
658   }
659 
660 #define GET_FIELD(member, assignTo) \
661   { assignTo = mCachedData.mDynamicData.member; }
662 
663 #define GET_BSTR(member, assignTo) \
664   { assignTo = CopyBSTR(mCachedData.mDynamicData.member); }
665 
666 /*** IAccessible ***/
667 
668 HRESULT
get_accParent(IDispatch ** ppdispParent)669 AccessibleHandler::get_accParent(IDispatch** ppdispParent) {
670   HRESULT hr = ResolveIA2();
671   if (FAILED(hr)) {
672     return hr;
673   }
674   return mIA2PassThru->get_accParent(ppdispParent);
675 }
676 
677 HRESULT
get_accChildCount(long * pcountChildren)678 AccessibleHandler::get_accChildCount(long* pcountChildren) {
679   if (!pcountChildren) {
680     return E_INVALIDARG;
681   }
682 
683   if (!HasPayload()) {
684     HRESULT hr = ResolveIA2();
685     if (FAILED(hr)) {
686       return hr;
687     }
688     return mIA2PassThru->get_accChildCount(pcountChildren);
689   }
690 
691   BEGIN_CACHE_ACCESS;
692   GET_FIELD(mChildCount, *pcountChildren);
693   return S_OK;
694 }
695 
696 HRESULT
get_accChild(VARIANT varChild,IDispatch ** ppdispChild)697 AccessibleHandler::get_accChild(VARIANT varChild, IDispatch** ppdispChild) {
698   if (!ppdispChild) {
699     return E_INVALIDARG;
700   }
701   // Unlikely, but we might as well optimize for it
702   if (varChild.vt == VT_I4 && varChild.lVal == CHILDID_SELF) {
703     RefPtr<IDispatch> disp(this);
704     disp.forget(ppdispChild);
705     return S_OK;
706   }
707 
708   if (mIsEmulatedWindow && varChild.vt == VT_I4 && varChild.lVal < 0 &&
709       (varChild.lVal & kIdContentProcessMask) !=
710           (mCachedData.mDynamicData.mUniqueId & kIdContentProcessMask)) {
711     // Window emulation is enabled and the target id is in a different
712     // process to this accessible.
713     // When window emulation is enabled, each tab document gets its own HWND.
714     // OOP iframes get the same HWND as their tab document and fire events with
715     // that HWND. However, the root accessible for the HWND (the tab document)
716     // can't return accessibles for OOP iframes. Therefore, we must get the root
717     // accessible from the main HWND and call accChild on that instead.
718     // We don't need an oleacc proxy, so send WM_GETOBJECT directly instead of
719     // calling AccessibleObjectFromEvent.
720     HWND rootHwnd = GetParent(
721         reinterpret_cast<HWND>(uintptr_t(mCachedData.mDynamicData.mHwnd)));
722     MOZ_ASSERT(rootHwnd);
723     LRESULT lresult = ::SendMessage(rootHwnd, WM_GETOBJECT, 0, OBJID_CLIENT);
724     if (lresult > 0) {
725       RefPtr<IAccessible2_3> rootAcc;
726       HRESULT hr = ::ObjectFromLresult(lresult, IID_IAccessible2_3, 0,
727                                        getter_AddRefs(rootAcc));
728       if (hr == S_OK) {
729         return rootAcc->get_accChild(varChild, ppdispChild);
730       }
731     }
732   }
733 
734   HRESULT hr = ResolveIA2();
735   if (FAILED(hr)) {
736     return hr;
737   }
738   return mIA2PassThru->get_accChild(varChild, ppdispChild);
739 }
740 
741 HRESULT
get_accName(VARIANT varChild,BSTR * pszName)742 AccessibleHandler::get_accName(VARIANT varChild, BSTR* pszName) {
743   if (!pszName) {
744     return E_INVALIDARG;
745   }
746 
747   if (varChild.lVal != CHILDID_SELF || !HasPayload()) {
748     HRESULT hr = ResolveIA2();
749     if (FAILED(hr)) {
750       return hr;
751     }
752     return mIA2PassThru->get_accName(varChild, pszName);
753   }
754 
755   BEGIN_CACHE_ACCESS;
756   GET_BSTR(mName, *pszName);
757   return S_OK;
758 }
759 
760 HRESULT
get_accValue(VARIANT varChild,BSTR * pszValue)761 AccessibleHandler::get_accValue(VARIANT varChild, BSTR* pszValue) {
762   if (!pszValue) {
763     return E_INVALIDARG;
764   }
765 
766   if (varChild.lVal != CHILDID_SELF || !HasPayload()) {
767     HRESULT hr = ResolveIA2();
768     if (FAILED(hr)) {
769       return hr;
770     }
771     return mIA2PassThru->get_accValue(varChild, pszValue);
772   }
773 
774   BEGIN_CACHE_ACCESS;
775   GET_BSTR(mValue, *pszValue);
776   return S_OK;
777 }
778 
779 HRESULT
get_accDescription(VARIANT varChild,BSTR * pszDescription)780 AccessibleHandler::get_accDescription(VARIANT varChild, BSTR* pszDescription) {
781   if (!pszDescription) {
782     return E_INVALIDARG;
783   }
784 
785   if (varChild.lVal != CHILDID_SELF || !HasPayload()) {
786     HRESULT hr = ResolveIA2();
787     if (FAILED(hr)) {
788       return hr;
789     }
790     return mIA2PassThru->get_accDescription(varChild, pszDescription);
791   }
792 
793   BEGIN_CACHE_ACCESS;
794   GET_BSTR(mDescription, *pszDescription);
795   return S_OK;
796 }
797 
798 HRESULT
get_accRole(VARIANT varChild,VARIANT * pvarRole)799 AccessibleHandler::get_accRole(VARIANT varChild, VARIANT* pvarRole) {
800   if (!pvarRole) {
801     return E_INVALIDARG;
802   }
803 
804   if (varChild.lVal != CHILDID_SELF || !HasPayload()) {
805     HRESULT hr = ResolveIA2();
806     if (FAILED(hr)) {
807       return hr;
808     }
809     return mIA2PassThru->get_accRole(varChild, pvarRole);
810   }
811 
812   BEGIN_CACHE_ACCESS;
813   return ::VariantCopy(pvarRole, &mCachedData.mDynamicData.mRole);
814 }
815 
816 HRESULT
get_accState(VARIANT varChild,VARIANT * pvarState)817 AccessibleHandler::get_accState(VARIANT varChild, VARIANT* pvarState) {
818   if (!pvarState) {
819     return E_INVALIDARG;
820   }
821 
822   if (varChild.lVal != CHILDID_SELF || !HasPayload()) {
823     HRESULT hr = ResolveIA2();
824     if (FAILED(hr)) {
825       return hr;
826     }
827     return mIA2PassThru->get_accState(varChild, pvarState);
828   }
829 
830   pvarState->vt = VT_I4;
831   BEGIN_CACHE_ACCESS;
832   GET_FIELD(mState, pvarState->lVal);
833   return S_OK;
834 }
835 
836 HRESULT
get_accHelp(VARIANT varChild,BSTR * pszHelp)837 AccessibleHandler::get_accHelp(VARIANT varChild, BSTR* pszHelp) {
838   // This matches what AccessibleWrap does
839   if (!pszHelp) {
840     return E_INVALIDARG;
841   }
842   *pszHelp = nullptr;
843   return S_FALSE;
844 }
845 
846 HRESULT
get_accHelpTopic(BSTR * pszHelpFile,VARIANT varChild,long * pidTopic)847 AccessibleHandler::get_accHelpTopic(BSTR* pszHelpFile, VARIANT varChild,
848                                     long* pidTopic) {
849   // This matches what AccessibleWrap does
850   if (!pszHelpFile || !pidTopic) {
851     return E_INVALIDARG;
852   }
853   *pszHelpFile = nullptr;
854   *pidTopic = 0;
855   return S_FALSE;
856 }
857 
858 HRESULT
get_accKeyboardShortcut(VARIANT varChild,BSTR * pszKeyboardShortcut)859 AccessibleHandler::get_accKeyboardShortcut(VARIANT varChild,
860                                            BSTR* pszKeyboardShortcut) {
861   if (!pszKeyboardShortcut) {
862     return E_INVALIDARG;
863   }
864 
865   if (varChild.lVal != CHILDID_SELF || !HasPayload()) {
866     HRESULT hr = ResolveIA2();
867     if (FAILED(hr)) {
868       return hr;
869     }
870     return mIA2PassThru->get_accKeyboardShortcut(varChild, pszKeyboardShortcut);
871   }
872 
873   BEGIN_CACHE_ACCESS;
874   GET_BSTR(mKeyboardShortcut, *pszKeyboardShortcut);
875   return S_OK;
876 }
877 
878 HRESULT
get_accFocus(VARIANT * pvarChild)879 AccessibleHandler::get_accFocus(VARIANT* pvarChild) {
880   HRESULT hr = ResolveIA2();
881   if (FAILED(hr)) {
882     return hr;
883   }
884   return mIA2PassThru->get_accFocus(pvarChild);
885 }
886 
887 HRESULT
get_accSelection(VARIANT * pvarChildren)888 AccessibleHandler::get_accSelection(VARIANT* pvarChildren) {
889   HRESULT hr = ResolveIA2();
890   if (FAILED(hr)) {
891     return hr;
892   }
893   return mIA2PassThru->get_accSelection(pvarChildren);
894 }
895 
896 HRESULT
get_accDefaultAction(VARIANT varChild,BSTR * pszDefaultAction)897 AccessibleHandler::get_accDefaultAction(VARIANT varChild,
898                                         BSTR* pszDefaultAction) {
899   if (!pszDefaultAction) {
900     return E_INVALIDARG;
901   }
902 
903   if (varChild.lVal != CHILDID_SELF || !HasPayload()) {
904     HRESULT hr = ResolveIA2();
905     if (FAILED(hr)) {
906       return hr;
907     }
908     return mIA2PassThru->get_accDefaultAction(varChild, pszDefaultAction);
909   }
910 
911   BEGIN_CACHE_ACCESS;
912   GET_BSTR(mDefaultAction, *pszDefaultAction);
913   return S_OK;
914 }
915 
916 HRESULT
accSelect(long flagsSelect,VARIANT varChild)917 AccessibleHandler::accSelect(long flagsSelect, VARIANT varChild) {
918   HRESULT hr = ResolveIA2();
919   if (FAILED(hr)) {
920     return hr;
921   }
922   return mIA2PassThru->accSelect(flagsSelect, varChild);
923 }
924 
925 HRESULT
accLocation(long * pxLeft,long * pyTop,long * pcxWidth,long * pcyHeight,VARIANT varChild)926 AccessibleHandler::accLocation(long* pxLeft, long* pyTop, long* pcxWidth,
927                                long* pcyHeight, VARIANT varChild) {
928   if (varChild.lVal != CHILDID_SELF || !HasPayload()) {
929     HRESULT hr = ResolveIA2();
930     if (FAILED(hr)) {
931       return hr;
932     }
933     return mIA2PassThru->accLocation(pxLeft, pyTop, pcxWidth, pcyHeight,
934                                      varChild);
935   }
936 
937   if (!pxLeft || !pyTop || !pcxWidth || !pcyHeight) {
938     return E_INVALIDARG;
939   }
940 
941   BEGIN_CACHE_ACCESS;
942   GET_FIELD(mLeft, *pxLeft);
943   GET_FIELD(mTop, *pyTop);
944   GET_FIELD(mWidth, *pcxWidth);
945   GET_FIELD(mHeight, *pcyHeight);
946   return S_OK;
947 }
948 
949 HRESULT
accNavigate(long navDir,VARIANT varStart,VARIANT * pvarEndUpAt)950 AccessibleHandler::accNavigate(long navDir, VARIANT varStart,
951                                VARIANT* pvarEndUpAt) {
952   HRESULT hr = ResolveIA2();
953   if (FAILED(hr)) {
954     return hr;
955   }
956   return mIA2PassThru->accNavigate(navDir, varStart, pvarEndUpAt);
957 }
958 
959 HRESULT
accHitTest(long xLeft,long yTop,VARIANT * pvarChild)960 AccessibleHandler::accHitTest(long xLeft, long yTop, VARIANT* pvarChild) {
961   HRESULT hr = ResolveIA2();
962   if (FAILED(hr)) {
963     return hr;
964   }
965   return mIA2PassThru->accHitTest(xLeft, yTop, pvarChild);
966 }
967 
968 HRESULT
accDoDefaultAction(VARIANT varChild)969 AccessibleHandler::accDoDefaultAction(VARIANT varChild) {
970   HRESULT hr = ResolveIA2();
971   if (FAILED(hr)) {
972     return hr;
973   }
974   return mIA2PassThru->accDoDefaultAction(varChild);
975 }
976 
977 HRESULT
put_accName(VARIANT varChild,BSTR szName)978 AccessibleHandler::put_accName(VARIANT varChild, BSTR szName) {
979   // This matches AccessibleWrap
980   return E_NOTIMPL;
981 }
982 
983 HRESULT
put_accValue(VARIANT varChild,BSTR szValue)984 AccessibleHandler::put_accValue(VARIANT varChild, BSTR szValue) {
985   HRESULT hr = ResolveIA2();
986   if (FAILED(hr)) {
987     return hr;
988   }
989   return mIA2PassThru->put_accValue(varChild, szValue);
990 }
991 
992 /*** IAccessible2 ***/
993 
994 HRESULT
get_nRelations(long * nRelations)995 AccessibleHandler::get_nRelations(long* nRelations) {
996   if (!nRelations) {
997     return E_INVALIDARG;
998   }
999 
1000   HRESULT hr;
1001   if (mCachedData.mGeckoBackChannel) {
1002     // If the caller wants nRelations, they will almost certainly want the
1003     // actual relations too.
1004     hr = GetRelationsInfo();
1005     if (SUCCEEDED(hr)) {
1006       *nRelations = mCachedNRelations;
1007       return S_OK;
1008     }
1009     // We fall back to a normal call if this fails.
1010   }
1011 
1012   hr = ResolveIA2();
1013   if (FAILED(hr)) {
1014     return hr;
1015   }
1016   return mIA2PassThru->get_nRelations(nRelations);
1017 }
1018 
1019 HRESULT
get_relation(long relationIndex,IAccessibleRelation ** relation)1020 AccessibleHandler::get_relation(long relationIndex,
1021                                 IAccessibleRelation** relation) {
1022   HRESULT hr = ResolveIA2();
1023   if (FAILED(hr)) {
1024     return hr;
1025   }
1026   return mIA2PassThru->get_relation(relationIndex, relation);
1027 }
1028 
1029 HRESULT
get_relations(long maxRelations,IAccessibleRelation ** relations,long * nRelations)1030 AccessibleHandler::get_relations(long maxRelations,
1031                                  IAccessibleRelation** relations,
1032                                  long* nRelations) {
1033   if (maxRelations == 0 || !relations || !nRelations) {
1034     return E_INVALIDARG;
1035   }
1036 
1037   // We currently only support retrieval of *all* cached relations at once.
1038   if (mCachedNRelations != -1 && maxRelations >= mCachedNRelations) {
1039     for (long index = 0; index < mCachedNRelations; ++index) {
1040       IARelationData& relData = mCachedRelations[index];
1041       RefPtr<IAccessibleRelation> hrel(new HandlerRelation(this, relData));
1042       hrel.forget(&relations[index]);
1043     }
1044     *nRelations = mCachedNRelations;
1045     // Clean up the cache, since we only cache for one call.
1046     // We don't use ClearRelationCache here because that scans for data to free
1047     // in the array and we don't we need that. The HandlerRelation instances
1048     // will handle freeing of the data.
1049     ::CoTaskMemFree(mCachedRelations);
1050     mCachedRelations = nullptr;
1051     mCachedNRelations = -1;
1052     return S_OK;
1053   }
1054 
1055   HRESULT hr = ResolveIA2();
1056   if (FAILED(hr)) {
1057     return hr;
1058   }
1059   return mIA2PassThru->get_relations(maxRelations, relations, nRelations);
1060 }
1061 
1062 HRESULT
role(long * role)1063 AccessibleHandler::role(long* role) {
1064   if (!role) {
1065     return E_INVALIDARG;
1066   }
1067 
1068   if (!HasPayload()) {
1069     HRESULT hr = ResolveIA2();
1070     if (FAILED(hr)) {
1071       return hr;
1072     }
1073     return mIA2PassThru->role(role);
1074   }
1075 
1076   BEGIN_CACHE_ACCESS;
1077   GET_FIELD(mIA2Role, *role);
1078   return S_OK;
1079 }
1080 
1081 HRESULT
scrollTo(IA2ScrollType scrollType)1082 AccessibleHandler::scrollTo(IA2ScrollType scrollType) {
1083   HRESULT hr = ResolveIA2();
1084   if (FAILED(hr)) {
1085     return hr;
1086   }
1087   return mIA2PassThru->scrollTo(scrollType);
1088 }
1089 
1090 HRESULT
scrollToPoint(IA2CoordinateType coordinateType,long x,long y)1091 AccessibleHandler::scrollToPoint(IA2CoordinateType coordinateType, long x,
1092                                  long y) {
1093   HRESULT hr = ResolveIA2();
1094   if (FAILED(hr)) {
1095     return hr;
1096   }
1097   return mIA2PassThru->scrollToPoint(coordinateType, x, y);
1098 }
1099 
1100 HRESULT
get_groupPosition(long * groupLevel,long * similarItemsInGroup,long * positionInGroup)1101 AccessibleHandler::get_groupPosition(long* groupLevel,
1102                                      long* similarItemsInGroup,
1103                                      long* positionInGroup) {
1104   HRESULT hr = ResolveIA2();
1105   if (FAILED(hr)) {
1106     return hr;
1107   }
1108   return mIA2PassThru->get_groupPosition(groupLevel, similarItemsInGroup,
1109                                          positionInGroup);
1110 }
1111 
1112 HRESULT
get_states(AccessibleStates * states)1113 AccessibleHandler::get_states(AccessibleStates* states) {
1114   if (!states) {
1115     return E_INVALIDARG;
1116   }
1117 
1118   if (!HasPayload()) {
1119     HRESULT hr = ResolveIA2();
1120     if (FAILED(hr)) {
1121       return hr;
1122     }
1123     return mIA2PassThru->get_states(states);
1124   }
1125 
1126   BEGIN_CACHE_ACCESS;
1127   GET_FIELD(mIA2States, *states);
1128   return S_OK;
1129 }
1130 
1131 HRESULT
get_extendedRole(BSTR * extendedRole)1132 AccessibleHandler::get_extendedRole(BSTR* extendedRole) {
1133   // This matches ia2Accessible
1134   if (!extendedRole) {
1135     return E_INVALIDARG;
1136   }
1137   *extendedRole = nullptr;
1138   return E_NOTIMPL;
1139 }
1140 
1141 HRESULT
get_localizedExtendedRole(BSTR * localizedExtendedRole)1142 AccessibleHandler::get_localizedExtendedRole(BSTR* localizedExtendedRole) {
1143   // This matches ia2Accessible
1144   if (!localizedExtendedRole) {
1145     return E_INVALIDARG;
1146   }
1147   *localizedExtendedRole = nullptr;
1148   return E_NOTIMPL;
1149 }
1150 
1151 HRESULT
get_nExtendedStates(long * nExtendedStates)1152 AccessibleHandler::get_nExtendedStates(long* nExtendedStates) {
1153   // This matches ia2Accessible
1154   if (!nExtendedStates) {
1155     return E_INVALIDARG;
1156   }
1157   *nExtendedStates = 0;
1158   return E_NOTIMPL;
1159 }
1160 
1161 HRESULT
get_extendedStates(long maxExtendedStates,BSTR ** extendedStates,long * nExtendedStates)1162 AccessibleHandler::get_extendedStates(long maxExtendedStates,
1163                                       BSTR** extendedStates,
1164                                       long* nExtendedStates) {
1165   // This matches ia2Accessible
1166   if (!extendedStates || !nExtendedStates) {
1167     return E_INVALIDARG;
1168   }
1169   *extendedStates = nullptr;
1170   *nExtendedStates = 0;
1171   return E_NOTIMPL;
1172 }
1173 
1174 HRESULT
get_localizedExtendedStates(long maxLocalizedExtendedStates,BSTR ** localizedExtendedStates,long * nLocalizedExtendedStates)1175 AccessibleHandler::get_localizedExtendedStates(long maxLocalizedExtendedStates,
1176                                                BSTR** localizedExtendedStates,
1177                                                long* nLocalizedExtendedStates) {
1178   // This matches ia2Accessible
1179   if (!localizedExtendedStates || !nLocalizedExtendedStates) {
1180     return E_INVALIDARG;
1181   }
1182   *localizedExtendedStates = nullptr;
1183   *nLocalizedExtendedStates = 0;
1184   return E_NOTIMPL;
1185 }
1186 
1187 HRESULT
get_uniqueID(long * uniqueID)1188 AccessibleHandler::get_uniqueID(long* uniqueID) {
1189   if (!uniqueID) {
1190     return E_INVALIDARG;
1191   }
1192   if (!HasPayload()) {
1193     HRESULT hr = ResolveIA2();
1194     if (FAILED(hr)) {
1195       return hr;
1196     }
1197     return mIA2PassThru->get_uniqueID(uniqueID);
1198   }
1199   *uniqueID = mCachedData.mDynamicData.mUniqueId;
1200   return S_OK;
1201 }
1202 
1203 HRESULT
get_windowHandle(HWND * windowHandle)1204 AccessibleHandler::get_windowHandle(HWND* windowHandle) {
1205   if (!windowHandle) {
1206     return E_INVALIDARG;
1207   }
1208 
1209   if (!HasPayload()) {
1210     HRESULT hr = ResolveIA2();
1211     if (FAILED(hr)) {
1212       return hr;
1213     }
1214     return mIA2PassThru->get_windowHandle(windowHandle);
1215   }
1216 
1217   BEGIN_CACHE_ACCESS;
1218   long hwnd = 0;
1219   GET_FIELD(mHwnd, hwnd);
1220   *windowHandle = reinterpret_cast<HWND>(uintptr_t(hwnd));
1221   return S_OK;
1222 }
1223 
1224 HRESULT
get_indexInParent(long * indexInParent)1225 AccessibleHandler::get_indexInParent(long* indexInParent) {
1226   HRESULT hr = ResolveIA2();
1227   if (FAILED(hr)) {
1228     return hr;
1229   }
1230   return mIA2PassThru->get_indexInParent(indexInParent);
1231 }
1232 
1233 HRESULT
get_locale(IA2Locale * locale)1234 AccessibleHandler::get_locale(IA2Locale* locale) {
1235   if (!locale) {
1236     return E_INVALIDARG;
1237   }
1238 
1239   if (!HasPayload()) {
1240     HRESULT hr = ResolveIA2();
1241     if (FAILED(hr)) {
1242       return hr;
1243     }
1244     return mIA2PassThru->get_locale(locale);
1245   }
1246 
1247   BEGIN_CACHE_ACCESS;
1248   GET_BSTR(mIA2Locale.language, locale->language);
1249   GET_BSTR(mIA2Locale.country, locale->country);
1250   GET_BSTR(mIA2Locale.variant, locale->variant);
1251   return S_OK;
1252 }
1253 
1254 HRESULT
get_attributes(BSTR * attributes)1255 AccessibleHandler::get_attributes(BSTR* attributes) {
1256   if (!attributes) {
1257     return E_INVALIDARG;
1258   }
1259 
1260   if (!HasPayload()) {
1261     HRESULT hr = ResolveIA2();
1262     if (FAILED(hr)) {
1263       return hr;
1264     }
1265     return mIA2PassThru->get_attributes(attributes);
1266   }
1267 
1268   BEGIN_CACHE_ACCESS;
1269   GET_BSTR(mAttributes, *attributes);
1270   return S_OK;
1271 }
1272 
1273 /*** IAccessible2_2 ***/
1274 
1275 HRESULT
get_attribute(BSTR name,VARIANT * attribute)1276 AccessibleHandler::get_attribute(BSTR name, VARIANT* attribute) {
1277   // Not yet implemented by ia2Accessible.
1278   // Once ia2Accessible implements this, we could either pass it through
1279   // or we could extract these individually from cached mAttributes.
1280   // The latter should be considered if traffic warrants it.
1281   return E_NOTIMPL;
1282 }
1283 
1284 HRESULT
get_accessibleWithCaret(IUnknown ** accessible,long * caretOffset)1285 AccessibleHandler::get_accessibleWithCaret(IUnknown** accessible,
1286                                            long* caretOffset) {
1287   HRESULT hr = ResolveIA2();
1288   if (FAILED(hr)) {
1289     return hr;
1290   }
1291   return mIA2PassThru->get_accessibleWithCaret(accessible, caretOffset);
1292 }
1293 
1294 HRESULT
get_relationTargetsOfType(BSTR type,long maxTargets,IUnknown *** targets,long * nTargets)1295 AccessibleHandler::get_relationTargetsOfType(BSTR type, long maxTargets,
1296                                              IUnknown*** targets,
1297                                              long* nTargets) {
1298   HRESULT hr = ResolveIA2();
1299   if (FAILED(hr)) {
1300     return hr;
1301   }
1302   return mIA2PassThru->get_relationTargetsOfType(type, maxTargets, targets,
1303                                                  nTargets);
1304 }
1305 
1306 /*** IAccessible2_3 ***/
1307 
1308 HRESULT
get_selectionRanges(IA2Range ** ranges,long * nRanges)1309 AccessibleHandler::get_selectionRanges(IA2Range** ranges, long* nRanges) {
1310   HRESULT hr = ResolveIA2();
1311   if (FAILED(hr)) {
1312     return hr;
1313   }
1314   return mIA2PassThru->get_selectionRanges(ranges, nRanges);
1315 }
1316 
1317 static const GUID kUnsupportedServices[] = {
1318     // clang-format off
1319   // Unknown, queried by Windows
1320   {0x33f139ee, 0xe509, 0x47f7, {0xbf, 0x39, 0x83, 0x76, 0x44, 0xf7, 0x45, 0x76}},
1321   // Unknown, queried by Windows
1322   {0xFDA075CF, 0x7C8B, 0x498C, { 0xB5, 0x14, 0xA9, 0xCB, 0x52, 0x1B, 0xBF, 0xB4 }},
1323   // Unknown, queried by Windows
1324   {0x8EDAA462, 0x21F4, 0x4C87, { 0xA0, 0x12, 0xB3, 0xCD, 0xA3, 0xAB, 0x01, 0xFC }},
1325   // Unknown, queried by Windows
1326   {0xacd46652, 0x829d, 0x41cb, { 0xa5, 0xfc, 0x17, 0xac, 0xf4, 0x36, 0x61, 0xac }},
1327   // SID_IsUIAutomationObject (undocumented), queried by Windows
1328   {0xb96fdb85, 0x7204, 0x4724, { 0x84, 0x2b, 0xc7, 0x05, 0x9d, 0xed, 0xb9, 0xd0 }},
1329   // IIS_IsOleaccProxy (undocumented), queried by Windows
1330   {0x902697FA, 0x80E4, 0x4560, {0x80, 0x2A, 0xA1, 0x3F, 0x22, 0xA6, 0x47, 0x09}},
1331   // IID_IHTMLElement, queried by JAWS
1332   {0x3050F1FF, 0x98B5, 0x11CF, {0xBB, 0x82, 0x00, 0xAA, 0x00, 0xBD, 0xCE, 0x0B}}
1333     // clang-format on
1334 };
1335 
1336 /*** IServiceProvider ***/
1337 
1338 HRESULT
QueryService(REFGUID aServiceId,REFIID aIid,void ** aOutInterface)1339 AccessibleHandler::QueryService(REFGUID aServiceId, REFIID aIid,
1340                                 void** aOutInterface) {
1341   static_assert(&NEWEST_IA2_IID == &IID_IAccessible2_3,
1342                 "You have modified NEWEST_IA2_IID. This code needs updating.");
1343   /* We're taking advantage of the fact that we are implementing IA2 as part
1344      of our own object to implement this just like a QI. */
1345   if (aIid == IID_IAccessible2_3 || aIid == IID_IAccessible2_2 ||
1346       aIid == IID_IAccessible2) {
1347     RefPtr<NEWEST_IA2_INTERFACE> ia2(this);
1348     ia2.forget(aOutInterface);
1349     return S_OK;
1350   }
1351 
1352   // JAWS uses QueryService for these, but QI will work just fine and we can
1353   // thus avoid a cross-process call. More importantly, if QS is used, the
1354   // handler won't get used for that object, so our caching won't be used.
1355   if (aIid == IID_IAccessibleAction || aIid == IID_IAccessibleText) {
1356     return InternalQueryInterface(aIid, aOutInterface);
1357   }
1358 
1359   for (uint32_t i = 0; i < ArrayLength(kUnsupportedServices); ++i) {
1360     if (aServiceId == kUnsupportedServices[i]) {
1361       return E_NOINTERFACE;
1362     }
1363   }
1364 
1365   if (!mServProvPassThru) {
1366     RefPtr<IUnknown> proxy(GetProxy());
1367     if (!proxy) {
1368       return E_UNEXPECTED;
1369     }
1370 
1371     HRESULT hr = proxy->QueryInterface(
1372         IID_IServiceProvider, reinterpret_cast<void**>(&mServProvPassThru));
1373     if (FAILED(hr)) {
1374       return hr;
1375     }
1376 
1377     // mServProvPassThru is a weak reference (see comments in
1378     // AccessibleHandler.h)
1379     mServProvPassThru->Release();
1380   }
1381 
1382   return mServProvPassThru->QueryService(aServiceId, aIid, aOutInterface);
1383 }
1384 
1385 /*** IProvideClassInfo ***/
1386 
1387 HRESULT
GetClassInfo(ITypeInfo ** aOutTypeInfo)1388 AccessibleHandler::GetClassInfo(ITypeInfo** aOutTypeInfo) {
1389   RefPtr<AccessibleHandlerControl> ctl(gControlFactory.GetOrCreateSingleton());
1390   if (!ctl) {
1391     return E_OUTOFMEMORY;
1392   }
1393 
1394   return ctl->GetHandlerTypeInfo(aOutTypeInfo);
1395 }
1396 
1397 /*** IAccessibleAction ***/
1398 
1399 HRESULT
nActions(long * nActions)1400 AccessibleHandler::nActions(long* nActions) {
1401   if (!nActions) {
1402     return E_INVALIDARG;
1403   }
1404 
1405   if (!HasPayload()) {
1406     HRESULT hr = ResolveIAHyperlink();
1407     if (FAILED(hr)) {
1408       return hr;
1409     }
1410     return mIAHyperlinkPassThru->nActions(nActions);
1411   }
1412 
1413   BEGIN_CACHE_ACCESS;
1414   GET_FIELD(mNActions, *nActions);
1415   return S_OK;
1416 }
1417 
1418 HRESULT
doAction(long actionIndex)1419 AccessibleHandler::doAction(long actionIndex) {
1420   HRESULT hr = ResolveIAHyperlink();
1421   if (FAILED(hr)) {
1422     return hr;
1423   }
1424   return mIAHyperlinkPassThru->doAction(actionIndex);
1425 }
1426 
1427 HRESULT
get_description(long actionIndex,BSTR * description)1428 AccessibleHandler::get_description(long actionIndex, BSTR* description) {
1429   HRESULT hr = ResolveIAHyperlink();
1430   if (FAILED(hr)) {
1431     return hr;
1432   }
1433   return mIAHyperlinkPassThru->get_description(actionIndex, description);
1434 }
1435 
1436 HRESULT
get_keyBinding(long actionIndex,long nMaxBindings,BSTR ** keyBindings,long * nBindings)1437 AccessibleHandler::get_keyBinding(long actionIndex, long nMaxBindings,
1438                                   BSTR** keyBindings, long* nBindings) {
1439   HRESULT hr = ResolveIAHyperlink();
1440   if (FAILED(hr)) {
1441     return hr;
1442   }
1443   return mIAHyperlinkPassThru->get_keyBinding(actionIndex, nMaxBindings,
1444                                               keyBindings, nBindings);
1445 }
1446 
1447 HRESULT
get_name(long actionIndex,BSTR * name)1448 AccessibleHandler::get_name(long actionIndex, BSTR* name) {
1449   if (!name) {
1450     return E_INVALIDARG;
1451   }
1452 
1453   if (HasPayload()) {
1454     if (actionIndex >= mCachedData.mDynamicData.mNActions) {
1455       // Action does not exist.
1456       return E_INVALIDARG;
1457     }
1458 
1459     if (actionIndex == 0) {
1460       // same as accDefaultAction.
1461       GET_BSTR(mDefaultAction, *name);
1462       return S_OK;
1463     }
1464   }
1465 
1466   // At this point, there's either no payload or actionIndex is > 0.
1467   HRESULT hr = ResolveIAHyperlink();
1468   if (FAILED(hr)) {
1469     return hr;
1470   }
1471   return mIAHyperlinkPassThru->get_name(actionIndex, name);
1472 }
1473 
1474 HRESULT
get_localizedName(long actionIndex,BSTR * localizedName)1475 AccessibleHandler::get_localizedName(long actionIndex, BSTR* localizedName) {
1476   HRESULT hr = ResolveIAHyperlink();
1477   if (FAILED(hr)) {
1478     return hr;
1479   }
1480   return mIAHyperlinkPassThru->get_localizedName(actionIndex, localizedName);
1481 }
1482 
1483 /*** IAccessibleHyperlink ***/
1484 
1485 HRESULT
get_anchor(long index,VARIANT * anchor)1486 AccessibleHandler::get_anchor(long index, VARIANT* anchor) {
1487   HRESULT hr = ResolveIAHyperlink();
1488   if (FAILED(hr)) {
1489     return hr;
1490   }
1491   return mIAHyperlinkPassThru->get_anchor(index, anchor);
1492 }
1493 
1494 HRESULT
get_anchorTarget(long index,VARIANT * anchorTarget)1495 AccessibleHandler::get_anchorTarget(long index, VARIANT* anchorTarget) {
1496   HRESULT hr = ResolveIAHyperlink();
1497   if (FAILED(hr)) {
1498     return hr;
1499   }
1500   return mIAHyperlinkPassThru->get_anchorTarget(index, anchorTarget);
1501 }
1502 
1503 HRESULT
get_startIndex(long * index)1504 AccessibleHandler::get_startIndex(long* index) {
1505   HRESULT hr = ResolveIAHyperlink();
1506   if (FAILED(hr)) {
1507     return hr;
1508   }
1509   return mIAHyperlinkPassThru->get_startIndex(index);
1510 }
1511 
1512 HRESULT
get_endIndex(long * index)1513 AccessibleHandler::get_endIndex(long* index) {
1514   HRESULT hr = ResolveIAHyperlink();
1515   if (FAILED(hr)) {
1516     return hr;
1517   }
1518   return mIAHyperlinkPassThru->get_endIndex(index);
1519 }
1520 
1521 HRESULT
get_valid(boolean * valid)1522 AccessibleHandler::get_valid(boolean* valid) {
1523   HRESULT hr = ResolveIAHyperlink();
1524   if (FAILED(hr)) {
1525     return hr;
1526   }
1527   return mIAHyperlinkPassThru->get_valid(valid);
1528 }
1529 
1530 /*** IAccessibleTableCell ***/
1531 
1532 HRESULT
get_columnExtent(long * nColumnsSpanned)1533 AccessibleHandler::get_columnExtent(long* nColumnsSpanned) {
1534   if (!nColumnsSpanned) {
1535     return E_INVALIDARG;
1536   }
1537 
1538   if (!HasPayload()) {
1539     HRESULT hr = ResolveIATableCell();
1540     if (FAILED(hr)) {
1541       return hr;
1542     }
1543     return mIATableCellPassThru->get_columnExtent(nColumnsSpanned);
1544   }
1545 
1546   BEGIN_CACHE_ACCESS;
1547   GET_FIELD(mColumnExtent, *nColumnsSpanned);
1548   return S_OK;
1549 }
1550 
1551 HRESULT
get_columnHeaderCells(IUnknown *** cellAccessibles,long * nColumnHeaderCells)1552 AccessibleHandler::get_columnHeaderCells(IUnknown*** cellAccessibles,
1553                                          long* nColumnHeaderCells) {
1554   if (!cellAccessibles || !nColumnHeaderCells) {
1555     return E_INVALIDARG;
1556   }
1557 
1558   HRESULT hr = S_OK;
1559   if (HasPayload()) {
1560     RefPtr<AccessibleHandlerControl> ctl(
1561         gControlFactory.GetOrCreateSingleton());
1562     if (!ctl) {
1563       return E_OUTOFMEMORY;
1564     }
1565     *nColumnHeaderCells = mCachedData.mDynamicData.mNColumnHeaderCells;
1566     *cellAccessibles = static_cast<IUnknown**>(
1567         ::CoTaskMemAlloc(sizeof(IUnknown*) * *nColumnHeaderCells));
1568     long i;
1569     for (i = 0; i < *nColumnHeaderCells; ++i) {
1570       RefPtr<AccessibleHandler> headerAcc;
1571       hr = ctl->GetCachedAccessible(
1572           mCachedData.mDynamicData.mColumnHeaderCellIds[i],
1573           getter_AddRefs(headerAcc));
1574       if (FAILED(hr)) {
1575         break;
1576       }
1577       hr = headerAcc->QueryInterface(IID_IUnknown,
1578                                      (void**)&(*cellAccessibles)[i]);
1579       if (FAILED(hr)) {
1580         break;
1581       }
1582     }
1583     if (SUCCEEDED(hr)) {
1584       return S_OK;
1585     }
1586     // If we failed to get any of the headers from the cache, don't use the
1587     // cache at all. We need to clean up anything we did so far.
1588     long failedHeader = i;
1589     for (i = 0; i < failedHeader; ++i) {
1590       (*cellAccessibles)[i]->Release();
1591     }
1592     ::CoTaskMemFree(*cellAccessibles);
1593     *cellAccessibles = nullptr;
1594   }
1595 
1596   hr = ResolveIATableCell();
1597   if (FAILED(hr)) {
1598     return hr;
1599   }
1600 
1601   return mIATableCellPassThru->get_columnHeaderCells(cellAccessibles,
1602                                                      nColumnHeaderCells);
1603 }
1604 
1605 HRESULT
get_columnIndex(long * columnIndex)1606 AccessibleHandler::get_columnIndex(long* columnIndex) {
1607   if (!columnIndex) {
1608     return E_INVALIDARG;
1609   }
1610 
1611   if (!HasPayload()) {
1612     HRESULT hr = ResolveIATableCell();
1613     if (FAILED(hr)) {
1614       return hr;
1615     }
1616     return mIATableCellPassThru->get_columnIndex(columnIndex);
1617   }
1618 
1619   BEGIN_CACHE_ACCESS;
1620   GET_FIELD(mColumnIndex, *columnIndex);
1621   return S_OK;
1622 }
1623 
1624 HRESULT
get_rowExtent(long * nRowsSpanned)1625 AccessibleHandler::get_rowExtent(long* nRowsSpanned) {
1626   if (!nRowsSpanned) {
1627     return E_INVALIDARG;
1628   }
1629 
1630   if (!HasPayload()) {
1631     HRESULT hr = ResolveIATableCell();
1632     if (FAILED(hr)) {
1633       return hr;
1634     }
1635     return mIATableCellPassThru->get_rowExtent(nRowsSpanned);
1636   }
1637 
1638   BEGIN_CACHE_ACCESS;
1639   GET_FIELD(mRowExtent, *nRowsSpanned);
1640   return S_OK;
1641 }
1642 
1643 HRESULT
get_rowHeaderCells(IUnknown *** cellAccessibles,long * nRowHeaderCells)1644 AccessibleHandler::get_rowHeaderCells(IUnknown*** cellAccessibles,
1645                                       long* nRowHeaderCells) {
1646   if (!cellAccessibles || !nRowHeaderCells) {
1647     return E_INVALIDARG;
1648   }
1649 
1650   HRESULT hr = S_OK;
1651   if (HasPayload()) {
1652     RefPtr<AccessibleHandlerControl> ctl(
1653         gControlFactory.GetOrCreateSingleton());
1654     if (!ctl) {
1655       return E_OUTOFMEMORY;
1656     }
1657     *nRowHeaderCells = mCachedData.mDynamicData.mNRowHeaderCells;
1658     *cellAccessibles = static_cast<IUnknown**>(
1659         ::CoTaskMemAlloc(sizeof(IUnknown*) * *nRowHeaderCells));
1660     long i;
1661     for (i = 0; i < *nRowHeaderCells; ++i) {
1662       RefPtr<AccessibleHandler> headerAcc;
1663       hr = ctl->GetCachedAccessible(
1664           mCachedData.mDynamicData.mRowHeaderCellIds[i],
1665           getter_AddRefs(headerAcc));
1666       if (FAILED(hr)) {
1667         break;
1668       }
1669       hr = headerAcc->QueryInterface(IID_IUnknown,
1670                                      (void**)&(*cellAccessibles)[i]);
1671       if (FAILED(hr)) {
1672         break;
1673       }
1674     }
1675     if (SUCCEEDED(hr)) {
1676       return S_OK;
1677     }
1678     // If we failed to get any of the headers from the cache, don't use the
1679     // cache at all. We need to clean up anything we did so far.
1680     long failedHeader = i;
1681     for (i = 0; i < failedHeader; ++i) {
1682       (*cellAccessibles)[i]->Release();
1683     }
1684     ::CoTaskMemFree(*cellAccessibles);
1685     *cellAccessibles = nullptr;
1686   }
1687 
1688   hr = ResolveIATableCell();
1689   if (FAILED(hr)) {
1690     return hr;
1691   }
1692 
1693   return mIATableCellPassThru->get_rowHeaderCells(cellAccessibles,
1694                                                   nRowHeaderCells);
1695 }
1696 
1697 HRESULT
get_rowIndex(long * rowIndex)1698 AccessibleHandler::get_rowIndex(long* rowIndex) {
1699   if (!rowIndex) {
1700     return E_INVALIDARG;
1701   }
1702 
1703   if (!HasPayload()) {
1704     HRESULT hr = ResolveIATableCell();
1705     if (FAILED(hr)) {
1706       return hr;
1707     }
1708     return mIATableCellPassThru->get_rowIndex(rowIndex);
1709   }
1710 
1711   BEGIN_CACHE_ACCESS;
1712   GET_FIELD(mRowIndex, *rowIndex);
1713   return S_OK;
1714 }
1715 
1716 HRESULT
get_isSelected(boolean * isSelected)1717 AccessibleHandler::get_isSelected(boolean* isSelected) {
1718   if (!isSelected) {
1719     return E_INVALIDARG;
1720   }
1721 
1722   if (!HasPayload()) {
1723     HRESULT hr = ResolveIATableCell();
1724     if (FAILED(hr)) {
1725       return hr;
1726     }
1727     return mIATableCellPassThru->get_isSelected(isSelected);
1728   }
1729 
1730   BEGIN_CACHE_ACCESS;
1731   GET_FIELD(mCellIsSelected, *isSelected);
1732   return S_OK;
1733 }
1734 
1735 HRESULT
get_rowColumnExtents(long * row,long * column,long * rowExtents,long * columnExtents,boolean * isSelected)1736 AccessibleHandler::get_rowColumnExtents(long* row, long* column,
1737                                         long* rowExtents, long* columnExtents,
1738                                         boolean* isSelected) {
1739   if (!row || !column || !rowExtents || !columnExtents || !isSelected) {
1740     return E_INVALIDARG;
1741   }
1742 
1743   if (!HasPayload()) {
1744     HRESULT hr = ResolveIATableCell();
1745     if (FAILED(hr)) {
1746       return hr;
1747     }
1748     return mIATableCellPassThru->get_rowColumnExtents(
1749         row, column, rowExtents, columnExtents, isSelected);
1750   }
1751 
1752   BEGIN_CACHE_ACCESS;
1753   GET_FIELD(mRowIndex, *row);
1754   GET_FIELD(mColumnIndex, *column);
1755   GET_FIELD(mRowExtent, *rowExtents);
1756   GET_FIELD(mColumnExtent, *columnExtents);
1757   GET_FIELD(mCellIsSelected, *isSelected);
1758   return S_OK;
1759 }
1760 
1761 HRESULT
get_table(IUnknown ** table)1762 AccessibleHandler::get_table(IUnknown** table) {
1763   HRESULT hr = ResolveIATableCell();
1764   if (FAILED(hr)) {
1765     return hr;
1766   }
1767 
1768   return mIATableCellPassThru->get_table(table);
1769 }
1770 
1771 /*** IAccessibleText ***/
1772 
1773 HRESULT
addSelection(long startOffset,long endOffset)1774 AccessibleHandler::addSelection(long startOffset, long endOffset) {
1775   HRESULT hr = ResolveIAHypertext();
1776   if (FAILED(hr)) {
1777     return hr;
1778   }
1779 
1780   return mIAHypertextPassThru->addSelection(startOffset, endOffset);
1781 }
1782 
1783 HRESULT
get_attributes(long offset,long * startOffset,long * endOffset,BSTR * textAttributes)1784 AccessibleHandler::get_attributes(long offset, long* startOffset,
1785                                   long* endOffset, BSTR* textAttributes) {
1786   if (!startOffset || !endOffset || !textAttributes) {
1787     return E_INVALIDARG;
1788   }
1789 
1790   if (mCachedNTextAttribRuns >= 0) {
1791     // We have cached attributes.
1792     for (long index = 0; index < mCachedNTextAttribRuns; ++index) {
1793       auto& attribRun = mCachedTextAttribRuns[index];
1794       if (attribRun.start <= offset && offset < attribRun.end) {
1795         *startOffset = attribRun.start;
1796         *endOffset = attribRun.end;
1797         *textAttributes = attribRun.text;
1798         // The caller will clean this up.
1799         // (We only keep each cached attribute run for one call.)
1800         attribRun.text = nullptr;
1801         // The cache for this run is now invalid, so don't visit it again.
1802         attribRun.end = 0;
1803         return S_OK;
1804       }
1805     }
1806   }
1807 
1808   HRESULT hr = ResolveIAHypertext();
1809   if (FAILED(hr)) {
1810     return hr;
1811   }
1812 
1813   return mIAHypertextPassThru->get_attributes(offset, startOffset, endOffset,
1814                                               textAttributes);
1815 }
1816 
1817 HRESULT
get_caretOffset(long * offset)1818 AccessibleHandler::get_caretOffset(long* offset) {
1819   HRESULT hr = ResolveIAHypertext();
1820   if (FAILED(hr)) {
1821     return hr;
1822   }
1823 
1824   return mIAHypertextPassThru->get_caretOffset(offset);
1825 }
1826 
1827 HRESULT
get_characterExtents(long offset,enum IA2CoordinateType coordType,long * x,long * y,long * width,long * height)1828 AccessibleHandler::get_characterExtents(long offset,
1829                                         enum IA2CoordinateType coordType,
1830                                         long* x, long* y, long* width,
1831                                         long* height) {
1832   HRESULT hr = ResolveIAHypertext();
1833   if (FAILED(hr)) {
1834     return hr;
1835   }
1836 
1837   return mIAHypertextPassThru->get_characterExtents(offset, coordType, x, y,
1838                                                     width, height);
1839 }
1840 
1841 HRESULT
get_nSelections(long * nSelections)1842 AccessibleHandler::get_nSelections(long* nSelections) {
1843   HRESULT hr = ResolveIAHypertext();
1844   if (FAILED(hr)) {
1845     return hr;
1846   }
1847 
1848   return mIAHypertextPassThru->get_nSelections(nSelections);
1849 }
1850 
1851 HRESULT
get_offsetAtPoint(long x,long y,enum IA2CoordinateType coordType,long * offset)1852 AccessibleHandler::get_offsetAtPoint(long x, long y,
1853                                      enum IA2CoordinateType coordType,
1854                                      long* offset) {
1855   HRESULT hr = ResolveIAHypertext();
1856   if (FAILED(hr)) {
1857     return hr;
1858   }
1859 
1860   return mIAHypertextPassThru->get_offsetAtPoint(x, y, coordType, offset);
1861 }
1862 
1863 HRESULT
get_selection(long selectionIndex,long * startOffset,long * endOffset)1864 AccessibleHandler::get_selection(long selectionIndex, long* startOffset,
1865                                  long* endOffset) {
1866   HRESULT hr = ResolveIAHypertext();
1867   if (FAILED(hr)) {
1868     return hr;
1869   }
1870 
1871   return mIAHypertextPassThru->get_selection(selectionIndex, startOffset,
1872                                              endOffset);
1873 }
1874 
1875 HRESULT
get_text(long startOffset,long endOffset,BSTR * text)1876 AccessibleHandler::get_text(long startOffset, long endOffset, BSTR* text) {
1877   if (!text) {
1878     return E_INVALIDARG;
1879   }
1880 
1881   HRESULT hr;
1882   if (mCachedData.mGeckoBackChannel && startOffset == 0 &&
1883       endOffset == IA2_TEXT_OFFSET_LENGTH) {
1884     // If the caller is retrieving all text, they will probably want all
1885     // hyperlinks and attributes as well.
1886     hr = GetAllTextInfo(text);
1887     if (SUCCEEDED(hr)) {
1888       return hr;
1889     }
1890     // We fall back to a normal call if this fails.
1891   }
1892 
1893   hr = ResolveIAHypertext();
1894   if (FAILED(hr)) {
1895     return hr;
1896   }
1897 
1898   return mIAHypertextPassThru->get_text(startOffset, endOffset, text);
1899 }
1900 
1901 HRESULT
get_textBeforeOffset(long offset,enum IA2TextBoundaryType boundaryType,long * startOffset,long * endOffset,BSTR * text)1902 AccessibleHandler::get_textBeforeOffset(long offset,
1903                                         enum IA2TextBoundaryType boundaryType,
1904                                         long* startOffset, long* endOffset,
1905                                         BSTR* text) {
1906   HRESULT hr = ResolveIAHypertext();
1907   if (FAILED(hr)) {
1908     return hr;
1909   }
1910 
1911   return mIAHypertextPassThru->get_textBeforeOffset(
1912       offset, boundaryType, startOffset, endOffset, text);
1913 }
1914 
1915 HRESULT
get_textAfterOffset(long offset,enum IA2TextBoundaryType boundaryType,long * startOffset,long * endOffset,BSTR * text)1916 AccessibleHandler::get_textAfterOffset(long offset,
1917                                        enum IA2TextBoundaryType boundaryType,
1918                                        long* startOffset, long* endOffset,
1919                                        BSTR* text) {
1920   HRESULT hr = ResolveIAHypertext();
1921   if (FAILED(hr)) {
1922     return hr;
1923   }
1924 
1925   return mIAHypertextPassThru->get_textAfterOffset(
1926       offset, boundaryType, startOffset, endOffset, text);
1927 }
1928 
1929 HRESULT
get_textAtOffset(long offset,enum IA2TextBoundaryType boundaryType,long * startOffset,long * endOffset,BSTR * text)1930 AccessibleHandler::get_textAtOffset(long offset,
1931                                     enum IA2TextBoundaryType boundaryType,
1932                                     long* startOffset, long* endOffset,
1933                                     BSTR* text) {
1934   HRESULT hr = ResolveIAHypertext();
1935   if (FAILED(hr)) {
1936     return hr;
1937   }
1938 
1939   return mIAHypertextPassThru->get_textAtOffset(offset, boundaryType,
1940                                                 startOffset, endOffset, text);
1941 }
1942 
1943 HRESULT
removeSelection(long selectionIndex)1944 AccessibleHandler::removeSelection(long selectionIndex) {
1945   HRESULT hr = ResolveIAHypertext();
1946   if (FAILED(hr)) {
1947     return hr;
1948   }
1949 
1950   return mIAHypertextPassThru->removeSelection(selectionIndex);
1951 }
1952 
1953 HRESULT
setCaretOffset(long offset)1954 AccessibleHandler::setCaretOffset(long offset) {
1955   HRESULT hr = ResolveIAHypertext();
1956   if (FAILED(hr)) {
1957     return hr;
1958   }
1959 
1960   return mIAHypertextPassThru->setCaretOffset(offset);
1961 }
1962 
1963 HRESULT
setSelection(long selectionIndex,long startOffset,long endOffset)1964 AccessibleHandler::setSelection(long selectionIndex, long startOffset,
1965                                 long endOffset) {
1966   HRESULT hr = ResolveIAHypertext();
1967   if (FAILED(hr)) {
1968     return hr;
1969   }
1970 
1971   return mIAHypertextPassThru->setSelection(selectionIndex, startOffset,
1972                                             endOffset);
1973 }
1974 
1975 HRESULT
get_nCharacters(long * nCharacters)1976 AccessibleHandler::get_nCharacters(long* nCharacters) {
1977   HRESULT hr = ResolveIAHypertext();
1978   if (FAILED(hr)) {
1979     return hr;
1980   }
1981 
1982   return mIAHypertextPassThru->get_nCharacters(nCharacters);
1983 }
1984 
1985 HRESULT
scrollSubstringTo(long startIndex,long endIndex,enum IA2ScrollType scrollType)1986 AccessibleHandler::scrollSubstringTo(long startIndex, long endIndex,
1987                                      enum IA2ScrollType scrollType) {
1988   HRESULT hr = ResolveIAHypertext();
1989   if (FAILED(hr)) {
1990     return hr;
1991   }
1992 
1993   return mIAHypertextPassThru->scrollSubstringTo(startIndex, endIndex,
1994                                                  scrollType);
1995 }
1996 
1997 HRESULT
scrollSubstringToPoint(long startIndex,long endIndex,enum IA2CoordinateType coordinateType,long x,long y)1998 AccessibleHandler::scrollSubstringToPoint(long startIndex, long endIndex,
1999                                           enum IA2CoordinateType coordinateType,
2000                                           long x, long y) {
2001   HRESULT hr = ResolveIAHypertext();
2002   if (FAILED(hr)) {
2003     return hr;
2004   }
2005 
2006   return mIAHypertextPassThru->scrollSubstringToPoint(startIndex, endIndex,
2007                                                       coordinateType, x, y);
2008 }
2009 
2010 HRESULT
get_newText(IA2TextSegment * newText)2011 AccessibleHandler::get_newText(IA2TextSegment* newText) {
2012   if (!newText) {
2013     return E_INVALIDARG;
2014   }
2015 
2016   RefPtr<AccessibleHandlerControl> ctl(gControlFactory.GetSingleton());
2017   MOZ_ASSERT(ctl);
2018   if (!ctl) {
2019     return S_OK;
2020   }
2021 
2022   long id;
2023   HRESULT hr = this->get_uniqueID(&id);
2024   if (FAILED(hr)) {
2025     return hr;
2026   }
2027 
2028   return ctl->GetNewText(id, WrapNotNull(newText));
2029 }
2030 
2031 HRESULT
get_oldText(IA2TextSegment * oldText)2032 AccessibleHandler::get_oldText(IA2TextSegment* oldText) {
2033   if (!oldText) {
2034     return E_INVALIDARG;
2035   }
2036 
2037   RefPtr<AccessibleHandlerControl> ctl(gControlFactory.GetSingleton());
2038   MOZ_ASSERT(ctl);
2039   if (!ctl) {
2040     return S_OK;
2041   }
2042 
2043   long id;
2044   HRESULT hr = this->get_uniqueID(&id);
2045   if (FAILED(hr)) {
2046     return hr;
2047   }
2048 
2049   return ctl->GetOldText(id, WrapNotNull(oldText));
2050 }
2051 
2052 /*** IAccessibleHypertext ***/
2053 
2054 HRESULT
get_nHyperlinks(long * hyperlinkCount)2055 AccessibleHandler::get_nHyperlinks(long* hyperlinkCount) {
2056   HRESULT hr = ResolveIAHypertext();
2057   if (FAILED(hr)) {
2058     return hr;
2059   }
2060 
2061   return mIAHypertextPassThru->get_nHyperlinks(hyperlinkCount);
2062 }
2063 
2064 HRESULT
get_hyperlink(long index,IAccessibleHyperlink ** hyperlink)2065 AccessibleHandler::get_hyperlink(long index, IAccessibleHyperlink** hyperlink) {
2066   HRESULT hr = ResolveIAHypertext();
2067   if (FAILED(hr)) {
2068     return hr;
2069   }
2070 
2071   return mIAHypertextPassThru->get_hyperlink(index, hyperlink);
2072 }
2073 
2074 HRESULT
get_hyperlinkIndex(long charIndex,long * hyperlinkIndex)2075 AccessibleHandler::get_hyperlinkIndex(long charIndex, long* hyperlinkIndex) {
2076   HRESULT hr = ResolveIAHypertext();
2077   if (FAILED(hr)) {
2078     return hr;
2079   }
2080 
2081   return mIAHypertextPassThru->get_hyperlinkIndex(charIndex, hyperlinkIndex);
2082 }
2083 
2084 /*** IAccessibleHypertext2 ***/
2085 
2086 HRESULT
get_hyperlinks(IAccessibleHyperlink *** hyperlinks,long * nHyperlinks)2087 AccessibleHandler::get_hyperlinks(IAccessibleHyperlink*** hyperlinks,
2088                                   long* nHyperlinks) {
2089   if (!hyperlinks || !nHyperlinks) {
2090     return E_INVALIDARG;
2091   }
2092 
2093   if (mCachedNHyperlinks >= 0) {
2094     // We have cached hyperlinks.
2095     *hyperlinks = mCachedHyperlinks;
2096     *nHyperlinks = mCachedNHyperlinks;
2097     // The client will clean these up. (We only keep the cache for one call.)
2098     mCachedHyperlinks = nullptr;
2099     mCachedNHyperlinks = -1;
2100     return mCachedNHyperlinks == 0 ? S_FALSE : S_OK;
2101   }
2102 
2103   HRESULT hr = ResolveIAHypertext();
2104   if (FAILED(hr)) {
2105     return hr;
2106   }
2107 
2108   return mIAHypertextPassThru->get_hyperlinks(hyperlinks, nHyperlinks);
2109 }
2110 
2111 }  // namespace a11y
2112 }  // namespace mozilla
2113 
2114 extern "C" HRESULT __stdcall ProxyDllCanUnloadNow();
2115 
DllCanUnloadNow()2116 extern "C" HRESULT __stdcall DllCanUnloadNow() {
2117   return mozilla::mscom::Module::CanUnload() && ProxyDllCanUnloadNow();
2118 }
2119 
2120 extern "C" HRESULT __stdcall ProxyDllGetClassObject(REFCLSID aClsid,
2121                                                     REFIID aIid,
2122                                                     LPVOID* aOutInterface);
2123 
DllGetClassObject(REFCLSID aClsid,REFIID aIid,LPVOID * aOutInterface)2124 extern "C" HRESULT __stdcall DllGetClassObject(REFCLSID aClsid, REFIID aIid,
2125                                                LPVOID* aOutInterface) {
2126   if (aClsid == CLSID_AccessibleHandler) {
2127     return mozilla::a11y::sHandlerFactory.QueryInterface(aIid, aOutInterface);
2128   }
2129   return ProxyDllGetClassObject(aClsid, aIid, aOutInterface);
2130 }
2131 
2132 extern "C" BOOL WINAPI ProxyDllMain(HINSTANCE aInstDll, DWORD aReason,
2133                                     LPVOID aReserved);
2134 
DllMain(HINSTANCE aInstDll,DWORD aReason,LPVOID aReserved)2135 BOOL WINAPI DllMain(HINSTANCE aInstDll, DWORD aReason, LPVOID aReserved) {
2136   if (aReason == DLL_PROCESS_ATTACH) {
2137     DisableThreadLibraryCalls((HMODULE)aInstDll);
2138   }
2139   // This is required for ProxyDllRegisterServer to work correctly
2140   return ProxyDllMain(aInstDll, aReason, aReserved);
2141 }
2142 
2143 extern "C" HRESULT __stdcall ProxyDllRegisterServer();
2144 
DllRegisterServer()2145 extern "C" HRESULT __stdcall DllRegisterServer() {
2146   HRESULT hr = mozilla::mscom::Handler::Register(CLSID_AccessibleHandler);
2147   if (FAILED(hr)) {
2148     return hr;
2149   }
2150 
2151   return ProxyDllRegisterServer();
2152 }
2153 
2154 extern "C" HRESULT __stdcall ProxyDllUnregisterServer();
2155 
DllUnregisterServer()2156 extern "C" HRESULT __stdcall DllUnregisterServer() {
2157   HRESULT hr = mozilla::mscom::Handler::Unregister(CLSID_AccessibleHandler);
2158   if (FAILED(hr)) {
2159     return hr;
2160   }
2161 
2162   return ProxyDllUnregisterServer();
2163 }
2164