Bug 1470786 - 2. Fix a text sync issue; r?esawin
Fix an issue in GeckoEditable where spans can be mistakenly deleted when
deleting text. The new code re-copies all spans to make sure similar
issues don't happen in the future.
MozReview-Commit-ID: G1fWsJkeTka
--- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoEditable.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/GeckoEditable.java
@@ -322,32 +322,39 @@ import android.view.inputmethod.EditorIn
}
// Copy the portion of the current text that has changed over to the shadow
// text, with consideration for any concurrent changes in the shadow text.
final int start = Math.min(mShadowStart, mCurrentStart);
final int shadowEnd = mShadowNewEnd + Math.max(0, mCurrentOldEnd - mShadowOldEnd);
final int currentEnd = mCurrentNewEnd + Math.max(0, mShadowOldEnd - mCurrentOldEnd);
- // Remove identical spans that are in the new text from the old text.
- // Otherwise the new spans won't be inserted due to the text already having
- // the old spans.
- Object[] spans = mCurrentText.getSpans(start, currentEnd, Object.class);
- for (final Object span : spans) {
- mShadowText.removeSpan(span);
- }
-
- // Also remove existing spans that are no longer in the new text.
- spans = mShadowText.getSpans(start, shadowEnd, Object.class);
+ // Remove existing spans that may no longer be in the new text.
+ Object[] spans = mShadowText.getSpans(start, shadowEnd, Object.class);
for (final Object span : spans) {
mShadowText.removeSpan(span);
}
mShadowText.replace(start, shadowEnd, mCurrentText, start, currentEnd);
+ // The replace() call may not have copied all affected spans, so we re-copy all the
+ // spans manually just in case. Expand bounds by 1 so we get all the spans.
+ spans = mCurrentText.getSpans(Math.max(start - 1, 0),
+ Math.min(currentEnd + 1, mCurrentText.length()),
+ Object.class);
+ for (final Object span : spans) {
+ if (span == Selection.SELECTION_START || span == Selection.SELECTION_END) {
+ continue;
+ }
+ mShadowText.setSpan(span,
+ mCurrentText.getSpanStart(span),
+ mCurrentText.getSpanEnd(span),
+ mCurrentText.getSpanFlags(span));
+ }
+
// SpannableStringBuilder has some internal logic to fix up selections, but we
// don't want that, so we always fix up the selection a second time.
final int selStart = Selection.getSelectionStart(mCurrentText);
final int selEnd = Selection.getSelectionEnd(mCurrentText);
Selection.setSelection(mShadowText, selStart, selEnd);
if (DEBUG && !checkEqualText(mShadowText, mCurrentText)) {
// Sanity check.
@@ -378,16 +385,20 @@ import android.view.inputmethod.EditorIn
private static boolean checkEqualText(final Spanned s1, final Spanned s2) {
if (!s1.toString().equals(s2.toString())) {
return false;
}
final Object[] o1s = s1.getSpans(0, s1.length(), Object.class);
final Object[] o2s = s2.getSpans(0, s2.length(), Object.class);
+ if (o1s.length != o2s.length) {
+ return false;
+ }
+
o1loop: for (final Object o1 : o1s) {
for (final Object o2 : o2s) {
if (o1 != o2) {
continue;
}
if (s1.getSpanStart(o1) != s2.getSpanStart(o2) ||
s1.getSpanEnd(o1) != s2.getSpanEnd(o2) ||
s1.getSpanFlags(o1) != s2.getSpanFlags(o2)) {