summaryrefslogtreecommitdiffstats
path: root/browser/base/content/test/webrtc/browser_devices_get_user_media_paused.js
diff options
context:
space:
mode:
Diffstat (limited to 'browser/base/content/test/webrtc/browser_devices_get_user_media_paused.js')
-rw-r--r--browser/base/content/test/webrtc/browser_devices_get_user_media_paused.js999
1 files changed, 999 insertions, 0 deletions
diff --git a/browser/base/content/test/webrtc/browser_devices_get_user_media_paused.js b/browser/base/content/test/webrtc/browser_devices_get_user_media_paused.js
new file mode 100644
index 0000000000..27271c2a45
--- /dev/null
+++ b/browser/base/content/test/webrtc/browser_devices_get_user_media_paused.js
@@ -0,0 +1,999 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function setCameraMuted(mute) {
+ return sendObserverNotification(
+ mute ? "getUserMedia:muteVideo" : "getUserMedia:unmuteVideo"
+ );
+}
+
+function setMicrophoneMuted(mute) {
+ return sendObserverNotification(
+ mute ? "getUserMedia:muteAudio" : "getUserMedia:unmuteAudio"
+ );
+}
+
+function sendObserverNotification(topic) {
+ const windowId = gBrowser.selectedBrowser.innerWindowID;
+ return SpecialPowers.spawn(
+ gBrowser.selectedBrowser,
+ [{ topic, windowId }],
+ function (args) {
+ Services.obs.notifyObservers(
+ content.window,
+ args.topic,
+ JSON.stringify(args.windowId)
+ );
+ }
+ );
+}
+
+function setTrackEnabled(audio, video) {
+ return SpecialPowers.spawn(
+ gBrowser.selectedBrowser,
+ [{ audio, video }],
+ function (args) {
+ let stream = content.wrappedJSObject.gStreams[0];
+ if (args.audio != null) {
+ stream.getAudioTracks()[0].enabled = args.audio;
+ }
+ if (args.video != null) {
+ stream.getVideoTracks()[0].enabled = args.video;
+ }
+ }
+ );
+}
+
+async function getVideoTrackMuted() {
+ return SpecialPowers.spawn(
+ gBrowser.selectedBrowser,
+ [],
+ () => content.wrappedJSObject.gStreams[0].getVideoTracks()[0].muted
+ );
+}
+
+async function getVideoTrackEvents() {
+ return SpecialPowers.spawn(
+ gBrowser.selectedBrowser,
+ [],
+ () => content.wrappedJSObject.gVideoEvents
+ );
+}
+
+async function getAudioTrackMuted() {
+ return SpecialPowers.spawn(
+ gBrowser.selectedBrowser,
+ [],
+ () => content.wrappedJSObject.gStreams[0].getAudioTracks()[0].muted
+ );
+}
+
+async function getAudioTrackEvents() {
+ return SpecialPowers.spawn(
+ gBrowser.selectedBrowser,
+ [],
+ () => content.wrappedJSObject.gAudioEvents
+ );
+}
+
+function cloneTracks(audio, video) {
+ return SpecialPowers.spawn(
+ gBrowser.selectedBrowser,
+ [{ audio, video }],
+ function (args) {
+ if (!content.wrappedJSObject.gClones) {
+ content.wrappedJSObject.gClones = [];
+ }
+ let clones = content.wrappedJSObject.gClones;
+ let stream = content.wrappedJSObject.gStreams[0];
+ if (args.audio != null) {
+ clones.push(stream.getAudioTracks()[0].clone());
+ }
+ if (args.video != null) {
+ clones.push(stream.getVideoTracks()[0].clone());
+ }
+ }
+ );
+}
+
+function stopClonedTracks(audio, video) {
+ return SpecialPowers.spawn(
+ gBrowser.selectedBrowser,
+ [{ audio, video }],
+ function (args) {
+ let clones = content.wrappedJSObject.gClones || [];
+ if (args.audio != null) {
+ clones.filter(t => t.kind == "audio").forEach(t => t.stop());
+ }
+ if (args.video != null) {
+ clones.filter(t => t.kind == "video").forEach(t => t.stop());
+ }
+ let liveClones = clones.filter(t => t.readyState == "live");
+ if (!liveClones.length) {
+ delete content.wrappedJSObject.gClones;
+ } else {
+ content.wrappedJSObject.gClones = liveClones;
+ }
+ }
+ );
+}
+
+var gTests = [
+ {
+ desc: "getUserMedia audio+video: disabling the stream shows the paused indicator",
+ run: async function checkDisabled() {
+ let observerPromise = expectObserverCalled("getUserMedia:request");
+ let promise = promisePopupNotificationShown("webRTC-shareDevices");
+ await promiseRequestDevice(true, true);
+ await promise;
+ await observerPromise;
+ checkDeviceSelectors(["microphone", "camera"]);
+
+ let indicator = promiseIndicatorWindow();
+ let observerPromise1 = expectObserverCalled(
+ "getUserMedia:response:allow"
+ );
+ let observerPromise2 = expectObserverCalled("recording-device-events");
+ await promiseMessage("ok", () => {
+ PopupNotifications.panel.firstElementChild.button.click();
+ });
+ await observerPromise1;
+ await observerPromise2;
+ Assert.deepEqual(
+ await getMediaCaptureState(),
+ { audio: true, video: true },
+ "expected camera and microphone to be shared"
+ );
+ await indicator;
+ await checkSharingUI({
+ video: STATE_CAPTURE_ENABLED,
+ audio: STATE_CAPTURE_ENABLED,
+ });
+
+ // Disable both audio and video.
+ observerPromise = expectObserverCalled("recording-device-events", 2);
+ await setTrackEnabled(false, false);
+
+ // Wait for capture state to propagate to the UI asynchronously.
+ await BrowserTestUtils.waitForCondition(
+ () =>
+ window.gPermissionPanel._sharingState.webRTC.camera ==
+ STATE_CAPTURE_DISABLED,
+ "video should be disabled"
+ );
+
+ await observerPromise;
+
+ // The identity UI should show both as disabled.
+ await checkSharingUI({
+ video: STATE_CAPTURE_DISABLED,
+ audio: STATE_CAPTURE_DISABLED,
+ });
+
+ // Enable only audio again.
+ observerPromise = expectObserverCalled("recording-device-events");
+ await setTrackEnabled(true);
+
+ await BrowserTestUtils.waitForCondition(
+ () =>
+ window.gPermissionPanel._sharingState.webRTC.microphone ==
+ STATE_CAPTURE_ENABLED,
+ "audio should be enabled"
+ );
+
+ await observerPromise;
+
+ // The identity UI should show only video as disabled.
+ await checkSharingUI({
+ video: STATE_CAPTURE_DISABLED,
+ audio: STATE_CAPTURE_ENABLED,
+ });
+
+ // Enable video again.
+ observerPromise = expectObserverCalled("recording-device-events");
+ await setTrackEnabled(null, true);
+
+ await BrowserTestUtils.waitForCondition(
+ () =>
+ window.gPermissionPanel._sharingState.webRTC.camera ==
+ STATE_CAPTURE_ENABLED,
+ "video should be enabled"
+ );
+
+ await observerPromise;
+
+ // Both streams should show as running.
+ await checkSharingUI({
+ video: STATE_CAPTURE_ENABLED,
+ audio: STATE_CAPTURE_ENABLED,
+ });
+ await closeStream();
+ },
+ },
+
+ {
+ desc: "getUserMedia audio+video: disabling the original tracks and stopping enabled clones shows the paused indicator",
+ run: async function checkDisabledAfterCloneStop() {
+ let observerPromise = expectObserverCalled("getUserMedia:request");
+ let promise = promisePopupNotificationShown("webRTC-shareDevices");
+ await promiseRequestDevice(true, true);
+ await promise;
+ await observerPromise;
+ checkDeviceSelectors(["microphone", "camera"]);
+
+ let indicator = promiseIndicatorWindow();
+ let observerPromise1 = expectObserverCalled(
+ "getUserMedia:response:allow"
+ );
+ let observerPromise2 = expectObserverCalled("recording-device-events");
+ await promiseMessage("ok", () => {
+ PopupNotifications.panel.firstElementChild.button.click();
+ });
+ await observerPromise1;
+ await observerPromise2;
+ Assert.deepEqual(
+ await getMediaCaptureState(),
+ { audio: true, video: true },
+ "expected camera and microphone to be shared"
+ );
+ await indicator;
+ await checkSharingUI({
+ video: STATE_CAPTURE_ENABLED,
+ audio: STATE_CAPTURE_ENABLED,
+ });
+
+ // Clone audio and video, their state will be enabled
+ await cloneTracks(true, true);
+
+ // Disable both audio and video.
+ await setTrackEnabled(false, false);
+
+ observerPromise = expectObserverCalled("recording-device-events", 2);
+
+ // Stop the clones. This should disable the sharing indicators.
+ await stopClonedTracks(true, true);
+
+ // Wait for capture state to propagate to the UI asynchronously.
+ await BrowserTestUtils.waitForCondition(
+ () =>
+ window.gPermissionPanel._sharingState.webRTC.camera ==
+ STATE_CAPTURE_DISABLED &&
+ window.gPermissionPanel._sharingState.webRTC.microphone ==
+ STATE_CAPTURE_DISABLED,
+ "video and audio should be disabled"
+ );
+
+ await observerPromise;
+
+ // The identity UI should show both as disabled.
+ await checkSharingUI({
+ video: STATE_CAPTURE_DISABLED,
+ audio: STATE_CAPTURE_DISABLED,
+ });
+
+ // Enable only audio again.
+ observerPromise = expectObserverCalled("recording-device-events");
+ await setTrackEnabled(true);
+
+ await BrowserTestUtils.waitForCondition(
+ () =>
+ window.gPermissionPanel._sharingState.webRTC.microphone ==
+ STATE_CAPTURE_ENABLED,
+ "audio should be enabled"
+ );
+
+ await observerPromise;
+
+ // The identity UI should show only video as disabled.
+ await checkSharingUI({
+ video: STATE_CAPTURE_DISABLED,
+ audio: STATE_CAPTURE_ENABLED,
+ });
+
+ // Enable video again.
+ observerPromise = expectObserverCalled("recording-device-events");
+ await setTrackEnabled(null, true);
+
+ await BrowserTestUtils.waitForCondition(
+ () =>
+ window.gPermissionPanel._sharingState.webRTC.camera ==
+ STATE_CAPTURE_ENABLED,
+ "video should be enabled"
+ );
+
+ await observerPromise;
+
+ // Both streams should show as running.
+ await checkSharingUI({
+ video: STATE_CAPTURE_ENABLED,
+ audio: STATE_CAPTURE_ENABLED,
+ });
+ await closeStream();
+ },
+ },
+
+ {
+ desc: "getUserMedia screen: disabling the stream shows the paused indicator",
+ run: async function checkScreenDisabled() {
+ let observerPromise = expectObserverCalled("getUserMedia:request");
+ let promise = promisePopupNotificationShown("webRTC-shareDevices");
+ await promiseRequestDevice(false, true, null, "screen");
+ await promise;
+ await observerPromise;
+
+ is(
+ PopupNotifications.getNotification("webRTC-shareDevices").anchorID,
+ "webRTC-shareScreen-notification-icon",
+ "anchored to device icon"
+ );
+ checkDeviceSelectors(["screen"]);
+
+ let menulist = document.getElementById("webRTC-selectWindow-menulist");
+ menulist.getItemAtIndex(menulist.itemCount - 1).doCommand();
+
+ let indicator = promiseIndicatorWindow();
+ let observerPromise1 = expectObserverCalled(
+ "getUserMedia:response:allow"
+ );
+ let observerPromise2 = expectObserverCalled("recording-device-events");
+ await promiseMessage("ok", () => {
+ PopupNotifications.panel.firstElementChild.button.click();
+ });
+ await observerPromise1;
+ await observerPromise2;
+ Assert.deepEqual(
+ await getMediaCaptureState(),
+ { screen: "Screen" },
+ "expected screen to be shared"
+ );
+
+ await indicator;
+ await checkSharingUI({ screen: "Screen" });
+
+ observerPromise = expectObserverCalled("recording-device-events");
+ await setTrackEnabled(null, false);
+
+ // Wait for capture state to propagate to the UI asynchronously.
+ await BrowserTestUtils.waitForCondition(
+ () =>
+ window.gPermissionPanel._sharingState.webRTC.screen == "ScreenPaused",
+ "screen should be disabled"
+ );
+ await observerPromise;
+ await checkSharingUI({ screen: "ScreenPaused" }, window, {
+ screen: "Screen",
+ });
+
+ observerPromise = expectObserverCalled("recording-device-events");
+ await setTrackEnabled(null, true);
+
+ await BrowserTestUtils.waitForCondition(
+ () => window.gPermissionPanel._sharingState.webRTC.screen == "Screen",
+ "screen should be enabled"
+ );
+ await observerPromise;
+ await checkSharingUI({ screen: "Screen" });
+ await closeStream();
+ },
+ },
+
+ {
+ desc: "getUserMedia audio+video: muting the camera shows the muted indicator",
+ run: async function checkCameraMuted() {
+ let observerPromise = expectObserverCalled("getUserMedia:request");
+ let promise = promisePopupNotificationShown("webRTC-shareDevices");
+ await promiseRequestDevice(true, true);
+ await promise;
+ await observerPromise;
+ checkDeviceSelectors(["microphone", "camera"]);
+
+ let indicator = promiseIndicatorWindow();
+ let observerPromise1 = expectObserverCalled(
+ "getUserMedia:response:allow"
+ );
+ let observerPromise2 = expectObserverCalled("recording-device-events");
+ await promiseMessage("ok", () => {
+ PopupNotifications.panel.firstElementChild.button.click();
+ });
+ await observerPromise1;
+ await observerPromise2;
+ Assert.deepEqual(
+ await getMediaCaptureState(),
+ { audio: true, video: true },
+ "expected camera and microphone to be shared"
+ );
+ await indicator;
+ await checkSharingUI({
+ video: STATE_CAPTURE_ENABLED,
+ audio: STATE_CAPTURE_ENABLED,
+ });
+ is(await getVideoTrackMuted(), false, "video track starts unmuted");
+ Assert.deepEqual(
+ await getVideoTrackEvents(),
+ [],
+ "no video track events fired yet"
+ );
+
+ // Mute camera.
+ observerPromise = expectObserverCalled("recording-device-events");
+ await setCameraMuted(true);
+
+ // Wait for capture state to propagate to the UI asynchronously.
+ await BrowserTestUtils.waitForCondition(
+ () =>
+ window.gPermissionPanel._sharingState.webRTC.camera ==
+ STATE_CAPTURE_DISABLED,
+ "video should be muted"
+ );
+
+ await observerPromise;
+
+ // The identity UI should show only camera as disabled.
+ await checkSharingUI({
+ video: STATE_CAPTURE_DISABLED,
+ audio: STATE_CAPTURE_ENABLED,
+ });
+ is(await getVideoTrackMuted(), true, "video track is muted");
+ Assert.deepEqual(await getVideoTrackEvents(), ["mute"], "mute fired");
+
+ // Unmute video again.
+ observerPromise = expectObserverCalled("recording-device-events");
+ await setCameraMuted(false);
+
+ await BrowserTestUtils.waitForCondition(
+ () =>
+ window.gPermissionPanel._sharingState.webRTC.camera ==
+ STATE_CAPTURE_ENABLED,
+ "video should be enabled"
+ );
+
+ await observerPromise;
+
+ // Both streams should show as running.
+ await checkSharingUI({
+ video: STATE_CAPTURE_ENABLED,
+ audio: STATE_CAPTURE_ENABLED,
+ });
+ is(await getVideoTrackMuted(), false, "video track is unmuted");
+ Assert.deepEqual(
+ await getVideoTrackEvents(),
+ ["mute", "unmute"],
+ "unmute fired"
+ );
+ await closeStream();
+ },
+ },
+
+ {
+ desc: "getUserMedia audio+video: muting the microphone shows the muted indicator",
+ run: async function checkMicrophoneMuted() {
+ let observerPromise = expectObserverCalled("getUserMedia:request");
+ let promise = promisePopupNotificationShown("webRTC-shareDevices");
+ await promiseRequestDevice(true, true);
+ await promise;
+ await observerPromise;
+ checkDeviceSelectors(["microphone", "camera"]);
+
+ let indicator = promiseIndicatorWindow();
+ let observerPromise1 = expectObserverCalled(
+ "getUserMedia:response:allow"
+ );
+ let observerPromise2 = expectObserverCalled("recording-device-events");
+ await promiseMessage("ok", () => {
+ PopupNotifications.panel.firstElementChild.button.click();
+ });
+ await observerPromise1;
+ await observerPromise2;
+ Assert.deepEqual(
+ await getMediaCaptureState(),
+ { audio: true, video: true },
+ "expected camera and microphone to be shared"
+ );
+ await indicator;
+ await checkSharingUI({
+ video: STATE_CAPTURE_ENABLED,
+ audio: STATE_CAPTURE_ENABLED,
+ });
+ is(await getAudioTrackMuted(), false, "audio track starts unmuted");
+ Assert.deepEqual(
+ await getAudioTrackEvents(),
+ [],
+ "no audio track events fired yet"
+ );
+
+ // Mute microphone.
+ observerPromise = expectObserverCalled("recording-device-events");
+ await setMicrophoneMuted(true);
+
+ // Wait for capture state to propagate to the UI asynchronously.
+ await BrowserTestUtils.waitForCondition(
+ () =>
+ window.gPermissionPanel._sharingState.webRTC.microphone ==
+ STATE_CAPTURE_DISABLED,
+ "audio should be muted"
+ );
+
+ await observerPromise;
+
+ // The identity UI should show only microphone as disabled.
+ await checkSharingUI({
+ video: STATE_CAPTURE_ENABLED,
+ audio: STATE_CAPTURE_DISABLED,
+ });
+ is(await getAudioTrackMuted(), true, "audio track is muted");
+ Assert.deepEqual(await getAudioTrackEvents(), ["mute"], "mute fired");
+
+ // Unmute audio again.
+ observerPromise = expectObserverCalled("recording-device-events");
+ await setMicrophoneMuted(false);
+
+ await BrowserTestUtils.waitForCondition(
+ () =>
+ window.gPermissionPanel._sharingState.webRTC.microphone ==
+ STATE_CAPTURE_ENABLED,
+ "audio should be enabled"
+ );
+
+ await observerPromise;
+
+ // Both streams should show as running.
+ await checkSharingUI({
+ video: STATE_CAPTURE_ENABLED,
+ audio: STATE_CAPTURE_ENABLED,
+ });
+ is(await getAudioTrackMuted(), false, "audio track is unmuted");
+ Assert.deepEqual(
+ await getAudioTrackEvents(),
+ ["mute", "unmute"],
+ "unmute fired"
+ );
+ await closeStream();
+ },
+ },
+
+ {
+ desc: "getUserMedia audio+video: disabling & muting camera in combination",
+ // Test the following combinations of disabling and muting camera:
+ // 1. Disable video track only.
+ // 2. Mute camera & disable audio (to have a condition to wait for)
+ // 3. Enable both audio and video tracks (only audio should flow).
+ // 4. Unmute camera again (video should flow).
+ // 5. Mute camera & disable both tracks.
+ // 6. Unmute camera & enable audio (only audio should flow)
+ // 7. Enable video track again (video should flow).
+ run: async function checkDisabledMutedCombination() {
+ let observerPromise = expectObserverCalled("getUserMedia:request");
+ let promise = promisePopupNotificationShown("webRTC-shareDevices");
+ await promiseRequestDevice(true, true);
+ await promise;
+ await observerPromise;
+ checkDeviceSelectors(["microphone", "camera"]);
+
+ let indicator = promiseIndicatorWindow();
+ let observerPromise1 = expectObserverCalled(
+ "getUserMedia:response:allow"
+ );
+ let observerPromise2 = expectObserverCalled("recording-device-events");
+ await promiseMessage("ok", () => {
+ PopupNotifications.panel.firstElementChild.button.click();
+ });
+ await observerPromise1;
+ await observerPromise2;
+ Assert.deepEqual(
+ await getMediaCaptureState(),
+ { audio: true, video: true },
+ "expected camera and microphone to be shared"
+ );
+ await indicator;
+ await checkSharingUI({
+ video: STATE_CAPTURE_ENABLED,
+ audio: STATE_CAPTURE_ENABLED,
+ });
+
+ // 1. Disable video track only.
+ observerPromise = expectObserverCalled("recording-device-events");
+ await setTrackEnabled(null, false);
+
+ // Wait for capture state to propagate to the UI asynchronously.
+ await BrowserTestUtils.waitForCondition(
+ () =>
+ window.gPermissionPanel._sharingState.webRTC.camera ==
+ STATE_CAPTURE_DISABLED,
+ "video should be disabled"
+ );
+
+ await observerPromise;
+
+ // The identity UI should show only video as disabled.
+ await checkSharingUI({
+ video: STATE_CAPTURE_DISABLED,
+ audio: STATE_CAPTURE_ENABLED,
+ });
+ is(await getVideoTrackMuted(), false, "video track still unmuted");
+ Assert.deepEqual(
+ await getVideoTrackEvents(),
+ [],
+ "no video track events fired yet"
+ );
+
+ // 2. Mute camera & disable audio (to have a condition to wait for)
+ observerPromise = expectObserverCalled("recording-device-events", 2);
+ await setCameraMuted(true);
+ await setTrackEnabled(false, null);
+
+ await BrowserTestUtils.waitForCondition(
+ () =>
+ window.gPermissionPanel._sharingState.webRTC.microphone ==
+ STATE_CAPTURE_DISABLED,
+ "audio should be disabled"
+ );
+
+ await observerPromise;
+
+ // The identity UI should show both as disabled.
+ await checkSharingUI({
+ video: STATE_CAPTURE_DISABLED,
+ audio: STATE_CAPTURE_DISABLED,
+ });
+ is(await getVideoTrackMuted(), true, "video track is muted");
+ Assert.deepEqual(
+ await getVideoTrackEvents(),
+ ["mute"],
+ "mute is still fired even though track was disabled"
+ );
+
+ // 3. Enable both audio and video tracks (only audio should flow).
+ observerPromise = expectObserverCalled("recording-device-events", 2);
+ await setTrackEnabled(true, true);
+
+ await BrowserTestUtils.waitForCondition(
+ () =>
+ window.gPermissionPanel._sharingState.webRTC.microphone ==
+ STATE_CAPTURE_ENABLED,
+ "audio should be enabled"
+ );
+
+ await observerPromise;
+
+ // The identity UI should show only audio as enabled, as video is muted.
+ await checkSharingUI({
+ video: STATE_CAPTURE_DISABLED,
+ audio: STATE_CAPTURE_ENABLED,
+ });
+ is(await getVideoTrackMuted(), true, "video track is still muted");
+ Assert.deepEqual(await getVideoTrackEvents(), ["mute"], "no new events");
+
+ // 4. Unmute camera again (video should flow).
+ observerPromise = expectObserverCalled("recording-device-events");
+ await setCameraMuted(false);
+
+ await BrowserTestUtils.waitForCondition(
+ () =>
+ window.gPermissionPanel._sharingState.webRTC.camera ==
+ STATE_CAPTURE_ENABLED,
+ "video should be enabled"
+ );
+
+ await observerPromise;
+
+ // Both streams should show as running.
+ await checkSharingUI({
+ video: STATE_CAPTURE_ENABLED,
+ audio: STATE_CAPTURE_ENABLED,
+ });
+ is(await getVideoTrackMuted(), false, "video track is unmuted");
+ Assert.deepEqual(
+ await getVideoTrackEvents(),
+ ["mute", "unmute"],
+ "unmute fired"
+ );
+
+ // 5. Mute camera & disable both tracks.
+ observerPromise = expectObserverCalled("recording-device-events", 3);
+ await setCameraMuted(true);
+ await setTrackEnabled(false, false);
+
+ await BrowserTestUtils.waitForCondition(
+ () =>
+ window.gPermissionPanel._sharingState.webRTC.camera ==
+ STATE_CAPTURE_DISABLED,
+ "video should be disabled"
+ );
+
+ await observerPromise;
+
+ // The identity UI should show both as disabled.
+ await checkSharingUI({
+ video: STATE_CAPTURE_DISABLED,
+ audio: STATE_CAPTURE_DISABLED,
+ });
+ is(await getVideoTrackMuted(), true, "video track is muted");
+ Assert.deepEqual(
+ await getVideoTrackEvents(),
+ ["mute", "unmute", "mute"],
+ "mute fired afain"
+ );
+
+ // 6. Unmute camera & enable audio (only audio should flow)
+ observerPromise = expectObserverCalled("recording-device-events", 2);
+ await setCameraMuted(false);
+ await setTrackEnabled(true, null);
+
+ await BrowserTestUtils.waitForCondition(
+ () =>
+ window.gPermissionPanel._sharingState.webRTC.microphone ==
+ STATE_CAPTURE_ENABLED,
+ "audio should be enabled"
+ );
+
+ await observerPromise;
+
+ // Only audio should show as running, as video track is still disabled.
+ await checkSharingUI({
+ video: STATE_CAPTURE_DISABLED,
+ audio: STATE_CAPTURE_ENABLED,
+ });
+ is(await getVideoTrackMuted(), false, "video track is unmuted");
+ Assert.deepEqual(
+ await getVideoTrackEvents(),
+ ["mute", "unmute", "mute", "unmute"],
+ "unmute fired even though track is disabled"
+ );
+
+ // 7. Enable video track again (video should flow).
+ observerPromise = expectObserverCalled("recording-device-events");
+ await setTrackEnabled(null, true);
+
+ await BrowserTestUtils.waitForCondition(
+ () =>
+ window.gPermissionPanel._sharingState.webRTC.camera ==
+ STATE_CAPTURE_ENABLED,
+ "video should be enabled"
+ );
+
+ await observerPromise;
+
+ // The identity UI should show both as running again.
+ await checkSharingUI({
+ video: STATE_CAPTURE_ENABLED,
+ audio: STATE_CAPTURE_ENABLED,
+ });
+ is(await getVideoTrackMuted(), false, "video track remains unmuted");
+ Assert.deepEqual(
+ await getVideoTrackEvents(),
+ ["mute", "unmute", "mute", "unmute"],
+ "no new events fired"
+ );
+ await closeStream();
+ },
+ },
+
+ {
+ desc: "getUserMedia audio+video: disabling & muting microphone in combination",
+ // Test the following combinations of disabling and muting microphone:
+ // 1. Disable audio track only.
+ // 2. Mute microphone & disable video (to have a condition to wait for)
+ // 3. Enable both audio and video tracks (only video should flow).
+ // 4. Unmute microphone again (audio should flow).
+ // 5. Mute microphone & disable both tracks.
+ // 6. Unmute microphone & enable video (only video should flow)
+ // 7. Enable audio track again (audio should flow).
+ run: async function checkDisabledMutedCombination() {
+ let observerPromise = expectObserverCalled("getUserMedia:request");
+ let promise = promisePopupNotificationShown("webRTC-shareDevices");
+ await promiseRequestDevice(true, true);
+ await promise;
+ await observerPromise;
+ checkDeviceSelectors(["microphone", "camera"]);
+
+ let indicator = promiseIndicatorWindow();
+ let observerPromise1 = expectObserverCalled(
+ "getUserMedia:response:allow"
+ );
+ let observerPromise2 = expectObserverCalled("recording-device-events");
+ await promiseMessage("ok", () => {
+ PopupNotifications.panel.firstElementChild.button.click();
+ });
+ await observerPromise1;
+ await observerPromise2;
+ Assert.deepEqual(
+ await getMediaCaptureState(),
+ { audio: true, video: true },
+ "expected camera and microphone to be shared"
+ );
+ await indicator;
+ await checkSharingUI({
+ video: STATE_CAPTURE_ENABLED,
+ audio: STATE_CAPTURE_ENABLED,
+ });
+
+ // 1. Disable audio track only.
+ observerPromise = expectObserverCalled("recording-device-events");
+ await setTrackEnabled(false, null);
+
+ // Wait for capture state to propagate to the UI asynchronously.
+ await BrowserTestUtils.waitForCondition(
+ () =>
+ window.gPermissionPanel._sharingState.webRTC.microphone ==
+ STATE_CAPTURE_DISABLED,
+ "audio should be disabled"
+ );
+
+ await observerPromise;
+
+ // The identity UI should show only audio as disabled.
+ await checkSharingUI({
+ video: STATE_CAPTURE_ENABLED,
+ audio: STATE_CAPTURE_DISABLED,
+ });
+ is(await getAudioTrackMuted(), false, "audio track still unmuted");
+ Assert.deepEqual(
+ await getAudioTrackEvents(),
+ [],
+ "no audio track events fired yet"
+ );
+
+ // 2. Mute microphone & disable video (to have a condition to wait for)
+ observerPromise = expectObserverCalled("recording-device-events", 2);
+ await setMicrophoneMuted(true);
+ await setTrackEnabled(null, false);
+
+ await BrowserTestUtils.waitForCondition(
+ () =>
+ window.gPermissionPanel._sharingState.webRTC.camera ==
+ STATE_CAPTURE_DISABLED,
+ "camera should be disabled"
+ );
+
+ await observerPromise;
+
+ // The identity UI should show both as disabled.
+ await checkSharingUI({
+ video: STATE_CAPTURE_DISABLED,
+ audio: STATE_CAPTURE_DISABLED,
+ });
+ is(await getAudioTrackMuted(), true, "audio track is muted");
+ Assert.deepEqual(
+ await getAudioTrackEvents(),
+ ["mute"],
+ "mute is still fired even though track was disabled"
+ );
+
+ // 3. Enable both audio and video tracks (only video should flow).
+ observerPromise = expectObserverCalled("recording-device-events", 2);
+ await setTrackEnabled(true, true);
+
+ await BrowserTestUtils.waitForCondition(
+ () =>
+ window.gPermissionPanel._sharingState.webRTC.camera ==
+ STATE_CAPTURE_ENABLED,
+ "video should be enabled"
+ );
+
+ await observerPromise;
+
+ // The identity UI should show only video as enabled, as audio is muted.
+ await checkSharingUI({
+ video: STATE_CAPTURE_ENABLED,
+ audio: STATE_CAPTURE_DISABLED,
+ });
+ is(await getAudioTrackMuted(), true, "audio track is still muted");
+ Assert.deepEqual(await getAudioTrackEvents(), ["mute"], "no new events");
+
+ // 4. Unmute microphone again (audio should flow).
+ observerPromise = expectObserverCalled("recording-device-events");
+ await setMicrophoneMuted(false);
+
+ await BrowserTestUtils.waitForCondition(
+ () =>
+ window.gPermissionPanel._sharingState.webRTC.microphone ==
+ STATE_CAPTURE_ENABLED,
+ "audio should be enabled"
+ );
+
+ await observerPromise;
+
+ // Both streams should show as running.
+ await checkSharingUI({
+ video: STATE_CAPTURE_ENABLED,
+ audio: STATE_CAPTURE_ENABLED,
+ });
+ is(await getAudioTrackMuted(), false, "audio track is unmuted");
+ Assert.deepEqual(
+ await getAudioTrackEvents(),
+ ["mute", "unmute"],
+ "unmute fired"
+ );
+
+ // 5. Mute microphone & disable both tracks.
+ observerPromise = expectObserverCalled("recording-device-events", 3);
+ await setMicrophoneMuted(true);
+ await setTrackEnabled(false, false);
+
+ await BrowserTestUtils.waitForCondition(
+ () =>
+ window.gPermissionPanel._sharingState.webRTC.microphone ==
+ STATE_CAPTURE_DISABLED,
+ "audio should be disabled"
+ );
+
+ await observerPromise;
+
+ // The identity UI should show both as disabled.
+ await checkSharingUI({
+ video: STATE_CAPTURE_DISABLED,
+ audio: STATE_CAPTURE_DISABLED,
+ });
+ is(await getAudioTrackMuted(), true, "audio track is muted");
+ Assert.deepEqual(
+ await getAudioTrackEvents(),
+ ["mute", "unmute", "mute"],
+ "mute fired again"
+ );
+
+ // 6. Unmute microphone & enable video (only video should flow)
+ observerPromise = expectObserverCalled("recording-device-events", 2);
+ await setMicrophoneMuted(false);
+ await setTrackEnabled(null, true);
+
+ await BrowserTestUtils.waitForCondition(
+ () =>
+ window.gPermissionPanel._sharingState.webRTC.camera ==
+ STATE_CAPTURE_ENABLED,
+ "video should be enabled"
+ );
+
+ await observerPromise;
+
+ // Only video should show as running, as audio track is still disabled.
+ await checkSharingUI({
+ video: STATE_CAPTURE_ENABLED,
+ audio: STATE_CAPTURE_DISABLED,
+ });
+ is(await getAudioTrackMuted(), false, "audio track is unmuted");
+ Assert.deepEqual(
+ await getAudioTrackEvents(),
+ ["mute", "unmute", "mute", "unmute"],
+ "unmute fired even though track is disabled"
+ );
+
+ // 7. Enable audio track again (audio should flow).
+ observerPromise = expectObserverCalled("recording-device-events");
+ await setTrackEnabled(true, null);
+
+ await BrowserTestUtils.waitForCondition(
+ () =>
+ window.gPermissionPanel._sharingState.webRTC.microphone ==
+ STATE_CAPTURE_ENABLED,
+ "audio should be enabled"
+ );
+
+ await observerPromise;
+
+ // The identity UI should show both as running again.
+ await checkSharingUI({
+ video: STATE_CAPTURE_ENABLED,
+ audio: STATE_CAPTURE_ENABLED,
+ });
+ is(await getAudioTrackMuted(), false, "audio track remains unmuted");
+ Assert.deepEqual(
+ await getAudioTrackEvents(),
+ ["mute", "unmute", "mute", "unmute"],
+ "no new events fired"
+ );
+ await closeStream();
+ },
+ },
+];
+
+add_task(async function test() {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["media.getusermedia.camera.off_while_disabled.delay_ms", 0],
+ ["media.getusermedia.microphone.off_while_disabled.delay_ms", 0],
+ ],
+ });
+
+ SimpleTest.requestCompleteLog();
+ await runTests(gTests);
+});