--- 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/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/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 {