This adds a policy with the capability of adding search engines, choosing the default search engine, and blocking the installation of new search engines. Additionally, fixes the messages for errors reported by MainProcessSingleton.addSearchEngine so that the offending URL is printed rather than "[xpconnect wrapped nsIURI]". MozReview-Commit-ID: HuLT15Rnq0r
691 lines
22 KiB
JavaScript
691 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 = [["search-keyword-warning-title"]];
|
|
if (eduplicate) {
|
|
msgids.push(["search-keyword-warning-engine", { name: dupName }]);
|
|
} else {
|
|
msgids.push(["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) { }
|
|
};
|