diff --git a/accessible/src/base/Makefile.in b/accessible/src/base/Makefile.in index ee0b0729e17c..a12edbbe574b 100644 --- a/accessible/src/base/Makefile.in +++ b/accessible/src/base/Makefile.in @@ -77,6 +77,7 @@ CPPSRCS = \ nsTextAccessible.cpp \ nsTextEquivUtils.cpp \ nsTextAttrs.cpp \ + TextUpdater.cpp \ $(NULL) EXPORTS = \ diff --git a/accessible/src/base/NotificationController.cpp b/accessible/src/base/NotificationController.cpp index 0956b4878ee2..859b46603abd 100644 --- a/accessible/src/base/NotificationController.cpp +++ b/accessible/src/base/NotificationController.cpp @@ -44,6 +44,7 @@ #include "nsDocAccessible.h" #include "nsEventShell.h" #include "nsTextAccessible.h" +#include "TextUpdater.h" //////////////////////////////////////////////////////////////////////////////// @@ -574,287 +575,6 @@ NotificationController::CreateTextChangeEventFor(AccMutationEvent* aEvent) //////////////////////////////////////////////////////////////////////////////// // Notification controller: text leaf accessible text update -/** - * Used to find a difference between old and new text and fire text change - * events. - */ -class TextUpdater -{ -public: - TextUpdater(nsDocAccessible* aDocument, nsTextAccessible* aTextLeaf) : - mDocument(aDocument), mTextLeaf(aTextLeaf) { } - ~TextUpdater() { mDocument = nsnull; mTextLeaf = nsnull; } - - /** - * Update text of the text leaf accessible, fire text change events for its - * container hypertext accessible. - */ - void Run(const nsAString& aNewText); - -private: - TextUpdater(); - TextUpdater(const TextUpdater&); - TextUpdater& operator = (const TextUpdater&); - - /** - * Fire text change events based on difference between strings. - */ - void FindDiffNFireEvents(const nsDependentSubstring& aStr1, - const nsDependentSubstring& aStr2, - PRUint32** aMatrix, - PRUint32 aStartOffset); - - /** - * Change type used to describe the diff between strings. - */ - enum ChangeType { - eNoChange, - eInsertion, - eRemoval, - eSubstitution - }; - - /** - * Helper to fire text change events. - */ - inline void MayFireEvent(nsAString* aInsertedText, nsAString* aRemovedText, - PRUint32 aOffset, ChangeType* aChange) - { - if (*aChange == eNoChange) - return; - - if (*aChange == eRemoval || *aChange == eSubstitution) { - FireEvent(*aRemovedText, aOffset, PR_FALSE); - aRemovedText->Truncate(); - } - - if (*aChange == eInsertion || *aChange == eSubstitution) { - FireEvent(*aInsertedText, aOffset, PR_TRUE); - aInsertedText->Truncate(); - } - - *aChange = eNoChange; - } - - /** - * Fire text change event. - */ - void FireEvent(const nsAString& aModText, PRUint32 aOffset, PRBool aType); - -private: - nsDocAccessible* mDocument; - nsTextAccessible* mTextLeaf; -}; - -void -TextUpdater::Run(const nsAString& aNewText) -{ - NS_ASSERTION(mTextLeaf, "No text leaf accessible?"); - - const nsString& oldText = mTextLeaf->Text(); - PRUint32 oldLen = oldText.Length(), newLen = aNewText.Length(); - PRUint32 minLen = oldLen < newLen ? oldLen : newLen; - - // Skip coinciding begin substrings. - PRUint32 skipIdx = 0; - for (; skipIdx < minLen; skipIdx++) { - if (aNewText[skipIdx] != oldText[skipIdx]) - break; - } - - // No change, text append or removal to/from the end. - if (skipIdx == minLen) { - if (oldLen == newLen) - return; - - // If text has been appended to the end, fire text inserted event. - if (oldLen < newLen) { - FireEvent(Substring(aNewText, oldLen), oldLen, PR_TRUE); - mTextLeaf->SetText(aNewText); - return; - } - - // Text has been removed from the end, fire text removed event. - FireEvent(Substring(oldText, newLen), newLen, PR_FALSE); - mTextLeaf->SetText(aNewText); - return; - } - - // Trim coinciding substrings from the end. - PRUint32 endIdx = minLen; - if (oldLen < newLen) { - PRUint32 delta = newLen - oldLen; - for (; endIdx > skipIdx; endIdx--) { - if (aNewText[endIdx + delta] != oldText[endIdx]) - break; - } - } else { - PRUint32 delta = oldLen - newLen; - for (; endIdx > skipIdx; endIdx--) { - if (aNewText[endIdx] != oldText[endIdx + delta]) - break; - } - } - PRUint32 oldEndIdx = oldLen - minLen + endIdx; - PRUint32 newEndIdx = newLen - minLen + endIdx; - - // Find the difference starting from start character, we can skip initial and - // final coinciding characters since they don't affect on the Levenshtein - // distance. - - const nsDependentSubstring& str1 = - Substring(oldText, skipIdx, oldEndIdx - skipIdx); - const nsDependentSubstring& str2 = - Substring(aNewText, skipIdx, newEndIdx - skipIdx); - - // Compute the matrix. - PRUint32 len1 = str1.Length() + 1, len2 = str2.Length() + 1; - - PRUint32** matrix = new PRUint32*[len1]; - for (PRUint32 i = 0; i < len1; i++) - matrix[i] = new PRUint32[len2]; - - matrix[0][0] = 0; - - for (PRUint32 i = 1; i < len1; i++) - matrix[i][0] = i; - - for (PRUint32 j = 1; j < len2; j++) - matrix[0][j] = j; - - for (PRUint32 i = 1; i < len1; i++) { - for (PRUint32 j = 1; j < len2; j++) { - if (str1[i - 1] != str2[j - 1]) { - PRUint32 left = matrix[i - 1][j]; - PRUint32 up = matrix[i][j - 1]; - - PRUint32 upleft = matrix[i - 1][j - 1]; - matrix[i][j] = - (left < up ? (upleft < left ? upleft : left) : - (upleft < up ? upleft : up)) + 1; - } else { - matrix[i][j] = matrix[i - 1][j - 1]; - } - } - } - - FindDiffNFireEvents(str1, str2, matrix, skipIdx); - - for (PRUint32 i = 0; i < len1; i++) - delete[] matrix[i]; - delete[] matrix; - - mTextLeaf->SetText(aNewText); -} - -void -TextUpdater::FindDiffNFireEvents(const nsDependentSubstring& aStr1, - const nsDependentSubstring& aStr2, - PRUint32** aMatrix, - PRUint32 aStartOffset) -{ - // Find the difference. - ChangeType change = eNoChange; - nsAutoString insertedText; - nsAutoString removedText; - PRUint32 offset = 0; - - PRInt32 i = aStr1.Length(), j = aStr2.Length(); - while (i >= 0 && j >= 0) { - if (aMatrix[i][j] == 0) { - MayFireEvent(&insertedText, &removedText, offset + aStartOffset, &change); - return; - } - - // move up left - if (i >= 1 && j >= 1) { - // no change - if (aStr1[i - 1] == aStr2[j - 1]) { - MayFireEvent(&insertedText, &removedText, offset + aStartOffset, &change); - - i--; j--; - continue; - } - - // substitution - if (aMatrix[i][j] == aMatrix[i - 1][j - 1] + 1) { - if (change != eSubstitution) - MayFireEvent(&insertedText, &removedText, offset + aStartOffset, &change); - - offset = j - 1; - insertedText.Append(aStr1[i - 1]); - removedText.Append(aStr2[offset]); - change = eSubstitution; - - i--; j--; - continue; - } - } - - // move up, insertion - if (j >= 1 && aMatrix[i][j] == aMatrix[i][j - 1] + 1) { - if (change != eInsertion) - MayFireEvent(&insertedText, &removedText, offset + aStartOffset, &change); - - offset = j - 1; - insertedText.Insert(aStr2[offset], 0); - change = eInsertion; - - j--; - continue; - } - - // move left, removal - if (i >= 1 && aMatrix[i][j] == aMatrix[i - 1][j] + 1) { - if (change != eRemoval) { - MayFireEvent(&insertedText, &removedText, offset + aStartOffset, &change); - - offset = j; - } - - removedText.Insert(aStr1[i - 1], 0); - change = eRemoval; - - i--; - continue; - } - - NS_NOTREACHED("Huh?"); - return; - } - - MayFireEvent(&insertedText, &removedText, offset + aStartOffset, &change); -} - -void -TextUpdater::FireEvent(const nsAString& aModText, PRUint32 aOffset, - PRBool aIsInserted) -{ - nsAccessible* parent = mTextLeaf->GetParent(); - NS_ASSERTION(parent, "No parent for text leaf!"); - - nsHyperTextAccessible* hyperText = parent->AsHyperText(); - NS_ASSERTION(hyperText, "Text leaf parnet is not hyper text!"); - - PRInt32 textLeafOffset = hyperText->GetChildOffset(mTextLeaf, PR_TRUE); - NS_ASSERTION(textLeafOffset != -1, - "Text leaf hasn't offset within hyper text!"); - - // Fire text change event. - nsRefPtr textChangeEvent = - new AccTextChangeEvent(hyperText, textLeafOffset + aOffset, aModText, - aIsInserted); - mDocument->FireDelayedAccessibleEvent(textChangeEvent); - - // Fire value change event. - if (hyperText->Role() == nsIAccessibleRole::ROLE_ENTRY) { - nsRefPtr valueChangeEvent = - new AccEvent(nsIAccessibleEvent::EVENT_VALUE_CHANGE, hyperText, - eAutoDetect, AccEvent::eRemoveDupes); - mDocument->FireDelayedAccessibleEvent(valueChangeEvent); - } -} - PLDHashOperator NotificationController::TextEnumerator(nsCOMPtrHashKey* aEntry, void* aUserArg) @@ -927,9 +647,7 @@ NotificationController::TextEnumerator(nsCOMPtrHashKey* aEntry, NS_ConvertUTF16toUTF8(text).get()); #endif - TextUpdater updater(document, textAcc->AsTextLeaf()); - updater.Run(text); - + TextUpdater::Run(document, textAcc->AsTextLeaf(), text); return PL_DHASH_NEXT; } diff --git a/accessible/src/base/TextUpdater.cpp b/accessible/src/base/TextUpdater.cpp new file mode 100644 index 000000000000..f12a715bcc51 --- /dev/null +++ b/accessible/src/base/TextUpdater.cpp @@ -0,0 +1,263 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Alexander Surkov (original author) + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "TextUpdater.h" + +#include "nsDocAccessible.h" +#include "nsTextAccessible.h" + +void +TextUpdater::Run(nsDocAccessible* aDocument, nsTextAccessible* aTextLeaf, + const nsAString& aNewText) +{ + NS_ASSERTION(aTextLeaf, "No text leaf accessible?"); + + const nsString& oldText = aTextLeaf->Text(); + PRUint32 oldLen = oldText.Length(), newLen = aNewText.Length(); + PRUint32 minLen = NS_MIN(oldLen, newLen); + + // Skip coinciding begin substrings. + PRUint32 skipStart = 0; + for (; skipStart < minLen; skipStart++) { + if (aNewText[skipStart] != oldText[skipStart]) + break; + } + + // The text was changed. Do update. + if (skipStart != minLen || oldLen != newLen) { + TextUpdater updater(aDocument, aTextLeaf); + updater.DoUpdate(aNewText, oldText, skipStart); + } +} + +void +TextUpdater::DoUpdate(const nsAString& aNewText, const nsAString& aOldText, + PRUint32 aSkipStart) +{ + nsAccessible* parent = mTextLeaf->GetParent(); + NS_ASSERTION(parent, "No parent for text leaf!"); + + mHyperText = parent->AsHyperText(); + if (!mHyperText) { + NS_ERROR("Text leaf parent is not hypertext!"); + return; + } + + // Get the text leaf accessible offset and invalidate cached offsets after it. + mTextOffset = mHyperText->GetChildOffset(mTextLeaf, PR_TRUE); + NS_ASSERTION(mTextOffset != -1, + "Text leaf hasn't offset within hyper text!"); + + PRUint32 oldLen = aOldText.Length(), newLen = aNewText.Length(); + PRUint32 minLen = NS_MIN(oldLen, newLen); + + // Text was appended or removed to/from the end. + if (aSkipStart == minLen) { + // If text has been appended to the end, fire text inserted event. + if (oldLen < newLen) { + UpdateTextNFireEvent(aNewText, Substring(aNewText, oldLen), + oldLen, PR_TRUE); + return; + } + + // Text has been removed from the end, fire text removed event. + UpdateTextNFireEvent(aNewText, Substring(aOldText, newLen), + newLen, PR_FALSE); + return; + } + + // Trim coinciding substrings from the end. + PRUint32 skipEnd = 0; + while (minLen - skipEnd > aSkipStart && + aNewText[newLen - skipEnd - 1] == aOldText[oldLen - skipEnd - 1]) { + skipEnd++; + } + + // Text was appended or removed to/from the start. + if (skipEnd == minLen) { + // If text has been appended to the start, fire text inserted event. + if (oldLen < newLen) { + UpdateTextNFireEvent(aNewText, Substring(aNewText, 0, newLen - skipEnd), + 0, PR_TRUE); + return; + } + + // Text has been removed from the start, fire text removed event. + UpdateTextNFireEvent(aNewText, Substring(aOldText, 0, oldLen - skipEnd), + 0, PR_FALSE); + return; + } + + // Find the difference between strings and fire events. + // Note: we can skip initial and final coinciding characters since they don't + // affect the Levenshtein distance. + + PRInt32 strLen1 = oldLen - aSkipStart - skipEnd; + PRInt32 strLen2 = newLen - aSkipStart - skipEnd; + + const nsAString& str1 = Substring(aOldText, aSkipStart, strLen1); + const nsAString& str2 = Substring(aNewText, aSkipStart, strLen2); + + // Increase offset of the text leaf on skipped characters amount. + mTextOffset += aSkipStart; + + // Compute the flat structured matrix need to compute the difference. + PRUint32 len1 = strLen1 + 1, len2 = strLen2 + 1; + PRUint32* entries = new PRUint32[len1 * len2]; + + for (PRUint32 colIdx = 0; colIdx < len1; colIdx++) + entries[colIdx] = colIdx; + + PRUint32* row = entries; + for (PRUint32 rowIdx = 1; rowIdx < len2; rowIdx++) { + PRUint32* prevRow = row; + row += len1; + row[0] = rowIdx; + for (PRUint32 colIdx = 1; colIdx < len1; colIdx++) { + if (str1[colIdx - 1] != str2[rowIdx - 1]) { + PRUint32 left = row[colIdx - 1]; + PRUint32 up = prevRow[colIdx]; + PRUint32 upleft = prevRow[colIdx - 1]; + row[colIdx] = NS_MIN(upleft, NS_MIN(left, up)) + 1; + } else { + row[colIdx] = prevRow[colIdx - 1]; + } + } + } + + // Compute events based on the difference. + nsTArray > events; + ComputeTextChangeEvents(str1, str2, entries, events); + + delete [] entries; + + // Fire events. + for (PRInt32 idx = events.Length() - 1; idx >= 0; idx--) + mDocument->FireDelayedAccessibleEvent(events[idx]); + + if (mHyperText->Role() == nsIAccessibleRole::ROLE_ENTRY) { + nsRefPtr valueChangeEvent = + new AccEvent(nsIAccessibleEvent::EVENT_VALUE_CHANGE, mHyperText, + eAutoDetect, AccEvent::eRemoveDupes); + mDocument->FireDelayedAccessibleEvent(valueChangeEvent); + } + + // Update the text. + mTextLeaf->SetText(aNewText); +} + +void +TextUpdater::ComputeTextChangeEvents(const nsAString& aStr1, + const nsAString& aStr2, + PRUint32* aEntries, + nsTArray >& aEvents) +{ + PRInt32 colIdx = aStr1.Length(), rowIdx = aStr2.Length(); + + // Point at which strings last matched. + PRInt32 colEnd = colIdx; + PRInt32 rowEnd = rowIdx; + + PRInt32 colLen = colEnd + 1; + PRUint32* row = aEntries + rowIdx * colLen; + PRInt32 dist = row[colIdx]; // current Levenshtein distance + while (rowIdx && colIdx) { // stop when we can't move diagonally + if (aStr1[colIdx - 1] == aStr2[rowIdx - 1]) { // match + if (rowIdx < rowEnd) { // deal with any pending insertion + FireInsertEvent(Substring(aStr2, rowIdx, rowEnd - rowIdx), + rowIdx, aEvents); + } + if (colIdx < colEnd) { // deal with any pending deletion + FireDeleteEvent(Substring(aStr1, colIdx, colEnd - colIdx), + rowIdx, aEvents); + } + + colEnd = --colIdx; // reset the match point + rowEnd = --rowIdx; + row -= colLen; + continue; + } + --dist; + if (dist == row[colIdx - 1 - colLen]) { // substitution + --colIdx; + --rowIdx; + row -= colLen; + continue; + } + if (dist == row[colIdx - colLen]) { // insertion + --rowIdx; + row -= colLen; + continue; + } + if (dist == row[colIdx - 1]) { // deletion + --colIdx; + continue; + } + NS_NOTREACHED("huh?"); + return; + } + + if (rowEnd) + FireInsertEvent(Substring(aStr2, 0, rowEnd), 0, aEvents); + if (colEnd) + FireDeleteEvent(Substring(aStr1, 0, colEnd), 0, aEvents); +} + +void +TextUpdater::UpdateTextNFireEvent(const nsAString& aNewText, + const nsAString& aChangeText, + PRUint32 aAddlOffset, + PRBool aIsInserted) +{ + // Fire text change event. + nsRefPtr textChangeEvent = + new AccTextChangeEvent(mHyperText, mTextOffset + aAddlOffset, aChangeText, + aIsInserted); + mDocument->FireDelayedAccessibleEvent(textChangeEvent); + + // Fire value change event. + if (mHyperText->Role() == nsIAccessibleRole::ROLE_ENTRY) { + nsRefPtr valueChangeEvent = + new AccEvent(nsIAccessibleEvent::EVENT_VALUE_CHANGE, mHyperText, + eAutoDetect, AccEvent::eRemoveDupes); + mDocument->FireDelayedAccessibleEvent(valueChangeEvent); + } + + // Update the text. + mTextLeaf->SetText(aNewText); +} diff --git a/accessible/src/base/TextUpdater.h b/accessible/src/base/TextUpdater.h new file mode 100644 index 000000000000..8dc7da50d457 --- /dev/null +++ b/accessible/src/base/TextUpdater.h @@ -0,0 +1,124 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Alexander Surkov (original author) + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef TextUpdater_h_ +#define TextUpdater_h_ + +#include "AccEvent.h" +#include "nsHyperTextAccessible.h" + +/** + * Used to find a difference between old and new text and fire text change + * events. + */ +class TextUpdater +{ +public: + /** + * Start text of the text leaf update. + */ + static void Run(nsDocAccessible* aDocument, nsTextAccessible* aTextLeaf, + const nsAString& aNewText); + +private: + TextUpdater(nsDocAccessible* aDocument, nsTextAccessible* aTextLeaf) : + mDocument(aDocument), mTextLeaf(aTextLeaf), mHyperText(nsnull), + mTextOffset(-1) { } + + ~TextUpdater() + { mDocument = nsnull; mTextLeaf = nsnull; mHyperText = nsnull; } + + /** + * Update text of the text leaf accessible, fire text change and value change + * (if applicable) events for its container hypertext accessible. + */ + void DoUpdate(const nsAString& aNewText, const nsAString& aOldText, + PRUint32 aSkipStart); + +private: + TextUpdater(); + TextUpdater(const TextUpdater&); + TextUpdater& operator=(const TextUpdater&); + + /** + * Fire text change events based on difference between strings. + */ + void ComputeTextChangeEvents(const nsAString& aStr1, + const nsAString& aStr2, + PRUint32* aEntries, + nsTArray >& aEvents); + + /** + * Helper to create text change events for inserted text. + */ + inline void FireInsertEvent(const nsAString& aText, PRUint32 aAddlOffset, + nsTArray >& aEvents) + { + nsRefPtr event = + new AccTextChangeEvent(mHyperText, mTextOffset + aAddlOffset, + aText, PR_TRUE); + aEvents.AppendElement(event); + } + + /** + * Helper to create text change events for removed text. + */ + inline void FireDeleteEvent(const nsAString& aText, PRUint32 aAddlOffset, + nsTArray >& aEvents) + { + nsRefPtr event = + new AccTextChangeEvent(mHyperText, mTextOffset + aAddlOffset, + aText, PR_FALSE); + aEvents.AppendElement(event); + } + + /** + * Update the text and fire text change/value change events. + */ + void UpdateTextNFireEvent(const nsAString& aNewText, + const nsAString& aChangeText, PRUint32 aAddlOffset, + PRBool aIsInserted); + +private: + nsDocAccessible* mDocument; + nsTextAccessible* mTextLeaf; + nsHyperTextAccessible* mHyperText; + PRInt32 mTextOffset; +}; + +#endif diff --git a/accessible/tests/mochitest/events.js b/accessible/tests/mochitest/events.js index a5027a9a3ef1..527772a0ddd5 100644 --- a/accessible/tests/mochitest/events.js +++ b/accessible/tests/mochitest/events.js @@ -331,7 +331,7 @@ function eventQueue(aEventType) var idx = 0; for (; idx < this.mEventSeq.length; idx++) { if (!this.isEventUnexpected(idx) && (invoker.wasCaught[idx] == true) && - this.compareEvents(idx, aEvent)) { + this.isAlreadyCaught(idx, aEvent)) { var msg = "Doubled event { event type: " + this.getEventTypeAsString(idx) + ", target: " + @@ -543,6 +543,15 @@ function eventQueue(aEventType) return target1 == target2; } + this.isAlreadyCaught = function eventQueue_isAlreadyCaught(aIdx, aEvent) + { + // We don't have stored info about handled event other than its type and + // target, thus we should filter text change events since they may occur + // on the same element because of complex changes. + return this.compareEvents(aIdx, aEvent) && + !(aEvent instanceof nsIAccessibleTextChangeEvent); + } + this.checkEvent = function eventQueue_checkEvent(aIdx, aEvent) { var eventItem = this.mEventSeq[aIdx]; diff --git a/accessible/tests/mochitest/events/Makefile.in b/accessible/tests/mochitest/events/Makefile.in index 7e1ed205608c..a4c7ab4d66f0 100644 --- a/accessible/tests/mochitest/events/Makefile.in +++ b/accessible/tests/mochitest/events/Makefile.in @@ -71,6 +71,7 @@ _TEST_FILES =\ test_scroll.xul \ test_selection.html \ test_statechange.html \ + test_text_alg.html \ test_text.html \ test_textattrchange.html \ test_tree.xul \ diff --git a/accessible/tests/mochitest/events/test_text_alg.html b/accessible/tests/mochitest/events/test_text_alg.html new file mode 100644 index 000000000000..6c86c1c97003 --- /dev/null +++ b/accessible/tests/mochitest/events/test_text_alg.html @@ -0,0 +1,205 @@ + + + + Accessible text update algorithm testing + + + + + + + + + + + + + + + + + Mozilla Bug 626660 + +
+ +

+ +
+  
+
+ +

wqrema

+

b

+

abc

+

abcabc

+

abc

+

abc

+

defabc

+

abcdef

+

abcDEFabc

+

!abcdef@

+

meilenstein

+ +