/* Any copyright is dedicated to the Public Domain.
   http://creativecommons.org/publicdomain/zero/1.0/ */

"use strict";

const TEST_ROOT = getRootDirectory(gTestPath).replace(
  "chrome://mochitests/content/",
  "https://example.com/"
);
const TEST_PAGE = TEST_ROOT + "get_user_media.html";

/**
 * Tests that the screen / window sharing permission popup offers the ability
 * for users to silence DOM notifications while sharing.
 */

/**
 * Helper function that exercises a specific browser to test whether or not the
 * user can silence notifications via the display sharing permission panel.
 *
 * First, we ensure that notification silencing is disabled by default. Then, we
 * request screen sharing from the browser, and check the checkbox that
 * silences notifications. Once screen sharing is established, then we ensure
 * that notification silencing is enabled. Then we stop sharing, and ensure that
 * notification silencing is disabled again.
 *
 * @param {<xul:browser>} aBrowser - The window to run the test on. This browser
 *   should have TEST_PAGE loaded.
 * @return Promise
 * @resolves undefined - When the test on the browser is complete.
 */
async function testNotificationSilencing(aBrowser) {
  let hasIndicator = Services.wm
    .getEnumerator("Browser:WebRTCGlobalIndicator")
    .hasMoreElements();

  let window = aBrowser.ownerGlobal;

  let alertsService = Cc["@mozilla.org/alerts-service;1"]
    .getService(Ci.nsIAlertsService)
    .QueryInterface(Ci.nsIAlertsDoNotDisturb);
  Assert.ok(alertsService, "Alerts Service implements nsIAlertsDoNotDisturb");
  Assert.ok(
    !alertsService.suppressForScreenSharing,
    "Should not be silencing notifications to start."
  );

  let observerPromise = expectObserverCalled(
    "getUserMedia:request",
    1,
    aBrowser
  );
  let promise = promisePopupNotificationShown(
    "webRTC-shareDevices",
    null,
    window
  );
  let indicatorPromise = hasIndicator
    ? Promise.resolve()
    : promiseIndicatorWindow();
  await promiseRequestDevice(false, true, null, "screen", aBrowser);
  await promise;
  await observerPromise;

  checkDeviceSelectors(["screen"], window);

  let document = window.document;

  // Select one of the windows / screens. It doesn't really matter which.
  let menulist = document.getElementById("webRTC-selectWindow-menulist");
  menulist.getItemAtIndex(menulist.itemCount - 1).doCommand();

  let notification = window.PopupNotifications.panel.firstElementChild;
  Assert.ok(
    notification.hasAttribute("warninghidden"),
    "Notification silencing warning message is hidden by default"
  );

  let checkbox = notification.checkbox;
  Assert.ok(!!checkbox, "Notification silencing checkbox is present");
  Assert.ok(!checkbox.checked, "checkbox is not checked by default");
  checkbox.click();
  Assert.ok(checkbox.checked, "checkbox now checked");
  // The orginal behaviour of the checkbox disabled the Allow button. Let's
  // make sure we're not still doing that.
  Assert.ok(!notification.button.disabled, "Allow button is not disabled");
  Assert.ok(
    notification.hasAttribute("warninghidden"),
    "No warning message is shown"
  );

  let observerPromise1 = expectObserverCalled(
    "getUserMedia:response:allow",
    1,
    aBrowser
  );
  let observerPromise2 = expectObserverCalled(
    "recording-device-events",
    1,
    aBrowser
  );
  await promiseMessage(
    "ok",
    () => {
      notification.button.click();
    },
    1,
    aBrowser
  );
  await observerPromise1;
  await observerPromise2;
  let indicator = await indicatorPromise;

  Assert.ok(
    alertsService.suppressForScreenSharing,
    "Should now be silencing notifications"
  );

  let indicatorClosedPromise = hasIndicator
    ? Promise.resolve()
    : BrowserTestUtils.domWindowClosed(indicator);

  await stopSharing("screen", true, aBrowser, window);
  await indicatorClosedPromise;

  Assert.ok(
    !alertsService.suppressForScreenSharing,
    "Should no longer be silencing notifications"
  );
}

add_setup(async function () {
  // Set prefs so that permissions prompts are shown and loopback devices
  // are not used. To test the chrome we want prompts to be shown, and
  // these tests are flakey when using loopback devices (though it would
  // be desirable to make them work with loopback in future). See bug 1643711.
  let prefs = [
    [PREF_PERMISSION_FAKE, true],
    [PREF_AUDIO_LOOPBACK, ""],
    [PREF_VIDEO_LOOPBACK, ""],
    [PREF_FAKE_STREAMS, true],
    [PREF_FOCUS_SOURCE, false],
  ];

  await SpecialPowers.pushPrefEnv({ set: prefs });
});

/**
 * Tests notification silencing in a normal browser window.
 */
add_task(async function testNormalWindow() {
  await BrowserTestUtils.withNewTab(
    {
      gBrowser,
      url: TEST_PAGE,
    },
    async browser => {
      await testNotificationSilencing(browser);
    }
  );
});

/**
 * Tests notification silencing in a private browser window.
 */
add_task(async function testPrivateWindow() {
  let privateWindow = await BrowserTestUtils.openNewBrowserWindow({
    private: true,
  });
  await BrowserTestUtils.withNewTab(
    {
      gBrowser: privateWindow.gBrowser,
      url: TEST_PAGE,
    },
    async browser => {
      await testNotificationSilencing(browser);
    }
  );

  await BrowserTestUtils.closeWindow(privateWindow);
});

/**
 * Tests notification silencing when sharing a screen while already
 * sharing the microphone. Alone ensures that if we stop sharing the
 * screen, but continue sharing the microphone, that notification
 * silencing ends.
 */
add_task(async function testWhileSharingMic() {
  await BrowserTestUtils.withNewTab(
    {
      gBrowser,
      url: TEST_PAGE,
    },
    async browser => {
      let promise = promisePopupNotificationShown("webRTC-shareDevices");
      let observerPromise = expectObserverCalled("getUserMedia:request");
      await promiseRequestDevice(true, true);
      await promise;
      await observerPromise;

      let indicatorPromise = 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"
      );

      let indicator = await indicatorPromise;
      await checkSharingUI({ audio: true, video: true });

      await testNotificationSilencing(browser);

      let indicatorClosedPromise = BrowserTestUtils.domWindowClosed(indicator);
      await closeStream();
      await indicatorClosedPromise;
    }
  );
});
