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