Bug 1490661 - Part 3. InputContext should reference enterkeyhint attribute for action hint. r=masayuki
authorMakoto Kato <m_kato@ga2.so-net.ne.jp>
Tue, 23 Jun 2020 06:37:50 +0000
changeset 536751 3605a382f0bff9ff9396d72112c05844ea34f6fb
parent 536750 9b7202e779eb71a7368c075fba8a183a3118fb43
child 536752 bd285002fe0c3e392c8679b46020cd731df8ba4e
push id119658
push userm_kato@ga2.so-net.ne.jp
push dateTue, 23 Jun 2020 07:01:50 +0000
treeherderautoland@2e38bb7fa471 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmasayuki
bugs1490661
milestone79.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 1490661 - Part 3. InputContext should reference enterkeyhint attribute for action hint. r=masayuki Set enterkeyhint to `InputContext.mActionHint`. Although it is used by `moz_action` attribute, enterkeyhint is standardized version of this. New logic is the following. 1. Read `enterkeyhint` that is from editing host 2. Read `moz_action` on `<input>` element if no `enterkeyhint` 3. If both is nothing, we infer this value from the `<form>`. Differential Revision: https://phabricator.services.mozilla.com/D79644
dom/events/IMEStateManager.cpp
widget/tests/test_actionhint.html
--- a/dom/events/IMEStateManager.cpp
+++ b/dom/events/IMEStateManager.cpp
@@ -1160,20 +1160,27 @@ static bool IsNextFocusableElementTextCo
   MOZ_DIAGNOSTIC_ASSERT(nextElement->IsHTMLElement(nsGkAtoms::input));
 
   HTMLInputElement* inputElement =
       HTMLInputElement::FromNodeOrNull(nextElement);
   return !inputElement->ReadOnly();
 }
 
 static void GetActionHint(nsIContent& aContent, nsAString& aActionHint) {
+  // If enterkeyhint is set, we don't infer action hint.
+  if (!aActionHint.IsEmpty()) {
+    return;
+  }
+
+  // XXX This is old compatibility, but we might be able to remove this.
   aContent.AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::moz_action_hint,
                                 aActionHint);
 
   if (!aActionHint.IsEmpty()) {
+    ToLowerCase(aActionHint);
     return;
   }
 
   // Get the input content corresponding to the focused node,
   // which may be an anonymous child of the input content.
   nsIContent* inputContent = aContent.FindFirstNonChromeOnlyAccessContent();
   if (!inputContent->IsHTMLElement(nsGkAtoms::input)) {
     return;
@@ -1264,26 +1271,31 @@ void IMEStateManager::SetIMEState(const 
 
   context.mHasHandledUserInput =
       aPresContext && aPresContext->PresShell()->HasHandledUserInput();
 
   context.mInPrivateBrowsing =
       aPresContext &&
       nsContentUtils::IsInPrivateBrowsing(aPresContext->Document());
 
-  if (aContent) {
+  if (aContent && aContent->IsHTMLElement()) {
+    if (aState.IsEditable() && StaticPrefs::dom_forms_enterkeyhint()) {
+      nsGenericHTMLElement::FromNode(aContent)->GetEnterKeyHint(
+          context.mActionHint);
+    }
+
     if (aContent->IsHTMLElement(nsGkAtoms::input)) {
       HTMLInputElement::FromNode(aContent)->GetType(context.mHTMLInputType);
       GetActionHint(*aContent, context.mActionHint);
     } else if (aContent->IsHTMLElement(nsGkAtoms::textarea)) {
       context.mHTMLInputType.Assign(nsGkAtoms::textarea->GetUTF16String());
       GetActionHint(*aContent, context.mActionHint);
     }
 
-    if (aContent->IsHTMLElement() && aState.IsEditable() &&
+    if (aState.IsEditable() &&
         (StaticPrefs::dom_forms_inputmode() ||
          nsContentUtils::IsChromeDoc(aContent->OwnerDoc()))) {
       aContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::inputmode,
                                      context.mHTMLInputInputmode);
       if (aContent->IsHTMLElement(nsGkAtoms::input) &&
           context.mHTMLInputInputmode.EqualsLiteral("mozAwesomebar")) {
         if (!nsContentUtils::IsChromeDoc(aContent->OwnerDoc())) {
           // mozAwesomebar should be allowed only in chrome
--- a/widget/tests/test_actionhint.html
+++ b/widget/tests/test_actionhint.html
@@ -16,48 +16,70 @@
 <form><input type="text" id="d1"><textarea></textarea><input type="submit"></form>
 <form><input type="text" id="e1"><input type="number"><input type="submit"></form>
 <form><input type="text" id="f1"><input type="date"><input type="submit"></form>
 <form><input type="text" id="g1"><input type="radio"><input type="submit"></form>
 <form><input type="text" id="h1"><input type="text" readonly><input type="submit"></form>
 <form><input type="text" id="i1"><input type="text" disabled><input type="submit"></form>
 <input type="text" id="j1"><input type="text"><input type="button">
 <form><input type="text" id="k1"><a href="#foo">foo</a><input type="text"><input type="submit"></form>
+<form>
+  <input id="l1" enterkeyhint="enter">
+  <input id="l2" enterkeyhint="DONE">
+  <input id="l3" enterkeyhint="go">
+  <input id="l4" enterkeyhint="Next">
+  <input id="l5" enterkeyhint="Previous">
+  <input id="l6" enterkeyhint="search">
+  <textarea id="l7" enterkeyhint="send"></textarea>
+  <input id="l8" enterkeyhint="NONE">
+</form>
 </div>
 <pre id="test">
 <script class=testbody" type="application/javascript">
 SimpleTest.waitForExplicitFinish();
 
-SimpleTest.waitForFocus(() => {
+SimpleTest.waitForFocus(async () => {
   const tests = [
     { id: "a1", hint: "next", desc: "next element is type=text" },
     { id: "a2", hint: "go", desc: "next element is type=submit" },
     { id: "b1", hint: "search", desc: "current is type=search" },
     { id: "c1", hint: "go", desc: "only this element" },
     { id: "d1", hint: "next", desc: "next element is textarea" },
     { id: "e1", hint: "next", desc: "next element is type=number" },
     { id: "h1", hint: "go", desc: "next element is readonly" },
     // XXX Feel free to change this result if you get some bugs reports
     { id: "i1", hint: "go", desc: "next element is disabled" },
     { id: "j1", hint: "", desc: "no form element" },
+    { id: "l1", hint: "enter", desc: "enterkeyhint=enter" },
+    { id: "l2", hint: "done", desc: "enterkeyhint=DONE" },
+    { id: "l3", hint: "go", desc: "enterkeyhint=go" },
+    { id: "l4", hint: "next", desc: "enterkeyhint=Next" },
+    { id: "l5", hint: "previous", desc: "enterkeyhint=Previous" },
+    { id: "l6", hint: "search", desc: "enterkeyhint=search" },
+    { id: "l7", hint: "send", desc: "enterkeyhint=send" },
+    // Since enterkeyhint is invalid, we infer action hint. So feel free to change this.
+    { id: "l8", hint: "go", desc: "enterkeyhint is invalid" },
   ];
 
   const todo_tests = [
     { id: "f1", hint: "next", desc: "next element is type=date" },
     { id: "k1", hint: "", desc: "next is anchor link" },
   ];
 
+  await SpecialPowers.setBoolPref("dom.forms.enterkeyhint", true);
+
   for (let test of tests) {
     document.getElementById(test.id).focus();
     is(SpecialPowers.DOMWindowUtils.focusedActionHint, test.hint, test.desc);
   }
 
   for (let test of todo_tests) {
     document.getElementById(test.id).focus();
     todo_is(SpecialPowers.DOMWindowUtils.focusedActionHint, test.hint, test.desc);
   }
 
+  SpecialPowers.clearUserPref("dom.forms.enterkeyhint");
   SimpleTest.finish();
 });
 </script>
 </pre>
 </body>
 </html>