<!DOCTYPE html>
<meta charset="utf-8"/>
<meta name="timeout" content="long">
<title>Service Worker: Same-site cookie behavior</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/common/get-host-info.sub.js"></script>
<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
<script src="/cookies/resources/cookie-helper.sub.js"></script>
<body>
<script>
'use strict';

const COOKIE_VALUE = 'COOKIE_VALUE';

function make_nested_url(nested_origins, target_url) {
  for (let i = nested_origins.length - 1; i >= 0; --i) {
    target_url = new URL(
      `./resources/nested-parent.html?target=${encodeURIComponent(target_url)}`,
      nested_origins[i] + self.location.pathname);
  }
  return target_url;
}

const scopepath = '/cookies/resources/postToParent.py?with-sw';

async function unregister_service_worker(origin, nested_origins=[]) {
  let target_url = origin +
      '/service-workers/service-worker/resources/unregister-rewrite-worker.html' +
      '?scopepath=' + encodeURIComponent(scopepath);
  target_url = make_nested_url(nested_origins, target_url);
  const w = window.open(target_url);
  try {
    await wait_for_message('SW-UNREGISTERED');
  } finally {
    w.close();
  }
}

async function register_service_worker(origin, nested_origins=[]) {
  let target_url = origin +
      '/service-workers/service-worker/resources/register-rewrite-worker.html' +
      '?scopepath=' + encodeURIComponent(scopepath);
  target_url = make_nested_url(nested_origins, target_url);
  const w = window.open(target_url);
  try {
    await wait_for_message('SW-REGISTERED');
  } finally {
    w.close();
  }
}

async function run_test(t, origin, navaction, swaction, expected,
                        redirect_origins=[], nested_origins=[]) {
  if (swaction === 'navpreload') {
    assert_true('navigationPreload' in ServiceWorkerRegistration.prototype,
                'navigation preload must be supported');
  }
  const sw_param = swaction === 'no-sw' ? 'no-sw' : 'with-sw';
  let action_param = '';
  if (swaction === 'fallback') {
    action_param = '&ignore';
  } else if (swaction !== 'no-sw') {
    action_param = '&' + swaction;
  }
  const navpreload_param = swaction === 'navpreload' ? '&navpreload' : '';
  const change_request_param = swaction === 'change-request' ? '&change-request' : '';
  const target_string = origin + `/cookies/resources/postToParent.py?` +
                                 `${sw_param}${action_param}`
  let target_url = new URL(target_string);

  for (let i = redirect_origins.length - 1; i >= 0; --i) {
    const redirect_url = new URL(
        `./resources/redirect.py?Status=307&Redirect=${encodeURIComponent(target_url)}`,
        redirect_origins[i] + self.location.pathname);
    target_url = redirect_url;
  }

  if (navaction === 'window.open') {
    target_url = new URL(
        `./resources/window-opener.html?target=${encodeURIComponent(target_url)}`,
        self.origin + self.location.pathname);
  } else if (navaction === 'form post') {
    target_url = new URL(
        `./resources/form-poster.html?target=${encodeURIComponent(target_url)}`,
        self.origin + self.location.pathname);
  } else if (navaction === 'set location') {
    target_url = new URL(
        `./resources/location-setter.html?target=${encodeURIComponent(target_url)}`,
        self.origin + self.location.pathname);
  }

  const w = window.open(make_nested_url(nested_origins, target_url));
  t.add_cleanup(() => w.close());

  const result = await wait_for_message('COOKIES');
  verifySameSiteCookieState(expected, COOKIE_VALUE, result.data);
}

promise_test(async t => {
  await resetSameSiteCookies(self.origin, COOKIE_VALUE);
  await register_service_worker(self.origin);

  await resetSameSiteCookies(SECURE_SUBDOMAIN_ORIGIN, COOKIE_VALUE);
  await register_service_worker(SECURE_SUBDOMAIN_ORIGIN);

  await resetSameSiteCookies(SECURE_CROSS_SITE_ORIGIN, COOKIE_VALUE);
  await register_service_worker(SECURE_CROSS_SITE_ORIGIN);

  await register_service_worker(self.origin,
      [self.origin, SECURE_CROSS_SITE_ORIGIN]);
}, 'Setup service workers');

promise_test(t => {
  return run_test(t, self.origin, 'window.open', 'no-sw',
                  SameSiteStatus.STRICT);
}, 'same-origin, window.open with no service worker');

promise_test(t => {
  return run_test(t, self.origin, 'window.open', 'fallback',
                  SameSiteStatus.STRICT);
}, 'same-origin, window.open with fallback');

promise_test(t => {
  return run_test(t, self.origin, 'window.open', 'passthrough',
                  SameSiteStatus.STRICT);
}, 'same-origin, window.open with passthrough');

promise_test(t => {
  return run_test(t, self.origin, 'window.open', 'change-request',
                  SameSiteStatus.STRICT);
}, 'same-origin, window.open with change-request');

promise_test(t => {
  return run_test(t, self.origin, 'window.open', 'navpreload',
                  SameSiteStatus.STRICT);
}, 'same-origin, window.open with navpreload');

promise_test(t => {
  return run_test(t, SECURE_SUBDOMAIN_ORIGIN, 'window.open', 'no-sw',
                  SameSiteStatus.STRICT);
}, 'same-site, window.open with no service worker');

promise_test(t => {
  return run_test(t, SECURE_SUBDOMAIN_ORIGIN, 'window.open', 'fallback',
                  SameSiteStatus.STRICT);
}, 'same-site, window.open with fallback');

promise_test(t => {
  return run_test(t, SECURE_SUBDOMAIN_ORIGIN, 'window.open', 'passthrough',
                  SameSiteStatus.STRICT);
}, 'same-site, window.open with passthrough');

promise_test(t => {
  return run_test(t, SECURE_SUBDOMAIN_ORIGIN, 'window.open', 'change-request',
                  SameSiteStatus.STRICT);
}, 'same-site, window.open with change-request');

promise_test(t => {
  return run_test(t, SECURE_SUBDOMAIN_ORIGIN, 'window.open', 'navpreload',
                  SameSiteStatus.STRICT);
}, 'same-site, window.open with navpreload');

promise_test(t => {
  return run_test(t, SECURE_CROSS_SITE_ORIGIN, 'window.open', 'no-sw',
                  SameSiteStatus.LAX);
}, 'cross-site, window.open with no service worker');

promise_test(t => {
  return run_test(t, SECURE_CROSS_SITE_ORIGIN, 'window.open', 'fallback',
                  SameSiteStatus.LAX);
}, 'cross-site, window.open with fallback');

promise_test(t => {
  return run_test(t, SECURE_CROSS_SITE_ORIGIN, 'window.open', 'passthrough',
                  SameSiteStatus.LAX);
}, 'cross-site, window.open with passthrough');

promise_test(t => {
  return run_test(t, SECURE_CROSS_SITE_ORIGIN, 'window.open', 'change-request',
                  SameSiteStatus.STRICT);
}, 'cross-site, window.open with change-request');

promise_test(t => {
  return run_test(t, SECURE_CROSS_SITE_ORIGIN, 'window.open', 'navpreload',
                  SameSiteStatus.LAX);
}, 'cross-site, window.open with navpreload');

//
// window.open redirect tests
//
promise_test(t => {
  return run_test(t, self.origin, 'window.open', 'no-sw',
                  SameSiteStatus.STRICT, [SECURE_SUBDOMAIN_ORIGIN]);
}, 'same-origin, window.open with no service worker and same-site redirect');

promise_test(t => {
  return run_test(t, self.origin, 'window.open', 'fallback',
                  SameSiteStatus.STRICT, [SECURE_SUBDOMAIN_ORIGIN]);
}, 'same-origin, window.open with fallback and same-site redirect');

promise_test(t => {
  return run_test(t, self.origin, 'window.open', 'passthrough',
                  SameSiteStatus.STRICT, [SECURE_SUBDOMAIN_ORIGIN]);
}, 'same-origin, window.open with passthrough and same-site redirect');

promise_test(t => {
  return run_test(t, self.origin, 'window.open', 'change-request',
                  SameSiteStatus.STRICT, [SECURE_SUBDOMAIN_ORIGIN]);
}, 'same-origin, window.open with change-request and same-site redirect');

promise_test(t => {
  return run_test(t, self.origin, 'window.open', 'navpreload',
                  SameSiteStatus.STRICT, [SECURE_SUBDOMAIN_ORIGIN]);
}, 'same-origin, window.open with navpreload and same-site redirect');

promise_test(t => {
  return run_test(t, self.origin, 'window.open', 'no-sw',
                  SameSiteStatus.LAX, [SECURE_CROSS_SITE_ORIGIN]);
}, 'same-origin, window.open with no service worker and cross-site redirect');

promise_test(t => {
  return run_test(t, self.origin, 'window.open', 'fallback',
                  SameSiteStatus.LAX, [SECURE_CROSS_SITE_ORIGIN]);
}, 'same-origin, window.open with fallback and cross-site redirect');

promise_test(t => {
  return run_test(t, self.origin, 'window.open', 'passthrough',
                  SameSiteStatus.LAX, [SECURE_CROSS_SITE_ORIGIN]);
}, 'same-origin, window.open with passthrough and cross-site redirect');

promise_test(t => {
  return run_test(t, self.origin, 'window.open', 'change-request',
                  SameSiteStatus.STRICT, [SECURE_CROSS_SITE_ORIGIN]);
}, 'same-origin, window.open with change-request and cross-site redirect');

promise_test(t => {
  return run_test(t, self.origin, 'window.open', 'navpreload',
                  SameSiteStatus.LAX, [SECURE_CROSS_SITE_ORIGIN]);
}, 'same-origin, window.open with navpreload and cross-site redirect');

promise_test(t => {
  return run_test(t, self.origin, 'window.open', 'no-sw',
                  SameSiteStatus.LAX, [SECURE_CROSS_SITE_ORIGIN, self.origin]);
}, 'same-origin, window.open with no service worker, cross-site redirect, and ' +
   'same-origin redirect');

promise_test(t => {
  return run_test(t, self.origin, 'window.open', 'fallback',
                  SameSiteStatus.LAX, [SECURE_CROSS_SITE_ORIGIN, self.origin]);
}, 'same-origin, window.open with fallback, cross-site redirect, and ' +
   'same-origin redirect');

promise_test(t => {
  return run_test(t, self.origin, 'window.open', 'passthrough',
                  SameSiteStatus.LAX, [SECURE_CROSS_SITE_ORIGIN, self.origin]);
}, 'same-origin, window.open with passthrough, cross-site redirect, and ' +
   'same-origin redirect');

promise_test(t => {
  return run_test(t, self.origin, 'window.open', 'change-request',
                  SameSiteStatus.STRICT, [SECURE_CROSS_SITE_ORIGIN, self.origin]);
}, 'same-origin, window.open with change-request, cross-site redirect, and ' +
   'same-origin redirect');

promise_test(t => {
  return run_test(t, self.origin, 'window.open', 'navpreload',
                  SameSiteStatus.LAX, [SECURE_CROSS_SITE_ORIGIN, self.origin]);
}, 'same-origin, window.open with navpreload, cross-site redirect, and ' +
   'same-origin redirect');

//
// Double-nested frame calling open.window() tests
//
promise_test(t => {
  return run_test(t, self.origin, 'window.open', 'no-sw',
                  SameSiteStatus.STRICT, [],
                  [self.origin, SECURE_CROSS_SITE_ORIGIN]);
}, 'same-origin, nested window.open with cross-site middle frame and ' +
   'no service worker');

promise_test(t => {
  return run_test(t, self.origin, 'window.open', 'fallback',
                  SameSiteStatus.STRICT, [],
                  [self.origin, SECURE_CROSS_SITE_ORIGIN]);
}, 'same-origin, nested window.open with cross-site middle frame and ' +
   'fallback service worker');

promise_test(t => {
  return run_test(t, self.origin, 'window.open', 'passthrough',
                  SameSiteStatus.STRICT, [],
                  [self.origin, SECURE_CROSS_SITE_ORIGIN]);
}, 'same-origin, nested window.open with cross-site middle frame and ' +
   'passthrough service worker');

promise_test(t => {
  return run_test(t, self.origin, 'window.open', 'change-request',
                  SameSiteStatus.STRICT, [],
                  [self.origin, SECURE_CROSS_SITE_ORIGIN]);
}, 'same-origin, nested window.open with cross-site middle frame and ' +
   'change-request service worker');

promise_test(t => {
  return run_test(t, self.origin, 'window.open', 'navpreload',
                  SameSiteStatus.STRICT, [],
                  [self.origin, SECURE_CROSS_SITE_ORIGIN]);
}, 'same-origin, nested window.open with cross-site middle frame and ' +
   'navpreload service worker');

//
// Double-nested frame setting location tests
//
promise_test(t => {
  return run_test(t, self.origin, 'set location', 'no-sw',
                  SameSiteStatus.CROSS_SITE, [],
                  [self.origin, SECURE_CROSS_SITE_ORIGIN]);
}, 'same-origin, nested set location with cross-site middle frame and ' +
   'no service worker');

promise_test(t => {
  return run_test(t, self.origin, 'set location', 'fallback',
                  SameSiteStatus.CROSS_SITE, [],
                  [self.origin, SECURE_CROSS_SITE_ORIGIN]);
}, 'same-origin, nested set location with cross-site middle frame and ' +
   'fallback service worker');

promise_test(t => {
  return run_test(t, self.origin, 'set location', 'passthrough',
                  SameSiteStatus.CROSS_SITE, [],
                  [self.origin, SECURE_CROSS_SITE_ORIGIN]);
}, 'same-origin, nested set location with cross-site middle frame and ' +
   'passthrough service worker');

promise_test(t => {
  return run_test(t, self.origin, 'set location', 'change-request',
                  SameSiteStatus.CROSS_SITE, [],
                  [self.origin, SECURE_CROSS_SITE_ORIGIN]);
}, 'same-origin, nested set location with cross-site middle frame and ' +
   'change-request service worker');

promise_test(t => {
  return run_test(t, self.origin, 'set location', 'navpreload',
                  SameSiteStatus.CROSS_SITE, [],
                  [self.origin, SECURE_CROSS_SITE_ORIGIN]);
}, 'same-origin, nested set location with cross-site middle frame and ' +
   'navpreload service worker');

//
// Form POST tests
//
promise_test(t => {
  return run_test(t, self.origin, 'form post', 'no-sw', SameSiteStatus.STRICT);
}, 'same-origin, form post with no service worker');

promise_test(t => {
  return run_test(t, self.origin, 'form post', 'fallback',
                  SameSiteStatus.STRICT);
}, 'same-origin, form post with fallback');

promise_test(t => {
  return run_test(t, self.origin, 'form post', 'passthrough',
                  SameSiteStatus.STRICT);
}, 'same-origin, form post with passthrough');

promise_test(t => {
  return run_test(t, self.origin, 'form post', 'change-request',
                  SameSiteStatus.STRICT);
}, 'same-origin, form post with change-request');

promise_test(t => {
  return run_test(t, SECURE_SUBDOMAIN_ORIGIN, 'form post', 'no-sw',
                  SameSiteStatus.STRICT);
}, 'same-site, form post with no service worker');

promise_test(t => {
  return run_test(t, SECURE_SUBDOMAIN_ORIGIN, 'form post', 'fallback',
                  SameSiteStatus.STRICT);
}, 'same-site, form post with fallback');

promise_test(t => {
  return run_test(t, SECURE_SUBDOMAIN_ORIGIN, 'form post', 'passthrough',
                  SameSiteStatus.STRICT);
}, 'same-site, form post with passthrough');

promise_test(t => {
  return run_test(t, SECURE_SUBDOMAIN_ORIGIN, 'form post', 'change-request',
                  SameSiteStatus.STRICT);
}, 'same-site, form post with change-request');

promise_test(t => {
  return run_test(t, SECURE_CROSS_SITE_ORIGIN, 'form post', 'no-sw',
                  SameSiteStatus.CROSS_SITE);
}, 'cross-site, form post with no service worker');

promise_test(t => {
  return run_test(t, SECURE_CROSS_SITE_ORIGIN, 'form post', 'fallback',
                  SameSiteStatus.CROSS_SITE);
}, 'cross-site, form post with fallback');

promise_test(t => {
  return run_test(t, SECURE_CROSS_SITE_ORIGIN, 'form post', 'passthrough',
                  SameSiteStatus.CROSS_SITE);
}, 'cross-site, form post with passthrough');

promise_test(t => {
  return run_test(t, SECURE_CROSS_SITE_ORIGIN, 'form post', 'change-request',
                  SameSiteStatus.STRICT);
}, 'cross-site, form post with change-request');

//
// Form POST redirect tests
//
promise_test(t => {
  return run_test(t, self.origin, 'form post', 'no-sw',
                  SameSiteStatus.STRICT, [SECURE_SUBDOMAIN_ORIGIN]);
}, 'same-origin, form post with no service worker and same-site redirect');

promise_test(t => {
  return run_test(t, self.origin, 'form post', 'fallback',
                  SameSiteStatus.STRICT, [SECURE_SUBDOMAIN_ORIGIN]);
}, 'same-origin, form post with fallback and same-site redirect');

promise_test(t => {
  return run_test(t, self.origin, 'form post', 'passthrough',
                  SameSiteStatus.STRICT, [SECURE_SUBDOMAIN_ORIGIN]);
}, 'same-origin, form post with passthrough and same-site redirect');

promise_test(t => {
  return run_test(t, self.origin, 'form post', 'change-request',
                  SameSiteStatus.STRICT, [SECURE_SUBDOMAIN_ORIGIN]);
}, 'same-origin, form post with change-request and same-site redirect');

// navpreload is not supported for POST requests

promise_test(t => {
  return run_test(t, self.origin, 'form post', 'no-sw',
                  SameSiteStatus.CROSS_SITE, [SECURE_CROSS_SITE_ORIGIN]);
}, 'same-origin, form post with no service worker and cross-site redirect');

promise_test(t => {
  return run_test(t, self.origin, 'form post', 'fallback',
                  SameSiteStatus.CROSS_SITE, [SECURE_CROSS_SITE_ORIGIN]);
}, 'same-origin, form post with fallback and cross-site redirect');

promise_test(t => {
  return run_test(t, self.origin, 'form post', 'passthrough',
                  SameSiteStatus.CROSS_SITE, [SECURE_CROSS_SITE_ORIGIN]);
}, 'same-origin, form post with passthrough and cross-site redirect');

promise_test(t => {
  return run_test(t, self.origin, 'form post', 'change-request',
                  SameSiteStatus.STRICT, [SECURE_CROSS_SITE_ORIGIN]);
}, 'same-origin, form post with change-request and cross-site redirect');

// navpreload is not supported for POST requests

promise_test(t => {
  return run_test(t, self.origin, 'form post', 'no-sw',
                  SameSiteStatus.CROSS_SITE, [SECURE_CROSS_SITE_ORIGIN,
                                              self.origin]);
}, 'same-origin, form post with no service worker, cross-site redirect, and ' +
   'same-origin redirect');

promise_test(t => {
  return run_test(t, self.origin, 'form post', 'fallback',
                  SameSiteStatus.CROSS_SITE, [SECURE_CROSS_SITE_ORIGIN,
                                              self.origin]);
}, 'same-origin, form post with fallback, cross-site redirect, and ' +
   'same-origin redirect');

promise_test(t => {
  return run_test(t, self.origin, 'form post', 'passthrough',
                  SameSiteStatus.CROSS_SITE, [SECURE_CROSS_SITE_ORIGIN,
                                              self.origin]);
}, 'same-origin, form post with passthrough, cross-site redirect, and ' +
   'same-origin redirect');

promise_test(t => {
  return run_test(t, self.origin, 'form post', 'change-request',
                  SameSiteStatus.STRICT, [SECURE_CROSS_SITE_ORIGIN,
                                          self.origin]);
}, 'same-origin, form post with change-request, cross-site redirect, and ' +
   'same-origin redirect');

// navpreload is not supported for POST requests

promise_test(async t => {
  await unregister_service_worker(self.origin);
  await unregister_service_worker(SECURE_SUBDOMAIN_ORIGIN);
  await unregister_service_worker(SECURE_CROSS_SITE_ORIGIN);
  await unregister_service_worker(self.origin,
      [self.origin, SECURE_CROSS_SITE_ORIGIN]);
}, 'Cleanup service workers');

</script>
</body>
