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