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.

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

mercurial