Bug 812289 - PermissionSettings doesn't enforce any restriction on permissions operations [r=sicking]
authorAntonio M. Amaya <amac@tid.es>
Sat, 15 Dec 2012 02:32:30 +0100
changeset 125528 ec4945922e6291717cb2b72d629e5ff3e2d5037d
parent 125527 8e79ce6ac5056e2592eed09d5630a077c7de89ac
child 125529 ba17dfaa86e991c6e5a7ec1ec6d8a5c77031bcd4
push id2151
push userlsblakk@mozilla.com
push dateTue, 19 Feb 2013 18:06:57 +0000
treeherdermozilla-beta@4952e88741ec [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssicking
bugs812289
milestone20.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 812289 - PermissionSettings doesn't enforce any restriction on permissions operations [r=sicking]
b2g/components/ContentPermissionPrompt.js
dom/apps/src/Makefile.in
dom/apps/src/PermissionsInstaller.jsm
dom/apps/src/PermissionsTable.jsm
dom/interfaces/permission/nsIDOMPermissionSettings.idl
dom/messages/SystemMessagePermissionsChecker.jsm
dom/permission/PermissionPromptHelper.jsm
dom/permission/PermissionSettings.js
dom/permission/PermissionSettings.jsm
--- a/b2g/components/ContentPermissionPrompt.js
+++ b/b2g/components/ContentPermissionPrompt.js
@@ -20,16 +20,17 @@ const Cc = Components.classes;
 
 const PROMPT_FOR_UNKNOWN = ['geolocation'];
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/Webapps.jsm");
 Cu.import("resource://gre/modules/AppsUtils.jsm");
 Cu.import("resource://gre/modules/PermissionsInstaller.jsm");
+Cu.import("resource://gre/modules/PermissionsTable.jsm");
 
 var permissionManager = Cc["@mozilla.org/permissionmanager;1"].getService(Ci.nsIPermissionManager);
 var secMan = Cc["@mozilla.org/scriptsecuritymanager;1"].getService(Ci.nsIScriptSecurityManager);
 
 XPCOMUtils.defineLazyServiceGetter(this,
                                    "PermSettings",
                                    "@mozilla.org/permissionSettings;1",
                                    "nsIDOMPermissionSettings");
--- a/dom/apps/src/Makefile.in
+++ b/dom/apps/src/Makefile.in
@@ -23,11 +23,12 @@ EXTRA_PP_JS_MODULES += \
   Webapps.jsm \
   $(NULL)
 
 EXTRA_JS_MODULES += \
   AppsServiceChild.jsm \
   AppsUtils.jsm \
   OfflineCacheInstaller.jsm \
   PermissionsInstaller.jsm \
+  PermissionsTable.jsm \
   $(NULL)
 
 include $(topsrcdir)/config/rules.mk
--- a/dom/apps/src/PermissionsInstaller.jsm
+++ b/dom/apps/src/PermissionsInstaller.jsm
@@ -5,22 +5,19 @@
 "use strict";
 
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/AppsUtils.jsm");
 Cu.import("resource://gre/modules/PermissionSettings.jsm");
+Cu.import("resource://gre/modules/PermissionsTable.jsm");
 
-this.EXPORTED_SYMBOLS = ["PermissionsInstaller",
-                         "expandPermissions",
-                         "PermissionsTable",
-                         "appendAccessToPermName"
-                        ];
+this.EXPORTED_SYMBOLS = ["PermissionsInstaller"];
 const UNKNOWN_ACTION = Ci.nsIPermissionManager.UNKNOWN_ACTION;
 const ALLOW_ACTION = Ci.nsIPermissionManager.ALLOW_ACTION;
 const DENY_ACTION = Ci.nsIPermissionManager.DENY_ACTION;
 const PROMPT_ACTION = Ci.nsIPermissionManager.PROMPT_ACTION;
 
 // Permission access flags
 const READONLY = "readonly";
 const CREATEONLY = "createonly";
@@ -28,355 +25,16 @@ const READCREATE = "readcreate";
 const READWRITE = "readwrite";
 
 const PERM_TO_STRING = ["unknown", "allow", "deny", "prompt"];
 
 function debug(aMsg) {
   //dump("-*-*- PermissionsInstaller.jsm : " + aMsg + "\n");
 }
 
-// Permissions Matrix: https://docs.google.com/spreadsheet/ccc?key=0Akyz_Bqjgf5pdENVekxYRjBTX0dCXzItMnRyUU1RQ0E#gid=0
-// Also, keep in sync with https://mxr.mozilla.org/mozilla-central/source/extensions/cookie/Permission.txt
-
-// Permissions that are implicit:
-// battery-status, network-information, vibration,
-// device-capabilities
-
-this.PermissionsTable =  { geolocation: {
-                             app: PROMPT_ACTION,
-                             privileged: PROMPT_ACTION,
-                             certified: ALLOW_ACTION
-                           },
-                           camera: {
-                             app: DENY_ACTION,
-                             privileged: DENY_ACTION,
-                             certified: ALLOW_ACTION
-                           },
-                           alarms: {
-                             app: ALLOW_ACTION,
-                             privileged: ALLOW_ACTION,
-                             certified: ALLOW_ACTION
-                           },
-                           "tcp-socket": {
-                             app: DENY_ACTION,
-                             privileged: ALLOW_ACTION,
-                             certified: ALLOW_ACTION
-                           },
-                           "network-events": {
-                             app: DENY_ACTION,
-                             privileged: DENY_ACTION,
-                             certified: ALLOW_ACTION
-                           },
-                           contacts: {
-                             app: DENY_ACTION,
-                             privileged: PROMPT_ACTION,
-                             certified: ALLOW_ACTION,
-                             access: ["read", "write", "create"]
-                           },
-                           "device-storage:apps": {
-                             app: DENY_ACTION,
-                             privileged: DENY_ACTION,
-                             certified: ALLOW_ACTION,
-                             access: ["read"]
-                           },
-                           "device-storage:pictures": {
-                             app: DENY_ACTION,
-                             privileged: PROMPT_ACTION,
-                             certified: ALLOW_ACTION,
-                             access: ["read", "write", "create"]
-                           },
-                           "device-storage:videos": {
-                             app: DENY_ACTION,
-                             privileged: PROMPT_ACTION,
-                             certified: ALLOW_ACTION,
-                             access: ["read", "write", "create"]
-                           },
-                           "device-storage:music": {
-                             app: DENY_ACTION,
-                             privileged: PROMPT_ACTION,
-                             certified: ALLOW_ACTION,
-                             access: ["read", "write", "create"]
-                           },
-                           "device-storage:sdcard": {
-                             app: DENY_ACTION,
-                             privileged: PROMPT_ACTION,
-                             certified: ALLOW_ACTION,
-                             access: ["read", "write", "create"]
-                           },
-                           sms: {
-                             app: DENY_ACTION,
-                             privileged: DENY_ACTION,
-                             certified: ALLOW_ACTION
-                           },
-                           telephony: {
-                             app: DENY_ACTION,
-                             privileged: DENY_ACTION,
-                             certified: ALLOW_ACTION
-                           },
-                           browser: {
-                             app: DENY_ACTION,
-                             privileged: ALLOW_ACTION,
-                             certified: ALLOW_ACTION
-                           },
-                           bluetooth: {
-                             app: DENY_ACTION,
-                             privileged: DENY_ACTION,
-                             certified: ALLOW_ACTION
-                           },
-                           mobileconnection: {
-                             app: DENY_ACTION,
-                             privileged: DENY_ACTION,
-                             certified: ALLOW_ACTION
-                           },
-                           power: {
-                             app: DENY_ACTION,
-                             privileged: DENY_ACTION,
-                             certified: ALLOW_ACTION
-                           },
-                           settings: {
-                             app: DENY_ACTION,
-                             privileged: DENY_ACTION,
-                             certified: ALLOW_ACTION,
-                             access: ["read", "write"],
-                             additional: ["indexedDB-chrome-settings"]
-                           },
-                           permissions: {
-                             app: DENY_ACTION,
-                             privileged: DENY_ACTION,
-                             certified: ALLOW_ACTION
-                           },
-                           fmradio: {
-                             app: ALLOW_ACTION,
-                             privileged: ALLOW_ACTION,
-                             certified: ALLOW_ACTION
-                           },
-                           attention: {
-                             app: DENY_ACTION,
-                             privileged: DENY_ACTION,
-                             certified: ALLOW_ACTION
-                           },
-                           "webapps-manage": {
-                             app: DENY_ACTION,
-                             privileged: DENY_ACTION,
-                             certified: ALLOW_ACTION
-                           },
-                           "backgroundservice": {
-                             app: DENY_ACTION,
-                             privileged: DENY_ACTION,
-                             certified: ALLOW_ACTION
-                           },
-                           "desktop-notification": {
-                             app: ALLOW_ACTION,
-                             privileged: ALLOW_ACTION,
-                             certified: ALLOW_ACTION
-                           },
-                           "networkstats-manage": {
-                             app: DENY_ACTION,
-                             privileged: DENY_ACTION,
-                             certified: ALLOW_ACTION
-                           },
-                           "wifi-manage": {
-                             app: DENY_ACTION,
-                             privileged: DENY_ACTION,
-                             certified: ALLOW_ACTION
-                           },
-                           "systemXHR": {
-                             app: DENY_ACTION,
-                             privileged: ALLOW_ACTION,
-                             certified: ALLOW_ACTION
-                           },
-                           "voicemail": {
-                             app: DENY_ACTION,
-                             privileged: DENY_ACTION,
-                             certified: ALLOW_ACTION
-                           },
-                           "deprecated-hwvideo": {
-                             app: DENY_ACTION,
-                             privileged: DENY_ACTION,
-                             certified: ALLOW_ACTION
-                           },
-                           "idle": {
-                             app: DENY_ACTION,
-                             privileged: DENY_ACTION,
-                             certified: ALLOW_ACTION
-                           },
-                           "time": {
-                             app: DENY_ACTION,
-                             privileged: DENY_ACTION,
-                             certified: ALLOW_ACTION
-                           },
-                           "embed-apps": {
-                             app: DENY_ACTION,
-                             privileged: DENY_ACTION,
-                             certified: ALLOW_ACTION
-                           },
-                           "storage": {
-                             app: DENY_ACTION,
-                             privileged: DENY_ACTION,
-                             certified: ALLOW_ACTION,
-                             substitute: [
-                               "indexedDB-unlimited",
-                               "offline-app",
-                               "pin-app"
-                             ]
-                           },
-                           "background-sensors": {
-                             app: DENY_ACTION,
-                             privileged: DENY_ACTION,
-                             certified: ALLOW_ACTION
-                           },
-                           cellbroadcast: {
-                             app: DENY_ACTION,
-                             privileged: DENY_ACTION,
-                             certified: ALLOW_ACTION
-                           },
-                           "audio-channel-normal": {
-                             app: ALLOW_ACTION,
-                             privileged: ALLOW_ACTION,
-                             certified: ALLOW_ACTION
-                           },
-                           "audio-channel-content": {
-                             app: ALLOW_ACTION,
-                             privileged: ALLOW_ACTION,
-                             certified: ALLOW_ACTION
-                           },
-                           "audio-channel-notification": {
-                             app: DENY_ACTION,
-                             privileged: ALLOW_ACTION,
-                             certified: ALLOW_ACTION
-                           },
-                           "audio-channel-alarm": {
-                             app: DENY_ACTION,
-                             privileged: ALLOW_ACTION,
-                             certified: ALLOW_ACTION
-                           },
-                           "audio-channel-telephony": {
-                             app: DENY_ACTION,
-                             privileged: DENY_ACTION,
-                             certified: ALLOW_ACTION
-                           },
-                           "audio-channel-ringer": {
-                             app: DENY_ACTION,
-                             privileged: DENY_ACTION,
-                             certified: ALLOW_ACTION
-                           },
-                           "audio-channel-publicnotification": {
-                             app: DENY_ACTION,
-                             privileged: DENY_ACTION,
-                             certified: ALLOW_ACTION
-                           },
-                           "open-remote-window": {
-                             app: DENY_ACTION,
-                             privileged: DENY_ACTION,
-                             certified: ALLOW_ACTION
-                           },
-                         };
-
-/**
- * Append access modes to the permission name as suffixes.
- *   e.g. permission name 'contacts' with ['read', 'write'] =
- *   ['contacts-read', contacts-write']
- * @param string aPermName
- * @param array aAccess
- * @returns array containing access-appended permission names.
- **/
-this.appendAccessToPermName = function appendAccessToPermName(aPermName, aAccess) {
-  if (aAccess.length == 0) {
-    return [aPermName];
-  }
-  return aAccess.map(function(aMode) {
-    return aPermName + "-" + aMode;
-  });
-};
-
-/**
- * Expand an access string into multiple permission names,
- *   e.g: permission name 'contacts' with 'readwrite' =
- *   ['contacts-read', 'contacts-create', 'contacts-write']
- * @param string aPermName
- * @param string aAccess (optional)
- * @returns array containing expanded permission names.
- **/
-this.expandPermissions = function expandPermissions(aPermName, aAccess) {
-  if (!PermissionsTable[aPermName]) {
-    Cu.reportError("PermissionsInstaller.jsm: expandPermissions: Unknown Permission: " + aPermName);
-    dump("PermissionsInstaller.jsm: expandPermissions: Unknown Permission: " + aPermName);
-    return [];
-  }
-
-  const tableEntry = PermissionsTable[aPermName];
-
-  if (tableEntry.substitute && tableEntry.additional) {
-    Cu.reportError("PermissionsInstaller.jsm: expandPermissions: Can't handle both 'substitute' " +
-                   "and 'additional' entries for permission: " + aPermName);
-    dump("PermissionsInstaller.jsm: expandPermissions: Can't handle both 'substitute' " +
-         "and 'additional' entries for permission: " + aPermName);
-    return [];
-  }
-
-  if (!aAccess && tableEntry.access ||
-      aAccess && !tableEntry.access) {
-    Cu.reportError("PermissionsTable.jsm: expandPermissions: Invalid Manifest : " +
-                   aPermName + " " + aAccess + "\n");
-    dump("PermissionsInstaller.jsm: expandPermissions: Invalid Manifest: " +
-         aPermName + " " + aAccess + "\n");
-    throw new Error("PermissionsInstaller.jsm: expandPermissions: Invalid Manifest: " +
-                    aPermName + " " + aAccess + "\n");
-  }
-
-  let expandedPermNames = [];
-
-  if (tableEntry.access && aAccess) {
-    let requestedSuffixes = [];
-    switch (aAccess) {
-    case READONLY:
-      requestedSuffixes.push("read");
-      break;
-    case CREATEONLY:
-      requestedSuffixes.push("create");
-      break;
-    case READCREATE:
-      requestedSuffixes.push("read", "create");
-      break;
-    case READWRITE:
-      requestedSuffixes.push("read", "create", "write");
-      break;
-    default:
-      return [];
-    }
-
-    let permArr = appendAccessToPermName(aPermName, requestedSuffixes);
-
-    // Add the same suffix to each of the additions.
-    if (tableEntry.additional) {
-      for each (let additional in tableEntry.additional) {
-        permArr = permArr.concat(appendAccessToPermName(additional, requestedSuffixes));
-      }
-    }
-
-    // Only add the suffixed version if the suffix exisits in the table.
-    for (let idx in permArr) {
-      let suffix = requestedSuffixes[idx % requestedSuffixes.length];
-      if (tableEntry.access.indexOf(suffix) != -1) {
-        expandedPermNames.push(permArr[idx]);
-      }
-    }
-  } else if (tableEntry.substitute) {
-    expandedPermNames = expandedPermNames.concat(tableEntry.substitute);
-  } else {
-    expandedPermNames.push(aPermName);
-    // Include each of the additions exactly as they appear in the table.
-    if (tableEntry.additional) {
-      expandedPermNames = expandedPermNames.concat(tableEntry.additional);
-    }
-  }
-
-  return expandedPermNames;
-};
-
 // An array carring all the possible (expanded) permission names.
 let AllPossiblePermissions = [];
 for (let permName in PermissionsTable) {
   let expandedPermNames = [];
   if (PermissionsTable[permName].access) {
     expandedPermNames = expandPermissions(permName, READWRITE);
   } else {
     expandedPermNames = expandPermissions(permName);
new file mode 100644
--- /dev/null
+++ b/dom/apps/src/PermissionsTable.jsm
@@ -0,0 +1,421 @@
+/* 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/. */
+
+"use strict";
+
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+
+this.EXPORTED_SYMBOLS = [
+  "PermissionsTable",
+  "PermissionsReverseTable",
+  "expandPermissions",
+  "appendAccessToPermName",
+  "isExplicitInPermissionsTable"
+];
+
+// Permission access flags
+const READONLY = "readonly";
+const CREATEONLY = "createonly";
+const READCREATE = "readcreate";
+const READWRITE = "readwrite";
+
+const UNKNOWN_ACTION = Ci.nsIPermissionManager.UNKNOWN_ACTION;
+const ALLOW_ACTION = Ci.nsIPermissionManager.ALLOW_ACTION;
+const DENY_ACTION = Ci.nsIPermissionManager.DENY_ACTION;
+const PROMPT_ACTION = Ci.nsIPermissionManager.PROMPT_ACTION;
+
+// Permissions Matrix: https://docs.google.com/spreadsheet/ccc?key=0Akyz_Bqjgf5pdENVekxYRjBTX0dCXzItMnRyUU1RQ0E#gid=0
+// Also, keep in sync with https://mxr.mozilla.org/mozilla-central/source/extensions/cookie/Permission.txt
+
+// Permissions that are implicit:
+// battery-status, network-information, vibration,
+// device-capabilities
+
+this.PermissionsTable =  { geolocation: {
+                             app: PROMPT_ACTION,
+                             privileged: PROMPT_ACTION,
+                             certified: ALLOW_ACTION
+                           },
+                           camera: {
+                             app: DENY_ACTION,
+                             privileged: DENY_ACTION,
+                             certified: ALLOW_ACTION
+                           },
+                           alarms: {
+                             app: ALLOW_ACTION,
+                             privileged: ALLOW_ACTION,
+                             certified: ALLOW_ACTION
+                           },
+                           "tcp-socket": {
+                             app: DENY_ACTION,
+                             privileged: ALLOW_ACTION,
+                             certified: ALLOW_ACTION
+                           },
+                           "network-events": {
+                             app: DENY_ACTION,
+                             privileged: DENY_ACTION,
+                             certified: ALLOW_ACTION
+                           },
+                           contacts: {
+                             app: DENY_ACTION,
+                             privileged: PROMPT_ACTION,
+                             certified: ALLOW_ACTION,
+                             access: ["read", "write", "create"]
+                           },
+                           "device-storage:apps": {
+                             app: DENY_ACTION,
+                             privileged: DENY_ACTION,
+                             certified: ALLOW_ACTION,
+                             access: ["read"]
+                           },
+                           "device-storage:pictures": {
+                             app: DENY_ACTION,
+                             privileged: PROMPT_ACTION,
+                             certified: ALLOW_ACTION,
+                             access: ["read", "write", "create"]
+                           },
+                           "device-storage:videos": {
+                             app: DENY_ACTION,
+                             privileged: PROMPT_ACTION,
+                             certified: ALLOW_ACTION,
+                             access: ["read", "write", "create"]
+                           },
+                           "device-storage:music": {
+                             app: DENY_ACTION,
+                             privileged: PROMPT_ACTION,
+                             certified: ALLOW_ACTION,
+                             access: ["read", "write", "create"]
+                           },
+                           "device-storage:sdcard": {
+                             app: DENY_ACTION,
+                             privileged: PROMPT_ACTION,
+                             certified: ALLOW_ACTION,
+                             access: ["read", "write", "create"]
+                           },
+                           sms: {
+                             app: DENY_ACTION,
+                             privileged: DENY_ACTION,
+                             certified: ALLOW_ACTION
+                           },
+                           telephony: {
+                             app: DENY_ACTION,
+                             privileged: DENY_ACTION,
+                             certified: ALLOW_ACTION
+                           },
+                           browser: {
+                             app: DENY_ACTION,
+                             privileged: ALLOW_ACTION,
+                             certified: ALLOW_ACTION
+                           },
+                           bluetooth: {
+                             app: DENY_ACTION,
+                             privileged: DENY_ACTION,
+                             certified: ALLOW_ACTION
+                           },
+                           mobileconnection: {
+                             app: DENY_ACTION,
+                             privileged: DENY_ACTION,
+                             certified: ALLOW_ACTION
+                           },
+                           power: {
+                             app: DENY_ACTION,
+                             privileged: DENY_ACTION,
+                             certified: ALLOW_ACTION
+                           },
+                           settings: {
+                             app: DENY_ACTION,
+                             privileged: DENY_ACTION,
+                             certified: ALLOW_ACTION,
+                             access: ["read", "write"],
+                             additional: ["indexedDB-chrome-settings"]
+                           },
+                           permissions: {
+                             app: DENY_ACTION,
+                             privileged: DENY_ACTION,
+                             certified: ALLOW_ACTION
+                           },
+                           fmradio: {
+                             app: ALLOW_ACTION,
+                             privileged: ALLOW_ACTION,
+                             certified: ALLOW_ACTION
+                           },
+                           attention: {
+                             app: DENY_ACTION,
+                             privileged: DENY_ACTION,
+                             certified: ALLOW_ACTION
+                           },
+                           "webapps-manage": {
+                             app: DENY_ACTION,
+                             privileged: DENY_ACTION,
+                             certified: ALLOW_ACTION
+                           },
+                           "backgroundservice": {
+                             app: DENY_ACTION,
+                             privileged: DENY_ACTION,
+                             certified: ALLOW_ACTION
+                           },
+                           "desktop-notification": {
+                             app: ALLOW_ACTION,
+                             privileged: ALLOW_ACTION,
+                             certified: ALLOW_ACTION
+                           },
+                           "networkstats-manage": {
+                             app: DENY_ACTION,
+                             privileged: DENY_ACTION,
+                             certified: ALLOW_ACTION
+                           },
+                           "wifi-manage": {
+                             app: DENY_ACTION,
+                             privileged: DENY_ACTION,
+                             certified: ALLOW_ACTION
+                           },
+                           "systemXHR": {
+                             app: DENY_ACTION,
+                             privileged: ALLOW_ACTION,
+                             certified: ALLOW_ACTION
+                           },
+                           "voicemail": {
+                             app: DENY_ACTION,
+                             privileged: DENY_ACTION,
+                             certified: ALLOW_ACTION
+                           },
+                           "deprecated-hwvideo": {
+                             app: DENY_ACTION,
+                             privileged: DENY_ACTION,
+                             certified: ALLOW_ACTION
+                           },
+                           "idle": {
+                             app: DENY_ACTION,
+                             privileged: DENY_ACTION,
+                             certified: ALLOW_ACTION
+                           },
+                           "time": {
+                             app: DENY_ACTION,
+                             privileged: DENY_ACTION,
+                             certified: ALLOW_ACTION
+                           },
+                           "embed-apps": {
+                             app: DENY_ACTION,
+                             privileged: DENY_ACTION,
+                             certified: ALLOW_ACTION
+                           },
+                           "storage": {
+                             app: DENY_ACTION,
+                             privileged: DENY_ACTION,
+                             certified: ALLOW_ACTION,
+                             substitute: [
+                               "indexedDB-unlimited",
+                               "offline-app",
+                               "pin-app"
+                             ]
+                           },
+                           "background-sensors": {
+                             app: DENY_ACTION,
+                             privileged: DENY_ACTION,
+                             certified: ALLOW_ACTION
+                           },
+                           cellbroadcast: {
+                             app: DENY_ACTION,
+                             privileged: DENY_ACTION,
+                             certified: ALLOW_ACTION
+                           },
+                           "audio-channel-normal": {
+                             app: ALLOW_ACTION,
+                             privileged: ALLOW_ACTION,
+                             certified: ALLOW_ACTION
+                           },
+                           "audio-channel-content": {
+                             app: ALLOW_ACTION,
+                             privileged: ALLOW_ACTION,
+                             certified: ALLOW_ACTION
+                           },
+                           "audio-channel-notification": {
+                             app: DENY_ACTION,
+                             privileged: ALLOW_ACTION,
+                             certified: ALLOW_ACTION
+                           },
+                           "audio-channel-alarm": {
+                             app: DENY_ACTION,
+                             privileged: ALLOW_ACTION,
+                             certified: ALLOW_ACTION
+                           },
+                           "audio-channel-telephony": {
+                             app: DENY_ACTION,
+                             privileged: DENY_ACTION,
+                             certified: ALLOW_ACTION
+                           },
+                           "audio-channel-ringer": {
+                             app: DENY_ACTION,
+                             privileged: DENY_ACTION,
+                             certified: ALLOW_ACTION
+                           },
+                           "audio-channel-publicnotification": {
+                             app: DENY_ACTION,
+                             privileged: DENY_ACTION,
+                             certified: ALLOW_ACTION
+                           },
+                           "open-remote-window": {
+                             app: DENY_ACTION,
+                             privileged: DENY_ACTION,
+                             certified: ALLOW_ACTION
+                           },
+                         };
+
+/**
+ * Append access modes to the permission name as suffixes.
+ *   e.g. permission name 'contacts' with ['read', 'write'] =
+ *   ['contacts-read', contacts-write']
+ * @param string aPermName
+ * @param array aAccess
+ * @returns array containing access-appended permission names.
+ **/
+this.appendAccessToPermName = function appendAccessToPermName(aPermName, aAccess) {
+  if (aAccess.length == 0) {
+    return [aPermName];
+  }
+  return aAccess.map(function(aMode) {
+    return aPermName + "-" + aMode;
+  });
+};
+
+/**
+ * Expand an access string into multiple permission names,
+ *   e.g: permission name 'contacts' with 'readwrite' =
+ *   ['contacts-read', 'contacts-create', 'contacts-write']
+ * @param string aPermName
+ * @param string aAccess (optional)
+ * @returns array containing expanded permission names.
+ **/
+this.expandPermissions = function expandPermissions(aPermName, aAccess) {
+  if (!PermissionsTable[aPermName]) {
+    let errorMsg = 
+      "PermissionsTable.jsm: expandPermissions: Unknown Permission: " + aPermName;
+    Cu.reportError(errorMsg);
+    dump(errorMsg);
+    return [];
+  }
+
+  const tableEntry = PermissionsTable[aPermName];
+
+  if (tableEntry.substitute && tableEntry.additional) {
+    let errorMsg = 
+      "PermissionsTable.jsm: expandPermissions: Can't handle both 'substitute' " +
+      "and 'additional' entries for permission: " + aPermName;
+    Cu.reportError(errorMsg);
+    dump(errorMsg);
+    return [];
+  }
+
+  if (!aAccess && tableEntry.access ||
+      aAccess && !tableEntry.access) {
+    let errorMsg = 
+      "PermissionsTable.jsm: expandPermissions: Invalid Manifest : " +
+      aPermName + " " + aAccess + "\n";
+    Cu.reportError(errorMsg);
+    dump(errorMsg);
+    throw new Error(errorMsg);
+  }
+
+  let expandedPermNames = [];
+
+  if (tableEntry.access && aAccess) {
+    let requestedSuffixes = [];
+    switch (aAccess) {
+    case READONLY:
+      requestedSuffixes.push("read");
+      break;
+    case CREATEONLY:
+      requestedSuffixes.push("create");
+      break;
+    case READCREATE:
+      requestedSuffixes.push("read", "create");
+      break;
+    case READWRITE:
+      requestedSuffixes.push("read", "create", "write");
+      break;
+    default:
+      return [];
+    }
+
+    let permArr = appendAccessToPermName(aPermName, requestedSuffixes);
+
+    // Add the same suffix to each of the additions.
+    if (tableEntry.additional) {
+      for each (let additional in tableEntry.additional) {
+        permArr = permArr.concat(appendAccessToPermName(additional, requestedSuffixes));
+      }
+    }
+
+    // Only add the suffixed version if the suffix exists in the table.
+    for (let idx in permArr) {
+      let suffix = requestedSuffixes[idx % requestedSuffixes.length];
+      if (tableEntry.access.indexOf(suffix) != -1) {
+        expandedPermNames.push(permArr[idx]);
+      }
+    }
+  } else if (tableEntry.substitute) {
+    expandedPermNames = expandedPermNames.concat(tableEntry.substitute);
+  } else {
+    expandedPermNames.push(aPermName);
+    // Include each of the additions exactly as they appear in the table.
+    if (tableEntry.additional) {
+      expandedPermNames = expandedPermNames.concat(tableEntry.additional);
+    }
+  }
+
+  return expandedPermNames;
+};
+
+this.PermissionsReverseTable = (function () {
+  // PermissionsTable as it is works well for direct searches, but not
+  // so well for reverse ones (that is, if I get something like
+  // device-storage:music-read or indexedDB-chrome-settings-read how
+  // do I know which permission it really is? Hence this table is
+  // born. The idea is that
+  // reverseTable[device-storage:music-read] should return
+  // device-storage:music
+  let reverseTable = {};
+
+  for (let permName in PermissionsTable) {
+    let permAliases;
+    if (PermissionsTable[permName].access) {
+      permAliases = expandPermissions(permName, "readwrite");
+    } else {
+      permAliases = expandPermissions(permName);
+    }
+    for (let i = 0; i < permAliases.length; i++) {
+      reverseTable[permAliases[i]] = permName;
+    }
+  }
+
+  return reverseTable;
+
+})();
+
+this.isExplicitInPermissionsTable = function(aPermName, aIntStatus) {
+
+  // Check to see if the 'webapp' is app/privileged/certified.
+  let appStatus;
+  switch (aIntStatus) {
+    case Ci.nsIPrincipal.APP_STATUS_CERTIFIED:
+      appStatus = "certified";
+      break;
+    case Ci.nsIPrincipal.APP_STATUS_PRIVILEGED:
+      appStatus = "privileged";
+      break;
+    default: // If it isn't certified or privileged, it's app
+      appStatus = "app";
+      break;
+  }
+
+  let realPerm = PermissionsReverseTable[aPermName];
+
+  if (realPerm) {
+    return (PermissionsTable[realPerm][appStatus] == 
+            Ci.nsIPermissionManager.PROMPT_ACTION);
+  } else {
+    return false;
+  }
+}
--- a/dom/interfaces/permission/nsIDOMPermissionSettings.idl
+++ b/dom/interfaces/permission/nsIDOMPermissionSettings.idl
@@ -1,15 +1,17 @@
 /* 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/. */
 
 #include "domstubs.idl"
 
 interface nsIDOMDOMRequest;
 
-[scriptable, uuid(18390770-02ab-11e2-a21f-0800200c9a66)]
+[scriptable, uuid(b3e3894e-b24e-4174-9c80-08115709615b)]
 interface nsIDOMPermissionSettings : nsISupports
 {
   DOMString get(in DOMString permission, in DOMString manifestURI, in DOMString origin, in bool browserFlag);
 
   void set(in DOMString permission, in DOMString value, in DOMString manifestURI, in DOMString origin, in bool browserFlag);
+
+  bool isExplicit(in DOMString permission, in DOMString manifestURI, in DOMString origin, in bool browserFlag);
 };
--- a/dom/messages/SystemMessagePermissionsChecker.jsm
+++ b/dom/messages/SystemMessagePermissionsChecker.jsm
@@ -6,16 +6,17 @@
 
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/AppsUtils.jsm");
 Cu.import("resource://gre/modules/PermissionsInstaller.jsm");
+Cu.import("resource://gre/modules/PermissionsTable.jsm");
 Cu.import("resource://gre/modules/PermissionSettings.jsm");
 
 this.EXPORTED_SYMBOLS = ["SystemMessagePermissionsChecker",
                          "SystemMessagePermissionsTable"];
 
 function debug(aStr) {
   // dump("SystemMessagePermissionsChecker.jsm: " + aStr + "\n");
 }
--- a/dom/permission/PermissionPromptHelper.jsm
+++ b/dom/permission/PermissionPromptHelper.jsm
@@ -30,16 +30,17 @@ const Cu = Components.utils;
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 
 this.EXPORTED_SYMBOLS = ["PermissionPromptHelper"];
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/PermissionsInstaller.jsm");
+Cu.import("resource://gre/modules/PermissionsTable.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
                                    "@mozilla.org/parentprocessmessagemanager;1",
                                    "nsIMessageListenerManager");
 
 XPCOMUtils.defineLazyServiceGetter(this, "permissionPromptService",
                                    "@mozilla.org/permission-prompt-service;1",
                                    "nsIPermissionPromptService");
--- a/dom/permission/PermissionSettings.js
+++ b/dom/permission/PermissionSettings.js
@@ -9,16 +9,17 @@ function debug(aMsg) {
 }
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/PermissionsTable.jsm");
 
 var cpm = Cc["@mozilla.org/childprocessmessagemanager;1"].getService(Ci.nsISyncMessageSender);
 
 // PermissionSettings
 
 const PERMISSIONSETTINGS_CONTRACTID = "@mozilla.org/permissionSettings;1";
 const PERMISSIONSETTINGS_CID        = Components.ID("{18390770-02ab-11e2-a21f-0800200c9a66}");
 const nsIDOMPermissionSettings      = Ci.nsIDOMPermissionSettings;
@@ -62,19 +63,41 @@ PermissionSettings.prototype = {
       case Ci.nsIPermissionManager.PROMPT_ACTION:
         return "prompt";
       default:
         dump("Unsupported PermissionSettings Action!\n");
         return "unknown";
     }
   },
 
-  set: function set(aPermName, aPermValue, aManifestURL, aOrigin, aBrowserFlag) {
-    debug("Set called with: " + aPermName + ", " + aManifestURL + ", " + aOrigin + ",  " + aPermValue + ", " + aBrowserFlag);
+  isExplicit: function isExplicit(aPermName, aManifestURL, aOrigin,
+                                  aBrowserFlag) {
+    debug("isExplicit: " + aPermName + ", " + aManifestURL + ", " + aOrigin);
+    let uri = Services.io.newURI(aOrigin, null, null);
+    let appID = appsService.getAppLocalIdByManifestURL(aManifestURL);
+    let principal = secMan.getAppCodebasePrincipal(uri, appID, aBrowserFlag);
+
+    return isExplicitInPermissionsTable(aPermName, principal.appStatus);
+  },
+
+  set: function set(aPermName, aPermValue, aManifestURL, aOrigin,
+                    aBrowserFlag) {
+    debug("Set called with: " + aPermName + ", " + aManifestURL + ", " +
+          aOrigin + ",  " + aPermValue + ", " + aBrowserFlag); 
     let action;
+    // Check for invalid calls so that we throw an exception rather than get
+    // killed by parent process
+    if (aPermValue === "unknown" ||
+        !this.isExplicit(aPermName, aManifestURL, aOrigin, aBrowserFlag)) {
+      let errorMsg = "PermissionSettings.js: '" + aPermName + "'" +
+                     " is an implicit permission for '" + aManifestURL+"'";
+      Cu.reportError(errorMsg);
+      throw new Components.Exception(errorMsg);
+    }
+
     cpm.sendSyncMessage("PermissionSettings:AddPermission", {
       type: aPermName,
       origin: aOrigin,
       manifestURL: aManifestURL,
       value: aPermValue,
       browserFlag: aBrowserFlag
     });
   },
--- a/dom/permission/PermissionSettings.jsm
+++ b/dom/permission/PermissionSettings.jsm
@@ -11,16 +11,17 @@ function debug(s) {
 const Cu = Components.utils;
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 
 this.EXPORTED_SYMBOLS = ["PermissionSettingsModule"];
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/PermissionsTable.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
                                    "@mozilla.org/parentprocessmessagemanager;1",
                                    "nsIMessageListenerManager");
 
 XPCOMUtils.defineLazyServiceGetter(this,
                                    "permissionManager",
                                    "@mozilla.org/permissionmanager;1",
@@ -38,17 +39,46 @@ XPCOMUtils.defineLazyServiceGetter(this,
 
 this.PermissionSettingsModule = {
   init: function init() {
     debug("Init");
     ppmm.addMessageListener("PermissionSettings:AddPermission", this);
     Services.obs.addObserver(this, "profile-before-change", false);
   },
 
+
+  _isChangeAllowed: function(aPrincipal, aPermName, aAction) {
+    // Bug 812289:
+    // Change is allowed from a child process when all of the following
+    // conditions stand true:
+    //   * the action isn't "unknown" (so the change isn't a delete)
+    //   * the permission already exists on the database
+    //   * the permission is marked as explicit on the permissions table
+    // Note that we *have* to check the first two conditions ere because
+    // permissionManager doesn't know if it's being called as a result of
+    // a parent process or child process request. We could check
+    // if the permission is actually explicit (and thus modifiable) or not
+    // on permissionManager also but we currently don't.
+    let perm =
+      permissionManager.testExactPermissionFromPrincipal(aPrincipal,aPermName);
+    let isExplicit = isExplicitInPermissionsTable(aPermName, aPrincipal.appStatus);
+    
+    return (aAction !== "unknown") &&
+           (perm !== Ci.nsIPermissionManager.UNKNOWN_ACTION) &&
+           isExplicit;
+  },
+
   addPermission: function addPermission(aData, aCallbacks) {
+
+    this._internalAddPermission(aData, true, aCallbacks);
+
+  },
+
+
+  _internalAddPermission: function _internalAddPermission(aData, aAllowAllChanges, aCallbacks) {
     let uri = Services.io.newURI(aData.origin, null, null);
     let appID = appsService.getAppLocalIdByManifestURL(aData.manifestURL);
     let principal = secMan.getAppCodebasePrincipal(uri, appID, aData.browserFlag);
 
     let action;
     switch (aData.value)
     {
       case "unknown":
@@ -62,18 +92,26 @@ this.PermissionSettingsModule = {
         break;
       case "prompt":
         action = Ci.nsIPermissionManager.PROMPT_ACTION;
         break;
       default:
         dump("Unsupported PermisionSettings Action: " + aData.value +"\n");
         action = Ci.nsIPermissionManager.UNKNOWN_ACTION;
     }
-    debug("add: " + aData.origin + " " + appID + " " + action);
-    permissionManager.addFromPrincipal(principal, aData.type, action);
+
+    if (aAllowAllChanges ||
+        this._isChangeAllowed(principal, aData.type, aData.value)) {
+      debug("add: " + aData.origin + " " + appID + " " + action);
+      permissionManager.addFromPrincipal(principal, aData.type, action);
+      return true;
+    } else {
+      debug("add Failure: " + aData.origin + " " + appID + " " + action);
+      return false; // This isn't currently used, see comment on setPermission
+    }
   },
 
   getPermission: function getPermission(aPermName, aManifestURL, aOrigin, aBrowserFlag) {
     debug("getPermission: " + aPermName + ", " + aManifestURL + ", " + aOrigin);
     let uri = Services.io.newURI(aOrigin, null, null);
     let appID = appsService.getAppLocalIdByManifestURL(aManifestURL);
     let principal = secMan.getAppCodebasePrincipal(uri, appID, aBrowserFlag);
     let result = permissionManager.testExactPermissionFromPrincipal(principal, aPermName);
@@ -103,20 +141,30 @@ this.PermissionSettingsModule = {
   receiveMessage: function receiveMessage(aMessage) {
     debug("PermissionSettings::receiveMessage " + aMessage.name);
     let mm = aMessage.target;
     let msg = aMessage.data;
 
     let result;
     switch (aMessage.name) {
       case "PermissionSettings:AddPermission":
-        if (!aMessage.target.assertPermission("permissions")) {
-          Cu.reportError("PermissionSettings message " + msg.name +
-                         " from a content process with no 'permissions' privileges.");
+        let success = false;
+        let errorMsg = 
+              " from a content process with no 'permissions' privileges.";
+        if (mm.assertPermission("permissions")) {
+          success = this._internalAddPermission(msg, false);
+          if (!success) { 
+            // Just kill the calling process
+            mm.assertPermission("permissions-modify-implicit");
+            errorMsg = " had an implicit permission change. Child process killed.";
+          }
+        }
+
+        if (!success) {
+          Cu.reportError("PermissionSettings message " + msg.name + errorMsg);
           return null;
         }
-        this.addPermission(msg);
         break;
     }
   }
 }
 
 PermissionSettingsModule.init();