<!DOCTYPE HTML>
<html>
<!--
Bug 1247685: Implement `applicationServerKey` for subscription association.

Any copyright is dedicated to the Public Domain.
http://creativecommons.org/licenses/publicdomain/

-->
<head>
  <title>Test for Bug 1247685</title>
  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
  <script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
  <script type="text/javascript" src="/tests/dom/push/test/test_utils.js"></script>
  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
  <meta http-equiv="Content-type" content="text/html;charset=UTF-8">
</head>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1247685">Mozilla Bug 1247685</a>
<p id="display"></p>
<div id="content" style="display: none">

</div>
<pre id="test">
</pre>

<script class="testbody" type="text/javascript">

  var isTestingMismatchedKey = false;
  var subscriptions = 0;
  var testKey; // Generated in `start`.

  function generateKey() {
    return crypto.subtle.generateKey({
      name: "ECDSA",
      namedCurve: "P-256",
    }, true, ["sign", "verify"]).then(cryptoKey =>
      crypto.subtle.exportKey("raw", cryptoKey.publicKey)
    ).then(publicKey => new Uint8Array(publicKey));
  }

  var registration;
  add_task(function* start() {
    yield setupPrefsAndReplaceService({
      register(pageRecord) {
        ok(pageRecord.appServerKey.length > 0,
          "App server key should not be empty");
        if (pageRecord.appServerKey.length != 65) {
          throw { result:
                  SpecialPowers.Cr.NS_ERROR_DOM_PUSH_INVALID_KEY_ERR };
        }
        if (isTestingMismatchedKey) {
          throw { result:
                  SpecialPowers.Cr.NS_ERROR_DOM_PUSH_MISMATCHED_KEY_ERR };
        }
        return {
          endpoint: "https://example.com/push/" + (++subscriptions),
          appServerKey: pageRecord.appServerKey,
        };
      },

      registration(pageRecord) {
        return {
          endpoint: "https://example.com/push/subWithKey",
          appServerKey: testKey,
        };
      },
    });
    yield setPushPermission(true);
    testKey = yield generateKey();

    var url = "worker.js" + "?" + (Math.random());
    registration = yield navigator.serviceWorker.register(url, {scope: "."});
    yield waitForActive(registration);
  });

  var controlledFrame;
  add_task(function* createControlledIFrame() {
    controlledFrame = yield injectControlledFrame();
  });

  add_task(function* emptyKey() {
    try {
      yield registration.pushManager.subscribe({
        applicationServerKey: new ArrayBuffer(0),
      });
      ok(false, "Should reject for empty app server keys");
    } catch (error) {
      ok(error instanceof DOMException,
        "Wrong exception type for empty key");
      is(error.name, "InvalidAccessError",
        "Wrong exception name for empty key");
    }
  });

  add_task(function* invalidKey() {
    try {
      yield registration.pushManager.subscribe({
        applicationServerKey: new Uint8Array([0]),
      });
      ok(false, "Should reject for invalid app server keys");
    } catch (error) {
      ok(error instanceof DOMException,
        "Wrong exception type for invalid key");
      is(error.name, "InvalidAccessError",
        "Wrong exception name for invalid key");
    }
  });

  add_task(function* validKey() {
    var pushSubscription = yield registration.pushManager.subscribe({
      applicationServerKey: yield generateKey(),
    });
    is(pushSubscription.endpoint, "https://example.com/push/1",
      "Wrong endpoint for subscription with key");
    is(pushSubscription.options.applicationServerKey,
       pushSubscription.options.applicationServerKey,
       "App server key getter should return the same object");
  });

  add_task(function* retrieveKey() {
    var pushSubscription = yield registration.pushManager.getSubscription();
    is(pushSubscription.endpoint, "https://example.com/push/subWithKey",
      "Got wrong endpoint for subscription with key");
    isDeeply(
      new Uint8Array(pushSubscription.options.applicationServerKey),
      testKey,
      "Got wrong app server key"
    );
  });

  add_task(function* mismatchedKey() {
    isTestingMismatchedKey = true;
    try {
      yield registration.pushManager.subscribe({
        applicationServerKey: yield generateKey(),
      });
      ok(false, "Should reject for mismatched app server keys");
    } catch (error) {
      ok(error instanceof DOMException,
        "Wrong exception type for mismatched key");
      is(error.name, "InvalidStateError",
        "Wrong exception name for mismatched key");
    } finally {
      isTestingMismatchedKey = false;
    }
  });

  add_task(function* emptyKeyInWorker() {
    var errorInfo = yield sendRequestToWorker({
      type: "subscribeWithKey",
      key: new ArrayBuffer(0),
    });
    ok(errorInfo.isDOMException,
      "Wrong exception type in worker for empty key");
    is(errorInfo.name, "InvalidAccessError",
      "Wrong exception name in worker for empty key");
  });

  add_task(function* invalidKeyInWorker() {
    var errorInfo = yield sendRequestToWorker({
      type: "subscribeWithKey",
      key: new Uint8Array([1]),
    });
    ok(errorInfo.isDOMException,
      "Wrong exception type in worker for invalid key");
    is(errorInfo.name, "InvalidAccessError",
      "Wrong exception name in worker for invalid key");
  });

  add_task(function* validKeyInWorker() {
    var key = yield generateKey();
    var data = yield sendRequestToWorker({
      type: "subscribeWithKey",
      key: key,
    });
    is(data.endpoint, "https://example.com/push/2",
      "Wrong endpoint for subscription with key created in worker");
    isDeeply(new Uint8Array(data.key), key,
      "Wrong app server key for subscription created in worker");
  });

  add_task(function* mismatchedKeyInWorker() {
    isTestingMismatchedKey = true;
    try {
      var errorInfo = yield sendRequestToWorker({
        type: "subscribeWithKey",
        key: yield generateKey(),
      });
      ok(errorInfo.isDOMException,
        "Wrong exception type in worker for mismatched key");
      is(errorInfo.name, "InvalidStateError",
        "Wrong exception name in worker for mismatched key");
    } finally {
      isTestingMismatchedKey = false;
    }
  });

  add_task(function* unsubscribe() {
    is(subscriptions, 2, "Wrong subscription count");
    controlledFrame.remove();
  });

  add_task(function* unregister() {
    yield registration.unregister();
  });

</script>
</body>
</html>