summaryrefslogtreecommitdiffstats
path: root/accessible/windows
diff options
context:
space:
mode:
Diffstat (limited to 'accessible/windows')
-rw-r--r--accessible/windows/ia2/ia2Accessible.cpp3
-rw-r--r--accessible/windows/msaa/MsaaAccessible.cpp12
-rw-r--r--accessible/windows/msaa/MsaaAccessible.h6
-rw-r--r--accessible/windows/msaa/MsaaRootAccessible.cpp8
-rw-r--r--accessible/windows/msaa/MsaaRootAccessible.h4
-rw-r--r--accessible/windows/msaa/Platform.cpp7
-rw-r--r--accessible/windows/uia/UiaRoot.cpp61
-rw-r--r--accessible/windows/uia/UiaRoot.h40
-rw-r--r--accessible/windows/uia/moz.build1
-rw-r--r--accessible/windows/uia/uiaRawElmProvider.cpp576
-rw-r--r--accessible/windows/uia/uiaRawElmProvider.h79
11 files changed, 763 insertions, 34 deletions
diff --git a/accessible/windows/ia2/ia2Accessible.cpp b/accessible/windows/ia2/ia2Accessible.cpp
index 7318dd5e6f..5bc421d086 100644
--- a/accessible/windows/ia2/ia2Accessible.cpp
+++ b/accessible/windows/ia2/ia2Accessible.cpp
@@ -156,7 +156,8 @@ ia2Accessible::role(long* aRole) {
if (!acc) return CO_E_OBJNOTCONNECTED;
#define ROLE(_geckoRole, stringRole, ariaRole, atkRole, macRole, macSubrole, \
- msaaRole, ia2Role, androidClass, iosIsElement, nameRule) \
+ msaaRole, ia2Role, androidClass, iosIsElement, uiaControlType, \
+ nameRule) \
case roles::_geckoRole: \
*aRole = ia2Role; \
break;
diff --git a/accessible/windows/msaa/MsaaAccessible.cpp b/accessible/windows/msaa/MsaaAccessible.cpp
index 3a0e8fc726..56e8cfd058 100644
--- a/accessible/windows/msaa/MsaaAccessible.cpp
+++ b/accessible/windows/msaa/MsaaAccessible.cpp
@@ -14,6 +14,7 @@
#include "mozilla/a11y/AccessibleWrap.h"
#include "mozilla/a11y/Compatibility.h"
#include "mozilla/a11y/DocAccessibleParent.h"
+#include "mozilla/StaticPrefs_accessibility.h"
#include "MsaaAccessible.h"
#include "MsaaDocAccessible.h"
#include "MsaaRootAccessible.h"
@@ -522,9 +523,11 @@ MsaaAccessible::QueryInterface(REFIID iid, void** ppv) {
if (SUCCEEDED(hr)) {
return hr;
}
- hr = uiaRawElmProvider::QueryInterface(iid, ppv);
- if (SUCCEEDED(hr)) {
- return hr;
+ if (StaticPrefs::accessibility_uia_enable()) {
+ hr = uiaRawElmProvider::QueryInterface(iid, ppv);
+ if (SUCCEEDED(hr)) {
+ return hr;
+ }
}
}
if (*ppv) {
@@ -769,7 +772,8 @@ MsaaAccessible::get_accRole(
uint32_t msaaRole = 0;
#define ROLE(_geckoRole, stringRole, ariaRole, atkRole, macRole, macSubrole, \
- _msaaRole, ia2Role, androidClass, iosIsElement, nameRule) \
+ _msaaRole, ia2Role, androidClass, iosIsElement, uiaControlType, \
+ nameRule) \
case roles::_geckoRole: \
msaaRole = _msaaRole; \
break;
diff --git a/accessible/windows/msaa/MsaaAccessible.h b/accessible/windows/msaa/MsaaAccessible.h
index 6d6582f010..fa43c441bf 100644
--- a/accessible/windows/msaa/MsaaAccessible.h
+++ b/accessible/windows/msaa/MsaaAccessible.h
@@ -31,7 +31,7 @@ class MsaaAccessible : public ia2Accessible,
public:
static MsaaAccessible* Create(Accessible* aAcc);
- Accessible* Acc() { return mAcc; }
+ Accessible* Acc() const { return mAcc; }
AccessibleWrap* LocalAcc();
uint32_t GetExistingID() const { return mID; }
@@ -146,6 +146,10 @@ class MsaaAccessible : public ia2Accessible,
EXCEPINFO* pExcepInfo,
UINT* puArgErr) override;
+ // UIA's IInvokeProvider has a method called Invoke too, but it's fine because
+ // it accepts very different parameters.
+ using uiaRawElmProvider::Invoke;
+
protected:
explicit MsaaAccessible(Accessible* aAcc);
virtual ~MsaaAccessible();
diff --git a/accessible/windows/msaa/MsaaRootAccessible.cpp b/accessible/windows/msaa/MsaaRootAccessible.cpp
index ac747ff3d1..2b28edb9b5 100644
--- a/accessible/windows/msaa/MsaaRootAccessible.cpp
+++ b/accessible/windows/msaa/MsaaRootAccessible.cpp
@@ -5,6 +5,7 @@
#include "mozilla/a11y/DocAccessibleParent.h"
#include "mozilla/dom/BrowserParent.h"
+#include "mozilla/StaticPrefs_accessibility.h"
#include "mozilla/WindowsVersion.h"
#include "MsaaRootAccessible.h"
#include "Relation.h"
@@ -36,6 +37,13 @@ MsaaRootAccessible::InternalQueryInterface(REFIID aIid, void** aOutInterface) {
return S_OK;
}
+ if (StaticPrefs::accessibility_uia_enable() &&
+ aIid == IID_IRawElementProviderFragmentRoot) {
+ RefPtr<IRawElementProviderFragmentRoot> root = this;
+ root.forget(aOutInterface);
+ return S_OK;
+ }
+
// ...Otherwise we pass through to the base COM implementation of
// QueryInterface which is provided by MsaaDocAccessible.
return MsaaDocAccessible::QueryInterface(aIid, aOutInterface);
diff --git a/accessible/windows/msaa/MsaaRootAccessible.h b/accessible/windows/msaa/MsaaRootAccessible.h
index a51533d7ba..efc42bc094 100644
--- a/accessible/windows/msaa/MsaaRootAccessible.h
+++ b/accessible/windows/msaa/MsaaRootAccessible.h
@@ -8,12 +8,14 @@
#include "mozilla/mscom/Aggregation.h"
#include "MsaaDocAccessible.h"
+#include "UiaRoot.h"
namespace mozilla {
namespace a11y {
+class RootAccessible;
-class MsaaRootAccessible : public MsaaDocAccessible {
+class MsaaRootAccessible : public MsaaDocAccessible, public UiaRoot {
public:
explicit MsaaRootAccessible(Accessible* aAcc)
: MsaaDocAccessible(aAcc), mOuter(&mInternalUnknown) {}
diff --git a/accessible/windows/msaa/Platform.cpp b/accessible/windows/msaa/Platform.cpp
index 018042c5d3..f4d1c7b176 100644
--- a/accessible/windows/msaa/Platform.cpp
+++ b/accessible/windows/msaa/Platform.cpp
@@ -69,10 +69,13 @@ void a11y::ProxyDestroyed(RemoteAccessible* aProxy) {
void a11y::PlatformEvent(Accessible* aTarget, uint32_t aEventType) {
MsaaAccessible::FireWinEvent(aTarget, aEventType);
+ uiaRawElmProvider::RaiseUiaEventForGeckoEvent(aTarget, aEventType);
}
-void a11y::PlatformStateChangeEvent(Accessible* aTarget, uint64_t, bool) {
+void a11y::PlatformStateChangeEvent(Accessible* aTarget, uint64_t aState,
+ bool aEnabled) {
MsaaAccessible::FireWinEvent(aTarget, nsIAccessibleEvent::EVENT_STATE_CHANGE);
+ uiaRawElmProvider::RaiseUiaEventForStateChange(aTarget, aState, aEnabled);
}
void a11y::PlatformFocusEvent(Accessible* aTarget,
@@ -91,6 +94,8 @@ void a11y::PlatformFocusEvent(Accessible* aTarget,
AccessibleWrap::UpdateSystemCaretFor(aTarget, aCaretRect);
MsaaAccessible::FireWinEvent(aTarget, nsIAccessibleEvent::EVENT_FOCUS);
+ uiaRawElmProvider::RaiseUiaEventForGeckoEvent(
+ aTarget, nsIAccessibleEvent::EVENT_FOCUS);
}
void a11y::PlatformCaretMoveEvent(Accessible* aTarget, int32_t aOffset,
diff --git a/accessible/windows/uia/UiaRoot.cpp b/accessible/windows/uia/UiaRoot.cpp
new file mode 100644
index 0000000000..0e8c86ce6a
--- /dev/null
+++ b/accessible/windows/uia/UiaRoot.cpp
@@ -0,0 +1,61 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=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 "UiaRoot.h"
+
+#include "MsaaRootAccessible.h"
+#include "RootAccessible.h"
+
+using namespace mozilla;
+using namespace mozilla::a11y;
+
+// UiaRoot
+
+Accessible* UiaRoot::Acc() {
+ auto* mr = static_cast<MsaaRootAccessible*>(this);
+ return static_cast<MsaaAccessible*>(mr)->Acc();
+}
+
+// IRawElementProviderFragmentRoot
+
+STDMETHODIMP
+UiaRoot::ElementProviderFromPoint(
+ double aX, double aY,
+ __RPC__deref_out_opt IRawElementProviderFragment** aRetVal) {
+ if (!aRetVal) {
+ return E_INVALIDARG;
+ }
+ *aRetVal = nullptr;
+ Accessible* acc = Acc();
+ if (!acc) {
+ return CO_E_OBJNOTCONNECTED;
+ }
+ if (Accessible* target = acc->ChildAtPoint(
+ aX, aY, Accessible::EWhichChildAtPoint::DeepestChild)) {
+ RefPtr<IRawElementProviderFragment> fragment =
+ MsaaAccessible::GetFrom(target);
+ fragment.forget(aRetVal);
+ }
+ return S_OK;
+}
+
+STDMETHODIMP
+UiaRoot::GetFocus(__RPC__deref_out_opt IRawElementProviderFragment** aRetVal) {
+ if (!aRetVal) {
+ return E_INVALIDARG;
+ }
+ *aRetVal = nullptr;
+ Accessible* acc = Acc();
+ if (!acc) {
+ return CO_E_OBJNOTCONNECTED;
+ }
+ if (Accessible* focus = acc->FocusedChild()) {
+ RefPtr<IRawElementProviderFragment> fragment =
+ MsaaAccessible::GetFrom(focus);
+ fragment.forget(aRetVal);
+ }
+ return S_OK;
+}
diff --git a/accessible/windows/uia/UiaRoot.h b/accessible/windows/uia/UiaRoot.h
new file mode 100644
index 0000000000..91d1c00b3d
--- /dev/null
+++ b/accessible/windows/uia/UiaRoot.h
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=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/. */
+
+#ifndef mozilla_a11y_UiaRoot_h__
+#define mozilla_a11y_UiaRoot_h__
+
+#include "objbase.h"
+#include "uiautomation.h"
+
+namespace mozilla {
+namespace a11y {
+class Accessible;
+
+/**
+ * IRawElementProviderFragmentRoot implementation.
+ */
+class UiaRoot : public IRawElementProviderFragmentRoot {
+ public:
+ // IRawElementProviderFragmentRoot
+ virtual HRESULT STDMETHODCALLTYPE ElementProviderFromPoint(
+ /* [in] */ double aX,
+ /* [in] */ double aY,
+ /* [retval][out] */
+ __RPC__deref_out_opt IRawElementProviderFragment** aRetVal);
+
+ virtual HRESULT STDMETHODCALLTYPE GetFocus(
+ /* [retval][out] */ __RPC__deref_out_opt IRawElementProviderFragment**
+ aRetVal);
+
+ private:
+ Accessible* Acc();
+};
+
+} // namespace a11y
+} // namespace mozilla
+
+#endif
diff --git a/accessible/windows/uia/moz.build b/accessible/windows/uia/moz.build
index 058aacc579..c52a24d612 100644
--- a/accessible/windows/uia/moz.build
+++ b/accessible/windows/uia/moz.build
@@ -6,6 +6,7 @@
SOURCES += [
"uiaRawElmProvider.cpp",
+ "UiaRoot.cpp",
]
LOCAL_INCLUDES += [
diff --git a/accessible/windows/uia/uiaRawElmProvider.cpp b/accessible/windows/uia/uiaRawElmProvider.cpp
index 881ed22277..c022e40cef 100644
--- a/accessible/windows/uia/uiaRawElmProvider.cpp
+++ b/accessible/windows/uia/uiaRawElmProvider.cpp
@@ -6,39 +6,174 @@
#include "uiaRawElmProvider.h"
+#include <comdef.h>
+#include <uiautomationcoreapi.h>
+
#include "AccAttributes.h"
#include "AccessibleWrap.h"
#include "ARIAMap.h"
#include "LocalAccessible-inl.h"
#include "mozilla/a11y/RemoteAccessible.h"
+#include "mozilla/StaticPrefs_accessibility.h"
#include "MsaaAccessible.h"
+#include "MsaaRootAccessible.h"
+#include "nsAccessibilityService.h"
+#include "nsAccUtils.h"
#include "nsTextEquivUtils.h"
+#include "RootAccessible.h"
using namespace mozilla;
using namespace mozilla::a11y;
+// Helper functions
+
+static ToggleState ToToggleState(uint64_t aState) {
+ if (aState & states::MIXED) {
+ return ToggleState_Indeterminate;
+ }
+ if (aState & (states::CHECKED | states::PRESSED)) {
+ return ToggleState_On;
+ }
+ return ToggleState_Off;
+}
+
+static ExpandCollapseState ToExpandCollapseState(uint64_t aState) {
+ if (aState & states::EXPANDED) {
+ return ExpandCollapseState_Expanded;
+ }
+ // If aria-haspopup is specified without aria-expanded, we should still expose
+ // collapsed, since aria-haspopup infers that it can be expanded. The
+ // alternative is ExpandCollapseState_LeafNode, but that means that the
+ // element can't be expanded nor collapsed.
+ if (aState & (states::COLLAPSED | states::HASPOPUP)) {
+ return ExpandCollapseState_Collapsed;
+ }
+ return ExpandCollapseState_LeafNode;
+}
+
////////////////////////////////////////////////////////////////////////////////
// uiaRawElmProvider
////////////////////////////////////////////////////////////////////////////////
-Accessible* uiaRawElmProvider::Acc() {
- return static_cast<MsaaAccessible*>(this)->Acc();
+Accessible* uiaRawElmProvider::Acc() const {
+ return static_cast<const MsaaAccessible*>(this)->Acc();
}
-// IUnknown
-
-// Because uiaRawElmProvider inherits multiple COM interfaces (and thus multiple
-// IUnknowns), we need to explicitly implement AddRef and Release to make
-// our QueryInterface implementation (IMPL_IUNKNOWN2) happy.
-ULONG STDMETHODCALLTYPE uiaRawElmProvider::AddRef() {
- return static_cast<MsaaAccessible*>(this)->AddRef();
+/* static */
+void uiaRawElmProvider::RaiseUiaEventForGeckoEvent(Accessible* aAcc,
+ uint32_t aGeckoEvent) {
+ if (!StaticPrefs::accessibility_uia_enable()) {
+ return;
+ }
+ auto* uia = MsaaAccessible::GetFrom(aAcc);
+ if (!uia) {
+ return;
+ }
+ PROPERTYID property = 0;
+ _variant_t newVal;
+ bool gotNewVal = false;
+ // For control pattern properties, we can't use GetPropertyValue. In those
+ // cases, we must set newVal appropriately and set gotNewVal to true.
+ switch (aGeckoEvent) {
+ case nsIAccessibleEvent::EVENT_DESCRIPTION_CHANGE:
+ property = UIA_FullDescriptionPropertyId;
+ break;
+ case nsIAccessibleEvent::EVENT_FOCUS:
+ ::UiaRaiseAutomationEvent(uia, UIA_AutomationFocusChangedEventId);
+ return;
+ case nsIAccessibleEvent::EVENT_NAME_CHANGE:
+ property = UIA_NamePropertyId;
+ break;
+ case nsIAccessibleEvent::EVENT_TEXT_VALUE_CHANGE:
+ property = UIA_ValueValuePropertyId;
+ newVal.vt = VT_BSTR;
+ uia->get_Value(&newVal.bstrVal);
+ gotNewVal = true;
+ break;
+ }
+ if (property && ::UiaClientsAreListening()) {
+ // We can't get the old value. Thankfully, clients don't seem to need it.
+ _variant_t oldVal;
+ if (!gotNewVal) {
+ // This isn't a pattern property, so we can use GetPropertyValue.
+ uia->GetPropertyValue(property, &newVal);
+ }
+ ::UiaRaiseAutomationPropertyChangedEvent(uia, property, oldVal, newVal);
+ }
}
-ULONG STDMETHODCALLTYPE uiaRawElmProvider::Release() {
- return static_cast<MsaaAccessible*>(this)->Release();
+/* static */
+void uiaRawElmProvider::RaiseUiaEventForStateChange(Accessible* aAcc,
+ uint64_t aState,
+ bool aEnabled) {
+ if (!StaticPrefs::accessibility_uia_enable()) {
+ return;
+ }
+ auto* uia = MsaaAccessible::GetFrom(aAcc);
+ if (!uia) {
+ return;
+ }
+ PROPERTYID property = 0;
+ _variant_t newVal;
+ switch (aState) {
+ case states::CHECKED:
+ case states::MIXED:
+ case states::PRESSED:
+ property = UIA_ToggleToggleStatePropertyId;
+ newVal.vt = VT_I4;
+ newVal.lVal = ToToggleState(aEnabled ? aState : 0);
+ break;
+ case states::COLLAPSED:
+ case states::EXPANDED:
+ case states::HASPOPUP:
+ property = UIA_ExpandCollapseExpandCollapseStatePropertyId;
+ newVal.vt = VT_I4;
+ newVal.lVal = ToExpandCollapseState(aEnabled ? aState : 0);
+ break;
+ case states::UNAVAILABLE:
+ property = UIA_IsEnabledPropertyId;
+ newVal.vt = VT_BOOL;
+ newVal.boolVal = aEnabled ? VARIANT_FALSE : VARIANT_TRUE;
+ break;
+ default:
+ return;
+ }
+ MOZ_ASSERT(property);
+ if (::UiaClientsAreListening()) {
+ // We can't get the old value. Thankfully, clients don't seem to need it.
+ _variant_t oldVal;
+ ::UiaRaiseAutomationPropertyChangedEvent(uia, property, oldVal, newVal);
+ }
}
-IMPL_IUNKNOWN2(uiaRawElmProvider, IAccessibleEx, IRawElementProviderSimple)
+// IUnknown
+
+STDMETHODIMP
+uiaRawElmProvider::QueryInterface(REFIID aIid, void** aInterface) {
+ *aInterface = nullptr;
+ if (aIid == IID_IAccessibleEx) {
+ *aInterface = static_cast<IAccessibleEx*>(this);
+ } else if (aIid == IID_IRawElementProviderSimple) {
+ *aInterface = static_cast<IRawElementProviderSimple*>(this);
+ } else if (aIid == IID_IRawElementProviderFragment) {
+ *aInterface = static_cast<IRawElementProviderFragment*>(this);
+ } else if (aIid == IID_IExpandCollapseProvider) {
+ *aInterface = static_cast<IExpandCollapseProvider*>(this);
+ } else if (aIid == IID_IInvokeProvider) {
+ *aInterface = static_cast<IInvokeProvider*>(this);
+ } else if (aIid == IID_IScrollItemProvider) {
+ *aInterface = static_cast<IScrollItemProvider*>(this);
+ } else if (aIid == IID_IToggleProvider) {
+ *aInterface = static_cast<IToggleProvider*>(this);
+ } else if (aIid == IID_IValueProvider) {
+ *aInterface = static_cast<IValueProvider*>(this);
+ } else {
+ return E_NOINTERFACE;
+ }
+ MOZ_ASSERT(*aInterface);
+ static_cast<MsaaAccessible*>(this)->AddRef();
+ return S_OK;
+}
////////////////////////////////////////////////////////////////////////////////
// IAccessibleEx
@@ -113,8 +248,9 @@ uiaRawElmProvider::get_ProviderOptions(
__RPC__out enum ProviderOptions* aOptions) {
if (!aOptions) return E_INVALIDARG;
- // This method is not used with IAccessibleEx implementations.
- *aOptions = ProviderOptions_ServerSideProvider;
+ *aOptions = static_cast<enum ProviderOptions>(
+ ProviderOptions_ServerSideProvider | ProviderOptions_UseComThreading |
+ ProviderOptions_HasNativeIAccessible);
return S_OK;
}
@@ -122,8 +258,46 @@ STDMETHODIMP
uiaRawElmProvider::GetPatternProvider(
PATTERNID aPatternId, __RPC__deref_out_opt IUnknown** aPatternProvider) {
if (!aPatternProvider) return E_INVALIDARG;
-
*aPatternProvider = nullptr;
+ Accessible* acc = Acc();
+ if (!acc) {
+ return CO_E_OBJNOTCONNECTED;
+ }
+ switch (aPatternId) {
+ case UIA_ExpandCollapsePatternId:
+ if (HasExpandCollapsePattern()) {
+ RefPtr<IExpandCollapseProvider> expand = this;
+ expand.forget(aPatternProvider);
+ }
+ return S_OK;
+ case UIA_InvokePatternId:
+ // Per the UIA documentation, we should only expose the Invoke pattern "if
+ // the same behavior is not exposed through another control pattern
+ // provider".
+ if (acc->ActionCount() > 0 && !HasTogglePattern() &&
+ !HasExpandCollapsePattern()) {
+ RefPtr<IInvokeProvider> invoke = this;
+ invoke.forget(aPatternProvider);
+ }
+ return S_OK;
+ case UIA_ScrollItemPatternId: {
+ RefPtr<IScrollItemProvider> scroll = this;
+ scroll.forget(aPatternProvider);
+ return S_OK;
+ }
+ case UIA_TogglePatternId:
+ if (HasTogglePattern()) {
+ RefPtr<IToggleProvider> toggle = this;
+ toggle.forget(aPatternProvider);
+ }
+ return S_OK;
+ case UIA_ValuePatternId:
+ if (HasValuePattern()) {
+ RefPtr<IValueProvider> value = this;
+ value.forget(aPatternProvider);
+ }
+ return S_OK;
+ }
return S_OK;
}
@@ -230,11 +404,71 @@ uiaRawElmProvider::GetPropertyValue(PROPERTYID aPropertyId,
break;
}
- case UIA_IsControlElementPropertyId:
+ case UIA_AutomationIdPropertyId: {
+ nsAutoString id;
+ acc->DOMNodeID(id);
+ if (!id.IsEmpty()) {
+ aPropertyValue->vt = VT_BSTR;
+ aPropertyValue->bstrVal = ::SysAllocString(id.get());
+ return S_OK;
+ }
+ break;
+ }
+
+ case UIA_ControlTypePropertyId:
+ aPropertyValue->vt = VT_I4;
+ aPropertyValue->lVal = GetControlType();
+ break;
+
+ case UIA_FullDescriptionPropertyId: {
+ nsAutoString desc;
+ acc->Description(desc);
+ if (!desc.IsEmpty()) {
+ aPropertyValue->vt = VT_BSTR;
+ aPropertyValue->bstrVal = ::SysAllocString(desc.get());
+ return S_OK;
+ }
+ break;
+ }
+
+ case UIA_HasKeyboardFocusPropertyId:
+ aPropertyValue->vt = VT_BOOL;
+ aPropertyValue->boolVal = VARIANT_FALSE;
+ if (auto* focusMgr = FocusMgr()) {
+ if (focusMgr->IsFocused(acc)) {
+ aPropertyValue->boolVal = VARIANT_TRUE;
+ }
+ }
+ return S_OK;
+
case UIA_IsContentElementPropertyId:
+ case UIA_IsControlElementPropertyId:
aPropertyValue->vt = VT_BOOL;
aPropertyValue->boolVal = IsControl() ? VARIANT_TRUE : VARIANT_FALSE;
return S_OK;
+
+ case UIA_IsEnabledPropertyId:
+ aPropertyValue->vt = VT_BOOL;
+ aPropertyValue->boolVal =
+ (acc->State() & states::UNAVAILABLE) ? VARIANT_FALSE : VARIANT_TRUE;
+ return S_OK;
+
+ case UIA_IsKeyboardFocusablePropertyId:
+ aPropertyValue->vt = VT_BOOL;
+ aPropertyValue->boolVal =
+ (acc->State() & states::FOCUSABLE) ? VARIANT_TRUE : VARIANT_FALSE;
+ return S_OK;
+
+ case UIA_NamePropertyId: {
+ nsAutoString name;
+ acc->Name(name);
+ if (!name.IsEmpty()) {
+ aPropertyValue->vt = VT_BSTR;
+ aPropertyValue->bstrVal = ::SysAllocString(name.get());
+ return S_OK;
+ }
+ break;
+ }
}
return S_OK;
@@ -244,9 +478,274 @@ STDMETHODIMP
uiaRawElmProvider::get_HostRawElementProvider(
__RPC__deref_out_opt IRawElementProviderSimple** aRawElmProvider) {
if (!aRawElmProvider) return E_INVALIDARG;
-
- // This method is not used with IAccessibleEx implementations.
*aRawElmProvider = nullptr;
+ Accessible* acc = Acc();
+ if (!acc) {
+ return CO_E_OBJNOTCONNECTED;
+ }
+ if (acc->IsRoot()) {
+ HWND hwnd = MsaaAccessible::GetHWNDFor(acc);
+ return UiaHostProviderFromHwnd(hwnd, aRawElmProvider);
+ }
+ return S_OK;
+}
+
+// IRawElementProviderFragment
+
+STDMETHODIMP
+uiaRawElmProvider::Navigate(
+ enum NavigateDirection aDirection,
+ __RPC__deref_out_opt IRawElementProviderFragment** aRetVal) {
+ if (!aRetVal) {
+ return E_INVALIDARG;
+ }
+ *aRetVal = nullptr;
+ Accessible* acc = Acc();
+ if (!acc) {
+ return CO_E_OBJNOTCONNECTED;
+ }
+ Accessible* target = nullptr;
+ switch (aDirection) {
+ case NavigateDirection_Parent:
+ if (!acc->IsRoot()) {
+ target = acc->Parent();
+ }
+ break;
+ case NavigateDirection_NextSibling:
+ if (!acc->IsRoot()) {
+ target = acc->NextSibling();
+ }
+ break;
+ case NavigateDirection_PreviousSibling:
+ if (!acc->IsRoot()) {
+ target = acc->PrevSibling();
+ }
+ break;
+ case NavigateDirection_FirstChild:
+ if (!nsAccUtils::MustPrune(acc)) {
+ target = acc->FirstChild();
+ }
+ break;
+ case NavigateDirection_LastChild:
+ if (!nsAccUtils::MustPrune(acc)) {
+ target = acc->LastChild();
+ }
+ break;
+ default:
+ return E_INVALIDARG;
+ }
+ RefPtr<IRawElementProviderFragment> fragment =
+ MsaaAccessible::GetFrom(target);
+ fragment.forget(aRetVal);
+ return S_OK;
+}
+
+STDMETHODIMP
+uiaRawElmProvider::get_BoundingRectangle(__RPC__out struct UiaRect* aRetVal) {
+ if (!aRetVal) {
+ return E_INVALIDARG;
+ }
+ Accessible* acc = Acc();
+ if (!acc) {
+ return CO_E_OBJNOTCONNECTED;
+ }
+ LayoutDeviceIntRect rect = acc->Bounds();
+ aRetVal->left = rect.X();
+ aRetVal->top = rect.Y();
+ aRetVal->width = rect.Width();
+ aRetVal->height = rect.Height();
+ return S_OK;
+}
+
+STDMETHODIMP
+uiaRawElmProvider::GetEmbeddedFragmentRoots(
+ __RPC__deref_out_opt SAFEARRAY** aRetVal) {
+ if (!aRetVal) {
+ return E_INVALIDARG;
+ }
+ *aRetVal = nullptr;
+ return S_OK;
+}
+
+STDMETHODIMP
+uiaRawElmProvider::SetFocus() {
+ Accessible* acc = Acc();
+ if (!acc) {
+ return CO_E_OBJNOTCONNECTED;
+ }
+ acc->TakeFocus();
+ return S_OK;
+}
+
+STDMETHODIMP
+uiaRawElmProvider::get_FragmentRoot(
+ __RPC__deref_out_opt IRawElementProviderFragmentRoot** aRetVal) {
+ if (!aRetVal) {
+ return E_INVALIDARG;
+ }
+ *aRetVal = nullptr;
+ Accessible* acc = Acc();
+ if (!acc) {
+ return CO_E_OBJNOTCONNECTED;
+ }
+ LocalAccessible* localAcc = acc->AsLocal();
+ if (!localAcc) {
+ localAcc = acc->AsRemote()->OuterDocOfRemoteBrowser();
+ if (!localAcc) {
+ return CO_E_OBJNOTCONNECTED;
+ }
+ }
+ MsaaAccessible* msaa = MsaaAccessible::GetFrom(localAcc->RootAccessible());
+ RefPtr<IRawElementProviderFragmentRoot> fragRoot =
+ static_cast<MsaaRootAccessible*>(msaa);
+ fragRoot.forget(aRetVal);
+ return S_OK;
+}
+
+// IInvokeProvider methods
+
+STDMETHODIMP
+uiaRawElmProvider::Invoke() {
+ Accessible* acc = Acc();
+ if (!acc) {
+ return CO_E_OBJNOTCONNECTED;
+ }
+ if (acc->DoAction(0)) {
+ // We don't currently have a way to notify when the action was actually
+ // handled. The UIA documentation says it's okay to fire this immediately if
+ // it "is not possible or practical to wait until the action is complete".
+ ::UiaRaiseAutomationEvent(this, UIA_Invoke_InvokedEventId);
+ }
+ return S_OK;
+}
+
+// IToggleProvider methods
+
+STDMETHODIMP
+uiaRawElmProvider::Toggle() {
+ Accessible* acc = Acc();
+ if (!acc) {
+ return CO_E_OBJNOTCONNECTED;
+ }
+ acc->DoAction(0);
+ return S_OK;
+}
+
+STDMETHODIMP
+uiaRawElmProvider::get_ToggleState(__RPC__out enum ToggleState* aRetVal) {
+ if (!aRetVal) {
+ return E_INVALIDARG;
+ }
+ Accessible* acc = Acc();
+ if (!acc) {
+ return CO_E_OBJNOTCONNECTED;
+ }
+ *aRetVal = ToToggleState(acc->State());
+ return S_OK;
+}
+
+// IExpandCollapseProvider methods
+
+STDMETHODIMP
+uiaRawElmProvider::Expand() {
+ Accessible* acc = Acc();
+ if (!acc) {
+ return CO_E_OBJNOTCONNECTED;
+ }
+ if (acc->State() & states::EXPANDED) {
+ return UIA_E_INVALIDOPERATION;
+ }
+ acc->DoAction(0);
+ return S_OK;
+}
+
+STDMETHODIMP
+uiaRawElmProvider::Collapse() {
+ Accessible* acc = Acc();
+ if (!acc) {
+ return CO_E_OBJNOTCONNECTED;
+ }
+ if (acc->State() & states::COLLAPSED) {
+ return UIA_E_INVALIDOPERATION;
+ }
+ acc->DoAction(0);
+ return S_OK;
+}
+
+STDMETHODIMP
+uiaRawElmProvider::get_ExpandCollapseState(
+ __RPC__out enum ExpandCollapseState* aRetVal) {
+ if (!aRetVal) {
+ return E_INVALIDARG;
+ }
+ Accessible* acc = Acc();
+ if (!acc) {
+ return CO_E_OBJNOTCONNECTED;
+ }
+ *aRetVal = ToExpandCollapseState(acc->State());
+ return S_OK;
+}
+
+// IScrollItemProvider methods
+
+MOZ_CAN_RUN_SCRIPT_BOUNDARY STDMETHODIMP uiaRawElmProvider::ScrollIntoView() {
+ Accessible* acc = Acc();
+ if (!acc) {
+ return CO_E_OBJNOTCONNECTED;
+ }
+ acc->ScrollTo(nsIAccessibleScrollType::SCROLL_TYPE_ANYWHERE);
+ return S_OK;
+}
+
+// IValueProvider methods
+
+STDMETHODIMP
+uiaRawElmProvider::SetValue(__RPC__in LPCWSTR aVal) {
+ Accessible* acc = Acc();
+ if (!acc) {
+ return CO_E_OBJNOTCONNECTED;
+ }
+ HyperTextAccessibleBase* ht = acc->AsHyperTextBase();
+ if (!ht || !acc->IsTextRole()) {
+ return UIA_E_INVALIDOPERATION;
+ }
+ if (acc->State() & (states::READONLY | states::UNAVAILABLE)) {
+ return UIA_E_INVALIDOPERATION;
+ }
+ nsAutoString text(aVal);
+ ht->ReplaceText(text);
+ return S_OK;
+}
+
+STDMETHODIMP
+uiaRawElmProvider::get_Value(__RPC__deref_out_opt BSTR* aRetVal) {
+ if (!aRetVal) {
+ return E_INVALIDARG;
+ }
+ *aRetVal = nullptr;
+ Accessible* acc = Acc();
+ if (!acc) {
+ return CO_E_OBJNOTCONNECTED;
+ }
+ nsAutoString value;
+ acc->Value(value);
+ *aRetVal = ::SysAllocStringLen(value.get(), value.Length());
+ if (!*aRetVal) {
+ return E_OUTOFMEMORY;
+ }
+ return S_OK;
+}
+
+STDMETHODIMP
+uiaRawElmProvider::get_IsReadOnly(__RPC__out BOOL* aRetVal) {
+ if (!aRetVal) {
+ return E_INVALIDARG;
+ }
+ Accessible* acc = Acc();
+ if (!acc) {
+ return CO_E_OBJNOTCONNECTED;
+ }
+ *aRetVal = acc->State() & states::READONLY;
return S_OK;
}
@@ -314,3 +813,44 @@ bool uiaRawElmProvider::IsControl() {
return true;
}
+
+long uiaRawElmProvider::GetControlType() const {
+ Accessible* acc = Acc();
+ MOZ_ASSERT(acc);
+#define ROLE(_geckoRole, stringRole, ariaRole, atkRole, macRole, macSubrole, \
+ msaaRole, ia2Role, androidClass, iosIsElement, uiaControlType, \
+ nameRule) \
+ case roles::_geckoRole: \
+ return uiaControlType; \
+ break;
+ switch (acc->Role()) {
+#include "RoleMap.h"
+ }
+#undef ROLE
+ MOZ_CRASH("Unknown role.");
+ return 0;
+}
+
+bool uiaRawElmProvider::HasTogglePattern() {
+ Accessible* acc = Acc();
+ MOZ_ASSERT(acc);
+ return acc->State() & states::CHECKABLE ||
+ acc->Role() == roles::TOGGLE_BUTTON;
+}
+
+bool uiaRawElmProvider::HasExpandCollapsePattern() {
+ Accessible* acc = Acc();
+ MOZ_ASSERT(acc);
+ return acc->State() & (states::EXPANDABLE | states::HASPOPUP);
+}
+
+bool uiaRawElmProvider::HasValuePattern() const {
+ Accessible* acc = Acc();
+ MOZ_ASSERT(acc);
+ if (acc->HasNumericValue() || acc->IsCombobox() || acc->IsHTMLLink() ||
+ acc->IsTextField()) {
+ return true;
+ }
+ const nsRoleMapEntry* roleMapEntry = acc->ARIARoleMap();
+ return roleMapEntry && roleMapEntry->Is(nsGkAtoms::textbox);
+}
diff --git a/accessible/windows/uia/uiaRawElmProvider.h b/accessible/windows/uia/uiaRawElmProvider.h
index 0e5172c805..0e05d1a030 100644
--- a/accessible/windows/uia/uiaRawElmProvider.h
+++ b/accessible/windows/uia/uiaRawElmProvider.h
@@ -7,9 +7,9 @@
#ifndef mozilla_a11y_uiaRawElmProvider_h__
#define mozilla_a11y_uiaRawElmProvider_h__
-#include "objbase.h"
-#include "IUnknownImpl.h"
-#include "uiautomation.h"
+#include <objbase.h>
+#include <stdint.h>
+#include <uiautomation.h>
namespace mozilla {
namespace a11y {
@@ -20,12 +20,21 @@ class Accessible;
* IRawElementProviderSimple implementation (maintains IAccessibleEx approach).
*/
class uiaRawElmProvider : public IAccessibleEx,
- public IRawElementProviderSimple {
+ public IRawElementProviderSimple,
+ public IRawElementProviderFragment,
+ public IInvokeProvider,
+ public IToggleProvider,
+ public IExpandCollapseProvider,
+ public IScrollItemProvider,
+ public IValueProvider {
public:
+ static void RaiseUiaEventForGeckoEvent(Accessible* aAcc,
+ uint32_t aGeckoEvent);
+ static void RaiseUiaEventForStateChange(Accessible* aAcc, uint64_t aState,
+ bool aEnabled);
+
// IUnknown
- DECL_IUNKNOWN_INHERITED
- ULONG STDMETHODCALLTYPE AddRef() override;
- ULONG STDMETHODCALLTYPE Release() override;
+ STDMETHODIMP QueryInterface(REFIID aIid, void** aInterface);
// IAccessibleEx
virtual HRESULT STDMETHODCALLTYPE GetObjectForChild(
@@ -59,9 +68,63 @@ class uiaRawElmProvider : public IAccessibleEx,
/* [retval][out] */ __RPC__deref_out_opt IRawElementProviderSimple**
aRawElmProvider);
+ // IRawElementProviderFragment
+ virtual HRESULT STDMETHODCALLTYPE Navigate(
+ /* [in] */ enum NavigateDirection aDirection,
+ /* [retval][out] */ __RPC__deref_out_opt IRawElementProviderFragment**
+ aRetVal);
+
+ // GetRuntimeId is shared with IAccessibleEx.
+
+ virtual HRESULT STDMETHODCALLTYPE get_BoundingRectangle(
+ /* [retval][out] */ __RPC__out struct UiaRect* aRetVal);
+
+ virtual HRESULT STDMETHODCALLTYPE GetEmbeddedFragmentRoots(
+ /* [retval][out] */ __RPC__deref_out_opt SAFEARRAY** aRetVal);
+
+ virtual HRESULT STDMETHODCALLTYPE SetFocus(void);
+
+ virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_FragmentRoot(
+ /* [retval][out] */ __RPC__deref_out_opt IRawElementProviderFragmentRoot**
+ aRetVal);
+
+ // IInvokeProvider
+ virtual HRESULT STDMETHODCALLTYPE Invoke(void);
+
+ // IToggleProvider
+ virtual HRESULT STDMETHODCALLTYPE Toggle(void);
+
+ virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_ToggleState(
+ /* [retval][out] */ __RPC__out enum ToggleState* aRetVal);
+
+ // IExpandCollapseProvider
+ virtual HRESULT STDMETHODCALLTYPE Expand(void);
+
+ virtual HRESULT STDMETHODCALLTYPE Collapse(void);
+
+ virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_ExpandCollapseState(
+ /* [retval][out] */ __RPC__out enum ExpandCollapseState* aRetVal);
+
+ // IScrollItemProvider
+ virtual HRESULT STDMETHODCALLTYPE ScrollIntoView(void);
+
+ // IValueProvider
+ virtual HRESULT STDMETHODCALLTYPE SetValue(
+ /* [in] */ __RPC__in LPCWSTR val);
+
+ virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_Value(
+ /* [retval][out] */ __RPC__deref_out_opt BSTR* pRetVal);
+
+ virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_IsReadOnly(
+ /* [retval][out] */ __RPC__out BOOL* pRetVal);
+
private:
- Accessible* Acc();
+ Accessible* Acc() const;
bool IsControl();
+ long GetControlType() const;
+ bool HasTogglePattern();
+ bool HasExpandCollapsePattern();
+ bool HasValuePattern() const;
};
} // namespace a11y