Wed, 31 Dec 2014 06:09:35 +0100
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 };