tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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