forked from mirrors/gecko-dev
MozReview-Commit-ID: 61k7xS2MlKr --HG-- extra : rebase_source : 32cddf29916beff5e03abf9dc599890fd08b3fe3
465 lines
15 KiB
JavaScript
465 lines
15 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/. */
|
|
|
|
// Imported via permissions.xul.
|
|
/* import-globals-from ../../../toolkit/content/treeUtils.js */
|
|
|
|
ChromeUtils.import("resource://gre/modules/Services.jsm");
|
|
ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
|
|
|
|
const nsIPermissionManager = Ci.nsIPermissionManager;
|
|
const nsICookiePermission = Ci.nsICookiePermission;
|
|
|
|
const NOTIFICATION_FLUSH_PERMISSIONS = "flush-pending-permissions";
|
|
|
|
const permissionExceptionsL10n = {
|
|
"trackingprotection": {
|
|
window: "permissions-exceptions-tracking-protection-window",
|
|
description: "permissions-exceptions-tracking-protection-desc",
|
|
},
|
|
"cookie": {
|
|
window: "permissions-exceptions-cookie-window",
|
|
description: "permissions-exceptions-cookie-desc",
|
|
},
|
|
"popup": {
|
|
window: "permissions-exceptions-popup-window",
|
|
description: "permissions-exceptions-popup-desc",
|
|
},
|
|
"login-saving": {
|
|
window: "permissions-exceptions-saved-logins-window",
|
|
description: "permissions-exceptions-saved-logins-desc",
|
|
},
|
|
"install": {
|
|
window: "permissions-exceptions-addons-window",
|
|
description: "permissions-exceptions-addons-desc",
|
|
},
|
|
"autoplay-media": {
|
|
window: "permissions-exceptions-autoplay-media-window",
|
|
description: "permissions-exceptions-autoplay-media-desc",
|
|
},
|
|
};
|
|
|
|
function Permission(principal, type, capability) {
|
|
this.principal = principal;
|
|
this.origin = principal.origin;
|
|
this.type = type;
|
|
this.capability = capability;
|
|
}
|
|
|
|
var gPermissionManager = {
|
|
_type: "",
|
|
_permissions: [],
|
|
_permissionsToAdd: new Map(),
|
|
_permissionsToDelete: new Map(),
|
|
_bundle: null,
|
|
_tree: null,
|
|
_observerRemoved: false,
|
|
|
|
_view: {
|
|
_rowCount: 0,
|
|
get rowCount() {
|
|
return this._rowCount;
|
|
},
|
|
getCellText(row, column) {
|
|
if (column.id == "siteCol")
|
|
return gPermissionManager._permissions[row].origin;
|
|
else if (column.id == "statusCol")
|
|
return gPermissionManager._permissions[row].capability;
|
|
return "";
|
|
},
|
|
|
|
isSeparator(index) { return false; },
|
|
isSorted() { return false; },
|
|
isContainer(index) { return false; },
|
|
setTree(tree) {},
|
|
getImageSrc(row, column) {},
|
|
getCellValue(row, column) {},
|
|
cycleHeader(column) {},
|
|
getRowProperties(row) { return ""; },
|
|
getColumnProperties(column) { return ""; },
|
|
getCellProperties(row, column) {
|
|
if (column.element.getAttribute("id") == "siteCol")
|
|
return "ltr";
|
|
|
|
return "";
|
|
}
|
|
},
|
|
|
|
onLoad() {
|
|
this._bundle = document.getElementById("bundlePreferences");
|
|
let params = window.arguments[0];
|
|
document.mozSubdialogReady = this.init(params);
|
|
},
|
|
|
|
async init(params) {
|
|
if (this._type) {
|
|
// reusing an open dialog, clear the old observer
|
|
this.uninit();
|
|
}
|
|
|
|
this._type = params.permissionType;
|
|
this._manageCapability = params.manageCapability;
|
|
|
|
const l10n = permissionExceptionsL10n[this._type];
|
|
let permissionsText = document.getElementById("permissionsText");
|
|
document.l10n.setAttributes(permissionsText, l10n.description);
|
|
|
|
document.l10n.setAttributes(document.documentElement, l10n.window);
|
|
|
|
await document.l10n.translateElements([
|
|
document.documentElement,
|
|
permissionsText,
|
|
]);
|
|
|
|
document.getElementById("btnBlock").hidden = !params.blockVisible;
|
|
document.getElementById("btnSession").hidden = !params.sessionVisible;
|
|
document.getElementById("btnAllow").hidden = !params.allowVisible;
|
|
|
|
let urlFieldVisible = (params.blockVisible || params.sessionVisible || params.allowVisible);
|
|
|
|
let urlField = document.getElementById("url");
|
|
urlField.value = params.prefilledHost;
|
|
urlField.hidden = !urlFieldVisible;
|
|
|
|
this.onHostInput(urlField);
|
|
|
|
let urlLabel = document.getElementById("urlLabel");
|
|
urlLabel.hidden = !urlFieldVisible;
|
|
|
|
if (params.hideStatusColumn) {
|
|
document.getElementById("statusCol").hidden = true;
|
|
}
|
|
|
|
let treecols = document.getElementsByTagName("treecols")[0];
|
|
treecols.addEventListener("click", event => {
|
|
if (event.target.nodeName != "treecol" || event.button != 0) {
|
|
return;
|
|
}
|
|
|
|
let sortField = event.target.getAttribute("data-field-name");
|
|
if (!sortField) {
|
|
return;
|
|
}
|
|
|
|
gPermissionManager.onPermissionSort(sortField);
|
|
});
|
|
|
|
Services.obs.notifyObservers(null, NOTIFICATION_FLUSH_PERMISSIONS, this._type);
|
|
Services.obs.addObserver(this, "perm-changed");
|
|
|
|
this._loadPermissions();
|
|
|
|
urlField.focus();
|
|
},
|
|
|
|
uninit() {
|
|
if (!this._observerRemoved) {
|
|
Services.obs.removeObserver(this, "perm-changed");
|
|
|
|
this._observerRemoved = true;
|
|
}
|
|
},
|
|
|
|
observe(subject, topic, data) {
|
|
if (topic == "perm-changed") {
|
|
let permission = subject.QueryInterface(Ci.nsIPermission);
|
|
|
|
// Ignore unrelated permission types.
|
|
if (permission.type != this._type)
|
|
return;
|
|
|
|
if (data == "added") {
|
|
this._addPermission(permission);
|
|
} else if (data == "changed") {
|
|
for (let i = 0; i < this._permissions.length; ++i) {
|
|
if (permission.matches(this._permissions[i].principal, true)) {
|
|
this._permissions[i].capability = this._getCapabilityString(permission.capability);
|
|
break;
|
|
}
|
|
}
|
|
this._handleCapabilityChange();
|
|
} else if (data == "deleted") {
|
|
this._removePermissionFromList(permission.principal);
|
|
}
|
|
}
|
|
},
|
|
|
|
_resortPermissions() {
|
|
gTreeUtils.sort(this._tree, this._view, this._permissions,
|
|
this._lastPermissionSortColumn,
|
|
this._permissionsComparator,
|
|
this._lastPermissionSortColumn,
|
|
!this._lastPermissionSortAscending); // keep sort direction
|
|
},
|
|
|
|
_handleCapabilityChange() {
|
|
// Re-do the sort, if the status changed from Block to Allow
|
|
// or vice versa, since if we're sorted on status, we may no
|
|
// longer be in order.
|
|
if (this._lastPermissionSortColumn == "statusCol") {
|
|
this._resortPermissions();
|
|
}
|
|
this._tree.treeBoxObject.invalidate();
|
|
},
|
|
|
|
_getCapabilityString(capability) {
|
|
let stringKey = null;
|
|
switch (capability) {
|
|
case nsIPermissionManager.ALLOW_ACTION:
|
|
stringKey = "can";
|
|
break;
|
|
case nsIPermissionManager.DENY_ACTION:
|
|
stringKey = "cannot";
|
|
break;
|
|
case nsICookiePermission.ACCESS_ALLOW_FIRST_PARTY_ONLY:
|
|
stringKey = "canAccessFirstParty";
|
|
break;
|
|
case nsICookiePermission.ACCESS_SESSION:
|
|
stringKey = "canSession";
|
|
break;
|
|
}
|
|
return this._bundle.getString(stringKey);
|
|
},
|
|
|
|
_addPermission(permission) {
|
|
this._addPermissionToList(permission);
|
|
++this._view._rowCount;
|
|
this._tree.treeBoxObject.rowCountChanged(this._view.rowCount - 1, 1);
|
|
// Re-do the sort, since we inserted this new item at the end.
|
|
this._resortPermissions();
|
|
},
|
|
|
|
_addPermissionToList(permission) {
|
|
if (permission.type == this._type &&
|
|
(!this._manageCapability ||
|
|
(permission.capability == this._manageCapability))) {
|
|
|
|
let principal = permission.principal;
|
|
let capabilityString = this._getCapabilityString(permission.capability);
|
|
let p = new Permission(principal,
|
|
permission.type,
|
|
capabilityString);
|
|
this._permissions.push(p);
|
|
}
|
|
},
|
|
|
|
addPermission(capability) {
|
|
let textbox = document.getElementById("url");
|
|
let input_url = textbox.value.replace(/^\s*/, ""); // trim any leading space
|
|
let principal;
|
|
try {
|
|
// The origin accessor on the principal object will throw if the
|
|
// principal doesn't have a canonical origin representation. This will
|
|
// help catch cases where the URI parser parsed something like
|
|
// `localhost:8080` as having the scheme `localhost`, rather than being
|
|
// an invalid URI. A canonical origin representation is required by the
|
|
// permission manager for storage, so this won't prevent any valid
|
|
// permissions from being entered by the user.
|
|
let uri;
|
|
try {
|
|
uri = Services.io.newURI(input_url);
|
|
principal = Services.scriptSecurityManager.createCodebasePrincipal(uri, {});
|
|
if (principal.origin.startsWith("moz-nullprincipal:")) {
|
|
throw "Null principal";
|
|
}
|
|
} catch (ex) {
|
|
uri = Services.io.newURI("http://" + input_url);
|
|
principal = Services.scriptSecurityManager.createCodebasePrincipal(uri, {});
|
|
// If we have ended up with an unknown scheme, the following will throw.
|
|
principal.origin;
|
|
}
|
|
} 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;
|
|
}
|
|
|
|
let capabilityString = this._getCapabilityString(capability);
|
|
|
|
// check whether the permission already exists, if not, add it
|
|
let permissionExists = false;
|
|
let capabilityExists = false;
|
|
for (let i = 0; i < this._permissions.length; ++i) {
|
|
if (this._permissions[i].principal.equals(principal)) {
|
|
permissionExists = true;
|
|
capabilityExists = this._permissions[i].capability == capabilityString;
|
|
if (!capabilityExists) {
|
|
this._permissions[i].capability = capabilityString;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
let permissionParams = {principal, type: this._type, capability};
|
|
if (!permissionExists) {
|
|
this._permissionsToAdd.set(principal.origin, permissionParams);
|
|
this._addPermission(permissionParams);
|
|
} else if (!capabilityExists) {
|
|
this._permissionsToAdd.set(principal.origin, permissionParams);
|
|
this._handleCapabilityChange();
|
|
}
|
|
|
|
textbox.value = "";
|
|
textbox.focus();
|
|
|
|
// covers a case where the site exists already, so the buttons don't disable
|
|
this.onHostInput(textbox);
|
|
|
|
// enable "remove all" button as needed
|
|
document.getElementById("removeAllPermissions").disabled = this._permissions.length == 0;
|
|
},
|
|
|
|
_removePermission(permission) {
|
|
this._removePermissionFromList(permission.principal);
|
|
|
|
// If this permission was added during this session, let's remove
|
|
// it from the pending adds list to prevent calls to the
|
|
// permission manager.
|
|
let isNewPermission = this._permissionsToAdd.delete(permission.principal.origin);
|
|
|
|
if (!isNewPermission) {
|
|
this._permissionsToDelete.set(permission.principal.origin, permission);
|
|
}
|
|
|
|
},
|
|
|
|
_removePermissionFromList(principal) {
|
|
for (let i = 0; i < this._permissions.length; ++i) {
|
|
if (this._permissions[i].principal.equals(principal)) {
|
|
this._permissions.splice(i, 1);
|
|
this._view._rowCount--;
|
|
this._tree.treeBoxObject.rowCountChanged(this._view.rowCount - 1, -1);
|
|
this._tree.treeBoxObject.invalidate();
|
|
break;
|
|
}
|
|
}
|
|
},
|
|
|
|
_loadPermissions() {
|
|
this._tree = document.getElementById("permissionsTree");
|
|
this._permissions = [];
|
|
|
|
// load permissions into a table
|
|
let enumerator = Services.perms.enumerator;
|
|
while (enumerator.hasMoreElements()) {
|
|
let nextPermission = enumerator.getNext().QueryInterface(Ci.nsIPermission);
|
|
this._addPermissionToList(nextPermission);
|
|
}
|
|
|
|
this._view._rowCount = this._permissions.length;
|
|
|
|
// sort and display the table
|
|
this._tree.view = this._view;
|
|
this.onPermissionSort("origin");
|
|
|
|
// disable "remove all" button if there are none
|
|
document.getElementById("removeAllPermissions").disabled = this._permissions.length == 0;
|
|
},
|
|
|
|
onWindowKeyPress(event) {
|
|
if (event.keyCode == KeyEvent.DOM_VK_ESCAPE)
|
|
window.close();
|
|
},
|
|
|
|
onPermissionKeyPress(event) {
|
|
if (event.keyCode == KeyEvent.DOM_VK_DELETE) {
|
|
this.onPermissionDelete();
|
|
} else if (AppConstants.platform == "macosx" &&
|
|
event.keyCode == KeyEvent.DOM_VK_BACK_SPACE) {
|
|
this.onPermissionDelete();
|
|
event.preventDefault();
|
|
}
|
|
},
|
|
|
|
onHostKeyPress(event) {
|
|
if (event.keyCode == KeyEvent.DOM_VK_RETURN)
|
|
document.getElementById("btnAllow").click();
|
|
},
|
|
|
|
onHostInput(siteField) {
|
|
document.getElementById("btnSession").disabled = !siteField.value;
|
|
document.getElementById("btnBlock").disabled = !siteField.value;
|
|
document.getElementById("btnAllow").disabled = !siteField.value;
|
|
},
|
|
|
|
onPermissionDelete() {
|
|
if (!this._view.rowCount)
|
|
return;
|
|
let removedPermissions = [];
|
|
gTreeUtils.deleteSelectedItems(this._tree, this._view, this._permissions, removedPermissions);
|
|
for (let i = 0; i < removedPermissions.length; ++i) {
|
|
let p = removedPermissions[i];
|
|
this._removePermission(p);
|
|
}
|
|
document.getElementById("removePermission").disabled = !this._permissions.length;
|
|
document.getElementById("removeAllPermissions").disabled = !this._permissions.length;
|
|
},
|
|
|
|
onAllPermissionsDelete() {
|
|
if (!this._view.rowCount)
|
|
return;
|
|
let removedPermissions = [];
|
|
gTreeUtils.deleteAll(this._tree, this._view, this._permissions, removedPermissions);
|
|
for (let i = 0; i < removedPermissions.length; ++i) {
|
|
let p = removedPermissions[i];
|
|
this._removePermission(p);
|
|
}
|
|
document.getElementById("removePermission").disabled = true;
|
|
document.getElementById("removeAllPermissions").disabled = true;
|
|
},
|
|
|
|
onPermissionSelect() {
|
|
let hasSelection = this._tree.view.selection.count > 0;
|
|
let hasRows = this._tree.view.rowCount > 0;
|
|
document.getElementById("removePermission").disabled = !hasRows || !hasSelection;
|
|
document.getElementById("removeAllPermissions").disabled = !hasRows;
|
|
},
|
|
|
|
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 permissionParams of this._permissionsToAdd.values()) {
|
|
Services.perms.addFromPrincipal(permissionParams.principal, permissionParams.type, permissionParams.capability);
|
|
}
|
|
|
|
for (let p of this._permissionsToDelete.values()) {
|
|
Services.perms.removeFromPrincipal(p.principal, p.type);
|
|
}
|
|
|
|
window.close();
|
|
},
|
|
|
|
_lastPermissionSortColumn: "",
|
|
_lastPermissionSortAscending: false,
|
|
_permissionsComparator(a, b) {
|
|
return a.toLowerCase().localeCompare(b.toLowerCase());
|
|
},
|
|
|
|
onPermissionSort(column) {
|
|
this._lastPermissionSortAscending = gTreeUtils.sort(this._tree,
|
|
this._view,
|
|
this._permissions,
|
|
column,
|
|
this._permissionsComparator,
|
|
this._lastPermissionSortColumn,
|
|
this._lastPermissionSortAscending);
|
|
this._lastPermissionSortColumn = column;
|
|
let sortDirection = this._lastPermissionSortAscending ? "descending" : "ascending";
|
|
let cols = document.querySelectorAll("treecol");
|
|
cols.forEach(c => c.removeAttribute("sortDirection"));
|
|
document.querySelector(`treecol[data-field-name=${column}]`)
|
|
.setAttribute("sortDirection", sortDirection);
|
|
},
|
|
};
|
|
|
|
function initWithParams(params) {
|
|
gPermissionManager.init(params);
|
|
}
|