diff options
Diffstat (limited to 'accessible/windows')
-rw-r--r-- | accessible/windows/ia2/ia2Accessible.cpp | 3 | ||||
-rw-r--r-- | accessible/windows/msaa/MsaaAccessible.cpp | 12 | ||||
-rw-r--r-- | accessible/windows/msaa/MsaaAccessible.h | 6 | ||||
-rw-r--r-- | accessible/windows/msaa/MsaaRootAccessible.cpp | 8 | ||||
-rw-r--r-- | accessible/windows/msaa/MsaaRootAccessible.h | 4 | ||||
-rw-r--r-- | accessible/windows/msaa/Platform.cpp | 7 | ||||
-rw-r--r-- | accessible/windows/uia/UiaRoot.cpp | 61 | ||||
-rw-r--r-- | accessible/windows/uia/UiaRoot.h | 40 | ||||
-rw-r--r-- | accessible/windows/uia/moz.build | 1 | ||||
-rw-r--r-- | accessible/windows/uia/uiaRawElmProvider.cpp | 576 | ||||
-rw-r--r-- | accessible/windows/uia/uiaRawElmProvider.h | 79 |
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 |