Bug 746142 - Part 2 - Use inputmode attribute to vary the virtual keyboard on Android. f=mounir r=cpeterson
☠☠ backed out by ef98bbc9c8d6 ☠ ☠
authorZoe Bellot <bellot.zoe@gmail.com>
Fri, 17 Aug 2012 12:02:18 +0200
changeset 102636 506268f7735e0e8195716f5d33d1e6da46546b7a
parent 102635 ea163112807db614628a87994b0ab6448fc278ab
child 102637 f16429aa2210a7271315816ec623b6163401471b
push id23301
push userryanvm@gmail.com
push dateSat, 18 Aug 2012 02:17:26 +0000
treeherdermozilla-central@812ea773f166 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscpeterson
bugs746142
milestone17.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 746142 - Part 2 - Use inputmode attribute to vary the virtual keyboard on Android. f=mounir r=cpeterson
content/events/src/nsIMEStateManager.cpp
dom/ipc/PBrowser.ipdl
dom/ipc/TabParent.cpp
dom/ipc/TabParent.h
embedding/android/GeckoAppShell.java
embedding/android/GeckoSurfaceView.java
mobile/android/base/GeckoAppShell.java
mobile/android/base/GeckoInputConnection.java
widget/android/AndroidBridge.cpp
widget/android/AndroidBridge.h
widget/android/nsWindow.cpp
widget/nsIWidget.h
widget/xpwidgets/PuppetWidget.cpp
--- a/content/events/src/nsIMEStateManager.cpp
+++ b/content/events/src/nsIMEStateManager.cpp
@@ -317,16 +317,18 @@ nsIMEStateManager::SetIMEState(const IME
   InputContext context;
   context.mIMEState = aState;
 
   if (aContent && aContent->GetNameSpaceID() == kNameSpaceID_XHTML &&
       (aContent->Tag() == nsGkAtoms::input ||
        aContent->Tag() == nsGkAtoms::textarea)) {
     aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::type,
                       context.mHTMLInputType);
+    aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::inputmode,
+                      context.mHTMLInputInputmode);
     aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::moz_action_hint,
                       context.mActionHint);
 
     // if we don't have an action hint and  return won't submit the form use "next"
     if (context.mActionHint.IsEmpty() && aContent->Tag() == nsGkAtoms::input) {
       bool willSubmit = false;
       nsCOMPtr<nsIFormControl> control(do_QueryInterface(aContent));
       mozilla::dom::Element* formElement = control->GetFormElement();
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -158,16 +158,17 @@ parent:
      */
     sync EndIMEComposition(bool cancel) returns (nsString composition);
 
     sync GetInputContext() returns (PRInt32 IMEEnabled, PRInt32 IMEOpen);
 
     SetInputContext(PRInt32 IMEEnabled,
                     PRInt32 IMEOpen,
                     nsString type,
+                    nsString inputmode,
                     nsString actionHint,
                     PRInt32 cause,
                     PRInt32 focusChange);
 
     /**
      * Gets the DPI of the screen corresponding to this browser.
      */
     sync GetDPI() returns (float value);
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -677,16 +677,17 @@ TabParent::RecvGetInputContext(PRInt32* 
   *aIMEOpen = static_cast<PRInt32>(context.mIMEState.mOpen);
   return true;
 }
 
 bool
 TabParent::RecvSetInputContext(const PRInt32& aIMEEnabled,
                                const PRInt32& aIMEOpen,
                                const nsString& aType,
+                               const nsString& aInputmode,
                                const nsString& aActionHint,
                                const PRInt32& aCause,
                                const PRInt32& aFocusChange)
 {
   // mIMETabParent (which is actually static) tracks which if any TabParent has IMEFocus
   // When the input mode is set to anything but IMEState::DISABLED,
   // mIMETabParent should be set to this
   mIMETabParent =
@@ -694,16 +695,17 @@ TabParent::RecvSetInputContext(const PRI
   nsCOMPtr<nsIWidget> widget = GetWidget();
   if (!widget || !AllowContentIME())
     return true;
 
   InputContext context;
   context.mIMEState.mEnabled = static_cast<IMEState::Enabled>(aIMEEnabled);
   context.mIMEState.mOpen = static_cast<IMEState::Open>(aIMEOpen);
   context.mHTMLInputType.Assign(aType);
+  context.mHTMLInputInputmode.Assign(aInputmode);
   context.mActionHint.Assign(aActionHint);
   InputContextAction action(
     static_cast<InputContextAction::Cause>(aCause),
     static_cast<InputContextAction::FocusChange>(aFocusChange));
   widget->SetInputContext(context, action);
 
   nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
   if (!observerService)
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -93,16 +93,17 @@ public:
     virtual bool RecvNotifyIMETextHint(const nsString& aText);
     virtual bool RecvEndIMEComposition(const bool& aCancel,
                                        nsString* aComposition);
     virtual bool RecvGetInputContext(PRInt32* aIMEEnabled,
                                      PRInt32* aIMEOpen);
     virtual bool RecvSetInputContext(const PRInt32& aIMEEnabled,
                                      const PRInt32& aIMEOpen,
                                      const nsString& aType,
+                                     const nsString& aInputmode,
                                      const nsString& aActionHint,
                                      const PRInt32& aCause,
                                      const PRInt32& aFocusChange);
     virtual bool RecvSetCursor(const PRUint32& aValue);
     virtual bool RecvSetBackgroundColor(const nscolor& aValue);
     virtual bool RecvGetDPI(float* aValue);
     virtual bool RecvGetWidgetNativeData(WindowsHandle* aValue);
     virtual bool RecvNotifyDOMTouchListenerAdded();
--- a/embedding/android/GeckoAppShell.java
+++ b/embedding/android/GeckoAppShell.java
@@ -534,26 +534,27 @@ public class GeckoAppShell
             break;
 
         case NOTIFY_IME_FOCUSCHANGE:
             IMEStateUpdater.resetIME();
             break;
         }
     }
 
-    public static void notifyIMEEnabled(int state, String typeHint,
+    public static void notifyIMEEnabled(int state, String typeHint, String modeHint,
                                         String actionHint, boolean landscapeFS)
     {
         if (GeckoApp.surfaceView == null)
             return;
 
         /* When IME is 'disabled', IME processing is disabled.
            In addition, the IME UI is hidden */
         GeckoApp.surfaceView.mIMEState = state;
         GeckoApp.surfaceView.mIMETypeHint = typeHint;
+        GeckoApp.surfaceView.mIMEModeHint = modeHint;
         GeckoApp.surfaceView.mIMEActionHint = actionHint;
         GeckoApp.surfaceView.mIMELandscapeFS = landscapeFS;
         IMEStateUpdater.enableIME();
     }
 
     public static void notifyIMEChange(String text, int start, int end, int newEnd) {
         if (GeckoApp.surfaceView == null ||
             GeckoApp.surfaceView.inputConnection == null)
--- a/embedding/android/GeckoSurfaceView.java
+++ b/embedding/android/GeckoSurfaceView.java
@@ -58,16 +58,17 @@ class GeckoSurfaceView
         mBufferHeight = 0;
 
         mSurfaceLock = new ReentrantLock();
 
         mEditableFactory = Editable.Factory.getInstance();
         initEditable("");
         mIMEState = IME_STATE_DISABLED;
         mIMETypeHint = "";
+        mIMEModeHint = "";
         mIMEActionHint = "";
     }
 
     protected void finalize() throws Throwable {
         super.finalize();
     }
 
     void drawSplashScreen() {
@@ -497,16 +498,30 @@ class GeckoSurfaceView
             outAttrs.inputType = InputType.TYPE_CLASS_DATETIME |
                                  InputType.TYPE_DATETIME_VARIATION_NORMAL;
         else if (mIMETypeHint.equalsIgnoreCase("date"))
             outAttrs.inputType = InputType.TYPE_CLASS_DATETIME |
                                  InputType.TYPE_DATETIME_VARIATION_DATE;
         else if (mIMETypeHint.equalsIgnoreCase("time"))
             outAttrs.inputType = InputType.TYPE_CLASS_DATETIME |
                                  InputType.TYPE_DATETIME_VARIATION_TIME;
+        else if (mIMEModeHint.equalsIgnoreCase("numeric"))
+            outAttrs.inputType = InputType.TYPE_CLASS_NUMBER |
+                                 InputType.TYPE_NUMBER_FLAG_SIGNED |
+                                 InputType.TYPE_NUMBER_FLAG_DECIMAL;
+        else if (mIMEModeHint.equalsIgnoreCase("digit"))
+            outAttrs.inputType = InputType.TYPE_CLASS_NUMBER;
+        else if (mIMEModeHint.equalsIgnoreCase("uppercase"))
+            outAttrs.inputType |= InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS;
+        else if (mIMEModeHint.equalsIgnoreCase("lowercase"))
+            outAttrs.inputType = InputType.TYPE_CLASS_TEXT; 
+        else if (mIMEModeHint.equalsIgnoreCase("titlecase"))
+            outAttrs.inputType |= InputType.TYPE_TEXT_FLAG_CAP_WORDS;
+        else if (mIMEModeHint.equalsIgnoreCase("autocapitalized"))
+            outAttrs.inputType |= InputType.TYPE_TEXT_FLAG_CAP_SENTENCES;
 
         if (mIMEActionHint.equalsIgnoreCase("go"))
             outAttrs.imeOptions = EditorInfo.IME_ACTION_GO;
         else if (mIMEActionHint.equalsIgnoreCase("done"))
             outAttrs.imeOptions = EditorInfo.IME_ACTION_DONE;
         else if (mIMEActionHint.equalsIgnoreCase("next"))
             outAttrs.imeOptions = EditorInfo.IME_ACTION_NEXT;
         else if (mIMEActionHint.equalsIgnoreCase("search"))
@@ -743,16 +758,17 @@ class GeckoSurfaceView
     public static final int IME_STATE_PLUGIN = 3;
 
     GeckoInputConnection inputConnection;
     KeyListener mKeyListener;
     Editable mEditable;
     Editable.Factory mEditableFactory;
     int mIMEState;
     String mIMETypeHint;
+    String mIMEModeHint;
     String mIMEActionHint;
     boolean mIMELandscapeFS;
 
     // Software rendering
     Bitmap mSoftwareBitmap;
     ByteBuffer mSoftwareBuffer;
     Bitmap mSoftwareBufferCopy;
 
--- a/mobile/android/base/GeckoAppShell.java
+++ b/mobile/android/base/GeckoAppShell.java
@@ -571,22 +571,22 @@ public class GeckoAppShell
     /*
      *  The Gecko-side API: API methods that Gecko calls
      */
     public static void notifyIME(int type, int state) {
         if (mInputConnection != null)
             mInputConnection.notifyIME(type, state);
     }
 
-    public static void notifyIMEEnabled(int state, String typeHint,
+    public static void notifyIMEEnabled(int state, String typeHint, String modeHint,
                                         String actionHint, boolean landscapeFS) {
         // notifyIMEEnabled() still needs the landscapeFS parameter because it is called from JNI
         // code that assumes it has the same signature as XUL Fennec's (which does use landscapeFS).
         if (mInputConnection != null)
-            mInputConnection.notifyIMEEnabled(state, typeHint, actionHint);
+            mInputConnection.notifyIMEEnabled(state, typeHint, modeHint, actionHint);
     }
 
     public static void notifyIMEChange(String text, int start, int end, int newEnd) {
         if (mInputConnection != null)
             mInputConnection.notifyIMEChange(text, start, end, newEnd);
     }
 
     // Called by AndroidBridge using JNI
--- a/mobile/android/base/GeckoInputConnection.java
+++ b/mobile/android/base/GeckoInputConnection.java
@@ -82,16 +82,17 @@ class GeckoInputConnection
     private static final char UNICODE_SQUARE_ROOT               = '\u221a';
     private static final char UNICODE_TRADEMARK_SIGN            = '\u2122';
     private static final char UNICODE_WHITE_BULLET              = '\u25e6';
     private static final char UNICODE_YEN_SIGN                  = '\u00a5';
 
     private static final Timer mIMETimer = new Timer("GeckoInputConnection Timer");
     private static int mIMEState;
     private static String mIMETypeHint = "";
+    private static String mIMEModeHint;
     private static String mIMEActionHint = "";
 
     private String mCurrentInputMethod;
 
     // Is a composition active?
     private int mCompositionStart = NO_COMPOSITION_STRING;
     private boolean mCommittingText;
     private KeyCharacterMap mKeyCharacterMap;
@@ -801,16 +802,30 @@ class GeckoInputConnection
             outAttrs.inputType = InputType.TYPE_CLASS_DATETIME
                                  | InputType.TYPE_DATETIME_VARIATION_NORMAL;
         else if (mIMETypeHint.equalsIgnoreCase("date"))
             outAttrs.inputType = InputType.TYPE_CLASS_DATETIME
                                  | InputType.TYPE_DATETIME_VARIATION_DATE;
         else if (mIMETypeHint.equalsIgnoreCase("time"))
             outAttrs.inputType = InputType.TYPE_CLASS_DATETIME
                                  | InputType.TYPE_DATETIME_VARIATION_TIME;
+        else if (mIMEModeHint.equalsIgnoreCase("numeric"))
+            outAttrs.inputType = InputType.TYPE_CLASS_NUMBER |
+                                 InputType.TYPE_NUMBER_FLAG_SIGNED |
+                                 InputType.TYPE_NUMBER_FLAG_DECIMAL;
+        else if (mIMEModeHint.equalsIgnoreCase("digit"))
+            outAttrs.inputType = InputType.TYPE_CLASS_NUMBER;
+        else if (mIMEModeHint.equalsIgnoreCase("uppercase"))
+            outAttrs.inputType |= InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS;
+        else if (mIMEModeHint.equalsIgnoreCase("lowercase"))
+            outAttrs.inputType = InputType.TYPE_CLASS_TEXT; 
+        else if (mIMEModeHint.equalsIgnoreCase("titlecase"))
+            outAttrs.inputType |= InputType.TYPE_TEXT_FLAG_CAP_WORDS;
+        else if (mIMEModeHint.equalsIgnoreCase("autocapitalized"))
+            outAttrs.inputType |= InputType.TYPE_TEXT_FLAG_CAP_SENTENCES;
 
         if (mIMEActionHint.equalsIgnoreCase("go"))
             outAttrs.imeOptions = EditorInfo.IME_ACTION_GO;
         else if (mIMEActionHint.equalsIgnoreCase("done"))
             outAttrs.imeOptions = EditorInfo.IME_ACTION_DONE;
         else if (mIMEActionHint.equalsIgnoreCase("next"))
             outAttrs.imeOptions = EditorInfo.IME_ACTION_NEXT;
         else if (mIMEActionHint.equalsIgnoreCase("search"))
@@ -1039,27 +1054,28 @@ class GeckoInputConnection
                         if (DEBUG)
                             throw new IllegalArgumentException("Unexpected NOTIFY_IME=" + type);
                         break;
                 }
             }
         });
     }
 
-    public void notifyIMEEnabled(final int state, final String typeHint, final String actionHint) {
+    public void notifyIMEEnabled(final int state, final String typeHint, final String modeHint, final String actionHint) {
         postToUiThread(new Runnable() {
             public void run() {
                 View v = getView();
                 if (v == null)
                     return;
 
                 /* When IME is 'disabled', IME processing is disabled.
                    In addition, the IME UI is hidden */
                 mIMEState = state;
                 mIMETypeHint = (typeHint == null) ? "" : typeHint;
+                mIMEModeHint = (modeHint == null) ? "" : modeHint;
                 mIMEActionHint = (actionHint == null) ? "" : actionHint;
                 IMEStateUpdater.enableIME();
             }
         });
     }
 
     public final void notifyIMEChange(final String text, final int start, final int end,
                                       final int newEnd) {
--- a/widget/android/AndroidBridge.cpp
+++ b/widget/android/AndroidBridge.cpp
@@ -94,17 +94,17 @@ AndroidBridge::Init(JNIEnv *jEnv,
     mOpenedGraphicsLibraries = false;
     mHasNativeBitmapAccess = false;
     mHasNativeWindowAccess = false;
     mHasNativeWindowFallback = false;
 
     mGeckoAppShellClass = (jclass) jEnv->NewGlobalRef(jGeckoAppShellClass);
 
     jNotifyIME = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "notifyIME", "(II)V");
-    jNotifyIMEEnabled = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "notifyIMEEnabled", "(ILjava/lang/String;Ljava/lang/String;Z)V");
+    jNotifyIMEEnabled = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "notifyIMEEnabled", "(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)V");
     jNotifyIMEChange = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "notifyIMEChange", "(Ljava/lang/String;III)V");
     jNotifyScreenShot = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "notifyScreenShot", "(Ljava/nio/ByteBuffer;IIIIIIII)V");
     jAcknowledgeEventSync = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "acknowledgeEventSync", "()V");
 
     jEnableLocation = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "enableLocation", "(Z)V");
     jEnableLocationHighAccuracy = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "enableLocationHighAccuracy", "(Z)V");
     jEnableSensor = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "enableSensor", "(I)V");
     jDisableSensor = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "disableSensor", "(I)V");
@@ -258,49 +258,51 @@ jstring NewJavaString(AutoLocalJNIFrame*
     jstring ret = frame->GetEnv()->NewString( string, len);
     if (frame->CheckForException())
         return NULL;
     return ret;
 }
 
 void
 AndroidBridge::NotifyIMEEnabled(int aState, const nsAString& aTypeHint,
-                                const nsAString& aActionHint)
+                                const nsAString& aModeHint, const nsAString& aActionHint)
 {
     ALOG_BRIDGE("AndroidBridge::NotifyIMEEnabled");
     if (!sBridge)
         return;
 
     JNIEnv *env = GetJNIEnv();
     if (!env)
         return;
 
     AutoLocalJNIFrame jniFrame(env);
     nsPromiseFlatString typeHint(aTypeHint);
+    nsPromiseFlatString modeHint(aModeHint);
     nsPromiseFlatString actionHint(aActionHint);
 
-    jvalue args[4];
+    jvalue args[5];
     args[0].i = aState;
     args[1].l = NewJavaString(&jniFrame, typeHint.get(), typeHint.Length());
-    args[2].l = NewJavaString(&jniFrame, actionHint.get(), actionHint.Length());
-    args[3].z = false;
+    args[2].l = env->NewString(modeHint.get(), modeHint.Length());
+    args[3].l = env->NewString(actionHint.get(), actionHint.Length());
+    args[4].z = false;
 
     PRInt32 landscapeFS;
     if (NS_SUCCEEDED(Preferences::GetInt(IME_FULLSCREEN_PREF, &landscapeFS))) {
         if (landscapeFS == 1) {
-            args[3].z = true;
+            args[4].z = true;
         } else if (landscapeFS == -1){
             if (NS_SUCCEEDED(
                     Preferences::GetInt(IME_FULLSCREEN_THRESHOLD_PREF,
                                         &landscapeFS))) {
                 // the threshold is hundreths of inches, so convert the
                 // threshold to pixels and multiply the height by 100
                 if (nsWindow::GetAndroidScreenBounds().height  * 100 <
                     landscapeFS * Bridge()->GetDPI()) {
-                    args[3].z = true;
+                    args[4].z = true;
                 }
             }
 
         }
     }
 
     env->CallStaticVoidMethodA(sBridge->mGeckoAppShellClass,
                                sBridge->jNotifyIMEEnabled, args);
--- a/widget/android/AndroidBridge.h
+++ b/widget/android/AndroidBridge.h
@@ -137,17 +137,17 @@ public:
     // us to use.  toolkit/xre/nsAndroidStartup.cpp calls
     // SetMainThread.
     bool SetMainThread(void *thr);
 
     /* These are all implemented in Java */
     static void NotifyIME(int aType, int aState);
 
     static void NotifyIMEEnabled(int aState, const nsAString& aTypeHint,
-                                 const nsAString& aActionHint);
+                                 const nsAString& aModeHint, const nsAString& aActionHint);
 
     static void NotifyIMEChange(const PRUnichar *aText, PRUint32 aTextLen, int aStart, int aEnd, int aNewEnd);
 
     nsresult TakeScreenshot(nsIDOMWindow *window, PRInt32 srcX, PRInt32 srcY, PRInt32 srcW, PRInt32 srcH, PRInt32 dstY, PRInt32 dstX, PRInt32 dstW, PRInt32 dstH, PRInt32 bufW, PRInt32 bufH, PRInt32 tabId, PRInt32 token, jobject buffer);
 
     static void NotifyPaintedRect(float top, float left, float bottom, float right);
 
     void AcknowledgeEventSync();
--- a/widget/android/nsWindow.cpp
+++ b/widget/android/nsWindow.cpp
@@ -2110,16 +2110,17 @@ nsWindow::SetInputContext(const InputCon
     // This avoids showing it whenever a plugin is focused. Bug 747492
     if (aContext.mIMEState.mEnabled == IMEState::PLUGIN &&
         aContext.mIMEState.mOpen != IMEState::OPEN) {
         enabled = int(IMEState::DISABLED);
     }
 
     AndroidBridge::NotifyIMEEnabled(enabled,
                                     aContext.mHTMLInputType,
+                                    aContext.mHTMLInputInputmode,
                                     aContext.mActionHint);
 }
 
 NS_IMETHODIMP_(InputContext)
 nsWindow::GetInputContext()
 {
     mInputContext.mIMEState.mOpen = IMEState::OPEN_STATE_NOT_SUPPORTED;
     return mInputContext;
--- a/widget/nsIWidget.h
+++ b/widget/nsIWidget.h
@@ -276,16 +276,19 @@ struct IMEState {
 };
 
 struct InputContext {
   IMEState mIMEState;
 
   /* The type of the input if the input is a html input field */
   nsString mHTMLInputType;
 
+  /* The type of the inputmode */
+  nsString mHTMLInputInputmode;
+
   /* A hint for the action that is performed when the input is submitted */
   nsString mActionHint;
 };
 
 struct InputContextAction {
   /**
    * mCause indicates what action causes calling nsIWidget::SetInputContext().
    * It must be one of following values.
--- a/widget/xpwidgets/PuppetWidget.cpp
+++ b/widget/xpwidgets/PuppetWidget.cpp
@@ -358,16 +358,17 @@ PuppetWidget::SetInputContext(const Inpu
 {
   if (!mTabChild) {
     return;
   }
   mTabChild->SendSetInputContext(
     static_cast<PRInt32>(aContext.mIMEState.mEnabled),
     static_cast<PRInt32>(aContext.mIMEState.mOpen),
     aContext.mHTMLInputType,
+    aContext.mHTMLInputInputmode,
     aContext.mActionHint,
     static_cast<PRInt32>(aAction.mCause),
     static_cast<PRInt32>(aAction.mFocusChange));
 }
 
 NS_IMETHODIMP_(InputContext)
 PuppetWidget::GetInputContext()
 {