author | Alexander Surkov <surkov.alexander@gmail.com> |
Thu, 09 Sep 2010 23:44:56 +0900 | |
changeset 52279 | b76cfd9e1028fde3cc886c24106e6d76be5c5d77 |
parent 52278 | 84fdf8e52709f3e129dc6b0bc0f4cf9e1bfeedbf |
child 52280 | 94a0c347256d04d1bbba72afc85e8424318f13a3 |
push id | 1 |
push user | root |
push date | Tue, 26 Apr 2011 22:38:44 +0000 |
treeherder | mozilla-beta@bfdb6e623a36 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | marcoz, davidb, blocking |
bugs | 592913 |
milestone | 2.0b6pre |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/accessible/public/nsIAccessibleDocument.idl +++ b/accessible/public/nsIAccessibleDocument.idl @@ -51,17 +51,17 @@ interface nsIDOMWindow; * there is an nsIAccessibleDocument for each document * whether it is XUL, HTML or whatever. * You can QueryInterface to nsIAccessibleDocument from * the nsIAccessible or nsIAccessNode for the root node * of a document. You can also get one from * nsIAccessNode::GetAccessibleDocument() or * nsIAccessibleEvent::GetAccessibleDocument() */ -[scriptable, uuid(03c6ce8a-aa40-4484-9282-e6579c56e054)] +[scriptable, uuid(451242bd-8a0c-4198-ae88-c053609a4e5d)] interface nsIAccessibleDocument : nsISupports { /** * The URL of the document */ readonly attribute AString URL; /** @@ -94,9 +94,24 @@ interface nsIAccessibleDocument : nsISup */ AString getNameSpaceURIForID(in short nameSpaceID); /** * The window handle for the OS window the document is being displayed in. * 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); };
--- a/accessible/src/base/nsDocAccessible.cpp +++ b/accessible/src/base/nsDocAccessible.cpp @@ -128,21 +128,28 @@ nsDocAccessible::~nsDocAccessible() // nsISupports NS_IMPL_CYCLE_COLLECTION_CLASS(nsDocAccessible) 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<nsIAccessible*>(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 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsDocAccessible) NS_INTERFACE_MAP_STATIC_AMBIGUOUS(nsDocAccessible) NS_INTERFACE_MAP_ENTRY(nsIAccessibleDocument) NS_INTERFACE_MAP_ENTRY(nsIDocumentObserver) NS_INTERFACE_MAP_ENTRY(nsIMutationObserver) @@ -493,16 +500,54 @@ nsDocAccessible::GetDOMDocument(nsIDOMDo *aDOMDocument = nsnull; if (mDocument) CallQueryInterface(mDocument, 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) { NS_ENSURE_ARG_POINTER(aEditor); *aEditor = nsnull; if (IsDefunct()) return NS_ERROR_FAILURE; @@ -526,16 +571,17 @@ NS_IMETHODIMP nsDocAccessible::GetAssoci PRBool isEditable; editor->GetIsDocumentEditable(&isEditable); if (isEditable) { NS_ADDREF(*aEditor = editor); } return NS_OK; } +// nsDocAccessible public method nsAccessible * nsDocAccessible::GetCachedAccessible(void *aUniqueID) { nsAccessible* accessible = mAccessibleCache.GetWeak(aUniqueID); // No accessible in the cache, check if the given ID is unique ID of this // document accessible. if (!accessible) { @@ -598,16 +644,20 @@ nsDocAccessible::Init() // Initialize event queue. mEventQueue = new nsAccEventQueue(this); if (!mEventQueue) return PR_FALSE; 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<AccEvent> reorderEvent = new AccReorderEvent(mParent, PR_FALSE, PR_TRUE, mDocument); if (!reorderEvent) return PR_FALSE; FireDelayedAccessibleEvent(reorderEvent); @@ -624,18 +674,25 @@ nsDocAccessible::Shutdown() if (mEventQueue) { mEventQueue->Shutdown(); mEventQueue = nsnull; } RemoveEventListeners(); - if (mParent) + if (mParent) { + nsDocAccessible* parentDocument = mParent->GetDocAccessible(); + if (parentDocument) + parentDocument->RemoveChildDocument(this); + mParent->RemoveChild(this); + } + + mChildDocuments.Clear(); mWeakShell = nsnull; // Avoid reentrancy ClearCache(mAccessibleCache); nsCOMPtr<nsIDocument> kungFuDeathGripDoc = mDocument; mDocument = nsnull; @@ -1273,16 +1330,33 @@ nsDocAccessible::HandleAccEvent(AccEvent return nsHyperTextAccessible::HandleAccEvent(aAccEvent); } #endif //////////////////////////////////////////////////////////////////////////////// // 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 void nsDocAccessible::FireValueChangeForTextFields(nsAccessible *aAccessible) { if (aAccessible->Role() != nsIAccessibleRole::ROLE_ENTRY)
--- a/accessible/src/base/nsDocAccessible.h +++ b/accessible/src/base/nsDocAccessible.h @@ -137,16 +137,34 @@ public: /** * Marks as loaded, used for error pages as workaround since they do not * receive pageshow event and as consequence nsIDocument::IsShowing() returns * false. */ 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. * * @param aEventType [in] the nsIAccessibleEvent event type * @param aDOMNode [in] DOM node the accesible event should be fired for * @param aAllowDupes [in] rule to process an event (see EEventRule constants) * @param aIsAsynch [in] set to PR_TRUE if this is not being called from * code synchronous with a DOM event */ @@ -184,16 +202,22 @@ public: * * @param aUniqueID [in] the unique ID used to cache the node. * * @return the accessible object */ 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. * * @param aUniquID [in] the unique identifier of accessible * @param aAccessible [in] accessible to cache * * @return true if accessible being cached, otherwise false */ PRBool CacheAccessible(void *aUniqueID, nsAccessible *aAccessible); @@ -213,16 +237,34 @@ protected: virtual void GetBoundsRect(nsRect& aRect, nsIFrame** aRelativeFrame); virtual nsresult AddEventListeners(); virtual nsresult RemoveEventListeners(); 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. * * @param aStartNode [in] the root of the subrtee to invalidate accessible * child/parent refs in */ void InvalidateChildrenInSubtree(nsINode *aStartNode); @@ -332,14 +374,16 @@ protected: /** * Specifies if the document was loaded, used for error pages only. */ PRPackedBool mIsLoaded; static PRUint32 gLastFocusedAccessiblesState; static nsIAtom *gLastFocusedFrameType; + + nsTArray<nsRefPtr<nsDocAccessible> > mChildDocuments; }; NS_DEFINE_STATIC_IID_ACCESSOR(nsDocAccessible, NS_DOCACCESSIBLE_IMPL_CID) #endif
--- a/accessible/src/msaa/nsAccessibleWrap.cpp +++ b/accessible/src/msaa/nsAccessibleWrap.cpp @@ -277,34 +277,31 @@ STDMETHODIMP nsAccessibleWrap::get_accCh } STDMETHODIMP nsAccessibleWrap::get_accChild( /* [in] */ VARIANT varChild, /* [retval][out] */ IDispatch __RPC_FAR *__RPC_FAR *ppdispChild) { __try { *ppdispChild = NULL; - if (!mWeakShell || varChild.vt != VT_I4) + if (IsDefunct()) return E_FAIL; - if (varChild.lVal == CHILDID_SELF) { - *ppdispChild = static_cast<IDispatch*>(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( /* [optional][in] */ VARIANT varChild, /* [retval][out] */ BSTR __RPC_FAR *pszName) { __try { *pszName = NULL; @@ -1799,17 +1796,17 @@ nsAccessibleWrap::UnattachIEnumVariant() { if (mEnumVARIANTPosition > 0) mEnumVARIANTPosition = kIEnumVariantDisconnected; } 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 if (aVarChild.lVal == CHILDID_SELF) return this; if (nsAccUtils::MustPrune(this)) return nsnull;
--- a/accessible/src/msaa/nsDocAccessibleWrap.cpp +++ b/accessible/src/msaa/nsDocAccessibleWrap.cpp @@ -98,51 +98,25 @@ STDMETHODIMP nsDocAccessibleWrap::QueryI nsAccessible* nsDocAccessibleWrap::GetXPAccessibleFor(const VARIANT& aVarChild) { // 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) - return IsDefunct() ? nsnull : GetXPAccessibleForChildID(aVarChild); + if (aVarChild.vt == VT_I4 && aVarChild.lVal < 0) { + // Convert child ID to unique ID. + void* uniqueID = reinterpret_cast<void*>(-aVarChild.lVal); + return GetCachedAccessibleInSubtree(uniqueID); + } 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<IDispatch*>(msaaAccessible); - - return S_OK; - } - - // Otherwise, the normal get_accChild() will do - return nsAccessibleWrap::get_accChild(varChild, ppdispChild); -} __except(FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { } - return E_FAIL; -} - STDMETHODIMP nsDocAccessibleWrap::get_URL(/* [out] */ BSTR __RPC_FAR *aURL) { __try { *aURL = NULL; nsAutoString URL; nsresult rv = GetURL(URL); if (NS_FAILED(rv)) @@ -266,19 +240,8 @@ STDMETHODIMP nsDocAccessibleWrap::get_ac if (role != nsIAccessibleRole::ROLE_DOCUMENT && role != nsIAccessibleRole::ROLE_APPLICATION && role != nsIAccessibleRole::ROLE_DIALOG && role != nsIAccessibleRole::ROLE_ALERT) return hr; 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<void*>(-aVarChild.lVal); - return GetAccService()->FindAccessibleInCache(uniqueID); -}
--- a/accessible/src/msaa/nsDocAccessibleWrap.h +++ b/accessible/src/msaa/nsDocAccessibleWrap.h @@ -78,32 +78,19 @@ public: virtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_nameSpaceURIForID( /* [in] */ short nameSpaceID, /* [out] */ BSTR __RPC_FAR *nameSpaceURI); virtual /* [id] */ HRESULT STDMETHODCALLTYPE put_alternateViewMediaTypes( /* [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( /* [optional][in] */ VARIANT varChild, /* [retval][out] */ BSTR __RPC_FAR *pszValue); // 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
--- a/accessible/tests/mochitest/tree/Makefile.in +++ b/accessible/tests/mochitest/tree/Makefile.in @@ -48,16 +48,17 @@ include $(topsrcdir)/config/rules.mk _TEST_FILES =\ $(warning test_applicationacc.xul temporarily disabled, see bug 561508) \ test_aria_globals.html \ test_aria_imgmap.html \ test_button.xul \ test_colorpicker.xul \ test_combobox.xul \ test_cssoverflow.html \ + test_dochierarchy.html \ test_filectrl.html \ test_formctrl.html \ test_formctrl.xul \ test_gencontent.html \ test_groupbox.xul \ test_iframe.html \ test_img.html \ test_list.html \
new file mode 100644 --- /dev/null +++ b/accessible/tests/mochitest/tree/test_dochierarchy.html @@ -0,0 +1,83 @@ +<!DOCTYPE html> +<html> +<head> + <title>Test document hierarchy</title> + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script type="application/javascript" + src="chrome://mochikit/content/MochiKit/packed.js"></script> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + + <script type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../role.js"></script> + <script type="application/javascript" + src="../states.js"></script> + + <script type="application/javascript"> + function doTest() + { + // tabDoc and testDoc are different documents depending on whether test + // is running in standalone mode or not. + + var root = getRootAccessible(); + var tabDoc = window.parent ? + getAccessible(window.parent.document, [nsIAccessibleDocument]) : + getAccessible(document, [nsIAccessibleDocument]); + var testDoc = getAccessible(document, [nsIAccessibleDocument]); + var iframeDoc = getAccessible("iframe").firstChild. + QueryInterface(nsIAccessibleDocument); + + is(root.parentDocument, null, + "Wrong parent document of root accessible"); + is(root.childDocumentCount, 1, + "Wrong child document count of root accessible"); + is(root.getChildDocumentAt(0), tabDoc, + "Wrong child document at index 0 of root accessible"); + + is(tabDoc.parentDocument, root, + "Wrong parent document of tab document"); + is(tabDoc.childDocumentCount, 1, + "Wrong child document count of tab document"); + is(tabDoc.getChildDocumentAt(0), (tabDoc == testDoc ? iframeDoc : testDoc), + "Wrong child document at index 0 of tab document"); + + if (tabDoc != testDoc) { + is(testDoc.parentDocument, tabDoc, + "Wrong parent document of test document"); + is(testDoc.childDocumentCount, 1, + "Wrong child document count of test document"); + is(testDoc.getChildDocumentAt(0), iframeDoc, + "Wrong child document at index 0 of test document"); + } + + is(iframeDoc.parentDocument, (tabDoc == testDoc ? tabDoc : testDoc), + "Wrong parent document of iframe document"); + is(iframeDoc.childDocumentCount, 0, + "Wrong child document count of iframe document"); + + SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> + +<body> + <a target="_blank" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=592913" + title="Provide a way to quickly determine whether an accessible object is a descendant of a tab document"> + Mozilla Bug 592913 + </a> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <iframe src="about:mozilla" id="iframe"></iframe> +</body> +</html>