// META: script=/common/utils.js
// META: script=/common/get-host-info.sub.js

const origins = get_host_info();

promise_test(async function () {
  const stash = token(),
        redirectPath = "/fetch/origin/resources/redirect-and-stash.py";

  // Cross-origin -> same-origin will result in setting the tainted origin flag for the second
  // request.
  let url = origins.HTTP_ORIGIN + redirectPath + "?stash=" + stash;
  url = origins.HTTP_REMOTE_ORIGIN + redirectPath + "?stash=" + stash + "&location=" + encodeURIComponent(url) + "&dummyJS";

  await fetch(url, { mode: "no-cors", method: "POST" });

  const json = await (await fetch(redirectPath + "?dump&stash=" + stash)).json();

  assert_equals(json[0], origins.HTTP_ORIGIN);
  assert_equals(json[1], "null");
}, "Origin header and 308 redirect");

promise_test(async function () {
  const stash = token(),
        redirectPath = "/fetch/origin/resources/redirect-and-stash.py";

  let url = origins.HTTP_ORIGIN + redirectPath + "?stash=" + stash;
  url = origins.HTTP_REMOTE_ORIGIN + redirectPath + "?stash=" + stash + "&location=" + encodeURIComponent(url);

  await new Promise(resolve => {
    const frame = document.createElement("iframe");
    frame.src = url;
    frame.onload = () => {
      resolve();
      frame.remove();
    }
    document.body.appendChild(frame);
  });

  const json = await (await fetch(redirectPath + "?dump&stash=" + stash)).json();

  assert_equals(json[0], "no Origin header");
  assert_equals(json[1], "no Origin header");
}, "Origin header and GET navigation");

promise_test(async function () {
  const stash = token(),
        redirectPath = "/fetch/origin/resources/redirect-and-stash.py";

  let url = origins.HTTP_ORIGIN + redirectPath + "?stash=" + stash;
  url = origins.HTTP_REMOTE_ORIGIN + redirectPath + "?stash=" + stash + "&location=" + encodeURIComponent(url);

  await new Promise(resolve => {
    const frame = document.createElement("iframe");
    self.addEventListener("message", e => {
      if (e.data === "loaded") {
        resolve();
        frame.remove();
      }
    }, { once: true });
    frame.onload = () => {
      const doc = frame.contentDocument,
            form = doc.body.appendChild(doc.createElement("form")),
            submit = form.appendChild(doc.createElement("input"));
      form.action = url;
      form.method = "POST";
      submit.type = "submit";
      submit.click();
    }
    document.body.appendChild(frame);
  });

  const json = await (await fetch(redirectPath + "?dump&stash=" + stash)).json();

  assert_equals(json[0], origins.HTTP_ORIGIN);
  assert_equals(json[1], "null");
}, "Origin header and POST navigation");

function navigationReferrerPolicy(referrerPolicy, destination, expectedOrigin) {
  return async function () {
    const stash = token();
    const referrerPolicyPath = "/fetch/origin/resources/referrer-policy.py";
    const redirectPath = "/fetch/origin/resources/redirect-and-stash.py";

    let postUrl =
            (destination === "same-origin" ? origins.HTTP_ORIGIN
                                           : origins.HTTP_REMOTE_ORIGIN) +
            redirectPath + "?stash=" + stash;

    await new Promise(resolve => {
      const frame = document.createElement("iframe");
      document.body.appendChild(frame);
      frame.src = origins.HTTP_ORIGIN + referrerPolicyPath +
                  "?referrerPolicy=" + referrerPolicy;
      self.addEventListener("message", function listener(e) {
        if (e.data === "loaded") {
          resolve();
          frame.remove();
          self.removeEventListener("message", listener);
        } else if (e.data === "action") {
          const doc = frame.contentDocument,
                form = doc.body.appendChild(doc.createElement("form")),
                submit = form.appendChild(doc.createElement("input"));
          form.action = postUrl;
          form.method = "POST";
          submit.type = "submit";
          submit.click();
        }
      });
    });

    const json = await (await fetch(redirectPath + "?dump&stash=" + stash)).json();

    assert_equals(json[0], expectedOrigin);
  };
}

function fetchReferrerPolicy(referrerPolicy, destination, fetchMode, expectedOrigin, httpMethod) {
  return async function () {
    const stash = token();
    const redirectPath = "/fetch/origin/resources/redirect-and-stash.py";

    let fetchUrl =
        (destination === "same-origin" ? origins.HTTP_ORIGIN
                                       : origins.HTTP_REMOTE_ORIGIN) +
        redirectPath + "?stash=" + stash + "&dummyJS";

    await fetch(fetchUrl, { mode: fetchMode, method: httpMethod , "referrerPolicy": referrerPolicy});

    const json = await (await fetch(redirectPath + "?dump&stash=" + stash)).json();

    assert_equals(json[0], expectedOrigin);
  };
}

function referrerPolicyTestString(referrerPolicy, method, destination) {
  return "Origin header and " + method + " " + destination + " with Referrer-Policy " +
         referrerPolicy;
}

[
  {
    "policy": "no-referrer",
    "expectedOriginForSameOrigin": "null",
    "expectedOriginForCrossOrigin": "null"
  },
  {
    "policy": "same-origin",
    "expectedOriginForSameOrigin": origins.HTTP_ORIGIN,
    "expectedOriginForCrossOrigin": "null"
  },
  {
    "policy": "origin-when-cross-origin",
    "expectedOriginForSameOrigin": origins.HTTP_ORIGIN,
    "expectedOriginForCrossOrigin": origins.HTTP_ORIGIN
  },
  {
    "policy": "no-referrer-when-downgrade",
    "expectedOriginForSameOrigin": origins.HTTP_ORIGIN,
    "expectedOriginForCrossOrigin": origins.HTTP_ORIGIN
  },
  {
    "policy": "unsafe-url",
    "expectedOriginForSameOrigin": origins.HTTP_ORIGIN,
    "expectedOriginForCrossOrigin": origins.HTTP_ORIGIN
  },
].forEach(testObj => {
  [
    {
      "name": "same-origin",
      "expectedOrigin": testObj.expectedOriginForSameOrigin
    },
    {
      "name": "cross-origin",
      "expectedOrigin": testObj.expectedOriginForCrossOrigin
    }
  ].forEach(destination => {
    // Test form POST navigation
    promise_test(navigationReferrerPolicy(testObj.policy,
                                          destination.name,
                                          destination.expectedOrigin),
                 referrerPolicyTestString(testObj.policy, "POST",
                                          destination.name + " navigation"));
    // Test fetch
    promise_test(fetchReferrerPolicy(testObj.policy,
                                     destination.name,
                                     "no-cors",
                                     destination.expectedOrigin,
                                     "POST"),
                 referrerPolicyTestString(testObj.policy, "POST",
                                          destination.name + " fetch no-cors mode"));

    // Test cors mode POST
    promise_test(fetchReferrerPolicy(testObj.policy,
                                     destination.name,
                                     "cors",
                                     origins.HTTP_ORIGIN,
                                     "POST"),
                 referrerPolicyTestString(testObj.policy, "POST",
                                          destination.name + " fetch cors mode"));

    // Test cors mode GET
    promise_test(fetchReferrerPolicy(testObj.policy,
                                     destination.name,
                                     "cors",
                                     (destination.name == "same-origin") ? "no Origin header" : origins.HTTP_ORIGIN,
                                     "GET"),
                 referrerPolicyTestString(testObj.policy, "GET",
                                          destination.name + " fetch cors mode"));
  });
});
