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