accessible/src/windows/msaa/AccessibleWrap.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* vim: set ts=2 et sw=2 tw=80: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include "AccessibleWrap.h"
michael@0 8 #include "Accessible-inl.h"
michael@0 9
michael@0 10 #include "Compatibility.h"
michael@0 11 #include "DocAccessible-inl.h"
michael@0 12 #include "EnumVariant.h"
michael@0 13 #include "nsAccUtils.h"
michael@0 14 #include "nsCoreUtils.h"
michael@0 15 #include "nsIAccessibleEvent.h"
michael@0 16 #include "nsIAccessibleRelation.h"
michael@0 17 #include "nsWinUtils.h"
michael@0 18 #include "ServiceProvider.h"
michael@0 19 #include "Relation.h"
michael@0 20 #include "Role.h"
michael@0 21 #include "RootAccessible.h"
michael@0 22 #include "sdnAccessible.h"
michael@0 23 #include "States.h"
michael@0 24
michael@0 25 #ifdef A11Y_LOG
michael@0 26 #include "Logging.h"
michael@0 27 #endif
michael@0 28
michael@0 29 #include "nsIMutableArray.h"
michael@0 30 #include "nsIFrame.h"
michael@0 31 #include "nsIScrollableFrame.h"
michael@0 32 #include "nsINodeInfo.h"
michael@0 33 #include "nsIServiceManager.h"
michael@0 34 #include "nsNameSpaceManager.h"
michael@0 35 #include "nsTextFormatter.h"
michael@0 36 #include "nsView.h"
michael@0 37 #include "nsViewManager.h"
michael@0 38 #include "nsEventMap.h"
michael@0 39 #include "nsArrayUtils.h"
michael@0 40 #include "mozilla/Preferences.h"
michael@0 41
michael@0 42 #include "oleacc.h"
michael@0 43
michael@0 44 using namespace mozilla;
michael@0 45 using namespace mozilla::a11y;
michael@0 46
michael@0 47 const uint32_t USE_ROLE_STRING = 0;
michael@0 48
michael@0 49 /* For documentation of the accessibility architecture,
michael@0 50 * see http://lxr.mozilla.org/seamonkey/source/accessible/accessible-docs.html
michael@0 51 */
michael@0 52
michael@0 53 //#define DEBUG_LEAKS
michael@0 54
michael@0 55 #ifdef DEBUG_LEAKS
michael@0 56 static gAccessibles = 0;
michael@0 57 #endif
michael@0 58
michael@0 59 static const int32_t kIEnumVariantDisconnected = -1;
michael@0 60
michael@0 61 ////////////////////////////////////////////////////////////////////////////////
michael@0 62 // AccessibleWrap
michael@0 63 ////////////////////////////////////////////////////////////////////////////////
michael@0 64
michael@0 65 ITypeInfo* AccessibleWrap::gTypeInfo = nullptr;
michael@0 66
michael@0 67 NS_IMPL_ISUPPORTS_INHERITED0(AccessibleWrap, Accessible)
michael@0 68
michael@0 69 //-----------------------------------------------------
michael@0 70 // IUnknown interface methods - see iunknown.h for documentation
michael@0 71 //-----------------------------------------------------
michael@0 72
michael@0 73 // Microsoft COM QueryInterface
michael@0 74 STDMETHODIMP
michael@0 75 AccessibleWrap::QueryInterface(REFIID iid, void** ppv)
michael@0 76 {
michael@0 77 A11Y_TRYBLOCK_BEGIN
michael@0 78
michael@0 79 if (!ppv)
michael@0 80 return E_INVALIDARG;
michael@0 81
michael@0 82 *ppv = nullptr;
michael@0 83
michael@0 84 if (IID_IUnknown == iid || IID_IDispatch == iid || IID_IAccessible == iid)
michael@0 85 *ppv = static_cast<IAccessible*>(this);
michael@0 86 else if (IID_IEnumVARIANT == iid) {
michael@0 87 // Don't support this interface for leaf elements.
michael@0 88 if (!HasChildren() || nsAccUtils::MustPrune(this))
michael@0 89 return E_NOINTERFACE;
michael@0 90
michael@0 91 *ppv = static_cast<IEnumVARIANT*>(new ChildrenEnumVariant(this));
michael@0 92 } else if (IID_IServiceProvider == iid)
michael@0 93 *ppv = new ServiceProvider(this);
michael@0 94 else if (IID_ISimpleDOMNode == iid) {
michael@0 95 if (IsDefunct() || (!HasOwnContent() && !IsDoc()))
michael@0 96 return E_NOINTERFACE;
michael@0 97
michael@0 98 *ppv = static_cast<ISimpleDOMNode*>(new sdnAccessible(GetNode()));
michael@0 99 }
michael@0 100
michael@0 101 if (nullptr == *ppv) {
michael@0 102 HRESULT hr = ia2Accessible::QueryInterface(iid, ppv);
michael@0 103 if (SUCCEEDED(hr))
michael@0 104 return hr;
michael@0 105 }
michael@0 106
michael@0 107 if (nullptr == *ppv) {
michael@0 108 HRESULT hr = ia2AccessibleComponent::QueryInterface(iid, ppv);
michael@0 109 if (SUCCEEDED(hr))
michael@0 110 return hr;
michael@0 111 }
michael@0 112
michael@0 113 if (nullptr == *ppv) {
michael@0 114 HRESULT hr = ia2AccessibleHyperlink::QueryInterface(iid, ppv);
michael@0 115 if (SUCCEEDED(hr))
michael@0 116 return hr;
michael@0 117 }
michael@0 118
michael@0 119 if (nullptr == *ppv) {
michael@0 120 HRESULT hr = ia2AccessibleValue::QueryInterface(iid, ppv);
michael@0 121 if (SUCCEEDED(hr))
michael@0 122 return hr;
michael@0 123 }
michael@0 124
michael@0 125 if (nullptr == *ppv)
michael@0 126 return E_NOINTERFACE;
michael@0 127
michael@0 128 (reinterpret_cast<IUnknown*>(*ppv))->AddRef();
michael@0 129 return S_OK;
michael@0 130
michael@0 131 A11Y_TRYBLOCK_END
michael@0 132 }
michael@0 133
michael@0 134 //-----------------------------------------------------
michael@0 135 // IAccessible methods
michael@0 136 //-----------------------------------------------------
michael@0 137
michael@0 138 STDMETHODIMP
michael@0 139 AccessibleWrap::get_accParent( IDispatch __RPC_FAR *__RPC_FAR *ppdispParent)
michael@0 140 {
michael@0 141 A11Y_TRYBLOCK_BEGIN
michael@0 142
michael@0 143 if (!ppdispParent)
michael@0 144 return E_INVALIDARG;
michael@0 145
michael@0 146 *ppdispParent = nullptr;
michael@0 147
michael@0 148 if (IsDefunct())
michael@0 149 return CO_E_OBJNOTCONNECTED;
michael@0 150
michael@0 151 DocAccessible* doc = AsDoc();
michael@0 152 if (doc) {
michael@0 153 // Return window system accessible object for root document and tab document
michael@0 154 // accessibles.
michael@0 155 if (!doc->ParentDocument() ||
michael@0 156 (nsWinUtils::IsWindowEmulationStarted() &&
michael@0 157 nsCoreUtils::IsTabDocument(doc->DocumentNode()))) {
michael@0 158 HWND hwnd = static_cast<HWND>(doc->GetNativeWindow());
michael@0 159 if (hwnd && SUCCEEDED(::AccessibleObjectFromWindow(hwnd, OBJID_WINDOW,
michael@0 160 IID_IAccessible,
michael@0 161 (void**)ppdispParent))) {
michael@0 162 return S_OK;
michael@0 163 }
michael@0 164 }
michael@0 165 }
michael@0 166
michael@0 167 Accessible* xpParentAcc = Parent();
michael@0 168 if (!xpParentAcc)
michael@0 169 return S_FALSE;
michael@0 170
michael@0 171 *ppdispParent = NativeAccessible(xpParentAcc);
michael@0 172 return S_OK;
michael@0 173
michael@0 174 A11Y_TRYBLOCK_END
michael@0 175 }
michael@0 176
michael@0 177 STDMETHODIMP
michael@0 178 AccessibleWrap::get_accChildCount( long __RPC_FAR *pcountChildren)
michael@0 179 {
michael@0 180 A11Y_TRYBLOCK_BEGIN
michael@0 181
michael@0 182 if (!pcountChildren)
michael@0 183 return E_INVALIDARG;
michael@0 184
michael@0 185 *pcountChildren = 0;
michael@0 186
michael@0 187 if (IsDefunct())
michael@0 188 return CO_E_OBJNOTCONNECTED;
michael@0 189
michael@0 190 if (nsAccUtils::MustPrune(this))
michael@0 191 return S_OK;
michael@0 192
michael@0 193 *pcountChildren = ChildCount();
michael@0 194 return S_OK;
michael@0 195
michael@0 196 A11Y_TRYBLOCK_END
michael@0 197 }
michael@0 198
michael@0 199 STDMETHODIMP
michael@0 200 AccessibleWrap::get_accChild(
michael@0 201 /* [in] */ VARIANT varChild,
michael@0 202 /* [retval][out] */ IDispatch __RPC_FAR *__RPC_FAR *ppdispChild)
michael@0 203 {
michael@0 204 A11Y_TRYBLOCK_BEGIN
michael@0 205
michael@0 206 if (!ppdispChild)
michael@0 207 return E_INVALIDARG;
michael@0 208
michael@0 209 *ppdispChild = nullptr;
michael@0 210 if (IsDefunct())
michael@0 211 return CO_E_OBJNOTCONNECTED;
michael@0 212
michael@0 213 // IAccessible::accChild is used to return this accessible or child accessible
michael@0 214 // at the given index or to get an accessible by child ID in the case of
michael@0 215 // document accessible (it's handled by overriden GetXPAccessibleFor method
michael@0 216 // on the document accessible). The getting an accessible by child ID is used
michael@0 217 // by AccessibleObjectFromEvent() called by AT when AT handles our MSAA event.
michael@0 218 Accessible* child = GetXPAccessibleFor(varChild);
michael@0 219 if (!child)
michael@0 220 return E_INVALIDARG;
michael@0 221
michael@0 222 if (child->IsDefunct())
michael@0 223 return CO_E_OBJNOTCONNECTED;
michael@0 224
michael@0 225 *ppdispChild = NativeAccessible(child);
michael@0 226 return S_OK;
michael@0 227
michael@0 228 A11Y_TRYBLOCK_END
michael@0 229 }
michael@0 230
michael@0 231 STDMETHODIMP
michael@0 232 AccessibleWrap::get_accName(
michael@0 233 /* [optional][in] */ VARIANT varChild,
michael@0 234 /* [retval][out] */ BSTR __RPC_FAR *pszName)
michael@0 235 {
michael@0 236 A11Y_TRYBLOCK_BEGIN
michael@0 237
michael@0 238 if (!pszName)
michael@0 239 return E_INVALIDARG;
michael@0 240
michael@0 241 *pszName = nullptr;
michael@0 242
michael@0 243 if (IsDefunct())
michael@0 244 return CO_E_OBJNOTCONNECTED;
michael@0 245
michael@0 246 Accessible* xpAccessible = GetXPAccessibleFor(varChild);
michael@0 247 if (!xpAccessible)
michael@0 248 return E_INVALIDARG;
michael@0 249
michael@0 250 if (xpAccessible->IsDefunct())
michael@0 251 return CO_E_OBJNOTCONNECTED;
michael@0 252
michael@0 253 nsAutoString name;
michael@0 254 xpAccessible->Name(name);
michael@0 255
michael@0 256 // The name was not provided, e.g. no alt attribute for an image. A screen
michael@0 257 // reader may choose to invent its own accessible name, e.g. from an image src
michael@0 258 // attribute. Refer to eNoNameOnPurpose return value.
michael@0 259 if (name.IsVoid())
michael@0 260 return S_FALSE;
michael@0 261
michael@0 262 *pszName = ::SysAllocStringLen(name.get(), name.Length());
michael@0 263 if (!*pszName)
michael@0 264 return E_OUTOFMEMORY;
michael@0 265 return S_OK;
michael@0 266
michael@0 267 A11Y_TRYBLOCK_END
michael@0 268 }
michael@0 269
michael@0 270
michael@0 271 STDMETHODIMP
michael@0 272 AccessibleWrap::get_accValue(
michael@0 273 /* [optional][in] */ VARIANT varChild,
michael@0 274 /* [retval][out] */ BSTR __RPC_FAR *pszValue)
michael@0 275 {
michael@0 276 A11Y_TRYBLOCK_BEGIN
michael@0 277
michael@0 278 if (!pszValue)
michael@0 279 return E_INVALIDARG;
michael@0 280
michael@0 281 *pszValue = nullptr;
michael@0 282
michael@0 283 if (IsDefunct())
michael@0 284 return CO_E_OBJNOTCONNECTED;
michael@0 285
michael@0 286 Accessible* xpAccessible = GetXPAccessibleFor(varChild);
michael@0 287 if (!xpAccessible)
michael@0 288 return E_INVALIDARG;
michael@0 289
michael@0 290 if (xpAccessible->IsDefunct())
michael@0 291 return CO_E_OBJNOTCONNECTED;
michael@0 292
michael@0 293 if (xpAccessible->NativeRole() == roles::PASSWORD_TEXT)
michael@0 294 return E_ACCESSDENIED;
michael@0 295
michael@0 296 nsAutoString value;
michael@0 297 xpAccessible->Value(value);
michael@0 298
michael@0 299 // See bug 438784: need to expose URL on doc's value attribute. For this,
michael@0 300 // reverting part of fix for bug 425693 to make this MSAA method behave
michael@0 301 // IAccessible2-style.
michael@0 302 if (value.IsEmpty())
michael@0 303 return S_FALSE;
michael@0 304
michael@0 305 *pszValue = ::SysAllocStringLen(value.get(), value.Length());
michael@0 306 if (!*pszValue)
michael@0 307 return E_OUTOFMEMORY;
michael@0 308 return S_OK;
michael@0 309
michael@0 310 A11Y_TRYBLOCK_END
michael@0 311 }
michael@0 312
michael@0 313 STDMETHODIMP
michael@0 314 AccessibleWrap::get_accDescription(VARIANT varChild,
michael@0 315 BSTR __RPC_FAR *pszDescription)
michael@0 316 {
michael@0 317 A11Y_TRYBLOCK_BEGIN
michael@0 318
michael@0 319 if (!pszDescription)
michael@0 320 return E_INVALIDARG;
michael@0 321
michael@0 322 *pszDescription = nullptr;
michael@0 323
michael@0 324 if (IsDefunct())
michael@0 325 return CO_E_OBJNOTCONNECTED;
michael@0 326
michael@0 327 Accessible* xpAccessible = GetXPAccessibleFor(varChild);
michael@0 328 if (!xpAccessible)
michael@0 329 return E_INVALIDARG;
michael@0 330
michael@0 331 if (xpAccessible->IsDefunct())
michael@0 332 return CO_E_OBJNOTCONNECTED;
michael@0 333
michael@0 334 nsAutoString description;
michael@0 335 xpAccessible->Description(description);
michael@0 336
michael@0 337 *pszDescription = ::SysAllocStringLen(description.get(),
michael@0 338 description.Length());
michael@0 339 return *pszDescription ? S_OK : E_OUTOFMEMORY;
michael@0 340
michael@0 341 A11Y_TRYBLOCK_END
michael@0 342 }
michael@0 343
michael@0 344 STDMETHODIMP
michael@0 345 AccessibleWrap::get_accRole(
michael@0 346 /* [optional][in] */ VARIANT varChild,
michael@0 347 /* [retval][out] */ VARIANT __RPC_FAR *pvarRole)
michael@0 348 {
michael@0 349 A11Y_TRYBLOCK_BEGIN
michael@0 350
michael@0 351 if (!pvarRole)
michael@0 352 return E_INVALIDARG;
michael@0 353
michael@0 354 VariantInit(pvarRole);
michael@0 355
michael@0 356 if (IsDefunct())
michael@0 357 return CO_E_OBJNOTCONNECTED;
michael@0 358
michael@0 359 Accessible* xpAccessible = GetXPAccessibleFor(varChild);
michael@0 360 if (!xpAccessible)
michael@0 361 return E_INVALIDARG;
michael@0 362
michael@0 363 if (xpAccessible->IsDefunct())
michael@0 364 return CO_E_OBJNOTCONNECTED;
michael@0 365
michael@0 366 #ifdef DEBUG
michael@0 367 NS_ASSERTION(nsAccUtils::IsTextInterfaceSupportCorrect(xpAccessible),
michael@0 368 "Does not support nsIAccessibleText when it should");
michael@0 369 #endif
michael@0 370
michael@0 371 a11y::role geckoRole = xpAccessible->Role();
michael@0 372 uint32_t msaaRole = 0;
michael@0 373
michael@0 374 #define ROLE(_geckoRole, stringRole, atkRole, macRole, \
michael@0 375 _msaaRole, ia2Role, nameRule) \
michael@0 376 case roles::_geckoRole: \
michael@0 377 msaaRole = _msaaRole; \
michael@0 378 break;
michael@0 379
michael@0 380 switch (geckoRole) {
michael@0 381 #include "RoleMap.h"
michael@0 382 default:
michael@0 383 MOZ_CRASH("Unknown role.");
michael@0 384 };
michael@0 385
michael@0 386 #undef ROLE
michael@0 387
michael@0 388 // Special case, if there is a ROLE_ROW inside of a ROLE_TREE_TABLE, then call the MSAA role
michael@0 389 // a ROLE_OUTLINEITEM for consistency and compatibility.
michael@0 390 // We need this because ARIA has a role of "row" for both grid and treegrid
michael@0 391 if (geckoRole == roles::ROW) {
michael@0 392 Accessible* xpParent = Parent();
michael@0 393 if (xpParent && xpParent->Role() == roles::TREE_TABLE)
michael@0 394 msaaRole = ROLE_SYSTEM_OUTLINEITEM;
michael@0 395 }
michael@0 396
michael@0 397 // -- Try enumerated role
michael@0 398 if (msaaRole != USE_ROLE_STRING) {
michael@0 399 pvarRole->vt = VT_I4;
michael@0 400 pvarRole->lVal = msaaRole; // Normal enumerated role
michael@0 401 return S_OK;
michael@0 402 }
michael@0 403
michael@0 404 // -- Try BSTR role
michael@0 405 // Could not map to known enumerated MSAA role like ROLE_BUTTON
michael@0 406 // Use BSTR role to expose role attribute or tag name + namespace
michael@0 407 nsIContent *content = xpAccessible->GetContent();
michael@0 408 if (!content)
michael@0 409 return E_FAIL;
michael@0 410
michael@0 411 if (content->IsElement()) {
michael@0 412 nsAutoString roleString;
michael@0 413 if (msaaRole != ROLE_SYSTEM_CLIENT &&
michael@0 414 !content->GetAttr(kNameSpaceID_None, nsGkAtoms::role, roleString)) {
michael@0 415 nsIDocument * document = content->GetCurrentDoc();
michael@0 416 if (!document)
michael@0 417 return E_FAIL;
michael@0 418
michael@0 419 nsINodeInfo *nodeInfo = content->NodeInfo();
michael@0 420 nodeInfo->GetName(roleString);
michael@0 421
michael@0 422 // Only append name space if different from that of current document.
michael@0 423 if (!nodeInfo->NamespaceEquals(document->GetDefaultNamespaceID())) {
michael@0 424 nsAutoString nameSpaceURI;
michael@0 425 nodeInfo->GetNamespaceURI(nameSpaceURI);
michael@0 426 roleString += NS_LITERAL_STRING(", ") + nameSpaceURI;
michael@0 427 }
michael@0 428 }
michael@0 429
michael@0 430 if (!roleString.IsEmpty()) {
michael@0 431 pvarRole->vt = VT_BSTR;
michael@0 432 pvarRole->bstrVal = ::SysAllocString(roleString.get());
michael@0 433 return S_OK;
michael@0 434 }
michael@0 435 }
michael@0 436
michael@0 437 return E_FAIL;
michael@0 438
michael@0 439 A11Y_TRYBLOCK_END
michael@0 440 }
michael@0 441
michael@0 442 STDMETHODIMP
michael@0 443 AccessibleWrap::get_accState(
michael@0 444 /* [optional][in] */ VARIANT varChild,
michael@0 445 /* [retval][out] */ VARIANT __RPC_FAR *pvarState)
michael@0 446 {
michael@0 447 A11Y_TRYBLOCK_BEGIN
michael@0 448
michael@0 449 if (!pvarState)
michael@0 450 return E_INVALIDARG;
michael@0 451
michael@0 452 VariantInit(pvarState);
michael@0 453 pvarState->vt = VT_I4;
michael@0 454 pvarState->lVal = 0;
michael@0 455
michael@0 456 if (IsDefunct())
michael@0 457 return CO_E_OBJNOTCONNECTED;
michael@0 458
michael@0 459 Accessible* xpAccessible = GetXPAccessibleFor(varChild);
michael@0 460 if (!xpAccessible)
michael@0 461 return E_INVALIDARG;
michael@0 462
michael@0 463 if (xpAccessible->IsDefunct())
michael@0 464 return CO_E_OBJNOTCONNECTED;
michael@0 465
michael@0 466 // MSAA only has 31 states and the lowest 31 bits of our state bit mask
michael@0 467 // are the same states as MSAA.
michael@0 468 // Note: we map the following Gecko states to different MSAA states:
michael@0 469 // REQUIRED -> ALERT_LOW
michael@0 470 // ALERT -> ALERT_MEDIUM
michael@0 471 // INVALID -> ALERT_HIGH
michael@0 472 // CHECKABLE -> MARQUEED
michael@0 473
michael@0 474 uint32_t msaaState = 0;
michael@0 475 nsAccUtils::To32States(xpAccessible->State(), &msaaState, nullptr);
michael@0 476 pvarState->lVal = msaaState;
michael@0 477 return S_OK;
michael@0 478
michael@0 479 A11Y_TRYBLOCK_END
michael@0 480 }
michael@0 481
michael@0 482
michael@0 483 STDMETHODIMP
michael@0 484 AccessibleWrap::get_accHelp(
michael@0 485 /* [optional][in] */ VARIANT varChild,
michael@0 486 /* [retval][out] */ BSTR __RPC_FAR *pszHelp)
michael@0 487 {
michael@0 488 A11Y_TRYBLOCK_BEGIN
michael@0 489
michael@0 490 if (!pszHelp)
michael@0 491 return E_INVALIDARG;
michael@0 492
michael@0 493 *pszHelp = nullptr;
michael@0 494 return S_FALSE;
michael@0 495
michael@0 496 A11Y_TRYBLOCK_END
michael@0 497 }
michael@0 498
michael@0 499 STDMETHODIMP
michael@0 500 AccessibleWrap::get_accHelpTopic(
michael@0 501 /* [out] */ BSTR __RPC_FAR *pszHelpFile,
michael@0 502 /* [optional][in] */ VARIANT varChild,
michael@0 503 /* [retval][out] */ long __RPC_FAR *pidTopic)
michael@0 504 {
michael@0 505 A11Y_TRYBLOCK_BEGIN
michael@0 506
michael@0 507 if (!pszHelpFile || !pidTopic)
michael@0 508 return E_INVALIDARG;
michael@0 509
michael@0 510 *pszHelpFile = nullptr;
michael@0 511 *pidTopic = 0;
michael@0 512 return S_FALSE;
michael@0 513
michael@0 514 A11Y_TRYBLOCK_END
michael@0 515 }
michael@0 516
michael@0 517 STDMETHODIMP
michael@0 518 AccessibleWrap::get_accKeyboardShortcut(
michael@0 519 /* [optional][in] */ VARIANT varChild,
michael@0 520 /* [retval][out] */ BSTR __RPC_FAR *pszKeyboardShortcut)
michael@0 521 {
michael@0 522 A11Y_TRYBLOCK_BEGIN
michael@0 523
michael@0 524 if (!pszKeyboardShortcut)
michael@0 525 return E_INVALIDARG;
michael@0 526 *pszKeyboardShortcut = nullptr;
michael@0 527
michael@0 528 if (IsDefunct())
michael@0 529 return CO_E_OBJNOTCONNECTED;
michael@0 530
michael@0 531 Accessible* acc = GetXPAccessibleFor(varChild);
michael@0 532 if (!acc)
michael@0 533 return E_INVALIDARG;
michael@0 534
michael@0 535 if (acc->IsDefunct())
michael@0 536 return CO_E_OBJNOTCONNECTED;
michael@0 537
michael@0 538 KeyBinding keyBinding = acc->AccessKey();
michael@0 539 if (keyBinding.IsEmpty())
michael@0 540 keyBinding = acc->KeyboardShortcut();
michael@0 541
michael@0 542 nsAutoString shortcut;
michael@0 543 keyBinding.ToString(shortcut);
michael@0 544
michael@0 545 *pszKeyboardShortcut = ::SysAllocStringLen(shortcut.get(),
michael@0 546 shortcut.Length());
michael@0 547 return *pszKeyboardShortcut ? S_OK : E_OUTOFMEMORY;
michael@0 548
michael@0 549 A11Y_TRYBLOCK_END
michael@0 550 }
michael@0 551
michael@0 552 STDMETHODIMP
michael@0 553 AccessibleWrap::get_accFocus(
michael@0 554 /* [retval][out] */ VARIANT __RPC_FAR *pvarChild)
michael@0 555 {
michael@0 556 A11Y_TRYBLOCK_BEGIN
michael@0 557
michael@0 558 if (!pvarChild)
michael@0 559 return E_INVALIDARG;
michael@0 560
michael@0 561 VariantInit(pvarChild);
michael@0 562
michael@0 563 // VT_EMPTY: None. This object does not have the keyboard focus itself
michael@0 564 // and does not contain a child that has the keyboard focus.
michael@0 565 // VT_I4: lVal is CHILDID_SELF. The object itself has the keyboard focus.
michael@0 566 // VT_I4: lVal contains the child ID of the child element with the keyboard focus.
michael@0 567 // VT_DISPATCH: pdispVal member is the address of the IDispatch interface
michael@0 568 // for the child object with the keyboard focus.
michael@0 569 if (IsDefunct())
michael@0 570 return CO_E_OBJNOTCONNECTED;
michael@0 571
michael@0 572 // Return the current IAccessible child that has focus
michael@0 573 Accessible* focusedAccessible = FocusedChild();
michael@0 574 if (focusedAccessible == this) {
michael@0 575 pvarChild->vt = VT_I4;
michael@0 576 pvarChild->lVal = CHILDID_SELF;
michael@0 577 }
michael@0 578 else if (focusedAccessible) {
michael@0 579 pvarChild->vt = VT_DISPATCH;
michael@0 580 pvarChild->pdispVal = NativeAccessible(focusedAccessible);
michael@0 581 }
michael@0 582 else {
michael@0 583 pvarChild->vt = VT_EMPTY; // No focus or focus is not a child
michael@0 584 }
michael@0 585
michael@0 586 return S_OK;
michael@0 587
michael@0 588 A11Y_TRYBLOCK_END
michael@0 589 }
michael@0 590
michael@0 591 // This helper class implements IEnumVARIANT for a nsIArray containing nsIAccessible objects.
michael@0 592
michael@0 593 class AccessibleEnumerator MOZ_FINAL : public IEnumVARIANT
michael@0 594 {
michael@0 595 public:
michael@0 596 AccessibleEnumerator(nsIArray* aArray) : mArray(aArray), mCurIndex(0) { }
michael@0 597 AccessibleEnumerator(const AccessibleEnumerator& toCopy) :
michael@0 598 mArray(toCopy.mArray), mCurIndex(toCopy.mCurIndex) { }
michael@0 599 ~AccessibleEnumerator() { }
michael@0 600
michael@0 601 // IUnknown
michael@0 602 DECL_IUNKNOWN
michael@0 603
michael@0 604 // IEnumVARIANT
michael@0 605 STDMETHODIMP Next(unsigned long celt, VARIANT FAR* rgvar, unsigned long FAR* pceltFetched);
michael@0 606 STDMETHODIMP Skip(unsigned long celt);
michael@0 607 STDMETHODIMP Reset()
michael@0 608 {
michael@0 609 mCurIndex = 0;
michael@0 610 return S_OK;
michael@0 611 }
michael@0 612 STDMETHODIMP Clone(IEnumVARIANT FAR* FAR* ppenum);
michael@0 613
michael@0 614 private:
michael@0 615 nsCOMPtr<nsIArray> mArray;
michael@0 616 uint32_t mCurIndex;
michael@0 617 };
michael@0 618
michael@0 619 STDMETHODIMP
michael@0 620 AccessibleEnumerator::QueryInterface(REFIID iid, void ** ppvObject)
michael@0 621 {
michael@0 622 A11Y_TRYBLOCK_BEGIN
michael@0 623
michael@0 624 if (iid == IID_IEnumVARIANT) {
michael@0 625 *ppvObject = static_cast<IEnumVARIANT*>(this);
michael@0 626 AddRef();
michael@0 627 return S_OK;
michael@0 628 }
michael@0 629 if (iid == IID_IUnknown) {
michael@0 630 *ppvObject = static_cast<IUnknown*>(this);
michael@0 631 AddRef();
michael@0 632 return S_OK;
michael@0 633 }
michael@0 634
michael@0 635 *ppvObject = nullptr;
michael@0 636 return E_NOINTERFACE;
michael@0 637
michael@0 638 A11Y_TRYBLOCK_END
michael@0 639 }
michael@0 640
michael@0 641 STDMETHODIMP
michael@0 642 AccessibleEnumerator::Next(unsigned long celt, VARIANT FAR* rgvar, unsigned long FAR* pceltFetched)
michael@0 643 {
michael@0 644 A11Y_TRYBLOCK_BEGIN
michael@0 645
michael@0 646 uint32_t length = 0;
michael@0 647 mArray->GetLength(&length);
michael@0 648
michael@0 649 HRESULT hr = S_OK;
michael@0 650
michael@0 651 // Can't get more elements than there are...
michael@0 652 if (celt > length - mCurIndex) {
michael@0 653 hr = S_FALSE;
michael@0 654 celt = length - mCurIndex;
michael@0 655 }
michael@0 656
michael@0 657 for (uint32_t i = 0; i < celt; ++i, ++mCurIndex) {
michael@0 658 // Copy the elements of the array into rgvar
michael@0 659 nsCOMPtr<nsIAccessible> accel(do_QueryElementAt(mArray, mCurIndex));
michael@0 660 NS_ASSERTION(accel, "Invalid pointer in mArray");
michael@0 661
michael@0 662 if (accel) {
michael@0 663 rgvar[i].vt = VT_DISPATCH;
michael@0 664 rgvar[i].pdispVal = AccessibleWrap::NativeAccessible(accel);
michael@0 665 }
michael@0 666 }
michael@0 667
michael@0 668 if (pceltFetched)
michael@0 669 *pceltFetched = celt;
michael@0 670
michael@0 671 return hr;
michael@0 672
michael@0 673 A11Y_TRYBLOCK_END
michael@0 674 }
michael@0 675
michael@0 676 STDMETHODIMP
michael@0 677 AccessibleEnumerator::Clone(IEnumVARIANT FAR* FAR* ppenum)
michael@0 678 {
michael@0 679 A11Y_TRYBLOCK_BEGIN
michael@0 680
michael@0 681 *ppenum = new AccessibleEnumerator(*this);
michael@0 682 if (!*ppenum)
michael@0 683 return E_OUTOFMEMORY;
michael@0 684 NS_ADDREF(*ppenum);
michael@0 685 return S_OK;
michael@0 686
michael@0 687 A11Y_TRYBLOCK_END
michael@0 688 }
michael@0 689
michael@0 690 STDMETHODIMP
michael@0 691 AccessibleEnumerator::Skip(unsigned long celt)
michael@0 692 {
michael@0 693 A11Y_TRYBLOCK_BEGIN
michael@0 694
michael@0 695 uint32_t length = 0;
michael@0 696 mArray->GetLength(&length);
michael@0 697 // Check if we can skip the requested number of elements
michael@0 698 if (celt > length - mCurIndex) {
michael@0 699 mCurIndex = length;
michael@0 700 return S_FALSE;
michael@0 701 }
michael@0 702 mCurIndex += celt;
michael@0 703 return S_OK;
michael@0 704
michael@0 705 A11Y_TRYBLOCK_END
michael@0 706 }
michael@0 707
michael@0 708 /**
michael@0 709 * This method is called when a client wants to know which children of a node
michael@0 710 * are selected. Note that this method can only find selected children for
michael@0 711 * nsIAccessible object which implement SelectAccessible.
michael@0 712 *
michael@0 713 * The VARIANT return value arguement is expected to either contain a single IAccessible
michael@0 714 * or an IEnumVARIANT of IAccessibles. We return the IEnumVARIANT regardless of the number
michael@0 715 * of children selected, unless there are none selected in which case we return an empty
michael@0 716 * VARIANT.
michael@0 717 *
michael@0 718 * We get the selected options from the select's accessible object and wrap
michael@0 719 * those in an AccessibleEnumerator which we then put in the return VARIANT.
michael@0 720 *
michael@0 721 * returns a VT_EMPTY VARIANT if:
michael@0 722 * - there are no selected children for this object
michael@0 723 * - the object is not the type that can have children selected
michael@0 724 */
michael@0 725 STDMETHODIMP
michael@0 726 AccessibleWrap::get_accSelection(VARIANT __RPC_FAR *pvarChildren)
michael@0 727 {
michael@0 728 A11Y_TRYBLOCK_BEGIN
michael@0 729
michael@0 730 if (!pvarChildren)
michael@0 731 return E_INVALIDARG;
michael@0 732
michael@0 733 VariantInit(pvarChildren);
michael@0 734 pvarChildren->vt = VT_EMPTY;
michael@0 735
michael@0 736 if (IsDefunct())
michael@0 737 return CO_E_OBJNOTCONNECTED;
michael@0 738
michael@0 739 if (IsSelect()) {
michael@0 740 nsCOMPtr<nsIArray> selectedItems = SelectedItems();
michael@0 741 if (selectedItems) {
michael@0 742 // 1) Create and initialize the enumeration
michael@0 743 nsRefPtr<AccessibleEnumerator> pEnum =
michael@0 744 new AccessibleEnumerator(selectedItems);
michael@0 745
michael@0 746 // 2) Put the enumerator in the VARIANT
michael@0 747 if (!pEnum)
michael@0 748 return E_OUTOFMEMORY;
michael@0 749 pvarChildren->vt = VT_UNKNOWN; // this must be VT_UNKNOWN for an IEnumVARIANT
michael@0 750 NS_ADDREF(pvarChildren->punkVal = pEnum);
michael@0 751 }
michael@0 752 }
michael@0 753 return S_OK;
michael@0 754
michael@0 755 A11Y_TRYBLOCK_END
michael@0 756 }
michael@0 757
michael@0 758 STDMETHODIMP
michael@0 759 AccessibleWrap::get_accDefaultAction(
michael@0 760 /* [optional][in] */ VARIANT varChild,
michael@0 761 /* [retval][out] */ BSTR __RPC_FAR *pszDefaultAction)
michael@0 762 {
michael@0 763 A11Y_TRYBLOCK_BEGIN
michael@0 764
michael@0 765 if (!pszDefaultAction)
michael@0 766 return E_INVALIDARG;
michael@0 767
michael@0 768 *pszDefaultAction = nullptr;
michael@0 769
michael@0 770 if (IsDefunct())
michael@0 771 return CO_E_OBJNOTCONNECTED;
michael@0 772
michael@0 773 Accessible* xpAccessible = GetXPAccessibleFor(varChild);
michael@0 774 if (!xpAccessible)
michael@0 775 return E_INVALIDARG;
michael@0 776
michael@0 777 if (xpAccessible->IsDefunct())
michael@0 778 return CO_E_OBJNOTCONNECTED;
michael@0 779
michael@0 780 nsAutoString defaultAction;
michael@0 781 if (NS_FAILED(xpAccessible->GetActionName(0, defaultAction)))
michael@0 782 return E_FAIL;
michael@0 783
michael@0 784 *pszDefaultAction = ::SysAllocStringLen(defaultAction.get(),
michael@0 785 defaultAction.Length());
michael@0 786 return *pszDefaultAction ? S_OK : E_OUTOFMEMORY;
michael@0 787
michael@0 788 A11Y_TRYBLOCK_END
michael@0 789 }
michael@0 790
michael@0 791 STDMETHODIMP
michael@0 792 AccessibleWrap::accSelect(
michael@0 793 /* [in] */ long flagsSelect,
michael@0 794 /* [optional][in] */ VARIANT varChild)
michael@0 795 {
michael@0 796 A11Y_TRYBLOCK_BEGIN
michael@0 797
michael@0 798 if (IsDefunct())
michael@0 799 return CO_E_OBJNOTCONNECTED;
michael@0 800
michael@0 801 // currently only handle focus and selection
michael@0 802 Accessible* xpAccessible = GetXPAccessibleFor(varChild);
michael@0 803 if (!xpAccessible)
michael@0 804 return E_INVALIDARG;
michael@0 805
michael@0 806 if (xpAccessible->IsDefunct())
michael@0 807 return CO_E_OBJNOTCONNECTED;
michael@0 808
michael@0 809 if (flagsSelect & (SELFLAG_TAKEFOCUS|SELFLAG_TAKESELECTION|SELFLAG_REMOVESELECTION))
michael@0 810 {
michael@0 811 if (flagsSelect & SELFLAG_TAKEFOCUS)
michael@0 812 xpAccessible->TakeFocus();
michael@0 813
michael@0 814 if (flagsSelect & SELFLAG_TAKESELECTION)
michael@0 815 xpAccessible->TakeSelection();
michael@0 816
michael@0 817 if (flagsSelect & SELFLAG_ADDSELECTION)
michael@0 818 xpAccessible->SetSelected(true);
michael@0 819
michael@0 820 if (flagsSelect & SELFLAG_REMOVESELECTION)
michael@0 821 xpAccessible->SetSelected(false);
michael@0 822
michael@0 823 if (flagsSelect & SELFLAG_EXTENDSELECTION)
michael@0 824 xpAccessible->ExtendSelection();
michael@0 825
michael@0 826 return S_OK;
michael@0 827 }
michael@0 828
michael@0 829 return E_FAIL;
michael@0 830
michael@0 831 A11Y_TRYBLOCK_END
michael@0 832 }
michael@0 833
michael@0 834 STDMETHODIMP
michael@0 835 AccessibleWrap::accLocation(
michael@0 836 /* [out] */ long __RPC_FAR *pxLeft,
michael@0 837 /* [out] */ long __RPC_FAR *pyTop,
michael@0 838 /* [out] */ long __RPC_FAR *pcxWidth,
michael@0 839 /* [out] */ long __RPC_FAR *pcyHeight,
michael@0 840 /* [optional][in] */ VARIANT varChild)
michael@0 841 {
michael@0 842 A11Y_TRYBLOCK_BEGIN
michael@0 843
michael@0 844 if (!pxLeft || !pyTop || !pcxWidth || !pcyHeight)
michael@0 845 return E_INVALIDARG;
michael@0 846
michael@0 847 *pxLeft = 0;
michael@0 848 *pyTop = 0;
michael@0 849 *pcxWidth = 0;
michael@0 850 *pcyHeight = 0;
michael@0 851
michael@0 852 if (IsDefunct())
michael@0 853 return CO_E_OBJNOTCONNECTED;
michael@0 854
michael@0 855 Accessible* xpAccessible = GetXPAccessibleFor(varChild);
michael@0 856 if (!xpAccessible)
michael@0 857 return E_INVALIDARG;
michael@0 858
michael@0 859 if (xpAccessible->IsDefunct())
michael@0 860 return CO_E_OBJNOTCONNECTED;
michael@0 861
michael@0 862 int32_t x, y, width, height;
michael@0 863 if (NS_FAILED(xpAccessible->GetBounds(&x, &y, &width, &height)))
michael@0 864 return E_FAIL;
michael@0 865
michael@0 866 *pxLeft = x;
michael@0 867 *pyTop = y;
michael@0 868 *pcxWidth = width;
michael@0 869 *pcyHeight = height;
michael@0 870 return S_OK;
michael@0 871
michael@0 872 A11Y_TRYBLOCK_END
michael@0 873 }
michael@0 874
michael@0 875 STDMETHODIMP
michael@0 876 AccessibleWrap::accNavigate(
michael@0 877 /* [in] */ long navDir,
michael@0 878 /* [optional][in] */ VARIANT varStart,
michael@0 879 /* [retval][out] */ VARIANT __RPC_FAR *pvarEndUpAt)
michael@0 880 {
michael@0 881 A11Y_TRYBLOCK_BEGIN
michael@0 882
michael@0 883 if (!pvarEndUpAt)
michael@0 884 return E_INVALIDARG;
michael@0 885
michael@0 886 VariantInit(pvarEndUpAt);
michael@0 887
michael@0 888 if (IsDefunct())
michael@0 889 return CO_E_OBJNOTCONNECTED;
michael@0 890
michael@0 891 Accessible* accessible = GetXPAccessibleFor(varStart);
michael@0 892 if (!accessible)
michael@0 893 return E_INVALIDARG;
michael@0 894
michael@0 895 if (accessible->IsDefunct())
michael@0 896 return CO_E_OBJNOTCONNECTED;
michael@0 897
michael@0 898 Accessible* navAccessible = nullptr;
michael@0 899 Maybe<RelationType> xpRelation;
michael@0 900
michael@0 901 #define RELATIONTYPE(geckoType, stringType, atkType, msaaType, ia2Type) \
michael@0 902 case msaaType: \
michael@0 903 xpRelation.construct(RelationType::geckoType); \
michael@0 904 break;
michael@0 905
michael@0 906 switch(navDir) {
michael@0 907 case NAVDIR_FIRSTCHILD:
michael@0 908 if (!nsAccUtils::MustPrune(accessible))
michael@0 909 navAccessible = accessible->FirstChild();
michael@0 910 break;
michael@0 911 case NAVDIR_LASTCHILD:
michael@0 912 if (!nsAccUtils::MustPrune(accessible))
michael@0 913 navAccessible = accessible->LastChild();
michael@0 914 break;
michael@0 915 case NAVDIR_NEXT:
michael@0 916 navAccessible = accessible->NextSibling();
michael@0 917 break;
michael@0 918 case NAVDIR_PREVIOUS:
michael@0 919 navAccessible = accessible->PrevSibling();
michael@0 920 break;
michael@0 921 case NAVDIR_DOWN:
michael@0 922 case NAVDIR_LEFT:
michael@0 923 case NAVDIR_RIGHT:
michael@0 924 case NAVDIR_UP:
michael@0 925 return E_NOTIMPL;
michael@0 926
michael@0 927 // MSAA relationship extensions to accNavigate
michael@0 928 #include "RelationTypeMap.h"
michael@0 929
michael@0 930 default:
michael@0 931 return E_INVALIDARG;
michael@0 932 }
michael@0 933
michael@0 934 #undef RELATIONTYPE
michael@0 935
michael@0 936 pvarEndUpAt->vt = VT_EMPTY;
michael@0 937
michael@0 938 if (!xpRelation.empty()) {
michael@0 939 Relation rel = RelationByType(xpRelation.ref());
michael@0 940 navAccessible = rel.Next();
michael@0 941 }
michael@0 942
michael@0 943 if (!navAccessible)
michael@0 944 return E_FAIL;
michael@0 945
michael@0 946 pvarEndUpAt->pdispVal = NativeAccessible(navAccessible);
michael@0 947 pvarEndUpAt->vt = VT_DISPATCH;
michael@0 948 return S_OK;
michael@0 949
michael@0 950 A11Y_TRYBLOCK_END
michael@0 951 }
michael@0 952
michael@0 953 STDMETHODIMP
michael@0 954 AccessibleWrap::accHitTest(
michael@0 955 /* [in] */ long xLeft,
michael@0 956 /* [in] */ long yTop,
michael@0 957 /* [retval][out] */ VARIANT __RPC_FAR *pvarChild)
michael@0 958 {
michael@0 959 A11Y_TRYBLOCK_BEGIN
michael@0 960
michael@0 961 if (!pvarChild)
michael@0 962 return E_INVALIDARG;
michael@0 963
michael@0 964 VariantInit(pvarChild);
michael@0 965
michael@0 966 if (IsDefunct())
michael@0 967 return CO_E_OBJNOTCONNECTED;
michael@0 968
michael@0 969 Accessible* accessible = ChildAtPoint(xLeft, yTop, eDirectChild);
michael@0 970
michael@0 971 // if we got a child
michael@0 972 if (accessible) {
michael@0 973 // if the child is us
michael@0 974 if (accessible == this) {
michael@0 975 pvarChild->vt = VT_I4;
michael@0 976 pvarChild->lVal = CHILDID_SELF;
michael@0 977 } else { // its not create an Accessible for it.
michael@0 978 pvarChild->vt = VT_DISPATCH;
michael@0 979 pvarChild->pdispVal = NativeAccessible(accessible);
michael@0 980 }
michael@0 981 } else {
michael@0 982 // no child at that point
michael@0 983 pvarChild->vt = VT_EMPTY;
michael@0 984 return S_FALSE;
michael@0 985 }
michael@0 986 return S_OK;
michael@0 987
michael@0 988 A11Y_TRYBLOCK_END
michael@0 989 }
michael@0 990
michael@0 991 STDMETHODIMP
michael@0 992 AccessibleWrap::accDoDefaultAction(
michael@0 993 /* [optional][in] */ VARIANT varChild)
michael@0 994 {
michael@0 995 A11Y_TRYBLOCK_BEGIN
michael@0 996
michael@0 997 if (IsDefunct())
michael@0 998 return CO_E_OBJNOTCONNECTED;
michael@0 999
michael@0 1000 Accessible* xpAccessible = GetXPAccessibleFor(varChild);
michael@0 1001 if (!xpAccessible)
michael@0 1002 return E_INVALIDARG;
michael@0 1003
michael@0 1004 if (xpAccessible->IsDefunct())
michael@0 1005 return CO_E_OBJNOTCONNECTED;
michael@0 1006
michael@0 1007 return GetHRESULT(xpAccessible->DoAction(0));
michael@0 1008
michael@0 1009 A11Y_TRYBLOCK_END
michael@0 1010 }
michael@0 1011
michael@0 1012 STDMETHODIMP
michael@0 1013 AccessibleWrap::put_accName(
michael@0 1014 /* [optional][in] */ VARIANT varChild,
michael@0 1015 /* [in] */ BSTR szName)
michael@0 1016 {
michael@0 1017 return E_NOTIMPL;
michael@0 1018 }
michael@0 1019
michael@0 1020 STDMETHODIMP
michael@0 1021 AccessibleWrap::put_accValue(
michael@0 1022 /* [optional][in] */ VARIANT varChild,
michael@0 1023 /* [in] */ BSTR szValue)
michael@0 1024 {
michael@0 1025 return E_NOTIMPL;
michael@0 1026 }
michael@0 1027
michael@0 1028 ////////////////////////////////////////////////////////////////////////////////
michael@0 1029 // IDispatch
michael@0 1030
michael@0 1031 STDMETHODIMP
michael@0 1032 AccessibleWrap::GetTypeInfoCount(UINT *pctinfo)
michael@0 1033 {
michael@0 1034 if (!pctinfo)
michael@0 1035 return E_INVALIDARG;
michael@0 1036
michael@0 1037 *pctinfo = 1;
michael@0 1038 return S_OK;
michael@0 1039 }
michael@0 1040
michael@0 1041 STDMETHODIMP
michael@0 1042 AccessibleWrap::GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo)
michael@0 1043 {
michael@0 1044 if (!ppTInfo)
michael@0 1045 return E_INVALIDARG;
michael@0 1046
michael@0 1047 *ppTInfo = nullptr;
michael@0 1048
michael@0 1049 if (iTInfo != 0)
michael@0 1050 return DISP_E_BADINDEX;
michael@0 1051
michael@0 1052 ITypeInfo * typeInfo = GetTI(lcid);
michael@0 1053 if (!typeInfo)
michael@0 1054 return E_FAIL;
michael@0 1055
michael@0 1056 typeInfo->AddRef();
michael@0 1057 *ppTInfo = typeInfo;
michael@0 1058
michael@0 1059 return S_OK;
michael@0 1060 }
michael@0 1061
michael@0 1062 STDMETHODIMP
michael@0 1063 AccessibleWrap::GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames,
michael@0 1064 UINT cNames, LCID lcid, DISPID *rgDispId)
michael@0 1065 {
michael@0 1066 ITypeInfo *typeInfo = GetTI(lcid);
michael@0 1067 if (!typeInfo)
michael@0 1068 return E_FAIL;
michael@0 1069
michael@0 1070 HRESULT hr = DispGetIDsOfNames(typeInfo, rgszNames, cNames, rgDispId);
michael@0 1071 return hr;
michael@0 1072 }
michael@0 1073
michael@0 1074 STDMETHODIMP
michael@0 1075 AccessibleWrap::Invoke(DISPID dispIdMember, REFIID riid,
michael@0 1076 LCID lcid, WORD wFlags, DISPPARAMS *pDispParams,
michael@0 1077 VARIANT *pVarResult, EXCEPINFO *pExcepInfo,
michael@0 1078 UINT *puArgErr)
michael@0 1079 {
michael@0 1080 ITypeInfo *typeInfo = GetTI(lcid);
michael@0 1081 if (!typeInfo)
michael@0 1082 return E_FAIL;
michael@0 1083
michael@0 1084 return typeInfo->Invoke(static_cast<IAccessible*>(this), dispIdMember,
michael@0 1085 wFlags, pDispParams, pVarResult, pExcepInfo,
michael@0 1086 puArgErr);
michael@0 1087 }
michael@0 1088
michael@0 1089
michael@0 1090 // nsIAccessible method
michael@0 1091 NS_IMETHODIMP
michael@0 1092 AccessibleWrap::GetNativeInterface(void **aOutAccessible)
michael@0 1093 {
michael@0 1094 *aOutAccessible = static_cast<IAccessible*>(this);
michael@0 1095 NS_ADDREF_THIS();
michael@0 1096 return NS_OK;
michael@0 1097 }
michael@0 1098
michael@0 1099 ////////////////////////////////////////////////////////////////////////////////
michael@0 1100 // Accessible
michael@0 1101
michael@0 1102 nsresult
michael@0 1103 AccessibleWrap::HandleAccEvent(AccEvent* aEvent)
michael@0 1104 {
michael@0 1105 nsresult rv = Accessible::HandleAccEvent(aEvent);
michael@0 1106 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1107
michael@0 1108 // Don't fire native MSAA events or mess with the system caret
michael@0 1109 // when running in metro mode. This confuses input focus tracking
michael@0 1110 // in metro's UIA implementation.
michael@0 1111 if (XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Metro) {
michael@0 1112 return NS_OK;
michael@0 1113 }
michael@0 1114
michael@0 1115 uint32_t eventType = aEvent->GetEventType();
michael@0 1116
michael@0 1117 static_assert(sizeof(gWinEventMap)/sizeof(gWinEventMap[0]) == nsIAccessibleEvent::EVENT_LAST_ENTRY,
michael@0 1118 "MSAA event map skewed");
michael@0 1119
michael@0 1120 NS_ENSURE_TRUE(eventType > 0 && eventType < ArrayLength(gWinEventMap), NS_ERROR_FAILURE);
michael@0 1121
michael@0 1122 uint32_t winEvent = gWinEventMap[eventType];
michael@0 1123 if (!winEvent)
michael@0 1124 return NS_OK;
michael@0 1125
michael@0 1126 // Means we're not active.
michael@0 1127 NS_ENSURE_TRUE(!IsDefunct(), NS_ERROR_FAILURE);
michael@0 1128
michael@0 1129 Accessible* accessible = aEvent->GetAccessible();
michael@0 1130 if (!accessible)
michael@0 1131 return NS_OK;
michael@0 1132
michael@0 1133 if (eventType == nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED ||
michael@0 1134 eventType == nsIAccessibleEvent::EVENT_FOCUS) {
michael@0 1135 UpdateSystemCaretFor(accessible);
michael@0 1136 }
michael@0 1137
michael@0 1138 int32_t childID = GetChildIDFor(accessible); // get the id for the accessible
michael@0 1139 if (!childID)
michael@0 1140 return NS_OK; // Can't fire an event without a child ID
michael@0 1141
michael@0 1142 HWND hWnd = GetHWNDFor(accessible);
michael@0 1143 NS_ENSURE_TRUE(hWnd, NS_ERROR_FAILURE);
michael@0 1144
michael@0 1145 nsAutoString tag;
michael@0 1146 nsAutoCString id;
michael@0 1147 nsIContent* cnt = accessible->GetContent();
michael@0 1148 if (cnt) {
michael@0 1149 cnt->Tag()->ToString(tag);
michael@0 1150 nsIAtom* aid = cnt->GetID();
michael@0 1151 if (aid)
michael@0 1152 aid->ToUTF8String(id);
michael@0 1153 }
michael@0 1154
michael@0 1155 #ifdef A11Y_LOG
michael@0 1156 if (logging::IsEnabled(logging::ePlatforms)) {
michael@0 1157 printf("\n\nMSAA event: event: %d, target: %s@id='%s', childid: %d, hwnd: %d\n\n",
michael@0 1158 eventType, NS_ConvertUTF16toUTF8(tag).get(), id.get(),
michael@0 1159 childID, hWnd);
michael@0 1160 }
michael@0 1161 #endif
michael@0 1162
michael@0 1163 // Fire MSAA event for client area window.
michael@0 1164 ::NotifyWinEvent(winEvent, hWnd, OBJID_CLIENT, childID);
michael@0 1165
michael@0 1166 // JAWS announces collapsed combobox navigation based on focus events.
michael@0 1167 if (Compatibility::IsJAWS()) {
michael@0 1168 if (eventType == nsIAccessibleEvent::EVENT_SELECTION &&
michael@0 1169 accessible->Role() == roles::COMBOBOX_OPTION) {
michael@0 1170 ::NotifyWinEvent(EVENT_OBJECT_FOCUS, hWnd, OBJID_CLIENT, childID);
michael@0 1171 }
michael@0 1172 }
michael@0 1173
michael@0 1174 return NS_OK;
michael@0 1175 }
michael@0 1176
michael@0 1177 ////////////////////////////////////////////////////////////////////////////////
michael@0 1178 // AccessibleWrap
michael@0 1179
michael@0 1180 //------- Helper methods ---------
michael@0 1181
michael@0 1182 int32_t
michael@0 1183 AccessibleWrap::GetChildIDFor(Accessible* aAccessible)
michael@0 1184 {
michael@0 1185 // A child ID of the window is required, when we use NotifyWinEvent,
michael@0 1186 // so that the 3rd party application can call back and get the IAccessible
michael@0 1187 // the event occurred on.
michael@0 1188
michael@0 1189 // Yes, this means we're only compatibible with 32 bit
michael@0 1190 // MSAA is only available for 32 bit windows, so it's okay
michael@0 1191 // XXX: bug 606080
michael@0 1192 return aAccessible ? - NS_PTR_TO_INT32(aAccessible->UniqueID()) : 0;
michael@0 1193 }
michael@0 1194
michael@0 1195 HWND
michael@0 1196 AccessibleWrap::GetHWNDFor(Accessible* aAccessible)
michael@0 1197 {
michael@0 1198 if (aAccessible) {
michael@0 1199 DocAccessible* document = aAccessible->Document();
michael@0 1200 if(!document)
michael@0 1201 return nullptr;
michael@0 1202
michael@0 1203 // Popup lives in own windows, use its HWND until the popup window is
michael@0 1204 // hidden to make old JAWS versions work with collapsed comboboxes (see
michael@0 1205 // discussion in bug 379678).
michael@0 1206 nsIFrame* frame = aAccessible->GetFrame();
michael@0 1207 if (frame) {
michael@0 1208 nsIWidget* widget = frame->GetNearestWidget();
michael@0 1209 if (widget && widget->IsVisible()) {
michael@0 1210 nsIPresShell* shell = document->PresShell();
michael@0 1211 nsViewManager* vm = shell->GetViewManager();
michael@0 1212 if (vm) {
michael@0 1213 nsCOMPtr<nsIWidget> rootWidget;
michael@0 1214 vm->GetRootWidget(getter_AddRefs(rootWidget));
michael@0 1215 // Make sure the accessible belongs to popup. If not then use
michael@0 1216 // document HWND (which might be different from root widget in the
michael@0 1217 // case of window emulation).
michael@0 1218 if (rootWidget != widget)
michael@0 1219 return static_cast<HWND>(widget->GetNativeData(NS_NATIVE_WINDOW));
michael@0 1220 }
michael@0 1221 }
michael@0 1222 }
michael@0 1223
michael@0 1224 return static_cast<HWND>(document->GetNativeWindow());
michael@0 1225 }
michael@0 1226 return nullptr;
michael@0 1227 }
michael@0 1228
michael@0 1229 IDispatch*
michael@0 1230 AccessibleWrap::NativeAccessible(nsIAccessible* aAccessible)
michael@0 1231 {
michael@0 1232 if (!aAccessible) {
michael@0 1233 NS_WARNING("Not passing in an aAccessible");
michael@0 1234 return nullptr;
michael@0 1235 }
michael@0 1236
michael@0 1237 IAccessible* msaaAccessible = nullptr;
michael@0 1238 aAccessible->GetNativeInterface(reinterpret_cast<void**>(&msaaAccessible));
michael@0 1239 return static_cast<IDispatch*>(msaaAccessible);
michael@0 1240 }
michael@0 1241
michael@0 1242 Accessible*
michael@0 1243 AccessibleWrap::GetXPAccessibleFor(const VARIANT& aVarChild)
michael@0 1244 {
michael@0 1245 if (aVarChild.vt != VT_I4)
michael@0 1246 return nullptr;
michael@0 1247
michael@0 1248 // if its us real easy - this seems to always be the case
michael@0 1249 if (aVarChild.lVal == CHILDID_SELF)
michael@0 1250 return this;
michael@0 1251
michael@0 1252 if (nsAccUtils::MustPrune(this))
michael@0 1253 return nullptr;
michael@0 1254
michael@0 1255 // If lVal negative then it is treated as child ID and we should look for
michael@0 1256 // accessible through whole accessible subtree including subdocuments.
michael@0 1257 // Otherwise we treat lVal as index in parent.
michael@0 1258
michael@0 1259 if (aVarChild.lVal < 0) {
michael@0 1260 // Convert child ID to unique ID.
michael@0 1261 void* uniqueID = reinterpret_cast<void*>(-aVarChild.lVal);
michael@0 1262
michael@0 1263 DocAccessible* document = Document();
michael@0 1264 Accessible* child =
michael@0 1265 document->GetAccessibleByUniqueIDInSubtree(uniqueID);
michael@0 1266
michael@0 1267 // If it is a document then just return an accessible.
michael@0 1268 if (IsDoc())
michael@0 1269 return child;
michael@0 1270
michael@0 1271 // Otherwise check whether the accessible is a child (this path works for
michael@0 1272 // ARIA documents and popups).
michael@0 1273 Accessible* parent = child;
michael@0 1274 while (parent && parent != document) {
michael@0 1275 if (parent == this)
michael@0 1276 return child;
michael@0 1277
michael@0 1278 parent = parent->Parent();
michael@0 1279 }
michael@0 1280
michael@0 1281 return nullptr;
michael@0 1282 }
michael@0 1283
michael@0 1284 // Gecko child indices are 0-based in contrast to indices used in MSAA.
michael@0 1285 return GetChildAt(aVarChild.lVal - 1);
michael@0 1286 }
michael@0 1287
michael@0 1288 void
michael@0 1289 AccessibleWrap::UpdateSystemCaretFor(Accessible* aAccessible)
michael@0 1290 {
michael@0 1291 // Move the system caret so that Windows Tablet Edition and tradional ATs with
michael@0 1292 // off-screen model can follow the caret
michael@0 1293 ::DestroyCaret();
michael@0 1294
michael@0 1295 HyperTextAccessible* text = aAccessible->AsHyperText();
michael@0 1296 if (!text)
michael@0 1297 return;
michael@0 1298
michael@0 1299 nsIWidget* widget = nullptr;
michael@0 1300 nsIntRect caretRect = text->GetCaretRect(&widget);
michael@0 1301 HWND caretWnd;
michael@0 1302 if (caretRect.IsEmpty() || !(caretWnd = (HWND)widget->GetNativeData(NS_NATIVE_WINDOW))) {
michael@0 1303 return;
michael@0 1304 }
michael@0 1305
michael@0 1306 // Create invisible bitmap for caret, otherwise its appearance interferes
michael@0 1307 // with Gecko caret
michael@0 1308 HBITMAP caretBitMap = CreateBitmap(1, caretRect.height, 1, 1, nullptr);
michael@0 1309 if (::CreateCaret(caretWnd, caretBitMap, 1, caretRect.height)) { // Also destroys the last caret
michael@0 1310 ::ShowCaret(caretWnd);
michael@0 1311 RECT windowRect;
michael@0 1312 ::GetWindowRect(caretWnd, &windowRect);
michael@0 1313 ::SetCaretPos(caretRect.x - windowRect.left, caretRect.y - windowRect.top);
michael@0 1314 ::DeleteObject(caretBitMap);
michael@0 1315 }
michael@0 1316 }
michael@0 1317
michael@0 1318 ITypeInfo*
michael@0 1319 AccessibleWrap::GetTI(LCID lcid)
michael@0 1320 {
michael@0 1321 if (gTypeInfo)
michael@0 1322 return gTypeInfo;
michael@0 1323
michael@0 1324 ITypeLib *typeLib = nullptr;
michael@0 1325 HRESULT hr = LoadRegTypeLib(LIBID_Accessibility, 1, 0, lcid, &typeLib);
michael@0 1326 if (FAILED(hr))
michael@0 1327 return nullptr;
michael@0 1328
michael@0 1329 hr = typeLib->GetTypeInfoOfGuid(IID_IAccessible, &gTypeInfo);
michael@0 1330 typeLib->Release();
michael@0 1331
michael@0 1332 if (FAILED(hr))
michael@0 1333 return nullptr;
michael@0 1334
michael@0 1335 return gTypeInfo;
michael@0 1336 }

mercurial