/* 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/. */

requestLongerTimeout(2);

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+video",
    run: async function checkAudioVideo() {
      let promise = promisePopupNotificationShown("webRTC-shareDevices");
      let observerPromise = expectObserverCalled("getUserMedia:request");
      await promiseRequestDevice(true, true);
      await promise;
      await observerPromise;

      is(
        PopupNotifications.getNotification("webRTC-shareDevices").anchorID,
        "webRTC-shareDevices-notification-icon",
        "anchored to device icon"
      );
      checkDeviceSelectors(["microphone", "camera"]);

      let indicator = promiseIndicatorWindow();
      let observerPromise1 = expectObserverCalled(
        "getUserMedia:response:allow"
      );
      let observerPromise2 = expectObserverCalled("recording-device-events");

      promise = promiseMessage("ok", () => {
        PopupNotifications.panel.firstElementChild.button.click();
      });
      await observerPromise1;
      await observerPromise2;
      await promise;

      Assert.deepEqual(
        await getMediaCaptureState(),
        { audio: true, video: true },
        "expected camera and microphone to be shared"
      );

      await indicator;
      await checkSharingUI({ audio: true, video: true });
      await closeStream();
    },
  },

  {
    desc: "getUserMedia audio only",
    run: async function checkAudioOnly() {
      let observerPromise = expectObserverCalled("getUserMedia:request");
      let promise = promisePopupNotificationShown("webRTC-shareDevices");
      await promiseRequestDevice(true);
      await promise;
      await observerPromise;

      is(
        PopupNotifications.getNotification("webRTC-shareDevices").anchorID,
        "webRTC-shareMicrophone-notification-icon",
        "anchored to mic icon"
      );
      checkDeviceSelectors(["microphone"]);

      let indicator = promiseIndicatorWindow();
      let observerPromise1 = expectObserverCalled(
        "getUserMedia:response:allow"
      );
      let observerPromise2 = expectObserverCalled("recording-device-events");

      promise = promiseMessage("ok", () => {
        PopupNotifications.panel.firstElementChild.button.click();
      });
      await observerPromise1;
      await observerPromise2;
      await promise;
      Assert.deepEqual(
        await getMediaCaptureState(),
        { audio: true },
        "expected microphone to be shared"
      );

      await indicator;
      await checkSharingUI({ audio: true });
      await closeStream();
    },
  },

  {
    desc: "getUserMedia video only",
    run: async function checkVideoOnly() {
      let observerPromise = expectObserverCalled("getUserMedia:request");
      let promise = promisePopupNotificationShown("webRTC-shareDevices");
      await promiseRequestDevice(false, true);
      await promise;
      await observerPromise;

      is(
        PopupNotifications.getNotification("webRTC-shareDevices").anchorID,
        "webRTC-shareDevices-notification-icon",
        "anchored to device icon"
      );
      checkDeviceSelectors(["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(),
        { video: true },
        "expected camera to be shared"
      );

      await indicator;
      await checkSharingUI({ video: true });
      await closeStream();
    },
  },

  {
    desc: 'getUserMedia audio+video, user clicks "Don\'t Share"',
    run: async function checkDontShare() {
      let observerPromise = expectObserverCalled("getUserMedia:request");
      let promise = promisePopupNotificationShown("webRTC-shareDevices");
      await promiseRequestDevice(true, true);
      await promise;
      await observerPromise;
      checkDeviceSelectors(["microphone", "camera"]);

      let observerPromise1 = expectObserverCalled("getUserMedia:response:deny");
      let observerPromise2 = expectObserverCalled("recording-window-ended");
      await promiseMessage(permissionError, () => {
        activateSecondaryAction(kActionDeny);
      });
      await observerPromise1;
      await observerPromise2;

      await checkNotSharing();

      // Verify that we set 'Temporarily blocked' permissions.
      let browser = gBrowser.selectedBrowser;
      let blockedPerms = document.getElementById(
        "blocked-permissions-container"
      );

      let { state, scope } = SitePermissions.getForPrincipal(
        null,
        "camera",
        browser
      );
      Assert.equal(state, SitePermissions.BLOCK);
      Assert.equal(scope, SitePermissions.SCOPE_TEMPORARY);
      ok(
        blockedPerms.querySelector(
          ".blocked-permission-icon.camera-icon[showing=true]"
        ),
        "the blocked camera icon is shown"
      );

      ({ state, scope } = SitePermissions.getForPrincipal(
        null,
        "microphone",
        browser
      ));
      Assert.equal(state, SitePermissions.BLOCK);
      Assert.equal(scope, SitePermissions.SCOPE_TEMPORARY);
      ok(
        blockedPerms.querySelector(
          ".blocked-permission-icon.microphone-icon[showing=true]"
        ),
        "the blocked microphone icon is shown"
      );

      info("requesting devices again to check temporarily blocked permissions");
      promise = promiseMessage(permissionError);
      observerPromise1 = expectObserverCalled("getUserMedia:request");
      observerPromise2 = expectObserverCalled("getUserMedia:response:deny");
      let observerPromise3 = expectObserverCalled("recording-window-ended");
      await promiseRequestDevice(true, true);
      await promise;
      await observerPromise1;
      await observerPromise2;
      await observerPromise3;
      await checkNotSharing();

      SitePermissions.removeFromPrincipal(
        browser.contentPrincipal,
        "camera",
        browser
      );
      SitePermissions.removeFromPrincipal(
        browser.contentPrincipal,
        "microphone",
        browser
      );
    },
  },

  {
    desc: "getUserMedia audio+video: stop sharing",
    run: async function checkStopSharing() {
      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: true, audio: true });

      await stopSharing();

      // the stream is already closed, but this will do some cleanup anyway
      await closeStream(true);

      // After stop sharing, 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"]);

      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+video: reloading the page removes all gUM UI",
    run: async function checkReloading() {
      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: true, audio: true });

      await reloadAndAssertClosedStreams();

      observerPromise = expectObserverCalled("getUserMedia:request");
      // After the reload, gUM(audio+camera) causes a prompt.
      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 prompt: Always/Never Share",
    run: async function checkRememberCheckbox() {
      let elt = id => document.getElementById(id);

      async function checkPerm(
        aRequestAudio,
        aRequestVideo,
        aExpectedAudioPerm,
        aExpectedVideoPerm,
        aNever
      ) {
        let observerPromise = expectObserverCalled("getUserMedia:request");
        let promise = promisePopupNotificationShown("webRTC-shareDevices");
        await promiseRequestDevice(aRequestAudio, aRequestVideo);
        await promise;
        await observerPromise;

        is(
          elt("webRTC-selectMicrophone").hidden,
          !aRequestAudio,
          "microphone selector expected to be " +
            (aRequestAudio ? "visible" : "hidden")
        );

        is(
          elt("webRTC-selectCamera").hidden,
          !aRequestVideo,
          "camera selector expected to be " +
            (aRequestVideo ? "visible" : "hidden")
        );

        let expected = {};
        let observerPromises = [];
        let expectedMessage = aNever ? permissionError : "ok";
        if (expectedMessage == "ok") {
          observerPromises.push(
            expectObserverCalled("getUserMedia:response:allow")
          );
          observerPromises.push(
            expectObserverCalled("recording-device-events")
          );
          if (aRequestVideo) {
            expected.video = true;
          }
          if (aRequestAudio) {
            expected.audio = true;
          }
        } else {
          observerPromises.push(
            expectObserverCalled("getUserMedia:response:deny")
          );
          observerPromises.push(expectObserverCalled("recording-window-ended"));
        }
        await promiseMessage(expectedMessage, () => {
          activateSecondaryAction(aNever ? kActionNever : kActionAlways);
        });
        await Promise.all(observerPromises);
        Assert.deepEqual(
          await getMediaCaptureState(),
          expected,
          "expected " + Object.keys(expected).join(" and ") + " to be shared"
        );

        function checkDevicePermissions(aDevice, aExpected) {
          let uri = gBrowser.selectedBrowser.documentURI;
          let devicePerms = PermissionTestUtils.testExactPermission(
            uri,
            aDevice
          );
          if (aExpected === undefined) {
            is(
              devicePerms,
              Services.perms.UNKNOWN_ACTION,
              "no " + aDevice + " persistent permissions"
            );
          } else {
            is(
              devicePerms,
              aExpected
                ? Services.perms.ALLOW_ACTION
                : Services.perms.DENY_ACTION,
              aDevice + " persistently " + (aExpected ? "allowed" : "denied")
            );
          }
          PermissionTestUtils.remove(uri, aDevice);
        }
        checkDevicePermissions("microphone", aExpectedAudioPerm);
        checkDevicePermissions("camera", aExpectedVideoPerm);

        if (expectedMessage == "ok") {
          await closeStream();
        }
      }

      // 3 cases where the user accepts the device prompt.
      info("audio+video, user grants, expect both Services.perms set to allow");
      await checkPerm(true, true, true, true);
      info(
        "audio only, user grants, check audio perm set to allow, video perm not set"
      );
      await checkPerm(true, false, true, undefined);
      info(
        "video only, user grants, check video perm set to allow, audio perm not set"
      );
      await checkPerm(false, true, undefined, true);

      // 3 cases where the user rejects the device request by using 'Never Share'.
      info(
        "audio only, user denies, expect audio perm set to deny, video not set"
      );
      await checkPerm(true, false, false, undefined, true);
      info(
        "video only, user denies, expect video perm set to deny, audio perm not set"
      );
      await checkPerm(false, true, undefined, false, true);
      info("audio+video, user denies, expect both Services.perms set to deny");
      await checkPerm(true, true, false, false, true);
    },
  },

  {
    desc: "getUserMedia without prompt: use persistent permissions",
    run: async function checkUsePersistentPermissions() {
      async function usePerm(
        aAllowAudio,
        aAllowVideo,
        aRequestAudio,
        aRequestVideo,
        aExpectStream
      ) {
        let uri = gBrowser.selectedBrowser.documentURI;

        if (aAllowAudio !== undefined) {
          PermissionTestUtils.add(
            uri,
            "microphone",
            aAllowAudio
              ? Services.perms.ALLOW_ACTION
              : Services.perms.DENY_ACTION
          );
        }
        if (aAllowVideo !== undefined) {
          PermissionTestUtils.add(
            uri,
            "camera",
            aAllowVideo
              ? Services.perms.ALLOW_ACTION
              : Services.perms.DENY_ACTION
          );
        }

        if (aExpectStream === undefined) {
          // Check that we get a prompt.
          let observerPromise = expectObserverCalled("getUserMedia:request");
          let promise = promisePopupNotificationShown("webRTC-shareDevices");
          await promiseRequestDevice(aRequestAudio, aRequestVideo);
          await promise;
          await observerPromise;

          // Deny the request to cleanup...
          let observerPromise1 = expectObserverCalled(
            "getUserMedia:response:deny"
          );
          let observerPromise2 = expectObserverCalled("recording-window-ended");
          await promiseMessage(permissionError, () => {
            activateSecondaryAction(kActionDeny);
          });
          await observerPromise1;
          await observerPromise2;

          let browser = gBrowser.selectedBrowser;
          SitePermissions.removeFromPrincipal(null, "camera", browser);
          SitePermissions.removeFromPrincipal(null, "microphone", browser);
        } else {
          let expectedMessage = aExpectStream ? "ok" : permissionError;

          let observerPromises = [expectObserverCalled("getUserMedia:request")];
          if (expectedMessage == "ok") {
            observerPromises.push(
              expectObserverCalled("getUserMedia:response:allow"),
              expectObserverCalled("recording-device-events")
            );
          } else {
            observerPromises.push(
              expectObserverCalled("getUserMedia:response:deny"),
              expectObserverCalled("recording-window-ended")
            );
          }

          let promise = promiseMessage(expectedMessage);
          await promiseRequestDevice(aRequestAudio, aRequestVideo);
          await promise;
          await Promise.all(observerPromises);

          if (expectedMessage == "ok") {
            await promiseNoPopupNotification("webRTC-shareDevices");

            // Check what's actually shared.
            let expected = {};
            if (aAllowVideo && aRequestVideo) {
              expected.video = true;
            }
            if (aAllowAudio && aRequestAudio) {
              expected.audio = true;
            }
            Assert.deepEqual(
              await getMediaCaptureState(),
              expected,
              "expected " +
                Object.keys(expected).join(" and ") +
                " to be shared"
            );

            await closeStream();
          }
        }

        PermissionTestUtils.remove(uri, "camera");
        PermissionTestUtils.remove(uri, "microphone");
      }

      // Set both permissions identically
      info("allow audio+video, request audio+video, expect ok (audio+video)");
      await usePerm(true, true, true, true, true);
      info("deny audio+video, request audio+video, expect denied");
      await usePerm(false, false, true, true, false);

      // Allow audio, deny video.
      info("allow audio, deny video, request audio+video, expect denied");
      await usePerm(true, false, true, true, false);
      info("allow audio, deny video, request audio, expect ok (audio)");
      await usePerm(true, false, true, false, true);
      info("allow audio, deny video, request video, expect denied");
      await usePerm(true, false, false, true, false);

      // Deny audio, allow video.
      info("deny audio, allow video, request audio+video, expect denied");
      await usePerm(false, true, true, true, false);
      info("deny audio, allow video, request audio, expect denied");
      await usePerm(false, true, true, false, false);
      info("deny audio, allow video, request video, expect ok (video)");
      await usePerm(false, true, false, true, true);

      // Allow audio, video not set.
      info("allow audio, request audio+video, expect prompt");
      await usePerm(true, undefined, true, true, undefined);
      info("allow audio, request audio, expect ok (audio)");
      await usePerm(true, undefined, true, false, true);
      info("allow audio, request video, expect prompt");
      await usePerm(true, undefined, false, true, undefined);

      // Deny audio, video not set.
      info("deny audio, request audio+video, expect denied");
      await usePerm(false, undefined, true, true, false);
      info("deny audio, request audio, expect denied");
      await usePerm(false, undefined, true, false, false);
      info("deny audio, request video, expect prompt");
      await usePerm(false, undefined, false, true, undefined);

      // Allow video, audio not set.
      info("allow video, request audio+video, expect prompt");
      await usePerm(undefined, true, true, true, undefined);
      info("allow video, request audio, expect prompt");
      await usePerm(undefined, true, true, false, undefined);
      info("allow video, request video, expect ok (video)");
      await usePerm(undefined, true, false, true, true);

      // Deny video, audio not set.
      info("deny video, request audio+video, expect denied");
      await usePerm(undefined, false, true, true, false);
      info("deny video, request audio, expect prompt");
      await usePerm(undefined, false, true, false, undefined);
      info("deny video, request video, expect denied");
      await usePerm(undefined, false, false, true, false);
    },
  },

  {
    desc: "Stop Sharing removes permissions",
    run: async function checkStopSharingRemovesPermissions() {
      async function stopAndCheckPerm(
        aRequestAudio,
        aRequestVideo,
        aStopAudio = aRequestAudio,
        aStopVideo = aRequestVideo
      ) {
        let uri = gBrowser.selectedBrowser.documentURI;

        // Initially set both permissions to 'allow'.
        PermissionTestUtils.add(uri, "microphone", Services.perms.ALLOW_ACTION);
        PermissionTestUtils.add(uri, "camera", Services.perms.ALLOW_ACTION);
        // Also set device-specific temporary allows.
        SitePermissions.setForPrincipal(
          gBrowser.contentPrincipal,
          "microphone^myDevice",
          SitePermissions.ALLOW,
          SitePermissions.SCOPE_TEMPORARY,
          gBrowser.selectedBrowser,
          10000000
        );
        SitePermissions.setForPrincipal(
          gBrowser.contentPrincipal,
          "camera^myDevice2",
          SitePermissions.ALLOW,
          SitePermissions.SCOPE_TEMPORARY,
          gBrowser.selectedBrowser,
          10000000
        );

        if (aRequestAudio || aRequestVideo) {
          let indicator = promiseIndicatorWindow();
          let observerPromise1 = expectObserverCalled("getUserMedia:request");
          let observerPromise2 = expectObserverCalled(
            "getUserMedia:response:allow"
          );
          let observerPromise3 = expectObserverCalled(
            "recording-device-events"
          );
          // Start sharing what's been requested.
          let promise = promiseMessage("ok");
          await promiseRequestDevice(aRequestAudio, aRequestVideo);
          await promise;
          await observerPromise1;
          await observerPromise2;
          await observerPromise3;

          await indicator;
          await checkSharingUI(
            { video: aRequestVideo, audio: aRequestAudio },
            undefined,
            undefined,
            {
              video: { scope: SitePermissions.SCOPE_PERSISTENT },
              audio: { scope: SitePermissions.SCOPE_PERSISTENT },
            }
          );
          await stopSharing(aStopVideo ? "camera" : "microphone");
        } else {
          await revokePermission(aStopVideo ? "camera" : "microphone");
        }

        // Check that permissions have been removed as expected.
        let audioPerm = SitePermissions.getForPrincipal(
          gBrowser.contentPrincipal,
          "microphone",
          gBrowser.selectedBrowser
        );
        let audioPermDevice = SitePermissions.getForPrincipal(
          gBrowser.contentPrincipal,
          "microphone^myDevice",
          gBrowser.selectedBrowser
        );

        if (
          aRequestAudio ||
          aRequestVideo ||
          aStopAudio ||
          (aStopVideo && aRequestAudio)
        ) {
          Assert.deepEqual(
            audioPerm,
            {
              state: SitePermissions.UNKNOWN,
              scope: SitePermissions.SCOPE_PERSISTENT,
            },
            "microphone permissions removed"
          );
          Assert.deepEqual(
            audioPermDevice,
            {
              state: SitePermissions.UNKNOWN,
              scope: SitePermissions.SCOPE_PERSISTENT,
            },
            "microphone device-specific permissions removed"
          );
        } else {
          Assert.deepEqual(
            audioPerm,
            {
              state: SitePermissions.ALLOW,
              scope: SitePermissions.SCOPE_PERSISTENT,
            },
            "microphone permissions untouched"
          );
          Assert.deepEqual(
            audioPermDevice,
            {
              state: SitePermissions.ALLOW,
              scope: SitePermissions.SCOPE_TEMPORARY,
            },
            "microphone device-specific permissions untouched"
          );
        }

        let videoPerm = SitePermissions.getForPrincipal(
          gBrowser.contentPrincipal,
          "camera",
          gBrowser.selectedBrowser
        );
        let videoPermDevice = SitePermissions.getForPrincipal(
          gBrowser.contentPrincipal,
          "camera^myDevice2",
          gBrowser.selectedBrowser
        );
        if (
          aRequestAudio ||
          aRequestVideo ||
          aStopVideo ||
          (aStopAudio && aRequestVideo)
        ) {
          Assert.deepEqual(
            videoPerm,
            {
              state: SitePermissions.UNKNOWN,
              scope: SitePermissions.SCOPE_PERSISTENT,
            },
            "camera permissions removed"
          );
          Assert.deepEqual(
            videoPermDevice,
            {
              state: SitePermissions.UNKNOWN,
              scope: SitePermissions.SCOPE_PERSISTENT,
            },
            "camera device-specific permissions removed"
          );
        } else {
          Assert.deepEqual(
            videoPerm,
            {
              state: SitePermissions.ALLOW,
              scope: SitePermissions.SCOPE_PERSISTENT,
            },
            "camera permissions untouched"
          );
          Assert.deepEqual(
            videoPermDevice,
            {
              state: SitePermissions.ALLOW,
              scope: SitePermissions.SCOPE_TEMPORARY,
            },
            "camera device-specific permissions untouched"
          );
        }
        await checkNotSharing();

        // Cleanup.
        await closeStream(true);

        SitePermissions.removeFromPrincipal(
          gBrowser.contentPrincipal,
          "camera",
          gBrowser.selectedBrowser
        );
        SitePermissions.removeFromPrincipal(
          gBrowser.contentPrincipal,
          "microphone",
          gBrowser.selectedBrowser
        );
      }

      info("request audio+video, stop sharing video resets both");
      await stopAndCheckPerm(true, true);
      info("request audio only, stop sharing audio resets both");
      await stopAndCheckPerm(true, false);
      info("request video only, stop sharing video resets both");
      await stopAndCheckPerm(false, true);
      info("request audio only, stop sharing video resets both");
      await stopAndCheckPerm(true, false, false, true);
      info("request video only, stop sharing audio resets both");
      await stopAndCheckPerm(false, true, true, false);
      info("request neither, stop audio affects audio only");
      await stopAndCheckPerm(false, false, true, false);
      info("request neither, stop video affects video only");
      await stopAndCheckPerm(false, false, false, true);
    },
  },

  {
    desc: "test showPermissionPanel",
    run: async function checkShowPermissionPanel() {
      if (!USING_LEGACY_INDICATOR) {
        // The indicator only links to the permission panel for the
        // legacy indicator.
        return;
      }

      let observerPromise = expectObserverCalled("getUserMedia:request");
      let promise = promisePopupNotificationShown("webRTC-shareDevices");
      await promiseRequestDevice(false, true);
      await promise;
      await observerPromise;
      checkDeviceSelectors(["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(),
        { video: true },
        "expected camera to be shared"
      );

      await indicator;
      await checkSharingUI({ video: true });

      ok(permissionPopupHidden(), "permission panel should be hidden");
      if (IS_MAC) {
        let activeStreams = webrtcUI.getActiveStreams(true, false, false);
        webrtcUI.showSharingDoorhanger(activeStreams[0]);
      } else {
        let win = Services.wm.getMostRecentWindow(
          "Browser:WebRTCGlobalIndicator"
        );

        let elt = win.document.getElementById("audioVideoButton");
        EventUtils.synthesizeMouseAtCenter(elt, {}, win);
      }

      await TestUtils.waitForCondition(
        () => !permissionPopupHidden(),
        "wait for permission panel to open"
      );
      ok(!permissionPopupHidden(), "permission panel should be open");

      gPermissionPanel._permissionPopup.hidePopup();

      await closeStream();
    },
  },

  {
    desc: "'Always Allow' disabled on http pages",
    run: async function checkNoAlwaysOnHttp() {
      // Load an http page instead of the https version.
      await SpecialPowers.pushPrefEnv({
        set: [
          ["media.devices.insecure.enabled", true],
          ["media.getusermedia.insecure.enabled", true],
          // explicitly testing an http page, setting
          // https-first to false.
          ["dom.security.https_first", false],
        ],
      });

      // Disable while loading a new page
      await disableObserverVerification();

      let browser = gBrowser.selectedBrowser;
      BrowserTestUtils.loadURIString(
        browser,
        // eslint-disable-next-line @microsoft/sdl/no-insecure-url
        browser.documentURI.spec.replace("https://", "http://")
      );
      await BrowserTestUtils.browserLoaded(browser);

      await enableObserverVerification();

      // Initially set both permissions to 'allow'.
      let uri = browser.documentURI;
      PermissionTestUtils.add(uri, "microphone", Services.perms.ALLOW_ACTION);
      PermissionTestUtils.add(uri, "camera", Services.perms.ALLOW_ACTION);

      // Request devices and expect a prompt despite the saved 'Allow' permission,
      // because the connection isn't secure.
      let observerPromise = expectObserverCalled("getUserMedia:request");
      let promise = promisePopupNotificationShown("webRTC-shareDevices");
      await promiseRequestDevice(true, true);
      await promise;
      await observerPromise;

      // Ensure that checking the 'Remember this decision' checkbox disables
      // 'Allow'.
      let notification = PopupNotifications.panel.firstElementChild;
      let checkbox = notification.checkbox;
      ok(!!checkbox, "checkbox is present");
      ok(!checkbox.checked, "checkbox is not checked");
      checkbox.click();
      ok(checkbox.checked, "checkbox now checked");
      ok(notification.button.disabled, "Allow button is disabled");
      ok(
        !notification.hasAttribute("warninghidden"),
        "warning message is shown"
      );

      // Cleanup.
      await closeStream(true);
      PermissionTestUtils.remove(uri, "camera");
      PermissionTestUtils.remove(uri, "microphone");
    },
  },
];

add_task(async function test() {
  await runTests(gTests);
});
