forked from mirrors/gecko-dev
		
	 dce6901df0
			
		
	
	
		dce6901df0
		
	
	
	
	
		
			
			For speaker permissions, this has the effect of presenting a menulist instead of a label when a device-specific ALLOW permission is set, providing the option to the user of changing this to a BLOCK. A global permission for speaker selection is not currently supported but if it were implemented and set to BLOCK, then a menulist would be shown for all sites with permissions set. It would include at least PROMPT and BLOCK menuitems. This change would mean that the menulist selected item can display "Allowed" when a device-specific ALLOW permission has already been granted through a selectAudioOutput() prompt. _permissionsToChange is modified in _removePermissionFromList() so that permissions changed then deleted are not added by onApplyChanges(). Differential Revision: https://phabricator.services.mozilla.com/D172085
		
			
				
	
	
		
			679 lines
		
	
	
	
		
			21 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			679 lines
		
	
	
	
		
			21 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/. */
 | |
| 
 | |
| /* import-globals-from ../extensionControlled.js */
 | |
| 
 | |
| var { AppConstants } = ChromeUtils.importESModule(
 | |
|   "resource://gre/modules/AppConstants.sys.mjs"
 | |
| );
 | |
| const { SitePermissions } = ChromeUtils.import(
 | |
|   "resource:///modules/SitePermissions.jsm"
 | |
| );
 | |
| 
 | |
| const sitePermissionsL10n = {
 | |
|   "desktop-notification": {
 | |
|     window: "permissions-site-notification-window2",
 | |
|     description: "permissions-site-notification-desc",
 | |
|     disableLabel: "permissions-site-notification-disable-label",
 | |
|     disableDescription: "permissions-site-notification-disable-desc",
 | |
|   },
 | |
|   geo: {
 | |
|     window: "permissions-site-location-window2",
 | |
|     description: "permissions-site-location-desc",
 | |
|     disableLabel: "permissions-site-location-disable-label",
 | |
|     disableDescription: "permissions-site-location-disable-desc",
 | |
|   },
 | |
|   xr: {
 | |
|     window: "permissions-site-xr-window2",
 | |
|     description: "permissions-site-xr-desc",
 | |
|     disableLabel: "permissions-site-xr-disable-label",
 | |
|     disableDescription: "permissions-site-xr-disable-desc",
 | |
|   },
 | |
|   camera: {
 | |
|     window: "permissions-site-camera-window2",
 | |
|     description: "permissions-site-camera-desc",
 | |
|     disableLabel: "permissions-site-camera-disable-label",
 | |
|     disableDescription: "permissions-site-camera-disable-desc",
 | |
|   },
 | |
|   microphone: {
 | |
|     window: "permissions-site-microphone-window2",
 | |
|     description: "permissions-site-microphone-desc",
 | |
|     disableLabel: "permissions-site-microphone-disable-label",
 | |
|     disableDescription: "permissions-site-microphone-disable-desc",
 | |
|   },
 | |
|   speaker: {
 | |
|     window: "permissions-site-speaker-window",
 | |
|     description: "permissions-site-speaker-desc",
 | |
|   },
 | |
|   "autoplay-media": {
 | |
|     window: "permissions-site-autoplay-window2",
 | |
|     description: "permissions-site-autoplay-desc",
 | |
|   },
 | |
| };
 | |
| 
 | |
| const sitePermissionsConfig = {
 | |
|   "autoplay-media": {
 | |
|     _getCapabilityString(capability) {
 | |
|       switch (capability) {
 | |
|         case SitePermissions.ALLOW:
 | |
|           return "permissions-capabilities-autoplay-allow";
 | |
|         case SitePermissions.BLOCK:
 | |
|           return "permissions-capabilities-autoplay-block";
 | |
|         case SitePermissions.AUTOPLAY_BLOCKED_ALL:
 | |
|           return "permissions-capabilities-autoplay-blockall";
 | |
|       }
 | |
|       throw new Error(`Unknown capability: ${capability}`);
 | |
|     },
 | |
|   },
 | |
| };
 | |
| 
 | |
| // A set of permissions for a single origin.  One PermissionGroup instance
 | |
| // corresponds to one row in the gSitePermissionsManager._list richlistbox.
 | |
| // Permissions may be single or double keyed, but the primary key of all
 | |
| // permissions matches the permission type of the dialog.
 | |
| class PermissionGroup {
 | |
|   #changedCapability;
 | |
| 
 | |
|   constructor(perm) {
 | |
|     this.principal = perm.principal;
 | |
|     this.origin = perm.principal.origin;
 | |
|     this.perms = [perm];
 | |
|   }
 | |
|   addPermission(perm) {
 | |
|     this.perms.push(perm);
 | |
|   }
 | |
|   removePermission(perm) {
 | |
|     this.perms = this.perms.filter(p => p.type != perm.type);
 | |
|   }
 | |
|   set capability(cap) {
 | |
|     this.#changedCapability = cap;
 | |
|   }
 | |
|   get capability() {
 | |
|     if (this.#changedCapability) {
 | |
|       return this.#changedCapability;
 | |
|     }
 | |
|     return this.savedCapability;
 | |
|   }
 | |
|   revert() {
 | |
|     this.#changedCapability = null;
 | |
|   }
 | |
|   get savedCapability() {
 | |
|     // This logic to present a single capability for permissions of different
 | |
|     // keys and capabilities caters for speaker-selection, where a block
 | |
|     // permission may be set for all devices with no second key, which would
 | |
|     // override any device-specific double-keyed allow permissions.
 | |
|     let cap;
 | |
|     for (let perm of this.perms) {
 | |
|       let [type] = perm.type.split(SitePermissions.PERM_KEY_DELIMITER);
 | |
|       if (type == perm.type) {
 | |
|         // No second key.  This overrides double-keyed perms.
 | |
|         return perm.capability;
 | |
|       }
 | |
|       // Double-keyed perms are not expected to have different capabilities.
 | |
|       cap = perm.capability;
 | |
|     }
 | |
|     return cap;
 | |
|   }
 | |
| }
 | |
| 
 | |
| const PERMISSION_STATES = [
 | |
|   SitePermissions.ALLOW,
 | |
|   SitePermissions.BLOCK,
 | |
|   SitePermissions.PROMPT,
 | |
|   SitePermissions.AUTOPLAY_BLOCKED_ALL,
 | |
| ];
 | |
| 
 | |
| const NOTIFICATIONS_PERMISSION_OVERRIDE_KEY = "webNotificationsDisabled";
 | |
| const NOTIFICATIONS_PERMISSION_PREF =
 | |
|   "permissions.default.desktop-notification";
 | |
| 
 | |
| const AUTOPLAY_PREF = "media.autoplay.default";
 | |
| 
 | |
| var gSitePermissionsManager = {
 | |
|   _type: "",
 | |
|   _isObserving: false,
 | |
|   _permissionGroups: new Map(),
 | |
|   _permissionsToChange: new Map(),
 | |
|   _permissionsToDelete: new Map(),
 | |
|   _list: null,
 | |
|   _removeButton: null,
 | |
|   _removeAllButton: null,
 | |
|   _searchBox: null,
 | |
|   _checkbox: null,
 | |
|   _currentDefaultPermissionsState: null,
 | |
|   _defaultPermissionStatePrefName: null,
 | |
| 
 | |
|   onLoad() {
 | |
|     let params = window.arguments[0];
 | |
|     document.mozSubdialogReady = this.init(params);
 | |
|   },
 | |
| 
 | |
|   async init(params) {
 | |
|     if (!this._isObserving) {
 | |
|       Services.obs.addObserver(this, "perm-changed");
 | |
|       this._isObserving = true;
 | |
|     }
 | |
| 
 | |
|     document.addEventListener("dialogaccept", () => this.onApplyChanges());
 | |
| 
 | |
|     this._type = params.permissionType;
 | |
|     this._list = document.getElementById("permissionsBox");
 | |
|     this._removeButton = document.getElementById("removePermission");
 | |
|     this._removeAllButton = document.getElementById("removeAllPermissions");
 | |
|     this._searchBox = document.getElementById("searchBox");
 | |
|     this._checkbox = document.getElementById("permissionsDisableCheckbox");
 | |
|     this._disableExtensionButton = document.getElementById(
 | |
|       "disableNotificationsPermissionExtension"
 | |
|     );
 | |
|     this._permissionsDisableDescription = document.getElementById(
 | |
|       "permissionsDisableDescription"
 | |
|     );
 | |
|     this._setAutoplayPref = document.getElementById("setAutoplayPref");
 | |
| 
 | |
|     let permissionsText = document.getElementById("permissionsText");
 | |
| 
 | |
|     document.l10n.pauseObserving();
 | |
|     let l10n = sitePermissionsL10n[this._type];
 | |
|     document.l10n.setAttributes(permissionsText, l10n.description);
 | |
|     if (l10n.disableLabel) {
 | |
|       document.l10n.setAttributes(this._checkbox, l10n.disableLabel);
 | |
|     }
 | |
|     if (l10n.disableDescription) {
 | |
|       document.l10n.setAttributes(
 | |
|         this._permissionsDisableDescription,
 | |
|         l10n.disableDescription
 | |
|       );
 | |
|     }
 | |
|     document.l10n.setAttributes(document.documentElement, l10n.window);
 | |
| 
 | |
|     await document.l10n.translateElements([
 | |
|       permissionsText,
 | |
|       this._checkbox,
 | |
|       this._permissionsDisableDescription,
 | |
|       document.documentElement,
 | |
|     ]);
 | |
|     document.l10n.resumeObserving();
 | |
| 
 | |
|     // Initialize the checkbox state and handle showing notification permission UI
 | |
|     // when it is disabled by an extension.
 | |
|     this._defaultPermissionStatePrefName = "permissions.default." + this._type;
 | |
|     this._watchPermissionPrefChange();
 | |
| 
 | |
|     this._loadPermissions();
 | |
|     this.buildPermissionsList();
 | |
| 
 | |
|     if (params.permissionType == "autoplay-media") {
 | |
|       await this.buildAutoplayMenulist();
 | |
|       this._setAutoplayPref.hidden = false;
 | |
|     }
 | |
| 
 | |
|     this._searchBox.focus();
 | |
|   },
 | |
| 
 | |
|   uninit() {
 | |
|     if (this._isObserving) {
 | |
|       Services.obs.removeObserver(this, "perm-changed");
 | |
|       this._isObserving = false;
 | |
|     }
 | |
|     if (this._setAutoplayPref) {
 | |
|       this._setAutoplayPref.hidden = true;
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   observe(subject, topic, data) {
 | |
|     if (topic !== "perm-changed") {
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     let permission = subject.QueryInterface(Ci.nsIPermission);
 | |
|     let [type] = permission.type.split(SitePermissions.PERM_KEY_DELIMITER);
 | |
| 
 | |
|     // Ignore unrelated permission types and permissions with unknown states.
 | |
|     if (
 | |
|       type !== this._type ||
 | |
|       !PERMISSION_STATES.includes(permission.capability)
 | |
|     ) {
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     if (data == "added") {
 | |
|       this._addPermissionToList(permission);
 | |
|     } else {
 | |
|       let group = this._permissionGroups.get(permission.principal.origin);
 | |
|       if (!group) {
 | |
|         // already moved to _permissionsToDelete
 | |
|         // or private browsing session permission
 | |
|         return;
 | |
|       }
 | |
|       if (data == "changed") {
 | |
|         group.removePermission(permission);
 | |
|         group.addPermission(permission);
 | |
|       } else if (data == "deleted") {
 | |
|         group.removePermission(permission);
 | |
|         if (!group.perms.length) {
 | |
|           this._removePermissionFromList(permission.principal.origin);
 | |
|           return;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|     this.buildPermissionsList();
 | |
|   },
 | |
| 
 | |
|   _handleCheckboxUIUpdates() {
 | |
|     let pref = Services.prefs.getPrefType(this._defaultPermissionStatePrefName);
 | |
|     if (pref != Services.prefs.PREF_INVALID) {
 | |
|       this._currentDefaultPermissionsState = Services.prefs.getIntPref(
 | |
|         this._defaultPermissionStatePrefName
 | |
|       );
 | |
|     }
 | |
| 
 | |
|     if (this._currentDefaultPermissionsState === null) {
 | |
|       this._checkbox.hidden = true;
 | |
|       this._permissionsDisableDescription.hidden = true;
 | |
|     } else if (this._currentDefaultPermissionsState == SitePermissions.BLOCK) {
 | |
|       this._checkbox.checked = true;
 | |
|     } else {
 | |
|       this._checkbox.checked = false;
 | |
|     }
 | |
| 
 | |
|     if (Services.prefs.prefIsLocked(this._defaultPermissionStatePrefName)) {
 | |
|       this._checkbox.disabled = true;
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Listen for changes to the permissions.default.* pref and make
 | |
|    * necessary changes to the UI.
 | |
|    */
 | |
|   _watchPermissionPrefChange() {
 | |
|     this._handleCheckboxUIUpdates();
 | |
| 
 | |
|     if (this._type == "desktop-notification") {
 | |
|       this._handleWebNotificationsDisable();
 | |
| 
 | |
|       this._disableExtensionButton.addEventListener(
 | |
|         "command",
 | |
|         makeDisableControllingExtension(
 | |
|           PREF_SETTING_TYPE,
 | |
|           NOTIFICATIONS_PERMISSION_OVERRIDE_KEY
 | |
|         )
 | |
|       );
 | |
|     }
 | |
| 
 | |
|     let observer = () => {
 | |
|       this._handleCheckboxUIUpdates();
 | |
|       if (this._type == "desktop-notification") {
 | |
|         this._handleWebNotificationsDisable();
 | |
|       }
 | |
|     };
 | |
|     Services.prefs.addObserver(this._defaultPermissionStatePrefName, observer);
 | |
|     window.addEventListener("unload", () => {
 | |
|       Services.prefs.removeObserver(
 | |
|         this._defaultPermissionStatePrefName,
 | |
|         observer
 | |
|       );
 | |
|     });
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Handles the UI update for web notifications disable by extensions.
 | |
|    */
 | |
|   async _handleWebNotificationsDisable() {
 | |
|     let prefLocked = Services.prefs.prefIsLocked(NOTIFICATIONS_PERMISSION_PREF);
 | |
|     if (prefLocked) {
 | |
|       // An extension can't control these settings if they're locked.
 | |
|       hideControllingExtension(NOTIFICATIONS_PERMISSION_OVERRIDE_KEY);
 | |
|     } else {
 | |
|       let isControlled = await handleControllingExtension(
 | |
|         PREF_SETTING_TYPE,
 | |
|         NOTIFICATIONS_PERMISSION_OVERRIDE_KEY
 | |
|       );
 | |
|       this._checkbox.disabled = isControlled;
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   _getCapabilityL10nId(element, type, capability) {
 | |
|     if (
 | |
|       type in sitePermissionsConfig &&
 | |
|       sitePermissionsConfig[type]._getCapabilityString
 | |
|     ) {
 | |
|       return sitePermissionsConfig[type]._getCapabilityString(capability);
 | |
|     }
 | |
|     switch (element.tagName) {
 | |
|       case "menuitem":
 | |
|         switch (capability) {
 | |
|           case Services.perms.ALLOW_ACTION:
 | |
|             return "permissions-capabilities-allow";
 | |
|           case Services.perms.DENY_ACTION:
 | |
|             return "permissions-capabilities-block";
 | |
|           case Services.perms.PROMPT_ACTION:
 | |
|             return "permissions-capabilities-prompt";
 | |
|           default:
 | |
|             throw new Error(`Unknown capability: ${capability}`);
 | |
|         }
 | |
|       case "label":
 | |
|         switch (capability) {
 | |
|           case Services.perms.ALLOW_ACTION:
 | |
|             return "permissions-capabilities-listitem-allow";
 | |
|           case Services.perms.DENY_ACTION:
 | |
|             return "permissions-capabilities-listitem-block";
 | |
|           default:
 | |
|             throw new Error(`Unexpected capability: ${capability}`);
 | |
|         }
 | |
|       default:
 | |
|         throw new Error(`Unexpected tag: ${element.tagName}`);
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   _addPermissionToList(perm) {
 | |
|     let [type] = perm.type.split(SitePermissions.PERM_KEY_DELIMITER);
 | |
|     // Ignore unrelated permission types and permissions with unknown states.
 | |
|     if (
 | |
|       type !== this._type ||
 | |
|       !PERMISSION_STATES.includes(perm.capability) ||
 | |
|       // Skip private browsing session permissions
 | |
|       (perm.principal.privateBrowsingId !==
 | |
|         Services.scriptSecurityManager.DEFAULT_PRIVATE_BROWSING_ID &&
 | |
|         perm.expireType === Services.perms.EXPIRE_SESSION)
 | |
|     ) {
 | |
|       return;
 | |
|     }
 | |
|     let group = this._permissionGroups.get(perm.principal.origin);
 | |
|     if (group) {
 | |
|       group.addPermission(perm);
 | |
|     } else {
 | |
|       group = new PermissionGroup(perm);
 | |
|       this._permissionGroups.set(group.origin, group);
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   _removePermissionFromList(origin) {
 | |
|     this._permissionGroups.delete(origin);
 | |
|     this._permissionsToChange.delete(origin);
 | |
|     let permissionlistitem = document.getElementsByAttribute(
 | |
|       "origin",
 | |
|       origin
 | |
|     )[0];
 | |
|     if (permissionlistitem) {
 | |
|       permissionlistitem.remove();
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   _loadPermissions() {
 | |
|     // load permissions into a table.
 | |
|     for (let nextPermission of Services.perms.all) {
 | |
|       this._addPermissionToList(nextPermission);
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   _createPermissionListItem(permissionGroup) {
 | |
|     let richlistitem = document.createXULElement("richlistitem");
 | |
|     richlistitem.setAttribute("origin", permissionGroup.origin);
 | |
|     let row = document.createXULElement("hbox");
 | |
| 
 | |
|     let hbox = document.createXULElement("hbox");
 | |
|     let website = document.createXULElement("label");
 | |
|     website.setAttribute("value", permissionGroup.origin);
 | |
|     hbox.setAttribute("class", "website-name");
 | |
|     hbox.appendChild(website);
 | |
| 
 | |
|     let states = SitePermissions.getAvailableStates(this._type).filter(
 | |
|       state => state != SitePermissions.UNKNOWN
 | |
|     );
 | |
|     // Handle the cases of a double-keyed ALLOW permission or a PROMPT
 | |
|     // permission after the default has been changed back to UNKNOWN.
 | |
|     if (!states.includes(permissionGroup.savedCapability)) {
 | |
|       states.unshift(permissionGroup.savedCapability);
 | |
|     }
 | |
|     let siteStatus;
 | |
|     if (states.length == 1) {
 | |
|       // Only a single state is available.  Show a label.
 | |
|       siteStatus = document.createXULElement("hbox");
 | |
|       let label = document.createXULElement("label");
 | |
|       siteStatus.appendChild(label);
 | |
|       document.l10n.setAttributes(
 | |
|         label,
 | |
|         this._getCapabilityL10nId(label, this._type, permissionGroup.capability)
 | |
|       );
 | |
|     } else {
 | |
|       // Multiple states are available.  Show a menulist.
 | |
|       siteStatus = document.createXULElement("menulist");
 | |
|       for (let state of states) {
 | |
|         let m = siteStatus.appendItem(undefined, state);
 | |
|         document.l10n.setAttributes(
 | |
|           m,
 | |
|           this._getCapabilityL10nId(m, this._type, state)
 | |
|         );
 | |
|       }
 | |
|       siteStatus.addEventListener("select", () => {
 | |
|         this.onPermissionChange(permissionGroup, Number(siteStatus.value));
 | |
|       });
 | |
|     }
 | |
|     siteStatus.setAttribute("class", "website-status");
 | |
|     siteStatus.value = permissionGroup.capability;
 | |
| 
 | |
|     row.appendChild(hbox);
 | |
|     row.appendChild(siteStatus);
 | |
|     richlistitem.appendChild(row);
 | |
|     return richlistitem;
 | |
|   },
 | |
| 
 | |
|   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 permissionGroup = this._permissionGroups.get(origin);
 | |
| 
 | |
|     this._removePermissionFromList(origin);
 | |
|     this._permissionsToDelete.set(permissionGroup.origin, permissionGroup);
 | |
| 
 | |
|     this._setRemoveButtonState();
 | |
|   },
 | |
| 
 | |
|   onAllPermissionsDelete() {
 | |
|     for (let permissionGroup of this._permissionGroups.values()) {
 | |
|       this._removePermissionFromList(permissionGroup.origin);
 | |
|       this._permissionsToDelete.set(permissionGroup.origin, permissionGroup);
 | |
|     }
 | |
| 
 | |
|     this._setRemoveButtonState();
 | |
|   },
 | |
| 
 | |
|   onPermissionSelect() {
 | |
|     this._setRemoveButtonState();
 | |
|   },
 | |
| 
 | |
|   onPermissionChange(perm, capability) {
 | |
|     let group = this._permissionGroups.get(perm.origin);
 | |
|     if (group.capability == capability) {
 | |
|       return;
 | |
|     }
 | |
|     if (capability == group.savedCapability) {
 | |
|       group.revert();
 | |
|       this._permissionsToChange.delete(group.origin);
 | |
|     } else {
 | |
|       group.capability = capability;
 | |
|       this._permissionsToChange.set(group.origin, group);
 | |
|     }
 | |
| 
 | |
|     // 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();
 | |
| 
 | |
|     // Delete even _permissionsToChange to clear out double-keyed permissions
 | |
|     for (let group of [
 | |
|       ...this._permissionsToDelete.values(),
 | |
|       ...this._permissionsToChange.values(),
 | |
|     ]) {
 | |
|       for (let perm of group.perms) {
 | |
|         SitePermissions.removeFromPrincipal(perm.principal, perm.type);
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     for (let group of this._permissionsToChange.values()) {
 | |
|       SitePermissions.setForPrincipal(
 | |
|         group.principal,
 | |
|         this._type,
 | |
|         group.capability
 | |
|       );
 | |
|     }
 | |
| 
 | |
|     if (this._checkbox.checked) {
 | |
|       Services.prefs.setIntPref(
 | |
|         this._defaultPermissionStatePrefName,
 | |
|         SitePermissions.BLOCK
 | |
|       );
 | |
|     } else if (this._currentDefaultPermissionsState == SitePermissions.BLOCK) {
 | |
|       Services.prefs.setIntPref(
 | |
|         this._defaultPermissionStatePrefName,
 | |
|         SitePermissions.UNKNOWN
 | |
|       );
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   buildPermissionsList(sortCol) {
 | |
|     // Clear old entries.
 | |
|     let oldItems = this._list.querySelectorAll("richlistitem");
 | |
|     for (let item of oldItems) {
 | |
|       item.remove();
 | |
|     }
 | |
|     let frag = document.createDocumentFragment();
 | |
| 
 | |
|     let permissionGroups = Array.from(this._permissionGroups.values());
 | |
| 
 | |
|     let keyword = this._searchBox.value.toLowerCase().trim();
 | |
|     for (let permissionGroup of permissionGroups) {
 | |
|       if (keyword && !permissionGroup.origin.includes(keyword)) {
 | |
|         continue;
 | |
|       }
 | |
| 
 | |
|       let richlistitem = this._createPermissionListItem(permissionGroup);
 | |
|       frag.appendChild(richlistitem);
 | |
|     }
 | |
| 
 | |
|     // Sort permissions.
 | |
|     this._sortPermissions(this._list, frag, sortCol);
 | |
| 
 | |
|     this._list.appendChild(frag);
 | |
| 
 | |
|     this._setRemoveButtonState();
 | |
|   },
 | |
| 
 | |
|   async buildAutoplayMenulist() {
 | |
|     let menulist = document.createXULElement("menulist");
 | |
|     let states = SitePermissions.getAvailableStates("autoplay-media");
 | |
|     document.l10n.pauseObserving();
 | |
|     for (let state of states) {
 | |
|       let m = menulist.appendItem(undefined, state);
 | |
|       document.l10n.setAttributes(
 | |
|         m,
 | |
|         this._getCapabilityL10nId(m, "autoplay-media", state)
 | |
|       );
 | |
|     }
 | |
| 
 | |
|     menulist.value = SitePermissions.getDefault("autoplay-media");
 | |
| 
 | |
|     menulist.addEventListener("select", () => {
 | |
|       SitePermissions.setDefault("autoplay-media", Number(menulist.value));
 | |
|     });
 | |
| 
 | |
|     menulist.menupopup.setAttribute("incontentshell", "false");
 | |
| 
 | |
|     menulist.disabled = Services.prefs.prefIsLocked(AUTOPLAY_PREF);
 | |
| 
 | |
|     document.getElementById("setAutoplayPref").appendChild(menulist);
 | |
|     await document.l10n.translateFragment(menulist);
 | |
|     document.l10n.resumeObserving();
 | |
|   },
 | |
| 
 | |
|   _sortPermissions(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 = null;
 | |
|     switch (column.id) {
 | |
|       case "siteCol":
 | |
|         sortFunc = (a, b) => {
 | |
|           return comp.compare(
 | |
|             a.getAttribute("origin"),
 | |
|             b.getAttribute("origin")
 | |
|           );
 | |
|         };
 | |
|         break;
 | |
| 
 | |
|       case "statusCol":
 | |
|         sortFunc = (a, b) => {
 | |
|           return (
 | |
|             parseInt(a.querySelector(".website-status").value) >
 | |
|             parseInt(b.querySelector(".website-status").value)
 | |
|           );
 | |
|         };
 | |
|         break;
 | |
|     }
 | |
| 
 | |
|     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);
 | |
|   },
 | |
| };
 |