321 lines
8.4 KiB
JavaScript
321 lines
8.4 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/. */
|
|
|
|
const { AppConstants } = ChromeUtils.importESModule(
|
|
"resource://gre/modules/AppConstants.sys.mjs"
|
|
);
|
|
|
|
var gDoHExceptionsManager = {
|
|
_exceptions: new Set(),
|
|
_list: null,
|
|
_prefLocked: false,
|
|
|
|
init() {
|
|
document.addEventListener("dialogaccept", () => this.onApplyChanges());
|
|
|
|
this._btnAddException = document.getElementById("btnAddException");
|
|
this._removeButton = document.getElementById("removeException");
|
|
this._removeAllButton = document.getElementById("removeAllExceptions");
|
|
|
|
this._list = document.getElementById("permissionsBox");
|
|
this._list.addEventListener("keypress", event =>
|
|
this.onListBoxKeyPress(event)
|
|
);
|
|
this._list.addEventListener("select", () => this.onListBoxSelect());
|
|
|
|
this._urlField = document.getElementById("url");
|
|
this._urlField.addEventListener("input", () => this.onExceptionInput());
|
|
this._urlField.addEventListener("keypress", event =>
|
|
this.onExceptionKeyPress(event)
|
|
);
|
|
|
|
document
|
|
.getElementById("siteCol")
|
|
.addEventListener("click", event =>
|
|
this.buildExceptionList(event.target)
|
|
);
|
|
|
|
document.addEventListener("command", this);
|
|
|
|
this.onExceptionInput();
|
|
this._loadExceptions();
|
|
this.buildExceptionList();
|
|
|
|
this._urlField.focus();
|
|
|
|
this._prefLocked = Services.prefs.prefIsLocked(
|
|
"network.trr.excluded-domains"
|
|
);
|
|
|
|
document.getElementById("exceptionDialog").getButton("accept").disabled =
|
|
this._prefLocked;
|
|
this._urlField.disabled = this._prefLocked;
|
|
},
|
|
|
|
handleEvent(event) {
|
|
switch (event.target.id) {
|
|
case "key_close":
|
|
window.close();
|
|
break;
|
|
|
|
case "btnAddException":
|
|
this.addException();
|
|
break;
|
|
case "removeException":
|
|
this.onExceptionDelete();
|
|
break;
|
|
case "removeAllExceptions":
|
|
this.onAllExceptionsDelete();
|
|
break;
|
|
}
|
|
},
|
|
|
|
_loadExceptions() {
|
|
let exceptionsFromPref = Services.prefs.getStringPref(
|
|
"network.trr.excluded-domains"
|
|
);
|
|
|
|
if (!exceptionsFromPref?.trim()) {
|
|
return;
|
|
}
|
|
|
|
let exceptions = exceptionsFromPref.trim().split(",");
|
|
for (let exception of exceptions) {
|
|
let trimmed = exception.trim();
|
|
if (trimmed) {
|
|
this._exceptions.add(trimmed);
|
|
}
|
|
}
|
|
},
|
|
|
|
addException() {
|
|
if (this._prefLocked) {
|
|
return;
|
|
}
|
|
|
|
let textbox = document.getElementById("url");
|
|
let inputValue = textbox.value.trim(); // trim any leading and trailing space
|
|
if (!inputValue.startsWith("http:") && !inputValue.startsWith("https:")) {
|
|
inputValue = `http://${inputValue}`;
|
|
}
|
|
let domain = "";
|
|
try {
|
|
let uri = Services.io.newURI(inputValue);
|
|
domain = uri.host;
|
|
} catch (ex) {
|
|
document.l10n
|
|
.formatValues([
|
|
{ id: "permissions-invalid-uri-title" },
|
|
{ id: "permissions-invalid-uri-label" },
|
|
])
|
|
.then(([title, message]) => {
|
|
Services.prompt.alert(window, title, message);
|
|
});
|
|
return;
|
|
}
|
|
|
|
if (!this._exceptions.has(domain)) {
|
|
this._exceptions.add(domain);
|
|
this.buildExceptionList();
|
|
}
|
|
|
|
textbox.value = "";
|
|
textbox.focus();
|
|
|
|
// covers a case where the site exists already, so the buttons don't disable
|
|
this.onExceptionInput();
|
|
|
|
// enable "remove all" button as needed
|
|
this._setRemoveButtonState();
|
|
},
|
|
|
|
onExceptionInput() {
|
|
this._btnAddException.disabled = !this._urlField.value;
|
|
},
|
|
|
|
onExceptionKeyPress(event) {
|
|
if (event.keyCode == KeyEvent.DOM_VK_RETURN) {
|
|
this._btnAddException.click();
|
|
if (document.activeElement == this._urlField) {
|
|
event.preventDefault();
|
|
}
|
|
}
|
|
},
|
|
|
|
onListBoxKeyPress(event) {
|
|
if (!this._list.selectedItem) {
|
|
return;
|
|
}
|
|
|
|
if (this._prefLocked) {
|
|
return;
|
|
}
|
|
|
|
if (
|
|
event.keyCode == KeyEvent.DOM_VK_DELETE ||
|
|
(AppConstants.platform == "macosx" &&
|
|
event.keyCode == KeyEvent.DOM_VK_BACK_SPACE)
|
|
) {
|
|
this.onExceptionDelete();
|
|
event.preventDefault();
|
|
}
|
|
},
|
|
|
|
onListBoxSelect() {
|
|
this._setRemoveButtonState();
|
|
},
|
|
|
|
_removeExceptionFromList(exception) {
|
|
this._exceptions.delete(exception);
|
|
let exceptionlistitem = document.getElementsByAttribute(
|
|
"domain",
|
|
exception
|
|
)[0];
|
|
if (exceptionlistitem) {
|
|
exceptionlistitem.remove();
|
|
}
|
|
},
|
|
|
|
onExceptionDelete() {
|
|
let richlistitem = this._list.selectedItem;
|
|
let exception = richlistitem.getAttribute("domain");
|
|
|
|
this._removeExceptionFromList(exception);
|
|
|
|
this._setRemoveButtonState();
|
|
},
|
|
|
|
onAllExceptionsDelete() {
|
|
for (let exception of this._exceptions.values()) {
|
|
this._removeExceptionFromList(exception);
|
|
}
|
|
|
|
this._setRemoveButtonState();
|
|
},
|
|
|
|
_createExceptionListItem(exception) {
|
|
let richlistitem = document.createXULElement("richlistitem");
|
|
richlistitem.setAttribute("domain", exception);
|
|
let row = document.createXULElement("hbox");
|
|
row.setAttribute("style", "flex: 1");
|
|
|
|
let hbox = document.createXULElement("hbox");
|
|
let website = document.createXULElement("label");
|
|
website.setAttribute("class", "website-name-value");
|
|
website.setAttribute("value", exception);
|
|
hbox.setAttribute("class", "website-name");
|
|
hbox.setAttribute("style", "flex: 3 3; width: 0");
|
|
hbox.appendChild(website);
|
|
row.appendChild(hbox);
|
|
|
|
richlistitem.appendChild(row);
|
|
return richlistitem;
|
|
},
|
|
|
|
_sortExceptions(list, frag, column) {
|
|
let sortDirection;
|
|
|
|
if (!column) {
|
|
column = document.querySelector("treecol[data-isCurrentSortCol=true]");
|
|
sortDirection =
|
|
column.getAttribute("data-last-sortDirection") || "ascending";
|
|
} else {
|
|
sortDirection = column.getAttribute("data-last-sortDirection");
|
|
sortDirection =
|
|
sortDirection === "ascending" ? "descending" : "ascending";
|
|
}
|
|
|
|
let sortFunc = (a, b) => {
|
|
return comp.compare(a.getAttribute("domain"), b.getAttribute("domain"));
|
|
};
|
|
|
|
let comp = new Services.intl.Collator(undefined, {
|
|
usage: "sort",
|
|
});
|
|
|
|
let items = Array.from(frag.querySelectorAll("richlistitem"));
|
|
|
|
if (sortDirection === "descending") {
|
|
items.sort((a, b) => sortFunc(b, a));
|
|
} else {
|
|
items.sort(sortFunc);
|
|
}
|
|
|
|
// Re-append items in the correct order:
|
|
items.forEach(item => frag.appendChild(item));
|
|
|
|
let cols = list.previousElementSibling.querySelectorAll("treecol");
|
|
cols.forEach(c => {
|
|
c.removeAttribute("data-isCurrentSortCol");
|
|
c.removeAttribute("sortDirection");
|
|
});
|
|
column.setAttribute("data-isCurrentSortCol", "true");
|
|
column.setAttribute("sortDirection", sortDirection);
|
|
column.setAttribute("data-last-sortDirection", sortDirection);
|
|
},
|
|
|
|
_setRemoveButtonState() {
|
|
if (!this._list) {
|
|
return;
|
|
}
|
|
|
|
if (this._prefLocked) {
|
|
this._removeAllButton.disabled = true;
|
|
this._removeButton.disabled = true;
|
|
return;
|
|
}
|
|
|
|
let hasSelection = this._list.selectedIndex >= 0;
|
|
|
|
this._removeButton.disabled = !hasSelection;
|
|
let disabledItems = this._list.querySelectorAll(
|
|
"label.website-name-value[disabled='true']"
|
|
);
|
|
|
|
this._removeAllButton.disabled =
|
|
this._list.itemCount == disabledItems.length;
|
|
},
|
|
|
|
onApplyChanges() {
|
|
if (this._exceptions.size == 0) {
|
|
Services.prefs.setStringPref("network.trr.excluded-domains", "");
|
|
return;
|
|
}
|
|
|
|
let exceptions = Array.from(this._exceptions);
|
|
let exceptionPrefString = exceptions.join(",");
|
|
|
|
Services.prefs.setStringPref(
|
|
"network.trr.excluded-domains",
|
|
exceptionPrefString
|
|
);
|
|
},
|
|
|
|
buildExceptionList(sortCol) {
|
|
// Clear old entries.
|
|
let oldItems = this._list.querySelectorAll("richlistitem");
|
|
for (let item of oldItems) {
|
|
item.remove();
|
|
}
|
|
let frag = document.createDocumentFragment();
|
|
|
|
let exceptions = Array.from(this._exceptions.values());
|
|
|
|
for (let exception of exceptions) {
|
|
let richlistitem = this._createExceptionListItem(exception);
|
|
frag.appendChild(richlistitem);
|
|
}
|
|
|
|
// Sort exceptions.
|
|
this._sortExceptions(this._list, frag, sortCol);
|
|
|
|
this._list.appendChild(frag);
|
|
|
|
this._setRemoveButtonState();
|
|
},
|
|
};
|
|
|
|
document.addEventListener("DOMContentLoaded", () => {
|
|
gDoHExceptionsManager.init();
|
|
});
|