accessible/windows/msaa/LazyInstantiator.cpp
author Sebastian Hengst <archaeopteryx@coole-files.de>
Fri, 27 Oct 2017 12:45:34 +0300
changeset 441908 ecef003d836730c07ecfcfa25df8e71a88aec99f
parent 438028 00bed5482a23c2ea8aff545f72247eff7097122c
child 435381 86498cd76359a5456fe0e2dd78bbd5a3e30efdf0
permissions -rw-r--r--
merge mozilla-inbound to mozilla-central. r=merge a=merge

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "LazyInstantiator.h"

#include "MainThreadUtils.h"
#include "mozilla/a11y/Accessible.h"
#include "mozilla/a11y/Platform.h"
#include "mozilla/Assertions.h"
#include "mozilla/mscom/MainThreadRuntime.h"
#include "mozilla/mscom/Registration.h"
#include "mozilla/UniquePtr.h"
#include "nsAccessibilityService.h"
#include "nsWindowsHelpers.h"
#include "nsCOMPtr.h"
#include "nsIFile.h"
#include "nsXPCOM.h"
#include "RootAccessibleWrap.h"
#include "WinUtils.h"

#if defined(MOZ_TELEMETRY_REPORTING)
#include "mozilla/Telemetry.h"
#endif // defined(MOZ_TELEMETRY_REPORTING)

#ifdef MOZ_CRASHREPORTER
#include "nsExceptionHandler.h"
#endif

#include <oaidl.h>

#if !defined(STATE_SYSTEM_NORMAL)
#define STATE_SYSTEM_NORMAL (0)
#endif // !defined(STATE_SYSTEM_NORMAL)

/**
 * Because our wrapped accessible is cycle-collected, we can't safely AddRef()
 * or Release() ourselves off the main thread. This template specialization
 * forces NewRunnableMethod() to use STAUniquePtr instead of RefPtr for managing
 * a runnable's lifetime. Once the runnable has completed, the STAUniquePtr will
 * post a runnable to the main thread to release ourselves from there.
 */
template<>
struct nsRunnableMethodReceiver<mozilla::a11y::LazyInstantiator, true>
{
  mozilla::mscom::STAUniquePtr<mozilla::a11y::LazyInstantiator> mObj;
  explicit nsRunnableMethodReceiver(mozilla::a11y::LazyInstantiator* aObj)
    : mObj(aObj)
  {
    MOZ_ASSERT(NS_IsMainThread());
    // STAUniquePtr does not implicitly AddRef(), so we must explicitly do so
    // here.
    aObj->AddRef();
  }
  ~nsRunnableMethodReceiver() { Revoke(); }
  mozilla::a11y::LazyInstantiator* Get() const { return mObj.get(); }
  void Revoke() { mObj = nullptr; }
};

namespace mozilla {
namespace a11y {

static const wchar_t kLazyInstantiatorProp[] = L"mozilla::a11y::LazyInstantiator";

/* static */
already_AddRefed<IAccessible>
LazyInstantiator::GetRootAccessible(HWND aHwnd)
{
  // There must only be one LazyInstantiator per HWND.
  // To track this, we set the kLazyInstantiatorProp on the HWND with a pointer
  // to an existing instance. We only create a new LazyInstatiator if that prop
  // has not already been set.
  LazyInstantiator* existingInstantiator =
    reinterpret_cast<LazyInstantiator*>(::GetProp(aHwnd, kLazyInstantiatorProp));

  RefPtr<IAccessible> result;
  if (existingInstantiator) {
    // Temporarily disable blind aggregation until we know that we have been
    // marshaled. See EnableBlindAggregation for more information.
    existingInstantiator->mAllowBlindAggregation = false;
    result = existingInstantiator;
    return result.forget();
  }

  // At this time we only want to check whether the acc service is running; We
  // don't actually want to create the acc service yet.
  if (!GetAccService()) {
    // a11y is not running yet, there are no existing LazyInstantiators for this
    // HWND, so create a new one and return it as a surrogate for the root
    // accessible.
    result = new LazyInstantiator(aHwnd);
    return result.forget();
  }

  // a11y is running, so we just resolve the real root accessible.
  a11y::Accessible* rootAcc = widget::WinUtils::GetRootAccessibleForHWND(aHwnd);
  if (!rootAcc) {
    return nullptr;
  }

  if (!rootAcc->IsRoot()) {
    // rootAcc might represent a popup as opposed to a true root accessible.
    // In that case we just use the regular Accessible::GetNativeInterface.
    rootAcc->GetNativeInterface(getter_AddRefs(result));
    return result.forget();
  }

  // Subtle: rootAcc might still be wrapped by a LazyInstantiator, but we
  // don't need LazyInstantiator's capabilities anymore (since a11y is already
  // running). We can bypass LazyInstantiator by retrieving the internal
  // unknown (which is not wrapped by the LazyInstantiator) and then querying
  // that for IID_IAccessible.
  a11y::RootAccessibleWrap* rootWrap =
    static_cast<a11y::RootAccessibleWrap*>(rootAcc);
  RefPtr<IUnknown> punk(rootWrap->GetInternalUnknown());

  MOZ_ASSERT(punk);
  if (!punk) {
    return nullptr;
  }

  punk->QueryInterface(IID_IAccessible, getter_AddRefs(result));
  return result.forget();
}

/**
 * When marshaling an interface, COM makes a whole bunch of QueryInterface
 * calls to determine what kind of marshaling the interface supports. We need
 * to handle those queries without instantiating a11y, so we temporarily
 * disable passing through of QueryInterface calls to a11y. Once we know that
 * COM is finished marshaling, we call EnableBlindAggregation to re-enable
 * QueryInterface passthrough.
 */
/* static */
void
LazyInstantiator::EnableBlindAggregation(HWND aHwnd)
{
  LazyInstantiator* existingInstantiator =
    reinterpret_cast<LazyInstantiator*>(::GetProp(aHwnd, kLazyInstantiatorProp));

  if (!existingInstantiator) {
    return;
  }

  existingInstantiator->mAllowBlindAggregation = true;
}

LazyInstantiator::LazyInstantiator(HWND aHwnd)
  : mHwnd(aHwnd)
  , mAllowBlindAggregation(false)
  , mWeakRootAccWrap(nullptr)
  , mWeakAccessible(nullptr)
  , mWeakDispatch(nullptr)
{
  MOZ_ASSERT(aHwnd);
  // Assign ourselves as the designated LazyInstantiator for aHwnd
  DebugOnly<BOOL> setPropOk = ::SetProp(aHwnd, kLazyInstantiatorProp,
                                        reinterpret_cast<HANDLE>(this));
  MOZ_ASSERT(setPropOk);
}

LazyInstantiator::~LazyInstantiator()
{
  if (mRealRootUnk) {
    // Disconnect ourselves from the root accessible.
    RefPtr<IUnknown> dummy(mWeakRootAccWrap->Aggregate(nullptr));
  }

  ClearProp();
}

void
LazyInstantiator::ClearProp()
{
  // Remove ourselves as the designated LazyInstantiator for mHwnd
  DebugOnly<HANDLE> removedProp = ::RemoveProp(mHwnd, kLazyInstantiatorProp);
  MOZ_ASSERT(!removedProp ||
             reinterpret_cast<LazyInstantiator*>(removedProp.value) == this);
}

/**
 * Given the remote client's thread ID, resolve its execuatable image name.
 */
bool
LazyInstantiator::GetClientExecutableName(const DWORD aClientTid,
                                          nsIFile** aOutClientExe)
{
  nsAutoHandle callingThread(::OpenThread(THREAD_QUERY_LIMITED_INFORMATION,
                                          FALSE, aClientTid));
  if (!callingThread) {
    return false;
  }

  DWORD callingPid = ::GetProcessIdOfThread(callingThread);

  nsAutoHandle callingProcess(::OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION,
                                            FALSE, callingPid));
  if (!callingProcess) {
    return false;
  }

  DWORD bufLen = MAX_PATH;
  UniquePtr<wchar_t[]> buf;

  while (true) {
    buf = MakeUnique<wchar_t[]>(bufLen);
    if (::QueryFullProcessImageName(callingProcess, 0, buf.get(), &bufLen)) {
      break;
    }

    DWORD lastError = ::GetLastError();
    MOZ_ASSERT(lastError == ERROR_INSUFFICIENT_BUFFER);
    if (lastError != ERROR_INSUFFICIENT_BUFFER) {
      return false;
    }

    bufLen *= 2;
  }

  nsCOMPtr<nsIFile> file;
  nsresult rv = NS_NewLocalFile(nsDependentString(buf.get(), bufLen), false,
                                getter_AddRefs(file));
  if (NS_FAILED(rv)) {
    return false;
  }

  file.forget(aOutClientExe);
  return NS_SUCCEEDED(rv);
}

/**
 * Given a remote client's thread ID, determine whether we should proceed with
 * a11y instantiation. This is where telemetry should be gathered and any
 * potential blocking of unwanted a11y clients should occur.
 *
 * @return true if we should instantiate a11y
 */
bool
LazyInstantiator::ShouldInstantiate(const DWORD aClientTid)
{
  if (!aClientTid) {
    // aClientTid == 0 implies that this is either an in-process call, or else
    // we failed to retrieve information about the remote caller.
    // We should always default to instantiating a11y in this case.
    return true;
  }

  nsCOMPtr<nsIFile> clientExe;
  if (!GetClientExecutableName(aClientTid, getter_AddRefs(clientExe))) {
#if defined(MOZ_TELEMETRY_REPORTING)
    AccumulateTelemetry(NS_LITERAL_STRING("(Failed to retrieve client image name)"));
#endif // defined(MOZ_TELEMETRY_REPORTING)
    // We should return true as a failsafe
    return true;
  }

  // Blocklist checks should go here. return false if we should not instantiate.
  /*
  if (ClientShouldBeBlocked(clientExe)) {
    return false;
  }
  */

  // Store full path to executable for support purposes.
  nsAutoString filePath;
  nsresult rv = clientExe->GetPath(filePath);
  if (NS_SUCCEEDED(rv)) {
    a11y::SetInstantiator(filePath);
  }

#if defined(MOZ_TELEMETRY_REPORTING) || defined(MOZ_CRASHREPORTER)
  if (!mTelemetryThread) {
    // Call GatherTelemetry on a background thread because it does I/O on
    // the executable file to retrieve version information.
    nsCOMPtr<nsIRunnable> runnable(
        NewRunnableMethod<nsCOMPtr<nsIFile>, RefPtr<AccumulateRunnable>>(
                                             "LazyInstantiator::GatherTelemetry",
                                             this,
                                             &LazyInstantiator::GatherTelemetry,
                                             clientExe,
                                             new AccumulateRunnable(this)));
    NS_NewThread(getter_AddRefs(mTelemetryThread), runnable);
  }
#endif // defined(MOZ_TELEMETRY_REPORTING) || defined(MOZ_CRASHREPORTER)

  return true;
}

#if defined(MOZ_TELEMETRY_REPORTING) || defined(MOZ_CRASHREPORTER)
/**
 * Appends version information in the format "|a.b.c.d".
 * If there is no version information, we append nothing.
 */
void
LazyInstantiator::AppendVersionInfo(nsIFile* aClientExe,
                                    nsAString& aStrToAppend)
{
  MOZ_ASSERT(!NS_IsMainThread());

  nsAutoString fullPath;
  nsresult rv = aClientExe->GetPath(fullPath);
  if (NS_FAILED(rv)) {
    return;
  }

  DWORD verInfoSize = ::GetFileVersionInfoSize(fullPath.get(), nullptr);
  if (!verInfoSize) {
    return;
  }

  auto verInfoBuf = MakeUnique<BYTE[]>(verInfoSize);

  if (!::GetFileVersionInfo(fullPath.get(), 0, verInfoSize, verInfoBuf.get())) {
    return;
  }

  VS_FIXEDFILEINFO* fixedInfo = nullptr;
  UINT fixedInfoLen = 0;

  if (!::VerQueryValue(verInfoBuf.get(), L"\\", (LPVOID*) &fixedInfo,
                       &fixedInfoLen)) {
    return;
  }

  uint32_t major = HIWORD(fixedInfo->dwFileVersionMS);
  uint32_t minor = LOWORD(fixedInfo->dwFileVersionMS);
  uint32_t patch = HIWORD(fixedInfo->dwFileVersionLS);
  uint32_t build = LOWORD(fixedInfo->dwFileVersionLS);

  aStrToAppend.AppendLiteral(u"|");

  NS_NAMED_LITERAL_STRING(dot, ".");

  aStrToAppend.AppendInt(major);
  aStrToAppend.Append(dot);
  aStrToAppend.AppendInt(minor);
  aStrToAppend.Append(dot);
  aStrToAppend.AppendInt(patch);
  aStrToAppend.Append(dot);
  aStrToAppend.AppendInt(build);
}

void
LazyInstantiator::GatherTelemetry(nsIFile* aClientExe,
                                  AccumulateRunnable* aRunnable)
{
  MOZ_ASSERT(!NS_IsMainThread());

  nsAutoString value;
  nsresult rv = aClientExe->GetLeafName(value);
  if (NS_SUCCEEDED(rv)) {
    AppendVersionInfo(aClientExe, value);
  }

  aRunnable->SetData(value);

  // Now that we've (possibly) obtained version info, send the resulting
  // string back to the main thread to accumulate in telemetry.
  NS_DispatchToMainThread(aRunnable);
}

void
LazyInstantiator::AccumulateTelemetry(const nsString& aValue)
{
  MOZ_ASSERT(NS_IsMainThread());

  if (!aValue.IsEmpty()) {
#if defined(MOZ_TELEMETRY_REPORTING)
    Telemetry::ScalarSet(Telemetry::ScalarID::A11Y_INSTANTIATORS,
                         aValue);
#endif // defined(MOZ_TELEMETRY_REPORTING)
#if defined(MOZ_CRASHREPORTER)
    CrashReporter::
      AnnotateCrashReport(NS_LITERAL_CSTRING("AccessibilityClient"),
                          NS_ConvertUTF16toUTF8(aValue));
#endif // defined(MOZ_CRASHREPORTER)
  }

  if (mTelemetryThread) {
    mTelemetryThread->Shutdown();
    mTelemetryThread = nullptr;
  }
}
#endif // defined(MOZ_TELEMETRY_REPORTING) || defined(MOZ_CRASHREPORTER)

RootAccessibleWrap*
LazyInstantiator::ResolveRootAccWrap()
{
  Accessible* acc = widget::WinUtils::GetRootAccessibleForHWND(mHwnd);
  if (!acc || !acc->IsRoot()) {
    return nullptr;
  }

  return static_cast<RootAccessibleWrap*>(acc);
}

/**
 * With COM aggregation, the aggregated inner object usually delegates its
 * reference counting to the outer object. In other words, we would expect
 * mRealRootUnk to delegate its AddRef() and Release() to this LazyInstantiator.
 *
 * This scheme will not work in our case because the RootAccessibleWrap is
 * cycle-collected!
 *
 * Instead, once a LazyInstantiator aggregates a RootAccessibleWrap, we transfer
 * our strong references into mRealRootUnk. Any future calls to AddRef or
 * Release now operate on mRealRootUnk instead of our intrinsic reference
 * count. This is a bit strange, but it is the only way for these objects to
 * share their reference count in a way that is safe for cycle collection.
 *
 * How do we know when it is safe to destroy ourselves? In
 * LazyInstantiator::Release, we examine the result of mRealRootUnk->Release().
 * If mRealRootUnk's resulting refcount is 1, then we know that the only
 * remaining reference to mRealRootUnk is the mRealRootUnk reference itself (and
 * thus nobody else holds references to either this or mRealRootUnk). Therefore
 * we may now delete ourselves.
 */
void
LazyInstantiator::TransplantRefCnt()
{
  MOZ_ASSERT(mRefCnt > 0);
  MOZ_ASSERT(mRealRootUnk);

  while (mRefCnt > 0) {
    mRealRootUnk.get()->AddRef();
    --mRefCnt;
  }
}

HRESULT
LazyInstantiator::MaybeResolveRoot()
{
  MOZ_ASSERT(NS_IsMainThread());
  if (mWeakAccessible) {
    return S_OK;
  }

  if (GetAccService() ||
      ShouldInstantiate(mscom::MainThreadRuntime::GetClientThreadId())) {
    mWeakRootAccWrap = ResolveRootAccWrap();
    if (!mWeakRootAccWrap) {
      return E_POINTER;
    }

    // Wrap ourselves around the root accessible wrap
    mRealRootUnk = mWeakRootAccWrap->Aggregate(static_cast<IAccessible*>(this));
    if (!mRealRootUnk) {
      return E_FAIL;
    }

    // Move our strong references into the root accessible (see the comments
    // above TransplantRefCnt for explanation).
    TransplantRefCnt();

    // Now obtain mWeakAccessible which we use to forward our incoming calls
    // to the real accesssible.
    HRESULT hr = mRealRootUnk->QueryInterface(IID_IAccessible,
                                              (void**) &mWeakAccessible);
    if (FAILED(hr)) {
      return hr;
    }

    // mWeakAccessible is weak, so don't hold a strong ref
    mWeakAccessible->Release();

    // Now that a11y is running, we don't need to remain registered with our
    // HWND anymore.
    ClearProp();

    return S_OK;
  }

  // If we don't want a real root, let's resolve a fake one.

  const WPARAM flags = 0xFFFFFFFFUL;
  // Synthesize a WM_GETOBJECT request to obtain a system-implemented
  // IAccessible object from DefWindowProc
  LRESULT lresult = ::DefWindowProc(mHwnd, WM_GETOBJECT, flags,
                                    static_cast<LPARAM>(OBJID_CLIENT));

  HRESULT hr = ObjectFromLresult(lresult, IID_IAccessible, flags,
                                 getter_AddRefs(mRealRootUnk));
  if (FAILED(hr)) {
    return hr;
  }

  if (!mRealRootUnk) {
    return E_NOTIMPL;
  }

  hr = mRealRootUnk->QueryInterface(IID_IAccessible,
                                    (void**) &mWeakAccessible);
  if (FAILED(hr)) {
    return hr;
  }

  // mWeakAccessible is weak, so don't hold a strong ref
  mWeakAccessible->Release();

  return S_OK;
}

#define RESOLVE_ROOT \
  { \
    HRESULT hr = MaybeResolveRoot(); \
    if (FAILED(hr)) { \
      return hr; \
    } \
  }

IMPL_IUNKNOWN_QUERY_HEAD(LazyInstantiator)
IMPL_IUNKNOWN_QUERY_IFACE_AMBIGIOUS(IUnknown, IAccessible)
IMPL_IUNKNOWN_QUERY_IFACE(IAccessible)
IMPL_IUNKNOWN_QUERY_IFACE(IDispatch)
IMPL_IUNKNOWN_QUERY_IFACE(IServiceProvider)
  // See EnableBlindAggregation for comments.
  if (!mAllowBlindAggregation) {
    return E_NOINTERFACE;
  }
// If the client queries for an interface that LazyInstantiator does not
// intrinsically support, then we must resolve the root accessible and pass
// on the QueryInterface call to mRealRootUnk.
RESOLVE_ROOT
IMPL_IUNKNOWN_QUERY_TAIL_AGGREGATED(mRealRootUnk)

ULONG
LazyInstantiator::AddRef()
{
  // Always delegate refcounting to mRealRootUnk when it exists
  if (mRealRootUnk) {
    return mRealRootUnk.get()->AddRef();
  }

  return ++mRefCnt;
}

ULONG
LazyInstantiator::Release()
{
  ULONG result;

  // Always delegate refcounting to mRealRootUnk when it exists
  if (mRealRootUnk) {
    result = mRealRootUnk.get()->Release();
    if (result == 1) {
      // mRealRootUnk is the only strong reference left, so nothing else holds
      // a strong reference to us. Drop result to zero so that we destroy
      // ourselves (See the comments above LazyInstantiator::TransplantRefCnt
      // for more info).
      --result;
    }
  } else {
    result = --mRefCnt;
  }

  if (!result) {
    delete this;
  }
  return result;
}

/**
 * Create a standard IDispatch implementation. mStdDispatch will translate any
 * IDispatch::Invoke calls into real IAccessible calls.
 */
HRESULT
LazyInstantiator::ResolveDispatch()
{
  if (mWeakDispatch) {
    return S_OK;
  }

  // The IAccessible typelib is embedded in oleacc.dll's resources.
  auto typelib = mscom::RegisterTypelib(L"oleacc.dll",
                                        mscom::RegistrationFlags::eUseSystemDirectory);
  if (!typelib) {
    return E_UNEXPECTED;
  }

  // Extract IAccessible's type info
  RefPtr<ITypeInfo> accTypeInfo;
  HRESULT hr = typelib->GetTypeInfoForGuid(IID_IAccessible,
                                           getter_AddRefs(accTypeInfo));
  if (FAILED(hr)) {
    return hr;
  }

  // Now create the standard IDispatch for IAccessible
  hr = ::CreateStdDispatch(static_cast<IAccessible*>(this),
                           static_cast<IAccessible*>(this),
                           accTypeInfo, getter_AddRefs(mStdDispatch));
  if (FAILED(hr)) {
    return hr;
  }

  hr = mStdDispatch->QueryInterface(IID_IDispatch, (void**)&mWeakDispatch);
  if (FAILED(hr)) {
    return hr;
  }

  // WEAK reference
  mWeakDispatch->Release();
  return S_OK;
}

#define RESOLVE_IDISPATCH \
  { \
    HRESULT hr = ResolveDispatch(); \
    if (FAILED(hr)) { \
      return hr; \
    } \
  }

/**
 * The remaining methods implement IDispatch, IAccessible, and IServiceProvider,
 * lazily resolving the real a11y objects and passing the call through.
 */

HRESULT
LazyInstantiator::GetTypeInfoCount(UINT* pctinfo)
{
  RESOLVE_IDISPATCH;
  return mWeakDispatch->GetTypeInfoCount(pctinfo);
}

HRESULT
LazyInstantiator::GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo)
{
  RESOLVE_IDISPATCH;
  return mWeakDispatch->GetTypeInfo(iTInfo, lcid, ppTInfo);
}

HRESULT
LazyInstantiator::GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, UINT cNames,
                                LCID lcid, DISPID* rgDispId)
{
  RESOLVE_IDISPATCH;
  return mWeakDispatch->GetIDsOfNames(riid, rgszNames, cNames, lcid, rgDispId);
}

HRESULT
LazyInstantiator::Invoke(DISPID dispIdMember, REFIID riid, LCID lcid,
                         WORD wFlags, DISPPARAMS* pDispParams,
                         VARIANT* pVarResult, EXCEPINFO* pExcepInfo,
                         UINT* puArgErr)
{
  RESOLVE_IDISPATCH;
  return mWeakDispatch->Invoke(dispIdMember, riid, lcid, wFlags, pDispParams,
                               pVarResult, pExcepInfo, puArgErr);
}

HRESULT
LazyInstantiator::get_accParent(IDispatch **ppdispParent)
{
  RESOLVE_ROOT;
  return mWeakAccessible->get_accParent(ppdispParent);
}

HRESULT
LazyInstantiator::get_accChildCount(long *pcountChildren)
{
  if (!pcountChildren) {
    return E_INVALIDARG;
  }

  RESOLVE_ROOT;
  return mWeakAccessible->get_accChildCount(pcountChildren);
}

HRESULT
LazyInstantiator::get_accChild(VARIANT varChild, IDispatch **ppdispChild)
{
  if (!ppdispChild) {
    return E_INVALIDARG;
  }

  RESOLVE_ROOT;
  return mWeakAccessible->get_accChild(varChild, ppdispChild);
}

HRESULT
LazyInstantiator::get_accName(VARIANT varChild, BSTR *pszName)
{
  if (!pszName) {
    return E_INVALIDARG;
  }

  RESOLVE_ROOT;
  return mWeakAccessible->get_accName(varChild, pszName);
}

HRESULT
LazyInstantiator::get_accValue(VARIANT varChild, BSTR *pszValue)
{
  if (!pszValue) {
    return E_INVALIDARG;
  }

  RESOLVE_ROOT;
  return mWeakAccessible->get_accValue(varChild, pszValue);
}

HRESULT
LazyInstantiator::get_accDescription(VARIANT varChild, BSTR *pszDescription)
{
  if (!pszDescription) {
    return E_INVALIDARG;
  }

  RESOLVE_ROOT;
  return mWeakAccessible->get_accDescription(varChild, pszDescription);
}

HRESULT
LazyInstantiator::get_accRole(VARIANT varChild, VARIANT *pvarRole)
{
  if (!pvarRole) {
    return E_INVALIDARG;
  }

  RESOLVE_ROOT;
  return mWeakAccessible->get_accRole(varChild, pvarRole);
}

HRESULT
LazyInstantiator::get_accState(VARIANT varChild, VARIANT *pvarState)
{
  if (!pvarState) {
    return E_INVALIDARG;
  }

  RESOLVE_ROOT;
  return mWeakAccessible->get_accState(varChild, pvarState);
}

HRESULT
LazyInstantiator::get_accHelp(VARIANT varChild, BSTR *pszHelp)
{
  return E_NOTIMPL;
}

HRESULT
LazyInstantiator::get_accHelpTopic(BSTR *pszHelpFile, VARIANT varChild,
                                   long *pidTopic)
{
  return E_NOTIMPL;
}

HRESULT
LazyInstantiator::get_accKeyboardShortcut(VARIANT varChild,
                                          BSTR *pszKeyboardShortcut)
{
  if (!pszKeyboardShortcut) {
    return E_INVALIDARG;
  }

  RESOLVE_ROOT;
  return mWeakAccessible->get_accKeyboardShortcut(varChild, pszKeyboardShortcut);
}

HRESULT
LazyInstantiator::get_accFocus(VARIANT *pvarChild)
{
  if (!pvarChild) {
    return E_INVALIDARG;
  }

  RESOLVE_ROOT;
  return mWeakAccessible->get_accFocus(pvarChild);
}

HRESULT
LazyInstantiator::get_accSelection(VARIANT *pvarChildren)
{
  if (!pvarChildren) {
    return E_INVALIDARG;
  }

  RESOLVE_ROOT;
  return mWeakAccessible->get_accSelection(pvarChildren);
}

HRESULT
LazyInstantiator::get_accDefaultAction(VARIANT varChild, BSTR *pszDefaultAction)
{
  if (!pszDefaultAction) {
    return E_INVALIDARG;
  }

  RESOLVE_ROOT;
  return mWeakAccessible->get_accDefaultAction(varChild, pszDefaultAction);
}

HRESULT
LazyInstantiator::accSelect(long flagsSelect, VARIANT varChild)
{
  RESOLVE_ROOT;
  return mWeakAccessible->accSelect(flagsSelect, varChild);
}

HRESULT
LazyInstantiator::accLocation(long *pxLeft, long *pyTop, long *pcxWidth,
                              long *pcyHeight, VARIANT varChild)
{
  RESOLVE_ROOT;
  return mWeakAccessible->accLocation(pxLeft, pyTop, pcxWidth, pcyHeight, varChild);
}

HRESULT
LazyInstantiator::accNavigate(long navDir, VARIANT varStart,
                              VARIANT *pvarEndUpAt)
{
  if (!pvarEndUpAt) {
    return E_INVALIDARG;
  }

  RESOLVE_ROOT;
  return mWeakAccessible->accNavigate(navDir, varStart, pvarEndUpAt);
}

HRESULT
LazyInstantiator::accHitTest(long xLeft, long yTop, VARIANT *pvarChild)
{
  if (!pvarChild) {
    return E_INVALIDARG;
  }

  RESOLVE_ROOT;
  return mWeakAccessible->accHitTest(xLeft, yTop, pvarChild);
}

HRESULT
LazyInstantiator::accDoDefaultAction(VARIANT varChild)
{
  RESOLVE_ROOT;
  return mWeakAccessible->accDoDefaultAction(varChild);
}

HRESULT
LazyInstantiator::put_accName(VARIANT varChild, BSTR szName)
{
  return E_NOTIMPL;
}

HRESULT
LazyInstantiator::put_accValue(VARIANT varChild, BSTR szValue)
{
  return E_NOTIMPL;
}

HRESULT
LazyInstantiator::QueryService(REFGUID aServiceId, REFIID aServiceIid,
                               void** aOutInterface)
{
  if (!aOutInterface) {
    return E_INVALIDARG;
  }

  *aOutInterface = nullptr;

  RESOLVE_ROOT;

  RefPtr<IServiceProvider> servProv;
  HRESULT hr = mRealRootUnk->QueryInterface(IID_IServiceProvider,
                                            getter_AddRefs(servProv));
  if (FAILED(hr)) {
    return hr;
  }

  return servProv->QueryService(aServiceId, aServiceIid, aOutInterface);
}

} // namespace a11y
} // namespace mozilla