ia2AccessibleAction.cpp (6061B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim:expandtab:shiftwidth=2:tabstop=2: 3 */ 4 /* This Source Code Form is subject to the terms of the Mozilla Public 5 * License, v. 2.0. If a copy of the MPL was not distributed with this 6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 7 8 #include "ia2AccessibleAction.h" 9 10 #include "AccessibleAction_i.c" 11 12 #include "AccessibleWrap.h" 13 #include "IUnknownImpl.h" 14 #include "MsaaAccessible.h" 15 #include "Relation.h" 16 17 using namespace mozilla::a11y; 18 19 Accessible* ia2AccessibleAction::Acc() { 20 return static_cast<MsaaAccessible*>(this)->Acc(); 21 } 22 23 // IUnknown 24 25 STDMETHODIMP 26 ia2AccessibleAction::QueryInterface(REFIID iid, void** ppv) { 27 if (!ppv) return E_INVALIDARG; 28 29 *ppv = nullptr; 30 31 if (IID_IAccessibleAction == iid) { 32 *ppv = static_cast<IAccessibleAction*>(this); 33 (reinterpret_cast<IUnknown*>(*ppv))->AddRef(); 34 return S_OK; 35 } 36 37 return E_NOINTERFACE; 38 } 39 40 // IAccessibleAction 41 42 STDMETHODIMP 43 ia2AccessibleAction::nActions(long* aActionCount) { 44 if (!aActionCount) return E_INVALIDARG; 45 46 *aActionCount = 0; 47 48 Accessible* acc = Acc(); 49 if (!acc) return CO_E_OBJNOTCONNECTED; 50 51 *aActionCount = acc->ActionCount(); 52 Relation customActions(acc->RelationByType(RelationType::ACTION)); 53 while (Accessible* target = customActions.Next()) { 54 if (target->HasPrimaryAction()) { 55 (*aActionCount)++; 56 } 57 } 58 59 return S_OK; 60 } 61 62 STDMETHODIMP 63 ia2AccessibleAction::doAction(long aActionIndex) { 64 Accessible* acc = Acc(); 65 if (!acc) return CO_E_OBJNOTCONNECTED; 66 67 uint8_t index = static_cast<uint8_t>(aActionIndex); 68 69 if (index < acc->ActionCount()) { 70 DebugOnly<bool> success = acc->DoAction(aActionIndex); 71 MOZ_ASSERT(success, "Failed to perform action"); 72 return S_OK; 73 } 74 75 // Check for custom actions. 76 Relation customActions(acc->RelationByType(RelationType::ACTION)); 77 uint8_t actionIndex = acc->ActionCount(); 78 while (Accessible* target = customActions.Next()) { 79 if (target->HasPrimaryAction()) { 80 MOZ_ASSERT(target->ActionCount() > 0); 81 if (actionIndex == index) { 82 DebugOnly<bool> success = target->DoAction(0); 83 MOZ_ASSERT(success, "Failed to perform action"); 84 return S_OK; 85 } 86 actionIndex++; 87 } 88 } 89 90 return E_INVALIDARG; 91 } 92 93 STDMETHODIMP 94 ia2AccessibleAction::get_description(long aActionIndex, BSTR* aDescription) { 95 return get_localizedName(aActionIndex, aDescription); 96 } 97 98 STDMETHODIMP 99 ia2AccessibleAction::get_keyBinding(long aActionIndex, long aNumMaxBinding, 100 BSTR** aKeyBinding, long* aNumBinding) { 101 if (!aKeyBinding) return E_INVALIDARG; 102 *aKeyBinding = nullptr; 103 104 if (!aNumBinding) return E_INVALIDARG; 105 *aNumBinding = 0; 106 107 if (aActionIndex != 0 || aNumMaxBinding < 1) return E_INVALIDARG; 108 109 Accessible* acc = Acc(); 110 if (!acc) return CO_E_OBJNOTCONNECTED; 111 112 // Expose KeyboardShortcut if it's not exposed via MSAA accKeyboardShortcut. 113 LocalAccessible* localAcc = acc->AsLocal(); 114 if (!localAcc) { 115 // RemoteAccessibles can't have a KeyboardShortcut. 116 return S_FALSE; 117 } 118 119 KeyBinding keyBinding = acc->AccessKey(); 120 if (keyBinding.IsEmpty()) { 121 // In this case, KeyboardShortcut will be exposed via MSAA 122 // accKeyboardShortcut. 123 return S_FALSE; 124 } 125 126 // MSAA accKeyboardShortcut will expose AccessKey. 127 keyBinding = localAcc->KeyboardShortcut(); 128 if (keyBinding.IsEmpty()) return S_FALSE; 129 130 nsAutoString keyStr; 131 keyBinding.ToString(keyStr); 132 133 *aKeyBinding = static_cast<BSTR*>(::CoTaskMemAlloc(sizeof(BSTR*))); 134 if (!*aKeyBinding) return E_OUTOFMEMORY; 135 136 *(aKeyBinding[0]) = ::SysAllocStringLen(keyStr.get(), keyStr.Length()); 137 if (!*(aKeyBinding[0])) { 138 ::CoTaskMemFree(*aKeyBinding); 139 return E_OUTOFMEMORY; 140 } 141 142 *aNumBinding = 1; 143 return S_OK; 144 } 145 146 STDMETHODIMP 147 ia2AccessibleAction::get_name(long aActionIndex, BSTR* aName) { 148 if (!aName) return E_INVALIDARG; 149 150 *aName = nullptr; 151 152 Accessible* acc = Acc(); 153 if (!acc) return CO_E_OBJNOTCONNECTED; 154 155 nsAutoString name; 156 uint8_t index = static_cast<uint8_t>(aActionIndex); 157 if (index < acc->ActionCount()) { 158 acc->ActionNameAt(aActionIndex, name); 159 } else { 160 // Check for custom actions. 161 Relation customActions(acc->RelationByType(RelationType::ACTION)); 162 uint8_t actionIndex = acc->ActionCount(); 163 while (Accessible* target = customActions.Next()) { 164 if (target->HasPrimaryAction()) { 165 MOZ_ASSERT(target->ActionCount() > 0); 166 if (actionIndex == index) { 167 name.AssignLiteral("custom"); 168 nsAutoString domNodeId; 169 target->DOMNodeID(domNodeId); 170 if (!domNodeId.IsEmpty()) { 171 name.AppendPrintf("_%s", NS_ConvertUTF16toUTF8(domNodeId).get()); 172 } 173 break; 174 } 175 actionIndex++; 176 } 177 } 178 } 179 180 if (name.IsEmpty()) return E_INVALIDARG; 181 182 *aName = ::SysAllocStringLen(name.get(), name.Length()); 183 return *aName ? S_OK : E_OUTOFMEMORY; 184 } 185 186 STDMETHODIMP 187 ia2AccessibleAction::get_localizedName(long aActionIndex, 188 BSTR* aLocalizedName) { 189 if (!aLocalizedName) return E_INVALIDARG; 190 *aLocalizedName = nullptr; 191 192 Accessible* acc = Acc(); 193 if (!acc) return CO_E_OBJNOTCONNECTED; 194 195 nsAutoString description; 196 uint8_t index = static_cast<uint8_t>(aActionIndex); 197 198 if (aActionIndex < acc->ActionCount()) { 199 acc->ActionDescriptionAt(index, description); 200 } else { 201 // Check for custom actions. 202 Relation customActions(acc->RelationByType(RelationType::ACTION)); 203 uint8_t actionIndex = acc->ActionCount(); 204 while (Accessible* target = customActions.Next()) { 205 if (target->HasPrimaryAction()) { 206 MOZ_ASSERT(target->ActionCount() > 0); 207 if (actionIndex == index) { 208 target->Name(description); 209 break; 210 } 211 actionIndex++; 212 } 213 } 214 } 215 216 if (description.IsEmpty()) return S_FALSE; 217 218 *aLocalizedName = 219 ::SysAllocStringLen(description.get(), description.Length()); 220 return *aLocalizedName ? S_OK : E_OUTOFMEMORY; 221 }