Bug 1572129 - Simplify and harmonise cookieViewer and permissionsManager code. r=frg a=frg
authorIan Neal <iann_cvs@blueyonder.co.uk>
Fri, 23 Aug 2019 01:11:18 +0200
changeset 32276 30dfd87ce129bbf0ab026092f17a106c76a2cdd8
parent 32275 009cd7edaeab4deaad2fa7eba7681122ea420340
child 32277 5a912babe8be5a38c89de23f8ef4d66c9192b83c
push id211
push userfrgrahl@gmx.net
push dateThu, 22 Aug 2019 23:17:49 +0000
treeherdercomm-esr60@75fdb3e8faed [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfrg, frg
bugs1572129
Bug 1572129 - Simplify and harmonise cookieViewer and permissionsManager code. r=frg a=frg
suite/components/permissions/content/cookieViewer.js
suite/components/permissions/content/cookieViewer.xul
suite/components/permissions/content/permissionsManager.js
suite/components/permissions/content/permissionsManager.xul
suite/components/permissions/content/treeUtils.js
--- a/suite/components/permissions/content/cookieViewer.js
+++ b/suite/components/permissions/content/cookieViewer.js
@@ -11,34 +11,42 @@ ChromeUtils.import("resource://gre/modul
 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.addObserver(cookieReloadDisplay, "cookie-changed");
-  Services.obs.addObserver.addObserver(cookieReloadDisplay, "perm-changed");
+  Services.obs.addObserver(cookieReloadDisplay, "cookie-changed");
+  Services.obs.addObserver(cookieReloadDisplay, "perm-changed");
 
   // 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();
@@ -85,39 +93,25 @@ function doSelectAll() {
 /*** =================== 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){
-    var rv="";
-    switch (column.id) {
-    case "domainCol":
-      rv = cookies[row].rawHost;
-      break;
-    case "nameCol":
-      rv = cookies[row].name;
-      break;
-    case "expiresCol":
-      rv = cookies[row].expires;
-      break;
-    }
-    return rv;
-  },
+  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;
@@ -134,22 +128,22 @@ 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[count] =
+    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);
+                 nextCookie.isSecure, nextCookie.expires));
   }
 
   // filter, sort and display the table
   cookiesTree.view = cookiesTreeView;
   filter(document.getElementById("filter").value);
 }
 
 function GetExpiresString(expires) {
@@ -282,84 +276,43 @@ function FinalizeCookieDeletions() {
 function HandleCookieKeyPress(e) {
   if (e.keyCode == KeyEvent.DOM_VK_DELETE ||
       (AppConstants.platform == "macosx" &&
        e.keyCode == KeyEvent.DOM_VK_BACK_SPACE)) {
     DeleteCookie();
   }
 }
 
-var lastCookieSortColumn = "rawHost";
-var lastCookieSortAscending = true;
-
 function CookieColumnSort(column, updateSelection) {
   lastCookieSortAscending =
       SortTree(cookiesTree, cookiesTreeView, cookies,
                column, lastCookieSortColumn, lastCookieSortAscending,
                updateSelection);
   lastCookieSortColumn = column;
-  // set the sortDirection attribute to get the styling going
-  // first we need to get the right element
-  var sortedCol;
-  switch (column) {
-    case "rawHost":
-      sortedCol = document.getElementById("domainCol");
-      break;
-    case "name":
-      sortedCol = document.getElementById("nameCol");
-      break;
-    case "expires":
-      sortedCol = document.getElementById("expiresCol");
-      break;
-  }
-  if (lastCookieSortAscending)
-    sortedCol.setAttribute("sortDirection", "ascending");
-  else
-    sortedCol.setAttribute("sortDirection", "descending");
 
-  // clear out the sortDirection attribute on the rest of the columns
-  var currentCol = sortedCol.parentNode.firstChild;
-  while (currentCol) {
-    if (currentCol != sortedCol && currentCol.localName == "treecol")
-      currentCol.removeAttribute("sortDirection");
-    currentCol = currentCol.nextSibling;
-  }
+  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) {
-    var rv = "";
-    switch (column.id) {
-      case "siteCol":
-        rv = permissions[row].host;
-        break;
-      case "siteCol2":
-        rv = permissions[row].scheme;
-        break;
-      case "capabilityCol":
-        rv = permissions[row].capability;
-        break;
-    }
-    return rv;
-  },
+  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;
@@ -367,95 +320,83 @@ function Permission(id, principal, type,
 }
 
 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()) {
-    var nextPermission = enumerator.getNext();
-    nextPermission = nextPermission.QueryInterface(Ci.nsIPermission);
+    permission = enumerator.getNext().QueryInterface(Ci.nsIPermission);
     // We are only interested in cookie permissions in this code.
-    if (nextPermission.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.
+    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 {
-        nextPermission.principal.URI.host;
+        permission.principal.URI.host;
       }
       catch (e) {
         Cu.reportError("Invalid permission found: " +
-                       nextPermission.principal.origin + " " +
-                       nextPermission.type);
+                       permission.principal.origin + " " + permission.type);
         continue;
       }
 
-      var capability;
-      switch (nextPermission.capability) {
+      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(permissions.length,
-                                      nextPermission.principal,
-                                      nextPermission.type,
+      permissions.push(new Permission(count++,
+                                      permission.principal,
+                                      permission.type,
                                       capability));
     }
   }
   permissionsTreeView.rowCount = permissions.length;
 
   // sort and display the table
   permissionsTree.view = permissionsTreeView;
-  permissionsTreeView.selection.select(-1);
+  permissionsTreeView.selection.clearSelection();
   SortTree(permissionsTree, permissionsTreeView, permissions,
            lastPermissionSortColumn, lastPermissionSortColumn,
            !lastPermissionSortAscending);
 
   // disable "remove all" button if there are no cookies
-  if (permissions.length == 0) {
-    document.getElementById("removeAllPermissions").setAttribute("disabled", "true");
-  } else {
-    document.getElementById("removeAllPermissions").removeAttribute("disabled");
-  }
-
-}
-
-function PermissionSelected() {
-  var selections = GetTreeSelections(permissionsTree);
-  if (selections.length)
-    document.getElementById("removePermission").removeAttribute("disabled");
-  else
-    document.getElementById("removePermission").setAttribute("disabled", "true");
+  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');
+  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;
@@ -465,41 +406,30 @@ function setCookiePermissions(action) {
     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') {
+  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 buttonEnabling(textfield) {
-  // trim any leading space
-  var site = textfield.value.replace(/^\s*([-\w]*:\/+)?/, "");
-  var block = document.getElementById("btnBlock");
-  var session = document.getElementById("btnSession");
-  var allow = document.getElementById("btnAllow");
-  block.disabled = !site;
-  session.disabled = !site;
-  allow.disabled = !site;
-}
-
 function DeleteAllPermissions() {
   if (PromptConfirm("deleteAllSitesTitle",
                     "deleteAllCookiesSites",
                     "deleteAllSitesYes") == 1) {
     return;
   }
 
   DeleteAllFromTree(permissionsTree, permissionsTreeView,
@@ -515,65 +445,40 @@ function FinalizePermissionDeletions() {
   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 == 46) {
+  if (e.keyCode == KeyEvent.DOM_VK_DELETE ||
+      (AppConstants.platform == "macosx" &&
+       e.keyCode == KeyEvent.DOM_VK_BACK_SPACE)) {
     DeletePermission();
   }
 }
 
-var lastPermissionSortColumn = "host";
-var lastPermissionSortAscending = true;
-
 function PermissionColumnSort(column, updateSelection) {
   lastPermissionSortAscending =
     SortTree(permissionsTree, permissionsTreeView, permissions,
                  column, lastPermissionSortColumn, lastPermissionSortAscending,
                  updateSelection);
   lastPermissionSortColumn = column;
 
-  // make sure sortDirection is set
-  var sortedCol;
-  switch (column) {
-    case "host":
-      sortedCol = document.getElementById("siteCol");
-      break;
-    case "scheme":
-      sortedCol = document.getElementById("siteCol2");
-      break;
-    case "capability":
-      sortedCol = document.getElementById("capabilityCol");
-      break;
-  }
-  if (lastPermissionSortAscending)
-    sortedCol.setAttribute("sortDirection", "ascending");
-  else
-    sortedCol.setAttribute("sortDirection", "descending");
-
-  // clear out the sortDirection attribute on the rest of the columns
-  var currentCol = sortedCol.parentNode.firstChild;
-  while (currentCol) {
-    if (currentCol != sortedCol && currentCol.localName == "treecol")
-      currentCol.removeAttribute("sortDirection");
-    currentCol = currentCol.nextSibling;
-  }
+  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');
+  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) {
--- a/suite/components/permissions/content/cookieViewer.xul
+++ b/suite/components/permissions/content/cookieViewer.xul
@@ -54,28 +54,41 @@
                      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();">
+                onkeypress="HandleCookieKeyPress(event);"
+                onselect="CookieSelected();"
+                sortAscending="true"
+                sortColumn="rawHost"
+                persist="sortAscending sortColumn">
             <treecols>
-              <treecol id="domainCol" label="&treehead.cookiedomain.label;" flex="5"
-                       onclick="CookieColumnSort('rawHost', true);" persist="width hidden"
-                       sortDirection="ascending"/>
+              <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="nameCol" label="&treehead.cookiename.label;" flex="5"
-                       onclick="CookieColumnSort('name', true);" persist="width hidden"/>
+              <treecol id="name"
+                       label="&treehead.cookiename.label;"
+                       flex="5"
+                       onclick="CookieColumnSort(this.id, true);"
+                       persist="width hidden"/>
               <splitter class="tree-splitter"/>
-              <treecol id="expiresCol" label="&treehead.cookieexpires.label;" flex="10"
-                       hidden="true" onclick="CookieColumnSort('expires', true);" persist="width hidden"/>
+              <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>
@@ -146,50 +159,57 @@
           </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="buttonEnabling(this);"/>
+            <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();">
+                onkeypress="HandlePermissionKeyPress(event);"
+                onselect="PermissionSelected(this);"
+                sortAscending="true"
+                sortColumn="host"
+                persist="sortAscending sortColumn">
             <treecols>
-              <treecol id="siteCol"
+              <treecol id="host"
                        label="&treehead.sitename.label;"
                        flex="5"
-                       onclick="PermissionColumnSort('host', true);" persist="width"
-                       sortDirection="ascending"/>
-              <splitter class="tree-splitter"/>
-              <treecol id="siteCol2" label="&treehead.scheme.label;"
-                       flex="5"
-                       onclick="PermissionColumnSort('scheme', true);"
+                       onclick="PermissionColumnSort(this.id, true);"
+                       sortDirection="ascending"
                        persist="width"/>
               <splitter class="tree-splitter"/>
-              <treecol id="capabilityCol"
+              <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('capability', true);"
+                       onclick="PermissionColumnSort(this.id, true);"
                        persist="width"/>
             </treecols>
             <treechildren/>
           </tree>
           <hbox>
             <button id="removePermission"
                     disabled="true"
                     label="&removepermission.label;"
--- a/suite/components/permissions/content/permissionsManager.js
+++ b/suite/components/permissions/content/permissionsManager.js
@@ -1,33 +1,28 @@
 /* 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/. */
 
-Cu.import("resource://gre/modules/Services.jsm");
+ChromeUtils.import("resource://gre/modules/Services.jsm");
+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) {
-      if (column.id == "siteCol")
-        return permissions[row].rawHost;
-      else if (column.id == "statusCol")
-        return permissions[row].capability;
-      return "";
-    },
+    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 ""; }
   };
@@ -78,17 +73,17 @@ function Startup() {
 
   var urlFieldVisible = params.blockVisible ||
                         params.sessionVisible ||
                         params.allowVisible;
 
   document.getElementById("url").hidden = !urlFieldVisible;
   document.getElementById("urlLabel").hidden = !urlFieldVisible;
 
-  handleHostInput(document.getElementById("url"));
+  handleHostInput(document.getElementById("url").value);
   loadPermissions();
 }
 
 function onAccept() {
   finalizeChanges();
   reInitialize();
 
   // Don't close the window.
@@ -101,53 +96,37 @@ function onCancel() {
   // 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 handleHostInput(aSiteField) {
-  // trim any leading and trailing spaces and scheme
-  // and set buttons appropiately
-  btnDisable(!trimSpacesAndScheme(aSiteField.value));
-}
-
-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 loadPermissions() {
   var enumerator = Services.perms.enumerator;
   var count = 0;
   var permission;
 
   try {
     while (enumerator.hasMoreElements()) {
       permission = enumerator.getNext().QueryInterface(Ci.nsIPermission);
@@ -194,23 +173,18 @@ function capabilityString(aCapability) {
   return permissionsBundle.getString(capability);
 }
 
 function permissionColumnSort(aColumn, aUpdateSelection) {
   sortAscending =
     SortTree(permissionsTree, permissionsTreeView, permissions,
              aColumn, sortColumn, sortAscending, aUpdateSelection);
   sortColumn = aColumn;
-}
 
-function permissionSelected() {
-  if (Services.perms) {
-    var selections = GetTreeSelections(permissionsTree);
-    document.getElementById("removePermission").disabled = (selections.length < 1);
-  }
+  SetSortDirection(permissionsTree, aColumn, sortAscending);
 }
 
 function deletePermissions() {
   DeleteSelectedItemFromTree(permissionsTree, permissionsTreeView,
                              permissions, removals,
                              "removePermission", "removeAllPermissions");
 }
 
@@ -244,17 +218,19 @@ function finalizeChanges() {
                                            p.type);
       }
     } catch(ex) {
     }
   }
 }
 
 function handlePermissionKeyPress(e) {
-  if (e.keyCode == 46) {
+  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);
@@ -262,17 +238,17 @@ function addPermission(aPermission) {
     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(textbox);
+    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;
@@ -294,17 +270,17 @@ function addPermission(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(textbox);
+  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;
--- a/suite/components/permissions/content/permissionsManager.xul
+++ b/suite/components/permissions/content/permissionsManager.xul
@@ -29,40 +29,47 @@
 
   <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);"/>
+    <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();"
+        onselect="PermissionSelected(this);"
         sortAscending="false"
         sortColumn="rawHost"
         persist="sortAscending sortColumn">
     <treecols>
-      <treecol id="siteCol" label="&treehead.sitename.label;" flex="3"
-                    onclick="permissionColumnSort('rawHost', true);" persist="width"/>
+      <treecol id="rawHost"
+               label="&treehead.sitename.label;"
+               flex="3"
+               onclick="permissionColumnSort(this.id, true);"
+               sortDirection="descending"
+               persist="width"/>
       <splitter class="tree-splitter"/>
-      <treecol id="statusCol" label="&treehead.status.label;" flex="1"
-                    onclick="permissionColumnSort('capability', true);" persist="width"/>
+      <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();"/>
--- a/suite/components/permissions/content/treeUtils.js
+++ b/suite/components/permissions/content/treeUtils.js
@@ -122,25 +122,61 @@ function SortTree(tree, view, table, col
 
   // restore the selection
   var selectedRow = -1;
   if (selectedNumber>=0 && updateSelection) {
     for (var s=0; s<table.length; s++) {
       if (table[s].id == selectedNumber) {
         // update selection
         // note: we need to deselect before reselecting in order to trigger ...Selected()
-        tree.view.selection.select(-1);
+        tree.view.selection.clearSelection();
         tree.view.selection.select(s);
         selectedRow = s;
         break;
       }
     }
   }
 
   // display the results
   tree.treeBoxObject.invalidate();
   if (selectedRow >= 0) {
     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");
+    }
+  }
+}