<!doctype html>
<meta name="timeout" content="long">
<title>Cross-Origin-Embedder-Policy header and nested navigable resource without such header</title>
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<script src="/common/get-host-info.sub.js"></script>
<script src="/common/utils.js"></script> <!-- Use token() to allow running tests in parallel -->
<div id=log></div>
<script>
const HOST = get_host_info();
const BASE = "/html/cross-origin-embedder-policy/resources";

// WebKit-specific version of the test because the original WPT test was assuming that REMOTE_ORIGIN was same-site.
HOST.SAME_SITE_ORIGIN = "https://" + HOST.ORIGINAL_HOST + ":" + HOST.HTTPS_PORT2;

async_test(t => {
  const frame = document.createElement("iframe");
  t.add_cleanup(() => frame.remove());
  frame.src = "/common/blank.html";
  document.body.append(frame);
  // Make sure the iframe didn't load. See https://github.com/whatwg/html/issues/125 for why a
  // timeout is used here. Long term all network error handling should be similar and have a
  // reliable event.
  assert_equals(frame.contentDocument.body.localName, "body");
  t.step_wait_func_done(() => frame.contentDocument === null);
}, `"require-corp" top-level: navigating a frame to "none" should fail`);

async_test(t => {
  const frame = document.createElement("iframe");
  t.add_cleanup(() => frame.remove());
  const bc = new BroadcastChannel(token());
  bc.onmessage = t.step_func((event) => {
    let payload = event.data;
    assert_equals(payload, "loaded");
    t.step_wait_func_done(() => frame.contentDocument === null);
  });
  frame.src = `/html/cross-origin-embedder-policy/resources/navigate-require-corp.sub.html?channelName=${bc.name}&to=/common/blank.html`;
  document.body.append(frame);
  assert_equals(frame.contentDocument.body.localName, "body");
}, `"require-corp" top-level: navigating a frame from "require-corp" to "none" should fail`);

async_test(t => {
  let pageLoaded = false;
  const bc = new BroadcastChannel(token());
  let finished = false;
  bc.onmessage = t.step_func((event) => {
    let payload = event.data;
    assert_equals(payload, "loaded");
    pageLoaded = true;
  });

  const bc2 = new BroadcastChannel(token());
  bc2.onmessage = t.step_func_done((event) => {
    let payload = event.data;
    assert_equals(payload, "loaded");
    assert_equals(pageLoaded, true);
  });

  const win = window.open(`/html/cross-origin-embedder-policy/resources/navigate-none.sub.html?channelName=${bc.name}&to=navigate-none.sub.html?channelName=${bc2.name}`, "_blank", "noopener");
  assert_equals(win, null);
}, `"require-corp" top-level: creating a noopener "none" popup should succeed`);

async_test(t => {
  let pageLoaded = false;
  const bc = new BroadcastChannel(token());
  bc.onmessage = t.step_func_done((event) => {
    pageLoaded = true;
    let payload = event.data;
    assert_equals(payload, "loaded");
  });

  const win = window.open(`/html/cross-origin-embedder-policy/resources/navigate-none.sub.html?channelName=${bc.name}&to=/common/blank.html`, "_blank");
  t.add_cleanup(() => win.close());
}, `"require-corp" top-level: creating a "none" popup should succeed.`);

[
  {
    "name": "",
    "title": "as popup"
  },
  {
    "name": "noopener",
    "title": "as noopener popup"
  },
  {
    "name": "clear opener",
    "title": "as popup with opener set to null"
  }
].forEach(({name, title}) => {
  async_test(t => {
    let pageLoaded = false;
    const bc = new BroadcastChannel(token());
    bc.onmessage = t.step_func(event => {
      pageLoaded = true;
      const payload = event.data;
      assert_equals(payload, "loaded");
    });

    const bc2 = new BroadcastChannel(token());
    bc2.onmessage = t.step_func_done(event => {
      const payload = event.data;
      assert_equals(payload, "loaded");
      assert_equals(pageLoaded, true);
    });

    let clearOpener = "";
    if (name === "clear opener") {
      clearOpener = "&clearOpener=true"
    }

    let noopener = undefined;
    if (name === "noopener") {
      noopener = "noopener"
    }

    const win = window.open(`/html/cross-origin-embedder-policy/resources/navigate-require-corp.sub.html?channelName=${bc.name}${clearOpener}&to=navigate-none.sub.html?channelName=${bc2.name}`, "_blank", noopener);
    if (name === "noopener") {
      assert_equals(win, null);
    } else {
      t.add_cleanup(() => win.close());
    }
  }, `"require-corp" top-level (${title}): navigating to "none" should succeed`);
});

promise_test(async t => {
  const response = await fetch(HOST.SAME_SITE_ORIGIN+"/html/cross-origin-embedder-policy/resources/nothing-cross-origin-corp.txt", {mode: "no-cors"});
  assert_equals(response.type, "opaque");
}, `"require-corp" top-level: fetch() to CORP: cross-origin response should succeed`);

promise_test(t => {
  return promise_rejects_js(t, TypeError, fetch(HOST.SAME_SITE_ORIGIN+"/common/blank.html", {mode: "no-cors"}));
}, `"require-corp" top-level: fetch() to response without CORP should fail`);

promise_test(t => {
  const w = window.open();
  return promise_rejects_js(t, w.TypeError, w.fetch(HOST.SAME_SITE_ORIGIN+"/common/blank.html", {mode: "no-cors"}));
}, `"require-corp" top-level: fetch() to response without CORP through a WindowProxy should fail`);

async_test(t => {
  let w = window.open();
  const frame = w.document.createElement("iframe");
  t.add_cleanup(() => {
    w.close();
    frame.remove();
  });
  frame.src = "/common/blank.html";
  document.body.append(frame);
  // Make sure the iframe didn't load. See https://github.com/whatwg/html/issues/125 for why a
  // timeout is used here. Long term all network error handling should be similar and have a
  // reliable event.
  assert_equals(frame.contentDocument.body.localName, "body");
  t.step_wait_func_done(() => frame.contentDocument === null);
}, `"require-corp" top-level: navigating an iframe to a page without CORP, through a WindowProxy, should fail`);

async_test(t => {
  const frame = document.createElement("iframe");
  const id = token();
  t.add_cleanup(() => frame.remove());
  window.addEventListener('message', t.step_func((e) => {
    if (e.data === id) {
      // Loaded!
      t.done();
    }
  }));
  // REMOTE_ORIGIN is cross-origin, same-site.
  frame.src = `${HOST.SAME_SITE_ORIGIN}${BASE}/navigate-require-corp-same-site.sub.html?token=${id}`;
  document.body.append(frame);
}, 'CORP: same-site is checked and allowed.');

async_test(t => {
  const frame = document.createElement("iframe");
  const id = token();
  t.add_cleanup(() => frame.remove());
  let loaded = false;
  window.addEventListener('message', t.step_func((e) => {
    if (e.data === id) {
      loaded = true;
    }
  }));
  t.step_timeout(() => {
    // Make sure the iframe didn't load. See https://github.com/whatwg/html/issues/125 for why a
    // timeout is used here. Long term all network error handling should be similar and have a
    // reliable event.
    assert_false(loaded);
    t.done();
  }, 2000);

  // NOTESAMESITE_ORIGIN is cross-origin, cross-site.
  frame.src = `${HOST.HTTPS_NOTSAMESITE_ORIGIN}${BASE}/navigate-require-corp-same-site.sub.html?token=${id}`;
  document.body.append(frame);
}, 'CORP: same-site is checked and blocked.');

async_test(t => {
  const frame = document.createElement("iframe");
  const bc = new BroadcastChannel(token());
  t.add_cleanup(() => frame.remove());
  bc.onmessage = t.step_func_done((event) => {
    const payload = event.data;
    assert_equals(payload, "loaded");
  });

  const dest = `${HOST.ORIGIN}${BASE}/navigate-require-corp.sub.html?channelName=${bc.name}`;
  // REMOTE_ORIGIN is cross-origin, same-site.
  frame.src = `${HOST.SAME_SITE_ORIGIN}${BASE}/navigate-require-corp-same-site.sub.html?to=${encodeURIComponent(dest)}`;
  document.body.append(frame);
}, 'navigation CORP is checked with the parent frame, not the navigation source - to be allowed');

async_test(t => {
  const frame = document.createElement("iframe");
  const bc = new BroadcastChannel(token());
  t.add_cleanup(() => frame.remove());
  let loaded = false;
  bc.onmessage = t.step_func((event) => {
    loaded = true;
  });
  t.step_timeout(() => {
    // Make sure the iframe didn't load. See https://github.com/whatwg/html/issues/125 for why a
    // timeout is used here. Long term all network error handling should be similar and have a
    // reliable event.
    assert_false(loaded);
    t.done();
  }, 2000);

  const dest = `${HOST.REMOTE_ORIGIN}${BASE}/navigate-require-corp.sub.html?channelName=${bc.name}`;
  // REMOTE_ORIGIN is cross-origin, same-site.
  frame.src = `${HOST.REMOTE_ORIGIN}${BASE}/navigate-require-corp-same-site.sub.html?to=${encodeURIComponent(dest)}`;
  document.body.append(frame);
}, 'navigation CORP is checked with the parent frame, not the navigation source - to be blocked');

async_test(t => {
  const bc = new BroadcastChannel(token());
  let loaded = false;
  bc.onmessage = t.step_func((event) => {
    loaded = true;
  });
  t.step_timeout(() => {
    // Make sure the iframe didn't load. See
    // https://github.com/whatwg/html/issues/125 for why a timeout is used
    // here. Long term all network error handling should be similar and have a
    // reliable event.
    assert_false(loaded);
    t.done();
  }, 2000);

  const dest = `${HOST.ORIGIN}${BASE}/navigate-require-corp.sub.html?channelName=${bc.name}`;
  const frame = document.createElement("iframe");
  t.add_cleanup(() => frame.remove());
  // |dest| is a same-origin URL and hence not blocked by CORP but reidirect.py
  // is a cross-origin (actually cross-site) URL, so blocked by CORP.
  frame.src = `${HOST.HTTPS_NOTSAMESITE_ORIGIN}/common/redirect.py?location=${encodeURIComponent(dest)}`;
  document.body.append(frame);
}, 'navigation CORP is checked for each redirect');

</script>
