Bug 853356 - gUM video permisson prompt. r=fabrice, felipc, wjohnston, mrbkap, jimm, jesup, khuey, jsmith

This commit is contained in:
Alfredo Yang 2014-02-09 15:34:40 -05:00
parent 5c683a0aa1
commit 3853c6783a
27 changed files with 666 additions and 349 deletions

View File

@ -5,7 +5,7 @@
"use strict"
function debug(str) {
//dump("-*- ContentPermissionPrompt: " + s + "\n");
//dump("-*- ContentPermissionPrompt: " + str + "\n");
}
const Ci = Components.interfaces;
@ -13,11 +13,14 @@ 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");
@ -41,7 +44,21 @@ XPCOMUtils.defineLazyServiceGetter(this,
"@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)
{
@ -49,12 +66,13 @@ function rememberPermission(aPermission, aPrincipal, aSession)
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,
@ -63,14 +81,18 @@ function rememberPermission(aPermission, aPrincipal, aSession)
}
}
// 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);
}
}
@ -78,23 +100,66 @@ 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
@ -106,49 +171,94 @@ ContentPermissionPrompt.prototype = {
.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;
}
@ -163,7 +273,7 @@ ContentPermissionPrompt.prototype = {
if (evt.detail.visible === true)
return;
self.cancelPrompt(request, requestId);
self.cancelPrompt(request, requestId, typesInfo);
cancelRequest();
}
@ -180,7 +290,7 @@ ContentPermissionPrompt.prototype = {
// 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);
});
};
@ -191,22 +301,17 @@ ContentPermissionPrompt.prototype = {
}
},
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();
}
@ -214,14 +319,20 @@ ContentPermissionPrompt.prototype = {
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();
@ -230,7 +341,7 @@ ContentPermissionPrompt.prototype = {
});
},
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)
@ -253,10 +364,15 @@ ContentPermissionPrompt.prototype = {
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,
@ -289,6 +405,5 @@ ContentPermissionPrompt.prototype = {
};
})();
//module initialization
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([ContentPermissionPrompt]);

View File

@ -1902,7 +1902,16 @@ ContentPermissionPrompt.prototype = {
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 = {
@ -1920,7 +1929,7 @@ 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
@ -2058,13 +2067,21 @@ ContentPermissionPrompt.prototype = {
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;
}
@ -2076,7 +2093,7 @@ ContentPermissionPrompt.prototype = {
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) {
@ -2087,7 +2104,7 @@ ContentPermissionPrompt.prototype = {
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;
}
@ -2101,7 +2118,7 @@ ContentPermissionPrompt.prototype = {
return;
// Show the prompt.
switch (request.type) {
switch (perm.type) {
case "geolocation":
this._promptGeo(request);
break;

View File

@ -44,6 +44,13 @@ let IndexedDB = {
}
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() {
@ -60,7 +67,7 @@ let IndexedDB = {
}
prompt.prompt({
type: type,
types: types,
uri: Services.io.newURI(payload.location, null, null),
window: null,
element: aMessage.target,

View File

@ -56,8 +56,8 @@ ContentPermissionPrompt.prototype = {
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;
@ -70,20 +70,28 @@ ContentPermissionPrompt.prototype = {
},
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"),
@ -96,7 +104,7 @@ ContentPermissionPrompt.prototype = {
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();
}
},
@ -104,7 +112,7 @@ ContentPermissionPrompt.prototype = {
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();
}
}];
@ -112,12 +120,12 @@ ContentPermissionPrompt.prototype = {
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"));

View File

@ -217,6 +217,8 @@
#include "mozilla/dom/XPathEvaluator.h"
#include "nsIDocumentEncoder.h"
#include "nsIStructuredCloneContainer.h"
#include "nsIMutableArray.h"
#include "nsContentPermissionHelper.h"
using namespace mozilla;
using namespace mozilla::dom;
@ -10771,17 +10773,11 @@ NS_IMPL_ISUPPORTS_INHERITED1(nsPointerLockPermissionRequest,
nsIContentPermissionRequest)
NS_IMETHODIMP
nsPointerLockPermissionRequest::GetType(nsACString& aType)
nsPointerLockPermissionRequest::GetTypes(nsIArray** aTypes)
{
aType = "pointerLock";
return NS_OK;
}
NS_IMETHODIMP
nsPointerLockPermissionRequest::GetAccess(nsACString& aAccess)
{
aAccess = "unused";
return NS_OK;
return CreatePermissionArray(NS_LITERAL_CSTRING("pointerLock"),
NS_LITERAL_CSTRING("unused"),
aTypes);
}
NS_IMETHODIMP

View File

@ -5,6 +5,7 @@
#ifndef MEDIAENGINE_H_
#define MEDIAENGINE_H_
#include "mozilla/RefPtr.h"
#include "nsIDOMFile.h"
#include "DOMMediaStream.h"
#include "MediaStreamGraph.h"
@ -35,7 +36,7 @@ enum {
kAudioTrack = 2
};
class MediaEngine
class MediaEngine : public RefCounted<MediaEngine>
{
public:
virtual ~MediaEngine() {}

View File

@ -321,6 +321,11 @@ this.PermissionsTable = { geolocation: {
privileged: DENY_ACTION,
certified: ALLOW_ACTION
},
"video-capture": {
app: PROMPT_ACTION,
privileged: PROMPT_ACTION,
certified: PROMPT_ACTION
},
};
/**

View File

@ -6,20 +6,156 @@
#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);
@ -31,14 +167,12 @@ nsContentPermissionRequestProxy::~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) {
@ -58,17 +192,14 @@ nsContentPermissionRequestProxy::OnParentDestroyed()
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
@ -136,10 +267,18 @@ nsContentPermissionRequestProxy::Allow()
}
#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
@ -147,55 +286,3 @@ nsContentPermissionRequestProxy::Allow()
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

View File

@ -6,60 +6,75 @@
#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
#endif // nsContentPermissionHelper_h

View File

@ -51,6 +51,7 @@
#include "nsIDocument.h"
#include <algorithm>
#include "private/pprio.h"
#include "nsContentPermissionHelper.h"
#include "mozilla/dom/DeviceStorageBinding.h"
@ -1771,17 +1772,14 @@ nsDOMDeviceStorageCursor::GetStorageType(nsAString & aType)
}
NS_IMETHODIMP
nsDOMDeviceStorageCursor::GetType(nsACString & aType)
nsDOMDeviceStorageCursor::GetTypes(nsIArray** aTypes)
{
return DeviceStorageTypeChecker::GetPermissionForType(mFile->mStorageType,
aType);
}
nsCString type;
nsresult rv =
DeviceStorageTypeChecker::GetPermissionForType(mFile->mStorageType, type);
NS_ENSURE_SUCCESS(rv, rv);
NS_IMETHODIMP
nsDOMDeviceStorageCursor::GetAccess(nsACString & aAccess)
{
aAccess = NS_LITERAL_CSTRING("read");
return NS_OK;
return CreatePermissionArray(type, NS_LITERAL_CSTRING("read"), aTypes);
}
NS_IMETHODIMP
@ -2386,8 +2384,10 @@ public:
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;
@ -2401,26 +2401,23 @@ public:
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)
@ -3557,8 +3554,10 @@ nsDOMDeviceStorage::EnumerateInternal(const nsAString& aPath,
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();

View File

@ -7,15 +7,13 @@
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".
@ -27,8 +25,22 @@ interface nsIContentPermissionRequest : nsISupports {
* "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;

View File

@ -16,6 +16,7 @@ include protocol PIndexedDB;
include DOMTypes;
include JavaScriptTypes;
include URIParams;
include PContentPermission;
using class IPC::Principal from "mozilla/dom/PermissionMessageUtils.h";
@ -222,10 +223,8 @@ parent:
* 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.
*
@ -233,7 +232,7 @@ parent:
* 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);

View File

@ -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

View File

@ -1128,12 +1128,11 @@ TabChild::ArraysToParams(const InfallibleTArray<int>& aIntParams,
#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;
}
@ -2066,7 +2065,8 @@ TabChild::DeallocPContentDialogChild(PContentDialogChild* aDialog)
}
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;

View File

@ -295,14 +295,12 @@ public:
#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;

View File

@ -15,6 +15,7 @@
#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"
@ -623,9 +624,10 @@ TabParent::DeallocPDocumentRendererParent(PDocumentRendererParent* actor)
}
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

View File

@ -253,8 +253,7 @@ public:
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;

View File

@ -68,6 +68,7 @@ IPDL_SOURCES += [
'PBrowser.ipdl',
'PContent.ipdl',
'PContentDialog.ipdl',
'PContentPermission.ipdlh',
'PContentPermissionRequest.ipdl',
'PCrashReporter.ipdl',
'PDocumentRenderer.ipdl',

View File

@ -41,7 +41,7 @@
#include "MediaEngineWebRTC.h"
#endif
#ifdef MOZ_WIDGET_GONK
#ifdef MOZ_B2G
#include "MediaPermissionGonk.h"
#endif
@ -780,7 +780,7 @@ public:
, mListener(aListener)
, mPrefs(aPrefs)
, mDeviceChosen(false)
, mBackendChosen(false)
, mBackend(nullptr)
, mManager(MediaManager::GetInstance())
{}
@ -804,15 +804,11 @@ public:
, mListener(aListener)
, mPrefs(aPrefs)
, mDeviceChosen(false)
, mBackendChosen(true)
, mBackend(aBackend)
, mManager(MediaManager::GetInstance())
{}
~GetUserMediaRunnable() {
if (mBackendChosen) {
delete mBackend;
}
}
/**
@ -834,14 +830,15 @@ public:
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;
}
@ -917,12 +914,12 @@ public:
}
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()) {
@ -936,7 +933,7 @@ public:
}
if (mConstraints.mAudio) {
ScopedDeletePtr<SourceSet> sources (GetSources(mBackend,
ScopedDeletePtr<SourceSet> sources (GetSources(backend,
mConstraints.mAudiom, &MediaEngine::EnumerateAudioDevices));
if (!sources->Length()) {
@ -1036,9 +1033,8 @@ private:
MediaEnginePrefs mPrefs;
bool mDeviceChosen;
bool mBackendChosen;
MediaEngine* mBackend;
RefPtr<MediaEngine> mBackend;
nsRefPtr<MediaManager> mManager; // get ref to this when creating the runnable
};
@ -1328,10 +1324,10 @@ MediaManager::GetUserMedia(JSContext* aCx, bool aPrivileged,
// 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

View File

@ -514,9 +514,7 @@ private:
// Make private because we want only one instance of this class
MediaManager();
~MediaManager() {
delete mBackend;
}
~MediaManager() {}
nsresult MediaCaptureWindowStateInternal(nsIDOMWindow* aWindow, bool* aVideo,
bool* aAudio);
@ -532,11 +530,11 @@ private:
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
};

View File

@ -20,14 +20,36 @@
#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)
@ -93,6 +115,7 @@ public:
private:
bool mAudio; // Request for audio permission
bool mVideo; // Request for video permission
nsRefPtr<dom::GetUserMediaRequest> mRequest;
nsTArray<nsCOMPtr<nsIMediaDevice> > mDevices; // candiate device list
};
@ -108,6 +131,7 @@ MediaPermissionRequest::MediaPermissionRequest(nsRefPtr<dom::GetUserMediaRequest
mRequest->GetConstraints(constraints);
mAudio = constraints.mAudio;
mVideo = constraints.mVideo;
for (uint32_t i = 0; i < aDevices.Length(); ++i) {
nsCOMPtr<nsIMediaDevice> device(aDevices[i]);
@ -116,10 +140,34 @@ MediaPermissionRequest::MediaPermissionRequest(nsRefPtr<dom::GetUserMediaRequest
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)
{
@ -135,24 +183,6 @@ MediaPermissionRequest::GetPrincipal(nsIPrincipal **aRequestingPrincipal)
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)
{
@ -278,13 +308,12 @@ MediaDeviceSuccessCallback::DoPrompt(nsRefPtr<MediaPermissionRequest> &req)
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));
@ -292,8 +321,7 @@ MediaDeviceSuccessCallback::DoPrompt(nsRefPtr<MediaPermissionRequest> &req)
req->AddRef();
child->SendPContentPermissionRequestConstructor(req,
type,
access,
permArray,
IPC::Principal(principal));
req->Sendprompt();

View File

@ -379,17 +379,11 @@ nsGeolocationRequest::GetPrincipal(nsIPrincipal * *aRequestingPrincipal)
}
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
@ -1477,12 +1471,15 @@ Geolocation::RegisterRequestWithPrompt(nsGeolocationRequest* request)
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();

View File

@ -15,6 +15,7 @@
#include "PCOMContentPermissionRequestChild.h"
#include "nsIScriptSecurityManager.h"
#include "nsServiceManagerUtils.h"
#include "PermissionMessageUtils.h"
namespace mozilla {
namespace dom {
@ -177,9 +178,12 @@ DesktopNotification::Init()
// 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();
@ -351,17 +355,11 @@ DesktopNotificationRequest::Allow()
}
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

View File

@ -24,6 +24,7 @@
#include "nsDOMJSUtils.h"
#include "nsIScriptSecurityManager.h"
#include "mozilla/dom/PermissionMessageUtils.h"
#include "nsContentPermissionHelper.h"
#ifdef MOZ_B2G
#include "nsIDOMDesktopNotification.h"
#endif
@ -267,9 +268,11 @@ NotificationPermissionRequest::Run()
// 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();
@ -342,17 +345,11 @@ NotificationPermissionRequest::CallCallback()
}
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

View File

@ -21,8 +21,8 @@ ContentPermissionPrompt.prototype = {
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;
@ -32,7 +32,7 @@ ContentPermissionPrompt.prototype = {
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;
}
@ -62,8 +62,16 @@ ContentPermissionPrompt.prototype = {
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);
@ -72,17 +80,17 @@ ContentPermissionPrompt.prototype = {
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();
@ -93,7 +101,7 @@ ContentPermissionPrompt.prototype = {
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();
}

View File

@ -34,9 +34,18 @@ 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);
}
},
@ -61,14 +70,17 @@ MockPermissionPromptInstance.prototype = {
prompt: function(request) {
this.promptResult = Services.perms.testExactPermissionFromPrincipal(request.principal,
request.type);
if (this.promptResult == Ci.nsIPermissionManager.ALLOW_ACTION) {
request.allow();
}
else {
request.cancel();
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;
}
}
request.allow();
}
};

View File

@ -30,17 +30,25 @@ ContentPermission.prototype = {
},
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
@ -56,7 +64,7 @@ ContentPermission.prototype = {
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;
}
@ -71,16 +79,16 @@ ContentPermission.prototype = {
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;
@ -90,10 +98,10 @@ ContentPermission.prototype = {
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);
}