LazyInstantiator.cpp (26329B)
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/Compatibility.h" 12 #include "mozilla/a11y/Platform.h" 13 #include "mozilla/Assertions.h" 14 #include "mozilla/mscom/ProcessRuntime.h" 15 #include "mozilla/WinHeaderOnlyUtils.h" 16 #include "MsaaRootAccessible.h" 17 #include "nsAccessibilityService.h" 18 #include "nsWindowsHelpers.h" 19 #include "nsCOMPtr.h" 20 #include "nsIFile.h" 21 #include "nsXPCOM.h" 22 #include "WinUtils.h" 23 #include "prenv.h" 24 25 #include <oaidl.h> 26 27 #if !defined(STATE_SYSTEM_NORMAL) 28 # define STATE_SYSTEM_NORMAL (0) 29 #endif // !defined(STATE_SYSTEM_NORMAL) 30 31 #define DLL_BLOCKLIST_ENTRY(name, ...) {L##name, __VA_ARGS__}, 32 #define DLL_BLOCKLIST_STRING_TYPE const wchar_t* 33 #include "mozilla/WindowsDllBlocklistA11yDefs.h" 34 35 namespace mozilla { 36 namespace a11y { 37 38 static const wchar_t kLazyInstantiatorProp[] = 39 L"mozilla::a11y::LazyInstantiator"; 40 41 Maybe<bool> LazyInstantiator::sShouldBlockUia; 42 43 template <class T> 44 already_AddRefed<T> LazyInstantiator::GetRoot(HWND aHwnd) { 45 RefPtr<T> result; 46 // At this time we only want to check whether the acc service is running. We 47 // don't actually want to create the acc service yet. 48 if (!GetAccService()) { 49 // There must only be one LazyInstantiator per HWND. 50 // To track this, we set the kLazyInstantiatorProp on the HWND with a 51 // pointer to an existing instance. We only create a new LazyInstatiator if 52 // that prop has not already been set. 53 LazyInstantiator* existingInstantiator = 54 reinterpret_cast<LazyInstantiator*>( 55 ::GetProp(aHwnd, kLazyInstantiatorProp)); 56 57 if (existingInstantiator) { 58 // Temporarily disable blind aggregation until we know that we have been 59 // marshaled. See EnableBlindAggregation for more information. 60 existingInstantiator->mAllowBlindAggregation = false; 61 result = existingInstantiator; 62 return result.forget(); 63 } 64 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 result = MsaaAccessible::GetFrom(rootAcc); 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 the interface we want. 93 RefPtr<IUnknown> punk(msaaRoot->GetInternalUnknown()); 94 95 MOZ_ASSERT(punk); 96 if (!punk) { 97 return nullptr; 98 } 99 100 punk->QueryInterface(__uuidof(T), getter_AddRefs(result)); 101 return result.forget(); 102 } 103 104 /* static */ 105 already_AddRefed<IAccessible> LazyInstantiator::GetRootAccessible(HWND aHwnd) { 106 return GetRoot<IAccessible>(aHwnd); 107 } 108 109 /* static */ 110 already_AddRefed<IRawElementProviderSimple> LazyInstantiator::GetRootUia( 111 HWND aHwnd) { 112 if (!Compatibility::IsUiaEnabled()) { 113 return nullptr; 114 } 115 return GetRoot<IRawElementProviderSimple>(aHwnd); 116 } 117 118 /** 119 * When marshaling an interface, COM makes a whole bunch of QueryInterface 120 * calls to determine what kind of marshaling the interface supports. We need 121 * to handle those queries without instantiating a11y, so we temporarily 122 * disable passing through of QueryInterface calls to a11y. Once we know that 123 * COM is finished marshaling, we call EnableBlindAggregation to re-enable 124 * QueryInterface passthrough. 125 */ 126 /* static */ 127 void LazyInstantiator::EnableBlindAggregation(HWND aHwnd) { 128 if (GetAccService()) { 129 // The accessibility service is already running. That means that 130 // LazyInstantiator::GetRootAccessible returned the real MsaaRootAccessible, 131 // rather than returning a LazyInstantiator with blind aggregation disabled. 132 // Thus, we have nothing to do here. 133 return; 134 } 135 136 LazyInstantiator* existingInstantiator = reinterpret_cast<LazyInstantiator*>( 137 ::GetProp(aHwnd, kLazyInstantiatorProp)); 138 139 if (!existingInstantiator) { 140 return; 141 } 142 143 existingInstantiator->mAllowBlindAggregation = true; 144 } 145 146 LazyInstantiator::LazyInstantiator(HWND aHwnd) 147 : mHwnd(aHwnd), 148 mAllowBlindAggregation(false), 149 mWeakMsaaRoot(nullptr), 150 mWeakAccessible(nullptr), 151 mWeakDispatch(nullptr), 152 mWeakUia(nullptr) { 153 MOZ_ASSERT(aHwnd); 154 // Assign ourselves as the designated LazyInstantiator for aHwnd 155 DebugOnly<BOOL> setPropOk = 156 ::SetProp(aHwnd, kLazyInstantiatorProp, reinterpret_cast<HANDLE>(this)); 157 MOZ_ASSERT(setPropOk); 158 } 159 160 LazyInstantiator::~LazyInstantiator() { 161 if (mRealRootUnk) { 162 // Disconnect ourselves from the root accessible. 163 RefPtr<IUnknown> dummy(mWeakMsaaRoot->Aggregate(nullptr)); 164 } 165 166 ClearProp(); 167 } 168 169 void LazyInstantiator::ClearProp() { 170 // Remove ourselves as the designated LazyInstantiator for mHwnd 171 DebugOnly<HANDLE> removedProp = ::RemoveProp(mHwnd, kLazyInstantiatorProp); 172 MOZ_ASSERT(!removedProp || 173 reinterpret_cast<LazyInstantiator*>(removedProp.value) == this); 174 } 175 176 /** 177 * Get the process id of a remote (out-of-process) MSAA/IA2 client. 178 */ 179 DWORD LazyInstantiator::GetRemoteMsaaClientPid() { 180 nsAutoHandle callingThread( 181 ::OpenThread(THREAD_QUERY_LIMITED_INFORMATION, FALSE, 182 mscom::ProcessRuntime::GetClientThreadId())); 183 if (!callingThread) { 184 return 0; 185 } 186 return ::GetProcessIdOfThread(callingThread); 187 } 188 189 /** 190 * This is the blocklist for known "bad" remote clients that instantiate a11y. 191 */ 192 static const char* gBlockedRemoteClients[] = { 193 "tbnotifier.exe", // Ask.com Toolbar, bug 1453876 194 "flow.exe", // Conexant Flow causes performance issues, bug 1569712 195 "rtop_bg.exe", // ByteFence Anti-Malware, bug 1713383 196 "osk.exe", // Windows On-Screen Keyboard, bug 1424505 197 "corplink-uc.exe", // Feilian CorpLink, bug 1951571 198 }; 199 200 /** 201 * Check for the presence of any known "bad" injected DLLs that may be trying 202 * to instantiate a11y. 203 * 204 * @return true to block a11y instantiation, otherwise false to continue 205 */ 206 bool LazyInstantiator::IsBlockedInjection() { 207 // Check debugging options see if we should disable the blocklist. 208 if (PR_GetEnv("MOZ_DISABLE_ACCESSIBLE_BLOCKLIST")) { 209 return false; 210 } 211 212 for (size_t index = 0, len = std::size(gBlockedInprocDlls); index < len; 213 ++index) { 214 const DllBlockInfo& blockedDll = gBlockedInprocDlls[index]; 215 HMODULE module = ::GetModuleHandleW(blockedDll.mName); 216 if (!module) { 217 // This dll isn't loaded. 218 continue; 219 } 220 221 LauncherResult<ModuleVersion> version = GetModuleVersion(module); 222 return version.isOk() && blockedDll.IsVersionBlocked(version.unwrap()); 223 } 224 225 return false; 226 } 227 228 /** 229 * Given a remote client's process ID, determine whether we should proceed with 230 * a11y instantiation. This is where telemetry should be gathered and any 231 * potential blocking of unwanted a11y clients should occur. 232 * 233 * @return true if we should instantiate a11y 234 */ 235 bool LazyInstantiator::ShouldInstantiate(const DWORD aClientPid) { 236 a11y::SetInstantiator(aClientPid); 237 238 nsCOMPtr<nsIFile> clientExe; 239 if (!a11y::GetInstantiator(getter_AddRefs(clientExe))) { 240 return true; 241 } 242 243 nsresult rv; 244 if (!PR_GetEnv("MOZ_DISABLE_ACCESSIBLE_BLOCKLIST")) { 245 // Debugging option is not present, so check blocklist. 246 nsAutoString leafName; 247 rv = clientExe->GetLeafName(leafName); 248 if (NS_SUCCEEDED(rv)) { 249 for (size_t i = 0, len = std::size(gBlockedRemoteClients); i < len; ++i) { 250 if (leafName.EqualsIgnoreCase(gBlockedRemoteClients[i])) { 251 // If client exe is in our blocklist, do not instantiate. 252 return false; 253 } 254 } 255 } 256 } 257 258 return true; 259 } 260 261 /** 262 * Determine whether we should proceed with a11y instantiation, considering the 263 * various different types of clients. 264 */ 265 bool LazyInstantiator::ShouldInstantiate() { 266 if (Compatibility::IsA11ySuppressed()) { 267 return false; 268 } 269 if (DWORD pid = GetRemoteMsaaClientPid()) { 270 return ShouldInstantiate(pid); 271 } 272 if (Compatibility::HasKnownNonUiaConsumer()) { 273 // We detected a known in-process client. 274 return true; 275 } 276 // UIA client detection can be expensive, so we cache the result. See the 277 // header comment for ResetUiaDetectionCache() for details. 278 if (sShouldBlockUia.isNothing()) { 279 // Unlike MSAA, we can't tell which specific UIA client is querying us right 280 // now. We can only determine which clients have tried querying us. 281 // Therefore, we must check all of them. 282 AutoTArray<DWORD, 1> uiaPids; 283 Compatibility::GetUiaClientPids(uiaPids); 284 if (uiaPids.IsEmpty()) { 285 // No UIA clients, so don't block UIA. However, we might block for 286 // non-UIA clients below. 287 sShouldBlockUia = Some(false); 288 } else { 289 for (const DWORD pid : uiaPids) { 290 if (ShouldInstantiate(pid)) { 291 sShouldBlockUia = Some(false); 292 return true; 293 } 294 } 295 // We didn't return in the loop above, so there are only blocked UIA 296 // clients. 297 sShouldBlockUia = Some(true); 298 } 299 } 300 if (*sShouldBlockUia) { 301 return false; 302 } 303 if (IsBlockedInjection()) { 304 return false; 305 } 306 return true; 307 } 308 309 MsaaRootAccessible* LazyInstantiator::ResolveMsaaRoot() { 310 LocalAccessible* acc = widget::WinUtils::GetRootAccessibleForHWND(mHwnd); 311 if (!acc || !acc->IsRoot()) { 312 return nullptr; 313 } 314 315 RefPtr<IAccessible> ia; 316 acc->GetNativeInterface(getter_AddRefs(ia)); 317 return static_cast<MsaaRootAccessible*>(ia.get()); 318 } 319 320 /** 321 * With COM aggregation, the aggregated inner object usually delegates its 322 * reference counting to the outer object. In other words, we would expect 323 * mRealRootUnk to delegate its AddRef() and Release() to this LazyInstantiator. 324 * 325 * This scheme will not work in our case because the RootAccessibleWrap is 326 * cycle-collected! 327 * 328 * Instead, once a LazyInstantiator aggregates a RootAccessibleWrap, we transfer 329 * our strong references into mRealRootUnk. Any future calls to AddRef or 330 * Release now operate on mRealRootUnk instead of our intrinsic reference 331 * count. This is a bit strange, but it is the only way for these objects to 332 * share their reference count in a way that is safe for cycle collection. 333 * 334 * How do we know when it is safe to destroy ourselves? In 335 * LazyInstantiator::Release, we examine the result of mRealRootUnk->Release(). 336 * If mRealRootUnk's resulting refcount is 1, then we know that the only 337 * remaining reference to mRealRootUnk is the mRealRootUnk reference itself (and 338 * thus nobody else holds references to either this or mRealRootUnk). Therefore 339 * we may now delete ourselves. 340 */ 341 void LazyInstantiator::TransplantRefCnt() { 342 MOZ_ASSERT(mRefCnt > 0); 343 MOZ_ASSERT(mRealRootUnk); 344 345 while (mRefCnt > 0) { 346 mRealRootUnk.get()->AddRef(); 347 --mRefCnt; 348 } 349 } 350 351 HRESULT 352 LazyInstantiator::MaybeResolveRoot() { 353 if (!GetAccService() && !ShouldInstantiate()) { 354 return E_FAIL; 355 } 356 357 if (!mWeakAccessible) { 358 mWeakMsaaRoot = ResolveMsaaRoot(); 359 if (!mWeakMsaaRoot) { 360 return E_POINTER; 361 } 362 363 // Wrap ourselves around the root accessible wrap 364 mRealRootUnk = mWeakMsaaRoot->Aggregate(static_cast<IAccessible*>(this)); 365 if (!mRealRootUnk) { 366 return E_FAIL; 367 } 368 369 // Move our strong references into the root accessible (see the comments 370 // above TransplantRefCnt for explanation). 371 TransplantRefCnt(); 372 373 // Now obtain mWeakAccessible which we use to forward our incoming calls 374 // to the real accessible. 375 HRESULT hr = 376 mRealRootUnk->QueryInterface(IID_IAccessible, (void**)&mWeakAccessible); 377 if (FAILED(hr)) { 378 return hr; 379 } 380 // mWeakAccessible is weak, so don't hold a strong ref 381 mWeakAccessible->Release(); 382 383 // Now that a11y is running, we don't need to remain registered with our 384 // HWND anymore. 385 ClearProp(); 386 } 387 388 // If the UIA pref is changed during the session, this method might be first 389 // called with UIA disabled and then called again later with UIA enabled. 390 // Thus, we handle mWeakUia separately from mWeakAccessible. 391 if (!mWeakUia && Compatibility::IsUiaEnabled()) { 392 MOZ_ASSERT(mWeakAccessible); 393 HRESULT hr = mRealRootUnk->QueryInterface(IID_IRawElementProviderSimple, 394 (void**)&mWeakUia); 395 if (FAILED(hr)) { 396 return hr; 397 } 398 mWeakUia->Release(); 399 } 400 401 return S_OK; 402 } 403 404 #define RESOLVE_ROOT \ 405 { \ 406 HRESULT hr = MaybeResolveRoot(); \ 407 if (FAILED(hr)) { \ 408 return hr; \ 409 } \ 410 } 411 412 #define RESOLVE_ROOT_UIA_RETURN_IF_FAIL \ 413 RESOLVE_ROOT \ 414 if (!mWeakUia) { \ 415 /* UIA was previously enabled, allowing QueryInterface to a UIA interface. \ 416 * It was subsequently disabled before we could resolve the root. \ 417 */ \ 418 return E_FAIL; \ 419 } 420 421 IMPL_IUNKNOWN_QUERY_HEAD(LazyInstantiator) 422 if (NS_WARN_IF(!NS_IsMainThread())) { 423 // Bug 1814780, bug 1949617: The COM marshaler sometimes calls QueryInterface 424 // on the wrong thread. 425 return RPC_E_WRONG_THREAD; 426 } 427 IMPL_IUNKNOWN_QUERY_IFACE_AMBIGIOUS(IUnknown, IAccessible) 428 IMPL_IUNKNOWN_QUERY_IFACE(IAccessible) 429 IMPL_IUNKNOWN_QUERY_IFACE(IDispatch) 430 IMPL_IUNKNOWN_QUERY_IFACE(IServiceProvider) 431 if (Compatibility::IsUiaEnabled()) { 432 IMPL_IUNKNOWN_QUERY_IFACE(IRawElementProviderSimple) 433 } 434 // See EnableBlindAggregation for comments. 435 if (!mAllowBlindAggregation) { 436 return E_NOINTERFACE; 437 } 438 439 if (aIID == IID_IAccIdentity) { 440 return E_NOINTERFACE; 441 } 442 // If the client queries for an interface that LazyInstantiator does not 443 // intrinsically support, then we must resolve the root accessible and pass 444 // on the QueryInterface call to mRealRootUnk. 445 RESOLVE_ROOT 446 IMPL_IUNKNOWN_QUERY_TAIL_AGGREGATED(mRealRootUnk) 447 448 ULONG 449 LazyInstantiator::AddRef() { 450 // Always delegate refcounting to mRealRootUnk when it exists 451 if (mRealRootUnk) { 452 return mRealRootUnk.get()->AddRef(); 453 } 454 455 return ++mRefCnt; 456 } 457 458 ULONG 459 LazyInstantiator::Release() { 460 ULONG result; 461 462 // Always delegate refcounting to mRealRootUnk when it exists 463 if (mRealRootUnk) { 464 result = mRealRootUnk.get()->Release(); 465 if (result == 1) { 466 // mRealRootUnk is the only strong reference left, so nothing else holds 467 // a strong reference to us. Drop result to zero so that we destroy 468 // ourselves (See the comments above LazyInstantiator::TransplantRefCnt 469 // for more info). 470 --result; 471 } 472 } else { 473 result = --mRefCnt; 474 } 475 476 if (!result) { 477 delete this; 478 } 479 return result; 480 } 481 482 /** 483 * Create a standard IDispatch implementation. mStdDispatch will translate any 484 * IDispatch::Invoke calls into real IAccessible calls. 485 */ 486 HRESULT 487 LazyInstantiator::ResolveDispatch() { 488 if (mWeakDispatch) { 489 return S_OK; 490 } 491 492 // Extract IAccessible's type info 493 RefPtr<ITypeInfo> accTypeInfo = MsaaAccessible::GetTI(LOCALE_USER_DEFAULT); 494 if (!accTypeInfo) { 495 return E_UNEXPECTED; 496 } 497 498 // Now create the standard IDispatch for IAccessible 499 HRESULT hr = ::CreateStdDispatch(static_cast<IAccessible*>(this), 500 static_cast<IAccessible*>(this), accTypeInfo, 501 getter_AddRefs(mStdDispatch)); 502 if (FAILED(hr)) { 503 return hr; 504 } 505 506 hr = mStdDispatch->QueryInterface(IID_IDispatch, (void**)&mWeakDispatch); 507 if (FAILED(hr)) { 508 return hr; 509 } 510 511 // WEAK reference 512 mWeakDispatch->Release(); 513 return S_OK; 514 } 515 516 #define RESOLVE_IDISPATCH \ 517 { \ 518 HRESULT hr = ResolveDispatch(); \ 519 if (FAILED(hr)) { \ 520 return hr; \ 521 } \ 522 } 523 524 /** 525 * The remaining methods implement IDispatch, IAccessible, and IServiceProvider, 526 * lazily resolving the real a11y objects and passing the call through. 527 */ 528 529 HRESULT 530 LazyInstantiator::GetTypeInfoCount(UINT* pctinfo) { 531 RESOLVE_IDISPATCH; 532 return mWeakDispatch->GetTypeInfoCount(pctinfo); 533 } 534 535 HRESULT 536 LazyInstantiator::GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo** ppTInfo) { 537 RESOLVE_IDISPATCH; 538 return mWeakDispatch->GetTypeInfo(iTInfo, lcid, ppTInfo); 539 } 540 541 HRESULT 542 LazyInstantiator::GetIDsOfNames(REFIID riid, LPOLESTR* rgszNames, UINT cNames, 543 LCID lcid, DISPID* rgDispId) { 544 RESOLVE_IDISPATCH; 545 return mWeakDispatch->GetIDsOfNames(riid, rgszNames, cNames, lcid, rgDispId); 546 } 547 548 HRESULT 549 LazyInstantiator::Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, 550 WORD wFlags, DISPPARAMS* pDispParams, 551 VARIANT* pVarResult, EXCEPINFO* pExcepInfo, 552 UINT* puArgErr) { 553 RESOLVE_IDISPATCH; 554 return mWeakDispatch->Invoke(dispIdMember, riid, lcid, wFlags, pDispParams, 555 pVarResult, pExcepInfo, puArgErr); 556 } 557 558 HRESULT 559 LazyInstantiator::get_accParent(IDispatch** ppdispParent) { 560 if (!mWeakAccessible) { 561 // If we'd resolve the root right now this would be the codepath we'd end 562 // up in anyway. So we might as well return it here. 563 return ::CreateStdAccessibleObject(mHwnd, OBJID_WINDOW, IID_IAccessible, 564 (void**)ppdispParent); 565 } 566 RESOLVE_ROOT; 567 return mWeakAccessible->get_accParent(ppdispParent); 568 } 569 570 HRESULT 571 LazyInstantiator::get_accChildCount(long* pcountChildren) { 572 if (!pcountChildren) { 573 return E_INVALIDARG; 574 } 575 576 RESOLVE_ROOT; 577 return mWeakAccessible->get_accChildCount(pcountChildren); 578 } 579 580 HRESULT 581 LazyInstantiator::get_accChild(VARIANT varChild, IDispatch** ppdispChild) { 582 if (!ppdispChild) { 583 return E_INVALIDARG; 584 } 585 586 if (V_VT(&varChild) == VT_I4 && V_I4(&varChild) == CHILDID_SELF) { 587 RefPtr<IDispatch> disp(this); 588 disp.forget(ppdispChild); 589 return S_OK; 590 } 591 592 if (NS_WARN_IF(!NS_IsMainThread())) { 593 // Bug 1965216: The COM runtime occasionally calls this method on the wrong 594 // thread, violating COM rules. We can't reproduce this and don't understand 595 // what causes it. 596 return RPC_E_WRONG_THREAD; 597 } 598 599 RESOLVE_ROOT; 600 return mWeakAccessible->get_accChild(varChild, ppdispChild); 601 } 602 603 HRESULT 604 LazyInstantiator::get_accName(VARIANT varChild, BSTR* pszName) { 605 if (!pszName) { 606 return E_INVALIDARG; 607 } 608 609 RESOLVE_ROOT; 610 return mWeakAccessible->get_accName(varChild, pszName); 611 } 612 613 HRESULT 614 LazyInstantiator::get_accValue(VARIANT varChild, BSTR* pszValue) { 615 if (!pszValue) { 616 return E_INVALIDARG; 617 } 618 619 RESOLVE_ROOT; 620 return mWeakAccessible->get_accValue(varChild, pszValue); 621 } 622 623 HRESULT 624 LazyInstantiator::get_accDescription(VARIANT varChild, BSTR* pszDescription) { 625 if (!pszDescription) { 626 return E_INVALIDARG; 627 } 628 629 RESOLVE_ROOT; 630 return mWeakAccessible->get_accDescription(varChild, pszDescription); 631 } 632 633 HRESULT 634 LazyInstantiator::get_accRole(VARIANT varChild, VARIANT* pvarRole) { 635 if (!pvarRole) { 636 return E_INVALIDARG; 637 } 638 639 if (V_VT(&varChild) == VT_I4 && V_I4(&varChild) == CHILDID_SELF) { 640 V_VT(pvarRole) = VT_I4; 641 V_I4(pvarRole) = ROLE_SYSTEM_APPLICATION; 642 return S_OK; 643 } 644 645 RESOLVE_ROOT; 646 return mWeakAccessible->get_accRole(varChild, pvarRole); 647 } 648 649 HRESULT 650 LazyInstantiator::get_accState(VARIANT varChild, VARIANT* pvarState) { 651 if (!pvarState) { 652 return E_INVALIDARG; 653 } 654 655 RESOLVE_ROOT; 656 return mWeakAccessible->get_accState(varChild, pvarState); 657 } 658 659 HRESULT 660 LazyInstantiator::get_accHelp(VARIANT varChild, BSTR* pszHelp) { 661 return E_NOTIMPL; 662 } 663 664 HRESULT 665 LazyInstantiator::get_accHelpTopic(BSTR* pszHelpFile, VARIANT varChild, 666 long* pidTopic) { 667 return E_NOTIMPL; 668 } 669 670 HRESULT 671 LazyInstantiator::get_accKeyboardShortcut(VARIANT varChild, 672 BSTR* pszKeyboardShortcut) { 673 if (!pszKeyboardShortcut) { 674 return E_INVALIDARG; 675 } 676 677 RESOLVE_ROOT; 678 return mWeakAccessible->get_accKeyboardShortcut(varChild, 679 pszKeyboardShortcut); 680 } 681 682 HRESULT 683 LazyInstantiator::get_accFocus(VARIANT* pvarChild) { 684 if (!pvarChild) { 685 return E_INVALIDARG; 686 } 687 688 RESOLVE_ROOT; 689 return mWeakAccessible->get_accFocus(pvarChild); 690 } 691 692 HRESULT 693 LazyInstantiator::get_accSelection(VARIANT* pvarChildren) { 694 if (!pvarChildren) { 695 return E_INVALIDARG; 696 } 697 698 RESOLVE_ROOT; 699 return mWeakAccessible->get_accSelection(pvarChildren); 700 } 701 702 HRESULT 703 LazyInstantiator::get_accDefaultAction(VARIANT varChild, 704 BSTR* pszDefaultAction) { 705 if (!pszDefaultAction) { 706 return E_INVALIDARG; 707 } 708 709 RESOLVE_ROOT; 710 return mWeakAccessible->get_accDefaultAction(varChild, pszDefaultAction); 711 } 712 713 HRESULT 714 LazyInstantiator::accSelect(long flagsSelect, VARIANT varChild) { 715 RESOLVE_ROOT; 716 return mWeakAccessible->accSelect(flagsSelect, varChild); 717 } 718 719 HRESULT 720 LazyInstantiator::accLocation(long* pxLeft, long* pyTop, long* pcxWidth, 721 long* pcyHeight, VARIANT varChild) { 722 RESOLVE_ROOT; 723 return mWeakAccessible->accLocation(pxLeft, pyTop, pcxWidth, pcyHeight, 724 varChild); 725 } 726 727 HRESULT 728 LazyInstantiator::accNavigate(long navDir, VARIANT varStart, 729 VARIANT* pvarEndUpAt) { 730 if (!pvarEndUpAt) { 731 return E_INVALIDARG; 732 } 733 734 RESOLVE_ROOT; 735 return mWeakAccessible->accNavigate(navDir, varStart, pvarEndUpAt); 736 } 737 738 HRESULT 739 LazyInstantiator::accHitTest(long xLeft, long yTop, VARIANT* pvarChild) { 740 if (!pvarChild) { 741 return E_INVALIDARG; 742 } 743 744 RESOLVE_ROOT; 745 return mWeakAccessible->accHitTest(xLeft, yTop, pvarChild); 746 } 747 748 HRESULT 749 LazyInstantiator::accDoDefaultAction(VARIANT varChild) { 750 RESOLVE_ROOT; 751 return mWeakAccessible->accDoDefaultAction(varChild); 752 } 753 754 HRESULT 755 LazyInstantiator::put_accName(VARIANT varChild, BSTR szName) { 756 return E_NOTIMPL; 757 } 758 759 HRESULT 760 LazyInstantiator::put_accValue(VARIANT varChild, BSTR szValue) { 761 return E_NOTIMPL; 762 } 763 764 static const GUID kUnsupportedServices[] = { 765 // clang-format off 766 // Unknown, queried by Windows on devices with touch screens or similar devices 767 // connected. 768 {0x33f139ee, 0xe509, 0x47f7, {0xbf, 0x39, 0x83, 0x76, 0x44, 0xf7, 0x45, 0x76}}, 769 // Unknown, queried by Windows 770 {0xFDA075CF, 0x7C8B, 0x498C, { 0xB5, 0x14, 0xA9, 0xCB, 0x52, 0x1B, 0xBF, 0xB4 }}, 771 // Unknown, queried by Windows 772 {0x8EDAA462, 0x21F4, 0x4C87, { 0xA0, 0x12, 0xB3, 0xCD, 0xA3, 0xAB, 0x01, 0xFC }}, 773 // Unknown, queried by Windows 774 {0xacd46652, 0x829d, 0x41cb, { 0xa5, 0xfc, 0x17, 0xac, 0xf4, 0x36, 0x61, 0xac }}, 775 // SID_IsUIAutomationObject (undocumented), queried by Windows 776 {0xb96fdb85, 0x7204, 0x4724, { 0x84, 0x2b, 0xc7, 0x05, 0x9d, 0xed, 0xb9, 0xd0 }}, 777 // IIS_IsOleaccProxy (undocumented), queried by Windows 778 {0x902697FA, 0x80E4, 0x4560, {0x80, 0x2A, 0xA1, 0x3F, 0x22, 0xA6, 0x47, 0x09}}, 779 // IID_IHTMLElement, queried by JAWS 780 {0x3050F1FF, 0x98B5, 0x11CF, {0xBB, 0x82, 0x00, 0xAA, 0x00, 0xBD, 0xCE, 0x0B}} 781 // clang-format on 782 }; 783 784 HRESULT 785 LazyInstantiator::QueryService(REFGUID aServiceId, REFIID aServiceIid, 786 void** aOutInterface) { 787 if (!aOutInterface) { 788 return E_INVALIDARG; 789 } 790 791 for (const GUID& unsupportedService : kUnsupportedServices) { 792 if (aServiceId == unsupportedService) { 793 return E_NOINTERFACE; 794 } 795 } 796 797 *aOutInterface = nullptr; 798 799 RESOLVE_ROOT; 800 801 RefPtr<IServiceProvider> servProv; 802 HRESULT hr = mRealRootUnk->QueryInterface(IID_IServiceProvider, 803 getter_AddRefs(servProv)); 804 if (FAILED(hr)) { 805 return hr; 806 } 807 808 return servProv->QueryService(aServiceId, aServiceIid, aOutInterface); 809 } 810 811 STDMETHODIMP 812 LazyInstantiator::get_ProviderOptions( 813 __RPC__out enum ProviderOptions* aOptions) { 814 // This method is called before a UIA connection is fully established and thus 815 // before we can detect the client. We must not call 816 // RESOLVE_ROOT_UIA_RETURN_IF_FAIL here because this might turn out to be a 817 // client we want to block. 818 if (!aOptions) { 819 return E_INVALIDARG; 820 } 821 *aOptions = uiaRawElmProvider::kProviderOptions; 822 return S_OK; 823 } 824 825 STDMETHODIMP 826 LazyInstantiator::GetPatternProvider( 827 PATTERNID aPatternId, __RPC__deref_out_opt IUnknown** aPatternProvider) { 828 RESOLVE_ROOT_UIA_RETURN_IF_FAIL; 829 return mWeakUia->GetPatternProvider(aPatternId, aPatternProvider); 830 } 831 832 STDMETHODIMP 833 LazyInstantiator::GetPropertyValue(PROPERTYID aPropertyId, 834 __RPC__out VARIANT* aPropertyValue) { 835 RESOLVE_ROOT_UIA_RETURN_IF_FAIL; 836 return mWeakUia->GetPropertyValue(aPropertyId, aPropertyValue); 837 } 838 839 STDMETHODIMP 840 LazyInstantiator::get_HostRawElementProvider( 841 __RPC__deref_out_opt IRawElementProviderSimple** aRawElmProvider) { 842 // This method is called before a UIA connection is fully established and thus 843 // before we can detect the client. We must not call 844 // RESOLVE_ROOT_UIA_RETURN_IF_FAIL here because this might turn out to be a 845 // client we want to block. 846 if (!aRawElmProvider) { 847 return E_INVALIDARG; 848 } 849 *aRawElmProvider = nullptr; 850 return UiaHostProviderFromHwnd(mHwnd, aRawElmProvider); 851 } 852 853 } // namespace a11y 854 } // namespace mozilla