/* 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 "ia2AccessibleAction.h" #include "AccessibleAction_i.c" #include "AccessibleWrap.h" #include "IUnknownImpl.h" #include "MsaaAccessible.h" #include "Relation.h" using namespace mozilla::a11y; Accessible* ia2AccessibleAction::Acc() { return static_cast(this)->Acc(); } // IUnknown STDMETHODIMP ia2AccessibleAction::QueryInterface(REFIID iid, void** ppv) { if (!ppv) return E_INVALIDARG; *ppv = nullptr; if (IID_IAccessibleAction == iid) { *ppv = static_cast(this); (reinterpret_cast(*ppv))->AddRef(); return S_OK; } return E_NOINTERFACE; } // IAccessibleAction STDMETHODIMP ia2AccessibleAction::nActions(long* aActionCount) { if (!aActionCount) return E_INVALIDARG; *aActionCount = 0; Accessible* acc = Acc(); if (!acc) return CO_E_OBJNOTCONNECTED; *aActionCount = acc->ActionCount(); Relation customActions(acc->RelationByType(RelationType::ACTION)); while (Accessible* target = customActions.Next()) { if (target->HasPrimaryAction()) { (*aActionCount)++; } } return S_OK; } STDMETHODIMP ia2AccessibleAction::doAction(long aActionIndex) { Accessible* acc = Acc(); if (!acc) return CO_E_OBJNOTCONNECTED; uint8_t index = static_cast(aActionIndex); if (index < acc->ActionCount()) { DebugOnly success = acc->DoAction(aActionIndex); MOZ_ASSERT(success, "Failed to perform action"); return S_OK; } // Check for custom actions. Relation customActions(acc->RelationByType(RelationType::ACTION)); uint8_t actionIndex = acc->ActionCount(); while (Accessible* target = customActions.Next()) { if (target->HasPrimaryAction()) { MOZ_ASSERT(target->ActionCount() > 0); if (actionIndex == index) { DebugOnly success = target->DoAction(0); MOZ_ASSERT(success, "Failed to perform action"); return S_OK; } actionIndex++; } } return E_INVALIDARG; } STDMETHODIMP ia2AccessibleAction::get_description(long aActionIndex, BSTR* aDescription) { return get_localizedName(aActionIndex, aDescription); } STDMETHODIMP ia2AccessibleAction::get_keyBinding(long aActionIndex, long aNumMaxBinding, BSTR** aKeyBinding, long* aNumBinding) { if (!aKeyBinding) return E_INVALIDARG; *aKeyBinding = nullptr; if (!aNumBinding) return E_INVALIDARG; *aNumBinding = 0; if (aActionIndex != 0 || aNumMaxBinding < 1) return E_INVALIDARG; Accessible* acc = Acc(); if (!acc) return CO_E_OBJNOTCONNECTED; // Expose KeyboardShortcut if it's not exposed via MSAA accKeyboardShortcut. LocalAccessible* localAcc = acc->AsLocal(); if (!localAcc) { // RemoteAccessibles can't have a KeyboardShortcut. return S_FALSE; } KeyBinding keyBinding = acc->AccessKey(); if (keyBinding.IsEmpty()) { // In this case, KeyboardShortcut will be exposed via MSAA // accKeyboardShortcut. return S_FALSE; } // MSAA accKeyboardShortcut will expose AccessKey. keyBinding = localAcc->KeyboardShortcut(); if (keyBinding.IsEmpty()) return S_FALSE; nsAutoString keyStr; keyBinding.ToString(keyStr); *aKeyBinding = static_cast(::CoTaskMemAlloc(sizeof(BSTR*))); if (!*aKeyBinding) return E_OUTOFMEMORY; *(aKeyBinding[0]) = ::SysAllocStringLen(keyStr.get(), keyStr.Length()); if (!*(aKeyBinding[0])) { ::CoTaskMemFree(*aKeyBinding); return E_OUTOFMEMORY; } *aNumBinding = 1; return S_OK; } STDMETHODIMP ia2AccessibleAction::get_name(long aActionIndex, BSTR* aName) { if (!aName) return E_INVALIDARG; *aName = nullptr; Accessible* acc = Acc(); if (!acc) return CO_E_OBJNOTCONNECTED; nsAutoString name; uint8_t index = static_cast(aActionIndex); if (index < acc->ActionCount()) { acc->ActionNameAt(aActionIndex, name); } else { // Check for custom actions. Relation customActions(acc->RelationByType(RelationType::ACTION)); uint8_t actionIndex = acc->ActionCount(); while (Accessible* target = customActions.Next()) { if (target->HasPrimaryAction()) { MOZ_ASSERT(target->ActionCount() > 0); if (actionIndex == index) { name.AssignLiteral("custom"); nsAutoString domNodeId; target->DOMNodeID(domNodeId); if (!domNodeId.IsEmpty()) { name.AppendPrintf("_%s", NS_ConvertUTF16toUTF8(domNodeId).get()); } break; } actionIndex++; } } } if (name.IsEmpty()) return E_INVALIDARG; *aName = ::SysAllocStringLen(name.get(), name.Length()); return *aName ? S_OK : E_OUTOFMEMORY; } STDMETHODIMP ia2AccessibleAction::get_localizedName(long aActionIndex, BSTR* aLocalizedName) { if (!aLocalizedName) return E_INVALIDARG; *aLocalizedName = nullptr; Accessible* acc = Acc(); if (!acc) return CO_E_OBJNOTCONNECTED; nsAutoString description; uint8_t index = static_cast(aActionIndex); if (aActionIndex < acc->ActionCount()) { acc->ActionDescriptionAt(index, description); } else { // Check for custom actions. Relation customActions(acc->RelationByType(RelationType::ACTION)); uint8_t actionIndex = acc->ActionCount(); while (Accessible* target = customActions.Next()) { if (target->HasPrimaryAction()) { MOZ_ASSERT(target->ActionCount() > 0); if (actionIndex == index) { target->Name(description); break; } actionIndex++; } } } if (description.IsEmpty()) return S_FALSE; *aLocalizedName = ::SysAllocStringLen(description.get(), description.Length()); return *aLocalizedName ? S_OK : E_OUTOFMEMORY; }