toolkit/components/viewconfig/content/config.js

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 // -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
     3 /* This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 Components.utils.import("resource://gre/modules/Services.jsm");
     9 const nsIPrefLocalizedString = Components.interfaces.nsIPrefLocalizedString;
    10 const nsISupportsString = Components.interfaces.nsISupportsString;
    11 const nsIPrefBranch = Components.interfaces.nsIPrefBranch;
    12 const nsIClipboardHelper = Components.interfaces.nsIClipboardHelper;
    13 const nsIAtomService = Components.interfaces.nsIAtomService;
    15 const nsSupportsString_CONTRACTID = "@mozilla.org/supports-string;1";
    16 const nsPrompt_CONTRACTID = "@mozilla.org/embedcomp/prompt-service;1";
    17 const nsPrefService_CONTRACTID = "@mozilla.org/preferences-service;1";
    18 const nsClipboardHelper_CONTRACTID = "@mozilla.org/widget/clipboardhelper;1";
    19 const nsAtomService_CONTRACTID = "@mozilla.org/atom-service;1";
    21 const gPrefBranch = Services.prefs;
    22 const gClipboardHelper = Components.classes[nsClipboardHelper_CONTRACTID].getService(nsIClipboardHelper);
    23 const gAtomService = Components.classes[nsAtomService_CONTRACTID].getService(nsIAtomService);
    25 var gLockProps = ["default", "user", "locked"];
    26 // we get these from a string bundle
    27 var gLockStrs = [];
    28 var gTypeStrs = [];
    30 const PREF_IS_DEFAULT_VALUE = 0;
    31 const PREF_IS_USER_SET = 1;
    32 const PREF_IS_LOCKED = 2;
    34 var gPrefHash = {};
    35 var gPrefArray = [];
    36 var gPrefView = gPrefArray; // share the JS array
    37 var gSortedColumn = "prefCol";
    38 var gSortFunction = null;
    39 var gSortDirection = 1; // 1 is ascending; -1 is descending
    40 var gConfigBundle = null;
    41 var gFilter = null;
    43 var view = {
    44   get rowCount() { return gPrefView.length; },
    45   getCellText : function(index, col) {
    46     if (!(index in gPrefView))
    47       return "";
    49     var value = gPrefView[index][col.id];
    51     switch (col.id) {
    52       case "lockCol":           
    53         return gLockStrs[value];
    54       case "typeCol":
    55         return gTypeStrs[value];
    56       default:
    57         return value;
    58     }
    59   },
    60   getRowProperties : function(index) { return ""; },
    61   getCellProperties : function(index, col) {
    62     if (index in gPrefView)
    63       return gLockProps[gPrefView[index].lockCol];
    65     return "";
    66   },
    67   getColumnProperties : function(col) { return ""; },
    68   treebox : null,
    69   selection : null,
    70   isContainer : function(index) { return false; },
    71   isContainerOpen : function(index) { return false; },
    72   isContainerEmpty : function(index) { return false; },
    73   isSorted : function() { return true; },
    74   canDrop : function(index, orientation) { return false; },
    75   drop : function(row, orientation) {},
    76   setTree : function(out) { this.treebox = out; },
    77   getParentIndex: function(rowIndex) { return -1; },
    78   hasNextSibling: function(rowIndex, afterIndex) { return false; },
    79   getLevel: function(index) { return 1; },
    80   getImageSrc: function(row, col) { return ""; },
    81   toggleOpenState : function(index) {},
    82   cycleHeader: function(col) {
    83     var index = this.selection.currentIndex;
    84     if (col.id == gSortedColumn) {
    85       gSortDirection = -gSortDirection;
    86       gPrefArray.reverse();
    87       if (gPrefView != gPrefArray)
    88         gPrefView.reverse();
    89       if (index >= 0)
    90         index = gPrefView.length - index - 1;
    91     }
    92     else {
    93       var pref = null;
    94       if (index >= 0)
    95         pref = gPrefView[index];
    97       var old = document.getElementById(gSortedColumn);
    98       old.setAttribute("sortDirection", "");
    99       gPrefArray.sort(gSortFunction = gSortFunctions[col.id]);
   100       if (gPrefView != gPrefArray)
   101         gPrefView.sort(gSortFunction);
   102       gSortedColumn = col.id;
   103       if (pref)
   104         index = getViewIndexOfPref(pref);
   105     }
   106     col.element.setAttribute("sortDirection", gSortDirection > 0 ? "ascending" : "descending");
   107     this.treebox.invalidate();
   108     if (index >= 0) {
   109       this.selection.select(index);
   110       this.treebox.ensureRowIsVisible(index);
   111     }
   112   },
   113   selectionChanged : function() {},
   114   cycleCell: function(row, col) {},
   115   isEditable: function(row, col) {return false; },
   116   isSelectable: function(row, col) {return false; },
   117   setCellValue: function(row, col, value) {},
   118   setCellText: function(row, col, value) {},
   119   performAction: function(action) {},
   120   performActionOnRow: function(action, row) {},
   121   performActionOnCell: function(action, row, col) {},
   122   isSeparator: function(index) {return false; }
   123 };
   125 // find the index in gPrefView of a pref object
   126 // or -1 if it does not exist in the filtered view
   127 function getViewIndexOfPref(pref)
   128 {
   129   var low = -1, high = gPrefView.length;
   130   var index = (low + high) >> 1;
   131   while (index > low) {
   132     var mid = gPrefView[index];
   133     if (mid == pref)
   134       return index;
   135     if (gSortFunction(mid, pref) < 0)
   136       low = index;
   137     else
   138       high = index;
   139     index = (low + high) >> 1;
   140   }
   141   return -1;
   142 }
   144 // find the index in gPrefView where a pref object belongs
   145 function getNearestViewIndexOfPref(pref)
   146 {
   147   var low = -1, high = gPrefView.length;
   148   var index = (low + high) >> 1;
   149   while (index > low) {
   150     if (gSortFunction(gPrefView[index], pref) < 0)
   151       low = index;
   152     else
   153       high = index;
   154     index = (low + high) >> 1;
   155   }
   156   return high;
   157 }
   159 // find the index in gPrefArray of a pref object
   160 function getIndexOfPref(pref)
   161 {
   162   var low = -1, high = gPrefArray.length;
   163   var index = (low + high) >> 1;
   164   while (index > low) {
   165     var mid = gPrefArray[index];
   166     if (mid == pref)
   167       return index;
   168     if (gSortFunction(mid, pref) < 0)
   169       low = index;
   170     else
   171       high = index;
   172     index = (low + high) >> 1;
   173   }
   174   return index;
   175 }
   177 function getNearestIndexOfPref(pref)
   178 {
   179   var low = -1, high = gPrefArray.length;
   180   var index = (low + high) >> 1;
   181   while (index > low) {
   182     if (gSortFunction(gPrefArray[index], pref) < 0)
   183       low = index;
   184     else
   185       high = index;
   186     index = (low + high) >> 1;
   187   }
   188   return high;
   189 }
   191 var gPrefListener =
   192 {
   193   observe: function(subject, topic, prefName)
   194   {
   195     if (topic != "nsPref:changed")
   196       return;
   198     var arrayIndex = gPrefArray.length;
   199     var viewIndex = arrayIndex;
   200     var selectedIndex = view.selection.currentIndex;
   201     var pref;
   202     var updateView = false;
   203     var updateArray = false;
   204     var addedRow = false;
   205     if (prefName in gPrefHash) {
   206       pref = gPrefHash[prefName];
   207       viewIndex = getViewIndexOfPref(pref);
   208       arrayIndex = getIndexOfPref(pref);
   209       fetchPref(prefName, arrayIndex);
   210       // fetchPref replaces the existing pref object
   211       pref = gPrefHash[prefName];
   212       if (viewIndex >= 0) {
   213         // Might need to update the filtered view
   214         gPrefView[viewIndex] = gPrefHash[prefName];
   215         view.treebox.invalidateRow(viewIndex);
   216       }
   217       if (gSortedColumn == "lockCol" || gSortedColumn == "valueCol") {
   218         updateArray = true;
   219         gPrefArray.splice(arrayIndex, 1);
   220         if (gFilter && gFilter.test(pref.prefCol + ";" + pref.valueCol)) {
   221           updateView = true;
   222           gPrefView.splice(viewIndex, 1);
   223         }
   224       }
   225     }
   226     else {
   227       fetchPref(prefName, arrayIndex);
   228       pref = gPrefArray.pop();
   229       updateArray = true;
   230       addedRow = true;
   231       if (gFilter && gFilter.test(pref.prefCol + ";" + pref.valueCol)) {
   232         updateView = true;
   233       }
   234     }
   235     if (updateArray) {
   236       // Reinsert in the data array
   237       var newIndex = getNearestIndexOfPref(pref);
   238       gPrefArray.splice(newIndex, 0, pref);
   240       if (updateView) {
   241         // View is filtered, reinsert in the view separately
   242         newIndex = getNearestViewIndexOfPref(pref);
   243         gPrefView.splice(newIndex, 0, pref);
   244       }
   245       else if (gFilter) {
   246         // View is filtered, but nothing to update
   247         return;
   248       }
   250       if (addedRow)
   251         view.treebox.rowCountChanged(newIndex, 1);
   253       // Invalidate the changed range in the view
   254       var low = Math.min(viewIndex, newIndex);
   255       var high = Math.max(viewIndex, newIndex);
   256       view.treebox.invalidateRange(low, high);
   258       if (selectedIndex == viewIndex) {
   259         selectedIndex = newIndex;
   260       }
   261       else if (selectedIndex >= low && selectedIndex <= high) {
   262         selectedIndex += (newIndex > viewIndex) ? -1 : 1;
   263       }
   264       if (selectedIndex >= 0) {
   265         view.selection.select(selectedIndex);
   266         if (selectedIndex == newIndex)
   267           view.treebox.ensureRowIsVisible(selectedIndex);
   268       }
   269     }
   270   }
   271 };
   273 function prefObject(prefName, prefIndex)
   274 {
   275   this.prefCol = prefName;
   276 }
   278 prefObject.prototype =
   279 {
   280   lockCol: PREF_IS_DEFAULT_VALUE,
   281   typeCol: nsIPrefBranch.PREF_STRING,
   282   valueCol: ""
   283 };
   285 function fetchPref(prefName, prefIndex)
   286 {
   287   var pref = new prefObject(prefName);
   289   gPrefHash[prefName] = pref;
   290   gPrefArray[prefIndex] = pref;
   292   if (gPrefBranch.prefIsLocked(prefName))
   293     pref.lockCol = PREF_IS_LOCKED;
   294   else if (gPrefBranch.prefHasUserValue(prefName))
   295     pref.lockCol = PREF_IS_USER_SET;
   297   try {
   298     switch (gPrefBranch.getPrefType(prefName)) {
   299       case gPrefBranch.PREF_BOOL:
   300         pref.typeCol = gPrefBranch.PREF_BOOL;
   301         // convert to a string
   302         pref.valueCol = gPrefBranch.getBoolPref(prefName).toString();
   303         break;
   304       case gPrefBranch.PREF_INT:
   305         pref.typeCol = gPrefBranch.PREF_INT;
   306         // convert to a string
   307         pref.valueCol = gPrefBranch.getIntPref(prefName).toString();
   308         break;
   309       default:
   310       case gPrefBranch.PREF_STRING:
   311         pref.valueCol = gPrefBranch.getComplexValue(prefName, nsISupportsString).data;
   312         // Try in case it's a localized string (will throw an exception if not)
   313         if (pref.lockCol == PREF_IS_DEFAULT_VALUE &&
   314             /^chrome:\/\/.+\/locale\/.+\.properties/.test(pref.valueCol))
   315           pref.valueCol = gPrefBranch.getComplexValue(prefName, nsIPrefLocalizedString).data;
   316         break;
   317     }
   318   } catch (e) {
   319     // Also catch obscure cases in which you can't tell in advance
   320     // that the pref exists but has no user or default value...
   321   }
   322 }
   324 function onConfigLoad()
   325 {
   326   // Load strings
   327   gConfigBundle = document.getElementById("configBundle");
   329   gLockStrs[PREF_IS_DEFAULT_VALUE] = gConfigBundle.getString("default");
   330   gLockStrs[PREF_IS_USER_SET] = gConfigBundle.getString("user");
   331   gLockStrs[PREF_IS_LOCKED] = gConfigBundle.getString("locked");
   333   gTypeStrs[nsIPrefBranch.PREF_STRING] = gConfigBundle.getString("string");
   334   gTypeStrs[nsIPrefBranch.PREF_INT] = gConfigBundle.getString("int");
   335   gTypeStrs[nsIPrefBranch.PREF_BOOL] = gConfigBundle.getString("bool");
   337   var showWarning = gPrefBranch.getBoolPref("general.warnOnAboutConfig");
   339   if (showWarning)
   340     document.getElementById("warningButton").focus();
   341   else
   342     ShowPrefs();
   343 }
   345 // Unhide the warning message
   346 function ShowPrefs()
   347 {
   348   gPrefBranch.getChildList("").forEach(fetchPref);
   350   var descending = document.getElementsByAttribute("sortDirection", "descending");
   351   if (descending.item(0)) {
   352     gSortedColumn = descending[0].id;
   353     gSortDirection = -1;
   354   }
   355   else {
   356     var ascending = document.getElementsByAttribute("sortDirection", "ascending");
   357     if (ascending.item(0))
   358       gSortedColumn = ascending[0].id;
   359     else
   360       document.getElementById(gSortedColumn).setAttribute("sortDirection", "ascending");
   361   }
   362   gSortFunction = gSortFunctions[gSortedColumn];
   363   gPrefArray.sort(gSortFunction);
   365   gPrefBranch.addObserver("", gPrefListener, false);
   367   var configTree = document.getElementById("configTree");
   368   configTree.view = view;
   369   configTree.controllers.insertControllerAt(0, configController);
   371   document.getElementById("configDeck").setAttribute("selectedIndex", 1);
   372   document.getElementById("configTreeKeyset").removeAttribute("disabled");
   373   if (!document.getElementById("showWarningNextTime").checked)
   374     gPrefBranch.setBoolPref("general.warnOnAboutConfig", false);
   376   // Process about:config?filter=<string>
   377   var textbox = document.getElementById("textbox");
   378   // About URIs don't support query params, so do this manually
   379   var loc = document.location.href;
   380   var matches = /[?&]filter\=([^&]+)/i.exec(loc);
   381   if (matches)
   382     textbox.value = decodeURIComponent(matches[1]);
   384   // Even if we did not set the filter string via the URL query,
   385   // textbox might have been set via some other mechanism
   386   if (textbox.value)
   387     FilterPrefs();
   388   textbox.focus();
   389 }
   391 function onConfigUnload()
   392 {
   393   if (document.getElementById("configDeck").getAttribute("selectedIndex") == 1) {
   394     gPrefBranch.removeObserver("", gPrefListener);
   395     var configTree = document.getElementById("configTree");
   396     configTree.view = null;
   397     configTree.controllers.removeController(configController);
   398   }
   399 }
   401 function FilterPrefs()
   402 {
   403   if (document.getElementById("configDeck").getAttribute("selectedIndex") != 1) {
   404     return;
   405   }
   407   var substring = document.getElementById("textbox").value;
   408   // Check for "/regex/[i]"
   409   if (substring.charAt(0) == '/') {
   410     var r = substring.match(/^\/(.*)\/(i?)$/);
   411     try {
   412       gFilter = RegExp(r[1], r[2]);
   413     }
   414     catch (e) {
   415       return; // Do nothing on incomplete or bad RegExp
   416     }
   417   }
   418   else if (substring) {
   419     gFilter = RegExp(substring.replace(/([^* \w])/g, "\\$1")
   420                               .replace(/^\*+/, "").replace(/\*+/g, ".*"), "i");
   421   } else {
   422     gFilter = null;
   423   }
   425   var prefCol = (view.selection && view.selection.currentIndex < 0) ?
   426                 null : gPrefView[view.selection.currentIndex].prefCol;
   427   var oldlen = gPrefView.length;
   428   gPrefView = gPrefArray;
   429   if (gFilter) {
   430     gPrefView = [];
   431     for (var i = 0; i < gPrefArray.length; ++i)
   432       if (gFilter.test(gPrefArray[i].prefCol + ";" + gPrefArray[i].valueCol))
   433         gPrefView.push(gPrefArray[i]);
   434   }
   435   view.treebox.invalidate();
   436   view.treebox.rowCountChanged(oldlen, gPrefView.length - oldlen);
   437   gotoPref(prefCol);
   438 }
   440 function prefColSortFunction(x, y)
   441 {
   442   if (x.prefCol > y.prefCol)
   443     return gSortDirection;
   444   if (x.prefCol < y.prefCol) 
   445     return -gSortDirection;
   446   return 0;
   447 }
   449 function lockColSortFunction(x, y)
   450 {
   451   if (x.lockCol != y.lockCol)
   452     return gSortDirection * (y.lockCol - x.lockCol);
   453   return prefColSortFunction(x, y);
   454 }
   456 function typeColSortFunction(x, y)
   457 {
   458   if (x.typeCol != y.typeCol) 
   459     return gSortDirection * (y.typeCol - x.typeCol);
   460   return prefColSortFunction(x, y);
   461 }
   463 function valueColSortFunction(x, y)
   464 {
   465   if (x.valueCol > y.valueCol)
   466     return gSortDirection;
   467   if (x.valueCol < y.valueCol) 
   468     return -gSortDirection;
   469   return prefColSortFunction(x, y);
   470 }
   472 const gSortFunctions =
   473 {
   474   prefCol: prefColSortFunction, 
   475   lockCol: lockColSortFunction, 
   476   typeCol: typeColSortFunction, 
   477   valueCol: valueColSortFunction
   478 };
   480 const configController = {
   481   supportsCommand: function supportsCommand(command) {
   482     return command == "cmd_copy";
   483   },
   484   isCommandEnabled: function isCommandEnabled(command) {
   485     return view.selection && view.selection.currentIndex >= 0;
   486   },
   487   doCommand: function doCommand(command) {
   488     copyPref();
   489   },
   490   onEvent: function onEvent(event) {
   491   }
   492 }
   494 function updateContextMenu()
   495 {
   496   var lockCol = PREF_IS_LOCKED;
   497   var typeCol = nsIPrefBranch.PREF_STRING;
   498   var valueCol = "";
   499   var copyDisabled = true;
   500   var prefSelected = view.selection.currentIndex >= 0;
   502   if (prefSelected) {
   503     var prefRow = gPrefView[view.selection.currentIndex];
   504     lockCol = prefRow.lockCol;
   505     typeCol = prefRow.typeCol;
   506     valueCol = prefRow.valueCol;
   507     copyDisabled = false;
   508   }
   510   var copyPref = document.getElementById("copyPref");
   511   copyPref.setAttribute("disabled", copyDisabled);
   513   var copyName = document.getElementById("copyName");
   514   copyName.setAttribute("disabled", copyDisabled);
   516   var copyValue = document.getElementById("copyValue");
   517   copyValue.setAttribute("disabled", copyDisabled);
   519   var resetSelected = document.getElementById("resetSelected");
   520   resetSelected.setAttribute("disabled", lockCol != PREF_IS_USER_SET);
   522   var canToggle = typeCol == nsIPrefBranch.PREF_BOOL && valueCol != "";
   523   // indicates that a pref is locked or no pref is selected at all
   524   var isLocked = lockCol == PREF_IS_LOCKED;
   526   var modifySelected = document.getElementById("modifySelected");
   527   modifySelected.setAttribute("disabled", isLocked);
   528   modifySelected.hidden = canToggle;
   530   var toggleSelected = document.getElementById("toggleSelected");
   531   toggleSelected.setAttribute("disabled", isLocked);
   532   toggleSelected.hidden = !canToggle;
   533 }
   535 function copyPref()
   536 {
   537   var pref = gPrefView[view.selection.currentIndex];
   538   gClipboardHelper.copyString(pref.prefCol + ';' + pref.valueCol, document);
   539 }
   541 function copyName()
   542 {
   543   gClipboardHelper.copyString(gPrefView[view.selection.currentIndex].prefCol, document);
   544 }
   546 function copyValue()
   547 {
   548   gClipboardHelper.copyString(gPrefView[view.selection.currentIndex].valueCol, document);
   549 }
   551 function ModifySelected()
   552 {
   553   if (view.selection.currentIndex >= 0)
   554     ModifyPref(gPrefView[view.selection.currentIndex]);
   555 }
   557 function ResetSelected()
   558 {
   559   var entry = gPrefView[view.selection.currentIndex];
   560   gPrefBranch.clearUserPref(entry.prefCol);
   561 }
   563 function NewPref(type)
   564 {
   565   var result = { value: "" };
   566   var dummy = { value: 0 };
   567   if (Services.prompt.prompt(window,
   568                              gConfigBundle.getFormattedString("new_title",
   569                                                               [gTypeStrs[type]]),
   570                              gConfigBundle.getString("new_prompt"),
   571                              result,
   572                              null,
   573                              dummy)) {
   574     result.value = result.value.trim();
   575     if (!result.value) {
   576       return;
   577     }
   579     var pref;
   580     if (result.value in gPrefHash)
   581       pref = gPrefHash[result.value];
   582     else
   583       pref = { prefCol: result.value, lockCol: PREF_IS_DEFAULT_VALUE, typeCol: type, valueCol: "" };
   584     if (ModifyPref(pref))
   585       setTimeout(gotoPref, 0, result.value);
   586   }
   587 }
   589 function gotoPref(pref)
   590 {
   591   // make sure the pref exists and is displayed in the current view
   592   var index = pref in gPrefHash ? getViewIndexOfPref(gPrefHash[pref]) : -1;
   593   if (index >= 0) {
   594     view.selection.select(index);
   595     view.treebox.ensureRowIsVisible(index);
   596   } else {
   597     view.selection.clearSelection();
   598     view.selection.currentIndex = -1;
   599   }
   600 }
   602 function ModifyPref(entry)
   603 {
   604   if (entry.lockCol == PREF_IS_LOCKED)
   605     return false;
   606   var title = gConfigBundle.getFormattedString("modify_title", [gTypeStrs[entry.typeCol]]);
   607   if (entry.typeCol == nsIPrefBranch.PREF_BOOL) {
   608     var check = { value: entry.valueCol == "false" };
   609     if (!entry.valueCol && !Services.prompt.select(window, title, entry.prefCol, 2, [false, true], check))
   610       return false;
   611     gPrefBranch.setBoolPref(entry.prefCol, check.value);
   612   } else {
   613     var result = { value: entry.valueCol };
   614     var dummy = { value: 0 };
   615     if (!Services.prompt.prompt(window, title, entry.prefCol, result, null, dummy))
   616       return false;
   617     if (entry.typeCol == nsIPrefBranch.PREF_INT) {
   618       // | 0 converts to integer or 0; - 0 to float or NaN.
   619       // Thus, this check should catch all cases.
   620       var val = result.value | 0;
   621       if (val != result.value - 0) {
   622         var err_title = gConfigBundle.getString("nan_title");
   623         var err_text = gConfigBundle.getString("nan_text");
   624         Services.prompt.alert(window, err_title, err_text);
   625         return false;
   626       }
   627       gPrefBranch.setIntPref(entry.prefCol, val);
   628     } else {
   629       var supportsString = Components.classes[nsSupportsString_CONTRACTID].createInstance(nsISupportsString);
   630       supportsString.data = result.value;
   631       gPrefBranch.setComplexValue(entry.prefCol, nsISupportsString, supportsString);
   632     }
   633   }
   635   Services.prefs.savePrefFile(null);
   636   return true;
   637 }

mercurial