diff options
Diffstat (limited to 'b2g/components/ContentPermissionPrompt.js')
-rw-r--r-- | b2g/components/ContentPermissionPrompt.js | 461 |
1 files changed, 0 insertions, 461 deletions
diff --git a/b2g/components/ContentPermissionPrompt.js b/b2g/components/ContentPermissionPrompt.js deleted file mode 100644 index e11b1b458..000000000 --- a/b2g/components/ContentPermissionPrompt.js +++ /dev/null @@ -1,461 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict" - -function debug(str) { - //dump("-*- ContentPermissionPrompt: " + str + "\n"); -} - -const Ci = Components.interfaces; -const Cr = Components.results; -const Cu = Components.utils; -const Cc = Components.classes; - -const PROMPT_FOR_UNKNOWN = ["audio-capture", - "desktop-notification", - "geolocation", - "video-capture"]; -// Due to privary issue, permission requests like GetUserMedia should prompt -// every time instead of providing session persistence. -const PERMISSION_NO_SESSION = ["audio-capture", "video-capture"]; -const ALLOW_MULTIPLE_REQUESTS = ["audio-capture", "video-capture"]; - -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"); - -var permissionManager = Cc["@mozilla.org/permissionmanager;1"].getService(Ci.nsIPermissionManager); -var secMan = Cc["@mozilla.org/scriptsecuritymanager;1"].getService(Ci.nsIScriptSecurityManager); - -var permissionSpecificChecker = {}; - -XPCOMUtils.defineLazyModuleGetter(this, "SystemAppProxy", - "resource://gre/modules/SystemAppProxy.jsm"); - -/** - * Determine if a permission should be prompt to user or not. - * - * @param aPerm requested permission - * @param aAction the action according to principal - * @return true if prompt is required - */ -function shouldPrompt(aPerm, aAction) { - return ((aAction == Ci.nsIPermissionManager.PROMPT_ACTION) || - (aAction == Ci.nsIPermissionManager.UNKNOWN_ACTION && - PROMPT_FOR_UNKNOWN.indexOf(aPerm) >= 0)); -} - -/** - * Create the default choices for the requested permissions - * - * @param aTypesInfo requested permissions - * @return the default choices for permissions with options, return - * undefined if no option in all requested permissions. - */ -function buildDefaultChoices(aTypesInfo) { - let choices; - for (let type of aTypesInfo) { - if (type.options.length > 0) { - if (!choices) { - choices = {}; - } - choices[type.access] = type.options[0]; - } - } - return choices; -} - -/** - * aTypesInfo is an array of {permission, access, action, deny} which keeps - * the information of each permission. This arrary is initialized in - * ContentPermissionPrompt.prompt and used among functions. - * - * aTypesInfo[].permission : permission name - * aTypesInfo[].access : permission name + request.access - * aTypesInfo[].action : the default action of this permission - * aTypesInfo[].deny : true if security manager denied this app's origin - * principal. - * Note: - * aTypesInfo[].permission will be sent to prompt only when - * aTypesInfo[].action is PROMPT_ACTION and aTypesInfo[].deny is false. - */ -function rememberPermission(aTypesInfo, aPrincipal, aSession) -{ - function convertPermToAllow(aPerm, aPrincipal) - { - let type = - permissionManager.testExactPermissionFromPrincipal(aPrincipal, aPerm); - if (shouldPrompt(aPerm, type)) { - debug("add " + aPerm + " to permission manager with ALLOW_ACTION"); - if (!aSession) { - permissionManager.addFromPrincipal(aPrincipal, - aPerm, - Ci.nsIPermissionManager.ALLOW_ACTION); - } else if (PERMISSION_NO_SESSION.indexOf(aPerm) < 0) { - permissionManager.addFromPrincipal(aPrincipal, - aPerm, - Ci.nsIPermissionManager.ALLOW_ACTION, - Ci.nsIPermissionManager.EXPIRE_SESSION, 0); - } - } - } - - for (let i in aTypesInfo) { - // Expand the permission to see if we have multiple access properties - // to convert - let perm = aTypesInfo[i].permission; - let access = PermissionsTable[perm].access; - if (access) { - for (let idx in access) { - convertPermToAllow(perm + "-" + access[idx], aPrincipal); - } - } else { - convertPermToAllow(perm, aPrincipal); - } - } -} - -function ContentPermissionPrompt() {} - -ContentPermissionPrompt.prototype = { - - handleExistingPermission: function handleExistingPermission(request, - typesInfo) { - typesInfo.forEach(function(type) { - type.action = - Services.perms.testExactPermissionFromPrincipal(request.principal, - type.access); - if (shouldPrompt(type.access, type.action)) { - type.action = Ci.nsIPermissionManager.PROMPT_ACTION; - } - }); - - // If all permissions are allowed already and no more than one option, - // call allow() without prompting. - let checkAllowPermission = function(type) { - if (type.action == Ci.nsIPermissionManager.ALLOW_ACTION && - type.options.length <= 1) { - return true; - } - return false; - } - if (typesInfo.every(checkAllowPermission)) { - debug("all permission requests are allowed"); - request.allow(buildDefaultChoices(typesInfo)); - return true; - } - - // If all permissions are DENY_ACTION or UNKNOWN_ACTION, call cancel() - // without prompting. - let checkDenyPermission = function(type) { - if (type.action == Ci.nsIPermissionManager.DENY_ACTION || - type.action == Ci.nsIPermissionManager.UNKNOWN_ACTION) { - return true; - } - return false; - } - if (typesInfo.every(checkDenyPermission)) { - debug("all permission requests are denied"); - request.cancel(); - return true; - } - return false; - }, - - // multiple requests should be audio and video - checkMultipleRequest: function checkMultipleRequest(typesInfo) { - if (typesInfo.length == 1) { - return true; - } else if (typesInfo.length > 1) { - let checkIfAllowMultiRequest = function(type) { - return (ALLOW_MULTIPLE_REQUESTS.indexOf(type.access) !== -1); - } - if (typesInfo.every(checkIfAllowMultiRequest)) { - debug("legal multiple requests"); - return true; - } - } - - return false; - }, - - handledByApp: function handledByApp(request, typesInfo) { - if (request.principal.appId == Ci.nsIScriptSecurityManager.NO_APP_ID || - request.principal.appId == Ci.nsIScriptSecurityManager.UNKNOWN_APP_ID) { - // This should not really happen - request.cancel(); - return true; - } - - let appsService = Cc["@mozilla.org/AppsService;1"] - .getService(Ci.nsIAppsService); - let app = appsService.getAppByLocalId(request.principal.appId); - - // Check each permission if it's denied by permission manager with app's - // URL. - let notDenyAppPrincipal = function(type) { - let url = Services.io.newURI(app.origin, null, null); - let principal = - secMan.createCodebasePrincipal(url, - {appId: request.principal.appId}); - let result = Services.perms.testExactPermissionFromPrincipal(principal, - type.access); - - if (result == Ci.nsIPermissionManager.ALLOW_ACTION || - result == Ci.nsIPermissionManager.PROMPT_ACTION) { - type.deny = false; - } - return !type.deny; - } - // Cancel the entire request if one of the requested permissions is denied - if (!typesInfo.every(notDenyAppPrincipal)) { - request.cancel(); - return true; - } - - return false; - }, - - handledByPermissionType: function handledByPermissionType(request, typesInfo) { - for (let i in typesInfo) { - if (permissionSpecificChecker.hasOwnProperty(typesInfo[i].permission) && - permissionSpecificChecker[typesInfo[i].permission](request)) { - return true; - } - } - - return false; - }, - - prompt: function(request) { - // Initialize the typesInfo and set the default value. - let typesInfo = []; - let perms = request.types.QueryInterface(Ci.nsIArray); - for (let idx = 0; idx < perms.length; idx++) { - let perm = perms.queryElementAt(idx, Ci.nsIContentPermissionType); - let tmp = { - permission: perm.type, - access: (perm.access && perm.access !== "unused") ? - perm.type + "-" + perm.access : perm.type, - options: [], - deny: true, - action: Ci.nsIPermissionManager.UNKNOWN_ACTION - }; - - // Append available options, if any. - let options = perm.options.QueryInterface(Ci.nsIArray); - for (let i = 0; i < options.length; i++) { - let option = options.queryElementAt(i, Ci.nsISupportsString).data; - tmp.options.push(option); - } - typesInfo.push(tmp); - } - - if (secMan.isSystemPrincipal(request.principal)) { - request.allow(buildDefaultChoices(typesInfo)); - return; - } - - - if (typesInfo.length == 0) { - request.cancel(); - return; - } - - if(!this.checkMultipleRequest(typesInfo)) { - request.cancel(); - return; - } - - if (this.handledByApp(request, typesInfo) || - this.handledByPermissionType(request, typesInfo)) { - return; - } - - // returns true if the request was handled - if (this.handleExistingPermission(request, typesInfo)) { - return; - } - - // prompt PROMPT_ACTION request or request with options. - typesInfo = typesInfo.filter(function(type) { - return !type.deny && (type.action == Ci.nsIPermissionManager.PROMPT_ACTION || type.options.length > 0) ; - }); - - if (!request.element) { - this.delegatePrompt(request, typesInfo); - return; - } - - var cancelRequest = function() { - request.requester.onVisibilityChange = null; - request.cancel(); - } - - var self = this; - - // If the request was initiated from a hidden iframe - // we don't forward it to content and cancel it right away - request.requester.getVisibility( { - notifyVisibility: function(isVisible) { - if (!isVisible) { - cancelRequest(); - return; - } - - // Monitor the frame visibility and cancel the request if the frame goes - // away but the request is still here. - request.requester.onVisibilityChange = { - notifyVisibility: function(isVisible) { - if (isVisible) - return; - - self.cancelPrompt(request, typesInfo); - cancelRequest(); - } - } - - self.delegatePrompt(request, typesInfo, function onCallback() { - request.requester.onVisibilityChange = null; - }); - } - }); - - }, - - cancelPrompt: function(request, typesInfo) { - this.sendToBrowserWindow("cancel-permission-prompt", request, - typesInfo); - }, - - delegatePrompt: function(request, typesInfo, callback) { - this.sendToBrowserWindow("permission-prompt", request, typesInfo, - function(type, remember, choices) { - if (type == "permission-allow") { - rememberPermission(typesInfo, request.principal, !remember); - if (callback) { - callback(); - } - request.allow(choices); - return; - } - - let addDenyPermission = function(type) { - debug("add " + type.permission + - " to permission manager with DENY_ACTION"); - if (remember) { - Services.perms.addFromPrincipal(request.principal, type.access, - Ci.nsIPermissionManager.DENY_ACTION); - } else if (PERMISSION_NO_SESSION.indexOf(type.access) < 0) { - Services.perms.addFromPrincipal(request.principal, type.access, - Ci.nsIPermissionManager.DENY_ACTION, - Ci.nsIPermissionManager.EXPIRE_SESSION, - 0); - } - } - try { - // This will trow if we are canceling because the remote process died. - // Just eat the exception and call the callback that will cleanup the - // visibility event listener. - typesInfo.forEach(addDenyPermission); - } catch(e) { } - - if (callback) { - callback(); - } - - try { - request.cancel(); - } catch(e) { } - }); - }, - - sendToBrowserWindow: function(type, request, typesInfo, callback) { - let requestId = Cc["@mozilla.org/uuid-generator;1"] - .getService(Ci.nsIUUIDGenerator).generateUUID().toString(); - if (callback) { - SystemAppProxy.addEventListener("mozContentEvent", function contentEvent(evt) { - let detail = evt.detail; - if (detail.id != requestId) - return; - SystemAppProxy.removeEventListener("mozContentEvent", contentEvent); - - callback(detail.type, detail.remember, detail.choices); - }) - } - - let principal = request.principal; - let isApp = principal.appStatus != Ci.nsIPrincipal.APP_STATUS_NOT_INSTALLED; - let remember = (principal.appStatus == Ci.nsIPrincipal.APP_STATUS_PRIVILEGED || - principal.appStatus == Ci.nsIPrincipal.APP_STATUS_CERTIFIED) - ? true - : request.remember; - let isGranted = typesInfo.every(function(type) { - return type.action == Ci.nsIPermissionManager.ALLOW_ACTION; - }); - let permissions = {}; - for (let i in typesInfo) { - debug("prompt " + typesInfo[i].permission); - permissions[typesInfo[i].permission] = typesInfo[i].options; - } - - let details = { - type: type, - permissions: permissions, - id: requestId, - // This system app uses the origin from permission events to - // compare against the mozApp.origin of app windows, so we - // are not concerned with origin suffixes here (appId, etc). - origin: principal.originNoSuffix, - isApp: isApp, - remember: remember, - isGranted: isGranted, - }; - - if (isApp) { - details.manifestURL = DOMApplicationRegistry.getManifestURLByLocalId(principal.appId); - } - - // request.element is defined for OOP content, while request.window - // is defined for In-Process content. - // In both cases the message needs to be dispatched to the top-level - // <iframe mozbrowser> container in the system app. - // So the above code iterates over window.realFrameElement in order - // to crosss mozbrowser iframes boundaries and find the top-level - // one in the system app. - // window.realFrameElement will be |null| if the code try to cross - // content -> chrome boundaries. - let targetElement = request.element; - let targetWindow = request.window || targetElement.ownerDocument.defaultView; - while (targetWindow.realFrameElement) { - targetElement = targetWindow.realFrameElement; - targetWindow = targetElement.ownerDocument.defaultView; - } - - SystemAppProxy.dispatchEvent(details, targetElement); - }, - - classID: Components.ID("{8c719f03-afe0-4aac-91ff-6c215895d467}"), - - QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentPermissionPrompt]) -}; - -(function() { - // Do not allow GetUserMedia while in call. - permissionSpecificChecker["audio-capture"] = function(request) { - let forbid = false; - - if (forbid) { - request.cancel(); - } - - return forbid; - }; -})(); - -//module initialization -this.NSGetFactory = XPCOMUtils.generateNSGetFactory([ContentPermissionPrompt]); |