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