diff --git a/accessible/src/base/NotificationController.cpp b/accessible/src/base/NotificationController.cpp index 00d87bfddba3..89172bfbc2a5 100644 --- a/accessible/src/base/NotificationController.cpp +++ b/accessible/src/base/NotificationController.cpp @@ -56,6 +56,8 @@ NotificationController::NotificationController(nsDocAccessible* aDocument, mObservingState(eNotObservingRefresh), mDocument(aDocument), mPresShell(aPresShell), mTreeConstructedState(eTreeConstructionPending) { + mTextHash.Init(); + // Schedule initial accessible tree construction. ScheduleProcessing(); } @@ -113,6 +115,7 @@ NotificationController::Shutdown() mDocument = nsnull; mPresShell = nsnull; + mTextHash.Clear(); mContentInsertions.Clear(); mNotifications.Clear(); mEvents.Clear(); @@ -208,6 +211,11 @@ NotificationController::WillRefresh(mozilla::TimeStamp aTime) if (!mDocument->IsBoundToParent()) return; +#ifdef DEBUG_NOTIFICATIONS + printf("\ninitial tree created, document: %p, document node: %p\n", + mDocument.get(), mDocument->GetDocumentNode()); +#endif + mTreeConstructedState = eTreeConstructed; mDocument->CacheChildrenInSubtree(mDocument); @@ -235,6 +243,10 @@ NotificationController::WillRefresh(mozilla::TimeStamp aTime) return; } + // Process rendered text change notifications. + mTextHash.EnumerateEntries(TextEnumerator, mDocument); + mTextHash.Clear(); + // Bind hanging child documents. PRUint32 childDocCount = mHangingChildDocuments.Length(); for (PRUint32 idx = 0; idx < childDocCount; idx++) { @@ -561,6 +573,89 @@ NotificationController::CreateTextChangeEventFor(AccMutationEvent* aEvent) aEvent->mIsFromUserInput ? eFromUserInput : eNoUserInput); } +PLDHashOperator +NotificationController::TextEnumerator(nsPtrHashKey* aEntry, + void* aUserArg) +{ + nsDocAccessible* document = static_cast(aUserArg); + nsIContent* textNode = aEntry->GetKey(); + nsAccessible* textAcc = document->GetAccessible(textNode); + + // If the text node is not in tree or doesn't have frame then this case should + // have been handled already by content removal notifications. + nsINode* containerNode = textNode->GetNodeParent(); + if (!containerNode) { + NS_ASSERTION(!textAcc, + "Text node was removed but accessible is kept alive!"); + return PL_DHASH_NEXT; + } + + nsIFrame* textFrame = textNode->GetPrimaryFrame(); + if (!textFrame) { + NS_ASSERTION(!textAcc, + "Text node isn't rendered but accessible is kept alive!"); + return PL_DHASH_NEXT; + } + + nsIContent* containerElm = containerNode->IsElement() ? + containerNode->AsElement() : nsnull; + + nsAutoString renderedText; + textFrame->GetRenderedText(&renderedText); + + // Remove text accessible if rendered text is empty. + if (textAcc) { + if (renderedText.IsEmpty()) { +#ifdef DEBUG_NOTIFICATIONS + PRUint32 index = containerNode->IndexOf(textNode); + + nsCAutoString tag; + nsCAutoString id; + if (containerElm) { + containerElm->Tag()->ToUTF8String(tag); + nsIAtom* atomid = containerElm->GetID(); + if (atomid) + atomid->ToUTF8String(id); + } + + printf("\npending text node removal: container: %s@id='%s', index in container: %d\n\n", + tag.get(), id.get(), index); +#endif + + document->ContentRemoved(containerElm, textNode); + } + + return PL_DHASH_NEXT; + } + + // Append an accessible if rendered text is not empty. + if (!renderedText.IsEmpty()) { +#ifdef DEBUG_NOTIFICATIONS + PRUint32 index = containerNode->IndexOf(textNode); + + nsCAutoString tag; + nsCAutoString id; + if (containerElm) { + containerElm->Tag()->ToUTF8String(tag); + nsIAtom* atomid = containerElm->GetID(); + if (atomid) + atomid->ToUTF8String(id); + } + + printf("\npending text node insertion: container: %s@id='%s', index in container: %d\n\n", + tag.get(), id.get(), index); +#endif + + nsAccessible* container = document->GetAccessibleOrContainer(containerNode); + nsTArray > insertedContents; + insertedContents.AppendElement(textNode); + document->ProcessContentInserted(container, &insertedContents); + } + + return PL_DHASH_NEXT; +} + + //////////////////////////////////////////////////////////////////////////////// // NotificationController: content inserted notification @@ -616,7 +711,7 @@ NotificationController::ContentInsertion::Process() catomid->ToUTF8String(cid); } - printf("\npending content insertion process: %s@id='%s', container: %s@id='%s', inserted content amount: %d\n\n", + printf("\npending content insertion: %s@id='%s', container: %s@id='%s', inserted content amount: %d\n\n", tag.get(), id.get(), ctag.get(), cid.get(), mInsertedContent.Length()); #endif diff --git a/accessible/src/base/NotificationController.h b/accessible/src/base/NotificationController.h index 6d53436b36c3..af7e94eeea1e 100644 --- a/accessible/src/base/NotificationController.h +++ b/accessible/src/base/NotificationController.h @@ -149,6 +149,16 @@ public: */ void ScheduleChildDocBinding(nsDocAccessible* aDocument); + /** + * Schedule the accessible tree update because of rendered text changes. + */ + inline void ScheduleTextUpdate(nsIContent* aTextNode) + { + // Ignore the notification if initial tree construction hasn't been done yet. + if (mTreeConstructedState != eTreeConstructionPending) + mTextHash.PutEntry(aTextNode); + } + /** * Pend accessible tree update for content insertion. */ @@ -323,6 +333,17 @@ private: */ nsTArray > mContentInsertions; + /** + * A pending accessible tree update notifications for rendered text changes. + */ + nsTHashtable > mTextHash; + + /** + * Update the accessible tree for pending rendered text change notifications. + */ + static PLDHashOperator TextEnumerator(nsPtrHashKey* aEntry, + void* aUserArg); + /** * Other notifications like DOM events. Don't make this an nsAutoTArray; we * use SwapElements() on it. diff --git a/accessible/src/base/nsAccessibilityService.cpp b/accessible/src/base/nsAccessibilityService.cpp index e514aa7af915..8b207009b285 100644 --- a/accessible/src/base/nsAccessibilityService.cpp +++ b/accessible/src/base/nsAccessibilityService.cpp @@ -528,6 +528,15 @@ nsAccessibilityService::ContentRemoved(nsIPresShell* aPresShell, docAccessible->ContentRemoved(aContainer, aChild); } +void +nsAccessibilityService::UpdateText(nsIPresShell* aPresShell, + nsIContent* aContent) +{ + nsDocAccessible* document = GetDocAccessible(aPresShell->GetDocument()); + if (document) + document->UpdateText(aContent); +} + void nsAccessibilityService::PresShellDestroyed(nsIPresShell *aPresShell) { diff --git a/accessible/src/base/nsAccessibilityService.h b/accessible/src/base/nsAccessibilityService.h index 4f156c6f0af4..629f7c64845b 100644 --- a/accessible/src/base/nsAccessibilityService.h +++ b/accessible/src/base/nsAccessibilityService.h @@ -118,6 +118,8 @@ public: virtual void ContentRemoved(nsIPresShell* aPresShell, nsIContent* aContainer, nsIContent* aChild); + virtual void UpdateText(nsIPresShell* aPresShell, nsIContent* aContent); + virtual void NotifyOfAnchorJumpTo(nsIContent *aTarget); virtual void PresShellDestroyed(nsIPresShell* aPresShell); diff --git a/accessible/src/base/nsDocAccessible.h b/accessible/src/base/nsDocAccessible.h index ba052586db90..9b5cf5b75adf 100644 --- a/accessible/src/base/nsDocAccessible.h +++ b/accessible/src/base/nsDocAccessible.h @@ -309,6 +309,17 @@ public: */ void ContentRemoved(nsIContent* aContainerNode, nsIContent* aChildNode); + /** + * Updates accessible tree when rendered text is changed. + */ + inline void UpdateText(nsIContent* aTextNode) + { + NS_ASSERTION(mNotificationController, "The document was shut down!"); + + if (mNotificationController) + mNotificationController->ScheduleTextUpdate(aTextNode); + } + /** * Recreate an accessible, results in hide/show events pair. */ diff --git a/accessible/tests/mochitest/events.js b/accessible/tests/mochitest/events.js index f115a3faf513..a5027a9a3ef1 100644 --- a/accessible/tests/mochitest/events.js +++ b/accessible/tests/mochitest/events.js @@ -907,7 +907,7 @@ function invokerChecker(aEventType, aTargetOrFunc, aTargetFuncArg) function invokerChecker_targetDescrGetter() { if (typeof this.mTarget == "function") - return this.mTarget.toSource() + this.mTargetFuncArg; + return this.mTarget.name + ", arg: " + this.mTargetFuncArg; return prettyName(this.mTarget); } diff --git a/accessible/tests/mochitest/tree/test_txtctrl.html b/accessible/tests/mochitest/tree/test_txtctrl.html index bc690b3e2df5..0f1471849b77 100644 --- a/accessible/tests/mochitest/tree/test_txtctrl.html +++ b/accessible/tests/mochitest/tree/test_txtctrl.html @@ -32,7 +32,7 @@ testAccessibleTree("txc1", accTree); - // input@type="text" + // input@type="text", value accTree = { role: ROLE_ENTRY, children: [ @@ -45,6 +45,14 @@ testAccessibleTree("txc2", accTree); + // input@type="text", no value + accTree = + { ENTRY: [ + { TEXT_LEAF: [ ] } + ] }; + + testAccessibleTree("txc3", accTree); + // textarea accTree = { role: ROLE_ENTRY, @@ -58,7 +66,7 @@ ] }; - testAccessibleTree("txc3", accTree); + testAccessibleTree("txc4", accTree); // input@type="password" accTree = { @@ -71,7 +79,7 @@ ] }; - testAccessibleTree("txc4", accTree); + testAccessibleTree("txc5", accTree); SimpleTest.finish(); } @@ -92,6 +100,11 @@ title="Create child accessibles for text controls from native anonymous content"> Mozilla Bug 542824 + + Mozilla Bug 625652 +

@@ -102,11 +115,12 @@ 1hellohello - - + diff --git a/accessible/tests/mochitest/treeupdate/Makefile.in b/accessible/tests/mochitest/treeupdate/Makefile.in index 256d476b1aeb..4fe21b12aaa5 100644 --- a/accessible/tests/mochitest/treeupdate/Makefile.in +++ b/accessible/tests/mochitest/treeupdate/Makefile.in @@ -54,6 +54,7 @@ _TEST_FILES =\ test_select.html \ test_textleaf.html \ test_visibility.html \ + test_whitespace.html \ $(NULL) libs:: $(_TEST_FILES) diff --git a/accessible/tests/mochitest/treeupdate/test_whitespace.html b/accessible/tests/mochitest/treeupdate/test_whitespace.html new file mode 100644 index 000000000000..f91925e2d7f9 --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_whitespace.html @@ -0,0 +1,188 @@ + + + + + Whitespace text accessible creation/desctruction + + + + + + + + + + + + + + + + Mozilla Bug 625652 + + +

+ +
+  
+ +
+
+ +
+ + diff --git a/layout/generic/nsTextFrameThebes.cpp b/layout/generic/nsTextFrameThebes.cpp index 031b167e00f0..94c7e58e4b12 100644 --- a/layout/generic/nsTextFrameThebes.cpp +++ b/layout/generic/nsTextFrameThebes.cpp @@ -6908,6 +6908,14 @@ nsTextFrame::ReflowText(nsLineLayout& aLineLayout, nscoord aAvailableWidth, aMetrics.width, aMetrics.height, aMetrics.ascent, aStatus); #endif + +#ifdef ACCESSIBILITY + // Schedule the update of accessible tree when rendered text might be changed. + nsAccessibilityService* accService = nsIPresShell::AccService(); + if (accService) { + accService->UpdateText(presContext->PresShell(), mContent); + } +#endif } /* virtual */ PRBool