Bug 1585617 - convert remaining <textbox> usages in JavaScript to using html:input. r=mkmelin
authorAlessandro Castellani <alessandro@thunderbird.net>
Fri, 04 Oct 2019 11:17:17 -0700
changeset 37108 0daedafc7c204fc261f746775d24fa7ec81ab3a4
parent 37107 b29c52c255ed74bc22828d55b46fcb042172d2e9
child 37109 e544df90a3f3a56de390b0e0e3d20d3dffaf826b
push id395
push userclokep@gmail.com
push dateMon, 02 Dec 2019 19:38:57 +0000
reviewersmkmelin
bugs1585617
Bug 1585617 - convert remaining <textbox> usages in JavaScript to using html:input. r=mkmelin
mail/base/modules/QuickFilterManager.jsm
mail/components/compose/content/dialogs/EdAECSSAttributes.js
mail/components/compose/content/dialogs/EdAEJSEAttributes.js
mail/components/compose/content/dialogs/EdDialogCommon.js
mail/components/customizableui/CustomizableUI.jsm
mail/components/customizableui/PanelMultiView.jsm
mail/components/im/content/chat.css
mail/components/im/content/joinchat.js
mail/components/im/content/joinchat.xul
mail/components/preferences/chat.js
mail/components/preferences/compose.js
mail/components/preferences/connection.js
mail/components/preferences/test/browser/browser_cloudfile.js
mail/themes/linux/mail/preferences/applications.css
mail/themes/osx/mail/preferences/applications.css
mail/themes/shared/mail/incontentprefs/applications.css
mail/themes/windows/mail/preferences/applications.css
--- a/mail/base/modules/QuickFilterManager.jsm
+++ b/mail/base/modules/QuickFilterManager.jsm
@@ -1149,17 +1149,17 @@ var MessageTextFilter = {
 
     // -- Blurring kills upsell.
     aNode.addEventListener(
       "blur",
       function(aEvent) {
         let panel = aDocument.getElementById("qfb-text-search-upsell");
         if (
           (Services.focus.activeWindow != aDocument.defaultView ||
-            aDocument.commandDispatcher.focusedElement != aNode.inputField) &&
+            aDocument.commandDispatcher.focusedElement != aNode) &&
           panel.state == "open"
         ) {
           panel.hidePopup();
         }
       },
       true
     );
 
@@ -1211,17 +1211,17 @@ var MessageTextFilter = {
       let panel = aDocument.getElementById("qfb-text-search-upsell");
       let line1 = aDocument.getElementById("qfb-upsell-line-one");
       let line2 = aDocument.getElementById("qfb-upsell-line-two");
       line1.value = line1.getAttribute("fmt").replace("#1", aFilterValue.text);
       line2.value = line2.getAttribute("fmt").replace("#1", aFilterValue.text);
 
       if (
         panel.state == "closed" &&
-        aDocument.commandDispatcher.focusedElement == aNode.inputField
+        aDocument.commandDispatcher.focusedElement == aNode
       ) {
         let filterBar = aDocument.getElementById("quick-filter-bar");
         // panel.sizeTo(filterBar.clientWidth - 20, filterBar.clientHeight - 20);
         panel.openPopup(filterBar, "after_end", -7, 7, false, true);
       }
       return;
     }
 
--- a/mail/components/compose/content/dialogs/EdAECSSAttributes.js
+++ b/mail/components/compose/content/dialogs/EdAECSSAttributes.js
@@ -84,17 +84,17 @@ function onInputCSSAttributeName() {
     newValue = existingValue;
   }
 
   gDialog.AddCSSAttributeValueInput.value = newValue;
 }
 
 function editCSSAttributeValue(targetCell) {
   if (IsNotTreeHeader(targetCell)) {
-    gDialog.AddCSSAttributeValueInput.inputField.select();
+    gDialog.AddCSSAttributeValueInput.select();
   }
 }
 
 function UpdateCSSAttributes() {
   var CSSAList = document.getElementById("CSSAList");
   var styleString = "";
   for (var i = 0; i < CSSAList.childNodes.length; i++) {
     var item = CSSAList.childNodes[i];
--- a/mail/components/compose/content/dialogs/EdAEJSEAttributes.js
+++ b/mail/components/compose/content/dialogs/EdAEJSEAttributes.js
@@ -137,17 +137,17 @@ function onInputJSEAttributeValue() {
     if (!UpdateExistingAttribute(name, value, "JSEAList") && value) {
       AddTreeItem(name, value, "JSEAList", JSEAttrs);
     }
   }
 }
 
 function editJSEAttributeValue(targetCell) {
   if (IsNotTreeHeader(targetCell)) {
-    gDialog.AddJSEAttributeValueInput.inputField.select();
+    gDialog.AddJSEAttributeValueInput.select();
   }
 }
 
 function UpdateJSEAttributes() {
   var JSEAList = document.getElementById("JSEAList");
   var i;
 
   // remove removed attributes
--- a/mail/components/compose/content/dialogs/EdDialogCommon.js
+++ b/mail/components/compose/content/dialogs/EdDialogCommon.js
@@ -31,17 +31,17 @@ var SeeMore = false;
 // dialog location relative to parent window
 var gLocation;
 
 // The element being edited - so AdvancedEdit can have access to it
 var globalElement;
 
 /* Validate contents of an input field
  *
- *  inputWidget    The 'textbox' XUL element for text input of the attribute's value
+ *  inputWidget    The 'input' element for the the attribute's value
  *  listWidget     The 'menulist' XUL element for choosing "pixel" or "percent"
  *                  May be null when no pixel/percent is used.
  *  minVal         minimum allowed for input widget's value
  *  maxVal         maximum allowed for input widget's value
  *                 (when "listWidget" is used, maxVal is used for "pixel" maximum,
  *                  100% is assumed if "percent" is the user's choice)
  *  element        The DOM element that we set the attribute on. May be null.
  *  attName        Name of the attribute to set.  May be null or ignored if "element" is null
@@ -181,28 +181,19 @@ function ValidateNumberRange(value, minV
   gValidationError = true;
   return "";
 }
 
 function SetTextboxFocusById(id) {
   SetTextboxFocus(document.getElementById(id));
 }
 
-function SetTextboxFocus(textbox) {
-  if (textbox) {
-    // XXX Using the setTimeout is hacky workaround for bug 103197
-    // Must create a new function to keep "textbox" in scope
-    setTimeout(
-      function(textbox) {
-        textbox.focus();
-        textbox.select();
-      },
-      0,
-      textbox
-    );
+function SetTextboxFocus(input) {
+  if (input) {
+    input.focus();
   }
 }
 
 function ShowInputErrorMessage(message) {
   Services.prompt.alert(window, GetString("InputError"), message);
   window.focus();
 }
 
--- a/mail/components/customizableui/CustomizableUI.jsm
+++ b/mail/components/customizableui/CustomizableUI.jsm
@@ -2018,17 +2018,17 @@ var CustomizableUIInternal = {
         }
         // Find containing browser or iframe element in the parent doc.
         target = target.defaultView.docShell.chromeEventHandler;
         if (!target) {
           break;
         }
       }
       let tagName = target.localName;
-      inInput = tagName == "input" || tagName == "textbox";
+      inInput = tagName == "input";
       inItem = tagName == "toolbaritem" || tagName == "toolbarbutton";
       let isMenuItem = tagName == "menuitem";
       inMenu = inMenu || isMenuItem;
 
       if (isMenuItem && target.hasAttribute("closemenu")) {
         let closemenuVal = target.getAttribute("closemenu");
         menuitemCloseMenu =
           closemenuVal == "single" || closemenuVal == "none"
--- a/mail/components/customizableui/PanelMultiView.jsm
+++ b/mail/components/customizableui/PanelMultiView.jsm
@@ -1500,17 +1500,16 @@ var PanelView = class extends Associated
   /**
    * Determine whether an element can only be navigated to with tab/shift+tab,
    * not the arrow keys.
    */
   _isNavigableWithTabOnly(element) {
     let tag = element.localName;
     return (
       tag == "menulist" ||
-      tag == "textbox" ||
       tag == "input" ||
       tag == "textarea" ||
       // Allow tab to reach embedded documents in extension panels.
       tag == "browser"
     );
   }
 
   /**
--- a/mail/components/im/content/chat.css
+++ b/mail/components/im/content/chat.css
@@ -101,15 +101,20 @@ toolbar[mode="text"] .badgeButton-badge 
 #joinChatGrid textbox, menulist {
   width: 100%;
 }
 
 #joinChatGrid html|div {
   display: flex;
   align-items: center;
 }
+
+#joinChatGrid html|div html|input:not([type="number"]):not([type="color"]) {
+  flex: 1;
+}
+
 .required {
   visibility: hidden;
 }
 
 #participantCount {
   background: transparent;
 }
--- a/mail/components/im/content/joinchat.js
+++ b/mail/components/im/content/joinchat.js
@@ -58,55 +58,66 @@ var joinChat = {
       let text = field.label;
       let match = /_(.)/.exec(text);
       if (match) {
         label.setAttribute("accesskey", match[1]);
         text = text.replace(/_/, "");
       }
       label.setAttribute("value", text);
       label.setAttribute("control", "field-" + field.identifier);
+      label.setAttribute("id", "field-" + field.identifier + "-label");
       div1.appendChild(label);
       joinChatGrid.appendChild(div1);
 
       let div2 = document.createElementNS(
         "http://www.w3.org/1999/xhtml",
         "div"
       );
-      let textbox = document.createXULElement("textbox");
-      textbox.setAttribute("id", "field-" + field.identifier);
+      let input = document.createElementNS(
+        "http://www.w3.org/1999/xhtml",
+        "input"
+      );
+      input.classList.add("input-inline");
+      input.setAttribute("id", "field-" + field.identifier);
+      input.setAttribute(
+        "aria-labelledby",
+        "field-" + field.identifier + "-label"
+      );
       let val = defaultValues.getValue(field.identifier);
       if (val) {
-        textbox.setAttribute("value", val);
+        input.setAttribute("value", val);
       }
       if (field.type == Ci.prplIChatRoomField.TYPE_PASSWORD) {
-        textbox.setAttribute("type", "password");
+        input.setAttribute("type", "password");
       } else if (field.type == Ci.prplIChatRoomField.TYPE_INT) {
-        textbox.setAttribute("type", "number");
-        textbox.setAttribute("min", field.min);
-        textbox.setAttribute("max", field.max);
+        input.setAttribute("type", "number");
+        input.setAttribute("min", field.min);
+        input.setAttribute("max", field.max);
+      } else {
+        input.setAttribute("type", "text");
       }
-      div2.appendChild(textbox);
+      div2.appendChild(input);
       joinChatGrid.appendChild(div2);
 
       let div3 = document.querySelector(".optional-col").cloneNode(true);
       div3.classList.toggle("required", field.required);
       joinChatGrid.appendChild(div3);
 
-      joinChat._fields.push({ field, textbox });
+      joinChat._fields.push({ field, input });
     }
 
     window.sizeToContent();
   },
 
   join() {
     let values = joinChat._values;
     for (let field of joinChat._fields) {
-      let val = field.textbox.value.trim();
+      let val = field.input.value.trim();
       if (!val && field.field.required) {
-        field.textbox.focus();
+        field.input.focus();
         // FIXME: why isn't the return false enough?
         throw new Error("Some required fields are empty!");
         // return false;
       }
       if (val) {
         values.setValue(field.field.identifier, val);
       }
     }
--- a/mail/components/im/content/joinchat.xul
+++ b/mail/components/im/content/joinchat.xul
@@ -1,29 +1,34 @@
 <?xml version="1.0"?>
 <!-- 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/. -->
 
 <?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
 <?xml-stylesheet href="chrome://messenger/skin/menulist.css" type="text/css"?>
 <?xml-stylesheet href="chrome://messenger/content/chat/chat.css" type="text/css"?>
+<?xml-stylesheet type="text/css" href="chrome://messenger/skin/input-fields.css"?>
 
 <!DOCTYPE window SYSTEM "chrome://messenger/locale/joinChat.dtd">
 
 <dialog xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml"
         xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
         id="joinChatDialog"
         windowtype="Messenger:JoinChat"
         title="&joinChatWindow.title;"
         buttons="accept,cancel"
         onload="joinChat.onload()"
         autojoinLabel="&autojoin.label;"
         autojoinAccesskey="&autojoin.accesskey;">
+
+  <script src="chrome://global/content/globalOverlay.js"/>
+  <script src="chrome://global/content/editMenuOverlay.js"/>
   <script src="chrome://messenger/content/chat/joinchat.js"/>
+
   <div xmlns="http://www.w3.org/1999/xhtml" id="joinChatGrid">
     <div>
       <xul:label value="&account.label;" control="accountlist"/>
     </div>
     <div>
       <xul:menulist id="accountlist" onselect="joinChat.onAccountSelect();"/>
     </div>
     <div class="optional-col required">
--- a/mail/components/preferences/chat.js
+++ b/mail/components/preferences/chat.js
@@ -87,21 +87,20 @@ var gChatPane = {
     let checked = Preferences.get("messenger.status.reportIdle").value;
     document.querySelectorAll(".idle-reporting-enabled").forEach(e => {
       e.disabled = !checked;
     });
   },
 
   updateMessageDisabledState() {
     let textbox = document.getElementById("defaultIdleAwayMessage");
-    if (Preferences.get("messenger.status.awayWhenIdle").value) {
-      textbox.removeAttribute("disabled");
-    } else {
-      textbox.setAttribute("disabled", "true");
-    }
+    textbox.toggleAttribute(
+      "disabled",
+      !Preferences.get("messenger.status.awayWhenIdle").value
+    );
   },
 
   convertURLToLocalFile(aFileURL) {
     // convert the file url into a nsIFile
     if (aFileURL) {
       return Services.io
         .getProtocolHandler("file")
         .QueryInterface(Ci.nsIFileProtocolHandler)
--- a/mail/components/preferences/compose.js
+++ b/mail/components/preferences/compose.js
@@ -452,17 +452,17 @@ var gCloudFile = {
       this._addAccountButton.hidden = true;
     }
   },
 
   makeRichListItemForAccount(aAccount) {
     let rli = document.createXULElement("richlistitem");
     rli.value = aAccount.accountKey;
     rli.setAttribute("align", "center");
-    rli.setAttribute("class", "cloudfileAccount");
+    rli.classList.add("cloudfileAccount", "input-container");
     rli.setAttribute("value", aAccount.accountKey);
 
     if (aAccount.iconURL) {
       rli.style.listStyleImage = "url('" + aAccount.iconURL + "')";
     }
 
     let icon = document.createXULElement("image");
     icon.setAttribute("class", "typeIcon");
@@ -473,22 +473,22 @@ var gCloudFile = {
     label.setAttribute("flex", "1");
     label.setAttribute(
       "value",
       cloudFileAccounts.getDisplayName(aAccount.accountKey)
     );
     label.addEventListener("click", this, true);
     rli.appendChild(label);
 
-    let textBox = document.createXULElement("textbox");
-    textBox.setAttribute("flex", "1");
-    textBox.hidden = true;
-    textBox.addEventListener("blur", this);
-    textBox.addEventListener("keypress", this);
-    rli.appendChild(textBox);
+    let input = document.createElement("input");
+    input.setAttribute("type", "text");
+    input.setAttribute("hidden", "hidden");
+    input.addEventListener("blur", this);
+    input.addEventListener("keypress", this);
+    rli.appendChild(input);
 
     let warningIcon = document.createXULElement("image");
     warningIcon.setAttribute("class", "configuredWarning typeIcon");
     warningIcon.setAttribute("src", "chrome://global/skin/icons/warning.svg");
     warningIcon.setAttribute(
       "tooltiptext",
       this._strings.GetStringFromName("notConfiguredYet")
     );
@@ -657,53 +657,53 @@ var gCloudFile = {
   handleEvent(aEvent) {
     switch (aEvent.type) {
       case "unload":
         this.destroy();
         break;
       case "click": {
         let label = aEvent.target;
         let item = label.parentNode;
-        let textBox = item.querySelector("textbox");
+        let input = item.querySelector("input");
         if (!item.selected) {
           return;
         }
         label.hidden = true;
-        textBox.value = label.value;
-        textBox.hidden = false;
-        textBox.select();
+        input.value = label.value;
+        input.removeAttribute("hidden");
+        input.focus();
         break;
       }
       case "blur": {
-        let textBox = aEvent.target;
-        let item = textBox.parentNode;
+        let input = aEvent.target;
+        let item = input.parentNode;
         let label = item.querySelector("label");
-        cloudFileAccounts.setDisplayName(item.value, textBox.value);
-        label.value = textBox.value;
+        cloudFileAccounts.setDisplayName(item.value, input.value);
+        label.value = input.value;
         label.hidden = false;
-        textBox.hidden = true;
+        input.setAttribute("hidden", "hidden");
         break;
       }
       case "keypress": {
-        let textBox = aEvent.target;
-        let item = textBox.parentNode;
+        let input = aEvent.target;
+        let item = input.parentNode;
         let label = item.querySelector("label");
 
         if (aEvent.key == "Enter") {
-          cloudFileAccounts.setDisplayName(item.value, textBox.value);
-          label.value = textBox.value;
+          cloudFileAccounts.setDisplayName(item.value, input.value);
+          label.value = input.value;
           label.hidden = false;
-          textBox.hidden = true;
+          input.setAttribute("hidden", "hidden");
           gCloudFile._list.focus();
 
           aEvent.preventDefault();
         } else if (aEvent.key == "Escape") {
-          textBox.value = label.value;
+          input.value = label.value;
           label.hidden = false;
-          textBox.hidden = true;
+          input.setAttribute("hidden", "hidden");
           gCloudFile._list.focus();
 
           aEvent.preventDefault();
         }
       }
     }
   },
 
--- a/mail/components/preferences/connection.js
+++ b/mail/components/preferences/connection.js
@@ -384,17 +384,17 @@ var gConnectionsDialog = {
     return undefined;
   },
 
   getProxyControls() {
     let controlGroup = document.getElementById("networkProxyType");
     return [
       ...controlGroup.querySelectorAll(":scope > radio"),
       ...controlGroup.querySelectorAll("label"),
-      ...controlGroup.querySelectorAll("textbox"),
+      ...controlGroup.querySelectorAll("input"),
       ...controlGroup.querySelectorAll("checkbox"),
       ...document.querySelectorAll("#networkProxySOCKSVersion > radio"),
       ...document.querySelectorAll("#ConnectionsDialogPane > checkbox"),
     ];
   },
 
   get dnsOverHttpsResolvers() {
     let rawValue = Preferences.get("network.trr.resolvers", "").value;
--- a/mail/components/preferences/test/browser/browser_cloudfile.js
+++ b/mail/components/preferences/test/browser/browser_cloudfile.js
@@ -169,32 +169,32 @@ add_task(async () => {
     accountListItem,
     { clickCount: 1 },
     prefsWindow
   );
 
   await new Promise(resolve => prefsWindow.requestAnimationFrame(resolve));
 
   is(
-    prefsDocument.activeElement.closest("textbox"),
-    accountListItem.querySelector("textbox")
+    prefsDocument.activeElement.closest("input"),
+    accountListItem.querySelector("input")
   );
   ok(accountListItem.querySelector("label").hidden);
-  ok(!accountListItem.querySelector("textbox").hidden);
-  is(accountListItem.querySelector("textbox").value, "Mochitest Account");
+  ok(!accountListItem.querySelector("input").hidden);
+  is(accountListItem.querySelector("input").value, "Mochitest Account");
   EventUtils.synthesizeKey("VK_RIGHT", undefined, prefsWindow);
   EventUtils.synthesizeKey("!", undefined, prefsWindow);
   EventUtils.synthesizeKey("VK_RETURN", undefined, prefsWindow);
 
   await new Promise(resolve => prefsWindow.requestAnimationFrame(resolve));
 
   is(prefsDocument.activeElement, accountList);
   ok(!accountListItem.querySelector("label").hidden);
   is(accountListItem.querySelector("label").value, "Mochitest Account!");
-  ok(accountListItem.querySelector("textbox").hidden);
+  ok(accountListItem.querySelector("input").hidden);
   is(
     Services.prefs.getCharPref(
       `mail.cloud_files.accounts.${accountKey}.displayName`
     ),
     "Mochitest Account!"
   );
 
   // Start to rename the account, but bail out.
@@ -203,31 +203,31 @@ add_task(async () => {
     accountListItem,
     { clickCount: 1 },
     prefsWindow
   );
 
   await new Promise(resolve => prefsWindow.requestAnimationFrame(resolve));
 
   is(
-    prefsDocument.activeElement.closest("textbox"),
-    accountListItem.querySelector("textbox")
+    prefsDocument.activeElement.closest("input"),
+    accountListItem.querySelector("input")
   );
   EventUtils.synthesizeKey("O", undefined, prefsWindow);
   EventUtils.synthesizeKey("o", undefined, prefsWindow);
   EventUtils.synthesizeKey("p", undefined, prefsWindow);
   EventUtils.synthesizeKey("s", undefined, prefsWindow);
   EventUtils.synthesizeKey("VK_ESCAPE", undefined, prefsWindow);
 
   await new Promise(resolve => prefsWindow.requestAnimationFrame(resolve));
 
   is(prefsDocument.activeElement, accountList);
   ok(!accountListItem.querySelector("label").hidden);
   is(accountListItem.querySelector("label").value, "Mochitest Account!");
-  ok(accountListItem.querySelector("textbox").hidden);
+  ok(accountListItem.querySelector("input").hidden);
   is(
     Services.prefs.getCharPref(
       `mail.cloud_files.accounts.${accountKey}.displayName`
     ),
     "Mochitest Account!"
   );
 
   // Configure the account.
--- a/mail/themes/linux/mail/preferences/applications.css
+++ b/mail/themes/linux/mail/preferences/applications.css
@@ -35,14 +35,14 @@
  */
 
 .cloudfileAccount > label {
   margin-inline-start: 1px;
   margin-top: initial;
   margin-bottom: initial;
 }
 
-.cloudfileAccount > textbox {
-  color: -moz-fieldtext !important;
-  min-height: unset;
-  margin: 0;
-  padding-inline-start: 4px;
+.cloudfileAccount > input {
+  min-height: unset !important;
+  margin: 0 !important;
+  padding-inline-start: 4px !important;
+  padding: 2px 3px 3px !important;
 }
--- a/mail/themes/osx/mail/preferences/applications.css
+++ b/mail/themes/osx/mail/preferences/applications.css
@@ -28,13 +28,14 @@
 .cloudfileAccount description{
   padding-left: 3px;
 }
 
 .cloudfileAccount .typeIcon {
   margin-inline-end: 5px;
 }
 
-.cloudfileAccount > textbox {
-  min-height: unset;
-  margin: 0;
-  padding-inline-start: 4px;
+.cloudfileAccount > input {
+  min-height: unset !important;
+  margin: 0 !important;
+  padding-inline-start: 4px !important;
+  padding: 2px 3px 3px !important;
 }
--- a/mail/themes/shared/mail/incontentprefs/applications.css
+++ b/mail/themes/shared/mail/incontentprefs/applications.css
@@ -107,15 +107,24 @@ menuitem[appHandlerIcon="save"] {
   padding: 4px;
 }
 
 .cloudfileAccount .typeIcon {
   height: 16px;
   width: 16px;
 }
 
+.cloudfileAccount > label {
+  flex: 1;
+}
+
+.cloudfileAccount > input {
+  min-width: 10ch !important;
+  width: 10ch;
+}
+
 .cloudfileAccount:not([selected]) > label {
   pointer-events: none;
 }
 
 #cloudFileToggleAndThreshold {
   padding-bottom: 6px;
-}
\ No newline at end of file
+}
--- a/mail/themes/windows/mail/preferences/applications.css
+++ b/mail/themes/windows/mail/preferences/applications.css
@@ -37,13 +37,14 @@
  * Used by the cloudFile manager
  */
 
 .cloudfileAccount > label {
   margin-top: initial;
   margin-bottom: initial;
 }
 
-.cloudfileAccount > textbox {
-  min-height: unset;
-  margin: 0;
-  padding-inline-start: 4px;
+.cloudfileAccount > input {
+  min-height: unset !important;
+  margin: 0 !important;
+  padding-inline-start: 4px !important;
+  padding: 2px 3px 3px !important;
 }