/* 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/. */ const permissionError = "error: NotAllowedError: The request is not allowed " + "by the user agent or the platform in the current context."; var gTests = [ { desc: "getUserMedia audio+camera", run: async function checkAudioVideoWhileLiveTracksExist_audio_camera() { let promise = promisePopupNotificationShown("webRTC-shareDevices"); let observerPromise = expectObserverCalled("getUserMedia:request"); await promiseRequestDevice(true, true); await promise; await observerPromise; 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({ audio: true, video: true }); // If there's an active audio+camera stream, // gUM(audio+camera) returns a stream without prompting; let observerPromises = [ expectObserverCalled("getUserMedia:request"), expectObserverCalled("getUserMedia:response:allow"), expectObserverCalled("recording-device-events"), ]; promise = promiseMessage("ok"); await promiseRequestDevice(true, true); await promise; await Promise.all(observerPromises); await promiseNoPopupNotification("webRTC-shareDevices"); Assert.deepEqual( await getMediaCaptureState(), { audio: true, video: true }, "expected camera and microphone to be shared" ); await checkSharingUI({ audio: true, video: true }); // gUM(screen) causes a prompt. observerPromise = expectObserverCalled("getUserMedia:request"); 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"]); observerPromise = expectObserverCalled("getUserMedia:response:deny"); await promiseMessage(permissionError, () => { activateSecondaryAction(kActionDeny); }); await observerPromise; // Revoke screen block (only). Don't over-revoke ahead of remaining steps. SitePermissions.removeFromPrincipal( null, "screen", gBrowser.selectedBrowser ); // After closing all streams, gUM(audio+camera) causes a prompt. await closeStream(); observerPromise = expectObserverCalled("getUserMedia:request"); promise = promisePopupNotificationShown("webRTC-shareDevices"); await promiseRequestDevice(true, true); await promise; await observerPromise; checkDeviceSelectors(["microphone", "camera"]); observerPromise1 = expectObserverCalled("getUserMedia:response:deny"); observerPromise2 = expectObserverCalled("recording-window-ended"); await promiseMessage(permissionError, () => { activateSecondaryAction(kActionDeny); }); await observerPromise1; await observerPromise2; await checkNotSharing(); SitePermissions.removeFromPrincipal( null, "screen", gBrowser.selectedBrowser ); SitePermissions.removeFromPrincipal( null, "camera", gBrowser.selectedBrowser ); SitePermissions.removeFromPrincipal( null, "microphone", gBrowser.selectedBrowser ); }, }, { desc: "getUserMedia audio+camera -camera", run: async function checkAudioVideoWhileLiveTracksExist_audio_nocamera() { // State: fresh { const popupShown = promisePopupNotificationShown("webRTC-shareDevices"); const request = expectObserverCalled("getUserMedia:request"); await promiseRequestDevice(true, true); await popupShown; await request; const indicator = promiseIndicatorWindow(); const response = expectObserverCalled("getUserMedia:response:allow"); const deviceEvents = expectObserverCalled("recording-device-events"); await promiseMessage("ok", () => { PopupNotifications.panel.firstElementChild.button.click(); }); await response; await deviceEvents; Assert.deepEqual( await getMediaCaptureState(), { audio: true, video: true }, "expected camera and microphone to be shared" ); await indicator; await checkSharingUI({ audio: true, video: true }); // Stop the camera track. await stopTracks("video"); await checkSharingUI({ audio: true, video: false }); } // State: live audio { // If there's an active audio track from an audio+camera request, // gUM(camera) causes a prompt. const request = expectObserverCalled("getUserMedia:request"); const popupShown = promisePopupNotificationShown("webRTC-shareDevices"); await promiseRequestDevice(false, true); await popupShown; await request; checkDeviceSelectors(["camera"]); // Allow and stop the camera again. const response = expectObserverCalled("getUserMedia:response:allow"); const deviceEvents = expectObserverCalled("recording-device-events"); await promiseMessage("ok", () => { PopupNotifications.panel.firstElementChild.button.click(); }); await response; await deviceEvents; Assert.deepEqual( await getMediaCaptureState(), { audio: true, video: true }, "expected camera and microphone to be shared" ); await checkSharingUI({ audio: true, video: true }); await stopTracks("video"); await checkSharingUI({ audio: true, video: false }); } // State: live audio { // If there's an active audio track from an audio+camera request, // gUM(audio+camera) causes a prompt. const request = expectObserverCalled("getUserMedia:request"); const popupShown = promisePopupNotificationShown("webRTC-shareDevices"); await promiseRequestDevice(true, true); await popupShown; await request; checkDeviceSelectors(["microphone", "camera"]); // Allow and stop the camera again. const response = expectObserverCalled("getUserMedia:response:allow"); const deviceEvents = expectObserverCalled("recording-device-events"); await promiseMessage("ok", () => { PopupNotifications.panel.firstElementChild.button.click(); }); await response; await deviceEvents; Assert.deepEqual( await getMediaCaptureState(), { audio: true, video: true }, "expected camera and microphone to be shared" ); await checkSharingUI({ audio: true, video: true }); await stopTracks("video"); await checkSharingUI({ audio: true, video: false }); } // State: live audio { // After closing all streams, gUM(audio) causes a prompt. await closeStream(); const request = expectObserverCalled("getUserMedia:request"); const popupShown = promisePopupNotificationShown("webRTC-shareDevices"); await promiseRequestDevice(true, false); await popupShown; await request; checkDeviceSelectors(["microphone"]); const response = expectObserverCalled("getUserMedia:response:deny"); const windowEnded = expectObserverCalled("recording-window-ended"); await promiseMessage(permissionError, () => { activateSecondaryAction(kActionDeny); }); await response; await windowEnded; await checkNotSharing(); SitePermissions.removeFromPrincipal( null, "camera", gBrowser.selectedBrowser ); SitePermissions.removeFromPrincipal( null, "microphone", gBrowser.selectedBrowser ); } }, }, { desc: "getUserMedia audio+camera -audio", run: async function checkAudioVideoWhileLiveTracksExist_camera_noaudio() { // State: fresh { const popupShown = promisePopupNotificationShown("webRTC-shareDevices"); const request = expectObserverCalled("getUserMedia:request"); await promiseRequestDevice(true, true); await popupShown; await request; const indicator = promiseIndicatorWindow(); const response = expectObserverCalled("getUserMedia:response:allow"); const deviceEvents = expectObserverCalled("recording-device-events"); await promiseMessage("ok", () => { PopupNotifications.panel.firstElementChild.button.click(); }); await response; await deviceEvents; Assert.deepEqual( await getMediaCaptureState(), { audio: true, video: true }, "expected camera and microphone to be shared" ); await indicator; await checkSharingUI({ audio: true, video: true }); // Stop the audio track. await stopTracks("audio"); await checkSharingUI({ audio: false, video: true }); } // State: live camera { // If there's an active video track from an audio+camera request, // gUM(audio) causes a prompt. const request = expectObserverCalled("getUserMedia:request"); const popupShown = promisePopupNotificationShown("webRTC-shareDevices"); await promiseRequestDevice(true, false); await popupShown; await request; checkDeviceSelectors(["microphone"]); // Allow and stop the microphone again. const response = expectObserverCalled("getUserMedia:response:allow"); const deviceEvents = expectObserverCalled("recording-device-events"); await promiseMessage("ok", () => { PopupNotifications.panel.firstElementChild.button.click(); }); await response; await deviceEvents; Assert.deepEqual( await getMediaCaptureState(), { audio: true, video: true }, "expected camera and microphone to be shared" ); await checkSharingUI({ audio: true, video: true }); await stopTracks("audio"); await checkSharingUI({ audio: false, video: true }); } // State: live camera { // If there's an active video track from an audio+camera request, // gUM(audio+camera) causes a prompt. const request = expectObserverCalled("getUserMedia:request"); const popupShown = promisePopupNotificationShown("webRTC-shareDevices"); await promiseRequestDevice(true, true); await popupShown; await request; checkDeviceSelectors(["microphone", "camera"]); // Allow and stop the microphone again. const response = expectObserverCalled("getUserMedia:response:allow"); const deviceEvents = expectObserverCalled("recording-device-events"); await promiseMessage("ok", () => { PopupNotifications.panel.firstElementChild.button.click(); }); await response; await deviceEvents; Assert.deepEqual( await getMediaCaptureState(), { audio: true, video: true }, "expected camera and microphone to be shared" ); await checkSharingUI({ audio: true, video: true }); await stopTracks("audio"); await checkSharingUI({ audio: false, video: true }); } // State: live camera { // After closing all streams, gUM(camera) causes a prompt. await closeStream(); const request = expectObserverCalled("getUserMedia:request"); const popupShown = promisePopupNotificationShown("webRTC-shareDevices"); await promiseRequestDevice(false, true); await popupShown; await request; checkDeviceSelectors(["camera"]); const response = expectObserverCalled("getUserMedia:response:deny"); const windowEnded = expectObserverCalled("recording-window-ended"); await promiseMessage(permissionError, () => { activateSecondaryAction(kActionDeny); }); await response; await windowEnded; await checkNotSharing(); SitePermissions.removeFromPrincipal( null, "camera", gBrowser.selectedBrowser ); SitePermissions.removeFromPrincipal( null, "microphone", gBrowser.selectedBrowser ); } }, }, { desc: "getUserMedia camera", run: async function checkAudioVideoWhileLiveTracksExist_camera() { let observerPromise = expectObserverCalled("getUserMedia:request"); let promise = promisePopupNotificationShown("webRTC-shareDevices"); await promiseRequestDevice(false, true); await promise; await observerPromise; 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(), { video: true }, "expected camera to be shared" ); await indicator; await checkSharingUI({ audio: false, video: true }); // If there's an active camera stream, // gUM(audio) causes a prompt; observerPromise = expectObserverCalled("getUserMedia:request"); promise = promisePopupNotificationShown("webRTC-shareDevices"); await promiseRequestDevice(true, false); await promise; await observerPromise; checkDeviceSelectors(["microphone"]); observerPromise = expectObserverCalled("getUserMedia:response:deny"); await promiseMessage(permissionError, () => { activateSecondaryAction(kActionDeny); }); await observerPromise; SitePermissions.removeFromPrincipal( null, "screen", gBrowser.selectedBrowser ); SitePermissions.removeFromPrincipal( null, "camera", gBrowser.selectedBrowser ); SitePermissions.removeFromPrincipal( null, "microphone", gBrowser.selectedBrowser ); // gUM(audio+camera) causes a prompt; observerPromise = expectObserverCalled("getUserMedia:request"); promise = promisePopupNotificationShown("webRTC-shareDevices"); await promiseRequestDevice(true, true); await promise; await observerPromise; checkDeviceSelectors(["microphone", "camera"]); observerPromise = expectObserverCalled("getUserMedia:response:deny"); await promiseMessage(permissionError, () => { activateSecondaryAction(kActionDeny); }); await observerPromise; SitePermissions.removeFromPrincipal( null, "screen", gBrowser.selectedBrowser ); SitePermissions.removeFromPrincipal( null, "camera", gBrowser.selectedBrowser ); SitePermissions.removeFromPrincipal( null, "microphone", gBrowser.selectedBrowser ); // gUM(screen) causes a prompt; observerPromise = expectObserverCalled("getUserMedia:request"); 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"]); observerPromise = expectObserverCalled("getUserMedia:response:deny"); await promiseMessage(permissionError, () => { activateSecondaryAction(kActionDeny); }); await observerPromise; SitePermissions.removeFromPrincipal( null, "screen", gBrowser.selectedBrowser ); SitePermissions.removeFromPrincipal( null, "camera", gBrowser.selectedBrowser ); SitePermissions.removeFromPrincipal( null, "microphone", gBrowser.selectedBrowser ); // gUM(camera) returns a stream without prompting. let observerPromises = [ expectObserverCalled("getUserMedia:request"), expectObserverCalled("getUserMedia:response:allow"), expectObserverCalled("recording-device-events"), ]; promise = promiseMessage("ok"); await promiseRequestDevice(false, true); await promise; await Promise.all(observerPromises); await promiseNoPopupNotification("webRTC-shareDevices"); Assert.deepEqual( await getMediaCaptureState(), { video: true }, "expected camera to be shared" ); await checkSharingUI({ audio: false, video: true }); // close all streams await closeStream(); }, }, { desc: "getUserMedia audio", run: async function checkAudioVideoWhileLiveTracksExist_audio() { let observerPromise = expectObserverCalled("getUserMedia:request"); let promise = promisePopupNotificationShown("webRTC-shareDevices"); await promiseRequestDevice(true, false); await promise; await observerPromise; 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 }, "expected microphone to be shared" ); await indicator; await checkSharingUI({ audio: true, video: false }); // If there's an active audio stream, // gUM(camera) causes a prompt; observerPromise = expectObserverCalled("getUserMedia:request"); promise = promisePopupNotificationShown("webRTC-shareDevices"); await promiseRequestDevice(false, true); await promise; await observerPromise; checkDeviceSelectors(["camera"]); observerPromise = expectObserverCalled("getUserMedia:response:deny"); await promiseMessage(permissionError, () => { activateSecondaryAction(kActionDeny); }); await observerPromise; SitePermissions.removeFromPrincipal( null, "screen", gBrowser.selectedBrowser ); SitePermissions.removeFromPrincipal( null, "camera", gBrowser.selectedBrowser ); SitePermissions.removeFromPrincipal( null, "microphone", gBrowser.selectedBrowser ); // gUM(audio+camera) causes a prompt; observerPromise = expectObserverCalled("getUserMedia:request"); promise = promisePopupNotificationShown("webRTC-shareDevices"); await promiseRequestDevice(true, true); await promise; await observerPromise; checkDeviceSelectors(["microphone", "camera"]); observerPromise = expectObserverCalled("getUserMedia:response:deny"); await promiseMessage(permissionError, () => { activateSecondaryAction(kActionDeny); }); await observerPromise; SitePermissions.removeFromPrincipal( null, "screen", gBrowser.selectedBrowser ); SitePermissions.removeFromPrincipal( null, "camera", gBrowser.selectedBrowser ); SitePermissions.removeFromPrincipal( null, "microphone", gBrowser.selectedBrowser ); // gUM(audio) returns a stream without prompting. let observerPromises = [ expectObserverCalled("getUserMedia:request"), expectObserverCalled("getUserMedia:response:allow"), expectObserverCalled("recording-device-events"), ]; promise = promiseMessage("ok"); await promiseRequestDevice(true, false); await promise; await observerPromises; await promiseNoPopupNotification("webRTC-shareDevices"); Assert.deepEqual( await getMediaCaptureState(), { audio: true }, "expected microphone to be shared" ); await checkSharingUI({ audio: true, video: false }); // close all streams await closeStream(); }, }, ]; add_task(async function test() { await runTests(gTests); });