// META: script=/common/subset-tests-by-key.js
// META: variant=?include=integrity-none
// META: variant=?include=integrity-pass
// META: variant=?include=integrity-fail
// META: timeout=long

// Given `{ digest: "...", body: "...", cors: true }`:
function scriptURL(data) {
  data.type = "application/javascript";
  let params = new URLSearchParams(data);
  return "./resource.py?" + params.toString();
}

const executable_body = "window.hello = `world`;";
const unreached_body = "assert_unreached(`This code should not execute.`);";
const executable_hashes = {
  "sha-256": "PZJ+9CdAAIacg7wfUe4t/RkDQJVKM0mCZ2K7qiRhHFc=",
  "sha-512": "6qaEeboWnnFooKiwqnorS3SbkLk5rZcqoSsgEeB97srB0WIH6hJk2QDevHAen7gym6/jW244Ogf5MhZMjPYFrA=="
};
const well_formed_but_incorrect_hashes = {
  "sha-256": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=",
  "sha-512": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
};

const EXPECT_BLOCKED = "block";
const EXPECT_LOADED = "loaded";
function generate_test(data, expectation, desc) {
  subsetTestByKey("integrity-none", async_test, t => {
    let s = document.createElement('script');
    s.src = scriptURL(data);
    if (expectation == EXPECT_BLOCKED) {
      s.onerror = t.step_func_done(e => {
        assert_equals("error", e.type);
      });
      s.onload = t.unreached_func("Script should not execute.");
    } else {
      s.onload = t.step_func_done(e => {
        assert_equals("load", e.type);
      });
      s.onerror = t.unreached_func("Script should not fail.");
    }
    document.body.appendChild(s);
  }, "No `integrity` attribute + " + desc);

  subsetTestByKey("integrity-pass", async_test, t => {
    let s = document.createElement('script');
    s.src = scriptURL(data);
    s.crossorigin = "anonymous";
    s.integrity = `sha256-${executable_hashes['sha-256']}`
    if (expectation == EXPECT_BLOCKED) {
      s.onerror = t.step_func_done(e => {
        assert_equals("error", e.type);
      });
      s.onload = t.unreached_func("Script should not execute.");
    } else {
      s.onload = t.step_func_done(e => {
        assert_equals("load", e.type);
      });
      s.onerror = t.unreached_func("Script should not fail.");
    }
    document.body.appendChild(s);
  }, "Matching `integrity` attribute + " + desc);

  subsetTestByKey("integrity-fail", async_test, t => {
    let s = document.createElement('script');
    s.src = scriptURL(data);
    s.crossorigin = "anonymous";
    s.integrity = `sha512-${well_formed_but_incorrect_hashes['sha-512']}`
    s.onerror = t.step_func_done(e => {
      assert_equals("error", e.type);
    });
    s.onload = t.unreached_func("Script should not execute.");
    document.body.appendChild(s);
  }, "Mismatching `integrity` attribute always blocks: " + desc);
}

// No header.
generate_test(
  { body: executable_body },
  EXPECT_LOADED,
  "No header: loads.");

let good_header_list = [];
let bad_header_list = [];
let mixed_header_list = [];
for (const key in executable_hashes) {
  let good_header = `${key}=:${executable_hashes[key]}:`;
  good_header_list.push(good_header);
  let bad_header = `${key}=:${well_formed_but_incorrect_hashes[key]}:`;
  bad_header_list.push(bad_header);
  mixed_header_list.push(good_header, bad_header);

  // - Good single headers:
  generate_test({
      body: executable_body,
      digest: good_header
    },
    EXPECT_LOADED,
    `Good ${key} header: loads.`);

  // - Good multiple headers:
  generate_test({
      body: executable_body,
      digest: `${good_header},${good_header}`
    },
    EXPECT_LOADED,
    `Repeated ${key} header: loads.`);

  generate_test({
      body: executable_body,
      digest: good_header_list.join(",")
    },
    EXPECT_LOADED,
    `Multiple good headers (previous += ${key}): loads.`);

  // - Bad single headers:
  generate_test({
      body: executable_body,
      digest: bad_header
    },
    EXPECT_BLOCKED,
    `Bad ${key} header: blocked.`);

  // - Bad multiple headers:
  generate_test({
      body: executable_body,
      digest: `${bad_header},${bad_header}`
    },
    EXPECT_BLOCKED,
    `Repeated ${key} header: blocked.`);

  generate_test({
      body: executable_body,
      digest: bad_header_list.join(",")
    },
    EXPECT_BLOCKED,
    `Multiple bad headers (previous += ${key}): blocked.`);
}

// - Mixed headers.
generate_test({
    body: executable_body,
    digest: mixed_header_list.join(","),
  },
  EXPECT_BLOCKED,
  `Mixed good and bad headers: blocked.`);
