diff --git a/accessible/public/nsIAccessibleDocument.idl b/accessible/public/nsIAccessibleDocument.idl index 8157b363793..73d909c065f 100644 --- a/accessible/public/nsIAccessibleDocument.idl +++ b/accessible/public/nsIAccessibleDocument.idl @@ -56,7 +56,7 @@ interface nsIDOMWindow; * nsIAccessNode::GetAccessibleDocument() or * nsIAccessibleEvent::GetAccessibleDocument() */ -[scriptable, uuid(03c6ce8a-aa40-4484-9282-e6579c56e054)] +[scriptable, uuid(451242bd-8a0c-4198-ae88-c053609a4e5d)] interface nsIAccessibleDocument : nsISupports { /** @@ -99,4 +99,19 @@ interface nsIAccessibleDocument : nsISupports * For example, in Windows you can static cast it to an HWND. */ [noscript] readonly attribute voidPtr windowHandle; + + /** + * Return the parent document accessible. + */ + readonly attribute nsIAccessibleDocument parentDocument; + + /** + * Return the count of child document accessibles. + */ + readonly attribute unsigned long childDocumentCount; + + /** + * Return the child document accessible at the given index. + */ + nsIAccessibleDocument getChildDocumentAt(in unsigned long index); }; diff --git a/accessible/src/base/nsDocAccessible.cpp b/accessible/src/base/nsDocAccessible.cpp index ef72c74f7f0..6cf01262806 100644 --- a/accessible/src/base/nsDocAccessible.cpp +++ b/accessible/src/base/nsDocAccessible.cpp @@ -133,11 +133,18 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsDocAccessible, nsAccessible) NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mEventQueue"); cb.NoteXPCOMChild(tmp->mEventQueue.get()); + PRUint32 i, length = tmp->mChildDocuments.Length(); + for (i = 0; i < length; ++i) { + NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mChildDocuments[i]"); + cb.NoteXPCOMChild(static_cast(tmp->mChildDocuments[i].get())); + } + CycleCollectorTraverseCache(tmp->mAccessibleCache, &cb); NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsDocAccessible, nsAccessible) NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mEventQueue) + NS_IMPL_CYCLE_COLLECTION_UNLINK_NSTARRAY(mChildDocuments) ClearCache(tmp->mAccessibleCache); NS_IMPL_CYCLE_COLLECTION_UNLINK_END @@ -498,6 +505,44 @@ nsDocAccessible::GetDOMDocument(nsIDOMDocument **aDOMDocument) return NS_OK; } +NS_IMETHODIMP +nsDocAccessible::GetParentDocument(nsIAccessibleDocument** aDocument) +{ + NS_ENSURE_ARG_POINTER(aDocument); + *aDocument = nsnull; + + if (!IsDefunct()) + NS_IF_ADDREF(*aDocument = ParentDocument()); + + return NS_OK; +} + +NS_IMETHODIMP +nsDocAccessible::GetChildDocumentCount(PRUint32* aCount) +{ + NS_ENSURE_ARG_POINTER(aCount); + *aCount = 0; + + if (!IsDefunct()) + *aCount = ChildDocumentCount(); + + return NS_OK; +} + +NS_IMETHODIMP +nsDocAccessible::GetChildDocumentAt(PRUint32 aIndex, + nsIAccessibleDocument** aDocument) +{ + NS_ENSURE_ARG_POINTER(aDocument); + *aDocument = nsnull; + + if (IsDefunct()) + return NS_OK; + + NS_IF_ADDREF(*aDocument = GetChildDocumentAt(aIndex)); + return *aDocument ? NS_OK : NS_ERROR_INVALID_ARG; +} + // nsIAccessibleHyperText method NS_IMETHODIMP nsDocAccessible::GetAssociatedEditor(nsIEditor **aEditor) { @@ -531,6 +576,7 @@ NS_IMETHODIMP nsDocAccessible::GetAssociatedEditor(nsIEditor **aEditor) return NS_OK; } +// nsDocAccessible public method nsAccessible * nsDocAccessible::GetCachedAccessible(void *aUniqueID) { @@ -603,6 +649,10 @@ nsDocAccessible::Init() AddEventListeners(); + nsDocAccessible* parentDocument = mParent->GetDocAccessible(); + if (parentDocument) + parentDocument->AppendChildDocument(this); + // Fire reorder event to notify new accessible document has been created and // attached to the tree. nsRefPtr reorderEvent = @@ -629,8 +679,15 @@ nsDocAccessible::Shutdown() RemoveEventListeners(); - if (mParent) + if (mParent) { + nsDocAccessible* parentDocument = mParent->GetDocAccessible(); + if (parentDocument) + parentDocument->RemoveChildDocument(this); + mParent->RemoveChild(this); + } + + mChildDocuments.Clear(); mWeakShell = nsnull; // Avoid reentrancy @@ -1278,6 +1335,23 @@ nsDocAccessible::HandleAccEvent(AccEvent* aAccEvent) //////////////////////////////////////////////////////////////////////////////// // Public members +nsAccessible* +nsDocAccessible::GetCachedAccessibleInSubtree(void* aUniqueID) +{ + nsAccessible* child = GetCachedAccessible(aUniqueID); + if (child) + return child; + + PRUint32 childDocCount = mChildDocuments.Length(); + for (PRUint32 childDocIdx= 0; childDocIdx < childDocCount; childDocIdx++) { + nsDocAccessible* childDocument = mChildDocuments.ElementAt(childDocIdx); + child = childDocument->GetCachedAccessibleInSubtree(aUniqueID); + if (child) + return child; + } + + return nsnull; +} //////////////////////////////////////////////////////////////////////////////// // Protected members diff --git a/accessible/src/base/nsDocAccessible.h b/accessible/src/base/nsDocAccessible.h index 1faff719d8a..4f1daaf3d7d 100644 --- a/accessible/src/base/nsDocAccessible.h +++ b/accessible/src/base/nsDocAccessible.h @@ -141,6 +141,24 @@ public: */ void MarkAsLoaded() { mIsLoaded = PR_TRUE; } + /** + * Return the parent document. + */ + nsDocAccessible* ParentDocument() const + { return mParent ? mParent->GetDocAccessible() : nsnull; } + + /** + * Return the child document count. + */ + PRUint32 ChildDocumentCount() const + { return mChildDocuments.Length(); } + + /** + * Return the child document at the given index. + */ + nsDocAccessible* GetChildDocumentAt(PRUint32 aIndex) const + { return mChildDocuments.SafeElementAt(aIndex, nsnull); } + /** * Non-virtual method to fire a delayed event after a 0 length timeout. * @@ -188,6 +206,12 @@ public: */ nsAccessible* GetCachedAccessible(void *aUniqueID); + /** + * Return the cached accessible by the given unique ID looking through + * this and nested documents. + */ + nsAccessible* GetCachedAccessibleInSubtree(void* aUniqueID); + /** * Cache the accessible. * @@ -217,6 +241,24 @@ protected: void AddScrollListener(); void RemoveScrollListener(); + /** + * Append the given document accessible to this document's child document + * accessibles. + */ + bool AppendChildDocument(nsDocAccessible* aChildDocument) + { + return mChildDocuments.AppendElement(aChildDocument); + } + + /** + * Remove the given document accessible from this document's child document + * accessibles. + */ + void RemoveChildDocument(nsDocAccessible* aChildDocument) + { + mChildDocuments.RemoveElement(aChildDocument); + } + /** * Invalidate parent-child relations for any cached accessible in the DOM * subtree. Accessible objects aren't destroyed. @@ -337,6 +379,8 @@ protected: static PRUint32 gLastFocusedAccessiblesState; static nsIAtom *gLastFocusedFrameType; + + nsTArray > mChildDocuments; }; NS_DEFINE_STATIC_IID_ACCESSOR(nsDocAccessible, diff --git a/accessible/src/msaa/nsAccessibleWrap.cpp b/accessible/src/msaa/nsAccessibleWrap.cpp index 5a102f9f0d9..6ac80ea5019 100644 --- a/accessible/src/msaa/nsAccessibleWrap.cpp +++ b/accessible/src/msaa/nsAccessibleWrap.cpp @@ -282,24 +282,21 @@ STDMETHODIMP nsAccessibleWrap::get_accChild( { __try { *ppdispChild = NULL; - if (!mWeakShell || varChild.vt != VT_I4) + if (IsDefunct()) return E_FAIL; - if (varChild.lVal == CHILDID_SELF) { - *ppdispChild = static_cast(this); - AddRef(); - return S_OK; - } + // IAccessible::accChild is used to return this accessible or child accessible + // at the given index or to get an accessible by child ID in the case of + // document accessible (it's handled by overriden GetXPAccessibleFor method + // on the document accessible). The getting an accessible by child ID is used + // by AccessibleObjectFromEvent() called by AT when AT handles our MSAA event. + nsAccessible* child = GetXPAccessibleFor(varChild); + if (child) + *ppdispChild = NativeAccessible(child); - if (!nsAccUtils::MustPrune(this)) { - nsAccessible* child = GetChildAt(varChild.lVal - 1); - if (child) { - *ppdispChild = NativeAccessible(child); - } - } } __except(FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { } - return (*ppdispChild)? S_OK: E_FAIL; + return (*ppdispChild)? S_OK: E_INVALIDARG; } STDMETHODIMP nsAccessibleWrap::get_accName( @@ -1804,7 +1801,7 @@ nsAccessibleWrap::UnattachIEnumVariant() nsAccessible* nsAccessibleWrap::GetXPAccessibleFor(const VARIANT& aVarChild) { - if (IsDefunct()) + if (aVarChild.vt != VT_I4) return nsnull; // if its us real easy - this seems to always be the case diff --git a/accessible/src/msaa/nsDocAccessibleWrap.cpp b/accessible/src/msaa/nsDocAccessibleWrap.cpp index 8fc4d693b55..dac5ec3fd11 100644 --- a/accessible/src/msaa/nsDocAccessibleWrap.cpp +++ b/accessible/src/msaa/nsDocAccessibleWrap.cpp @@ -103,39 +103,13 @@ nsDocAccessibleWrap::GetXPAccessibleFor(const VARIANT& aVarChild) // accessible through whole accessible subtree including subdocuments. // Otherwise we treat lVal as index in parent. - if (aVarChild.lVal < 0) - return IsDefunct() ? nsnull : GetXPAccessibleForChildID(aVarChild); - - return nsAccessibleWrap::GetXPAccessibleFor(aVarChild); -} - -STDMETHODIMP -nsDocAccessibleWrap::get_accChild(VARIANT varChild, - IDispatch __RPC_FAR *__RPC_FAR *ppdispChild) -{ -__try { - *ppdispChild = NULL; - - if (varChild.vt == VT_I4 && varChild.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. - - nsAccessible *xpAccessible = GetXPAccessibleForChildID(varChild); - if (!xpAccessible) - return E_FAIL; - - IAccessible *msaaAccessible = NULL; - xpAccessible->GetNativeInterface((void**)&msaaAccessible); - *ppdispChild = static_cast(msaaAccessible); - - return S_OK; + if (aVarChild.vt == VT_I4 && aVarChild.lVal < 0) { + // Convert child ID to unique ID. + void* uniqueID = reinterpret_cast(-aVarChild.lVal); + return GetCachedAccessibleInSubtree(uniqueID); } - // Otherwise, the normal get_accChild() will do - return nsAccessibleWrap::get_accChild(varChild, ppdispChild); -} __except(FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { } - return E_FAIL; + return nsAccessibleWrap::GetXPAccessibleFor(aVarChild); } STDMETHODIMP nsDocAccessibleWrap::get_URL(/* [out] */ BSTR __RPC_FAR *aURL) @@ -271,14 +245,3 @@ STDMETHODIMP nsDocAccessibleWrap::get_accValue( return get_URL(pszValue); } - -nsAccessible* -nsDocAccessibleWrap::GetXPAccessibleForChildID(const VARIANT& aVarChild) -{ - 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); - return GetAccService()->FindAccessibleInCache(uniqueID); -} diff --git a/accessible/src/msaa/nsDocAccessibleWrap.h b/accessible/src/msaa/nsDocAccessibleWrap.h index 50dddbbfbc3..a49610296a3 100644 --- a/accessible/src/msaa/nsDocAccessibleWrap.h +++ b/accessible/src/msaa/nsDocAccessibleWrap.h @@ -83,10 +83,6 @@ public: /* [in] */ BSTR __RPC_FAR *commaSeparatedMediaTypes); // IAccessible - // Override get_accChild so that it can get any child via the unique ID - virtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_accChild( - /* [in] */ VARIANT varChild, - /* [retval][out] */ IDispatch __RPC_FAR *__RPC_FAR *ppdispChild); // Override get_accValue to provide URL when no other value is available virtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_accValue( @@ -95,15 +91,6 @@ public: // nsAccessibleWrap virtual nsAccessible *GetXPAccessibleFor(const VARIANT& varChild); - - // nsDocAccessibleWrap - - /** - * Find an accessible by the given child ID in cached documents. - * - * @param aVarChild [in] variant pointing to the child ID - */ - static nsAccessible *GetXPAccessibleForChildID(const VARIANT& aVarChild); }; #endif diff --git a/accessible/tests/mochitest/tree/Makefile.in b/accessible/tests/mochitest/tree/Makefile.in index 1401396d43e..9a857b3c140 100644 --- a/accessible/tests/mochitest/tree/Makefile.in +++ b/accessible/tests/mochitest/tree/Makefile.in @@ -53,6 +53,7 @@ _TEST_FILES =\ test_colorpicker.xul \ test_combobox.xul \ test_cssoverflow.html \ + test_dochierarchy.html \ test_filectrl.html \ test_formctrl.html \ test_formctrl.xul \ diff --git a/accessible/tests/mochitest/tree/test_dochierarchy.html b/accessible/tests/mochitest/tree/test_dochierarchy.html new file mode 100644 index 00000000000..bdbe2875953 --- /dev/null +++ b/accessible/tests/mochitest/tree/test_dochierarchy.html @@ -0,0 +1,83 @@ + + + + Test document hierarchy + + + + + + + + + + + + + + + Mozilla Bug 592913 + +

+ +
+  
+ + + +