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 #include "LazyInstantiator.h"
8
9 #include "MainThreadUtils.h"
10 #include "mozilla/a11y/LocalAccessible.h"
11 #include "mozilla/a11y/AccessibleHandler.h"
12 #include "mozilla/a11y/Compatibility.h"
13 #include "mozilla/a11y/Platform.h"
14 #include "mozilla/Assertions.h"
15 #include "mozilla/mscom/ProcessRuntime.h"
16 #include "mozilla/mscom/Registration.h"
17 #include "mozilla/UniquePtr.h"
18 #include "mozilla/WinHeaderOnlyUtils.h"
19 #include "MsaaRootAccessible.h"
20 #include "nsAccessibilityService.h"
21 #include "nsWindowsHelpers.h"
22 #include "nsCOMPtr.h"
23 #include "nsIFile.h"
24 #include "nsXPCOM.h"
25 #include "WinUtils.h"
26 #include "prenv.h"
27
28 #include <oaidl.h>
29
30 #if !defined(STATE_SYSTEM_NORMAL)
31 # define STATE_SYSTEM_NORMAL (0)
32 #endif // !defined(STATE_SYSTEM_NORMAL)
33
34 #define DLL_BLOCKLIST_ENTRY(name, ...) {L##name, __VA_ARGS__},
35 #define DLL_BLOCKLIST_STRING_TYPE const wchar_t*
36 #include "mozilla/WindowsDllBlocklistA11yDefs.h"
37
38 namespace mozilla {
39 namespace a11y {
40
41 static const wchar_t kLazyInstantiatorProp[] =
42 L"mozilla::a11y::LazyInstantiator";
43
44 /* static */
GetRootAccessible(HWND aHwnd)45 already_AddRefed<IAccessible> LazyInstantiator::GetRootAccessible(HWND aHwnd) {
46 // There must only be one LazyInstantiator per HWND.
47 // To track this, we set the kLazyInstantiatorProp on the HWND with a pointer
48 // to an existing instance. We only create a new LazyInstatiator if that prop
49 // has not already been set.
50 LazyInstantiator* existingInstantiator = reinterpret_cast<LazyInstantiator*>(
51 ::GetProp(aHwnd, kLazyInstantiatorProp));
52
53 RefPtr<IAccessible> result;
54 if (existingInstantiator) {
55 // Temporarily disable blind aggregation until we know that we have been
56 // marshaled. See EnableBlindAggregation for more information.
57 existingInstantiator->mAllowBlindAggregation = false;
58 result = existingInstantiator;
59 return result.forget();
60 }
61
62 // At this time we only want to check whether the acc service is running; We
63 // don't actually want to create the acc service yet.
64 if (!GetAccService()) {
65 // a11y is not running yet, there are no existing LazyInstantiators for this
66 // HWND, so create a new one and return it as a surrogate for the root
67 // accessible.
68 result = new LazyInstantiator(aHwnd);
69 return result.forget();
70 }
71
72 // a11y is running, so we just resolve the real root accessible.
73 a11y::LocalAccessible* rootAcc =
74 widget::WinUtils::GetRootAccessibleForHWND(aHwnd);
75 if (!rootAcc) {
76 return nullptr;
77 }
78
79 if (!rootAcc->IsRoot()) {
80 // rootAcc might represent a popup as opposed to a true root accessible.
81 // In that case we just use the regular LocalAccessible::GetNativeInterface.
82 rootAcc->GetNativeInterface(getter_AddRefs(result));
83 return result.forget();
84 }
85
86 auto msaaRoot =
87 static_cast<MsaaRootAccessible*>(MsaaAccessible::GetFrom(rootAcc));
88 // Subtle: msaaRoot might still be wrapped by a LazyInstantiator, but we
89 // don't need LazyInstantiator's capabilities anymore (since a11y is already
90 // running). We can bypass LazyInstantiator by retrieving the internal
91 // unknown (which is not wrapped by the LazyInstantiator) and then querying
92 // that for IID_IAccessible.
93 RefPtr<IUnknown> punk(msaaRoot->GetInternalUnknown());
94
95 MOZ_ASSERT(punk);
96 if (!punk) {
97 return nullptr;
98 }
99
100 punk->QueryInterface(IID_IAccessible, getter_AddRefs(result));
101 return result.forget();
102 }
103
104 /**
105 * When marshaling an interface, COM makes a whole bunch of QueryInterface
106 * calls to determine what kind of marshaling the interface supports. We need
107 * to handle those queries without instantiating a11y, so we temporarily
108 * disable passing through of QueryInterface calls to a11y. Once we know that
109 * COM is finished marshaling, we call EnableBlindAggregation to re-enable
110 * QueryInterface passthrough.
111 */
112 /* static */
EnableBlindAggregation(HWND aHwnd)113 void LazyInstantiator::EnableBlindAggregation(HWND aHwnd) {
114 LazyInstantiator* existingInstantiator = reinterpret_cast<LazyInstantiator*>(
115 ::GetProp(aHwnd, kLazyInstantiatorProp));
116
117 if (!existingInstantiator) {
118 return;
119 }
120
121 existingInstantiator->mAllowBlindAggregation = true;
122 }
123
LazyInstantiator(HWND aHwnd)124 LazyInstantiator::LazyInstantiator(HWND aHwnd)
125 : mHwnd(aHwnd),
126 mAllowBlindAggregation(false),
127 mWeakMsaaRoot(nullptr),
128 mWeakAccessible(nullptr),
129 mWeakDispatch(nullptr) {
130 MOZ_ASSERT(aHwnd);
131 // Assign ourselves as the designated LazyInstantiator for aHwnd
132 DebugOnly<BOOL> setPropOk =
133 ::SetProp(aHwnd, kLazyInstantiatorProp, reinterpret_cast<HANDLE>(this));
134 MOZ_ASSERT(setPropOk);
135 }
136
~LazyInstantiator()137 LazyInstantiator::~LazyInstantiator() {
138 if (mRealRootUnk) {
139 // Disconnect ourselves from the root accessible.
140 RefPtr<IUnknown> dummy(mWeakMsaaRoot->Aggregate(nullptr));
141 }
142
143 ClearProp();
144 }
145
ClearProp()146 void LazyInstantiator::ClearProp() {
147 // Remove ourselves as the designated LazyInstantiator for mHwnd
148 DebugOnly<HANDLE> removedProp = ::RemoveProp(mHwnd, kLazyInstantiatorProp);
149 MOZ_ASSERT(!removedProp ||
150 reinterpret_cast<LazyInstantiator*>(removedProp.value) == this);
151 }
152
153 /**
154 * Given the remote client's thread ID, resolve its process ID.
155 */
156 DWORD
GetClientPid(const DWORD aClientTid)157 LazyInstantiator::GetClientPid(const DWORD aClientTid) {
158 nsAutoHandle callingThread(
159 ::OpenThread(THREAD_QUERY_LIMITED_INFORMATION, FALSE, aClientTid));
160 if (!callingThread) {
161 return 0;
162 }
163
164 return ::GetProcessIdOfThread(callingThread);
165 }
166
167 /**
168 * This is the blocklist for known "bad" remote clients that instantiate a11y.
169 */
170 static const char* gBlockedRemoteClients[] = {
171 "tbnotifier.exe", // Ask.com Toolbar, bug 1453876
172 "flow.exe", // Conexant Flow causes performance issues, bug 1569712
173 "rtop_bg.exe", // ByteFence Anti-Malware, bug 1713383
174 };
175
176 /**
177 * Check for the presence of any known "bad" injected DLLs that may be trying
178 * to instantiate a11y.
179 *
180 * @return true to block a11y instantiation, otherwise false to continue
181 */
IsBlockedInjection()182 bool LazyInstantiator::IsBlockedInjection() {
183 // Check debugging options see if we should disable the blocklist.
184 if (PR_GetEnv("MOZ_DISABLE_ACCESSIBLE_BLOCKLIST")) {
185 return false;
186 }
187
188 if (Compatibility::HasKnownNonUiaConsumer()) {
189 // If we already see a known AT, don't block a11y instantiation
190 return false;
191 }
192
193 for (size_t index = 0, len = ArrayLength(gBlockedInprocDlls); index < len;
194 ++index) {
195 const DllBlockInfo& blockedDll = gBlockedInprocDlls[index];
196 HMODULE module = ::GetModuleHandleW(blockedDll.mName);
197 if (!module) {
198 // This dll isn't loaded.
199 continue;
200 }
201
202 LauncherResult<ModuleVersion> version = GetModuleVersion(module);
203 return version.isOk() && blockedDll.IsVersionBlocked(version.unwrap());
204 }
205
206 return false;
207 }
208
209 /**
210 * Given a remote client's thread ID, determine whether we should proceed with
211 * a11y instantiation. This is where telemetry should be gathered and any
212 * potential blocking of unwanted a11y clients should occur.
213 *
214 * @return true if we should instantiate a11y
215 */
ShouldInstantiate(const DWORD aClientTid)216 bool LazyInstantiator::ShouldInstantiate(const DWORD aClientTid) {
217 if (!aClientTid) {
218 // aClientTid == 0 implies that this is either an in-process call, or else
219 // we failed to retrieve information about the remote caller.
220 // We should always default to instantiating a11y in this case, provided
221 // that we don't see any known bad injected DLLs.
222 return !IsBlockedInjection();
223 }
224
225 a11y::SetInstantiator(GetClientPid(aClientTid));
226
227 nsCOMPtr<nsIFile> clientExe;
228 if (!a11y::GetInstantiator(getter_AddRefs(clientExe))) {
229 return true;
230 }
231
232 nsresult rv;
233 if (!PR_GetEnv("MOZ_DISABLE_ACCESSIBLE_BLOCKLIST")) {
234 // Debugging option is not present, so check blocklist.
235 nsAutoString leafName;
236 rv = clientExe->GetLeafName(leafName);
237 if (NS_SUCCEEDED(rv)) {
238 for (size_t i = 0, len = ArrayLength(gBlockedRemoteClients); i < len;
239 ++i) {
240 if (leafName.EqualsIgnoreCase(gBlockedRemoteClients[i])) {
241 // If client exe is in our blocklist, do not instantiate.
242 return false;
243 }
244 }
245 }
246 }
247
248 return true;
249 }
250
ResolveMsaaRoot()251 MsaaRootAccessible* LazyInstantiator::ResolveMsaaRoot() {
252 LocalAccessible* acc = widget::WinUtils::GetRootAccessibleForHWND(mHwnd);
253 if (!acc || !acc->IsRoot()) {
254 return nullptr;
255 }
256
257 RefPtr<IAccessible> ia;
258 acc->GetNativeInterface(getter_AddRefs(ia));
259 return static_cast<MsaaRootAccessible*>(ia.get());
260 }
261
262 /**
263 * With COM aggregation, the aggregated inner object usually delegates its
264 * reference counting to the outer object. In other words, we would expect
265 * mRealRootUnk to delegate its AddRef() and Release() to this LazyInstantiator.
266 *
267 * This scheme will not work in our case because the RootAccessibleWrap is
268 * cycle-collected!
269 *
270 * Instead, once a LazyInstantiator aggregates a RootAccessibleWrap, we transfer
271 * our strong references into mRealRootUnk. Any future calls to AddRef or
272 * Release now operate on mRealRootUnk instead of our intrinsic reference
273 * count. This is a bit strange, but it is the only way for these objects to
274 * share their reference count in a way that is safe for cycle collection.
275 *
276 * How do we know when it is safe to destroy ourselves? In
277 * LazyInstantiator::Release, we examine the result of mRealRootUnk->Release().
278 * If mRealRootUnk's resulting refcount is 1, then we know that the only
279 * remaining reference to mRealRootUnk is the mRealRootUnk reference itself (and
280 * thus nobody else holds references to either this or mRealRootUnk). Therefore
281 * we may now delete ourselves.
282 */
TransplantRefCnt()283 void LazyInstantiator::TransplantRefCnt() {
284 MOZ_ASSERT(mRefCnt > 0);
285 MOZ_ASSERT(mRealRootUnk);
286
287 while (mRefCnt > 0) {
288 mRealRootUnk.get()->AddRef();
289 --mRefCnt;
290 }
291 }
292
293 HRESULT
MaybeResolveRoot()294 LazyInstantiator::MaybeResolveRoot() {
295 MOZ_ASSERT(NS_IsMainThread());
296 if (mWeakAccessible) {
297 return S_OK;
298 }
299
300 if (GetAccService() ||
301 ShouldInstantiate(mscom::ProcessRuntime::GetClientThreadId())) {
302 mWeakMsaaRoot = ResolveMsaaRoot();
303 if (!mWeakMsaaRoot) {
304 return E_POINTER;
305 }
306
307 // Wrap ourselves around the root accessible wrap
308 mRealRootUnk = mWeakMsaaRoot->Aggregate(static_cast<IAccessible*>(this));
309 if (!mRealRootUnk) {
310 return E_FAIL;
311 }
312
313 // Move our strong references into the root accessible (see the comments
314 // above TransplantRefCnt for explanation).
315 TransplantRefCnt();
316
317 // Now obtain mWeakAccessible which we use to forward our incoming calls
318 // to the real accesssible.
319 HRESULT hr =
320 mRealRootUnk->QueryInterface(IID_IAccessible, (void**)&mWeakAccessible);
321 if (FAILED(hr)) {
322 return hr;
323 }
324
325 // mWeakAccessible is weak, so don't hold a strong ref
326 mWeakAccessible->Release();
327
328 // Now that a11y is running, we don't need to remain registered with our
329 // HWND anymore.
330 ClearProp();
331
332 return S_OK;
333 }
334
335 // If we don't want a real root, let's resolve a fake one.
336
337 const WPARAM flags = 0xFFFFFFFFUL;
338 // Synthesize a WM_GETOBJECT request to obtain a system-implemented
339 // IAccessible object from DefWindowProc
340 LRESULT lresult = ::DefWindowProc(mHwnd, WM_GETOBJECT, flags,
341 static_cast<LPARAM>(OBJID_CLIENT));
342
343 HRESULT hr = ObjectFromLresult(lresult, IID_IAccessible, flags,
344 getter_AddRefs(mRealRootUnk));
345 if (FAILED(hr)) {
346 return hr;
347 }
348
349 if (!mRealRootUnk) {
350 return E_NOTIMPL;
351 }
352
353 hr = mRealRootUnk->QueryInterface(IID_IAccessible, (void**)&mWeakAccessible);
354 if (FAILED(hr)) {
355 return hr;
356 }
357
358 // mWeakAccessible is weak, so don't hold a strong ref
359 mWeakAccessible->Release();
360
361 return S_OK;
362 }
363
364 #define RESOLVE_ROOT \
365 { \
366 HRESULT hr = MaybeResolveRoot(); \
367 if (FAILED(hr)) { \
368 return hr; \
369 } \
370 }
371
372 IMPL_IUNKNOWN_QUERY_HEAD(LazyInstantiator)
373 IMPL_IUNKNOWN_QUERY_IFACE_AMBIGIOUS(IUnknown, IAccessible)
374 IMPL_IUNKNOWN_QUERY_IFACE(IAccessible)
375 IMPL_IUNKNOWN_QUERY_IFACE(IDispatch)
376 IMPL_IUNKNOWN_QUERY_IFACE(IServiceProvider)
377 // See EnableBlindAggregation for comments.
378 if (!mAllowBlindAggregation) {
379 return E_NOINTERFACE;
380 }
381
382 if (aIID == IID_IAccIdentity) {
383 return E_NOINTERFACE;
384 }
385 // If the client queries for an interface that LazyInstantiator does not
386 // intrinsically support, then we must resolve the root accessible and pass
387 // on the QueryInterface call to mRealRootUnk.
388 RESOLVE_ROOT
IMPL_IUNKNOWN_QUERY_TAIL_AGGREGATED(mRealRootUnk)389 IMPL_IUNKNOWN_QUERY_TAIL_AGGREGATED(mRealRootUnk)
390
391 ULONG
392 LazyInstantiator::AddRef() {
393 // Always delegate refcounting to mRealRootUnk when it exists
394 if (mRealRootUnk) {
395 return mRealRootUnk.get()->AddRef();
396 }
397
398 return ++mRefCnt;
399 }
400
401 ULONG
Release()402 LazyInstantiator::Release() {
403 ULONG result;
404
405 // Always delegate refcounting to mRealRootUnk when it exists
406 if (mRealRootUnk) {
407 result = mRealRootUnk.get()->Release();
408 if (result == 1) {
409 // mRealRootUnk is the only strong reference left, so nothing else holds
410 // a strong reference to us. Drop result to zero so that we destroy
411 // ourselves (See the comments above LazyInstantiator::TransplantRefCnt
412 // for more info).
413 --result;
414 }
415 } else {
416 result = --mRefCnt;
417 }
418
419 if (!result) {
420 delete this;
421 }
422 return result;
423 }
424
425 /**
426 * Create a standard IDispatch implementation. mStdDispatch will translate any
427 * IDispatch::Invoke calls into real IAccessible calls.
428 */
429 HRESULT
ResolveDispatch()430 LazyInstantiator::ResolveDispatch() {
431 if (mWeakDispatch) {
432 return S_OK;
433 }
434
435 // The IAccessible typelib is embedded in oleacc.dll's resources.
436 auto typelib = mscom::RegisterTypelib(
437 L"oleacc.dll", mscom::RegistrationFlags::eUseSystemDirectory);
438 if (!typelib) {
439 return E_UNEXPECTED;
440 }
441
442 // Extract IAccessible's type info
443 RefPtr<ITypeInfo> accTypeInfo;
444 HRESULT hr =
445 typelib->GetTypeInfoForGuid(IID_IAccessible, getter_AddRefs(accTypeInfo));
446 if (FAILED(hr)) {
447 return hr;
448 }
449
450 // Now create the standard IDispatch for IAccessible
451 hr = ::CreateStdDispatch(static_cast<IAccessible*>(this),
452 static_cast<IAccessible*>(this), accTypeInfo,
453 getter_AddRefs(mStdDispatch));
454 if (FAILED(hr)) {
455 return hr;
456 }
457
458 hr = mStdDispatch->QueryInterface(IID_IDispatch, (void**)&mWeakDispatch);
459 if (FAILED(hr)) {
460 return hr;
461 }
462
463 // WEAK reference
464 mWeakDispatch->Release();
465 return S_OK;
466 }
467
468 #define RESOLVE_IDISPATCH \
469 { \
470 HRESULT hr = ResolveDispatch(); \
471 if (FAILED(hr)) { \
472 return hr; \
473 } \
474 }
475
476 /**
477 * The remaining methods implement IDispatch, IAccessible, and IServiceProvider,
478 * lazily resolving the real a11y objects and passing the call through.
479 */
480
481 HRESULT
GetTypeInfoCount(UINT * pctinfo)482 LazyInstantiator::GetTypeInfoCount(UINT* pctinfo) {
483 RESOLVE_IDISPATCH;
484 return mWeakDispatch->GetTypeInfoCount(pctinfo);
485 }
486
487 HRESULT
GetTypeInfo(UINT iTInfo,LCID lcid,ITypeInfo ** ppTInfo)488 LazyInstantiator::GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo) {
489 RESOLVE_IDISPATCH;
490 return mWeakDispatch->GetTypeInfo(iTInfo, lcid, ppTInfo);
491 }
492
493 HRESULT
GetIDsOfNames(REFIID riid,LPOLESTR * rgszNames,UINT cNames,LCID lcid,DISPID * rgDispId)494 LazyInstantiator::GetIDsOfNames(REFIID riid, LPOLESTR* rgszNames, UINT cNames,
495 LCID lcid, DISPID* rgDispId) {
496 RESOLVE_IDISPATCH;
497 return mWeakDispatch->GetIDsOfNames(riid, rgszNames, cNames, lcid, rgDispId);
498 }
499
500 HRESULT
Invoke(DISPID dispIdMember,REFIID riid,LCID lcid,WORD wFlags,DISPPARAMS * pDispParams,VARIANT * pVarResult,EXCEPINFO * pExcepInfo,UINT * puArgErr)501 LazyInstantiator::Invoke(DISPID dispIdMember, REFIID riid, LCID lcid,
502 WORD wFlags, DISPPARAMS* pDispParams,
503 VARIANT* pVarResult, EXCEPINFO* pExcepInfo,
504 UINT* puArgErr) {
505 RESOLVE_IDISPATCH;
506 return mWeakDispatch->Invoke(dispIdMember, riid, lcid, wFlags, pDispParams,
507 pVarResult, pExcepInfo, puArgErr);
508 }
509
510 HRESULT
get_accParent(IDispatch ** ppdispParent)511 LazyInstantiator::get_accParent(IDispatch** ppdispParent) {
512 if (!mWeakAccessible) {
513 // If we'd resolve the root right now this would be the codepath we'd end
514 // up in anyway. So we might as well return it here.
515 return ::AccessibleObjectFromWindow(mHwnd, OBJID_WINDOW, IID_IAccessible,
516 (void**)ppdispParent);
517 }
518 RESOLVE_ROOT;
519 return mWeakAccessible->get_accParent(ppdispParent);
520 }
521
522 HRESULT
get_accChildCount(long * pcountChildren)523 LazyInstantiator::get_accChildCount(long* pcountChildren) {
524 if (!pcountChildren) {
525 return E_INVALIDARG;
526 }
527
528 RESOLVE_ROOT;
529 return mWeakAccessible->get_accChildCount(pcountChildren);
530 }
531
532 HRESULT
get_accChild(VARIANT varChild,IDispatch ** ppdispChild)533 LazyInstantiator::get_accChild(VARIANT varChild, IDispatch** ppdispChild) {
534 if (!ppdispChild) {
535 return E_INVALIDARG;
536 }
537
538 if (V_VT(&varChild) == VT_I4 && V_I4(&varChild) == CHILDID_SELF) {
539 RefPtr<IDispatch> disp(this);
540 disp.forget(ppdispChild);
541 return S_OK;
542 }
543
544 RESOLVE_ROOT;
545 return mWeakAccessible->get_accChild(varChild, ppdispChild);
546 }
547
548 HRESULT
get_accName(VARIANT varChild,BSTR * pszName)549 LazyInstantiator::get_accName(VARIANT varChild, BSTR* pszName) {
550 if (!pszName) {
551 return E_INVALIDARG;
552 }
553
554 RESOLVE_ROOT;
555 return mWeakAccessible->get_accName(varChild, pszName);
556 }
557
558 HRESULT
get_accValue(VARIANT varChild,BSTR * pszValue)559 LazyInstantiator::get_accValue(VARIANT varChild, BSTR* pszValue) {
560 if (!pszValue) {
561 return E_INVALIDARG;
562 }
563
564 RESOLVE_ROOT;
565 return mWeakAccessible->get_accValue(varChild, pszValue);
566 }
567
568 HRESULT
get_accDescription(VARIANT varChild,BSTR * pszDescription)569 LazyInstantiator::get_accDescription(VARIANT varChild, BSTR* pszDescription) {
570 if (!pszDescription) {
571 return E_INVALIDARG;
572 }
573
574 RESOLVE_ROOT;
575 return mWeakAccessible->get_accDescription(varChild, pszDescription);
576 }
577
578 HRESULT
get_accRole(VARIANT varChild,VARIANT * pvarRole)579 LazyInstantiator::get_accRole(VARIANT varChild, VARIANT* pvarRole) {
580 if (!pvarRole) {
581 return E_INVALIDARG;
582 }
583
584 if (V_VT(&varChild) == VT_I4 && V_I4(&varChild) == CHILDID_SELF) {
585 V_VT(pvarRole) = VT_I4;
586 V_I4(pvarRole) = ROLE_SYSTEM_APPLICATION;
587 return S_OK;
588 }
589
590 RESOLVE_ROOT;
591 return mWeakAccessible->get_accRole(varChild, pvarRole);
592 }
593
594 HRESULT
get_accState(VARIANT varChild,VARIANT * pvarState)595 LazyInstantiator::get_accState(VARIANT varChild, VARIANT* pvarState) {
596 if (!pvarState) {
597 return E_INVALIDARG;
598 }
599
600 RESOLVE_ROOT;
601 return mWeakAccessible->get_accState(varChild, pvarState);
602 }
603
604 HRESULT
get_accHelp(VARIANT varChild,BSTR * pszHelp)605 LazyInstantiator::get_accHelp(VARIANT varChild, BSTR* pszHelp) {
606 return E_NOTIMPL;
607 }
608
609 HRESULT
get_accHelpTopic(BSTR * pszHelpFile,VARIANT varChild,long * pidTopic)610 LazyInstantiator::get_accHelpTopic(BSTR* pszHelpFile, VARIANT varChild,
611 long* pidTopic) {
612 return E_NOTIMPL;
613 }
614
615 HRESULT
get_accKeyboardShortcut(VARIANT varChild,BSTR * pszKeyboardShortcut)616 LazyInstantiator::get_accKeyboardShortcut(VARIANT varChild,
617 BSTR* pszKeyboardShortcut) {
618 if (!pszKeyboardShortcut) {
619 return E_INVALIDARG;
620 }
621
622 RESOLVE_ROOT;
623 return mWeakAccessible->get_accKeyboardShortcut(varChild,
624 pszKeyboardShortcut);
625 }
626
627 HRESULT
get_accFocus(VARIANT * pvarChild)628 LazyInstantiator::get_accFocus(VARIANT* pvarChild) {
629 if (!pvarChild) {
630 return E_INVALIDARG;
631 }
632
633 RESOLVE_ROOT;
634 return mWeakAccessible->get_accFocus(pvarChild);
635 }
636
637 HRESULT
get_accSelection(VARIANT * pvarChildren)638 LazyInstantiator::get_accSelection(VARIANT* pvarChildren) {
639 if (!pvarChildren) {
640 return E_INVALIDARG;
641 }
642
643 RESOLVE_ROOT;
644 return mWeakAccessible->get_accSelection(pvarChildren);
645 }
646
647 HRESULT
get_accDefaultAction(VARIANT varChild,BSTR * pszDefaultAction)648 LazyInstantiator::get_accDefaultAction(VARIANT varChild,
649 BSTR* pszDefaultAction) {
650 if (!pszDefaultAction) {
651 return E_INVALIDARG;
652 }
653
654 RESOLVE_ROOT;
655 return mWeakAccessible->get_accDefaultAction(varChild, pszDefaultAction);
656 }
657
658 HRESULT
accSelect(long flagsSelect,VARIANT varChild)659 LazyInstantiator::accSelect(long flagsSelect, VARIANT varChild) {
660 RESOLVE_ROOT;
661 return mWeakAccessible->accSelect(flagsSelect, varChild);
662 }
663
664 HRESULT
accLocation(long * pxLeft,long * pyTop,long * pcxWidth,long * pcyHeight,VARIANT varChild)665 LazyInstantiator::accLocation(long* pxLeft, long* pyTop, long* pcxWidth,
666 long* pcyHeight, VARIANT varChild) {
667 RESOLVE_ROOT;
668 return mWeakAccessible->accLocation(pxLeft, pyTop, pcxWidth, pcyHeight,
669 varChild);
670 }
671
672 HRESULT
accNavigate(long navDir,VARIANT varStart,VARIANT * pvarEndUpAt)673 LazyInstantiator::accNavigate(long navDir, VARIANT varStart,
674 VARIANT* pvarEndUpAt) {
675 if (!pvarEndUpAt) {
676 return E_INVALIDARG;
677 }
678
679 RESOLVE_ROOT;
680 return mWeakAccessible->accNavigate(navDir, varStart, pvarEndUpAt);
681 }
682
683 HRESULT
accHitTest(long xLeft,long yTop,VARIANT * pvarChild)684 LazyInstantiator::accHitTest(long xLeft, long yTop, VARIANT* pvarChild) {
685 if (!pvarChild) {
686 return E_INVALIDARG;
687 }
688
689 RESOLVE_ROOT;
690 return mWeakAccessible->accHitTest(xLeft, yTop, pvarChild);
691 }
692
693 HRESULT
accDoDefaultAction(VARIANT varChild)694 LazyInstantiator::accDoDefaultAction(VARIANT varChild) {
695 RESOLVE_ROOT;
696 return mWeakAccessible->accDoDefaultAction(varChild);
697 }
698
699 HRESULT
put_accName(VARIANT varChild,BSTR szName)700 LazyInstantiator::put_accName(VARIANT varChild, BSTR szName) {
701 return E_NOTIMPL;
702 }
703
704 HRESULT
put_accValue(VARIANT varChild,BSTR szValue)705 LazyInstantiator::put_accValue(VARIANT varChild, BSTR szValue) {
706 return E_NOTIMPL;
707 }
708
709 HRESULT
QueryService(REFGUID aServiceId,REFIID aServiceIid,void ** aOutInterface)710 LazyInstantiator::QueryService(REFGUID aServiceId, REFIID aServiceIid,
711 void** aOutInterface) {
712 if (!aOutInterface) {
713 return E_INVALIDARG;
714 }
715
716 for (const GUID& unsupportedService : kUnsupportedServices) {
717 if (aServiceId == unsupportedService) {
718 return E_NOINTERFACE;
719 }
720 }
721
722 *aOutInterface = nullptr;
723
724 RESOLVE_ROOT;
725
726 RefPtr<IServiceProvider> servProv;
727 HRESULT hr = mRealRootUnk->QueryInterface(IID_IServiceProvider,
728 getter_AddRefs(servProv));
729 if (FAILED(hr)) {
730 return hr;
731 }
732
733 return servProv->QueryService(aServiceId, aServiceIid, aOutInterface);
734 }
735
736 } // namespace a11y
737 } // namespace mozilla
738