summaryrefslogtreecommitdiffstats
path: root/comm/suite/components/permissions
diff options
context:
space:
mode:
Diffstat (limited to 'comm/suite/components/permissions')
-rw-r--r--comm/suite/components/permissions/content/cookieViewer.js531
-rw-r--r--comm/suite/components/permissions/content/cookieViewer.xul225
-rw-r--r--comm/suite/components/permissions/content/permissionsManager.js287
-rw-r--r--comm/suite/components/permissions/content/permissionsManager.xul81
-rw-r--r--comm/suite/components/permissions/content/permissionsUtils.js130
-rw-r--r--comm/suite/components/permissions/jar.mn10
-rw-r--r--comm/suite/components/permissions/moz.build7
7 files changed, 1271 insertions, 0 deletions
diff --git a/comm/suite/components/permissions/content/cookieViewer.js b/comm/suite/components/permissions/content/cookieViewer.js
new file mode 100644
index 0000000000..d864f72908
--- /dev/null
+++ b/comm/suite/components/permissions/content/cookieViewer.js
@@ -0,0 +1,531 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * 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/. */
+
+var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+const {AppConstants} = ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
+
+// cookies and permissions list
+var cookies = [];
+var permissions = [];
+var allCookies = [];
+var deletedCookies = [];
+var deletedPermissions = [];
+
+var cookieBundle;
+var gUpdatingBatch = "";
+var lastCookieSortColumn;
+var lastCookieSortAscending;
+var lastPermissionSortColumn;
+var lastPermissionSortAscending;
+
+function Startup() {
+
+ // arguments passed to this routine:
+ // cookieManager
+
+ // intialize string bundle
+ cookieBundle = document.getElementById("cookieBundle");
+
+ // load in the cookies and permissions
+ cookiesTree = document.getElementById("cookiesTree");
+ lastCookieSortAscending = (cookiesTree.getAttribute("sortAscending") == "true");
+ lastCookieSortColumn = cookiesTree.getAttribute("sortColumn");
+ permissionsTree = document.getElementById("permissionsTree");
+ lastPermissionSortAscending = (permissionsTree.getAttribute("sortAscending") == "true");
+ lastPermissionSortColumn = permissionsTree.getAttribute("sortColumn");
+ loadCookies();
+ loadPermissions();
+
+ // be prepared to reload the display if anything changes
+ Services.obs.addObserver(cookieReloadDisplay, "cookie-changed", false);
+ Services.obs.addObserver(cookieReloadDisplay, "perm-changed", false);
+
+ // filter the table if requested by caller
+ if (window.arguments &&
+ window.arguments[0] &&
+ window.arguments[0].filterString)
+ setFilter(window.arguments[0].filterString);
+
+ document.getElementById("filter").focus();
+}
+
+function Shutdown() {
+ Services.obs.removeObserver(cookieReloadDisplay, "cookie-changed");
+ Services.obs.removeObserver(cookieReloadDisplay, "perm-changed");
+}
+
+function PromptConfirm(title, msg, yes) {
+ var flags =
+ ((Services.prompt.BUTTON_TITLE_IS_STRING * Services.prompt.BUTTON_POS_0) +
+ (Services.prompt.BUTTON_TITLE_CANCEL * Services.prompt.BUTTON_POS_1) +
+ Services.prompt.BUTTON_POS_1_DEFAULT)
+ return Services.prompt.confirmEx(window,
+ cookieBundle.getString(title),
+ cookieBundle.getString(msg),
+ flags,
+ cookieBundle.getString(yes),
+ null, null, null, {value:0});
+}
+
+var cookieReloadDisplay = {
+ observe: function(subject, topic, state) {
+ if (topic == gUpdatingBatch)
+ return;
+ if (topic == "cookie-changed") {
+ allCookies.length = 0;
+ loadCookies();
+ } else if (topic == "perm-changed") {
+ permissions.length = 0;
+ loadPermissions();
+ }
+ }
+}
+
+function doSelectAll() {
+ var elem = document.commandDispatcher.focusedElement;
+ if (elem && "treeBoxObject" in elem)
+ elem.view.selection.selectAll();
+}
+
+/*** =================== COOKIES CODE =================== ***/
+
+var cookiesTreeView = {
+ rowCount : 0,
+ setTree : function(tree){},
+ getImageSrc : function(row,column) {},
+ getProgressMode : function(row,column) {},
+ getCellValue : function(row,column) {},
+ getCellText : function(row,column){ return cookies[row][column.id]; },
+ isSeparator : function(index) {return false;},
+ isSorted: function() { return false; },
+ isContainer : function(index) {return false;},
+ cycleHeader : function(aCol) {},
+ getRowProperties : function(row) { return ""; },
+ getColumnProperties : function(column) { return ""; },
+ getCellProperties : function(row, column) { return ""; }
+};
+var cookiesTree;
+
+function Cookie(id, host, name, path, originAttributes, value,
+ isDomain, rawHost, isSecure, expires) {
+ this.id = id;
+ this.host = host;
+ this.name = name;
+ this.path = path;
+ this.originAttributes = originAttributes;
+ this.value = value;
+ this.isDomain = isDomain;
+ this.rawHost = rawHost;
+ this.isSecure = isSecure;
+ this.expires = GetExpiresString(expires);
+ this.expiresSortValue = expires;
+}
+
+function loadCookies() {
+ // load cookies into a table
+ var enumerator = Services.cookies.enumerator;
+ var count = 0;
+ while (enumerator.hasMoreElements()) {
+ var nextCookie = enumerator.getNext();
+ if (!nextCookie) break;
+ nextCookie = nextCookie.QueryInterface(Ci.nsICookie);
+ var host = nextCookie.host;
+ allCookies.push(
+ new Cookie(count++, host, nextCookie.name,
+ nextCookie.path, nextCookie.originAttributes,
+ nextCookie.value, nextCookie.isDomain,
+ host.charAt(0)=="." ? host.slice(1) : host,
+ nextCookie.isSecure, nextCookie.expires));
+ }
+
+ // filter, sort and display the table
+ cookiesTree.view = cookiesTreeView;
+ filter(document.getElementById("filter").value);
+}
+
+function GetExpiresString(expires) {
+ if (expires) {
+ var date = new Date(1000*expires);
+
+ // if a server manages to set a really long-lived cookie, the dateservice
+ // can't cope with it properly, so we'll just return a blank string
+ // see bug 238045 for details
+ var expiry = "";
+ try {
+ const dateTimeFormatter = new Services.intl.DateTimeFormat(undefined, {
+ dateStyle: "full", timeStyle: "long" });
+ expiry = dateTimeFormatter.format(date);
+ } catch(ex) {
+ // do nothing
+ }
+ return expiry;
+ }
+ return cookieBundle.getString("expireAtEndOfSession");
+}
+
+function CookieSelected() {
+ var selections = GetTreeSelections(cookiesTree);
+ if (selections.length) {
+ document.getElementById("removeCookie").removeAttribute("disabled");
+ } else {
+ document.getElementById("removeCookie").setAttribute("disabled", "true");
+ ClearCookieProperties();
+ return true;
+ }
+
+ var idx = selections[0];
+ if (idx >= cookies.length) {
+ // Something got out of synch. See bug 119812 for details
+ dump("Tree and viewer state are out of sync! " +
+ "Help us figure out the problem in bug 119812");
+ return false;
+ }
+
+ var props = [
+ {id: "ifl_name", value: cookies[idx].name},
+ {id: "ifl_value", value: cookies[idx].value},
+ {id: "ifl_isDomain",
+ value: cookies[idx].isDomain ?
+ cookieBundle.getString("domainColon") : cookieBundle.getString("hostColon")},
+ {id: "ifl_host", value: cookies[idx].host},
+ {id: "ifl_path", value: cookies[idx].path},
+ {id: "ifl_isSecure",
+ value: cookies[idx].isSecure ?
+ cookieBundle.getString("forSecureOnly") :
+ cookieBundle.getString("forAnyConnection")},
+ {id: "ifl_expires", value: cookies[idx].expires}
+ ];
+
+ var value;
+ var field;
+
+ for (let lProp of props)
+ {
+ field = document.getElementById(lProp.id);
+ if ((selections.length > 1) && (lProp.id != "ifl_isDomain")) {
+ value = ""; // clear field if multiple selections
+ } else {
+ value = lProp.value;
+ }
+ field.value = value;
+ }
+ return true;
+}
+
+function ClearCookieProperties() {
+ var properties =
+ ["ifl_name","ifl_value","ifl_host","ifl_path","ifl_isSecure","ifl_expires"];
+ for (let prop of properties) {
+ document.getElementById(prop).value = "";
+ }
+}
+
+function DeleteCookie() {
+ if (cookiesTreeView.selection.count > 1 &&
+ PromptConfirm("deleteSelectedCookiesTitle",
+ "deleteSelectedCookies",
+ "deleteSelectedCookiesYes") == 1) {
+ return;
+ }
+ DeleteSelectedItemFromTree(cookiesTree, cookiesTreeView,
+ cookies, deletedCookies,
+ "removeCookie", "removeAllCookies");
+ if (document.getElementById("filter").value) {
+ // remove selected cookies from unfiltered set
+ for (let cookie of deletedCookies) {
+ allCookies.splice(allCookies.indexOf(cookie), 1);
+ }
+ }
+ if (!cookies.length) {
+ ClearCookieProperties();
+ }
+ FinalizeCookieDeletions();
+}
+
+function DeleteAllCookies() {
+ if (PromptConfirm("deleteAllCookiesTitle",
+ "deleteAllCookies",
+ "deleteAllCookiesYes") == 1) {
+ return;
+ }
+
+ ClearCookieProperties();
+ DeleteAllFromTree(cookiesTree, cookiesTreeView,
+ cookies, deletedCookies,
+ "removeCookie", "removeAllCookies");
+ allCookies.length = 0;
+ FinalizeCookieDeletions();
+}
+
+function FinalizeCookieDeletions() {
+ gUpdatingBatch = "cookie-changed";
+ for (let delCookie of deletedCookies) {
+ Services.cookies.remove(delCookie.host,
+ delCookie.name,
+ delCookie.path,
+ document.getElementById("checkbox").checked,
+ delCookie.originAttributes);
+ }
+ deletedCookies.length = 0;
+ gUpdatingBatch = "";
+}
+
+function HandleCookieKeyPress(e) {
+ if (e.keyCode == KeyEvent.DOM_VK_DELETE ||
+ (AppConstants.platform == "macosx" &&
+ e.keyCode == KeyEvent.DOM_VK_BACK_SPACE)) {
+ DeleteCookie();
+ }
+}
+
+function CookieColumnSort(column, updateSelection) {
+ lastCookieSortAscending =
+ SortTree(cookiesTree, cookiesTreeView, cookies,
+ column, lastCookieSortColumn, lastCookieSortAscending,
+ updateSelection);
+ lastCookieSortColumn = column;
+
+ SetSortDirection(cookiesTree, column, lastCookieSortAscending);
+}
+
+/*** =================== PERMISSIONS CODE =================== ***/
+
+var permissionsTreeView = {
+ rowCount : 0,
+ setTree : function(tree){},
+ getImageSrc : function(row,column) {},
+ getProgressMode : function(row,column) {},
+ getCellValue : function(row,column) {},
+ getCellText : function(row,column) { return permissions[row][column.id]; },
+ isSeparator : function(index) {return false;},
+ isSorted: function() { return false; },
+ isContainer : function(index) {return false;},
+ cycleHeader : function(aCol) {},
+ getRowProperties : function(row) { return ""; },
+ getColumnProperties : function(column) { return ""; },
+ getCellProperties : function(row, column) { return ""; }
+};
+var permissionsTree;
+
+function Permission(id, principal, type, capability) {
+ this.id = id;
+ this.principal = principal;
+ this.host = principal.URI.hostPort;
+ this.scheme = principal.URI.scheme;
+ this.type = type;
+ this.capability = capability;
+}
+
+function loadPermissions() {
+ // load permissions into a table
+ var enumerator = Services.perms.enumerator;
+ var canStr = cookieBundle.getString("can");
+ var canSessionStr = cookieBundle.getString("canSession");
+ var cannotStr = cookieBundle.getString("cannot");
+ var capability;
+ var count = 0;
+ var permission;
+ while (enumerator.hasMoreElements()) {
+ permission = enumerator.getNext().QueryInterface(Ci.nsIPermission);
+ // We are only interested in cookie permissions in this code.
+ if (permission.type == "cookie") {
+ // It is currently possible to add a cookie permission for about:xxx
+ // and other internal pages. They are probably invalid and will be
+ // ignored for now.
+ // Test if the permission has a host.
+ try {
+ permission.principal.URI.host;
+ }
+ catch (e) {
+ Cu.reportError("Invalid permission found: " +
+ permission.principal.origin + " " + permission.type);
+ continue;
+ }
+
+ switch (permission.capability) {
+ case Ci.nsIPermissionManager.ALLOW_ACTION:
+ capability = canStr;
+ break;
+ case Ci.nsIPermissionManager.DENY_ACTION:
+ capability = cannotStr;
+ break;
+ case Ci.nsICookiePermission.ACCESS_SESSION:
+ capability = canSessionStr;
+ break;
+ default:
+ continue;
+ }
+ permissions.push(new Permission(count++,
+ permission.principal,
+ permission.type,
+ capability));
+ }
+ }
+ permissionsTreeView.rowCount = permissions.length;
+
+ // sort and display the table
+ permissionsTree.view = permissionsTreeView;
+ permissionsTreeView.selection.clearSelection();
+ SortTree(permissionsTree, permissionsTreeView, permissions,
+ lastPermissionSortColumn, lastPermissionSortColumn,
+ !lastPermissionSortAscending);
+
+ // disable "remove all" button if there are no cookies
+ document.getElementById("removeAllPermissions").disabled = permissions.length == 0;
+}
+
+function DeletePermission() {
+ if (permissionsTreeView.selection.count > 1 &&
+ PromptConfirm("deleteSelectedSitesTitle",
+ "deleteSelectedCookiesSites",
+ "deleteSelectedSitesYes") == 1) {
+ return;
+ }
+ DeleteSelectedItemFromTree(permissionsTree, permissionsTreeView,
+ permissions, deletedPermissions,
+ "removePermission", "removeAllPermissions");
+ FinalizePermissionDeletions();
+}
+
+function setCookiePermissions(action) {
+ var site = document.getElementById("cookie-site");
+
+ // let the backend do the validation
+ try {
+ var url = new URL(site.value);
+ } catch (e) {
+ // show an error if URL is invalid
+ window.alert(cookieBundle.getString("allowedURLSchemes"));
+ return;
+ }
+
+ try {
+ var uri = Services.io.newURI(url);
+ } catch (e) {
+ // show an error if URI can not be constructed or adding it failed
+ window.alert(cookieBundle.getString("errorAddPermission"));
+ return;
+ }
+ // only allow a few schemes here
+ // others like file:// would produce an invalid entry in the database
+ if (uri.scheme != "http" &&
+ uri.scheme != "https") {
+ // show an error if uri uses invalid scheme
+ window.alert(uri.scheme + ": " + cookieBundle.getString("allowedURLSchemes"));
+ return;
+ }
+
+ if (Services.perms.testPermission(uri, "cookie") != action)
+ Services.perms.add(uri, "cookie", action);
+
+ site.focus();
+ site.value = "";
+}
+
+function DeleteAllPermissions() {
+ if (PromptConfirm("deleteAllSitesTitle",
+ "deleteAllCookiesSites",
+ "deleteAllSitesYes") == 1) {
+ return;
+ }
+
+ DeleteAllFromTree(permissionsTree, permissionsTreeView,
+ permissions, deletedPermissions,
+ "removePermission", "removeAllPermissions");
+ FinalizePermissionDeletions();
+}
+
+function FinalizePermissionDeletions() {
+ if (!deletedPermissions.length)
+ return;
+
+ gUpdatingBatch = "perm-changed";
+ for (let permission of deletedPermissions)
+ Services.perms.removeFromPrincipal(permission.principal, permission.type);
+ deletedPermissions.length = 0;
+ gUpdatingBatch = "";
+}
+
+function HandlePermissionKeyPress(e) {
+ if (e.keyCode == KeyEvent.DOM_VK_DELETE ||
+ (AppConstants.platform == "macosx" &&
+ e.keyCode == KeyEvent.DOM_VK_BACK_SPACE)) {
+ DeletePermission();
+ }
+}
+
+function PermissionColumnSort(column, updateSelection) {
+ lastPermissionSortAscending =
+ SortTree(permissionsTree, permissionsTreeView, permissions,
+ column, lastPermissionSortColumn, lastPermissionSortAscending,
+ updateSelection);
+ lastPermissionSortColumn = column;
+
+ SetSortDirection(permissionsTree, column, lastPermissionSortAscending);
+}
+
+/*** ============ CODE FOR HELP BUTTON =================== ***/
+
+function doHelpButton()
+{
+ var selTab = document.getElementById("tabbox").selectedTab;
+ var key = selTab.getAttribute("help");
+ openHelp(key, "chrome://communicator/locale/help/suitehelp.rdf");
+}
+
+/*** =================== FILTER CODE =================== ***/
+
+function filterCookies(aFilterValue)
+{
+ var filterSet = [];
+ for (let cookie of allCookies) {
+ if (cookie.rawHost.includes(aFilterValue) ||
+ cookie.name.includes(aFilterValue) ||
+ cookie.value.includes(aFilterValue))
+ filterSet.push(cookie);
+ }
+ return filterSet;
+}
+
+function filter(filter)
+{
+ // clear the display
+ var oldCount = cookiesTreeView.rowCount;
+ cookiesTreeView.rowCount = 0;
+ cookiesTree.treeBoxObject.rowCountChanged(0, -oldCount);
+
+ // set up the display
+ cookies = filter ? filterCookies(filter) : allCookies;
+ cookiesTreeView.rowCount = cookies.length;
+ cookiesTree.treeBoxObject.rowCountChanged(0, cookiesTreeView.rowCount);
+
+ // sort the tree according to the last sort parameters
+ SortTree(cookiesTree, cookiesTreeView, cookies, lastCookieSortColumn,
+ lastCookieSortColumn, !lastCookieSortAscending);
+
+ // disable Remove All Cookies button if the view is filtered or there are no cookies
+ if (filter || !cookies.length)
+ document.getElementById("removeAllCookies").setAttribute("disabled", "true");
+ else
+ document.getElementById("removeAllCookies").removeAttribute("disabled");
+
+ // if the view is filtered and not empty then select the first item
+ if (filter && cookies.length)
+ cookiesTreeView.selection.select(0);
+}
+
+function setFilter(aFilterString)
+{
+ document.getElementById("filter").value = aFilterString;
+ filter(aFilterString);
+}
+
+function focusFilterBox()
+{
+ var filterBox = document.getElementById("filter");
+ filterBox.focus();
+ filterBox.select();
+}
diff --git a/comm/suite/components/permissions/content/cookieViewer.xul b/comm/suite/components/permissions/content/cookieViewer.xul
new file mode 100644
index 0000000000..62a9db9ad3
--- /dev/null
+++ b/comm/suite/components/permissions/content/cookieViewer.xul
@@ -0,0 +1,225 @@
+<?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/. -->
+
+<!-- CHANGE THIS WHEN MOVING FILES -->
+<?xml-stylesheet href="chrome://communicator/skin/" type="text/css"?>
+
+<!-- CHANGE THIS WHEN MOVING FILES -->
+<!DOCTYPE dialog SYSTEM "chrome://communicator/locale/permissions/cookieViewer.dtd" >
+
+<dialog id="cookieviewer"
+ buttons="help"
+ title="&windowtitle.label;"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ windowtype="mozilla:cookieviewer"
+ style="width: 65ch; height: 42em;"
+ onload="Startup()"
+ onunload="Shutdown()"
+ ondialoghelp="doHelpButton();"
+ persist="screenX screenY width height">
+
+ <script src="chrome://communicator/content/permissions/cookieViewer.js"/>
+ <script src="chrome://communicator/content/permissions/permissionsUtils.js"/>
+ <script src="chrome://global/content/treeUtils.js"/>
+ <script src="chrome://help/content/contextHelp.js" />
+
+ <keyset id="dialogKeys">
+ <key key="&focusSearch.key;"
+ modifiers="accel"
+ oncommand="focusFilterBox();"/>
+ <key key="&selectAll.key;"
+ modifiers="accel"
+ oncommand="doSelectAll();"/>
+ </keyset>
+ <stringbundle id="cookieBundle"
+ src="chrome://communicator/locale/permissions/cookieViewer.properties"/>
+
+ <tabbox id="tabbox" flex="1">
+ <tabs>
+ <tab id="cookiesTab" label="&tab.cookiesonsystem.label;" help="cookies_stored"/>
+ <tab id="permissionsTab" label="&tab.bannedservers.label;" help="cookie_sites"/>
+ </tabs>
+ <tabpanels id="panel" flex="1">
+ <vbox class="tabpanel" id="system" flex="1">
+ <vbox id="dummyContainer" flex="1">
+ <!-- filter -->
+ <hbox align="center">
+ <textbox id="filter"
+ flex="1"
+ type="search"
+ aria-controls="cookiesTree"
+ placeholder="&search.placeholder;"
+ oncommand="filter(this.value);"/>
+ </hbox>
+ <separator class="thin"/>
+ <label value="&div.cookiesonsystem.label;" control="cookiesTree"/>
+ <separator class="thin"/>
+ <tree id="cookiesTree" flex="1" style="height: 10em;"
+ onkeypress="HandleCookieKeyPress(event);"
+ onselect="CookieSelected();"
+ sortAscending="true"
+ sortColumn="rawHost"
+ persist="sortAscending sortColumn">
+ <treecols>
+ <treecol id="rawHost"
+ label="&treehead.cookiedomain.label;"
+ flex="5"
+ onclick="CookieColumnSort(this.id, true);"
+ sortDirection="ascending"
+ persist="width hidden"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="name"
+ label="&treehead.cookiename.label;"
+ flex="5"
+ onclick="CookieColumnSort(this.id, true);"
+ persist="width hidden"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="expires"
+ label="&treehead.cookieexpires.label;"
+ flex="10"
+ hidden="true"
+ onclick="CookieColumnSort(this.id, true);"
+ persist="width hidden"/>
+ </treecols>
+ <treechildren/>
+ </tree>
+ <groupbox>
+ <caption label="&treehead.infoselected.label;"/>
+ <!-- labels -->
+ <grid flex="1">
+ <columns>
+ <column/>
+ <column flex="1"/>
+ </columns>
+ <rows>
+
+ <row align="center">
+ <hbox align="center" pack="end">
+ <label value="&props.name.label;" control="ifl_name"/>
+ </hbox>
+ <textbox id="ifl_name" readonly="true" class="plain"/>
+ </row>
+
+ <row align="center">
+ <hbox align="center" pack="end">
+ <label value="&props.value.label;" control="ifl_value"/>
+ </hbox>
+ <textbox id="ifl_value" readonly="true" class="plain"/>
+ </row>
+
+ <row align="center">
+ <hbox align="center" pack="end">
+ <label id="ifl_isDomain" value="&props.domain.label;" control="ifl_host"/>
+ </hbox>
+ <textbox id="ifl_host" readonly="true" class="plain"/>
+ </row>
+
+ <row align="center">
+ <hbox align="center" pack="end">
+ <label value="&props.path.label;" control="ifl_path"/>
+ </hbox>
+ <textbox id="ifl_path" readonly="true" class="plain"/>
+ </row>
+
+ <row align="center">
+ <hbox align="center" pack="end">
+ <label value="&props.secure.label;" control="ifl_isSecure"/>
+ </hbox>
+ <textbox id="ifl_isSecure" readonly="true" class="plain"/>
+ </row>
+
+ <row align="center">
+ <hbox align="center" pack="end">
+ <label value="&props.expires.label;" control="ifl_expires"/>
+ </hbox>
+ <textbox id="ifl_expires" readonly="true" class="plain"/>
+ </row>
+
+ </rows>
+ </grid>
+ </groupbox>
+ <hbox>
+ <button id="removeCookie" disabled="true"
+ label="&button.removecookie.label;"
+ accesskey="&button.removecookie.accesskey;"
+ oncommand="DeleteCookie();"/>
+ <button id="removeAllCookies"
+ label="&button.removeallcookies.label;"
+ accesskey="&button.removeallcookies.accesskey;"
+ oncommand="DeleteAllCookies();"/>
+ <!-- todo: <button id="restoreCookies" class="dialog push" disabled="true" label="&button.restorecookie.label;" oncommand="RestoreCookies();"/> -->
+ </hbox>
+ <separator class="thin"/>
+ <hbox align="start">
+ <checkbox id="checkbox" label="&futureCookies.label;" accesskey="&futureCookies.accesskey;" persist="checked"/>
+ </hbox>
+ </vbox>
+ </vbox>
+
+ <vbox id="servers" flex="1">
+ <description id="permissionsText">&div.bannedservers.label;</description>
+ <separator class="thin"/>
+ <hbox>
+ <textbox id="cookie-site"
+ flex="1"
+ oninput="handleHostInput(this.value);"/>
+ <button id="btnBlock" label="&blockSite.label;" disabled="true"
+ accesskey="&blockSite.accesskey;"
+ oncommand="setCookiePermissions(Ci.nsIPermissionManager.DENY_ACTION);"/>
+ <button id="btnSession" label="&allowSiteSession.label;" disabled="true"
+ accesskey="&allowSiteSession.accesskey;"
+ oncommand="setCookiePermissions(Ci.nsICookiePermission.ACCESS_SESSION);"/>
+ <button id="btnAllow" label="&allowSite.label;" disabled="true"
+ accesskey="&allowSite.accesskey;"
+ oncommand="setCookiePermissions(Ci.nsIPermissionManager.ALLOW_ACTION);"/>
+ </hbox>
+ <separator class="thin"/>
+ <tree id="permissionsTree"
+ flex="1"
+ style="height: 10em;"
+ hidecolumnpicker="true"
+ onkeypress="HandlePermissionKeyPress(event);"
+ onselect="PermissionSelected(this);"
+ sortAscending="true"
+ sortColumn="host"
+ persist="sortAscending sortColumn">
+ <treecols>
+ <treecol id="host"
+ label="&treehead.sitename.label;"
+ flex="5"
+ onclick="PermissionColumnSort(this.id, true);"
+ sortDirection="ascending"
+ persist="width"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="scheme"
+ label="&treehead.scheme.label;"
+ flex="5"
+ onclick="PermissionColumnSort(this.id, true);"
+ persist="width"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="capability"
+ label="&treehead.status.label;"
+ flex="5"
+ onclick="PermissionColumnSort(this.id, true);"
+ persist="width"/>
+ </treecols>
+ <treechildren/>
+ </tree>
+ <hbox>
+ <button id="removePermission"
+ disabled="true"
+ label="&removepermission.label;"
+ accesskey="&removepermission.accesskey;"
+ oncommand="DeletePermission();"/>
+ <button id="removeAllPermissions"
+ label="&removeallpermissions.label;"
+ accesskey="&removeallpermissions.accesskey;"
+ oncommand="DeleteAllPermissions();"/>
+ </hbox>
+ </vbox>
+
+ </tabpanels>
+ </tabbox>
+</dialog>
diff --git a/comm/suite/components/permissions/content/permissionsManager.js b/comm/suite/components/permissions/content/permissionsManager.js
new file mode 100644
index 0000000000..9004dc3cda
--- /dev/null
+++ b/comm/suite/components/permissions/content/permissionsManager.js
@@ -0,0 +1,287 @@
+/* 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/. */
+
+var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+const {AppConstants} = ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
+
+var permissions = [];
+var removals = [];
+
+var sortColumn;
+var sortAscending;
+
+var permissionsTreeView = {
+ rowCount: 0,
+ setTree: function(tree) {},
+ getImageSrc: function(row, column) {},
+ getProgressMode: function(row, column) {},
+ getCellValue: function(row, column) {},
+ getCellText: function(row, column) { return permissions[row][column.id]; },
+ isSeparator: function(index) { return false; },
+ isSorted: function() { return false; },
+ isContainer: function(index) { return false; },
+ cycleHeader: function(column) {},
+ getRowProperties: function(row, column) { return ""; },
+ getColumnProperties: function(column) { return ""; },
+ getCellProperties: function(row, column) { return ""; }
+ };
+
+var permissionsTree;
+var permissionType = "popup";
+var gManageCapability;
+
+var permissionsBundle;
+
+function Startup() {
+ var introText, windowTitle;
+
+ permissionsTree = document.getElementById("permissionsTree");
+
+ permissionsBundle = document.getElementById("permissionsBundle");
+
+ sortAscending = (permissionsTree.getAttribute("sortAscending") == "true");
+ sortColumn = permissionsTree.getAttribute("sortColumn");
+
+ var params = { blockVisible : true,
+ sessionVisible : true,
+ allowVisible : true,
+ manageCapability : true
+ };
+
+ if (window.arguments && window.arguments[0]) {
+ params = window.arguments[0];
+ setHost(params.prefilledHost);
+ permissionType = params.permissionType;
+ gManageCapability = params.manageCapability;
+ introText = params.introText;
+ windowTitle = params.windowTitle;
+ }
+
+ document.getElementById("btnBlock").hidden = !params.blockVisible;
+ document.getElementById("btnSession").hidden = !params.sessionVisible;
+ document.getElementById("btnAllow").hidden = !params.allowVisible;
+
+ document.getElementById("permissionsText").textContent = introText ||
+ permissionsBundle.getString(permissionType + "permissionstext");
+
+ document.title = windowTitle ||
+ permissionsBundle.getString(permissionType + "permissionstitle");
+
+ var dialogElement = document.getElementById("permissionsManager");
+ dialogElement.setAttribute("windowtype", "permissions-" + permissionType);
+
+ var urlFieldVisible = params.blockVisible ||
+ params.sessionVisible ||
+ params.allowVisible;
+
+ document.getElementById("url").hidden = !urlFieldVisible;
+ document.getElementById("urlLabel").hidden = !urlFieldVisible;
+
+ handleHostInput(document.getElementById("url").value);
+ loadPermissions();
+}
+
+function onAccept() {
+ finalizeChanges();
+ reInitialize();
+
+ // Don't close the window.
+ return false;
+}
+
+function onCancel() {
+ reInitialize();
+
+ // Don't close the window.
+ return false;
+}
+
+function reInitialize() {
+ permissions = [];
+ removals = [];
+
+ // loadPermissions will reverse the sort direction so flip it now.
+ sortAscending = !sortAscending;
+
+ // Reload permissions tree.
+ loadPermissions();
+}
+
+function setHost(aHost) {
+ document.getElementById("url").value = aHost;
+}
+
+function Permission(id, principal, host, type, capability, perm) {
+ this.id = id;
+ this.principal = principal;
+ this.host = host;
+ this.rawHost = host.replace(/^\./, "");
+ this.type = type;
+ this.capability = capability;
+ this.perm = perm;
+}
+
+function loadPermissions() {
+ var enumerator = Services.perms.enumerator;
+ var count = 0;
+ var permission;
+
+ try {
+ while (enumerator.hasMoreElements()) {
+ permission = enumerator.getNext().QueryInterface(Ci.nsIPermission);
+ if (permission.type == permissionType &&
+ (!gManageCapability || permission.capability == gManageCapability)) {
+ permissions.push(new Permission(count++,
+ permission.principal,
+ permission.principal.URI.host,
+ permission.type,
+ capabilityString(permission.capability),
+ permission.capability));
+ }
+ }
+ } catch(ex) {
+ }
+
+ permissionsTreeView.rowCount = permissions.length;
+
+ // sort and display the table
+ permissionsTree.view = permissionsTreeView;
+ permissionColumnSort(sortColumn, false);
+
+ // disable "remove all" button if there are none
+ document.getElementById("removeAllPermissions").disabled =
+ permissions.length == 0;
+}
+
+function capabilityString(aCapability) {
+ var capability = null;
+ switch (aCapability) {
+ case Ci.nsIPermissionManager.ALLOW_ACTION:
+ capability = "can";
+ break;
+ case Ci.nsIPermissionManager.DENY_ACTION:
+ capability = "cannot";
+ break;
+ // we should only ever hit this for cookies
+ case Ci.nsICookiePermission.ACCESS_SESSION:
+ capability = "canSession";
+ break;
+ default:
+ break;
+ }
+ return permissionsBundle.getString(capability);
+}
+
+function permissionColumnSort(aColumn, aUpdateSelection) {
+ sortAscending =
+ SortTree(permissionsTree, permissionsTreeView, permissions,
+ aColumn, sortColumn, sortAscending, aUpdateSelection);
+ sortColumn = aColumn;
+
+ SetSortDirection(permissionsTree, aColumn, sortAscending);
+}
+
+function deletePermissions() {
+ DeleteSelectedItemFromTree(permissionsTree, permissionsTreeView,
+ permissions, removals,
+ "removePermission", "removeAllPermissions");
+}
+
+function deleteAllPermissions() {
+ DeleteAllFromTree(permissionsTree, permissionsTreeView, permissions,
+ removals, "removePermission", "removeAllPermissions");
+}
+
+function finalizeChanges() {
+ let p;
+
+ for (let i in permissions) {
+ p = permissions[i];
+ try {
+ // Principal is null so a permission we just added in this session.
+ if (p.principal == null) {
+ let uri = Services.io.newURI("https://" + p.host);
+ Services.perms.add(uri, p.type, p.perm);
+ }
+ } catch(ex) {
+ }
+ }
+
+ for (let i in removals) {
+ p = removals[i];
+ try {
+ // Principal is not null so not a permission we just added in this
+ // session.
+ if (p.principal) {
+ Services.perms.removeFromPrincipal(p.principal,
+ p.type);
+ }
+ } catch(ex) {
+ }
+ }
+}
+
+function handlePermissionKeyPress(e) {
+ if (e.keyCode == KeyEvent.DOM_VK_DELETE ||
+ (AppConstants.platform == "macosx" &&
+ e.keyCode == KeyEvent.DOM_VK_BACK_SPACE)) {
+ deletePermissions();
+ }
+}
+
+function addPermission(aPermission) {
+ var textbox = document.getElementById("url");
+ // trim any leading and trailing spaces and scheme
+ var host = trimSpacesAndScheme(textbox.value);
+ try {
+ let uri = Services.io.newURI("https://" + host);
+ host = uri.host;
+ } catch(ex) {
+ var message = permissionsBundle.getFormattedString("alertInvalid", [host]);
+ var title = permissionsBundle.getString("alertInvalidTitle");
+ Services.prompt.alert(window, title, message);
+ textbox.value = "";
+ textbox.focus();
+ handleHostInput("");
+ return;
+ }
+
+ // we need this whether the perm exists or not
+ var stringCapability = capabilityString(aPermission);
+
+ // check whether the permission already exists, if not, add it
+ var exists = false;
+ for (var i in permissions) {
+ if (permissions[i].rawHost == host) {
+ // Avoid calling the permission manager if the capability settings are
+ // the same. Otherwise allow the call to the permissions manager to
+ // update the listbox for us.
+ exists = permissions[i].perm == aPermission;
+ break;
+ }
+ }
+
+ if (!exists) {
+ permissions.push(new Permission(permissions.length, null, host,
+ permissionType, stringCapability,
+ aPermission));
+
+ permissionsTreeView.rowCount = permissions.length;
+ permissionsTree.treeBoxObject.rowCountChanged(permissions.length - 1, 1);
+ permissionsTree.treeBoxObject.ensureRowIsVisible(permissions.length - 1);
+ }
+ textbox.value = "";
+ textbox.focus();
+
+ // covers a case where the site exists already, so the buttons don't disable
+ handleHostInput("");
+
+ // enable "remove all" button as needed
+ document.getElementById("removeAllPermissions").disabled = permissions.length == 0;
+}
+
+function doHelpButton() {
+ openHelp(permissionsBundle.getString(permissionType + "permissionshelp"), "chrome://communicator/locale/help/suitehelp.rdf");
+ return true;
+}
diff --git a/comm/suite/components/permissions/content/permissionsManager.xul b/comm/suite/components/permissions/content/permissionsManager.xul
new file mode 100644
index 0000000000..4231fd1a96
--- /dev/null
+++ b/comm/suite/components/permissions/content/permissionsManager.xul
@@ -0,0 +1,81 @@
+<?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://communicator/skin/" type="text/css"?>
+
+<!DOCTYPE dialog SYSTEM "chrome://communicator/locale/permissions/permissionsManager.dtd" >
+
+<dialog id="permissionsManager"
+ buttons="accept,cancel,help"
+ windowtype="exceptions"
+ title="&windowtitle.label;"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ style="width:32em; height:42em;"
+ persist="width height screenX screenY"
+ onload="Startup();"
+ ondialogaccept="return onAccept();"
+ ondialogcancel="return onCancel();"
+ ondialoghelp="return doHelpButton();">
+
+ <script src="chrome://communicator/content/permissions/permissionsManager.js"/>
+ <script src="chrome://communicator/content/permissions/permissionsUtils.js"/>
+ <script src="chrome://global/content/treeUtils.js"/>
+ <script src="chrome://help/content/contextHelp.js"/>
+
+ <stringbundle id="permissionsBundle"
+ src="chrome://communicator/locale/permissions/permissionsManager.properties"/>
+
+ <description id="permissionsText"/>
+ <separator class="thin"/>
+ <label id="urlLabel"
+ value="&address.label;"
+ accesskey="&address.accesskey;"
+ control="url"/>
+ <hbox align="start">
+ <textbox id="url" flex="1" oninput="handleHostInput(event.target.value);"/>
+ </hbox>
+ <hbox pack="end">
+ <button id="btnBlock" disabled="true" accesskey="&block.accesskey;"
+ label="&block.label;" oncommand="addPermission(Ci.nsIPermissionManager.DENY_ACTION);"/>
+ <button id="btnSession" disabled="true" accesskey="&session.accesskey;"
+ label="&session.label;" oncommand="addPermission(Ci.nsICookiePermission.ACCESS_SESSION);"/>
+ <button id="btnAllow" disabled="true" accesskey="&allow.accesskey;"
+ label="&allow.label;" oncommand="addPermission(Ci.nsIPermissionManager.ALLOW_ACTION);"/>
+ </hbox>
+ <separator class="thin"/>
+ <tree id="permissionsTree" flex="1" style="height: 18em;"
+ hidecolumnpicker="true"
+ onkeypress="handlePermissionKeyPress(event)"
+ onselect="PermissionSelected(this);"
+ sortAscending="false"
+ sortColumn="rawHost"
+ persist="sortAscending sortColumn">
+ <treecols>
+ <treecol id="rawHost"
+ label="&treehead.sitename.label;"
+ flex="3"
+ onclick="permissionColumnSort(this.id, true);"
+ sortDirection="descending"
+ persist="width"/>
+ <splitter class="tree-splitter"/>
+ <treecol id="capability"
+ label="&treehead.status.label;"
+ flex="1"
+ onclick="permissionColumnSort(this.id, true);"
+ persist="width"/>
+ </treecols>
+ <treechildren/>
+ </tree>
+ <separator class="thin"/>
+ <hbox>
+ <button id="removePermission" disabled="true"
+ label="&remove.label;" accesskey="&remove.accesskey;"
+ oncommand="deletePermissions();"/>
+ <button id="removeAllPermissions"
+ label="&removeall.label;" accesskey="&removeall.accesskey;"
+ oncommand="deleteAllPermissions();"/>
+ </hbox>
+</dialog>
diff --git a/comm/suite/components/permissions/content/permissionsUtils.js b/comm/suite/components/permissions/content/permissionsUtils.js
new file mode 100644
index 0000000000..4b208f87da
--- /dev/null
+++ b/comm/suite/components/permissions/content/permissionsUtils.js
@@ -0,0 +1,130 @@
+/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * 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/. */
+
+function DeleteAllFromTree
+ (tree, view, table, deletedTable, removeButton, removeAllButton) {
+
+ gTreeUtils.deleteAll(tree, view, table, deletedTable);
+
+ // disable buttons
+ document.getElementById(removeButton).setAttribute("disabled", "true")
+ document.getElementById(removeAllButton).setAttribute("disabled","true");
+}
+
+function DeleteSelectedItemFromTree
+ (tree, view, table, deletedTable, removeButton, removeAllButton) {
+
+ gTreeUtils.deleteSelectedItems(tree, view, table, deletedTable);
+
+ // disable buttons if nothing left in the table
+ if (!table.length) {
+ document.getElementById(removeButton).setAttribute("disabled", "true")
+ document.getElementById(removeAllButton).setAttribute("disabled","true");
+ }
+}
+
+function GetTreeSelections(tree) {
+ var selections = [];
+ var select = tree.view.selection;
+ if (select) {
+ var count = select.getRangeCount();
+ var min = new Object();
+ var max = new Object();
+ for (var i=0; i<count; i++) {
+ select.getRangeAt(i, min, max);
+ for (var k=min.value; k<=max.value; k++) {
+ if (k != -1) {
+ selections[selections.length] = k;
+ }
+ }
+ }
+ }
+ return selections;
+}
+
+function SortTree(tree, view, table, column, lastSortColumn, lastSortAscending, updateSelection) {
+
+ // remember which item was selected so we can restore it after the sort
+ var selections = GetTreeSelections(tree);
+ var selectedNumber = selections.length ? table[selections[0]].id : -1;
+
+ // do the sort or re-sort
+ // this is a temporary hack for 1.7, we should implement
+ // display and sort variables here for trees in general
+ var sortColumn;
+ var comparator;
+ if (column == "expires") {
+ sortColumn = "expiresSortValue";
+ comparator = function compare(a, b) { return a - b; };
+ } else {
+ sortColumn = column;
+ comparator = function compare(a, b) {
+ return a.toLowerCase().localeCompare(b.toLowerCase());
+ };
+ }
+ if (lastSortColumn == "expires") {
+ lastSortColumn = "expiresSortValue";
+ }
+ var ascending = gTreeUtils.sort(tree, view, table, sortColumn, comparator,
+ lastSortColumn, lastSortAscending);
+
+ // restore the selection
+ if (selectedNumber >= 0 && updateSelection) {
+ var selectedRow = -1;
+ for (var s = 0; s < table.length; s++) {
+ if (table[s].id == selectedNumber) {
+ selectedRow = s;
+ break;
+ }
+ }
+
+ if (selectedRow > 0) {
+ // update selection and display the results
+ tree.view.selection.select(selectedRow);
+ tree.treeBoxObject.invalidate();
+ tree.treeBoxObject.ensureRowIsVisible(selectedRow);
+ }
+ }
+
+ return ascending;
+}
+
+function handleHostInput(aValue) {
+ // trim any leading and trailing spaces and scheme
+ // and set buttons appropiately
+ btnDisable(!trimSpacesAndScheme(aValue));
+}
+
+function trimSpacesAndScheme(aString) {
+ if (!aString)
+ return "";
+ return aString.trim().replace(/([-\w]*:\/+)?/, "");
+}
+
+function btnDisable(aDisabled) {
+ document.getElementById("btnSession").disabled = aDisabled;
+ document.getElementById("btnBlock").disabled = aDisabled;
+ document.getElementById("btnAllow").disabled = aDisabled;
+}
+
+function PermissionSelected(tree) {
+ var hasSelection = tree.view.selection.count > 0;
+ document.getElementById("removePermission").disabled = !hasSelection;
+}
+
+function SetSortDirection(tree, column, ascending) {
+ // first we need to get the right elements
+ for (let col of tree.getElementsByTagName("treecol")) {
+ if (col.id == column) {
+ // set the sortDirection attribute to get the styling going
+ col.setAttribute("sortDirection", ascending ? "ascending" : "descending");
+ }
+ else {
+ // clear out the sortDirection attribute on the rest of the columns
+ col.removeAttribute("sortDirection");
+ }
+ }
+}
diff --git a/comm/suite/components/permissions/jar.mn b/comm/suite/components/permissions/jar.mn
new file mode 100644
index 0000000000..15784fc80b
--- /dev/null
+++ b/comm/suite/components/permissions/jar.mn
@@ -0,0 +1,10 @@
+# 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/.
+
+comm.jar:
+ content/communicator/permissions/cookieViewer.js (content/cookieViewer.js)
+ content/communicator/permissions/cookieViewer.xul (content/cookieViewer.xul)
+ content/communicator/permissions/permissionsManager.js (content/permissionsManager.js)
+ content/communicator/permissions/permissionsManager.xul (content/permissionsManager.xul)
+ content/communicator/permissions/permissionsUtils.js (content/permissionsUtils.js)
diff --git a/comm/suite/components/permissions/moz.build b/comm/suite/components/permissions/moz.build
new file mode 100644
index 0000000000..d988c0ff9b
--- /dev/null
+++ b/comm/suite/components/permissions/moz.build
@@ -0,0 +1,7 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+JAR_MANIFESTS += ["jar.mn"]