Bug 770731 - Expose JS API for modifying app permissions. r=sicking
authorGregor Wagner <anygregor@gmail.com>
Tue, 18 Sep 2012 14:52:39 -0700
changeset 107545 5f3887536eef97feed9a8724d86489aa69704bb2
parent 107544 7e4a6bba7ce557aee006d24974d4ba721b47c1cd
child 107546 6be84c0e0f0e7584e6ea19bde12a3561974bbcc4
push id82
push usershu@rfrn.org
push dateFri, 05 Oct 2012 13:20:22 +0000
reviewerssicking
bugs770731
milestone18.0a1
Bug 770731 - Expose JS API for modifying app permissions. r=sicking
b2g/app/b2g.js
b2g/chrome/content/shell.js
b2g/installer/package-manifest.in
browser/installer/package-manifest.in
dom/Makefile.in
dom/interfaces/permission/Makefile.in
dom/interfaces/permission/nsIDOMPermissionSettings.idl
dom/permission/Makefile.in
dom/permission/PermissionSettings.js
dom/permission/PermissionSettings.jsm
dom/permission/PermissionSettings.manifest
dom/permission/tests/Makefile.in
dom/permission/tests/test_permission_basics.html
modules/libpref/src/init/all.js
netwerk/base/public/nsIPermissionManager.idl
testing/mochitest/android.json
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -382,16 +382,17 @@ pref("dom.sms.enabled", true);
 // Temporary permission hack for WebContacts
 pref("dom.mozContacts.enabled", true);
 
 // WebAlarms
 pref("dom.mozAlarms.enabled", true);
 
 // WebSettings
 pref("dom.mozSettings.enabled", true);
+pref("dom.mozPermissionSettings.enabled", true);
 
 // controls if we want camera support
 pref("device.camera.enabled", true);
 pref("media.realtime_decoder.enabled", true);
 
 // TCPSocket
 pref("dom.mozTCPSocket.enabled", true);
 
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -12,16 +12,17 @@ const Cr = Components.results;
 Cu.import('resource://gre/modules/XPCOMUtils.jsm');
 Cu.import('resource://gre/modules/Services.jsm');
 Cu.import('resource://gre/modules/ContactService.jsm');
 Cu.import('resource://gre/modules/SettingsChangeNotifier.jsm');
 Cu.import('resource://gre/modules/Webapps.jsm');
 Cu.import('resource://gre/modules/AlarmService.jsm');
 Cu.import('resource://gre/modules/ActivitiesService.jsm');
 Cu.import('resource://gre/modules/PermissionPromptHelper.jsm');
+Cu.import('resource://gre/modules/PermissionSettings.jsm');
 Cu.import('resource://gre/modules/ObjectWrapper.jsm');
 Cu.import('resource://gre/modules/accessibility/AccessFu.jsm');
 Cu.import('resource://gre/modules/Payment.jsm');
 
 XPCOMUtils.defineLazyServiceGetter(Services, 'env',
                                    '@mozilla.org/process/environment;1',
                                    'nsIEnvironment');
 
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -187,16 +187,17 @@
 #ifdef MOZ_B2G_RIL
 @BINPATH@/components/dom_mms.xpt
 #endif
 @BINPATH@/components/dom_browserelement.xpt
 @BINPATH@/components/dom_messages.xpt
 @BINPATH@/components/dom_power.xpt
 @BINPATH@/components/dom_range.xpt
 @BINPATH@/components/dom_settings.xpt
+@BINPATH@/components/dom_permissionsettings.xpt
 @BINPATH@/components/dom_sidebar.xpt
 @BINPATH@/components/dom_sms.xpt
 @BINPATH@/components/dom_storage.xpt
 @BINPATH@/components/dom_stylesheets.xpt
 @BINPATH@/components/dom_threads.xpt
 @BINPATH@/components/dom_traversal.xpt
 @BINPATH@/components/dom_views.xpt
 @BINPATH@/components/dom_xbl.xpt
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -194,16 +194,17 @@
 @BINPATH@/components/dom_json.xpt
 #ifdef MOZ_B2G_RIL
 @BINPATH@/components/dom_mms.xpt
 #endif
 @BINPATH@/components/dom_browserelement.xpt
 @BINPATH@/components/dom_power.xpt
 @BINPATH@/components/dom_range.xpt
 @BINPATH@/components/dom_settings.xpt
+@BINPATH@/components/dom_permissionsettings.xpt
 @BINPATH@/components/dom_sidebar.xpt
 @BINPATH@/components/dom_sms.xpt
 @BINPATH@/components/dom_storage.xpt
 @BINPATH@/components/dom_stylesheets.xpt
 @BINPATH@/components/dom_traversal.xpt
 @BINPATH@/components/dom_xbl.xpt
 @BINPATH@/components/dom_xpath.xpt
 @BINPATH@/components/dom_xul.xpt
--- a/dom/Makefile.in
+++ b/dom/Makefile.in
@@ -30,16 +30,17 @@ PARALLEL_DIRS = \
   interfaces/xpath \
   interfaces/load-save \
   interfaces/xul \
   interfaces/storage \
   interfaces/json \
   interfaces/offline \
   interfaces/geolocation \
   interfaces/notification \
+  interfaces/permission \
   interfaces/svg \
   interfaces/smil \
   interfaces/apps \
   $(NULL)
 
 PARALLEL_DIRS += \
   apps \
   base \
new file mode 100644
--- /dev/null
+++ b/dom/interfaces/permission/Makefile.in
@@ -0,0 +1,20 @@
+# 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/.
+
+DEPTH          = ../../..
+topsrcdir      = @top_srcdir@
+srcdir         = @srcdir@
+VPATH          = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+MODULE         = dom
+XPIDL_MODULE   = dom_permissionsettings
+GRE_MODULE     = 1
+
+XPIDLSRCS =                             \
+            nsIDOMPermissionSettings.idl    \
+            $(NULL)
+
+include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/dom/interfaces/permission/nsIDOMPermissionSettings.idl
@@ -0,0 +1,15 @@
+/* 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(36e73ef0-c6f4-11e1-9b21-0800200c9a66)]
+interface nsIDOMPermissionSettings : nsISupports
+{
+  DOMString get(in DOMString permission, in DOMString access, in DOMString manifestURI, in DOMString origin, in bool browserFlag);
+
+  void set(in DOMString permission, in DOMString access, in DOMString manifestURI, in DOMString origin, in DOMString value, in bool browserFlag);
+};
--- a/dom/permission/Makefile.in
+++ b/dom/permission/Makefile.in
@@ -10,17 +10,25 @@ VPATH = @srcdir@
 include $(DEPTH)/config/autoconf.mk
 
 MODULE         = dom
 LIBRARY_NAME   = jsdompermission_s
 
 XPIDL_MODULE   = dom_permission
 GRE_MODULE     = 1
 
+EXTRA_COMPONENTS =        \
+  PermissionSettings.js       \
+  PermissionSettings.manifest \
+  $(NULL)
+
 EXTRA_JS_MODULES =   \
   PermissionPromptHelper.jsm \
+  PermissionSettings.jsm \
   $(NULL)
 
+TEST_DIRS += tests
+
 include $(topsrcdir)/config/config.mk
 include $(topsrcdir)/ipc/chromium/chromium-config.mk
 include $(topsrcdir)/config/rules.mk
 
 DEFINES += -D_IMPL_NS_LAYOUT
new file mode 100644
--- /dev/null
+++ b/dom/permission/PermissionSettings.js
@@ -0,0 +1,97 @@
+/* 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";
+
+let DEBUG = 0;
+if (DEBUG)
+  debug = function (s) { dump("-*- PermissionSettings: " + s + "\n"); }
+else
+  debug = function (s) {}
+
+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/PermissionSettings.jsm");
+
+var cpm = Components.classes["@mozilla.org/childprocessmessagemanager;1"].getService(Components.interfaces.nsISyncMessageSender);
+
+// PermissionSettings
+
+const PERMISSIONSETTINGS_CONTRACTID = "@mozilla.org/permissionSettings;1";
+const PERMISSIONSETTINGS_CID        = Components.ID("{36e73ef0-c6f4-11e1-9b21-0800200c9a66}");
+const nsIDOMPermissionSettings      = Components.interfaces.nsIDOMPermissionSettings;
+
+function PermissionSettings()
+{
+  debug("Constructor");
+}
+
+var permissionManager = Components.classes["@mozilla.org/permissionmanager;1"].getService(Ci.nsIPermissionManager);
+var secMan = Components.classes["@mozilla.org/scriptsecuritymanager;1"].getService(Components.interfaces.nsIScriptSecurityManager);
+var appsService = Components.classes["@mozilla.org/AppsService;1"].getService(Components.interfaces.nsIAppsService);
+
+PermissionSettings.prototype = {
+  get: function get(aPermission, aAccess, aManifestURL, aOrigin, aBrowserFlag) {
+    debug("Get called with: " + aPermission + ", " + aAccess + ", " + aManifestURL + ", " + aOrigin + ", " + aBrowserFlag);
+    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, aPermission);
+
+    switch (result)
+    {
+      case Ci.nsIPermissionManager.UNKNOWN_ACTION:
+        return "unknown"
+      case Ci.nsIPermissionManager.ALLOW_ACTION:
+        return "allow";
+      case Ci.nsIPermissionManager.DENY_ACTION:
+        return "deny";
+      case Ci.nsIPermissionManager.PROMPT_ACTION:
+        return "prompt";
+      default:
+        dump("Unsupported PermissionSettings Action!\n");
+        return "unknown";
+    }
+  },
+
+  set: function set(aPermission, aAccess, aManifestURL, aOrigin, aValue, aBrowserFlag) {
+    debug("Set called with: " + aPermission + ", " + aAccess + ", " + aManifestURL + ", " + aOrigin + ",  " + aValue + ", " + aBrowserFlag);
+    let action;
+    cpm.sendSyncMessage("PermissionSettings:AddPermission", {
+      type: aPermission,
+      access: aAccess,
+      origin: aOrigin,
+      manifestURL: aManifestURL,
+      value: aValue,
+      browserFlag: aBrowserFlag
+    });
+  },
+
+  init: function(aWindow) {
+    debug("init");
+    // Set navigator.mozPermissionSettings to null.
+    if (!Services.prefs.getBoolPref("dom.mozPermissionSettings.enabled")
+        || Services.perms.testExactPermissionFromPrincipal(aWindow.document.nodePrincipal, "permissions")) {
+      debug("Permission to get/set permissions not granted!");
+      return null;
+    }
+
+    debug("Permission to get/set permissions granted!");
+  },
+
+  classID : PERMISSIONSETTINGS_CID,
+  QueryInterface : XPCOMUtils.generateQI([nsIDOMPermissionSettings, Ci.nsIDOMGlobalPropertyInitializer]),
+
+  classInfo : XPCOMUtils.generateCI({classID: PERMISSIONSETTINGS_CID,
+                                     contractID: PERMISSIONSETTINGS_CONTRACTID,
+                                     classDescription: "PermissionSettings",
+                                     interfaces: [nsIDOMPermissionSettings],
+                                     flags: Ci.nsIClassInfo.DOM_OBJECT})
+}
+
+const NSGetFactory = XPCOMUtils.generateNSGetFactory([PermissionSettings])
new file mode 100644
--- /dev/null
+++ b/dom/permission/PermissionSettings.jsm
@@ -0,0 +1,83 @@
+/* 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";
+
+let DEBUG = 0;
+if (DEBUG)
+  debug = function (s) { dump("-*- PermissionSettings Module: " + s + "\n"); }
+else
+  debug = function (s) {}
+
+const Cu = Components.utils; 
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+
+let EXPORTED_SYMBOLS = ["PermissionSettingsModule"];
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+
+XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
+                                   "@mozilla.org/parentprocessmessagemanager;1",
+                                   "nsIMessageListenerManager");
+
+var permissionManager = Cc["@mozilla.org/permissionmanager;1"].getService(Ci.nsIPermissionManager);
+var secMan = Cc["@mozilla.org/scriptsecuritymanager;1"].getService(Ci.nsIScriptSecurityManager);
+var appsService = Cc["@mozilla.org/AppsService;1"].getService(Ci.nsIAppsService);
+
+let PermissionSettingsModule = {
+  init: function() {
+    debug("Init");
+    ppmm.addMessageListener("PermissionSettings:AddPermission", this);
+    Services.obs.addObserver(this, "profile-before-change", false);
+  },
+
+  addPermission: function(aData, 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 "unknonwn":
+        action = Ci.nsIPermissionManager.UNKNOWN_ACTION;
+        break;
+      case "allow":
+        action = Ci.nsIPermissionManager.ALLOW_ACTION;
+        break;
+      case "deny":
+        action = Ci.nsIPermissionManager.DENY_ACTION;
+        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);
+  },
+
+  observe: function(aSubject, aTopic, aData) {
+    ppmm.removeMessageListener("PermissionSettings:AddPermission", this);
+    Services.obs.removeObserver(this, "profile-before-change");
+    ppmm = null;
+  },
+
+  receiveMessage: function(aMessage) {
+    debug("PermissionSettings::receiveMessage " + aMessage.name);
+    let mm = aMessage.target;
+    let msg = aMessage.data;
+
+    let result;
+    if (aMessage.name == "PermissionSettings:AddPermission") {
+      this.addPermission(msg);
+    }
+  }
+}
+
+PermissionSettingsModule.init();
new file mode 100644
--- /dev/null
+++ b/dom/permission/PermissionSettings.manifest
@@ -0,0 +1,3 @@
+component {36e73ef0-c6f4-11e1-9b21-0800200c9a66} PermissionSettings.js
+contract @mozilla.org/permissionSettings;1 {36e73ef0-c6f4-11e1-9b21-0800200c9a66}
+category JavaScript-navigator-property mozPermissionSettings @mozilla.org/permissionSettings;1
new file mode 100644
--- /dev/null
+++ b/dom/permission/tests/Makefile.in
@@ -0,0 +1,27 @@
+# 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/.
+
+DEPTH            = ../../..
+topsrcdir        = @top_srcdir@
+srcdir           = @srcdir@
+VPATH            = @srcdir@
+
+relativesrcdir   = dom/permission/tests
+
+include $(DEPTH)/config/autoconf.mk
+
+DIRS = \
+  $(NULL)
+
+include $(topsrcdir)/config/rules.mk
+
+_TEST_FILES = \
+  test_permission_basics.html \
+  $(NULL)
+
+_CHROME_TEST_FILES = \
+  $(NULL)
+
+libs:: $(_TEST_FILES)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/dom/permission/tests/test_permission_basics.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id={770731}
+-->
+<head>
+  <title>Test for Bug {770731} Permissions</title>
+  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id={770731}">Mozilla Bug {674720}</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+"use strict";
+
+var comp = SpecialPowers.wrap(Components);
+SpecialPowers.pushPrefEnv({ "set": [["dom.mozPermissionSettings.enabled", true]] }, 
+                          function() {
+                            SpecialPowers.addPermission("permissions", true, document);
+                          });
+
+comp.utils.import("resource://gre/modules/PermissionSettings.jsm");
+var mozPermissions = window.navigator.mozPermissionSettings;
+
+function permissionTest() {
+  if (gPermissionssEnabled) {
+    mozPermissions.set("a", "read", "http://examplemanifestURI.com", "http://origin.com", "unknown", true);
+    var result = mozPermissions.get("a","read", "http://examplemanifestURI.com", "http://origin.com", true);
+    is(result, "unknown", "same result");
+    mozPermissions.set("a", "read", "http://examplemanifestURI.com", "http://origin.com", "allow", true);
+    result = mozPermissions.get("a","read", "http://examplemanifestURI.com", "http://origin.com", true);
+    is(result, "allow", "same result");
+    mozPermissions.set("a", "read", "http://examplemanifestURI.com", "http://origin.com", "deny", true);
+    result = mozPermissions.get("a","read", "http://examplemanifestURI.com", "http://origin.com", true);
+    is(result, "deny", "same result");
+    mozPermissions.set("a", "read", "http://examplemanifestURI.com", "http://origin.com", "prompt", true);
+    result = mozPermissions.get("a","read", "http://examplemanifestURI.com", "http://origin.com", true);
+    is(result, "prompt", "same result");
+    SimpleTest.finish();
+  } else {
+    is(mozPermissions, null, "mozPermissionSettings is null when not enabled.");
+    SimpleTest.finish();
+  }
+}
+
+var gPermissionssEnabled = SpecialPowers.getBoolPref("dom.mozPermissionSettings.enabled");
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(permissionTest);
+
+ok(true, "test passed");
+</script>
+</pre>
+</body>
+</html>
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -3671,16 +3671,17 @@ pref("dom.sms.enabled", false);
 // WebContacts
 pref("dom.mozContacts.enabled", false);
 
 // WebAlarms
 pref("dom.mozAlarms.enabled", false);
 
 // WebSettings
 pref("dom.mozSettings.enabled", false);
+pref("dom.mozPermissionSettings.enabled", false);
 
 // enable JS dump() function.
 pref("browser.dom.window.dump.enabled", false);
 
 // SPS Profiler
 pref("profiler.enabled", false);
 pref("profiler.interval", 10);
 pref("profiler.entries", 100000);
--- a/netwerk/base/public/nsIPermissionManager.idl
+++ b/netwerk/base/public/nsIPermissionManager.idl
@@ -43,16 +43,17 @@ interface nsIPermissionManager : nsISupp
    * the permission param of the add method
    * NOTE: UNKNOWN_ACTION (0) is reserved to represent the
    * default permission when no entry is found for a host, and
    * should not be used by consumers to indicate otherwise.
    */
   const uint32_t UNKNOWN_ACTION = 0;
   const uint32_t ALLOW_ACTION = 1;
   const uint32_t DENY_ACTION = 2;
+  const uint32_t PROMPT_ACTION = 3;
 
   /**
    * Predefined expiration types for permissions.  Permissions can be permanent
    * (never expire), expire at the end of the session, or expire at a specified
    * time.
    */
   const uint32_t EXPIRE_NEVER = 0;
   const uint32_t EXPIRE_SESSION = 1;
--- a/testing/mochitest/android.json
+++ b/testing/mochitest/android.json
@@ -151,16 +151,17 @@
  "dom/indexedDB/test/test_app_isolation_inproc.html": "TIMED_OUT",
  "dom/indexedDB/test/test_app_isolation_oop.html": "TIMED_OUT",
  "dom/network/tests/test_network_basics.html": "",
  "dom/settings/tests/test_settings_events.html": "",
  "dom/settings/tests/test_settings_basics.html": "",
  "dom/contacts/tests/test_contacts_blobs.html": "",
  "dom/contacts/tests/test_contacts_basics.html": "",
  "dom/contacts/tests/test_contacts_events.html": "",
+ "dom/permission/tests/test_permission_basics.html": "",
  "dom/sms/tests/test_sms_basics.html": "",
  "dom/tests/mochitest/ajax/jquery/test_jQuery.html": "bug 775227",
  "dom/tests/mochitest/ajax/offline/test_simpleManifest.html": "TIMED_OUT",
  "dom/tests/mochitest/ajax/offline/test_updatingManifest.html": "TIMED_OUT",
  "dom/tests/mochitest/ajax/offline/test_xhtmlManifest.xhtml": "TIMED_OUT",
  "dom/tests/mochitest/ajax/prototype/test_Prototype.html": "",
  "dom/tests/mochitest/ajax/scriptaculous/test_Scriptaculous.html": "",
  "dom/tests/mochitest/browser-frame/test_browserFrame1.html": "TIMED_OUT",