summaryrefslogtreecommitdiffstats
path: root/accessible/windows
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-12 05:43:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-12 05:43:14 +0000
commit8dd16259287f58f9273002717ec4d27e97127719 (patch)
tree3863e62a53829a84037444beab3abd4ed9dfc7d0 /accessible/windows
parentReleasing progress-linux version 126.0.1-1~progress7.99u1. (diff)
downloadfirefox-8dd16259287f58f9273002717ec4d27e97127719.tar.xz
firefox-8dd16259287f58f9273002717ec4d27e97127719.zip
Merging upstream version 127.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'accessible/windows')
-rw-r--r--accessible/windows/ia2/ia2AccessibleTable.cpp14
-rw-r--r--accessible/windows/ia2/ia2AccessibleTable.h2
-rw-r--r--accessible/windows/ia2/ia2AccessibleTableCell.cpp2
-rw-r--r--accessible/windows/ia2/ia2AccessibleTableCell.h2
-rw-r--r--accessible/windows/msaa/IUnknownImpl.h42
-rw-r--r--accessible/windows/msaa/LazyInstantiator.cpp82
-rw-r--r--accessible/windows/msaa/LazyInstantiator.h27
-rw-r--r--accessible/windows/msaa/Platform.cpp1
-rw-r--r--accessible/windows/uia/UiaGrid.cpp151
-rw-r--r--accessible/windows/uia/UiaGrid.h52
-rw-r--r--accessible/windows/uia/UiaGridItem.cpp130
-rw-r--r--accessible/windows/uia/UiaGridItem.h51
-rw-r--r--accessible/windows/uia/moz.build3
-rw-r--r--accessible/windows/uia/uiaRawElmProvider.cpp594
-rw-r--r--accessible/windows/uia/uiaRawElmProvider.h75
15 files changed, 1183 insertions, 45 deletions
diff --git a/accessible/windows/ia2/ia2AccessibleTable.cpp b/accessible/windows/ia2/ia2AccessibleTable.cpp
index 50bdc79967..9ce94e8348 100644
--- a/accessible/windows/ia2/ia2AccessibleTable.cpp
+++ b/accessible/windows/ia2/ia2AccessibleTable.cpp
@@ -21,7 +21,7 @@
using namespace mozilla::a11y;
TableAccessible* ia2AccessibleTable::TableAcc() {
- Accessible* acc = Acc();
+ Accessible* acc = MsaaAccessible::Acc();
return acc ? acc->AsTable() : nullptr;
}
@@ -46,6 +46,18 @@ ia2AccessibleTable::QueryInterface(REFIID iid, void** ppv) {
return S_OK;
}
+ if (IID_IGridProvider == iid) {
+ *ppv = static_cast<IGridProvider*>(this);
+ (reinterpret_cast<IUnknown*>(*ppv))->AddRef();
+ return S_OK;
+ }
+
+ if (IID_ITableProvider == iid) {
+ *ppv = static_cast<ITableProvider*>(this);
+ (reinterpret_cast<IUnknown*>(*ppv))->AddRef();
+ return S_OK;
+ }
+
return ia2AccessibleHypertext::QueryInterface(iid, ppv);
}
diff --git a/accessible/windows/ia2/ia2AccessibleTable.h b/accessible/windows/ia2/ia2AccessibleTable.h
index 622187c379..e8479b1733 100644
--- a/accessible/windows/ia2/ia2AccessibleTable.h
+++ b/accessible/windows/ia2/ia2AccessibleTable.h
@@ -12,6 +12,7 @@
#include "AccessibleTable2.h"
#include "ia2AccessibleHypertext.h"
#include "IUnknownImpl.h"
+#include "UiaGrid.h"
namespace mozilla {
namespace a11y {
@@ -20,6 +21,7 @@ class TableAccessible;
class ia2AccessibleTable : public IAccessibleTable,
public IAccessibleTable2,
+ public UiaGrid,
public ia2AccessibleHypertext {
public:
// IUnknown
diff --git a/accessible/windows/ia2/ia2AccessibleTableCell.cpp b/accessible/windows/ia2/ia2AccessibleTableCell.cpp
index 0204983a08..c8e98d6a2f 100644
--- a/accessible/windows/ia2/ia2AccessibleTableCell.cpp
+++ b/accessible/windows/ia2/ia2AccessibleTableCell.cpp
@@ -27,6 +27,8 @@ TableCellAccessible* ia2AccessibleTableCell::CellAcc() {
// IUnknown
IMPL_IUNKNOWN_QUERY_HEAD(ia2AccessibleTableCell)
IMPL_IUNKNOWN_QUERY_IFACE(IAccessibleTableCell)
+IMPL_IUNKNOWN_QUERY_IFACE(IGridItemProvider)
+IMPL_IUNKNOWN_QUERY_IFACE(ITableItemProvider)
IMPL_IUNKNOWN_QUERY_TAIL_INHERITED(ia2AccessibleHypertext)
////////////////////////////////////////////////////////////////////////////////
diff --git a/accessible/windows/ia2/ia2AccessibleTableCell.h b/accessible/windows/ia2/ia2AccessibleTableCell.h
index 04e978cb96..6925cd7b17 100644
--- a/accessible/windows/ia2/ia2AccessibleTableCell.h
+++ b/accessible/windows/ia2/ia2AccessibleTableCell.h
@@ -11,12 +11,14 @@
#include "AccessibleTableCell.h"
#include "ia2AccessibleHypertext.h"
#include "IUnknownImpl.h"
+#include "UiaGridItem.h"
namespace mozilla {
namespace a11y {
class TableCellAccessible;
class ia2AccessibleTableCell : public IAccessibleTableCell,
+ public UiaGridItem,
public ia2AccessibleHypertext {
public:
// IUnknown
diff --git a/accessible/windows/msaa/IUnknownImpl.h b/accessible/windows/msaa/IUnknownImpl.h
index 7935ebedda..757bff44bf 100644
--- a/accessible/windows/msaa/IUnknownImpl.h
+++ b/accessible/windows/msaa/IUnknownImpl.h
@@ -47,31 +47,31 @@ class AutoRefCnt {
} // namespace a11y
} // namespace mozilla
-#define DECL_IUNKNOWN \
- public: \
- virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, void**); \
- ULONG STDMETHODCALLTYPE AddRef() override { \
- MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt"); \
- ++mRefCnt; \
- return mRefCnt; \
- } \
- ULONG STDMETHODCALLTYPE Release() override { \
- MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release"); \
- --mRefCnt; \
- if (mRefCnt) return mRefCnt; \
- \
- delete this; \
- return 0; \
- } \
- \
- private: \
- mozilla::a11y::AutoRefCnt mRefCnt; \
- \
+#define DECL_IUNKNOWN \
+ public: \
+ virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, void**) override; \
+ ULONG STDMETHODCALLTYPE AddRef() override { \
+ MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt"); \
+ ++mRefCnt; \
+ return mRefCnt; \
+ } \
+ ULONG STDMETHODCALLTYPE Release() override { \
+ MOZ_ASSERT(int32_t(mRefCnt) > 0, "dup release"); \
+ --mRefCnt; \
+ if (mRefCnt) return mRefCnt; \
+ \
+ delete this; \
+ return 0; \
+ } \
+ \
+ private: \
+ mozilla::a11y::AutoRefCnt mRefCnt; \
+ \
public:
#define DECL_IUNKNOWN_INHERITED \
public: \
- virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, void**);
+ virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID, void**) override;
#define IMPL_IUNKNOWN_QUERY_HEAD(Class) \
STDMETHODIMP \
diff --git a/accessible/windows/msaa/LazyInstantiator.cpp b/accessible/windows/msaa/LazyInstantiator.cpp
index 61814d059a..3ace26d9c0 100644
--- a/accessible/windows/msaa/LazyInstantiator.cpp
+++ b/accessible/windows/msaa/LazyInstantiator.cpp
@@ -12,6 +12,7 @@
#include "mozilla/a11y/Platform.h"
#include "mozilla/Assertions.h"
#include "mozilla/mscom/ProcessRuntime.h"
+#include "mozilla/StaticPrefs_accessibility.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/WinHeaderOnlyUtils.h"
#include "MsaaRootAccessible.h"
@@ -41,9 +42,9 @@ static const wchar_t kLazyInstantiatorProp[] =
Maybe<bool> LazyInstantiator::sShouldBlockUia;
-/* static */
-already_AddRefed<IAccessible> LazyInstantiator::GetRootAccessible(HWND aHwnd) {
- RefPtr<IAccessible> result;
+template <class T>
+already_AddRefed<T> LazyInstantiator::GetRoot(HWND aHwnd) {
+ RefPtr<T> result;
// 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()) {
@@ -80,7 +81,7 @@ already_AddRefed<IAccessible> LazyInstantiator::GetRootAccessible(HWND aHwnd) {
if (!rootAcc->IsRoot()) {
// rootAcc might represent a popup as opposed to a true root accessible.
// In that case we just use the regular LocalAccessible::GetNativeInterface.
- rootAcc->GetNativeInterface(getter_AddRefs(result));
+ result = MsaaAccessible::GetFrom(rootAcc);
return result.forget();
}
@@ -90,7 +91,7 @@ already_AddRefed<IAccessible> LazyInstantiator::GetRootAccessible(HWND aHwnd) {
// 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.
+ // that for the interface we want.
RefPtr<IUnknown> punk(msaaRoot->GetInternalUnknown());
MOZ_ASSERT(punk);
@@ -98,10 +99,24 @@ already_AddRefed<IAccessible> LazyInstantiator::GetRootAccessible(HWND aHwnd) {
return nullptr;
}
- punk->QueryInterface(IID_IAccessible, getter_AddRefs(result));
+ punk->QueryInterface(__uuidof(T), getter_AddRefs(result));
return result.forget();
}
+/* static */
+already_AddRefed<IAccessible> LazyInstantiator::GetRootAccessible(HWND aHwnd) {
+ return GetRoot<IAccessible>(aHwnd);
+}
+
+/* static */
+already_AddRefed<IRawElementProviderSimple> LazyInstantiator::GetRootUia(
+ HWND aHwnd) {
+ if (!StaticPrefs::accessibility_uia_enable()) {
+ return nullptr;
+ }
+ return GetRoot<IRawElementProviderSimple>(aHwnd);
+}
+
/**
* When marshaling an interface, COM makes a whole bunch of QueryInterface
* calls to determine what kind of marshaling the interface supports. We need
@@ -135,7 +150,8 @@ LazyInstantiator::LazyInstantiator(HWND aHwnd)
mAllowBlindAggregation(false),
mWeakMsaaRoot(nullptr),
mWeakAccessible(nullptr),
- mWeakDispatch(nullptr) {
+ mWeakDispatch(nullptr),
+ mWeakUia(nullptr) {
MOZ_ASSERT(aHwnd);
// Assign ourselves as the designated LazyInstantiator for aHwnd
DebugOnly<BOOL> setPropOk =
@@ -374,9 +390,16 @@ LazyInstantiator::MaybeResolveRoot() {
if (FAILED(hr)) {
return hr;
}
-
// mWeakAccessible is weak, so don't hold a strong ref
mWeakAccessible->Release();
+ if (StaticPrefs::accessibility_uia_enable()) {
+ hr = mRealRootUnk->QueryInterface(IID_IRawElementProviderSimple,
+ (void**)&mWeakUia);
+ if (FAILED(hr)) {
+ return hr;
+ }
+ mWeakUia->Release();
+ }
// Now that a11y is running, we don't need to remain registered with our
// HWND anymore.
@@ -401,6 +424,9 @@ IMPL_IUNKNOWN_QUERY_IFACE_AMBIGIOUS(IUnknown, IAccessible)
IMPL_IUNKNOWN_QUERY_IFACE(IAccessible)
IMPL_IUNKNOWN_QUERY_IFACE(IDispatch)
IMPL_IUNKNOWN_QUERY_IFACE(IServiceProvider)
+if (StaticPrefs::accessibility_uia_enable()) {
+ IMPL_IUNKNOWN_QUERY_IFACE(IRawElementProviderSimple)
+}
// See EnableBlindAggregation for comments.
if (!mAllowBlindAggregation) {
return E_NOINTERFACE;
@@ -771,5 +797,45 @@ LazyInstantiator::QueryService(REFGUID aServiceId, REFIID aServiceIid,
return servProv->QueryService(aServiceId, aServiceIid, aOutInterface);
}
+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.
+ if (!aOptions) {
+ return E_INVALIDARG;
+ }
+ *aOptions = uiaRawElmProvider::kProviderOptions;
+ return S_OK;
+}
+
+STDMETHODIMP
+LazyInstantiator::GetPatternProvider(
+ PATTERNID aPatternId, __RPC__deref_out_opt IUnknown** aPatternProvider) {
+ RESOLVE_ROOT;
+ return mWeakUia->GetPatternProvider(aPatternId, aPatternProvider);
+}
+
+STDMETHODIMP
+LazyInstantiator::GetPropertyValue(PROPERTYID aPropertyId,
+ __RPC__out VARIANT* aPropertyValue) {
+ RESOLVE_ROOT;
+ return mWeakUia->GetPropertyValue(aPropertyId, aPropertyValue);
+}
+
+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.
+ if (!aRawElmProvider) {
+ return E_INVALIDARG;
+ }
+ *aRawElmProvider = nullptr;
+ return UiaHostProviderFromHwnd(mHwnd, aRawElmProvider);
+}
+
} // namespace a11y
} // namespace mozilla
diff --git a/accessible/windows/msaa/LazyInstantiator.h b/accessible/windows/msaa/LazyInstantiator.h
index 00fa4ba6ed..adbb0f70b5 100644
--- a/accessible/windows/msaa/LazyInstantiator.h
+++ b/accessible/windows/msaa/LazyInstantiator.h
@@ -13,6 +13,7 @@
#include "nsString.h"
#include <oleacc.h>
+#include <uiautomation.h>
class nsIFile;
@@ -29,10 +30,14 @@ class MsaaRootAccessible;
* services in order to fulfill; and
* (2) LazyInstantiator::ShouldInstantiate returns true.
*/
-class LazyInstantiator final : public IAccessible, public IServiceProvider {
+class LazyInstantiator final : public IAccessible,
+ public IServiceProvider,
+ public IRawElementProviderSimple {
public:
[[nodiscard]] static already_AddRefed<IAccessible> GetRootAccessible(
HWND aHwnd);
+ [[nodiscard]] static already_AddRefed<IRawElementProviderSimple> GetRootUia(
+ HWND aHwnd);
static void EnableBlindAggregation(HWND aHwnd);
// IUnknown
@@ -83,6 +88,22 @@ class LazyInstantiator final : public IAccessible, public IServiceProvider {
STDMETHODIMP QueryService(REFGUID aServiceId, REFIID aServiceIid,
void** aOutInterface) override;
+ // IRawElementProviderSimple
+ virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_ProviderOptions(
+ /* [retval][out] */ __RPC__out enum ProviderOptions* aProviderOptions);
+
+ virtual HRESULT STDMETHODCALLTYPE GetPatternProvider(
+ /* [in] */ PATTERNID aPatternId,
+ /* [retval][out] */ __RPC__deref_out_opt IUnknown** aPatternProvider);
+
+ virtual HRESULT STDMETHODCALLTYPE GetPropertyValue(
+ /* [in] */ PROPERTYID aPropertyId,
+ /* [retval][out] */ __RPC__out VARIANT* aPropertyValue);
+
+ virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_HostRawElementProvider(
+ /* [retval][out] */ __RPC__deref_out_opt IRawElementProviderSimple**
+ aRawElmProvider);
+
/**
* We cache the result of UIA detection because it could be expensive if a
* client repeatedly queries us. This function is called to reset that cache
@@ -117,6 +138,9 @@ class LazyInstantiator final : public IAccessible, public IServiceProvider {
void TransplantRefCnt();
void ClearProp();
+ template <class T>
+ static already_AddRefed<T> GetRoot(HWND aHwnd);
+
private:
mozilla::a11y::AutoRefCnt mRefCnt;
HWND mHwnd;
@@ -133,6 +157,7 @@ class LazyInstantiator final : public IAccessible, public IServiceProvider {
MsaaRootAccessible* mWeakMsaaRoot;
IAccessible* mWeakAccessible;
IDispatch* mWeakDispatch;
+ IRawElementProviderSimple* mWeakUia;
static Maybe<bool> sShouldBlockUia;
};
diff --git a/accessible/windows/msaa/Platform.cpp b/accessible/windows/msaa/Platform.cpp
index f4d1c7b176..eaf9e7c126 100644
--- a/accessible/windows/msaa/Platform.cpp
+++ b/accessible/windows/msaa/Platform.cpp
@@ -129,6 +129,7 @@ void a11y::PlatformShowHideEvent(Accessible* aTarget, Accessible*, bool aInsert,
void a11y::PlatformSelectionEvent(Accessible* aTarget, Accessible*,
uint32_t aType) {
MsaaAccessible::FireWinEvent(aTarget, aType);
+ uiaRawElmProvider::RaiseUiaEventForGeckoEvent(aTarget, aType);
}
static bool GetInstantiatorExecutable(const DWORD aPid,
diff --git a/accessible/windows/uia/UiaGrid.cpp b/accessible/windows/uia/UiaGrid.cpp
new file mode 100644
index 0000000000..2a22a1f4da
--- /dev/null
+++ b/accessible/windows/uia/UiaGrid.cpp
@@ -0,0 +1,151 @@
+/* -*- 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 "ia2AccessibleTable.h"
+#include "mozilla/a11y/TableAccessible.h"
+#include "nsIAccessiblePivot.h"
+#include "Pivot.h"
+#include "UiaGrid.h"
+
+using namespace mozilla;
+using namespace mozilla::a11y;
+
+// Helpers
+
+// Used to search for all row and column headers in a table. This could be slow,
+// as it potentially walks all cells in the table. However, it's unclear if,
+// when or how often clients will use this. If this proves to be a performance
+// problem, we will need to add methods to TableAccessible to get all row and
+// column headers in a faster way.
+class HeaderRule : public PivotRule {
+ public:
+ explicit HeaderRule(role aRole) : mRole(aRole) {}
+
+ virtual uint16_t Match(Accessible* aAcc) override {
+ role accRole = aAcc->Role();
+ if (accRole == mRole) {
+ return nsIAccessibleTraversalRule::FILTER_MATCH |
+ nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE;
+ }
+ if (accRole == roles::CAPTION || aAcc->IsTableCell()) {
+ return nsIAccessibleTraversalRule::FILTER_IGNORE |
+ nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE;
+ }
+ return nsIAccessibleTraversalRule::FILTER_IGNORE;
+ }
+
+ private:
+ role mRole;
+};
+
+static SAFEARRAY* GetAllHeaders(Accessible* aTable, role aRole) {
+ AutoTArray<Accessible*, 20> headers;
+ Pivot pivot(aTable);
+ HeaderRule rule(aRole);
+ for (Accessible* header = pivot.Next(aTable, rule); header;
+ header = pivot.Next(header, rule)) {
+ headers.AppendElement(header);
+ }
+ return AccessibleArrayToUiaArray(headers);
+}
+
+// UiaGrid
+
+Accessible* UiaGrid::Acc() {
+ auto* ia2t = static_cast<ia2AccessibleTable*>(this);
+ return ia2t->MsaaAccessible::Acc();
+}
+
+TableAccessible* UiaGrid::TableAcc() {
+ Accessible* acc = Acc();
+ return acc ? acc->AsTable() : nullptr;
+}
+
+// IGridProvider methods
+
+STDMETHODIMP
+UiaGrid::GetItem(int aRow, int aColumn,
+ __RPC__deref_out_opt IRawElementProviderSimple** aRetVal) {
+ if (!aRetVal) {
+ return E_INVALIDARG;
+ }
+ *aRetVal = nullptr;
+ TableAccessible* table = TableAcc();
+ if (!table) {
+ return CO_E_OBJNOTCONNECTED;
+ }
+ Accessible* cell = table->CellAt(aRow, aColumn);
+ if (!cell) {
+ return E_INVALIDARG;
+ }
+ RefPtr<IRawElementProviderSimple> uia = MsaaAccessible::GetFrom(cell);
+ uia.forget(aRetVal);
+ return S_OK;
+}
+
+STDMETHODIMP
+UiaGrid::get_RowCount(__RPC__out int* aRetVal) {
+ if (!aRetVal) {
+ return E_INVALIDARG;
+ }
+ TableAccessible* table = TableAcc();
+ if (!table) {
+ return CO_E_OBJNOTCONNECTED;
+ }
+ *aRetVal = table->RowCount();
+ return S_OK;
+}
+
+STDMETHODIMP
+UiaGrid::get_ColumnCount(__RPC__out int* aRetVal) {
+ if (!aRetVal) {
+ return E_INVALIDARG;
+ }
+ TableAccessible* table = TableAcc();
+ if (!table) {
+ return CO_E_OBJNOTCONNECTED;
+ }
+ *aRetVal = table->ColCount();
+ return S_OK;
+}
+
+// ITableProvider methods
+
+STDMETHODIMP
+UiaGrid::GetRowHeaders(__RPC__deref_out_opt SAFEARRAY** aRetVal) {
+ if (!aRetVal) {
+ return E_INVALIDARG;
+ }
+ Accessible* acc = Acc();
+ if (!acc) {
+ return CO_E_OBJNOTCONNECTED;
+ }
+ *aRetVal = GetAllHeaders(acc, roles::ROWHEADER);
+ return S_OK;
+}
+
+STDMETHODIMP
+UiaGrid::GetColumnHeaders(__RPC__deref_out_opt SAFEARRAY** aRetVal) {
+ if (!aRetVal) {
+ return E_INVALIDARG;
+ }
+ Accessible* acc = Acc();
+ if (!acc) {
+ return CO_E_OBJNOTCONNECTED;
+ }
+ *aRetVal = GetAllHeaders(acc, roles::COLUMNHEADER);
+ return S_OK;
+}
+
+STDMETHODIMP
+UiaGrid::get_RowOrColumnMajor(__RPC__out enum RowOrColumnMajor* aRetVal) {
+ if (!aRetVal) {
+ return E_INVALIDARG;
+ }
+ // HTML and ARIA tables are always in row major order.
+ *aRetVal = RowOrColumnMajor_RowMajor;
+ return S_OK;
+}
diff --git a/accessible/windows/uia/UiaGrid.h b/accessible/windows/uia/UiaGrid.h
new file mode 100644
index 0000000000..c38442246c
--- /dev/null
+++ b/accessible/windows/uia/UiaGrid.h
@@ -0,0 +1,52 @@
+/* -*- 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_UiaGrid_h__
+#define mozilla_a11y_UiaGrid_h__
+
+#include "objbase.h"
+#include "uiautomation.h"
+
+namespace mozilla::a11y {
+class Accessible;
+class TableAccessible;
+
+/**
+ * IGridProvider and ITableProvider implementations.
+ */
+class UiaGrid : public IGridProvider, public ITableProvider {
+ public:
+ // IGridProvider
+ virtual HRESULT STDMETHODCALLTYPE GetItem(
+ /* [in] */ int aRow,
+ /* [in] */ int aColumn,
+ /* [retval][out] */
+ __RPC__deref_out_opt IRawElementProviderSimple** aRetVal);
+
+ virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_RowCount(
+ /* [retval][out] */ __RPC__out int* aRetVal);
+
+ virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_ColumnCount(
+ /* [retval][out] */ __RPC__out int* aRetVal);
+
+ // ITableProvider
+ virtual HRESULT STDMETHODCALLTYPE GetRowHeaders(
+ /* [retval][out] */ __RPC__deref_out_opt SAFEARRAY** aRetVal);
+
+ virtual HRESULT STDMETHODCALLTYPE GetColumnHeaders(
+ /* [retval][out] */ __RPC__deref_out_opt SAFEARRAY** aRetVal);
+
+ virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_RowOrColumnMajor(
+ /* [retval][out] */ __RPC__out enum RowOrColumnMajor* aRetVal);
+
+ private:
+ Accessible* Acc();
+ TableAccessible* TableAcc();
+};
+
+} // namespace mozilla::a11y
+
+#endif
diff --git a/accessible/windows/uia/UiaGridItem.cpp b/accessible/windows/uia/UiaGridItem.cpp
new file mode 100644
index 0000000000..89c56b8d45
--- /dev/null
+++ b/accessible/windows/uia/UiaGridItem.cpp
@@ -0,0 +1,130 @@
+/* -*- 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 "ia2AccessibleTableCell.h"
+#include "mozilla/a11y/TableAccessible.h"
+#include "mozilla/a11y/TableCellAccessible.h"
+#include "UiaGridItem.h"
+
+using namespace mozilla;
+using namespace mozilla::a11y;
+
+// UiaGridItem
+
+TableCellAccessible* UiaGridItem::CellAcc() {
+ auto* derived = static_cast<ia2AccessibleTableCell*>(this);
+ Accessible* acc = derived->Acc();
+ return acc ? acc->AsTableCell() : nullptr;
+}
+
+// IGridItemProvider methods
+
+STDMETHODIMP
+UiaGridItem::get_Row(__RPC__out int* aRetVal) {
+ if (!aRetVal) {
+ return E_INVALIDARG;
+ }
+ TableCellAccessible* cell = CellAcc();
+ if (!cell) {
+ return CO_E_OBJNOTCONNECTED;
+ }
+ *aRetVal = cell->RowIdx();
+ return S_OK;
+}
+
+STDMETHODIMP
+UiaGridItem::get_Column(__RPC__out int* aRetVal) {
+ if (!aRetVal) {
+ return E_INVALIDARG;
+ }
+ TableCellAccessible* cell = CellAcc();
+ if (!cell) {
+ return CO_E_OBJNOTCONNECTED;
+ }
+ *aRetVal = cell->ColIdx();
+ return S_OK;
+}
+
+STDMETHODIMP
+UiaGridItem::get_RowSpan(__RPC__out int* aRetVal) {
+ if (!aRetVal) {
+ return E_INVALIDARG;
+ }
+ TableCellAccessible* cell = CellAcc();
+ if (!cell) {
+ return CO_E_OBJNOTCONNECTED;
+ }
+ *aRetVal = cell->RowExtent();
+ return S_OK;
+}
+
+STDMETHODIMP
+UiaGridItem::get_ColumnSpan(__RPC__out int* aRetVal) {
+ if (!aRetVal) {
+ return E_INVALIDARG;
+ }
+ TableCellAccessible* cell = CellAcc();
+ if (!cell) {
+ return CO_E_OBJNOTCONNECTED;
+ }
+ *aRetVal = cell->ColExtent();
+ return S_OK;
+}
+
+STDMETHODIMP
+UiaGridItem::get_ContainingGrid(
+ __RPC__deref_out_opt IRawElementProviderSimple** aRetVal) {
+ if (!aRetVal) {
+ return E_INVALIDARG;
+ }
+ *aRetVal = nullptr;
+ TableCellAccessible* cell = CellAcc();
+ if (!cell) {
+ return CO_E_OBJNOTCONNECTED;
+ }
+ TableAccessible* table = cell->Table();
+ if (!table) {
+ return E_FAIL;
+ }
+ Accessible* tableAcc = table->AsAccessible();
+ RefPtr<IRawElementProviderSimple> uia = MsaaAccessible::GetFrom(tableAcc);
+ uia.forget(aRetVal);
+ return S_OK;
+}
+
+// ITableItemProvider methods
+
+STDMETHODIMP
+UiaGridItem::GetRowHeaderItems(__RPC__deref_out_opt SAFEARRAY** aRetVal) {
+ if (!aRetVal) {
+ return E_INVALIDARG;
+ }
+ *aRetVal = nullptr;
+ TableCellAccessible* cell = CellAcc();
+ if (!cell) {
+ return CO_E_OBJNOTCONNECTED;
+ }
+ AutoTArray<Accessible*, 10> cells;
+ cell->RowHeaderCells(&cells);
+ *aRetVal = AccessibleArrayToUiaArray(cells);
+ return S_OK;
+}
+
+STDMETHODIMP
+UiaGridItem::GetColumnHeaderItems(__RPC__deref_out_opt SAFEARRAY** aRetVal) {
+ if (!aRetVal) {
+ return E_INVALIDARG;
+ }
+ *aRetVal = nullptr;
+ TableCellAccessible* cell = CellAcc();
+ if (!cell) {
+ return CO_E_OBJNOTCONNECTED;
+ }
+ AutoTArray<Accessible*, 10> cells;
+ cell->ColHeaderCells(&cells);
+ *aRetVal = AccessibleArrayToUiaArray(cells);
+ return S_OK;
+}
diff --git a/accessible/windows/uia/UiaGridItem.h b/accessible/windows/uia/UiaGridItem.h
new file mode 100644
index 0000000000..6c59630b0f
--- /dev/null
+++ b/accessible/windows/uia/UiaGridItem.h
@@ -0,0 +1,51 @@
+/* -*- 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_UiaGridItem_h__
+#define mozilla_a11y_UiaGridItem_h__
+
+#include "objbase.h"
+#include "uiautomation.h"
+
+namespace mozilla::a11y {
+class TableCellAccessible;
+
+/**
+ * IGridItemProvider and ITableItemProvider implementations.
+ */
+class UiaGridItem : public IGridItemProvider, public ITableItemProvider {
+ public:
+ // IGridItemProvider
+ virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_Row(
+ /* [retval][out] */ __RPC__out int* aRetVal);
+
+ virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_Column(
+ /* [retval][out] */ __RPC__out int* aRetVal);
+
+ virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_RowSpan(
+ /* [retval][out] */ __RPC__out int* aRetVal);
+
+ virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_ColumnSpan(
+ /* [retval][out] */ __RPC__out int* aRetVal);
+
+ virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_ContainingGrid(
+ /* [retval][out] */ __RPC__deref_out_opt IRawElementProviderSimple**
+ aRetVal);
+
+ // ITableItemProvider
+ virtual HRESULT STDMETHODCALLTYPE GetRowHeaderItems(
+ /* [retval][out] */ __RPC__deref_out_opt SAFEARRAY** aRetVal);
+
+ virtual HRESULT STDMETHODCALLTYPE GetColumnHeaderItems(
+ /* [retval][out] */ __RPC__deref_out_opt SAFEARRAY** aRetVal);
+
+ private:
+ TableCellAccessible* CellAcc();
+};
+
+} // namespace mozilla::a11y
+
+#endif
diff --git a/accessible/windows/uia/moz.build b/accessible/windows/uia/moz.build
index c52a24d612..2061261494 100644
--- a/accessible/windows/uia/moz.build
+++ b/accessible/windows/uia/moz.build
@@ -5,6 +5,8 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
SOURCES += [
+ "UiaGrid.cpp",
+ "UiaGridItem.cpp",
"uiaRawElmProvider.cpp",
"UiaRoot.cpp",
]
@@ -13,6 +15,7 @@ LOCAL_INCLUDES += [
"/accessible/base",
"/accessible/generic",
"/accessible/html",
+ "/accessible/windows/ia2",
"/accessible/windows/msaa",
"/accessible/xpcom",
"/accessible/xul",
diff --git a/accessible/windows/uia/uiaRawElmProvider.cpp b/accessible/windows/uia/uiaRawElmProvider.cpp
index c022e40cef..82726bb9aa 100644
--- a/accessible/windows/uia/uiaRawElmProvider.cpp
+++ b/accessible/windows/uia/uiaRawElmProvider.cpp
@@ -11,7 +11,10 @@
#include "AccAttributes.h"
#include "AccessibleWrap.h"
+#include "ApplicationAccessible.h"
#include "ARIAMap.h"
+#include "ia2AccessibleTable.h"
+#include "ia2AccessibleTableCell.h"
#include "LocalAccessible-inl.h"
#include "mozilla/a11y/RemoteAccessible.h"
#include "mozilla/StaticPrefs_accessibility.h"
@@ -19,12 +22,25 @@
#include "MsaaRootAccessible.h"
#include "nsAccessibilityService.h"
#include "nsAccUtils.h"
+#include "nsIAccessiblePivot.h"
#include "nsTextEquivUtils.h"
+#include "Pivot.h"
+#include "Relation.h"
#include "RootAccessible.h"
using namespace mozilla;
using namespace mozilla::a11y;
+#ifdef __MINGW32__
+// These constants are missing in mingw-w64. This code should be removed once
+// we update to a version which includes them.
+const long UIA_CustomLandmarkTypeId = 80000;
+const long UIA_FormLandmarkTypeId = 80001;
+const long UIA_MainLandmarkTypeId = 80002;
+const long UIA_NavigationLandmarkTypeId = 80003;
+const long UIA_SearchLandmarkTypeId = 80004;
+#endif // __MINGW32__
+
// Helper functions
static ToggleState ToToggleState(uint64_t aState) {
@@ -51,6 +67,34 @@ static ExpandCollapseState ToExpandCollapseState(uint64_t aState) {
return ExpandCollapseState_LeafNode;
}
+static bool IsRadio(Accessible* aAcc) {
+ role r = aAcc->Role();
+ return r == roles::RADIOBUTTON || r == roles::RADIO_MENU_ITEM;
+}
+
+// Used to search for a text leaf descendant for the LabeledBy property.
+class LabelTextLeafRule : public PivotRule {
+ public:
+ virtual uint16_t Match(Accessible* aAcc) override {
+ if (aAcc->IsTextLeaf()) {
+ nsAutoString name;
+ aAcc->Name(name);
+ if (name.IsEmpty() || name.EqualsLiteral(" ")) {
+ // An empty or white space text leaf isn't useful as a label.
+ return nsIAccessibleTraversalRule::FILTER_IGNORE;
+ }
+ return nsIAccessibleTraversalRule::FILTER_MATCH;
+ }
+ if (!nsTextEquivUtils::HasNameRule(aAcc, eNameFromSubtreeIfReqRule)) {
+ // Don't descend into things that can't be used as label content; e.g.
+ // text boxes.
+ return nsIAccessibleTraversalRule::FILTER_IGNORE |
+ nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE;
+ }
+ return nsIAccessibleTraversalRule::FILTER_IGNORE;
+ }
+};
+
////////////////////////////////////////////////////////////////////////////////
// uiaRawElmProvider
////////////////////////////////////////////////////////////////////////////////
@@ -84,12 +128,32 @@ void uiaRawElmProvider::RaiseUiaEventForGeckoEvent(Accessible* aAcc,
case nsIAccessibleEvent::EVENT_NAME_CHANGE:
property = UIA_NamePropertyId;
break;
+ case nsIAccessibleEvent::EVENT_SELECTION:
+ ::UiaRaiseAutomationEvent(uia, UIA_SelectionItem_ElementSelectedEventId);
+ return;
+ case nsIAccessibleEvent::EVENT_SELECTION_ADD:
+ ::UiaRaiseAutomationEvent(
+ uia, UIA_SelectionItem_ElementAddedToSelectionEventId);
+ return;
+ case nsIAccessibleEvent::EVENT_SELECTION_REMOVE:
+ ::UiaRaiseAutomationEvent(
+ uia, UIA_SelectionItem_ElementRemovedFromSelectionEventId);
+ return;
+ case nsIAccessibleEvent::EVENT_SELECTION_WITHIN:
+ ::UiaRaiseAutomationEvent(uia, UIA_Selection_InvalidatedEventId);
+ return;
case nsIAccessibleEvent::EVENT_TEXT_VALUE_CHANGE:
property = UIA_ValueValuePropertyId;
newVal.vt = VT_BSTR;
uia->get_Value(&newVal.bstrVal);
gotNewVal = true;
break;
+ case nsIAccessibleEvent::EVENT_VALUE_CHANGE:
+ property = UIA_RangeValueValuePropertyId;
+ newVal.vt = VT_R8;
+ uia->get_Value(&newVal.dblVal);
+ gotNewVal = true;
+ break;
}
if (property && ::UiaClientsAreListening()) {
// We can't get the old value. Thankfully, clients don't seem to need it.
@@ -117,6 +181,13 @@ void uiaRawElmProvider::RaiseUiaEventForStateChange(Accessible* aAcc,
_variant_t newVal;
switch (aState) {
case states::CHECKED:
+ if (aEnabled && IsRadio(aAcc)) {
+ ::UiaRaiseAutomationEvent(uia,
+ UIA_SelectionItem_ElementSelectedEventId);
+ return;
+ }
+ // For other checkable things, the Toggle pattern is used.
+ [[fallthrough]];
case states::MIXED:
case states::PRESSED:
property = UIA_ToggleToggleStatePropertyId;
@@ -161,8 +232,14 @@ uiaRawElmProvider::QueryInterface(REFIID aIid, void** aInterface) {
*aInterface = static_cast<IExpandCollapseProvider*>(this);
} else if (aIid == IID_IInvokeProvider) {
*aInterface = static_cast<IInvokeProvider*>(this);
+ } else if (aIid == IID_IRangeValueProvider) {
+ *aInterface = static_cast<IRangeValueProvider*>(this);
} else if (aIid == IID_IScrollItemProvider) {
*aInterface = static_cast<IScrollItemProvider*>(this);
+ } else if (aIid == IID_ISelectionItemProvider) {
+ *aInterface = static_cast<ISelectionItemProvider*>(this);
+ } else if (aIid == IID_ISelectionProvider) {
+ *aInterface = static_cast<ISelectionProvider*>(this);
} else if (aIid == IID_IToggleProvider) {
*aInterface = static_cast<IToggleProvider*>(this);
} else if (aIid == IID_IValueProvider) {
@@ -248,9 +325,7 @@ uiaRawElmProvider::get_ProviderOptions(
__RPC__out enum ProviderOptions* aOptions) {
if (!aOptions) return E_INVALIDARG;
- *aOptions = static_cast<enum ProviderOptions>(
- ProviderOptions_ServerSideProvider | ProviderOptions_UseComThreading |
- ProviderOptions_HasNativeIAccessible);
+ *aOptions = kProviderOptions;
return S_OK;
}
@@ -270,21 +345,78 @@ uiaRawElmProvider::GetPatternProvider(
expand.forget(aPatternProvider);
}
return S_OK;
+ case UIA_GridPatternId:
+ if (acc->IsTable()) {
+ auto grid = GetPatternFromDerived<ia2AccessibleTable, IGridProvider>();
+ grid.forget(aPatternProvider);
+ }
+ return S_OK;
+ case UIA_GridItemPatternId:
+ if (acc->IsTableCell()) {
+ auto item =
+ GetPatternFromDerived<ia2AccessibleTableCell, IGridItemProvider>();
+ item.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()) {
+ !HasExpandCollapsePattern() && !HasSelectionItemPattern()) {
RefPtr<IInvokeProvider> invoke = this;
invoke.forget(aPatternProvider);
}
return S_OK;
+ case UIA_RangeValuePatternId:
+ if (acc->HasNumericValue()) {
+ RefPtr<IValueProvider> value = this;
+ value.forget(aPatternProvider);
+ }
+ return S_OK;
case UIA_ScrollItemPatternId: {
RefPtr<IScrollItemProvider> scroll = this;
scroll.forget(aPatternProvider);
return S_OK;
}
+ case UIA_SelectionItemPatternId:
+ if (HasSelectionItemPattern()) {
+ RefPtr<ISelectionItemProvider> item = this;
+ item.forget(aPatternProvider);
+ }
+ return S_OK;
+ case UIA_SelectionPatternId:
+ // According to the UIA documentation, radio button groups should support
+ // the Selection pattern. However:
+ // 1. The Core AAM spec doesn't specify the Selection pattern for
+ // the radiogroup role.
+ // 2. HTML radio buttons might not be contained by a dedicated group.
+ // 3. Chromium exposes the Selection pattern on radio groups, but it
+ // doesn't expose any selected items, even when there is a checked radio
+ // child.
+ // 4. Radio menu items are similar to radio buttons and all the above
+ // also applies to menus.
+ // For now, we don't support the Selection pattern for radio groups or
+ // menus, only for list boxes, tab lists, etc.
+ if (acc->IsSelect()) {
+ RefPtr<ISelectionProvider> selection = this;
+ selection.forget(aPatternProvider);
+ }
+ return S_OK;
+ case UIA_TablePatternId:
+ if (acc->IsTable()) {
+ auto table =
+ GetPatternFromDerived<ia2AccessibleTable, ITableProvider>();
+ table.forget(aPatternProvider);
+ }
+ return S_OK;
+ case UIA_TableItemPatternId:
+ if (acc->IsTableCell()) {
+ auto item =
+ GetPatternFromDerived<ia2AccessibleTableCell, ITableItemProvider>();
+ item.forget(aPatternProvider);
+ }
+ return S_OK;
case UIA_TogglePatternId:
if (HasTogglePattern()) {
RefPtr<IToggleProvider> toggle = this;
@@ -349,19 +481,19 @@ uiaRawElmProvider::GetPropertyValue(PROPERTYID aPropertyId,
break;
}
- // ARIA Role / shortcut
case UIA_AriaRolePropertyId: {
- nsAutoString xmlRoles;
-
- RefPtr<AccAttributes> attributes = acc->Attributes();
- attributes->GetAttribute(nsGkAtoms::xmlroles, xmlRoles);
-
- if (!xmlRoles.IsEmpty()) {
+ nsAutoString role;
+ if (acc->HasARIARole()) {
+ RefPtr<AccAttributes> attributes = acc->Attributes();
+ attributes->GetAttribute(nsGkAtoms::xmlroles, role);
+ } else if (nsStaticAtom* computed = acc->ComputedARIARole()) {
+ computed->ToString(role);
+ }
+ if (!role.IsEmpty()) {
aPropertyValue->vt = VT_BSTR;
- aPropertyValue->bstrVal = ::SysAllocString(xmlRoles.get());
+ aPropertyValue->bstrVal = ::SysAllocString(role.get());
return S_OK;
}
-
break;
}
@@ -415,11 +547,57 @@ uiaRawElmProvider::GetPropertyValue(PROPERTYID aPropertyId,
break;
}
+ case UIA_ClassNamePropertyId: {
+ nsAutoString className;
+ acc->DOMNodeClass(className);
+ if (!className.IsEmpty()) {
+ aPropertyValue->vt = VT_BSTR;
+ aPropertyValue->bstrVal = ::SysAllocString(className.get());
+ return S_OK;
+ }
+ break;
+ }
+
+ case UIA_ControllerForPropertyId:
+ aPropertyValue->vt = VT_UNKNOWN | VT_ARRAY;
+ aPropertyValue->parray = AccRelationsToUiaArray(
+ {RelationType::CONTROLLER_FOR, RelationType::ERRORMSG});
+ return S_OK;
+
case UIA_ControlTypePropertyId:
aPropertyValue->vt = VT_I4;
aPropertyValue->lVal = GetControlType();
break;
+ case UIA_DescribedByPropertyId:
+ aPropertyValue->vt = VT_UNKNOWN | VT_ARRAY;
+ aPropertyValue->parray = AccRelationsToUiaArray(
+ {RelationType::DESCRIBED_BY, RelationType::DETAILS});
+ return S_OK;
+
+ case UIA_FlowsFromPropertyId:
+ aPropertyValue->vt = VT_UNKNOWN | VT_ARRAY;
+ aPropertyValue->parray =
+ AccRelationsToUiaArray({RelationType::FLOWS_FROM});
+ return S_OK;
+
+ case UIA_FlowsToPropertyId:
+ aPropertyValue->vt = VT_UNKNOWN | VT_ARRAY;
+ aPropertyValue->parray = AccRelationsToUiaArray({RelationType::FLOWS_TO});
+ return S_OK;
+
+ case UIA_FrameworkIdPropertyId:
+ if (ApplicationAccessible* app = ApplicationAcc()) {
+ nsAutoString name;
+ app->PlatformName(name);
+ if (!name.IsEmpty()) {
+ aPropertyValue->vt = VT_BSTR;
+ aPropertyValue->bstrVal = ::SysAllocString(name.get());
+ return S_OK;
+ }
+ }
+ break;
+
case UIA_FullDescriptionPropertyId: {
nsAutoString desc;
acc->Description(desc);
@@ -459,6 +637,39 @@ uiaRawElmProvider::GetPropertyValue(PROPERTYID aPropertyId,
(acc->State() & states::FOCUSABLE) ? VARIANT_TRUE : VARIANT_FALSE;
return S_OK;
+ case UIA_LabeledByPropertyId:
+ if (Accessible* target = GetLabeledBy()) {
+ aPropertyValue->vt = VT_UNKNOWN;
+ RefPtr<IRawElementProviderSimple> uia = MsaaAccessible::GetFrom(target);
+ uia.forget(&aPropertyValue->punkVal);
+ return S_OK;
+ }
+ break;
+
+ case UIA_LandmarkTypePropertyId:
+ if (long type = GetLandmarkType()) {
+ aPropertyValue->vt = VT_I4;
+ aPropertyValue->lVal = type;
+ return S_OK;
+ }
+ break;
+
+ case UIA_LevelPropertyId:
+ aPropertyValue->vt = VT_I4;
+ aPropertyValue->lVal = acc->GroupPosition().level;
+ return S_OK;
+
+ case UIA_LocalizedLandmarkTypePropertyId: {
+ nsAutoString landmark;
+ GetLocalizedLandmarkType(landmark);
+ if (!landmark.IsEmpty()) {
+ aPropertyValue->vt = VT_BSTR;
+ aPropertyValue->bstrVal = ::SysAllocString(landmark.get());
+ return S_OK;
+ }
+ break;
+ }
+
case UIA_NamePropertyId: {
nsAutoString name;
acc->Name(name);
@@ -469,6 +680,16 @@ uiaRawElmProvider::GetPropertyValue(PROPERTYID aPropertyId,
}
break;
}
+
+ case UIA_PositionInSetPropertyId:
+ aPropertyValue->vt = VT_I4;
+ aPropertyValue->lVal = acc->GroupPosition().posInSet;
+ return S_OK;
+
+ case UIA_SizeOfSetPropertyId:
+ aPropertyValue->vt = VT_I4;
+ aPropertyValue->lVal = acc->GroupPosition().setSize;
+ return S_OK;
}
return S_OK;
@@ -749,6 +970,223 @@ uiaRawElmProvider::get_IsReadOnly(__RPC__out BOOL* aRetVal) {
return S_OK;
}
+// IRangeValueProvider methods
+
+STDMETHODIMP
+uiaRawElmProvider::SetValue(double aVal) {
+ Accessible* acc = Acc();
+ if (!acc) {
+ return CO_E_OBJNOTCONNECTED;
+ }
+ if (!acc->SetCurValue(aVal)) {
+ return UIA_E_INVALIDOPERATION;
+ }
+ return S_OK;
+}
+
+STDMETHODIMP
+uiaRawElmProvider::get_Value(__RPC__out double* aRetVal) {
+ if (!aRetVal) {
+ return E_INVALIDARG;
+ }
+ Accessible* acc = Acc();
+ if (!acc) {
+ return CO_E_OBJNOTCONNECTED;
+ }
+ *aRetVal = acc->CurValue();
+ return S_OK;
+}
+
+STDMETHODIMP
+uiaRawElmProvider::get_Maximum(__RPC__out double* aRetVal) {
+ if (!aRetVal) {
+ return E_INVALIDARG;
+ }
+ Accessible* acc = Acc();
+ if (!acc) {
+ return CO_E_OBJNOTCONNECTED;
+ }
+ *aRetVal = acc->MaxValue();
+ return S_OK;
+}
+
+STDMETHODIMP
+uiaRawElmProvider::get_Minimum(
+ /* [retval][out] */ __RPC__out double* aRetVal) {
+ if (!aRetVal) {
+ return E_INVALIDARG;
+ }
+ Accessible* acc = Acc();
+ if (!acc) {
+ return CO_E_OBJNOTCONNECTED;
+ }
+ *aRetVal = acc->MinValue();
+ return S_OK;
+}
+
+STDMETHODIMP
+uiaRawElmProvider::get_LargeChange(
+ /* [retval][out] */ __RPC__out double* aRetVal) {
+ if (!aRetVal) {
+ return E_INVALIDARG;
+ }
+ Accessible* acc = Acc();
+ if (!acc) {
+ return CO_E_OBJNOTCONNECTED;
+ }
+ // We want the change that would occur if the user pressed page up or page
+ // down. For HTML input elements, this is 10% of the total range unless step
+ // is larger. See:
+ // https://searchfox.org/mozilla-central/rev/c7df16ffad1f12a19c81c16bce0b65e4a15304d0/dom/html/HTMLInputElement.cpp#3878
+ double step = acc->Step();
+ double min = acc->MinValue();
+ double max = acc->MaxValue();
+ if (std::isnan(step) || std::isnan(min) || std::isnan(max)) {
+ *aRetVal = UnspecifiedNaN<double>();
+ } else {
+ *aRetVal = std::max(step, (max - min) / 10);
+ }
+ return S_OK;
+}
+
+STDMETHODIMP
+uiaRawElmProvider::get_SmallChange(
+ /* [retval][out] */ __RPC__out double* aRetVal) {
+ if (!aRetVal) {
+ return E_INVALIDARG;
+ }
+ Accessible* acc = Acc();
+ if (!acc) {
+ return CO_E_OBJNOTCONNECTED;
+ }
+ *aRetVal = acc->Step();
+ return S_OK;
+}
+
+// ISelectionProvider methods
+
+STDMETHODIMP
+uiaRawElmProvider::GetSelection(__RPC__deref_out_opt SAFEARRAY** aRetVal) {
+ if (!aRetVal) {
+ return E_INVALIDARG;
+ }
+ *aRetVal = nullptr;
+ Accessible* acc = Acc();
+ if (!acc) {
+ return CO_E_OBJNOTCONNECTED;
+ }
+ AutoTArray<Accessible*, 10> items;
+ acc->SelectedItems(&items);
+ *aRetVal = AccessibleArrayToUiaArray(items);
+ return S_OK;
+}
+
+STDMETHODIMP
+uiaRawElmProvider::get_CanSelectMultiple(__RPC__out BOOL* aRetVal) {
+ if (!aRetVal) {
+ return E_INVALIDARG;
+ }
+ Accessible* acc = Acc();
+ if (!acc) {
+ return CO_E_OBJNOTCONNECTED;
+ }
+ *aRetVal = acc->State() & states::MULTISELECTABLE;
+ return S_OK;
+}
+
+STDMETHODIMP
+uiaRawElmProvider::get_IsSelectionRequired(__RPC__out BOOL* aRetVal) {
+ if (!aRetVal) {
+ return E_INVALIDARG;
+ }
+ Accessible* acc = Acc();
+ if (!acc) {
+ return CO_E_OBJNOTCONNECTED;
+ }
+ *aRetVal = acc->State() & states::REQUIRED;
+ return S_OK;
+}
+
+// ISelectionItemProvider methods
+
+STDMETHODIMP
+uiaRawElmProvider::Select() {
+ Accessible* acc = Acc();
+ if (!acc) {
+ return CO_E_OBJNOTCONNECTED;
+ }
+ if (IsRadio(acc)) {
+ acc->DoAction(0);
+ } else {
+ acc->TakeSelection();
+ }
+ return S_OK;
+}
+
+STDMETHODIMP
+uiaRawElmProvider::AddToSelection() {
+ Accessible* acc = Acc();
+ if (!acc) {
+ return CO_E_OBJNOTCONNECTED;
+ }
+ if (IsRadio(acc)) {
+ acc->DoAction(0);
+ } else {
+ acc->SetSelected(true);
+ }
+ return S_OK;
+}
+
+STDMETHODIMP
+uiaRawElmProvider::RemoveFromSelection() {
+ Accessible* acc = Acc();
+ if (!acc) {
+ return CO_E_OBJNOTCONNECTED;
+ }
+ if (IsRadio(acc)) {
+ return UIA_E_INVALIDOPERATION;
+ }
+ acc->SetSelected(false);
+ return S_OK;
+}
+
+STDMETHODIMP
+uiaRawElmProvider::get_IsSelected(__RPC__out BOOL* aRetVal) {
+ if (!aRetVal) {
+ return E_INVALIDARG;
+ }
+ Accessible* acc = Acc();
+ if (!acc) {
+ return CO_E_OBJNOTCONNECTED;
+ }
+ if (IsRadio(acc)) {
+ *aRetVal = acc->State() & states::CHECKED;
+ } else {
+ *aRetVal = acc->State() & states::SELECTED;
+ }
+ return S_OK;
+}
+
+STDMETHODIMP
+uiaRawElmProvider::get_SelectionContainer(
+ __RPC__deref_out_opt IRawElementProviderSimple** aRetVal) {
+ if (!aRetVal) {
+ return E_INVALIDARG;
+ }
+ *aRetVal = nullptr;
+ Accessible* acc = Acc();
+ if (!acc) {
+ return CO_E_OBJNOTCONNECTED;
+ }
+ Accessible* container = nsAccUtils::GetSelectableContainer(acc, acc->State());
+ if (!container) {
+ return E_FAIL;
+ }
+ RefPtr<IRawElementProviderSimple> uia = MsaaAccessible::GetFrom(container);
+ uia.forget(aRetVal);
+ return S_OK;
+}
+
// Private methods
bool uiaRawElmProvider::IsControl() {
@@ -854,3 +1292,133 @@ bool uiaRawElmProvider::HasValuePattern() const {
const nsRoleMapEntry* roleMapEntry = acc->ARIARoleMap();
return roleMapEntry && roleMapEntry->Is(nsGkAtoms::textbox);
}
+
+template <class Derived, class Interface>
+RefPtr<Interface> uiaRawElmProvider::GetPatternFromDerived() {
+ // MsaaAccessible inherits from uiaRawElmProvider. Derived
+ // inherits from MsaaAccessible and Interface. The compiler won't let us
+ // directly static_cast to Interface, hence the intermediate casts.
+ auto* msaa = static_cast<MsaaAccessible*>(this);
+ auto* derived = static_cast<Derived*>(msaa);
+ return derived;
+}
+
+bool uiaRawElmProvider::HasSelectionItemPattern() {
+ Accessible* acc = Acc();
+ MOZ_ASSERT(acc);
+ // In UIA, radio buttons and radio menu items are exposed as selected or
+ // unselected.
+ return acc->State() & states::SELECTABLE || IsRadio(acc);
+}
+
+SAFEARRAY* uiaRawElmProvider::AccRelationsToUiaArray(
+ std::initializer_list<RelationType> aTypes) const {
+ Accessible* acc = Acc();
+ MOZ_ASSERT(acc);
+ AutoTArray<Accessible*, 10> targets;
+ for (RelationType type : aTypes) {
+ Relation rel = acc->RelationByType(type);
+ while (Accessible* target = rel.Next()) {
+ targets.AppendElement(target);
+ }
+ }
+ return AccessibleArrayToUiaArray(targets);
+}
+
+Accessible* uiaRawElmProvider::GetLabeledBy() const {
+ // Per the UIA documentation, some control types should never get a value for
+ // the LabeledBy property.
+ switch (GetControlType()) {
+ case UIA_ButtonControlTypeId:
+ case UIA_CheckBoxControlTypeId:
+ case UIA_DataItemControlTypeId:
+ case UIA_MenuControlTypeId:
+ case UIA_MenuBarControlTypeId:
+ case UIA_RadioButtonControlTypeId:
+ case UIA_ScrollBarControlTypeId:
+ case UIA_SeparatorControlTypeId:
+ case UIA_StatusBarControlTypeId:
+ case UIA_TabItemControlTypeId:
+ case UIA_TextControlTypeId:
+ case UIA_ToolBarControlTypeId:
+ case UIA_ToolTipControlTypeId:
+ case UIA_TreeItemControlTypeId:
+ return nullptr;
+ }
+
+ Accessible* acc = Acc();
+ MOZ_ASSERT(acc);
+ // Even when LabeledBy is supported, it can only return a single "static text"
+ // element.
+ Relation rel = acc->RelationByType(RelationType::LABELLED_BY);
+ LabelTextLeafRule rule;
+ while (Accessible* target = rel.Next()) {
+ // If target were a text leaf, we should return that, but that shouldn't be
+ // possible because only an element (not a text node) can be the target of a
+ // relation.
+ MOZ_ASSERT(!target->IsTextLeaf());
+ Pivot pivot(target);
+ if (Accessible* leaf = pivot.Next(target, rule)) {
+ return leaf;
+ }
+ }
+ return nullptr;
+}
+
+long uiaRawElmProvider::GetLandmarkType() const {
+ Accessible* acc = Acc();
+ MOZ_ASSERT(acc);
+ nsStaticAtom* landmark = acc->LandmarkRole();
+ if (!landmark) {
+ return 0;
+ }
+ if (landmark == nsGkAtoms::form) {
+ return UIA_FormLandmarkTypeId;
+ }
+ if (landmark == nsGkAtoms::main) {
+ return UIA_MainLandmarkTypeId;
+ }
+ if (landmark == nsGkAtoms::navigation) {
+ return UIA_NavigationLandmarkTypeId;
+ }
+ if (landmark == nsGkAtoms::search) {
+ return UIA_SearchLandmarkTypeId;
+ }
+ return UIA_CustomLandmarkTypeId;
+}
+
+void uiaRawElmProvider::GetLocalizedLandmarkType(nsAString& aLocalized) const {
+ Accessible* acc = Acc();
+ MOZ_ASSERT(acc);
+ nsStaticAtom* landmark = acc->LandmarkRole();
+ // The system provides strings for landmarks explicitly supported by the UIA
+ // LandmarkType property; i.e. form, main, navigation and search. We must
+ // provide strings for landmarks considered custom by UIA. For now, we only
+ // support landmarks in the core ARIA specification, not other ARIA modules
+ // such as DPub.
+ if (landmark == nsGkAtoms::banner || landmark == nsGkAtoms::complementary ||
+ landmark == nsGkAtoms::contentinfo || landmark == nsGkAtoms::region) {
+ nsAutoString unlocalized;
+ landmark->ToString(unlocalized);
+ Accessible::TranslateString(unlocalized, aLocalized);
+ }
+}
+
+SAFEARRAY* a11y::AccessibleArrayToUiaArray(const nsTArray<Accessible*>& aAccs) {
+ if (aAccs.IsEmpty()) {
+ // The UIA documentation is unclear about this, but the UIA client
+ // framework seems to treat a null value the same as an empty array. This
+ // is also what Chromium does.
+ return nullptr;
+ }
+ SAFEARRAY* uias = SafeArrayCreateVector(VT_UNKNOWN, 0, aAccs.Length());
+ LONG indices[1] = {0};
+ for (Accessible* acc : aAccs) {
+ // SafeArrayPutElement calls AddRef on the element, so we use a raw pointer
+ // here.
+ IRawElementProviderSimple* uia = MsaaAccessible::GetFrom(acc);
+ SafeArrayPutElement(uias, indices, uia);
+ ++indices[0];
+ }
+ return uias;
+}
diff --git a/accessible/windows/uia/uiaRawElmProvider.h b/accessible/windows/uia/uiaRawElmProvider.h
index 0e05d1a030..ea713d8bc1 100644
--- a/accessible/windows/uia/uiaRawElmProvider.h
+++ b/accessible/windows/uia/uiaRawElmProvider.h
@@ -11,10 +11,20 @@
#include <stdint.h>
#include <uiautomation.h>
+#include <initializer_list>
+
+#include "nsString.h"
+
+template <class T>
+class nsTArray;
+template <class T>
+class RefPtr;
+
namespace mozilla {
namespace a11y {
class Accessible;
+enum class RelationType;
/**
* IRawElementProviderSimple implementation (maintains IAccessibleEx approach).
@@ -26,8 +36,16 @@ class uiaRawElmProvider : public IAccessibleEx,
public IToggleProvider,
public IExpandCollapseProvider,
public IScrollItemProvider,
- public IValueProvider {
+ public IValueProvider,
+ public IRangeValueProvider,
+ public ISelectionProvider,
+ public ISelectionItemProvider {
public:
+ static constexpr enum ProviderOptions kProviderOptions =
+ static_cast<enum ProviderOptions>(ProviderOptions_ServerSideProvider |
+ ProviderOptions_UseComThreading |
+ ProviderOptions_HasNativeIAccessible);
+
static void RaiseUiaEventForGeckoEvent(Accessible* aAcc,
uint32_t aGeckoEvent);
static void RaiseUiaEventForStateChange(Accessible* aAcc, uint64_t aState,
@@ -118,6 +136,51 @@ class uiaRawElmProvider : public IAccessibleEx,
virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_IsReadOnly(
/* [retval][out] */ __RPC__out BOOL* pRetVal);
+ // IRangeValueProvider
+ virtual HRESULT STDMETHODCALLTYPE SetValue(
+ /* [in] */ double aVal);
+
+ virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_Value(
+ /* [retval][out] */ __RPC__out double* aRetVal);
+
+ // get_IsReadOnly is shared with IValueProvider.
+
+ virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_Maximum(
+ /* [retval][out] */ __RPC__out double* aRetVal);
+
+ virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_Minimum(
+ /* [retval][out] */ __RPC__out double* aRetVal);
+
+ virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_LargeChange(
+ /* [retval][out] */ __RPC__out double* aRetVal);
+
+ virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_SmallChange(
+ /* [retval][out] */ __RPC__out double* aRetVal);
+
+ // ISelectionProvider
+ virtual HRESULT STDMETHODCALLTYPE GetSelection(
+ /* [retval][out] */ __RPC__deref_out_opt SAFEARRAY** aRetVal);
+
+ virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_CanSelectMultiple(
+ /* [retval][out] */ __RPC__out BOOL* aRetVal);
+
+ virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_IsSelectionRequired(
+ /* [retval][out] */ __RPC__out BOOL* aRetVal);
+
+ // ISelectionItemProvider methods
+ virtual HRESULT STDMETHODCALLTYPE Select(void);
+
+ virtual HRESULT STDMETHODCALLTYPE AddToSelection(void);
+
+ virtual HRESULT STDMETHODCALLTYPE RemoveFromSelection(void);
+
+ virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_IsSelected(
+ /* [retval][out] */ __RPC__out BOOL* aRetVal);
+
+ virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_SelectionContainer(
+ /* [retval][out] */ __RPC__deref_out_opt IRawElementProviderSimple**
+ aRetVal);
+
private:
Accessible* Acc() const;
bool IsControl();
@@ -125,8 +188,18 @@ class uiaRawElmProvider : public IAccessibleEx,
bool HasTogglePattern();
bool HasExpandCollapsePattern();
bool HasValuePattern() const;
+ template <class Derived, class Interface>
+ RefPtr<Interface> GetPatternFromDerived();
+ bool HasSelectionItemPattern();
+ SAFEARRAY* AccRelationsToUiaArray(
+ std::initializer_list<RelationType> aTypes) const;
+ Accessible* GetLabeledBy() const;
+ long GetLandmarkType() const;
+ void GetLocalizedLandmarkType(nsAString& aLocalized) const;
};
+SAFEARRAY* AccessibleArrayToUiaArray(const nsTArray<Accessible*>& aAccs);
+
} // namespace a11y
} // namespace mozilla