author | Alexander Surkov <surkov.alexander@gmail.com> |
Wed, 26 Jan 2011 14:35:51 +0800 | |
changeset 61317 | e0fc18b3bc41ad073fb6bd48891c005b1496a4e2 |
parent 61316 | 0f592a9724829c19e1b8fecf218c11e3b802ca78 |
child 61318 | af7e65f1ee6f04175e054674da78847f057fdbb6 |
push id | 18308 |
push user | surkov.alexander@gmail.com |
push date | Wed, 26 Jan 2011 06:37:03 +0000 |
treeherder | mozilla-central@e0fc18b3bc41 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | davidb, blocking2.0Final |
bugs | 606924 |
milestone | 2.0b11pre |
first release with | nightly linux32
e0fc18b3bc41
/
4.0b11pre
/
20110126030333
/
files
nightly linux64
e0fc18b3bc41
/
4.0b11pre
/
20110126030333
/
files
nightly mac
e0fc18b3bc41
/
4.0b11pre
/
20110126030333
/
files
nightly win32
e0fc18b3bc41
/
4.0b11pre
/
20110126064706
/
files
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
releases | nightly linux32
4.0b11pre
/
20110126030333
/
pushlog to previous
nightly linux64
4.0b11pre
/
20110126030333
/
pushlog to previous
nightly mac
4.0b11pre
/
20110126030333
/
pushlog to previous
nightly win32
4.0b11pre
/
20110126064706
/
pushlog to previous
|
--- a/accessible/src/base/NotificationController.cpp +++ b/accessible/src/base/NotificationController.cpp @@ -77,16 +77,18 @@ NS_IMPL_CYCLE_COLLECTION_CLASS(Notificat NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_NATIVE(NotificationController) tmp->Shutdown(); NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_BEGIN(NotificationController) NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mDocument"); cb.NoteXPCOMChild(static_cast<nsIAccessible*>(tmp->mDocument.get())); + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSTARRAY_MEMBER(mHangingChildDocuments, + nsDocAccessible) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSTARRAY_MEMBER(mContentInsertions, ContentInsertion) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSTARRAY_MEMBER(mEvents, AccEvent) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(NotificationController, AddRef) NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(NotificationController, Release) @@ -96,18 +98,26 @@ NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(N void NotificationController::Shutdown() { if (mObservingState != eNotObservingRefresh && mPresShell->RemoveRefreshObserver(this, Flush_Display)) { mObservingState = eNotObservingRefresh; } + // Shutdown handling child documents. + PRInt32 childDocCount = mHangingChildDocuments.Length(); + for (PRInt32 idx = childDocCount - 1; idx >= 0; idx--) + mHangingChildDocuments[idx]->Shutdown(); + + mHangingChildDocuments.Clear(); + mDocument = nsnull; mPresShell = nsnull; + mContentInsertions.Clear(); mNotifications.Clear(); mEvents.Clear(); } void NotificationController::QueueEvent(AccEvent* aEvent) { @@ -122,16 +132,24 @@ NotificationController::QueueEvent(AccEv AccMutationEvent* showOrHideEvent = downcast_accEvent(aEvent); if (showOrHideEvent && !showOrHideEvent->mTextChangeEvent) CreateTextChangeEventFor(showOrHideEvent); ScheduleProcessing(); } void +NotificationController::ScheduleChildDocBinding(nsDocAccessible* aDocument) +{ + // Schedule child document binding to the tree. + mHangingChildDocuments.AppendElement(aDocument); + ScheduleProcessing(); +} + +void NotificationController::ScheduleContentInsertion(nsAccessible* aContainer, nsIContent* aStartChildNode, nsIContent* aEndChildNode) { // Ignore content insertions until we constructed accessible tree. if (mTreeConstructedState == eTreeConstructionPending) return; @@ -180,16 +198,21 @@ NotificationController::WillRefresh(mozi return; // Any generic notifications should be queued if we're processing content // insertions or generic notifications. mObservingState = eRefreshProcessingForUpdate; // Initial accessible tree construction. if (mTreeConstructedState == eTreeConstructionPending) { + // If document is not bound to parent at this point then the document is not + // ready yet (process notifications later). + if (!mDocument->IsBoundToParent()) + return; + mTreeConstructedState = eTreeConstructed; mDocument->CacheChildrenInSubtree(mDocument); NS_ASSERTION(mContentInsertions.Length() == 0, "Pending content insertions while initial accessible tree isn't created!"); } // Process content inserted notifications to update the tree. Process other @@ -207,16 +230,46 @@ NotificationController::WillRefresh(mozi PRUint32 insertionCount = contentInsertions.Length(); for (PRUint32 idx = 0; idx < insertionCount; idx++) { contentInsertions[idx]->Process(); if (!mDocument) return; } + // Bind hanging child documents. + PRUint32 childDocCount = mHangingChildDocuments.Length(); + for (PRUint32 idx = 0; idx < childDocCount; idx++) { + nsDocAccessible* childDoc = mHangingChildDocuments[idx]; + + nsIContent* ownerContent = mDocument->GetDocumentNode()-> + FindContentForSubDocument(childDoc->GetDocumentNode()); + if (ownerContent) { + nsAccessible* outerDocAcc = mDocument->GetCachedAccessible(ownerContent); + if (outerDocAcc && outerDocAcc->AppendChild(childDoc)) { + if (mDocument->AppendChildDocument(childDoc)) { + // Fire reorder event to notify new accessible document has been + // attached to the tree. + nsRefPtr<AccEvent> reorderEvent = + new AccEvent(nsIAccessibleEvent::EVENT_REORDER, outerDocAcc, + eAutoDetect, AccEvent::eCoalesceFromSameSubtree); + if (reorderEvent) + QueueEvent(reorderEvent); + + continue; + } + outerDocAcc->RemoveChild(childDoc); + } + + // Failed to bind the child document, destroy it. + childDoc->Shutdown(); + } + } + mHangingChildDocuments.Clear(); + // Process only currently queued generic notifications. nsTArray < nsRefPtr<Notification> > notifications; notifications.SwapElements(mNotifications); PRUint32 notificationCount = notifications.Length(); for (PRUint32 idx = 0; idx < notificationCount; idx++) { notifications[idx]->Process(); if (!mDocument)
--- a/accessible/src/base/NotificationController.h +++ b/accessible/src/base/NotificationController.h @@ -122,26 +122,39 @@ public: virtual ~NotificationController(); NS_IMETHOD_(nsrefcnt) AddRef(void); NS_IMETHOD_(nsrefcnt) Release(void); NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(NotificationController) /** + * Return true when tree is constructed. + */ + inline bool IsTreeConstructed() + { + return mTreeConstructedState == eTreeConstructed; + } + + /** * Shutdown the notification controller. */ void Shutdown(); /** * Put an accessible event into the queue to process it later. */ void QueueEvent(AccEvent* aEvent); /** + * Schedule binding the child document to the tree of this document. + */ + void ScheduleChildDocBinding(nsDocAccessible* aDocument); + + /** * Pend accessible tree update for content insertion. */ void ScheduleContentInsertion(nsAccessible* aContainer, nsIContent* aStartChildNode, nsIContent* aEndChildNode); /** * Process the generic notification synchronously if there are no pending @@ -263,16 +276,21 @@ private: */ enum eTreeConstructedState { eTreeConstructed, eTreeConstructionPending }; eTreeConstructedState mTreeConstructedState; /** + * Child documents that needs to be bound to the tree. + */ + nsTArray<nsRefPtr<nsDocAccessible> > mHangingChildDocuments; + + /** * Storage for content inserted notification information. */ class ContentInsertion { public: ContentInsertion(nsDocAccessible* aDocument, nsAccessible* aContainer, nsIContent* aStartChildNode, nsIContent* aEndChildNode); virtual ~ContentInsertion() { mDocument = nsnull; }
--- a/accessible/src/base/nsAccDocManager.cpp +++ b/accessible/src/base/nsAccDocManager.cpp @@ -424,68 +424,68 @@ nsAccDocManager::CreateDocOrRootAccessib // Do not create document accessible until role content is loaded, otherwise // we get accessible document with wrong role. nsIContent *rootElm = nsCoreUtils::GetRoleContent(aDocument); if (!rootElm) return nsnull; PRBool isRootDoc = nsCoreUtils::IsRootDocument(aDocument); - // Ensure the document container node is accessible, otherwise do not create - // document accessible. - nsAccessible *outerDocAcc = nsnull; - if (isRootDoc) { - outerDocAcc = nsAccessNode::GetApplicationAccessible(); - - } else { - nsIDocument* parentDoc = aDocument->GetParentDocument(); - if (!parentDoc) - return nsnull; - - nsIContent* ownerContent = parentDoc->FindContentForSubDocument(aDocument); - if (!ownerContent) - return nsnull; - + nsDocAccessible* parentDocAcc = nsnull; + if (!isRootDoc) { // XXXaaronl: ideally we would traverse the presshell chain. Since there's // no easy way to do that, we cheat and use the document hierarchy. // GetAccessible() is bad because it doesn't support our concept of multiple // presshells per doc. It should be changed to use // GetAccessibleInWeakShell(). - outerDocAcc = GetAccService()->GetAccessible(ownerContent); + parentDocAcc = GetDocAccessible(aDocument->GetParentDocument()); + NS_ASSERTION(parentDocAcc, + "Can't create an accessible for the document!"); + if (!parentDocAcc) + return nsnull; } - if (!outerDocAcc) - return nsnull; - // We only create root accessibles for the true root, otherwise create a // doc accessible. nsCOMPtr<nsIWeakReference> weakShell(do_GetWeakReference(presShell)); nsRefPtr<nsDocAccessible> docAcc = isRootDoc ? new nsRootAccessibleWrap(aDocument, rootElm, weakShell) : new nsDocAccessibleWrap(aDocument, rootElm, weakShell); // Cache the document accessible into document cache. if (!docAcc || !mDocAccessibleCache.Put(aDocument, docAcc)) return nsnull; - // Bind the document accessible into tree. - if (!outerDocAcc->AppendChild(docAcc)) { - mDocAccessibleCache.Remove(aDocument); - return nsnull; - } - - // Initialize the document accessible. Note, Init() should be called after - // the document accessible is bound to the tree. + // Initialize the document accessible. if (!docAcc->Init()) { docAcc->Shutdown(); - mDocAccessibleCache.Remove(aDocument); return nsnull; } docAcc->SetRoleMapEntry(nsAccUtils::GetRoleMapEntry(aDocument)); + // Bind the document to the tree. + if (isRootDoc) { + nsAccessible* appAcc = nsAccessNode::GetApplicationAccessible(); + if (!appAcc->AppendChild(docAcc)) { + docAcc->Shutdown(); + return nsnull; + } + + // Fire reorder event to notify new accessible document has been attached to + // the tree. + nsRefPtr<AccEvent> reorderEvent = + new AccEvent(nsIAccessibleEvent::EVENT_REORDER, appAcc, eAutoDetect, + AccEvent::eCoalesceFromSameSubtree); + if (reorderEvent) + docAcc->FireDelayedAccessibleEvent(reorderEvent); + + } else { + parentDocAcc->BindChildDocument(docAcc); + } + NS_LOG_ACCDOCCREATE("document creation finished", aDocument) AddListeners(aDocument, isRootDoc); return docAcc; } //////////////////////////////////////////////////////////////////////////////// // nsAccDocManager static
--- a/accessible/src/base/nsAccessibilityService.cpp +++ b/accessible/src/base/nsAccessibilityService.cpp @@ -1242,17 +1242,17 @@ nsAccessibilityService::GetAccessibleByR nsIWeakReference* aWeakShell, EWhatAccToGet aWhatToGet) { if (!aNode || !aWeakShell) return nsnull; if (aWhatToGet & eGetAccForNode) { nsAccessible* cachedAcc = GetCachedAccessible(aNode, aWeakShell); - if (cachedAcc && cachedAcc->IsBoundToParent()) + if (cachedAcc && (cachedAcc->IsBoundToParent() || cachedAcc->IsDocument())) return cachedAcc; } // Go up looking for the nearest accessible container having cached children. nsTArray<nsINode*> nodes; nsINode* node = aNode; nsAccessible* cachedAcc = nsnull;
--- a/accessible/src/base/nsDocAccessible.cpp +++ b/accessible/src/base/nsDocAccessible.cpp @@ -315,17 +315,18 @@ nsDocAccessible::GetStateInternal(PRUint // XXX Need to invent better check to see if doc is focusable, // which it should be if it is scrollable. A XUL document could be focusable. // See bug 376803. *aState |= nsIAccessibleStates::STATE_FOCUSABLE; if (gLastFocusedNode == mDocument) *aState |= nsIAccessibleStates::STATE_FOCUSED; } - if (!mIsLoaded) { + // Expose state busy until the document is loaded or tree is constructed. + if (!mIsLoaded || !mNotificationController->IsTreeConstructed()) { *aState |= nsIAccessibleStates::STATE_BUSY; if (aExtraState) { *aExtraState |= nsIAccessibleStates::EXT_STATE_STALE; } } nsIFrame* frame = GetFrame(); while (frame != nsnull && !frame->HasView()) { @@ -624,32 +625,17 @@ nsDocAccessible::Init() // Initialize notification controller. nsCOMPtr<nsIPresShell> shell(GetPresShell()); mNotificationController = new NotificationController(this, shell); if (!mNotificationController) 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 AccEvent(nsIAccessibleEvent::EVENT_REORDER, mParent, eAutoDetect, - AccEvent::eCoalesceFromSameSubtree); - if (reorderEvent) { - FireDelayedAccessibleEvent(reorderEvent); - return PR_TRUE; - } - - return PR_FALSE; + return PR_TRUE; } void nsDocAccessible::Shutdown() { if (!mWeakShell) // already shutdown return;
--- a/accessible/src/base/nsDocAccessible.h +++ b/accessible/src/base/nsDocAccessible.h @@ -194,16 +194,24 @@ public: */ inline void HandleAnchorJump(nsIContent* aTargetNode) { HandleNotification<nsDocAccessible, nsIContent> (this, &nsDocAccessible::ProcessAnchorJump, aTargetNode); } /** + * Bind the child document to the tree. + */ + inline void BindChildDocument(nsDocAccessible* aDocument) + { + mNotificationController->ScheduleChildDocBinding(aDocument); + } + + /** * Process the generic notification. * * @note The caller must guarantee that the given instance still exists when * notification is processed. * @see NotificationController::HandleNotification */ template<class Class, class Arg> inline void HandleNotification(Class* aInstance,
--- a/accessible/tests/mochitest/events.js +++ b/accessible/tests/mochitest/events.js @@ -425,17 +425,17 @@ function eventQueue(aEventType) var eventType = this.getEventType(idx); if (gLogger.isEnabled()) { var msg = "registered"; if (this.isEventUnexpected(idx)) msg += " unexpected"; msg += ": event type: " + this.getEventTypeAsString(idx) + - ", target: " + prettyName(this.getEventTarget(idx)); + ", target: " + this.getEventTargetDescr(idx); gLogger.logToConsole(msg); gLogger.logToDOM(msg, true); } if (typeof eventType == "string") { // DOM event var target = this.getEventTarget(idx); @@ -482,16 +482,22 @@ function eventQueue(aEventType) return (typeof type == "string") ? type : eventTypeToString(type); } this.getEventTarget = function eventQueue_getEventTarget(aIdx) { return this.mEventSeq[aIdx].target; } + this.getEventTargetDescr = function eventQueue_getEventTargetDescr(aIdx) + { + var descr = this.mEventSeq[aIdx].targetDescr; + return descr ? descr : "no target description"; + } + this.getEventPhase = function eventQueue_getEventPhase(aIdx) { var eventItem = this.mEventSeq[aIdx]; if ("phase" in eventItem) return eventItem.phase; return true; } @@ -890,16 +896,26 @@ function invokerChecker(aEventType, aTar } function invokerChecker_targetSetter(aValue) { this.mTarget = aValue; return this.mTarget; } + this.__defineGetter__("targetDescr", invokerChecker_targetDescrGetter); + + function invokerChecker_targetDescrGetter() + { + if (typeof this.mTarget == "function") + return this.mTarget.toSource() + this.mTargetFuncArg; + + return prettyName(this.mTarget); + } + this.mTarget = aTargetOrFunc; this.mTargetFuncArg = aTargetFuncArg; } /** * Text inserted/removed events checker. */ function textChangeChecker(aID, aStart, aEnd, aTextOrFunc, aIsInserted)
--- a/accessible/tests/mochitest/events/docload_wnd.html +++ b/accessible/tests/mochitest/events/docload_wnd.html @@ -1,21 +1,39 @@ <html> <head> <title>Accessible events testing for document</title> <script> + const STATE_BUSY = Components.interfaces.nsIAccessibleStates.STATE_BUSY; + + var gRetrieval = null; + function waitForDocLoad() + { + if (!gRetrieval) { + gRetrieval = Components.classes["@mozilla.org/accessibleRetrieval;1"]. + getService(Components.interfaces.nsIAccessibleRetrieval); + } + + var accDoc = gRetrieval.getAccessibleFor(document); + + var state = {}; + accDoc.getState(state, {}); + if (state.value & STATE_BUSY) { + window.setTimeout(waitForDocLoad, 0); + return; + } + + hideIFrame(); + } + function hideIFrame() { var iframe = document.getElementById("iframe"); - - var accRetrieval = Components.classes["@mozilla.org/accessibleRetrieval;1"]. - getService(Components.interfaces.nsIAccessibleRetrieval); - accRetrieval.getAccessibleFor(iframe.contentDocument); - + gRetrieval.getAccessibleFor(iframe.contentDocument); iframe.style.display = 'none'; } </script> </head> -<body onload="hideIFrame();"> +<body onload="waitForDocLoad();"> <iframe id="iframe"></iframe> </body> </html>
--- a/accessible/tests/mochitest/events/test_docload.html +++ b/accessible/tests/mochitest/events/test_docload.html @@ -192,16 +192,20 @@ var docChecker = { type: EVENT_HIDE, get target() { var iframe = this.invoker.mDialog.document.getElementById("iframe"); this.invoker.iframeDoc = iframe.contentDocument; return iframe; }, + get targetDescr() + { + return "inner iframe of docload_wnd.html document"; + }, invoker: thisObj }; this.eventSeq.push(docChecker); this.finalCheck = function openWndShutdownDoc_finalCheck() { // After timeout after event hide for iframe was handled the document