Bug 936442 - part 1: refactor CharsetMenu to allow Australis code to use it, r=Unfocused
authorGijs Kruitbosch <gijskruitbosch@gmail.com>
Mon, 27 Jan 2014 11:56:04 +0000
changeset 181871 33f826ee0dfc4b6c2f01901296916bf8e4727517
parent 181870 7e243174acf5de1efe6ea4f1f3d07b3ced7f5eaf
child 181872 45a117e6d2546ce01884bd6279eff9f2ca222c45
push id3343
push userffxbld
push dateMon, 17 Mar 2014 21:55:32 +0000
treeherdermozilla-beta@2f7d3415f79f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersUnfocused
bugs936442
milestone29.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 936442 - part 1: refactor CharsetMenu to allow Australis code to use it, r=Unfocused
browser/base/content/browser-charsetmenu.inc
browser/components/customizableui/content/panelUI.inc.xul
browser/components/customizableui/src/CustomizableWidgets.jsm
toolkit/components/viewsource/content/viewSource.xul
toolkit/locales/en-US/chrome/global/charsetMenu.dtd
toolkit/locales/en-US/chrome/global/charsetMenu.properties
toolkit/modules/CharsetMenu.jsm
--- a/browser/base/content/browser-charsetmenu.inc
+++ b/browser/base/content/browser-charsetmenu.inc
@@ -6,57 +6,16 @@
 
 #expand <menu id="__ID_PREFIX__charsetMenu"
     label="&charsetMenu.label;"
 #ifndef OMIT_ACCESSKEYS
     accesskey="&charsetMenu.accesskey;"
 #endif
     oncommand="MultiplexHandler(event)"
 #ifdef OMIT_ACCESSKEYS
-#expand    onpopupshowing="CharsetMenu.build(event, '__ID_PREFIX__', false);"
+#expand    onpopupshowing="CharsetMenu.build(event.target, '__ID_PREFIX__', false);"
 #else
-#expand    onpopupshowing="CharsetMenu.build(event, '__ID_PREFIX__');"
+#expand    onpopupshowing="CharsetMenu.build(event.target, '__ID_PREFIX__');"
 #endif
     onpopupshown="UpdateMenus(event);">
   <menupopup>
-    <menu label="&charsetMenuAutodet.label;"
-#ifndef OMIT_ACCESSKEYS
-          accesskey="&charsetMenuAutodet.accesskey;"
-#endif
-        >
-      <menupopup>
-        <menuitem type="radio"
-                  name="detectorGroup"
-#expand           id="__ID_PREFIX__chardet.off"
-                  label="&charsetMenuAutodet.off.label;"
-#ifndef OMIT_ACCESSKEYS
-                  accesskey="&charsetMenuAutodet.off.accesskey;"
-#endif
-                  />
-        <menuitem type="radio"
-                  name="detectorGroup"
-#expand           id="__ID_PREFIX__chardet.ja_parallel_state_machine"
-                  label="&charsetMenuAutodet.ja.label;"
-#ifndef OMIT_ACCESSKEYS
-                  accesskey="&charsetMenuAutodet.ja.accesskey;"
-#endif
-                  />
-        <menuitem type="radio"
-                  name="detectorGroup"
-#expand           id="__ID_PREFIX__chardet.ruprob"
-                  label="&charsetMenuAutodet.ru.label;"
-#ifndef OMIT_ACCESSKEYS
-                  accesskey="&charsetMenuAutodet.ru.accesskey;"
-#endif
-                  />
-        <menuitem type="radio"
-                  name="detectorGroup"
-#expand           id="__ID_PREFIX__chardet.ukprob"
-                  label="&charsetMenuAutodet.uk.label;"
-#ifndef OMIT_ACCESSKEYS
-                  accesskey="&charsetMenuAutodet.uk.accesskey;"
-#endif
-                  />
-      </menupopup>
-    </menu>
-    <menuseparator/>
   </menupopup>
 </menu>
--- a/browser/components/customizableui/content/panelUI.inc.xul
+++ b/browser/components/customizableui/content/panelUI.inc.xul
@@ -145,17 +145,17 @@
     </panelview>
 
     <panelview id="PanelUI-characterEncodingView" flex="1">
       <label value="&charsetMenu.label;" class="panel-subview-header"/>
 
       <vbox id="PanelUI-characterEncodingView-customlist"
             class="PanelUI-characterEncodingView-list"/>
       <vbox>
-        <label value="&charsetMenuAutodet.label;"/>
+        <label id="PanelUI-characterEncodingView-autodetect-label"/>
         <vbox id="PanelUI-characterEncodingView-autodetect"
               class="PanelUI-characterEncodingView-list"/>
       </vbox>
     </panelview>
 
   </panelmultiview>
   <!-- These menupopups are located here to prevent flickering,
        see bug 492960 comment 20. -->
--- a/browser/components/customizableui/src/CustomizableWidgets.jsm
+++ b/browser/components/customizableui/src/CustomizableWidgets.jsm
@@ -16,16 +16,21 @@ XPCOMUtils.defineLazyModuleGetter(this, 
   "resource:///modules/PlacesUIUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "RecentlyClosedTabsAndWindowsMenuUtils",
   "resource:///modules/sessionstore/RecentlyClosedTabsAndWindowsMenuUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "ShortcutUtils",
   "resource://gre/modules/ShortcutUtils.jsm");
 XPCOMUtils.defineLazyServiceGetter(this, "CharsetManager",
                                    "@mozilla.org/charset-converter-manager;1",
                                    "nsICharsetConverterManager");
+
+XPCOMUtils.defineLazyGetter(this, "CharsetBundle", function() {
+  const kCharsetBundle = "chrome://global/locale/charsetMenu.properties";
+  return Services.strings.createBundle(kCharsetBundle);
+});
 XPCOMUtils.defineLazyGetter(this, "BrandBundle", function() {
   const kBrandBundle = "chrome://branding/locale/brand.properties";
   return Services.strings.createBundle(kBrandBundle);
 });
 
 const kNSXUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 const kPrefCustomizationDebug = "browser.uiCustomization.debug";
 const kWidePanelItemClass = "panel-wide-item";
@@ -760,16 +765,21 @@ const CustomizableWidgets = [{
           elem.setAttribute("disabled", "true");
         elem.setAttribute("class", "subviewbutton");
         containerElem.appendChild(elem);
       }
     },
     onViewShowing: function(aEvent) {
       let document = aEvent.target.ownerDocument;
 
+      let autoDetectLabelId = "PanelUI-characterEncodingView-autodetect-label";
+      let autoDetectLabel = document.getElementById(autoDetectLabelId);
+      let label = CharsetBundle.GetStringFromName("charsetMenuAutodet");
+      autoDetectLabel.setAttribute("value", label);
+
       this.populateList(document,
                         "PanelUI-characterEncodingView-customlist",
                         "browser");
       this.populateList(document,
                         "PanelUI-characterEncodingView-autodetect",
                         "autodetect");
     },
     onCommand: function(aEvent) {
--- a/toolkit/components/viewsource/content/viewSource.xul
+++ b/toolkit/components/viewsource/content/viewSource.xul
@@ -197,45 +197,19 @@
             </menupopup>
           </menu>
 
           <!-- Charset Menu -->
           <menu id="charsetMenu"
                 label="&charsetMenu.label;"
                 accesskey="&charsetMenu.accesskey;"
                 oncommand="MultiplexHandler(event);"
-                onpopupshowing="CharsetMenu.build(event);"
+                onpopupshowing="CharsetMenu.build(event.target);"
                 onpopupshown="UpdateMenus();">
             <menupopup>
-              <menu label="&charsetMenuAutodet.label;"
-                    accesskey="&charsetMenuAutodet.accesskey;">
-                <menupopup>
-                  <menuitem type="radio"
-                            name="detectorGroup"
-                            id="chardet.off"
-                            label="&charsetMenuAutodet.off.label;"
-                            accesskey="&charsetMenuAutodet.off.accesskey;"/>
-                  <menuitem type="radio"
-                            name="detectorGroup"
-                            id="chardet.ja_parallel_state_machine"
-                            label="&charsetMenuAutodet.ja.label;"
-                            accesskey="&charsetMenuAutodet.ja.accesskey;"/>
-                  <menuitem type="radio"
-                            name="detectorGroup"
-                            id="chardet.ruprob"
-                            label="&charsetMenuAutodet.ru.label;"
-                            accesskey="&charsetMenuAutodet.ru.accesskey;"/>
-                  <menuitem type="radio"
-                            name="detectorGroup"
-                            id="chardet.ukprob"
-                            label="&charsetMenuAutodet.uk.label;"
-                            accesskey="&charsetMenuAutodet.uk.accesskey;"/>
-                </menupopup>
-              </menu>
-              <menuseparator/>
             </menupopup>
           </menu>
           <menuseparator/>
           <menuitem id="menu_wrapLongLines" type="checkbox" command="cmd_wrapLongLines"
                     label="&menu_wrapLongLines.title;" accesskey="&menu_wrapLongLines.accesskey;"/>
           <menuitem type="checkbox" id="menu_highlightSyntax" command="cmd_highlightSyntax"
                     label="&menu_highlightSyntax.label;" accesskey="&menu_highlightSyntax.accesskey;"/>
         </menupopup>
--- a/toolkit/locales/en-US/chrome/global/charsetMenu.dtd
+++ b/toolkit/locales/en-US/chrome/global/charsetMenu.dtd
@@ -1,18 +1,6 @@
 <!-- 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/. -->
 
 <!ENTITY charsetMenu.label             "Character Encoding">
 <!ENTITY charsetMenu.accesskey         "C">
-<!ENTITY charsetMenuAutodet.label      "Auto-Detect">
-<!ENTITY charsetMenuAutodet.accesskey  "D"><!-- A reserved for Arabic -->
-
-<!ENTITY charsetMenuAutodet.off.label     "(off)">
-<!ENTITY charsetMenuAutodet.off.accesskey "o">
-<!ENTITY charsetMenuAutodet.ja.label      "Japanese">
-<!ENTITY charsetMenuAutodet.ja.accesskey  "J">
-<!ENTITY charsetMenuAutodet.ru.label      "Russian">
-<!ENTITY charsetMenuAutodet.ru.accesskey  "R">
-<!ENTITY charsetMenuAutodet.uk.label      "Ukrainian">
-<!ENTITY charsetMenuAutodet.uk.accesskey  "U">
-
--- a/toolkit/locales/en-US/chrome/global/charsetMenu.properties
+++ b/toolkit/locales/en-US/chrome/global/charsetMenu.properties
@@ -19,16 +19,30 @@
 # For the items whose property key does not end in ".key" and whose value
 # includes "(" U+0028 LEFT PARENTHESIS, the "(" character is significant for
 # processing by CharsetMenu.jsm. If your localization does not use ASCII
 # parentheses where en-US does in this file, please file a bug to make
 # CharsetMenu.jsm also recognize the delimiter your localization uses.
 # (When this code was developed, all localizations appeared to use
 # U+0028 LEFT PARENTHESIS for this purpose.)
 
+# Auto-Detect (sub)menu
+charsetMenuCharsets = Character Encoding
+charsetMenuAutodet = Auto-Detect
+# 'A' is reserved for Arabic:
+charsetMenuAutodet.key = D
+charsetMenuAutodet.off = (off)
+charsetMenuAutodet.off.key = o
+charsetMenuAutodet.ja = Japanese
+charsetMenuAutodet.ja.key = J
+charsetMenuAutodet.ru = Russian
+charsetMenuAutodet.ru.key = R
+charsetMenuAutodet.uk = Ukrainian
+charsetMenuAutodet.uk.key = U
+
 # Globally-relevant
 UTF-8.key        = U
 UTF-8            = Unicode
 windows-1252.key = W
 windows-1252     = Western
 
 # Arabic
 windows-1256.key = A
--- a/toolkit/modules/CharsetMenu.jsm
+++ b/toolkit/modules/CharsetMenu.jsm
@@ -7,16 +7,24 @@ this.EXPORTED_SYMBOLS = [ "CharsetMenu" 
 const { classes: Cc, interfaces: Ci, utils: Cu} = Components;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 XPCOMUtils.defineLazyGetter(this, "gBundle", function() {
   const kUrl = "chrome://global/locale/charsetMenu.properties";
   return Services.strings.createBundle(kUrl);
 });
+
+const kAutoDetectors = [
+  ["off", "off"],
+  ["ja", "ja_parallel_state_machine"],
+  ["ru", "ruprob"],
+  ["uk", "ukprob"]
+];
+
 /**
  * This set contains encodings that are in the Encoding Standard, except:
  *  - XSS-dangerous encodings (except ISO-2022-JP which is assumed to be
  *    too common not to be included).
  *  - x-user-defined, which practically never makes sense as an end-user-chosen
  *    override.
  *  - Encodings that IE11 doesn't have in its correspoding menu.
  */
@@ -75,87 +83,152 @@ const kEncodings = new Set([
 ]);
 
 // Always at the start of the menu, in this order, followed by a separator.
 const kPinned = [
   "UTF-8",
   "windows-1252"
 ];
 
-this.CharsetMenu = Object.freeze({
-  build: function BuildCharsetMenu(event, idPrefix="", showAccessKeys=true) {
-    let parent = event.target;
-    if (parent.lastChild.localName != "menuseparator") {
+kPinned.forEach(x => kEncodings.delete(x));
+
+
+let gDetectorInfoCache, gCharsetInfoCache, gPinnedInfoCache;
+
+let CharsetMenu = {
+  build: function(parent, idPrefix="", showAccessKeys=true) {
+    function createDOMNode(doc, nodeInfo) {
+      let node = doc.createElement("menuitem");
+      node.setAttribute("type", "radio");
+      node.setAttribute("name", nodeInfo.name);
+      node.setAttribute("label", nodeInfo.label);
+      if (showAccessKeys && nodeInfo.accesskey) {
+        node.setAttribute("accesskey", nodeInfo.accesskey);
+      }
+      if (idPrefix) {
+        node.id = idPrefix + nodeInfo.id;
+      } else {
+        node.id = nodeInfo.id;
+      }
+      return node;
+    }
+
+    if (parent.childElementCount > 0) {
       // Detector menu or charset menu already built
       return;
     }
     let doc = parent.ownerDocument;
 
-    function createItem(encoding) {
-      let menuItem = doc.createElement("menuitem");
-      menuItem.setAttribute("type", "radio");
-      menuItem.setAttribute("name", "charsetGroup");
-      try {
-        menuItem.setAttribute("label", gBundle.GetStringFromName(encoding));
-      } catch (e) {
-        // Localization error but put *something* in the menu to recover.
-        menuItem.setAttribute("label", encoding);
-      }
-      if (showAccessKeys) {
-        try {
-          menuItem.setAttribute("accesskey",
-                                gBundle.GetStringFromName(encoding + ".key"));
-        } catch (e) {
-          // Some items intentionally don't have an accesskey
-        }
-      }
-      menuItem.setAttribute("id", idPrefix + "charset." + encoding);
-      return menuItem;
+    let menuNode = doc.createElement("menu");
+    menuNode.setAttribute("label", gBundle.GetStringFromName("charsetMenuAutodet"));
+    if (showAccessKeys) {
+      menuNode.setAttribute("accesskey", gBundle.GetStringFromName("charsetMenuAutodet.key"));
     }
+    parent.appendChild(menuNode);
 
-    // Clone the set in order to be able to remove the pinned encodings from
-    // the cloned set.
-    let encodings = new Set(kEncodings);
-    for (let encoding of kPinned) {
-      encodings.delete(encoding);
-      parent.appendChild(createItem(encoding));
+    let menuPopupNode = doc.createElement("menupopup");
+    menuNode.appendChild(menuPopupNode);
+
+    this._ensureDataReady();
+    gDetectorInfoCache.forEach(detectorInfo => menuPopupNode.appendChild(createDOMNode(doc, detectorInfo)));
+    parent.appendChild(doc.createElement("menuseparator"));
+    gPinnedInfoCache.forEach(charsetInfo => parent.appendChild(createDOMNode(doc, charsetInfo)));
+    parent.appendChild(doc.createElement("menuseparator"));
+    gCharsetInfoCache.forEach(charsetInfo => parent.appendChild(createDOMNode(doc, charsetInfo)));
+  },
+
+  getData: function() {
+    this._ensureDataReady();
+    return {
+      detectors: gDetectorInfoCache,
+      pinnedCharsets: gPinnedInfoCache,
+      otherCharsets: gCharsetInfoCache
+    };
+  },
+
+  _ensureDataReady: function() {
+    if (!gDetectorInfoCache) {
+      gDetectorInfoCache = this.getDetectorInfo();
+      gPinnedInfoCache = this.getCharsetInfo(kPinned, false);
+      gCharsetInfoCache = this.getCharsetInfo([...kEncodings]);
     }
-    parent.appendChild(doc.createElement("menuseparator"));
-    let list = [];
-    for (let encoding of encodings) {
-      list.push(createItem(encoding));
+  },
+
+  getDetectorInfo: function() {
+    return kAutoDetectors.map(([detectorName, nodeId]) => ({
+      id: "chardet." + nodeId,
+      label: this._getDetectorLabel(detectorName),
+      accesskey: this._getDetectorAccesskey(detectorName),
+      name: "detectorGroup",
+    }));
+  },
+
+  getCharsetInfo: function(charsets, sort=true) {
+    let list = charsets.map(charset => ({
+      id: "charset." + charset,
+      label: this._getCharsetLabel(charset),
+      accesskey: this._getCharsetAccessKey(charset),
+      name: "charsetGroup",
+    }));
+
+    if (!sort) {
+      return list;
     }
 
     list.sort(function (a, b) {
-      let titleA = a.getAttribute("label");
-      let titleB = b.getAttribute("label");
+      let titleA = a.label;
+      let titleB = b.label;
       // Normal sorting sorts the part in parenthesis in an order that
       // happens to make the less frequently-used items first.
       let index;
       if ((index = titleA.indexOf("(")) > -1) {
         titleA = titleA.substring(0, index);
       }
       if ((index = titleB.indexOf("(")) > -1) {
         titleA = titleB.substring(0, index);
       }
       let comp = titleA.localeCompare(titleB);
       if (comp) {
         return comp;
       }
       // secondarily reverse sort by encoding name to sort "windows" or
       // "shift_jis" first. This works regardless of localization, because
       // the ids aren't localized.
-      let idA = a.getAttribute("id");
-      let idB = b.getAttribute("id");
-      if (idA < idB) {
+      if (a.id < b.id) {
         return 1;
       }
-      if (idB < idA) {
+      if (b.id < a.id) {
         return -1;
       }
       return 0;
     });
+    return list;
+  },
 
-    for (let item of list) {
-      parent.appendChild(item);
-    }
+  _getDetectorLabel: function(detector) {
+    try {
+      return gBundle.GetStringFromName("charsetMenuAutodet." + detector);
+    } catch (ex) {}
+    return detector;
+  },
+  _getDetectorAccesskey: function(detector) {
+    try {
+      return gBundle.GetStringFromName("charsetMenuAutodet." + detector + ".key");
+    } catch (ex) {}
+    return "";
   },
-});
+
+  _getCharsetLabel: function(charset) {
+    try {
+      return gBundle.GetStringFromName(charset);
+    } catch (ex) {}
+    return charset;
+  },
+  _getCharsetAccessKey: function(charset) {
+    try {
+      accesskey = gBundle.GetStringFromName(charset + ".key");
+    } catch (ex) {}
+    return "";
+  },
+};
+
+Object.freeze(CharsetMenu);
+