// META: script=/storage-access-api/helpers.js
// META: script=/resources/testdriver.js
// META: script=/resources/testdriver-vendor.js
'use strict';

const requestedOrigin = 'https://foo.com';
const altOrigin = 'https://{{hosts[alt][www]}}:{{ports[https][0]}}';

promise_test(
    async () => {
      assert_not_equals(document.requestStorageAccessFor, undefined);
    },
    '[top-level-context] document.requestStorageAccessFor() should be supported on the document interface');

promise_test(
  t => {
    return promise_rejects_js(t, TypeError,
      document.requestStorageAccessFor(),
      'document.requestStorageAccessFor() call without origin argument');
  },
  '[top-level-context] document.requestStorageAccessFor() should be rejected when called with no argument');

// Most tests need to start with the feature in "prompt" state.
// For tests that rely on the permission state, this function is intended to be
// run prior to executing test logic, rather than using clean-up functions for
// the permission.
async function CommonSetup() {
  await test_driver.set_permission(
    { name: 'top-level-storage-access', requestedOrigin }, 'prompt');
  await test_driver.set_permission(
    { name: 'top-level-storage-access', requestedOrigin: altOrigin }, 'prompt');
}

promise_test(async t => {
      await CommonSetup();
      return promise_rejects_dom(t, 'NotAllowedError',
        document.requestStorageAccessFor(requestedOrigin),
        'document.requestStorageAccessFor() call without user gesture');
    },
    '[top-level-context] document.requestStorageAccessFor() should be rejected by default with no user gesture');

promise_test(async t => {
  const description =
      'document.requestStorageAccessFor() call in a detached frame';
  // Can't use promise_rejects_dom here because the exception is from the wrong global.
  return CreateDetachedFrame().requestStorageAccessFor(requestedOrigin)
      .then(t.unreached_func('Should have rejected: ' + description))
      .catch((e) => {
        assert_equals(e.name, 'InvalidStateError', description);
      });
}, '[non-fully-active] document.requestStorageAccessFor() should not resolve when run in a detached frame');

promise_test(async t => {
  const description =
      'document.requestStorageAccessFor() in a detached DOMParser result';
  return CreateDocumentViaDOMParser().requestStorageAccessFor(requestedOrigin)
      .then(t.unreached_func('Should have rejected: ' + description))
      .catch((e) => {
        assert_equals(e.name, 'InvalidStateError', description);
      });
}, '[non-fully-active] document.requestStorageAccessFor() should not resolve when run in a detached DOMParser document');

promise_test(
    async t => {
      await CommonSetup();
      await test_driver.set_permission(
          {name: 'top-level-storage-access', requestedOrigin}, 'granted');

      await document.requestStorageAccessFor(requestedOrigin);
    },
    '[top-level-context] document.requestStorageAccessFor() should be resolved without a user gesture with an existing permission');

promise_test(
    async t => {
      await CommonSetup();
      await test_driver.set_permission(
          {name: 'top-level-storage-access', requestedOrigin: altOrigin},
          'granted');

      const frame = await CreateFrame(
        altOrigin + '/storage-access-api/resources/script-with-cookie-header.py?script=embedded_responder.js');

      await RunCallbackWithGesture(() => document.requestStorageAccessFor(altOrigin));
      assert_true(await RequestStorageAccessInFrame(frame));
    },
    '[top-level-context] document.requestStorageAccess() should be resolved without a user gesture after a successful requestStorageAccessFor() call');

promise_test(
    async t => {
      await RunCallbackWithGesture(
        () => document.requestStorageAccessFor(document.location.origin));
    },
    '[top-level-context] document.requestStorageAccessFor() should be resolved when called properly with a user gesture and the same origin');

promise_test(
   async t =>{
    await RunCallbackWithGesture(
      () => promise_rejects_js(t, TypeError, document.requestStorageAccessFor('bogus-url'),
          'document.requestStorageAccessFor() call with bogus URL'));
    },
    '[top-level-context] document.requestStorageAccessFor() should be rejected when called with an invalid origin');

promise_test(
    async t => {
      await RunCallbackWithGesture(
        () => promise_rejects_dom(t, 'NotAllowedError', document.requestStorageAccessFor('data:,Hello%2C%20World%21'),
          'document.requestStorageAccessFor() call with data URL'));
    },
    '[top-level-context] document.requestStorageAccessFor() should be rejected when called with an opaque origin');

promise_test(
    async (t) => {
      const altEchoCookieHeaderUrl =
          `${altOrigin}/storage-access-api/resources/echo-cookie-header.py`;

      await MaybeSetStorageAccess('*', '*', 'blocked');
      await CommonSetup();

      await test_driver.set_permission(
          {name: 'top-level-storage-access', requestedOrigin: altOrigin},
          'granted');

      // Set cross-site cookie for altOrigin. Note that this only works with
      // an existing top-level storage access permission.
      await fetch(
          `${altOrigin}/cookies/resources/set-cookie.py?name=cookie&path=/&samesite=None&secure=`,
          {mode: 'cors', credentials: 'include'});

      const httpCookies1 = await fetch(altEchoCookieHeaderUrl, {
                              mode: 'cors',
                              credentials: 'include'
                            }).then((resp) => resp.text());
      assert_true(
          httpCookies1.includes('cookie=1'),
          'After obtaining top-level storage access, cross-site subresource requests with CORS mode should have cookie access.');

      const httpCookies2 = await fetch(altEchoCookieHeaderUrl, {
                              mode: 'no-cors',
                              credentials: 'include'
                            }).then((resp) => resp.text());
      assert_false(
          httpCookies2.includes('cookie=1'),
          'Cross-site subresource requests without CORS mode cannot access cookie even with an existing permission.');
    },
    '[top-level-context] Top-level storage access only allows cross-site subresource requests to access cookie when using CORS mode.');

promise_test(
    async () => {
      const frame = await CreateFrame(
        '/storage-access-api/resources/script-with-cookie-header.py?script=embedded_responder.js');
      assert_not_equals(frame.contentWindow.document.requestStorageAccessFor, undefined);
    },
    '[same-origin-iframe] document.requestStorageAccessFor() should be supported on the document interface');

promise_test(
    async t => {
      const frame = await CreateFrame(
        '/storage-access-api/resources/script-with-cookie-header.py?script=embedded_responder.js');
      return promise_rejects_js(t, frame.contentWindow.TypeError,
        frame.contentWindow.document.requestStorageAccessFor(),
        'document.requestStorageAccessFor() call without origin argument');
    },
    '[same-origin-iframe] document.requestStorageAccessFor() should be rejected when called with no argument');

promise_test(
    async t => {
      const frame = await CreateFrame(
        '/storage-access-api/resources/script-with-cookie-header.py?script=embedded_responder.js');

      await RunCallbackWithGesture(() =>
          promise_rejects_dom(t, 'NotAllowedError', frame.contentWindow.DOMException,
            frame.contentWindow.document.requestStorageAccessFor(document.location.origin),
            'document.requestStorageAccessFor() call in a non-top-level context'));
    },
    '[same-origin-iframe] document.requestStorageAccessFor() should be rejected when called in an iframe');

promise_test(
    async (t) => {
      await MaybeSetStorageAccess('*', '*', 'blocked');
      await CommonSetup();

      const frame = await CreateFrame(
        `/storage-access-api/resources/script-with-cookie-header.py?script=embedded_responder.js`);

      // Set cross-site cookie for altOrigin. Note that cookie won't be set
      // even with an existing top-level storage access permission in an
      // iframe.
      await FetchFromFrame(frame,
          `${altOrigin}/cookies/resources/set-cookie.py?name=cookie&path=/&samesite=None&secure=`);

      await test_driver.set_permission(
          {name: 'top-level-storage-access', requestedOrigin: altOrigin},
          'granted');

      const httpCookies = await FetchSubresourceCookiesFromFrame(frame, altOrigin);
      assert_false(httpCookies.includes('cookie=1'));
    },
    '[same-origin-iframe] Existing top-level storage access permission should not allow cookie access for the cross-site subresource requests made in a non-top-level context.');
