diff --git a/accessible/src/msaa/nsDocAccessibleWrap.cpp b/accessible/src/msaa/nsDocAccessibleWrap.cpp index 01efb9f1e1d8..5d21aa0cb46f 100644 --- a/accessible/src/msaa/nsDocAccessibleWrap.cpp +++ b/accessible/src/msaa/nsDocAccessibleWrap.cpp @@ -93,66 +93,47 @@ STDMETHODIMP nsDocAccessibleWrap::QueryInterface(REFIID iid, void** ppv) return S_OK; } -void nsDocAccessibleWrap::GetXPAccessibleFor(const VARIANT& aVarChild, nsIAccessible **aXPAccessible) +void +nsDocAccessibleWrap::GetXPAccessibleFor(const VARIANT& aVarChild, + nsIAccessible **aXPAccessible) { *aXPAccessible = nsnull; - if (!mWeakShell) - return; // This document has been shut down - if (aVarChild.lVal < 0) { - // Get from hash table - void *uniqueID = (void*)(-aVarChild.lVal); // Convert child ID back to unique ID - nsCOMPtr accessNode; - GetCachedAccessNode(uniqueID, getter_AddRefs(accessNode)); - nsCOMPtr accessible(do_QueryInterface(accessNode)); - NS_IF_ADDREF(*aXPAccessible = accessible); + if (IsDefunct()) return; - } - nsDocAccessible::GetXPAccessibleFor(aVarChild, aXPAccessible); + // If lVal negative then it is treated as child ID and we should look for + // accessible through whole accessible subtree including subdocuments. + // Otherwise we treat lVal as index in parent. + + if (aVarChild.lVal < 0) + GetXPAccessibleForChildID(aVarChild, aXPAccessible); + else + nsDocAccessible::GetXPAccessibleFor(aVarChild, aXPAccessible); } -STDMETHODIMP nsDocAccessibleWrap::get_accChild( - /* [in] */ VARIANT varChild, - /* [retval][out] */ IDispatch __RPC_FAR *__RPC_FAR *ppdispChild) +STDMETHODIMP +nsDocAccessibleWrap::get_accChild(VARIANT varChild, + IDispatch __RPC_FAR *__RPC_FAR *ppdispChild) { __try { *ppdispChild = NULL; if (varChild.vt == VT_I4 && varChild.lVal < 0) { - // AccessibleObjectFromEvent() being called - // that's why the lVal < 0 + // IAccessible::accChild can be used to get an accessible by child ID. + // It is used by AccessibleObjectFromEvent() called by AT when AT handles + // our MSAA event. + nsCOMPtr xpAccessible; - GetXPAccessibleFor(varChild, getter_AddRefs(xpAccessible)); - if (xpAccessible) { - IAccessible *msaaAccessible; - xpAccessible->GetNativeInterface((void**)&msaaAccessible); - *ppdispChild = static_cast(msaaAccessible); - return S_OK; - } - else if (mDocument) { - // If child ID from event can't be found in this window, ask parent. - // This is especially relevant for times when a xul menu item - // has focus, but the system thinks the content window has focus. - nsIDocument* parentDoc = mDocument->GetParentDocument(); - if (parentDoc) { - nsIPresShell *parentShell = parentDoc->GetPrimaryShell(); - nsCOMPtr weakParentShell(do_GetWeakReference(parentShell)); - if (weakParentShell) { - nsCOMPtr parentDocAccessible = - nsAccessNode::GetDocAccessibleFor(weakParentShell); - nsCOMPtr accessible(do_QueryInterface(parentDocAccessible)); - IAccessible *msaaParentDoc; - if (accessible) { - accessible->GetNativeInterface((void**)&msaaParentDoc); - HRESULT rv = msaaParentDoc->get_accChild(varChild, ppdispChild); - msaaParentDoc->Release(); - return rv; - } - } - } - } - return E_FAIL; + GetXPAccessibleForChildID(varChild, getter_AddRefs(xpAccessible)); + if (!xpAccessible) + return E_FAIL; + + IAccessible *msaaAccessible = NULL; + xpAccessible->GetNativeInterface((void**)&msaaAccessible); + *ppdispChild = static_cast(msaaAccessible); + + return S_OK; } // Otherwise, the normal get_accChild() will do @@ -329,3 +310,53 @@ STDMETHODIMP nsDocAccessibleWrap::get_accValue( return get_URL(pszValue); } + +struct nsSearchAccessibleInCacheArg +{ + nsCOMPtr mAccessNode; + void *mUniqueID; +}; + +static PLDHashOperator +SearchAccessibleInCache(const void* aKey, nsIAccessNode* aAccessNode, + void* aUserArg) +{ + nsCOMPtr docAccessible(do_QueryInterface(aAccessNode)); + NS_ASSERTION(docAccessible, + "No doc accessible for the object in doc accessible cache!"); + + if (docAccessible) { + nsSearchAccessibleInCacheArg* arg = + static_cast(aUserArg); + nsCOMPtr accessNode; + docAccessible->GetCachedAccessNode(arg->mUniqueID, + getter_AddRefs(accessNode)); + if (accessNode) { + arg->mAccessNode = accessNode; + return PL_DHASH_STOP; + } + } + + return PL_DHASH_NEXT; +} + +void +nsDocAccessibleWrap::GetXPAccessibleForChildID(const VARIANT& aVarChild, + nsIAccessible **aAccessible) +{ + *aAccessible = nsnull; + + NS_PRECONDITION(aVarChild.vt == VT_I4 && aVarChild.lVal < 0, + "Variant doesn't point to child ID!"); + + // Convert child ID to unique ID. + void *uniqueID = reinterpret_cast(-aVarChild.lVal); + + nsSearchAccessibleInCacheArg arg; + arg.mUniqueID = uniqueID; + + gGlobalDocAccessibleCache.EnumerateRead(SearchAccessibleInCache, + static_cast(&arg)); + if (arg.mAccessNode) + CallQueryInterface(arg.mAccessNode, aAccessible); +} diff --git a/accessible/src/msaa/nsDocAccessibleWrap.h b/accessible/src/msaa/nsDocAccessibleWrap.h index 6eb658cf8266..b16db8e09a5a 100644 --- a/accessible/src/msaa/nsDocAccessibleWrap.h +++ b/accessible/src/msaa/nsDocAccessibleWrap.h @@ -93,6 +93,17 @@ public: /* [retval][out] */ BSTR __RPC_FAR *pszValue); virtual void FireAnchorJumpEvent(); + + // nsDocAccessibleWrap + + /** + * Find an accessible by the given child ID in cached documents. + * + * @param aVarChild [in] variant pointing to the child ID + * @param aAccessible [out] the found accessible + */ + static void GetXPAccessibleForChildID(const VARIANT& aVarChild, + nsIAccessible **aAccessible); }; #endif