// META: script=/resources/testdriver.js
// META: script=/resources/testdriver-vendor.js
// META: script=/common/dispatcher/dispatcher.js
// META: script=/common/get-host-info.sub.js
// META: script=/common/utils.js
// META: script=/common/subset-tests-by-key.js
// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js
// META: variant=?include=variant-1-test
// META: variant=?include=variant-2-test
// META: variant=?include=variant-3-test
// META: variant=?include=variant-4-test
// META: variant=?include=variant-5-test
// META: variant=?include=variant-6-test
// META: variant=?include=variant-7-test
// META: variant=?include=variant-8-test
// META: variant=?include=variant-9-test
// META: variant=?include=variant-10-test
// META: variant=?include=variant-11-test
// META: timeout=long

'use strict';

const TEST_VALUE = "popin-partition-test";

async function seedPartition(remoteContextWrapper, name) {
  await remoteContextWrapper.executeScript((name, TEST_VALUE) => {
    window.localStorage.setItem(name, TEST_VALUE);
    document.cookie = name + "-strict=" + TEST_VALUE + "; SameSite=Strict; Secure";
    document.cookie = name + "-lax=" + TEST_VALUE + "; SameSite=Lax; Secure";
    document.cookie = name + "-none=" + TEST_VALUE + "; SameSite=None; Secure";
    document.cookie = name + "-partitioned-strict=" + TEST_VALUE + "; Partitioned; SameSite=Strict; Secure";
    document.cookie = name + "-partitioned-lax=" + TEST_VALUE + "; Partitioned; SameSite=Lax; Secure";
    document.cookie = name + "-partitioned-none=" + TEST_VALUE + "; Partitioned; SameSite=None; Secure";
  }, [name, TEST_VALUE]);
}

async function openPopin(test, remoteContextWrapper, origin) {
  const popin = await remoteContextWrapper.addWindow(
    /*extraConfig=*/ origin ? { origin } : null,
    /*options=*/ { features: "popin" });
  assert_equals(await popin.executeScript(() => window.popinContextType()), "partitioned");
  test.add_cleanup(async () => {
    await popin.executeScript(() => window.close());
  });
  return popin;
}

async function getCookies(remoteContextWrapper) {
  return await remoteContextWrapper.executeScript((TEST_VALUE) => {
    if (!document.cookie) {
      return [];
    }
    const cookies = document.cookie.split(";");
    let cookieNames = [];
    for (let i = 0; i < cookies.length; i++) {
      let cookieName = cookies[i].split("=")[0].trim();
      let cookieValue = cookies[i].split("=")[1].trim();
      if (cookieValue === TEST_VALUE) {
        cookieNames.push(cookieName);
      }
    }
    return cookieNames.sort();
  }, [TEST_VALUE]);
}

async function getLocalStorage(remoteContextWrapper) {
  return await remoteContextWrapper.executeScript((TEST_VALUE) => {
    let storageNames = [];
    for (let i = 0; i < window.localStorage.length; i++) {
      if (window.localStorage.getItem(window.localStorage.key(i)) === TEST_VALUE) {
        storageNames.push(window.localStorage.key(i));
      }
    }
    return storageNames.sort();
  }, [TEST_VALUE]);
}

const rcHelper = new RemoteContextHelper();
const handles = {};

promise_setup(async () => {
  assert_in_array("partitioned", window.popinContextTypesSupported());

  handles.main = await rcHelper.addWindow();
  handles.frameSameSite = await handles.main.addIframe();
  handles.frameCrossSite = await handles.main.addIframe(
    /*extraConfig=*/ {
      origin: "HTTPS_NOTSAMESITE_ORIGIN",
    },
    /*attributes=*/ {
      allow: "popins",
    }
  );
  handles.frameSameSiteWithCrossSiteAncestor =
    await handles.frameCrossSite.addIframe(
      /*extraConfig=*/ null,
      /*attributes=*/ {
        allow: "popins",
      }
    );
  handles.crossSite = await rcHelper.addWindow(
    /*extraConfig=*/ {
      origin: "HTTPS_NOTSAMESITE_ORIGIN",
    }
  );
  handles.crossSiteFrameSameSite = await handles.crossSite.addIframe();
  Object.freeze(handles);

  await seedPartition(handles.main, "main");
  await seedPartition(handles.frameSameSite, "frameSameSite");
  await seedPartition(handles.frameCrossSite, "frameCrossSite");
  await seedPartition(handles.frameSameSiteWithCrossSiteAncestor, "frameSameSiteWithCrossSiteAncestor");
  await seedPartition(handles.crossSite, "crossSite");
  await seedPartition(handles.crossSiteFrameSameSite, "crossSiteFrameSameSite");
});

subsetTestByKey("variant-1-test", promise_test, async t => {
  const popin = await openPopin(t, handles.main);

  // The popin, it's opener and all their ancestors are same-site, so the popin
  // should have access to all cookies and storage of the main host.
  assert_array_equals(
    await getCookies(popin),
    [
      "crossSiteFrameSameSite-none",
      "frameSameSite-lax",
      "frameSameSite-none",
      "frameSameSite-partitioned-lax",
      "frameSameSite-partitioned-none",
      "frameSameSite-partitioned-strict",
      "frameSameSite-strict",
      "frameSameSiteWithCrossSiteAncestor-none",
      "main-lax",
      "main-none",
      "main-partitioned-lax",
      "main-partitioned-none",
      "main-partitioned-strict",
      "main-strict",
    ]);
  assert_array_equals(await getLocalStorage(popin), ["frameSameSite", "main"]);

  t.done();
}, "Verify Partitioned Popins have access to the proper cookie/storage partitions - Main site opens same-site popin.");

subsetTestByKey("variant-2-test", promise_test, async t => {
  const popin = await openPopin(t, handles.main, "HTTPS_NOTSAMESITE_ORIGIN");

  // The popin is cross-site to its opener, so the popin should only have access
  // to the local storage variable and SameSite=None cookies of the cross-site
  // frame, and the unpartitioned SameSite=None cookie of the cross-site window.
  assert_array_equals(await getCookies(popin), ["crossSite-none", "frameCrossSite-none", "frameCrossSite-partitioned-none"]);
  assert_array_equals(await getLocalStorage(popin), ["frameCrossSite"]);

  t.done();
}, "Verify Partitioned Popins have access to the proper cookie/storage partitions - Main site opens cross-site popin.");

subsetTestByKey("variant-3-test", promise_test, async t => {
  const popin = await openPopin(t, handles.frameSameSite);

  // The popin, it's opener and all their ancestors are same-site, so the popin
  // should have access to all cookies and storage of the main host.
  assert_array_equals(
    await getCookies(popin),
    [
      "crossSiteFrameSameSite-none",
      "frameSameSite-lax",
      "frameSameSite-none",
      "frameSameSite-partitioned-lax",
      "frameSameSite-partitioned-none",
      "frameSameSite-partitioned-strict",
      "frameSameSite-strict",
      "frameSameSiteWithCrossSiteAncestor-none",
      "main-lax",
      "main-none",
      "main-partitioned-lax",
      "main-partitioned-none",
      "main-partitioned-strict",
      "main-strict",
    ]);
  assert_array_equals(await getLocalStorage(popin), ["frameSameSite", "main"]);

  t.done();
}, "Verify Partitioned Popins have access to the proper cookie/storage partitions - Same-site frame opens same-site popin.");

subsetTestByKey("variant-4-test", promise_test, async t => {
  const popin = await openPopin(t, handles.frameCrossSite);

  // The main-host popin has a cross-site ancestor, so it should only have
  // access to the local storage variable and SameSite=None cookies set in a
  // cross-site ancestor context, and the unpartitioned SameSite=None cookies of
  // the main-host contexts.
  assert_array_equals(await getCookies(popin),
    [
      "crossSiteFrameSameSite-none",
      "frameSameSite-none",
      "frameSameSiteWithCrossSiteAncestor-none",
      "frameSameSiteWithCrossSiteAncestor-partitioned-none",
      "main-none",
    ]);
  assert_array_equals(await getLocalStorage(popin), ["frameSameSiteWithCrossSiteAncestor"]);

  t.done();
}, "Verify Partitioned Popins have access to the proper cookie/storage partitions - Cross-site frame opens main-host popin.");

subsetTestByKey("variant-5-test", promise_test, async t => {
  const popin = await openPopin(t, handles.frameCrossSite, "HTTPS_NOTSAMESITE_ORIGIN");

  // The popin and its opener is cross-site to the main frame, so the popin
  // should only have access to the local storage variable and SameSite=None
  // cookies of the cross-site frame, and the unpartitioned SameSite=None cookie
  // of the cross-site window.
  assert_array_equals(await getCookies(popin), ["crossSite-none", "frameCrossSite-none", "frameCrossSite-partitioned-none"]);
  assert_array_equals(await getLocalStorage(popin), ["frameCrossSite"]);

  t.done();
}, "Verify Partitioned Popins have access to the proper cookie/storage partitions - Cross-site frame opens alternative-host popin.");

subsetTestByKey("variant-6-test", promise_test, async t => {
  const popin = await openPopin(t, handles.frameSameSiteWithCrossSiteAncestor);

  // The main-host popin has a cross-site ancestor, so it should only have
  // access to the local storage variable and SameSite=None cookies set in a
  // cross-site ancestor context, and the unpartitioned SameSite=None cookies of
  // the main-host contexts.
  assert_array_equals(await getCookies(popin),
    [
      "crossSiteFrameSameSite-none",
      "frameSameSite-none",
      "frameSameSiteWithCrossSiteAncestor-none",
      "frameSameSiteWithCrossSiteAncestor-partitioned-none",
      "main-none",
    ]);
  assert_array_equals(await getLocalStorage(popin), ["frameSameSiteWithCrossSiteAncestor"]);

  t.done();
}, "Verify Partitioned Popins have access to the proper cookie/storage partitions - Same-site frame with cross-site ancestor opens main-host popin.");

subsetTestByKey("variant-7-test", promise_test, async t => {
  const popin = await openPopin(t, handles.main);

  // The frame in the popin, the popin, it's opener and all their ancestors are
  // same-site, so the popin should have access to all cookies and storage of
  // the main host.
  const popinFrame = await popin.addIframe();
  assert_array_equals(
    await getCookies(popinFrame),
    [
      "crossSiteFrameSameSite-none",
      "frameSameSite-lax",
      "frameSameSite-none",
      "frameSameSite-partitioned-lax",
      "frameSameSite-partitioned-none",
      "frameSameSite-partitioned-strict",
      "frameSameSite-strict",
      "frameSameSiteWithCrossSiteAncestor-none",
      "main-lax",
      "main-none",
      "main-partitioned-lax",
      "main-partitioned-none",
      "main-partitioned-strict",
      "main-strict",
    ]);
  assert_array_equals(await getLocalStorage(popinFrame), ["frameSameSite", "main"]);

  t.done();
}, "Verify Partitioned Popins have access to the proper cookie/storage partitions - Main site opens same-site popin with same-site frame.");

subsetTestByKey("variant-8-test", promise_test, async t => {
  const popin = await openPopin(t, handles.main);

  const popinFrame = await popin.addIframe(
    /*extraConfig=*/ {
      origin: "HTTPS_NOTSAMESITE_ORIGIN",
    },
    /*attributes=*/ {
      allow: "popins",
    }
  );

  // The frame in the popin is cross-site to the main frame, so the popin should
  // only have access to the local storage variable and SameSite=None cookies of
  // the cross-site frame, and the unpartitioned SameSite=None cookie of the
  // cross-site window.
  assert_array_equals(await getCookies(popinFrame), ["crossSite-none", "frameCrossSite-none", "frameCrossSite-partitioned-none"]);
  assert_array_equals(await getLocalStorage(popinFrame), ["frameCrossSite"]);

  t.done();
}, "Verify Partitioned Popins have access to the proper cookie/storage partitions - Main site opens same-site popin with cross-site frame.");

subsetTestByKey("variant-9-test", promise_test, async t => {
  const popin = await openPopin(t, handles.main, "HTTPS_NOTSAMESITE_ORIGIN");

  const popinFrame = await popin.addIframe();

  // The main-host frame in the popin has a cross-site ancestor, so it should
  // only have access to the local storage variable and SameSite=None cookies
  // set in a cross-site ancestor context, and the unpartitioned SameSite=None
  // cookies of the main-host contexts.
  assert_array_equals(await getCookies(popinFrame),
    [
      "crossSiteFrameSameSite-none",
      "frameSameSite-none",
      "frameSameSiteWithCrossSiteAncestor-none",
      "frameSameSiteWithCrossSiteAncestor-partitioned-none",
      "main-none",
    ]);
  assert_array_equals(await getLocalStorage(popinFrame), ["frameSameSiteWithCrossSiteAncestor"]);

  t.done();
}, "Verify Partitioned Popins have access to the proper cookie/storage partitions - Main site opens cross-site popin with main-host frame.");

subsetTestByKey("variant-10-test", promise_test, async t => {
  const popin = await openPopin(t, handles.frameCrossSite);

  const popinFrame = await popin.addIframe(
    /*extraConfig=*/ {
      origin: "HTTPS_NOTSAMESITE_ORIGIN",
    },
    /*attributes=*/ {
      allow: "popins",
    }
  );

  // The frame in the popin is cross-site to the main frame, so the popin should
  // only have access to the local storage variable and SameSite=None cookies of
  // the cross-site frame, and the unpartitioned SameSite=Nonecookie of the
  // cross-site window.
  assert_array_equals(await getCookies(popinFrame), ["crossSite-none", "frameCrossSite-none", "frameCrossSite-partitioned-none"]);
  assert_array_equals(await getLocalStorage(popinFrame), ["frameCrossSite"]);

  t.done();
}, "Verify Partitioned Popins have access to the proper cookie/storage partitions - Cross-site frame opens main-host popin with cross-site frame.");

subsetTestByKey("variant-11-test", promise_test, async t => {
  const popin = await openPopin(t, handles.frameCrossSite, "HTTPS_NOTSAMESITE_ORIGIN");

  const popinFrame = await popin.addIframe();

  // The main-host frame in the popin has a cross-site ancestor, so it should
  // only have access to the local storage variable and SameSite=None cookies
  //set in a cross-site ancestor context, and the unpartitioned SameSite=None
  // cookies of the main-host contexts.
  assert_array_equals(await getCookies(popinFrame),
    [
      "crossSiteFrameSameSite-none",
      "frameSameSite-none",
      "frameSameSiteWithCrossSiteAncestor-none",
      "frameSameSiteWithCrossSiteAncestor-partitioned-none",
      "main-none",
    ]);
  assert_array_equals(await getLocalStorage(popinFrame), ["frameSameSiteWithCrossSiteAncestor"]);

  t.done();
}, "Verify Partitioned Popins have access to the proper cookie/storage partitions - Cross-site frame opens alternative-host popin with main-host frame.");
