mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-09 11:25:00 +00:00
Bug 1596847 - DoH settings UI for excluded domains, r=fluent-reviewers,settings-reviewers,Gijs,kershaw
Differential Revision: https://phabricator.services.mozilla.com/D173521
This commit is contained in:
parent
d735948483
commit
994ea8292e
288
browser/components/preferences/dialogs/dohExceptions.js
Normal file
288
browser/components/preferences/dialogs/dohExceptions.js
Normal file
@ -0,0 +1,288 @@
|
||||
/* 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();
|
||||
});
|
69
browser/components/preferences/dialogs/dohExceptions.xhtml
Normal file
69
browser/components/preferences/dialogs/dohExceptions.xhtml
Normal file
@ -0,0 +1,69 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<!-- 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/. -->
|
||||
|
||||
<?xml-stylesheet href="chrome://global/skin/global.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://browser/content/preferences/dialogs/sitePermissions.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://browser/skin/preferences/preferences.css" type="text/css"?>
|
||||
|
||||
<window id="DoHExceptionsDialog"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
xmlns:html="http://www.w3.org/1999/xhtml"
|
||||
data-l10n-id="permissions-exceptions-doh-window"
|
||||
data-l10n-attrs="title, style"
|
||||
persist="width height">
|
||||
|
||||
<dialog id="exceptionDialog"
|
||||
buttons="accept,cancel"
|
||||
data-l10n-id="permission-dialog"
|
||||
data-l10n-attrs="buttonlabelaccept, buttonaccesskeyaccept">
|
||||
|
||||
<linkset>
|
||||
<html:link rel="localization" href="branding/brand.ftl"/>
|
||||
<html:link rel="localization" href="browser/preferences/permissions.ftl"/>
|
||||
</linkset>
|
||||
|
||||
<script src="chrome://browser/content/preferences/dialogs/dohExceptions.js"/>
|
||||
|
||||
<keyset>
|
||||
<key data-l10n-id="permissions-close-key" modifiers="accel" oncommand="window.close();"/>
|
||||
</keyset>
|
||||
|
||||
<vbox class="contentPane">
|
||||
<description id="dohExceptionText" control="url" data-l10n-id="permissions-exceptions-manage-doh-desc"/>
|
||||
<separator class="thin"/>
|
||||
<label id="urlLabel" control="url" data-l10n-id="permissions-doh-entry-field"/>
|
||||
<hbox align="start">
|
||||
<html:input id="url" type="text"
|
||||
style="flex: 1;"
|
||||
oninput="gDoHExceptionsManager.onExceptionInput();"
|
||||
onkeypress="gDoHExceptionsManager.onExceptionKeyPress(event);"/>
|
||||
</hbox>
|
||||
<hbox pack="end">
|
||||
<button id="btnAddException" disabled="true" data-l10n-id="permissions-doh-add-exception"
|
||||
oncommand="gDoHExceptionsManager.addException();"/>
|
||||
</hbox>
|
||||
<separator class="thin"/>
|
||||
<listheader>
|
||||
<treecol id="siteCol" data-l10n-id="permissions-doh-col" style="flex: 3 3 auto; width: 0"
|
||||
data-isCurrentSortCol = "true"
|
||||
onclick="gDoHExceptionsManager.buildExceptionList(event.target)"
|
||||
/>
|
||||
</listheader>
|
||||
<richlistbox id="permissionsBox" selected="false"
|
||||
onkeypress="gDoHExceptionsManager.onListBoxKeyPress(event);"
|
||||
onselect="gDoHExceptionsManager.onListBoxSelect();"/>
|
||||
</vbox>
|
||||
|
||||
<hbox class="actionButtons">
|
||||
<button id="removeException" disabled="true"
|
||||
data-l10n-id="permissions-doh-remove"
|
||||
oncommand="gDoHExceptionsManager.onExceptionDelete();"/>
|
||||
<button id="removeAllExceptions"
|
||||
data-l10n-id="permissions-doh-remove-all"
|
||||
oncommand="gDoHExceptionsManager.onAllExceptionsDelete();"/>
|
||||
</hbox>
|
||||
</dialog>
|
||||
</window>
|
@ -19,6 +19,8 @@ browser.jar:
|
||||
content/browser/preferences/dialogs/colors.js
|
||||
content/browser/preferences/dialogs/connection.xhtml
|
||||
content/browser/preferences/dialogs/connection.js
|
||||
content/browser/preferences/dialogs/dohExceptions.xhtml
|
||||
content/browser/preferences/dialogs/dohExceptions.js
|
||||
content/browser/preferences/dialogs/fonts.xhtml
|
||||
content/browser/preferences/dialogs/fonts.js
|
||||
content/browser/preferences/dialogs/handlers.css
|
||||
|
@ -1215,6 +1215,21 @@
|
||||
<label class="doh-status-label" id="dohResolver"/>
|
||||
<label class="doh-status-label" id="dohSteeringStatus" data-l10n-id="preferences-doh-steering-status" hidden="true"/>
|
||||
</vbox>
|
||||
<hbox id="dohExceptionBox">
|
||||
<label flex="1" data-l10n-id="preferences-doh-exceptions-description"/>
|
||||
<button id="dohExceptionsButton"
|
||||
is="highlightable-button"
|
||||
class="accessory-button"
|
||||
data-l10n-id="preferences-doh-manage-exceptions"
|
||||
search-l10n-ids="
|
||||
permissions-doh-entry-field,
|
||||
permissions-doh-add-exception.label,
|
||||
permissions-doh-remove.label,
|
||||
permissions-doh-remove-all.label,
|
||||
permissions-exceptions-doh-window.title,
|
||||
permissions-exceptions-manage-doh-desc,
|
||||
"/>
|
||||
</hbox>
|
||||
<vbox>
|
||||
<label><html:h2 id="dohGroupMessage" data-l10n-id="preferences-doh-group-message"/></label>
|
||||
<vbox id="dohCategories">
|
||||
|
@ -887,6 +887,11 @@ var gPrivacyPane = {
|
||||
"command",
|
||||
gPrivacyPane.showHttpsOnlyModeExceptions
|
||||
);
|
||||
setEventListener(
|
||||
"dohExceptionsButton",
|
||||
"command",
|
||||
gPrivacyPane.showDoHExceptions
|
||||
);
|
||||
setEventListener(
|
||||
"clearDataSettings",
|
||||
"command",
|
||||
@ -2210,6 +2215,13 @@ var gPrivacyPane = {
|
||||
);
|
||||
},
|
||||
|
||||
showDoHExceptions() {
|
||||
gSubDialog.open(
|
||||
"chrome://browser/content/preferences/dialogs/dohExceptions.xhtml",
|
||||
undefined
|
||||
);
|
||||
},
|
||||
|
||||
showSiteDataSettings() {
|
||||
gSubDialog.open(
|
||||
"chrome://browser/content/preferences/dialogs/siteDataSettings.xhtml"
|
||||
|
@ -197,3 +197,26 @@ permissions-site-speaker-window =
|
||||
.title = Settings - Speaker Permissions
|
||||
.style = { permissions-window2.style }
|
||||
permissions-site-speaker-desc = The following websites have requested to select an audio output device. You can specify which websites are allowed to select an audio output device.
|
||||
|
||||
permissions-exceptions-doh-window =
|
||||
.title = Website Exceptions for DNS over HTTPS
|
||||
.style = { permissions-window2.style }
|
||||
permissions-exceptions-manage-doh-desc = { -brand-short-name } won’t use secure DNS on these sites and their subdomains.
|
||||
|
||||
permissions-doh-entry-field = Enter website domain name
|
||||
.accesskey = d
|
||||
|
||||
permissions-doh-add-exception =
|
||||
.label = Add
|
||||
.accesskey = A
|
||||
|
||||
permissions-doh-col =
|
||||
.label = Domain
|
||||
|
||||
permissions-doh-remove =
|
||||
.label = Remove
|
||||
.accesskey = R
|
||||
|
||||
permissions-doh-remove-all =
|
||||
.label = Remove All
|
||||
.accesskey = e
|
||||
|
@ -1530,6 +1530,12 @@ preferences-doh-checkbox-warn =
|
||||
|
||||
preferences-doh-select-resolver = Choose provider:
|
||||
|
||||
preferences-doh-exceptions-description = { -brand-short-name } won’t use secure DNS on these sites
|
||||
|
||||
preferences-doh-manage-exceptions =
|
||||
.label = Manage Exceptions…
|
||||
.accesskey = x
|
||||
|
||||
## The following strings are used in the Download section of settings
|
||||
|
||||
desktop-folder-name = Desktop
|
||||
|
Loading…
Reference in New Issue
Block a user