mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-12 04:45:45 +00:00
Bug 1479051 - [macOS 10.14] WebRTC sites silently fail if user previously clicked "Don't Allow" for Firefox camera/mic access r=johannh
Check if we have permission from the OS to access the camera and microphone after the user has granted access to a site. If permission is denied at the OS level, but granted to the site within Firefox, return NotFoundError. Differential Revision: https://phabricator.services.mozilla.com/D5458 --HG-- extra : moz-landing-system : lando
This commit is contained in:
parent
5a64fcf173
commit
206f542c37
@ -240,7 +240,13 @@ function prompt(aContentWindow, aWindowID, aCallID, aConstraints, aDevices, aSec
|
||||
}
|
||||
|
||||
function denyGUMRequest(aData) {
|
||||
Services.obs.notifyObservers(null, "getUserMedia:response:deny", aData.callID);
|
||||
let subject;
|
||||
if (aData.noOSPermission) {
|
||||
subject = "getUserMedia:response:noOSPermission";
|
||||
} else {
|
||||
subject = "getUserMedia:response:deny";
|
||||
}
|
||||
Services.obs.notifyObservers(null, subject, aData.callID);
|
||||
|
||||
if (!aData.windowID)
|
||||
return;
|
||||
|
@ -23,6 +23,10 @@ XPCOMUtils.defineLazyGetter(this, "gBrandBundle", function() {
|
||||
return Services.strings.createBundle("chrome://branding/locale/brand.properties");
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "OSPermissions",
|
||||
"@mozilla.org/ospermissionrequest;1",
|
||||
"nsIOSPermissionRequest");
|
||||
|
||||
var webrtcUI = {
|
||||
peerConnectionBlockers: new Set(),
|
||||
emitter: new EventEmitter(),
|
||||
@ -300,6 +304,67 @@ function denyRequest(aBrowser, aRequest) {
|
||||
windowID: aRequest.windowID});
|
||||
}
|
||||
|
||||
//
|
||||
// Deny the request because the browser does not have access to the
|
||||
// camera or microphone due to OS security restrictions. The user may
|
||||
// have granted camera/microphone access to the site, but not have
|
||||
// allowed the browser access in OS settings.
|
||||
//
|
||||
function denyRequestNoPermission(aBrowser, aRequest) {
|
||||
aBrowser.messageManager.sendAsyncMessage("webrtc:Deny",
|
||||
{callID: aRequest.callID,
|
||||
windowID: aRequest.windowID,
|
||||
noOSPermission: true});
|
||||
}
|
||||
|
||||
//
|
||||
// Check if we have permission to access the camera and or microphone at the
|
||||
// OS level. Triggers a request to access the device if access is needed and
|
||||
// the permission state has not yet been determined.
|
||||
//
|
||||
async function checkOSPermission(camNeeded, micNeeded) {
|
||||
let camStatus = {}, micStatus = {};
|
||||
OSPermissions.getMediaCapturePermissionState(camStatus, micStatus);
|
||||
if (camNeeded) {
|
||||
let camPermission = camStatus.value;
|
||||
let camAccessible = await checkAndGetOSPermission(camPermission,
|
||||
OSPermissions.requestVideoCapturePermission);
|
||||
if (!camAccessible) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (micNeeded) {
|
||||
let micPermission = micStatus.value;
|
||||
let micAccessible = await checkAndGetOSPermission(micPermission,
|
||||
OSPermissions.requestAudioCapturePermission);
|
||||
if (!micAccessible) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// Given a device's permission, return true if the device is accessible. If
|
||||
// the device's permission is not yet determined, request access to the device.
|
||||
// |requestPermissionFunc| must return a promise that resolves with true
|
||||
// if the device is accessible and false otherwise.
|
||||
//
|
||||
async function checkAndGetOSPermission(devicePermission,
|
||||
requestPermissionFunc) {
|
||||
if (devicePermission == OSPermissions.PERMISSION_STATE_DENIED ||
|
||||
devicePermission == OSPermissions.PERMISSION_STATE_RESTRICTED) {
|
||||
return false;
|
||||
}
|
||||
if (devicePermission == OSPermissions.PERMISSION_STATE_NOTDETERMINED) {
|
||||
let deviceAllowed = await requestPermissionFunc();
|
||||
if (!deviceAllowed) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function getHostOrExtensionName(uri, href) {
|
||||
let host;
|
||||
try {
|
||||
@ -517,10 +582,19 @@ function prompt(aBrowser, aRequest) {
|
||||
browser._devicePermissionURIs = browser._devicePermissionURIs || [];
|
||||
browser._devicePermissionURIs.push(uri);
|
||||
|
||||
let mm = browser.messageManager;
|
||||
mm.sendAsyncMessage("webrtc:Allow", {callID: aRequest.callID,
|
||||
windowID: aRequest.windowID,
|
||||
devices: allowedDevices});
|
||||
let camNeeded = videoDevices.length > 0;
|
||||
let micNeeded = audioDevices.length > 0;
|
||||
checkOSPermission(camNeeded, micNeeded).then((havePermission) => {
|
||||
if (havePermission) {
|
||||
let mm = browser.messageManager;
|
||||
mm.sendAsyncMessage("webrtc:Allow", {callID: aRequest.callID,
|
||||
windowID: aRequest.windowID,
|
||||
devices: allowedDevices});
|
||||
} else {
|
||||
denyRequestNoPermission(browser, aRequest);
|
||||
}
|
||||
});
|
||||
|
||||
this.remove();
|
||||
return true;
|
||||
}
|
||||
@ -717,7 +791,7 @@ function prompt(aBrowser, aRequest) {
|
||||
if (!sharingAudio)
|
||||
listDevices(micMenupopup, audioDevices);
|
||||
|
||||
this.mainAction.callback = function(aState) {
|
||||
this.mainAction.callback = async function(aState) {
|
||||
let remember = aState && aState.checkboxChecked;
|
||||
let allowedDevices = [];
|
||||
let perms = Services.perms;
|
||||
@ -784,6 +858,14 @@ function prompt(aBrowser, aRequest) {
|
||||
aBrowser._devicePermissionURIs.push(uri);
|
||||
}
|
||||
|
||||
let camNeeded = videoDevices.length > 0;
|
||||
let micNeeded = audioDevices.length > 0;
|
||||
let havePermission = await checkOSPermission(camNeeded, micNeeded);
|
||||
if (!havePermission) {
|
||||
denyRequestNoPermission(notification.browser, aRequest);
|
||||
return;
|
||||
}
|
||||
|
||||
let mm = notification.browser.messageManager;
|
||||
mm.sendAsyncMessage("webrtc:Allow", {callID: aRequest.callID,
|
||||
windowID: aRequest.windowID,
|
||||
|
@ -2260,6 +2260,7 @@ MediaManager::Get() {
|
||||
obs->AddObserver(sSingleton, "getUserMedia:privileged:allow", false);
|
||||
obs->AddObserver(sSingleton, "getUserMedia:response:allow", false);
|
||||
obs->AddObserver(sSingleton, "getUserMedia:response:deny", false);
|
||||
obs->AddObserver(sSingleton, "getUserMedia:response:noOSPermission", false);
|
||||
obs->AddObserver(sSingleton, "getUserMedia:revoke", false);
|
||||
}
|
||||
// else MediaManager won't work properly and will leak (see bug 837874)
|
||||
@ -3634,6 +3635,7 @@ MediaManager::Shutdown()
|
||||
obs->RemoveObserver(this, "getUserMedia:privileged:allow");
|
||||
obs->RemoveObserver(this, "getUserMedia:response:allow");
|
||||
obs->RemoveObserver(this, "getUserMedia:response:deny");
|
||||
obs->RemoveObserver(this, "getUserMedia:response:noOSPermission");
|
||||
obs->RemoveObserver(this, "getUserMedia:revoke");
|
||||
|
||||
nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
|
||||
@ -3769,12 +3771,30 @@ MediaManager::SendPendingGUMRequest()
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
IsGUMResponseNoAccess(const char* aTopic, MediaMgrError::Name& aErrorName)
|
||||
{
|
||||
if (!strcmp(aTopic, "getUserMedia:response:deny")) {
|
||||
aErrorName = MediaMgrError::Name::NotAllowedError;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!strcmp(aTopic, "getUserMedia:response:noOSPermission")) {
|
||||
aErrorName = MediaMgrError::Name::NotFoundError;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
nsresult
|
||||
MediaManager::Observe(nsISupports* aSubject, const char* aTopic,
|
||||
const char16_t* aData)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
MediaMgrError::Name gumNoAccessError = MediaMgrError::Name::NotAllowedError;
|
||||
|
||||
if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
|
||||
nsCOMPtr<nsIPrefBranch> branch( do_QueryInterface(aSubject) );
|
||||
if (branch) {
|
||||
@ -3860,12 +3880,12 @@ MediaManager::Observe(nsISupports* aSubject, const char* aTopic,
|
||||
MediaManager::PostTask(task.forget());
|
||||
return NS_OK;
|
||||
|
||||
} else if (!strcmp(aTopic, "getUserMedia:response:deny")) {
|
||||
} else if (IsGUMResponseNoAccess(aTopic, gumNoAccessError)) {
|
||||
nsString key(aData);
|
||||
RefPtr<GetUserMediaTask> task;
|
||||
mActiveCallbacks.Remove(key, getter_AddRefs(task));
|
||||
if (task) {
|
||||
task->Denied(MediaMgrError::Name::NotAllowedError);
|
||||
task->Denied(gumNoAccessError);
|
||||
nsTArray<nsString>* array;
|
||||
if (!mCallIds.Get(task->GetWindowID(), &array)) {
|
||||
return NS_OK;
|
||||
|
Loading…
Reference in New Issue
Block a user