michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: "use strict"; michael@0: michael@0: function debug(s) { michael@0: //dump("-*- PermissionSettings Module: " + s + "\n"); michael@0: } michael@0: michael@0: const Cu = Components.utils; michael@0: const Cc = Components.classes; michael@0: const Ci = Components.interfaces; michael@0: michael@0: this.EXPORTED_SYMBOLS = ["PermissionSettingsModule"]; michael@0: michael@0: Cu.import("resource://gre/modules/XPCOMUtils.jsm"); michael@0: Cu.import("resource://gre/modules/Services.jsm"); michael@0: Cu.import("resource://gre/modules/PermissionsTable.jsm"); michael@0: michael@0: XPCOMUtils.defineLazyServiceGetter(this, "ppmm", michael@0: "@mozilla.org/parentprocessmessagemanager;1", michael@0: "nsIMessageListenerManager"); michael@0: michael@0: XPCOMUtils.defineLazyServiceGetter(this, michael@0: "permissionManager", michael@0: "@mozilla.org/permissionmanager;1", michael@0: "nsIPermissionManager"); michael@0: michael@0: XPCOMUtils.defineLazyServiceGetter(this, michael@0: "secMan", michael@0: "@mozilla.org/scriptsecuritymanager;1", michael@0: "nsIScriptSecurityManager"); michael@0: michael@0: XPCOMUtils.defineLazyServiceGetter(this, michael@0: "appsService", michael@0: "@mozilla.org/AppsService;1", michael@0: "nsIAppsService"); michael@0: michael@0: this.PermissionSettingsModule = { michael@0: init: function init() { michael@0: debug("Init"); michael@0: ppmm.addMessageListener("PermissionSettings:AddPermission", this); michael@0: Services.obs.addObserver(this, "profile-before-change", false); michael@0: }, michael@0: michael@0: michael@0: _isChangeAllowed: function(aPrincipal, aPermName, aAction) { michael@0: // Bug 812289: michael@0: // Change is allowed from a child process when all of the following michael@0: // conditions stand true: michael@0: // * the action isn't "unknown" (so the change isn't a delete) if the app michael@0: // is installed michael@0: // * the permission already exists on the database michael@0: // * the permission is marked as explicit on the permissions table michael@0: // Note that we *have* to check the first two conditions here because michael@0: // permissionManager doesn't know if it's being called as a result of michael@0: // a parent process or child process request. We could check michael@0: // if the permission is actually explicit (and thus modifiable) or not michael@0: // on permissionManager also but we currently don't. michael@0: let perm = michael@0: permissionManager.testExactPermissionFromPrincipal(aPrincipal,aPermName); michael@0: let isExplicit = isExplicitInPermissionsTable(aPermName, aPrincipal.appStatus); michael@0: michael@0: return (aAction === "unknown" && michael@0: aPrincipal.appStatus === Ci.nsIPrincipal.APP_STATUS_NOT_INSTALLED) || michael@0: (aAction !== "unknown" && michael@0: (perm !== Ci.nsIPermissionManager.UNKNOWN_ACTION) && michael@0: isExplicit); michael@0: }, michael@0: michael@0: addPermission: function addPermission(aData, aCallbacks) { michael@0: michael@0: this._internalAddPermission(aData, true, aCallbacks); michael@0: michael@0: }, michael@0: michael@0: michael@0: _internalAddPermission: function _internalAddPermission(aData, aAllowAllChanges, aCallbacks) { michael@0: let uri = Services.io.newURI(aData.origin, null, null); michael@0: let appID = appsService.getAppLocalIdByManifestURL(aData.manifestURL); michael@0: let principal = secMan.getAppCodebasePrincipal(uri, appID, aData.browserFlag); michael@0: michael@0: let action; michael@0: switch (aData.value) michael@0: { michael@0: case "unknown": michael@0: action = Ci.nsIPermissionManager.UNKNOWN_ACTION; michael@0: break; michael@0: case "allow": michael@0: action = Ci.nsIPermissionManager.ALLOW_ACTION; michael@0: break; michael@0: case "deny": michael@0: action = Ci.nsIPermissionManager.DENY_ACTION; michael@0: break; michael@0: case "prompt": michael@0: action = Ci.nsIPermissionManager.PROMPT_ACTION; michael@0: break; michael@0: default: michael@0: dump("Unsupported PermisionSettings Action: " + aData.value +"\n"); michael@0: action = Ci.nsIPermissionManager.UNKNOWN_ACTION; michael@0: } michael@0: michael@0: if (aAllowAllChanges || michael@0: this._isChangeAllowed(principal, aData.type, aData.value)) { michael@0: debug("add: " + aData.origin + " " + appID + " " + action); michael@0: permissionManager.addFromPrincipal(principal, aData.type, action); michael@0: return true; michael@0: } else { michael@0: debug("add Failure: " + aData.origin + " " + appID + " " + action); michael@0: return false; // This isn't currently used, see comment on setPermission michael@0: } michael@0: }, michael@0: michael@0: getPermission: function getPermission(aPermName, aManifestURL, aOrigin, aBrowserFlag) { michael@0: debug("getPermission: " + aPermName + ", " + aManifestURL + ", " + aOrigin); michael@0: let uri = Services.io.newURI(aOrigin, null, null); michael@0: let appID = appsService.getAppLocalIdByManifestURL(aManifestURL); michael@0: let principal = secMan.getAppCodebasePrincipal(uri, appID, aBrowserFlag); michael@0: let result = permissionManager.testExactPermissionFromPrincipal(principal, aPermName); michael@0: michael@0: switch (result) michael@0: { michael@0: case Ci.nsIPermissionManager.UNKNOWN_ACTION: michael@0: return "unknown"; michael@0: case Ci.nsIPermissionManager.ALLOW_ACTION: michael@0: return "allow"; michael@0: case Ci.nsIPermissionManager.DENY_ACTION: michael@0: return "deny"; michael@0: case Ci.nsIPermissionManager.PROMPT_ACTION: michael@0: return "prompt"; michael@0: default: michael@0: dump("Unsupported PermissionSettings Action!\n"); michael@0: return "unknown"; michael@0: } michael@0: }, michael@0: michael@0: removePermission: function removePermission(aPermName, aManifestURL, aOrigin, aBrowserFlag) { michael@0: let data = { michael@0: type: aPermName, michael@0: origin: aOrigin, michael@0: manifestURL: aManifestURL, michael@0: value: "unknown", michael@0: browserFlag: aBrowserFlag michael@0: }; michael@0: this._internalAddPermission(data, true); michael@0: }, michael@0: michael@0: observe: function observe(aSubject, aTopic, aData) { michael@0: ppmm.removeMessageListener("PermissionSettings:AddPermission", this); michael@0: Services.obs.removeObserver(this, "profile-before-change"); michael@0: ppmm = null; michael@0: }, michael@0: michael@0: receiveMessage: function receiveMessage(aMessage) { michael@0: debug("PermissionSettings::receiveMessage " + aMessage.name); michael@0: let mm = aMessage.target; michael@0: let msg = aMessage.data; michael@0: michael@0: let result; michael@0: switch (aMessage.name) { michael@0: case "PermissionSettings:AddPermission": michael@0: let success = false; michael@0: let errorMsg = michael@0: " from a content process with no 'permissions' privileges."; michael@0: if (mm.assertPermission("permissions")) { michael@0: success = this._internalAddPermission(msg, false); michael@0: if (!success) { michael@0: // Just kill the calling process michael@0: mm.assertPermission("permissions-modify-implicit"); michael@0: errorMsg = " had an implicit permission change. Child process killed."; michael@0: } michael@0: } michael@0: michael@0: if (!success) { michael@0: Cu.reportError("PermissionSettings message " + msg.type + errorMsg); michael@0: return null; michael@0: } michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: michael@0: PermissionSettingsModule.init();