toolkit/modules/CharsetMenu.jsm

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
     3  * You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 this.EXPORTED_SYMBOLS = [ "CharsetMenu" ];
     7 const { classes: Cc, interfaces: Ci, utils: Cu} = Components;
     9 Cu.import("resource://gre/modules/Services.jsm");
    10 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
    11 XPCOMUtils.defineLazyGetter(this, "gBundle", function() {
    12   const kUrl = "chrome://global/locale/charsetMenu.properties";
    13   return Services.strings.createBundle(kUrl);
    14 });
    16 const kAutoDetectors = [
    17   ["off", ""],
    18   ["ja", "ja_parallel_state_machine"],
    19   ["ru", "ruprob"],
    20   ["uk", "ukprob"]
    21 ];
    23 /**
    24  * This set contains encodings that are in the Encoding Standard, except:
    25  *  - XSS-dangerous encodings (except ISO-2022-JP which is assumed to be
    26  *    too common not to be included).
    27  *  - x-user-defined, which practically never makes sense as an end-user-chosen
    28  *    override.
    29  *  - Encodings that IE11 doesn't have in its correspoding menu.
    30  */
    31 const kEncodings = new Set([
    32   // Globally relevant
    33   "UTF-8",
    34   "windows-1252",
    35   // Arabic
    36   "windows-1256",
    37   "ISO-8859-6",
    38   // Baltic
    39   "windows-1257",
    40   "ISO-8859-4",
    41   // "ISO-8859-13", // Hidden since not in menu in IE11
    42   // Central European
    43   "windows-1250",
    44   "ISO-8859-2",
    45   // Chinese, Simplified
    46   "gbk",
    47   // Chinese, Traditional
    48   "Big5",
    49   // Cyrillic
    50   "windows-1251",
    51   "ISO-8859-5",
    52   "KOI8-R",
    53   "KOI8-U",
    54   "IBM866", // Not in menu in Chromium. Maybe drop this?
    55   // "x-mac-cyrillic", // Not in menu in IE11 or Chromium.
    56   // Greek
    57   "windows-1253",
    58   "ISO-8859-7",
    59   // Hebrew
    60   "windows-1255",
    61   "ISO-8859-8",
    62   // Japanese
    63   "Shift_JIS",
    64   "EUC-JP",
    65   "ISO-2022-JP",
    66   // Korean
    67   "EUC-KR",
    68   // Thai
    69   "windows-874",
    70   // Turkish
    71   "windows-1254",
    72   // Vietnamese
    73   "windows-1258",
    74   // Hiding rare European encodings that aren't in the menu in IE11 and would
    75   // make the menu messy by sorting all over the place
    76   // "ISO-8859-3",
    77   // "ISO-8859-10",
    78   // "ISO-8859-14",
    79   // "ISO-8859-15",
    80   // "ISO-8859-16",
    81   // "macintosh"
    82 ]);
    84 // Always at the start of the menu, in this order, followed by a separator.
    85 const kPinned = [
    86   "UTF-8",
    87   "windows-1252"
    88 ];
    90 kPinned.forEach(x => kEncodings.delete(x));
    92 function CharsetComparator(a, b) {
    93   // Normal sorting sorts the part in parenthesis in an order that
    94   // happens to make the less frequently-used items first.
    95   let titleA = a.label.replace(/\(.*/, "") + b.value;
    96   let titleB = b.label.replace(/\(.*/, "") + a.value;
    97   // Secondarily reverse sort by encoding name to sort "windows" or
    98   // "shift_jis" first.
    99   return titleA.localeCompare(titleB) || b.value.localeCompare(a.value);
   100 }
   102 function SetDetector(event) {
   103   let str = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString);
   104   str.data = event.target.getAttribute("detector");
   105   Services.prefs.setComplexValue("intl.charset.detector", Ci.nsISupportsString, str);
   106 }
   108 function UpdateDetectorMenu(event) {
   109   event.stopPropagation();
   110   let detector = Services.prefs.getComplexValue("intl.charset.detector", Ci.nsIPrefLocalizedString);
   111   let menuitem = this.getElementsByAttribute("detector", detector).item(0);
   112   if (menuitem) {
   113     menuitem.setAttribute("checked", "true");
   114   }
   115 }
   117 let gDetectorInfoCache, gCharsetInfoCache, gPinnedInfoCache;
   119 let CharsetMenu = {
   120   build: function(parent, showAccessKeys=true, showDetector=true) {
   121     function createDOMNode(doc, nodeInfo) {
   122       let node = doc.createElement("menuitem");
   123       node.setAttribute("type", "radio");
   124       node.setAttribute("name", nodeInfo.name + "Group");
   125       node.setAttribute(nodeInfo.name, nodeInfo.value);
   126       node.setAttribute("label", nodeInfo.label);
   127       if (showAccessKeys && nodeInfo.accesskey) {
   128         node.setAttribute("accesskey", nodeInfo.accesskey);
   129       }
   130       return node;
   131     }
   133     if (parent.hasChildNodes()) {
   134       // Detector menu or charset menu already built
   135       return;
   136     }
   137     this._ensureDataReady();
   138     let doc = parent.ownerDocument;
   140     if (showDetector) {
   141       let menuNode = doc.createElement("menu");
   142       menuNode.setAttribute("label", gBundle.GetStringFromName("charsetMenuAutodet"));
   143       if (showAccessKeys) {
   144         menuNode.setAttribute("accesskey", gBundle.GetStringFromName("charsetMenuAutodet.key"));
   145       }
   146       parent.appendChild(menuNode);
   148       let menuPopupNode = doc.createElement("menupopup");
   149       menuNode.appendChild(menuPopupNode);
   150       menuPopupNode.addEventListener("command", SetDetector);
   151       menuPopupNode.addEventListener("popupshown", UpdateDetectorMenu);
   153       gDetectorInfoCache.forEach(detectorInfo => menuPopupNode.appendChild(createDOMNode(doc, detectorInfo)));
   154       parent.appendChild(doc.createElement("menuseparator"));
   155     }
   157     gPinnedInfoCache.forEach(charsetInfo => parent.appendChild(createDOMNode(doc, charsetInfo)));
   158     parent.appendChild(doc.createElement("menuseparator"));
   159     gCharsetInfoCache.forEach(charsetInfo => parent.appendChild(createDOMNode(doc, charsetInfo)));
   160   },
   162   getData: function() {
   163     this._ensureDataReady();
   164     return {
   165       detectors: gDetectorInfoCache,
   166       pinnedCharsets: gPinnedInfoCache,
   167       otherCharsets: gCharsetInfoCache
   168     };
   169   },
   171   _ensureDataReady: function() {
   172     if (!gDetectorInfoCache) {
   173       gDetectorInfoCache = this.getDetectorInfo();
   174       gPinnedInfoCache = this.getCharsetInfo(kPinned, false);
   175       gCharsetInfoCache = this.getCharsetInfo(kEncodings);
   176     }
   177   },
   179   getDetectorInfo: function() {
   180     return kAutoDetectors.map(([detectorName, nodeId]) => ({
   181       label: this._getDetectorLabel(detectorName),
   182       accesskey: this._getDetectorAccesskey(detectorName),
   183       name: "detector",
   184       value: nodeId
   185     }));
   186   },
   188   getCharsetInfo: function(charsets, sort=true) {
   189     let list = [{
   190       label: this._getCharsetLabel(charset),
   191       accesskey: this._getCharsetAccessKey(charset),
   192       name: "charset",
   193       value: charset
   194     } for (charset of charsets)];
   196     if (sort) {
   197       list.sort(CharsetComparator);
   198     }
   199     return list;
   200   },
   202   _getDetectorLabel: function(detector) {
   203     try {
   204       return gBundle.GetStringFromName("charsetMenuAutodet." + detector);
   205     } catch (ex) {}
   206     return detector;
   207   },
   208   _getDetectorAccesskey: function(detector) {
   209     try {
   210       return gBundle.GetStringFromName("charsetMenuAutodet." + detector + ".key");
   211     } catch (ex) {}
   212     return "";
   213   },
   215   _getCharsetLabel: function(charset) {
   216     if (charset == "gbk") {
   217       // Localization key has been revised
   218       charset = "gbk.bis";
   219     }
   220     try {
   221       return gBundle.GetStringFromName(charset);
   222     } catch (ex) {}
   223     return charset;
   224   },
   225   _getCharsetAccessKey: function(charset) {
   226     if (charset == "gbk") {
   227       // Localization key has been revised
   228       charset = "gbk.bis";
   229     }
   230     try {
   231       return gBundle.GetStringFromName(charset + ".key");
   232     } catch (ex) {}
   233     return "";
   234   },
   236   /**
   237    * For substantially similar encodings, treat two encodings as the same
   238    * for the purpose of the check mark.
   239    */
   240   foldCharset: function(charset) {
   241     switch (charset) {
   242       case "ISO-8859-8-I":
   243         return "windows-1255";
   245       case "gb18030":
   246         return "gbk";
   248       default:
   249         return charset;
   250     }
   251   },
   253   update: function(parent, charset) {
   254     let menuitem = parent.getElementsByAttribute("charset", this.foldCharset(charset)).item(0);
   255     if (menuitem) {
   256       menuitem.setAttribute("checked", "true");
   257     }
   258   },
   259 };
   261 Object.freeze(CharsetMenu);

mercurial