/**
 * These tests test the ability for the PermissionUI module to open
 * permission prompts to the user. It also tests to ensure that
 * add-ons can introduce their own permission prompts.
 */

"use strict";

ChromeUtils.import("resource://gre/modules/Integration.jsm", this);
ChromeUtils.import("resource:///modules/PermissionUI.jsm", this);
ChromeUtils.import("resource:///modules/SitePermissions.jsm", this);
const { PermissionTestUtils } = ChromeUtils.import(
  "resource://testing-common/PermissionTestUtils.jsm"
);

// Tests that GeolocationPermissionPrompt works as expected
add_task(async function test_geo_permission_prompt() {
  await testPrompt(PermissionUI.GeolocationPermissionPrompt);
});

// Tests that XRPermissionPrompt works as expected
add_task(async function test_xr_permission_prompt() {
  await testPrompt(PermissionUI.XRPermissionPrompt);
});

// Tests that DesktopNotificationPermissionPrompt works as expected
add_task(async function test_desktop_notification_permission_prompt() {
  Services.prefs.setBoolPref(
    "dom.webnotifications.requireuserinteraction",
    false
  );
  Services.prefs.setBoolPref(
    "permissions.desktop-notification.notNow.enabled",
    true
  );
  await testPrompt(PermissionUI.DesktopNotificationPermissionPrompt);
  Services.prefs.clearUserPref("dom.webnotifications.requireuserinteraction");
  Services.prefs.clearUserPref(
    "permissions.desktop-notification.notNow.enabled"
  );
});

// Tests that PersistentStoragePermissionPrompt works as expected
add_task(async function test_persistent_storage_permission_prompt() {
  await testPrompt(PermissionUI.PersistentStoragePermissionPrompt);
});

// Tests that MidiPrompt works as expected
add_task(async function test_midi_permission_prompt() {
  await testPrompt(PermissionUI.MIDIPermissionPrompt);
});

// Tests that StoragePermissionPrompt works as expected
add_task(async function test_storage_access_permission_prompt() {
  Services.prefs.setBoolPref("dom.storage_access.auto_grants", false);
  await testPrompt(PermissionUI.StorageAccessPermissionPrompt);
  Services.prefs.clearUserPref("dom.storage_access.auto_grants");
});

async function testPrompt(Prompt) {
  await BrowserTestUtils.withNewTab(
    {
      gBrowser,
      url: "http://example.com",
    },
    async function(browser) {
      let mockRequest = makeMockPermissionRequest(browser);
      let principal = mockRequest.principal;
      let TestPrompt = new Prompt(mockRequest);
      let permissionKey =
        TestPrompt.usePermissionManager && TestPrompt.permissionKey;

      registerCleanupFunction(function() {
        if (permissionKey) {
          PermissionTestUtils.remove(principal.URI, permissionKey);
        }
      });

      let shownPromise = BrowserTestUtils.waitForEvent(
        PopupNotifications.panel,
        "popupshown"
      );
      TestPrompt.prompt();
      await shownPromise;
      let notification = PopupNotifications.getNotification(
        TestPrompt.notificationID,
        browser
      );
      Assert.ok(notification, "Should have gotten the notification");

      let curPerm;
      if (permissionKey) {
        curPerm = SitePermissions.getForPrincipal(
          principal,
          permissionKey,
          browser
        );
        Assert.equal(
          curPerm.state,
          SitePermissions.UNKNOWN,
          "Should be no permission set to begin with."
        );
      }

      // First test denying the permission request without the checkbox checked.
      let popupNotification = getPopupNotificationNode();
      popupNotification.checkbox.checked = false;

      let isNotificationPrompt =
        Prompt == PermissionUI.DesktopNotificationPermissionPrompt;
      let isPersistentStoragePrompt =
        Prompt == PermissionUI.PersistentStoragePermissionPrompt;

      let expectedSecondaryActionsCount =
        isNotificationPrompt || isPersistentStoragePrompt ? 2 : 1;
      Assert.equal(
        notification.secondaryActions.length,
        expectedSecondaryActionsCount,
        "There should only be " +
          expectedSecondaryActionsCount +
          " secondary action(s)"
      );
      await clickSecondaryAction();
      if (permissionKey) {
        curPerm = SitePermissions.getForPrincipal(
          principal,
          permissionKey,
          browser
        );
        Assert.deepEqual(
          curPerm,
          {
            state: SitePermissions.BLOCK,
            scope: SitePermissions.SCOPE_TEMPORARY,
          },
          "Should have denied the action temporarily"
        );

        Assert.ok(
          mockRequest._cancelled,
          "The request should have been cancelled"
        );
        Assert.ok(
          !mockRequest._allowed,
          "The request should not have been allowed"
        );
      }

      SitePermissions.removeFromPrincipal(
        principal,
        TestPrompt.permissionKey,
        browser
      );
      mockRequest._cancelled = false;

      // Bring the PopupNotification back up now...
      shownPromise = BrowserTestUtils.waitForEvent(
        PopupNotifications.panel,
        "popupshown"
      );
      TestPrompt.prompt();
      await shownPromise;

      // Test denying the permission request with the checkbox checked (for geolocation)
      // or by clicking the "never" option from the dropdown (for notifications and persistent-storage).
      popupNotification = getPopupNotificationNode();
      let secondaryActionToClickIndex = 0;
      if (isNotificationPrompt || isPersistentStoragePrompt) {
        secondaryActionToClickIndex = 1;
      } else {
        popupNotification.checkbox.checked = true;
      }

      Assert.equal(
        notification.secondaryActions.length,
        expectedSecondaryActionsCount,
        "There should only be " +
          expectedSecondaryActionsCount +
          " secondary action(s)"
      );
      await clickSecondaryAction(secondaryActionToClickIndex);
      if (permissionKey) {
        curPerm = PermissionTestUtils.getPermissionObject(
          principal.URI,
          permissionKey
        );
        Assert.equal(
          curPerm.capability,
          Services.perms.DENY_ACTION,
          "Should have denied the action"
        );
        Assert.equal(curPerm.expireTime, 0, "Deny should be permanent");
        Assert.ok(
          mockRequest._cancelled,
          "The request should have been cancelled"
        );
        Assert.ok(
          !mockRequest._allowed,
          "The request should not have been allowed"
        );
      }

      SitePermissions.removeFromPrincipal(
        principal,
        TestPrompt.permissionKey,
        browser
      );
      mockRequest._cancelled = false;

      // Bring the PopupNotification back up now...
      shownPromise = BrowserTestUtils.waitForEvent(
        PopupNotifications.panel,
        "popupshown"
      );
      TestPrompt.prompt();
      await shownPromise;

      // Test allowing the permission request with the checkbox checked.
      popupNotification = getPopupNotificationNode();
      popupNotification.checkbox.checked = true;

      await clickMainAction();
      if (permissionKey) {
        curPerm = PermissionTestUtils.getPermissionObject(
          principal.URI,
          permissionKey
        );
        Assert.equal(
          curPerm.capability,
          Services.perms.ALLOW_ACTION,
          "Should have allowed the action"
        );
        Assert.equal(curPerm.expireTime, 0, "Allow should be permanent");
        Assert.ok(
          !mockRequest._cancelled,
          "The request should not have been cancelled"
        );
        Assert.ok(mockRequest._allowed, "The request should have been allowed");
      }
    }
  );
}
