528 lines
15 KiB
JavaScript
528 lines
15 KiB
JavaScript
/* -*- indent-tabs-mode: nil; js-indent-level: 4 -*- */
|
|
/* 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/. */
|
|
|
|
"use strict";
|
|
|
|
/**
|
|
* The permission type to give to Services.perms for Translations.
|
|
*/
|
|
const TRANSLATIONS_PERMISSION = "translations";
|
|
/**
|
|
* The list of BCP-47 language tags that will trigger auto-translate.
|
|
*/
|
|
const ALWAYS_TRANSLATE_LANGS_PREF =
|
|
"browser.translations.alwaysTranslateLanguages";
|
|
/**
|
|
* The list of BCP-47 language tags that will prevent showing Translations UI.
|
|
*/
|
|
const NEVER_TRANSLATE_LANGS_PREF =
|
|
"browser.translations.neverTranslateLanguages";
|
|
|
|
ChromeUtils.defineESModuleGetters(this, {
|
|
TranslationsParent: "resource://gre/actors/TranslationsParent.sys.mjs",
|
|
});
|
|
|
|
function Tree(aId, aData) {
|
|
this._data = aData;
|
|
this._tree = document.getElementById(aId);
|
|
this._tree.view = this;
|
|
}
|
|
|
|
Tree.prototype = {
|
|
get tree() {
|
|
return this._tree;
|
|
},
|
|
get isEmpty() {
|
|
return !this._data.length;
|
|
},
|
|
get hasSelection() {
|
|
return this.selection.count > 0;
|
|
},
|
|
getSelectedItems() {
|
|
let result = [];
|
|
|
|
let rc = this.selection.getRangeCount();
|
|
for (let i = 0; i < rc; ++i) {
|
|
let min = {},
|
|
max = {};
|
|
this.selection.getRangeAt(i, min, max);
|
|
for (let j = min.value; j <= max.value; ++j) {
|
|
result.push(this._data[j]);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
},
|
|
|
|
// nsITreeView implementation
|
|
get rowCount() {
|
|
return this._data.length;
|
|
},
|
|
getCellText(aRow) {
|
|
return this._data[aRow];
|
|
},
|
|
isSeparator() {
|
|
return false;
|
|
},
|
|
isSorted() {
|
|
return false;
|
|
},
|
|
isContainer() {
|
|
return false;
|
|
},
|
|
setTree() {},
|
|
getImageSrc() {},
|
|
getCellValue() {},
|
|
cycleHeader() {},
|
|
getRowProperties() {
|
|
return "";
|
|
},
|
|
getColumnProperties() {
|
|
return "";
|
|
},
|
|
getCellProperties() {
|
|
return "";
|
|
},
|
|
QueryInterface: ChromeUtils.generateQI(["nsITreeView"]),
|
|
};
|
|
|
|
function Lang(aCode, label) {
|
|
this.langCode = aCode;
|
|
this._label = label;
|
|
}
|
|
|
|
Lang.prototype = {
|
|
toString() {
|
|
return this._label;
|
|
},
|
|
};
|
|
|
|
var gTranslationsSettings = {
|
|
onLoad() {
|
|
if (this._neverTranslateSiteTree) {
|
|
// Re-using an open dialog, clear the old observers.
|
|
this.removeObservers();
|
|
}
|
|
|
|
// Load site permissions into an array.
|
|
this._neverTranslateSites = TranslationsParent.listNeverTranslateSites();
|
|
|
|
// Load language tags into arrays.
|
|
this._alwaysTranslateLangs = this.getAlwaysTranslateLanguages();
|
|
this._neverTranslateLangs = this.getNeverTranslateLanguages();
|
|
|
|
// Add observers for relevant prefs and permissions.
|
|
Services.obs.addObserver(this, "perm-changed");
|
|
Services.prefs.addObserver(ALWAYS_TRANSLATE_LANGS_PREF, this);
|
|
Services.prefs.addObserver(NEVER_TRANSLATE_LANGS_PREF, this);
|
|
|
|
window.addEventListener("unload", this);
|
|
document.addEventListener("command", this);
|
|
|
|
// Build trees from the arrays.
|
|
this._alwaysTranslateLangsTree = new Tree(
|
|
"alwaysTranslateLanguagesTree",
|
|
this._alwaysTranslateLangs
|
|
);
|
|
this._neverTranslateLangsTree = new Tree(
|
|
"neverTranslateLanguagesTree",
|
|
this._neverTranslateLangs
|
|
);
|
|
this._neverTranslateSiteTree = new Tree(
|
|
"neverTranslateSitesTree",
|
|
this._neverTranslateSites
|
|
);
|
|
|
|
for (let { tree } of [
|
|
this._alwaysTranslateLangsTree,
|
|
this._neverTranslateLangsTree,
|
|
this._neverTranslateSiteTree,
|
|
]) {
|
|
tree.addEventListener("keypress", this);
|
|
tree.addEventListener("select", this);
|
|
}
|
|
|
|
// Ensure the UI for each group is in the correct state.
|
|
this.onSelectAlwaysTranslateLanguage();
|
|
this.onSelectNeverTranslateLanguage();
|
|
this.onSelectNeverTranslateSite();
|
|
},
|
|
|
|
/**
|
|
* Retrieves the value of a char-pref splits its value into an
|
|
* array delimited by commas.
|
|
*
|
|
* This is used for the translations preferences which are comma-
|
|
* separated lists of BCP-47 language tags.
|
|
*
|
|
* @param {string} pref
|
|
* @returns {Array<string>}
|
|
*/
|
|
getLangsFromPref(pref) {
|
|
let rawLangs = Services.prefs.getCharPref(pref);
|
|
if (!rawLangs) {
|
|
return [];
|
|
}
|
|
|
|
let langArr = rawLangs.split(",");
|
|
let displayNames = Services.intl.getLanguageDisplayNames(
|
|
undefined,
|
|
langArr
|
|
);
|
|
let langs = langArr.map((lang, i) => new Lang(lang, displayNames[i]));
|
|
langs.sort();
|
|
|
|
return langs;
|
|
},
|
|
|
|
/**
|
|
* Retrieves the always-translate language tags as an array.
|
|
* @returns {Array<string>}
|
|
*/
|
|
getAlwaysTranslateLanguages() {
|
|
return this.getLangsFromPref(ALWAYS_TRANSLATE_LANGS_PREF);
|
|
},
|
|
|
|
/**
|
|
* Retrieves the never-translate language tags as an array.
|
|
* @returns {Array<string>}
|
|
*/
|
|
getNeverTranslateLanguages() {
|
|
return this.getLangsFromPref(NEVER_TRANSLATE_LANGS_PREF);
|
|
},
|
|
|
|
/**
|
|
* Handles updating the UI components on pref or permission changes.
|
|
*/
|
|
observe(aSubject, aTopic, aData) {
|
|
if (aTopic === "perm-changed") {
|
|
if (aData === "cleared") {
|
|
// Permissions have been cleared
|
|
if (!this._neverTranslateSites.length) {
|
|
// There were no sites with permissions set, nothing to do.
|
|
return;
|
|
}
|
|
// Update the tree based on the amount of permissions removed.
|
|
let removed = this._neverTranslateSites.splice(
|
|
0,
|
|
this._neverTranslateSites.length
|
|
);
|
|
this._neverTranslateSiteTree.tree.rowCountChanged(0, -removed.length);
|
|
} else {
|
|
let perm = aSubject.QueryInterface(Ci.nsIPermission);
|
|
if (perm.type != TRANSLATIONS_PERMISSION) {
|
|
// The updated permission was not for Translations, nothing to do.
|
|
return;
|
|
}
|
|
if (aData === "added") {
|
|
if (perm.capability != Services.perms.DENY_ACTION) {
|
|
// We are only showing data for sites we should never translate.
|
|
// If the permission is not DENY_ACTION, we don't care about it here.
|
|
return;
|
|
}
|
|
this._neverTranslateSites.push(perm.principal.origin);
|
|
this._neverTranslateSites.sort();
|
|
let tree = this._neverTranslateSiteTree.tree;
|
|
tree.rowCountChanged(0, 1);
|
|
tree.invalidate();
|
|
} else if (aData == "deleted") {
|
|
let index = this._neverTranslateSites.indexOf(perm.principal.origin);
|
|
if (index == -1) {
|
|
// The deleted permission was not in the tree, nothing to do.
|
|
return;
|
|
}
|
|
this._neverTranslateSites.splice(index, 1);
|
|
this._neverTranslateSiteTree.tree.rowCountChanged(index, -1);
|
|
}
|
|
}
|
|
// Ensure the UI updates to the changes.
|
|
this.onSelectNeverTranslateSite();
|
|
} else if (aTopic === "nsPref:changed") {
|
|
switch (aData) {
|
|
case ALWAYS_TRANSLATE_LANGS_PREF: {
|
|
this._alwaysTranslateLangs = this.getAlwaysTranslateLanguages();
|
|
|
|
let alwaysTranslateLangsChange =
|
|
this._alwaysTranslateLangs.length -
|
|
this._alwaysTranslateLangsTree.rowCount;
|
|
|
|
this._alwaysTranslateLangsTree._data = this._alwaysTranslateLangs;
|
|
let alwaysTranslateLangsTree = this._alwaysTranslateLangsTree.tree;
|
|
|
|
if (alwaysTranslateLangsChange) {
|
|
alwaysTranslateLangsTree.rowCountChanged(
|
|
0,
|
|
alwaysTranslateLangsChange
|
|
);
|
|
}
|
|
|
|
alwaysTranslateLangsTree.invalidate();
|
|
|
|
// Ensure the UI updates to the changes.
|
|
this.onSelectAlwaysTranslateLanguage();
|
|
break;
|
|
}
|
|
case NEVER_TRANSLATE_LANGS_PREF: {
|
|
this._neverTranslateLangs = this.getNeverTranslateLanguages();
|
|
|
|
let neverTranslateLangsChange =
|
|
this._neverTranslateLangs.length -
|
|
this._neverTranslateLangsTree.rowCount;
|
|
|
|
this._neverTranslateLangsTree._data = this._neverTranslateLangs;
|
|
let neverTranslateLangsTree = this._neverTranslateLangsTree.tree;
|
|
|
|
if (neverTranslateLangsChange) {
|
|
neverTranslateLangsTree.rowCountChanged(
|
|
0,
|
|
neverTranslateLangsChange
|
|
);
|
|
}
|
|
|
|
neverTranslateLangsTree.invalidate();
|
|
|
|
// Ensure the UI updates to the changes.
|
|
this.onSelectNeverTranslateLanguage();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
handleEvent(event) {
|
|
switch (event.type) {
|
|
case "unload":
|
|
this.removeObservers();
|
|
break;
|
|
case "command":
|
|
switch (event.target.id) {
|
|
case "key_close":
|
|
window.close();
|
|
break;
|
|
|
|
case "removeAlwaysTranslateLanguage":
|
|
this.onRemoveAlwaysTranslateLanguage();
|
|
break;
|
|
case "removeAllAlwaysTranslateLanguages":
|
|
this.onRemoveAllAlwaysTranslateLanguages();
|
|
break;
|
|
case "removeNeverTranslateLanguage":
|
|
this.onRemoveNeverTranslateLanguage();
|
|
break;
|
|
case "removeAllNeverTranslateLanguages":
|
|
this.onRemoveAllNeverTranslateLanguages();
|
|
break;
|
|
case "removeNeverTranslateSite":
|
|
this.onRemoveNeverTranslateSite();
|
|
break;
|
|
case "removeAllNeverTranslateSites":
|
|
this.onRemoveAllNeverTranslateSites();
|
|
break;
|
|
}
|
|
break;
|
|
case "keypress":
|
|
switch (event.currentTarget.id) {
|
|
case "alwaysTranslateLanguagesTree":
|
|
this.onAlwaysTranslateLanguageKeyPress(event);
|
|
break;
|
|
case "neverTranslateLanguagesTree":
|
|
this.onNeverTranslateLanguageKeyPress(event);
|
|
break;
|
|
case "neverTranslateSitesTree":
|
|
this.onNeverTranslateSiteKeyPress(event);
|
|
break;
|
|
}
|
|
break;
|
|
case "select":
|
|
switch (event.currentTarget.id) {
|
|
case "alwaysTranslateLanguagesTree":
|
|
this.onSelectAlwaysTranslateLanguage();
|
|
break;
|
|
case "neverTranslateLanguagesTree":
|
|
this.onSelectNeverTranslateLanguage();
|
|
break;
|
|
case "neverTranslateSitesTree":
|
|
this.onSelectNeverTranslateSite();
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Ensures that buttons states are enabled/disabled accordingly based on the
|
|
* content of the trees.
|
|
*
|
|
* The remove button should be enabled only if an item is selected.
|
|
* The removeAll button should be enabled any time the tree has content.
|
|
*
|
|
* @param {Tree} aTree
|
|
* @param {string} aIdPart
|
|
*/
|
|
_handleButtonDisabling(aTree, aIdPart) {
|
|
let empty = aTree.isEmpty;
|
|
document.getElementById("removeAll" + aIdPart + "s").disabled = empty;
|
|
document.getElementById("remove" + aIdPart).disabled =
|
|
empty || !aTree.hasSelection;
|
|
},
|
|
|
|
/**
|
|
* Updates the UI state for the always-translate languages section.
|
|
*/
|
|
onSelectAlwaysTranslateLanguage() {
|
|
this._handleButtonDisabling(
|
|
this._alwaysTranslateLangsTree,
|
|
"AlwaysTranslateLanguage"
|
|
);
|
|
},
|
|
|
|
/**
|
|
* Updates the UI state for the never-translate languages section.
|
|
*/
|
|
onSelectNeverTranslateLanguage() {
|
|
this._handleButtonDisabling(
|
|
this._neverTranslateLangsTree,
|
|
"NeverTranslateLanguage"
|
|
);
|
|
},
|
|
|
|
/**
|
|
* Updates the UI state for the never-translate sites section.
|
|
*/
|
|
onSelectNeverTranslateSite() {
|
|
this._handleButtonDisabling(
|
|
this._neverTranslateSiteTree,
|
|
"NeverTranslateSite"
|
|
);
|
|
},
|
|
|
|
/**
|
|
* Updates the value of a language pref to match when a language is removed
|
|
* through the UI.
|
|
*
|
|
* @param {string} pref
|
|
* @param {Tree} tree
|
|
*/
|
|
_onRemoveLanguage(pref, tree) {
|
|
let langs = Services.prefs.getCharPref(pref);
|
|
if (!langs) {
|
|
return;
|
|
}
|
|
|
|
let removed = tree.getSelectedItems().map(l => l.langCode);
|
|
|
|
langs = langs.split(",").filter(l => !removed.includes(l));
|
|
Services.prefs.setCharPref(pref, langs.join(","));
|
|
},
|
|
|
|
/**
|
|
* Updates the never-translate language pref when a never-translate language
|
|
* is removed via the UI.
|
|
*/
|
|
onRemoveAlwaysTranslateLanguage() {
|
|
this._onRemoveLanguage(
|
|
ALWAYS_TRANSLATE_LANGS_PREF,
|
|
this._alwaysTranslateLangsTree
|
|
);
|
|
},
|
|
|
|
/**
|
|
* Updates the always-translate language pref when a always-translate language
|
|
* is removed via the UI.
|
|
*/
|
|
onRemoveNeverTranslateLanguage() {
|
|
this._onRemoveLanguage(
|
|
NEVER_TRANSLATE_LANGS_PREF,
|
|
this._neverTranslateLangsTree
|
|
);
|
|
},
|
|
|
|
/**
|
|
* Updates the permissions for a never-translate site when it is removed via the UI.
|
|
*/
|
|
onRemoveNeverTranslateSite() {
|
|
let removedNeverTranslateSites =
|
|
this._neverTranslateSiteTree.getSelectedItems();
|
|
for (let origin of removedNeverTranslateSites) {
|
|
TranslationsParent.setNeverTranslateSiteByOrigin(false, origin);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Clears the always-translate languages pref when the list is cleared in the UI.
|
|
*/
|
|
onRemoveAllAlwaysTranslateLanguages() {
|
|
Services.prefs.setCharPref(ALWAYS_TRANSLATE_LANGS_PREF, "");
|
|
},
|
|
|
|
/**
|
|
* Clears the never-translate languages pref when the list is cleared in the UI.
|
|
*/
|
|
onRemoveAllNeverTranslateLanguages() {
|
|
Services.prefs.setCharPref(NEVER_TRANSLATE_LANGS_PREF, "");
|
|
},
|
|
|
|
/**
|
|
* Clears the never-translate sites pref when the list is cleared in the UI.
|
|
*/
|
|
onRemoveAllNeverTranslateSites() {
|
|
if (this._neverTranslateSiteTree.isEmpty) {
|
|
return;
|
|
}
|
|
|
|
let removedNeverTranslateSites = this._neverTranslateSites.splice(
|
|
0,
|
|
this._neverTranslateSites.length
|
|
);
|
|
this._neverTranslateSiteTree.tree.rowCountChanged(
|
|
0,
|
|
-removedNeverTranslateSites.length
|
|
);
|
|
|
|
for (let origin of removedNeverTranslateSites) {
|
|
TranslationsParent.setNeverTranslateSiteByOrigin(false, origin);
|
|
}
|
|
|
|
this.onSelectNeverTranslateSite();
|
|
},
|
|
|
|
/**
|
|
* Handles removing a selected always-translate language via the keyboard.
|
|
*/
|
|
onAlwaysTranslateLanguageKeyPress(aEvent) {
|
|
if (aEvent.keyCode == KeyEvent.DOM_VK_DELETE) {
|
|
this.onRemoveAlwaysTranslateLanguage();
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Handles removing a selected never-translate language via the keyboard.
|
|
*/
|
|
onNeverTranslateLanguageKeyPress(aEvent) {
|
|
if (aEvent.keyCode == KeyEvent.DOM_VK_DELETE) {
|
|
this.onRemoveNeverTranslateLanguage();
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Handles removing a selected never-translate site via the keyboard.
|
|
*/
|
|
onNeverTranslateSiteKeyPress(aEvent) {
|
|
if (aEvent.keyCode == KeyEvent.DOM_VK_DELETE) {
|
|
this.onRemoveNeverTranslateSite();
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Removes any active preference and permissions observers.
|
|
*/
|
|
removeObservers() {
|
|
Services.obs.removeObserver(this, "perm-changed");
|
|
Services.prefs.removeObserver(ALWAYS_TRANSLATE_LANGS_PREF, this);
|
|
Services.prefs.removeObserver(NEVER_TRANSLATE_LANGS_PREF, this);
|
|
},
|
|
};
|
|
|
|
window.addEventListener("load", () => gTranslationsSettings.onLoad());
|