Bug 652168: Add thumb for input box. [r=mfinkle]
☠☠ backed out by d2deb477dc2b ☠ ☠
authorSriram Ramasubramanian <sriram@mozilla.com>
Thu, 13 Sep 2012 10:19:11 -0700
changeset 107103 4607eda1a89ff321c6ab904fbd4d07845a64782f
parent 107102 de38f0156b33d55e8b90eea2ab8e32352f5e858c
child 107104 eb6caf30255cfc749d9d4287301ad9d0fb743dae
push id74
push usershu@rfrn.org
push dateTue, 18 Sep 2012 19:23:47 +0000
reviewersmfinkle
bugs652168
milestone18.0a1
Bug 652168: Add thumb for input box. [r=mfinkle]
mobile/android/base/GeckoApp.java
mobile/android/base/Makefile.in
mobile/android/base/TextSelection.java
mobile/android/base/TextSelectionHandle.java
mobile/android/base/resources/drawable-hdpi/handle_middle.png
mobile/android/base/resources/drawable-xhdpi/handle_middle.png
mobile/android/base/resources/drawable/handle_middle.png
mobile/android/base/resources/layout/text_selection_handles.xml.in
mobile/android/base/resources/values/attrs.xml
mobile/android/chrome/content/browser.js
--- a/mobile/android/base/GeckoApp.java
+++ b/mobile/android/base/GeckoApp.java
@@ -1608,16 +1608,17 @@ abstract public class GeckoApp
         mBatteryReceiver.registerFor(mAppContext);
 
         mConnectivityReceiver = new GeckoConnectivityReceiver();
         mConnectivityReceiver.registerFor(mAppContext);
 
         mPromptService = new PromptService();
 
         mTextSelection = new TextSelection((TextSelectionHandle) findViewById(R.id.start_handle),
+                                           (TextSelectionHandle) findViewById(R.id.middle_handle),
                                            (TextSelectionHandle) findViewById(R.id.end_handle),
                                            GeckoAppShell.getEventDispatcher());
 
         GeckoNetworkManager.getInstance().init();
         GeckoNetworkManager.getInstance().start();
 
         UpdateServiceHelper.registerForUpdates(this);
 
--- a/mobile/android/base/Makefile.in
+++ b/mobile/android/base/Makefile.in
@@ -499,16 +499,17 @@ RES_DRAWABLE_BASE = \
   res/drawable/reader.png \
   res/drawable/reading_list.png \
   res/drawable/validation_arrow.png \
   res/drawable/validation_arrow_inverted.png \
   res/drawable/validation_bg.9.png \
   res/drawable/bookmarkdefaults_favicon_support.png \
   res/drawable/bookmarkdefaults_favicon_addons.png \
   res/drawable/handle_end.png \
+  res/drawable/handle_middle.png \
   res/drawable/handle_start.png \
   $(addprefix res/drawable-mdpi/,$(notdir $(SYNC_RES_DRAWABLE_MDPI))) \
   $(NULL)
 
 RES_DRAWABLE_LDPI = \
   $(addprefix res/drawable-ldpi/,$(notdir $(SYNC_RES_DRAWABLE_LDPI))) \
   $(NULL)
 
@@ -567,16 +568,17 @@ RES_DRAWABLE_HDPI = \
   res/drawable-hdpi/site_security_verified.png \
   res/drawable-hdpi/urlbar_stop.png \
   res/drawable-hdpi/reader.png \
   res/drawable-hdpi/reading_list.png \
   res/drawable-hdpi/validation_arrow.png \
   res/drawable-hdpi/validation_arrow_inverted.png \
   res/drawable-hdpi/validation_bg.9.png \
   res/drawable-hdpi/handle_end.png \
+  res/drawable-hdpi/handle_middle.png \
   res/drawable-hdpi/handle_start.png \
   $(addprefix res/drawable-hdpi/,$(notdir $(SYNC_RES_DRAWABLE_HDPI))) \
   $(NULL)
 
 RES_DRAWABLE_XHDPI = \
   res/drawable-xhdpi/favicon.png \
   res/drawable-xhdpi/folder.png \
   res/drawable-xhdpi/abouthome_icon.png \
@@ -629,16 +631,17 @@ RES_DRAWABLE_XHDPI = \
   res/drawable-xhdpi/remote_tabs_off.png \
   res/drawable-xhdpi/remote_tabs_on.png \
   res/drawable-xhdpi/site_security_identified.png \
   res/drawable-xhdpi/site_security_verified.png \
   res/drawable-xhdpi/validation_arrow.png \
   res/drawable-xhdpi/validation_arrow_inverted.png \
   res/drawable-xhdpi/validation_bg.9.png \
   res/drawable-xhdpi/handle_end.png \
+  res/drawable-xhdpi/handle_middle.png \
   res/drawable-xhdpi/handle_start.png \
   $(NULL)
 
 RES_DRAWABLE_MDPI_V11 = \
   res/drawable-mdpi-v11/alert_addon.png \
   res/drawable-mdpi-v11/alert_app.png \
   res/drawable-mdpi-v11/alert_download.png \
   res/drawable-mdpi-v11/ic_menu_back.png \
--- a/mobile/android/base/TextSelection.java
+++ b/mobile/android/base/TextSelection.java
@@ -6,40 +6,45 @@ package org.mozilla.gecko;
 
 import org.mozilla.gecko.gfx.Layer;
 import org.mozilla.gecko.gfx.Layer.RenderContext;
 import org.mozilla.gecko.gfx.LayerView;
 import org.mozilla.gecko.util.EventDispatcher;
 import org.mozilla.gecko.util.FloatUtils;
 import org.mozilla.gecko.util.GeckoEventListener;
 
+import org.json.JSONArray;
 import org.json.JSONObject;
 
 import android.util.Log;
 import android.view.View;
 
 class TextSelection extends Layer implements GeckoEventListener {
     private static final String LOGTAG = "GeckoTextSelection";
 
     private final TextSelectionHandle mStartHandle;
+    private final TextSelectionHandle mMiddleHandle;
     private final TextSelectionHandle mEndHandle;
     private final EventDispatcher mEventDispatcher;
 
     private float mViewLeft;
     private float mViewTop;
     private float mViewZoom;
 
-    TextSelection(TextSelectionHandle startHandle, TextSelectionHandle endHandle,
+    TextSelection(TextSelectionHandle startHandle,
+                  TextSelectionHandle middleHandle,
+                  TextSelectionHandle endHandle,
                   EventDispatcher eventDispatcher) {
         mStartHandle = startHandle;
+        mMiddleHandle = middleHandle;
         mEndHandle = endHandle;
         mEventDispatcher = eventDispatcher;
 
-        // Only register listeners if we have valid start/end handles
-        if (mStartHandle == null || mEndHandle == null) {
+        // Only register listeners if we have valid start/middle/end handles
+        if (mStartHandle == null || mMiddleHandle == null || mEndHandle == null) {
             Log.e(LOGTAG, "Failed to initialize text selection because at least one handle is null");
         } else {
             registerEventListener("TextSelection:ShowHandles");
             registerEventListener("TextSelection:HideHandles");
             registerEventListener("TextSelection:PositionHandles");
         }
     }
 
@@ -47,52 +52,83 @@ class TextSelection extends Layer implem
         unregisterEventListener("TextSelection:ShowHandles");
         unregisterEventListener("TextSelection:HideHandles");
         unregisterEventListener("TextSelection:PositionHandles");
     }
 
     public void handleMessage(String event, JSONObject message) {
         try {
             if (event.equals("TextSelection:ShowHandles")) {
+                final JSONArray handles = message.getJSONArray("handles");
                 GeckoApp.mAppContext.mMainHandler.post(new Runnable() {
                     public void run() {
-                        mStartHandle.setVisibility(View.VISIBLE);
-                        mEndHandle.setVisibility(View.VISIBLE);
+                        try {
+                            for (int i=0; i < handles.length(); i++) {
+                                String handle = handles.getString(i);
 
-                        mViewLeft = 0.0f;
-                        mViewTop = 0.0f;
-                        mViewZoom = 0.0f;
-                        LayerView layerView = GeckoApp.mAppContext.getLayerView();
-                        if (layerView != null) {
-                            layerView.addLayer(TextSelection.this);
-                        }
+                                if (handle.equals("START"))
+                                    mStartHandle.setVisibility(View.VISIBLE);
+                                else if (handle.equals("MIDDLE"))
+                                    mMiddleHandle.setVisibility(View.VISIBLE);
+                                else
+                                    mEndHandle.setVisibility(View.VISIBLE);
+                            }
+
+                            mViewLeft = 0.0f;
+                            mViewTop = 0.0f;
+                            mViewZoom = 0.0f;
+                            LayerView layerView = GeckoApp.mAppContext.getLayerView();
+                            if (layerView != null) {
+                                layerView.addLayer(TextSelection.this);
+                            }
+                        } catch(Exception e) {}
                     }
                 });
             } else if (event.equals("TextSelection:HideHandles")) {
+                final JSONArray handles = message.getJSONArray("handles");
                 GeckoApp.mAppContext.mMainHandler.post(new Runnable() {
                     public void run() {
-                        LayerView layerView = GeckoApp.mAppContext.getLayerView();
-                        if (layerView != null) {
-                            layerView.removeLayer(TextSelection.this);
-                        }
+                        try {
+                            LayerView layerView = GeckoApp.mAppContext.getLayerView();
+                            if (layerView != null) {
+                                layerView.removeLayer(TextSelection.this);
+                            }
 
-                        mStartHandle.setVisibility(View.GONE);
-                        mEndHandle.setVisibility(View.GONE);
+                            for (int i=0; i < handles.length(); i++) {
+                                String handle = handles.getString(i);
+                                if (handle.equals("START"))
+                                    mStartHandle.setVisibility(View.GONE);
+                                else if (handle.equals("MIDDLE"))
+                                    mMiddleHandle.setVisibility(View.GONE);
+                                else
+                                    mEndHandle.setVisibility(View.GONE);
+                            }
+
+                        } catch(Exception e) {}
                     }
                 });
             } else if (event.equals("TextSelection:PositionHandles")) {
-                final int startLeft = message.getInt("startLeft");
-                final int startTop = message.getInt("startTop");
-                final int endLeft = message.getInt("endLeft");
-                final int endTop = message.getInt("endTop");
-
+                final JSONArray positions = message.getJSONArray("positions");
                 GeckoApp.mAppContext.mMainHandler.post(new Runnable() {
                     public void run() {
-                        mStartHandle.positionFromGecko(startLeft, startTop);
-                        mEndHandle.positionFromGecko(endLeft, endTop);
+                        try {
+                            for (int i=0; i < positions.length(); i++) {
+                                JSONObject position = positions.getJSONObject(i);
+                                String handle = position.getString("handle");
+                                int left = position.getInt("left");
+                                int top = position.getInt("top");
+
+                                if (handle.equals("START"))
+                                    mStartHandle.positionFromGecko(left, top);
+                                else if (handle.equals("MIDDLE"))
+                                    mMiddleHandle.positionFromGecko(left, top);
+                                else
+                                    mEndHandle.positionFromGecko(left, top);
+                             }
+                        } catch (Exception e) { }
                     }
                 });
             }
         } catch (Exception e) {
             Log.e(LOGTAG, "Exception handling message \"" + event + "\":", e);
         }
     }
 
@@ -108,16 +144,17 @@ class TextSelection extends Layer implem
         }
         mViewLeft = context.viewport.left;
         mViewTop = context.viewport.top;
         mViewZoom = context.zoomFactor;
 
         GeckoApp.mAppContext.mMainHandler.post(new Runnable() {
             public void run() {
                 mStartHandle.repositionWithViewport(context.viewport.left, context.viewport.top, context.zoomFactor);
+                mMiddleHandle.repositionWithViewport(context.viewport.left, context.viewport.top, context.zoomFactor);
                 mEndHandle.repositionWithViewport(context.viewport.left, context.viewport.top, context.zoomFactor);
             }
         });
     }
 
     private void registerEventListener(String event) {
         mEventDispatcher.registerEventListener(event, this);
     }
--- a/mobile/android/base/TextSelectionHandle.java
+++ b/mobile/android/base/TextSelectionHandle.java
@@ -17,17 +17,17 @@ import android.util.Log;
 import android.view.MotionEvent;
 import android.view.View;
 import android.widget.ImageView;
 import android.widget.RelativeLayout;
 
 class TextSelectionHandle extends ImageView implements View.OnTouchListener {
     private static final String LOGTAG = "GeckoTextSelectionHandle";
 
-    private enum HandleType { START, END }; 
+    private enum HandleType { START, MIDDLE, END }; 
 
     private final HandleType mHandleType;
     private final int mWidth;
     private final int mHeight;
     private final int mShadow;
 
     private int mLeft;
     private int mTop;
@@ -37,18 +37,26 @@ class TextSelectionHandle extends ImageV
 
     private RelativeLayout.LayoutParams mLayoutParams;
 
     TextSelectionHandle(Context context, AttributeSet attrs) {
         super(context, attrs);
         setOnTouchListener(this);
 
         TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TextSelectionHandle);
-        String handleType = a.getString(R.styleable.TextSelectionHandle_handleType);
-        mHandleType = handleType.equals("START") ? HandleType.START : HandleType.END;
+        int handleType = a.getInt(R.styleable.TextSelectionHandle_handleType, 0x01);
+
+        if (handleType == 0x01)
+            mHandleType = HandleType.START;
+        else if (handleType == 0x02)
+            mHandleType = HandleType.MIDDLE;
+        else
+            mHandleType = HandleType.END;
+
+        mGeckoPoint = new PointF(0.0f, 0.0f);
 
         mWidth = getResources().getDimensionPixelSize(R.dimen.text_selection_handle_width);
         mHeight = getResources().getDimensionPixelSize(R.dimen.text_selection_handle_height);
         mShadow = getResources().getDimensionPixelSize(R.dimen.text_selection_handle_shadow);
     }
 
     public boolean onTouch(View v, MotionEvent event) {
         switch (event.getActionMasked()) {
@@ -84,17 +92,24 @@ class TextSelectionHandle extends ImageV
         mTop = mTop + newY - mTouchStartY;
 
         LayerView layerView = GeckoApp.mAppContext.getLayerView();
         if (layerView == null) {
             Log.e(LOGTAG, "Can't move selection because layerView is null");
             return;
         }
         // Send x coordinate on the right side of the start handle, left side of the end handle.
-        float left = (float) mLeft + (mHandleType.equals(HandleType.START) ? mWidth - mShadow : mShadow);
+        float left = (float) mLeft;
+        if (mHandleType.equals(HandleType.START))
+            left +=  mWidth - mShadow;
+        else if (mHandleType.equals(HandleType.MIDDLE))
+            left +=  (float) ((mWidth - mShadow) / 2);
+        else
+            left += mShadow;
+
         PointF geckoPoint = new PointF(left, (float) mTop);
         geckoPoint = layerView.convertViewPointToLayerPoint(geckoPoint);
 
         JSONObject args = new JSONObject();
         try {
             args.put("handleType", mHandleType.toString());
             args.put("x", Math.round(geckoPoint.x));
             args.put("y", Math.round(geckoPoint.y));
@@ -117,17 +132,24 @@ class TextSelectionHandle extends ImageV
         ImmutableViewportMetrics metrics = layerView.getViewportMetrics();
         repositionWithViewport(metrics.viewportRectLeft, metrics.viewportRectTop, metrics.zoomFactor);
     }
 
     void repositionWithViewport(float x, float y, float zoom) {
         PointF viewPoint = new PointF((mGeckoPoint.x * zoom) - x,
                                       (mGeckoPoint.y * zoom) - y);
 
-        mLeft = Math.round(viewPoint.x) - (mHandleType.equals(HandleType.START) ? mWidth - mShadow : mShadow);
+        mLeft = Math.round(viewPoint.x);
+        if (mHandleType.equals(HandleType.START))
+            mLeft -=  mWidth - mShadow;
+        else if (mHandleType.equals(HandleType.MIDDLE))
+            mLeft -=  (float) ((mWidth - mShadow) / 2);
+        else
+            mLeft -= mShadow;
+
         mTop = Math.round(viewPoint.y);
 
         setLayoutPosition();
     }
 
     private void setLayoutPosition() {
         if (mLayoutParams == null) {
             mLayoutParams = (RelativeLayout.LayoutParams) getLayoutParams();
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..5dcee14f7a70c720d23716cef4cae3c399c7f96b
GIT binary patch
literal 2517
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
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..2a1774ff98a51d11cef6932d315261eec239ed3c
GIT binary patch
literal 3034
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
VnLvWU!kXp3Z8wUSYZZx^{BQTt{73)*
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..751eb898b136ab9814248a83df833d43f0e778dc
GIT binary patch
literal 1850
zc$}S8X;c$e6pn%lh@iB9sMRsJp=6mXlLcZ7SukiKhNvhO7?KH@Viq$a38;Xm6bmZW
z9npddl&XkYL_uT~mzsK9QE|iCQbh$7*F&Wf3)a))@kig8nfLB}-*@kQ_uYGDRk$*A
zklQdf5{WcOK2@S3=8?o3=+d9~TGCydiOCO_M&c1B9d3m!2uY+h#UX&)0LLRL1lA@k
zJc9(2NKSfG9f?OOrt>u>0~K~)s5XO{<QTz1n;F(5AUF_*#G^(5xxKQQ44_&8c@A4a
zSD3|!9-W$OK_ZfsYE5#2hNmS9Cj-GYK4HLs;4ok_EHGkxn}Gb@E}vLC)HE{i9)c$b
z$o~WtsR#$eCJO?vsZ5H74uSv#Q9%|Pg1|t4K?fN$I`Kdhki}<1d<F;T8DzqnMXTeh
zB(fe~gd`yAaoo(O(X3W0)ykxrEb%mu$KyF{Fc=g9LBW!YIBcUBG5=l*5(Lv&P&1C2
zjDSOi<4lWi0h!!;1cO<j_+Z$G^(2Z&8O;WpX&{wOGZ<*SXbe{&y3eCAb&?sOsSwPx
z$f6<cL+AgItIty%Kq4Cc3=2wJ3cNsK(kwC{MqDltkcl0t7S-})T#gI`IV?JdL5H{?
z$fa|cvJeJO#1l&y3?|rX<5OG_1Txu>h!-N`u|QA?^29udE0J-zA|^-5l`(p`awCSr
zMh()-MgQe;Kg;EdEeMR8ENYW!L2m_w>rJ=`)0@nII06DDC}0h0bSMHG7t|9gVnGuT
zt;}LF0Pi!*M?Vn(D1jhI#FT=vk4_HBSX_|Fk?|NTat~Mgf3ij+f}#DVTzXYR4LX(|
zik}cZv=3q=dd5OD2agesIC@Sl5vgrA9&K)(_T|i>HPu_IDy>c7QO$|Se)jcQGr{`o
z5TDVVqJq=v&VnWSNd>BqOe!ue9pju;qpHs@dbd3PRCIFj0N09QL))#{3vb=Rat|+-
zWl-#MLt}1uPS*X(yYnWcHE8qJ_R^d@t&95S*z9ErFE`)Zu&|ZL!MbsMkp2~(?6RxJ
zqJ`B-_k#n*JH?+2Sxoi0GV*zip(w?D)*lBFCs)kBxy3!t+o|fp6JGN1`OW30==MJ^
zjNh<pTkUOzeS77Jx?u-9>}iyS2OBq~Uwm7iF+TBm_Vm?-^m6HxGXg_~Utvi|Q|jaF
z$}sn?m{|XVPq*V=>GwgNLFW#KvE3T-@zlNn*z$E8wcjW!tHLed=27p`H4#X{c4m25
z<Fh(+>!reFqnK%4o&)q|QfuLPbX(OXIBv$<3-5N;ezSA@agVm^H_i=dEs<vVr0f(H
zEFAq}UWb2t<IdtLDz>5Xx22hVCY{z-4cPA!Q?+P<aMBh{Y}W|Zp_a?w;C_Pm*0b)q
z^(j|=ue^j_er8WYv`fEx6@`DTcv;Z5O|?TL%WqLUR>y@tl2`Awzl5#P6`S@#GppOu
z&lJ+z?N^JRyqSMK_=xvfIkYNrgx4$h?jyNyE#>~PRO6k9s7*>~m(lsimd=UN)S|`t
zr+p@JemN=rqUGevsmPtN)&r{AvtDcc>QjNEvc%n$yUXkR45wpHts6nBpFOtj;g;ge
zjY&a`Yj&J?Q8Mset2wu8%<Gl$Z*w0vO>^HnNY*_@*3vJsUx3zUxVOvM?72lFp`gN`
z4B@K|ie*??9oW6c&NbHQ5fA?yRblB&@BXLiLWa+=O-YJWTJz@FfgH;DIQNTL3z9q%
zu9dYrC1o9AJ!D<e)jpki={07*n8|MTXaq~g4cV9W*f+xx*}~Jo@he)^b}0Ll=V5zl
z0?clCeT!<k{fD}i#5LzQH*|G-cL`jc*V5^#`c=Yvuc8L)R=!x0oHamLGwq4zt?zez
z%L-`yLAx);cQp6)kZ9LNFQ@gsow~lav0-=H8@vwf%A=b3$~#RpyF0`=DD^^c_Bwsx
z88)`#lB@Es4ZcpH69T8K_HC-1H!c8v$eh9(0yl{GxiRUxqkhU<)jb#qboC$CRvp$P
sI*Te-g#OsrG*okrBY8kdl%x!#liu0<J<_IMx$XFy$)!q3Nl0w^-v-y!SpWb4
--- a/mobile/android/base/resources/layout/text_selection_handles.xml.in
+++ b/mobile/android/base/resources/layout/text_selection_handles.xml.in
@@ -7,17 +7,24 @@
 <merge xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:gecko="http://schemas.android.com/apk/res/@ANDROID_PACKAGE_NAME@">
 
    <org.mozilla.gecko.TextSelectionHandle android:id="@+id/start_handle"
                                           android:layout_width="@dimen/text_selection_handle_width"
                                           android:layout_height="@dimen/text_selection_handle_height"
                                           android:src="@drawable/handle_start"
                                           android:visibility="gone"
-                                          gecko:handleType="START"/>
+                                          gecko:handleType="start"/>
+
+   <org.mozilla.gecko.TextSelectionHandle android:id="@+id/middle_handle"
+                                          android:layout_width="@dimen/text_selection_handle_width"
+                                          android:layout_height="@dimen/text_selection_handle_height"
+                                          android:src="@drawable/handle_middle"
+                                          android:visibility="gone"
+                                          gecko:handleType="middle"/>
 
    <org.mozilla.gecko.TextSelectionHandle android:id="@+id/end_handle"
                                           android:layout_width="@dimen/text_selection_handle_width"
                                           android:layout_height="@dimen/text_selection_handle_height"
                                           android:src="@drawable/handle_end"
                                           android:visibility="gone"
-                                          gecko:handleType="END"/>
+                                          gecko:handleType="end"/>
 </merge>
--- a/mobile/android/base/resources/values/attrs.xml
+++ b/mobile/android/base/resources/values/attrs.xml
@@ -45,13 +45,17 @@
         <attr name="cropped" format="boolean"/>
     </declare-styleable>
 
     <declare-styleable name="TabsPanel">
         <attr name="sidebar" format="boolean"/>
     </declare-styleable>
 
     <declare-styleable name="TextSelectionHandle">
-        <attr name="handleType" format="string"/>
+        <attr name="handleType">
+            <flag name="start" value="0x01" />
+            <flag name="middle" value="0x02" />
+            <flag name="end" value="0x03" />
+        </attr>
     </declare-styleable>
 
 </resources>
 
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -1539,22 +1539,27 @@ var NativeWindow = {
     _stripScheme: function(aString) {
       return aString.slice(aString.indexOf(":") + 1);
     }
   }
 };
 
 var SelectionHandler = {
   HANDLE_TYPE_START: "START",
+  HANDLE_TYPE_MIDDLE: "MIDDLE",
   HANDLE_TYPE_END: "END",
 
+  TYPE_NONE: 0,
+  TYPE_CURSOR: 1,
+  TYPE_SELECTION: 2,
+
   // Keeps track of data about the dimensions of the selection. Coordinates
   // stored here are relative to the _view window.
   cache: null,
-  _active: false,
+  _activeType: this.TYPE_NONE,
 
   // The window that holds the selection (can be a sub-frame)
   get _view() {
     if (this._viewRef)
       return this._viewRef.get();
     return null;
   },
 
@@ -1594,72 +1599,91 @@ var SelectionHandler = {
     Services.obs.removeObserver(this, "Window:Resize");
     Services.obs.removeObserver(this, "Tab:Selected");
     Services.obs.removeObserver(this, "after-viewport-change");
     Services.obs.removeObserver(this, "TextSelection:Move");
     Services.obs.removeObserver(this, "TextSelection:Position");
   },
 
   observe: function sh_observe(aSubject, aTopic, aData) {
-    if (!this._active)
-      return;
-
     switch (aTopic) {
       case "Gesture:SingleTap": {
-        let data = JSON.parse(aData);
-        this.endSelection(data.x, data.y);
+        if (this._activeType == this.TYPE_SELECTION) {
+          let data = JSON.parse(aData);
+          this.endSelection(data.x, data.y);
+        }
         break;
       }
       case "Tab:Selected":
       case "Window:Resize": {
-        // Knowing when the page is done drawing is hard, so let's just cancel
-        // the selection when the window changes. We should fix this later.
-        this.endSelection();
+        if (this._activeType == this.TYPE_SELECTION) {
+          // Knowing when the page is done drawing is hard, so let's just cancel
+          // the selection when the window changes. We should fix this later.
+          this.endSelection();
+        } else if (this._activeType == this.TYPE_CURSOR) {
+          this.hideThumb();
+        }
         break;
       }
       case "after-viewport-change": {
-        // Update the cache after the viewport changes (e.g. panning, zooming).
-        this.updateCacheForSelection();
+        if (this._activeType == this.TYPE_SELECTION) {
+          // Update the cache after the viewport changes (e.g. panning, zooming).
+          this.updateCacheForSelection();
+        } else if (this._activeType == this.TYPE_CURSOR) {
+          this.hideThumb();
+        }
         break;
       }
       case "TextSelection:Move": {
         let data = JSON.parse(aData);
-        this.moveSelection(data.handleType == this.HANDLE_TYPE_START, data.x, data.y);
+        if (this._activeType == this.TYPE_SELECTION)
+          this.moveSelection(data.handleType == this.HANDLE_TYPE_START, data.x, data.y);
+        else if (this._activeType == this.TYPE_CURSOR)
+          this._sendMouseEvents(data.x, data.y);
         break;
       }
       case "TextSelection:Position": {
-        let data = JSON.parse(aData);
-
-        // Reverse the handles if necessary.
-        let selectionReversed = this.updateCacheForSelection(data.handleType == this.HANDLE_TYPE_START);
-        if (selectionReversed) {
-          // Re-send mouse events to update the selection corresponding to the new handles.
-          if (this._isRTL) {
-            this._sendMouseEvents(this.cache.end.x, this.cache.end.y, false);
-            this._sendMouseEvents(this.cache.start.x, this.cache.start.y, true);
-          } else {
-            this._sendMouseEvents(this.cache.start.x, this.cache.start.y, false);
-            this._sendMouseEvents(this.cache.end.x, this.cache.end.y, true);
+        if (this._activeType == this.TYPE_SELECTION) {
+          let data = JSON.parse(aData);
+
+          // Reverse the handles if necessary.
+          let selectionReversed = this.updateCacheForSelection(data.handleType == this.HANDLE_TYPE_START);
+          if (selectionReversed) {
+            // Re-send mouse events to update the selection corresponding to the new handles.
+            if (this._isRTL) {
+              this._sendMouseEvents(this.cache.end.x, this.cache.end.y, false);
+              this._sendMouseEvents(this.cache.start.x, this.cache.start.y, true);
+            } else {
+              this._sendMouseEvents(this.cache.start.x, this.cache.start.y, false);
+              this._sendMouseEvents(this.cache.end.x, this.cache.end.y, true);
+            }
           }
+
+          // Position the handles to align with the edges of the selection.
+          this.positionHandles();
+        } else if (this._activeType == this.TYPE_CURSOR) {
+          this.positionHandles();
         }
-
-        // Position the handles to align with the edges of the selection.
-        this.positionHandles();
         break;
       }
     }
   },
 
   handleEvent: function sh_handleEvent(aEvent) {
-    if (!this._active)
-      return;
-
     switch (aEvent.type) {
       case "pagehide":
-        this.endSelection();
+        if (this._activeType == this.TYPE_SELECTION)
+          this.endSelection();
+        else
+          this.hideThumb();
+        break;
+
+      case "keydown":
+        if (this._activeType == this.TYPE_CURSOR)
+          this.hideThumb();
         break;
     }
   },
 
   _ignoreCollapsedSelection: false,
 
   notifySelectionChanged: function sh_notifySelectionChanged(aDoc, aSel, aReason) {
     if (aSel.isCollapsed) {
@@ -1687,18 +1711,22 @@ var SelectionHandler = {
              aElement instanceof Ci.nsIDOMHTMLEmbedElement ||
              aElement instanceof Ci.nsIDOMHTMLImageElement ||
              aElement instanceof Ci.nsIDOMHTMLMediaElement);
   },
 
   // aX/aY are in top-level window browser coordinates
   startSelection: function sh_startSelection(aElement, aX, aY) {
     // Clear out any existing selection
-    if (this._active)
+    if (this._activeType == this.TYPE_SELECTION) {
       this.endSelection();
+    } else if (this._activeType == this.TYPE_CURSOR) {
+      // Hide the cursor handles.
+      this.hideThumb();
+    }
 
     // Get the element's view
     this._view = aElement.ownerDocument.defaultView;
 
     if (aElement instanceof Ci.nsIDOMNSEditableElement)
       this._target = aElement;
     else
       this._target = this._view;
@@ -1737,18 +1765,25 @@ var SelectionHandler = {
 
     // Add a listener to end the selection if it's removed programatically
     selection.QueryInterface(Ci.nsISelectionPrivate).addSelectionListener(this);
 
     // Initialize the cache
     this.cache = { start: {}, end: {}};
     this.updateCacheForSelection();
 
-    this.showHandles();
-    this._active = true;
+    this._activeType = this.TYPE_SELECTION;
+    this.positionHandles();
+
+    sendMessageToJava({
+      gecko: {
+        type: "TextSelection:ShowHandles",
+        handles: [this.HANDLE_TYPE_START, this.HANDLE_TYPE_END]
+      }
+    });
 
     if (aElement instanceof Ci.nsIDOMNSEditableElement)
       aElement.focus();
   },
 
   getSelection: function sh_getSelection() {
     if (this._target instanceof Ci.nsIDOMNSEditableElement)
       return this._target.QueryInterface(Ci.nsIDOMNSEditableElement).editor.selection;
@@ -1764,17 +1799,17 @@ var SelectionHandler = {
                           getInterface(Ci.nsIWebNavigation).
                           QueryInterface(Ci.nsIInterfaceRequestor).
                           getInterface(Ci.nsISelectionDisplay).
                           QueryInterface(Ci.nsISelectionController);
   },
 
   // Used by the contextmenu "matches" functions in ClipboardHelper
   shouldShowContextMenu: function sh_shouldShowContextMenu(aX, aY) {
-    return this._active && this._pointInSelection(aX, aY);
+    return (this._active != this.TYPE_NONE) && this._pointInSelection(aX, aY);
   },
 
   selectAll: function sh_selectAll(aElement, aX, aY) {
     if (!this._active)
       this.startSelection(aElement, aX, aY);
 
     let selectionController = this.getSelectionController();
     selectionController.selectAll();
@@ -1816,21 +1851,27 @@ var SelectionHandler = {
   _sendMouseEvents: function sh_sendMouseEvents(aX, aY, useShift) {
     // Send mouse event 1px too high to prevent selection from entering the line below where it should be
     this._cwu.sendMouseEventToWindow("mousedown", aX, aY - 1, 0, 0, useShift ? Ci.nsIDOMNSEvent.SHIFT_MASK : 0, true);
     this._cwu.sendMouseEventToWindow("mouseup", aX, aY - 1, 0, 0, useShift ? Ci.nsIDOMNSEvent.SHIFT_MASK : 0, true);
   },
 
   // aX/aY are in top-level window browser coordinates
   endSelection: function sh_endSelection(aX, aY) {
-    if (!this._active)
+    if (this._activeType != this.TYPE_SELECTION)
       return "";
-
-    this._active = false;
-    this.hideHandles();
+ 
+    this._activeType = this.TYPE_NONE;
+    sendMessageToJava({
+      gecko: {
+        type: "TextSelection:HideHandles",
+        handles: [this.HANDLE_TYPE_START, this.HANDLE_TYPE_END]
+      }
+    });
+
 
     let selectedText = "";
     let pointInSelection = false;
     if (this._view) {
       let selection = this.getSelection();
       if (selection) {
         // Get the text before we clear the selection!
         selectedText = selection.toString().trim();
@@ -1910,54 +1951,81 @@ var SelectionHandler = {
     }
 
     this.cache.start = start;
     this.cache.end = end;
 
     return selectionReversed;
   },
 
+  showThumb: function sh_showThumb(aElement) {
+    if (!aElement)
+      return;
+
+    // Get the element's view
+    this._view = aElement.ownerDocument.defaultView;
+    this._target = aElement;
+
+    this._view.addEventListener("pagehide", this, false);
+    this._view.addEventListener("keydown", this, false);
+
+    this._activeType = this.TYPE_CURSOR;
+    this.positionHandles();
+
+    sendMessageToJava({
+      gecko: {
+        type: "TextSelection:ShowHandles",
+        handles: [this.HANDLE_TYPE_MIDDLE]
+      }
+    });
+  },
+
+  hideThumb: function sh_hideThumb() {
+    this._activeType = this.TYPE_NONE;
+    this._cleanUp();
+
+    sendMessageToJava({
+      gecko: {
+        type: "TextSelection:HideHandles",
+        handles: [this.HANDLE_TYPE_MIDDLE]
+      }
+    });
+  },
+
   positionHandles: function sh_positionHandles() {
     // Translate coordinates to account for selections in sub-frames. We can't cache
     // this because the top-level page may have scrolled since selection started.
     let offset = this._getViewOffset();
     let scrollX = {}, scrollY = {};
     this._view.top.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils).getScrollXY(false, scrollX, scrollY);
 
+    let positions = null;
+    if (this._activeType == this.TYPE_CURSOR) {
+      let cursor = this._cwu.sendQueryContentEvent(this._cwu.QUERY_CARET_RECT, this._target.selectionEnd, 0, 0, 0);
+      positions = [ { handle: this.HANDLE_TYPE_MIDDLE,
+                     left: cursor.left + offset.x + scrollX.value,
+                     top: cursor.top + cursor.height + offset.y + scrollY.value } ];
+    } else {
+      positions = [ { handle: this.HANDLE_TYPE_START,
+                     left: this.cache.start.x + offset.x + scrollX.value,
+                     top: this.cache.start.y + offset.y + scrollY.value },
+                   { handle: this.HANDLE_TYPE_END,
+                     left: this.cache.end.x + offset.x + scrollX.value,
+                     top: this.cache.end.y + offset.y + scrollY.value } ];
+    }
+
     sendMessageToJava({
       gecko: {
         type: "TextSelection:PositionHandles",
-        startLeft: this.cache.start.x + offset.x + scrollX.value,
-        startTop: this.cache.start.y + offset.y + scrollY.value,
-        endLeft: this.cache.end.x + offset.x + scrollX.value,
-        endTop: this.cache.end.y + offset.y + scrollY.value
-      }
-    });
-  },
-
-  showHandles: function sh_showHandles() {
-    this.positionHandles();
-
-    sendMessageToJava({
-      gecko: {
-        type: "TextSelection:ShowHandles"
-      }
-    });
-  },
-
-  hideHandles: function sh_hideHandles() {
-    sendMessageToJava({
-      gecko: {
-        type: "TextSelection:HideHandles"
+        positions: positions
       }
     });
   }
 };
 
-
 var UserAgent = {
   DESKTOP_UA: null,
 
   init: function ua_init() {
     Services.obs.addObserver(this, "DesktopMode:Change", false);
     Services.obs.addObserver(this, "http-on-modify-request", false);
 
     // See https://developer.mozilla.org/en/Gecko_user_agent_string_reference
@@ -3514,16 +3582,20 @@ var BrowserEventHandler = {
             [data.x, data.y] = this._moveClickPoint(element, data.x, data.y);
             element = ElementTouchHelper.anyElementFromPoint(data.x, data.y);
             isClickable = ElementTouchHelper.isElementClickable(element);
           }
 
           this._sendMouseEvent("mousemove", element, data.x, data.y);
           this._sendMouseEvent("mousedown", element, data.x, data.y);
           this._sendMouseEvent("mouseup",   element, data.x, data.y);
+
+          // See if its a input element
+          if ((element instanceof HTMLInputElement && element.mozIsTextField(false)) || (element instanceof HTMLTextAreaElement))
+             SelectionHandler.showThumb(element);
   
           if (isClickable)
             Haptic.performSimpleAction(Haptic.LongPress);
         } catch(e) {
           Cu.reportError(e);
         }
       }
       this._cancelTapHighlight();