Bug 1472716 - Part 2 - Convert the listbox in "languages.xul" to "richlistbox". r=jaws,gandalf draft
authorPaolo Amadini <paolo.mozmail@amadzone.org>
Tue, 10 Jul 2018 15:07:15 +0100
changeset 816057 1aced307b5aeb140ad1304da533803cf12db8d1a
parent 816056 af49686b874f071089f9efbc009adcdee2872a1c
child 816068 b30a2abf58dcc9461f00e48e5d3135f733d9544c
push id115732
push userpaolo.mozmail@amadzone.org
push dateTue, 10 Jul 2018 14:09:01 +0000
reviewersjaws, gandalf
bugs1472716
milestone63.0a1
Bug 1472716 - Part 2 - Convert the listbox in "languages.xul" to "richlistbox". r=jaws,gandalf Support for "listbox" in preferences is also removed, in preparation for the removal of the "listbox" element and binding. MozReview-Commit-ID: Bi2VKKi5rdk
browser/components/preferences/in-content/tests/browser_bug1184989_prevent_scrolling_when_preferences_flipped.js
browser/components/preferences/in-content/tests/browser_bug1184989_prevent_scrolling_when_preferences_flipped.xul
browser/components/preferences/languages.js
browser/components/preferences/languages.xul
browser/locales/en-US/browser/preferences/languages.ftl
browser/themes/shared/incontentprefs/preferences.inc.css
toolkit/content/preferencesBindings.js
toolkit/themes/shared/in-content/common.inc.css
toolkit/themes/windows/global/in-content/common.css
--- a/browser/components/preferences/in-content/tests/browser_bug1184989_prevent_scrolling_when_preferences_flipped.js
+++ b/browser/components/preferences/in-content/tests/browser_bug1184989_prevent_scrolling_when_preferences_flipped.js
@@ -18,24 +18,16 @@ add_task(async function() {
 
     // Test checkbox
     let checkbox = doc.getElementById("checkbox");
     checkbox.focus();
     EventUtils.sendString(" ");
     ok(checkbox.checked, "Checkbox is checked");
     await checkPageScrolling(container, "checkbox");
 
-    // Test listbox
-    let listbox = doc.getElementById("listbox");
-    let listitem = doc.getElementById("listitem");
-    listbox.focus();
-    EventUtils.sendString(" ");
-    ok(listitem.selected, "Listitem is selected");
-    await checkPageScrolling(container, "listbox");
-
     // Test radio
     let radiogroup = doc.getElementById("radiogroup");
     radiogroup.focus();
     EventUtils.sendString(" ");
     await checkPageScrolling(container, "radio");
   });
 
   await BrowserTestUtils.withNewTab({ gBrowser, url: "about:preferences#search" }, async function(browser) {
--- a/browser/components/preferences/in-content/tests/browser_bug1184989_prevent_scrolling_when_preferences_flipped.xul
+++ b/browser/components/preferences/in-content/tests/browser_bug1184989_prevent_scrolling_when_preferences_flipped.xul
@@ -10,23 +10,16 @@
     <hbox>
       <button id="button" label="button" />
     </hbox>
 
     <hbox>
       <checkbox id="checkbox" label="checkbox" />
     </hbox>
 
-    <hbox style="height: 50px;">
-      <listbox id="listbox">
-        <listitem id="listitem" label="listitem" />
-        <listitem label="listitem" />
-      </listbox>
-    </hbox>
-
     <hbox>
       <radiogroup id="radiogroup">
         <radio id="radio" label="radio" />
       </radiogroup>
     </hbox>
   </vbox>
 </vbox>
 
--- a/browser/components/preferences/languages.js
+++ b/browser/components/preferences/languages.js
@@ -17,39 +17,34 @@ Preferences.addAll([
 
 var gLanguagesDialog = {
 
   _availableLanguagesList: [],
   _acceptLanguages: { },
 
   _selectedItemID: null,
 
-  init() {
-    if (!this._availableLanguagesList.length)
-      this._loadAvailableLanguages();
-  },
+  onLoad() {
+    Preferences.get("intl.accept_languages").on("change",
+      () => this._readAcceptLanguages().catch(Cu.reportError));
 
-  // Ugly hack used to trigger extra reflow in order to work around XUL bug 1194844;
-  // see bug 1194346.
-  forceReflow() {
-    this._activeLanguages.style.fontKerning = "none";
-    setTimeout(() => {
-      this._activeLanguages.style.removeProperty("font-kerning");
-    }, 0);
+    if (!this._availableLanguagesList.length) {
+      document.mozSubdialogReady = this._loadAvailableLanguages();
+    }
   },
 
   get _activeLanguages() {
     return document.getElementById("activeLanguages");
   },
 
   get _availableLanguages() {
     return document.getElementById("availableLanguages");
   },
 
-  _loadAvailableLanguages() {
+  async _loadAvailableLanguages() {
     // This is a parser for: resource://gre/res/language.properties
     // The file is formatted like so:
     // ab[-cd].accept=true|false
     //  ab = language
     //  cd = region
     var bundleAccepted    = document.getElementById("bundleAccepted");
 
     function LocaleInfo(aLocaleName, aLocaleCode, aIsVisible) {
@@ -80,17 +75,18 @@ var gLanguagesDialog = {
     for (let i in localeCodes) {
       let isVisible = localeValues[i] == "true" &&
         (!(localeCodes[i] in this._acceptLanguages) || !this._acceptLanguages[localeCodes[i]]);
 
       let li = new LocaleInfo(localeNames[i], localeCodes[i], isVisible);
       this._availableLanguagesList.push(li);
     }
 
-    this._buildAvailableLanguageList();
+    await this._buildAvailableLanguageList();
+    await this._readAcceptLanguages();
   },
 
   async _buildAvailableLanguageList() {
     var availableLanguagesPopup = document.getElementById("availableLanguagesPopup");
     while (availableLanguagesPopup.hasChildNodes())
       availableLanguagesPopup.firstChild.remove();
 
     let frag = document.createDocumentFragment();
@@ -127,33 +123,35 @@ var gLanguagesDialog = {
     // Re-append items in the correct order:
     items.forEach(item => frag.appendChild(item));
 
     availableLanguagesPopup.appendChild(frag);
 
     this._availableLanguages.setAttribute("label", this._availableLanguages.getAttribute("placeholder"));
   },
 
-  async readAcceptLanguages() {
+  async _readAcceptLanguages() {
     while (this._activeLanguages.hasChildNodes())
       this._activeLanguages.firstChild.remove();
 
     var selectedIndex = 0;
     var preference = Preferences.get("intl.accept_languages");
     if (preference.value == "")
-      return undefined;
+      return;
     var languages = preference.value.toLowerCase().split(/\s*,\s*/);
     for (var i = 0; i < languages.length; ++i) {
-      var listitem = document.createElement("listitem");
+      var listitem = document.createElement("richlistitem");
+      var label = document.createElement("label");
+      listitem.appendChild(label);
       listitem.id = languages[i];
       if (languages[i] == this._selectedItemID)
         selectedIndex = i;
       this._activeLanguages.appendChild(listitem);
       var localeName = this._getLocaleName(languages[i]);
-      document.l10n.setAttributes(listitem, "languages-code-format", {
+      document.l10n.setAttributes(label, "languages-active-code-format", {
         locale: localeName,
         code: languages[i],
       });
 
       // Hash this language as an "Active" language so we don't
       // show it in the list that can be added.
       this._acceptLanguages[languages[i]] = true;
     }
@@ -166,22 +164,16 @@ var gLanguagesDialog = {
     if (this._activeLanguages.childNodes.length > 0) {
       this._activeLanguages.ensureIndexIsVisible(selectedIndex);
       this._activeLanguages.selectedIndex = selectedIndex;
     }
 
     // Update states of accept-language list and buttons according to
     // privacy.resistFingerprinting and privacy.spoof_english.
     this.readSpoofEnglish();
-
-    return undefined;
-  },
-
-  writeAcceptLanguages() {
-    return undefined;
   },
 
   onAvailableLanguageSelect() {
     var availableLanguages = this._availableLanguages;
     var addButton = document.getElementById("addButton");
     addButton.disabled = availableLanguages.disabled ||
                          availableLanguages.selectedIndex < 0;
 
@@ -205,17 +197,17 @@ var gLanguagesDialog = {
       arrayOfPrefs.unshift(selectedID);
       preference.value = arrayOfPrefs.join(",");
     }
 
     this._acceptLanguages[selectedID] = true;
     this._availableLanguages.selectedItem = null;
 
     // Rebuild the available list with the added item removed...
-    this._buildAvailableLanguageList();
+    this._buildAvailableLanguageList().catch(Cu.reportError);
   },
 
   removeLanguage() {
     // Build the new preference value string.
     var languagesArray = [];
     for (var i = 0; i < this._activeLanguages.childNodes.length; ++i) {
       var item = this._activeLanguages.childNodes[i];
       if (!item.selected)
@@ -232,17 +224,17 @@ var gLanguagesDialog = {
     selectItem = selectItem ? selectItem.id : null;
 
     this._selectedItemID = selectItem;
 
     // Update the preference and force a UI rebuild
     var preference = Preferences.get("intl.accept_languages");
     preference.value = string;
 
-    this._buildAvailableLanguageList();
+    this._buildAvailableLanguageList().catch(Cu.reportError);
   },
 
   _getLocaleName(localeCode) {
     if (!this._availableLanguagesList.length)
       this._loadAvailableLanguages();
     for (var i = 0; i < this._availableLanguagesList.length; ++i) {
       if (localeCode == this._availableLanguagesList[i].code)
         return this._availableLanguagesList[i].name;
@@ -345,13 +337,8 @@ var gLanguagesDialog = {
       return false;
     }
   },
 
   writeSpoofEnglish() {
     return document.getElementById("spoofEnglish").checked ? 2 : 1;
   }
 };
-
-// These focus and resize handlers hack around XUL bug 1194844
-// by triggering extra reflow (see bug 1194346).
-window.addEventListener("focus", () => gLanguagesDialog.forceReflow());
-window.addEventListener("resize", () => gLanguagesDialog.forceReflow());
--- a/browser/components/preferences/languages.xul
+++ b/browser/components/preferences/languages.xul
@@ -10,17 +10,17 @@
 
 <dialog id="LanguagesDialog" type="child" class="prefwindow"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
         data-l10n-id="languages-window"
         data-l10n-attrs="title, style"
         buttons="accept,cancel,help"
         persist="lastSelected screenX screenY"
         role="dialog"
-        onload="gLanguagesDialog.init();"
+        onload="gLanguagesDialog.onLoad();"
         helpTopic="prefs-languages"
         ondialoghelp="openPrefsHelp()">
 
   <link rel="localization" href="browser/preferences/languages.ftl"/>
   <script type="application/javascript" src="chrome://global/content/l10n.js"></script>
 
   <script type="application/javascript" src="chrome://browser/content/utilityOverlay.js"/>
   <script type="application/javascript" src="chrome://global/content/preferencesBindings.js"/>
@@ -44,21 +44,19 @@
               onsynctopreference="return gLanguagesDialog.writeSpoofEnglish();"/>
     <grid flex="1">
       <columns>
         <column flex="1"/>
         <column/>
       </columns>
       <rows>
         <row flex="1">
-          <listbox id="activeLanguages" flex="1" rows="6"
-                    seltype="multiple" onselect="gLanguagesDialog.onLanguageSelect();"
-                    preference="intl.accept_languages"
-                    onsyncfrompreference="return gLanguagesDialog.readAcceptLanguages();"
-                    onsynctopreference="return gLanguagesDialog.writeAcceptLanguages();"/>
+          <richlistbox id="activeLanguages" flex="1" height="200"
+                       seltype="multiple"
+                       onselect="gLanguagesDialog.onLanguageSelect();"/>
           <vbox>
             <button id="up" class="up" oncommand="gLanguagesDialog.moveUp();" disabled="true"
                     data-l10n-id="languages-customize-moveup"
                     preference="pref.browser.language.disable_button.up"/>
             <button id="down" class="down" oncommand="gLanguagesDialog.moveDown();" disabled="true"
                     data-l10n-id="languages-customize-movedown"
                     preference="pref.browser.language.disable_button.down"/>
             <button id="remove" oncommand="gLanguagesDialog.removeLanguage();" disabled="true"
--- a/browser/locales/en-US/browser/preferences/languages.ftl
+++ b/browser/locales/en-US/browser/preferences/languages.ftl
@@ -40,8 +40,11 @@ languages-customize-add =
 #   Icelandic [is]
 #   Spanish (Chile) [es-CL]
 #
 # Variables:
 #   $locale (String) - A name of the locale (for example: "Icelandic", "Spanish (Chile)")
 #   $code (String) - Locale code of the locale (for example: "is", "es-CL")
 languages-code-format =
     .label = { $locale } [{ $code }]
+
+languages-active-code-format =
+    .value = { languages-code-format.label }
--- a/browser/themes/shared/incontentprefs/preferences.inc.css
+++ b/browser/themes/shared/incontentprefs/preferences.inc.css
@@ -204,16 +204,20 @@ button > hbox > label {
   width: 30px;
   margin-inline-start: 5px;
 }
 
 #getStarted {
   font-size: 90%;
 }
 
+#activeLanguages > richlistitem {
+  padding: 0.3em;
+}
+
 #downloadFolder {
   margin-inline-start: 0;
   padding-inline-start: 30px;
   background-repeat: no-repeat;
   background-size: 16px;
   background-position: center left 8px;
 }
 
--- a/toolkit/content/preferencesBindings.js
+++ b/toolkit/content/preferencesBindings.js
@@ -434,17 +434,16 @@ const Preferences = window.Preferences =
     }
 
     isElementEditable(aElement) {
       switch (aElement.localName) {
       case "checkbox":
       case "colorpicker":
       case "radiogroup":
       case "textbox":
-      case "listbox":
       case "menulist":
         return true;
       }
       return false;
     }
 
     updateElements() {
       if (!this.id)
--- a/toolkit/themes/shared/in-content/common.inc.css
+++ b/toolkit/themes/shared/in-content/common.inc.css
@@ -222,25 +222,20 @@ xul|menulist[open="true"]:not([disabled=
   background-color: var(--in-content-box-background-active);
 }
 
 html|button:disabled,
 html|select:disabled,
 html|*.numberbox-input:disabled::-moz-number-spin-box,
 xul|button[disabled="true"],
 xul|colorpicker[type="button"][disabled="true"],
-xul|menulist[disabled="true"],
-xul|listbox[disabled="true"] {
+xul|menulist[disabled="true"] {
   opacity: 0.5;
 }
 
-xul|listbox[disabled="true"] xul|listitem:hover {
-  background-color: transparent;
-}
-
 *|button.primary {
   background-color: var(--in-content-primary-button-background);
   border-color: transparent;
   color: var(--in-content-selected-text);
 }
 
 html|button.primary:enabled:hover,
 xul|button.primary:not([disabled="true"]):hover {
@@ -744,44 +739,40 @@ xul|*.radio-label-box {
   line-height: 1.3em;
   margin: 0;
   -moz-user-select: none;
 }
 
 /* List boxes */
 
 html|select[size][multiple],
-xul|richlistbox,
-xul|listbox {
+xul|richlistbox {
   -moz-appearance: none;
   margin-inline-start: 0;
   background-color: var(--in-content-box-background);
   border: 1px solid var(--in-content-box-border-color);
   border-radius: 2px;
   color: var(--in-content-text-color);
 }
 
 html|select[size][multiple] > html|option,
-xul|treechildren::-moz-tree-row,
-xul|listbox xul|listitem {
+xul|treechildren::-moz-tree-row {
   padding: 0.3em;
   margin: 0;
   border: none;
   border-radius: 0;
   background-image: none;
 }
 
 html|select[size][multiple] > html|option:hover,
-xul|treechildren::-moz-tree-row(hover),
-xul|listbox xul|listitem:hover {
+xul|treechildren::-moz-tree-row(hover) {
   background-color: var(--in-content-item-hover);
 }
 
-xul|treechildren::-moz-tree-row(selected),
-xul|listbox xul|listitem[selected="true"] {
+xul|treechildren::-moz-tree-row(selected) {
   background-color: var(--in-content-item-selected);
   color: var(--in-content-selected-text);
 }
 
 /* Trees */
 
 xul|tree {
   -moz-appearance: none;
--- a/toolkit/themes/windows/global/in-content/common.css
+++ b/toolkit/themes/windows/global/in-content/common.css
@@ -49,18 +49,17 @@ html|input[type="checkbox"]:-moz-focusri
 *|button.primary:focus {
   outline: 1px dotted;
   outline-offset: -3px;
 }
 
 /* Use a 2px border so that selected row highlight is still visible behind
     an existing high-contrast border that uses the background color */
 @media (-moz-windows-default-theme: 0) {
-  xul|treechildren::-moz-tree-row(selected),
-  xul|listbox xul|listitem[selected="true"] {
+  xul|treechildren::-moz-tree-row(selected) {
      border: 2px dotted Highlight;
   }
 }
 
 html|button {
   /* XUL button min-width */
   min-width: 6.3em;
 }