author | Phil Ringnalda <philringnalda@gmail.com> |
Sun, 09 Feb 2014 18:36:38 -0800 | |
changeset 184985 | ecf20a2484b6aaeb50c9a7f08272646923a9dff1 |
parent 184975 | ce819018434bb72961b9c764e9de8b1c9a29930b (current diff) |
parent 184984 | 31556bc216e494dfda5845451e46eb396bac7ea8 (diff) |
child 185004 | 923f863b867212e0acce4dddfe5fd2f585bb2421 |
child 185015 | 4cc4315a62d90ea6376d6c715feb8697249cc564 |
child 185023 | 6b41b92d0f6b7b878d6d8996f885d9e5fef121cd |
push id | 3503 |
push user | raliiev@mozilla.com |
push date | Mon, 28 Apr 2014 18:51:11 +0000 |
treeherder | mozilla-beta@c95ac01e332e [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
milestone | 30.0a1 |
first release with | nightly linux32
ecf20a2484b6
/
30.0a1
/
20140210030201
/
files
nightly linux64
ecf20a2484b6
/
30.0a1
/
20140210030201
/
files
nightly mac
ecf20a2484b6
/
30.0a1
/
20140210030201
/
files
nightly win32
ecf20a2484b6
/
30.0a1
/
20140210030201
/
files
nightly win64
ecf20a2484b6
/
30.0a1
/
20140210030201
/
files
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
releases | nightly linux32
30.0a1
/
20140210030201
/
pushlog to previous
nightly linux64
30.0a1
/
20140210030201
/
pushlog to previous
nightly mac
30.0a1
/
20140210030201
/
pushlog to previous
nightly win32
30.0a1
/
20140210030201
/
pushlog to previous
nightly win64
30.0a1
/
20140210030201
/
pushlog to previous
|
--- a/b2g/components/ContentPermissionPrompt.js +++ b/b2g/components/ContentPermissionPrompt.js @@ -1,28 +1,31 @@ /* 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: " + s + "\n"); + //dump("-*- ContentPermissionPrompt: " + str + "\n"); } const Ci = Components.interfaces; const Cr = Components.results; const Cu = Components.utils; const Cc = Components.classes; -const PROMPT_FOR_UNKNOWN = ["geolocation", "desktop-notification", - "audio-capture"]; +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"]; +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/Webapps.jsm"); Cu.import("resource://gre/modules/AppsUtils.jsm"); Cu.import("resource://gre/modules/PermissionsInstaller.jsm"); Cu.import("resource://gre/modules/PermissionsTable.jsm"); @@ -36,206 +39,314 @@ XPCOMUtils.defineLazyServiceGetter(this, "@mozilla.org/permissionSettings;1", "nsIDOMPermissionSettings"); XPCOMUtils.defineLazyServiceGetter(this, "AudioManager", "@mozilla.org/telephony/audiomanager;1", "nsIAudioManager"); -function rememberPermission(aPermission, aPrincipal, aSession) +/** + * 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 (type == Ci.nsIPermissionManager.PROMPT_ACTION || (type == Ci.nsIPermissionManager.UNKNOWN_ACTION && - PROMPT_FOR_UNKNOWN.indexOf(aPermission) >= 0)) { + PROMPT_FOR_UNKNOWN.indexOf(aPerm) >= 0)) { + 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(aPermission) < 0) { + } else if (PERMISSION_NO_SESSION.indexOf(aPerm) < 0) { permissionManager.addFromPrincipal(aPrincipal, aPerm, Ci.nsIPermissionManager.ALLOW_ACTION, Ci.nsIPermissionManager.EXPIRE_SESSION, 0); } } } - // Expand the permission to see if we have multiple access properties to convert - let access = PermissionsTable[aPermission].access; - if (access) { - for (let idx in access) { - convertPermToAllow(aPermission + "-" + access[idx], aPrincipal); + 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); } - } else { - convertPermToAllow(aPermission, aPrincipal); } } function ContentPermissionPrompt() {} ContentPermissionPrompt.prototype = { - handleExistingPermission: function handleExistingPermission(request) { - let access = (request.access && request.access !== "unused") ? request.type + "-" + request.access : - request.type; - let result = Services.perms.testExactPermissionFromPrincipal(request.principal, access); - if (result == Ci.nsIPermissionManager.ALLOW_ACTION) { + handleExistingPermission: function handleExistingPermission(request, + typesInfo) { + typesInfo.forEach(function(type) { + type.action = + Services.perms.testExactPermissionFromPrincipal(request.principal, + type.access); + if (type.action == Ci.nsIPermissionManager.UNKNOWN_ACTION && + PROMPT_FOR_UNKNOWN.indexOf(type.access) >= 0) { + type.action = Ci.nsIPermissionManager.PROMPT_ACTION; + } + }); + + // If all permissions are allowed already, call allow() without prompting. + let checkAllowPermission = function(type) { + if (type.action == Ci.nsIPermissionManager.ALLOW_ACTION) { + return true; + } + return false; + } + if (typesInfo.every(checkAllowPermission)) { + debug("all permission requests are allowed"); request.allow(); return true; } - if (result == Ci.nsIPermissionManager.DENY_ACTION || - result == Ci.nsIPermissionManager.UNKNOWN_ACTION && PROMPT_FOR_UNKNOWN.indexOf(access) < 0) { + + // 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; }, - handledByApp: function handledByApp(request) { + // 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); - let url = Services.io.newURI(app.origin, null, null); - let principal = secMan.getAppCodebasePrincipal(url, request.principal.appId, - /*mozbrowser*/false); - let access = (request.access && request.access !== "unused") ? request.type + "-" + request.access : - request.type; - let result = Services.perms.testExactPermissionFromPrincipal(principal, access); + // 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.getAppCodebasePrincipal(url, + request.principal.appId, + /*mozbrowser*/false); + let result = Services.perms.testExactPermissionFromPrincipal(principal, + type.access); - if (result == Ci.nsIPermissionManager.ALLOW_ACTION || - result == Ci.nsIPermissionManager.PROMPT_ACTION) { - return false; + if (result == Ci.nsIPermissionManager.ALLOW_ACTION || + result == Ci.nsIPermissionManager.PROMPT_ACTION) { + type.deny = false; + } + return !type.deny; + } + if (typesInfo.filter(notDenyAppPrincipal).length === 0) { + request.cancel(); + return true; } - request.cancel(); - return true; + return false; }, - handledByPermissionType: function handledByPermissionType(request) { - return permissionSpecificChecker.hasOwnProperty(request.type) - ? permissionSpecificChecker[request.type](request) - : 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; }, _id: 0, prompt: function(request) { if (secMan.isSystemPrincipal(request.principal)) { request.allow(); - return true; + return; } - if (this.handledByApp(request) || - this.handledByPermissionType(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, + deny: true, + action: Ci.nsIPermissionManager.UNKNOWN_ACTION + }; + typesInfo.push(tmp); + } + 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)) + if (this.handleExistingPermission(request, typesInfo)) { return; + } + + // prompt PROMPT_ACTION request only. + typesInfo.forEach(function(aType, aIndex) { + if (aType.action != Ci.nsIPermissionManager.PROMPT_ACTION || aType.deny) { + typesInfo.splice(aIndex); + } + }); let frame = request.element; let requestId = this._id++; if (!frame) { - this.delegatePrompt(request, requestId); + this.delegatePrompt(request, requestId, typesInfo); return; } frame = frame.wrappedJSObject; var cancelRequest = function() { frame.removeEventListener("mozbrowservisibilitychange", onVisibilityChange); request.cancel(); } var self = this; var onVisibilityChange = function(evt) { if (evt.detail.visible === true) return; - self.cancelPrompt(request, requestId); + self.cancelPrompt(request, requestId, typesInfo); cancelRequest(); } // If the request was initiated from a hidden iframe // we don't forward it to content and cancel it right away let domRequest = frame.getVisible(); domRequest.onsuccess = function gv_success(evt) { if (!evt.target.result) { cancelRequest(); return; } // Monitor the frame visibility and cancel the request if the frame goes // away but the request is still here. frame.addEventListener("mozbrowservisibilitychange", onVisibilityChange); - self.delegatePrompt(request, requestId, function onCallback() { + self.delegatePrompt(request, requestId, typesInfo, function onCallback() { frame.removeEventListener("mozbrowservisibilitychange", onVisibilityChange); }); }; // Something went wrong. Let's cancel the request just in case. domRequest.onerror = function gv_error() { cancelRequest(); } }, - cancelPrompt: function(request, requestId) { - this.sendToBrowserWindow("cancel-permission-prompt", request, requestId); + cancelPrompt: function(request, requestId, typesInfo) { + this.sendToBrowserWindow("cancel-permission-prompt", request, requestId, + typesInfo); }, - delegatePrompt: function(request, requestId, callback) { - let access = (request.access && request.access !== "unused") ? request.type + "-" + request.access : - request.type; - let principal = request.principal; + delegatePrompt: function(request, requestId, typesInfo, callback) { - this._permission = access; - this._uri = principal.URI.spec; - this._origin = principal.origin; - - this.sendToBrowserWindow("permission-prompt", request, requestId, function(type, remember) { + this.sendToBrowserWindow("permission-prompt", request, requestId, typesInfo, + function(type, remember) { if (type == "permission-allow") { - rememberPermission(request.type, principal, !remember); + rememberPermission(typesInfo, request.principal, !remember); if (callback) { callback(); } request.allow(); return; } - if (remember) { - Services.perms.addFromPrincipal(principal, access, - Ci.nsIPermissionManager.DENY_ACTION); - } else { - Services.perms.addFromPrincipal(principal, access, - Ci.nsIPermissionManager.DENY_ACTION, - Ci.nsIPermissionManager.EXPIRE_SESSION, 0); + 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(aPerm) < 0) { + Services.perms.addFromPrincipal(request.principal, type.access, + Ci.nsIPermissionManager.DENY_ACTION, + Ci.nsIPermissionManager.EXPIRE_SESSION, + 0); + } } + typesInfo.forEach(addDenyPermission); if (callback) { callback(); } request.cancel(); }); }, - sendToBrowserWindow: function(type, request, requestId, callback) { + sendToBrowserWindow: function(type, request, requestId, typesInfo, callback) { let browser = Services.wm.getMostRecentWindow("navigator:browser"); let content = browser.getContentWindow(); if (!content) return; if (callback) { content.addEventListener("mozContentEvent", function contentEvent(evt) { let detail = evt.detail; @@ -248,20 +359,25 @@ ContentPermissionPrompt.prototype = { } 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 permissions = {}; + for (let i in typesInfo) { + debug("prompt " + typesInfo[i].permission); + permissions[typesInfo[i].permission] = []; + } let details = { type: type, - permission: request.type, + permissions: permissions, id: requestId, origin: principal.origin, isApp: isApp, remember: remember }; if (!isApp) { browser.shell.sendChromeEvent(details); @@ -284,11 +400,10 @@ ContentPermissionPrompt.prototype = { request.cancel(); return true; } else { return false; } }; })(); - //module initialization this.NSGetFactory = XPCOMUtils.generateNSGetFactory([ContentPermissionPrompt]);
--- a/b2g/components/test/mochitest/permission_handler_chrome.js +++ b/b2g/components/test/mochitest/permission_handler_chrome.js @@ -43,25 +43,29 @@ function addChromeEventListener(type, li content.removeEventListener("mozChromeEvent", chromeListener); } }); } function checkPromptEvent(prompt_evt) { let detail = prompt_evt.detail; - if (detail.permission == "audio-capture") { - sendAsyncMessage("permission.granted", "audio-capture"); - test_counts--; - } else if (detail.permission == "desktop-notification") { - sendAsyncMessage("permission.granted", "desktop-notification"); - test_counts--; - } else if (detail.permission == "geolocation") { - sendAsyncMessage("permission.granted", "geolocation"); - test_counts--; + if (detail.permissions) { + if ("audio-capture" in detail.permissions) { + sendAsyncMessage("permission.granted", "audio-capture"); + test_counts--; + } + if ("desktop-notification" in detail.permissions) { + sendAsyncMessage("permission.granted", "desktop-notification"); + test_counts--; + } + if ("geolocation" in detail.permissions) { + sendAsyncMessage("permission.granted", "geolocation"); + test_counts--; + } } if (!test_counts) { debug("remove prompt event listener."); return true; } return false;
--- a/b2g/config/emulator-ics/sources.xml +++ b/b2g/config/emulator-ics/sources.xml @@ -7,17 +7,17 @@ <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/> <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/> <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/> <!-- Gonk specific things and forks --> <project name="platform_build" path="build" remote="b2g" revision="59605a7c026ff06cc1613af3938579b1dddc6cfe"> <copyfile dest="Makefile" src="core/root.mk"/> </project> <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/> - <project name="gaia.git" path="gaia" remote="mozillaorg" revision="f1aa7a363bc38040d0ad942e993d8641af8db752"/> + <project name="gaia.git" path="gaia" remote="mozillaorg" revision="c273bd6525f7f295539592ce74d5e6b225d53be1"/> <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="0a8bdd0f43e2d8fc7d45b3b0d97125834c0ac72f"/> <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/> <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="eda08beb3ba9a159843c70ffde0f9660ec351eb9"/> <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="87aa8679560ce09f6445621d6f370d9de722cdba"/> <project name="moztt" path="external/moztt" remote="b2g" revision="e33ea242b4328fb0d1824c951f379332b5021512"/> <project name="apitrace" path="external/apitrace" remote="apitrace" revision="788d9ce293a9b44f64536130cf4ad577e8101dbe"/> <!-- Stock Android things --> <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/emulator-jb/sources.xml +++ b/b2g/config/emulator-jb/sources.xml @@ -6,17 +6,17 @@ <remote fetch="https://git.mozilla.org/external/caf" name="caf"/> <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/> <!-- B2G specific things. --> <project name="platform_build" path="build" remote="b2g" revision="317f25e0a4cb3e8e86e2b76c37a14081372f0307"> <copyfile dest="Makefile" src="core/root.mk"/> </project> <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/> <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/> - <project name="gaia" path="gaia" remote="mozillaorg" revision="f1aa7a363bc38040d0ad942e993d8641af8db752"/> + <project name="gaia" path="gaia" remote="mozillaorg" revision="c273bd6525f7f295539592ce74d5e6b225d53be1"/> <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="0a8bdd0f43e2d8fc7d45b3b0d97125834c0ac72f"/> <project name="moztt" path="external/moztt" remote="b2g" revision="e33ea242b4328fb0d1824c951f379332b5021512"/> <project name="apitrace" path="external/apitrace" remote="apitrace" revision="788d9ce293a9b44f64536130cf4ad577e8101dbe"/> <project name="valgrind" path="external/valgrind" remote="b2g" revision="905bfa3548eb75cf1792d0d8412b92113bbd4318"/> <project name="vex" path="external/VEX" remote="b2g" revision="c3d7efc45414f1b44cd9c479bb2758c91c4707c0"/> <!-- Stock Android things --> <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/> <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
--- a/b2g/config/emulator/sources.xml +++ b/b2g/config/emulator/sources.xml @@ -7,17 +7,17 @@ <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/> <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/> <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/> <!-- Gonk specific things and forks --> <project name="platform_build" path="build" remote="b2g" revision="59605a7c026ff06cc1613af3938579b1dddc6cfe"> <copyfile dest="Makefile" src="core/root.mk"/> </project> <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/> - <project name="gaia.git" path="gaia" remote="mozillaorg" revision="f1aa7a363bc38040d0ad942e993d8641af8db752"/> + <project name="gaia.git" path="gaia" remote="mozillaorg" revision="c273bd6525f7f295539592ce74d5e6b225d53be1"/> <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="0a8bdd0f43e2d8fc7d45b3b0d97125834c0ac72f"/> <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/> <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="eda08beb3ba9a159843c70ffde0f9660ec351eb9"/> <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="87aa8679560ce09f6445621d6f370d9de722cdba"/> <project name="moztt" path="external/moztt" remote="b2g" revision="e33ea242b4328fb0d1824c951f379332b5021512"/> <project name="apitrace" path="external/apitrace" remote="apitrace" revision="788d9ce293a9b44f64536130cf4ad577e8101dbe"/> <!-- Stock Android things --> <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/gaia.json +++ b/b2g/config/gaia.json @@ -1,4 +1,4 @@ { - "revision": "775dadf70f866376fd19178c27e19e88c6d57026", + "revision": "5db3ae28ac3a44dd2c08c41732758cdf4e7116f2", "repo_path": "/integration/gaia-central" }
--- a/b2g/config/hamachi/sources.xml +++ b/b2g/config/hamachi/sources.xml @@ -6,17 +6,17 @@ <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/> <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/> <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/> <!-- Gonk specific things and forks --> <project name="platform_build" path="build" remote="b2g" revision="59605a7c026ff06cc1613af3938579b1dddc6cfe"> <copyfile dest="Makefile" src="core/root.mk"/> </project> <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/> - <project name="gaia.git" path="gaia" remote="mozillaorg" revision="f1aa7a363bc38040d0ad942e993d8641af8db752"/> + <project name="gaia.git" path="gaia" remote="mozillaorg" revision="c273bd6525f7f295539592ce74d5e6b225d53be1"/> <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="0a8bdd0f43e2d8fc7d45b3b0d97125834c0ac72f"/> <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/> <project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/> <project name="moztt" path="external/moztt" remote="b2g" revision="e33ea242b4328fb0d1824c951f379332b5021512"/> <project name="apitrace" path="external/apitrace" remote="apitrace" revision="788d9ce293a9b44f64536130cf4ad577e8101dbe"/> <!-- Stock Android things --> <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/> <project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
--- a/b2g/config/helix/sources.xml +++ b/b2g/config/helix/sources.xml @@ -5,17 +5,17 @@ <remote fetch="https://git.mozilla.org/external/caf" name="caf"/> <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/> <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/> <!-- Gonk specific things and forks --> <project name="platform_build" path="build" remote="b2g" revision="59605a7c026ff06cc1613af3938579b1dddc6cfe"> <copyfile dest="Makefile" src="core/root.mk"/> </project> <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/> - <project name="gaia.git" path="gaia" remote="mozillaorg" revision="f1aa7a363bc38040d0ad942e993d8641af8db752"/> + <project name="gaia.git" path="gaia" remote="mozillaorg" revision="c273bd6525f7f295539592ce74d5e6b225d53be1"/> <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="0a8bdd0f43e2d8fc7d45b3b0d97125834c0ac72f"/> <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/> <project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/> <project name="moztt" path="external/moztt" remote="b2g" revision="e33ea242b4328fb0d1824c951f379332b5021512"/> <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/> <!-- Stock Android things --> <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/> <project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
--- a/b2g/config/inari/sources.xml +++ b/b2g/config/inari/sources.xml @@ -7,17 +7,17 @@ <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/> <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/> <default remote="caf" revision="ics_chocolate_rb4.2" sync-j="4"/> <!-- Gonk specific things and forks --> <project name="platform_build" path="build" remote="b2g" revision="59605a7c026ff06cc1613af3938579b1dddc6cfe"> <copyfile dest="Makefile" src="core/root.mk"/> </project> <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/> - <project name="gaia.git" path="gaia" remote="mozillaorg" revision="f1aa7a363bc38040d0ad942e993d8641af8db752"/> + <project name="gaia.git" path="gaia" remote="mozillaorg" revision="c273bd6525f7f295539592ce74d5e6b225d53be1"/> <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="0a8bdd0f43e2d8fc7d45b3b0d97125834c0ac72f"/> <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/> <project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/> <project name="moztt" path="external/moztt" remote="b2g" revision="e33ea242b4328fb0d1824c951f379332b5021512"/> <project name="apitrace" path="external/apitrace" remote="apitrace" revision="788d9ce293a9b44f64536130cf4ad577e8101dbe"/> <!-- Stock Android things --> <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/> <project name="platform/bionic" path="bionic" revision="cd5dfce80bc3f0139a56b58aca633202ccaee7f8"/>
--- a/b2g/config/leo/sources.xml +++ b/b2g/config/leo/sources.xml @@ -6,17 +6,17 @@ <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/> <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/> <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/> <!-- Gonk specific things and forks --> <project name="platform_build" path="build" remote="b2g" revision="59605a7c026ff06cc1613af3938579b1dddc6cfe"> <copyfile dest="Makefile" src="core/root.mk"/> </project> <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/> - <project name="gaia.git" path="gaia" remote="mozillaorg" revision="f1aa7a363bc38040d0ad942e993d8641af8db752"/> + <project name="gaia.git" path="gaia" remote="mozillaorg" revision="c273bd6525f7f295539592ce74d5e6b225d53be1"/> <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="0a8bdd0f43e2d8fc7d45b3b0d97125834c0ac72f"/> <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/> <project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/> <project name="moztt" path="external/moztt" remote="b2g" revision="e33ea242b4328fb0d1824c951f379332b5021512"/> <project name="apitrace" path="external/apitrace" remote="apitrace" revision="788d9ce293a9b44f64536130cf4ad577e8101dbe"/> <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/> <!-- Stock Android things --> <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
--- a/b2g/config/mako/sources.xml +++ b/b2g/config/mako/sources.xml @@ -6,17 +6,17 @@ <remote fetch="https://git.mozilla.org/external/caf" name="caf"/> <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/> <!-- B2G specific things. --> <project name="platform_build" path="build" remote="b2g" revision="317f25e0a4cb3e8e86e2b76c37a14081372f0307"> <copyfile dest="Makefile" src="core/root.mk"/> </project> <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/> <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/> - <project name="gaia" path="gaia" remote="mozillaorg" revision="f1aa7a363bc38040d0ad942e993d8641af8db752"/> + <project name="gaia" path="gaia" remote="mozillaorg" revision="c273bd6525f7f295539592ce74d5e6b225d53be1"/> <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="0a8bdd0f43e2d8fc7d45b3b0d97125834c0ac72f"/> <project name="moztt" path="external/moztt" remote="b2g" revision="e33ea242b4328fb0d1824c951f379332b5021512"/> <project name="apitrace" path="external/apitrace" remote="apitrace" revision="788d9ce293a9b44f64536130cf4ad577e8101dbe"/> <project name="valgrind" path="external/valgrind" remote="b2g" revision="905bfa3548eb75cf1792d0d8412b92113bbd4318"/> <project name="vex" path="external/VEX" remote="b2g" revision="c3d7efc45414f1b44cd9c479bb2758c91c4707c0"/> <!-- Stock Android things --> <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/> <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
--- a/b2g/config/wasabi/sources.xml +++ b/b2g/config/wasabi/sources.xml @@ -6,17 +6,17 @@ <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/> <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/> <default remote="caf" revision="ics_chocolate_rb4.2" sync-j="4"/> <!-- Gonk specific things and forks --> <project name="platform_build" path="build" remote="b2g" revision="59605a7c026ff06cc1613af3938579b1dddc6cfe"> <copyfile dest="Makefile" src="core/root.mk"/> </project> <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/> - <project name="gaia.git" path="gaia" remote="mozillaorg" revision="f1aa7a363bc38040d0ad942e993d8641af8db752"/> + <project name="gaia.git" path="gaia" remote="mozillaorg" revision="c273bd6525f7f295539592ce74d5e6b225d53be1"/> <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="0a8bdd0f43e2d8fc7d45b3b0d97125834c0ac72f"/> <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/> <project name="librecovery" path="librecovery" remote="b2g" revision="84f2f2fce22605e17d511ff1767e54770067b5b5"/> <project name="moztt" path="external/moztt" remote="b2g" revision="e33ea242b4328fb0d1824c951f379332b5021512"/> <project name="apitrace" path="external/apitrace" remote="apitrace" revision="788d9ce293a9b44f64536130cf4ad577e8101dbe"/> <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/> <!-- Stock Android things --> <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
--- a/browser/components/nsBrowserGlue.js +++ b/browser/components/nsBrowserGlue.js @@ -1897,17 +1897,26 @@ ContentPermissionPrompt.prototype = { popupNotificationActions.push(action); } var mainAction = popupNotificationActions.length ? popupNotificationActions[0] : null; var secondaryActions = popupNotificationActions.splice(1); - if (aRequest.type == "pointerLock") { + // Only allow exactly one permission rquest here. + let types = aRequest.types.QueryInterface(Ci.nsIArray); + if (types.length != 1) { + aRequest.cancel(); + return; + } + + let perm = types.queryElementAt(0, Ci.nsIContentPermissionType); + + if (perm.type == "pointerLock") { // If there's no mainAction, this is the autoAllow warning prompt. let autoAllow = !mainAction; aOptions = { removeOnDismissal: autoAllow, eventCallback: type => { if (type == "removed") { browser.removeEventListener("mozfullscreenchange", onFullScreen, true); if (autoAllow) { @@ -1915,17 +1924,17 @@ ContentPermissionPrompt.prototype = { } } }, }; } var popup = chromeWin.PopupNotifications.show(browser, aNotificationId, aMessage, aAnchorId, mainAction, secondaryActions, aOptions); - if (aRequest.type == "pointerLock") { + if (perm.type == "pointerLock") { // pointerLock is automatically allowed in fullscreen mode (and revoked // upon exit), so if the page enters fullscreen mode after requesting // pointerLock (but before the user has granted permission), we should // remove the now-impotent notification. browser.addEventListener("mozfullscreenchange", onFullScreen, true); } }, @@ -2053,60 +2062,68 @@ ContentPermissionPrompt.prototype = { } this._showPrompt(aRequest, message, "pointerLock", actions, "pointerLock", "pointerLock-notification-icon", null); }, prompt: function CPP_prompt(request) { + // Only allow exactly one permission rquest here. + let types = request.types.QueryInterface(Ci.nsIArray); + if (types.length != 1) { + request.cancel(); + return; + } + let perm = types.queryElementAt(0, Ci.nsIContentPermissionType); + const kFeatureKeys = { "geolocation" : "geo", "desktop-notification" : "desktop-notification", "pointerLock" : "pointerLock", }; // Make sure that we support the request. - if (!(request.type in kFeatureKeys)) { + if (!(perm.type in kFeatureKeys)) { return; } var requestingPrincipal = request.principal; var requestingURI = requestingPrincipal.URI; // Ignore requests from non-nsIStandardURLs if (!(requestingURI instanceof Ci.nsIStandardURL)) return; var autoAllow = false; - var permissionKey = kFeatureKeys[request.type]; + var permissionKey = kFeatureKeys[perm.type]; var result = Services.perms.testExactPermissionFromPrincipal(requestingPrincipal, permissionKey); if (result == Ci.nsIPermissionManager.DENY_ACTION) { request.cancel(); return; } if (result == Ci.nsIPermissionManager.ALLOW_ACTION) { autoAllow = true; // For pointerLock, we still want to show a warning prompt. - if (request.type != "pointerLock") { + if (perm.type != "pointerLock") { request.allow(); return; } } var browser = this._getBrowserForRequest(request); var chromeWin = browser.ownerDocument.defaultView; if (!chromeWin.PopupNotifications) // Ignore requests from browsers hosted in windows that don't support // PopupNotifications. return; // Show the prompt. - switch (request.type) { + switch (perm.type) { case "geolocation": this._promptGeo(request); break; case "desktop-notification": this._promptWebNotifications(request); break; case "pointerLock": this._promptPointerLock(request, autoAllow);
--- a/browser/metro/base/content/helperui/IndexedDB.js +++ b/browser/metro/base/content/helperui/IndexedDB.js @@ -39,33 +39,40 @@ let IndexedDB = { } else if (topic == this._quotaCancel) { payload.permission = Ci.nsIPermissionManager.UNKNOWN_ACTION; browser.messageManager.sendAsyncMessage("IndexedDB:Response", payload); // XXX Need to actually save this? return; } let prompt = Cc["@mozilla.org/content-permission/prompt;1"].createInstance(Ci.nsIContentPermissionPrompt); + let types = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray); + let promptType = { + type: type, + access: "unused", + QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentPermissionType]) + }; + types.appendElement(promptType, false); // If the user waits a long time before responding, we default to UNKNOWN_ACTION. let timeoutId = setTimeout(function() { payload.permission = Ci.nsIPermissionManager.UNKNOWN_ACTION; browser.messageManager.sendAsyncMessage("IndexedDB:Response", payload); timeoutId = null; }, 30000); function checkTimeout() { if (timeoutId === null) return true; clearTimeout(timeoutId); timeoutId = null; return false; } prompt.prompt({ - type: type, + types: types, uri: Services.io.newURI(payload.location, null, null), window: null, element: aMessage.target, cancel: function() { if (checkTimeout()) return; payload.permission = Ci.nsIPermissionManager.DENY_ACTION; browser.messageManager.sendAsyncMessage("IndexedDB:Response", payload);
--- a/browser/metro/components/ContentPermissionPrompt.js +++ b/browser/metro/components/ContentPermissionPrompt.js @@ -51,78 +51,86 @@ ContentPermissionPrompt.prototype = { let requestingWindow = request.window.top; let windowID = request.window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID; let browser = chromeWin.Browser.getBrowserForWindowId(windowID); return chromeWin.getNotificationBox(browser); } return chromeWin.Browser.getNotificationBox(request.element); }, - handleExistingPermission: function handleExistingPermission(request) { - let result = Services.perms.testExactPermissionFromPrincipal(request.principal, request.type); + handleExistingPermission: function handleExistingPermission(request, type) { + let result = Services.perms.testExactPermissionFromPrincipal(request.principal, type); if (result == Ci.nsIPermissionManager.ALLOW_ACTION) { request.allow(); return true; } if (result == Ci.nsIPermissionManager.DENY_ACTION) { request.cancel(); return true; } return false; }, prompt: function(request) { + // Only allow exactly one permission rquest here. + let types = request.types.QueryInterface(Ci.nsIArray); + if (types.length != 1) { + request.cancel(); + return; + } + let perm = types.queryElementAt(0, Ci.nsIContentPermissionType); + // returns true if the request was handled - if (this.handleExistingPermission(request)) + if (this.handleExistingPermission(request, perm.type)) return; let pm = Services.perms; let notificationBox = this.getNotificationBoxForRequest(request); let browserBundle = Services.strings.createBundle("chrome://browser/locale/browser.properties"); - let notification = notificationBox.getNotificationWithValue(request.type); + let notification = notificationBox.getNotificationWithValue(perm.type); if (notification) return; - let entityName = kEntities[request.type]; - let icon = kIcons[request.type] || ""; + let entityName = kEntities[perm.type]; + let icon = kIcons[perm.type] || ""; let buttons = [{ label: browserBundle.GetStringFromName(entityName + ".allow"), accessKey: "", callback: function(notification) { request.allow(); } }, { label: browserBundle.GetStringFromName("contentPermissions.alwaysForSite"), accessKey: "", callback: function(notification) { - Services.perms.addFromPrincipal(request.principal, request.type, Ci.nsIPermissionManager.ALLOW_ACTION); + Services.perms.addFromPrincipal(request.principal, perm.type, Ci.nsIPermissionManager.ALLOW_ACTION); request.allow(); } }, { label: browserBundle.GetStringFromName("contentPermissions.neverForSite"), accessKey: "", callback: function(notification) { - Services.perms.addFromPrincipal(request.principal, request.type, Ci.nsIPermissionManager.DENY_ACTION); + Services.perms.addFromPrincipal(request.principal, perm.type, Ci.nsIPermissionManager.DENY_ACTION); request.cancel(); } }]; let message = browserBundle.formatStringFromName(entityName + ".wantsTo", [request.principal.URI.host], 1); let newBar = notificationBox.appendNotification(message, - request.type, + perm.type, icon, notificationBox.PRIORITY_WARNING_MEDIUM, buttons); - if (request.type == "geolocation") { + if (perm.type == "geolocation") { // Add the "learn more" link. let link = newBar.ownerDocument.createElement("label"); link.setAttribute("value", browserBundle.GetStringFromName("geolocation.learnMore")); link.setAttribute("class", "text-link notification-link"); newBar.insertBefore(link, newBar.firstChild); let win = this.getChromeWindowForRequest(request); link.addEventListener("click", function() {
--- a/content/base/src/nsDocument.cpp +++ b/content/base/src/nsDocument.cpp @@ -212,16 +212,18 @@ #include "mozilla/css/Rule.h" #include "nsIDOMLocation.h" #include "nsIHttpChannelInternal.h" #include "nsISecurityConsoleMessage.h" #include "nsCharSeparatedTokenizer.h" #include "mozilla/dom/XPathEvaluator.h" #include "nsIDocumentEncoder.h" #include "nsIStructuredCloneContainer.h" +#include "nsIMutableArray.h" +#include "nsContentPermissionHelper.h" using namespace mozilla; using namespace mozilla::dom; typedef nsTArray<Link*> LinkArray; #ifdef PR_LOGGING static PRLogModuleInfo* gDocumentLeakPRLog; @@ -10766,27 +10768,21 @@ public: bool mUserInputOrChromeCaller; }; NS_IMPL_ISUPPORTS_INHERITED1(nsPointerLockPermissionRequest, nsRunnable, nsIContentPermissionRequest) NS_IMETHODIMP -nsPointerLockPermissionRequest::GetType(nsACString& aType) -{ - aType = "pointerLock"; - return NS_OK; -} - -NS_IMETHODIMP -nsPointerLockPermissionRequest::GetAccess(nsACString& aAccess) -{ - aAccess = "unused"; - return NS_OK; +nsPointerLockPermissionRequest::GetTypes(nsIArray** aTypes) +{ + return CreatePermissionArray(NS_LITERAL_CSTRING("pointerLock"), + NS_LITERAL_CSTRING("unused"), + aTypes); } NS_IMETHODIMP nsPointerLockPermissionRequest::GetPrincipal(nsIPrincipal** aPrincipal) { nsCOMPtr<nsIDocument> d = do_QueryReferent(mDocument); if (d) { NS_ADDREF(*aPrincipal = d->NodePrincipal());
--- a/content/media/webrtc/MediaEngine.h +++ b/content/media/webrtc/MediaEngine.h @@ -1,15 +1,16 @@ /* 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/. */ #ifndef MEDIAENGINE_H_ #define MEDIAENGINE_H_ +#include "mozilla/RefPtr.h" #include "nsIDOMFile.h" #include "DOMMediaStream.h" #include "MediaStreamGraph.h" namespace mozilla { /** * Abstract interface for managing audio and video devices. Each platform @@ -30,17 +31,17 @@ enum MediaEngineState { }; // We only support 1 audio and 1 video track for now. enum { kVideoTrack = 1, kAudioTrack = 2 }; -class MediaEngine +class MediaEngine : public RefCounted<MediaEngine> { public: virtual ~MediaEngine() {} static const int DEFAULT_VIDEO_FPS = 30; static const int DEFAULT_VIDEO_MIN_FPS = 10; static const int DEFAULT_VIDEO_WIDTH = 640; static const int DEFAULT_VIDEO_HEIGHT = 480;
--- a/dom/apps/src/PermissionsTable.jsm +++ b/dom/apps/src/PermissionsTable.jsm @@ -316,16 +316,21 @@ this.PermissionsTable = { geolocation: privileged: ALLOW_ACTION, certified: ALLOW_ACTION }, "downloads": { app: DENY_ACTION, privileged: DENY_ACTION, certified: ALLOW_ACTION }, + "video-capture": { + app: PROMPT_ACTION, + privileged: PROMPT_ACTION, + certified: PROMPT_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
--- a/dom/base/nsContentPermissionHelper.cpp +++ b/dom/base/nsContentPermissionHelper.cpp @@ -1,49 +1,183 @@ /* 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/. */ #ifdef MOZ_WIDGET_GONK #include "GonkPermission.h" #include "mozilla/dom/ContentParent.h" #endif // MOZ_WIDGET_GONK -#include "nsContentPermissionHelper.h" -#include "nsIContentPermissionPrompt.h" #include "nsCOMPtr.h" #include "nsIDOMElement.h" #include "nsIPrincipal.h" #include "mozilla/dom/Element.h" +#include "mozilla/dom/PContentPermission.h" +#include "mozilla/dom/PermissionMessageUtils.h" +#include "mozilla/dom/PContentPermissionRequestParent.h" #include "mozilla/dom/TabParent.h" #include "mozilla/unused.h" #include "nsComponentManagerUtils.h" +#include "nsArrayUtils.h" +#include "nsIMutableArray.h" +#include "nsContentPermissionHelper.h" using mozilla::unused; // <snicker> using namespace mozilla::dom; using namespace mozilla; +namespace mozilla { +namespace dom { + +class ContentPermissionRequestParent : public PContentPermissionRequestParent +{ + public: + ContentPermissionRequestParent(const nsTArray<PermissionRequest>& aRequests, + Element* element, + const IPC::Principal& principal); + virtual ~ContentPermissionRequestParent(); + + bool IsBeingDestroyed(); + + nsCOMPtr<nsIPrincipal> mPrincipal; + nsCOMPtr<Element> mElement; + nsCOMPtr<nsContentPermissionRequestProxy> mProxy; + nsTArray<PermissionRequest> mRequests; + + private: + virtual bool Recvprompt(); + virtual void ActorDestroy(ActorDestroyReason why); +}; + +ContentPermissionRequestParent::ContentPermissionRequestParent(const nsTArray<PermissionRequest>& aRequests, + Element* aElement, + const IPC::Principal& aPrincipal) +{ + MOZ_COUNT_CTOR(ContentPermissionRequestParent); + + mPrincipal = aPrincipal; + mElement = aElement; + mRequests = aRequests; +} + +ContentPermissionRequestParent::~ContentPermissionRequestParent() +{ + MOZ_COUNT_DTOR(ContentPermissionRequestParent); +} + +bool +ContentPermissionRequestParent::Recvprompt() +{ + mProxy = new nsContentPermissionRequestProxy(); + NS_ASSERTION(mProxy, "Alloc of request proxy failed"); + if (NS_FAILED(mProxy->Init(mRequests, this))) { + mProxy->Cancel(); + } + return true; +} + +void +ContentPermissionRequestParent::ActorDestroy(ActorDestroyReason why) +{ + if (mProxy) { + mProxy->OnParentDestroyed(); + } +} + +bool +ContentPermissionRequestParent::IsBeingDestroyed() +{ + // When TabParent::Destroy() is called, we are being destroyed. It's unsafe + // to send out any message now. + TabParent* tabParent = static_cast<TabParent*>(Manager()); + return tabParent->IsDestroyed(); +} + +NS_IMPL_ISUPPORTS1(ContentPermissionType, nsIContentPermissionType) + +ContentPermissionType::ContentPermissionType(const nsACString& aType, + const nsACString& aAccess) +{ + mType = aType; + mAccess = aAccess; +} + +ContentPermissionType::~ContentPermissionType() +{ +} + +NS_IMETHODIMP +ContentPermissionType::GetType(nsACString& aType) +{ + aType = mType; + return NS_OK; +} + +NS_IMETHODIMP +ContentPermissionType::GetAccess(nsACString& aAccess) +{ + aAccess = mAccess; + return NS_OK; +} + +uint32_t +ConvertPermissionRequestToArray(nsTArray<PermissionRequest>& aSrcArray, + nsIMutableArray* aDesArray) +{ + uint32_t len = aSrcArray.Length(); + for (uint32_t i = 0; i < len; i++) { + nsRefPtr<ContentPermissionType> cpt = + new ContentPermissionType(aSrcArray[i].type(), aSrcArray[i].access()); + aDesArray->AppendElement(cpt, false); + } + return len; +} + +nsresult +CreatePermissionArray(const nsACString& aType, + const nsACString& aAccess, + nsIArray** aTypesArray) +{ + nsCOMPtr<nsIMutableArray> types = do_CreateInstance(NS_ARRAY_CONTRACTID); + nsRefPtr<ContentPermissionType> permType = new ContentPermissionType(aType, + aAccess); + types->AppendElement(permType, false); + types.forget(aTypesArray); + + return NS_OK; +} + +PContentPermissionRequestParent* +CreateContentPermissionRequestParent(const nsTArray<PermissionRequest>& aRequests, + Element* element, + const IPC::Principal& principal) +{ + return new ContentPermissionRequestParent(aRequests, element, principal); +} + +} // namespace dom +} // namespace mozilla + nsContentPermissionRequestProxy::nsContentPermissionRequestProxy() { MOZ_COUNT_CTOR(nsContentPermissionRequestProxy); } nsContentPermissionRequestProxy::~nsContentPermissionRequestProxy() { MOZ_COUNT_DTOR(nsContentPermissionRequestProxy); } nsresult -nsContentPermissionRequestProxy::Init(const nsACString & type, - const nsACString & access, +nsContentPermissionRequestProxy::Init(const nsTArray<PermissionRequest>& requests, ContentPermissionRequestParent* parent) { NS_ASSERTION(parent, "null parent"); mParent = parent; - mType = type; - mAccess = access; + mPermissionRequests = requests; nsCOMPtr<nsIContentPermissionPrompt> prompt = do_CreateInstance(NS_CONTENT_PERMISSION_PROMPT_CONTRACTID); if (!prompt) { return NS_ERROR_FAILURE; } prompt->Prompt(this); return NS_OK; @@ -53,27 +187,24 @@ void nsContentPermissionRequestProxy::OnParentDestroyed() { mParent = nullptr; } NS_IMPL_ISUPPORTS1(nsContentPermissionRequestProxy, nsIContentPermissionRequest) NS_IMETHODIMP -nsContentPermissionRequestProxy::GetType(nsACString & aType) +nsContentPermissionRequestProxy::GetTypes(nsIArray** aTypes) { - aType = mType; - return NS_OK; -} - -NS_IMETHODIMP -nsContentPermissionRequestProxy::GetAccess(nsACString & aAccess) -{ - aAccess = mAccess; - return NS_OK; + nsCOMPtr<nsIMutableArray> types = do_CreateInstance(NS_ARRAY_CONTRACTID); + if (ConvertPermissionRequestToArray(mPermissionRequests, types)) { + types.forget(aTypes); + return NS_OK; + } + return NS_ERROR_FAILURE; } NS_IMETHODIMP nsContentPermissionRequestProxy::GetWindow(nsIDOMWindow * *aRequestingWindow) { NS_ENSURE_ARG_POINTER(aRequestingWindow); *aRequestingWindow = nullptr; // ipc doesn't have a window return NS_OK; @@ -131,71 +262,27 @@ nsContentPermissionRequestProxy::Allow() // Don't send out the delete message when the managing protocol (PBrowser) is // being destroyed and PContentPermissionRequest will soon be. if (mParent->IsBeingDestroyed()) { return NS_ERROR_FAILURE; } #ifdef MOZ_WIDGET_GONK - if (mType.Equals("audio-capture")) { - GonkPermissionService::GetInstance()->addGrantInfo( - "android.permission.RECORD_AUDIO", - static_cast<TabParent*>(mParent->Manager())->Manager()->Pid()); + uint32_t len = mPermissionRequests.Length(); + for (uint32_t i = 0; i < len; i++) { + if (mPermissionRequests[i].type().Equals("audio-capture")) { + GonkPermissionService::GetInstance()->addGrantInfo( + "android.permission.RECORD_AUDIO", + static_cast<TabParent*>(mParent->Manager())->Manager()->Pid()); + } + if (mPermissionRequests[i].type().Equals("video-capture")) { + GonkPermissionService::GetInstance()->addGrantInfo( + "android.permission.CAMERA", + static_cast<TabParent*>(mParent->Manager())->Manager()->Pid()); + } } #endif unused << ContentPermissionRequestParent::Send__delete__(mParent, true); mParent = nullptr; return NS_OK; } - -namespace mozilla { -namespace dom { - -ContentPermissionRequestParent::ContentPermissionRequestParent(const nsACString& aType, - const nsACString& aAccess, - Element* aElement, - const IPC::Principal& aPrincipal) -{ - MOZ_COUNT_CTOR(ContentPermissionRequestParent); - - mPrincipal = aPrincipal; - mElement = aElement; - mType = aType; - mAccess = aAccess; -} - -ContentPermissionRequestParent::~ContentPermissionRequestParent() -{ - MOZ_COUNT_DTOR(ContentPermissionRequestParent); -} - -bool -ContentPermissionRequestParent::Recvprompt() -{ - mProxy = new nsContentPermissionRequestProxy(); - NS_ASSERTION(mProxy, "Alloc of request proxy failed"); - if (NS_FAILED(mProxy->Init(mType, mAccess, this))) { - mProxy->Cancel(); - } - return true; -} - -void -ContentPermissionRequestParent::ActorDestroy(ActorDestroyReason why) -{ - if (mProxy) { - mProxy->OnParentDestroyed(); - } -} - -bool -ContentPermissionRequestParent::IsBeingDestroyed() -{ - // When TabParent::Destroy() is called, we are being destroyed. It's unsafe - // to send out any message now. - TabParent* tabParent = static_cast<TabParent*>(Manager()); - return tabParent->IsDestroyed(); -} - -} // namespace dom -} // namespace mozilla
--- a/dom/base/nsContentPermissionHelper.h +++ b/dom/base/nsContentPermissionHelper.h @@ -1,65 +1,80 @@ /* 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/. */ #ifndef nsContentPermissionHelper_h #define nsContentPermissionHelper_h #include "nsIContentPermissionPrompt.h" -#include "nsString.h" - -#include "mozilla/dom/PermissionMessageUtils.h" -#include "mozilla/dom/PContentPermissionRequestParent.h" +#include "nsTArray.h" +#include "nsIMutableArray.h" class nsContentPermissionRequestProxy; +// Forward declare IPC::Principal here which is defined in +// PermissionMessageUtils.h. Include this file will transitively includes +// "windows.h" and it defines +// #define CreateEvent CreateEventW +// #define LoadImage LoadImageW +// That will mess up windows build. +namespace IPC { +class Principal; +} + namespace mozilla { namespace dom { class Element; +class PermissionRequest; +class ContentPermissionRequestParent; +class PContentPermissionRequestParent; -class ContentPermissionRequestParent : public PContentPermissionRequestParent +class ContentPermissionType : public nsIContentPermissionType { - public: - ContentPermissionRequestParent(const nsACString& type, - const nsACString& access, - Element* element, - const IPC::Principal& principal); - virtual ~ContentPermissionRequestParent(); +public: + NS_DECL_ISUPPORTS + NS_DECL_NSICONTENTPERMISSIONTYPE - bool IsBeingDestroyed(); + ContentPermissionType(const nsACString& aType, const nsACString& aAccess); + virtual ~ContentPermissionType(); - nsCOMPtr<nsIPrincipal> mPrincipal; - nsCOMPtr<Element> mElement; - nsCOMPtr<nsContentPermissionRequestProxy> mProxy; +protected: nsCString mType; nsCString mAccess; +}; - private: - virtual bool Recvprompt(); - virtual void ActorDestroy(ActorDestroyReason why); -}; +uint32_t ConvertPermissionRequestToArray(nsTArray<PermissionRequest>& aSrcArray, + nsIMutableArray* aDesArray); + +nsresult CreatePermissionArray(const nsACString& aType, + const nsACString& aAccess, + nsIArray** aTypesArray); + +PContentPermissionRequestParent* +CreateContentPermissionRequestParent(const nsTArray<PermissionRequest>& aRequests, + Element* element, + const IPC::Principal& principal); } // namespace dom } // namespace mozilla class nsContentPermissionRequestProxy : public nsIContentPermissionRequest { public: + NS_DECL_ISUPPORTS + NS_DECL_NSICONTENTPERMISSIONREQUEST + nsContentPermissionRequestProxy(); virtual ~nsContentPermissionRequestProxy(); - nsresult Init(const nsACString& type, const nsACString& access, mozilla::dom::ContentPermissionRequestParent* parent); + nsresult Init(const nsTArray<mozilla::dom::PermissionRequest>& requests, + mozilla::dom::ContentPermissionRequestParent* parent); void OnParentDestroyed(); - NS_DECL_ISUPPORTS - NS_DECL_NSICONTENTPERMISSIONREQUEST - private: // Non-owning pointer to the ContentPermissionRequestParent object which owns this proxy. mozilla::dom::ContentPermissionRequestParent* mParent; - nsCString mType; - nsCString mAccess; + nsTArray<mozilla::dom::PermissionRequest> mPermissionRequests; }; + #endif // nsContentPermissionHelper_h -
--- a/dom/devicestorage/nsDeviceStorage.cpp +++ b/dom/devicestorage/nsDeviceStorage.cpp @@ -46,16 +46,17 @@ #include "GeneratedEvents.h" #include "nsIMIMEService.h" #include "nsCExternalHandlerService.h" #include "nsIPermissionManager.h" #include "nsIStringBundle.h" #include "nsIDocument.h" #include <algorithm> #include "private/pprio.h" +#include "nsContentPermissionHelper.h" #include "mozilla/dom/DeviceStorageBinding.h" // Microsoft's API Name hackery sucks #undef CreateEvent #ifdef MOZ_WIDGET_GONK #include "nsIVolume.h" @@ -1766,27 +1767,24 @@ nsDOMDeviceStorageCursor::~nsDOMDeviceSt void nsDOMDeviceStorageCursor::GetStorageType(nsAString & aType) { aType = mFile->mStorageType; } NS_IMETHODIMP -nsDOMDeviceStorageCursor::GetType(nsACString & aType) +nsDOMDeviceStorageCursor::GetTypes(nsIArray** aTypes) { - return DeviceStorageTypeChecker::GetPermissionForType(mFile->mStorageType, - aType); -} - -NS_IMETHODIMP -nsDOMDeviceStorageCursor::GetAccess(nsACString & aAccess) -{ - aAccess = NS_LITERAL_CSTRING("read"); - return NS_OK; + nsCString type; + nsresult rv = + DeviceStorageTypeChecker::GetPermissionForType(mFile->mStorageType, type); + NS_ENSURE_SUCCESS(rv, rv); + + return CreatePermissionArray(type, NS_LITERAL_CSTRING("read"), aTypes); } NS_IMETHODIMP nsDOMDeviceStorageCursor::GetPrincipal(nsIPrincipal * *aRequestingPrincipal) { NS_IF_ADDREF(*aRequestingPrincipal = mPrincipal); return NS_OK; } @@ -2381,51 +2379,50 @@ public: return rv; } nsCString access; rv = DeviceStorageTypeChecker::GetAccessForRequest( DeviceStorageRequestType(mRequestType), access); if (NS_FAILED(rv)) { return rv; } + nsTArray<PermissionRequest> permArray; + permArray.AppendElement(PermissionRequest(type, access)); child->SendPContentPermissionRequestConstructor( - this, type, access, IPC::Principal(mPrincipal)); + this, permArray, IPC::Principal(mPrincipal)); Sendprompt(); return NS_OK; } nsCOMPtr<nsIContentPermissionPrompt> prompt = do_CreateInstance(NS_CONTENT_PERMISSION_PROMPT_CONTRACTID); if (prompt) { prompt->Prompt(this); } return NS_OK; } - NS_IMETHOD GetType(nsACString & aType) + NS_IMETHODIMP GetTypes(nsIArray** aTypes) { nsCString type; - nsresult rv - = DeviceStorageTypeChecker::GetPermissionForType(mFile->mStorageType, - aType); + nsresult rv = + DeviceStorageTypeChecker::GetPermissionForType(mFile->mStorageType, type); if (NS_FAILED(rv)) { return rv; } - return NS_OK; - } - - NS_IMETHOD GetAccess(nsACString & aAccess) - { - nsresult rv = DeviceStorageTypeChecker::GetAccessForRequest( - DeviceStorageRequestType(mRequestType), aAccess); + + nsCString access; + rv = DeviceStorageTypeChecker::GetAccessForRequest( + DeviceStorageRequestType(mRequestType), access); if (NS_FAILED(rv)) { return rv; } - return NS_OK; + + return CreatePermissionArray(type, access, aTypes); } NS_IMETHOD GetPrincipal(nsIPrincipal * *aRequestingPrincipal) { NS_IF_ADDREF(*aRequestingPrincipal = mPrincipal); return NS_OK; } @@ -3552,18 +3549,20 @@ nsDOMDeviceStorage::EnumerateInternal(co // Corresponding release occurs in DeallocPContentPermissionRequest. r->AddRef(); nsCString type; aRv = DeviceStorageTypeChecker::GetPermissionForType(mStorageType, type); if (aRv.Failed()) { return nullptr; } - child->SendPContentPermissionRequestConstructor(r, type, - NS_LITERAL_CSTRING("read"), + nsTArray<PermissionRequest> permArray; + permArray.AppendElement(PermissionRequest(type, NS_LITERAL_CSTRING("read"))); + child->SendPContentPermissionRequestConstructor(r, + permArray, IPC::Principal(mPrincipal)); r->Sendprompt(); return cursor.forget(); } nsCOMPtr<nsIContentPermissionPrompt> prompt
--- a/dom/interfaces/base/nsIContentPermissionPrompt.idl +++ b/dom/interfaces/base/nsIContentPermissionPrompt.idl @@ -2,38 +2,50 @@ * 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 "nsISupports.idl" interface nsIPrincipal; interface nsIDOMWindow; interface nsIDOMElement; +interface nsIArray; /** - * Interface allows access to a content to request - * permission to perform a privileged operation such as - * geolocation. + * Interface provides the request type and its access. */ -[scriptable, uuid(1de67000-2de8-11e2-81c1-0800200c9a66)] -interface nsIContentPermissionRequest : nsISupports { - +[scriptable, uuid(384b6cc4-a66b-4bea-98e0-eb10562a9ba4)] +interface nsIContentPermissionType : nsISupports { /** * The type of the permission request, such as * "geolocation". */ readonly attribute ACString type; /** * The access of the permission request, such as * "read". */ readonly attribute ACString access; +}; +/** + * Interface allows access to a content to request + * permission to perform a privileged operation such as + * geolocation. + */ +[scriptable, uuid(69a39d88-d1c4-4ba9-9b19-bafc7a1bb783)] +interface nsIContentPermissionRequest : nsISupports { /** + * The array will include the request types. Elements of this array are + * nsIContentPermissionType object. + */ + readonly attribute nsIArray types; + + /* * The principal of the permission request. */ readonly attribute nsIPrincipal principal; /** * The window or element that the permission request was * originated in. Typically the element will be non-null * in when using out of process content. window or
--- a/dom/ipc/PBrowser.ipdl +++ b/dom/ipc/PBrowser.ipdl @@ -11,16 +11,17 @@ include protocol PContentDialog; include protocol PDocumentRenderer; include protocol PContentPermissionRequest; include protocol PRenderFrame; include protocol POfflineCacheUpdate; include protocol PIndexedDB; include DOMTypes; include JavaScriptTypes; include URIParams; +include PContentPermission; using class IPC::Principal from "mozilla/dom/PermissionMessageUtils.h"; using struct mozilla::gfx::Matrix from "mozilla/gfx/Matrix.h"; using struct gfxSize from "gfxPoint.h"; using CSSRect from "Units.h"; using struct mozilla::layers::FrameMetrics from "FrameMetrics.h"; using struct mozilla::layers::ScrollableLayerGuid from "FrameMetrics.h"; @@ -217,28 +218,26 @@ parent: */ ShowTooltip(uint32_t x, uint32_t y, nsString tooltip); HideTooltip(); /** * Initiates an asynchronous request for permission for the * provided principal. * - * @param aType - * The type of permission to request. - * @param aAccess - * Access type. "read" for example. + * @param aRequests + * The array of permissions to request. * @param aPrincipal * The principal of the request. * * NOTE: The principal is untrusted in the parent process. Only * principals that can live in the content process should * provided. */ - PContentPermissionRequest(nsCString aType, nsCString aAccess, Principal principal); + PContentPermissionRequest(PermissionRequest[] aRequests, Principal aPrincipal); PContentDialog(uint32_t aType, nsCString aName, nsCString aFeatures, int32_t[] aIntParams, nsString[] aStringParams); /** * Create a layout frame (encapsulating a remote layer tree) for * the page that is currently loaded in the <browser>. */
new file mode 100644 --- /dev/null +++ b/dom/ipc/PContentPermission.ipdlh @@ -0,0 +1,14 @@ +/* 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/. */ + +namespace mozilla { +namespace dom { + +struct PermissionRequest { + nsCString type; + nsCString access; +}; + +} // namespace dom +} // namespace mozilla
--- a/dom/ipc/TabChild.cpp +++ b/dom/ipc/TabChild.cpp @@ -1123,22 +1123,21 @@ TabChild::ArraysToParams(const Infallibl aParams->SetString(j, aStringParams[j].get()); } } } #ifdef DEBUG PContentPermissionRequestChild* TabChild:: SendPContentPermissionRequestConstructor(PContentPermissionRequestChild* aActor, - const nsCString& aType, - const nsCString& aAccess, + const InfallibleTArray<PermissionRequest>& aRequests, const IPC::Principal& aPrincipal) { PCOMContentPermissionRequestChild* child = static_cast<PCOMContentPermissionRequestChild*>(aActor); - PContentPermissionRequestChild* request = PBrowserChild::SendPContentPermissionRequestConstructor(aActor, aType, aAccess, aPrincipal); + PContentPermissionRequestChild* request = PBrowserChild::SendPContentPermissionRequestConstructor(aActor, aRequests, aPrincipal); child->mIPCOpen = true; return request; } #endif /* DEBUG */ void TabChild::DestroyWindow() { @@ -2061,17 +2060,18 @@ TabChild::AllocPContentDialogChild(const bool TabChild::DeallocPContentDialogChild(PContentDialogChild* aDialog) { delete aDialog; return true; } PContentPermissionRequestChild* -TabChild::AllocPContentPermissionRequestChild(const nsCString& aType, const nsCString& aAccess, const IPC::Principal&) +TabChild::AllocPContentPermissionRequestChild(const InfallibleTArray<PermissionRequest>& aRequests, + const IPC::Principal& aPrincipal) { NS_RUNTIMEABORT("unused"); return nullptr; } bool TabChild::DeallocPContentPermissionRequestChild(PContentPermissionRequestChild* actor) {
--- a/dom/ipc/TabChild.h +++ b/dom/ipc/TabChild.h @@ -290,24 +290,22 @@ public: InfallibleTArray<nsString>& aStringParams); static void ArraysToParams(const InfallibleTArray<int>& aIntParams, const InfallibleTArray<nsString>& aStringParams, nsIDialogParamBlock* aParams); #ifdef DEBUG virtual PContentPermissionRequestChild* SendPContentPermissionRequestConstructor(PContentPermissionRequestChild* aActor, - const nsCString& aType, - const nsCString& aAccess, + const InfallibleTArray<PermissionRequest>& aRequests, const IPC::Principal& aPrincipal); #endif /* DEBUG */ virtual PContentPermissionRequestChild* - AllocPContentPermissionRequestChild(const nsCString& aType, - const nsCString& aAccess, + AllocPContentPermissionRequestChild(const InfallibleTArray<PermissionRequest>& aRequests, const IPC::Principal& aPrincipal) MOZ_OVERRIDE; virtual bool DeallocPContentPermissionRequestChild(PContentPermissionRequestChild* actor) MOZ_OVERRIDE; virtual POfflineCacheUpdateChild* AllocPOfflineCacheUpdateChild( const URIParams& manifestURI, const URIParams& documentURI, const bool& stickDocument) MOZ_OVERRIDE;
--- a/dom/ipc/TabParent.cpp +++ b/dom/ipc/TabParent.cpp @@ -10,16 +10,17 @@ #include "AppProcessChecker.h" #include "IDBFactory.h" #include "IndexedDBParent.h" #include "mozIApplication.h" #include "mozilla/BrowserElementParent.h" #include "mozilla/docshell/OfflineCacheUpdateParent.h" #include "mozilla/dom/ContentParent.h" +#include "mozilla/dom/PContentPermissionRequestParent.h" #include "mozilla/Hal.h" #include "mozilla/ipc/DocumentRendererParent.h" #include "mozilla/layers/CompositorParent.h" #include "mozilla/layout/RenderFrameParent.h" #include "mozilla/MouseEvents.h" #include "mozilla/Preferences.h" #include "mozilla/TextEvents.h" #include "mozilla/TouchEvents.h" @@ -618,19 +619,20 @@ TabParent::AllocPDocumentRendererParent( bool TabParent::DeallocPDocumentRendererParent(PDocumentRendererParent* actor) { delete actor; return true; } PContentPermissionRequestParent* -TabParent::AllocPContentPermissionRequestParent(const nsCString& type, const nsCString& access, const IPC::Principal& principal) +TabParent::AllocPContentPermissionRequestParent(const InfallibleTArray<PermissionRequest>& aRequests, + const IPC::Principal& aPrincipal) { - return new ContentPermissionRequestParent(type, access, mFrameElement, principal); + return CreateContentPermissionRequestParent(aRequests, mFrameElement, aPrincipal); } bool TabParent::DeallocPContentPermissionRequestParent(PContentPermissionRequestParent* actor) { delete actor; return true; }
--- a/dom/ipc/TabParent.h +++ b/dom/ipc/TabParent.h @@ -248,18 +248,17 @@ public: const gfx::Matrix& transform, const nsString& bgcolor, const uint32_t& renderFlags, const bool& flushLayout, const nsIntSize& renderSize) MOZ_OVERRIDE; virtual bool DeallocPDocumentRendererParent(PDocumentRendererParent* actor) MOZ_OVERRIDE; virtual PContentPermissionRequestParent* - AllocPContentPermissionRequestParent(const nsCString& aType, - const nsCString& aAccess, + AllocPContentPermissionRequestParent(const InfallibleTArray<PermissionRequest>& aRequests, const IPC::Principal& aPrincipal) MOZ_OVERRIDE; virtual bool DeallocPContentPermissionRequestParent(PContentPermissionRequestParent* actor) MOZ_OVERRIDE; virtual POfflineCacheUpdateParent* AllocPOfflineCacheUpdateParent(const URIParams& aManifestURI, const URIParams& aDocumentURI, const bool& aStickDocument) MOZ_OVERRIDE;
--- a/dom/ipc/moz.build +++ b/dom/ipc/moz.build @@ -63,16 +63,17 @@ SOURCES += [ IPDL_SOURCES += [ 'DOMTypes.ipdlh', 'PBlob.ipdl', 'PBlobStream.ipdl', 'PBrowser.ipdl', 'PContent.ipdl', 'PContentDialog.ipdl', + 'PContentPermission.ipdlh', 'PContentPermissionRequest.ipdl', 'PCrashReporter.ipdl', 'PDocumentRenderer.ipdl', 'PMemoryReportRequest.ipdl', 'PTabContext.ipdlh', ] FAIL_ON_WARNINGS = True
--- a/dom/media/MediaManager.cpp +++ b/dom/media/MediaManager.cpp @@ -36,17 +36,17 @@ #include "mozilla/Preferences.h" /* Using WebRTC backend on Desktops (Mac, Windows, Linux), otherwise default */ #include "MediaEngineDefault.h" #if defined(MOZ_WEBRTC) #include "MediaEngineWebRTC.h" #endif -#ifdef MOZ_WIDGET_GONK +#ifdef MOZ_B2G #include "MediaPermissionGonk.h" #endif // GetCurrentTime is defined in winbase.h as zero argument macro forwarding to // GetTickCount() and conflicts with MediaStream::GetCurrentTime. #ifdef GetCurrentTime #undef GetCurrentTime #endif @@ -775,17 +775,17 @@ public: , mSuccess(nullptr) , mError(nullptr) , mSuccessHolder(aSuccess) , mErrorHolder(aError) , mWindowID(aWindowID) , mListener(aListener) , mPrefs(aPrefs) , mDeviceChosen(false) - , mBackendChosen(false) + , mBackend(nullptr) , mManager(MediaManager::GetInstance()) {} /** * The caller can also choose to provide their own backend instead of * using the one provided by MediaManager::GetBackend. */ GetUserMediaRunnable( @@ -799,25 +799,21 @@ public: , mSuccess(nullptr) , mError(nullptr) , mSuccessHolder(aSuccess) , mErrorHolder(aError) , mWindowID(aWindowID) , mListener(aListener) , mPrefs(aPrefs) , mDeviceChosen(false) - , mBackendChosen(true) , mBackend(aBackend) , mManager(MediaManager::GetInstance()) {} ~GetUserMediaRunnable() { - if (mBackendChosen) { - delete mBackend; - } } /** * Once "armed", GetUserMediaRunnable will leak its JS callbacks if destroyed. * Arm() must be called before the runnable can be dispatched or used, and the * runnable must be dispatched or used once armed. Callbacks get released on * the main thread at the runnable's completion. */ @@ -829,24 +825,25 @@ public: NS_IMETHOD Run() { NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread"); MOZ_ASSERT(mSuccess.mRawPtr); MOZ_ASSERT(mError.mRawPtr); + MediaEngine* backend = mBackend; // Was a backend provided? - if (!mBackendChosen) { - mBackend = mManager->GetBackend(mWindowID); + if (!backend) { + backend = mManager->GetBackend(mWindowID); } // Was a device provided? if (!mDeviceChosen) { - nsresult rv = SelectDevice(); + nsresult rv = SelectDevice(backend); if (rv != NS_OK) { return rv; } } // It is an error if audio or video are requested along with picture. if (mConstraints.mPicture && (mConstraints.mAudio || mConstraints.mVideo)) { NS_DispatchToMainThread(new ErrorCallbackRunnable( @@ -912,36 +909,36 @@ public: SetVideoDevice(MediaDevice* aVideoDevice) { mVideoDevice = aVideoDevice; mDeviceChosen = true; return NS_OK; } nsresult - SelectDevice() + SelectDevice(MediaEngine* backend) { MOZ_ASSERT(mSuccess.mRawPtr); MOZ_ASSERT(mError.mRawPtr); if (mConstraints.mPicture || mConstraints.mVideo) { - ScopedDeletePtr<SourceSet> sources (GetSources(mBackend, + ScopedDeletePtr<SourceSet> sources (GetSources(backend, mConstraints.mVideom, &MediaEngine::EnumerateVideoDevices)); if (!sources->Length()) { NS_DispatchToMainThread(new ErrorCallbackRunnable( mSuccess, mError, NS_LITERAL_STRING("NO_DEVICES_FOUND"), mWindowID)); return NS_ERROR_FAILURE; } // Pick the first available device. mVideoDevice = do_QueryObject((*sources)[0]); LOG(("Selected video device")); } if (mConstraints.mAudio) { - ScopedDeletePtr<SourceSet> sources (GetSources(mBackend, + ScopedDeletePtr<SourceSet> sources (GetSources(backend, mConstraints.mAudiom, &MediaEngine::EnumerateAudioDevices)); if (!sources->Length()) { NS_DispatchToMainThread(new ErrorCallbackRunnable( mSuccess, mError, NS_LITERAL_STRING("NO_DEVICES_FOUND"), mWindowID)); return NS_ERROR_FAILURE; } // Pick the first available device. @@ -1031,19 +1028,18 @@ private: nsCOMPtr<nsIDOMGetUserMediaErrorCallback> mErrorHolder; uint64_t mWindowID; nsRefPtr<GetUserMediaCallbackMediaStreamListener> mListener; nsRefPtr<MediaDevice> mAudioDevice; nsRefPtr<MediaDevice> mVideoDevice; MediaEnginePrefs mPrefs; bool mDeviceChosen; - bool mBackendChosen; - MediaEngine* mBackend; + RefPtr<MediaEngine> mBackend; nsRefPtr<MediaManager> mManager; // get ref to this when creating the runnable }; /** * Similar to GetUserMediaRunnable, but used for the chrome-only * GetUserMediaDevices function. Enumerates a list of audio & video devices, * wraps them up in nsIMediaDevice objects and returns it to the success * callback. @@ -1323,20 +1319,20 @@ MediaManager::GetUserMedia(JSContext* aC } #endif static bool created = false; if (!created) { // Force MediaManager to startup before we try to access it from other threads // Hack: should init singleton earlier unless it's expensive (mem or CPU) (void) MediaManager::Get(); -#ifdef MOZ_WIDGET_GONK +#ifdef MOZ_B2G // Initialize MediaPermissionManager before send out any permission request. (void) MediaPermissionManager::GetInstance(); -#endif //MOZ_WIDGET_GONK +#endif //MOZ_B2G } // Store the WindowID in a hash table and mark as active. The entry is removed // when this window is closed or navigated away from. uint64_t windowID = aWindow->WindowID(); // This is safe since we're on main-thread, and the windowlist can only // be invalidated from the main-thread (see OnNavigation) StreamListeners* listeners = GetActiveWindows()->Get(windowID);
--- a/dom/media/MediaManager.h +++ b/dom/media/MediaManager.h @@ -509,36 +509,34 @@ private: const char *aData, int32_t *aVal); void GetPrefBool(nsIPrefBranch *aBranch, const char *aPref, const char *aData, bool *aVal); void GetPrefs(nsIPrefBranch *aBranch, const char *aData); // Make private because we want only one instance of this class MediaManager(); - ~MediaManager() { - delete mBackend; - } + ~MediaManager() {} nsresult MediaCaptureWindowStateInternal(nsIDOMWindow* aWindow, bool* aVideo, bool* aAudio); void StopMediaStreams(); // ONLY access from MainThread so we don't need to lock WindowTable mActiveWindows; nsRefPtrHashtable<nsStringHashKey, GetUserMediaRunnable> mActiveCallbacks; nsClassHashtable<nsUint64HashKey, nsTArray<nsString>> mCallIds; // Always exists nsCOMPtr<nsIThread> mMediaThread; Mutex mMutex; // protected with mMutex: - MediaEngine* mBackend; + RefPtr<MediaEngine> mBackend; static StaticRefPtr<MediaManager> sSingleton; -#ifdef MOZ_WIDGET_GONK +#ifdef MOZ_B2G_CAMERA nsRefPtr<nsDOMCameraManager> mCameraManager; #endif }; } // namespace mozilla
--- a/dom/media/MediaPermissionGonk.cpp +++ b/dom/media/MediaPermissionGonk.cpp @@ -15,24 +15,46 @@ #include "nsTArray.h" #include "GetUserMediaRequest.h" #include "PCOMContentPermissionRequestChild.h" #include "mozilla/dom/PBrowserChild.h" #include "mozilla/dom/TabChild.h" #include "mozilla/dom/MediaStreamTrackBinding.h" #include "nsISupportsPrimitives.h" #include "nsServiceManagerUtils.h" +#include "nsArrayUtils.h" +#include "nsContentPermissionHelper.h" #include "mozilla/dom/PermissionMessageUtils.h" #define AUDIO_PERMISSION_NAME "audio-capture" +#define VIDEO_PERMISSION_NAME "video-capture" + +using namespace mozilla::dom; namespace mozilla { static MediaPermissionManager *gMediaPermMgr = nullptr; +static uint32_t +ConvertArrayToPermissionRequest(nsIArray* aSrcArray, + nsTArray<PermissionRequest>& aDesArray) +{ + uint32_t len = 0; + aSrcArray->GetLength(&len); + for (uint32_t i = 0; i < len; i++) { + nsCOMPtr<nsIContentPermissionType> cpt = do_QueryElementAt(aSrcArray, i); + nsAutoCString type; + nsAutoCString access; + cpt->GetType(type); + cpt->GetAccess(access); + aDesArray.AppendElement(PermissionRequest(type, access)); + } + return len; +} + // Helper function for notifying permission granted static nsresult NotifyPermissionAllow(const nsAString &aCallID, nsTArray<nsCOMPtr<nsIMediaDevice> > &aDevices) { nsresult rv; nsCOMPtr<nsISupportsArray> array; rv = NS_NewISupportsArray(getter_AddRefs(array)); NS_ENSURE_SUCCESS(rv, rv); @@ -88,77 +110,85 @@ public: // It will be called when prompt dismissed. virtual bool Recv__delete__(const bool &allow) MOZ_OVERRIDE; virtual void IPDLRelease() MOZ_OVERRIDE { Release(); } already_AddRefed<nsPIDOMWindow> GetOwner(); private: bool mAudio; // Request for audio permission + bool mVideo; // Request for video permission nsRefPtr<dom::GetUserMediaRequest> mRequest; nsTArray<nsCOMPtr<nsIMediaDevice> > mDevices; // candiate device list }; // MediaPermissionRequest NS_IMPL_ISUPPORTS1(MediaPermissionRequest, nsIContentPermissionRequest) MediaPermissionRequest::MediaPermissionRequest(nsRefPtr<dom::GetUserMediaRequest> &aRequest, nsTArray<nsCOMPtr<nsIMediaDevice> > &aDevices) : mRequest(aRequest) { dom::MediaStreamConstraintsInternal constraints; mRequest->GetConstraints(constraints); mAudio = constraints.mAudio; + mVideo = constraints.mVideo; for (uint32_t i = 0; i < aDevices.Length(); ++i) { nsCOMPtr<nsIMediaDevice> device(aDevices[i]); nsAutoString deviceType; device->GetType(deviceType); if (mAudio && deviceType.EqualsLiteral("audio")) { mDevices.AppendElement(device); } + if (mVideo && deviceType.EqualsLiteral("video")) { + mDevices.AppendElement(device); + } } } // nsIContentPermissionRequest methods NS_IMETHODIMP +MediaPermissionRequest::GetTypes(nsIArray** aTypes) +{ + nsCOMPtr<nsIMutableArray> types = do_CreateInstance(NS_ARRAY_CONTRACTID); + if (mAudio) { + nsCOMPtr<ContentPermissionType> AudioType = + new ContentPermissionType(NS_LITERAL_CSTRING(AUDIO_PERMISSION_NAME), + NS_LITERAL_CSTRING("unused")); + types->AppendElement(AudioType, false); + } + if (mVideo) { + nsCOMPtr<ContentPermissionType> VideoType = + new ContentPermissionType(NS_LITERAL_CSTRING(VIDEO_PERMISSION_NAME), + NS_LITERAL_CSTRING("unused")); + types->AppendElement(VideoType, false); + } + NS_IF_ADDREF(*aTypes = types); + + return NS_OK; +} + +NS_IMETHODIMP MediaPermissionRequest::GetPrincipal(nsIPrincipal **aRequestingPrincipal) { NS_ENSURE_ARG_POINTER(aRequestingPrincipal); nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(mRequest->GetParentObject()); NS_ENSURE_TRUE(window, NS_ERROR_FAILURE); nsCOMPtr<nsIDocument> doc = window->GetExtantDoc(); NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE); NS_ADDREF(*aRequestingPrincipal = doc->NodePrincipal()); return NS_OK; } NS_IMETHODIMP -MediaPermissionRequest::GetType(nsACString &aType) -{ - if (mAudio) { - aType = AUDIO_PERMISSION_NAME; - return NS_OK; - } - - return NS_OK; -} - -NS_IMETHODIMP -MediaPermissionRequest::GetAccess(nsACString &aAccess) -{ - aAccess = "unused"; - return NS_OK; -} - -NS_IMETHODIMP MediaPermissionRequest::GetWindow(nsIDOMWindow** aRequestingWindow) { NS_ENSURE_ARG_POINTER(aRequestingWindow); nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(mRequest->GetParentObject()); window.forget(aRequestingWindow); return NS_OK; } @@ -273,32 +303,30 @@ MediaDeviceSuccessCallback::DoPrompt(nsR nsresult rv; nsCOMPtr<nsPIDOMWindow> window(req->GetOwner()); NS_ENSURE_TRUE(window, NS_ERROR_FAILURE); dom::TabChild* child = dom::TabChild::GetFrom(window->GetDocShell()); NS_ENSURE_TRUE(child, NS_ERROR_FAILURE); - nsAutoCString type; - rv = req->GetType(type); + nsCOMPtr<nsIArray> typeArray; + rv = req->GetTypes(getter_AddRefs(typeArray)); NS_ENSURE_SUCCESS(rv, rv); - nsAutoCString access; - rv = req->GetAccess(access); - NS_ENSURE_SUCCESS(rv, rv); + nsTArray<PermissionRequest> permArray; + ConvertArrayToPermissionRequest(typeArray, permArray); nsCOMPtr<nsIPrincipal> principal; rv = req->GetPrincipal(getter_AddRefs(principal)); NS_ENSURE_SUCCESS(rv, rv); req->AddRef(); child->SendPContentPermissionRequestConstructor(req, - type, - access, + permArray, IPC::Principal(principal)); req->Sendprompt(); return NS_OK; } // for chrome process nsCOMPtr<nsIContentPermissionPrompt> prompt =
--- a/dom/permission/PermissionPromptHelper.jsm +++ b/dom/permission/PermissionPromptHelper.jsm @@ -69,20 +69,28 @@ this.PermissionPromptHelper = { if (permValue == Ci.nsIPermissionManager.DENY_ACTION || permValue == Ci.nsIPermissionManager.UNKNOWN_ACTION) { aCallbacks.cancel(); return; } if (permValue == Ci.nsIPermissionManager.PROMPT_ACTION) { + // create an array with a nsIContentPermissionType element + let type = { + type: msg.type, + access: msg.access ? msg.access : "unused", + QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentPermissionType]) + }; + let typeArray = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray); + typeArray.appendElement(type, false); + // create a nsIContentPermissionRequest let request = { - type: msg.type, - access: msg.access ? msg.access : "unused", + types: typeArray, principal: principal, QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentPermissionRequest]), allow: aCallbacks.allow, cancel: aCallbacks.cancel, window: Services.wm.getOuterWindowWithId(msg.windowID) }; permissionPromptService.getPermission(request);
--- a/dom/permission/PermissionPromptService.js +++ b/dom/permission/PermissionPromptService.js @@ -63,18 +63,26 @@ PermissionPromptService.prototype = { **/ getPermission: function PS_getPermission(aRequest) { if (!(aRequest instanceof Ci.nsIContentPermissionRequest)) { throw new Error("PermissionService.getPermission: " + "2nd argument must be type 'nsIContentPermissionRequest'"); } - let type = aRequest.access !== "unused" ? aRequest.type + "-" + aRequest.access - : aRequest.type; + // Only allow exactly one permission request here. + let types = aRequest.types.QueryInterface(Ci.nsIArray); + if (types.length != 1) { + aRequest.cancel(); + return; + } + let reqType = types.queryElementAt(0, Ci.nsIContentPermissionType); + + let type = reqType.access !== "unused" ? reqType.type + "-" + reqType.access + : reqType.type; let perm = permissionManager.testExactPermissionFromPrincipal(aRequest.principal, type); switch (perm) { case Ci.nsIPermissionManager.ALLOW_ACTION: aRequest.allow(); break; case Ci.nsIPermissionManager.PROMPT_ACTION:
--- a/dom/src/geolocation/nsGeolocation.cpp +++ b/dom/src/geolocation/nsGeolocation.cpp @@ -374,27 +374,21 @@ nsGeolocationRequest::GetPrincipal(nsIPr nsCOMPtr<nsIPrincipal> principal = mLocator->GetPrincipal(); principal.forget(aRequestingPrincipal); return NS_OK; } NS_IMETHODIMP -nsGeolocationRequest::GetType(nsACString & aType) +nsGeolocationRequest::GetTypes(nsIArray** aTypes) { - aType = "geolocation"; - return NS_OK; -} - -NS_IMETHODIMP -nsGeolocationRequest::GetAccess(nsACString & aAccess) -{ - aAccess = "unused"; - return NS_OK; + return CreatePermissionArray(NS_LITERAL_CSTRING("geolocation"), + NS_LITERAL_CSTRING("unused"), + aTypes); } NS_IMETHODIMP nsGeolocationRequest::GetWindow(nsIDOMWindow * *aRequestingWindow) { NS_ENSURE_ARG_POINTER(aRequestingWindow); nsCOMPtr<nsIDOMWindow> window = do_QueryReferent(mLocator->GetOwner()); @@ -1472,22 +1466,25 @@ Geolocation::RegisterRequestWithPrompt(n // because owner implements nsITabChild, we can assume that it is // the one and only TabChild. TabChild* child = TabChild::GetFrom(window->GetDocShell()); if (!child) { return false; } + nsTArray<PermissionRequest> permArray; + permArray.AppendElement(PermissionRequest(NS_LITERAL_CSTRING("geolocation"), + NS_LITERAL_CSTRING("unused"))); + // Retain a reference so the object isn't deleted without IPDL's knowledge. // Corresponding release occurs in DeallocPContentPermissionRequest. request->AddRef(); child->SendPContentPermissionRequestConstructor(request, - NS_LITERAL_CSTRING("geolocation"), - NS_LITERAL_CSTRING("unused"), + permArray, IPC::Principal(mPrincipal)); request->Sendprompt(); return true; } nsCOMPtr<nsIRunnable> ev = new RequestPromptEvent(request); NS_DispatchToMainThread(ev);
--- a/dom/src/notification/DesktopNotification.cpp +++ b/dom/src/notification/DesktopNotification.cpp @@ -10,16 +10,17 @@ #include "nsIDOMDesktopNotification.h" #include "TabChild.h" #include "mozilla/Preferences.h" #include "nsGlobalWindow.h" #include "nsIAppsService.h" #include "PCOMContentPermissionRequestChild.h" #include "nsIScriptSecurityManager.h" #include "nsServiceManagerUtils.h" +#include "PermissionMessageUtils.h" namespace mozilla { namespace dom { /* * Simple Request */ class DesktopNotificationRequest : public nsIContentPermissionRequest, @@ -172,19 +173,22 @@ DesktopNotification::Init() // because owner implements nsITabChild, we can assume that it is // the one and only TabChild for this docshell. TabChild* child = TabChild::GetFrom(GetOwner()->GetDocShell()); // Retain a reference so the object isn't deleted without IPDL's knowledge. // Corresponding release occurs in DeallocPContentPermissionRequest. nsRefPtr<DesktopNotificationRequest> copy = request; + nsTArray<PermissionRequest> permArray; + permArray.AppendElement(PermissionRequest( + NS_LITERAL_CSTRING("desktop-notification"), + NS_LITERAL_CSTRING("unused"))); child->SendPContentPermissionRequestConstructor(copy.forget().get(), - NS_LITERAL_CSTRING("desktop-notification"), - NS_LITERAL_CSTRING("unused"), + permArray, IPC::Principal(mPrincipal)); request->Sendprompt(); return; } // otherwise, dispatch it NS_DispatchToMainThread(request); @@ -346,23 +350,17 @@ NS_IMETHODIMP DesktopNotificationRequest::Allow() { nsresult rv = mDesktopNotification->SetAllow(true); mDesktopNotification = nullptr; return rv; } NS_IMETHODIMP -DesktopNotificationRequest::GetType(nsACString & aType) +DesktopNotificationRequest::GetTypes(nsIArray** aTypes) { - aType = "desktop-notification"; - return NS_OK; -} - -NS_IMETHODIMP -DesktopNotificationRequest::GetAccess(nsACString & aAccess) -{ - aAccess = "unused"; - return NS_OK; + return CreatePermissionArray(NS_LITERAL_CSTRING("desktop-notification"), + NS_LITERAL_CSTRING("unused"), + aTypes); } } // namespace dom } // namespace mozilla
--- a/dom/src/notification/Notification.cpp +++ b/dom/src/notification/Notification.cpp @@ -19,16 +19,17 @@ #include "nsIPermissionManager.h" #include "nsIUUIDGenerator.h" #include "nsServiceManagerUtils.h" #include "nsToolkitCompsCID.h" #include "nsGlobalWindow.h" #include "nsDOMJSUtils.h" #include "nsIScriptSecurityManager.h" #include "mozilla/dom/PermissionMessageUtils.h" +#include "nsContentPermissionHelper.h" #ifdef MOZ_B2G #include "nsIDOMDesktopNotification.h" #endif namespace mozilla { namespace dom { class NotificationStorageCallback MOZ_FINAL : public nsINotificationStorageCallback @@ -262,19 +263,21 @@ NotificationPermissionRequest::Run() if (!child) { return NS_ERROR_NOT_AVAILABLE; } // Retain a reference so the object isn't deleted without IPDL's knowledge. // Corresponding release occurs in DeallocPContentPermissionRequest. AddRef(); - NS_NAMED_LITERAL_CSTRING(type, "desktop-notification"); - NS_NAMED_LITERAL_CSTRING(access, "unused"); - child->SendPContentPermissionRequestConstructor(this, type, access, + nsTArray<PermissionRequest> permArray; + permArray.AppendElement(PermissionRequest( + NS_LITERAL_CSTRING("desktop-notification"), + NS_LITERAL_CSTRING("unused"))); + child->SendPContentPermissionRequestConstructor(this, permArray, IPC::Principal(mPrincipal)); Sendprompt(); return NS_OK; } nsCOMPtr<nsIContentPermissionPrompt> prompt = do_GetService(NS_CONTENT_PERMISSION_PROMPT_CONTRACTID); @@ -337,27 +340,21 @@ nsresult NotificationPermissionRequest::CallCallback() { ErrorResult rv; mCallback->Call(mPermission, rv); return rv.ErrorCode(); } NS_IMETHODIMP -NotificationPermissionRequest::GetAccess(nsACString& aAccess) +NotificationPermissionRequest::GetTypes(nsIArray** aTypes) { - aAccess.AssignLiteral("unused"); - return NS_OK; -} - -NS_IMETHODIMP -NotificationPermissionRequest::GetType(nsACString& aType) -{ - aType.AssignLiteral("desktop-notification"); - return NS_OK; + return CreatePermissionArray(NS_LITERAL_CSTRING("desktop-notification"), + NS_LITERAL_CSTRING("unused"), + aTypes); } bool NotificationPermissionRequest::Recv__delete__(const bool& aAllow) { if (aAllow) { (void) Allow(); } else {
--- a/mobile/android/components/ContentPermissionPrompt.js +++ b/mobile/android/components/ContentPermissionPrompt.js @@ -16,28 +16,28 @@ const kEntities = { "geolocation": "geol function ContentPermissionPrompt() {} ContentPermissionPrompt.prototype = { classID: Components.ID("{C6E8C44D-9F39-4AF7-BCC0-76E38A8310F5}"), QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentPermissionPrompt]), - handleExistingPermission: function handleExistingPermission(request, isApp) { - let result = Services.perms.testExactPermissionFromPrincipal(request.principal, request.type); + handleExistingPermission: function handleExistingPermission(request, type, isApp) { + let result = Services.perms.testExactPermissionFromPrincipal(request.principal, type); if (result == Ci.nsIPermissionManager.ALLOW_ACTION) { request.allow(); return true; } if (result == Ci.nsIPermissionManager.DENY_ACTION) { request.cancel(); return true; } - if (isApp && (result == Ci.nsIPermissionManager.UNKNOWN_ACTION && !!kEntities[request.type])) { + if (isApp && (result == Ci.nsIPermissionManager.UNKNOWN_ACTION && !!kEntities[type])) { request.cancel(); return true; } return false; }, getChromeWindow: function getChromeWindow(aWindow) { @@ -57,48 +57,56 @@ ContentPermissionPrompt.prototype = { return this.getChromeWindow(requestingWindow).wrappedJSObject; } return request.element.ownerDocument.defaultView; }, prompt: function(request) { let isApp = request.principal.appId !== Ci.nsIScriptSecurityManager.NO_APP_ID && request.principal.appId !== Ci.nsIScriptSecurityManager.UNKNOWN_APP_ID; + // Only allow exactly one permission rquest here. + let types = request.types.QueryInterface(Ci.nsIArray); + if (types.length != 1) { + request.cancel(); + return; + } + let perm = types.queryElementAt(0, Ci.nsIContentPermissionType); + // Returns true if the request was handled - if (this.handleExistingPermission(request, isApp)) + if (this.handleExistingPermission(request, perm.type, isApp)) return; let chromeWin = this.getChromeForRequest(request); let tab = chromeWin.BrowserApp.getTabForWindow(request.window.top); if (!tab) return; let browserBundle = Services.strings.createBundle("chrome://browser/locale/browser.properties"); - let entityName = kEntities[request.type]; + let entityName = kEntities[perm.type]; let buttons = [{ label: browserBundle.GetStringFromName(entityName + ".allow"), callback: function(aChecked) { // If the user checked "Don't ask again", make a permanent exception if (aChecked) { - Services.perms.addFromPrincipal(request.principal, request.type, Ci.nsIPermissionManager.ALLOW_ACTION); + Services.perms.addFromPrincipal(request.principal, perm.type, Ci.nsIPermissionManager.ALLOW_ACTION); } else if (isApp || entityName == "desktopNotification") { // Otherwise allow the permission for the current session (if the request comes from an app or if it's a desktop-notification request) - Services.perms.addFromPrincipal(request.principal, request.type, Ci.nsIPermissionManager.ALLOW_ACTION, Ci.nsIPermissionManager.EXPIRE_SESSION); + Services.perms.addFromPrincipal(request.principal, perm.type, Ci.nsIPermissionManager.ALLOW_ACTION, Ci.nsIPermissionManager.EXPIRE_SESSION); } request.allow(); } }, { label: browserBundle.GetStringFromName(entityName + ".dontAllow"), callback: function(aChecked) { // If the user checked "Don't ask again", make a permanent exception if (aChecked) - Services.perms.addFromPrincipal(request.principal, request.type, Ci.nsIPermissionManager.DENY_ACTION); + Services.perms.addFromPrincipal(request.principal, perm.type, Ci.nsIPermissionManager.DENY_ACTION); request.cancel(); } }]; let requestor = chromeWin.BrowserApp.manifest ? "'" + chromeWin.BrowserApp.manifest.name + "'" : request.principal.URI.host; let message = browserBundle.formatStringFromName(entityName + ".ask", [requestor], 1); let options = { checkbox: browserBundle.GetStringFromName(entityName + ".dontAskAgain") };
--- a/testing/specialpowers/content/MockPermissionPrompt.jsm +++ b/testing/specialpowers/content/MockPermissionPrompt.jsm @@ -29,19 +29,28 @@ var newFactory = { }, QueryInterface: XPCOMUtils.generateQI([Ci.nsIFactory]) }; this.MockPermissionPrompt = { init: function() { this.reset(); if (!registrar.isCIDRegistered(newClassID)) { - oldClassID = registrar.contractIDToCID(CONTRACT_ID); - oldFactory = Cm.getClassObject(Cc[CONTRACT_ID], Ci.nsIFactory); - registrar.unregisterFactory(oldClassID, oldFactory); + try { + oldClassID = registrar.contractIDToCID(CONTRACT_ID); + oldFactory = Cm.getClassObject(Cc[CONTRACT_ID], Ci.nsIFactory); + } catch (ex) { + oldClassID = ""; + oldFactory = null; + dump("TEST-INFO | can't get permission prompt registered component, " + + "assuming there is none"); + } + if (oldFactory) { + registrar.unregisterFactory(oldClassID, oldFactory); + } registrar.registerFactory(newClassID, "", CONTRACT_ID, newFactory); } }, reset: function() { }, cleanup: function() { @@ -56,24 +65,27 @@ this.MockPermissionPrompt = { function MockPermissionPromptInstance() { }; MockPermissionPromptInstance.prototype = { QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentPermissionPrompt]), promptResult: Ci.nsIPermissionManager.UNKNOWN_ACTION, prompt: function(request) { - this.promptResult = Services.perms.testExactPermissionFromPrincipal(request.principal, - request.type); - if (this.promptResult == Ci.nsIPermissionManager.ALLOW_ACTION) { - request.allow(); + let perms = request.types.QueryInterface(Ci.nsIArray); + for (let idx = 0; idx < perms.length; idx++) { + let perm = perms.queryElementAt(idx, Ci.nsIContentPermissionType); + if (Services.perms.testExactPermissionFromPrincipal( + request.principal, perm.type) != Ci.nsIPermissionManager.ALLOW_ACTION) { + request.cancel(); + return; + } } - else { - request.cancel(); - } + + request.allow(); } }; // Expose everything to content. We call reset() here so that all of the relevant // lazy expandos get added. MockPermissionPrompt.reset(); function exposeAll(obj) { var props = {};
--- a/webapprt/ContentPermission.js +++ b/webapprt/ContentPermission.js @@ -25,80 +25,88 @@ ContentPermission.prototype = { .QueryInterface(Ci.nsIDocShellTreeItem) .rootTreeItem .QueryInterface(Ci.nsIInterfaceRequestor) .getInterface(Ci.nsIDOMWindow) .QueryInterface(Ci.nsIDOMChromeWindow); }, prompt: function(request) { + // Only allow exactly one permission rquest here. + let types = request.types.QueryInterface(Ci.nsIArray); + if (types.length != 1) { + request.cancel(); + return; + } + let perm = types.queryElementAt(0, Ci.nsIContentPermissionType); + // Reuse any remembered permission preferences let result = Services.perms.testExactPermissionFromPrincipal(request.principal, - request.type); + perm.type); // We used to use the name "geo" for the geolocation permission, now we're // using "geolocation". We need to check both to support existing // installations. if ((result == Ci.nsIPermissionManager.UNKNOWN_ACTION || result == Ci.nsIPermissionManager.PROMPT_ACTION) && - request.type == "geolocation") { + perm.type == "geolocation") { let geoResult = Services.perms.testExactPermission(request.principal.URI, "geo"); // We override the result only if the "geo" permission was allowed or // denied. if (geoResult == Ci.nsIPermissionManager.ALLOW_ACTION || geoResult == Ci.nsIPermissionManager.DENY_ACTION) { result = geoResult; } } if (result == Ci.nsIPermissionManager.ALLOW_ACTION) { request.allow(); return; } else if (result == Ci.nsIPermissionManager.DENY_ACTION || (result == Ci.nsIPermissionManager.UNKNOWN_ACTION && - UNKNOWN_FAIL.indexOf(request.type) >= 0)) { + UNKNOWN_FAIL.indexOf(perm.type) >= 0)) { request.cancel(); return; } // Display a prompt at the top level let {name} = WebappRT.localeManifest; let requestingWindow = request.window.top; let chromeWin = this._getChromeWindow(requestingWindow); let bundle = Services.strings.createBundle("chrome://webapprt/locale/webapp.properties"); // Construct a prompt with share/don't and remember checkbox let remember = {value: false}; let choice = Services.prompt.confirmEx( chromeWin, - bundle.formatStringFromName(request.type + ".title", [name], 1), - bundle.GetStringFromName(request.type + ".description"), + bundle.formatStringFromName(perm.type + ".title", [name], 1), + bundle.GetStringFromName(perm.type + ".description"), // Set both buttons to strings with the cancel button being default Ci.nsIPromptService.BUTTON_POS_1_DEFAULT | Ci.nsIPromptService.BUTTON_TITLE_IS_STRING * Ci.nsIPromptService.BUTTON_POS_0 | Ci.nsIPromptService.BUTTON_TITLE_IS_STRING * Ci.nsIPromptService.BUTTON_POS_1, - bundle.GetStringFromName(request.type + ".allow"), - bundle.GetStringFromName(request.type + ".deny"), + bundle.GetStringFromName(perm.type + ".allow"), + bundle.GetStringFromName(perm.type + ".deny"), null, - bundle.GetStringFromName(request.type + ".remember"), + bundle.GetStringFromName(perm.type + ".remember"), remember); let action = Ci.nsIPermissionManager.ALLOW_ACTION; if (choice != 0) { action = Ci.nsIPermissionManager.DENY_ACTION; } if (remember.value) { // Persist the choice if the user wants to remember - Services.perms.addFromPrincipal(request.principal, request.type, action); + Services.perms.addFromPrincipal(request.principal, perm.type, action); } else { // Otherwise allow the permission for the current session - Services.perms.addFromPrincipal(request.principal, request.type, action, + Services.perms.addFromPrincipal(request.principal, perm.type, action, Ci.nsIPermissionManager.EXPIRE_SESSION); } // Trigger the selected choice if (choice == 0) { request.allow(); } else {