forked from mirrors/gecko-dev
		
	 691543ee89
			
		
	
	
		691543ee89
		
	
	
	
	
		
			
			Automatic changes by ESLint, except for manual corrections for .xml files. Differential Revision: https://phabricator.services.mozilla.com/D4439 --HG-- extra : moz-landing-system : lando
		
			
				
	
	
		
			690 lines
		
	
	
	
		
			22 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			690 lines
		
	
	
	
		
			22 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /* This Source Code Form is subject to the terms of the Mozilla Public
 | |
|  * License, v. 2.0. If a copy of the MPL was not distributed with this
 | |
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | |
| 
 | |
| /* import-globals-from extensionControlled.js */
 | |
| /* import-globals-from preferences.js */
 | |
| 
 | |
| ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 | |
| ChromeUtils.defineModuleGetter(this, "PlacesUtils",
 | |
|                                "resource://gre/modules/PlacesUtils.jsm");
 | |
| ChromeUtils.defineModuleGetter(this, "ExtensionSettingsStore",
 | |
|                                "resource://gre/modules/ExtensionSettingsStore.jsm");
 | |
| 
 | |
| Preferences.addAll([
 | |
|   { id: "browser.search.suggest.enabled", type: "bool" },
 | |
|   { id: "browser.urlbar.suggest.searches", type: "bool" },
 | |
|   { id: "browser.search.hiddenOneOffs", type: "unichar" },
 | |
|   { id: "browser.search.widget.inNavBar", type: "bool" },
 | |
|   { id: "browser.urlbar.matchBuckets", type: "string" },
 | |
| ]);
 | |
| 
 | |
| const ENGINE_FLAVOR = "text/x-moz-search-engine";
 | |
| const SEARCH_TYPE = "default_search";
 | |
| const SEARCH_KEY = "defaultSearch";
 | |
| 
 | |
| var gEngineView = null;
 | |
| 
 | |
| var gSearchPane = {
 | |
| 
 | |
|   /**
 | |
|    * Initialize autocomplete to ensure prefs are in sync.
 | |
|    */
 | |
|   _initAutocomplete() {
 | |
|     Cc["@mozilla.org/autocomplete/search;1?name=unifiedcomplete"]
 | |
|       .getService(Ci.mozIPlacesAutoComplete);
 | |
|   },
 | |
| 
 | |
|   init() {
 | |
|     gEngineView = new EngineView(new EngineStore());
 | |
|     document.getElementById("engineList").view = gEngineView;
 | |
|     this.buildDefaultEngineDropDown();
 | |
| 
 | |
|     if (Services.policies &&
 | |
|         !Services.policies.isAllowed("installSearchEngine")) {
 | |
|       document.getElementById("addEnginesBox").hidden = true;
 | |
|     } else {
 | |
|       let addEnginesLink = document.getElementById("addEngines");
 | |
|       let searchEnginesURL = Services.wm.getMostRecentWindow("navigator:browser")
 | |
|                                         .BrowserSearch.searchEnginesURL;
 | |
|       addEnginesLink.setAttribute("href", searchEnginesURL);
 | |
|     }
 | |
| 
 | |
|     window.addEventListener("click", this);
 | |
|     window.addEventListener("command", this);
 | |
|     window.addEventListener("dragstart", this);
 | |
|     window.addEventListener("keypress", this);
 | |
|     window.addEventListener("select", this);
 | |
|     window.addEventListener("blur", this, true);
 | |
| 
 | |
|     Services.obs.addObserver(this, "browser-search-engine-modified");
 | |
|     window.addEventListener("unload", () => {
 | |
|       Services.obs.removeObserver(this, "browser-search-engine-modified");
 | |
|     });
 | |
| 
 | |
|     this._initAutocomplete();
 | |
| 
 | |
|     let suggestsPref = Preferences.get("browser.search.suggest.enabled");
 | |
|     let urlbarSuggestsPref = Preferences.get("browser.urlbar.suggest.searches");
 | |
|     let updateSuggestionCheckboxes = this._updateSuggestionCheckboxes.bind(this);
 | |
|     suggestsPref.on("change", updateSuggestionCheckboxes);
 | |
|     urlbarSuggestsPref.on("change", updateSuggestionCheckboxes);
 | |
|     let urlbarSuggests = document.getElementById("urlBarSuggestion");
 | |
|     urlbarSuggests.addEventListener("command", () => {
 | |
|       urlbarSuggestsPref.value = urlbarSuggests.checked;
 | |
|     });
 | |
| 
 | |
|     this._initShowSearchSuggestionsFirst();
 | |
|     this._updateSuggestionCheckboxes();
 | |
|   },
 | |
| 
 | |
|   _initShowSearchSuggestionsFirst() {
 | |
|     this._urlbarSuggestionsPosPref = Preferences.get("browser.urlbar.matchBuckets");
 | |
|     let checkbox =
 | |
|       document.getElementById("showSearchSuggestionsFirstCheckbox");
 | |
| 
 | |
|     this._urlbarSuggestionsPosPref.on("change", () => {
 | |
|       this._syncFromShowSearchSuggestionsFirstPref(checkbox);
 | |
|     });
 | |
|     this._syncFromShowSearchSuggestionsFirstPref(checkbox);
 | |
| 
 | |
|     checkbox.addEventListener("command", () => {
 | |
|       this._syncToShowSearchSuggestionsFirstPref(checkbox.checked);
 | |
|     });
 | |
|   },
 | |
| 
 | |
|   _syncFromShowSearchSuggestionsFirstPref(checkbox) {
 | |
|     if (!this._urlbarSuggestionsPosPref.value) {
 | |
|       // The pref is cleared, meaning search suggestions are shown first.
 | |
|       checkbox.checked = true;
 | |
|       return;
 | |
|     }
 | |
|     // The pref has a value.  If the first bucket in the pref is search
 | |
|     // suggestions, then check the checkbox.
 | |
|     let buckets = PlacesUtils.convertMatchBucketsStringToArray(this._urlbarSuggestionsPosPref.value);
 | |
|     checkbox.checked = buckets[0] && buckets[0][0] == "suggestion";
 | |
|   },
 | |
| 
 | |
|   _syncToShowSearchSuggestionsFirstPref(checked) {
 | |
|     if (checked) {
 | |
|       // Show search suggestions first, so clear the pref since that's the
 | |
|       // default.
 | |
|       this._urlbarSuggestionsPosPref.reset();
 | |
|       return;
 | |
|     }
 | |
|     // Show history first.
 | |
|     this._urlbarSuggestionsPosPref.value = "general:5,suggestion:Infinity";
 | |
|   },
 | |
| 
 | |
|   _updateSuggestionCheckboxes() {
 | |
|     let suggestsPref = Preferences.get("browser.search.suggest.enabled");
 | |
|     let permanentPB =
 | |
|       Services.prefs.getBoolPref("browser.privatebrowsing.autostart");
 | |
|     let urlbarSuggests = document.getElementById("urlBarSuggestion");
 | |
|     let positionCheckbox =
 | |
|       document.getElementById("showSearchSuggestionsFirstCheckbox");
 | |
| 
 | |
|     urlbarSuggests.disabled = !suggestsPref.value || permanentPB;
 | |
| 
 | |
|     let urlbarSuggestsPref = Preferences.get("browser.urlbar.suggest.searches");
 | |
|     urlbarSuggests.checked = urlbarSuggestsPref.value;
 | |
|     if (urlbarSuggests.disabled) {
 | |
|       urlbarSuggests.checked = false;
 | |
|     }
 | |
| 
 | |
|     if (urlbarSuggests.checked) {
 | |
|       positionCheckbox.disabled = false;
 | |
|       this._syncFromShowSearchSuggestionsFirstPref(positionCheckbox);
 | |
|     } else {
 | |
|       positionCheckbox.disabled = true;
 | |
|       positionCheckbox.checked = false;
 | |
|     }
 | |
| 
 | |
|     let permanentPBLabel =
 | |
|       document.getElementById("urlBarSuggestionPermanentPBLabel");
 | |
|     permanentPBLabel.hidden = urlbarSuggests.hidden || !permanentPB;
 | |
|   },
 | |
| 
 | |
|   buildDefaultEngineDropDown() {
 | |
|     // This is called each time something affects the list of engines.
 | |
|     let list = document.getElementById("defaultEngine");
 | |
|     // Set selection to the current default engine.
 | |
|     let currentEngine = Services.search.currentEngine.name;
 | |
| 
 | |
|     // If the current engine isn't in the list any more, select the first item.
 | |
|     let engines = gEngineView._engineStore._engines;
 | |
|     if (!engines.some(e => e.name == currentEngine))
 | |
|       currentEngine = engines[0].name;
 | |
| 
 | |
|     // Now clean-up and rebuild the list.
 | |
|     list.removeAllItems();
 | |
|     gEngineView._engineStore._engines.forEach(e => {
 | |
|       let item = list.appendItem(e.name);
 | |
|       item.setAttribute("class", "menuitem-iconic searchengine-menuitem menuitem-with-favicon");
 | |
|       if (e.iconURI) {
 | |
|         item.setAttribute("image", e.iconURI.spec);
 | |
|       }
 | |
|       item.engine = e;
 | |
|       if (e.name == currentEngine)
 | |
|         list.selectedItem = item;
 | |
|     });
 | |
| 
 | |
|     handleControllingExtension(SEARCH_TYPE, SEARCH_KEY);
 | |
|     let searchEngineListener = {
 | |
|       observe(subject, topic, data) {
 | |
|         handleControllingExtension(SEARCH_TYPE, SEARCH_KEY);
 | |
|       },
 | |
|     };
 | |
|     Services.obs.addObserver(searchEngineListener, "browser-search-engine-modified");
 | |
|     window.addEventListener("unload", () => {
 | |
|       Services.obs.removeObserver(searchEngineListener, "browser-search-engine-modified");
 | |
|     });
 | |
|   },
 | |
| 
 | |
|   handleEvent(aEvent) {
 | |
|     switch (aEvent.type) {
 | |
|       case "click":
 | |
|         if (aEvent.target.id != "engineChildren" &&
 | |
|             !aEvent.target.classList.contains("searchEngineAction")) {
 | |
|           let engineList = document.getElementById("engineList");
 | |
|           // We don't want to toggle off selection while editing keyword
 | |
|           // so proceed only when the input field is hidden.
 | |
|           // We need to check that engineList.view is defined here
 | |
|           // because the "click" event listener is on <window> and the
 | |
|           // view might have been destroyed if the pane has been navigated
 | |
|           // away from.
 | |
|           if (engineList.inputField.hidden && engineList.view) {
 | |
|             let selection = engineList.view.selection;
 | |
|             if (selection.count > 0) {
 | |
|               selection.toggleSelect(selection.currentIndex);
 | |
|             }
 | |
|             engineList.blur();
 | |
|           }
 | |
|         }
 | |
|         break;
 | |
|       case "command":
 | |
|         switch (aEvent.target.id) {
 | |
|           case "":
 | |
|             if (aEvent.target.parentNode &&
 | |
|                 aEvent.target.parentNode.parentNode &&
 | |
|                 aEvent.target.parentNode.parentNode.id == "defaultEngine") {
 | |
|               gSearchPane.setDefaultEngine();
 | |
|             }
 | |
|             break;
 | |
|           case "restoreDefaultSearchEngines":
 | |
|             gSearchPane.onRestoreDefaults();
 | |
|             break;
 | |
|           case "removeEngineButton":
 | |
|             Services.search.removeEngine(gEngineView.selectedEngine.originalEngine);
 | |
|             break;
 | |
|         }
 | |
|         break;
 | |
|       case "dragstart":
 | |
|         if (aEvent.target.id == "engineChildren") {
 | |
|           onDragEngineStart(aEvent);
 | |
|         }
 | |
|         break;
 | |
|       case "keypress":
 | |
|         if (aEvent.target.id == "engineList") {
 | |
|           gSearchPane.onTreeKeyPress(aEvent);
 | |
|         }
 | |
|         break;
 | |
|       case "select":
 | |
|         if (aEvent.target.id == "engineList") {
 | |
|           gSearchPane.onTreeSelect();
 | |
|         }
 | |
|         break;
 | |
|       case "blur":
 | |
|         if (aEvent.target.id == "engineList" &&
 | |
|             aEvent.target.inputField == document.getBindingParent(aEvent.originalTarget)) {
 | |
|           gSearchPane.onInputBlur();
 | |
|         }
 | |
|         break;
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   observe(aEngine, aTopic, aVerb) {
 | |
|     if (aTopic == "browser-search-engine-modified") {
 | |
|       aEngine.QueryInterface(Ci.nsISearchEngine);
 | |
|       switch (aVerb) {
 | |
|       case "engine-added":
 | |
|         gEngineView._engineStore.addEngine(aEngine);
 | |
|         gEngineView.rowCountChanged(gEngineView.lastIndex, 1);
 | |
|         gSearchPane.buildDefaultEngineDropDown();
 | |
|         break;
 | |
|       case "engine-changed":
 | |
|         gEngineView._engineStore.reloadIcons();
 | |
|         gEngineView.invalidate();
 | |
|         break;
 | |
|       case "engine-removed":
 | |
|         gSearchPane.remove(aEngine);
 | |
|         break;
 | |
|       case "engine-current":
 | |
|         // If the user is going through the drop down using up/down keys, the
 | |
|         // dropdown may still be open (eg. on Windows) when engine-current is
 | |
|         // fired, so rebuilding the list unconditionally would get in the way.
 | |
|         let selectedEngine =
 | |
|           document.getElementById("defaultEngine").selectedItem.engine;
 | |
|         if (selectedEngine.name != aEngine.name)
 | |
|           gSearchPane.buildDefaultEngineDropDown();
 | |
|         break;
 | |
|       case "engine-default":
 | |
|         // Not relevant
 | |
|         break;
 | |
|       }
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   onInputBlur(aEvent) {
 | |
|     let tree = document.getElementById("engineList");
 | |
|     if (!tree.hasAttribute("editing"))
 | |
|       return;
 | |
| 
 | |
|     // Accept input unless discarded.
 | |
|     let accept = aEvent.charCode != KeyEvent.DOM_VK_ESCAPE;
 | |
|     tree.stopEditing(accept);
 | |
|   },
 | |
| 
 | |
|   onTreeSelect() {
 | |
|     document.getElementById("removeEngineButton").disabled =
 | |
|       !gEngineView.isEngineSelectedAndRemovable();
 | |
|   },
 | |
| 
 | |
|   onTreeKeyPress(aEvent) {
 | |
|     let index = gEngineView.selectedIndex;
 | |
|     let tree = document.getElementById("engineList");
 | |
|     if (tree.hasAttribute("editing"))
 | |
|       return;
 | |
| 
 | |
|     if (aEvent.charCode == KeyEvent.DOM_VK_SPACE) {
 | |
|       // Space toggles the checkbox.
 | |
|       let newValue = !gEngineView._engineStore.engines[index].shown;
 | |
|       gEngineView.setCellValue(index, tree.columns.getFirstColumn(),
 | |
|                                newValue.toString());
 | |
|       // Prevent page from scrolling on the space key.
 | |
|       aEvent.preventDefault();
 | |
|     } else {
 | |
|       let isMac = Services.appinfo.OS == "Darwin";
 | |
|       if ((isMac && aEvent.keyCode == KeyEvent.DOM_VK_RETURN) ||
 | |
|           (!isMac && aEvent.keyCode == KeyEvent.DOM_VK_F2)) {
 | |
|         tree.startEditing(index, tree.columns.getLastColumn());
 | |
|       } else if (aEvent.keyCode == KeyEvent.DOM_VK_DELETE ||
 | |
|                  (isMac && aEvent.shiftKey &&
 | |
|                   aEvent.keyCode == KeyEvent.DOM_VK_BACK_SPACE &&
 | |
|                   gEngineView.isEngineSelectedAndRemovable())) {
 | |
|         // Delete and Shift+Backspace (Mac) removes selected engine.
 | |
|         Services.search.removeEngine(gEngineView.selectedEngine.originalEngine);
 | |
|      }
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   onRestoreDefaults() {
 | |
|     let num = gEngineView._engineStore.restoreDefaultEngines();
 | |
|     gEngineView.rowCountChanged(0, num);
 | |
|     gEngineView.invalidate();
 | |
|   },
 | |
| 
 | |
|   showRestoreDefaults(aEnable) {
 | |
|     document.getElementById("restoreDefaultSearchEngines").disabled = !aEnable;
 | |
|   },
 | |
| 
 | |
|   remove(aEngine) {
 | |
|     let index = gEngineView._engineStore.removeEngine(aEngine);
 | |
|     gEngineView.rowCountChanged(index, -1);
 | |
|     gEngineView.invalidate();
 | |
|     gEngineView.selection.select(Math.min(index, gEngineView.lastIndex));
 | |
|     gEngineView.ensureRowIsVisible(gEngineView.currentIndex);
 | |
|     document.getElementById("engineList").focus();
 | |
|   },
 | |
| 
 | |
|   async editKeyword(aEngine, aNewKeyword) {
 | |
|     let keyword = aNewKeyword.trim();
 | |
|     if (keyword) {
 | |
|       let eduplicate = false;
 | |
|       let dupName = "";
 | |
| 
 | |
|       // Check for duplicates in Places keywords.
 | |
|       let bduplicate = !!(await PlacesUtils.keywords.fetch(keyword));
 | |
| 
 | |
|       // Check for duplicates in changes we haven't committed yet
 | |
|       let engines = gEngineView._engineStore.engines;
 | |
|       let lc_keyword = keyword.toLocaleLowerCase();
 | |
|       for (let engine of engines) {
 | |
|         if (engine.alias &&
 | |
|             engine.alias.toLocaleLowerCase() == lc_keyword &&
 | |
|             engine.name != aEngine.name) {
 | |
|           eduplicate = true;
 | |
|           dupName = engine.name;
 | |
|           break;
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       // Notify the user if they have chosen an existing engine/bookmark keyword
 | |
|       if (eduplicate || bduplicate) {
 | |
|         let msgids = [{id: "search-keyword-warning-title"}];
 | |
|         if (eduplicate) {
 | |
|           msgids.push({id: "search-keyword-warning-engine", args: { name: dupName }});
 | |
|         } else {
 | |
|           msgids.push({id: "search-keyword-warning-bookmark"});
 | |
|         }
 | |
| 
 | |
|         let [dtitle, msg] = await document.l10n.formatValues(msgids);
 | |
| 
 | |
|         Services.prompt.alert(window, dtitle, msg);
 | |
|         return false;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     gEngineView._engineStore.changeEngine(aEngine, "alias", keyword);
 | |
|     gEngineView.invalidate();
 | |
|     return true;
 | |
|   },
 | |
| 
 | |
|   saveOneClickEnginesList() {
 | |
|     let hiddenList = [];
 | |
|     for (let engine of gEngineView._engineStore.engines) {
 | |
|       if (!engine.shown)
 | |
|         hiddenList.push(engine.name);
 | |
|     }
 | |
|     Preferences.get("browser.search.hiddenOneOffs").value =
 | |
|       hiddenList.join(",");
 | |
|   },
 | |
| 
 | |
|   setDefaultEngine() {
 | |
|     Services.search.currentEngine =
 | |
|       document.getElementById("defaultEngine").selectedItem.engine;
 | |
|     ExtensionSettingsStore.setByUser(SEARCH_TYPE, SEARCH_KEY);
 | |
|   },
 | |
| };
 | |
| 
 | |
| function onDragEngineStart(event) {
 | |
|   var selectedIndex = gEngineView.selectedIndex;
 | |
|   var tree = document.getElementById("engineList");
 | |
|   var row = { }, col = { }, child = { };
 | |
|   tree.treeBoxObject.getCellAt(event.clientX, event.clientY, row, col, child);
 | |
|   if (selectedIndex >= 0 && !gEngineView.isCheckBox(row.value, col.value)) {
 | |
|     event.dataTransfer.setData(ENGINE_FLAVOR, selectedIndex.toString());
 | |
|     event.dataTransfer.effectAllowed = "move";
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| function EngineStore() {
 | |
|   let pref = Preferences.get("browser.search.hiddenOneOffs").value;
 | |
|   this.hiddenList = pref ? pref.split(",") : [];
 | |
| 
 | |
|   this._engines = Services.search.getVisibleEngines().map(this._cloneEngine, this);
 | |
|   this._defaultEngines = Services.search.getDefaultEngines().map(this._cloneEngine, this);
 | |
| 
 | |
|   // check if we need to disable the restore defaults button
 | |
|   var someHidden = this._defaultEngines.some(e => e.hidden);
 | |
|   gSearchPane.showRestoreDefaults(someHidden);
 | |
| }
 | |
| EngineStore.prototype = {
 | |
|   _engines: null,
 | |
|   _defaultEngines: null,
 | |
| 
 | |
|   get engines() {
 | |
|     return this._engines;
 | |
|   },
 | |
|   set engines(val) {
 | |
|     this._engines = val;
 | |
|     return val;
 | |
|   },
 | |
| 
 | |
|   _getIndexForEngine(aEngine) {
 | |
|     return this._engines.indexOf(aEngine);
 | |
|   },
 | |
| 
 | |
|   _getEngineByName(aName) {
 | |
|     return this._engines.find(engine => engine.name == aName);
 | |
|   },
 | |
| 
 | |
|   _cloneEngine(aEngine) {
 | |
|     var clonedObj = {};
 | |
|     for (var i in aEngine)
 | |
|       clonedObj[i] = aEngine[i];
 | |
|     clonedObj.originalEngine = aEngine;
 | |
|     clonedObj.shown = !this.hiddenList.includes(clonedObj.name);
 | |
|     return clonedObj;
 | |
|   },
 | |
| 
 | |
|   // Callback for Array's some(). A thisObj must be passed to some()
 | |
|   _isSameEngine(aEngineClone) {
 | |
|     return aEngineClone.originalEngine == this.originalEngine;
 | |
|   },
 | |
| 
 | |
|   addEngine(aEngine) {
 | |
|     this._engines.push(this._cloneEngine(aEngine));
 | |
|   },
 | |
| 
 | |
|   moveEngine(aEngine, aNewIndex) {
 | |
|     if (aNewIndex < 0 || aNewIndex > this._engines.length - 1)
 | |
|       throw new Error("ES_moveEngine: invalid aNewIndex!");
 | |
|     var index = this._getIndexForEngine(aEngine);
 | |
|     if (index == -1)
 | |
|       throw new Error("ES_moveEngine: invalid engine?");
 | |
| 
 | |
|     if (index == aNewIndex)
 | |
|       return; // nothing to do
 | |
| 
 | |
|     // Move the engine in our internal store
 | |
|     var removedEngine = this._engines.splice(index, 1)[0];
 | |
|     this._engines.splice(aNewIndex, 0, removedEngine);
 | |
| 
 | |
|     Services.search.moveEngine(aEngine.originalEngine, aNewIndex);
 | |
|   },
 | |
| 
 | |
|   removeEngine(aEngine) {
 | |
|     if (this._engines.length == 1) {
 | |
|       throw new Error("Cannot remove last engine!");
 | |
|     }
 | |
| 
 | |
|     let engineName = aEngine.name;
 | |
|     let index = this._engines.findIndex(element => element.name == engineName);
 | |
| 
 | |
|     if (index == -1)
 | |
|       throw new Error("invalid engine?");
 | |
| 
 | |
|     let removedEngine = this._engines.splice(index, 1)[0];
 | |
| 
 | |
|     if (this._defaultEngines.some(this._isSameEngine, removedEngine))
 | |
|       gSearchPane.showRestoreDefaults(true);
 | |
|     gSearchPane.buildDefaultEngineDropDown();
 | |
|     return index;
 | |
|   },
 | |
| 
 | |
|   restoreDefaultEngines() {
 | |
|     var added = 0;
 | |
| 
 | |
|     for (var i = 0; i < this._defaultEngines.length; ++i) {
 | |
|       var e = this._defaultEngines[i];
 | |
| 
 | |
|       // If the engine is already in the list, just move it.
 | |
|       if (this._engines.some(this._isSameEngine, e)) {
 | |
|         this.moveEngine(this._getEngineByName(e.name), i);
 | |
|       } else {
 | |
|         // Otherwise, add it back to our internal store
 | |
| 
 | |
|         // The search service removes the alias when an engine is hidden,
 | |
|         // so clear any alias we may have cached before unhiding the engine.
 | |
|         e.alias = "";
 | |
| 
 | |
|         this._engines.splice(i, 0, e);
 | |
|         let engine = e.originalEngine;
 | |
|         engine.hidden = false;
 | |
|         Services.search.moveEngine(engine, i);
 | |
|         added++;
 | |
|       }
 | |
|     }
 | |
|     Services.search.resetToOriginalDefaultEngine();
 | |
|     gSearchPane.showRestoreDefaults(false);
 | |
|     gSearchPane.buildDefaultEngineDropDown();
 | |
|     return added;
 | |
|   },
 | |
| 
 | |
|   changeEngine(aEngine, aProp, aNewValue) {
 | |
|     var index = this._getIndexForEngine(aEngine);
 | |
|     if (index == -1)
 | |
|       throw new Error("invalid engine?");
 | |
| 
 | |
|     this._engines[index][aProp] = aNewValue;
 | |
|     aEngine.originalEngine[aProp] = aNewValue;
 | |
|   },
 | |
| 
 | |
|   reloadIcons() {
 | |
|     this._engines.forEach(function(e) {
 | |
|       e.uri = e.originalEngine.uri;
 | |
|     });
 | |
|   },
 | |
| };
 | |
| 
 | |
| function EngineView(aEngineStore) {
 | |
|   this._engineStore = aEngineStore;
 | |
| }
 | |
| EngineView.prototype = {
 | |
|   _engineStore: null,
 | |
|   tree: null,
 | |
| 
 | |
|   get lastIndex() {
 | |
|     return this.rowCount - 1;
 | |
|   },
 | |
|   get selectedIndex() {
 | |
|     var seln = this.selection;
 | |
|     if (seln.getRangeCount() > 0) {
 | |
|       var min = {};
 | |
|       seln.getRangeAt(0, min, {});
 | |
|       return min.value;
 | |
|     }
 | |
|     return -1;
 | |
|   },
 | |
|   get selectedEngine() {
 | |
|     return this._engineStore.engines[this.selectedIndex];
 | |
|   },
 | |
| 
 | |
|   // Helpers
 | |
|   rowCountChanged(index, count) {
 | |
|     this.tree.rowCountChanged(index, count);
 | |
|   },
 | |
| 
 | |
|   invalidate() {
 | |
|     this.tree.invalidate();
 | |
|   },
 | |
| 
 | |
|   ensureRowIsVisible(index) {
 | |
|     this.tree.ensureRowIsVisible(index);
 | |
|   },
 | |
| 
 | |
|   getSourceIndexFromDrag(dataTransfer) {
 | |
|     return parseInt(dataTransfer.getData(ENGINE_FLAVOR));
 | |
|   },
 | |
| 
 | |
|   isCheckBox(index, column) {
 | |
|     return column.id == "engineShown";
 | |
|   },
 | |
| 
 | |
|   isEngineSelectedAndRemovable() {
 | |
|     return this.selectedIndex != -1 && this.lastIndex != 0;
 | |
|   },
 | |
| 
 | |
|   // nsITreeView
 | |
|   get rowCount() {
 | |
|     return this._engineStore.engines.length;
 | |
|   },
 | |
| 
 | |
|   getImageSrc(index, column) {
 | |
|     if (column.id == "engineName") {
 | |
|       if (this._engineStore.engines[index].iconURI)
 | |
|         return this._engineStore.engines[index].iconURI.spec;
 | |
| 
 | |
|       if (window.devicePixelRatio > 1)
 | |
|         return "chrome://browser/skin/search-engine-placeholder@2x.png";
 | |
|       return "chrome://browser/skin/search-engine-placeholder.png";
 | |
|     }
 | |
| 
 | |
|     return "";
 | |
|   },
 | |
| 
 | |
|   getCellText(index, column) {
 | |
|     if (column.id == "engineName")
 | |
|       return this._engineStore.engines[index].name;
 | |
|     else if (column.id == "engineKeyword")
 | |
|       return this._engineStore.engines[index].alias;
 | |
|     return "";
 | |
|   },
 | |
| 
 | |
|   setTree(tree) {
 | |
|     this.tree = tree;
 | |
|   },
 | |
| 
 | |
|   canDrop(targetIndex, orientation, dataTransfer) {
 | |
|     var sourceIndex = this.getSourceIndexFromDrag(dataTransfer);
 | |
|     return (sourceIndex != -1 &&
 | |
|             sourceIndex != targetIndex &&
 | |
|             sourceIndex != targetIndex + orientation);
 | |
|   },
 | |
| 
 | |
|   drop(dropIndex, orientation, dataTransfer) {
 | |
|     var sourceIndex = this.getSourceIndexFromDrag(dataTransfer);
 | |
|     var sourceEngine = this._engineStore.engines[sourceIndex];
 | |
| 
 | |
|     const nsITreeView = Ci.nsITreeView;
 | |
|     if (dropIndex > sourceIndex) {
 | |
|       if (orientation == nsITreeView.DROP_BEFORE)
 | |
|         dropIndex--;
 | |
|     } else if (orientation == nsITreeView.DROP_AFTER) {
 | |
|       dropIndex++;
 | |
|     }
 | |
| 
 | |
|     this._engineStore.moveEngine(sourceEngine, dropIndex);
 | |
|     gSearchPane.showRestoreDefaults(true);
 | |
|     gSearchPane.buildDefaultEngineDropDown();
 | |
| 
 | |
|     // Redraw, and adjust selection
 | |
|     this.invalidate();
 | |
|     this.selection.select(dropIndex);
 | |
|   },
 | |
| 
 | |
|   selection: null,
 | |
|   getRowProperties(index) { return ""; },
 | |
|   getCellProperties(index, column) { return ""; },
 | |
|   getColumnProperties(column) { return ""; },
 | |
|   isContainer(index) { return false; },
 | |
|   isContainerOpen(index) { return false; },
 | |
|   isContainerEmpty(index) { return false; },
 | |
|   isSeparator(index) { return false; },
 | |
|   isSorted(index) { return false; },
 | |
|   getParentIndex(index) { return -1; },
 | |
|   hasNextSibling(parentIndex, index) { return false; },
 | |
|   getLevel(index) { return 0; },
 | |
|   getCellValue(index, column) {
 | |
|     if (column.id == "engineShown")
 | |
|       return this._engineStore.engines[index].shown;
 | |
|     return undefined;
 | |
|   },
 | |
|   toggleOpenState(index) { },
 | |
|   cycleHeader(column) { },
 | |
|   selectionChanged() { },
 | |
|   cycleCell(row, column) { },
 | |
|   isEditable(index, column) { return column.id != "engineName"; },
 | |
|   isSelectable(index, column) { return false; },
 | |
|   setCellValue(index, column, value) {
 | |
|     if (column.id == "engineShown") {
 | |
|       this._engineStore.engines[index].shown = value == "true";
 | |
|       gEngineView.invalidate();
 | |
|       gSearchPane.saveOneClickEnginesList();
 | |
|     }
 | |
|   },
 | |
|   setCellText(index, column, value) {
 | |
|     if (column.id == "engineKeyword") {
 | |
|       gSearchPane.editKeyword(this._engineStore.engines[index], value)
 | |
|                  .then(valid => {
 | |
|         if (!valid)
 | |
|           document.getElementById("engineList").startEditing(index, column);
 | |
|       });
 | |
|     }
 | |
|   },
 | |
|   performAction(action) { },
 | |
|   performActionOnRow(action, index) { },
 | |
|   performActionOnCell(action, index, column) { },
 | |
| };
 |