michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set ts=2 et sw=2 tw=80: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "AccessibleWrap.h" michael@0: #include "Accessible-inl.h" michael@0: michael@0: #include "Compatibility.h" michael@0: #include "DocAccessible-inl.h" michael@0: #include "EnumVariant.h" michael@0: #include "nsAccUtils.h" michael@0: #include "nsCoreUtils.h" michael@0: #include "nsIAccessibleEvent.h" michael@0: #include "nsIAccessibleRelation.h" michael@0: #include "nsWinUtils.h" michael@0: #include "ServiceProvider.h" michael@0: #include "Relation.h" michael@0: #include "Role.h" michael@0: #include "RootAccessible.h" michael@0: #include "sdnAccessible.h" michael@0: #include "States.h" michael@0: michael@0: #ifdef A11Y_LOG michael@0: #include "Logging.h" michael@0: #endif michael@0: michael@0: #include "nsIMutableArray.h" michael@0: #include "nsIFrame.h" michael@0: #include "nsIScrollableFrame.h" michael@0: #include "nsINodeInfo.h" michael@0: #include "nsIServiceManager.h" michael@0: #include "nsNameSpaceManager.h" michael@0: #include "nsTextFormatter.h" michael@0: #include "nsView.h" michael@0: #include "nsViewManager.h" michael@0: #include "nsEventMap.h" michael@0: #include "nsArrayUtils.h" michael@0: #include "mozilla/Preferences.h" michael@0: michael@0: #include "oleacc.h" michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::a11y; michael@0: michael@0: const uint32_t USE_ROLE_STRING = 0; michael@0: michael@0: /* For documentation of the accessibility architecture, michael@0: * see http://lxr.mozilla.org/seamonkey/source/accessible/accessible-docs.html michael@0: */ michael@0: michael@0: //#define DEBUG_LEAKS michael@0: michael@0: #ifdef DEBUG_LEAKS michael@0: static gAccessibles = 0; michael@0: #endif michael@0: michael@0: static const int32_t kIEnumVariantDisconnected = -1; michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // AccessibleWrap michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: ITypeInfo* AccessibleWrap::gTypeInfo = nullptr; michael@0: michael@0: NS_IMPL_ISUPPORTS_INHERITED0(AccessibleWrap, Accessible) michael@0: michael@0: //----------------------------------------------------- michael@0: // IUnknown interface methods - see iunknown.h for documentation michael@0: //----------------------------------------------------- michael@0: michael@0: // Microsoft COM QueryInterface michael@0: STDMETHODIMP michael@0: AccessibleWrap::QueryInterface(REFIID iid, void** ppv) michael@0: { michael@0: A11Y_TRYBLOCK_BEGIN michael@0: michael@0: if (!ppv) michael@0: return E_INVALIDARG; michael@0: michael@0: *ppv = nullptr; michael@0: michael@0: if (IID_IUnknown == iid || IID_IDispatch == iid || IID_IAccessible == iid) michael@0: *ppv = static_cast(this); michael@0: else if (IID_IEnumVARIANT == iid) { michael@0: // Don't support this interface for leaf elements. michael@0: if (!HasChildren() || nsAccUtils::MustPrune(this)) michael@0: return E_NOINTERFACE; michael@0: michael@0: *ppv = static_cast(new ChildrenEnumVariant(this)); michael@0: } else if (IID_IServiceProvider == iid) michael@0: *ppv = new ServiceProvider(this); michael@0: else if (IID_ISimpleDOMNode == iid) { michael@0: if (IsDefunct() || (!HasOwnContent() && !IsDoc())) michael@0: return E_NOINTERFACE; michael@0: michael@0: *ppv = static_cast(new sdnAccessible(GetNode())); michael@0: } michael@0: michael@0: if (nullptr == *ppv) { michael@0: HRESULT hr = ia2Accessible::QueryInterface(iid, ppv); michael@0: if (SUCCEEDED(hr)) michael@0: return hr; michael@0: } michael@0: michael@0: if (nullptr == *ppv) { michael@0: HRESULT hr = ia2AccessibleComponent::QueryInterface(iid, ppv); michael@0: if (SUCCEEDED(hr)) michael@0: return hr; michael@0: } michael@0: michael@0: if (nullptr == *ppv) { michael@0: HRESULT hr = ia2AccessibleHyperlink::QueryInterface(iid, ppv); michael@0: if (SUCCEEDED(hr)) michael@0: return hr; michael@0: } michael@0: michael@0: if (nullptr == *ppv) { michael@0: HRESULT hr = ia2AccessibleValue::QueryInterface(iid, ppv); michael@0: if (SUCCEEDED(hr)) michael@0: return hr; michael@0: } michael@0: michael@0: if (nullptr == *ppv) michael@0: return E_NOINTERFACE; michael@0: michael@0: (reinterpret_cast(*ppv))->AddRef(); michael@0: return S_OK; michael@0: michael@0: A11Y_TRYBLOCK_END michael@0: } michael@0: michael@0: //----------------------------------------------------- michael@0: // IAccessible methods michael@0: //----------------------------------------------------- michael@0: michael@0: STDMETHODIMP michael@0: AccessibleWrap::get_accParent( IDispatch __RPC_FAR *__RPC_FAR *ppdispParent) michael@0: { michael@0: A11Y_TRYBLOCK_BEGIN michael@0: michael@0: if (!ppdispParent) michael@0: return E_INVALIDARG; michael@0: michael@0: *ppdispParent = nullptr; michael@0: michael@0: if (IsDefunct()) michael@0: return CO_E_OBJNOTCONNECTED; michael@0: michael@0: DocAccessible* doc = AsDoc(); michael@0: if (doc) { michael@0: // Return window system accessible object for root document and tab document michael@0: // accessibles. michael@0: if (!doc->ParentDocument() || michael@0: (nsWinUtils::IsWindowEmulationStarted() && michael@0: nsCoreUtils::IsTabDocument(doc->DocumentNode()))) { michael@0: HWND hwnd = static_cast(doc->GetNativeWindow()); michael@0: if (hwnd && SUCCEEDED(::AccessibleObjectFromWindow(hwnd, OBJID_WINDOW, michael@0: IID_IAccessible, michael@0: (void**)ppdispParent))) { michael@0: return S_OK; michael@0: } michael@0: } michael@0: } michael@0: michael@0: Accessible* xpParentAcc = Parent(); michael@0: if (!xpParentAcc) michael@0: return S_FALSE; michael@0: michael@0: *ppdispParent = NativeAccessible(xpParentAcc); michael@0: return S_OK; michael@0: michael@0: A11Y_TRYBLOCK_END michael@0: } michael@0: michael@0: STDMETHODIMP michael@0: AccessibleWrap::get_accChildCount( long __RPC_FAR *pcountChildren) michael@0: { michael@0: A11Y_TRYBLOCK_BEGIN michael@0: michael@0: if (!pcountChildren) michael@0: return E_INVALIDARG; michael@0: michael@0: *pcountChildren = 0; michael@0: michael@0: if (IsDefunct()) michael@0: return CO_E_OBJNOTCONNECTED; michael@0: michael@0: if (nsAccUtils::MustPrune(this)) michael@0: return S_OK; michael@0: michael@0: *pcountChildren = ChildCount(); michael@0: return S_OK; michael@0: michael@0: A11Y_TRYBLOCK_END michael@0: } michael@0: michael@0: STDMETHODIMP michael@0: AccessibleWrap::get_accChild( michael@0: /* [in] */ VARIANT varChild, michael@0: /* [retval][out] */ IDispatch __RPC_FAR *__RPC_FAR *ppdispChild) michael@0: { michael@0: A11Y_TRYBLOCK_BEGIN michael@0: michael@0: if (!ppdispChild) michael@0: return E_INVALIDARG; michael@0: michael@0: *ppdispChild = nullptr; michael@0: if (IsDefunct()) michael@0: return CO_E_OBJNOTCONNECTED; michael@0: michael@0: // IAccessible::accChild is used to return this accessible or child accessible michael@0: // at the given index or to get an accessible by child ID in the case of michael@0: // document accessible (it's handled by overriden GetXPAccessibleFor method michael@0: // on the document accessible). The getting an accessible by child ID is used michael@0: // by AccessibleObjectFromEvent() called by AT when AT handles our MSAA event. michael@0: Accessible* child = GetXPAccessibleFor(varChild); michael@0: if (!child) michael@0: return E_INVALIDARG; michael@0: michael@0: if (child->IsDefunct()) michael@0: return CO_E_OBJNOTCONNECTED; michael@0: michael@0: *ppdispChild = NativeAccessible(child); michael@0: return S_OK; michael@0: michael@0: A11Y_TRYBLOCK_END michael@0: } michael@0: michael@0: STDMETHODIMP michael@0: AccessibleWrap::get_accName( michael@0: /* [optional][in] */ VARIANT varChild, michael@0: /* [retval][out] */ BSTR __RPC_FAR *pszName) michael@0: { michael@0: A11Y_TRYBLOCK_BEGIN michael@0: michael@0: if (!pszName) michael@0: return E_INVALIDARG; michael@0: michael@0: *pszName = nullptr; michael@0: michael@0: if (IsDefunct()) michael@0: return CO_E_OBJNOTCONNECTED; michael@0: michael@0: Accessible* xpAccessible = GetXPAccessibleFor(varChild); michael@0: if (!xpAccessible) michael@0: return E_INVALIDARG; michael@0: michael@0: if (xpAccessible->IsDefunct()) michael@0: return CO_E_OBJNOTCONNECTED; michael@0: michael@0: nsAutoString name; michael@0: xpAccessible->Name(name); michael@0: michael@0: // The name was not provided, e.g. no alt attribute for an image. A screen michael@0: // reader may choose to invent its own accessible name, e.g. from an image src michael@0: // attribute. Refer to eNoNameOnPurpose return value. michael@0: if (name.IsVoid()) michael@0: return S_FALSE; michael@0: michael@0: *pszName = ::SysAllocStringLen(name.get(), name.Length()); michael@0: if (!*pszName) michael@0: return E_OUTOFMEMORY; michael@0: return S_OK; michael@0: michael@0: A11Y_TRYBLOCK_END michael@0: } michael@0: michael@0: michael@0: STDMETHODIMP michael@0: AccessibleWrap::get_accValue( michael@0: /* [optional][in] */ VARIANT varChild, michael@0: /* [retval][out] */ BSTR __RPC_FAR *pszValue) michael@0: { michael@0: A11Y_TRYBLOCK_BEGIN michael@0: michael@0: if (!pszValue) michael@0: return E_INVALIDARG; michael@0: michael@0: *pszValue = nullptr; michael@0: michael@0: if (IsDefunct()) michael@0: return CO_E_OBJNOTCONNECTED; michael@0: michael@0: Accessible* xpAccessible = GetXPAccessibleFor(varChild); michael@0: if (!xpAccessible) michael@0: return E_INVALIDARG; michael@0: michael@0: if (xpAccessible->IsDefunct()) michael@0: return CO_E_OBJNOTCONNECTED; michael@0: michael@0: if (xpAccessible->NativeRole() == roles::PASSWORD_TEXT) michael@0: return E_ACCESSDENIED; michael@0: michael@0: nsAutoString value; michael@0: xpAccessible->Value(value); michael@0: michael@0: // See bug 438784: need to expose URL on doc's value attribute. For this, michael@0: // reverting part of fix for bug 425693 to make this MSAA method behave michael@0: // IAccessible2-style. michael@0: if (value.IsEmpty()) michael@0: return S_FALSE; michael@0: michael@0: *pszValue = ::SysAllocStringLen(value.get(), value.Length()); michael@0: if (!*pszValue) michael@0: return E_OUTOFMEMORY; michael@0: return S_OK; michael@0: michael@0: A11Y_TRYBLOCK_END michael@0: } michael@0: michael@0: STDMETHODIMP michael@0: AccessibleWrap::get_accDescription(VARIANT varChild, michael@0: BSTR __RPC_FAR *pszDescription) michael@0: { michael@0: A11Y_TRYBLOCK_BEGIN michael@0: michael@0: if (!pszDescription) michael@0: return E_INVALIDARG; michael@0: michael@0: *pszDescription = nullptr; michael@0: michael@0: if (IsDefunct()) michael@0: return CO_E_OBJNOTCONNECTED; michael@0: michael@0: Accessible* xpAccessible = GetXPAccessibleFor(varChild); michael@0: if (!xpAccessible) michael@0: return E_INVALIDARG; michael@0: michael@0: if (xpAccessible->IsDefunct()) michael@0: return CO_E_OBJNOTCONNECTED; michael@0: michael@0: nsAutoString description; michael@0: xpAccessible->Description(description); michael@0: michael@0: *pszDescription = ::SysAllocStringLen(description.get(), michael@0: description.Length()); michael@0: return *pszDescription ? S_OK : E_OUTOFMEMORY; michael@0: michael@0: A11Y_TRYBLOCK_END michael@0: } michael@0: michael@0: STDMETHODIMP michael@0: AccessibleWrap::get_accRole( michael@0: /* [optional][in] */ VARIANT varChild, michael@0: /* [retval][out] */ VARIANT __RPC_FAR *pvarRole) michael@0: { michael@0: A11Y_TRYBLOCK_BEGIN michael@0: michael@0: if (!pvarRole) michael@0: return E_INVALIDARG; michael@0: michael@0: VariantInit(pvarRole); michael@0: michael@0: if (IsDefunct()) michael@0: return CO_E_OBJNOTCONNECTED; michael@0: michael@0: Accessible* xpAccessible = GetXPAccessibleFor(varChild); michael@0: if (!xpAccessible) michael@0: return E_INVALIDARG; michael@0: michael@0: if (xpAccessible->IsDefunct()) michael@0: return CO_E_OBJNOTCONNECTED; michael@0: michael@0: #ifdef DEBUG michael@0: NS_ASSERTION(nsAccUtils::IsTextInterfaceSupportCorrect(xpAccessible), michael@0: "Does not support nsIAccessibleText when it should"); michael@0: #endif michael@0: michael@0: a11y::role geckoRole = xpAccessible->Role(); michael@0: uint32_t msaaRole = 0; michael@0: michael@0: #define ROLE(_geckoRole, stringRole, atkRole, macRole, \ michael@0: _msaaRole, ia2Role, nameRule) \ michael@0: case roles::_geckoRole: \ michael@0: msaaRole = _msaaRole; \ michael@0: break; michael@0: michael@0: switch (geckoRole) { michael@0: #include "RoleMap.h" michael@0: default: michael@0: MOZ_CRASH("Unknown role."); michael@0: }; michael@0: michael@0: #undef ROLE michael@0: michael@0: // Special case, if there is a ROLE_ROW inside of a ROLE_TREE_TABLE, then call the MSAA role michael@0: // a ROLE_OUTLINEITEM for consistency and compatibility. michael@0: // We need this because ARIA has a role of "row" for both grid and treegrid michael@0: if (geckoRole == roles::ROW) { michael@0: Accessible* xpParent = Parent(); michael@0: if (xpParent && xpParent->Role() == roles::TREE_TABLE) michael@0: msaaRole = ROLE_SYSTEM_OUTLINEITEM; michael@0: } michael@0: michael@0: // -- Try enumerated role michael@0: if (msaaRole != USE_ROLE_STRING) { michael@0: pvarRole->vt = VT_I4; michael@0: pvarRole->lVal = msaaRole; // Normal enumerated role michael@0: return S_OK; michael@0: } michael@0: michael@0: // -- Try BSTR role michael@0: // Could not map to known enumerated MSAA role like ROLE_BUTTON michael@0: // Use BSTR role to expose role attribute or tag name + namespace michael@0: nsIContent *content = xpAccessible->GetContent(); michael@0: if (!content) michael@0: return E_FAIL; michael@0: michael@0: if (content->IsElement()) { michael@0: nsAutoString roleString; michael@0: if (msaaRole != ROLE_SYSTEM_CLIENT && michael@0: !content->GetAttr(kNameSpaceID_None, nsGkAtoms::role, roleString)) { michael@0: nsIDocument * document = content->GetCurrentDoc(); michael@0: if (!document) michael@0: return E_FAIL; michael@0: michael@0: nsINodeInfo *nodeInfo = content->NodeInfo(); michael@0: nodeInfo->GetName(roleString); michael@0: michael@0: // Only append name space if different from that of current document. michael@0: if (!nodeInfo->NamespaceEquals(document->GetDefaultNamespaceID())) { michael@0: nsAutoString nameSpaceURI; michael@0: nodeInfo->GetNamespaceURI(nameSpaceURI); michael@0: roleString += NS_LITERAL_STRING(", ") + nameSpaceURI; michael@0: } michael@0: } michael@0: michael@0: if (!roleString.IsEmpty()) { michael@0: pvarRole->vt = VT_BSTR; michael@0: pvarRole->bstrVal = ::SysAllocString(roleString.get()); michael@0: return S_OK; michael@0: } michael@0: } michael@0: michael@0: return E_FAIL; michael@0: michael@0: A11Y_TRYBLOCK_END michael@0: } michael@0: michael@0: STDMETHODIMP michael@0: AccessibleWrap::get_accState( michael@0: /* [optional][in] */ VARIANT varChild, michael@0: /* [retval][out] */ VARIANT __RPC_FAR *pvarState) michael@0: { michael@0: A11Y_TRYBLOCK_BEGIN michael@0: michael@0: if (!pvarState) michael@0: return E_INVALIDARG; michael@0: michael@0: VariantInit(pvarState); michael@0: pvarState->vt = VT_I4; michael@0: pvarState->lVal = 0; michael@0: michael@0: if (IsDefunct()) michael@0: return CO_E_OBJNOTCONNECTED; michael@0: michael@0: Accessible* xpAccessible = GetXPAccessibleFor(varChild); michael@0: if (!xpAccessible) michael@0: return E_INVALIDARG; michael@0: michael@0: if (xpAccessible->IsDefunct()) michael@0: return CO_E_OBJNOTCONNECTED; michael@0: michael@0: // MSAA only has 31 states and the lowest 31 bits of our state bit mask michael@0: // are the same states as MSAA. michael@0: // Note: we map the following Gecko states to different MSAA states: michael@0: // REQUIRED -> ALERT_LOW michael@0: // ALERT -> ALERT_MEDIUM michael@0: // INVALID -> ALERT_HIGH michael@0: // CHECKABLE -> MARQUEED michael@0: michael@0: uint32_t msaaState = 0; michael@0: nsAccUtils::To32States(xpAccessible->State(), &msaaState, nullptr); michael@0: pvarState->lVal = msaaState; michael@0: return S_OK; michael@0: michael@0: A11Y_TRYBLOCK_END michael@0: } michael@0: michael@0: michael@0: STDMETHODIMP michael@0: AccessibleWrap::get_accHelp( michael@0: /* [optional][in] */ VARIANT varChild, michael@0: /* [retval][out] */ BSTR __RPC_FAR *pszHelp) michael@0: { michael@0: A11Y_TRYBLOCK_BEGIN michael@0: michael@0: if (!pszHelp) michael@0: return E_INVALIDARG; michael@0: michael@0: *pszHelp = nullptr; michael@0: return S_FALSE; michael@0: michael@0: A11Y_TRYBLOCK_END michael@0: } michael@0: michael@0: STDMETHODIMP michael@0: AccessibleWrap::get_accHelpTopic( michael@0: /* [out] */ BSTR __RPC_FAR *pszHelpFile, michael@0: /* [optional][in] */ VARIANT varChild, michael@0: /* [retval][out] */ long __RPC_FAR *pidTopic) michael@0: { michael@0: A11Y_TRYBLOCK_BEGIN michael@0: michael@0: if (!pszHelpFile || !pidTopic) michael@0: return E_INVALIDARG; michael@0: michael@0: *pszHelpFile = nullptr; michael@0: *pidTopic = 0; michael@0: return S_FALSE; michael@0: michael@0: A11Y_TRYBLOCK_END michael@0: } michael@0: michael@0: STDMETHODIMP michael@0: AccessibleWrap::get_accKeyboardShortcut( michael@0: /* [optional][in] */ VARIANT varChild, michael@0: /* [retval][out] */ BSTR __RPC_FAR *pszKeyboardShortcut) michael@0: { michael@0: A11Y_TRYBLOCK_BEGIN michael@0: michael@0: if (!pszKeyboardShortcut) michael@0: return E_INVALIDARG; michael@0: *pszKeyboardShortcut = nullptr; michael@0: michael@0: if (IsDefunct()) michael@0: return CO_E_OBJNOTCONNECTED; michael@0: michael@0: Accessible* acc = GetXPAccessibleFor(varChild); michael@0: if (!acc) michael@0: return E_INVALIDARG; michael@0: michael@0: if (acc->IsDefunct()) michael@0: return CO_E_OBJNOTCONNECTED; michael@0: michael@0: KeyBinding keyBinding = acc->AccessKey(); michael@0: if (keyBinding.IsEmpty()) michael@0: keyBinding = acc->KeyboardShortcut(); michael@0: michael@0: nsAutoString shortcut; michael@0: keyBinding.ToString(shortcut); michael@0: michael@0: *pszKeyboardShortcut = ::SysAllocStringLen(shortcut.get(), michael@0: shortcut.Length()); michael@0: return *pszKeyboardShortcut ? S_OK : E_OUTOFMEMORY; michael@0: michael@0: A11Y_TRYBLOCK_END michael@0: } michael@0: michael@0: STDMETHODIMP michael@0: AccessibleWrap::get_accFocus( michael@0: /* [retval][out] */ VARIANT __RPC_FAR *pvarChild) michael@0: { michael@0: A11Y_TRYBLOCK_BEGIN michael@0: michael@0: if (!pvarChild) michael@0: return E_INVALIDARG; michael@0: michael@0: VariantInit(pvarChild); michael@0: michael@0: // VT_EMPTY: None. This object does not have the keyboard focus itself michael@0: // and does not contain a child that has the keyboard focus. michael@0: // VT_I4: lVal is CHILDID_SELF. The object itself has the keyboard focus. michael@0: // VT_I4: lVal contains the child ID of the child element with the keyboard focus. michael@0: // VT_DISPATCH: pdispVal member is the address of the IDispatch interface michael@0: // for the child object with the keyboard focus. michael@0: if (IsDefunct()) michael@0: return CO_E_OBJNOTCONNECTED; michael@0: michael@0: // Return the current IAccessible child that has focus michael@0: Accessible* focusedAccessible = FocusedChild(); michael@0: if (focusedAccessible == this) { michael@0: pvarChild->vt = VT_I4; michael@0: pvarChild->lVal = CHILDID_SELF; michael@0: } michael@0: else if (focusedAccessible) { michael@0: pvarChild->vt = VT_DISPATCH; michael@0: pvarChild->pdispVal = NativeAccessible(focusedAccessible); michael@0: } michael@0: else { michael@0: pvarChild->vt = VT_EMPTY; // No focus or focus is not a child michael@0: } michael@0: michael@0: return S_OK; michael@0: michael@0: A11Y_TRYBLOCK_END michael@0: } michael@0: michael@0: // This helper class implements IEnumVARIANT for a nsIArray containing nsIAccessible objects. michael@0: michael@0: class AccessibleEnumerator MOZ_FINAL : public IEnumVARIANT michael@0: { michael@0: public: michael@0: AccessibleEnumerator(nsIArray* aArray) : mArray(aArray), mCurIndex(0) { } michael@0: AccessibleEnumerator(const AccessibleEnumerator& toCopy) : michael@0: mArray(toCopy.mArray), mCurIndex(toCopy.mCurIndex) { } michael@0: ~AccessibleEnumerator() { } michael@0: michael@0: // IUnknown michael@0: DECL_IUNKNOWN michael@0: michael@0: // IEnumVARIANT michael@0: STDMETHODIMP Next(unsigned long celt, VARIANT FAR* rgvar, unsigned long FAR* pceltFetched); michael@0: STDMETHODIMP Skip(unsigned long celt); michael@0: STDMETHODIMP Reset() michael@0: { michael@0: mCurIndex = 0; michael@0: return S_OK; michael@0: } michael@0: STDMETHODIMP Clone(IEnumVARIANT FAR* FAR* ppenum); michael@0: michael@0: private: michael@0: nsCOMPtr mArray; michael@0: uint32_t mCurIndex; michael@0: }; michael@0: michael@0: STDMETHODIMP michael@0: AccessibleEnumerator::QueryInterface(REFIID iid, void ** ppvObject) michael@0: { michael@0: A11Y_TRYBLOCK_BEGIN michael@0: michael@0: if (iid == IID_IEnumVARIANT) { michael@0: *ppvObject = static_cast(this); michael@0: AddRef(); michael@0: return S_OK; michael@0: } michael@0: if (iid == IID_IUnknown) { michael@0: *ppvObject = static_cast(this); michael@0: AddRef(); michael@0: return S_OK; michael@0: } michael@0: michael@0: *ppvObject = nullptr; michael@0: return E_NOINTERFACE; michael@0: michael@0: A11Y_TRYBLOCK_END michael@0: } michael@0: michael@0: STDMETHODIMP michael@0: AccessibleEnumerator::Next(unsigned long celt, VARIANT FAR* rgvar, unsigned long FAR* pceltFetched) michael@0: { michael@0: A11Y_TRYBLOCK_BEGIN michael@0: michael@0: uint32_t length = 0; michael@0: mArray->GetLength(&length); michael@0: michael@0: HRESULT hr = S_OK; michael@0: michael@0: // Can't get more elements than there are... michael@0: if (celt > length - mCurIndex) { michael@0: hr = S_FALSE; michael@0: celt = length - mCurIndex; michael@0: } michael@0: michael@0: for (uint32_t i = 0; i < celt; ++i, ++mCurIndex) { michael@0: // Copy the elements of the array into rgvar michael@0: nsCOMPtr accel(do_QueryElementAt(mArray, mCurIndex)); michael@0: NS_ASSERTION(accel, "Invalid pointer in mArray"); michael@0: michael@0: if (accel) { michael@0: rgvar[i].vt = VT_DISPATCH; michael@0: rgvar[i].pdispVal = AccessibleWrap::NativeAccessible(accel); michael@0: } michael@0: } michael@0: michael@0: if (pceltFetched) michael@0: *pceltFetched = celt; michael@0: michael@0: return hr; michael@0: michael@0: A11Y_TRYBLOCK_END michael@0: } michael@0: michael@0: STDMETHODIMP michael@0: AccessibleEnumerator::Clone(IEnumVARIANT FAR* FAR* ppenum) michael@0: { michael@0: A11Y_TRYBLOCK_BEGIN michael@0: michael@0: *ppenum = new AccessibleEnumerator(*this); michael@0: if (!*ppenum) michael@0: return E_OUTOFMEMORY; michael@0: NS_ADDREF(*ppenum); michael@0: return S_OK; michael@0: michael@0: A11Y_TRYBLOCK_END michael@0: } michael@0: michael@0: STDMETHODIMP michael@0: AccessibleEnumerator::Skip(unsigned long celt) michael@0: { michael@0: A11Y_TRYBLOCK_BEGIN michael@0: michael@0: uint32_t length = 0; michael@0: mArray->GetLength(&length); michael@0: // Check if we can skip the requested number of elements michael@0: if (celt > length - mCurIndex) { michael@0: mCurIndex = length; michael@0: return S_FALSE; michael@0: } michael@0: mCurIndex += celt; michael@0: return S_OK; michael@0: michael@0: A11Y_TRYBLOCK_END michael@0: } michael@0: michael@0: /** michael@0: * This method is called when a client wants to know which children of a node michael@0: * are selected. Note that this method can only find selected children for michael@0: * nsIAccessible object which implement SelectAccessible. michael@0: * michael@0: * The VARIANT return value arguement is expected to either contain a single IAccessible michael@0: * or an IEnumVARIANT of IAccessibles. We return the IEnumVARIANT regardless of the number michael@0: * of children selected, unless there are none selected in which case we return an empty michael@0: * VARIANT. michael@0: * michael@0: * We get the selected options from the select's accessible object and wrap michael@0: * those in an AccessibleEnumerator which we then put in the return VARIANT. michael@0: * michael@0: * returns a VT_EMPTY VARIANT if: michael@0: * - there are no selected children for this object michael@0: * - the object is not the type that can have children selected michael@0: */ michael@0: STDMETHODIMP michael@0: AccessibleWrap::get_accSelection(VARIANT __RPC_FAR *pvarChildren) michael@0: { michael@0: A11Y_TRYBLOCK_BEGIN michael@0: michael@0: if (!pvarChildren) michael@0: return E_INVALIDARG; michael@0: michael@0: VariantInit(pvarChildren); michael@0: pvarChildren->vt = VT_EMPTY; michael@0: michael@0: if (IsDefunct()) michael@0: return CO_E_OBJNOTCONNECTED; michael@0: michael@0: if (IsSelect()) { michael@0: nsCOMPtr selectedItems = SelectedItems(); michael@0: if (selectedItems) { michael@0: // 1) Create and initialize the enumeration michael@0: nsRefPtr pEnum = michael@0: new AccessibleEnumerator(selectedItems); michael@0: michael@0: // 2) Put the enumerator in the VARIANT michael@0: if (!pEnum) michael@0: return E_OUTOFMEMORY; michael@0: pvarChildren->vt = VT_UNKNOWN; // this must be VT_UNKNOWN for an IEnumVARIANT michael@0: NS_ADDREF(pvarChildren->punkVal = pEnum); michael@0: } michael@0: } michael@0: return S_OK; michael@0: michael@0: A11Y_TRYBLOCK_END michael@0: } michael@0: michael@0: STDMETHODIMP michael@0: AccessibleWrap::get_accDefaultAction( michael@0: /* [optional][in] */ VARIANT varChild, michael@0: /* [retval][out] */ BSTR __RPC_FAR *pszDefaultAction) michael@0: { michael@0: A11Y_TRYBLOCK_BEGIN michael@0: michael@0: if (!pszDefaultAction) michael@0: return E_INVALIDARG; michael@0: michael@0: *pszDefaultAction = nullptr; michael@0: michael@0: if (IsDefunct()) michael@0: return CO_E_OBJNOTCONNECTED; michael@0: michael@0: Accessible* xpAccessible = GetXPAccessibleFor(varChild); michael@0: if (!xpAccessible) michael@0: return E_INVALIDARG; michael@0: michael@0: if (xpAccessible->IsDefunct()) michael@0: return CO_E_OBJNOTCONNECTED; michael@0: michael@0: nsAutoString defaultAction; michael@0: if (NS_FAILED(xpAccessible->GetActionName(0, defaultAction))) michael@0: return E_FAIL; michael@0: michael@0: *pszDefaultAction = ::SysAllocStringLen(defaultAction.get(), michael@0: defaultAction.Length()); michael@0: return *pszDefaultAction ? S_OK : E_OUTOFMEMORY; michael@0: michael@0: A11Y_TRYBLOCK_END michael@0: } michael@0: michael@0: STDMETHODIMP michael@0: AccessibleWrap::accSelect( michael@0: /* [in] */ long flagsSelect, michael@0: /* [optional][in] */ VARIANT varChild) michael@0: { michael@0: A11Y_TRYBLOCK_BEGIN michael@0: michael@0: if (IsDefunct()) michael@0: return CO_E_OBJNOTCONNECTED; michael@0: michael@0: // currently only handle focus and selection michael@0: Accessible* xpAccessible = GetXPAccessibleFor(varChild); michael@0: if (!xpAccessible) michael@0: return E_INVALIDARG; michael@0: michael@0: if (xpAccessible->IsDefunct()) michael@0: return CO_E_OBJNOTCONNECTED; michael@0: michael@0: if (flagsSelect & (SELFLAG_TAKEFOCUS|SELFLAG_TAKESELECTION|SELFLAG_REMOVESELECTION)) michael@0: { michael@0: if (flagsSelect & SELFLAG_TAKEFOCUS) michael@0: xpAccessible->TakeFocus(); michael@0: michael@0: if (flagsSelect & SELFLAG_TAKESELECTION) michael@0: xpAccessible->TakeSelection(); michael@0: michael@0: if (flagsSelect & SELFLAG_ADDSELECTION) michael@0: xpAccessible->SetSelected(true); michael@0: michael@0: if (flagsSelect & SELFLAG_REMOVESELECTION) michael@0: xpAccessible->SetSelected(false); michael@0: michael@0: if (flagsSelect & SELFLAG_EXTENDSELECTION) michael@0: xpAccessible->ExtendSelection(); michael@0: michael@0: return S_OK; michael@0: } michael@0: michael@0: return E_FAIL; michael@0: michael@0: A11Y_TRYBLOCK_END michael@0: } michael@0: michael@0: STDMETHODIMP michael@0: AccessibleWrap::accLocation( michael@0: /* [out] */ long __RPC_FAR *pxLeft, michael@0: /* [out] */ long __RPC_FAR *pyTop, michael@0: /* [out] */ long __RPC_FAR *pcxWidth, michael@0: /* [out] */ long __RPC_FAR *pcyHeight, michael@0: /* [optional][in] */ VARIANT varChild) michael@0: { michael@0: A11Y_TRYBLOCK_BEGIN michael@0: michael@0: if (!pxLeft || !pyTop || !pcxWidth || !pcyHeight) michael@0: return E_INVALIDARG; michael@0: michael@0: *pxLeft = 0; michael@0: *pyTop = 0; michael@0: *pcxWidth = 0; michael@0: *pcyHeight = 0; michael@0: michael@0: if (IsDefunct()) michael@0: return CO_E_OBJNOTCONNECTED; michael@0: michael@0: Accessible* xpAccessible = GetXPAccessibleFor(varChild); michael@0: if (!xpAccessible) michael@0: return E_INVALIDARG; michael@0: michael@0: if (xpAccessible->IsDefunct()) michael@0: return CO_E_OBJNOTCONNECTED; michael@0: michael@0: int32_t x, y, width, height; michael@0: if (NS_FAILED(xpAccessible->GetBounds(&x, &y, &width, &height))) michael@0: return E_FAIL; michael@0: michael@0: *pxLeft = x; michael@0: *pyTop = y; michael@0: *pcxWidth = width; michael@0: *pcyHeight = height; michael@0: return S_OK; michael@0: michael@0: A11Y_TRYBLOCK_END michael@0: } michael@0: michael@0: STDMETHODIMP michael@0: AccessibleWrap::accNavigate( michael@0: /* [in] */ long navDir, michael@0: /* [optional][in] */ VARIANT varStart, michael@0: /* [retval][out] */ VARIANT __RPC_FAR *pvarEndUpAt) michael@0: { michael@0: A11Y_TRYBLOCK_BEGIN michael@0: michael@0: if (!pvarEndUpAt) michael@0: return E_INVALIDARG; michael@0: michael@0: VariantInit(pvarEndUpAt); michael@0: michael@0: if (IsDefunct()) michael@0: return CO_E_OBJNOTCONNECTED; michael@0: michael@0: Accessible* accessible = GetXPAccessibleFor(varStart); michael@0: if (!accessible) michael@0: return E_INVALIDARG; michael@0: michael@0: if (accessible->IsDefunct()) michael@0: return CO_E_OBJNOTCONNECTED; michael@0: michael@0: Accessible* navAccessible = nullptr; michael@0: Maybe xpRelation; michael@0: michael@0: #define RELATIONTYPE(geckoType, stringType, atkType, msaaType, ia2Type) \ michael@0: case msaaType: \ michael@0: xpRelation.construct(RelationType::geckoType); \ michael@0: break; michael@0: michael@0: switch(navDir) { michael@0: case NAVDIR_FIRSTCHILD: michael@0: if (!nsAccUtils::MustPrune(accessible)) michael@0: navAccessible = accessible->FirstChild(); michael@0: break; michael@0: case NAVDIR_LASTCHILD: michael@0: if (!nsAccUtils::MustPrune(accessible)) michael@0: navAccessible = accessible->LastChild(); michael@0: break; michael@0: case NAVDIR_NEXT: michael@0: navAccessible = accessible->NextSibling(); michael@0: break; michael@0: case NAVDIR_PREVIOUS: michael@0: navAccessible = accessible->PrevSibling(); michael@0: break; michael@0: case NAVDIR_DOWN: michael@0: case NAVDIR_LEFT: michael@0: case NAVDIR_RIGHT: michael@0: case NAVDIR_UP: michael@0: return E_NOTIMPL; michael@0: michael@0: // MSAA relationship extensions to accNavigate michael@0: #include "RelationTypeMap.h" michael@0: michael@0: default: michael@0: return E_INVALIDARG; michael@0: } michael@0: michael@0: #undef RELATIONTYPE michael@0: michael@0: pvarEndUpAt->vt = VT_EMPTY; michael@0: michael@0: if (!xpRelation.empty()) { michael@0: Relation rel = RelationByType(xpRelation.ref()); michael@0: navAccessible = rel.Next(); michael@0: } michael@0: michael@0: if (!navAccessible) michael@0: return E_FAIL; michael@0: michael@0: pvarEndUpAt->pdispVal = NativeAccessible(navAccessible); michael@0: pvarEndUpAt->vt = VT_DISPATCH; michael@0: return S_OK; michael@0: michael@0: A11Y_TRYBLOCK_END michael@0: } michael@0: michael@0: STDMETHODIMP michael@0: AccessibleWrap::accHitTest( michael@0: /* [in] */ long xLeft, michael@0: /* [in] */ long yTop, michael@0: /* [retval][out] */ VARIANT __RPC_FAR *pvarChild) michael@0: { michael@0: A11Y_TRYBLOCK_BEGIN michael@0: michael@0: if (!pvarChild) michael@0: return E_INVALIDARG; michael@0: michael@0: VariantInit(pvarChild); michael@0: michael@0: if (IsDefunct()) michael@0: return CO_E_OBJNOTCONNECTED; michael@0: michael@0: Accessible* accessible = ChildAtPoint(xLeft, yTop, eDirectChild); michael@0: michael@0: // if we got a child michael@0: if (accessible) { michael@0: // if the child is us michael@0: if (accessible == this) { michael@0: pvarChild->vt = VT_I4; michael@0: pvarChild->lVal = CHILDID_SELF; michael@0: } else { // its not create an Accessible for it. michael@0: pvarChild->vt = VT_DISPATCH; michael@0: pvarChild->pdispVal = NativeAccessible(accessible); michael@0: } michael@0: } else { michael@0: // no child at that point michael@0: pvarChild->vt = VT_EMPTY; michael@0: return S_FALSE; michael@0: } michael@0: return S_OK; michael@0: michael@0: A11Y_TRYBLOCK_END michael@0: } michael@0: michael@0: STDMETHODIMP michael@0: AccessibleWrap::accDoDefaultAction( michael@0: /* [optional][in] */ VARIANT varChild) michael@0: { michael@0: A11Y_TRYBLOCK_BEGIN michael@0: michael@0: if (IsDefunct()) michael@0: return CO_E_OBJNOTCONNECTED; michael@0: michael@0: Accessible* xpAccessible = GetXPAccessibleFor(varChild); michael@0: if (!xpAccessible) michael@0: return E_INVALIDARG; michael@0: michael@0: if (xpAccessible->IsDefunct()) michael@0: return CO_E_OBJNOTCONNECTED; michael@0: michael@0: return GetHRESULT(xpAccessible->DoAction(0)); michael@0: michael@0: A11Y_TRYBLOCK_END michael@0: } michael@0: michael@0: STDMETHODIMP michael@0: AccessibleWrap::put_accName( michael@0: /* [optional][in] */ VARIANT varChild, michael@0: /* [in] */ BSTR szName) michael@0: { michael@0: return E_NOTIMPL; michael@0: } michael@0: michael@0: STDMETHODIMP michael@0: AccessibleWrap::put_accValue( michael@0: /* [optional][in] */ VARIANT varChild, michael@0: /* [in] */ BSTR szValue) michael@0: { michael@0: return E_NOTIMPL; michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // IDispatch michael@0: michael@0: STDMETHODIMP michael@0: AccessibleWrap::GetTypeInfoCount(UINT *pctinfo) michael@0: { michael@0: if (!pctinfo) michael@0: return E_INVALIDARG; michael@0: michael@0: *pctinfo = 1; michael@0: return S_OK; michael@0: } michael@0: michael@0: STDMETHODIMP michael@0: AccessibleWrap::GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo) michael@0: { michael@0: if (!ppTInfo) michael@0: return E_INVALIDARG; michael@0: michael@0: *ppTInfo = nullptr; michael@0: michael@0: if (iTInfo != 0) michael@0: return DISP_E_BADINDEX; michael@0: michael@0: ITypeInfo * typeInfo = GetTI(lcid); michael@0: if (!typeInfo) michael@0: return E_FAIL; michael@0: michael@0: typeInfo->AddRef(); michael@0: *ppTInfo = typeInfo; michael@0: michael@0: return S_OK; michael@0: } michael@0: michael@0: STDMETHODIMP michael@0: AccessibleWrap::GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, michael@0: UINT cNames, LCID lcid, DISPID *rgDispId) michael@0: { michael@0: ITypeInfo *typeInfo = GetTI(lcid); michael@0: if (!typeInfo) michael@0: return E_FAIL; michael@0: michael@0: HRESULT hr = DispGetIDsOfNames(typeInfo, rgszNames, cNames, rgDispId); michael@0: return hr; michael@0: } michael@0: michael@0: STDMETHODIMP michael@0: AccessibleWrap::Invoke(DISPID dispIdMember, REFIID riid, michael@0: LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, michael@0: VARIANT *pVarResult, EXCEPINFO *pExcepInfo, michael@0: UINT *puArgErr) michael@0: { michael@0: ITypeInfo *typeInfo = GetTI(lcid); michael@0: if (!typeInfo) michael@0: return E_FAIL; michael@0: michael@0: return typeInfo->Invoke(static_cast(this), dispIdMember, michael@0: wFlags, pDispParams, pVarResult, pExcepInfo, michael@0: puArgErr); michael@0: } michael@0: michael@0: michael@0: // nsIAccessible method michael@0: NS_IMETHODIMP michael@0: AccessibleWrap::GetNativeInterface(void **aOutAccessible) michael@0: { michael@0: *aOutAccessible = static_cast(this); michael@0: NS_ADDREF_THIS(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // Accessible michael@0: michael@0: nsresult michael@0: AccessibleWrap::HandleAccEvent(AccEvent* aEvent) michael@0: { michael@0: nsresult rv = Accessible::HandleAccEvent(aEvent); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // Don't fire native MSAA events or mess with the system caret michael@0: // when running in metro mode. This confuses input focus tracking michael@0: // in metro's UIA implementation. michael@0: if (XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Metro) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: uint32_t eventType = aEvent->GetEventType(); michael@0: michael@0: static_assert(sizeof(gWinEventMap)/sizeof(gWinEventMap[0]) == nsIAccessibleEvent::EVENT_LAST_ENTRY, michael@0: "MSAA event map skewed"); michael@0: michael@0: NS_ENSURE_TRUE(eventType > 0 && eventType < ArrayLength(gWinEventMap), NS_ERROR_FAILURE); michael@0: michael@0: uint32_t winEvent = gWinEventMap[eventType]; michael@0: if (!winEvent) michael@0: return NS_OK; michael@0: michael@0: // Means we're not active. michael@0: NS_ENSURE_TRUE(!IsDefunct(), NS_ERROR_FAILURE); michael@0: michael@0: Accessible* accessible = aEvent->GetAccessible(); michael@0: if (!accessible) michael@0: return NS_OK; michael@0: michael@0: if (eventType == nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED || michael@0: eventType == nsIAccessibleEvent::EVENT_FOCUS) { michael@0: UpdateSystemCaretFor(accessible); michael@0: } michael@0: michael@0: int32_t childID = GetChildIDFor(accessible); // get the id for the accessible michael@0: if (!childID) michael@0: return NS_OK; // Can't fire an event without a child ID michael@0: michael@0: HWND hWnd = GetHWNDFor(accessible); michael@0: NS_ENSURE_TRUE(hWnd, NS_ERROR_FAILURE); michael@0: michael@0: nsAutoString tag; michael@0: nsAutoCString id; michael@0: nsIContent* cnt = accessible->GetContent(); michael@0: if (cnt) { michael@0: cnt->Tag()->ToString(tag); michael@0: nsIAtom* aid = cnt->GetID(); michael@0: if (aid) michael@0: aid->ToUTF8String(id); michael@0: } michael@0: michael@0: #ifdef A11Y_LOG michael@0: if (logging::IsEnabled(logging::ePlatforms)) { michael@0: printf("\n\nMSAA event: event: %d, target: %s@id='%s', childid: %d, hwnd: %d\n\n", michael@0: eventType, NS_ConvertUTF16toUTF8(tag).get(), id.get(), michael@0: childID, hWnd); michael@0: } michael@0: #endif michael@0: michael@0: // Fire MSAA event for client area window. michael@0: ::NotifyWinEvent(winEvent, hWnd, OBJID_CLIENT, childID); michael@0: michael@0: // JAWS announces collapsed combobox navigation based on focus events. michael@0: if (Compatibility::IsJAWS()) { michael@0: if (eventType == nsIAccessibleEvent::EVENT_SELECTION && michael@0: accessible->Role() == roles::COMBOBOX_OPTION) { michael@0: ::NotifyWinEvent(EVENT_OBJECT_FOCUS, hWnd, OBJID_CLIENT, childID); michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // AccessibleWrap michael@0: michael@0: //------- Helper methods --------- michael@0: michael@0: int32_t michael@0: AccessibleWrap::GetChildIDFor(Accessible* aAccessible) michael@0: { michael@0: // A child ID of the window is required, when we use NotifyWinEvent, michael@0: // so that the 3rd party application can call back and get the IAccessible michael@0: // the event occurred on. michael@0: michael@0: // Yes, this means we're only compatibible with 32 bit michael@0: // MSAA is only available for 32 bit windows, so it's okay michael@0: // XXX: bug 606080 michael@0: return aAccessible ? - NS_PTR_TO_INT32(aAccessible->UniqueID()) : 0; michael@0: } michael@0: michael@0: HWND michael@0: AccessibleWrap::GetHWNDFor(Accessible* aAccessible) michael@0: { michael@0: if (aAccessible) { michael@0: DocAccessible* document = aAccessible->Document(); michael@0: if(!document) michael@0: return nullptr; michael@0: michael@0: // Popup lives in own windows, use its HWND until the popup window is michael@0: // hidden to make old JAWS versions work with collapsed comboboxes (see michael@0: // discussion in bug 379678). michael@0: nsIFrame* frame = aAccessible->GetFrame(); michael@0: if (frame) { michael@0: nsIWidget* widget = frame->GetNearestWidget(); michael@0: if (widget && widget->IsVisible()) { michael@0: nsIPresShell* shell = document->PresShell(); michael@0: nsViewManager* vm = shell->GetViewManager(); michael@0: if (vm) { michael@0: nsCOMPtr rootWidget; michael@0: vm->GetRootWidget(getter_AddRefs(rootWidget)); michael@0: // Make sure the accessible belongs to popup. If not then use michael@0: // document HWND (which might be different from root widget in the michael@0: // case of window emulation). michael@0: if (rootWidget != widget) michael@0: return static_cast(widget->GetNativeData(NS_NATIVE_WINDOW)); michael@0: } michael@0: } michael@0: } michael@0: michael@0: return static_cast(document->GetNativeWindow()); michael@0: } michael@0: return nullptr; michael@0: } michael@0: michael@0: IDispatch* michael@0: AccessibleWrap::NativeAccessible(nsIAccessible* aAccessible) michael@0: { michael@0: if (!aAccessible) { michael@0: NS_WARNING("Not passing in an aAccessible"); michael@0: return nullptr; michael@0: } michael@0: michael@0: IAccessible* msaaAccessible = nullptr; michael@0: aAccessible->GetNativeInterface(reinterpret_cast(&msaaAccessible)); michael@0: return static_cast(msaaAccessible); michael@0: } michael@0: michael@0: Accessible* michael@0: AccessibleWrap::GetXPAccessibleFor(const VARIANT& aVarChild) michael@0: { michael@0: if (aVarChild.vt != VT_I4) michael@0: return nullptr; michael@0: michael@0: // if its us real easy - this seems to always be the case michael@0: if (aVarChild.lVal == CHILDID_SELF) michael@0: return this; michael@0: michael@0: if (nsAccUtils::MustPrune(this)) michael@0: return nullptr; michael@0: michael@0: // If lVal negative then it is treated as child ID and we should look for michael@0: // accessible through whole accessible subtree including subdocuments. michael@0: // Otherwise we treat lVal as index in parent. michael@0: michael@0: if (aVarChild.lVal < 0) { michael@0: // Convert child ID to unique ID. michael@0: void* uniqueID = reinterpret_cast(-aVarChild.lVal); michael@0: michael@0: DocAccessible* document = Document(); michael@0: Accessible* child = michael@0: document->GetAccessibleByUniqueIDInSubtree(uniqueID); michael@0: michael@0: // If it is a document then just return an accessible. michael@0: if (IsDoc()) michael@0: return child; michael@0: michael@0: // Otherwise check whether the accessible is a child (this path works for michael@0: // ARIA documents and popups). michael@0: Accessible* parent = child; michael@0: while (parent && parent != document) { michael@0: if (parent == this) michael@0: return child; michael@0: michael@0: parent = parent->Parent(); michael@0: } michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: // Gecko child indices are 0-based in contrast to indices used in MSAA. michael@0: return GetChildAt(aVarChild.lVal - 1); michael@0: } michael@0: michael@0: void michael@0: AccessibleWrap::UpdateSystemCaretFor(Accessible* aAccessible) michael@0: { michael@0: // Move the system caret so that Windows Tablet Edition and tradional ATs with michael@0: // off-screen model can follow the caret michael@0: ::DestroyCaret(); michael@0: michael@0: HyperTextAccessible* text = aAccessible->AsHyperText(); michael@0: if (!text) michael@0: return; michael@0: michael@0: nsIWidget* widget = nullptr; michael@0: nsIntRect caretRect = text->GetCaretRect(&widget); michael@0: HWND caretWnd; michael@0: if (caretRect.IsEmpty() || !(caretWnd = (HWND)widget->GetNativeData(NS_NATIVE_WINDOW))) { michael@0: return; michael@0: } michael@0: michael@0: // Create invisible bitmap for caret, otherwise its appearance interferes michael@0: // with Gecko caret michael@0: HBITMAP caretBitMap = CreateBitmap(1, caretRect.height, 1, 1, nullptr); michael@0: if (::CreateCaret(caretWnd, caretBitMap, 1, caretRect.height)) { // Also destroys the last caret michael@0: ::ShowCaret(caretWnd); michael@0: RECT windowRect; michael@0: ::GetWindowRect(caretWnd, &windowRect); michael@0: ::SetCaretPos(caretRect.x - windowRect.left, caretRect.y - windowRect.top); michael@0: ::DeleteObject(caretBitMap); michael@0: } michael@0: } michael@0: michael@0: ITypeInfo* michael@0: AccessibleWrap::GetTI(LCID lcid) michael@0: { michael@0: if (gTypeInfo) michael@0: return gTypeInfo; michael@0: michael@0: ITypeLib *typeLib = nullptr; michael@0: HRESULT hr = LoadRegTypeLib(LIBID_Accessibility, 1, 0, lcid, &typeLib); michael@0: if (FAILED(hr)) michael@0: return nullptr; michael@0: michael@0: hr = typeLib->GetTypeInfoOfGuid(IID_IAccessible, &gTypeInfo); michael@0: typeLib->Release(); michael@0: michael@0: if (FAILED(hr)) michael@0: return nullptr; michael@0: michael@0: return gTypeInfo; michael@0: }