Bug 989094 - Remember video and audio sources for webrtc. r=mfinkle

This commit is contained in:
Wes Johnston 2014-09-16 16:28:51 -07:00
parent 2f673f8913
commit 60c559be95

View File

@ -4,9 +4,14 @@
"use strict";
XPCOMUtils.defineLazyModuleGetter(this, "Notifications", "resource://gre/modules/Notifications.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "contentPrefs",
"@mozilla.org/content-pref/service;1",
"nsIContentPrefService2");
var WebrtcUI = {
_notificationId: null,
VIDEO_SOURCE: "videoSource",
AUDIO_SOURCE: "audioDevice",
observe: function(aSubject, aTopic, aData) {
if (aTopic === "getUserMedia:request") {
@ -78,39 +83,42 @@ var WebrtcUI = {
contentWindow.navigator.mozGetUserMediaDevices(
constraints,
function (devices) {
WebrtcUI.prompt(contentWindow, aSubject.callID, constraints.audio,
constraints.video, devices);
function (aDevices) {
WebrtcUI.prompt(contentWindow, aSubject.callID, constraints.audio, constraints.video, aDevices);
},
function (error) {
Cu.reportError(error);
},
aSubject.innerWindowID);
Cu.reportError, aSubject.innerWindowID);
},
getDeviceButtons: function(audioDevices, videoDevices, aCallID) {
getDeviceButtons: function(aAudioDevices, aVideoDevices, aCallID, aHost) {
return [{
label: Strings.browser.GetStringFromName("getUserMedia.denyRequest.label"),
callback: function() {
callback: () => {
Services.obs.notifyObservers(null, "getUserMedia:response:deny", aCallID);
}
},
{
}, {
label: Strings.browser.GetStringFromName("getUserMedia.shareRequest.label"),
callback: function(checked /* ignored */, inputs) {
callback: (checked /* ignored */, inputs) => {
let allowedDevices = Cc["@mozilla.org/supports-array;1"].createInstance(Ci.nsISupportsArray);
let audioId = 0;
if (inputs && inputs.audioDevice != undefined)
audioId = inputs.audioDevice;
if (audioDevices[audioId])
allowedDevices.AppendElement(audioDevices[audioId]);
if (inputs && inputs[this.AUDIO_SOURCE] != undefined) {
audioId = inputs[this.AUDIO_SOURCE];
}
if (aAudioDevices[audioId]) {
allowedDevices.AppendElement(aAudioDevices[audioId]);
this.setDefaultDevice(this.AUDIO_SOURCE, aAudioDevices[audioId].name, aHost);
}
let videoId = 0;
if (inputs && inputs.videoSource != undefined)
videoId = inputs.videoSource;
if (videoDevices[videoId])
allowedDevices.AppendElement(videoDevices[videoId]);
if (inputs && inputs[this.VIDEO_SOURCE] != undefined) {
videoId = inputs[this.VIDEO_SOURCE];
}
if (aVideoDevices[videoId]) {
allowedDevices.AppendElement(aVideoDevices[videoId]);
this.setDefaultDevice(this.VIDEO_SOURCE, aVideoDevices[videoId].name, aHost);
}
Services.obs.notifyObservers(allowedDevices, "getUserMedia:response:allow", aCallID);
}
@ -121,30 +129,34 @@ var WebrtcUI = {
_getList: function(aDevices, aType) {
let defaultCount = 0;
return aDevices.map(function(device) {
// if this is a Camera input, convert the name to something readable
let res = /Camera\ \d+,\ Facing (front|back)/.exec(device.name);
if (res)
return Strings.browser.GetStringFromName("getUserMedia." + aType + "." + res[1] + "Camera");
let name = device.name;
// if this is a Camera input, convert the name to something readable
let res = /Camera\ \d+,\ Facing (front|back)/.exec(name);
if (res) {
return Strings.browser.GetStringFromName("getUserMedia." + aType + "." + res[1] + "Camera");
}
if (device.name.startsWith("&") && device.name.endsWith(";"))
return Strings.browser.GetStringFromName(device.name.substring(1, device.name.length -1));
if (name.startsWith("&") && name.endsWith(";")) {
return Strings.browser.GetStringFromName(name.substring(1, name.length -1));
}
if (device.name.trim() == "") {
defaultCount++;
return Strings.browser.formatStringFromName("getUserMedia." + aType + ".default", [defaultCount], 1);
}
return device.name
}, this);
if (name.trim() == "") {
defaultCount++;
return Strings.browser.formatStringFromName("getUserMedia." + aType + ".default", [defaultCount], 1);
}
return name;
}, this);
},
_addDevicesToOptions: function(aDevices, aType, aOptions, extraOptions) {
if (aDevices.length) {
_addDevicesToOptions: function(aDevices, aType, aOptions, aHost, aContext) {
if (aDevices.length == 0) {
return Promise.resolve(aOptions);
}
let updateOptions = () => {
// Filter out empty items from the list
let list = this._getList(aDevices, aType);
if (extraOptions)
list = list.concat(extraOptions);
if (list.length > 0) {
aOptions.inputs.push({
id: aType,
@ -152,40 +164,96 @@ var WebrtcUI = {
label: Strings.browser.GetStringFromName("getUserMedia." + aType + ".prompt"),
values: list
});
}
return aOptions;
}
return this.getDefaultDevice(aType, aHost, aContext).then((defaultDevice) => {
aDevices.sort((a, b) => {
if (b.name === defaultDevice) return 1;
return 0;
});
return updateOptions();
}).catch(updateOptions);
},
// Sets the default for a aHost. If no aHost is specified, sets the browser wide default.
// Saving is async, but this doesn't wait for a result.
setDefaultDevice: function(aType, aValue, aHost, aContext) {
if (aHost) {
contentPrefs.set(aHost, "webrtc." + aType, aValue, aContext);
} else {
contentPrefs.setGlobal("webrtc." + aType, aValue, aContext);
}
},
prompt: function prompt(aContentWindow, aCallID, aAudioRequested,
aVideoRequested, aDevices) {
_checkContentPref(aHost, aType, aContext) {
return new Promise((resolve, reject) => {
let result = null;
let handler = {
handleResult: (aResult) => result = aResult,
handleCompletion: function(aReason) {
if (aReason == Components.interfaces.nsIContentPrefCallback2.COMPLETE_OK &&
result instanceof Components.interfaces.nsIContentPref) {
resolve(result.value);
} else {
reject(result);
}
}
};
if (aHost) {
contentPrefs.getByDomainAndName(aHost, "webrtc." + aType, aContext, handler);
} else {
contentPrefs.getGlobal("webrtc." + aType, aContext, handler);
}
});
},
// Returns the default device for this aHost. If no aHost is specified, returns a browser wide default
getDefaultDevice: function(aType, aHost, aContext) {
return this._checkContentPref(aHost, aType, aContext).catch(() => {
// If we found nothing for the initial pref, try looking for a global one
return this._checkContentPref(null, aType, aContext);
});
},
prompt: function (aWindow, aCallID, aAudioRequested, aVideoRequested, aDevices) {
let audioDevices = [];
let videoDevices = [];
// Split up all the available aDevices into audio and video categories
for (let device of aDevices) {
device = device.QueryInterface(Ci.nsIMediaDevice);
switch (device.type) {
case "audio":
if (aAudioRequested)
if (aAudioRequested) {
audioDevices.push(device);
}
break;
case "video":
if (aVideoRequested)
if (aVideoRequested) {
videoDevices.push(device);
}
break;
}
}
// Bsaed on the aTypes available, setup the prompt and icon text
let requestType;
if (audioDevices.length && videoDevices.length)
if (audioDevices.length && videoDevices.length) {
requestType = "CameraAndMicrophone";
else if (audioDevices.length)
} else if (audioDevices.length) {
requestType = "Microphone";
else if (videoDevices.length)
} else if (videoDevices.length) {
requestType = "Camera";
else
} else {
return;
}
let host = aContentWindow.document.documentURIObject.host;
let host = aWindow.document.documentURIObject.host;
// Show the app name if this is a WebRT app, otherwise show the host.
let requestor = BrowserApp.manifest ? "'" + BrowserApp.manifest.name + "'" : host;
let message = Strings.browser.formatStringFromName("getUserMedia.share" + requestType + ".message", [ requestor ], 1);
@ -193,24 +261,20 @@ var WebrtcUI = {
// if the users only option would be to select "No Audio" or "No Video"
// i.e. we're only showing audio or only video and there is only one device for that type
// don't bother showing a menulist to select from
var extraItems = null;
if (videoDevices.length > 1 || audioDevices.length > 0) {
// Only show the No Video option if there are also Audio devices to choose from
if (audioDevices.length > 0)
extraItems = [ Strings.browser.GetStringFromName("getUserMedia.videoSource.none") ];
// videoSource is both the string used for l10n lookup and the object that will be returned
this._addDevicesToOptions(videoDevices, "videoSource", options, extraItems);
if (videoDevices.length > 0 && audioDevices.length > 0) {
videoDevices.push({ name: Strings.browser.GetStringFromName("getUserMedia.videoSource.none") });
audioDevices.push({ name: Strings.browser.GetStringFromName("getUserMedia.audioDevice.none") });
}
if (audioDevices.length > 1 || videoDevices.length > 0) {
// Only show the No Audio option if there are also Video devices to choose from
if (videoDevices.length > 0)
extraItems = [ Strings.browser.GetStringFromName("getUserMedia.audioDevice.none") ];
this._addDevicesToOptions(audioDevices, "audioDevice", options, extraItems);
}
let buttons = this.getDeviceButtons(audioDevices, videoDevices, aCallID);
NativeWindow.doorhanger.show(message, "webrtc-request", buttons, BrowserApp.selectedTab.id, options);
let loadContext = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsILoadContext);
// videoSource is both the string used for l10n lookup and the object that will be returned
this._addDevicesToOptions(videoDevices, this.VIDEO_SOURCE, options, host, loadContext).then((aOptions) => {
return this._addDevicesToOptions(audioDevices, this.AUDIO_SOURCE, aOptions, host, loadContext);
}).catch(Cu.reportError).then((aOptions) => {
let buttons = this.getDeviceButtons(audioDevices, videoDevices, aCallID, host);
NativeWindow.doorhanger.show(message, "webrtc-request", buttons, BrowserApp.selectedTab.id, aOptions);
});
}
}