From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:32:43 +0200 Subject: Adding upstream version 1:115.7.0. Signed-off-by: Daniel Baumann --- .../preferences/dialogs/dohExceptions.js | 287 +++++++++++++++++++++ 1 file changed, 287 insertions(+) create mode 100644 browser/components/preferences/dialogs/dohExceptions.js (limited to 'browser/components/preferences/dialogs/dohExceptions.js') diff --git a/browser/components/preferences/dialogs/dohExceptions.js b/browser/components/preferences/dialogs/dohExceptions.js new file mode 100644 index 0000000000..f4c1d4d80d --- /dev/null +++ b/browser/components/preferences/dialogs/dohExceptions.js @@ -0,0 +1,287 @@ +/* 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._urlField = document.getElementById("url"); + this.onExceptionInput(); + + this._loadExceptions(); + this.buildExceptionList(); + + this._urlField.focus(); + + this._prefLocked = Services.prefs.prefIsLocked( + "network.trr.excluded-domains" + ); + + this._btnAddException.disabled = this._prefLocked; + document.getElementById("exceptionDialog").getButton("accept").disabled = + this._prefLocked; + this._urlField.disabled = this._prefLocked; + }, + + _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(); +}); -- cgit v1.2.3