Bug 861716 - add a mochitest for gUM request queue in MediaManager. r?jib, florian
MozReview-Commit-ID: 750T4pzvf95
--- a/browser/base/content/test/webrtc/browser.ini
+++ b/browser/base/content/test/webrtc/browser.ini
@@ -13,9 +13,11 @@ skip-if = debug # bug 1369731
[browser_devices_get_user_media_multi_process.js]
skip-if = e10s && (asan || debug) # bug 1347625
[browser_devices_get_user_media_screen.js]
[browser_devices_get_user_media_tear_off_tab.js]
[browser_devices_get_user_media_unprompted_access.js]
[browser_devices_get_user_media_unprompted_access_in_frame.js]
[browser_devices_get_user_media_unprompted_access_tear_off_tab.js]
skip-if = (os == "win" && bits == 64) # win8: bug 1334752
+[browser_devices_get_user_media_unprompted_access_queue_request.js]
[browser_webrtc_hooks.js]
+[browser_devices_get_user_media_queue_request.js]
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/webrtc/browser_devices_get_user_media_queue_request.js
@@ -0,0 +1,158 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const permissionError = "error: NotAllowedError: The request is not allowed " +
+ "by the user agent or the platform in the current context.";
+
+const badDeviceError =
+ "error: NotReadableError: Failed to allocate videosource";
+
+var gTests = [
+
+{
+ desc: "test queueing deny audio behind allow video",
+ run: async function testQueuingDenyAudioBehindAllowVideo() {
+ let promise = promisePopupNotificationShown("webRTC-shareDevices");
+ await promiseRequestDevice(false, true);
+ await promiseRequestDevice(true, false);
+ await promise;
+ promise = promisePopupNotificationShown("webRTC-shareDevices");
+ checkDeviceSelectors(false, true);
+ await expectObserverCalled("getUserMedia:request");
+ let indicator = promiseIndicatorWindow();
+
+ await promiseMessage("ok", () => {
+ PopupNotifications.panel.firstChild.button.click();
+ });
+
+ await expectObserverCalled("getUserMedia:response:allow");
+ await expectObserverCalled("recording-device-events");
+ Assert.deepEqual((await getMediaCaptureState()), {video: true},
+ "expected camera to be shared");
+ await indicator;
+ await checkSharingUI({audio: false, video: true});
+
+ await promise;
+ await expectObserverCalled("getUserMedia:request");
+ checkDeviceSelectors(true, false);
+
+ await promiseMessage(permissionError, () => {
+ activateSecondaryAction(kActionDeny);
+ });
+
+ await expectObserverCalled("getUserMedia:response:deny");
+ SitePermissions.remove(null, "microphone", gBrowser.selectedBrowser);
+
+ // close all streams
+ await closeStream();
+ }
+},
+
+{
+ desc: "test queueing allow video behind deny audio",
+ run: async function testQueuingAllowVideoBehindDenyAudio() {
+ let promise = promisePopupNotificationShown("webRTC-shareDevices");
+ await promiseRequestDevice(true, false);
+ await promiseRequestDevice(false, true);
+ await promise;
+ promise = promisePopupNotificationShown("webRTC-shareDevices");
+ await expectObserverCalled("getUserMedia:request");
+ checkDeviceSelectors(true, false);
+
+ await promiseMessage(permissionError, () => {
+ activateSecondaryAction(kActionDeny);
+ });
+
+ await expectObserverCalled("getUserMedia:response:deny");
+
+ await promise;
+ checkDeviceSelectors(false, true);
+ await expectObserverCalled("getUserMedia:request");
+
+ let indicator = promiseIndicatorWindow();
+
+ await promiseMessage("ok", () => {
+ PopupNotifications.panel.firstChild.button.click();
+ });
+
+ await expectObserverCalled("getUserMedia:response:allow");
+ await expectObserverCalled("recording-device-events");
+ Assert.deepEqual((await getMediaCaptureState()), {video: true},
+ "expected camera to be shared");
+ await indicator;
+ await checkSharingUI({audio: false, video: true});
+
+ SitePermissions.remove(null, "microphone", gBrowser.selectedBrowser);
+
+ // close all streams
+ await closeStream();
+ }
+},
+
+{
+ desc: "test queueing allow audio behind allow video with error",
+ run: async function testQueuingAllowAudioBehindAllowVideoWithError() {
+ let promise = promisePopupNotificationShown("webRTC-shareDevices");
+ await promiseRequestDevice(false, true, null, null, gBrowser.selectedBrowser, true);
+ await promiseRequestDevice(true, false);
+ await promise;
+ promise = promisePopupNotificationShown("webRTC-shareDevices");
+
+ checkDeviceSelectors(false, true);
+
+ await expectObserverCalled("getUserMedia:request");
+
+ await promiseMessage(badDeviceError, () => {
+ PopupNotifications.panel.firstChild.button.click();
+ });
+
+ await expectObserverCalled("getUserMedia:response:allow");
+
+ await promise;
+ checkDeviceSelectors(true, false);
+ await expectObserverCalled("getUserMedia:request");
+ let indicator = promiseIndicatorWindow();
+
+ await promiseMessage("ok", () => {
+ PopupNotifications.panel.firstChild.button.click();
+ });
+
+ await expectObserverCalled("getUserMedia:response:allow");
+ await expectObserverCalled("recording-device-events");
+ Assert.deepEqual((await getMediaCaptureState()), {audio: true},
+ "expected microphone to be shared");
+ await indicator;
+ await checkSharingUI({audio: true, video: false});
+
+ // close all streams
+ await closeStream();
+ }
+},
+
+{
+ desc: "test queueing audio+video behind deny audio",
+ run: async function testQueuingAllowVideoBehindDenyAudio() {
+ let promise = promisePopupNotificationShown("webRTC-shareDevices");
+ await promiseRequestDevice(true, false);
+ await promiseRequestDevice(true, true);
+ await promise;
+ await expectObserverCalled("getUserMedia:request");
+ checkDeviceSelectors(true, false);
+
+ promise = promiseSpecificMessageReceived(permissionError, 2);
+ activateSecondaryAction(kActionDeny);
+ await promise;
+
+ await expectObserverCalled("getUserMedia:request");
+ await expectObserverCalled("getUserMedia:response:deny", 2);
+ await expectObserverCalled("recording-window-ended");
+
+ SitePermissions.remove(null, "microphone", gBrowser.selectedBrowser);
+ }
+}
+
+];
+
+add_task(async function test() {
+ await runTests(gTests);
+});
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/webrtc/browser_devices_get_user_media_unprompted_access_queue_request.js
@@ -0,0 +1,37 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+var gTests = [
+
+{
+ desc: "test queueing allow video behind allow video",
+ run: async function testQueuingAllowVideoBehindAllowVideo() {
+ let promise = promisePopupNotificationShown("webRTC-shareDevices");
+ await promiseRequestDevice(false, true);
+ await promiseRequestDevice(false, true);
+ await promise;
+ checkDeviceSelectors(false, true);
+ await expectObserverCalled("getUserMedia:request");
+
+ let promiseOK = promiseSpecificMessageReceived("ok", 2);
+ PopupNotifications.panel.firstChild.button.click();
+ await promiseOK;
+
+ await promiseNoPopupNotification("webRTC-shareDevices");
+ await expectObserverCalled("getUserMedia:request");
+ await expectObserverCalled("getUserMedia:response:allow", 2);
+ Assert.deepEqual((await getMediaCaptureState()), {video: true},
+ "expected camera to be shared");
+ await expectObserverCalled("recording-device-events", 2);
+
+ // close all streams
+ await closeStream();
+ }
+}
+
+];
+
+add_task(async function test() {
+ SimpleTest.requestCompleteLog();
+ await runTests(gTests);
+});
--- a/browser/base/content/test/webrtc/get_user_media.html
+++ b/browser/base/content/test/webrtc/get_user_media.html
@@ -19,27 +19,41 @@ try {
function message(m) {
document.getElementById("message").innerHTML = m;
window.parent.postMessage(m, "*");
}
var gStreams = [];
-function requestDevice(aAudio, aVideo, aShare) {
+function requestDevice(aAudio, aVideo, aShare, aBadDevice = false) {
var opts = {video: aVideo, audio: aAudio};
if (aShare) {
opts.video = {
mozMediaSource: aShare,
mediaSource: aShare
}
} else if (useFakeStreams) {
opts.fake = true;
}
+ if (aVideo && aBadDevice) {
+ opts.video = {
+ deviceId: "bad device"
+ }
+ opts.fake = true;
+ }
+
+ if (aAudio && aBadDevice) {
+ opts.audio = {
+ deviceId: "bad device"
+ }
+ opts.fake = true;
+ }
+
window.navigator.mediaDevices.getUserMedia(opts)
.then(stream => {
gStreams.push(stream);
message("ok");
}, err => message("error: " + err));
}
message("pending");
--- a/browser/base/content/test/webrtc/get_user_media_content_script.js
+++ b/browser/base/content/test/webrtc/get_user_media_content_script.js
@@ -46,21 +46,22 @@ function observer(aSubject, aTopic, aDat
else
++gObservedTopics[aTopic];
}
kObservedTopics.forEach(topic => {
Services.obs.addObserver(observer, topic);
});
-addMessageListener("Test:ExpectObserverCalled", ({data}) => {
+addMessageListener("Test:ExpectObserverCalled", ({ data: { topic, count } }) => {
sendAsyncMessage("Test:ExpectObserverCalled:Reply",
- {count: gObservedTopics[data]});
- if (data in gObservedTopics)
- --gObservedTopics[data];
+ {count: gObservedTopics[topic]});
+ if (topic in gObservedTopics) {
+ gObservedTopics[topic] -= count;
+ }
});
addMessageListener("Test:ExpectNoObserverCalled", data => {
sendAsyncMessage("Test:ExpectNoObserverCalled:Reply", gObservedTopics);
gObservedTopics = {};
});
function _getMediaCaptureState() {
@@ -115,13 +116,23 @@ addMessageListener("Test:WaitForObserver
if (!(topic in gObservedTopics))
gObservedTopics[topic] = -1;
else
--gObservedTopics[topic];
}
}, topic);
});
+function messageListener({data}) {
+ sendAsyncMessage("Test:MessageReceived", data);
+}
+
addMessageListener("Test:WaitForMessage", () => {
- content.addEventListener("message", ({data}) => {
- sendAsyncMessage("Test:MessageReceived", data);
- }, {once: true});
+ content.addEventListener("message", messageListener, {once: true});
});
+
+addMessageListener("Test:WaitForMultipleMessages", () => {
+ content.addEventListener("message", messageListener);
+});
+
+addMessageListener("Test:StopWaitForMultipleMessages", () => {
+ content.removeEventListener("message", messageListener);
+});
--- a/browser/base/content/test/webrtc/head.js
+++ b/browser/base/content/test/webrtc/head.js
@@ -197,26 +197,26 @@ function promiseObserverCalled(aTopic) {
mm.removeMessageListener("Test:ObserverCalled", listener);
resolve();
}
});
mm.sendAsyncMessage("Test:WaitForObserverCall", aTopic);
});
}
-function expectObserverCalled(aTopic) {
+function expectObserverCalled(aTopic, aCount = 1) {
return new Promise(resolve => {
let mm = _mm();
mm.addMessageListener("Test:ExpectObserverCalled:Reply",
function listener({data}) {
- is(data.count, 1, "expected notification " + aTopic);
+ is(data.count, aCount, "expected notification " + aTopic);
mm.removeMessageListener("Test:ExpectObserverCalled:Reply", listener);
resolve();
});
- mm.sendAsyncMessage("Test:ExpectObserverCalled", aTopic);
+ mm.sendAsyncMessage("Test:ExpectObserverCalled", {topic: aTopic, count: aCount});
});
}
function expectNoObserverCalled() {
return new Promise(resolve => {
let mm = _mm();
mm.addMessageListener("Test:ExpectNoObserverCalled:Reply",
function listener({data}) {
@@ -251,16 +251,34 @@ function promiseMessageReceived() {
mm.addMessageListener("Test:MessageReceived", function listener({data}) {
mm.removeMessageListener("Test:MessageReceived", listener);
resolve(data);
});
mm.sendAsyncMessage("Test:WaitForMessage");
});
}
+function promiseSpecificMessageReceived(aMessage, aCount = 1) {
+ return new Promise(resolve => {
+ let mm = _mm();
+ let counter = 0;
+ mm.addMessageListener("Test:MessageReceived", function listener({data}) {
+ if (data == aMessage) {
+ counter++;
+ if (counter == aCount) {
+ mm.sendAsyncMessage("Test:StopWaitForMultipleMessages");
+ mm.removeMessageListener("Test:MessageReceived", listener);
+ resolve(data);
+ }
+ }
+ });
+ mm.sendAsyncMessage("Test:WaitForMultipleMessages");
+ });
+}
+
function promiseMessage(aMessage, aAction) {
let promise = new Promise((resolve, reject) => {
promiseMessageReceived(aAction).then(data => {
is(data, aMessage, "received " + aMessage);
if (data == aMessage)
resolve();
else
reject();
@@ -365,25 +383,26 @@ async function stopSharing(aType = "came
await expectNoObserverCalled();
if (!aShouldKeepSharing)
await checkNotSharing();
}
function promiseRequestDevice(aRequestAudio, aRequestVideo, aFrameId, aType,
- aBrowser = gBrowser.selectedBrowser) {
+ aBrowser = gBrowser.selectedBrowser,
+ aBadDevice = false) {
info("requesting devices");
return ContentTask.spawn(aBrowser,
- {aRequestAudio, aRequestVideo, aFrameId, aType},
+ {aRequestAudio, aRequestVideo, aFrameId, aType, aBadDevice},
async function(args) {
let global = content.wrappedJSObject;
if (args.aFrameId)
global = global.document.getElementById(args.aFrameId).contentWindow;
- global.requestDevice(args.aRequestAudio, args.aRequestVideo, args.aType);
+ global.requestDevice(args.aRequestAudio, args.aRequestVideo, args.aType, args.aBadDevice);
});
}
async function closeStream(aAlreadyClosed, aFrameId) {
await expectNoObserverCalled();
let promises;
if (!aAlreadyClosed) {