<!--
  Any copyright is dedicated to the Public Domain.
  http://creativecommons.org/publicdomain/zero/1.0/
-->
<!DOCTYPE HTML>
<html>

<head>
  <meta charset="utf-8">
  <title>Test for Permissions API</title>
  <script src="/tests/SimpleTest/SimpleTest.js"></script>
  <link rel="stylesheet" href="/tests/SimpleTest/test.css">
</head>

<body>
  <pre id="test"></pre>
  <script type="application/javascript">
  "use strict";

  const {
    UNKNOWN_ACTION,
    PROMPT_ACTION,
    ALLOW_ACTION,
    DENY_ACTION,
  } = SpecialPowers.Ci.nsIPermissionManager;

  SimpleTest.waitForExplicitFinish();

  const PERMISSIONS = [{
    name: "geolocation",
    type: "geo",
  }, {
    name: "notifications",
    type: "desktop-notification",
  }, {
    name: "push",
    type: "desktop-notification",
  }, {
    name: "persistent-storage",
    type: "persistent-storage",
  }, {
    name: "midi",
    type: "midi",
  } ];

  const UNSUPPORTED_PERMISSIONS = [
    "foobarbaz", // Not in spec, for testing only.
  ];

  // Create a closure, so that tests are run on the correct window object.
  function createPermissionTester(aWindow) {
    return {
      setPermissions(allow) {
        const permissions = PERMISSIONS.map(({ type }) => {
          return {
            type,
            allow,
            "context": aWindow.document,
          };
        });
        return new Promise((resolve) => {
          SpecialPowers.popPermissions(() => {
            SpecialPowers.pushPermissions(permissions, resolve);
          });
        });
      },
      revokePermissions() {
        const promisesToRevoke = PERMISSIONS.map(({ name }) => {
          return aWindow.navigator.permissions
            .revoke({ name })
            .then(
              ({ state }) => is(state, "prompt", `correct state for '${name}'`),
              () => ok(false, `revoke should not have rejected for '${name}'`)
            );
        });
        return Promise.all(promisesToRevoke);
      },
      revokeUnsupportedPermissions() {
        const promisesToRevoke = UNSUPPORTED_PERMISSIONS.map(({ name }) => {
          return aWindow.navigator.permissions
            .revoke({ name })
            .then(
              () => ok(false, `revoke should not have resolved for '${name}'`),
              error => is(error.name, "TypeError", `revoke should have thrown TypeError for '${name}'`)
            );
        });
        return Promise.all(promisesToRevoke);
      },
      checkPermissions(state) {
        const promisesToQuery = PERMISSIONS.map(({ name }) => {
          return aWindow.navigator.permissions
            .query({ name })
            .then(
              () => is(state, state, `correct state for '${name}'`),
              () => ok(false, `query should not have rejected for '${name}'`)
            );
          });
        return Promise.all(promisesToQuery);
      },
      checkUnsupportedPermissions() {
        const promisesToQuery = UNSUPPORTED_PERMISSIONS.map(({ name }) => {
          return aWindow.navigator.permissions
            .query({ name })
            .then(
              () => ok(false, `query should not have resolved for '${name}'`),
              error => {
                is(error.name, "TypeError",
                  `query should have thrown TypeError for '${name}'`);
              }
            );
          });
        return Promise.all(promisesToQuery);
      },
      promiseStateChanged(name, state) {
        return aWindow.navigator.permissions
          .query({ name })
          .then(status => {
            return new Promise( resolve => {
              status.onchange = () => {
                status.onchange = null;
                is(status.state, state, `state changed for '${name}'`);
                resolve();
              };
            });
          },
          () => ok(false, `query should not have rejected for '${name}'`));
      },
      testStatusOnChange() {
        return new Promise((resolve) => {
          SpecialPowers.popPermissions(() => {
            const permission = "geolocation";
            const promiseGranted = this.promiseStateChanged(permission, "granted");
            this.setPermissions(ALLOW_ACTION);
            promiseGranted.then(async () => {
              const promisePrompt = this.promiseStateChanged(permission, "prompt");
              await SpecialPowers.popPermissions();
              return promisePrompt;
            }).then(resolve);
          });
        });
      },
      testInvalidQuery() {
        return aWindow.navigator.permissions
          .query({ name: "invalid" })
          .then(
            () => ok(false, "invalid query should not have resolved"),
            () => ok(true, "invalid query should have rejected")
          );
      },
      testInvalidRevoke() {
        return aWindow.navigator.permissions
          .revoke({ name: "invalid" })
          .then(
            () => ok(false, "invalid revoke should not have resolved"),
            () => ok(true, "invalid revoke should have rejected")
          );
      },
    };
  }

  function enablePrefs() {
    const ops = {
      "set": [
        ["dom.permissions.revoke.enable", true],
        ["privacy.firstparty.isolate", true],
      ],
    };
    return SpecialPowers.pushPrefEnv(ops);
  }

  function createIframe() {
    return new Promise((resolve) => {
      const iframe = document.createElement("iframe");
      iframe.src = "file_empty.html";
      iframe.onload = () => resolve(iframe.contentWindow);
      document.body.appendChild(iframe);
    });
  }

  window.onload = () => {
    enablePrefs()
      .then(createIframe)
      .then(createPermissionTester)
      .then((tester) => {
        return tester
          .checkUnsupportedPermissions()
          .then(() => tester.setPermissions(UNKNOWN_ACTION))
          .then(() => tester.checkPermissions("prompt"))
          .then(() => tester.setPermissions(PROMPT_ACTION))
          .then(() => tester.checkPermissions("prompt"))
          .then(() => tester.setPermissions(ALLOW_ACTION))
          .then(() => tester.checkPermissions("granted"))
          .then(() => tester.setPermissions(DENY_ACTION))
          .then(() => tester.checkPermissions("denied"))
          .then(() => tester.testStatusOnChange())
          .then(() => tester.testInvalidQuery())
          .then(() => tester.revokeUnsupportedPermissions())
          .then(() => tester.revokePermissions())
          .then(() => tester.checkPermissions("prompt"))
          .then(() => tester.testInvalidRevoke());
      })
      .then(SimpleTest.finish)
      .catch((e) => {
        ok(false, `Unexpected error ${e}`);
        SimpleTest.finish();
      });
  };
  </script>
</body>

</html>
