text: /* Any copyright is dedicated to the Public Domain.
   * http://creativecommons.org/publicdomain/zero/1.0/
   */
  "use strict";
  
  const { SitePermissions } = ChromeUtils.import(
    "resource:///modules/SitePermissions.jsm"
  );
  const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
  
  const RESIST_FINGERPRINTING_ENABLED = Services.prefs.getBoolPref(
    "privacy.resistFingerprinting"
  );
  const HTTPS_ONLY_MODE_ENABLED = Services.prefs.getBoolPref(
    "dom.security.https_only_mode"
  );
  const MIDI_ENABLED = Services.prefs.getBoolPref("dom.webmidi.enabled");
  
  add_task(async function testPermissionsListing() {
    let expectedPermissions = [
      "autoplay-media",
      "camera",
      "cookie",
      "desktop-notification",
      "focus-tab-by-prompt",
      "geo",
      "install",
      "microphone",
      "popup",
      "screen",
      "shortcuts",
      "persistent-storage",
      "storage-access",
      "xr",
    ];
    if (RESIST_FINGERPRINTING_ENABLED) {
      // Canvas permission should be hidden unless privacy.resistFingerprinting
      // is true.
      expectedPermissions.push("canvas");
    }
    if (HTTPS_ONLY_MODE_ENABLED) {
      // Exception permission should be hidden unless HTTPS-Only Mode is enabled
      expectedPermissions.push("https-only-load-insecure");
    }
    if (MIDI_ENABLED) {
      // Should remove this checking and add it as default after it is fully pref'd-on.
      expectedPermissions.push("midi");
      expectedPermissions.push("midi-sysex");
    }
    Assert.deepEqual(
      SitePermissions.listPermissions().sort(),
      expectedPermissions.sort(),
      "Correct list of all permissions"
    );
  });
  
  add_task(async function testGetAllByPrincipal() {
    // check that it returns an empty array on an invalid principal
    // like a principal with an about URI, which doesn't support site permissions
    let wrongPrincipal = Services.scriptSecurityManager.createContentPrincipalFromOrigin(
      "about:config"
    );
    Assert.deepEqual(SitePermissions.getAllByPrincipal(wrongPrincipal), []);
  
    let principal = Services.scriptSecurityManager.createContentPrincipalFromOrigin(
      "https://example.com"
    );
    Assert.deepEqual(SitePermissions.getAllByPrincipal(principal), []);
  
    SitePermissions.setForPrincipal(principal, "camera", SitePermissions.ALLOW);
    Assert.deepEqual(SitePermissions.getAllByPrincipal(principal), [
      {
        id: "camera",
        state: SitePermissions.ALLOW,
        scope: SitePermissions.SCOPE_PERSISTENT,
      },
    ]);
  
    SitePermissions.setForPrincipal(
      principal,
      "microphone",
      SitePermissions.ALLOW,
      SitePermissions.SCOPE_SESSION
    );
    SitePermissions.setForPrincipal(
      principal,
      "desktop-notification",
      SitePermissions.BLOCK
    );
  
    Assert.deepEqual(SitePermissions.getAllByPrincipal(principal), [
      {
        id: "camera",
        state: SitePermissions.ALLOW,
        scope: SitePermissions.SCOPE_PERSISTENT,
      },
      {
        id: "microphone",
        state: SitePermissions.ALLOW,
        scope: SitePermissions.SCOPE_SESSION,
      },
      {
        id: "desktop-notification",
        state: SitePermissions.BLOCK,
        scope: SitePermissions.SCOPE_PERSISTENT,
      },
    ]);
  
    SitePermissions.removeFromPrincipal(principal, "microphone");
    Assert.deepEqual(SitePermissions.getAllByPrincipal(principal), [
      {
        id: "camera",
        state: SitePermissions.ALLOW,
        scope: SitePermissions.SCOPE_PERSISTENT,
      },
      {
        id: "desktop-notification",
        state: SitePermissions.BLOCK,
        scope: SitePermissions.SCOPE_PERSISTENT,
      },
    ]);
  
    SitePermissions.removeFromPrincipal(principal, "camera");
    SitePermissions.removeFromPrincipal(principal, "desktop-notification");
    Assert.deepEqual(SitePermissions.getAllByPrincipal(principal), []);
  
    Assert.equal(Services.prefs.getIntPref("permissions.default.shortcuts"), 0);
    SitePermissions.setForPrincipal(
      principal,
      "shortcuts",
      SitePermissions.BLOCK
    );
  
    // Customized preference should have been enabled, but the default should not.
    Assert.equal(Services.prefs.getIntPref("permissions.default.shortcuts"), 0);
    Assert.deepEqual(SitePermissions.getAllByPrincipal(principal), [
      {
        id: "shortcuts",
        state: SitePermissions.BLOCK,
        scope: SitePermissions.SCOPE_PERSISTENT,
      },
    ]);
  
    SitePermissions.removeFromPrincipal(principal, "shortcuts");
    Services.prefs.clearUserPref("permissions.default.shortcuts");
  });
  
  add_task(async function testGetAvailableStates() {
    Assert.deepEqual(SitePermissions.getAvailableStates("camera"), [
      SitePermissions.UNKNOWN,
      SitePermissions.ALLOW,
      SitePermissions.BLOCK,
    ]);
  
    // Test available states with a default permission set.
    Services.prefs.setIntPref(
      "permissions.default.camera",
      SitePermissions.ALLOW
    );
    Assert.deepEqual(SitePermissions.getAvailableStates("camera"), [
      SitePermissions.PROMPT,
      SitePermissions.ALLOW,
      SitePermissions.BLOCK,
    ]);
    Services.prefs.clearUserPref("permissions.default.camera");
  
    Assert.deepEqual(SitePermissions.getAvailableStates("cookie"), [
      SitePermissions.ALLOW,
      SitePermissions.ALLOW_COOKIES_FOR_SESSION,
      SitePermissions.BLOCK,
    ]);
  
    Assert.deepEqual(SitePermissions.getAvailableStates("popup"), [
      SitePermissions.ALLOW,
      SitePermissions.BLOCK,
    ]);
  });
  
  add_task(async function testExactHostMatch() {
    let principal = Services.scriptSecurityManager.createContentPrincipalFromOrigin(
      "https://example.com"
    );
    let subPrincipal = Services.scriptSecurityManager.createContentPrincipalFromOrigin(
      "https://test1.example.com"
    );
  
    let exactHostMatched = [
      "autoplay-media",
      "desktop-notification",
      "focus-tab-by-prompt",
      "camera",
      "microphone",
      "screen",
      "geo",
      "xr",
      "persistent-storage",
    ];
    if (RESIST_FINGERPRINTING_ENABLED) {
      // Canvas permission should be hidden unless privacy.resistFingerprinting
      // is true.
      exactHostMatched.push("canvas");
    }
    if (HTTPS_ONLY_MODE_ENABLED) {
      // Exception permission should be hidden unless HTTPS-Only Mode is enabled
      exactHostMatched.push("https-only-load-insecure");
    }
    if (MIDI_ENABLED) {
      // WebMIDI is only pref'd on in nightly.
      // Should remove this checking and add it as default after it is fully pref-on.
      exactHostMatched.push("midi");
      exactHostMatched.push("midi-sysex");
    }
    let nonExactHostMatched = [
      "cookie",
      "popup",
      "install",
      "shortcuts",
      "storage-access",
    ];
  
    let permissions = SitePermissions.listPermissions();
    for (let permission of permissions) {
      SitePermissions.setForPrincipal(
        principal,
        permission,
        SitePermissions.ALLOW
      );
  
      if (exactHostMatched.includes(permission)) {
        // Check that the sub-origin does not inherit the permission from its parent.
        Assert.equal(
          SitePermissions.getForPrincipal(subPrincipal, permission).state,
          SitePermissions.getDefault(permission),
          `${permission} should exact-host match`
        );
      } else if (nonExactHostMatched.includes(permission)) {
        // Check that the sub-origin does inherit the permission from its parent.
        Assert.equal(
          SitePermissions.getForPrincipal(subPrincipal, permission).state,
          SitePermissions.ALLOW,
          `${permission} should not exact-host match`
        );
      } else {
        Assert.ok(
          false,
          `Found an unknown permission ${permission} in exact host match test.` +
            "Please add new permissions from SitePermissions.jsm to this test."
        );
      }
  
      // Check that the permission can be made specific to the sub-origin.
      SitePermissions.setForPrincipal(
        subPrincipal,
        permission,
        SitePermissions.PROMPT
      );
      Assert.equal(
        SitePermissions.getForPrincipal(subPrincipal, permission).state,
        SitePermissions.PROMPT
      );
      Assert.equal(
        SitePermissions.getForPrincipal(principal, permission).state,
        SitePermissions.ALLOW
      );
  
      SitePermissions.removeFromPrincipal(subPrincipal, permission);
      SitePermissions.removeFromPrincipal(principal, permission);
    }
  });
  
  add_task(async function testDefaultPrefs() {
    let principal = Services.scriptSecurityManager.createContentPrincipalFromOrigin(
      "https://example.com"
    );
  
    // Check that without a pref the default return value is UNKNOWN.
    Assert.deepEqual(SitePermissions.getForPrincipal(principal, "camera"), {
      state: SitePermissions.UNKNOWN,
      scope: SitePermissions.SCOPE_PERSISTENT,
    });
  
    // Check that the default return value changed after setting the pref.
    Services.prefs.setIntPref(
      "permissions.default.camera",
      SitePermissions.BLOCK
    );
    Assert.deepEqual(SitePermissions.getForPrincipal(principal, "camera"), {
      state: SitePermissions.BLOCK,
      scope: SitePermissions.SCOPE_PERSISTENT,
    });
  
    // Check that other permissions still return UNKNOWN.
    Assert.deepEqual(SitePermissions.getForPrincipal(principal, "microphone"), {
      state: SitePermissions.UNKNOWN,
      scope: SitePermissions.SCOPE_PERSISTENT,
    });
  
    // Check that the default return value changed after changing the pref.
    Services.prefs.setIntPref(
      "permissions.default.camera",
      SitePermissions.ALLOW
    );
    Assert.deepEqual(SitePermissions.getForPrincipal(principal, "camera"), {
      state: SitePermissions.ALLOW,
      scope: SitePermissions.SCOPE_PERSISTENT,
    });
  
    // Check that the preference is ignored if there is a value.
    SitePermissions.setForPrincipal(principal, "camera", SitePermissions.BLOCK);
    Assert.deepEqual(SitePermissions.getForPrincipal(principal, "camera"), {
      state: SitePermissions.BLOCK,
      scope: SitePermissions.SCOPE_PERSISTENT,
    });
  
    // The preference should be honored again, after resetting the permissions.
    SitePermissions.removeFromPrincipal(principal, "camera");
    Assert.deepEqual(SitePermissions.getForPrincipal(principal, "camera"), {
      state: SitePermissions.ALLOW,
      scope: SitePermissions.SCOPE_PERSISTENT,
    });
  
    // Should be UNKNOWN after clearing the pref.
    Services.prefs.clearUserPref("permissions.default.camera");
    Assert.deepEqual(SitePermissions.getForPrincipal(principal, "camera"), {
      state: SitePermissions.UNKNOWN,
      scope: SitePermissions.SCOPE_PERSISTENT,
    });
  });
  
  add_task(async function testCanvasPermission() {
    let resistFingerprinting = Services.prefs.getBoolPref(
      "privacy.resistFingerprinting",
      false
    );
    let principal = Services.scriptSecurityManager.createContentPrincipalFromOrigin(
      "https://example.com"
    );
  
    SitePermissions.setForPrincipal(principal, "canvas", SitePermissions.ALLOW);
  
    // Canvas permission is hidden when privacy.resistFingerprinting is false.
    Services.prefs.setBoolPref("privacy.resistFingerprinting", false);
    Assert.equal(SitePermissions.listPermissions().indexOf("canvas"), -1);
    Assert.equal(
      SitePermissions.getAllByPrincipal(principal).filter(
        permission => permission.id === "canvas"
      ).length,
      0
    );
  
    // Canvas permission is visible when privacy.resistFingerprinting is true.
    Services.prefs.setBoolPref("privacy.resistFingerprinting", true);
    Assert.notEqual(SitePermissions.listPermissions().indexOf("canvas"), -1);
    Assert.notEqual(
      SitePermissions.getAllByPrincipal(principal).filter(
        permission => permission.id === "canvas"
      ).length,
      0
    );
  
    SitePermissions.removeFromPrincipal(principal, "canvas");
    Services.prefs.setBoolPref(
      "privacy.resistFingerprinting",
      resistFingerprinting
    );
  });
  
  add_task(async function testHttpsOnlyPermission() {
    let originalValue = Services.prefs.getBoolPref(
      "dom.security.https_only_mode",
      false
    );
    let principal = Services.scriptSecurityManager.createContentPrincipalFromOrigin(
      "https://example.com"
    );
  
    SitePermissions.setForPrincipal(
      principal,
      "https-only-load-insecure",
      SitePermissions.ALLOW
    );
  
    // Exception permission is hidden when HTTPS-Only Mode is disabled
    Services.prefs.setBoolPref("dom.security.https_only_mode", false);
    Assert.equal(
      SitePermissions.listPermissions().indexOf("https-only-load-insecure"),
      -1
    );
    Assert.equal(
      SitePermissions.getAllByPrincipal(principal).filter(
        permission => permission.id === "https-only-load-insecure"
      ).length,
      0
    );
  
    // Exception permission should show up when HTTPS-Only Mode is enabled
    Services.prefs.setBoolPref("dom.security.https_only_mode", true);
    Assert.notEqual(
      SitePermissions.listPermissions().indexOf("https-only-load-insecure"),
      -1
    );
    Assert.notEqual(
      SitePermissions.getAllByPrincipal(principal).filter(
        permission => permission.id === "https-only-load-insecure"
      ).length,
      0
    );
  
    // Reset everything
    SitePermissions.removeFromPrincipal(principal, "https-only-load-insecure");
    Services.prefs.setBoolPref("dom.security.https_only_mode", originalValue);
  });
  
  add_task(async function testFilePermissions() {
    let principal = Services.scriptSecurityManager.createContentPrincipalFromOrigin(
      "file:///example.js"
    );
    Assert.deepEqual(SitePermissions.getAllByPrincipal(principal), []);
  
    SitePermissions.setForPrincipal(principal, "camera", SitePermissions.ALLOW);
    Assert.deepEqual(SitePermissions.getAllByPrincipal(principal), [
      {
        id: "camera",
        state: SitePermissions.ALLOW,
        scope: SitePermissions.SCOPE_PERSISTENT,
      },
    ]);
    SitePermissions.removeFromPrincipal(principal, "camera");
    Assert.deepEqual(SitePermissions.getAllByPrincipal(principal), []);
  });
  