forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			288 lines
		
	
	
	
		
			7.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			288 lines
		
	
	
	
		
			7.6 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._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();
 | |
| });
 | 
