browser/components/search/content/engineManager.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 /* 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
     3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 Components.utils.import("resource://gre/modules/Services.jsm");
     7 const Ci = Components.interfaces;
     8 const Cc = Components.classes;
    10 const ENGINE_FLAVOR = "text/x-moz-search-engine";
    12 const BROWSER_SUGGEST_PREF = "browser.search.suggest.enabled";
    14 var gEngineView = null;
    16 var gEngineManagerDialog = {
    17   init: function engineManager_init() {
    18     gEngineView = new EngineView(new EngineStore());
    20     var suggestEnabled = Services.prefs.getBoolPref(BROWSER_SUGGEST_PREF);
    21     document.getElementById("enableSuggest").checked = suggestEnabled;
    23     var tree = document.getElementById("engineList");
    24     tree.view = gEngineView;
    26     Services.obs.addObserver(this, "browser-search-engine-modified", false);
    27   },
    29   destroy: function engineManager_destroy() {
    30     // Remove the observer
    31     Services.obs.removeObserver(this, "browser-search-engine-modified");
    32   },
    34   observe: function engineManager_observe(aEngine, aTopic, aVerb) {
    35     if (aTopic == "browser-search-engine-modified") {
    36       aEngine.QueryInterface(Ci.nsISearchEngine);
    37       switch (aVerb) {
    38       case "engine-added":
    39         gEngineView._engineStore.addEngine(aEngine);
    40         gEngineView.rowCountChanged(gEngineView.lastIndex, 1);
    41         break;
    42       case "engine-changed":
    43         gEngineView._engineStore.reloadIcons();
    44         gEngineView.invalidate();
    45         break;
    46       case "engine-removed":
    47       case "engine-current":
    48       case "engine-default":
    49         // Not relevant
    50         break;
    51       }
    52     }
    53   },
    55   onOK: function engineManager_onOK() {
    56     // Set the preference
    57     var newSuggestEnabled = document.getElementById("enableSuggest").checked;
    58     Services.prefs.setBoolPref(BROWSER_SUGGEST_PREF, newSuggestEnabled);
    60     // Commit the changes
    61     gEngineView._engineStore.commit();
    62   },
    64   onRestoreDefaults: function engineManager_onRestoreDefaults() {
    65     var num = gEngineView._engineStore.restoreDefaultEngines();
    66     gEngineView.rowCountChanged(0, num);
    67     gEngineView.invalidate();
    68   },
    70   showRestoreDefaults: function engineManager_showRestoreDefaults(val) {
    71     document.documentElement.getButton("extra2").disabled = !val;
    72   },
    74   loadAddEngines: function engineManager_loadAddEngines() {
    75     this.onOK();
    76     window.opener.BrowserSearch.loadAddEngines();
    77     window.close();
    78   },
    80   remove: function engineManager_remove() {
    81     gEngineView._engineStore.removeEngine(gEngineView.selectedEngine);
    82     var index = gEngineView.selectedIndex;
    83     gEngineView.rowCountChanged(index, -1);
    84     gEngineView.invalidate();
    85     gEngineView.selection.select(Math.min(index, gEngineView.lastIndex));
    86     gEngineView.ensureRowIsVisible(gEngineView.currentIndex);
    87     document.getElementById("engineList").focus();
    88   },
    90   /**
    91    * Moves the selected engine either up or down in the engine list
    92    * @param aDir
    93    *        -1 to move the selected engine down, +1 to move it up.
    94    */
    95   bump: function engineManager_move(aDir) {
    96     var selectedEngine = gEngineView.selectedEngine;
    97     var newIndex = gEngineView.selectedIndex - aDir;
    99     gEngineView._engineStore.moveEngine(selectedEngine, newIndex);
   101     gEngineView.invalidate();
   102     gEngineView.selection.select(newIndex);
   103     gEngineView.ensureRowIsVisible(newIndex);
   104     this.showRestoreDefaults(true);
   105     document.getElementById("engineList").focus();
   106   },
   108   editKeyword: function engineManager_editKeyword() {
   109     var selectedEngine = gEngineView.selectedEngine;
   110     if (!selectedEngine)
   111       return;
   113     var alias = { value: selectedEngine.alias };
   114     var strings = document.getElementById("engineManagerBundle");
   115     var title = strings.getString("editTitle");
   116     var msg = strings.getFormattedString("editMsg", [selectedEngine.name]);
   118     while (Services.prompt.prompt(window, title, msg, alias, null, {})) {
   119       var bduplicate = false;
   120       var eduplicate = false;
   121       var dupName = "";
   123       if (alias.value != "") {
   124         try {
   125           let bmserv = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
   126                        getService(Ci.nsINavBookmarksService);
   127           if (bmserv.getURIForKeyword(alias.value))
   128             bduplicate = true;
   129         } catch(ex) {}
   131         // Check for duplicates in changes we haven't committed yet
   132         let engines = gEngineView._engineStore.engines;
   133         for each (let engine in engines) {
   134           if (engine.alias == alias.value &&
   135               engine.name != selectedEngine.name) {
   136             eduplicate = true;
   137             dupName = engine.name;
   138             break;
   139           }
   140         }
   141       }
   143       // Notify the user if they have chosen an existing engine/bookmark keyword
   144       if (eduplicate || bduplicate) {
   145         var dtitle = strings.getString("duplicateTitle");
   146         var bmsg = strings.getString("duplicateBookmarkMsg");
   147         var emsg = strings.getFormattedString("duplicateEngineMsg", [dupName]);
   149         Services.prompt.alert(window, dtitle, eduplicate ? emsg : bmsg);
   150       } else {
   151         gEngineView._engineStore.changeEngine(selectedEngine, "alias",
   152                                               alias.value);
   153         gEngineView.invalidate();
   154         break;
   155       }
   156     }
   157   },
   159   onSelect: function engineManager_onSelect() {
   160     // Buttons only work if an engine is selected and it's not the last engine,
   161     // the latter is true when the selected is first and last at the same time.
   162     var lastSelected = (gEngineView.selectedIndex == gEngineView.lastIndex);
   163     var firstSelected = (gEngineView.selectedIndex == 0);
   164     var noSelection = (gEngineView.selectedIndex == -1);
   166     document.getElementById("cmd_remove")
   167             .setAttribute("disabled", noSelection ||
   168                                       (firstSelected && lastSelected));
   170     document.getElementById("cmd_moveup")
   171             .setAttribute("disabled", noSelection || firstSelected);
   173     document.getElementById("cmd_movedown")
   174             .setAttribute("disabled", noSelection || lastSelected);
   176     document.getElementById("cmd_editkeyword")
   177             .setAttribute("disabled", noSelection);
   178   }
   179 };
   181 function onDragEngineStart(event) {
   182   var selectedIndex = gEngineView.selectedIndex;
   183   if (selectedIndex >= 0) {
   184     event.dataTransfer.setData(ENGINE_FLAVOR, selectedIndex.toString());
   185     event.dataTransfer.effectAllowed = "move";
   186   }
   187 }
   189 // "Operation" objects
   190 function EngineMoveOp(aEngineClone, aNewIndex) {
   191   if (!aEngineClone)
   192     throw new Error("bad args to new EngineMoveOp!");
   193   this._engine = aEngineClone.originalEngine;
   194   this._newIndex = aNewIndex;
   195 }
   196 EngineMoveOp.prototype = {
   197   _engine: null,
   198   _newIndex: null,
   199   commit: function EMO_commit() {
   200     Services.search.moveEngine(this._engine, this._newIndex);
   201   }
   202 }
   204 function EngineRemoveOp(aEngineClone) {
   205   if (!aEngineClone)
   206     throw new Error("bad args to new EngineRemoveOp!");
   207   this._engine = aEngineClone.originalEngine;
   208 }
   209 EngineRemoveOp.prototype = {
   210   _engine: null,
   211   commit: function ERO_commit() {
   212     Services.search.removeEngine(this._engine);
   213   }
   214 }
   216 function EngineUnhideOp(aEngineClone, aNewIndex) {
   217   if (!aEngineClone)
   218     throw new Error("bad args to new EngineUnhideOp!");
   219   this._engine = aEngineClone.originalEngine;
   220   this._newIndex = aNewIndex;
   221 }
   222 EngineUnhideOp.prototype = {
   223   _engine: null,
   224   _newIndex: null,
   225   commit: function EUO_commit() {
   226     this._engine.hidden = false;
   227     Services.search.moveEngine(this._engine, this._newIndex);
   228   }
   229 }
   231 function EngineChangeOp(aEngineClone, aProp, aValue) {
   232   if (!aEngineClone)
   233     throw new Error("bad args to new EngineChangeOp!");
   235   this._engine = aEngineClone.originalEngine;
   236   this._prop = aProp;
   237   this._newValue = aValue;
   238 }
   239 EngineChangeOp.prototype = {
   240   _engine: null,
   241   _prop: null,
   242   _newValue: null,
   243   commit: function ECO_commit() {
   244     this._engine[this._prop] = this._newValue;
   245   }
   246 }
   248 function EngineStore() {
   249   this._engines = Services.search.getVisibleEngines().map(this._cloneEngine);
   250   this._defaultEngines = Services.search.getDefaultEngines().map(this._cloneEngine);
   252   this._ops = [];
   254   // check if we need to disable the restore defaults button
   255   var someHidden = this._defaultEngines.some(function (e) e.hidden);
   256   gEngineManagerDialog.showRestoreDefaults(someHidden);
   257 }
   258 EngineStore.prototype = {
   259   _engines: null,
   260   _defaultEngines: null,
   261   _ops: null,
   263   get engines() {
   264     return this._engines;
   265   },
   266   set engines(val) {
   267     this._engines = val;
   268     return val;
   269   },
   271   _getIndexForEngine: function ES_getIndexForEngine(aEngine) {
   272     return this._engines.indexOf(aEngine);
   273   },
   275   _getEngineByName: function ES_getEngineByName(aName) {
   276     for each (var engine in this._engines)
   277       if (engine.name == aName)
   278         return engine;
   280     return null;
   281   },
   283   _cloneEngine: function ES_cloneEngine(aEngine) {
   284     var clonedObj={};
   285     for (var i in aEngine)
   286       clonedObj[i] = aEngine[i];
   287     clonedObj.originalEngine = aEngine;
   288     return clonedObj;
   289   },
   291   // Callback for Array's some(). A thisObj must be passed to some()
   292   _isSameEngine: function ES_isSameEngine(aEngineClone) {
   293     return aEngineClone.originalEngine == this.originalEngine;
   294   },
   296   commit: function ES_commit() {
   297     var currentEngine = this._cloneEngine(Services.search.currentEngine);
   298     for (var i = 0; i < this._ops.length; i++)
   299       this._ops[i].commit();
   301     // Restore currentEngine if it is a default engine that is still visible.
   302     // Needed if the user deletes currentEngine and then restores it.
   303     if (this._defaultEngines.some(this._isSameEngine, currentEngine) &&
   304         !currentEngine.originalEngine.hidden)
   305       Services.search.currentEngine = currentEngine.originalEngine;
   306   },
   308   addEngine: function ES_addEngine(aEngine) {
   309     this._engines.push(this._cloneEngine(aEngine));
   310   },
   312   moveEngine: function ES_moveEngine(aEngine, aNewIndex) {
   313     if (aNewIndex < 0 || aNewIndex > this._engines.length - 1)
   314       throw new Error("ES_moveEngine: invalid aNewIndex!");
   315     var index = this._getIndexForEngine(aEngine);
   316     if (index == -1)
   317       throw new Error("ES_moveEngine: invalid engine?");
   319     if (index == aNewIndex)
   320       return; // nothing to do
   322     // Move the engine in our internal store
   323     var removedEngine = this._engines.splice(index, 1)[0];
   324     this._engines.splice(aNewIndex, 0, removedEngine);
   326     this._ops.push(new EngineMoveOp(aEngine, aNewIndex));
   327   },
   329   removeEngine: function ES_removeEngine(aEngine) {
   330     var index = this._getIndexForEngine(aEngine);
   331     if (index == -1)
   332       throw new Error("invalid engine?");
   334     this._engines.splice(index, 1);
   335     this._ops.push(new EngineRemoveOp(aEngine));
   336     if (this._defaultEngines.some(this._isSameEngine, aEngine))
   337       gEngineManagerDialog.showRestoreDefaults(true);
   338   },
   340   restoreDefaultEngines: function ES_restoreDefaultEngines() {
   341     var added = 0;
   343     for (var i = 0; i < this._defaultEngines.length; ++i) {
   344       var e = this._defaultEngines[i];
   346       // If the engine is already in the list, just move it.
   347       if (this._engines.some(this._isSameEngine, e)) {
   348         this.moveEngine(this._getEngineByName(e.name), i);
   349       } else {
   350         // Otherwise, add it back to our internal store
   351         this._engines.splice(i, 0, e);
   352         this._ops.push(new EngineUnhideOp(e, i));
   353         added++;
   354       }
   355     }
   356     gEngineManagerDialog.showRestoreDefaults(false);
   357     return added;
   358   },
   360   changeEngine: function ES_changeEngine(aEngine, aProp, aNewValue) {
   361     var index = this._getIndexForEngine(aEngine);
   362     if (index == -1)
   363       throw new Error("invalid engine?");
   365     this._engines[index][aProp] = aNewValue;
   366     this._ops.push(new EngineChangeOp(aEngine, aProp, aNewValue));
   367   },
   369   reloadIcons: function ES_reloadIcons() {
   370     this._engines.forEach(function (e) {
   371       e.uri = e.originalEngine.uri;
   372     });
   373   }
   374 }
   376 function EngineView(aEngineStore) {
   377   this._engineStore = aEngineStore;
   378 }
   379 EngineView.prototype = {
   380   _engineStore: null,
   381   tree: null,
   383   get lastIndex() {
   384     return this.rowCount - 1;
   385   },
   386   get selectedIndex() {
   387     var seln = this.selection;
   388     if (seln.getRangeCount() > 0) {
   389       var min = {};
   390       seln.getRangeAt(0, min, {});
   391       return min.value;
   392     }
   393     return -1;
   394   },
   395   get selectedEngine() {
   396     return this._engineStore.engines[this.selectedIndex];
   397   },
   399   // Helpers
   400   rowCountChanged: function (index, count) {
   401     this.tree.rowCountChanged(index, count);
   402   },
   404   invalidate: function () {
   405     this.tree.invalidate();
   406   },
   408   ensureRowIsVisible: function (index) {
   409     this.tree.ensureRowIsVisible(index);
   410   },
   412   getSourceIndexFromDrag: function (dataTransfer) {
   413     return parseInt(dataTransfer.getData(ENGINE_FLAVOR));
   414   },
   416   // nsITreeView
   417   get rowCount() {
   418     return this._engineStore.engines.length;
   419   },
   421   getImageSrc: function(index, column) {
   422     if (column.id == "engineName" && this._engineStore.engines[index].iconURI)
   423       return this._engineStore.engines[index].iconURI.spec;
   424     return "";
   425   },
   427   getCellText: function(index, column) {
   428     if (column.id == "engineName")
   429       return this._engineStore.engines[index].name;
   430     else if (column.id == "engineKeyword")
   431       return this._engineStore.engines[index].alias;
   432     return "";
   433   },
   435   setTree: function(tree) {
   436     this.tree = tree;
   437   },
   439   canDrop: function(targetIndex, orientation, dataTransfer) {
   440     var sourceIndex = this.getSourceIndexFromDrag(dataTransfer);
   441     return (sourceIndex != -1 &&
   442             sourceIndex != targetIndex &&
   443             sourceIndex != targetIndex + orientation);
   444   },
   446   drop: function(dropIndex, orientation, dataTransfer) {
   447     var sourceIndex = this.getSourceIndexFromDrag(dataTransfer);
   448     var sourceEngine = this._engineStore.engines[sourceIndex];
   450     if (dropIndex > sourceIndex) {
   451       if (orientation == Ci.nsITreeView.DROP_BEFORE)
   452         dropIndex--;
   453     } else {
   454       if (orientation == Ci.nsITreeView.DROP_AFTER)
   455         dropIndex++;
   456     }
   458     this._engineStore.moveEngine(sourceEngine, dropIndex);
   459     gEngineManagerDialog.showRestoreDefaults(true);
   461     // Redraw, and adjust selection
   462     this.invalidate();
   463     this.selection.select(dropIndex);
   464   },
   466   selection: null,
   467   getRowProperties: function(index) { return ""; },
   468   getCellProperties: function(index, column) { return ""; },
   469   getColumnProperties: function(column) { return ""; },
   470   isContainer: function(index) { return false; },
   471   isContainerOpen: function(index) { return false; },
   472   isContainerEmpty: function(index) { return false; },
   473   isSeparator: function(index) { return false; },
   474   isSorted: function(index) { return false; },
   475   getParentIndex: function(index) { return -1; },
   476   hasNextSibling: function(parentIndex, index) { return false; },
   477   getLevel: function(index) { return 0; },
   478   getProgressMode: function(index, column) { },
   479   getCellValue: function(index, column) { },
   480   toggleOpenState: function(index) { },
   481   cycleHeader: function(column) { },
   482   selectionChanged: function() { },
   483   cycleCell: function(row, column) { },
   484   isEditable: function(index, column) { return false; },
   485   isSelectable: function(index, column) { return false; },
   486   setCellValue: function(index, column, value) { },
   487   setCellText: function(index, column, value) { },
   488   performAction: function(action) { },
   489   performActionOnRow: function(action, index) { },
   490   performActionOnCell: function(action, index, column) { }
   491 };

mercurial