Index: content/public/android/java/src/org/chromium/content/browser/input/CursorAnchorInfoController.java |
diff --git a/content/public/android/java/src/org/chromium/content/browser/input/CursorAnchorInfoController.java b/content/public/android/java/src/org/chromium/content/browser/input/CursorAnchorInfoController.java |
index 718db1632ed332fb1d255499929383050720e4aa..7e3171456c6cbb36bd9a7729cdb65d5f1da192c9 100644 |
--- a/content/public/android/java/src/org/chromium/content/browser/input/CursorAnchorInfoController.java |
+++ b/content/public/android/java/src/org/chromium/content/browser/input/CursorAnchorInfoController.java |
@@ -14,7 +14,9 @@ import org.chromium.base.VisibleForTesting; |
import org.chromium.base.annotations.SuppressFBWarnings; |
import org.chromium.content.browser.RenderCoordinates; |
+import java.util.ArrayList; |
import java.util.Arrays; |
+import java.util.HashMap; |
import javax.annotation.Nonnull; |
import javax.annotation.Nullable; |
@@ -45,6 +47,12 @@ final class CursorAnchorInfoController { |
int getComposingTextEnd(); |
} |
+ /** |
+ * An interface implemented by classes that want to listener to |
+ * CursorAnchorInfo updates. |
+ */ |
+ public interface Listener { void onCursorAnchorInfoUpdate(CursorAnchorInfo info); } |
+ |
// Current focus and monitoring states. |
private boolean mIsEditable; |
private boolean mHasPendingImmediateRequest; |
@@ -82,6 +90,10 @@ final class CursorAnchorInfoController { |
@Nonnull |
private final ViewDelegate mViewDelegate; |
+ @Nonnull |
+ private final HashMap<View, ArrayList<Listener>> mListeners = |
+ new HashMap<View, ArrayList<Listener>>(); |
+ |
private CursorAnchorInfoController(InputMethodManagerWrapper inputMethodManagerWrapper, |
ComposingTextDelegate composingTextDelegate, ViewDelegate viewDelegate) { |
mInputMethodManagerWrapper = inputMethodManagerWrapper; |
@@ -92,8 +104,8 @@ final class CursorAnchorInfoController { |
public static CursorAnchorInfoController create( |
InputMethodManagerWrapper inputMethodManagerWrapper, |
ComposingTextDelegate composingTextDelegate) { |
- return new CursorAnchorInfoController(inputMethodManagerWrapper, |
- composingTextDelegate, new ViewDelegate() { |
+ return new CursorAnchorInfoController( |
+ inputMethodManagerWrapper, composingTextDelegate, new ViewDelegate() { |
@Override |
public void getLocationOnScreen(View view, int[] location) { |
view.getLocationOnScreen(location); |
@@ -110,10 +122,9 @@ final class CursorAnchorInfoController { |
@VisibleForTesting |
public static CursorAnchorInfoController createForTest( |
InputMethodManagerWrapper inputMethodManagerWrapper, |
- ComposingTextDelegate composingTextDelegate, |
- ViewDelegate viewDelegate) { |
- return new CursorAnchorInfoController(inputMethodManagerWrapper, composingTextDelegate, |
- viewDelegate); |
+ ComposingTextDelegate composingTextDelegate, ViewDelegate viewDelegate) { |
+ return new CursorAnchorInfoController( |
+ inputMethodManagerWrapper, composingTextDelegate, viewDelegate); |
} |
/** |
@@ -193,10 +204,10 @@ final class CursorAnchorInfoController { |
mInsertionMarkerBottom = insertionMarkerBottom; |
} |
- // Notify to IME if there is a pending request, or if it is in monitor mode and we have |
- // some change in the state. |
+ // Notify to IME if there is a pending request, or if we're monitoring |
+ // for the view and we have some change in the state. |
if (mHasPendingImmediateRequest |
- || (mMonitorModeEnabled && mLastCursorAnchorInfo == null)) { |
+ || (monitoringForView(view) && mLastCursorAnchorInfo == null)) { |
updateCursorAnchorInfo(view); |
} |
} |
@@ -212,12 +223,15 @@ final class CursorAnchorInfoController { |
View view) { |
if (!mIsEditable) return false; |
- if (mMonitorModeEnabled && !monitorRequest) { |
- // Invalidate saved cursor anchor info if monitor request is cancelled since no longer |
- // new values will be arrived from renderer and immediate request may return too old |
- // position. |
- invalidateLastCursorAnchorInfo(); |
+ if (!monitorRequest && monitoringForView(view)) { |
+ // Invalidate saved cursor anchor info if we're no longer going to |
+ // be monitoring for this view to prevent returning stale values for |
+ // immediate requests. |
+ if (!mListeners.containsKey(view) || mListeners.get(view).isEmpty()) { |
+ invalidateLastCursorAnchorInfo(); |
+ } |
} |
+ |
mMonitorModeEnabled = monitorRequest; |
if (immediateRequest) { |
mHasPendingImmediateRequest = true; |
@@ -226,6 +240,27 @@ final class CursorAnchorInfoController { |
return true; |
} |
+ public void addListener(Listener listener, View view) { |
+ if (!mListeners.containsKey(view)) { |
+ mListeners.put(view, new ArrayList<Listener>()); |
+ } |
+ mListeners.get(view).add(listener); |
+ |
+ mHasPendingImmediateRequest = true; |
+ } |
+ |
+ public void removeListener(Listener listener, View view) { |
+ mListeners.get(view).remove(listener); |
+ } |
+ |
+ private boolean monitoringForView(View view) { |
+ if (mMonitorModeEnabled) { |
+ return true; |
+ } |
+ |
+ return mListeners.containsKey(view) && !mListeners.get(view).isEmpty(); |
+ } |
+ |
/** |
* Computes the CursorAnchorInfo instance and notify to InputMethodManager if needed. |
*/ |
@@ -277,6 +312,12 @@ final class CursorAnchorInfoController { |
if (mInputMethodManagerWrapper != null) { |
mInputMethodManagerWrapper.updateCursorAnchorInfo(view, mLastCursorAnchorInfo); |
} |
+ |
+ if (mListeners.containsKey(view)) { |
+ for (Listener listener : mListeners.get(view)) { |
+ listener.onCursorAnchorInfoUpdate(mLastCursorAnchorInfo); |
+ } |
+ } |
mHasPendingImmediateRequest = false; |
} |
} |