/* 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 'Not now' label queueing audio twice behind allow video",
    run: async function testQueuingDenyAudioBehindAllowVideo() {
      let observerPromise = expectObserverCalled("getUserMedia:request");
      let promise = promisePopupNotificationShown("webRTC-shareDevices");
      await promiseRequestDevice(false, true);
      await promiseRequestDevice(true, false);
      await promiseRequestDevice(true, false);
      await promise;
      promise = promisePopupNotificationShown("webRTC-shareDevices");
      checkDeviceSelectors(["camera"]);
      await observerPromise;
      let indicator = promiseIndicatorWindow();

      observerPromise = expectObserverCalled("getUserMedia:request");
      let observerPromise1 = expectObserverCalled(
        "getUserMedia:response:allow"
      );
      let observerPromise2 = expectObserverCalled("recording-device-events");
      await promiseMessage("ok", () => {
        is(
          PopupNotifications.panel.firstElementChild.secondaryButton.label,
          "Block",
          "We offer Block because of no active camera/mic device"
        );
        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 });

      await promise;
      promise = promisePopupNotificationShown("webRTC-shareDevices");
      await observerPromise;
      observerPromise = expectObserverCalled("getUserMedia:request");
      checkDeviceSelectors(["microphone"]);

      observerPromise1 = expectObserverCalled("getUserMedia:response:deny");
      await promiseMessage(permissionError, () => {
        is(
          PopupNotifications.panel.firstElementChild.secondaryButton.label,
          "Not now",
          "We offer Not now option because of an allowed camera/mic device"
        );
        activateSecondaryAction(kActionDeny);
      });

      await observerPromise1;

      await promise;
      await observerPromise;
      checkDeviceSelectors(["microphone"]);

      observerPromise = expectObserverCalled("getUserMedia:response:deny");
      await promiseMessage(permissionError, () => {
        is(
          PopupNotifications.panel.firstElementChild.secondaryButton.label,
          "Not now",
          "We offer Not now option again because of an allowed camera/mic device"
        );
        activateSecondaryAction(kActionDeny);
      });

      await observerPromise;

      // Clean up the active camera in the activePerms map
      webrtcUI.activePerms.delete(gBrowser.selectedBrowser.outerWindowID);

      // close all streams
      await closeStream();
    },
  },

  {
    desc: "test 'Not now'/'Block' label queueing microphone behind screen behind allow camera",
    run: async function testQueuingAudioAndScreenBehindAllowVideo() {
      let observerPromise = expectObserverCalled("getUserMedia:request");
      let promise = promisePopupNotificationShown("webRTC-shareDevices");
      await promiseRequestDevice(false, true);
      await promiseRequestDevice(false, true, null, "screen");
      await promiseRequestDevice(true, false);
      await promise;
      promise = promisePopupNotificationShown("webRTC-shareDevices");
      checkDeviceSelectors(["camera"]);
      await observerPromise;

      observerPromise = expectObserverCalled("getUserMedia:request");
      let observerPromise1 = expectObserverCalled(
        "getUserMedia:response:allow"
      );
      let observerPromise2 = expectObserverCalled("recording-device-events");
      await promiseMessage("ok", () => {
        is(
          PopupNotifications.panel.firstElementChild.secondaryButton.label,
          "Block",
          "We offer Block because of no active camera/mic device"
        );
        PopupNotifications.panel.firstElementChild.button.click();
      });
      await observerPromise1;
      await observerPromise2;
      Assert.deepEqual(
        await getMediaCaptureState(),
        { video: true },
        "expected camera to be shared"
      );
      await checkSharingUI({ audio: false, video: true });

      await promise;
      promise = promisePopupNotificationShown("webRTC-shareDevices");
      await observerPromise;
      observerPromise = expectObserverCalled("getUserMedia:request");
      checkDeviceSelectors(["screen"]);
      observerPromise = expectObserverCalled("getUserMedia:response:deny");
      await promiseMessage(permissionError, () => {
        is(
          PopupNotifications.panel.firstElementChild.secondaryButton.label,
          "Not now",
          "We offer Not now option because we are asking for screen"
        );
        activateSecondaryAction(kActionDeny);
      });
      await observerPromise;

      await promise;
      await observerPromise;
      checkDeviceSelectors(["microphone"]);
      observerPromise = expectObserverCalled("getUserMedia:response:deny");
      await promiseMessage(permissionError, () => {
        is(
          PopupNotifications.panel.firstElementChild.secondaryButton.label,
          "Not now",
          "We offer Not now option because we are asking for mic and cam is already active"
        );
        activateSecondaryAction(kActionDeny);
      });
      await observerPromise;

      // Clean up
      webrtcUI.activePerms.delete(gBrowser.selectedBrowser.outerWindowID);
      // close all streams
      await closeStream();
    },
  },

  {
    desc: "test queueing allow video behind deny audio",
    run: async function testQueuingAllowVideoBehindDenyAudio() {
      let observerPromise = expectObserverCalled("getUserMedia:request");
      let promise = promisePopupNotificationShown("webRTC-shareDevices");
      await promiseRequestDevice(true, false);
      await promiseRequestDevice(false, true);
      await promise;
      promise = promisePopupNotificationShown("webRTC-shareDevices");
      await observerPromise;
      checkDeviceSelectors(["microphone"]);

      let observerPromises = [
        expectObserverCalled("getUserMedia:request"),
        expectObserverCalled("getUserMedia:response:deny"),
      ];

      await promiseMessage(permissionError, () => {
        activateSecondaryAction(kActionDeny);
      });

      await Promise.all(observerPromises);
      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(
        { audio: false, video: true },
        undefined,
        undefined,
        {
          audio: { state: SitePermissions.BLOCK },
        }
      );

      SitePermissions.removeFromPrincipal(
        null,
        "microphone",
        gBrowser.selectedBrowser
      );

      // close all streams
      await closeStream();
    },
  },

  {
    desc: "test queueing allow audio behind allow video with error",
    run: async function testQueuingAllowAudioBehindAllowVideoWithError() {
      let observerPromise = expectObserverCalled("getUserMedia:request");
      let promise = promisePopupNotificationShown("webRTC-shareDevices");
      await promiseRequestDevice(
        false,
        true,
        null,
        null,
        gBrowser.selectedBrowser,
        true
      );
      await promiseRequestDevice(true, false);
      await observerPromise;
      await promise;
      promise = promisePopupNotificationShown("webRTC-shareDevices");

      checkDeviceSelectors(["camera"]);

      let observerPromise1 = expectObserverCalled("getUserMedia:request");
      let observerPromise2 = expectObserverCalled(
        "getUserMedia:response:allow"
      );
      await promiseMessage(badDeviceError, () => {
        PopupNotifications.panel.firstElementChild.button.click();
      });

      await observerPromise1;
      await observerPromise2;
      await promise;
      checkDeviceSelectors(["microphone"]);

      let indicator = promiseIndicatorWindow();

      observerPromise1 = expectObserverCalled("getUserMedia:response:allow");
      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 },
        undefined,
        undefined,
        {
          video: { state: SitePermissions.PROMPT },
        }
      );

      // Clean up the active microphone in the activePerms map
      webrtcUI.activePerms.delete(gBrowser.selectedBrowser.outerWindowID);

      // close all streams
      await closeStream();
    },
  },

  {
    desc: "test queueing audio+video behind deny audio",
    run: async function testQueuingAllowVideoBehindDenyAudio() {
      let observerPromise = expectObserverCalled("getUserMedia:request");
      let promise = promisePopupNotificationShown("webRTC-shareDevices");
      await promiseRequestDevice(true, false);
      await promiseRequestDevice(true, true);
      await promise;
      await observerPromise;
      checkDeviceSelectors(["microphone"]);

      let observerPromises = [
        expectObserverCalled("getUserMedia:request"),
        expectObserverCalled("getUserMedia:response:deny", 2),
        expectObserverCalled("recording-window-ended"),
      ];

      await promiseMessage(
        permissionError,
        () => {
          activateSecondaryAction(kActionDeny);
        },
        2
      );
      await Promise.all(observerPromises);

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

  {
    desc: "test queueing audio, video behind reload after pending audio, video",
    run: async function testQueuingDenyAudioBehindAllowVideo() {
      let observerPromise = expectObserverCalled("getUserMedia:request");
      let promise = promisePopupNotificationShown("webRTC-shareDevices");
      await promiseRequestDevice(true, false);
      await promiseRequestDevice(false, true);
      await promise;
      await observerPromise;
      checkDeviceSelectors(["microphone"]);

      await reloadAndAssertClosedStreams();

      observerPromise = expectObserverCalled("getUserMedia:request");
      // After the reload, gUM(audio) causes a prompt.
      promise = promisePopupNotificationShown("webRTC-shareDevices");
      await promiseRequestDevice(true, false);
      await promiseRequestDevice(false, true);
      await promise;
      await observerPromise;
      checkDeviceSelectors(["microphone"]);

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

      // expect pending camera prompt to appear after ok'ing microphone one.
      observerPromise = expectObserverCalled("getUserMedia:request");
      promise = promisePopupNotificationShown("webRTC-shareDevices");

      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(
        { video: false, audio: true },
        undefined,
        undefined,
        {
          video: { state: SitePermissions.PROMPT },
        }
      );

      await promise;
      await observerPromise;
      checkDeviceSelectors(["camera"]);

      observerPromise1 = expectObserverCalled("getUserMedia:response:allow");
      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 microphone and camera to be shared"
      );

      // close all streams
      await closeStream();
    },
  },
];

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