forked from mirrors/gecko-dev
		
	 15b4f36f56
			
		
	
	
		15b4f36f56
		
	
	
	
	
		
			
			Part 1 added support for changing default permissions via pref. This patch adds support in the frontend code, which is required to actually make it work for most permission prompts. This patch introduces the concept of SitePermissions.PROMPT (which already exists in the permission manager) to distinguish between the default UNKNOWN state and the explicit PROMPT state. They both have the same effect (always asking the user for confirmation). MozReview-Commit-ID: 2Gg9uwigter --HG-- extra : rebase_source : 2c8da24f849cee53e17be8897c0b320ca9e39e7e
		
			
				
	
	
		
			336 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			336 lines
		
	
	
	
		
			10 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/. */
 | |
| 
 | |
| Components.utils.import("resource://gre/modules/Services.jsm");
 | |
| Components.utils.import("resource://gre/modules/AppConstants.jsm");
 | |
| Components.utils.import("resource:///modules/SitePermissions.jsm");
 | |
| 
 | |
| function Permission(principal, type, capability, capabilityString) {
 | |
|   this.principal = principal;
 | |
|   this.origin = principal.origin;
 | |
|   this.type = type;
 | |
|   this.capability = capability;
 | |
|   this.capabilityString = capabilityString;
 | |
| }
 | |
| 
 | |
| const PERMISSION_STATES = [SitePermissions.ALLOW, SitePermissions.BLOCK, SitePermissions.PROMPT];
 | |
| 
 | |
| var gSitePermissionsManager = {
 | |
|   _type: "",
 | |
|   _isObserving: false,
 | |
|   _permissions: new Map(),
 | |
|   _permissionsToChange: new Map(),
 | |
|   _permissionsToDelete: new Map(),
 | |
|   _list: null,
 | |
|   _bundle: null,
 | |
|   _removeButton: null,
 | |
|   _removeAllButton: null,
 | |
|   _searchBox: null,
 | |
| 
 | |
|   onLoad() {
 | |
|     let params = window.arguments[0];
 | |
|     this.init(params);
 | |
|   },
 | |
| 
 | |
|   init(params) {
 | |
|     if (!this._isObserving) {
 | |
|       Services.obs.addObserver(this, "perm-changed");
 | |
|       this._isObserving = true;
 | |
|     }
 | |
| 
 | |
|     this._bundle = document.getElementById("bundlePreferences");
 | |
|     this._type = params.permissionType;
 | |
|     this._list = document.getElementById("permissionsBox");
 | |
|     this._removeButton = document.getElementById("removePermission");
 | |
|     this._removeAllButton = document.getElementById("removeAllPermissions");
 | |
|     this._searchBox = document.getElementById("searchBox");
 | |
| 
 | |
|     let permissionsText = document.getElementById("permissionsText");
 | |
|     while (permissionsText.hasChildNodes())
 | |
|       permissionsText.firstChild.remove();
 | |
|     permissionsText.appendChild(document.createTextNode(params.introText));
 | |
| 
 | |
|     document.title = params.windowTitle;
 | |
| 
 | |
|     this._loadPermissions();
 | |
|     this.buildPermissionsList();
 | |
| 
 | |
|     this._searchBox.focus();
 | |
|   },
 | |
| 
 | |
|   uninit() {
 | |
|     if (this._isObserving) {
 | |
|       Services.obs.removeObserver(this, "perm-changed");
 | |
|       this._isObserving = false;
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   observe(subject, topic, data) {
 | |
|     if (topic !== "perm-changed")
 | |
|       return;
 | |
| 
 | |
|     let permission = subject.QueryInterface(Components.interfaces.nsIPermission);
 | |
| 
 | |
|     // Ignore unrelated permission types and permissions with unknown states.
 | |
|     if (permission.type !== this._type || !PERMISSION_STATES.includes(permission.capability))
 | |
|       return;
 | |
| 
 | |
|     if (data == "added") {
 | |
|       this._addPermissionToList(permission);
 | |
|       this.buildPermissionsList();
 | |
|     } else if (data == "changed") {
 | |
|       let p = this._permissions.get(permission.principal.origin);
 | |
|       p.capability = permission.capability;
 | |
|       p.capabilityString = this._getCapabilityString(permission.capability);
 | |
|       this._handleCapabilityChange(p);
 | |
|       this.buildPermissionsList();
 | |
|     } else if (data == "deleted") {
 | |
|       this._removePermissionFromList(permission.principal.origin);
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   _handleCapabilityChange(perm) {
 | |
|     let permissionlistitem = document.getElementsByAttribute("origin", perm.origin)[0];
 | |
|     let menulist = permissionlistitem.getElementsByTagName("menulist")[0];
 | |
|     menulist.selectedItem =
 | |
|       menulist.getElementsByAttribute("value", perm.capability)[0];
 | |
|   },
 | |
| 
 | |
|   _getCapabilityString(capability) {
 | |
|     let stringKey = null;
 | |
|     switch (capability) {
 | |
|     case Services.perms.ALLOW_ACTION:
 | |
|       stringKey = "can";
 | |
|       break;
 | |
|     case Services.perms.DENY_ACTION:
 | |
|       stringKey = "cannot";
 | |
|       break;
 | |
|     case Services.perms.PROMPT_ACTION:
 | |
|       stringKey = "prompt";
 | |
|       break;
 | |
|     }
 | |
|     return this._bundle.getString(stringKey);
 | |
|   },
 | |
| 
 | |
|   _addPermissionToList(perm) {
 | |
|     // Ignore unrelated permission types and permissions with unknown states.
 | |
|     if (perm.type !== this._type || !PERMISSION_STATES.includes(perm.capability))
 | |
|       return;
 | |
|     let capabilityString = this._getCapabilityString(perm.capability);
 | |
|     let p = new Permission(perm.principal, perm.type, perm.capability,
 | |
|                            capabilityString);
 | |
|     this._permissions.set(p.origin, p);
 | |
|   },
 | |
| 
 | |
|   _removePermissionFromList(origin) {
 | |
|     this._permissions.delete(origin);
 | |
|     let permissionlistitem = document.getElementsByAttribute("origin", origin)[0];
 | |
|     this._list.removeItemAt(this._list.getIndexOfItem(permissionlistitem));
 | |
|   },
 | |
| 
 | |
|   _loadPermissions() {
 | |
|     // load permissions into a table.
 | |
|     let enumerator = Services.perms.enumerator;
 | |
|     while (enumerator.hasMoreElements()) {
 | |
|       let nextPermission = enumerator.getNext().QueryInterface(Components.interfaces.nsIPermission);
 | |
|       this._addPermissionToList(nextPermission);
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   _createPermissionListItem(permission) {
 | |
|     let richlistitem = document.createElement("richlistitem");
 | |
|     richlistitem.setAttribute("origin", permission.origin);
 | |
|     let row = document.createElement("hbox");
 | |
|     row.setAttribute("flex", "1");
 | |
| 
 | |
|     let hbox = document.createElement("hbox");
 | |
|     let website = document.createElement("label");
 | |
|     website.setAttribute("value", permission.origin);
 | |
|     website.setAttribute("width", "50");
 | |
|     hbox.setAttribute("class", "website-name");
 | |
|     hbox.setAttribute("flex", "3");
 | |
|     hbox.appendChild(website);
 | |
| 
 | |
|     let menulist = document.createElement("menulist");
 | |
|     let menupopup = document.createElement("menupopup");
 | |
|     menulist.setAttribute("flex", "1");
 | |
|     menulist.setAttribute("width", "50");
 | |
|     menulist.setAttribute("class", "website-status");
 | |
|     menulist.appendChild(menupopup);
 | |
|     let states = SitePermissions.getAvailableStates(permission.type);
 | |
|     for (let state of states) {
 | |
|       // Work around the (rare) edge case when a user has changed their
 | |
|       // default permission type back to UNKNOWN while still having a
 | |
|       // PROMPT permission set for an origin.
 | |
|       if (state == SitePermissions.UNKNOWN &&
 | |
|           permission.capability == SitePermissions.PROMPT) {
 | |
|         state = SitePermissions.PROMPT;
 | |
|       } else if (state == SitePermissions.UNKNOWN) {
 | |
|         continue;
 | |
|       }
 | |
|       let m = document.createElement("menuitem");
 | |
|       m.setAttribute("label", this._getCapabilityString(state));
 | |
|       m.setAttribute("value", state);
 | |
|       menupopup.appendChild(m);
 | |
|     }
 | |
|     menulist.value = permission.capability;
 | |
| 
 | |
|     menulist.addEventListener("select", () => {
 | |
|       this.onPermissionChange(permission, Number(menulist.selectedItem.value));
 | |
|     });
 | |
| 
 | |
|     row.appendChild(hbox);
 | |
|     row.appendChild(menulist);
 | |
|     richlistitem.appendChild(row);
 | |
|     this._list.appendChild(richlistitem);
 | |
|   },
 | |
| 
 | |
|   onWindowKeyPress(event) {
 | |
|     if (event.keyCode == KeyEvent.DOM_VK_ESCAPE)
 | |
|       window.close();
 | |
|   },
 | |
| 
 | |
|   onPermissionKeyPress(event) {
 | |
|     if (!this._list.selectedItem)
 | |
|       return;
 | |
| 
 | |
|     if (event.keyCode == KeyEvent.DOM_VK_DELETE ||
 | |
|        (AppConstants.platform == "macosx" &&
 | |
|         event.keyCode == KeyEvent.DOM_VK_BACK_SPACE)) {
 | |
|       this.onPermissionDelete();
 | |
|       event.preventDefault();
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   _setRemoveButtonState() {
 | |
|     if (!this._list)
 | |
|       return;
 | |
| 
 | |
|     let hasSelection = this._list.selectedIndex >= 0;
 | |
|     let hasRows = this._list.itemCount > 0;
 | |
|     this._removeButton.disabled = !hasSelection;
 | |
|     this._removeAllButton.disabled = !hasRows;
 | |
|   },
 | |
| 
 | |
|   onPermissionDelete() {
 | |
|     let richlistitem = this._list.selectedItem;
 | |
|     let origin = richlistitem.getAttribute("origin");
 | |
|     let permission = this._permissions.get(origin);
 | |
| 
 | |
|     this._removePermissionFromList(origin);
 | |
|     this._permissionsToDelete.set(permission.origin, permission);
 | |
| 
 | |
|     this._setRemoveButtonState();
 | |
|   },
 | |
| 
 | |
|   onAllPermissionsDelete() {
 | |
|     for (let permission of this._permissions.values()) {
 | |
|       this._removePermissionFromList(permission.origin);
 | |
|       this._permissionsToDelete.set(permission.origin, permission);
 | |
|     }
 | |
| 
 | |
|     this._setRemoveButtonState();
 | |
|   },
 | |
| 
 | |
|   onPermissionSelect() {
 | |
|     this._setRemoveButtonState();
 | |
|   },
 | |
| 
 | |
|   onPermissionChange(perm, capability) {
 | |
|     let p = this._permissions.get(perm.origin);
 | |
|     if (p.capability == capability)
 | |
|       return;
 | |
|     p.capability = capability;
 | |
|     p.capabilityString = this._getCapabilityString(capability);
 | |
|     this._permissionsToChange.set(p.origin, p);
 | |
| 
 | |
|     // enable "remove all" button as needed
 | |
|     this._setRemoveButtonState();
 | |
|   },
 | |
| 
 | |
|   onApplyChanges() {
 | |
|     // Stop observing permission changes since we are about
 | |
|     // to write out the pending adds/deletes and don't need
 | |
|     // to update the UI
 | |
|     this.uninit();
 | |
| 
 | |
|     for (let p of this._permissionsToChange.values()) {
 | |
|       let uri = Services.io.newURI(p.origin);
 | |
|       SitePermissions.set(uri, p.type, p.capability);
 | |
|     }
 | |
| 
 | |
|     for (let p of this._permissionsToDelete.values()) {
 | |
|       let uri = Services.io.newURI(p.origin);
 | |
|       SitePermissions.remove(uri, p.type);
 | |
|     }
 | |
|     window.close();
 | |
|   },
 | |
| 
 | |
|   buildPermissionsList(sortCol) {
 | |
|     // Clear old entries.
 | |
|     let oldItems = this._list.querySelectorAll("richlistitem");
 | |
|     for (let item of oldItems) {
 | |
|       item.remove();
 | |
|     }
 | |
| 
 | |
|     // Sort permissions.
 | |
|     let sortedPermissions = this._sortPermissions(sortCol);
 | |
| 
 | |
|     let keyword = this._searchBox.value.toLowerCase().trim();
 | |
|     for (let permission of sortedPermissions) {
 | |
|       if (keyword && !permission.origin.includes(keyword)) {
 | |
|         continue;
 | |
|       }
 | |
| 
 | |
|       this._createPermissionListItem(permission);
 | |
|     }
 | |
| 
 | |
|     this._setRemoveButtonState();
 | |
|   },
 | |
| 
 | |
|   _sortPermissions(column) {
 | |
|     let permissions = Array.from(this._permissions.values());
 | |
|     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 = null;
 | |
|     switch (column.id) {
 | |
|       case "siteCol":
 | |
|         sortFunc = (a, b) => {
 | |
|           return a.origin.localeCompare(b.origin);
 | |
|         };
 | |
|         break;
 | |
| 
 | |
|       case "statusCol":
 | |
|         sortFunc = (a, b) => {
 | |
|           return a.capabilityString.localeCompare(b.capabilityString);
 | |
|         };
 | |
|         break;
 | |
|     }
 | |
| 
 | |
|     if (sortDirection === "descending") {
 | |
|       permissions.sort((a, b) => sortFunc(b, a));
 | |
|     } else {
 | |
|       permissions.sort(sortFunc);
 | |
|     }
 | |
| 
 | |
|     let cols = this._list.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);
 | |
| 
 | |
|     return permissions;
 | |
|   },
 | |
| };
 |