Merge last PGO-green changeset from m-i to m-c.
authorMs2ger <ms2ger@gmail.com>
Mon, 27 Aug 2012 10:52:35 +0200
changeset 105560 5c49d6790357cfb8d593e585b43b3cad7c2d28f2
parent 105548 8af6a22827ecaffe6328a02633411da0421ccb90 (current diff)
parent 105559 6bf3ae02890edbd368c03e13f9cb16a2791fad55 (diff)
child 105574 49aded7afc77808e3e6c6917e9fe0cc48d3e376f
push id55
push usershu@rfrn.org
push dateThu, 30 Aug 2012 01:33:09 +0000
milestone17.0a1
Merge last PGO-green changeset from m-i to m-c.
--- a/accessible/src/jsat/Utils.jsm
+++ b/accessible/src/jsat/Utils.jsm
@@ -7,27 +7,33 @@
 const Cu = Components.utils;
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 
 Cu.import('resource://gre/modules/Services.jsm');
 
 var EXPORTED_SYMBOLS = ['Utils', 'Logger'];
 
-var gAccRetrieval = Cc['@mozilla.org/accessibleRetrieval;1'].
-  getService(Ci.nsIAccessibleRetrieval);
-
 var Utils = {
   _buildAppMap: {
     '{3c2e2abc-06d4-11e1-ac3b-374f68613e61}': 'b2g',
     '{ec8030f7-c20a-464f-9b0e-13a3a9e97384}': 'browser',
     '{aa3c5121-dab2-40e2-81ca-7ea25febc110}': 'mobile/android',
     '{a23983c0-fd0e-11dc-95ff-0800200c9a66}': 'mobile/xul'
   },
 
+  get AccRetrieval() {
+    if (!this._AccRetrieval) {
+      this._AccRetrieval = Cc['@mozilla.org/accessibleRetrieval;1'].
+        getService(Ci.nsIAccessibleRetrieval);
+    }
+
+    return this._AccRetrieval;
+  },
+
   get MozBuildApp() {
     if (!this._buildApp)
       this._buildApp = this._buildAppMap[Services.appinfo.ID];
     return this._buildApp;
   },
 
   get OS() {
     if (!this._OS)
@@ -67,17 +73,17 @@ var Utils = {
 
   getCurrentContentDoc: function getCurrentContentDoc(aWindow) {
     if (this.MozBuildApp == "b2g")
       return this.getBrowserApp(aWindow).contentBrowser.contentDocument;
     return this.getBrowserApp(aWindow).selectedBrowser.contentDocument;
   },
 
   getAllDocuments: function getAllDocuments(aWindow) {
-    let doc = gAccRetrieval.
+    let doc = this.AccRetrieval.
       getAccessibleFor(this.getCurrentContentDoc(aWindow)).
       QueryInterface(Ci.nsIAccessibleDocument);
     let docs = [];
     function getAllDocuments(aDocument) {
       docs.push(aDocument.DOMDocument);
       for (let i = 0; i < aDocument.childDocumentCount; i++)
         getAllDocuments(aDocument.getChildDocumentAt(i));
     }
@@ -101,17 +107,17 @@ var Utils = {
     let state = {};
     let extState = {};
     aAccessible.getState(state, extState);
     return [state.value, extState.value];
   },
 
   getVirtualCursor: function getVirtualCursor(aDocument) {
     let doc = (aDocument instanceof Ci.nsIAccessible) ? aDocument :
-      gAccRetrieval.getAccessibleFor(aDocument);
+      this.AccRetrieval.getAccessibleFor(aDocument);
 
     while (doc) {
       try {
         return doc.QueryInterface(Ci.nsIAccessibleCursorable).virtualCursor;
       } catch (x) {
         doc = doc.parentDocument;
       }
     }
@@ -165,17 +171,17 @@ var Utils = {
   changePage: function changePage(aWindow, aPage) {
     for each (let doc in this.getAllDocuments(aWindow)) {
       // Get current main section or active target.
       let main = doc.querySelector('[role=main]') ||
         doc.querySelector(':target');
       if (!main)
         continue;
 
-      let mainAcc = gAccRetrieval.getAccessibleFor(main);
+      let mainAcc = this.AccRetrieval.getAccessibleFor(main);
       if (!mainAcc)
         continue;
 
       let controllers = mainAcc.
         getRelationByType(Ci.nsIAccessibleRelation.RELATION_CONTROLLED_BY);
 
       for (var i=0; controllers.targetsCount > i; i++) {
         let controller = controllers.getTarget(i);
@@ -232,41 +238,41 @@ var Logger = {
   error: function error() {
     this.log.apply(
       this, [this.ERROR].concat(Array.prototype.slice.call(arguments)));
   },
 
   accessibleToString: function accessibleToString(aAccessible) {
     let str = '[ defunct ]';
     try {
-      str = '[ ' + gAccRetrieval.getStringRole(aAccessible.role) +
+      str = '[ ' + Utils.AccRetrieval.getStringRole(aAccessible.role) +
         ' | ' + aAccessible.name + ' ]';
     } catch (x) {
     }
 
     return str;
   },
 
   eventToString: function eventToString(aEvent) {
-    let str = gAccRetrieval.getStringEventType(aEvent.eventType);
+    let str = Utils.AccRetrieval.getStringEventType(aEvent.eventType);
     if (aEvent.eventType == Ci.nsIAccessibleEvent.EVENT_STATE_CHANGE) {
       let event = aEvent.QueryInterface(Ci.nsIAccessibleStateChangeEvent);
       let stateStrings = (event.isExtraState()) ?
-        gAccRetrieval.getStringStates(0, event.state) :
-        gAccRetrieval.getStringStates(event.state, 0);
+        Utils.AccRetrieval.getStringStates(0, event.state) :
+        Utils.AccRetrieval.getStringStates(event.state, 0);
       str += ' (' + stateStrings.item(0) + ')';
     }
 
     return str;
   },
 
   statesToString: function statesToString(aAccessible) {
     let [state, extState] = Utils.getStates(aAccessible);
     let stringArray = [];
-    let stateStrings = gAccRetrieval.getStringStates(state, extState);
+    let stateStrings = Utils.AccRetrieval.getStringStates(state, extState);
     for (var i=0; i < stateStrings.length; i++)
       stringArray.push(stateStrings.item(i));
     return stringArray.join(' ');
   },
 
   dumpTree: function dumpTree(aLogLevel, aRootAccessible) {
     if (aLogLevel < this.logLevel)
       return;
--- a/accessible/src/jsat/UtteranceGenerator.jsm
+++ b/accessible/src/jsat/UtteranceGenerator.jsm
@@ -12,21 +12,21 @@ const Cr = Components.results;
 const INCLUDE_DESC = 0x01;
 const INCLUDE_NAME = 0x02;
 const INCLUDE_CUSTOM = 0x04;
 
 var gStringBundle = Cc['@mozilla.org/intl/stringbundle;1'].
   getService(Ci.nsIStringBundleService).
   createBundle('chrome://global/locale/AccessFu.properties');
 
-var gAccRetrieval = Cc['@mozilla.org/accessibleRetrieval;1'].
-  getService(Ci.nsIAccessibleRetrieval);
 
 var EXPORTED_SYMBOLS = ['UtteranceGenerator'];
 
+Cu.import('resource://gre/modules/accessibility/Utils.jsm');
+
 /**
  * Generates speech utterances from objects, actions and state changes.
  * An utterance is an array of strings.
  *
  * It should not be assumed that flattening an utterance array would create a
  * gramatically correct sentence. For example, {@link genForObject} might
  * return: ['graphic', 'Welcome to my home page'].
  * Each string element in an utterance should be gramatically correct in itself.
@@ -61,17 +61,17 @@ var UtteranceGenerator = {
    * @param {nsIAccessible} aAccessible accessible object to generate utterance
    *    for.
    * @return {Array} Two string array. The first string describes the object
    *    and its states. The second string is the object's name. Whether the
    *    object's description or it's role is included is determined by
    *    {@link verbosityRoleMap}.
    */
   genForObject: function genForObject(aAccessible) {
-    let roleString = gAccRetrieval.getStringRole(aAccessible.role);
+    let roleString = Utils.AccRetrieval.getStringRole(aAccessible.role);
 
     let func = this.objectUtteranceFunctions[roleString] ||
       this.objectUtteranceFunctions.defaultFunc;
 
     let flags = this.verbosityRoleMap[roleString] || 0;
 
     if (aAccessible.childCount == 0)
       flags |= INCLUDE_NAME;
--- a/accessible/src/jsat/VirtualCursorController.jsm
+++ b/accessible/src/jsat/VirtualCursorController.jsm
@@ -9,19 +9,16 @@ const Ci = Components.interfaces;
 const Cu = Components.utils;
 const Cr = Components.results;
 
 var EXPORTED_SYMBOLS = ['VirtualCursorController'];
 
 Cu.import('resource://gre/modules/accessibility/Utils.jsm');
 Cu.import('resource://gre/modules/XPCOMUtils.jsm');
 
-var gAccRetrieval = Cc['@mozilla.org/accessibleRetrieval;1'].
-  getService(Ci.nsIAccessibleRetrieval);
-
 function BaseTraversalRule(aRoles, aMatchFunc) {
   this._matchRoles = aRoles;
   this._matchFunc = aMatchFunc;
 }
 
 BaseTraversalRule.prototype = {
     getMatchRoles: function BaseTraversalRule_getmatchRoles(aRules) {
       aRules.value = this._matchRoles;
@@ -344,48 +341,48 @@ var VirtualCursorController = {
     if (aLast) {
       virtualCursor.moveLast(TraversalRules.Simple);
     } else {
       try {
         virtualCursor.moveNext(aRule || TraversalRules.Simple);
       } catch (x) {
         this.moveCursorToObject(
           virtualCursor,
-          gAccRetrieval.getAccessibleFor(aDocument.activeElement), aRule);
+          Utils.AccRetrieval.getAccessibleFor(aDocument.activeElement), aRule);
       }
     }
   },
 
   moveBackward: function moveBackward(aDocument, aFirst, aRule) {
     let virtualCursor = Utils.getVirtualCursor(aDocument);
     if (aFirst) {
       virtualCursor.moveFirst(TraversalRules.Simple);
     } else {
       try {
         virtualCursor.movePrevious(aRule || TraversalRules.Simple);
       } catch (x) {
         this.moveCursorToObject(
           virtualCursor,
-          gAccRetrieval.getAccessibleFor(aDocument.activeElement), aRule);
+          Utils.AccRetrieval.getAccessibleFor(aDocument.activeElement), aRule);
       }
     }
   },
 
   activateCurrent: function activateCurrent(document) {
     let virtualCursor = Utils.getVirtualCursor(document);
     let acc = virtualCursor.position;
 
     if (acc.actionCount > 0) {
       acc.doAction(0);
     } else {
       // XXX Some mobile widget sets do not expose actions properly
       // (via ARIA roles, etc.), so we need to generate a click.
       // Could possibly be made simpler in the future. Maybe core
       // engine could expose nsCoreUtiles::DispatchMouseEvent()?
-      let docAcc = gAccRetrieval.getAccessibleFor(this.chromeWin.document);
+      let docAcc = Utils.AccRetrieval.getAccessibleFor(this.chromeWin.document);
       let docX = {}, docY = {}, docW = {}, docH = {};
       docAcc.getBounds(docX, docY, docW, docH);
 
       let objX = {}, objY = {}, objW = {}, objH = {};
       acc.getBounds(objX, objY, objW, objH);
 
       let x = Math.round((objX.value - docX.value) + objW.value / 2);
       let y = Math.round((objY.value - docY.value) + objH.value / 2);
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -520,8 +520,11 @@ pref("hal.processPriorityManager.gonk.ba
 // (hiding latency).
 pref("dom.ipc.processPrelauch.enabled", true);
 // Wait this long before pre-launching a new subprocess.
 pref("dom.ipc.processPrelauch.delayMs", 1000);
 #endif
 
 // Ignore the "dialog=1" feature in window.open.
 pref("dom.disable_window_open_dialog_feature", true);
+
+// Screen reader support
+pref("accessibility.accessfu.activate", 2);
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -13,16 +13,17 @@ Cu.import('resource://gre/modules/XPCOMU
 Cu.import('resource://gre/modules/Services.jsm');
 Cu.import('resource://gre/modules/ContactService.jsm');
 Cu.import('resource://gre/modules/SettingsChangeNotifier.jsm');
 Cu.import('resource://gre/modules/Webapps.jsm');
 Cu.import('resource://gre/modules/AlarmService.jsm');
 Cu.import('resource://gre/modules/ActivitiesService.jsm');
 Cu.import('resource://gre/modules/PermissionPromptHelper.jsm');
 Cu.import('resource://gre/modules/ObjectWrapper.jsm');
+Cu.import("resource://gre/modules/accessibility/AccessFu.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(Services, 'env',
                                    '@mozilla.org/process/environment;1',
                                    'nsIEnvironment');
 
 XPCOMUtils.defineLazyServiceGetter(Services, 'ss',
                                    '@mozilla.org/content/style-sheet-service;1',
                                    'nsIStyleSheetService');
@@ -144,16 +145,17 @@ var shell = {
     try {
       Services.audioManager.masterVolume = 0.5;
     } catch(e) {
       dump('Error setting master volume: ' + e + '\n');
     }
 
     CustomEventManager.init();
     WebappsHelper.init();
+    AccessFu.attach(window);
 
     // XXX could factor out into a settings->pref map.  Not worth it yet.
     SettingsListener.observe("debug.fps.enabled", false, function(value) {
       Services.prefs.setBoolPref("layers.acceleration.draw-fps", value);
     });
     SettingsListener.observe("debug.paint-flashing.enabled", false, function(value) {
       Services.prefs.setBoolPref("nglayout.debug.paint_flashing", value);
     });
--- a/browser/themes/gnomestripe/browser.css
+++ b/browser/themes/gnomestripe/browser.css
@@ -2808,37 +2808,36 @@ stack[anonid=browserStack][responsivemod
   -moz-border-end: 1px solid gray;
 }
 
 .chatbar-button[open="true"],
 .chatbar-button:active:hover {
   box-shadow: inset 0 2px 5px rgba(0,0,0,0.6), 0 1px rgba(255,255,255,0.2);
 }
 
-/* toolbarbutton-icon */
 .chatbar-button > .toolbarbutton-text,
 .chatbar-button > .toolbarbutton-menu-dropmarker {
   display: none;
 }
 
 .chatbar-innerbox {
   background: transparent;
-  margin: -200px -1px 0 -1px;
+  margin: -285px -1px 0 -1px;
   overflow: hidden;
 }
 
-chatbar > chatbox {
-  height: 200px;
-  width: 200px;
+chatbar {
+  -moz-margin-end: 20px;
+}
+
+chatbox {
+  height: 285px;
+  width: 260px;
+  -moz-margin-start: 4px;
   background-color: white;
   border: 1px solid gray;
   border-bottom: none;
 }
 
-chatbar > chatbox[minimized="true"] {
-  width: 100px;
+chatbox[minimized="true"] {
+  width: 160px;
   height: 20px;
-  border-bottom: none;
-}
-
-chatbar > chatbox + chatbox {
-  -moz-margin-start: -1px;
-}
+}
--- a/browser/themes/pinstripe/browser.css
+++ b/browser/themes/pinstripe/browser.css
@@ -3497,38 +3497,36 @@ stack[anonid=browserStack][responsivemod
   -moz-border-end: 1px solid #404040;
 }
 
 .chatbar-button[open="true"],
 .chatbar-button:active:hover {
   box-shadow: inset 0 2px 5px rgba(0,0,0,0.6), 0 1px rgba(255,255,255,0.2);
 }
 
-/* toolbarbutton-icon */
 .chatbar-button > .toolbarbutton-text,
 .chatbar-button > .toolbarbutton-menu-dropmarker {
   display: none;
 }
 
 .chatbar-innerbox {
   background: transparent;
-  margin: -200px -1px 0 -1px;
+  margin: -285px -1px 0 -1px;
   overflow: hidden;
 }
 
-chatbar > chatbox {
-  height: 200px;
-  width: 200px;
+chatbar {
+  -moz-margin-end: 20px;
+}
+
+chatbox {
+  height: 285px;
+  width: 260px;
+  -moz-margin-start: 4px;
   background-color: white;
   border: 1px solid #404040;
   border-bottom: none;
 }
 
-chatbar > chatbox[minimized="true"] {
-  width: 100px;
+chatbox[minimized="true"] {
+  width: 160px;
   height: 20px;
-  border-bottom: none;
-}
-
-chatbar > chatbox + chatbox {
-  -moz-margin-start: -1px;
-}
-
+}
--- a/browser/themes/winstripe/browser.css
+++ b/browser/themes/winstripe/browser.css
@@ -3520,38 +3520,36 @@ stack[anonid=browserStack][responsivemod
 }
 
 .chatbar-button[open="true"],
 .chatbar-button:hover,
 .chatbar-button:active:hover {
   box-shadow: inset 0 2px 5px rgba(0,0,0,0.6), 0 1px rgba(255,255,255,0.2);
 }
 
-/* toolbarbutton-icon */
 .chatbar-button > .toolbarbutton-text,
 .chatbar-button > .toolbarbutton-menu-dropmarker {
   display: none;
 }
 
 .chatbar-innerbox {
   background: transparent;
-  margin: -200px -1px 0 -1px;
+  margin: -285px -1px 0 -1px;
   overflow: hidden;
 }
 
-chatbar > chatbox {
-  height: 200px;
-  width: 200px;
+chatbar {
+  -moz-margin-end: 20px;
+}
+
+chatbox {
+  height: 285px;
+  width: 260px;
+  -moz-margin-start: 4px;
   background-color: white;
   border: 1px solid gray;
   border-bottom: none;
 }
 
-chatbar > chatbox[minimized="true"] {
-  width: 100px;
+chatbox[minimized="true"] {
+  width: 160px;
   height: 20px;
-  border-bottom: none;
-}
-
-chatbar > chatbox + chatbox {
-  -moz-margin-start: -1px;
-}
-
+}
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -440,16 +440,17 @@ GK_ATOM(indeterminate, "indeterminate")
 GK_ATOM(index, "index")
 GK_ATOM(infer, "infer")
 GK_ATOM(infinity, "infinity")
 GK_ATOM(inherit, "inherit")
 GK_ATOM(inherits, "inherits")
 GK_ATOM(inheritstyle, "inheritstyle")
 GK_ATOM(initial_scale, "initial-scale")
 GK_ATOM(input, "input")
+GK_ATOM(inputmode, "inputmode")
 GK_ATOM(ins, "ins")
 GK_ATOM(insertafter, "insertafter")
 GK_ATOM(insertbefore, "insertbefore")
 GK_ATOM(instanceOf, "instanceOf")
 GK_ATOM(int32, "int32")
 GK_ATOM(int64, "int64")
 GK_ATOM(integer, "integer")
 GK_ATOM(intersection, "intersection")
--- 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/content/html/content/src/nsHTMLInputElement.cpp
+++ b/content/html/content/src/nsHTMLInputElement.cpp
@@ -142,16 +142,38 @@ static const nsAttrValue::EnumTable kInp
   { "on", NS_INPUT_AUTOCOMPLETE_ON },
   { "off", NS_INPUT_AUTOCOMPLETE_OFF },
   { 0 }
 };
 
 // Default autocomplete value is "".
 static const nsAttrValue::EnumTable* kInputDefaultAutocomplete = &kInputAutocompleteTable[0];
 
+static const uint8_t NS_INPUT_INPUTMODE_AUTO              = 0;
+static const uint8_t NS_INPUT_INPUTMODE_NUMERIC           = 1;
+static const uint8_t NS_INPUT_INPUTMODE_DIGIT             = 2;
+static const uint8_t NS_INPUT_INPUTMODE_UPPERCASE         = 3;
+static const uint8_t NS_INPUT_INPUTMODE_LOWERCASE         = 4;
+static const uint8_t NS_INPUT_INPUTMODE_TITLECASE         = 5;
+static const uint8_t NS_INPUT_INPUTMODE_AUTOCAPITALIZED   = 6;
+
+static const nsAttrValue::EnumTable kInputInputmodeTable[] = {
+  { "auto", NS_INPUT_INPUTMODE_AUTO },
+  { "numeric", NS_INPUT_INPUTMODE_NUMERIC },
+  { "digit", NS_INPUT_INPUTMODE_DIGIT },
+  { "uppercase", NS_INPUT_INPUTMODE_UPPERCASE },
+  { "lowercase", NS_INPUT_INPUTMODE_LOWERCASE },
+  { "titlecase", NS_INPUT_INPUTMODE_TITLECASE },
+  { "autocapitalized", NS_INPUT_INPUTMODE_AUTOCAPITALIZED },
+  { 0 }
+};
+
+// Default inputmode value is "auto".
+static const nsAttrValue::EnumTable* kInputDefaultInputmode = &kInputInputmodeTable[0];
+
 const double nsHTMLInputElement::kDefaultStepBase = 0;
 const double nsHTMLInputElement::kStepAny = 0;
 
 #define NS_INPUT_ELEMENT_STATE_IID                 \
 { /* dc3b3d14-23e2-4479-b513-7b369343e3a0 */       \
   0xdc3b3d14,                                      \
   0x23e2,                                          \
   0x4479,                                          \
@@ -861,16 +883,18 @@ NS_IMPL_STRING_ATTR(nsHTMLInputElement, 
 NS_IMPL_STRING_ATTR(nsHTMLInputElement, Min, min)
 NS_IMPL_ACTION_ATTR(nsHTMLInputElement, FormAction, formaction)
 NS_IMPL_ENUM_ATTR_DEFAULT_VALUE(nsHTMLInputElement, FormEnctype, formenctype,
                                 kFormDefaultEnctype->tag)
 NS_IMPL_ENUM_ATTR_DEFAULT_VALUE(nsHTMLInputElement, FormMethod, formmethod,
                                 kFormDefaultMethod->tag)
 NS_IMPL_BOOL_ATTR(nsHTMLInputElement, FormNoValidate, formnovalidate)
 NS_IMPL_STRING_ATTR(nsHTMLInputElement, FormTarget, formtarget)
+NS_IMPL_ENUM_ATTR_DEFAULT_VALUE(nsHTMLInputElement, Inputmode, inputmode,
+                                kInputDefaultInputmode->tag)
 NS_IMPL_BOOL_ATTR(nsHTMLInputElement, Multiple, multiple)
 NS_IMPL_NON_NEGATIVE_INT_ATTR(nsHTMLInputElement, MaxLength, maxlength)
 NS_IMPL_STRING_ATTR(nsHTMLInputElement, Name, name)
 NS_IMPL_BOOL_ATTR(nsHTMLInputElement, ReadOnly, readonly)
 NS_IMPL_BOOL_ATTR(nsHTMLInputElement, Required, required)
 NS_IMPL_URI_ATTR(nsHTMLInputElement, Src, src)
 NS_IMPL_STRING_ATTR(nsHTMLInputElement, Step, step)
 NS_IMPL_INT_ATTR(nsHTMLInputElement, TabIndex, tabindex)
@@ -2736,16 +2760,19 @@ nsHTMLInputElement::ParseAttribute(int32
       return aResult.ParseEnumValue(aValue, kFormMethodTable, false);
     }
     if (aAttribute == nsGkAtoms::formenctype) {
       return aResult.ParseEnumValue(aValue, kFormEnctypeTable, false);
     }
     if (aAttribute == nsGkAtoms::autocomplete) {
       return aResult.ParseEnumValue(aValue, kInputAutocompleteTable, false);
     }
+    if (aAttribute == nsGkAtoms::inputmode) {
+      return aResult.ParseEnumValue(aValue, kInputInputmodeTable, false);
+    }
     if (ParseImageAttribute(aAttribute, aValue, aResult)) {
       // We have to call |ParseImageAttribute| unconditionally since we
       // don't know if we're going to have a type="image" attribute yet,
       // (or could have it set dynamically in the future).  See bug
       // 214077.
       return true;
     }
   }
--- a/content/html/content/test/forms/test_input_attributes_reflection.html
+++ b/content/html/content/test/forms/test_input_attributes_reflection.html
@@ -105,16 +105,25 @@ reflectString({
 reflectUnsignedInt({
   element: document.createElement("input"),
   attribute: "height",
   nonZero: false
 });
 
 // .indeterminate doesn't reflect a content attribute.
 
+// .inputmode
+reflectLimitedEnumerated({
+  element: document.createElement("input"),
+  attribute: "inputmode",
+  validValues: [ "numeric", "digit", "uppercase", "lowercase", "titlecase", "autocapitalized", "auto" ],
+  invalidValues: [ "", "foo", "tulip" ],
+  defaultValue: "auto"
+});
+
 // TODO: list (HTMLElement)
 
 // .max
 reflectString({
   element: document.createElement('input'),
   attribute: 'max',
 });
 
--- a/dom/interfaces/html/nsIDOMHTMLInputElement.idl
+++ b/dom/interfaces/html/nsIDOMHTMLInputElement.idl
@@ -15,17 +15,17 @@ interface nsIDOMValidityState;
   *
   * This interface is trying to follow the DOM Level 2 HTML specification:
   * http://www.w3.org/TR/DOM-Level-2-HTML/
   *
   * with changes from the work-in-progress WHATWG HTML specification:
   * http://www.whatwg.org/specs/web-apps/current-work/
   */
 
-[scriptable, uuid(c12471c8-155f-4368-9e8b-13a231e85f3b)]
+[scriptable, uuid(83984fd0-b0b2-11e1-afa6-0800200c9a66)]
 interface nsIDOMHTMLInputElement : nsIDOMHTMLElement
 {
            attribute DOMString             accept;
            attribute DOMString             alt;
 
            attribute DOMString             autocomplete;
            attribute boolean               autofocus;
            attribute boolean               defaultChecked;
@@ -38,16 +38,18 @@ interface nsIDOMHTMLInputElement : nsIDO
            attribute boolean               formNoValidate;
            attribute DOMString             formTarget;
 
   readonly attribute nsIDOMFileList        files;
 
            attribute unsigned long height;
            attribute boolean               indeterminate;
 
+           attribute DOMString             inputmode;
+
   readonly attribute nsIDOMHTMLElement     list;
            attribute DOMString             max;
            attribute long                  maxLength;
            attribute DOMString             min;
 
            attribute boolean               multiple;
            attribute DOMString             name;
 
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -156,16 +156,17 @@ parent:
      */
     sync EndIMEComposition(bool cancel) returns (nsString composition);
 
     sync GetInputContext() returns (int32_t IMEEnabled, int32_t IMEOpen);
 
     SetInputContext(int32_t IMEEnabled,
                     int32_t IMEOpen,
                     nsString type,
+                    nsString inputmode,
                     nsString actionHint,
                     int32_t cause,
                     int32_t 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
@@ -679,16 +679,17 @@ TabParent::RecvGetInputContext(int32_t* 
   *aIMEOpen = static_cast<int32_t>(context.mIMEState.mOpen);
   return true;
 }
 
 bool
 TabParent::RecvSetInputContext(const int32_t& aIMEEnabled,
                                const int32_t& aIMEOpen,
                                const nsString& aType,
+                               const nsString& aInputmode,
                                const nsString& aActionHint,
                                const int32_t& aCause,
                                const int32_t& 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 =
@@ -696,16 +697,17 @@ TabParent::RecvSetInputContext(const int
   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(int32_t* aIMEEnabled,
                                      int32_t* aIMEOpen);
     virtual bool RecvSetInputContext(const int32_t& aIMEEnabled,
                                      const int32_t& aIMEOpen,
                                      const nsString& aType,
+                                     const nsString& aInputmode,
                                      const nsString& aActionHint,
                                      const int32_t& aCause,
                                      const int32_t& aFocusChange);
     virtual bool RecvSetCursor(const uint32_t& aValue);
     virtual bool RecvSetBackgroundColor(const nscolor& aValue);
     virtual bool RecvGetDPI(float* aValue);
     virtual bool RecvGetWidgetNativeData(WindowsHandle* aValue);
     virtual bool RecvZoomToRect(const gfxRect& aRect);
--- a/dom/wifi/WifiWorker.js
+++ b/dom/wifi/WifiWorker.js
@@ -891,21 +891,21 @@ var WifiManager = (function() {
       });
     }
 
     setProperty("ctl.stop", "wpa_supplicant", tick);
   }
 
   function didConnectSupplicant(reconnected, callback) {
     waitForEvent();
-    notify("supplicantconnection");
 
     // Load up the supplicant state.
     statusCommand(function(status) {
       parseStatus(status, reconnected);
+      notify("supplicantconnection");
       callback();
     });
   }
 
   function prepareForStartup(callback) {
     // First, check to see if there's a wpa_supplicant running that we can
     // connect to.
     getProperty(SUPP_PROP, "stopped", function (value) {
@@ -1940,17 +1940,17 @@ WifiWorker.prototype = {
 
         break;
       }
     }
   },
 
   getNetworks: function(msg) {
     const message = "WifiManager:getNetworks:Return";
-    if (WifiManager.state === "UNINITIALIZED") {
+    if (!WifiManager.enabled) {
       this._sendMessage(message, false, "Wifi is disabled", msg);
       return;
     }
 
     this.waitForScan((function (networks) {
       this._sendMessage(message, networks !== null, networks, msg);
     }).bind(this));
     WifiManager.scan(true, function() {});
@@ -2018,17 +2018,17 @@ WifiWorker.prototype = {
     if (this._stateRequests.length === 1)
       WifiManager.setWifiEnabled(msg.enabled, this._setWifiEnabledCallback.bind(this));
   },
 
   associate: function(msg) {
     const MAX_PRIORITY = 9999;
     const message = "WifiManager:associate:Return";
     let network = msg.data;
-    if (WifiManager.state === "UNINITIALIZED") {
+    if (!WifiManager.enabled) {
       this._sendMessage(message, false, "Wifi is disabled", msg);
       return;
     }
 
     let privnet = network;
     let self = this;
     function networkReady() {
       // saveConfig now before we disable most of the other networks.
@@ -2088,17 +2088,17 @@ WifiWorker.prototype = {
         networkReady();
       }).bind(this));
     }
   },
 
   forget: function(msg) {
     const message = "WifiManager:forget:Return";
     let network = msg.data;
-    if (WifiManager.state === "UNINITIALIZED") {
+    if (!WifiManager.enabled) {
       this._sendMessage(message, false, "Wifi is disabled", msg);
       return;
     }
 
     let ssid = network.ssid;
     if (!(ssid in this.configuredNetworks)) {
       this._sendMessage(message, false, "Trying to forget an unknown network", msg);
       return;
--- 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/layout/style/nsStyleAnimation.cpp
+++ b/layout/style/nsStyleAnimation.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* Utilities for animation of computed style values */
 
 #include "mozilla/Util.h"
+#include "mozilla/MathAlgorithms.h"
 
 #include "nsStyleAnimation.h"
 #include "nsCOMArray.h"
 #include "nsIStyleRule.h"
 #include "mozilla/css/StyleRule.h"
 #include "nsString.h"
 #include "nsStyleContext.h"
 #include "nsStyleSet.h"
@@ -237,43 +238,16 @@ ToPrimitive(nsCSSValue::Array* aArray)
       break;
     }
     default:
       arr = aArray;
   }
   return arr.forget();
 }
 
-// Greatest Common Divisor
-static uint32_t
-gcd(uint32_t a, uint32_t b)
-{
-  // Euclid's algorithm; O(N) in the worst case.  (There are better
-  // ways, but we don't need them for stroke-dasharray animation.)
-  NS_ABORT_IF_FALSE(a > 0 && b > 0, "positive numbers expected");
-
-  while (a != b) {
-    if (a > b) {
-      a = a - b;
-    } else {
-      b = b - a;
-    }
-  }
-
-  return a;
-}
-
-// Least Common Multiple
-static uint32_t
-lcm(uint32_t a, uint32_t b)
-{
-  // Divide first to reduce overflow risk.
-  return (a / gcd(a, b)) * b;
-}
-
 inline void
 nscoordToCSSValue(nscoord aCoord, nsCSSValue& aCSSValue)
 {
   aCSSValue.SetFloatValue(nsPresContext::AppUnitsToFloatCSSPixels(aCoord),
                           eCSSUnit_Pixel);
 }
 
 // Like nsStyleCoord::Calc, but with length in float pixels instead of nscoord.
@@ -1948,17 +1922,17 @@ nsStyleAnimation::AddWeighted(nsCSSPrope
           (list1->mValue.GetUnit() != eCSSUnit_None || len1 == 1) &&
           (list2->mValue.GetUnit() != eCSSUnit_None || len2 == 1),
           "multi-value valuelist with 'none' as first element");
         return false;
       }
 
       nsAutoPtr<nsCSSValueList> result;
       nsCSSValueList **resultTail = getter_Transfers(result);
-      for (uint32_t i = 0, i_end = lcm(len1, len2); i != i_end; ++i) {
+      for (uint32_t i = 0, i_end = EuclidLCM<uint32_t>(len1, len2); i != i_end; ++i) {
         const nsCSSValue &v1 = list1->mValue;
         const nsCSSValue &v2 = list2->mValue;
         NS_ABORT_IF_FALSE(v1.GetUnit() == eCSSUnit_Number ||
                           v1.GetUnit() == eCSSUnit_Percent, "unexpected");
         NS_ABORT_IF_FALSE(v2.GetUnit() == eCSSUnit_Number ||
                           v2.GetUnit() == eCSSUnit_Percent, "unexpected");
         if (v1.GetUnit() != v2.GetUnit()) {
           // Can't animate between lengths and percentages (until calc()).
new file mode 100644
--- /dev/null
+++ b/mfbt/MathAlgorithms.h
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* mfbt maths algorithms. */
+
+#ifndef mozilla_MathAlgorithms_h_
+#define mozilla_MathAlgorithms_h_
+
+#include "mozilla/Assertions.h"
+
+namespace mozilla {
+
+// Greatest Common Divisor
+template<typename IntegerType>
+MOZ_ALWAYS_INLINE IntegerType
+EuclidGCD(IntegerType a, IntegerType b)
+{
+  // Euclid's algorithm; O(N) in the worst case.  (There are better
+  // ways, but we don't need them for the current use of this algo.)
+  MOZ_ASSERT(a > 0);
+  MOZ_ASSERT(b > 0);
+
+  while (a != b) {
+    if (a > b) {
+      a = a - b;
+    } else {
+      b = b - a;
+    }
+  }
+
+  return a;
+}
+
+// Least Common Multiple
+template<typename IntegerType>
+MOZ_ALWAYS_INLINE IntegerType
+EuclidLCM(IntegerType a, IntegerType b)
+{
+  // Divide first to reduce overflow risk.
+  return (a / EuclidGCD(a, b)) * b;
+}
+
+} /* namespace mozilla */
+
+#endif  /* mozilla_MathAlgorithms_h_ */
--- a/mfbt/exported_headers.mk
+++ b/mfbt/exported_headers.mk
@@ -14,16 +14,17 @@ EXPORTS_mozilla += \
   BloomFilter.h \
   CheckedInt.h \
   Constants.h \
   FloatingPoint.h \
   GuardObjects.h \
   HashFunctions.h \
   Likely.h \
   LinkedList.h \
+  MathAlgorithms.h \
   MSStdInt.h \
   RangedPtr.h \
   RefPtr.h \
   Scoped.h \
   StandardInteger.h \
   SHA1.h \
   ThreadLocal.h \
   TypeTraits.h \
--- a/mobile/android/base/GeckoAppShell.java
+++ b/mobile/android/base/GeckoAppShell.java
@@ -580,22 +580,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
@@ -81,16 +81,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;
@@ -799,16 +800,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"))
@@ -1025,27 +1040,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) {
@@ -1436,19 +1452,20 @@ private static final class DebugGeckoInp
     @Override
     public void notifyIME(int type, int state) {
         Log.d(LOGTAG, "IME: >notifyIME(type=" + type + ", state=" + state + ")");
         GeckoApp.assertOnGeckoThread();
         super.notifyIME(type, state);
     }
 
     @Override
-    public void notifyIMEEnabled(int state, String typeHint, String actionHint) {
+    public void notifyIMEEnabled(int state, String typeHint, String modeHint, String actionHint) {
         Log.d(LOGTAG, "IME: >notifyIMEEnabled(state=" + state + ", typeHint=\"" + typeHint
-                      + "\", actionHint=\"" + actionHint + "\"");
+                      + "\", modeHint=\"" + modeHint + "\", actionHint=\""
+                      + actionHint + "\"");
         GeckoApp.assertOnGeckoThread();
         if (state < IME_STATE_DISABLED || state > IME_STATE_PLUGIN)
             throw new IllegalArgumentException("Unexpected IMEState=" + state);
-        super.notifyIMEEnabled(state, typeHint, actionHint);
+        super.notifyIMEEnabled(state, typeHint, modeHint, actionHint);
     }
 }
 
 }
--- 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;
 
     int32_t 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, uint32_t aTextLen, int aStart, int aEnd, int aNewEnd);
 
     nsresult TakeScreenshot(nsIDOMWindow *window, int32_t srcX, int32_t srcY, int32_t srcW, int32_t srcH, int32_t dstY, int32_t dstX, int32_t dstW, int32_t dstH, int32_t bufW, int32_t bufH, int32_t tabId, int32_t 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<int32_t>(aContext.mIMEState.mEnabled),
     static_cast<int32_t>(aContext.mIMEState.mOpen),
     aContext.mHTMLInputType,
+    aContext.mHTMLInputInputmode,
     aContext.mActionHint,
     static_cast<int32_t>(aAction.mCause),
     static_cast<int32_t>(aAction.mFocusChange));
 }
 
 NS_IMETHODIMP_(InputContext)
 PuppetWidget::GetInputContext()
 {