Bug 1963625: Fix some cases where UIA is enabled or disabled at runtime. r=eeejay

When a UIA client first queries us, the accessibility engine hasn't yet been initialised.
Previously, this also meant that we hadn't yet called InitConsumers() to figure out which Windows clients were present.
This in turn meant that we would initially believe UIA should be enabled, even if we later realised NVDA/a Vispero product was present and then believed it should be disabled.
To fix this, call InitConsumers() appropriately if it hasn't been called yet.

Even with the above fix, the following scenario might still be possible:

1. UIA is enabled.
2. A client QueryInterfaces to IRawElementProviderSimple.
3. UIA gets disabled.
4. Only then does the client try to call a method on IRawElementProviderSimple.

In this scenario, LazyInstantiator::MaybeResolveRoot won't set mWeakUia, but the IRawElementProviderSimple method will try to use mWeakUia, causing a crash.
To fix this, add a new RESOLVE_ROOT_UIA macro used by IRawElementProviderSimple methods.
In addition to calling RESOLVE_ROOT, this also checks mWeakUia and fails gracefully if that is null.

Differential Revision: https://phabricator.services.mozilla.com/D247504
This commit is contained in:
James Teh
2025-05-02 00:36:11 +00:00
committed by jteh@mozilla.com
parent a1d64e2d7c
commit f2254a1d58
2 changed files with 23 additions and 6 deletions

View File

@@ -228,6 +228,12 @@ bool Compatibility::IsUiaEnabled() {
}
// Bug 1956415: Some screen readers break when native UIA is enabled, so don't
// enable it when they're detected.
// Call InitConsumers() if we haven't already. It's safe to call this multiple
// times, but it still does a small amount of work and we can easily avoid
// unnecessary calls here.
if (!(sConsumers & UIAUTOMATION)) {
InitConsumers();
}
return !IsJAWS() && !IsOldJAWS() && !IsVisperoShared() &&
!(sConsumers & NVDA);
}

View File

@@ -410,6 +410,15 @@ LazyInstantiator::MaybeResolveRoot() {
} \
}
#define RESOLVE_ROOT_UIA_RETURN_IF_FAIL \
RESOLVE_ROOT \
if (!mWeakUia) { \
/* UIA was previously enabled, allowing QueryInterface to a UIA interface. \
* It was subsequently disabled before we could resolve the root. \
*/ \
return E_FAIL; \
}
IMPL_IUNKNOWN_QUERY_HEAD(LazyInstantiator)
if (NS_WARN_IF(!NS_IsMainThread())) {
// Bug 1814780, bug 1949617: The COM marshaler sometimes calls QueryInterface
@@ -797,8 +806,9 @@ STDMETHODIMP
LazyInstantiator::get_ProviderOptions(
__RPC__out enum ProviderOptions* aOptions) {
// This method is called before a UIA connection is fully established and thus
// before we can detect the client. We must not call RESOLVE_ROOT here because
// this might turn out to be a client we want to block.
// before we can detect the client. We must not call
// RESOLVE_ROOT_UIA_RETURN_IF_FAIL here because this might turn out to be a
// client we want to block.
if (!aOptions) {
return E_INVALIDARG;
}
@@ -809,14 +819,14 @@ LazyInstantiator::get_ProviderOptions(
STDMETHODIMP
LazyInstantiator::GetPatternProvider(
PATTERNID aPatternId, __RPC__deref_out_opt IUnknown** aPatternProvider) {
RESOLVE_ROOT;
RESOLVE_ROOT_UIA_RETURN_IF_FAIL;
return mWeakUia->GetPatternProvider(aPatternId, aPatternProvider);
}
STDMETHODIMP
LazyInstantiator::GetPropertyValue(PROPERTYID aPropertyId,
__RPC__out VARIANT* aPropertyValue) {
RESOLVE_ROOT;
RESOLVE_ROOT_UIA_RETURN_IF_FAIL;
return mWeakUia->GetPropertyValue(aPropertyId, aPropertyValue);
}
@@ -824,8 +834,9 @@ STDMETHODIMP
LazyInstantiator::get_HostRawElementProvider(
__RPC__deref_out_opt IRawElementProviderSimple** aRawElmProvider) {
// This method is called before a UIA connection is fully established and thus
// before we can detect the client. We must not call RESOLVE_ROOT here because
// this might turn out to be a client we want to block.
// before we can detect the client. We must not call
// RESOLVE_ROOT_UIA_RETURN_IF_FAIL here because this might turn out to be a
// client we want to block.
if (!aRawElmProvider) {
return E_INVALIDARG;
}